mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-15 08:52:00 +00:00
VP8 video playback as (side-by-side) replacement of ANM moving pictures.
Usage: For an ANM file <somefile>.anm/ANM, EDuke32 looks for <somefile>.ivf, which is the VP8 stream transported by an IVF container. It can be extracted from a WebM file with e.g. mkvextract tracks <filename>.webm 1:<filename>.ivf (part of Mkvtoolnix, the Matroska toolset) Libvpx is required, and the 'YUV'-->RGB conversion is implemented using a fragment shader, so it's for OpenGL modes only. Also, this commit doesn't enable the code. Unfinished: sound, aspect correction for fullscreen w/ non-square pixels, ... ? --- Make MAXNODESPERLINE in engine_priv.h actually a macro that depends on MAXYSAVES and MAXDIM instead of using the obsolete precomputed value. I think this might have been the cause for the latest patched-up overhead view crash. git-svn-id: https://svn.eduke32.com/eduke32@1933 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
24dcfc6633
commit
8b6857b360
6 changed files with 671 additions and 3 deletions
|
@ -107,6 +107,9 @@ EDITOROBJS=$(OBJ)/astub.$o \
|
|||
$(OBJ)/mathutil.$o \
|
||||
$(OBJ)/sounds_mapster32.$o
|
||||
|
||||
ifneq ($(USE_LIBVPX),0)
|
||||
GAMEOBJS+= $(OBJ)/animvpx.$o
|
||||
endif
|
||||
# PLATFORM SPECIFIC SETTINGS
|
||||
|
||||
ifeq ($(PLATFORM),LINUX)
|
||||
|
|
|
@ -24,9 +24,11 @@ BUILD32_ON_64 ?= 0
|
|||
# DO NOT SET THIS TO 1 AND COMMIT IT.
|
||||
NEDMALLOC = 0
|
||||
USE_LIBPNG = 0
|
||||
USE_LIBVPX = 0
|
||||
|
||||
ifeq (0,$(USE_OPENGL))
|
||||
POLYMER = 0
|
||||
USE_LIBVPX = 0
|
||||
endif
|
||||
|
||||
|
||||
|
@ -69,6 +71,9 @@ LIBS=-lm
|
|||
ifneq (0,$(USE_LIBPNG))
|
||||
LIBS+= -lpng
|
||||
endif
|
||||
ifneq (0,$(USE_LIBVPX))
|
||||
LIBS+= -lvpx
|
||||
endif
|
||||
|
||||
ifneq (0,$(RELEASE))
|
||||
# Debugging disabled
|
||||
|
@ -109,6 +114,9 @@ BASECFLAGS=$(debug) -W -Wall -Wimplicit -Werror-implicit-function-declaration \
|
|||
ifneq (0,$(USE_LIBPNG))
|
||||
BASECFLAGS+= -DUSE_LIBPNG
|
||||
endif
|
||||
ifneq (0,$(USE_LIBVPX))
|
||||
BASECFLAGS+= -DUSE_LIBVPX
|
||||
endif
|
||||
|
||||
BASECXXFLAGS= -fno-exceptions -fno-rtti
|
||||
BASEASFLAGS=-s #-g
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#define MAXTILEFILES 256
|
||||
// MAXYSAVES is 983040 (!) right now:
|
||||
#define MAXYSAVES ((MAXXDIM*MAXSPRITES)>>7)
|
||||
#define MAXNODESPERLINE 42 //Warning: This depends on MAXYSAVES & MAXYDIM!
|
||||
#define MAXNODESPERLINE (MAXYSAVES/MAXYDIM) // 307
|
||||
#define MAXCLIPDIST 1024
|
||||
|
||||
// uncomment to clear the screen before each top-level draw
|
||||
|
|
|
@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include "mouse.h"
|
||||
#include "compat.h"
|
||||
|
||||
#ifdef USE_LIBVPX
|
||||
# include "animvpx.h"
|
||||
#endif
|
||||
|
||||
void endanimsounds(int32_t fr)
|
||||
{
|
||||
switch (ud.volume_number)
|
||||
|
@ -219,6 +223,106 @@ void G_PlayAnim(const char *fn,char t)
|
|||
goto ENDOFANIMLOOP;
|
||||
}
|
||||
|
||||
#ifdef USE_LIBVPX
|
||||
while (getrendermode() >= 3 && glinfo.glsl) // if, really
|
||||
{
|
||||
char vpxfn[BMAX_PATH], *dot;
|
||||
animvpx_ivf_header_t info;
|
||||
|
||||
animvpx_codec_ctx codec;
|
||||
uint8_t *pic;
|
||||
uint32_t msecsperframe, nextframetime;
|
||||
int32_t running = 1;
|
||||
|
||||
Bstrncpy(vpxfn, fn, BMAX_PATH);
|
||||
vpxfn[BMAX_PATH-1] = 0;
|
||||
|
||||
dot = Bstrrchr(vpxfn, '.');
|
||||
if (!dot || (dot-vpxfn)+4 >= BMAX_PATH)
|
||||
break;
|
||||
|
||||
dot[1] = 'i';
|
||||
dot[2] = 'v';
|
||||
dot[3] = 'f';
|
||||
dot[4] = 0;
|
||||
|
||||
handle = kopen4loadfrommod(vpxfn, 0);
|
||||
if (handle == -1)
|
||||
break;
|
||||
|
||||
i = animvpx_read_ivf_header(handle, &info);
|
||||
if (i)
|
||||
{
|
||||
OSD_Printf("Failed reading IVF file: %s\n",
|
||||
animvpx_read_ivf_header_errmsg[i]);
|
||||
kclose(handle);
|
||||
break;
|
||||
}
|
||||
|
||||
animvpx_setup_glstate();
|
||||
if (animvpx_init_codec(&info, handle, &codec))
|
||||
{
|
||||
animvpx_restore_glstate();
|
||||
break;
|
||||
}
|
||||
|
||||
msecsperframe = ((uint64_t)info.fpsdenom*1000)/info.fpsnumer;
|
||||
// OSD_Printf("msecs per frame: %d\n", msecsperframe);
|
||||
|
||||
nextframetime = getticks();
|
||||
|
||||
while (running)
|
||||
{
|
||||
nextframetime += msecsperframe;
|
||||
|
||||
i = animvpx_nextpic(&codec, &pic);
|
||||
if (i)
|
||||
{
|
||||
OSD_Printf("Failed getting next pic: %s\n",
|
||||
animvpx_nextpic_errmsg[i]);
|
||||
if (codec.errmsg)
|
||||
{
|
||||
OSD_Printf(" %s\n", codec.errmsg);
|
||||
if (codec.errmsg_detail)
|
||||
OSD_Printf(" detail: %s\n", codec.errmsg_detail);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pic)
|
||||
break; // no more pics!
|
||||
|
||||
animvpx_render_frame(&codec);
|
||||
|
||||
// this and showframe() instead of nextpage() are so that
|
||||
// nobody tramples on our carefully set up GL state!
|
||||
palfadedelta = 0;
|
||||
showframe(0);
|
||||
|
||||
while (getticks() < nextframetime)
|
||||
{
|
||||
handleevents();
|
||||
Net_GetPackets();
|
||||
|
||||
if (KB_KeyWaiting() || (MOUSE_GetButtons()&LEFT_MOUSE))
|
||||
{
|
||||
running = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
kclose(handle);
|
||||
animvpx_restore_glstate();
|
||||
animvpx_uninit_codec(&codec);
|
||||
|
||||
MOUSE_ClearButton(LEFT_MOUSE);
|
||||
return; // done with playing VP8!
|
||||
}
|
||||
#endif
|
||||
// ANM playback --- v v v ---
|
||||
|
||||
handle = kopen4load((char *)fn,0);
|
||||
if (handle == -1) return;
|
||||
length = kfilelength(handle);
|
||||
|
|
470
polymer/eduke32/source/animvpx.c
Normal file
470
polymer/eduke32/source/animvpx.c
Normal file
|
@ -0,0 +1,470 @@
|
|||
/* ANM file replacement with VP8 video */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "baselayer.h"
|
||||
#include "build.h"
|
||||
#include "glbuild.h"
|
||||
|
||||
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
|
||||
#include "duke3d.h"
|
||||
#include "game.h" // kopen4loadfrommod
|
||||
#include "animvpx.h"
|
||||
|
||||
const char *animvpx_read_ivf_header_errmsg[] = {
|
||||
"All OK",
|
||||
"couldn't read 32-byte IVF header",
|
||||
"magic mismatch, not an IVF file",
|
||||
"unrecognized IVF version, expected 0",
|
||||
"only VP8 video stream supported",
|
||||
"invalid framerate numerator or denominator after correction, must not be 0",
|
||||
"INTERNAL ERROR, IVF header size wrong"
|
||||
};
|
||||
|
||||
int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr)
|
||||
{
|
||||
if (sizeof(animvpx_ivf_header_t) != 32)
|
||||
return 6;
|
||||
|
||||
if (kread(inhandle, hdr, sizeof(animvpx_ivf_header_t)) != sizeof(animvpx_ivf_header_t))
|
||||
return 1; // "couldn't read header"
|
||||
|
||||
if (Bmemcmp(hdr,"DKIF",4))
|
||||
return 2; // "not an IVF file"
|
||||
|
||||
hdr->version = B_LITTLE16(hdr->version);
|
||||
if (hdr->version != 0)
|
||||
return 3; // "unrecognized IVF version"
|
||||
|
||||
hdr->hdrlen = B_LITTLE16(hdr->hdrlen);
|
||||
// fourcc is left as-is
|
||||
|
||||
if (Bmemcmp(hdr->fourcc, "VP80", 4))
|
||||
return 4; // "only VP8 supported"
|
||||
|
||||
hdr->width = B_LITTLE16(hdr->width);
|
||||
hdr->height = B_LITTLE16(hdr->height);
|
||||
hdr->fpsnumer = B_LITTLE16(hdr->fpsnumer);
|
||||
hdr->fpsdenom = B_LITTLE16(hdr->fpsdenom);
|
||||
|
||||
hdr->numframes = B_LITTLE32(hdr->numframes);
|
||||
|
||||
// the rest is snatched from vpxdec.c (except 0 check for fps)
|
||||
|
||||
/* Some versions of vpxenc used 1/(2*fps) for the timebase, so
|
||||
* we can guess the framerate using only the timebase in this
|
||||
* case. Other files would require reading ahead to guess the
|
||||
* timebase, like we do for webm.
|
||||
*/
|
||||
if (hdr->fpsnumer < 1000)
|
||||
{
|
||||
/* Correct for the factor of 2 applied to the timebase in the
|
||||
* encoder.
|
||||
*/
|
||||
if (hdr->fpsnumer&1)
|
||||
hdr->fpsdenom <<= 1;
|
||||
else
|
||||
hdr->fpsnumer >>= 1;
|
||||
|
||||
if (hdr->fpsdenom==0 || hdr->fpsnumer==0)
|
||||
return 5; // "invalid framerate numerator or denominator"
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't know FPS for sure, and don't have readahead code
|
||||
* (yet?), so just default to 30fps.
|
||||
*/
|
||||
hdr->fpsnumer = 30;
|
||||
hdr->fpsdenom = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////// CODEC STUFF //////////
|
||||
static void get_codec_error(animvpx_codec_ctx *codec)
|
||||
{
|
||||
codec->errmsg_detail = vpx_codec_error_detail(&codec->codec);
|
||||
codec->errmsg = vpx_codec_error(&codec->codec);
|
||||
}
|
||||
|
||||
// no checks for double-init!
|
||||
int32_t animvpx_init_codec(const animvpx_ivf_header_t *info, int32_t inhandle, animvpx_codec_ctx *codec)
|
||||
{
|
||||
vpx_codec_dec_cfg_t cfg;
|
||||
|
||||
cfg.threads = 1;
|
||||
cfg.w = info->width;
|
||||
cfg.h = info->height;
|
||||
|
||||
codec->width = info->width;
|
||||
codec->height = info->height;
|
||||
|
||||
//
|
||||
codec->inhandle = inhandle;
|
||||
codec->pic = Bcalloc(info->width*info->height,4);
|
||||
|
||||
codec->compbuflen = codec->compbufallocsiz = 0;
|
||||
codec->compbuf = NULL;
|
||||
|
||||
codec->iter = NULL;
|
||||
|
||||
if (codec->pic == NULL)
|
||||
{
|
||||
codec->initstate = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vpx_codec_dec_init(&codec->codec, &vpx_codec_vp8_dx_algo, &cfg, 0))
|
||||
{
|
||||
get_codec_error(codec);
|
||||
codec->initstate = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
codec->initstate = 1;
|
||||
codec->decstate = 0;
|
||||
|
||||
codec->errmsg_detail = codec->errmsg = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t animvpx_uninit_codec(animvpx_codec_ctx *codec)
|
||||
{
|
||||
if (codec->initstate <= 0)
|
||||
return 2;
|
||||
|
||||
Bfree(codec->pic);
|
||||
codec->pic = NULL;
|
||||
|
||||
if (vpx_codec_destroy(&codec->codec))
|
||||
{
|
||||
get_codec_error(codec);
|
||||
codec->initstate = -2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
codec->initstate = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////// FRAME RETRIEVAL //////////
|
||||
|
||||
// read one IVF/VP8 frame, which may code multiple "picture-frames"
|
||||
static int32_t animvpx_read_frame(int32_t inhandle, uint8_t **bufptr, uint32_t *bufsizptr, uint32_t *bufallocsizptr)
|
||||
{
|
||||
#pragma pack(push,1)
|
||||
struct { uint32_t framesiz; uint64_t timestamp; } hdr;
|
||||
#pragma pack(pop)
|
||||
|
||||
if (kread(inhandle, &hdr, sizeof(hdr)) != sizeof(hdr))
|
||||
return 1;
|
||||
|
||||
if (hdr.framesiz == 0)
|
||||
return 6; // must be 6, see animvpx_nextpic_errmsg[]
|
||||
|
||||
// OSD_Printf("frame size: %u\n", hdr.framesiz);
|
||||
|
||||
if (!*bufptr)
|
||||
{
|
||||
*bufptr = Bmalloc(hdr.framesiz);
|
||||
if (!*bufptr)
|
||||
return 2;
|
||||
*bufallocsizptr = hdr.framesiz;
|
||||
}
|
||||
else if (*bufallocsizptr < hdr.framesiz)
|
||||
{
|
||||
*bufptr = Brealloc(*bufptr, hdr.framesiz);
|
||||
if (!*bufptr)
|
||||
return 2;
|
||||
*bufallocsizptr = hdr.framesiz;
|
||||
}
|
||||
|
||||
*bufsizptr = hdr.framesiz;
|
||||
|
||||
if (kread(inhandle, *bufptr, hdr.framesiz) != (signed)hdr.framesiz)
|
||||
return 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *animvpx_nextpic_errmsg[] = {
|
||||
"All OK",
|
||||
"INTERNAL ERROR, animvpx_codec_ctx not initalized!",
|
||||
"OUT OF MEMORY",
|
||||
"couldn't read whole frame",
|
||||
"decoder error, couldn't decode frame",
|
||||
"picture dimension mismatch",
|
||||
"INTERNAL ERROR: read 0 frame length",
|
||||
"Failed getting corruption status (VP8D_GET_FRAME_CORRUPTED)"
|
||||
};
|
||||
|
||||
// retrieves one picture-frame from the stream
|
||||
// pic format: lines of [Y U V 0] pixels
|
||||
// *picptr==NULL means EOF has been reached
|
||||
int32_t animvpx_nextpic(animvpx_codec_ctx *codec, uint8_t **picptr)
|
||||
{
|
||||
int32_t ret, corrupted;
|
||||
uint32_t x, y;
|
||||
vpx_image_t *img;
|
||||
|
||||
if (codec->initstate <= 0) // not inited or error
|
||||
return 1;
|
||||
|
||||
if (codec->decstate == 0) // first time / begin
|
||||
{
|
||||
read_ivf_frame:
|
||||
corrupted = 0;
|
||||
|
||||
ret = animvpx_read_frame(codec->inhandle, &codec->compbuf, &codec->compbuflen,
|
||||
&codec->compbufallocsiz);
|
||||
if (ret == 1)
|
||||
{
|
||||
// reached EOF
|
||||
*picptr = NULL;
|
||||
codec->decstate = 2;
|
||||
return 0;
|
||||
}
|
||||
else if (ret == 2 || ret == 3 || ret == 6)
|
||||
{
|
||||
*picptr = NULL;
|
||||
codec->decstate = -1;
|
||||
return ret;
|
||||
}
|
||||
// ^^^ keep in sync with all animvpx_read_frame() errors!
|
||||
|
||||
// codec->compbuf now contains one IVF/VP8 frame
|
||||
codec->decstate = 1;
|
||||
|
||||
// decode it!
|
||||
if (vpx_codec_decode(&codec->codec, codec->compbuf, codec->compbuflen, NULL, 0))
|
||||
{
|
||||
get_codec_error(codec);
|
||||
codec->decstate = -2;
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (vpx_codec_control(&codec->codec, VP8D_GET_FRAME_CORRUPTED, &corrupted))
|
||||
{
|
||||
get_codec_error(codec);
|
||||
codec->decstate = -2;
|
||||
return 7;
|
||||
}
|
||||
|
||||
if (corrupted)
|
||||
OSD_Printf("warning: corrupted frame!\n");
|
||||
}
|
||||
|
||||
img = vpx_codec_get_frame(&codec->codec, &codec->iter);
|
||||
if (img == NULL)
|
||||
{
|
||||
codec->iter = NULL; // !
|
||||
goto read_ivf_frame;
|
||||
}
|
||||
|
||||
if (img->d_w != codec->width || img->d_h != codec->height)
|
||||
{
|
||||
codec->decstate = -1;
|
||||
return 5;
|
||||
}
|
||||
|
||||
/*** 3 planes --> packed conversion ***/
|
||||
for (y=0; y<img->d_h; y++)
|
||||
for (x=0; x<img->d_w; x++)
|
||||
codec->pic[(img->d_w*y + x)<<2] = img->planes[VPX_PLANE_Y][img->stride[VPX_PLANE_Y]*y + x];
|
||||
|
||||
for (y=0; y<img->d_h; y++)
|
||||
for (x=0; x<img->d_w; x++)
|
||||
codec->pic[((img->d_w*y + x)<<2) + 1] = img->planes[VPX_PLANE_U][img->stride[VPX_PLANE_U]*(y>>1) + (x>>1)];
|
||||
for (y=0; y<img->d_h; y++)
|
||||
for (x=0; x<img->d_w; x++)
|
||||
codec->pic[((img->d_w*y + x)<<2) + 2] = img->planes[VPX_PLANE_V][img->stride[VPX_PLANE_V]*(y>>1) + (x>>1)];
|
||||
|
||||
*picptr = codec->pic;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/////////////// DRAWING! ///////////////
|
||||
static GLuint texname = -1;
|
||||
static int32_t texuploaded;
|
||||
|
||||
// YUV->RGB conversion fragment shader adapted from
|
||||
// http://www.fourcc.org/fccyvrgb.php: "Want some sample code?"
|
||||
// direct link: http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
|
||||
static char *fragprog_src =
|
||||
"#version 120\n"
|
||||
|
||||
"uniform sampler2D tex;\n"
|
||||
|
||||
"void main(void) {\n"
|
||||
|
||||
" float r,g,b,y,u,v;\n"
|
||||
" vec3 yuv;\n"
|
||||
|
||||
" yuv = texture2D(tex, gl_TexCoord[0].st).rgb;\n"
|
||||
" y = yuv.r;\n"
|
||||
" u = yuv.g;\n"
|
||||
" v = yuv.b;\n"
|
||||
|
||||
" y = 1.1643*(y-0.0625);\n"
|
||||
" u = u-0.5;\n"
|
||||
" v = v-0.5;\n"
|
||||
|
||||
" r = y + 1.5958*v;\n"
|
||||
" g = y - 0.39173*u - 0.81290*v;\n"
|
||||
" b = y + 2.017*u;\n"
|
||||
|
||||
" gl_FragColor = vec4(r,g,b,1.0);\n"
|
||||
"}\n";
|
||||
|
||||
void animvpx_setup_glstate(void)
|
||||
{
|
||||
GLint gli;
|
||||
GLhandleARB FSHandle, PHandle;
|
||||
static char logbuf[512];
|
||||
|
||||
// first, compile the fragment shader
|
||||
/* Set up program objects. */
|
||||
PHandle = bglCreateProgramObjectARB();
|
||||
FSHandle = bglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
|
||||
|
||||
/* Compile the shader. */
|
||||
bglShaderSourceARB(FSHandle, 1, (const GLcharARB **)&fragprog_src, NULL);
|
||||
bglCompileShaderARB(FSHandle);
|
||||
|
||||
/* Print the compilation log. */
|
||||
bglGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &gli);
|
||||
bglGetInfoLogARB(FSHandle, sizeof(logbuf), NULL, logbuf);
|
||||
if (logbuf[0])
|
||||
OSD_Printf("animvpx compile log: %s\n", logbuf);
|
||||
|
||||
/* Create a complete program object. */
|
||||
bglAttachObjectARB(PHandle, FSHandle);
|
||||
bglLinkProgramARB(PHandle);
|
||||
|
||||
/* And print the link log. */
|
||||
bglGetInfoLogARB(PHandle, sizeof(logbuf), NULL, logbuf);
|
||||
if (logbuf[0])
|
||||
OSD_Printf("animvpx link log: %s\n", logbuf);
|
||||
|
||||
/* Finally, use the program. */
|
||||
bglUseProgramObjectARB(PHandle);
|
||||
|
||||
////////// GL STATE //////////
|
||||
|
||||
//Force fullscreen (glox1=-1 forces it to restore afterwards)
|
||||
bglViewport(0,0,xdim,ydim); glox1 = -1;
|
||||
|
||||
bglMatrixMode(GL_MODELVIEW);
|
||||
bglLoadIdentity();
|
||||
|
||||
bglMatrixMode(GL_PROJECTION);
|
||||
bglLoadIdentity();
|
||||
|
||||
bglMatrixMode(GL_COLOR);
|
||||
bglLoadIdentity();
|
||||
|
||||
bglMatrixMode(GL_TEXTURE);
|
||||
bglLoadIdentity();
|
||||
|
||||
bglPushAttrib(GL_ENABLE_BIT);
|
||||
bglDisable(GL_ALPHA_TEST);
|
||||
bglDisable(GL_LIGHTING);
|
||||
bglDisable(GL_DEPTH_TEST);
|
||||
bglDisable(GL_BLEND);
|
||||
bglDisable(GL_CULL_FACE);
|
||||
bglDisable(GL_SCISSOR_TEST);
|
||||
bglEnable(GL_TEXTURE_2D);
|
||||
|
||||
|
||||
bglActiveTextureARB(GL_TEXTURE0_ARB);
|
||||
bglGenTextures(1, &texname);
|
||||
gli = bglGetUniformLocationARB(PHandle,"tex");
|
||||
bglUniform1iARB(gli,0); // 0: texture unit
|
||||
bglBindTexture(GL_TEXTURE_2D, texname);
|
||||
|
||||
bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
bglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
texuploaded = 0;
|
||||
////////////////////
|
||||
|
||||
bglClearColor(0.0,0.0,0.0,1.0);
|
||||
bglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void animvpx_restore_glstate(void)
|
||||
{
|
||||
bglUseProgramObjectARB(0);
|
||||
|
||||
bglPopAttrib();
|
||||
|
||||
bglDeleteTextures(1, &texname);
|
||||
texname = -1;
|
||||
texuploaded = 0;
|
||||
}
|
||||
|
||||
int32_t animvpx_render_frame(const animvpx_codec_ctx *codec)
|
||||
{
|
||||
if (codec->initstate <= 0) // not inited or error
|
||||
return 1;
|
||||
|
||||
if (codec->pic == NULL)
|
||||
return 2; // shouldn't happen
|
||||
|
||||
if (!texuploaded)
|
||||
{
|
||||
bglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, codec->width,codec->height,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
|
||||
texuploaded = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bglTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, codec->width,codec->height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, codec->pic);
|
||||
}
|
||||
|
||||
{
|
||||
float vid_wbyh = ((float)codec->width)/codec->height;
|
||||
float scr_wbyh = ((float)xdim)/ydim;
|
||||
|
||||
float x=1.0, y=1.0;
|
||||
#if 1
|
||||
// aspect correction by pillarboxing/letterboxing
|
||||
// TODO: fullscreen? can't assume square pixels there
|
||||
if (vid_wbyh != scr_wbyh)
|
||||
{
|
||||
if (vid_wbyh < scr_wbyh)
|
||||
x = vid_wbyh/scr_wbyh;
|
||||
else
|
||||
y = scr_wbyh/vid_wbyh;
|
||||
}
|
||||
#endif
|
||||
bglBegin(GL_QUADS);
|
||||
|
||||
bglTexCoord2f(0.0,1.0);
|
||||
bglVertex3f(-x, -y, 0.0);
|
||||
|
||||
bglTexCoord2f(0.0,0.0);
|
||||
bglVertex3f(-x, y, 0.0);
|
||||
|
||||
bglTexCoord2f(1.0,0.0);
|
||||
bglVertex3f(x, y, 0.0);
|
||||
|
||||
bglTexCoord2f(1.0,1.0);
|
||||
bglVertex3f(x, -y, 0.0);
|
||||
|
||||
bglEnd();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
83
polymer/eduke32/source/animvpx.h
Normal file
83
polymer/eduke32/source/animvpx.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef USE_OPENGL
|
||||
# error "VP8 support requires OpenGL"
|
||||
#endif
|
||||
|
||||
#ifndef ANIM_VPX_H
|
||||
#define ANIM_VPX_H
|
||||
|
||||
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||
#include <vpx/vpx_decoder.h>
|
||||
//#include <vpx/vp8dx.h>
|
||||
|
||||
// IVF format: http://wiki.multimedia.cx/index.php?title=IVF
|
||||
#pragma pack(push,1)
|
||||
typedef struct
|
||||
{
|
||||
char magic[4];
|
||||
uint16_t version;
|
||||
uint16_t hdrlen;
|
||||
|
||||
char fourcc[4];
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
uint32_t fpsnumer;
|
||||
uint32_t fpsdenom;
|
||||
|
||||
uint32_t numframes;
|
||||
uint32_t unused_;
|
||||
} animvpx_ivf_header_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
const char *animvpx_read_ivf_header_errmsg[7];
|
||||
int32_t animvpx_read_ivf_header(int32_t inhandle, animvpx_ivf_header_t *hdr);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *errmsg; // non-NULL if codec error? better always check...
|
||||
const char *errmsg_detail; // may be NULL even if codec error
|
||||
|
||||
uint16_t width, height;
|
||||
uint8_t *pic; // lines of [Y U V 0], calloc'ed on init
|
||||
|
||||
// VVV everything that follows should be considered private! VVV
|
||||
|
||||
int32_t inhandle; // the kread() file handle
|
||||
|
||||
// state of this struct:
|
||||
// 0: uninited (either not yet or already)
|
||||
// 1: codec init OK
|
||||
// -1: error while initing
|
||||
// -2: error while uniniting
|
||||
int32_t initstate;
|
||||
|
||||
// decoder state:
|
||||
// 0: first time / begin
|
||||
// 1: have more frames
|
||||
// 2: reached EOF
|
||||
// -1: unspecified error
|
||||
// -2: decoder error
|
||||
int32_t decstate;
|
||||
|
||||
uint32_t compbuflen;
|
||||
uint32_t compbufallocsiz;
|
||||
uint8_t *compbuf; // compressed data buffer (one IVF/VP8 frame)
|
||||
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_iter_t iter;
|
||||
} animvpx_codec_ctx;
|
||||
|
||||
|
||||
int32_t animvpx_init_codec(const animvpx_ivf_header_t *info, int32_t inhandle, animvpx_codec_ctx *codec);
|
||||
int32_t animvpx_uninit_codec(animvpx_codec_ctx *codec);
|
||||
|
||||
const char *animvpx_nextpic_errmsg[8];
|
||||
int32_t animvpx_nextpic(animvpx_codec_ctx *codec, uint8_t **pic);
|
||||
|
||||
void animvpx_setup_glstate(void);
|
||||
void animvpx_restore_glstate(void);
|
||||
int32_t animvpx_render_frame(const animvpx_codec_ctx *codec);
|
||||
|
||||
|
||||
#endif // !defined ANIM_VPX_H
|
Loading…
Reference in a new issue