Subversion Repositories eduke32

Rev

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