Rev 4762 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4762 | Rev 4998 | ||
---|---|---|---|
1 | /* ANM file replacement with VP8 video */
|
1 | /* ANM file replacement with VP8 video */
|
2 | 2 | ||
3 | #ifdef USE_LIBVPX
|
3 | #ifdef USE_LIBVPX
|
4 | 4 | ||
5 | #ifndef __STDC_FORMAT_MACROS
|
5 | #ifndef __STDC_FORMAT_MACROS
|
6 | #define __STDC_FORMAT_MACROS
|
6 | #define __STDC_FORMAT_MACROS
|
7 | #endif
|
7 | #endif
|
8 | #ifndef __STDC_LIMIT_MACROS
|
8 | #ifndef __STDC_LIMIT_MACROS
|
9 | #define __STDC_LIMIT_MACROS
|
9 | #define __STDC_LIMIT_MACROS
|
10 | #endif
|
10 | #endif
|
11 | 11 | ||
12 | 12 | ||
13 | #include <stdint.h>
|
13 | #include <stdint.h>
|
14 | 14 | ||
15 | #include "compat.h"
|
15 | #include "compat.h"
|
16 | #include "baselayer.h"
|
16 | #include "baselayer.h"
|
17 | #include "build.h"
|
17 | #include "build.h"
|
18 | #include "glbuild.h"
|
18 | #include "glbuild.h"
|
19 | 19 | ||
20 | #define VPX_CODEC_DISABLE_COMPAT 1
|
20 | #define VPX_CODEC_DISABLE_COMPAT 1
|
21 | #include <vpx/vpx_decoder.h>
|
21 | #include <vpx/vpx_decoder.h>
|
22 | #include <vpx/vp8dx.h>
|
22 | #include <vpx/vp8dx.h>
|
23 | 23 | ||
24 | #include "duke3d.h"
|
24 | #include "duke3d.h"
|
25 | #include "game.h" // kopen4loadfrommod
|
25 | #include "game.h" // kopen4loadfrommod
|
26 | #include "animvpx.h"
|
26 | #include "animvpx.h"
|
27 | 27 | ||
28 | const char *animvpx_read_ivf_header_errmsg[] = { |
28 | const char *animvpx_read_ivf_header_errmsg[] = { |
29 | "All OK", |
29 | "All OK", |
30 | "couldn't read 32-byte IVF header", |
30 | "couldn't read 32-byte IVF header", |
31 | "magic mismatch, not an IVF file", |
31 | "magic mismatch, not an IVF file", |
32 | "unrecognized IVF version, expected 0", |
32 | "unrecognized IVF version, expected 0", |
33 | "only VP8 video stream supported", |
33 | "only VP8 video stream supported", |
34 | "invalid framerate numerator or denominator after correction, must not be 0", |
34 | "invalid framerate numerator or denominator after correction, must not be 0", |
35 | }; |
35 | }; |
36 | 36 | ||
37 | EDUKE32_STATIC_ASSERT(sizeof(animvpx_ivf_header_t) == 32); |
37 | EDUKE32_STATIC_ASSERT(sizeof(animvpx_ivf_header_t) == 32); |
38 | 38 | ||
39 | int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr) |
39 | int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr) |
40 | {
|
40 | {
|
41 | int32_t err; |
41 | int32_t err; |
42 | 42 | ||
43 | if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t)) |
43 | if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t)) |
44 | return 1; // "couldn't read header" |
44 | return 1; // "couldn't read header" |
45 | 45 | ||
46 | err = animvpx_check_header(hdr); |
46 | err = animvpx_check_header(hdr); |
47 | if (err) |
47 | if (err) |
48 | return err; |
48 | return err; |
49 | 49 | ||
50 | hdr->hdrlen = B_LITTLE16(hdr->hdrlen); |
50 | hdr->hdrlen = B_LITTLE16(hdr->hdrlen); |
51 | 51 | ||
52 | hdr->width = B_LITTLE16(hdr->width); |
52 | hdr->width = B_LITTLE16(hdr->width); |
53 | hdr->height = B_LITTLE16(hdr->height); |
53 | hdr->height = B_LITTLE16(hdr->height); |
54 | hdr->fpsnumer = B_LITTLE16(hdr->fpsnumer); |
54 | hdr->fpsnumer = B_LITTLE16(hdr->fpsnumer); |
55 | hdr->fpsdenom = B_LITTLE16(hdr->fpsdenom); |
55 | hdr->fpsdenom = B_LITTLE16(hdr->fpsdenom); |
56 | 56 | ||
57 | hdr->numframes = B_LITTLE32(hdr->numframes); |
57 | hdr->numframes = B_LITTLE32(hdr->numframes); |
58 | 58 | ||
59 | // the rest is based on vpxdec.c --> file_is_ivf()
|
59 | // the rest is based on vpxdec.c --> file_is_ivf()
|
60 | 60 | ||
61 | if (hdr->fpsnumer < 1000) |
61 | if (hdr->fpsnumer < 1000) |
62 | {
|
62 | {
|
63 | // NOTE: We got rid of the 1/(2*fps) correction from libvpx's vpxdec.c,
|
63 | // NOTE: We got rid of the 1/(2*fps) correction from libvpx's vpxdec.c,
|
64 | // users are encouraged to use the "ivfrate" utility from the source/
|
64 | // users are encouraged to use the "ivfrate" utility from the source/
|
65 | // directory instead.
|
65 | // directory instead.
|
66 | 66 | ||
67 | if (hdr->fpsdenom==0 || hdr->fpsnumer==0) |
67 | if (hdr->fpsdenom==0 || hdr->fpsnumer==0) |
68 | return 5; // "invalid framerate numerator or denominator" |
68 | return 5; // "invalid framerate numerator or denominator" |
69 | 69 | ||
70 | initprintf("animvpx: rate is %d frames / %d seconds (%.03f fps).\n", |
70 | initprintf("animvpx: rate is %d frames / %d seconds (%.03f fps).\n", |
71 | hdr->fpsnumer, hdr->fpsdenom, (double)hdr->fpsnumer/hdr->fpsdenom); |
71 | hdr->fpsnumer, hdr->fpsdenom, (double)hdr->fpsnumer/hdr->fpsdenom); |
72 | }
|
72 | }
|
73 | else
|
73 | else
|
74 | {
|
74 | {
|
75 | double fps = (hdr->fpsdenom==0) ? 0.0 : (double)hdr->fpsnumer/hdr->fpsdenom; |
75 | double fps = (hdr->fpsdenom==0) ? 0.0 : (double)hdr->fpsnumer/hdr->fpsdenom; |
76 | 76 | ||
77 | initprintf("animvpx: set rate to 30 fps (header says %d frames / %d seconds = %.03f fps).\n", |
77 | initprintf("animvpx: set rate to 30 fps (header says %d frames / %d seconds = %.03f fps).\n", |
78 | hdr->fpsnumer, hdr->fpsdenom, fps); |
78 | hdr->fpsnumer, hdr->fpsdenom, fps); |
79 | 79 | ||
80 | /* Don't know FPS for sure, and don't have readahead code
|
80 | /* Don't know FPS for sure, and don't have readahead code
|
81 | * (yet?), so just default to 30fps.
|
81 | * (yet?), so just default to 30fps.
|
82 | */
|
82 | */
|
83 | hdr->fpsnumer = 30; |
83 | hdr->fpsnumer = 30; |
84 | hdr->fpsdenom = 1; |
84 | hdr->fpsdenom = 1; |
85 | }
|
85 | }
|
86 | 86 | ||
87 | return 0; |
87 | return 0; |
88 | }
|
88 | }
|
89 | 89 | ||
90 | ////////// CODEC STUFF //////////
|
90 | ////////// CODEC STUFF //////////
|
91 | static void get_codec_error(animvpx_codec_ctx *codec) |
91 | static void get_codec_error(animvpx_codec_ctx *codec) |
92 | {
|
92 | {
|
93 | codec->errmsg_detail = vpx_codec_error_detail(&codec->codec); |
93 | codec->errmsg_detail = vpx_codec_error_detail(&codec->codec); |
94 | codec->errmsg = vpx_codec_error(&codec->codec); |
94 | codec->errmsg = vpx_codec_error(&codec->codec); |
95 | }
|
95 | }
|
96 | 96 | ||
97 | // no checks for double-init!
|
97 | // no checks for double-init!
|
98 | int32_t animvpx_init_codec(const animvpx_ivf_header_t *info, int32_t inhandle, animvpx_codec_ctx *codec) |
98 | int32_t animvpx_init_codec(const animvpx_ivf_header_t *info, int32_t inhandle, animvpx_codec_ctx *codec) |
99 | {
|
99 | {
|
100 | vpx_codec_dec_cfg_t cfg;
|
100 | vpx_codec_dec_cfg_t cfg;
|
101 | 101 | ||
102 | cfg.threads = 1; |
102 | cfg.threads = 1; |
103 | cfg.w = info->width; |
103 | cfg.w = info->width; |
104 | cfg.h = info->height; |
104 | cfg.h = info->height; |
105 | 105 | ||
106 | codec->width = info->width; |
106 | codec->width = info->width; |
107 | codec->height = info->height; |
107 | codec->height = info->height; |
108 | 108 | ||
109 | //
|
109 | //
|
110 | codec->inhandle = inhandle; |
110 | codec->inhandle = inhandle; |
111 | codec->pic = (uint8_t *)Bcalloc(info->width*info->height,4); |
111 | codec->pic = (uint8_t *)Bcalloc(info->width*info->height,4); |
112 | 112 | ||
113 | codec->compbuflen = codec->compbufallocsiz = 0; |
113 | codec->compbuflen = codec->compbufallocsiz = 0; |
114 | codec->compbuf = NULL; |
114 | codec->compbuf = NULL; |
115 | 115 | ||
116 | codec->iter = NULL; |
116 | codec->iter = NULL; |
117 | 117 | ||
118 | if (codec->pic == NULL) |
118 | if (codec->pic == NULL) |
119 | {
|
119 | {
|
120 | codec->initstate = -1; |
120 | codec->initstate = -1; |
121 | return 1; |
121 | return 1; |
122 | }
|
122 | }
|
123 | 123 | ||
124 | if (vpx_codec_dec_init(&codec->codec, &vpx_codec_vp8_dx_algo, &cfg, 0)) |
124 | if (vpx_codec_dec_init(&codec->codec, &vpx_codec_vp8_dx_algo, &cfg, 0)) |
125 | {
|
125 | {
|
126 | get_codec_error(codec); |
126 | get_codec_error(codec); |
127 | codec->initstate = -1; |
127 | codec->initstate = -1; |
128 | return 1; |
128 | return 1; |
129 | }
|
129 | }
|
130 | 130 | ||
131 | codec->initstate = 1; |
131 | codec->initstate = 1; |
132 | codec->decstate = 0; |
132 | codec->decstate = 0; |
133 | 133 | ||
134 | codec->errmsg_detail = codec->errmsg = NULL; |
134 | codec->errmsg_detail = codec->errmsg = NULL; |
135 | 135 | ||
136 | codec->numframes = 0; |
136 | codec->numframes = 0; |
137 | Bmemset(codec->sumtimes, 0, sizeof(codec->sumtimes)); |
137 | Bmemset(codec->sumtimes, 0, sizeof(codec->sumtimes)); |
138 | Bmemset(codec->maxtimes, 0, sizeof(codec->maxtimes)); |
138 | Bmemset(codec->maxtimes, 0, sizeof(codec->maxtimes)); |
139 | 139 | ||
140 | return 0; |
140 | return 0; |
141 | }
|
141 | }
|
142 | 142 | ||
143 | int32_t animvpx_uninit_codec(animvpx_codec_ctx *codec) |
143 | int32_t animvpx_uninit_codec(animvpx_codec_ctx *codec) |
144 | {
|
144 | {
|
145 | if (codec->initstate <= 0) |
145 | if (codec->initstate <= 0) |
146 | return 2; |
146 | return 2; |
147 | 147 | ||
148 | Bfree(codec->pic); |
148 | Bfree(codec->pic); |
149 | codec->pic = NULL; |
149 | codec->pic = NULL; |
150 | 150 | ||
151 | if (vpx_codec_destroy(&codec->codec)) |
151 | if (vpx_codec_destroy(&codec->codec)) |
152 | {
|
152 | {
|
153 | get_codec_error(codec); |
153 | get_codec_error(codec); |
154 | codec->initstate = -2; |
154 | codec->initstate = -2; |
155 | return 1; |
155 | return 1; |
156 | }
|
156 | }
|
157 | 157 | ||
158 | codec->initstate = 0; |
158 | codec->initstate = 0; |
159 | 159 | ||
160 | return 0; |
160 | return 0; |
161 | }
|
161 | }
|
162 | 162 | ||
163 | ////////// FRAME RETRIEVAL //////////
|
163 | ////////// FRAME RETRIEVAL //////////
|
164 | 164 | ||
165 | // read one IVF/VP8 frame, which may code multiple "picture-frames"
|
165 | // read one IVF/VP8 frame, which may code multiple "picture-frames"
|
166 | static int32_t animvpx_read_frame(int32_t inhandle, uint8_t **bufptr, uint32_t *bufsizptr, uint32_t *bufallocsizptr) |
166 | static int32_t animvpx_read_frame(int32_t inhandle, uint8_t **bufptr, uint32_t *bufsizptr, uint32_t *bufallocsizptr) |
167 | {
|
167 | {
|
168 | #pragma pack(push,1)
|
168 | #pragma pack(push,1)
|
169 | struct { uint32_t framesiz; uint64_t timestamp; } hdr; |
169 | struct { uint32_t framesiz; uint64_t timestamp; } hdr; |
170 | #pragma pack(pop)
|
170 | #pragma pack(pop)
|
171 | 171 | ||
172 | if (kread(inhandle, &hdr, sizeof(hdr)) != sizeof(hdr)) |
172 | if (kread(inhandle, &hdr, sizeof(hdr)) != sizeof(hdr)) |
173 | return 1; |
173 | return 1; |
174 | 174 | ||
175 | if (hdr.framesiz == 0) |
175 | if (hdr.framesiz == 0) |
176 | return 6; // must be 6, see animvpx_nextpic_errmsg[] |
176 | return 6; // must be 6, see animvpx_nextpic_errmsg[] |
177 | 177 | ||
178 | // OSD_Printf("frame size: %u\n", hdr.framesiz);
|
178 | // OSD_Printf("frame size: %u\n", hdr.framesiz);
|
179 | 179 | ||
180 | if (!*bufptr) |
180 | if (!*bufptr) |
181 | {
|
181 | {
|
182 | *bufptr = (uint8_t *)Bmalloc(hdr.framesiz); |
182 | *bufptr = (uint8_t *)Bmalloc(hdr.framesiz); |
183 | if (!*bufptr) |
183 | if (!*bufptr) |
184 | return 2; |
184 | return 2; |
185 | *bufallocsizptr = hdr.framesiz; |
185 | *bufallocsizptr = hdr.framesiz; |
186 | }
|
186 | }
|
187 | else if (*bufallocsizptr < hdr.framesiz) |
187 | else if (*bufallocsizptr < hdr.framesiz) |
188 | {
|
188 | {
|
189 | *bufptr = (uint8_t *)Brealloc(*bufptr, hdr.framesiz); |
189 | *bufptr = (uint8_t *)Brealloc(*bufptr, hdr.framesiz); |
190 | if (!*bufptr) |
190 | if (!*bufptr) |
191 | return 2; |
191 | return 2; |
192 | *bufallocsizptr = hdr.framesiz; |
192 | *bufallocsizptr = hdr.framesiz; |
193 | }
|
193 | }
|
194 | 194 | ||
195 | *bufsizptr = hdr.framesiz; |
195 | *bufsizptr = hdr.framesiz; |
196 | 196 | ||
197 | if (kread(inhandle, *bufptr, hdr.framesiz) != (signed)hdr.framesiz) |
197 | if (kread(inhandle, *bufptr, hdr.framesiz) != (signed)hdr.framesiz) |
198 | return 3; |
198 | return 3; |
199 | 199 | ||
200 | return 0; |
200 | return 0; |
201 | }
|
201 | }
|
202 | 202 | ||
203 | const char *animvpx_nextpic_errmsg[] = { |
203 | const char *animvpx_nextpic_errmsg[] = { |
204 | "All OK", |
204 | "All OK", |
205 | "INTERNAL ERROR, animvpx_codec_ctx not initalized!", |
205 | "INTERNAL ERROR, animvpx_codec_ctx not initalized!", |
206 | "OUT OF MEMORY", |
206 | "OUT OF MEMORY", |
207 | "couldn't read whole frame", |
207 | "couldn't read whole frame", |
208 | "decoder error, couldn't decode frame", |
208 | "decoder error, couldn't decode frame", |
209 | "picture dimension mismatch", |
209 | "picture dimension mismatch", |
210 | "INTERNAL ERROR: read 0 frame length", |
210 | "INTERNAL ERROR: read 0 frame length", |
211 | "Failed getting corruption status (VP8D_GET_FRAME_CORRUPTED)"
|
211 | "Failed getting corruption status (VP8D_GET_FRAME_CORRUPTED)"
|
212 | }; |
212 | }; |
213 | 213 | ||
214 | // retrieves one picture-frame from the stream
|
214 | // retrieves one picture-frame from the stream
|
215 | // pic format: lines of [Y U V 0] pixels
|
215 | // pic format: lines of [Y U V 0] pixels
|
216 | // *picptr==NULL means EOF has been reached
|
216 | // *picptr==NULL means EOF has been reached
|
217 | #ifdef DEBUGGINGAIDS
|
217 | #ifdef DEBUGGINGAIDS
|
218 | ATTRIBUTE_OPTIMIZE("O1") |
218 | ATTRIBUTE_OPTIMIZE("O1") |
219 | #else
|
219 | #else
|
220 | ATTRIBUTE_OPTIMIZE("O3") |
220 | ATTRIBUTE_OPTIMIZE("O3") |
221 | #endif
|
221 | #endif
|
222 | int32_t animvpx_nextpic(animvpx_codec_ctx *codec, uint8_t **picptr) |
222 | int32_t animvpx_nextpic(animvpx_codec_ctx *codec, uint8_t **picptr) |
223 | {
|
223 | {
|
224 | int32_t ret, corrupted; |
224 | int32_t ret, corrupted; |
225 | vpx_image_t *img; |
225 | vpx_image_t *img; |
226 | 226 | ||
227 | int32_t t[3]; |
227 | int32_t t[3]; |
228 | 228 | ||
229 | if (codec->initstate <= 0) // not inited or error |
229 | if (codec->initstate <= 0) // not inited or error |
230 | return 1; |
230 | return 1; |
231 | 231 | ||
232 | t[0] = getticks(); |
232 | t[0] = getticks(); |
233 | 233 | ||
234 | if (codec->decstate == 0) // first time / begin |
234 | if (codec->decstate == 0) // first time / begin |
235 | {
|
235 | {
|
236 | read_ivf_frame:
|
236 | read_ivf_frame:
|
237 | corrupted = 0; |
237 | corrupted = 0; |
238 | 238 | ||
239 | ret = animvpx_read_frame(codec->inhandle, &codec->compbuf, &codec->compbuflen, |
239 | ret = animvpx_read_frame(codec->inhandle, &codec->compbuf, &codec->compbuflen, |
240 | &codec->compbufallocsiz); |
240 | &codec->compbufallocsiz); |
241 | if (ret == 1) |
241 | if (ret == 1) |
242 | {
|
242 | {
|
243 | // reached EOF
|
243 | // reached EOF
|
244 | *picptr = NULL; |
244 | *picptr = NULL; |
245 | codec->decstate = 2; |
245 | codec->decstate = 2; |
246 | return 0; |
246 | return 0; |
247 | }
|
247 | }
|
248 | else if (ret == 2 || ret == 3 || ret == 6) |
248 | else if (ret == 2 || ret == 3 || ret == 6) |
249 | {
|
249 | {
|
250 | *picptr = NULL; |
250 | *picptr = NULL; |
251 | codec->decstate = -1; |
251 | codec->decstate = -1; |
252 | return ret; |
252 | return ret; |
253 | }
|
253 | }
|
254 | // ^^^ keep in sync with all animvpx_read_frame() errors!
|
254 | // ^^^ keep in sync with all animvpx_read_frame() errors!
|
255 | 255 | ||
256 | // codec->compbuf now contains one IVF/VP8 frame
|
256 | // codec->compbuf now contains one IVF/VP8 frame
|
257 | codec->decstate = 1; |
257 | codec->decstate = 1; |
258 | 258 | ||
259 | // decode it!
|
259 | // decode it!
|
260 | if (vpx_codec_decode(&codec->codec, codec->compbuf, codec->compbuflen, NULL, 0)) |
260 | if (vpx_codec_decode(&codec->codec, codec->compbuf, codec->compbuflen, NULL, 0)) |
261 | {
|
261 | {
|
262 | get_codec_error(codec); |
262 | get_codec_error(codec); |
263 | codec->decstate = -2; |
263 | codec->decstate = -2; |
264 | return 4; |
264 | return 4; |
265 | }
|
265 | }
|
266 | 266 | ||
267 | // Compilation fix for Debian 6.0 (squeeze), which doesn't have
|
267 | // Compilation fix for Debian 6.0 (squeeze), which doesn't have
|
268 | // VP8D_GET_FRAME_CORRUPTED.
|
268 | // VP8D_GET_FRAME_CORRUPTED.
|
269 | // LibVPX doesn't seem to have a version #define, so we use the
|
269 | // LibVPX doesn't seem to have a version #define, so we use the
|
270 | // following one to determine conditional compilation.
|
270 | // following one to determine conditional compilation.
|
271 | #ifdef VPX_CODEC_CAP_ERROR_CONCEALMENT
|
271 | #ifdef VPX_CODEC_CAP_ERROR_CONCEALMENT
|
272 | if (vpx_codec_control(&codec->codec, VP8D_GET_FRAME_CORRUPTED, &corrupted)) |
272 | if (vpx_codec_control(&codec->codec, VP8D_GET_FRAME_CORRUPTED, &corrupted)) |
273 | {
|
273 | {
|
274 | get_codec_error(codec); |
274 | get_codec_error(codec); |
275 | codec->decstate = -2; |
275 | codec->decstate = -2; |
276 | return 7; |
276 | return 7; |
277 | }
|
277 | }
|
278 | #endif
|
278 | #endif
|
279 | if (corrupted) |
279 | if (corrupted) |
280 | OSD_Printf("warning: corrupted frame!\n"); |
280 | OSD_Printf("warning: corrupted frame!\n"); |
281 | }
|
281 | }
|
282 | 282 | ||
283 | img = vpx_codec_get_frame(&codec->codec, &codec->iter); |
283 | img = vpx_codec_get_frame(&codec->codec, &codec->iter); |
284 | if (img == NULL) |
284 | if (img == NULL) |
285 | {
|
285 | {
|
286 | codec->iter = NULL; // ! |
286 | codec->iter = NULL; // ! |
287 | goto read_ivf_frame; |
287 | goto read_ivf_frame; |
288 | }
|
288 | }
|
289 | 289 | ||
290 | if (img->d_w != codec->width || img->d_h != codec->height) |
290 | if (img->d_w != codec->width || img->d_h != codec->height) |
291 | {
|
291 | {
|
292 | codec->decstate = -1; |
292 | codec->decstate = -1; |
293 | return 5; |
293 | return 5; |
294 | }
|
294 | }
|
295 | 295 | ||
296 | t[1] = getticks(); |
296 | t[1] = getticks(); |
297 | 297 | ||
298 | /*** 3 planes --> packed conversion ***/
|
- | |
299 | {
|
- | |
300 | uint8_t *const dstpic = codec->pic; |
298 | uint8_t *const dstpic = codec->pic; |
301 | 299 | ||
302 | const uint8_t *const yplane = img->planes[VPX_PLANE_Y]; |
300 | uint8_t const *const yplane = img->planes[VPX_PLANE_Y]; |
303 | const uint8_t *const uplane = img->planes[VPX_PLANE_U]; |
301 | uint8_t const *const uplane = img->planes[VPX_PLANE_U]; |
304 | const uint8_t *const vplane = img->planes[VPX_PLANE_V]; |
302 | uint8_t const *const vplane = img->planes[VPX_PLANE_V]; |
305 | 303 | ||
306 | int32_t ystride = img->stride[VPX_PLANE_Y]; |
304 | const int32_t ystride = img->stride[VPX_PLANE_Y]; |
307 | int32_t ustride = img->stride[VPX_PLANE_U]; |
305 | const int32_t ustride = img->stride[VPX_PLANE_U]; |
308 | int32_t vstride = img->stride[VPX_PLANE_V]; |
306 | const int32_t vstride = img->stride[VPX_PLANE_V]; |
309 | 307 | ||
- | 308 | if (glinfo.glsl) /*** 3 planes --> packed conversion ***/ |
|
- | 309 | {
|
|
310 | int32_t x, y; |
310 | int32_t x, y; |
311 | const int32_t width=img->d_w, height = img->d_h; |
311 | const int32_t width = img->d_w, height = img->d_h; |
312 | 312 | ||
313 | for (y=0; y<height; y+=2) |
313 | for (y = 0; y < height; y += 2) |
314 | {
|
314 | {
|
315 | for (x=0; x<width; x+=2) |
315 | for (x = 0; x < width; x += 2) |
316 | {
|
316 | {
|
317 | uint8_t u = uplane[ustride*(y>>1) + (x>>1)]; |
317 | uint8_t u = uplane[ustride * (y >> 1) + (x >> 1)]; |
318 | uint8_t v = vplane[vstride*(y>>1) + (x>>1)]; |
318 | uint8_t v = vplane[vstride * (y >> 1) + (x >> 1)]; |
319 | 319 | ||
320 | dstpic[(width*y + x)<<2] = yplane[ystride*y + x]; |
320 | dstpic[(width * y + x) << 2] = yplane[ystride * y + x]; |
321 | dstpic[(width*y + x+1)<<2] = yplane[ystride*y + x+1]; |
321 | dstpic[(width * y + x + 1) << 2] = yplane[ystride * y + x + 1]; |
322 | dstpic[(width*(y+1) + x)<<2] = yplane[ystride*(y+1) + x]; |
322 | dstpic[(width * (y + 1) + x) << 2] = yplane[ystride * (y + 1) + x]; |
323 | dstpic[(width*(y+1) + x+1)<<2] = yplane[ystride*(y+1) + x+1]; |
323 | dstpic[(width * (y + 1) + x + 1) << 2] = yplane[ystride * (y + 1) + x + 1]; |
324 | 324 | ||
325 | dstpic[((width*y + x)<<2) + 1] = u; |
325 | dstpic[((width * y + x) << 2) + 1] = u; |
326 | dstpic[((width*y + x+1)<<2) + 1] = u; |
326 | dstpic[((width * y + x + 1) << 2) + 1] = u; |
327 | dstpic[((width*(y+1) + x)<<2) + 1] = u; |
327 | dstpic[((width * (y + 1) + x) << 2) + 1] = u; |
328 | dstpic[((width*(y+1) + x+1)<<2) + 1] = u; |
328 | dstpic[((width * (y + 1) + x + 1) << 2) + 1] = u; |
329 | 329 | ||
330 | dstpic[((width*y + x)<<2) + 2] = v; |
330 | dstpic[((width * y + x) << 2) + 2] = v; |
331 | dstpic[((width*y + x+1)<<2) + 2] = v; |
331 | dstpic[((width * y + x + 1) << 2) + 2] = v; |
332 | dstpic[((width*(y+1) + x)<<2) + 2] = v; |
332 | dstpic[((width * (y + 1) + x) << 2) + 2] = v; |
333 | dstpic[((width*(y+1) + x+1)<<2) + 2] = v; |
333 | dstpic[((width * (y + 1) + x + 1) << 2) + 2] = v; |
334 | }
|
334 | }
|
335 | }
|
335 | }
|
336 | }
|
336 | }
|
- | 337 | else /*** 3 planes --> packed conversion (RGB) ***/ |
|
- | 338 | {
|
|
- | 339 | int i = 0; |
|
- | 340 | ||
- | 341 | for (unsigned int imgY = 0; imgY < img->d_h; imgY++) |
|
- | 342 | {
|
|
- | 343 | for (unsigned int imgX = 0; imgX < img->d_w; imgX++) |
|
- | 344 | {
|
|
- | 345 | uint8_t const y = yplane[imgY * ystride + imgX]; |
|
- | 346 | uint8_t const u = uplane[(imgY >> 1) * ustride + (imgX >> 1)]; |
|
- | 347 | uint8_t const v = vplane[(imgY >> 1) * vstride + (imgX >> 1)]; |
|
- | 348 | ||
- | 349 | int const c = y - 16; |
|
- | 350 | int const d = (u + -128); |
|
- | 351 | int const e = (v + -128); |
|
- | 352 | int const c298 = c * 298; |
|
- | 353 | ||
- | 354 | dstpic[i + 0] = (uint8_t)clamp((c298 + 409 * e - -128) >> 8, 0, 255); |
|
- | 355 | dstpic[i + 1] = (uint8_t)clamp((c298 - 100 * d - 208 * e - -128) >> 8, 0, 255); |
|
- | 356 | dstpic[i + 2] = (uint8_t)clamp((c298 + 516 * d - -128) >> 8, 0, 255); |
|
- | 357 | ||
- | 358 | i += 3; |
|
- | 359 | }
|
|
- | 360 | }
|
|
- | 361 | }
|
|
337 | 362 | ||
338 | t[2] = getticks(); |
363 | t[2] = getticks(); |
339 | 364 | ||
340 | codec->sumtimes[0] += t[1]-t[0]; |
365 | codec->sumtimes[0] += t[1]-t[0]; |
341 | codec->sumtimes[1] += t[2]-t[1]; |
366 | codec->sumtimes[1] += t[2]-t[1]; |
342 | 367 | ||
343 | codec->maxtimes[0] = max(codec->maxtimes[0], t[1]-t[0]); |
368 | codec->maxtimes[0] = max(codec->maxtimes[0], t[1]-t[0]); |
344 | codec->maxtimes[1] = max(codec->maxtimes[0], t[2]-t[1]); |
369 | codec->maxtimes[1] = max(codec->maxtimes[0], t[2]-t[1]); |
345 | 370 | ||
346 | *picptr = codec->pic; |
371 | *picptr = codec->pic; |
347 | return 0; |
372 | return 0; |
348 | }
|
373 | }
|
349 | 374 | ||
350 | 375 | ||
351 | /////////////// DRAWING! ///////////////
|
376 | /////////////// DRAWING! ///////////////
|
352 | static GLuint texname = 0; |
377 | static GLuint texname = 0; |
353 | static int32_t texuploaded; |
378 | static int32_t texuploaded; |
354 | 379 | ||
355 | // YUV->RGB conversion fragment shader adapted from
|
380 | // YUV->RGB conversion fragment shader adapted from
|
356 | // http://www.fourcc.org/fccyvrgb.php: "Want some sample code?"
|
381 | // http://www.fourcc.org/fccyvrgb.php: "Want some sample code?"
|
357 | // direct link: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
|
382 | // direct link: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
|
358 | static char *fragprog_src = |
383 | static char *fragprog_src = |
359 | "#version 120\n"
|
384 | "#version 120\n"
|
360 | 385 | ||
361 | "uniform sampler2D tex;\n"
|
386 | "uniform sampler2D tex;\n"
|
362 | 387 | ||
363 | "void main(void) {\n"
|
388 | "void main(void) {\n"
|
364 | 389 | ||
365 | " float r,g,b,y,u,v;\n"
|
390 | " float r,g,b,y,u,v;\n"
|
366 | " vec3 yuv;\n"
|
391 | " vec3 yuv;\n"
|
367 | 392 | ||
368 | " yuv = texture2D(tex, gl_TexCoord[0].st).rgb;\n"
|
393 | " yuv = texture2D(tex, gl_TexCoord[0].st).rgb;\n"
|
369 | " y = yuv.r;\n"
|
394 | " y = yuv.r;\n"
|
370 | " u = yuv.g;\n"
|
395 | " u = yuv.g;\n"
|
371 | " v = yuv.b;\n"
|
396 | " v = yuv.b;\n"
|
372 | 397 | ||
373 | " y = 1.1643*(y-0.0625);\n"
|
398 | " y = 1.1643*(y-0.0625);\n"
|
374 | " u = u-0.5;\n"
|
399 | " u = u-0.5;\n"
|
375 | " v = v-0.5;\n"
|
400 | " v = v-0.5;\n"
|
376 | 401 | ||
377 | " r = y + 1.5958*v;\n"
|
402 | " r = y + 1.5958*v;\n"
|
378 | " g = y - 0.39173*u - 0.81290*v;\n"
|
403 | " g = y - 0.39173*u - 0.81290*v;\n"
|
379 | " b = y + 2.017*u;\n"
|
404 | " b = y + 2.017*u;\n"
|
380 | 405 | ||
381 | " gl_FragColor = vec4(r,g,b,1.0);\n"
|
406 | " gl_FragColor = vec4(r,g,b,1.0);\n"
|
382 | "}\n"; |
407 | "}\n"; |
383 | 408 | ||
384 | void animvpx_setup_glstate(void) |
409 | void animvpx_setup_glstate(void) |
385 | {
|
410 | {
|
- | 411 | if (glinfo.glsl) |
|
- | 412 | {
|
|
386 | GLint gli;
|
413 | GLint gli;
|
387 | GLhandleARB FSHandle, PHandle; |
414 | GLhandleARB FSHandle, PHandle; |
388 | static char logbuf[512]; |
415 | static char logbuf[512]; |
389 | 416 | ||
390 | // first, compile the fragment shader
|
417 | // first, compile the fragment shader
|
391 | /* Set up program objects. */
|
418 | /* Set up program objects. */
|
392 | PHandle = bglCreateProgramObjectARB(); |
419 | PHandle = bglCreateProgramObjectARB(); |
393 | FSHandle = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); |
420 | FSHandle = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); |
394 | 421 | ||
395 | /* Compile the shader. */
|
422 | /* Compile the shader. */
|
396 | bglShaderSourceARB(FSHandle, 1, (const GLcharARB **)&fragprog_src, NULL); |
423 | bglShaderSourceARB(FSHandle, 1, (const GLcharARB **)&fragprog_src, NULL); |
397 | bglCompileShaderARB(FSHandle); |
424 | bglCompileShaderARB(FSHandle); |
398 | 425 | ||
399 | /* Print the compilation log. */
|
426 | /* Print the compilation log. */
|
400 | bglGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &gli); |
427 | bglGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &gli); |
401 | bglGetInfoLogARB(FSHandle, sizeof(logbuf), NULL, logbuf); |
428 | bglGetInfoLogARB(FSHandle, sizeof(logbuf), NULL, logbuf); |
402 | if (logbuf[0]) |
429 | if (logbuf[0]) |
403 | OSD_Printf("animvpx compile log: %s\n", logbuf); |
430 | OSD_Printf("animvpx compile log: %s\n", logbuf); |
404 | 431 | ||
405 | /* Create a complete program object. */
|
432 | /* Create a complete program object. */
|
406 | bglAttachObjectARB(PHandle, FSHandle); |
433 | bglAttachObjectARB(PHandle, FSHandle); |
407 | bglLinkProgramARB(PHandle); |
434 | bglLinkProgramARB(PHandle); |
408 | 435 | ||
409 | /* And print the link log. */
|
436 | /* And print the link log. */
|
410 | bglGetInfoLogARB(PHandle, sizeof(logbuf), NULL, logbuf); |
437 | bglGetInfoLogARB(PHandle, sizeof(logbuf), NULL, logbuf); |
411 | if (logbuf[0]) |
438 | if (logbuf[0]) |
412 | OSD_Printf("animvpx link log: %s\n", logbuf); |
439 | OSD_Printf("animvpx link log: %s\n", logbuf); |
413 | 440 | ||
414 | /* Finally, use the program. */
|
441 | /* Finally, use the program. */
|
415 | bglUseProgramObjectARB(PHandle); |
442 | bglUseProgramObjectARB(PHandle); |
- | 443 | }
|
|
416 | 444 | ||
417 | ////////// GL STATE //////////
|
445 | ////////// GL STATE //////////
|
418 | 446 | ||
419 | //Force fullscreen (glox1=-1 forces it to restore afterwards)
|
447 | //Force fullscreen (glox1=-1 forces it to restore afterwards)
|
420 | bglViewport(0,0,xdim,ydim); glox1 = -1; |
448 | bglViewport(0,0,xdim,ydim); glox1 = -1; |
421 | 449 | ||
422 | bglMatrixMode(GL_MODELVIEW); |
450 | bglMatrixMode(GL_MODELVIEW); |
423 | bglLoadIdentity(); |
451 | bglLoadIdentity(); |
424 | 452 | ||
425 | bglMatrixMode(GL_PROJECTION); |
453 | bglMatrixMode(GL_PROJECTION); |
426 | bglLoadIdentity(); |
454 | bglLoadIdentity(); |
427 | 455 | ||
428 | bglMatrixMode(GL_COLOR); |
- | |
429 | bglLoadIdentity(); |
- | |
430 | - | ||
431 | bglMatrixMode(GL_TEXTURE); |
456 | bglMatrixMode(GL_TEXTURE); |
432 | bglLoadIdentity(); |
457 | bglLoadIdentity(); |
433 | 458 | ||
434 | bglPushAttrib(GL_ENABLE_BIT); |
459 | // bglPushAttrib(GL_ENABLE_BIT);
|
435 | bglDisable(GL_ALPHA_TEST); |
460 | bglDisable(GL_ALPHA_TEST); |
436 | // bglDisable(GL_LIGHTING);
|
- | |
437 | bglDisable(GL_DEPTH_TEST); |
461 | bglDisable(GL_DEPTH_TEST); |
438 | bglDisable(GL_BLEND); |
462 | bglDisable(GL_BLEND); |
439 | bglDisable(GL_CULL_FACE); |
463 | bglDisable(GL_CULL_FACE); |
440 | // bglDisable(GL_SCISSOR_TEST);
|
- | |
441 | bglEnable(GL_TEXTURE_2D); |
464 | bglEnable(GL_TEXTURE_2D); |
442 | 465 | ||
443 | - | ||
444 | bglActiveTextureARB(GL_TEXTURE0_ARB); |
466 | bglActiveTextureARB(GL_TEXTURE0_ARB); |
445 | bglGenTextures(1, &texname); |
467 | bglGenTextures(1, &texname); |
446 | // gli = bglGetUniformLocationARB(PHandle,"tex");
|
- | |
447 | // bglUniform1iARB(gli,0); // 0: texture unit
|
- | |
448 | bglBindTexture(GL_TEXTURE_2D, texname); |
468 | bglBindTexture(GL_TEXTURE_2D, texname); |
449 | 469 | ||
450 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); |
470 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); |
451 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); |
471 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glinfo.clamptoedge?GL_CLAMP_TO_EDGE:GL_CLAMP); |
452 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
472 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
453 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
473 | bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
454 | 474 | ||
455 | texuploaded = 0; |
475 | texuploaded = 0; |
456 | ////////////////////
|
476 | ////////////////////
|
457 | 477 | ||
458 | bglClearColor(0.0,0.0,0.0,1.0); |
478 | bglClearColor(0.0,0.0,0.0,1.0); |
459 | bglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
479 | bglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
460 | }
|
480 | }
|
461 | 481 | ||
462 | void animvpx_restore_glstate(void) |
482 | void animvpx_restore_glstate(void) |
463 | {
|
483 | {
|
- | 484 | if (glinfo.glsl) |
|
464 | bglUseProgramObjectARB(0); |
485 | bglUseProgramObjectARB(0); |
465 | 486 | ||
466 | bglPopAttrib(); |
487 | // bglPopAttrib();
|
467 | 488 | ||
468 | bglDeleteTextures(1, &texname); |
489 | bglDeleteTextures(1, &texname); |
469 | texname = 0; |
490 | texname = 0; |
470 | texuploaded = 0; |
491 | texuploaded = 0; |
471 | }
|
492 | }
|
472 | 493 | ||
473 | int32_t animvpx_render_frame(animvpx_codec_ctx *codec) |
494 | int32_t animvpx_render_frame(animvpx_codec_ctx *codec) |
474 | {
|
495 | {
|
475 | int32_t t = getticks(); |
496 | int32_t t = getticks(); |
476 | 497 | ||
477 | if (codec->initstate <= 0) // not inited or error |
498 | if (codec->initstate <= 0) // not inited or error |
478 | return 1; |
499 | return 1; |
479 | 500 | ||
480 | if (codec->pic == NULL) |
501 | if (codec->pic == NULL) |
481 | return 2; // shouldn't happen |
502 | return 2; // shouldn't happen |
482 | 503 | ||
- | 504 | int fmt = glinfo.glsl ? GL_RGBA : GL_RGB; |
|
- | 505 | ||
483 | if (!texuploaded) |
506 | if (!texuploaded) |
484 | {
|
507 | {
|
485 | bglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, codec->width,codec->height, |
508 | bglTexImage2D(GL_TEXTURE_2D, 0, fmt, codec->width,codec->height, |
486 | 0, GL_RGBA, GL_UNSIGNED_BYTE, codec->pic); |
509 | 0, fmt, GL_UNSIGNED_BYTE, codec->pic); |
487 | texuploaded = 1; |
510 | texuploaded = 1; |
488 | }
|
511 | }
|
489 | else
|
512 | else
|
490 | {
|
513 | {
|
491 | bglTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, codec->width,codec->height, |
514 | bglTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, codec->width,codec->height, |
492 | GL_RGBA, GL_UNSIGNED_BYTE, codec->pic); |
515 | fmt, GL_UNSIGNED_BYTE, codec->pic); |
493 | }
|
516 | }
|
494 | 517 | ||
495 | {
|
- | |
496 | float vid_wbyh = ((float)codec->width)/codec->height; |
518 | float vid_wbyh = ((float)codec->width)/codec->height; |
497 | float scr_wbyh = ((float)xdim)/ydim; |
519 | float scr_wbyh = ((float)xdim)/ydim; |
498 | 520 | ||
499 | float x=1.0, y=1.0; |
521 | float x=1.0, y=1.0; |
500 | #if 1
|
522 | #if 1
|
501 | // aspect correction by pillarboxing/letterboxing
|
523 | // aspect correction by pillarboxing/letterboxing
|
502 | // TODO: fullscreen? can't assume square pixels there
|
524 | // TODO: fullscreen? can't assume square pixels there
|
503 | if (vid_wbyh != scr_wbyh) |
525 | if (vid_wbyh != scr_wbyh) |
504 | {
|
526 | {
|
505 | if (vid_wbyh < scr_wbyh) |
527 | if (vid_wbyh < scr_wbyh) |
506 | x = vid_wbyh/scr_wbyh; |
528 | x = vid_wbyh/scr_wbyh; |
507 | else
|
529 | else
|
508 | y = scr_wbyh/vid_wbyh; |
530 | y = scr_wbyh/vid_wbyh; |
509 | }
|
531 | }
|
510 | #endif
|
532 | #endif
|
511 | bglBegin(GL_QUADS); |
533 | bglBegin(GL_QUADS); |
512 | 534 | ||
- | 535 | if (!glinfo.glsl) |
|
- | 536 | bglColor3f(1.0, 1.0, 1.0); |
|
- | 537 | ||
513 | bglTexCoord2f(0.0,1.0); |
538 | bglTexCoord2f(0.0,1.0); |
514 | bglVertex3f(-x, -y, 0.0); |
539 | bglVertex3f(-x, -y, 0.0); |
515 | 540 | ||
516 | bglTexCoord2f(0.0,0.0); |
541 | bglTexCoord2f(0.0,0.0); |
517 | bglVertex3f(-x, y, 0.0); |
542 | bglVertex3f(-x, y, 0.0); |
518 | 543 | ||
519 | bglTexCoord2f(1.0,0.0); |
544 | bglTexCoord2f(1.0,0.0); |
520 | bglVertex3f(x, y, 0.0); |
545 | bglVertex3f(x, y, 0.0); |
521 | 546 | ||
522 | bglTexCoord2f(1.0,1.0); |
547 | bglTexCoord2f(1.0,1.0); |
523 | bglVertex3f(x, -y, 0.0); |
548 | bglVertex3f(x, -y, 0.0); |
524 | 549 | ||
525 | bglEnd(); |
550 | bglEnd(); |
526 | }
|
- | |
527 | 551 | ||
528 | t = getticks()-t; |
552 | t = getticks()-t; |
529 | codec->sumtimes[2] += t; |
553 | codec->sumtimes[2] += t; |
530 | codec->maxtimes[2] = max(codec->maxtimes[2], t); |
554 | codec->maxtimes[2] = max(codec->maxtimes[2], t); |
531 | codec->numframes++; |
555 | codec->numframes++; |
532 | 556 | ||
533 | return 0; |
557 | return 0; |
534 | }
|
558 | }
|
535 | 559 | ||
536 | void animvpx_print_stats(const animvpx_codec_ctx *codec) |
560 | void animvpx_print_stats(const animvpx_codec_ctx *codec) |
537 | {
|
561 | {
|
538 | if (codec->numframes != 0) |
562 | if (codec->numframes != 0) |
539 | {
|
563 | {
|
540 | const int32_t *s = codec->sumtimes; |
564 | const int32_t *s = codec->sumtimes; |
541 | const int32_t *m = codec->maxtimes; |
565 | const int32_t *m = codec->maxtimes; |
542 | int32_t n = codec->numframes; |
566 | int32_t n = codec->numframes; |
543 | 567 | ||
- | 568 | if (glinfo.glsl) |
|
- | 569 | initprintf("animvpx: GLSL mode\n"); |
|
- | 570 | ||
544 | initprintf("VP8 timing stats (mean, max) [ms] for %d frames:\n" |
571 | initprintf("VP8 timing stats (mean, max) [ms] for %d frames:\n" |
545 | " read and decode frame: %.02f, %d\n"
|
572 | " read and decode frame: %.02f, %d\n"
|
546 | " 3 planes -> packed conversion: %.02f, %d\n"
|
573 | " 3 planes -> packed conversion: %.02f, %d\n"
|
547 | " upload and display: %.02f, %d\n", |
574 | " upload and display: %.02f, %d\n", |
548 | n, (double)s[0]/n, m[0], (double)s[1]/n, m[1], (double)s[2]/n, m[2]); |
575 | n, (double)s[0]/n, m[0], (double)s[1]/n, m[1], (double)s[2]/n, m[2]); |
549 | }
|
576 | }
|
550 | }
|
577 | }
|
551 | 578 | ||
552 | #endif
|
579 | #endif
|
553 | 580 |