diff options
Diffstat (limited to 'src/libs/video/videodec.c')
-rw-r--r-- | src/libs/video/videodec.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/libs/video/videodec.c b/src/libs/video/videodec.c new file mode 100644 index 0000000..b99a442 --- /dev/null +++ b/src/libs/video/videodec.c @@ -0,0 +1,363 @@ +/* + * 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 <string.h> +#include "video.h" +#include "videodec.h" +#include "dukvid.h" +#include "libs/log.h" +#include "libs/memlib.h" + +#define MAX_REG_DECODERS 31 + +static bool vd_inited = false; +static TFB_PixelFormat vd_vidfmt; +static int vd_flags = 0; + +struct TFB_RegVideoDecoder +{ + bool builtin; + bool used; // ever used indicator + const char* ext; + const TFB_VideoDecoderFuncs* funcs; +}; +static TFB_RegVideoDecoder vd_decoders[MAX_REG_DECODERS + 1] = +{ + {true, true, "duk", &dukv_DecoderVtbl}, + {false, false, NULL, NULL}, // null term +}; + +static void vd_computeMasks (uint32 mask, DWORD* shift, DWORD* loss); + +const char* +VideoDecoder_GetName (TFB_VideoDecoder *decoder) +{ + if (!decoder || !decoder->funcs) + return "(Null)"; + return decoder->funcs->GetName (); +} + +bool +VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask, + uint32 Bmask, uint32 Amask) +{ + TFB_RegVideoDecoder* info; + + vd_inited = false; + + if (depth < 15 || depth > 32) + { + log_add (log_Error, "VideoDecoder_Init: " + "Unsupported video depth %d", depth); + return false; + } + + if ((Rmask & Gmask) || (Rmask & Bmask) || (Rmask & Amask) || + (Gmask & Bmask) || (Gmask & Amask) || (Bmask & Amask)) + { + log_add (log_Error, "VideoDecoder_Init: Invalid channel masks"); + 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 + + // init built-in decoders + for (info = vd_decoders; info->ext; info++) + { + if (!info->funcs->InitModule (flags)) + { + log_add (log_Error, "VideoDecoder_Init(): " + "%s video decoder init failed", + info->funcs->GetName ()); + } + } + + vd_flags = flags; + vd_inited = true; + + return true; +} + +void +VideoDecoder_Uninit (void) +{ + TFB_RegVideoDecoder* info; + + // uninit all decoders + // and unregister loaded decoders + for (info = vd_decoders; info->used; info++) + { + if (info->ext) // check if present + info->funcs->TermModule (); + + if (!info->builtin) + { + info->used = false; + info->ext = NULL; + } + } + + vd_inited = false; +} + +TFB_RegVideoDecoder* +VideoDecoder_Register (const char* fileext, TFB_VideoDecoderFuncs* decvtbl) +{ + TFB_RegVideoDecoder* info; + TFB_RegVideoDecoder* newslot = NULL; + + if (!decvtbl) + { + log_add (log_Warning, "VideoDecoder_Register(): Null decoder table"); + return NULL; + } + if (!fileext) + { + log_add (log_Warning, "VideoDecoder_Register(): Bad file type for %s", + decvtbl->GetName ()); + return NULL; + } + + // check if extension already registered + for (info = vd_decoders; info->used && + (!info->ext || strcmp (info->ext, fileext) != 0); + ++info) + { + // and pick up an empty slot (where available) + if (!newslot && !info->ext) + newslot = info; + } + + if (info >= vd_decoders + MAX_REG_DECODERS) + { + log_add (log_Warning, "VideoDecoder_Register(): Decoders limit reached"); + return NULL; + } + else if (info->ext) + { + log_add (log_Warning, "VideoDecoder_Register(): " + "'%s' decoder already registered (%s denied)", + fileext, decvtbl->GetName ()); + return NULL; + } + + if (!decvtbl->InitModule (vd_flags)) + { + log_add (log_Warning, "VideoDecoder_Register(): %s decoder init failed", + decvtbl->GetName ()); + return NULL; + } + + if (!newslot) + { + newslot = info; + newslot->used = true; + // make next one a term + info[1].builtin = false; + info[1].used = false; + info[1].ext = NULL; + } + + newslot->ext = fileext; + newslot->funcs = decvtbl; + + return newslot; +} + +void +VideoDecoder_Unregister (TFB_RegVideoDecoder* regdec) +{ + if (regdec < vd_decoders || regdec >= vd_decoders + MAX_REG_DECODERS || + !regdec->ext || !regdec->funcs) + { + log_add (log_Warning, "VideoDecoder_Unregister(): " + "Invalid or expired decoder passed"); + return; + } + + regdec->funcs->TermModule (); + regdec->ext = NULL; + regdec->funcs = NULL; +} + +const TFB_VideoDecoderFuncs* +VideoDecoder_Lookup (const char* fileext) +{ + TFB_RegVideoDecoder* info; + + for (info = vd_decoders; info->used && + (!info->ext || strcmp (info->ext, fileext) != 0); + ++info) + ; + return info->ext ? info->funcs : NULL; +} + +TFB_VideoDecoder* +VideoDecoder_Load (uio_DirHandle *dir, const char *filename) +{ + const char* pext; + TFB_RegVideoDecoder* info; + TFB_VideoDecoder* decoder; + + + if (!vd_inited) + return NULL; + + pext = strrchr (filename, '.'); + if (!pext) + { + log_add (log_Warning, "VideoDecoder_Load: Unknown file type"); + return NULL; + } + ++pext; + + for (info = vd_decoders; info->used && + (!info->ext || strcmp (info->ext, pext) != 0); + ++info) + ; + if (!info->ext) + { + log_add (log_Warning, "VideoDecoder_Load: Unsupported file type"); + return NULL; + } + + decoder = HCalloc (info->funcs->GetStructSize ()); + decoder->funcs = info->funcs; + if (!decoder->funcs->Init (decoder, &vd_vidfmt)) + { + log_add (log_Warning, "VideoDecoder_Load: " + "Cannot init '%s' decoder, code %d", + decoder->funcs->GetName (), + decoder->funcs->GetError (decoder)); + HFree (decoder); + return NULL; + } + + 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)) + { + log_add (log_Warning, "VideoDecoder_Load: " + "'%s' decoder did not load %s, code %d", + decoder->funcs->GetName (), 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, DWORD* shift, DWORD* loss) +{ + *shift = 0; + *loss = 8; + if (mask) + { + for (; !(mask & 1); mask >>= 1 ) + ++*shift; + + for (; (mask & 1); mask >>= 1 ) + --*loss; + } +} +// END: adapted from SDL |