#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)"};
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;
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)
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 )
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)
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;
}
// 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);
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;
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
#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
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();
+}
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
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);
}
unsigned int jam_getwidth(void *stream)
{
jamdecodestream_t *s = (jamdecodestream_t *)stream;
- if (s->doubleres)
- return s->info_imagewidth * 2;
return s->info_imagewidth;
}
unsigned int jam_getheight(void *stream)
{
jamdecodestream_t *s = (jamdecodestream_t *)stream;
- if (s->doubleres)
- return s->info_imageheight * 2;
return s->info_imageheight;
}
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)
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
--- /dev/null
+/*\r
+ Libavcodec integration for Darkplaces by Timofeyev Pavel\r
+\r
+ This program is free software; you can redistribute it and/or\r
+ modify it under the terms of the GNU General Public License\r
+ as published by the Free Software Foundation; either version 2\r
+ of the License, or (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
+\r
+ See the GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with this program; if not, write to:\r
+\r
+ Free Software Foundation, Inc.\r
+ 59 Temple Place - Suite 330\r
+ Boston, MA 02111-1307, USA\r
+\r
+*/\r
+\r
+#include "common.h"\r
+#ifdef _MSC_VERSION\r
+#include "stdint.h"\r
+#else\r
+#include <stdint.h>\r
+#endif\r
+\r
+// scaler type\r
+#define LIBAVW_SCALER_BILINEAR 0\r
+#define LIBAVW_SCALER_BICUBIC 1\r
+#define LIBAVW_SCALER_X 2\r
+#define LIBAVW_SCALER_POINT 3\r
+#define LIBAVW_SCALER_AREA 4\r
+#define LIBAVW_SCALER_BICUBLIN 5\r
+#define LIBAVW_SCALER_GAUSS 6\r
+#define LIBAVW_SCALER_SINC 7\r
+#define LIBAVW_SCALER_LANCZOS 8\r
+#define LIBAVW_SCALER_SPLINE 9\r
+// output format\r
+#define LIBAVW_PIXEL_FORMAT_BGR 0\r
+#define LIBAVW_PIXEL_FORMAT_BGRA 1\r
+// print levels\r
+#define LIBAVW_PRINT_WARNING 1\r
+#define LIBAVW_PRINT_ERROR 2\r
+#define LIBAVW_PRINT_FATAL 3\r
+#define LIBAVW_PRINT_PANIC 4\r
+// exported callback functions:\r
+typedef void avwCallbackPrint(int, const char *);\r
+typedef int avwCallbackIoRead(void *, uint8_t *, int);\r
+typedef int64_t avwCallbackIoSeek(void *, int64_t, int);\r
+typedef int64_t avwCallbackIoSeekSize(void *);\r
+// exported functions:\r
+int (*qLibAvW_Init)(avwCallbackPrint *printfunction); // init library, returns error code\r
+const char *(*qLibAvW_ErrorString)(int errorcode); // get string for error code\r
+const char *(*qLibAvW_AvcVersion)(void); // get a string containing libavcodec version wrapper was built for\r
+float (*qLibAvW_Version)(void); // get wrapper version\r
+int (*qLibAvW_CreateStream)(void **stream); // create stream, returns error code\r
+void (*qLibAvW_RemoveStream)(void *stream); // flush and remove stream\r
+int (*qLibAvW_StreamGetVideoWidth)(void *stream); // get video parameters of stream\r
+int (*qLibAvW_StreamGetVideoHeight)(void *stream);\r
+double (*qLibAvW_StreamGetFramerate)(void *stream);\r
+int (*qLibAvW_StreamGetError)(void *stream); // get last function errorcode from stream\r
+// simple API to play video\r
+int (*qLibAvW_PlayVideo)(void *stream, void *file, avwCallbackIoRead *IoRead, avwCallbackIoSeek *IoSeek, avwCallbackIoSeekSize *IoSeekSize);\r
+int (*qLibAvW_PlaySeekNextFrame)(void *stream);\r
+int (*qLibAvW_PlayGetFrameImage)(void *stream, int pixel_format, void *imagedata, int imagewidth, int imageheight, int scaler);\r
+\r
+static dllfunction_t libavwfuncs[] =\r
+{\r
+ {"LibAvW_Init", (void **) &qLibAvW_Init },\r
+ {"LibAvW_ErrorString", (void **) &qLibAvW_ErrorString },\r
+ {"LibAvW_AvcVersion", (void **) &qLibAvW_AvcVersion },\r
+ {"LibAvW_Version", (void **) &qLibAvW_Version },\r
+ {"LibAvW_CreateStream", (void **) &qLibAvW_CreateStream },\r
+ {"LibAvW_RemoveStream", (void **) &qLibAvW_RemoveStream },\r
+ {"LibAvW_StreamGetVideoWidth", (void **) &qLibAvW_StreamGetVideoWidth },\r
+ {"LibAvW_StreamGetVideoHeight",(void **) &qLibAvW_StreamGetVideoHeight },\r
+ {"LibAvW_StreamGetFramerate", (void **) &qLibAvW_StreamGetFramerate },\r
+ {"LibAvW_StreamGetError", (void **) &qLibAvW_StreamGetError },\r
+ {"LibAvW_PlayVideo", (void **) &qLibAvW_PlayVideo },\r
+ {"LibAvW_PlaySeekNextFrame", (void **) &qLibAvW_PlaySeekNextFrame },\r
+ {"LibAvW_PlayGetFrameImage", (void **) &qLibAvW_PlayGetFrameImage },\r
+ {NULL, NULL}\r
+};\r
+\r
+const char* dllnames_libavw[] =\r
+{\r
+#if defined(WIN32)\r
+ "libavw.dll",\r
+#elif defined(MACOSX)\r
+ "libavw.dylib",\r
+#else\r
+ "libavw.so.1",\r
+ "libavw.so",\r
+#endif\r
+ NULL\r
+};\r
+\r
+static dllhandle_t libavw_dll = NULL;\r
+\r
+// DP videostream\r
+typedef struct libavwstream_s\r
+{\r
+ qfile_t *file;\r
+ double info_framerate;\r
+ unsigned int info_imagewidth;\r
+ unsigned int info_imageheight;\r
+ double info_aspectratio;\r
+ void *stream;\r
+\r
+ // channel the sound file is being played on\r
+ sfx_t *sfx;\r
+ int sndchan;\r
+ int sndstarted;\r
+}\r
+libavwstream_t;\r
+\r
+cvar_t cl_video_libavw_minwidth = {CVAR_SAVE, "cl_video_libavw_minwidth", "0", "if videos width is lesser than minimal, thay will be upscaled"};\r
+cvar_t cl_video_libavw_minheight = {CVAR_SAVE, "cl_video_libavw_minheight", "0", "if videos height is lesser than minimal, thay will be upscaled"};\r
+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."};\r
+\r
+// video extensions\r
+const char* libavw_extensions[] =\r
+{\r
+ "ogv",\r
+ "avi",\r
+ "mpg",\r
+ "mp4",\r
+ "mkv",\r
+ "webm",\r
+ "bik",\r
+ "roq",\r
+ "flv",\r
+ "wmv",\r
+ "mpeg",\r
+ "mjpeg",\r
+ "mpeg4",\r
+ NULL\r
+};\r
+\r
+/*\r
+=================================================================\r
+\r
+ Video decoding\r
+ a features that is not supported yet and likely to be done\r
+ - streaming audio from videofiles\r
+ - streaming subtitles\r
+\r
+=================================================================\r
+*/\r
+\r
+unsigned int libavw_getwidth(void *stream);\r
+unsigned int libavw_getheight(void *stream);\r
+double libavw_getframerate(void *stream);\r
+double libavw_getaspectratio(void *stream);\r
+void libavw_close(void *stream);\r
+\r
+int libavw_decodeframe(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)\r
+{\r
+ int pixel_format = LIBAVW_PIXEL_FORMAT_BGR;\r
+ int errorcode;\r
+\r
+ libavwstream_t *s = (libavwstream_t *)stream;\r
+\r
+ // start sound\r
+ if (!s->sndstarted)\r
+ {\r
+ if (s->sfx != NULL)\r
+ s->sndchan = S_StartSound(-1, 0, s->sfx, vec3_origin, 1.0f, 0);\r
+ s->sndstarted = 1;\r
+ }\r
+\r
+ // read frame\r
+ if (!qLibAvW_PlaySeekNextFrame(s->stream))\r
+ {\r
+ // got error or file end\r
+ errorcode = qLibAvW_StreamGetError(s->stream);\r
+ if (errorcode)\r
+ Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(errorcode));\r
+ return 1;\r
+ }\r
+\r
+ // decode into bgr texture\r
+ if (bytesperpixel == 4)\r
+ pixel_format = LIBAVW_PIXEL_FORMAT_BGRA;\r
+ else if (bytesperpixel == 3)\r
+ pixel_format = LIBAVW_PIXEL_FORMAT_BGR;\r
+ else\r
+ {\r
+ Con_Printf("LibAvW: cannot determine pixel format for bpp %i\n", bytesperpixel);\r
+ return 1;\r
+ }\r
+ if (!qLibAvW_PlayGetFrameImage(s->stream, pixel_format, imagedata, s->info_imagewidth, s->info_imageheight, min(9, max(0, cl_video_libavw_scaler.integer))))\r
+ Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream)));\r
+ return 0;\r
+}\r
+\r
+// get stream info\r
+unsigned int libavw_getwidth(void *stream)\r
+{\r
+ return ((libavwstream_t *)stream)->info_imagewidth;\r
+}\r
+\r
+unsigned int libavw_getheight(void *stream)\r
+{\r
+ return ((libavwstream_t *)stream)->info_imageheight;\r
+}\r
+\r
+double libavw_getframerate(void *stream)\r
+{\r
+ return ((libavwstream_t *)stream)->info_framerate;\r
+}\r
+\r
+double libavw_getaspectratio(void *stream)\r
+{\r
+ return ((libavwstream_t *)stream)->info_aspectratio;\r
+}\r
+\r
+// close stream\r
+void libavw_close(void *stream)\r
+{\r
+ libavwstream_t *s = (libavwstream_t *)stream;\r
+\r
+ if (s->stream)\r
+ qLibAvW_RemoveStream(s->stream);\r
+ s->stream = NULL;\r
+ if (s->file)\r
+ FS_Close(s->file);\r
+ s->file = NULL;\r
+ if (s->sndchan >= 0)\r
+ S_StopChannel(s->sndchan, true, true);\r
+ s->sndchan = -1;\r
+}\r
+\r
+// IO wrapper\r
+int LibAvW_FS_Read(void *opaque, uint8_t *buf, int buf_size)\r
+{\r
+ return FS_Read((qfile_t *)opaque, buf, buf_size);\r
+}\r
+int64_t LibAvW_FS_Seek(void *opaque, int64_t pos, int whence)\r
+{\r
+ return (int64_t)FS_Seek((qfile_t *)opaque, pos, whence);\r
+}\r
+int64_t LibAvW_FS_SeekSize(void *opaque)\r
+{\r
+ return (int64_t)FS_FileSize((qfile_t *)opaque);\r
+}\r
+\r
+// open as DP video stream\r
+void *LibAvW_OpenVideo(clvideo_t *video, char *filename, const char **errorstring)\r
+{\r
+ libavwstream_t *s;\r
+ char filebase[MAX_OSPATH], check[MAX_OSPATH];\r
+ unsigned int i;\r
+ int errorcode;\r
+ char *wavename;\r
+ size_t len;\r
+\r
+ if (!libavw_dll)\r
+ return NULL;\r
+\r
+ // allocate stream\r
+ s = (libavwstream_t *)Z_Malloc(sizeof(libavwstream_t));\r
+ s->sndchan = -1;\r
+ memset(s, 0, sizeof(libavwstream_t));\r
+ if (s == NULL)\r
+ {\r
+ *errorstring = "unable to allocate memory for stream info structure";\r
+ return NULL;\r
+ }\r
+\r
+ // open file\r
+ s->file = FS_OpenVirtualFile(filename, true);\r
+ if (!s->file)\r
+ {\r
+ FS_StripExtension(filename, filebase, sizeof(filebase));\r
+ // we tried .dpv, try another extensions\r
+ for (i = 0; libavw_extensions[i] != NULL; i++)\r
+ {\r
+ dpsnprintf(check, sizeof(check), "%s.%s", filebase, libavw_extensions[i]);\r
+ s->file = FS_OpenVirtualFile(check, true);\r
+ if (s->file)\r
+ break;\r
+ }\r
+ if (!s->file)\r
+ {\r
+ *errorstring = "unable to open videofile";\r
+ libavw_close(s);\r
+ Z_Free(s);\r
+ return NULL;\r
+ }\r
+ }\r
+\r
+ // allocate libavw stream\r
+ if ((errorcode = qLibAvW_CreateStream(&s->stream)))\r
+ {\r
+ *errorstring = qLibAvW_ErrorString(errorcode);\r
+ libavw_close(s);\r
+ Z_Free(s);\r
+ return NULL;\r
+ }\r
+\r
+ // open video for playing\r
+ if (!qLibAvW_PlayVideo(s->stream, s->file, &LibAvW_FS_Read, &LibAvW_FS_Seek, &LibAvW_FS_SeekSize))\r
+ {\r
+ *errorstring = qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream));\r
+ libavw_close(s);\r
+ Z_Free(s);\r
+ return NULL;\r
+ }\r
+\r
+ // all right, start codec\r
+ s->info_imagewidth = qLibAvW_StreamGetVideoWidth(s->stream);\r
+ s->info_imageheight = qLibAvW_StreamGetVideoHeight(s->stream);\r
+ s->info_framerate = qLibAvW_StreamGetFramerate(s->stream);\r
+ s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;\r
+ video->close = libavw_close;\r
+ video->getwidth = libavw_getwidth;\r
+ video->getheight = libavw_getheight;\r
+ video->getframerate = libavw_getframerate;\r
+ video->decodeframe = libavw_decodeframe;\r
+ video->getaspectratio = libavw_getaspectratio;\r
+\r
+ // apply min-width, min-height, keep aspect rate\r
+ if (cl_video_libavw_minwidth.integer > 0)\r
+ s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavw_minwidth.integer);\r
+ if (cl_video_libavw_minheight.integer > 0)\r
+ s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavw_minheight.integer);\r
+ \r
+ // provide sound in separate .wav\r
+ len = strlen(filename) + 10;\r
+ wavename = (char *)Z_Malloc(len);\r
+ if (wavename)\r
+ {\r
+ FS_StripExtension(filename, wavename, len-1);\r
+ strlcat(wavename, ".wav", len);\r
+ s->sfx = S_PrecacheSound(wavename, false, false);\r
+ s->sndchan = -1;\r
+ Z_Free(wavename);\r
+ }\r
+ return s;\r
+}\r
+\r
+void libavw_message(int level, const char *message)\r
+{\r
+ if (level == LIBAVW_PRINT_WARNING)\r
+ Con_Printf("LibAvcodec warning: %s\n", message);\r
+ else if (level == LIBAVW_PRINT_ERROR)\r
+ Con_Printf("LibAvcodec error: %s\n", message);\r
+ else if (level == LIBAVW_PRINT_FATAL)\r
+ Con_Printf("LibAvcodec fatal error: %s\n", message);\r
+ else\r
+ Con_Printf("LibAvcodec panic: %s\n", message);\r
+}\r
+\r
+qboolean LibAvW_OpenLibrary(void)\r
+{\r
+ int errorcode;\r
+\r
+ // COMMANDLINEOPTION: Video: -nolibavw disables libavcodec wrapper support\r
+ if (COM_CheckParm("-nolibavw"))\r
+ return false;\r
+\r
+ // load DLL's\r
+ Sys_LoadLibrary(dllnames_libavw, &libavw_dll, libavwfuncs);\r
+ if (!libavw_dll)\r
+ return false;\r
+\r
+ // initialize libav wrapper\r
+ if ((errorcode = qLibAvW_Init(&libavw_message)))\r
+ {\r
+ Con_Printf("LibAvW failed to initialize: %s\n", qLibAvW_ErrorString(errorcode));\r
+ Sys_UnloadLibrary(&libavw_dll);\r
+ }\r
+\r
+ Cvar_RegisterVariable(&cl_video_libavw_minwidth);\r
+ Cvar_RegisterVariable(&cl_video_libavw_minheight);\r
+ Cvar_RegisterVariable(&cl_video_libavw_scaler);\r
+\r
+ return true;\r
+}\r
+\r
+void LibAvW_CloseLibrary(void)\r
+{\r
+ Sys_UnloadLibrary(&libavw_dll);\r
+}\r
+\r
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;
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)
{
video->getheight = dpvsimpledecode_getheight;
video->getframerate = dpvsimpledecode_getframerate;
video->decodeframe = dpvsimpledecode_video;
+ video->getaspectratio = dpvsimpledecode_getaspectratio;
return s;
}
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;
// 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);