Subversion Repositories eduke32

Rev

Rev 2834 | Rev 3110 | 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
 
3
#include <stdint.h>
4
 
5
#include "compat.h"
6
#include "baselayer.h"
7
#include "build.h"
8
#include "glbuild.h"
9
 
10
#define VPX_CODEC_DISABLE_COMPAT 1
11
#include <vpx/vpx_decoder.h>
12
#include <vpx/vp8dx.h>
13
 
14
#include "duke3d.h"
15
#include "game.h"  // kopen4loadfrommod
16
#include "animvpx.h"
17
 
18
const char *animvpx_read_ivf_header_errmsg[] = {
19
    "All OK",
20
    "couldn't read 32-byte IVF header",
21
    "magic mismatch, not an IVF file",
22
    "unrecognized IVF version, expected 0",
23
    "only VP8 video stream supported",
24
    "invalid framerate numerator or denominator after correction, must not be 0",
25
    "INTERNAL ERROR, IVF header size wrong"
26
};
27
 
28
int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr)
29
{
30
    if (sizeof(animvpx_ivf_header_t) != 32)
31
        return 6;
32
 
33
    if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t))
34
        return 1;  // "couldn't read header"
35
 
36
    if (Bmemcmp(hdr,"DKIF",4))
37
        return 2;  // "not an IVF file"
38
 
39
    hdr->version = B_LITTLE16(hdr->version);
40
    if (hdr->version != 0)
41
        return 3;  // "unrecognized IVF version"
42
 
43
    hdr->hdrlen = B_LITTLE16(hdr->hdrlen);
44
    // fourcc is left as-is
45
 
46
    if (Bmemcmp(hdr->fourcc, "VP80", 4))
47
        return 4;  // "only VP8 supported"
48
 
49
    hdr->width = B_LITTLE16(hdr->width);
50
    hdr->height = B_LITTLE16(hdr->height);
51
    hdr->fpsnumer = B_LITTLE16(hdr->fpsnumer);
52
    hdr->fpsdenom = B_LITTLE16(hdr->fpsdenom);
53
 
54
    hdr->numframes = B_LITTLE32(hdr->numframes);
55
 
56
    // the rest is snatched from vpxdec.c (except 0 check for fps)
57
 
58
    /* Some versions of vpxenc used 1/(2*fps) for the timebase, so
59
     * we can guess the framerate using only the timebase in this
60
     * case. Other files would require reading ahead to guess the
61
     * timebase, like we do for webm.
62
     */
63
    if (hdr->fpsnumer < 1000)
64
    {
65
        /* Correct for the factor of 2 applied to the timebase in the
66
         * encoder.
67
         */
68
        if (hdr->fpsnumer&1)
69
            hdr->fpsdenom <<= 1;
70
        else
71
            hdr->fpsnumer >>= 1;
72
 
73
        if (hdr->fpsdenom==0 || hdr->fpsnumer==0)
74
            return 5;  // "invalid framerate numerator or denominator"
75
    }
76
    else
77
    {
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;
109
    codec->pic = Bcalloc(info->width*info->height,4);
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
    {
180
        *bufptr = Bmalloc(hdr.framesiz);
181
        if (!*bufptr)
182
            return 2;
183
        *bufallocsizptr = hdr.framesiz;
184
    }
185
    else if (*bufallocsizptr < hdr.framesiz)
186
    {
187
        *bufptr = Brealloc(*bufptr, hdr.framesiz);
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
2158 helixhorne 215
#if !defined __clang__ && !defined USING_LTO
2107 helixhorne 216
# ifdef DEBUGGINGAIDS
2042 helixhorne 217
ATTRIBUTE((optimize("O1")))
2107 helixhorne 218
# else
2042 helixhorne 219
ATTRIBUTE((optimize("O3")))
2107 helixhorne 220
# endif
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
}