/* * 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. */ /* DUCK video player * * Status: fully functional */ #include "video.h" #include "dukvid.h" #include #include #include "libs/uio.h" #include "libs/memlib.h" #include "endian_uqm.h" #define THIS_PTR TFB_VideoDecoder* This static const char* dukv_GetName (void); static bool dukv_InitModule (int flags); static void dukv_TermModule (void); static uint32 dukv_GetStructSize (void); static int dukv_GetError (THIS_PTR); static bool dukv_Init (THIS_PTR, TFB_PixelFormat* fmt); static void dukv_Term (THIS_PTR); static bool dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename); static void dukv_Close (THIS_PTR); static int dukv_DecodeNext (THIS_PTR); static uint32 dukv_SeekFrame (THIS_PTR, uint32 frame); static float dukv_SeekTime (THIS_PTR, float time); static uint32 dukv_GetFrame (THIS_PTR); static float dukv_GetTime (THIS_PTR); TFB_VideoDecoderFuncs dukv_DecoderVtbl = { dukv_GetName, dukv_InitModule, dukv_TermModule, dukv_GetStructSize, 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; // specific video decoder struct derived from TFB_VideoDecoder // the only sane way in C one can :) typedef struct tfb_duckvideodecoder { // always the first member TFB_VideoDecoder decoder; 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 // generated TFB_DuckVideoDeltas d; uint8* inbuf; uint32* decbuf; } TFB_DuckVideoDecoder; #define DUCK_GENERAL_FPS 14.622f #define DUCK_MAX_FRAME_SIZE 0x8000U #define DUCK_END_OF_SEQUENCE 1 static 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]; } } } } static 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; } } } } static bool dukv_OpenStream (TFB_DuckVideoDecoder* dukv) { char filename[280]; strcat (strcpy (filename, dukv->basename), ".duk"); return (dukv->stream = uio_fopen (dukv->basedir, filename, "rb")) != NULL; } static bool dukv_ReadFrames (TFB_DuckVideoDecoder* dukv) { char filename[280]; uint32 i; uio_Stream *fp; strcat (strcpy (filename, dukv->basename), ".frm"); if (!(fp = uio_fopen (dukv->basedir, filename, "rb"))) return false; // get number of frames uio_fseek (fp, 0, SEEK_END); dukv->cframes = uio_ftell (fp) / sizeof (uint32); uio_fseek (fp, 0, SEEK_SET); dukv->frames = (uint32*) HMalloc (dukv->cframes * sizeof (uint32)); if (uio_fread (dukv->frames, sizeof (uint32), dukv->cframes, fp) != dukv->cframes) { HFree (dukv->frames); dukv->frames = 0; return 0; } uio_fclose (fp); for (i = 0; i < dukv->cframes; ++i) dukv->frames[i] = UQM_SwapBE32 (dukv->frames[i]); return true; } static bool dukv_ReadVectors (TFB_DuckVideoDecoder* dukv, uint8* vectors) { uio_Stream *fp; char filename[280]; int ret; strcat (strcpy (filename, dukv->basename), ".tbl"); if (!(fp = uio_fopen (dukv->basedir, filename, "rb"))) return false; ret = uio_fread (vectors, NUM_VEC_ITEMS, NUM_VECTORS, fp); uio_fclose (fp); return ret == NUM_VECTORS; } static bool dukv_ReadHeader (TFB_DuckVideoDecoder* 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 = uio_fopen (dukv->basedir, filename, "rb"))) return false; ret = uio_fread (&hdr, sizeof (hdr), 1, fp); uio_fclose (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->decoder.w = dukv->wb * 4; dukv->decoder.h = dukv->hb * 4; return true; } static 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; } } static 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; } } static 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]); } } static void dukv_InitDeltas (TFB_DuckVideoDecoder* 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]); } } static inline uint32 dukv_PixelConv (uint16 pix, const TFB_PixelFormat* 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); } static void dukv_RenderFrame (THIS_PTR) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; const TFB_PixelFormat* fmt = This->format; uint32 h, x, y; uint32* dec = dukv->decbuf; h = dukv->decoder.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->decoder.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->decoder.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->decoder.w; ++x, ++dec, ++dst0, ++dst1) { uint32 pair = *dec; *dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt); *dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt); } } break; } default: ; } } static const char* dukv_GetName (void) { return "DukVid"; } static bool dukv_InitModule (int flags) { // no flags are defined for now return true; (void)flags; // dodge compiler warning } static void dukv_TermModule (void) { // do an extensive search on the word 'nothing' } static uint32 dukv_GetStructSize (void) { return sizeof (TFB_DuckVideoDecoder); } static int dukv_GetError (THIS_PTR) { return This->error; } static bool dukv_Init (THIS_PTR, TFB_PixelFormat* fmt) { This->format = fmt; This->audio_synced = true; return true; } static void dukv_Term (THIS_PTR) { //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; dukv_Close (This); } static bool dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; 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->length = (float) dukv->cframes / DUCK_GENERAL_FPS; This->frame_count = dukv->cframes; This->interframe_wait = (uint32) (1000.0 / DUCK_GENERAL_FPS); dukv->inbuf = HMalloc (DUCK_MAX_FRAME_SIZE); dukv->decbuf = HMalloc ( dukv->decoder.w * dukv->decoder.h * sizeof (uint16)); return true; } static void dukv_Close (THIS_PTR) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; if (dukv->basename) { HFree (dukv->basename); dukv->basename = NULL; } if (dukv->frames) { HFree (dukv->frames); dukv->frames = NULL; } if (dukv->stream) { uio_fclose (dukv->stream); dukv->stream = NULL; } if (dukv->inbuf) { HFree (dukv->inbuf); dukv->inbuf = NULL; } if (dukv->decbuf) { HFree (dukv->decbuf); dukv->decbuf = NULL; } } static int dukv_DecodeNext (THIS_PTR) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; uint32 fh[2]; uint32 vofs; uint32 vsize; uint16 ver; if (!dukv->stream || dukv->iframe >= dukv->cframes) return 0; uio_fseek (dukv->stream, dukv->frames[dukv->iframe], SEEK_SET); if (uio_fread (&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; } uio_fseek (dukv->stream, vofs, SEEK_CUR); if (uio_fread (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++; This->callbacks.BeginFrame (This); dukv_RenderFrame (This); This->callbacks.EndFrame (This); if (!This->audio_synced) This->callbacks.SetTimer (This, (uint32) (1000.0f / DUCK_GENERAL_FPS)); return 1; } static uint32 dukv_SeekFrame (THIS_PTR, uint32 frame) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; if (frame > dukv->cframes) frame = dukv->cframes; // EOS return dukv->iframe = frame; } static float dukv_SeekTime (THIS_PTR, float time) { //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; uint32 frame = (uint32) (time * DUCK_GENERAL_FPS); // Note that DUCK_GENERAL_FPS is a float constant return dukv_SeekFrame (This, frame) / DUCK_GENERAL_FPS; } static uint32 dukv_GetFrame (THIS_PTR) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; return dukv->iframe; } static float dukv_GetTime (THIS_PTR) { TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This; return (float) dukv->iframe / DUCK_GENERAL_FPS; }