Subversion Repositories eduke32

Rev

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