Subversion Repositories eduke32

Rev

Rev 2830 | Rev 2834 | 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
 
267
        if (vpx_codec_control(&codec->codec, VP8D_GET_FRAME_CORRUPTED, &corrupted))
268
        {
269
            get_codec_error(codec);
270
            codec->decstate = -2;
271
            return 7;
272
        }
273
 
274
        if (corrupted)
275
            OSD_Printf("warning: corrupted frame!\n");
276
    }
277
 
278
    img = vpx_codec_get_frame(&codec->codec, &codec->iter);
279
    if (img == NULL)
280
    {
281
        codec->iter = NULL;  // !
282
        goto read_ivf_frame;
283
    }
284
 
285
    if (img->d_w != codec->width || img->d_h != codec->height)
286
    {
287
        codec->decstate = -1;
288
        return 5;
289
    }
290
 
2832 helixhorne 291
    t[1] = getticks();
292
 
1933 helixhorne 293
    /*** 3 planes --> packed conversion ***/
2241 helixhorne 294
    {
295
        uint8_t *const dstpic = codec->pic;
1933 helixhorne 296
 
2241 helixhorne 297
        const uint8_t *const yplane = img->planes[VPX_PLANE_Y];
298
        const uint8_t *const uplane = img->planes[VPX_PLANE_U];
299
        const uint8_t *const vplane = img->planes[VPX_PLANE_V];
300
 
301
        int32_t ystride = img->stride[VPX_PLANE_Y];
302
        int32_t ustride = img->stride[VPX_PLANE_U];
303
        int32_t vstride = img->stride[VPX_PLANE_V];
304
 
305
        int32_t x, y;
306
        const int32_t width=img->d_w, height = img->d_h;
307
 
2830 helixhorne 308
        for (y=0; y<height; y+=2)
309
        {
310
            for (x=0; x<width; x+=2)
2241 helixhorne 311
            {
2830 helixhorne 312
                uint8_t u = uplane[ustride*(y>>1) + (x>>1)];
313
                uint8_t v = vplane[vstride*(y>>1) + (x>>1)];
314
 
2241 helixhorne 315
                dstpic[(width*y + x)<<2] = yplane[ystride*y + x];
2830 helixhorne 316
                dstpic[(width*y + x+1)<<2] = yplane[ystride*y + x+1];
317
                dstpic[(width*(y+1) + x)<<2] = yplane[ystride*(y+1) + x];
318
                dstpic[(width*(y+1) + x+1)<<2] = yplane[ystride*(y+1) + x+1];
319
 
320
                dstpic[((width*y + x)<<2) + 1] = u;
321
                dstpic[((width*y + x+1)<<2) + 1] = u;
322
                dstpic[((width*(y+1) + x)<<2) + 1] = u;
323
                dstpic[((width*(y+1) + x+1)<<2) + 1] = u;
324
 
325
                dstpic[((width*y + x)<<2) + 2] = v;
326
                dstpic[((width*y + x+1)<<2) + 2] = v;
327
                dstpic[((width*(y+1) + x)<<2) + 2] = v;
328
                dstpic[((width*(y+1) + x+1)<<2) + 2] = v;
2241 helixhorne 329
            }
2830 helixhorne 330
        }
2241 helixhorne 331
    }
332
 
2832 helixhorne 333
    t[2] = getticks();
334
 
335
    codec->sumtimes[0] += t[1]-t[0];
336
    codec->sumtimes[1] += t[2]-t[1];
337
 
338
    codec->maxtimes[0] = max(codec->maxtimes[0], t[1]-t[0]);
339
    codec->maxtimes[1] = max(codec->maxtimes[0], t[2]-t[1]);
340
 
1933 helixhorne 341
    *picptr = codec->pic;
342
    return 0;
343
}
344
 
345
 
346
/////////////// DRAWING! ///////////////
1943 helixhorne 347
static GLuint texname = 0;
1933 helixhorne 348
static int32_t texuploaded;
349
 
350
// YUV->RGB conversion fragment shader adapted from
351
// http://www.fourcc.org/fccyvrgb.php: "Want some sample code?"
352
// direct link: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
353
static char *fragprog_src =
354
    "#version 120\n"
355
 
356
    "uniform sampler2D tex;\n"
357
 
358
    "void main(void) {\n"
359
 
360
    "  float r,g,b,y,u,v;\n"
361
    "  vec3 yuv;\n"
362
 
363
    "  yuv = texture2D(tex, gl_TexCoord[0].st).rgb;\n"
364
    "  y = yuv.r;\n"
365
    "  u = yuv.g;\n"
366
    "  v = yuv.b;\n"
367
 
368
    "  y = 1.1643*(y-0.0625);\n"
369
    "  u = u-0.5;\n"
370
    "  v = v-0.5;\n"
371
 
372
    "  r = y + 1.5958*v;\n"
373
    "  g = y - 0.39173*u - 0.81290*v;\n"
374
    "  b = y + 2.017*u;\n"
375
 
376
    "  gl_FragColor = vec4(r,g,b,1.0);\n"
377
    "}\n";
378
 
379
void animvpx_setup_glstate(void)
380
{
381
    GLint gli;
382
    GLhandleARB FSHandle, PHandle;
383
    static char logbuf[512];
384
 
385
    // first, compile the fragment shader
386
    /* Set up program objects. */
387
    PHandle = bglCreateProgramObjectARB();
388
    FSHandle = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
389
 
390
    /* Compile the shader. */
391
    bglShaderSourceARB(FSHandle, 1, (const GLcharARB **)&fragprog_src, NULL);
392
    bglCompileShaderARB(FSHandle);
393
 
394
    /* Print the compilation log. */
395
    bglGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &gli);
