Subversion Repositories eduke32

Rev

Rev 3116 | Rev 3159 | 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;
105
    codec->pic = Bcalloc(info->width*info->height,4);
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
    {
176
        *bufptr = Bmalloc(hdr.framesiz);
177
        if (!*bufptr)
178
            return 2;
179
        *bufallocsizptr = hdr.framesiz;
180
    }
181
    else if (*bufallocsizptr < hdr.framesiz)
182
    {
183
        *bufptr = Brealloc(*bufptr, hdr.framesiz);
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
2158 helixhorne 211
#if !defined __clang__ && !defined USING_LTO
2107 helixhorne 212
# ifdef DEBUGGINGAIDS
2042 helixhorne 213
ATTRIBUTE((optimize("O1")))
2107 helixhorne 214
# else
2042 helixhorne 215
ATTRIBUTE((optimize("O3")))
2107 helixhorne 216
# endif
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
}