Subversion Repositories eduke32

Rev

Rev 3026 | Rev 3116 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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