Subversion Repositories eduke32

Rev

Rev 2158 | Rev 2830 | 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
 
299
        for (y=0; y<height; y++)
300
            for (x=0; x<width; x++)
301
            {
302
                dstpic[(width*y + x)<<2] = yplane[ystride*y + x];
303
                dstpic[((width*y + x)<<2) + 1] = uplane[ustride*(y>>1) + (x>>1)];
304
                dstpic[((width*y + x)<<2) + 2] = vplane[vstride*(y>>1) + (x>>1)];
305
            }
306
 
307
//        initprintf("%d ms\n", getticks()-t);
308
    }
309
 
1933 helixhorne 310
    *picptr = codec->pic;
311
    return 0;
312
}
313
 
314
 
315
/////////////// DRAWING! ///////////////
1943 helixhorne 316
static GLuint texname = 0;
1933 helixhorne 317
static int32_t texuploaded;
318
 
319
// YUV->RGB conversion fragment shader adapted from
320
// http://www.fourcc.org/fccyvrgb.php: "Want some sample code?"
321
// direct link: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
322
static char *fragprog_src =
323
    "#version 120\n"
324
 
325
    "uniform sampler2D tex;\n"
326
 
327
    "void main(void) {\n"
328
 
329
    "  float r,g,b,y,u,v;\n"
330
    "  vec3 yuv;\n"
331
 
332
    "  yuv = texture2D(tex, gl_TexCoord[0].st).rgb;\n"
333
    "  y = yuv.r;\n"
334
    "  u = yuv.g;\n"
335
    "  v = yuv.b;\n"
336
 
337
    "  y = 1.1643*(y-0.0625);\n"
338
    "  u = u-0.5;\n"
339
    "  v = v-0.5;\n"
340
 
341
    "  r = y + 1.5958*v;\n"
342
    "  g = y - 0.39173*u - 0.81290*v;\n"
343
    "  b = y + 2.017*u;\n"
344
 
345
    "  gl_FragColor = vec4(r,g,b,1.0);\n"
346
    "}\n";
347
 
348
void animvpx_setup_glstate(void)
349
{
350
    GLint gli;
351
    GLhandleARB FSHandle, PHandle;
352
    static char logbuf[512];
353
 
354
    // first, compile the fragment shader
355
    /* Set up program objects. */
356
    PHandle = bglCreateProgramObjectARB();
357
    FSHandle = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
358
 
359
    /* Compile the shader. */
360
    bglShaderSourceARB(FSHandle, 1, (const GLcharARB **)&fragprog_src, NULL);
361
    bglCompileShaderARB(FSHandle);
362
 
363
    /* Print the compilation log. */
364
    bglGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &gli);
365
    bglGetInfoLogARB(FSHandle, sizeof(logbuf), NULL, logbuf);
366
    if (logbuf[0])
367
        OSD_Printf("animvpx compile log: %s\n", logbuf);
368
 
369
    /* Create a complete program object. */
370
    bglAttachObjectARB(PHandle, FSHandle);
371
    bglLinkProgramARB(PHandle);
372
 
373
    /* And print the link log. */
374
    bglGetInfoLogARB(PHandle, sizeof(logbuf), NULL, logbuf);
375
    if (logbuf[0])
376
        OSD_Printf("animvpx link log: %s\n", logbuf);
377
 
378
    /* Finally, use the program. */
379
    bglUseProgramObjectARB(PHandle);
380
 
381
    ////////// GL STATE //////////
382
 
383
    //Force fullscreen (glox1=-1 forces it to restore afterwards)
384
    bglViewport(0,0,xdim,ydim); glox1 = -1;
385
 
386
    bglMatrixMode(GL_MODELVIEW);
387
    bglLoadIdentity();
388
 
389
    bglMatrixMode(GL_PROJECTION);
390
    bglLoadIdentity();
391
 
392
    bglMatrixMode(GL_COLOR);
393
    bglLoadIdentity();
394
 
395
    bglMatrixMode(GL_TEXTURE);
396
    bglLoadIdentity();
397
 
398
    bglPushAttrib(GL_ENABLE_BIT);
399
    bglDisable(GL_ALPHA_TEST);
2083 helixhorne 400
//    bglDisable(GL_LIGHTING);
1933 helixhorne 401
    bglDisable(GL_DEPTH_TEST);
402
    bglDisable(GL_BLEND);
403
    bglDisable(GL_CULL_FACE);
2083 helixhorne 404
//    bglDisable(GL_SCISSOR_TEST);
1933 helixhorne 405
    bglEnable(GL_TEXTURE_2D);
406
 
407
 
408
    bglActiveTextureARB(GL_TEXTURE0_ARB);
409
    bglGenTextures(1, &texname);
2083 helixhorne 410
//    gli = bglGetUniformLocationARB(PHandle,"tex");
411
//    bglUniform1iARB(gli,0);  // 0: texture unit
1933 helixhorne 412
    bglBindTexture(GL_TEXTURE_2D, texname);
413
 
414
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
415
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
416
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
417
    bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
418
 
419
    texuploaded = 0;
420
    ////////////////////
421
 
422
    bglClearColor(0.0,0.0,0.0,1.0);
423
    bglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
424
}
425
 
426
void animvpx_restore_glstate(void)
427
{
428
    bglUseProgramObjectARB(0);
429
 
430
    bglPopAttrib();
431
 
432
    bglDeleteTextures(1, &texname);
1943 helixhorne 433
    texname = 0;
1933 helixhorne 434
    texuploaded = 0;
435
}
436
 
437
int32_t animvpx_render_frame(const animvpx_codec_ctx *codec)
438
{
439
    if (codec->initstate <= 0)  // not inited or error
440
        return 1;
441
 
442
    if (codec->pic == NULL)
443
        return 2;  // shouldn't happen
444
 
445
    if (!texuploaded)
446
    {
447
        bglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, codec->width,codec->height,
448
                     0, GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
449
        texuploaded = 1;
450
    }
451
    else
452
    {
453
        bglTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, codec->width,codec->height,
454
                        GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
455
    }
456
 
457
    {
458
        float vid_wbyh = ((float)codec->width)/codec->height;
459
        float scr_wbyh = ((float)xdim)/ydim;
460
 
461
        float x=1.0, y=1.0;
462
#if 1
463
        // aspect correction by pillarboxing/letterboxing
464
        // TODO: fullscreen? can't assume square pixels there
465
        if (vid_wbyh != scr_wbyh)
466
        {
467
            if (vid_wbyh < scr_wbyh)
468
                x = vid_wbyh/scr_wbyh;
469
            else
470
                y = scr_wbyh/vid_wbyh;
471
        }
472
#endif
473
        bglBegin(GL_QUADS);
474
 
475
        bglTexCoord2f(0.0,1.0);
476
        bglVertex3f(-x, -y, 0.0);
477
 
478
        bglTexCoord2f(0.0,0.0);
479
        bglVertex3f(-x, y, 0.0);
480
 
481
        bglTexCoord2f(1.0,0.0);
482
        bglVertex3f(x, y, 0.0);
483
 
484
        bglTexCoord2f(1.0,1.0);
485
        bglVertex3f(x, -y, 0.0);
486
 
487
        bglEnd();
488
    }
489
 
490
    return 0;
491
}