Subversion Repositories eduke32

Rev

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