396
    bglGetInfoLogARB(FSHandle, sizeof(logbuf), NULL, logbuf);
397
    if (logbuf[0])
398
        OSD_Printf("animvpx compile log: %s\n", logbuf);
399
 
400
    /* Create a complete program object. */
401
    bglAttachObjectARB(PHandle, FSHandle);
402
    bglLinkProgramARB(PHandle);
403
 
404
    /* And print the link log. */
405
    bglGetInfoLogARB(PHandle, sizeof(logbuf), NULL, logbuf);
406
    if (logbuf[0])
407
        OSD_Printf("animvpx link log: %s\n", logbuf);
408
 
409
    /* Finally, use the program. */
410
    bglUseProgramObjectARB(PHandle);
411
 
412
    ////////// GL STATE //////////
413
 
414
    //Force fullscreen (glox1=-1 forces it to restore afterwards)
415
    bglViewport(0,0,xdim,ydim); glox1 = -1;
416
 
417
    bglMatrixMode(GL_MODELVIEW);
418
    bglLoadIdentity();
419
 
420
    bglMatrixMode(GL_PROJECTION);
421
    bglLoadIdentity();
422
 
423
    bglMatrixMode(GL_COLOR);
424
    bglLoadIdentity();
425
 
426
    bglMatrixMode(GL_TEXTURE);
427
    bglLoadIdentity();
428
 
429
    bglPushAttrib(GL_ENABLE_BIT);
430
    bglDisable(GL_ALPHA_TEST);
2083 helixhorne 431
//    bglDisable(GL_LIGHTING);
1933 helixhorne 432
    bglDisable(GL_DEPTH_TEST);
433
    bglDisable(GL_BLEND);
434
    bglDisable(GL_CULL_FACE);
2083 helixhorne 435
//    bglDisable(GL_SCISSOR_TEST);
1933 helixhorne 436
    bglEnable(GL_TEXTURE_2D);
437
 
438
 
439
    bglActiveTextureARB(GL_TEXTURE0_ARB);
440
    bglGenTextures(1, &texname);
2083 helixhorne 441
//    gli = bglGetUniformLocationARB(PHandle,"tex");
442
//    bglUniform1iARB(gli,0);  // 0: texture unit
1933 helixhorne 443
    bglBindTexture(GL_TEXTURE_2D, texname);
444
 
445
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
446
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
447
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
448
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
449
 
450
    texuploaded = 0;
451
    ////////////////////
452
 
453
    bglClearColor(0.0,0.0,0.0,1.0);
454
    bglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
455
}
456
 
457
void animvpx_restore_glstate(void)
458
{
459
    bglUseProgramObjectARB(0);
460
 
461
    bglPopAttrib();
462
 
463
    bglDeleteTextures(1, &texname);
1943 helixhorne 464
    texname = 0;
1933 helixhorne 465
    texuploaded = 0;
466
}
467
 
2832 helixhorne 468
int32_t animvpx_render_frame(animvpx_codec_ctx *codec)
1933 helixhorne 469
{
2832 helixhorne 470
    int32_t t = getticks();
471
 
1933 helixhorne 472
    if (codec->initstate <= 0)  // not inited or error
473
        return 1;
474
 
475
    if (codec->pic == NULL)
476
        return 2;  // shouldn't happen
477
 
478
    if (!texuploaded)
479
    {
480
        bglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, codec->width,codec->height,
481
                     0, GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
482
        texuploaded = 1;
483
    }
484
    else
485
    {
486
        bglTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, codec->width,codec->height,
487
                        GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
488
    }
489
 
490
    {
491
        float vid_wbyh = ((float)codec->width)/codec->height;
492
        float scr_wbyh = ((float)xdim)/ydim;
493
 
494
        float x=1.0, y=1.0;
495
#if 1
496
        // aspect correction by pillarboxing/letterboxing
497
        // TODO: fullscreen? can't assume square pixels there
498
        if (vid_wbyh != scr_wbyh)
499
        {
500
            if (vid_wbyh < scr_wbyh)
501
                x = vid_wbyh/scr_wbyh;
502
            else
503
                y = scr_wbyh/vid_wbyh;
504
        }
505
#endif
506
        bglBegin(GL_QUADS);
507
 
508
        bglTexCoord2f(0.0,1.0);
509
        bglVertex3f(-x, -y, 0.0);
510
 
511
        bglTexCoord2f(0.0,0.0);
512
        bglVertex3f(-x, y, 0.0);
513
 
514
        bglTexCoord2f(1.0,0.0);
515
        bglVertex3f(x, y, 0.0);
516
 
517
        bglTexCoord2f(1.0,1.0);
518
        bglVertex3f(x, -y, 0.0);
519
 
520
        bglEnd();
521
    }
522
 
2832 helixhorne 523
    t = getticks()-t;
524
    codec->sumtimes[2] += t;
525
    codec->maxtimes[2] = max(codec->maxtimes[2], t);
526
    codec->numframes++;
527
 
1933 helixhorne 528
    return 0;
529
}
2832 helixhorne 530
 
531
void animvpx_print_stats(const animvpx_codec_ctx *codec)
532
{
533
    if (codec->numframes != 0)
534
    {
535
        const int32_t *s = codec->sumtimes;
536
        const int32_t *m = codec->maxtimes;
537
        int32_t n = codec->numframes;
538
 
539
        initprintf("VP8 timing stats (mean, max) [ms] for %d frames:\n"
540
                   " read and decode frame: %.02f, %d\n"
541
                   " 3 planes -> packed conversion: %.02f, %d\n"
542
                   " upload and display: %.02f, %d\n",
543
                   n, (double)s[0]/n, m[0], (double)s[1]/n, m[1], (double)s[2]/n, m[2]);
544
    }
545
}