summaryrefslogtreecommitdiff
path: root/src/libs/video/videodec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/video/videodec.c')
-rw-r--r--src/libs/video/videodec.c363
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