Subversion Repositories eduke32

Rev

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