Subversion Repositories eduke32

Rev

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