Subversion Repositories eduke32

Rev

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