Commit e42e4168 authored by vortex's avatar vortex
6 Browse files

Introducing LibAvW - a wrapper DLL to play videos using libav. Basic support...

Introducing LibAvW - a wrapper DLL to play videos using libav. Basic support includes extraction of video frames (sound files should be placed as separate .ogg's) and scaling. No ABI requirements (wrapper takes all dirty code). LibAvW can be found at: svn://svn.icculus.org/razorwind/trunk/dplibavw .

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11766 d7cf8633-e32d-0410-b094-e92efae38249
parent cf5fd377
......@@ -2,13 +2,6 @@
#include "quakedef.h"
#include "cl_dyntexture.h"
#include "cl_video.h"
#include "dpvsimpledecode.h"
// VorteX: JAM video module used by Blood Omnicide
#define USEJAM
#ifdef USEJAM
#include "cl_video_jamdecode.c"
#endif
// cvars
cvar_t cl_video_subtitles = {CVAR_SAVE, "cl_video_subtitles", "0", "show subtitles for videos (if they are present)"};
......@@ -22,6 +15,19 @@ cvar_t cl_video_keepaspectratio = {CVAR_SAVE, "cl_video_keepaspectratio", "0", "
cvar_t cl_video_fadein = {CVAR_SAVE, "cl_video_fadein", "0", "fading-from-black effect once video is started, in seconds"};
cvar_t cl_video_fadeout = {CVAR_SAVE, "cl_video_fadeout", "0", "fading-to-black effect once video is ended, in seconds"};
cvar_t v_glslgamma_video = {CVAR_SAVE, "v_glslgamma_video", "1", "applies GLSL gamma to played video, could be a fraction, requires r_glslgamma_2d 1."};
// DPV stream decoder
#include "dpvsimpledecode.h"
// VorteX: libavcodec implementation
#include "cl_video_libavw.c"
// JAM video decoder used by Blood Omnicide
#ifdef JAMVIDEO
#include "cl_video_jamdecode.c"
#endif
// constants (and semi-constants)
static int cl_videormask;
static int cl_videobmask;
......@@ -44,18 +50,23 @@ static clvideo_t *FindUnusedVid( void )
static qboolean OpenStream( clvideo_t * video )
{
const char *errorstring;
video->stream = dpvsimpledecode_open( video, video->filename, &errorstring);
if (!video->stream )
{
#ifdef USEJAM
video->stream = jam_open( video, video->filename, &errorstring);
if (video->stream)
return true;
if (video->stream)
return true;
#ifdef JAMVIDEO
video->stream = jam_open( video, video->filename, &errorstring);
if (video->stream)
return true;
#endif
Con_Printf("unable to open \"%s\", error: %s\n", video->filename, errorstring);
return false;
}
return true;
video->stream = LibAvW_OpenVideo( video, video->filename, &errorstring);
if (video->stream)
return true;
Con_Printf("unable to open \"%s\", error: %s\n", video->filename, errorstring);
return false;
}
static void VideoUpdateCallback(rtexture_t *rt, void *data)
......@@ -89,7 +100,11 @@ static void SuspendVideo( clvideo_t * video )
UnlinkVideoTexture(video);
// if we are in firstframe mode, also close the stream
if (video->state == CLVIDEO_FIRSTFRAME)
video->close(video->stream);
{
if (video->stream)
video->close(video->stream);
video->stream = NULL;
}
}
static qboolean WakeVideo( clvideo_t * video )
......@@ -129,7 +144,7 @@ static void LoadSubtitles( clvideo_t *video, const char *subtitlesfile )
subtitle_text = NULL;
if (langcvar)
{
dpsnprintf(overridename, sizeof(overridename), "script/locale/%s/%s", langcvar->string, subtitlesfile);
dpsnprintf(overridename, sizeof(overridename), "locale/%s/%s", langcvar->string, subtitlesfile);
subtitle_text = (char *)FS_LoadFile(overridename, cls.permanentmempool, false, NULL);
}
if (!subtitle_text)
......@@ -303,7 +318,9 @@ void CL_RestartVideo(clvideo_t *video)
video->framenum = -1;
// reopen stream
video->close(video->stream);
if (video->stream)
video->close(video->stream);
video->stream = NULL;
if (!OpenStream(video))
video->state = CLVIDEO_UNUSED;
}
......@@ -318,7 +335,11 @@ void CL_CloseVideo(clvideo_t * video)
// close stream
if (!video->suspended || video->state != CLVIDEO_FIRSTFRAME)
video->close(video->stream);
{
if (video->stream)
video->close(video->stream);
video->stream = NULL;
}
// unlink texture
if (!video->suspended)
UnlinkVideoTexture(video);
......@@ -389,13 +410,6 @@ void CL_Video_Frame(void)
cl_num_videos--;
}
void CL_Video_Shutdown( void )
{
int i;
for (i = 0 ; i < cl_num_videos ; i++)
CL_CloseVideo(&cl_videos[ i ]);
}
void CL_PurgeOwner( int owner )
{
int i;
......@@ -474,7 +488,7 @@ void CL_DrawVideo(void)
st[6] = 1.0; st[7] = 1.0;
if (cl_video_keepaspectratio.integer)
{
float a = ((float)video->cpif.width / (float)video->cpif.height) / ((float)vid.width / (float)vid.height);
float a = video->getaspectratio(video->stream) / ((float)vid.width / (float)vid.height);
if (cl_video_keepaspectratio.integer >= 2)
{
// clip instead of scale
......@@ -542,7 +556,14 @@ void CL_DrawVideo(void)
#endif
// draw video
DrawQ_SuperPic(px, py, &video->cpif, sx, sy, st[0], st[1], b, b, b, 1, st[2], st[3], b, b, b, 1, st[4], st[5], b, b, b, 1, st[6], st[7], b, b, b, 1, 0);
if (v_glslgamma_video.value >= 1)
DrawQ_SuperPic(px, py, &video->cpif, sx, sy, st[0], st[1], b, b, b, 1, st[2], st[3], b, b, b, 1, st[4], st[5], b, b, b, 1, st[6], st[7], b, b, b, 1, 0);
else
{
DrawQ_SuperPic(px, py, &video->cpif, sx, sy, st[0], st[1], b, b, b, 1, st[2], st[3], b, b, b, 1, st[4], st[5], b, b, b, 1, st[6], st[7], b, b, b, 1, DRAWFLAG_NOGAMMA);
if (v_glslgamma_video.value > 0.0)
DrawQ_SuperPic(px, py, &video->cpif, sx, sy, st[0], st[1], b, b, b, v_glslgamma_video.value, st[2], st[3], b, b, b, v_glslgamma_video.value, st[4], st[5], b, b, b, v_glslgamma_video.value, st[6], st[7], b, b, b, v_glslgamma_video.value, 0);
}
#ifndef USE_GLES2
// disable video-only stipple
......@@ -704,5 +725,19 @@ void CL_Video_Init( void )
Cvar_RegisterVariable(&cl_video_fadein);
Cvar_RegisterVariable(&cl_video_fadeout);
Cvar_RegisterVariable(&v_glslgamma_video);
R_RegisterModule( "CL_Video", cl_video_start, cl_video_shutdown, cl_video_newmap, NULL, NULL );
}
\ No newline at end of file
LibAvW_OpenLibrary();
}
void CL_Video_Shutdown( void )
{
int i;
for (i = 0 ; i < cl_num_videos ; i++)
CL_CloseVideo(&cl_videos[ i ]);
LibAvW_CloseLibrary();
}
......@@ -59,6 +59,7 @@ typedef struct clvideo_s
unsigned int (*getwidth) (void *stream);
unsigned int (*getheight) (void *stream);
double (*getframerate) (void *stream);
double (*getaspectratio) (void *stream);
int (*decodeframe) (void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
// if a video is suspended, it is automatically paused (else we'd still have to process the frames)
......
// JAM format decoder, used by Blood Omnicide
#ifdef LIBAVCODEC
//#define JAM_USELIBAVCODECSCALE
#endif
typedef struct jamdecodestream_s
{
int error;
qfile_t *file;
double info_framerate;
unsigned int info_frames;
unsigned int info_imagewidth;
unsigned int info_imageheight;
double info_aspectratio;
float colorscale;
unsigned char colorsub;
qfile_t *file;
double info_framerate;
unsigned int info_frames;
unsigned int info_imagewidth;
unsigned int info_imageheight;
int doubleres;
float colorscale;
unsigned char colorsub;
float stipple;
// info used during decoding
unsigned char *frame;
unsigned char *frame_prev;
unsigned char frame_palette[768];
unsigned char *frame_compressed;
unsigned int framesize;
unsigned int framenum;
// info used durign decoding
unsigned char *videopixels;
unsigned char *compressed;
unsigned char *framedata;
unsigned char *prevframedata;
unsigned char colormap[768];
unsigned int framesize;
unsigned int framenum;
// libavcodec scaling
#ifdef JAM_USELIBAVCODECSCALE
unsigned char *frame_output_buffer;
AVFrame *frame_output;
AVFrame *frame_output_scale;
unsigned int framewidth;
unsigned int frameheight;
#endif
// channel the sound file is being played on
int sndchan;
}
jamdecodestream_t;
#define JAMDECODEERROR_NONE 0
#define JAMDECODEERROR_EOF 1
#define JAMDECODEERROR_READERROR 2
#define JAMDECODEERROR_BAD_FRAME_HEADER 3
#define JAMDECODEERROR_BAD_OUTPUT_SIZE 4
#define JAMDECODEERROR_BAD_COLORMAP 5
// opens a stream
void jam_close(void *stream);
unsigned int jam_getwidth(void *stream);
unsigned int jam_getheight(void *stream);
double jam_getframerate(void *stream);
double jam_getaspectratio(void *stream);
int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
static void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
{
unsigned char jamHead[16];
char *wavename;
char jamHead[16];
jamdecodestream_t *s;
qfile_t *file;
char *wavename;
// allocate stream structure
s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));
if (s != NULL)
memset(s, 0, sizeof(jamdecodestream_t));
s->sndchan = -1;
if (s == NULL)
{
if ((file = FS_OpenVirtualFile(filename, false)))
{
s->file = file;
if (FS_Read(s->file, &jamHead, 16))
{
if (!memcmp(jamHead, "JAM", 3))
{
s->info_imagewidth = LittleLong(*(jamHead + 4));
s->info_imageheight = LittleLong(*(jamHead + 8));
s->info_frames = LittleLong(*(jamHead + 12));
s->info_framerate = 15;
s->doubleres = 0;
s->colorscale = 0.70;
s->colorsub = 8;
s->stipple = 0.4;
s->framesize = s->info_imagewidth * s->info_imageheight;
if (s->framesize > 0)
{
s->compressed = (unsigned char *)Z_Malloc(s->framesize);
s->framedata = (unsigned char *)Z_Malloc(s->framesize * 2);
s->prevframedata = (unsigned char *)Z_Malloc(s->framesize * 2);
s->videopixels = (unsigned char *)Z_Malloc(s->framesize * 4); // bgra, doubleres
if (s->compressed != NULL && s->framedata != NULL && s->prevframedata != NULL && s->videopixels != NULL)
{
size_t namelen;
*errorstring = "unable to allocate memory for stream info structure";
return NULL;
}
namelen = strlen(filename) + 10;
wavename = (char *)Z_Malloc(namelen);
if (wavename)
{
sfx_t* sfx;
// open file
s->file = FS_OpenVirtualFile(filename, true);
if (!s->file)
{
*errorstring = "unable to open videofile";
jam_close(s);
return NULL;
}
FS_StripExtension(filename, wavename, namelen);
strlcat(wavename, ".wav", namelen);
sfx = S_PrecacheSound(wavename, false, false);
if (sfx != NULL)
s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
else
s->sndchan = -1;
Z_Free(wavename);
}
// all is well...
// set the module functions
s->framenum = 0;
video->close = jam_close;
video->getwidth = jam_getwidth;
video->getheight = jam_getheight;
video->getframerate = jam_getframerate;
video->decodeframe = jam_video;
return s;
}
else if (errorstring != NULL)
*errorstring = "unable to allocate memory for stream info structure";
if (s->compressed != NULL)
Z_Free(s->compressed);
if (s->framedata != NULL)
Z_Free(s->framedata);
if (s->prevframedata != NULL)
Z_Free(s->prevframedata);
if (s->videopixels != NULL)
Z_Free(s->videopixels);
}
else if (errorstring != NULL)
*errorstring = "bad framesize";
}
else if (errorstring != NULL)
*errorstring = "not JAM videofile";
}
else if (errorstring != NULL)
*errorstring = "unexpected EOF";
FS_Close(file);
}
else if (errorstring != NULL)
*errorstring = "unable to open videofile";
// read header
if (!FS_Read(s->file, jamHead, 16))
{
*errorstring = "JamDecoder: unexpected EOF reading header";
jam_close(s);
return NULL;
}
if (memcmp(jamHead, "JAM", 4))
{
*errorstring = "JamDecoder: not a JAM file";
jam_close(s);
return NULL;
}
s->info_imagewidth = LittleLong(*(jamHead + 4));
s->info_imageheight = LittleLong(*(jamHead + 8));
s->info_frames = LittleLong(*(jamHead + 12)) - 1;
s->info_framerate = 15;
s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
s->colorscale = 0.90;
s->colorsub = 4;
s->framesize = s->info_imagewidth * s->info_imageheight;
// allocate frame input/output
if (s->framesize < 0)
{
*errorstring = "JamDecoder: bad framesize";
jam_close(s);
return NULL;
}
s->frame = (unsigned char *)Z_Malloc(s->framesize * 2);
s->frame_prev = (unsigned char *)Z_Malloc(s->framesize * 2);
s->frame_compressed = (unsigned char *)Z_Malloc(s->framesize);
if (s->frame_compressed == NULL || s->frame == NULL || s->frame_prev == NULL)
{
*errorstring = "JamDecoder: unable to allocate memory for video decoder";
jam_close(s);
return NULL;
}
// scale support provided by libavcodec
#ifdef JAM_USELIBAVCODECSCALE
s->framewidth = s->info_imagewidth;
s->frameheight = s->info_imageheight;
// min size
if (cl_video_libavcodec_minwidth.integer > 0)
s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavcodec_minwidth.integer);
if (cl_video_libavcodec_minheight.integer > 0)
s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavcodec_minheight.integer);
// allocate output
s->frame_output_buffer = (unsigned char *)Z_Malloc(s->framesize * 4);
s->frame_output = AvCodec_AllocFrame();
s->frame_output_scale = AvCodec_AllocFrame();
if (!s->frame_output_buffer || !s->frame_output || !s->frame_output_scale)
{
*errorstring = "JamDecoder: failed to allocate LibAvcodec frame";
jam_close(s);
Z_Free(s);
return NULL;
}
else if (errorstring != NULL)
*errorstring = "unable to allocate memory for stream info structure";
return NULL;
#endif
// everything is ok
// set the module functions
s->framenum = 0;
video->close = jam_close;
video->getwidth = jam_getwidth;
video->getheight = jam_getheight;
video->getframerate = jam_getframerate;
video->decodeframe = jam_video;
video->getaspectratio = jam_getaspectratio;
// set sound
size_t namelen;
namelen = strlen(filename) + 10;
wavename = (char *)Z_Malloc(namelen);
if (wavename)
{
sfx_t* sfx;
FS_StripExtension(filename, wavename, namelen);
strlcat(wavename, ".wav", namelen);
sfx = S_PrecacheSound(wavename, false, false);
if (sfx != NULL)
s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
else
s->sndchan = -1;
Z_Free(wavename);
}
return s;
}
// closes a stream
......@@ -138,14 +169,32 @@ void jam_close(void *stream)
jamdecodestream_t *s = (jamdecodestream_t *)stream;
if (s == NULL)
return;
Z_Free(s->compressed);
Z_Free(s->framedata);
Z_Free(s->prevframedata);
Z_Free(s->videopixels);
if (s->frame_compressed)
Z_Free(s->frame_compressed);
s->frame_compressed = NULL;
if (s->frame)
Z_Free(s->frame);
s->frame = NULL;
if (s->frame_prev)
Z_Free(s->frame_prev);
s->frame_prev = NULL;
if (s->sndchan != -1)
S_StopChannel(s->sndchan, true, true);
s->sndchan = -1;
if (s->file)
FS_Close(s->file);
s->file = NULL;
#ifdef JAM_USELIBAVCODECSCALE
if (s->frame_output_buffer)
Z_Free(s->frame_output_buffer);
s->frame_output_buffer = NULL;
if (s->frame_output)
AvUtil_Free(s->frame_output);
s->frame_output = NULL;
if (s->frame_output_scale)
AvUtil_Free(s->frame_output_scale);
s->frame_output_scale = NULL;
#endif
Z_Free(s);
}
......@@ -153,8 +202,6 @@ void jam_close(void *stream)
unsigned int jam_getwidth(void *stream)
{
jamdecodestream_t *s = (jamdecodestream_t *)stream;
if (s->doubleres)
return s->info_imagewidth * 2;
return s->info_imagewidth;
}
......@@ -162,8 +209,6 @@ unsigned int jam_getwidth(void *stream)
unsigned int jam_getheight(void *stream)
{
jamdecodestream_t *s = (jamdecodestream_t *)stream;
if (s->doubleres)
return s->info_imageheight * 2;
return s->info_imageheight;
}
......@@ -174,6 +219,12 @@ double jam_getframerate(void *stream)
return s->info_framerate;
}
// returns aspect ration of the stream
double jam_getaspectratio(void *stream)
{
jamdecodestream_t *s = (jamdecodestream_t *)stream;
return s->info_aspectratio;
}
// decode JAM frame
static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)
......@@ -239,117 +290,89 @@ static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigne
int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
{
unsigned char frameHead[16], *b;
unsigned int compsize, outsize, i, j;
unsigned int compsize, outsize, i;
jamdecodestream_t *s = (jamdecodestream_t *)stream;
s->error = DPVSIMPLEDECODEERROR_NONE;
if (s->framenum < s->info_frames)
{
// EOF
if (s->framenum >= s->info_frames)
return 1;
s->framenum++;
readframe:
if (FS_Read(s->file, &frameHead, 16))
{
compsize = LittleLong(*(frameHead + 8)) - 16;
outsize = LittleLong(*(frameHead + 12));
if (compsize > s->framesize || outsize > s->framesize)
s->error = JAMDECODEERROR_BAD_FRAME_HEADER;
else if (FS_Read(s->file, s->compressed, compsize))
{
// palette goes interleaved with special flag
if (frameHead[0] == 2)
{
if (compsize == 768)
{
memcpy(s->colormap, s->compressed, 768);
for(i = 0; i < 768; i++)
s->colormap[i] = (unsigned char)(bound(0, (s->colormap[i] * s->colorscale) - s->colorsub, 255));
goto readframe;
}
//else
// s->error = JAMDECODEERROR_BAD_COLORMAP;
}
else
{
// decode frame
// shift buffers to provide current and previous one, decode
b = s->prevframedata;
s->prevframedata = s->framedata;
s->framedata = b;
jam_decodeframe(s->compressed, s->framedata, s->prevframedata, outsize, frameHead[4]);
// make 32bit imagepixels from 8bit palettized frame
if (s->doubleres)
b = s->videopixels;
else
b = (unsigned char *)imagedata;
for(i = 0; i < s->framesize; i++)
{
// bgra
*b++ = s->colormap[s->framedata[i]*3 + 2];
*b++ = s->colormap[s->framedata[i]*3 + 1];
*b++ = s->colormap[s->framedata[i]*3];
*b++ = 255;
}
// nearest 2x
if (s->doubleres)
{
for (i = 0; i < s->info_imageheight; i++)
{
b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2);
for (j = 0; j < s->info_imagewidth; j++)
{
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
//
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
}
b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2 + 1);
for (j = 0; j < s->info_imagewidth; j++)
{
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
//
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
*b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
}
}
// do stippling
if (s->stipple)
{
for (i = 0; i < s->info_imageheight; i++)
{
b = (unsigned char *)imagedata + (s->info_imagewidth * 4 * 2 * 2 * i);
for (j = 0; j < s->info_imagewidth; j++)
{
b[0] = b[0] * s->stipple;
b[1] = b[1] * s->stipple;
b[2] = b[2] * s->stipple;
b += 4;
b[0] = b[0] * s->stipple;
b[1] = b[1] * s->stipple;
b[2] = b[2] * s->stipple;
b += 4;
}
}
}
}
// read frame header
if (!FS_Read(s->file, &frameHead, 16))
{
Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
return 1;
}
compsize = LittleLong(*(frameHead + 8)) - 16;
outsize = LittleLong(*(frameHead + 12));
if (compsize > s->framesize || outsize > s->framesize)
{
Con_Printf("JamDecoder: got bogus header on frame %i\n", s->framenum);
return 1;
}
}
}
else
s->error = JAMDECODEERROR_READERROR;
// read frame contents
if (!FS_Read(s->file, s->frame_compressed, compsize))
{
Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
return 1;
}
// palette goes interleaved with special flag
if (frameHead[0] == 2)
{
if (compsize == 768)
{
memcpy(s->frame_palette, s->frame_compressed, 768);
for(i = 0; i < 768; i++)
s->frame_palette[i] = (unsigned char)(bound(0, (s->frame_palette[i] * s->colorscale) - s->colorsub, 255));
goto readframe;
}
else
s->error = JAMDECODEERROR_READERROR;
}
else
s->error = DPVSIMPLEDECODEERROR_EOF;
return s->error;
}
{
// decode frame
// shift buffers to provide current and previous one, decode
b = s->frame_prev;
s->frame_prev = s->frame;
s->frame = b;
jam_decodeframe(s->frame_compressed, s->frame, s->frame_prev, outsize, frameHead[4]);
#ifdef JAM_USELIBAVCODECSCALE
// make BGRA imagepixels from 8bit palettized frame
b = (unsigned char *)s->frame_output_buffer;
for(i = 0; i < s->framesize; i++)
{
*b++ = s->frame_palette[s->frame[i]*3 + 2];
*b++ = s->frame_palette[s->frame[i]*3 + 1];
*b++ = s->frame_palette[s->frame[i]*3];
*b++ = 255;
}
// scale
AvCodec_FillPicture((AVPicture *)s->frame_output, (uint8_t *)s->frame_output_buffer, PIX_FMT_BGRA, s->framewidth, s->frameheight);
AvCodec_FillPicture((AVPicture *)s->frame_output_scale, (uint8_t *)imagedata, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight);
SwsContext *scale_context = SwScale_GetCachedContext(NULL, s->framewidth, s->frameheight, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight, PIX_FMT_BGRA, libavcodec_scalers[max(0, min(LIBAVCODEC_SCALERS, cl_video_libavcodec_scaler.integer))], NULL, NULL, NULL);
if (!scale_context)
{
Con_Printf("JamDecoder: LibAvcodec: error creating scale context frame %i\n", s->framenum);
return 1;
}
if (!SwScale_Scale(scale_context, s->frame_output->data, s->frame_output->linesize, 0, s->frameheight, s->frame_output_scale->data, s->frame_output_scale->linesize))
Con_Printf("JamDecoder: LibAvcodec : error scaling frame\n", s->framenum);
SwScale_FreeContext(scale_context);
#else
// make BGRA imagepixels from 8bit palettized frame
b = (unsigned char *)imagedata;
for(i = 0; i < s->framesize; i++)
{
// bgra
*b++ = s->frame_palette[s->frame[i]*3 + 2];
*b++ = s->frame_palette[s->frame[i]*3 + 1];
*b++ = s->frame_palette[s->frame[i]*3];
*b++ = 255;
}
#endif
}
return 0;
}
\ No newline at end of file
/*
Libavcodec integration for Darkplaces by Timofeyev Pavel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#include "common.h"
#ifdef _MSC_VERSION
#include "stdint.h"
#else
#include <stdint.h>
#endif
// scaler type
#define LIBAVW_SCALER_BILINEAR 0
#define LIBAVW_SCALER_BICUBIC 1
#define LIBAVW_SCALER_X 2
#define LIBAVW_SCALER_POINT 3
#define LIBAVW_SCALER_AREA 4
#define LIBAVW_SCALER_BICUBLIN 5
#define LIBAVW_SCALER_GAUSS 6
#define LIBAVW_SCALER_SINC 7
#define LIBAVW_SCALER_LANCZOS 8
#define LIBAVW_SCALER_SPLINE 9
// output format
#define LIBAVW_PIXEL_FORMAT_BGR 0
#define LIBAVW_PIXEL_FORMAT_BGRA 1
// print levels
#define LIBAVW_PRINT_WARNING 1
#define LIBAVW_PRINT_ERROR 2
#define LIBAVW_PRINT_FATAL 3
#define LIBAVW_PRINT_PANIC 4
// exported callback functions:
typedef void avwCallbackPrint(int, const char *);
typedef int avwCallbackIoRead(void *, uint8_t *, int);
typedef int64_t avwCallbackIoSeek(void *, int64_t, int);
typedef int64_t avwCallbackIoSeekSize(void *);
// exported functions:
int (*qLibAvW_Init)(avwCallbackPrint *printfunction); // init library, returns error code
const char *(*qLibAvW_ErrorString)(int errorcode); // get string for error code
const char *(*qLibAvW_AvcVersion)(void); // get a string containing libavcodec version wrapper was built for
float (*qLibAvW_Version)(void); // get wrapper version
int (*qLibAvW_CreateStream)(void **stream); // create stream, returns error code
void (*qLibAvW_RemoveStream)(void *stream); // flush and remove stream
int (*qLibAvW_StreamGetVideoWidth)(void *stream); // get video parameters of stream
int (*qLibAvW_StreamGetVideoHeight)(void *stream);
double (*qLibAvW_StreamGetFramerate)(void *stream);
int (*qLibAvW_StreamGetError)(void *stream); // get last function errorcode from stream
// simple API to play video
int (*qLibAvW_PlayVideo)(void *stream, void *file, avwCallbackIoRead *IoRead, avwCallbackIoSeek *IoSeek, avwCallbackIoSeekSize *IoSeekSize);
int (*qLibAvW_PlaySeekNextFrame)(void *stream);
int (*qLibAvW_PlayGetFrameImage)(void *stream, int pixel_format, void *imagedata, int imagewidth, int imageheight, int scaler);
static dllfunction_t libavwfuncs[] =
{
{"LibAvW_Init", (void **) &qLibAvW_Init },
{"LibAvW_ErrorString", (void **) &qLibAvW_ErrorString },
{"LibAvW_AvcVersion", (void **) &qLibAvW_AvcVersion },
{"LibAvW_Version", (void **) &qLibAvW_Version },
{"LibAvW_CreateStream", (void **) &qLibAvW_CreateStream },
{"LibAvW_RemoveStream", (void **) &qLibAvW_RemoveStream },
{"LibAvW_StreamGetVideoWidth", (void **) &qLibAvW_StreamGetVideoWidth },
{"LibAvW_StreamGetVideoHeight",(void **) &qLibAvW_StreamGetVideoHeight },
{"LibAvW_StreamGetFramerate", (void **) &qLibAvW_StreamGetFramerate },
{"LibAvW_StreamGetError", (void **) &qLibAvW_StreamGetError },
{"LibAvW_PlayVideo", (void **) &qLibAvW_PlayVideo },
{"LibAvW_PlaySeekNextFrame", (void **) &qLibAvW_PlaySeekNextFrame },
{"LibAvW_PlayGetFrameImage", (void **) &qLibAvW_PlayGetFrameImage },
{NULL, NULL}
};
const char* dllnames_libavw[] =
{
#if defined(WIN32)
"libavw.dll",
#elif defined(MACOSX)
"libavw.dylib",
#else
"libavw.so.1",
"libavw.so",
#endif
NULL
};
static dllhandle_t libavw_dll = NULL;
// DP videostream
typedef struct libavwstream_s
{
qfile_t *file;
double info_framerate;
unsigned int info_imagewidth;
unsigned int info_imageheight;
double info_aspectratio;
void *stream;
// channel the sound file is being played on
sfx_t *sfx;
int sndchan;
int sndstarted;
}
libavwstream_t;
cvar_t cl_video_libavw_minwidth = {CVAR_SAVE, "cl_video_libavw_minwidth", "0", "if videos width is lesser than minimal, thay will be upscaled"};
cvar_t cl_video_libavw_minheight = {CVAR_SAVE, "cl_video_libavw_minheight", "0", "if videos height is lesser than minimal, thay will be upscaled"};
cvar_t cl_video_libavw_scaler = {CVAR_SAVE, "cl_video_libavw_scaler", "1", "selects a scaler for libavcode played videos. Scalers are: 0 - bilinear, 1 - bicubic, 2 - x, 3 - point, 4 - area, 5 - bicublin, 6 - gauss, 7 - sinc, 8 - lanczos, 9 - spline."};
// video extensions
const char* libavw_extensions[] =
{
"ogv",
"avi",
"mpg",
"mp4",
"mkv",
"webm",
"bik",
"roq",
"flv",
"wmv",
"mpeg",
"mjpeg",
"mpeg4",
NULL
};
/*
=================================================================
Video decoding
a features that is not supported yet and likely to be done
- streaming audio from videofiles
- streaming subtitles
=================================================================
*/
unsigned int libavw_getwidth(void *stream);
unsigned int libavw_getheight(void *stream);
double libavw_getframerate(void *stream);
double libavw_getaspectratio(void *stream);
void libavw_close(void *stream);
int libavw_decodeframe(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
{
int pixel_format = LIBAVW_PIXEL_FORMAT_BGR;
int errorcode;
libavwstream_t *s = (libavwstream_t *)stream;
// start sound
if (!s->sndstarted)
{
if (s->sfx != NULL)
s->sndchan = S_StartSound(-1, 0, s->sfx, vec3_origin, 1.0f, 0);
s->sndstarted = 1;
}
// read frame
if (!qLibAvW_PlaySeekNextFrame(s->stream))
{
// got error or file end
errorcode = qLibAvW_StreamGetError(s->stream);
if (errorcode)
Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(errorcode));
return 1;
}
// decode into bgr texture
if (bytesperpixel == 4)
pixel_format = LIBAVW_PIXEL_FORMAT_BGRA;
else if (bytesperpixel == 3)
pixel_format = LIBAVW_PIXEL_FORMAT_BGR;
else
{
Con_Printf("LibAvW: cannot determine pixel format for bpp %i\n", bytesperpixel);
return 1;
}
if (!qLibAvW_PlayGetFrameImage(s->stream, pixel_format, imagedata, s->info_imagewidth, s->info_imageheight, min(9, max(0, cl_video_libavw_scaler.integer))))
Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream)));
return 0;
}
// get stream info
unsigned int libavw_getwidth(void *stream)
{
return ((libavwstream_t *)stream)->info_imagewidth;
}
unsigned int libavw_getheight(void *stream)
{
return ((libavwstream_t *)stream)->info_imageheight;
}
double libavw_getframerate(void *stream)
{
return ((libavwstream_t *)stream)->info_framerate;
}
double libavw_getaspectratio(void *stream)
{
return ((libavwstream_t *)stream)->info_aspectratio;
}
// close stream
void libavw_close(void *stream)
{
libavwstream_t *s = (libavwstream_t *)stream;
if (s->stream)
qLibAvW_RemoveStream(s->stream);
s->stream = NULL;
if (s->file)
FS_Close(s->file);
s->file = NULL;
if (s->sndchan >= 0)
S_StopChannel(s->sndchan, true, true);
s->sndchan = -1;
}
// IO wrapper
int LibAvW_FS_Read(void *opaque, uint8_t *buf, int buf_size)
{
return FS_Read((qfile_t *)opaque, buf, buf_size);
}
int64_t LibAvW_FS_Seek(void *opaque, int64_t pos, int whence)
{
return (int64_t)FS_Seek((qfile_t *)opaque, pos, whence);
}
int64_t LibAvW_FS_SeekSize(void *opaque)
{
return (int64_t)FS_FileSize((qfile_t *)opaque);
}
// open as DP video stream
void *LibAvW_OpenVideo(clvideo_t *video, char *filename, const char **errorstring)
{
libavwstream_t *s;
char filebase[MAX_OSPATH], check[MAX_OSPATH];
unsigned int i;
int errorcode;
char *wavename;
size_t len;
if (!libavw_dll)
return NULL;
// allocate stream
s = (libavwstream_t *)Z_Malloc(sizeof(libavwstream_t));
s->sndchan = -1;
memset(s, 0, sizeof(libavwstream_t));
if (s == NULL)
{
*errorstring = "unable to allocate memory for stream info structure";
return NULL;
}
// open file
s->file = FS_OpenVirtualFile(filename, true);
if (!s->file)
{
FS_StripExtension(filename, filebase, sizeof(filebase));
// we tried .dpv, try another extensions
for (i = 0; libavw_extensions[i] != NULL; i++)
{
dpsnprintf(check, sizeof(check), "%s.%s", filebase, libavw_extensions[i]);
s->file = FS_OpenVirtualFile(check, true);
if (s->file)
break;
}
if (!s->file)
{
*errorstring = "unable to open videofile";
libavw_close(s);
Z_Free(s);
return NULL;
}
}
// allocate libavw stream
if ((errorcode = qLibAvW_CreateStream(&s->stream)))
{
*errorstring = qLibAvW_ErrorString(errorcode);
libavw_close(s);
Z_Free(s);
return NULL;
}
// open video for playing
if (!qLibAvW_PlayVideo(s->stream, s->file, &LibAvW_FS_Read, &LibAvW_FS_Seek, &LibAvW_FS_SeekSize))
{
*errorstring = qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream));
libavw_close(s);
Z_Free(s);
return NULL;
}
// all right, start codec
s->info_imagewidth = qLibAvW_StreamGetVideoWidth(s->stream);
s->info_imageheight = qLibAvW_StreamGetVideoHeight(s->stream);
s->info_framerate = qLibAvW_StreamGetFramerate(s->stream);
s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
video->close = libavw_close;
video->getwidth = libavw_getwidth;
video->getheight = libavw_getheight;
video->getframerate = libavw_getframerate;
video->decodeframe = libavw_decodeframe;
video->getaspectratio = libavw_getaspectratio;
// apply min-width, min-height, keep aspect rate
if (cl_video_libavw_minwidth.integer > 0)
s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavw_minwidth.integer);
if (cl_video_libavw_minheight.integer > 0)
s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavw_minheight.integer);
// provide sound in separate .wav
len = strlen(filename) + 10;
wavename = (char *)Z_Malloc(len);
if (wavename)
{
FS_StripExtension(filename, wavename, len-1);
strlcat(wavename, ".wav", len);
s->sfx = S_PrecacheSound(wavename, false, false);
s->sndchan = -1;
Z_Free(wavename);
}
return s;
}
void libavw_message(int level, const char *message)
{
if (level == LIBAVW_PRINT_WARNING)
Con_Printf("LibAvcodec warning: %s\n", message);
else if (level == LIBAVW_PRINT_ERROR)
Con_Printf("LibAvcodec error: %s\n", message);
else if (level == LIBAVW_PRINT_FATAL)
Con_Printf("LibAvcodec fatal error: %s\n", message);
else
Con_Printf("LibAvcodec panic: %s\n", message);
}
qboolean LibAvW_OpenLibrary(void)
{
int errorcode;
// COMMANDLINEOPTION: Video: -nolibavw disables libavcodec wrapper support
if (COM_CheckParm("-nolibavw"))
return false;
// load DLL's
Sys_LoadLibrary(dllnames_libavw, &libavw_dll, libavwfuncs);
if (!libavw_dll)
return false;
// initialize libav wrapper
if ((errorcode = qLibAvW_Init(&libavw_message)))
{
Con_Printf("LibAvW failed to initialize: %s\n", qLibAvW_ErrorString(errorcode));
Sys_UnloadLibrary(&libavw_dll);
}
Cvar_RegisterVariable(&cl_video_libavw_minwidth);
Cvar_RegisterVariable(&cl_video_libavw_minheight);
Cvar_RegisterVariable(&cl_video_libavw_scaler);
return true;
}
void LibAvW_CloseLibrary(void)
{
Sys_UnloadLibrary(&libavw_dll);
}
......@@ -230,6 +230,7 @@ typedef struct dpvsimpledecodestream_s
unsigned int info_imageBmask;
unsigned int info_imageBshift;
unsigned int info_imagesize;
double info_aspectratio;
// current video frame (needed because of delta compression)
int videoframenum;
......@@ -360,6 +361,7 @@ void *dpvsimpledecode_open(clvideo_t *video, char *filename, const char **errors
s->info_imagewidth = hz_bitstream_read_short(s->framedatablocks);
s->info_imageheight = hz_bitstream_read_short(s->framedatablocks);
s->info_framerate = (double) hz_bitstream_read_int(s->framedatablocks) * (1.0 / 65536.0);
s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
if (s->info_framerate > 0.0)
{
......@@ -391,6 +393,7 @@ void *dpvsimpledecode_open(clvideo_t *video, char *filename, const char **errors
video->getheight = dpvsimpledecode_getheight;
video->getframerate = dpvsimpledecode_getframerate;
video->decodeframe = dpvsimpledecode_video;
video->getaspectratio = dpvsimpledecode_getaspectratio;
return s;
}
......@@ -512,6 +515,13 @@ double dpvsimpledecode_getframerate(void *stream)
return s->info_framerate;
}
// return aspect ratio of the stream
double dpvsimpledecode_getaspectratio(void *stream)
{
dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream;
return s->info_aspectratio;
}
static int dpvsimpledecode_convertpixels(dpvsimpledecodestream_t *s, void *imagedata, int imagebytesperrow)
{
unsigned int a, x, y, width, height;
......
......@@ -40,6 +40,9 @@ unsigned int dpvsimpledecode_getheight(void *stream);
// returns the framerate of the stream
double dpvsimpledecode_getframerate(void *stream);
// returns aspect ratio of the stream
double dpvsimpledecode_getaspectratio(void *stream);
// decodes a video frame to the supplied output pixels
int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment