Subversion Repositories eduke32

Rev

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