Subversion Repositories eduke32

Rev

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