/* GStreamer divx decoder plugin * Copyright (C) 2003 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "config.h" #endif #include #include "gstdivxdec.h" #include /* elementfactory information */ GstElementDetails gst_divxdec_details = { "Divx decoder", "Codec/Video/Decoder", "Commercial", "Divx decoder based on divxdecore", VERSION, "Ronald Bultje ", "(C) 2003", }; GST_PAD_TEMPLATE_FACTORY(sink_template, "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_CAPS_NEW( "divxdec_sink", "video/x-divx", "divxversion", GST_PROPS_INT_RANGE(3, 5), "width", GST_PROPS_INT_RANGE(0, G_MAXINT), "height", GST_PROPS_INT_RANGE(0, G_MAXINT), "framerate", GST_PROPS_FLOAT_RANGE(0, G_MAXFLOAT) ) ) GST_PAD_TEMPLATE_FACTORY(src_template, "src", GST_PAD_SRC, GST_PAD_ALWAYS, gst_caps_new( "divxdec_src", "video/x-raw-yuv", GST_VIDEO_YUV_PAD_TEMPLATE_PROPS( GST_PROPS_LIST( GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')), GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')), GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','1','2')), GST_PROPS_FOURCC(GST_MAKE_FOURCC('U','Y','V','Y')) ) ) ), gst_caps_new( "divxdec_src_rgb1", "video/x-raw-rgb", GST_VIDEO_RGB_PAD_TEMPLATE_PROPS_24_32 ), gst_caps_new( "divxdec_src_rgb2", "video/x-raw-rgb", GST_VIDEO_RGB_PAD_TEMPLATE_PROPS_15_16 ) ) /* DivxDec signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0 /* FILL ME */ }; static void gst_divxdec_class_init (GstDivxDecClass *klass); static void gst_divxdec_init (GstDivxDec *divxdec); static void gst_divxdec_dispose (GObject *object); static void gst_divxdec_chain (GstPad *pad, GstBuffer *buf); static GstPadLinkReturn gst_divxdec_connect (GstPad *pad, GstCaps *vscapslist); static GstPadLinkReturn gst_divxdec_negotiate (GstDivxDec *divxdec); static GstElementClass *parent_class = NULL; /* static guint gst_divxdec_signals[LAST_SIGNAL] = { 0 }; */ static gchar * gst_divxdec_error (int errorcode) { gchar *error; switch (errorcode) { case DEC_OK: error = "No error"; break; case DEC_MEMORY: error = "Invalid memory"; break; case DEC_BAD_FORMAT: error = "Invalid format"; break; case DEC_INVALID_ARGUMENT: error = "Invalid argument"; break; case DEC_NOT_IMPLEMENTED: error = "Not implemented"; break; default: error = "Unknown error"; break; } return error; } GType gst_divxdec_get_type(void) { static GType divxdec_type = 0; if (!divxdec_type) { static const GTypeInfo divxdec_info = { sizeof(GstDivxDecClass), NULL, NULL, (GClassInitFunc) gst_divxdec_class_init, NULL, NULL, sizeof(GstDivxDec), 0, (GInstanceInitFunc) gst_divxdec_init, }; divxdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstDivxDec", &divxdec_info, 0); } return divxdec_type; } static void gst_divxdec_class_init (GstDivxDecClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; parent_class = g_type_class_ref(GST_TYPE_ELEMENT); gobject_class->dispose = gst_divxdec_dispose; } static void gst_divxdec_init (GstDivxDec *divxdec) { /* create the sink pad */ divxdec->sinkpad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET(sink_template), "sink"); gst_element_add_pad(GST_ELEMENT(divxdec), divxdec->sinkpad); gst_pad_set_chain_function(divxdec->sinkpad, gst_divxdec_chain); gst_pad_set_link_function(divxdec->sinkpad, gst_divxdec_connect); /* create the src pad */ divxdec->srcpad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET(src_template), "src"); gst_element_add_pad(GST_ELEMENT(divxdec), divxdec->srcpad); /* bitrate, etc. */ divxdec->width = divxdec->height = divxdec->csp = divxdec->bitcnt = -1; /* set divx handle to NULL */ divxdec->handle = NULL; } static void gst_divxdec_unset (GstDivxDec *divxdec) { if (divxdec->handle) { /* unref this instance */ decore(divxdec->handle, DEC_OPT_RELEASE, NULL, NULL); divxdec->handle = NULL; } } static gboolean gst_divxdec_setup (GstDivxDec *divxdec) { void *handle; DEC_INIT xinit; DivXBitmapInfoHeader output; int ret; /* initialize the handle */ memset(&xinit, 0, sizeof(DEC_INIT)); if ((ret = decore(&handle, DEC_OPT_INIT, &xinit, NULL)) != 0) { gst_element_error(GST_ELEMENT(divxdec), "Error initializing divx decoding library: %s (%d)", gst_divxdec_error(ret), ret); return FALSE; } /* we've got a handle now */ divxdec->handle = handle; /* initialise parameters, see divx documentation */ memset(&output, 0, sizeof(DivXBitmapInfoHeader)); output.biSize = sizeof(DivXBitmapInfoHeader); output.biWidth = divxdec->width; output.biHeight = divxdec->height; output.biBitCount = divxdec->bitcnt; output.biCompression = divxdec->csp; if ((ret = decore(divxdec->handle, DEC_OPT_SETOUT, &output, NULL)) != 0) { gst_element_error(GST_ELEMENT(divxdec), "Error setting output format: %s (%d)", gst_divxdec_error(ret), ret); gst_divxdec_unset(divxdec); return FALSE; } return TRUE; } static void gst_divxdec_dispose (GObject *object) { GstDivxDec *divxdec = GST_DIVXDEC(object); gst_divxdec_unset(divxdec); } static void gst_divxdec_chain (GstPad *pad, GstBuffer *buf) { GstDivxDec *divxdec; GstBuffer *outbuf; DEC_FRAME xframe; int ret; g_return_if_fail(pad != NULL); g_return_if_fail(GST_IS_PAD(pad)); g_return_if_fail(buf != NULL); divxdec = GST_DIVXDEC(GST_OBJECT_PARENT(pad)); if (!divxdec->handle) { if (gst_divxdec_negotiate(divxdec) <= 0) { gst_element_error(GST_ELEMENT(divxdec), "No format set - aborting"); gst_buffer_unref(buf); return; } } outbuf = gst_buffer_new_and_alloc(divxdec->width * divxdec->height * divxdec->bpp / 8); GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf); GST_BUFFER_SIZE(outbuf) = divxdec->width * divxdec->height * divxdec->bpp / 8; /* encode and so ... */ xframe.bitstream = (void *) GST_BUFFER_DATA(buf); xframe.bmp = (void *) GST_BUFFER_DATA(outbuf); xframe.length = GST_BUFFER_SIZE(buf); xframe.stride = 0; xframe.render_flag = 1; if ((ret = decore(divxdec->handle, DEC_OPT_FRAME, &xframe, NULL))) { gst_element_error(GST_ELEMENT(divxdec), "Error decoding divx frame: %s (%d)", gst_divxdec_error(ret), ret); gst_buffer_unref(buf); return; } gst_pad_push(divxdec->srcpad, outbuf); gst_buffer_unref(buf); } static GstPadLinkReturn gst_divxdec_negotiate (GstDivxDec *divxdec) { GstPadLinkReturn ret; GstCaps *caps; struct { guint32 fourcc; gint depth, bpp; guint32 csp; gint bitcnt; } fmt_list[] = { { GST_MAKE_FOURCC('Y','U','Y','2'), 16, 16, GST_MAKE_FOURCC('Y','U','Y','2'), 0 }, { GST_MAKE_FOURCC('U','Y','V','Y'), 16, 16, GST_MAKE_FOURCC('U','Y','V','Y'), 0 }, { GST_MAKE_FOURCC('I','4','2','0'), 12, 12, GST_MAKE_FOURCC('I','4','2','0'), 0 }, { GST_MAKE_FOURCC('Y','V','1','2'), 12, 12, GST_MAKE_FOURCC('Y','V','1','2'), 0 }, { GST_MAKE_FOURCC('R','G','B',' '), 32, 32, #if (G_BYTE_ORDER == G_BIG_ENDIAN) GST_MAKE_FOURCC('A','B','G','R'), 32 }, #else 0, 32 }, #endif { GST_MAKE_FOURCC('R','G','B',' '), 24, 24, #if (G_BYTE_ORDER == G_BIG_ENDIAN) GST_MAKE_FOURCC('A','B','G','R'), 24 }, #else 0, 24 }, #endif { GST_MAKE_FOURCC('R','G','B',' '), 16, 16, 3, 16 }, { GST_MAKE_FOURCC('R','G','B',' '), 15, 16, 0, 16 }, { 0, 0, 0, 0, 0 } }; gint i; for (i = 0; fmt_list[i].fourcc != 0; i++) { divxdec->csp = fmt_list[i].csp; /* try making a caps to set on the other side */ if (fmt_list[i].fourcc == GST_MAKE_FOURCC('R','G','B',' ')) { guint32 r_mask = 0, b_mask = 0, g_mask = 0; gint endianness = 0; switch (fmt_list[i].depth) { case 15: endianness = G_BYTE_ORDER; r_mask = 0xf800; g_mask = 0x07c0; b_mask = 0x003e; break; case 16: endianness = G_BYTE_ORDER; r_mask = 0xf800; g_mask = 0x07e0; b_mask = 0x001f; break; case 24: endianness = G_BIG_ENDIAN; r_mask = R_MASK_24; g_mask = G_MASK_24; b_mask = B_MASK_24; break; case 32: endianness = G_BIG_ENDIAN; r_mask = R_MASK_32; g_mask = G_MASK_32; b_mask = B_MASK_32; break; } caps = GST_CAPS_NEW("divxdec_src_pad_rgb", "video/x-raw-rgb", "width", GST_PROPS_INT(divxdec->width), "height", GST_PROPS_INT(divxdec->height), "framerate", GST_PROPS_FLOAT(divxdec->fps), "depth", GST_PROPS_INT(fmt_list[i].depth), "bpp", GST_PROPS_INT(fmt_list[i].bpp), "endianness", GST_PROPS_INT(endianness), "red_mask", GST_PROPS_INT(r_mask), "green_mask", GST_PROPS_INT(g_mask), "blue_mask", GST_PROPS_INT(b_mask)); } else { caps = GST_CAPS_NEW("divxdec_src_pad_yuv", "video/x-raw-yuv", "width", GST_PROPS_INT(divxdec->width), "height", GST_PROPS_INT(divxdec->height), "framerate", GST_PROPS_FLOAT(divxdec->fps), "format", GST_PROPS_FOURCC(fmt_list[i].fourcc)); } if ((ret = gst_pad_try_set_caps(divxdec->srcpad, caps)) > 0) { divxdec->csp = fmt_list[i].csp; divxdec->bpp = fmt_list[i].bpp; divxdec->bitcnt = fmt_list[i].bitcnt; if (gst_divxdec_setup(divxdec)) return GST_PAD_LINK_OK; } else if (ret == GST_PAD_LINK_DELAYED) { return ret; /* trying more is useless */ } } /* if we got here - it's not good */ return GST_PAD_LINK_REFUSED; } static GstPadLinkReturn gst_divxdec_connect (GstPad *pad, GstCaps *vscaps) { GstDivxDec *divxdec; divxdec = GST_DIVXDEC(gst_pad_get_parent (pad)); /* if there's something old around, remove it */ if (divxdec->handle) { gst_divxdec_unset(divxdec); } /* we are not going to act on variable caps */ if (!GST_CAPS_IS_FIXED(vscaps)) return GST_PAD_LINK_DELAYED; /* if we get here, we know the input is divx. we * only need to bother with the output colorspace */ gst_caps_get_int(vscaps, "width", &divxdec->width); gst_caps_get_int(vscaps, "height", &divxdec->height); gst_caps_get_float(vscaps, "framerate", &divxdec->fps); return gst_divxdec_negotiate(divxdec); } static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; int lib_version; lib_version = decore(NULL, DEC_OPT_VERSION, 0, 0); if (lib_version != DECORE_VERSION) { g_warning("Version mismatch! This plugin was compiled for " "DivX version %d, while your library has version %d!", DECORE_VERSION, lib_version); } /* create an elementfactory for the v4lmjpegsrcparse element */ factory = gst_element_factory_new("divxdec", GST_TYPE_DIVXDEC, &gst_divxdec_details); g_return_val_if_fail(factory != NULL, FALSE); /* add pad templates */ gst_element_factory_add_pad_template(factory, GST_PAD_TEMPLATE_GET(sink_template)); gst_element_factory_add_pad_template(factory, GST_PAD_TEMPLATE_GET(src_template)); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); gst_element_factory_set_rank(factory, GST_ELEMENT_RANK_PRIMARY); return TRUE; } GstPluginDesc plugin_desc = { GST_VERSION_MAJOR, GST_VERSION_MINOR, "divxdec", plugin_init };