Added video player and .duk video decoder
(video playback support phase 2) git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1248 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
uqm_CFILES="vfileins.c"
|
uqm_CFILES="vfileins.c video.c videodec.c vidplayer.c dukvid.c"
|
||||||
|
uqm_SUBDIRS="sdl"
|
||||||
|
|||||||
@@ -0,0 +1,687 @@
|
|||||||
|
/* DUCK video player
|
||||||
|
*
|
||||||
|
* Status: as raw as it gets
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "video.h"
|
||||||
|
#include "libs/sndlib.h"
|
||||||
|
#include "libs/sound/sound.h"
|
||||||
|
#include "dukvid.h"
|
||||||
|
#include "endian_uqm.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define _THIS TFB_VideoDecoder* This
|
||||||
|
|
||||||
|
int dukv_GetError (_THIS);
|
||||||
|
bool dukv_Init (_THIS, TFB_VideoFormat* fmt);
|
||||||
|
void dukv_Term (_THIS);
|
||||||
|
bool dukv_Open (_THIS, uio_DirHandle *dir, const char *filename);
|
||||||
|
void dukv_Close (_THIS);
|
||||||
|
int dukv_DecodeNext (_THIS);
|
||||||
|
uint32 dukv_SeekFrame (_THIS, uint32 frame);
|
||||||
|
float dukv_SeekTime (_THIS, float time);
|
||||||
|
uint32 dukv_GetFrame (_THIS);
|
||||||
|
float dukv_GetTime (_THIS);
|
||||||
|
|
||||||
|
TFB_VideoDecoderFuncs dukv_DecoderVtbl =
|
||||||
|
{
|
||||||
|
dukv_GetError,
|
||||||
|
dukv_Init,
|
||||||
|
dukv_Term,
|
||||||
|
dukv_Open,
|
||||||
|
dukv_Close,
|
||||||
|
dukv_DecodeNext,
|
||||||
|
dukv_SeekFrame,
|
||||||
|
dukv_SeekTime,
|
||||||
|
dukv_GetFrame,
|
||||||
|
dukv_GetTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct tfb_duckvideoheader
|
||||||
|
{
|
||||||
|
uint32 version;
|
||||||
|
uint32 scrn_x_ofs; // horz screen offset in pixels
|
||||||
|
uint32 scrn_y_ofs; // vert screen offset in pixels
|
||||||
|
uint16 wb, hb; // width + height in blocks
|
||||||
|
sint16 lumas[8]; // future luminance deltas
|
||||||
|
sint16 chromas[8]; // future chrominance deltas
|
||||||
|
|
||||||
|
} TFB_DuckVideoHeader;
|
||||||
|
|
||||||
|
#define NUM_VEC_ITEMS 0x010
|
||||||
|
#define NUM_VECTORS 0x100
|
||||||
|
|
||||||
|
typedef struct tfb_duckvideodeltas
|
||||||
|
{
|
||||||
|
sint32 lumas[NUM_VECTORS][NUM_VEC_ITEMS];
|
||||||
|
sint32 chromas[NUM_VECTORS][NUM_VEC_ITEMS];
|
||||||
|
|
||||||
|
} TFB_DuckVideoDeltas;
|
||||||
|
|
||||||
|
typedef struct tfb_duckvideo
|
||||||
|
{
|
||||||
|
sint32 last_error;
|
||||||
|
uio_DirHandle* basedir;
|
||||||
|
char* basename;
|
||||||
|
uio_Stream *stream;
|
||||||
|
|
||||||
|
// loaded from disk
|
||||||
|
uint32* frames;
|
||||||
|
uint32 cframes;
|
||||||
|
uint32 iframe;
|
||||||
|
uint32 version;
|
||||||
|
uint32 wb, hb; // width + height in blocks
|
||||||
|
uint32 w, h;
|
||||||
|
|
||||||
|
// generated
|
||||||
|
TFB_DuckVideoDeltas d;
|
||||||
|
|
||||||
|
uint8* inbuf;
|
||||||
|
uint32* decbuf;
|
||||||
|
|
||||||
|
} TFB_DuckVideo;
|
||||||
|
|
||||||
|
#define DUCK_GENERAL_FPS 14.622f
|
||||||
|
#define DUCK_MAX_FRAME_SIZE 0x8000U
|
||||||
|
#define DUCK_END_OF_SEQUENCE 1
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_DecodeFrame (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
|
||||||
|
TFB_DuckVideoDeltas* deltas)
|
||||||
|
{
|
||||||
|
int iVec;
|
||||||
|
int iSeq;
|
||||||
|
uint32 x, y;
|
||||||
|
sint32 w;
|
||||||
|
uint32 *d_p0, *d_p1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
w = wb * 4;
|
||||||
|
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < hb; ++y)
|
||||||
|
{
|
||||||
|
sint32 accum0, accum1, corr, corr0, corr1, delta;
|
||||||
|
sint32 pix[4];
|
||||||
|
|
||||||
|
d_p0 = dst_p + y * w * 2;
|
||||||
|
d_p1 = d_p0 + w;
|
||||||
|
|
||||||
|
accum0 = 0;
|
||||||
|
accum1 = 0;
|
||||||
|
corr0 = 0;
|
||||||
|
corr1 = 0;
|
||||||
|
|
||||||
|
for (x = 0; x < wb; ++x)
|
||||||
|
{
|
||||||
|
if (y == 0)
|
||||||
|
{
|
||||||
|
pix[0] = pix[1] = pix[2] = pix[3] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32* p_p = d_p0 - w;
|
||||||
|
pix[0] = p_p[0];
|
||||||
|
pix[1] = p_p[1];
|
||||||
|
pix[2] = p_p[2];
|
||||||
|
pix[3] = p_p[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with chroma delta
|
||||||
|
delta = deltas->chromas[iVec][iSeq++];
|
||||||
|
iSeq++; // correctors ignored
|
||||||
|
|
||||||
|
accum0 += delta >> 1;
|
||||||
|
if (delta & 1)
|
||||||
|
{
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line 0
|
||||||
|
for (i = 0; i < 4; ++i, ++d_p0)
|
||||||
|
{
|
||||||
|
delta = deltas->lumas[iVec][iSeq++];
|
||||||
|
corr = deltas->lumas[iVec][iSeq++];
|
||||||
|
|
||||||
|
accum0 += delta >> 1;
|
||||||
|
corr0 ^= corr;
|
||||||
|
pix[i] += accum0;
|
||||||
|
pix[i] ^= corr0;
|
||||||
|
|
||||||
|
if (delta & 1)
|
||||||
|
{
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d_p0 = pix[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// line 1
|
||||||
|
for (i = 0; i < 4; ++i, ++d_p1)
|
||||||
|
{
|
||||||
|
delta = deltas->lumas[iVec][iSeq++];
|
||||||
|
corr = deltas->lumas[iVec][iSeq++];
|
||||||
|
|
||||||
|
accum1 += delta >> 1;
|
||||||
|
corr1 ^= corr;
|
||||||
|
pix[i] += accum1;
|
||||||
|
pix[i] ^= corr1;
|
||||||
|
|
||||||
|
if (delta & 1)
|
||||||
|
{
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d_p1 = pix[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_DecodeFrameV3 (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
|
||||||
|
TFB_DuckVideoDeltas* deltas)
|
||||||
|
{
|
||||||
|
int iVec;
|
||||||
|
int iSeq;
|
||||||
|
uint32 x, y;
|
||||||
|
sint32 w;
|
||||||
|
uint32* d_p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
|
||||||
|
hb *= 2;
|
||||||
|
w = wb * 4;
|
||||||
|
|
||||||
|
for (y = 0; y < hb; ++y)
|
||||||
|
{
|
||||||
|
sint32 accum, delta, pix;
|
||||||
|
|
||||||
|
d_p = dst_p + y * w;
|
||||||
|
|
||||||
|
accum = 0;
|
||||||
|
|
||||||
|
for (x = 0; x < wb; ++x)
|
||||||
|
{
|
||||||
|
// start with chroma delta
|
||||||
|
delta = deltas->chromas[iVec][iSeq];
|
||||||
|
iSeq += 2; // correctors ignored
|
||||||
|
|
||||||
|
accum += delta >> 1;
|
||||||
|
|
||||||
|
if (delta & DUCK_END_OF_SEQUENCE)
|
||||||
|
{
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i, ++d_p)
|
||||||
|
{
|
||||||
|
if (y == 0)
|
||||||
|
pix = 0;
|
||||||
|
else
|
||||||
|
pix = d_p[-w];
|
||||||
|
|
||||||
|
// get next luma delta
|
||||||
|
delta = deltas->lumas[iVec][iSeq];
|
||||||
|
iSeq += 2; // correctors ignored
|
||||||
|
|
||||||
|
accum += delta >> 1;
|
||||||
|
pix += accum;
|
||||||
|
|
||||||
|
if (delta & DUCK_END_OF_SEQUENCE)
|
||||||
|
{
|
||||||
|
iVec = *(src_p++);
|
||||||
|
iSeq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d_p = pix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_OpenStream (TFB_DuckVideo* dukv)
|
||||||
|
{
|
||||||
|
char filename[280];
|
||||||
|
|
||||||
|
strcat (strcpy (filename, dukv->basename), ".duk");
|
||||||
|
|
||||||
|
return (dukv->stream =
|
||||||
|
res_OpenResFile (dukv->basedir, filename, "rb")) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_ReadFrames (TFB_DuckVideo* dukv)
|
||||||
|
{
|
||||||
|
char filename[280];
|
||||||
|
uint32 i;
|
||||||
|
uio_Stream *fp;
|
||||||
|
|
||||||
|
strcat (strcpy (filename, dukv->basename), ".frm");
|
||||||
|
|
||||||
|
if (!(fp = res_OpenResFile (dukv->basedir, filename, "rb")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dukv->cframes = LengthResFile (fp) / sizeof (uint32);
|
||||||
|
dukv->frames = (uint32*) HMalloc (dukv->cframes * sizeof (uint32));
|
||||||
|
|
||||||
|
if ((uint32)ReadResFile (dukv->frames, sizeof (uint32), dukv->cframes, fp)
|
||||||
|
!= dukv->cframes)
|
||||||
|
{
|
||||||
|
HFree (dukv->frames);
|
||||||
|
dukv->frames = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
res_CloseResFile (fp);
|
||||||
|
|
||||||
|
for (i = 0; i < dukv->cframes; ++i)
|
||||||
|
dukv->frames[i] = UQM_SwapBE32 (dukv->frames[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_ReadVectors (TFB_DuckVideo* dukv, uint8* vectors)
|
||||||
|
{
|
||||||
|
uio_Stream *fp;
|
||||||
|
char filename[280];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
strcat (strcpy (filename, dukv->basename), ".tbl");
|
||||||
|
|
||||||
|
if (!(fp = res_OpenResFile (dukv->basedir, filename, "rb")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ret = ReadResFile (vectors, NUM_VEC_ITEMS * NUM_VECTORS, 1, fp);
|
||||||
|
res_CloseResFile (fp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_ReadHeader (TFB_DuckVideo* dukv, sint32* pl, sint32* pc)
|
||||||
|
{
|
||||||
|
uio_Stream *fp;
|
||||||
|
char filename[280];
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
TFB_DuckVideoHeader hdr;
|
||||||
|
|
||||||
|
strcat (strcpy (filename, dukv->basename), ".hdr");
|
||||||
|
|
||||||
|
if (!(fp = res_OpenResFile (dukv->basedir, filename, "rb")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ret = ReadResFile (&hdr, sizeof (hdr), 1, fp);
|
||||||
|
res_CloseResFile (fp);
|
||||||
|
if (!ret)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dukv->version = UQM_SwapBE32 (hdr.version);
|
||||||
|
dukv->wb = UQM_SwapBE16 (hdr.wb);
|
||||||
|
dukv->hb = UQM_SwapBE16 (hdr.hb);
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
pl[i] = (sint16) UQM_SwapBE16 (hdr.lumas[i]);
|
||||||
|
pc[i] = (sint16) UQM_SwapBE16 (hdr.chromas[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dukv->w = dukv->wb * 4;
|
||||||
|
dukv->h = dukv->hb * 4;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sint32
|
||||||
|
dukv_make_delta (sint32* protos, bool is_chroma, int i1, int i2)
|
||||||
|
{
|
||||||
|
sint32 d1, d2;
|
||||||
|
|
||||||
|
if (!is_chroma)
|
||||||
|
{
|
||||||
|
// 0x421 is (r,g,b)=(1,1,1) in 15bit pixel coding
|
||||||
|
d1 = (protos[i1] >> 1) * 0x421;
|
||||||
|
d2 = (protos[i2] >> 1) * 0x421;
|
||||||
|
return ((d1 << 16) + d2) << 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d1 = (protos[i1] << 10) + protos[i2];
|
||||||
|
return ((d1 << 16) + d1) << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sint32
|
||||||
|
dukv_make_corr (sint32* protos, bool is_chroma, int i1, int i2)
|
||||||
|
{
|
||||||
|
sint32 d1, d2;
|
||||||
|
|
||||||
|
if (!is_chroma)
|
||||||
|
{
|
||||||
|
d1 = (protos[i1] & 1) << 15;
|
||||||
|
d2 = (protos[i2] & 1) << 15;
|
||||||
|
return (d1 << 16) + d2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (i1 << 3) + i2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_DecodeVector (uint8* vec, sint32* p, bool is_chroma, sint32* deltas)
|
||||||
|
{
|
||||||
|
int citems = vec[0];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < citems; i += 2, vec += 2, deltas += 2)
|
||||||
|
{
|
||||||
|
sint32 d = dukv_make_delta (p, is_chroma, vec[1], vec[2]);
|
||||||
|
|
||||||
|
if (i == citems - 2)
|
||||||
|
d |= DUCK_END_OF_SEQUENCE;
|
||||||
|
|
||||||
|
deltas[0] = d;
|
||||||
|
deltas[1] = dukv_make_corr (p, is_chroma, vec[1], vec[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_InitDeltas (TFB_DuckVideo* dukv, uint8* vectors, sint32* pl, sint32* pc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_VECTORS; ++i)
|
||||||
|
{
|
||||||
|
uint8* vector = vectors + i * NUM_VEC_ITEMS;
|
||||||
|
dukv_DecodeVector (vector, pl, false, dukv->d.lumas[i]);
|
||||||
|
dukv_DecodeVector (vector, pc, true, dukv->d.chromas[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__inline__ uint32
|
||||||
|
dukv_PixelConv (uint16 pix, const TFB_VideoFormat* fmt)
|
||||||
|
{
|
||||||
|
uint32 r, g, b;
|
||||||
|
|
||||||
|
r = (pix >> 7) & 0xf8;
|
||||||
|
g = (pix >> 2) & 0xf8;
|
||||||
|
b = (pix << 3) & 0xf8;
|
||||||
|
|
||||||
|
return
|
||||||
|
((r >> fmt->Rloss) << fmt->Rshift) |
|
||||||
|
((g >> fmt->Gloss) << fmt->Gshift) |
|
||||||
|
((b >> fmt->Bloss) << fmt->Bshift);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_RenderFrame (_THIS)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
const TFB_VideoFormat* fmt = This->format;
|
||||||
|
uint32 h, x, y;
|
||||||
|
uint32* dec = dukv->decbuf;
|
||||||
|
|
||||||
|
h = dukv->h / 2;
|
||||||
|
|
||||||
|
// separate bpp versions for speed
|
||||||
|
switch (fmt->BytesPerPixel)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
uint16 *dst0, *dst1;
|
||||||
|
|
||||||
|
dst0 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2);
|
||||||
|
dst1 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
|
||||||
|
|
||||||
|
for (x = 0; x < dukv->w; ++x, ++dec, ++dst0, ++dst1)
|
||||||
|
{
|
||||||
|
uint32 pair = *dec;
|
||||||
|
*dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
|
||||||
|
*dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
uint8 *dst0, *dst1;
|
||||||
|
|
||||||
|
dst0 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2);
|
||||||
|
dst1 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
|
||||||
|
|
||||||
|
for (x = 0; x < dukv->w; ++x, ++dec, dst0 += 3, dst1 += 3)
|
||||||
|
{
|
||||||
|
uint32 pair = *dec;
|
||||||
|
*(uint32*)dst0 =
|
||||||
|
dukv_PixelConv ((uint16)(pair >> 16), fmt);
|
||||||
|
*(uint32*)dst1 =
|
||||||
|
dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
for (y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
uint32 *dst0, *dst1;
|
||||||
|
|
||||||
|
dst0 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2);
|
||||||
|
dst1 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
|
||||||
|
|
||||||
|
for (x = 0; x < dukv->w; ++x, ++dec, ++dst0, ++dst1)
|
||||||
|
{
|
||||||
|
uint32 pair = *dec;
|
||||||
|
*dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
|
||||||
|
*dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dukv_GetError (_THIS)
|
||||||
|
{
|
||||||
|
return This->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_Init (_THIS, TFB_VideoFormat* fmt)
|
||||||
|
{
|
||||||
|
This->format = fmt;
|
||||||
|
This->audio_synced = 1;
|
||||||
|
This->dec_data = HCalloc (sizeof (TFB_DuckVideo));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_Term (_THIS)
|
||||||
|
{
|
||||||
|
//TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
|
||||||
|
dukv_Close (This);
|
||||||
|
HFree (This->dec_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dukv_Open (_THIS, uio_DirHandle *dir, const char *filename)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
char* pext;
|
||||||
|
sint32 lumas[8], chromas[8];
|
||||||
|
uint8* vectors;
|
||||||
|
|
||||||
|
dukv->basedir = dir;
|
||||||
|
dukv->basename = HMalloc (strlen (filename) + 1);
|
||||||
|
strcpy (dukv->basename, filename);
|
||||||
|
pext = strrchr (dukv->basename, '.');
|
||||||
|
if (pext) // strip extension
|
||||||
|
*pext = 0;
|
||||||
|
|
||||||
|
vectors = HMalloc (NUM_VEC_ITEMS * NUM_VECTORS);
|
||||||
|
|
||||||
|
if (!dukv_OpenStream (dukv)
|
||||||
|
|| !dukv_ReadFrames (dukv)
|
||||||
|
|| !dukv_ReadHeader (dukv, lumas, chromas)
|
||||||
|
|| !dukv_ReadVectors (dukv, vectors))
|
||||||
|
{
|
||||||
|
HFree (vectors);
|
||||||
|
dukv_Close (This);
|
||||||
|
dukv->last_error = dukve_BadFile;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dukv_InitDeltas (dukv, vectors, lumas, chromas);
|
||||||
|
HFree (vectors);
|
||||||
|
|
||||||
|
This->w = dukv->w;
|
||||||
|
This->h = dukv->h;
|
||||||
|
This->length = (float) dukv->cframes / DUCK_GENERAL_FPS;
|
||||||
|
This->frame_count = dukv->cframes;
|
||||||
|
This->max_frame_wait =
|
||||||
|
(uint32) (1000.0f / DUCK_GENERAL_FPS * 1.1);
|
||||||
|
|
||||||
|
dukv->inbuf = HMalloc (DUCK_MAX_FRAME_SIZE);
|
||||||
|
dukv->decbuf = HMalloc (dukv->w * dukv->h * sizeof (uint16));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dukv_Close (_THIS)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
|
||||||
|
if (dukv->basename)
|
||||||
|
{
|
||||||
|
HFree (dukv->basename);
|
||||||
|
dukv->basename = NULL;
|
||||||
|
}
|
||||||
|
if (dukv->frames)
|
||||||
|
{
|
||||||
|
HFree (dukv->frames);
|
||||||
|
dukv->frames = NULL;
|
||||||
|
}
|
||||||
|
if (dukv->stream)
|
||||||
|
{
|
||||||
|
res_CloseResFile (dukv->stream);
|
||||||
|
dukv->stream = NULL;
|
||||||
|
}
|
||||||
|
if (dukv->inbuf)
|
||||||
|
{
|
||||||
|
HFree (dukv->inbuf);
|
||||||
|
dukv->inbuf = NULL;
|
||||||
|
}
|
||||||
|
if (dukv->decbuf)
|
||||||
|
{
|
||||||
|
HFree (dukv->decbuf);
|
||||||
|
dukv->decbuf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dukv_DecodeNext (_THIS)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
uint32 fh[2];
|
||||||
|
uint32 vofs;
|
||||||
|
uint32 vsize;
|
||||||
|
uint16 ver;
|
||||||
|
|
||||||
|
if (!dukv->stream || dukv->iframe >= dukv->cframes)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SeekResFile (dukv->stream, dukv->frames[dukv->iframe], SEEK_SET);
|
||||||
|
if (ReadResFile (&fh, sizeof (fh), 1, dukv->stream) != 1)
|
||||||
|
{
|
||||||
|
dukv->last_error = dukve_EOF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vofs = UQM_SwapBE32 (fh[0]);
|
||||||
|
vsize = UQM_SwapBE32 (fh[1]);
|
||||||
|
if (vsize > DUCK_MAX_FRAME_SIZE)
|
||||||
|
{
|
||||||
|
dukv->last_error = dukve_OutOfBuf;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekResFile (dukv->stream, vofs, SEEK_CUR);
|
||||||
|
if ((uint32)ReadResFile (dukv->inbuf, 1, vsize, dukv->stream) != vsize)
|
||||||
|
{
|
||||||
|
dukv->last_error = dukve_EOF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ver = UQM_SwapBE16 (*(uint16*)dukv->inbuf);
|
||||||
|
if (ver == 0x0300)
|
||||||
|
dukv_DecodeFrameV3 (dukv->inbuf + 0x10, dukv->decbuf,
|
||||||
|
dukv->wb, dukv->hb, &dukv->d);
|
||||||
|
else
|
||||||
|
dukv_DecodeFrame (dukv->inbuf + 0x10, dukv->decbuf,
|
||||||
|
dukv->wb, dukv->hb, &dukv->d);
|
||||||
|
|
||||||
|
dukv->iframe++;
|
||||||
|
|
||||||
|
dukv_RenderFrame (This);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32
|
||||||
|
dukv_SeekFrame (_THIS, uint32 frame)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
|
||||||
|
if (frame > dukv->cframes)
|
||||||
|
frame = dukv->cframes; // EOS
|
||||||
|
|
||||||
|
return dukv->iframe = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
dukv_SeekTime (_THIS, float time)
|
||||||
|
{
|
||||||
|
//TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
uint32 frame = (uint32) (time * DUCK_GENERAL_FPS);
|
||||||
|
|
||||||
|
return (float) dukv_SeekFrame (This, frame) / DUCK_GENERAL_FPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32
|
||||||
|
dukv_GetFrame (_THIS)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
|
||||||
|
return dukv->iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
dukv_GetTime (_THIS)
|
||||||
|
{
|
||||||
|
TFB_DuckVideo* dukv = This->dec_data;
|
||||||
|
|
||||||
|
return (float) dukv->iframe / DUCK_GENERAL_FPS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DUKVID_H
|
||||||
|
#define _DUKVID_H
|
||||||
|
|
||||||
|
#include "libs/video/videodec.h"
|
||||||
|
|
||||||
|
extern TFB_VideoDecoderFuncs dukv_DecoderVtbl;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
// positive values are the same as in errno
|
||||||
|
dukve_None = 0,
|
||||||
|
dukve_Unknown = -1,
|
||||||
|
dukve_BadFile = -2,
|
||||||
|
dukve_BadArg = -3,
|
||||||
|
dukve_OutOfBuf = -4,
|
||||||
|
dukve_EOF = -5,
|
||||||
|
dukve_Other = -1000,
|
||||||
|
} DukVid_Error;
|
||||||
|
|
||||||
|
#endif // _DUKVID_H
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uqm_CFILES="sdlvideo.c"
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libs/graphics/sdl/sdl_common.h"
|
||||||
|
#include "libs/video/video.h"
|
||||||
|
#include "sdlvideo.h"
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_GetScreenFormat (TFB_VideoFormat *fmt)
|
||||||
|
{
|
||||||
|
SDL_PixelFormat *sdl = SDL_Video->format;
|
||||||
|
fmt->BitsPerPixel = sdl->BitsPerPixel;
|
||||||
|
fmt->Rmask = sdl->Rmask;
|
||||||
|
fmt->Gmask = sdl->Gmask;
|
||||||
|
fmt->Bmask = sdl->Bmask;
|
||||||
|
fmt->Amask = 0; // no alpha support for now
|
||||||
|
}
|
||||||
|
|
||||||
|
TFB_Image*
|
||||||
|
TFB_CreateVideoImage (int w, int h)
|
||||||
|
{
|
||||||
|
TFB_Image* img;
|
||||||
|
SDL_PixelFormat* fmt = SDL_Video->format;
|
||||||
|
|
||||||
|
img = HMalloc (sizeof (TFB_Image));
|
||||||
|
img->mutex = CreateMutex ();
|
||||||
|
img->ScaledImg = NULL;
|
||||||
|
img->MipmapImg = NULL;
|
||||||
|
img->colormap_index = -1;
|
||||||
|
img->last_scale_type = -1;
|
||||||
|
img->Palette = NULL;
|
||||||
|
|
||||||
|
img->NormalImg = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
|
||||||
|
fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, 0);
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
TFB_GetVideoLine (TFB_Image* img, uint32 line)
|
||||||
|
{
|
||||||
|
SDL_Surface* surf = img->NormalImg;
|
||||||
|
return (uint8*)surf->pixels + surf->pitch * line;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SDLVIDEO_H
|
||||||
|
#define _SDLVIDEO_H
|
||||||
|
|
||||||
|
#include "libs/graphics/gfx_common.h"
|
||||||
|
#include "libs/graphics/tfb_draw.h"
|
||||||
|
#include "libs/gfxlib.h"
|
||||||
|
|
||||||
|
void TFB_GetScreenFormat (TFB_VideoFormat *fmt);
|
||||||
|
TFB_Image* TFB_CreateVideoImage (int w, int h);
|
||||||
|
void* TFB_GetVideoLine (TFB_Image* img, uint32 line);
|
||||||
|
|
||||||
|
#endif // _SDLVIDEO_H
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
//Copyright Paul Reiche, Fred Ford. 1992-2002
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "video.h"
|
||||||
|
#include "sdl/sdlvideo.h"
|
||||||
|
#include "vidplayer.h"
|
||||||
|
|
||||||
|
#define NULL_VIDEO_REF (0)
|
||||||
|
static VIDEO_REF _cur_video = NULL_VIDEO_REF;
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
InitVideo (BOOLEAN useCDROM)
|
||||||
|
//useCDROM doesn't really apply to us
|
||||||
|
{
|
||||||
|
TFB_VideoFormat fmt;
|
||||||
|
TFB_GetScreenFormat (&fmt);
|
||||||
|
return VideoDecoder_Init (0, fmt.BitsPerPixel, fmt.Rmask,
|
||||||
|
fmt.Gmask, fmt.Bmask, fmt.Amask);
|
||||||
|
|
||||||
|
(void)useCDROM; /* dodge compiler warning */
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UninitVideo ()
|
||||||
|
{
|
||||||
|
VideoDecoder_Uninit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VidStop ()
|
||||||
|
{
|
||||||
|
if (_cur_video)
|
||||||
|
{
|
||||||
|
TFB_StopVideo (_cur_video);
|
||||||
|
TFB_FadeClearScreen ();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cur_video = NULL_VIDEO_REF;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIDEO_REF
|
||||||
|
VidPlaying ()
|
||||||
|
// this should just probably return BOOLEAN
|
||||||
|
{
|
||||||
|
if (!_cur_video)
|
||||||
|
return NULL_VIDEO_REF;
|
||||||
|
|
||||||
|
if (TFB_VideoPlaying (_cur_video))
|
||||||
|
return _cur_video;
|
||||||
|
|
||||||
|
return NULL_VIDEO_REF;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIDEO_TYPE
|
||||||
|
VidPlay (VIDEO_REF VidRef, char *loopname, BOOLEAN uninit)
|
||||||
|
// uninit was used to uninit the game kernel
|
||||||
|
// before spawning duck exe
|
||||||
|
{
|
||||||
|
VIDEO_TYPE ret;
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) VidRef;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return NO_FMV;
|
||||||
|
|
||||||
|
if (_cur_video)
|
||||||
|
TFB_StopVideo (_cur_video);
|
||||||
|
_cur_video = NULL_VIDEO_REF;
|
||||||
|
|
||||||
|
TFB_FadeClearScreen ();
|
||||||
|
FlushInput ();
|
||||||
|
LockCrossThreadMutex (GraphicsLock);
|
||||||
|
SetContext (ScreenContext);
|
||||||
|
// play video in the center of the screen
|
||||||
|
if (TFB_PlayVideo (VidRef, (SCREEN_WIDTH - vid->w) / 2,
|
||||||
|
(SCREEN_HEIGHT - vid->h) / 2))
|
||||||
|
{
|
||||||
|
_cur_video = VidRef;
|
||||||
|
ret = SOFTWARE_FMV;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = NO_FMV;
|
||||||
|
}
|
||||||
|
UnlockCrossThreadMutex (GraphicsLock);
|
||||||
|
|
||||||
|
/* dodge compiler warnings */
|
||||||
|
(void) loopname;
|
||||||
|
(void) uninit;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VidDoInput (void)
|
||||||
|
{
|
||||||
|
if (_cur_video)
|
||||||
|
TFB_VideoInput (_cur_video);
|
||||||
|
}
|
||||||
|
|
||||||
|
VIDEO_REF
|
||||||
|
_init_video_file(PVOID pStr)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid;
|
||||||
|
TFB_VideoDecoder* dec;
|
||||||
|
|
||||||
|
dec = VideoDecoder_Load (contentDir, (const char*)pStr);
|
||||||
|
if (!dec)
|
||||||
|
return NULL_VIDEO_REF;
|
||||||
|
|
||||||
|
vid = (TFB_VideoClip*) HCalloc (sizeof (*vid));
|
||||||
|
vid->decoder = dec;
|
||||||
|
vid->length = dec->length;
|
||||||
|
vid->w = vid->decoder->w;
|
||||||
|
vid->h = vid->decoder->h;
|
||||||
|
vid->guard = CreateMutex ();
|
||||||
|
vid->frame_lock = CreateCondVar ();
|
||||||
|
|
||||||
|
return (VIDEO_REF) vid;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
DestroyVideo (VIDEO_REF VideoRef)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) VideoRef;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
VideoDecoder_Free (vid->decoder);
|
||||||
|
DestroyMutex (vid->guard);
|
||||||
|
DestroyCondVar (vid->frame_lock);
|
||||||
|
HFree (vid);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UQM_VIDEO_H
|
||||||
|
#define _UQM_VIDEO_H
|
||||||
|
|
||||||
|
#include "starcon.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "vidlib.h"
|
||||||
|
#include "libs/graphics/tfb_draw.h"
|
||||||
|
#include "videodec.h"
|
||||||
|
#include "libs/sound/sound.h"
|
||||||
|
|
||||||
|
typedef struct tfb_videoclip
|
||||||
|
{
|
||||||
|
TFB_VideoDecoder *decoder; // decoder to read from
|
||||||
|
float length; // total length of clip seconds
|
||||||
|
uint32 w, h;
|
||||||
|
RECT dst_rect; // destination screen rect
|
||||||
|
RECT src_rect; // source rect
|
||||||
|
|
||||||
|
MUSIC_REF hAudio;
|
||||||
|
Task play_task;
|
||||||
|
uint32 frame_time; // time when next frame should be rendered
|
||||||
|
TFB_Image* frame;
|
||||||
|
uint32 cur_frame;
|
||||||
|
uint32 max_frame_wait;
|
||||||
|
bool playing;
|
||||||
|
|
||||||
|
Mutex guard;
|
||||||
|
CondVar frame_lock;
|
||||||
|
uint32 want_frame;
|
||||||
|
|
||||||
|
void* data; // user-defined data
|
||||||
|
|
||||||
|
} TFB_VideoClip;
|
||||||
|
|
||||||
|
#endif // _UQM_VIDEO_H
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "video.h"
|
||||||
|
#include "videodec.h"
|
||||||
|
#include "dukvid.h"
|
||||||
|
|
||||||
|
static bool vd_inited = 0;
|
||||||
|
static TFB_VideoFormat vd_vidfmt;
|
||||||
|
|
||||||
|
static struct vd_DecoderInfo
|
||||||
|
{
|
||||||
|
const char* ext;
|
||||||
|
const char* name;
|
||||||
|
TFB_VideoDecoderFuncs* funcs;
|
||||||
|
}
|
||||||
|
vd_decoders[] =
|
||||||
|
{
|
||||||
|
{"duk", "DukVid", &dukv_DecoderVtbl},
|
||||||
|
{NULL, NULL, NULL} // null term
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vd_computeMasks (uint32 mask, uint32* shift, uint32* loss);
|
||||||
|
|
||||||
|
bool
|
||||||
|
VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
|
||||||
|
uint32 Bmask, uint32 Amask)
|
||||||
|
{
|
||||||
|
vd_inited = 0;
|
||||||
|
|
||||||
|
if (depth < 15 || depth > 32)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Init: Unsupported video depth %d\n", depth);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Rmask & Gmask) || (Rmask & Bmask) || (Rmask & Amask) ||
|
||||||
|
(Gmask & Bmask) || (Gmask & Amask) || (Bmask & Amask))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Init: Invalid channel masks\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN: adapted from SDL
|
||||||
|
vd_vidfmt.BitsPerPixel = depth;
|
||||||
|
vd_vidfmt.BytesPerPixel = (depth + 7) / 8;
|
||||||
|
vd_vidfmt.Rmask = Rmask;
|
||||||
|
vd_vidfmt.Gmask = Gmask;
|
||||||
|
vd_vidfmt.Bmask = Bmask;
|
||||||
|
vd_vidfmt.Amask = Amask;
|
||||||
|
vd_computeMasks (Rmask, &vd_vidfmt.Rshift, &vd_vidfmt.Rloss);
|
||||||
|
vd_computeMasks (Gmask, &vd_vidfmt.Gshift, &vd_vidfmt.Gloss);
|
||||||
|
vd_computeMasks (Bmask, &vd_vidfmt.Bshift, &vd_vidfmt.Bloss);
|
||||||
|
vd_computeMasks (Amask, &vd_vidfmt.Ashift, &vd_vidfmt.Aloss);
|
||||||
|
// END: adapted from SDL
|
||||||
|
|
||||||
|
vd_inited = 1;
|
||||||
|
(void)flags; // dodge compiler warning
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VideoDecoder_Uninit (void)
|
||||||
|
{
|
||||||
|
vd_inited = 0;
|
||||||
|
// go for a strall in a park
|
||||||
|
}
|
||||||
|
|
||||||
|
TFB_VideoDecoder*
|
||||||
|
VideoDecoder_Load (uio_DirHandle *dir, const char *filename)
|
||||||
|
{
|
||||||
|
const char* pext;
|
||||||
|
struct vd_DecoderInfo* dinfo;
|
||||||
|
TFB_VideoDecoder* decoder;
|
||||||
|
|
||||||
|
|
||||||
|
if (!vd_inited)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pext = strrchr (filename, '.');
|
||||||
|
if (!pext)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Load: Unknown file type\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
++pext;
|
||||||
|
|
||||||
|
for (dinfo = vd_decoders;
|
||||||
|
dinfo->ext && strcmp(dinfo->ext, pext) != 0;
|
||||||
|
++dinfo)
|
||||||
|
;
|
||||||
|
if (!dinfo->ext)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Load: Unsupported file type\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder = (TFB_VideoDecoder*) HCalloc (sizeof (*decoder));
|
||||||
|
decoder->funcs = dinfo->funcs;
|
||||||
|
if (!decoder->funcs->Init (decoder, &vd_vidfmt))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Load: Cannot init '%s' decoder, code %d\n",
|
||||||
|
dinfo->name, decoder->funcs->GetError (decoder));
|
||||||
|
HFree (decoder);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder->decoder_info = dinfo->name;
|
||||||
|
decoder->dir = dir;
|
||||||
|
decoder->filename = (char *) HMalloc (strlen (filename) + 1);
|
||||||
|
strcpy (decoder->filename, filename);
|
||||||
|
decoder->error = VIDEODECODER_OK;
|
||||||
|
|
||||||
|
if (!decoder->funcs->Open (decoder, dir, filename))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VideoDecoder_Load: '%s' decoder did not load %s, code %d\n",
|
||||||
|
dinfo->name, filename, decoder->funcs->GetError (decoder));
|
||||||
|
|
||||||
|
VideoDecoder_Free (decoder);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return: >0 = OK, 0 = EOF, <0 = Error
|
||||||
|
int
|
||||||
|
VideoDecoder_Decode (TFB_VideoDecoder *decoder)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!decoder)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
decoder->cur_frame = decoder->funcs->GetFrame (decoder);
|
||||||
|
decoder->pos = decoder->funcs->GetTime (decoder);
|
||||||
|
|
||||||
|
ret = decoder->funcs->DecodeNext (decoder);
|
||||||
|
if (ret == 0)
|
||||||
|
decoder->error = VIDEODECODER_EOF;
|
||||||
|
else if (ret < 0)
|
||||||
|
decoder->error = VIDEODECODER_ERROR;
|
||||||
|
else
|
||||||
|
decoder->error = VIDEODECODER_OK;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
VideoDecoder_Seek (TFB_VideoDecoder *decoder, float pos)
|
||||||
|
{
|
||||||
|
if (!decoder)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
decoder->pos = decoder->funcs->SeekTime (decoder, pos);
|
||||||
|
decoder->cur_frame = decoder->funcs->GetFrame (decoder);
|
||||||
|
|
||||||
|
return decoder->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32
|
||||||
|
VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame)
|
||||||
|
{
|
||||||
|
if (!decoder)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
decoder->cur_frame = decoder->funcs->SeekFrame (decoder, frame);
|
||||||
|
decoder->pos = decoder->funcs->GetTime (decoder);
|
||||||
|
|
||||||
|
return decoder->cur_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VideoDecoder_Rewind (TFB_VideoDecoder *decoder)
|
||||||
|
{
|
||||||
|
if (!decoder)
|
||||||
|
return;
|
||||||
|
|
||||||
|
VideoDecoder_Seek (decoder, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VideoDecoder_Free (TFB_VideoDecoder *decoder)
|
||||||
|
{
|
||||||
|
if (!decoder)
|
||||||
|
return;
|
||||||
|
|
||||||
|
decoder->funcs->Close (decoder);
|
||||||
|
decoder->funcs->Term (decoder);
|
||||||
|
|
||||||
|
HFree (decoder->filename);
|
||||||
|
HFree (decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN: adapted from SDL
|
||||||
|
static void
|
||||||
|
vd_computeMasks (uint32 mask, uint32* shift, uint32* loss)
|
||||||
|
{
|
||||||
|
*shift = 0;
|
||||||
|
*loss = 8;
|
||||||
|
if (mask)
|
||||||
|
{
|
||||||
|
for (; !(mask & 1); mask >>= 1 )
|
||||||
|
++*shift;
|
||||||
|
|
||||||
|
for (; (mask & 1); mask >>= 1 )
|
||||||
|
--*loss;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END: adapted from SDL
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _VIDEODEC_H
|
||||||
|
#define _VIDEODEC_H
|
||||||
|
|
||||||
|
#include "vidlib.h"
|
||||||
|
#include "libs/video/video.h"
|
||||||
|
#include "reslib.h"
|
||||||
|
|
||||||
|
// forward-declare
|
||||||
|
typedef struct tfb_videodecoder TFB_VideoDecoder;
|
||||||
|
|
||||||
|
// we do not support paletted format for now
|
||||||
|
typedef struct tfb_videoformat
|
||||||
|
{
|
||||||
|
uint32 BitsPerPixel;
|
||||||
|
uint32 BytesPerPixel;
|
||||||
|
uint32 Rmask, Gmask, Bmask, Amask;
|
||||||
|
uint32 Rshift, Gshift, Bshift, Ashift;
|
||||||
|
uint32 Rloss, Gloss, Bloss, Aloss;
|
||||||
|
|
||||||
|
} TFB_VideoFormat;
|
||||||
|
|
||||||
|
#define _THIS TFB_VideoDecoder*
|
||||||
|
|
||||||
|
typedef struct tfb_videodecoderfunc
|
||||||
|
{
|
||||||
|
int (* GetError) (_THIS);
|
||||||
|
bool (* Init) (_THIS, TFB_VideoFormat* fmt);
|
||||||
|
void (* Term) (_THIS);
|
||||||
|
bool (* Open) (_THIS, uio_DirHandle *dir, const char *filename);
|
||||||
|
void (* Close) (_THIS);
|
||||||
|
int (* DecodeNext) (_THIS);
|
||||||
|
uint32 (* SeekFrame) (_THIS, uint32 frame);
|
||||||
|
float (* SeekTime) (_THIS, float time);
|
||||||
|
uint32 (* GetFrame) (_THIS);
|
||||||
|
float (* GetTime) (_THIS);
|
||||||
|
|
||||||
|
} TFB_VideoDecoderFuncs;
|
||||||
|
|
||||||
|
// decoder will call these to get info
|
||||||
|
// from the player
|
||||||
|
typedef struct tfb_videocallbacks
|
||||||
|
{
|
||||||
|
// any decoder calls this
|
||||||
|
void* (* GetCanvasLine) (_THIS, uint32 line);
|
||||||
|
// non-audio-driven decoders call this to figure out
|
||||||
|
// when the next frame should be drawn
|
||||||
|
uint32 (* GetTicks) (_THIS);
|
||||||
|
// non-audio-driven decoders call this to inform
|
||||||
|
// the player when the next frame should be drawn
|
||||||
|
bool (* SetTimer) (_THIS, uint32 msecs);
|
||||||
|
|
||||||
|
} TFB_VideoCallbacks;
|
||||||
|
|
||||||
|
#undef _THIS
|
||||||
|
|
||||||
|
struct tfb_videodecoder
|
||||||
|
{
|
||||||
|
// decoder virtual funcs - R/O
|
||||||
|
const TFB_VideoDecoderFuncs *funcs;
|
||||||
|
// video formats - R/O
|
||||||
|
const TFB_VideoFormat *format;
|
||||||
|
// decoder-set data - R/O
|
||||||
|
uint32 w, h;
|
||||||
|
float length; // total length in seconds
|
||||||
|
uint32 frame_count;
|
||||||
|
uint32 max_frame_wait; // maximum interframe delay in msecs
|
||||||
|
bool audio_synced;
|
||||||
|
// decoder callbacks
|
||||||
|
TFB_VideoCallbacks callbacks;
|
||||||
|
// decoder-defined data - don't mess with
|
||||||
|
void *dec_data;
|
||||||
|
|
||||||
|
// other - public
|
||||||
|
bool looping;
|
||||||
|
void* data; // user-defined data
|
||||||
|
// info - public R/O
|
||||||
|
sint32 error;
|
||||||
|
float pos; // position in seconds
|
||||||
|
uint32 cur_frame;
|
||||||
|
const char *decoder_info;
|
||||||
|
|
||||||
|
// semi-private
|
||||||
|
uio_DirHandle *dir;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// return values
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
VIDEODECODER_OK,
|
||||||
|
VIDEODECODER_ERROR,
|
||||||
|
VIDEODECODER_EOF,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
|
||||||
|
uint32 Bmask, uint32 Amask);
|
||||||
|
void VideoDecoder_Uninit (void);
|
||||||
|
TFB_VideoDecoder* VideoDecoder_Load (uio_DirHandle *dir,
|
||||||
|
const char *filename);
|
||||||
|
int VideoDecoder_Decode (TFB_VideoDecoder *decoder);
|
||||||
|
float VideoDecoder_Seek (TFB_VideoDecoder *decoder, float time_pos);
|
||||||
|
uint32 VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame_pos);
|
||||||
|
void VideoDecoder_Rewind (TFB_VideoDecoder *decoder);
|
||||||
|
void VideoDecoder_Free (TFB_VideoDecoder *decoder);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _VIDEODEC_H
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vidplayer.h"
|
||||||
|
#include "sdl/sdlvideo.h"
|
||||||
|
|
||||||
|
// video callbacks
|
||||||
|
static void* vp_GetCanvasLine (TFB_VideoDecoder*, uint32 line);
|
||||||
|
static uint32 vp_GetTicks (TFB_VideoDecoder*);
|
||||||
|
static bool vp_SetTimer (TFB_VideoDecoder*, uint32 msecs);
|
||||||
|
|
||||||
|
TFB_VideoCallbacks vp_DecoderCBs =
|
||||||
|
{
|
||||||
|
vp_GetCanvasLine,
|
||||||
|
vp_GetTicks,
|
||||||
|
vp_SetTimer
|
||||||
|
};
|
||||||
|
|
||||||
|
// audio stream callbacks
|
||||||
|
static bool vp_AudioStart (TFB_SoundSample* sample);
|
||||||
|
static void vp_AudioEnd (TFB_SoundSample* sample);
|
||||||
|
static void vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag);
|
||||||
|
static void vp_QueueBuffer (TFB_SoundSample* sample, TFBSound_Object buffer);
|
||||||
|
|
||||||
|
static TFB_SoundCallbacks vp_AudioCBs =
|
||||||
|
{
|
||||||
|
vp_AudioStart,
|
||||||
|
NULL,
|
||||||
|
vp_AudioEnd,
|
||||||
|
vp_BufferTag,
|
||||||
|
vp_QueueBuffer
|
||||||
|
};
|
||||||
|
|
||||||
|
// inter-thread param guarded by mutex
|
||||||
|
static Semaphore vp_interthread_lock = 0;
|
||||||
|
void* vp_interthread_clip = NULL;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// standard state required by DoInput
|
||||||
|
BOOLEAN (*InputFunc) (PVOID pInputState);
|
||||||
|
COUNT MenuRepeatDelay;
|
||||||
|
|
||||||
|
VIDEO_REF CurVideo;
|
||||||
|
|
||||||
|
} VIDEO_INPUT_STATE;
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_FadeClearScreen ()
|
||||||
|
{
|
||||||
|
BYTE xform_buf[1];
|
||||||
|
RECT r = {{0, 0}, {SCREEN_WIDTH, SCREEN_HEIGHT}};
|
||||||
|
|
||||||
|
xform_buf[0] = FadeAllToBlack;
|
||||||
|
SleepThreadUntil (XFormColorMap (
|
||||||
|
(COLORMAPPTR) xform_buf, ONE_SECOND / 2));
|
||||||
|
|
||||||
|
// paint black rect over screen
|
||||||
|
LockCrossThreadMutex (GraphicsLock);
|
||||||
|
SetContext (ScreenContext);
|
||||||
|
SetContextForeGroundColor (BUILD_COLOR (
|
||||||
|
MAKE_RGB15 (0x0, 0x0, 0x0), 0x00));
|
||||||
|
DrawFilledRectangle (&r);
|
||||||
|
UnlockCrossThreadMutex (GraphicsLock);
|
||||||
|
|
||||||
|
// fade in black rect instantly
|
||||||
|
xform_buf[0] = FadeAllToColor;
|
||||||
|
XFormColorMap ((COLORMAPPTR) xform_buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ac_video_play_task (void* data)
|
||||||
|
{
|
||||||
|
Task task = (Task) data;
|
||||||
|
volatile TFB_VideoClip* vid;
|
||||||
|
int ret;
|
||||||
|
uint32 want_frame;
|
||||||
|
TimeCount TimeOut;
|
||||||
|
int bTerm;
|
||||||
|
uint32 clagged;
|
||||||
|
|
||||||
|
// snatch the clip pointer and release
|
||||||
|
vid = vp_interthread_clip;
|
||||||
|
vp_interthread_clip = NULL;
|
||||||
|
ClearSemaphore (vp_interthread_lock);
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
want_frame = vid->want_frame;
|
||||||
|
PlayMusic (vid->hAudio, FALSE, 1);
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
|
||||||
|
clagged = 0;
|
||||||
|
|
||||||
|
ret = VideoDecoder_Decode (vid->decoder);
|
||||||
|
TimeOut = GetTimeCounter () + vid->decoder->max_frame_wait *
|
||||||
|
ONE_SECOND / 1000;
|
||||||
|
|
||||||
|
while (!(bTerm = Task_ReadState (task, TASK_EXIT)) && ret > 0)
|
||||||
|
{
|
||||||
|
// wait till its time to render next frame
|
||||||
|
while (!(bTerm = Task_ReadState (task, TASK_EXIT))
|
||||||
|
&& want_frame == vid->cur_frame - clagged
|
||||||
|
&& TimeOut > GetTimeCounter ())
|
||||||
|
{
|
||||||
|
TaskSwitch ();
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
want_frame = vid->want_frame;
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bTerm)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (want_frame == vid->cur_frame - clagged)
|
||||||
|
{ // timed out - draw the next frame
|
||||||
|
++clagged;
|
||||||
|
}
|
||||||
|
else if (want_frame > vid->cur_frame - clagged
|
||||||
|
&& want_frame <= vid->cur_frame)
|
||||||
|
{
|
||||||
|
// catching up
|
||||||
|
clagged = vid->cur_frame - want_frame;
|
||||||
|
// try again
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (want_frame != vid->cur_frame + 1)
|
||||||
|
{ // out of sequence frame, let's get it
|
||||||
|
vid->cur_frame = VideoDecoder_SeekFrame (
|
||||||
|
vid->decoder, want_frame);
|
||||||
|
ret = VideoDecoder_Decode (vid->decoder);
|
||||||
|
clagged = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clagged = 0;
|
||||||
|
}
|
||||||
|
vid->cur_frame = vid->decoder->cur_frame;
|
||||||
|
|
||||||
|
// draw the frame
|
||||||
|
if (ret > 0)
|
||||||
|
{
|
||||||
|
LockCrossThreadMutex (GraphicsLock);
|
||||||
|
SetContext (ScreenContext);
|
||||||
|
TFB_DrawScreen_Image (vid->frame,
|
||||||
|
vid->dst_rect.corner.x, vid->dst_rect.corner.y,
|
||||||
|
GSCALE_IDENTITY, NULL, TFB_SCREEN_MAIN);
|
||||||
|
UnlockCrossThreadMutex (GraphicsLock);
|
||||||
|
FlushGraphics (); // needed to prevent half-frame updates
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase timeout with lag-count to allow audio to catch up
|
||||||
|
TimeOut = GetTimeCounter () + vid->decoder->max_frame_wait *
|
||||||
|
ONE_SECOND / 1000 + clagged * ONE_SECOND / 100;
|
||||||
|
|
||||||
|
ret = VideoDecoder_Decode (vid->decoder);
|
||||||
|
}
|
||||||
|
StopMusic ();
|
||||||
|
vid->playing = false;
|
||||||
|
|
||||||
|
FinishTask (task);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
video_play_task (void* data)
|
||||||
|
{
|
||||||
|
Task task = (Task) data;
|
||||||
|
TFB_VideoClip* vid;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// snatch the clip pointer and release
|
||||||
|
vid = vp_interthread_clip;
|
||||||
|
vp_interthread_clip = NULL;
|
||||||
|
ClearSemaphore (vp_interthread_lock);
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
if (vid->hAudio)
|
||||||
|
PlayMusic (vid->hAudio, FALSE, 1);
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
|
||||||
|
// this works like so:
|
||||||
|
// 1. you call VideoDecoder_Seek() [when necessary] and
|
||||||
|
// VideoDecoder_Decode()
|
||||||
|
// 2. the decoder calls back vp_GetTicks() and vp_SetTimer()
|
||||||
|
// to figure out and tell you when to render the frame
|
||||||
|
// being decoded
|
||||||
|
// On a seek operation, the decoder should reset its internal
|
||||||
|
// clock and call vp_GetTicks() again
|
||||||
|
//
|
||||||
|
ret = VideoDecoder_Decode (vid->decoder);
|
||||||
|
|
||||||
|
while (!Task_ReadState (task, TASK_EXIT) && ret > 0)
|
||||||
|
{
|
||||||
|
// wait till its time to render next frame
|
||||||
|
SleepThreadUntil (vid->frame_time);
|
||||||
|
vid->cur_frame = vid->decoder->cur_frame;
|
||||||
|
|
||||||
|
LockCrossThreadMutex (GraphicsLock);
|
||||||
|
SetContext (ScreenContext);
|
||||||
|
TFB_DrawScreen_Image (vid->frame,
|
||||||
|
vid->dst_rect.corner.x, vid->dst_rect.corner.y,
|
||||||
|
GSCALE_IDENTITY, NULL, TFB_SCREEN_MAIN);
|
||||||
|
UnlockCrossThreadMutex (GraphicsLock);
|
||||||
|
FlushGraphics (); // needed to prevent half-frame updates
|
||||||
|
|
||||||
|
ret = VideoDecoder_Decode (vid->decoder);
|
||||||
|
}
|
||||||
|
vid->playing = false;
|
||||||
|
if (vid->hAudio)
|
||||||
|
StopMusic ();
|
||||||
|
|
||||||
|
FinishTask (task);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) VidRef;
|
||||||
|
RECT scrn_r;
|
||||||
|
RECT clip_r = {{0, 0}, {vid->w, vid->h}};
|
||||||
|
RECT vid_r = {{0, 0}, {SCREEN_WIDTH, SCREEN_HEIGHT}};
|
||||||
|
RECT dr = {{x, y}, {vid->w, vid->h}};
|
||||||
|
RECT sr;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// calculate the frame-source and screen-destination rects
|
||||||
|
GetContextClipRect (&scrn_r);
|
||||||
|
if (scrn_r.extent.width <= 0 || scrn_r.extent.height <= 0)
|
||||||
|
{ // bad or empty rect
|
||||||
|
scrn_r = vid_r;
|
||||||
|
}
|
||||||
|
else if (!BoxIntersect(&scrn_r, &vid_r, &scrn_r))
|
||||||
|
return false; // drawing outside visible
|
||||||
|
|
||||||
|
sr = dr;
|
||||||
|
sr.corner.x = -sr.corner.x;
|
||||||
|
sr.corner.y = -sr.corner.y;
|
||||||
|
if (!BoxIntersect (&clip_r, &sr, &sr))
|
||||||
|
return false; // drawing outside visible
|
||||||
|
|
||||||
|
dr.corner.x += scrn_r.corner.x;
|
||||||
|
dr.corner.y += scrn_r.corner.y;
|
||||||
|
if (!BoxIntersect (&scrn_r, &dr, &vid->dst_rect))
|
||||||
|
return false; // drawing outside visible
|
||||||
|
|
||||||
|
vid->src_rect = vid->dst_rect;
|
||||||
|
vid->src_rect.corner.x = sr.corner.x;
|
||||||
|
vid->src_rect.corner.y = sr.corner.y;
|
||||||
|
|
||||||
|
vid->decoder->callbacks = vp_DecoderCBs;
|
||||||
|
vid->decoder->data = vid;
|
||||||
|
|
||||||
|
vid->frame = TFB_CreateVideoImage (vid->w, vid->h);
|
||||||
|
vid->cur_frame = -1;
|
||||||
|
vid->want_frame = -1;
|
||||||
|
|
||||||
|
vid->hAudio = LoadMusicFile (vid->decoder->filename);
|
||||||
|
StopMusic ();
|
||||||
|
|
||||||
|
if (vid->decoder->audio_synced)
|
||||||
|
{
|
||||||
|
TFB_SoundSample **pmus;
|
||||||
|
|
||||||
|
if (!vid->hAudio)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "TFB_PlayVideo: Cannot load sound-track for audio-synced video\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nasty hack for now
|
||||||
|
LockMusicData (vid->hAudio, &pmus);
|
||||||
|
(*pmus)->buffer_tag = HCalloc (sizeof (TFB_SoundTag) * (*pmus)->num_buffers);
|
||||||
|
(*pmus)->callbacks = vp_AudioCBs;
|
||||||
|
(*pmus)->data = vid; // hijack data ;)
|
||||||
|
UnlockMusicData (vid->hAudio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// creation probably needs better handling
|
||||||
|
if (!vp_interthread_lock)
|
||||||
|
vp_interthread_lock = CreateSemaphore (1, "inter-thread param lock");
|
||||||
|
SetSemaphore (vp_interthread_lock);
|
||||||
|
vp_interthread_clip = vid;
|
||||||
|
|
||||||
|
vid->playing = true;
|
||||||
|
if (vid->decoder->audio_synced)
|
||||||
|
vid->play_task = AssignTask (ac_video_play_task, 4096, "a/c video player");
|
||||||
|
else
|
||||||
|
vid->play_task = AssignTask (video_play_task, 4096, "video player");
|
||||||
|
|
||||||
|
if (!vid->play_task)
|
||||||
|
{
|
||||||
|
vid->playing = false;
|
||||||
|
//UnlockCrossThreadMutex (vid->frame_lock);
|
||||||
|
ClearSemaphore (vp_interthread_lock);
|
||||||
|
TFB_StopVideo (VidRef);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_StopVideo (VIDEO_REF VidRef)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) VidRef;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vid->playing = false;
|
||||||
|
if (vid->play_task)
|
||||||
|
ConcludeTask (vid->play_task);
|
||||||
|
|
||||||
|
if (vid->hAudio)
|
||||||
|
{
|
||||||
|
StopMusic ();
|
||||||
|
DestroyMusic (vid->hAudio);
|
||||||
|
vid->hAudio = 0;
|
||||||
|
}
|
||||||
|
if (vid->frame)
|
||||||
|
{
|
||||||
|
TFB_DrawScreen_DeleteImage (vid->frame);
|
||||||
|
vid->frame = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TFB_VideoPlaying (VIDEO_REF VidRef)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) VidRef;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return vid->playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
TFB_DoVideoInput (PVOID pIS)
|
||||||
|
{
|
||||||
|
VIDEO_INPUT_STATE* pVIS = (VIDEO_INPUT_STATE*) pIS;
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) pVIS->CurVideo;
|
||||||
|
|
||||||
|
if (!pVIS->CurVideo || !TFB_VideoPlaying (pVIS->CurVideo))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (CurrentMenuState.select
|
||||||
|
|| CurrentMenuState.cancel
|
||||||
|
|| CurrentMenuState.special)
|
||||||
|
{ // abort movie
|
||||||
|
TFB_StopVideo (pVIS->CurVideo);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else if (CurrentMenuState.left || CurrentMenuState.right)
|
||||||
|
{
|
||||||
|
if (vid->decoder->audio_synced)
|
||||||
|
{
|
||||||
|
float newpos;
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
newpos = vid->decoder->pos;
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
|
||||||
|
if (CurrentMenuState.left)
|
||||||
|
newpos -= 2;
|
||||||
|
else if (CurrentMenuState.right)
|
||||||
|
newpos += 1;
|
||||||
|
if (newpos < 0)
|
||||||
|
newpos = 0;
|
||||||
|
|
||||||
|
LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
|
||||||
|
SeekStream (MUSIC_SOURCE, (uint32) (newpos * 1000));
|
||||||
|
UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
|
||||||
|
|
||||||
|
TaskSwitch ();
|
||||||
|
}
|
||||||
|
// non a/c decoder seeking is not supported yet
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TFB_VideoInput (VIDEO_REF VidRef)
|
||||||
|
{
|
||||||
|
VIDEO_INPUT_STATE vis;
|
||||||
|
|
||||||
|
vis.MenuRepeatDelay = 0;
|
||||||
|
vis.InputFunc = TFB_DoVideoInput;
|
||||||
|
vis.CurVideo = VidRef;
|
||||||
|
|
||||||
|
DoInput (&vis, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void*
|
||||||
|
vp_GetCanvasLine (TFB_VideoDecoder* decoder, uint32 line)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) decoder->data;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TFB_GetVideoLine (vid->frame, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
vp_GetTicks (TFB_VideoDecoder* decoder)
|
||||||
|
{
|
||||||
|
return GetTimeCounter () * 1000 / ONE_SECOND;
|
||||||
|
|
||||||
|
(void)decoder; // gobble up compiler warning
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vp_SetTimer (TFB_VideoDecoder* decoder, uint32 msecs)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = (TFB_VideoClip*) decoder->data;
|
||||||
|
|
||||||
|
if (!vid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// time when next frame should be displayed
|
||||||
|
vid->frame_time = GetTimeCounter () + msecs * ONE_SECOND / 1000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vp_AudioStart (TFB_SoundSample* sample)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = sample->data;
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
vid->want_frame = SoundDecoder_GetFrame (sample->decoder);
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vp_AudioEnd (TFB_SoundSample* sample)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = sample->data;
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
vid->want_frame = vid->decoder->frame_count; // end it
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag)
|
||||||
|
{
|
||||||
|
TFB_VideoClip* vid = sample->data;
|
||||||
|
uint32 frame = (uint32) tag->data;
|
||||||
|
|
||||||
|
LockMutex (vid->guard);
|
||||||
|
vid->want_frame = frame; // let it go!
|
||||||
|
UnlockMutex (vid->guard);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vp_QueueBuffer (TFB_SoundSample* sample, TFBSound_Object buffer)
|
||||||
|
{
|
||||||
|
//TFB_VideoClip* vid = sample->data;
|
||||||
|
|
||||||
|
TFB_TagBuffer (sample, buffer,
|
||||||
|
(void*) SoundDecoder_GetFrame (sample->decoder));
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _VIDPLAYER_H
|
||||||
|
#define _VIDPLAYER_H
|
||||||
|
|
||||||
|
#include "video.h"
|
||||||
|
|
||||||
|
void TFB_FadeClearScreen ();
|
||||||
|
bool TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y);
|
||||||
|
void TFB_StopVideo (VIDEO_REF VidRef);
|
||||||
|
bool TFB_VideoPlaying (VIDEO_REF VidRef);
|
||||||
|
void TFB_VideoInput (VIDEO_REF VidRef);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _VIDPLAYER_H
|
||||||
Reference in New Issue
Block a user