diff --git a/sc2/src/sc2code/libs/video/Makeinfo b/sc2/src/sc2code/libs/video/Makeinfo index 01abf6f67..339bbd53a 100644 --- a/sc2/src/sc2code/libs/video/Makeinfo +++ b/sc2/src/sc2code/libs/video/Makeinfo @@ -1 +1,2 @@ -uqm_CFILES="vfileins.c" +uqm_CFILES="vfileins.c video.c videodec.c vidplayer.c dukvid.c" +uqm_SUBDIRS="sdl" diff --git a/sc2/src/sc2code/libs/video/dukvid.c b/sc2/src/sc2code/libs/video/dukvid.c new file mode 100644 index 000000000..147a64c41 --- /dev/null +++ b/sc2/src/sc2code/libs/video/dukvid.c @@ -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 + +#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; +} diff --git a/sc2/src/sc2code/libs/video/dukvid.h b/sc2/src/sc2code/libs/video/dukvid.h new file mode 100644 index 000000000..6fcec452c --- /dev/null +++ b/sc2/src/sc2code/libs/video/dukvid.h @@ -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 diff --git a/sc2/src/sc2code/libs/video/sdl/Makeinfo b/sc2/src/sc2code/libs/video/sdl/Makeinfo new file mode 100644 index 000000000..f400650ea --- /dev/null +++ b/sc2/src/sc2code/libs/video/sdl/Makeinfo @@ -0,0 +1 @@ +uqm_CFILES="sdlvideo.c" diff --git a/sc2/src/sc2code/libs/video/sdl/sdlvideo.c b/sc2/src/sc2code/libs/video/sdl/sdlvideo.c new file mode 100644 index 000000000..b75277cc5 --- /dev/null +++ b/sc2/src/sc2code/libs/video/sdl/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; +} diff --git a/sc2/src/sc2code/libs/video/sdl/sdlvideo.h b/sc2/src/sc2code/libs/video/sdl/sdlvideo.h new file mode 100644 index 000000000..5ac298b41 --- /dev/null +++ b/sc2/src/sc2code/libs/video/sdl/sdlvideo.h @@ -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 diff --git a/sc2/src/sc2code/libs/video/video.c b/sc2/src/sc2code/libs/video/video.c new file mode 100644 index 000000000..1c1b211d5 --- /dev/null +++ b/sc2/src/sc2code/libs/video/video.c @@ -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; +} diff --git a/sc2/src/sc2code/libs/video/video.h b/sc2/src/sc2code/libs/video/video.h new file mode 100644 index 000000000..d17199628 --- /dev/null +++ b/sc2/src/sc2code/libs/video/video.h @@ -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 diff --git a/sc2/src/sc2code/libs/video/videodec.c b/sc2/src/sc2code/libs/video/videodec.c new file mode 100644 index 000000000..be9da6d36 --- /dev/null +++ b/sc2/src/sc2code/libs/video/videodec.c @@ -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 diff --git a/sc2/src/sc2code/libs/video/videodec.h b/sc2/src/sc2code/libs/video/videodec.h new file mode 100644 index 000000000..532e6e739 --- /dev/null +++ b/sc2/src/sc2code/libs/video/videodec.h @@ -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 diff --git a/sc2/src/sc2code/libs/video/vidplayer.c b/sc2/src/sc2code/libs/video/vidplayer.c new file mode 100644 index 000000000..cce2eda5b --- /dev/null +++ b/sc2/src/sc2code/libs/video/vidplayer.c @@ -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)); +} diff --git a/sc2/src/sc2code/libs/video/vidplayer.h b/sc2/src/sc2code/libs/video/vidplayer.h new file mode 100644 index 000000000..b02265625 --- /dev/null +++ b/sc2/src/sc2code/libs/video/vidplayer.h @@ -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