diff options
Diffstat (limited to 'gst/real')
-rw-r--r-- | gst/real/Makefile.am | 4 | ||||
-rw-r--r-- | gst/real/gstreal.c | 791 | ||||
-rw-r--r-- | gst/real/gstrealvideodec.c | 878 | ||||
-rw-r--r-- | gst/real/gstrealvideodec.h | 90 |
4 files changed, 979 insertions, 784 deletions
diff --git a/gst/real/Makefile.am b/gst/real/Makefile.am index d1d924c8..8096c579 100644 --- a/gst/real/Makefile.am +++ b/gst/real/Makefile.am @@ -1,7 +1,9 @@ plugin_LTLIBRARIES = libgstreal.la -libgstreal_la_SOURCES = gstreal.c +libgstreal_la_SOURCES = gstrealvideodec.c gstreal.c libgstreal_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstreal_la_LIBADD = $(GST_BASE_LIBS) libgstreal_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstrealvideodec.h
\ No newline at end of file diff --git a/gst/real/gstreal.c b/gst/real/gstreal.c index a6ba4e45..afb28d0e 100644 --- a/gst/real/gstreal.c +++ b/gst/real/gstreal.c @@ -1,6 +1,6 @@ -/* RealVideo wrapper plugin +/* Real wrapper plugin * - * Copyright (C) 2005 Lutz Mueller <lutz@topfrose.de> + * Copyright (C) Edward Hervey <bilboed@bilboed.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,792 +18,17 @@ * Boston, MA 02111-1307, USA. */ -#include <config.h> +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif -#include <gst/gstelement.h> -#include <gst/gsterror.h> -#include <gst/gstinfo.h> -#include <gst/gstplugin.h> -#include <gst/gstutils.h> -#include <gst/gstversion.h> - -#include <gst/base/gstadapter.h> - -#include <dlfcn.h> -#include <string.h> - -#define GST_TYPE_REAL_DEC (gst_realdec_get_type()) -#define GST_REAL_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_REAL_DEC,GstRealDec)) -#define GST_REAL_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_REAL_DEC,GstRealDecClass)) - -GST_DEBUG_CATEGORY_STATIC (realdec_debug); -#define GST_CAT_DEFAULT realdec_debug - -typedef struct _GstRealDec GstRealDec; -typedef struct _GstRealDecClass GstRealDecClass; - -static GstElementDetails realdec_details = -GST_ELEMENT_DETAILS ("RealVideo decoder", - "Codec/Decoder", "Decoder for RealVideo streams", - "Lutz Mueller <lutz@topfrose.de>"); - -static GstStaticPadTemplate snk_t = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-pn-realvideo, " "rmversion = (int) [ 2, 4 ]")); -static GstStaticPadTemplate src_t = -GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-yuv, " - "format = (fourcc) I420, " - "framerate = (fraction) [0/1, MAX], " - "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ] ")); - -#define DEFAULT_PATH_RV20 "/usr/lib/win32/drv2.so.6.0" -#define DEFAULT_PATH_RV30 "/usr/lib/win32/drv3.so.6.0" -#define DEFAULT_PATH_RV40 "/usr/lib/win32/drv4.so.6.0" - -enum -{ - PROP_0, - PROP_PATH_RV20, - PROP_PATH_RV30, - PROP_PATH_RV40 -}; - -typedef enum -{ - GST_REAL_DEC_VERSION_2 = 2, - GST_REAL_DEC_VERSION_3 = 3, - GST_REAL_DEC_VERSION_4 = 4 -} GstRealDecVersion; - -struct _GstRealDec -{ - GstElement parent; - - GstPad *src, *snk; - - /* Caps */ - GstRealDecVersion version; - guint width, height; - gint format, subformat; - gint framerate_num, framerate_denom; - - /* Variables needed for fixing timestamps. */ - GstClockTime next_ts, last_ts; - guint16 next_seq, last_seq; - - /* Hooks */ - gpointer handle; - guint32 (*custom_message) (guint32 *, gpointer); - guint32 (*free) (gpointer); - guint32 (*init) (gpointer, gpointer); - guint32 (*transform) (gchar *, gchar *, guint32 *, guint32 *, gpointer); - - /* Used by the REAL library. */ - gpointer context; - - /* State */ - GstAdapter *adapter; - guint8 seqnum, subseq; - guint16 length; - guint32 fragment_count; - guint32 fragments[256]; - - /* Properties */ - gchar *path_rv20, *path_rv30, *path_rv40; -}; - -struct _GstRealDecClass -{ - GstElementClass parent_class; -}; - -GST_BOILERPLATE (GstRealDec, gst_realdec, GstElement, GST_TYPE_ELEMENT); - -static GstFlowReturn -gst_realdec_alloc_buffer (GstRealDec * dec, GstClockTime timestamp, - GstBuffer ** buf) -{ - GstFlowReturn ret; - const guint8 *b; - guint8 frame_type; - guint16 seq; - GstClockTime ts = timestamp; - - GST_LOG_OBJECT (dec, "timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (ts)); - - /* Fix timestamp. */ - b = gst_adapter_peek (dec->adapter, 4); - switch (dec->version) { - case GST_REAL_DEC_VERSION_2: - { - - /* - * Bit 1- 2: frame type - * Bit 3- 9: ? - * Bit 10-22: sequence number - * Bit 23-32: ? - */ - frame_type = (b[0] >> 6) & 0x03; - seq = ((b[1] & 0x7f) << 6) + ((b[2] & 0xfc) >> 2); - break; - } - case GST_REAL_DEC_VERSION_3: - { - /* - * Bit 1- 2: ? - * Bit 3: skip packet if 1 - * Bit 4- 5: frame type - * Bit 6-12: ? - * Bit 13-25: sequence number - * Bit 26-32: ? - */ - frame_type = (b[0] >> 3) & 0x03; - seq = ((b[1] & 0x0f) << 9) + (b[2] << 1) + ((b[3] & 0x80) >> 7); - break; - } - case GST_REAL_DEC_VERSION_4: - { - /* - * Bit 1: skip packet if 1 - * Bit 2- 3: frame type - * Bit 4-13: ? - * Bit 14-26: sequence number - * Bit 27-32: ? - */ - frame_type = (b[0] >> 5) & 0x03; - seq = ((b[1] & 0x07) << 10) + (b[2] << 2) + ((b[3] & 0xc0) >> 6); - break; - } - default: - goto unknown_version; - } - - GST_LOG_OBJECT (dec, "frame_type:%d", frame_type); - - switch (frame_type) { - case 0: - case 1: - { - /* I frame */ - timestamp = dec->next_ts; - dec->last_ts = dec->next_ts; - dec->next_ts = ts; - dec->last_seq = dec->next_seq; - dec->next_seq = seq; - - break; - } - case 2: - { - /* P frame */ - timestamp = dec->last_ts = dec->next_ts; - if (seq < dec->next_seq) - dec->next_ts += (seq + 0x2000 - dec->next_seq) * GST_MSECOND; - else - dec->next_ts += (seq - dec->next_seq) * GST_MSECOND; - dec->last_seq = dec->next_seq; - dec->next_seq = seq; - break; - } - case 3: - { - /* B frame */ - if (seq < dec->last_seq) { - timestamp = (seq + 0x2000 - dec->last_seq) * GST_MSECOND + dec->last_ts; - } else { - timestamp = (seq - dec->last_seq) * GST_MSECOND + dec->last_ts; - } - - break; - } - default: - goto unknown_frame_type; - } - - ret = gst_pad_alloc_buffer (dec->src, GST_BUFFER_OFFSET_NONE, - dec->width * dec->height * 3 / 2, GST_PAD_CAPS (dec->src), buf); - - if (ret == GST_FLOW_OK) - GST_BUFFER_TIMESTAMP (*buf) = timestamp; - - return ret; - - /* Errors */ -unknown_version: - { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Unknown version: %i.", dec->version), (NULL)); - return GST_FLOW_ERROR; - } - -unknown_frame_type: - { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Unknown frame type."), (NULL)); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_realdec_decode (GstRealDec * dec, GstBuffer * in, guint offset) -{ - guint32 result; - GstBuffer *out = NULL; - GstFlowReturn ret; - guint8 *data, hdr_subseq, hdr_seqnum; - guint32 hdr_offset, hdr_length; - guint16 n; - gboolean bres; - guint8 *buf = GST_BUFFER_DATA (in) + offset; - guint len = GST_BUFFER_SIZE (in) - offset; - GstClockTime timestamp = GST_BUFFER_TIMESTAMP (in); - - GST_LOG_OBJECT (dec, - "Got buffer %p with timestamp %" GST_TIME_FORMAT " offset %d", in, - GST_TIME_ARGS (timestamp), offset); - - /* Subsequence */ - if (len < 1) - goto not_enough_data; - if (*buf != 0x40 && *buf != 0x41 && *buf != 0x42 && - *buf != 0x43 && *buf != 0x44 && *buf != 0x45) { - hdr_subseq = *buf & 0x7f; - buf++; - len--; - if (hdr_subseq == 64) - hdr_subseq = 1; - } else { - hdr_subseq = 1; - } - - /* Length */ - if (len < 2) - goto not_enough_data; - hdr_length = GST_READ_UINT16_BE (buf); - if (!(hdr_length & 0xc000)) { - if (len < 4) - goto not_enough_data; - hdr_length = GST_READ_UINT32_BE (buf); - buf += 4; - len -= 4; - } else { - hdr_length &= 0x3fff; - buf += 2; - len -= 2; - } - - /* Offset */ - if (len < 2) - goto not_enough_data; - hdr_offset = GST_READ_UINT16_BE (buf); - if (!(hdr_offset & 0xc000)) { - if (len < 4) - goto not_enough_data; - hdr_offset = GST_READ_UINT32_BE (buf); - buf += 4; - len -= 4; - } else { - hdr_offset &= 0x3fff; - buf += 2; - len -= 2; - } - - /* Sequence number */ - if (len < 1) - goto not_enough_data; - hdr_seqnum = *buf; - buf++; - len--; - if (len < 1) - goto not_enough_data; - - /* Verify the sequence numbers. */ - if (hdr_subseq == 1) { - n = gst_adapter_available_fast (dec->adapter); - if (n > 0) { - GST_DEBUG_OBJECT (dec, "Dropping data for sequence %i " - "because we are already receiving data for sequence %i.", - dec->seqnum, hdr_seqnum); - gst_adapter_clear (dec->adapter); - } - dec->seqnum = hdr_seqnum; - dec->length = hdr_length; - dec->fragment_count = 1; - } else { - if (dec->seqnum != hdr_seqnum) { - GST_DEBUG_OBJECT (dec, "Expected sequence %i, got sequence %i " - "(subseq=%i). Dropping packet.", dec->seqnum, hdr_seqnum, hdr_subseq); - return GST_FLOW_OK; - } else if (dec->subseq + 1 != hdr_subseq) { - GST_DEBUG_OBJECT (dec, "Expected subsequence %i, got subseqence %i. " - "Dropping packet.", dec->subseq + 1, hdr_subseq); - return GST_FLOW_OK; - } - dec->fragment_count++; - } - dec->subseq = hdr_subseq; - - /* Remember the offset */ - if (sizeof (dec->fragments) < 2 * (dec->fragment_count - 1) + 1) - goto too_many_fragments; - dec->fragments[2 * (dec->fragment_count - 1)] = 1; - dec->fragments[2 * (dec->fragment_count - 1) + 1] = - gst_adapter_available (dec->adapter); - - /* Some buffers need to be skipped. */ - if (((dec->version == GST_REAL_DEC_VERSION_3) && (*buf & 0x20)) || - ((dec->version == GST_REAL_DEC_VERSION_4) && (*buf & 0x80))) { - dec->fragment_count--; - dec->length -= len; - } else { - gst_adapter_push (dec->adapter, - gst_buffer_create_sub (in, GST_BUFFER_SIZE (in) - len, len)); - } - - /* All bytes received? */ - n = gst_adapter_available (dec->adapter); - GST_LOG_OBJECT (dec, "We know have %d bytes, and we need %d", n, dec->length); - if (dec->length <= n) { - guint32 tin[6] = { 0, 0, 0, 0, 0, 0 } - , tout[5]; - - if ((ret = gst_realdec_alloc_buffer (dec, timestamp, &out)) != GST_FLOW_OK) - return ret; - - /* Decode */ - tin[0] = dec->length; - tin[2] = dec->fragment_count - 1; - tin[3] = (guint32) dec->fragments; - tin[5] = GST_BUFFER_TIMESTAMP (out); - data = gst_adapter_take (dec->adapter, dec->length); - result = dec->transform ( - (gchar *) data, - (gchar *) GST_BUFFER_DATA (out), tin, tout, dec->context); - g_free (data); - if (result) - goto could_not_transform; - - /* Check for new dimensions */ - if (tout[0] && ((dec->width != tout[3]) || (dec->height != tout[4]))) { - GstCaps *caps = gst_caps_copy (GST_PAD_CAPS (dec->src)); - GstStructure *s = gst_caps_get_structure (caps, 0); - - GST_DEBUG_OBJECT (dec, "New dimensions: %" - G_GUINT32_FORMAT " x %" G_GUINT32_FORMAT, tout[3], tout[4]); - gst_structure_set (s, "width", G_TYPE_LONG, tout[3], - "height", G_TYPE_LONG, tout[4], NULL); - bres = gst_pad_set_caps (dec->src, caps); - gst_caps_unref (caps); - if (!bres) - goto new_dimensions_failed; - dec->width = tout[3]; - dec->height = tout[4]; - } - - GST_DEBUG_OBJECT (dec, - "Pushing out buffer with timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out))); - - if ((ret = gst_pad_push (dec->src, out)) != GST_FLOW_OK) - goto could_not_push; - - n = gst_adapter_available (dec->adapter); - if (n > 0) { - GST_LOG_OBJECT (dec, "Data left in the adapter: %d", n); - in = gst_adapter_take_buffer (dec->adapter, n); - ret = gst_realdec_decode (dec, in, 0); - gst_buffer_unref (in); - if (ret != GST_FLOW_OK) - return ret; - } - } - - return GST_FLOW_OK; - - /* Errors */ -not_enough_data: - { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Not enough data."), (NULL)); - return GST_FLOW_ERROR; - } - -too_many_fragments: - { - gst_buffer_unref (in); - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Got more fragments (%i) than can be handled (%i).", - dec->fragment_count, sizeof (dec->fragments)), (NULL)); - return GST_FLOW_ERROR; - } - -could_not_transform: - { - gst_buffer_unref (out); - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Could not decode buffer: %" G_GUINT32_FORMAT, result), (NULL)); - return GST_FLOW_ERROR; - } - -new_dimensions_failed: - { - gst_buffer_unref (out); - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Could not set new dimensions."), (NULL)); - return GST_FLOW_ERROR; - } - -could_not_push: - { - GST_DEBUG_OBJECT (dec, "Could not push buffer: %s", - gst_flow_get_name (ret)); - return ret; - } -} - -static GstFlowReturn -gst_realdec_chain (GstPad * pad, GstBuffer * in) -{ - GstRealDec *dec = GST_REAL_DEC (GST_PAD_PARENT (pad)); - guint8 flags, *buf = GST_BUFFER_DATA (in); - guint len = GST_BUFFER_SIZE (in); - GstFlowReturn ret; - - /* Flags */ - if (len < 1) - goto not_enough_data; - flags = *buf; - buf++; - len--; - - if (flags == 0x40) { - GST_DEBUG_OBJECT (dec, "Don't know how to handle buffer of type 0x40 " - "(size=%i).", len); - return GST_FLOW_OK; - } - - ret = gst_realdec_decode (dec, in, 1); - gst_buffer_unref (in); - if (ret != GST_FLOW_OK) - return ret; - return GST_FLOW_OK; - - /* Errors */ -not_enough_data: - { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Not enough data."), (NULL)); - gst_buffer_unref (in); - return GST_FLOW_ERROR; - } -} - -static gboolean -gst_realdec_activate_push (GstPad * pad, gboolean active) -{ - GstRealDec *dec = GST_REAL_DEC (GST_PAD_PARENT (pad)); - - gst_adapter_clear (dec->adapter); - - return TRUE; -} - -static gboolean -gst_realdec_setcaps (GstPad * pad, GstCaps * caps) -{ - GstRealDec *dec = GST_REAL_DEC (GST_PAD_PARENT (pad)); - GstStructure *s = gst_caps_get_structure (caps, 0); - gchar *path; - gint version, res; - gchar data[36]; - gboolean bres; - const GValue *v; - - if (!gst_structure_get_int (s, "rmversion", &version) || - !gst_structure_get_int (s, "width", (gint *) & dec->width) || - !gst_structure_get_int (s, "height", (gint *) & dec->height) || - !gst_structure_get_int (s, "format", &dec->format) || - !gst_structure_get_int (s, "subformat", &dec->subformat) || - !gst_structure_get_fraction (s, "framerate", &dec->framerate_num, - &dec->framerate_denom)) - goto missing_keys; - dec->version = version; - - GST_LOG_OBJECT (dec, "Setting version to %d", version); - - switch (dec->version) { - case GST_REAL_DEC_VERSION_2: - path = dec->path_rv20 ? dec->path_rv20 : DEFAULT_PATH_RV20; - break; - case GST_REAL_DEC_VERSION_3: - path = dec->path_rv30 ? dec->path_rv30 : DEFAULT_PATH_RV30; - break; - case GST_REAL_DEC_VERSION_4: - path = dec->path_rv40 ? dec->path_rv40 : DEFAULT_PATH_RV40; - break; - default: - goto unknown_version; - } - - if (dec->handle) - dlclose (dec->handle); - dec->handle = dlopen (path, RTLD_LAZY); - if (!dec->handle) - goto could_not_open; - dec->custom_message = dlsym (dec->handle, "RV20toYUV420CustomMessage"); - dec->free = dlsym (dec->handle, "RV20toYUV420Free"); - dec->init = dlsym (dec->handle, "RV20toYUV420Init"); - dec->transform = dlsym (dec->handle, "RV20toYUV420Transform"); - if (!(dec->custom_message && dec->free && dec->init && dec->transform)) - goto could_not_load; - - /* Initialize REAL driver. */ - GST_WRITE_UINT16_LE (data + 0, 11); - GST_WRITE_UINT16_LE (data + 2, dec->width); - GST_WRITE_UINT16_LE (data + 4, dec->height); - GST_WRITE_UINT16_LE (data + 6, 0); - GST_WRITE_UINT32_LE (data + 8, 0); - GST_WRITE_UINT32_LE (data + 12, dec->subformat); - GST_WRITE_UINT32_LE (data + 16, 0); - GST_WRITE_UINT32_LE (data + 20, dec->format); - res = dec->init (&data, &dec->context); - if (res) - goto could_not_initialize; - - if ((v = gst_structure_get_value (s, "codec_data"))) { - GstBuffer *buf = g_value_peek_pointer (v); - guint32 *msg = g_new0 (guint32, 11 + GST_BUFFER_SIZE (buf)); - guint i; - - if (!msg) - goto could_not_allocate; - msg[0] = 0x24; - msg[1] = 1 + ((dec->subformat >> 16) & 7); - msg[2] = (guint32) & msg[9]; - msg[9] = dec->width; - msg[10] = dec->height; - for (i = 0; i < GST_BUFFER_SIZE (buf); i++) - msg[i + 11] = 4 * GST_BUFFER_DATA (buf)[i]; - res = dec->custom_message (msg, dec->context); - g_free (msg); - if (res) - goto could_not_send_message; - } - - caps = gst_caps_new_simple ("video/x-raw-yuv", - "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), - "framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom, - "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL); - bres = gst_pad_set_caps (GST_PAD (dec->src), caps); - gst_caps_unref (caps); - if (!bres) - goto could_not_set_caps; - - return TRUE; - -missing_keys: - { - GST_DEBUG_OBJECT (dec, "Could not find all necessary keys in structure."); - return FALSE; - } - -unknown_version: - { - GST_DEBUG_OBJECT (dec, "Cannot handle version %i.", version); - return FALSE; - } - -could_not_open: - { - GST_DEBUG_OBJECT (dec, "Could not open library '%s'.", path); - return FALSE; - } - -could_not_load: - { - dlclose (dec->handle); - dec->handle = NULL; - GST_DEBUG_OBJECT (dec, "Could not load all symbols."); - return FALSE; - } - -could_not_initialize: - { - dlclose (dec->handle); - dec->handle = NULL; - GST_DEBUG_OBJECT (dec, "Initialization of REAL driver failed (%i).", res); - return FALSE; - } - -could_not_allocate: - { - dlclose (dec->handle); - dec->handle = NULL; - GST_DEBUG_OBJECT (dec, "Could not allocate memory."); - return FALSE; - } - -could_not_send_message: - { - dlclose (dec->handle); - dec->handle = NULL; - GST_DEBUG_OBJECT (dec, "Failed to send custom message needed for " - "initialization (%i).", res); - return FALSE; - } - -could_not_set_caps: - { - dlclose (dec->handle); - dec->handle = NULL; - GST_DEBUG_OBJECT (dec, "Could not convince peer to accept dimensions " - "%i x %i.", dec->width, dec->height); - return FALSE; - } -} - -static void -gst_realdec_init (GstRealDec * dec, GstRealDecClass * klass) -{ - dec->snk = - gst_pad_new_from_template (gst_static_pad_template_get (&snk_t), "sink"); - gst_pad_set_setcaps_function (dec->snk, gst_realdec_setcaps); - gst_pad_set_chain_function (dec->snk, gst_realdec_chain); - gst_pad_set_activatepush_function (dec->snk, gst_realdec_activate_push); - gst_element_add_pad (GST_ELEMENT (dec), dec->snk); - - dec->src = - gst_pad_new_from_template (gst_static_pad_template_get (&src_t), "src"); - gst_element_add_pad (GST_ELEMENT (dec), dec->src); - - dec->adapter = g_object_new (GST_TYPE_ADAPTER, NULL); -} - -static void -gst_realdec_base_init (gpointer g_class) -{ - GstElementClass *ec = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&snk_t)); - gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&src_t)); - gst_element_class_set_details (ec, &realdec_details); -} - -static void -gst_realdec_finalize (GObject * object) -{ - GstRealDec *dec = GST_REAL_DEC (object); - - if (dec->context) { - dec->free (dec->context); - dec->context = NULL; - } - if (dec->adapter) { - g_object_unref (G_OBJECT (dec->adapter)); - dec->adapter = NULL; - } - if (dec->handle) { - dlclose (dec->handle); - dec->handle = NULL; - } - - if (dec->path_rv20) { - g_free (dec->path_rv20); - dec->path_rv20 = NULL; - } - if (dec->path_rv30) { - g_free (dec->path_rv30); - dec->path_rv30 = NULL; - } - if (dec->path_rv40) { - g_free (dec->path_rv40); - dec->path_rv40 = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_realdec_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstRealDec *dec = GST_REAL_DEC (object); - - /* Changing the location of the .so supposes it's not being done - * in a state greater than READY ! - */ - - switch (prop_id) { - case PROP_PATH_RV20: - if (dec->path_rv20) - g_free (dec->path_rv20); - dec->path_rv20 = g_value_dup_string (value); - break; - case PROP_PATH_RV30: - if (dec->path_rv30) - g_free (dec->path_rv30); - dec->path_rv30 = g_value_dup_string (value); - break; - case PROP_PATH_RV40: - if (dec->path_rv40) - g_free (dec->path_rv40); - dec->path_rv40 = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_realdec_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstRealDec *dec = GST_REAL_DEC (object); - - switch (prop_id) { - case PROP_PATH_RV20: - g_value_set_string (value, dec->path_rv20 ? dec->path_rv20 : - DEFAULT_PATH_RV20); - break; - case PROP_PATH_RV30: - g_value_set_string (value, dec->path_rv30 ? dec->path_rv30 : - DEFAULT_PATH_RV30); - break; - case PROP_PATH_RV40: - g_value_set_string (value, dec->path_rv40 ? dec->path_rv40 : - DEFAULT_PATH_RV40); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_realdec_class_init (GstRealDecClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = gst_realdec_set_property; - object_class->get_property = gst_realdec_get_property; - object_class->finalize = gst_realdec_finalize; - - g_object_class_install_property (object_class, PROP_PATH_RV20, - g_param_spec_string ("path_rv20", "Path to rv20 driver", - "Path to rv20 driver", DEFAULT_PATH_RV20, G_PARAM_READWRITE)); - g_object_class_install_property (object_class, PROP_PATH_RV30, - g_param_spec_string ("path_rv30", "Path to rv30 driver", - "Path to rv30 driver", DEFAULT_PATH_RV30, G_PARAM_READWRITE)); - g_object_class_install_property (object_class, PROP_PATH_RV40, - g_param_spec_string ("path_rv40", "Path to rv40 driver", - "Path to rv40 driver", DEFAULT_PATH_RV40, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT (realdec_debug, "realdec", 0, "REAL decoder"); -} +#include "gstrealvideodec.h" static gboolean plugin_init (GstPlugin * p) { - if (!gst_element_register (p, "realdec", GST_RANK_PRIMARY, GST_TYPE_REAL_DEC)) + if (!gst_element_register (p, "realvideodec", GST_RANK_PRIMARY, + GST_TYPE_REAL_VIDEO_DEC)) return FALSE; return TRUE; } diff --git a/gst/real/gstrealvideodec.c b/gst/real/gstrealvideodec.c new file mode 100644 index 00000000..3dfab829 --- /dev/null +++ b/gst/real/gstrealvideodec.c @@ -0,0 +1,878 @@ +/* RealVideo wrapper plugin + * + * Copyright (C) 2005 Lutz Mueller <lutz@topfrose.de> + * 2006 Edward Hervey <bilboed@bilboed.com> + * + * 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 +#include "config.h" +#endif + +#include "gstrealvideodec.h" + +#include <dlfcn.h> +#include <string.h> + +GST_DEBUG_CATEGORY_STATIC (realvideode_debug); +#define GST_CAT_DEFAULT realvideode_debug + +static GstElementDetails realvideode_details = +GST_ELEMENT_DETAILS ("RealVideo decoder", + "Codec/Decoder", "Decoder for RealVideo streams", + "Lutz Mueller <lutz@topfrose.de>"); + +static GstStaticPadTemplate snk_t = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-pn-realvideo, " "rmversion = (int) [ 2, 4 ]")); +static GstStaticPadTemplate src_t = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv, " + "format = (fourcc) I420, " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ] ")); + +#ifdef HAVE_CPU_I386 +#define DEFAULT_PATH_RV20 "/usr/lib/win32/drv2.so.6.0" +#define DEFAULT_PATH_RV30 "/usr/lib/win32/drv3.so.6.0" +#define DEFAULT_PATH_RV40 "/usr/lib/win32/drv4.so.6.0" +#endif +#ifdef HAVE_CPU_X86_64 +#define DEFAULT_PATH_RV20 "/usr/lib/drvc.so" +#define DEFAULT_PATH_RV30 "/usr/lib/drvc.so" +#define DEFAULT_PATH_RV40 "/usr/lib/drvc.so" +#endif + +enum +{ + PROP_0, + PROP_PATH_RV20, + PROP_PATH_RV30, + PROP_PATH_RV40 +}; + + +GST_BOILERPLATE (GstRealVideoDec, gst_real_video_dec, GstElement, + GST_TYPE_ELEMENT); + +static gboolean open_library (GstRealVideoDec * dec); +static void close_library (GstRealVideoDec * dec); + +typedef struct +{ + guint32 datalen; + gint32 interpolate; + gint32 nfragments; + gpointer fragments; + guint32 flags; + guint32 timestamp; +} RVInData; + +typedef struct +{ + guint32 frames; + guint32 notes; + guint32 timestamp; + guint32 width; + guint32 height; +} RVOutData; + +static GstFlowReturn +gst_real_video_dec_alloc_buffer (GstRealVideoDec * dec, GstClockTime timestamp, + GstBuffer ** buf) +{ + GstFlowReturn ret; + const guint8 *b; + guint8 frame_type; + guint16 seq; + GstClockTime ts = timestamp; + + GST_LOG_OBJECT (dec, "timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (ts)); + + /* Fix timestamp. */ + b = gst_adapter_peek (dec->adapter, 4); + switch (dec->version) { + case GST_REAL_VIDEO_DEC_VERSION_2: + { + + /* + * Bit 1- 2: frame type + * Bit 3- 9: ? + * Bit 10-22: sequence number + * Bit 23-32: ? + */ + frame_type = (b[0] >> 6) & 0x03; + seq = ((b[1] & 0x7f) << 6) + ((b[2] & 0xfc) >> 2); + break; + } + case GST_REAL_VIDEO_DEC_VERSION_3: + { + /* + * Bit 1- 2: ? + * Bit 3: skip packet if 1 + * Bit 4- 5: frame type + * Bit 6-12: ? + * Bit 13-25: sequence number + * Bit 26-32: ? + */ + frame_type = (b[0] >> 3) & 0x03; + seq = ((b[1] & 0x0f) << 9) + (b[2] << 1) + ((b[3] & 0x80) >> 7); + break; + } + case GST_REAL_VIDEO_DEC_VERSION_4: + { + /* + * Bit 1: skip packet if 1 + * Bit 2- 3: frame type + * Bit 4-13: ? + * Bit 14-26: sequence number + * Bit 27-32: ? + */ + frame_type = (b[0] >> 5) & 0x03; + seq = ((b[1] & 0x07) << 10) + (b[2] << 2) + ((b[3] & 0xc0) >> 6); + break; + } + default: + goto unknown_version; + } + + GST_LOG_OBJECT (dec, "frame_type:%d", frame_type); + + switch (frame_type) { + case 0: + case 1: + { + /* I frame */ + timestamp = dec->next_ts; + dec->last_ts = dec->next_ts; + dec->next_ts = ts; + dec->last_seq = dec->next_seq; + dec->next_seq = seq; + + break; + } + case 2: + { + /* P frame */ + timestamp = dec->last_ts = dec->next_ts; + if (seq < dec->next_seq) + dec->next_ts += (seq + 0x2000 - dec->next_seq) * GST_MSECOND; + else + dec->next_ts += (seq - dec->next_seq) * GST_MSECOND; + dec->last_seq = dec->next_seq; + dec->next_seq = seq; + break; + } + case 3: + { + /* B frame */ + if (seq < dec->last_seq) { + timestamp = (seq + 0x2000 - dec->last_seq) * GST_MSECOND + dec->last_ts; + } else { + timestamp = (seq - dec->last_seq) * GST_MSECOND + dec->last_ts; + } + + break; + } + default: + goto unknown_frame_type; + } + + ret = gst_pad_alloc_buffer (dec->src, GST_BUFFER_OFFSET_NONE, + dec->width * dec->height * 3 / 2, GST_PAD_CAPS (dec->src), buf); + + if (ret == GST_FLOW_OK) + GST_BUFFER_TIMESTAMP (*buf) = timestamp; + + return ret; + + /* Errors */ +unknown_version: + { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, + ("Unknown version: %i.", dec->version), (NULL)); + return GST_FLOW_ERROR; + } + +unknown_frame_type: + { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Unknown frame type."), (NULL)); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_real_video_dec_decode (GstRealVideoDec * dec, GstBuffer * in, guint offset) +{ + guint32 result; + GstBuffer *out = NULL; + GstFlowReturn ret; + guint8 *data, hdr_subseq, hdr_seqnum; + guint32 hdr_offset, hdr_length; + guint16 n; + gboolean bres; + guint8 *buf = GST_BUFFER_DATA (in) + offset; + guint len = GST_BUFFER_SIZE (in) - offset; + GstClockTime timestamp = GST_BUFFER_TIMESTAMP (in); + + GST_LOG_OBJECT (dec, + "Got buffer %p with timestamp %" GST_TIME_FORMAT " offset %d", in, + GST_TIME_ARGS (timestamp), offset); + + /* Subsequence */ + if (len < 1) + goto not_enough_data; + if (*buf != 0x40 && *buf != 0x41 && *buf != 0x42 && + *buf != 0x43 && *buf != 0x44 && *buf != 0x45) { + hdr_subseq = *buf & 0x7f; + buf++; + len--; + if (hdr_subseq == 64) + hdr_subseq = 1; + } else { + hdr_subseq = 1; + } + + /* Length */ + if (len < 2) + goto not_enough_data; + hdr_length = GST_READ_UINT16_BE (buf); + if (!(hdr_length & 0xc000)) { + if (len < 4) + goto not_enough_data; + hdr_length = GST_READ_UINT32_BE (buf); + buf += 4; + len -= 4; + } else { + hdr_length &= 0x3fff; + buf += 2; + len -= 2; + } + + /* Offset */ + if (len < 2) + goto not_enough_data; + hdr_offset = GST_READ_UINT16_BE (buf); + if (!(hdr_offset & 0xc000)) { + if (len < 4) + goto not_enough_data; + hdr_offset = GST_READ_UINT32_BE (buf); + buf += 4; + len -= 4; + } else { + hdr_offset &= 0x3fff; + buf += 2; + len -= 2; + } + + /* Sequence number */ + if (len < 1) + goto not_enough_data; + hdr_seqnum = *buf; + buf++; + len--; + if (len < 1) + goto not_enough_data; + + /* Verify the sequence numbers. */ + if (hdr_subseq == 1) { + n = gst_adapter_available_fast (dec->adapter); + if (n > 0) { + GST_DEBUG_OBJECT (dec, "Dropping data for sequence %i " + "because we are already receiving data for sequence %i.", + dec->seqnum, hdr_seqnum); + gst_adapter_clear (dec->adapter); + } + dec->seqnum = hdr_seqnum; + dec->length = hdr_length; + dec->fragment_count = 1; + } else { + if (dec->seqnum != hdr_seqnum) { + GST_DEBUG_OBJECT (dec, "Expected sequence %i, got sequence %i " + "(subseq=%i). Dropping packet.", dec->seqnum, hdr_seqnum, hdr_subseq); + return GST_FLOW_OK; + } else if (dec->subseq + 1 != hdr_subseq) { + GST_DEBUG_OBJECT (dec, "Expected subsequence %i, got subseqence %i. " + "Dropping packet.", dec->subseq + 1, hdr_subseq); + return GST_FLOW_OK; + } + dec->fragment_count++; + } + dec->subseq = hdr_subseq; + + /* Remember the offset */ + if (sizeof (dec->fragments) < 2 * (dec->fragment_count - 1) + 1) + goto too_many_fragments; + dec->fragments[2 * (dec->fragment_count - 1)] = 1; + dec->fragments[2 * (dec->fragment_count - 1) + 1] = + gst_adapter_available (dec->adapter); + + /* Some buffers need to be skipped. */ + if (((dec->version == GST_REAL_VIDEO_DEC_VERSION_3) && (*buf & 0x20)) || + ((dec->version == GST_REAL_VIDEO_DEC_VERSION_4) && (*buf & 0x80))) { + dec->fragment_count--; + dec->length -= len; + } else { + gst_adapter_push (dec->adapter, + gst_buffer_create_sub (in, GST_BUFFER_SIZE (in) - len, len)); + } + + /* All bytes received? */ + n = gst_adapter_available (dec->adapter); + GST_LOG_OBJECT (dec, "We know have %d bytes, and we need %d", n, dec->length); + if (dec->length <= n) { + RVInData tin; + RVOutData tout; + + if ((ret = + gst_real_video_dec_alloc_buffer (dec, timestamp, + &out)) != GST_FLOW_OK) + return ret; + + /* Decode */ + tin.datalen = dec->length; + tin.interpolate = 0; + tin.nfragments = dec->fragment_count - 1; + tin.fragments = dec->fragments; + tin.flags = 0; + tin.timestamp = GST_BUFFER_TIMESTAMP (out); + data = gst_adapter_take (dec->adapter, dec->length); + + result = dec->transform ( + (gchar *) data, + (gchar *) GST_BUFFER_DATA (out), &tin, &tout, dec->context); + + g_free (data); + if (result) + goto could_not_transform; + + /* Check for new dimensions */ + if (tout.frames && ((dec->width != tout.width) + || (dec->height != tout.height))) { + GstCaps *caps = gst_caps_copy (GST_PAD_CAPS (dec->src)); + GstStructure *s = gst_caps_get_structure (caps, 0); + + GST_DEBUG_OBJECT (dec, "New dimensions: %" + G_GUINT32_FORMAT " x %" G_GUINT32_FORMAT, tout.width, tout.height); + gst_structure_set (s, "width", G_TYPE_LONG, tout.width, + "height", G_TYPE_LONG, tout.height, NULL); + bres = gst_pad_set_caps (dec->src, caps); + gst_caps_unref (caps); + if (!bres) + goto new_dimensions_failed; + dec->width = tout.width; + dec->height = tout.height; + } + + GST_DEBUG_OBJECT (dec, + "Pushing out buffer with timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out))); + + if ((ret = gst_pad_push (dec->src, out)) != GST_FLOW_OK) + goto could_not_push; + + n = gst_adapter_available (dec->adapter); + if (n > 0) { + GST_LOG_OBJECT (dec, "Data left in the adapter: %d", n); + in = gst_adapter_take_buffer (dec->adapter, n); + ret = gst_real_video_dec_decode (dec, in, 0); + gst_buffer_unref (in); + if (ret != GST_FLOW_OK) + return ret; + } + } + + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Not enough data."), (NULL)); + return GST_FLOW_ERROR; + } + +too_many_fragments: + { + gst_buffer_unref (in); + GST_ELEMENT_ERROR (dec, STREAM, DECODE, + ("Got more fragments (%i) than can be handled (%i).", + dec->fragment_count, sizeof (dec->fragments)), (NULL)); + return GST_FLOW_ERROR; + } + +could_not_transform: + { + gst_buffer_unref (out); + GST_ELEMENT_ERROR (dec, STREAM, DECODE, + ("Could not decode buffer: %" G_GUINT32_FORMAT, result), (NULL)); + return GST_FLOW_ERROR; + } + +new_dimensions_failed: + { + gst_buffer_unref (out); + GST_ELEMENT_ERROR (dec, STREAM, DECODE, + ("Could not set new dimensions."), (NULL)); + return GST_FLOW_ERROR; + } + +could_not_push: + { + GST_DEBUG_OBJECT (dec, "Could not push buffer: %s", + gst_flow_get_name (ret)); + return ret; + } +} + +static GstFlowReturn +gst_real_video_dec_chain (GstPad * pad, GstBuffer * in) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (GST_PAD_PARENT (pad)); + guint8 flags, *buf = GST_BUFFER_DATA (in); + guint len = GST_BUFFER_SIZE (in); + GstFlowReturn ret; + + /* Flags */ + if (len < 1) + goto not_enough_data; + flags = *buf; + buf++; + len--; + + if (flags == 0x40) { + GST_DEBUG_OBJECT (dec, "Don't know how to handle buffer of type 0x40 " + "(size=%i).", len); + return GST_FLOW_OK; + } + + ret = gst_real_video_dec_decode (dec, in, 1); + gst_buffer_unref (in); + if (ret != GST_FLOW_OK) + return ret; + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Not enough data."), (NULL)); + gst_buffer_unref (in); + return GST_FLOW_ERROR; + } +} + +static gboolean +gst_real_video_dec_activate_push (GstPad * pad, gboolean active) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (GST_PAD_PARENT (pad)); + + gst_adapter_clear (dec->adapter); + + return TRUE; +} + +static gboolean +gst_real_video_dec_setcaps (GstPad * pad, GstCaps * caps) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (GST_PAD_PARENT (pad)); + GstStructure *s = gst_caps_get_structure (caps, 0); + gint version, res; + gchar data[36]; + gboolean bres; + const GValue *v; + + if (!gst_structure_get_int (s, "rmversion", &version) || + !gst_structure_get_int (s, "width", (gint *) & dec->width) || + !gst_structure_get_int (s, "height", (gint *) & dec->height) || + !gst_structure_get_int (s, "format", &dec->format) || + !gst_structure_get_int (s, "subformat", &dec->subformat) || + !gst_structure_get_fraction (s, "framerate", &dec->framerate_num, + &dec->framerate_denom)) + goto missing_keys; + dec->version = version; + + GST_LOG_OBJECT (dec, "Setting version to %d", version); + + if (!open_library (dec)) + return FALSE; + + /* Initialize REAL driver. */ + GST_WRITE_UINT16_LE (data + 0, 11); + GST_WRITE_UINT16_LE (data + 2, dec->width); + GST_WRITE_UINT16_LE (data + 4, dec->height); + GST_WRITE_UINT16_LE (data + 6, 0); + GST_WRITE_UINT32_LE (data + 8, 0); + GST_WRITE_UINT32_LE (data + 12, dec->subformat); + GST_WRITE_UINT32_LE (data + 16, 0); + GST_WRITE_UINT32_LE (data + 20, dec->format); + res = dec->init (&data, &dec->context); + if (res) + goto could_not_initialize; + + if ((v = gst_structure_get_value (s, "codec_data"))) { + GstBuffer *buf; + guint32 *msgdata; + guint i; + struct + { + guint32 type; + guint32 msg; + gpointer data; + guint32 extra[6]; + } msg; + + buf = g_value_peek_pointer (v); + + GST_LOG_OBJECT (dec, "Creating custom message of length %d", + GST_BUFFER_SIZE (buf)); + + msgdata = g_new0 (guint32, GST_BUFFER_SIZE (buf) + 2); + if (!msgdata) + goto could_not_allocate; + + msg.type = 0x24; + msg.msg = 1 + ((dec->subformat >> 16) & 7); + msg.data = msgdata; + for (i = 0; i < 6; i++) + msg.extra[i] = 0; + msgdata[0] = dec->width; + msgdata[1] = dec->height; + for (i = 0; i < GST_BUFFER_SIZE (buf); i++) + msgdata[i + 2] = 4 * GST_BUFFER_DATA (buf)[i]; + + res = dec->custom_message (&msg, dec->context); + + g_free (msgdata); + if (res) + goto could_not_send_message; + } + + caps = gst_caps_new_simple ("video/x-raw-yuv", + "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), + "framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom, + "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL); + bres = gst_pad_set_caps (GST_PAD (dec->src), caps); + gst_caps_unref (caps); + if (!bres) + goto could_not_set_caps; + + return TRUE; + +missing_keys: + { + GST_DEBUG_OBJECT (dec, "Could not find all necessary keys in structure."); + return FALSE; + } + +could_not_initialize: + { + dlclose (dec->handle); + dec->handle = NULL; + GST_DEBUG_OBJECT (dec, "Initialization of REAL driver failed (%i).", res); + return FALSE; + } + +could_not_allocate: + { + dlclose (dec->handle); + dec->handle = NULL; + GST_DEBUG_OBJECT (dec, "Could not allocate memory."); + return FALSE; + } + +could_not_send_message: + { + dlclose (dec->handle); + dec->handle = NULL; + GST_DEBUG_OBJECT (dec, "Failed to send custom message needed for " + "initialization (%i).", res); + return FALSE; + } + +could_not_set_caps: + { + dlclose (dec->handle); + dec->handle = NULL; + GST_DEBUG_OBJECT (dec, "Could not convince peer to accept dimensions " + "%i x %i.", dec->width, dec->height); + return FALSE; + } +} + +/* Attempts to open the correct library for the configured version */ + +static gboolean +open_library (GstRealVideoDec * dec) +{ + gchar *path = NULL; + + GST_DEBUG_OBJECT (dec, + "Attempting to open shared library for real video version %d", + dec->version); + + /* FIXME : Search for the correct library in various places if dec->path_rv20 + * isn't set explicitely ! + * Library names can also be different (ex : drv30.so vs drvc.so) + */ + + switch (dec->version) { + case GST_REAL_VIDEO_DEC_VERSION_2: + { + if (dec->path_rv20) + path = dec->path_rv20; + else if (g_file_test (DEFAULT_PATH_RV20, G_FILE_TEST_EXISTS)) + path = DEFAULT_PATH_RV20; + else if (g_file_test ("/usr/lib/drv2.so.6.0", G_FILE_TEST_EXISTS)) + path = "/usr/lib/drv2.so.6.0"; + else + goto no_known_libraries; + break; + } + case GST_REAL_VIDEO_DEC_VERSION_3: + { + if (dec->path_rv30) + path = dec->path_rv30; + else if (g_file_test (DEFAULT_PATH_RV30, G_FILE_TEST_EXISTS)) + path = DEFAULT_PATH_RV30; + else if (g_file_test ("/usr/lib/drv3.so.6.0", G_FILE_TEST_EXISTS)) + path = "/usr/lib/drv3.so.6.0"; + else + goto no_known_libraries; + break; + } + case GST_REAL_VIDEO_DEC_VERSION_4: + { + if (dec->path_rv40) + path = dec->path_rv40; + else if (g_file_test (DEFAULT_PATH_RV40, G_FILE_TEST_EXISTS)) + path = DEFAULT_PATH_RV40; + else if (g_file_test ("/usr/lib/drv4.so.6.0", G_FILE_TEST_EXISTS)) + path = "/usr/lib/drv4.so.6.0"; + else + goto no_known_libraries; + break; + } + default: + goto unknown_version; + } + + /* First close any open library */ + close_library (dec); + + dec->handle = dlopen (path, RTLD_LAZY); + if (!dec->handle) + goto could_not_open; + + /* First try opening legacy symbols */ + dec->custom_message = dlsym (dec->handle, "RV20toYUV420CustomMessage"); + dec->free = dlsym (dec->handle, "RV20toYUV420Free"); + dec->init = dlsym (dec->handle, "RV20toYUV420Init"); + dec->transform = dlsym (dec->handle, "RV20toYUV420Transform"); + + if (!(dec->custom_message && dec->free && dec->init && dec->transform)) { + /* Else try loading new symbols */ + dec->custom_message = dlsym (dec->handle, "RV40toYUV420CustomMessage"); + dec->free = dlsym (dec->handle, "RV40toYUV420Free"); + dec->init = dlsym (dec->handle, "RV40toYUV420Init"); + dec->transform = dlsym (dec->handle, "RV40toYUV420Transform"); + + if (!(dec->custom_message && dec->free && dec->init && dec->transform)) + goto could_not_load; + } + + return TRUE; + +no_known_libraries: + { + GST_ELEMENT_ERROR (dec, LIBRARY, INIT, + ("Couldn't find a realvideo shared library for version %d", + dec->version), (NULL)); + return FALSE; + } + +unknown_version: + { + GST_ERROR_OBJECT (dec, "Cannot handle version %i.", dec->version); + return FALSE; + } + +could_not_open: + { + GST_ERROR_OBJECT (dec, "Could not open library '%s'.", path); + return FALSE; + } + +could_not_load: + { + close_library (dec); + GST_ERROR_OBJECT (dec, "Could not load all symbols."); + return FALSE; + } +} + +static void +close_library (GstRealVideoDec * dec) +{ + if (dec->context && dec->free) { + dec->free (dec->context); + dec->context = NULL; + } + + dec->custom_message = NULL; + dec->free = NULL; + dec->init = NULL; + dec->transform = NULL; + + if (dec->handle) + dlclose (dec->handle); + dec->handle = NULL; +} + +static void +gst_real_video_dec_init (GstRealVideoDec * dec, GstRealVideoDecClass * klass) +{ + dec->snk = + gst_pad_new_from_template (gst_static_pad_template_get (&snk_t), "sink"); + gst_pad_set_setcaps_function (dec->snk, gst_real_video_dec_setcaps); + gst_pad_set_chain_function (dec->snk, gst_real_video_dec_chain); + gst_pad_set_activatepush_function (dec->snk, + gst_real_video_dec_activate_push); + gst_element_add_pad (GST_ELEMENT (dec), dec->snk); + + dec->src = + gst_pad_new_from_template (gst_static_pad_template_get (&src_t), "src"); + gst_element_add_pad (GST_ELEMENT (dec), dec->src); + + dec->adapter = g_object_new (GST_TYPE_ADAPTER, NULL); +} + +static void +gst_real_video_dec_base_init (gpointer g_class) +{ + GstElementClass *ec = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&snk_t)); + gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&src_t)); + gst_element_class_set_details (ec, &realvideode_details); +} + +static void +gst_real_video_dec_finalize (GObject * object) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (object); + + if (dec->adapter) { + g_object_unref (G_OBJECT (dec->adapter)); + dec->adapter = NULL; + } + + close_library (dec); + + if (dec->path_rv20) { + g_free (dec->path_rv20); + dec->path_rv20 = NULL; + } + if (dec->path_rv30) { + g_free (dec->path_rv30); + dec->path_rv30 = NULL; + } + if (dec->path_rv40) { + g_free (dec->path_rv40); + dec->path_rv40 = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_real_video_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (object); + + /* Changing the location of the .so supposes it's not being done + * in a state greater than READY ! + */ + + switch (prop_id) { + case PROP_PATH_RV20: + if (dec->path_rv20) + g_free (dec->path_rv20); + dec->path_rv20 = g_value_dup_string (value); + break; + case PROP_PATH_RV30: + if (dec->path_rv30) + g_free (dec->path_rv30); + dec->path_rv30 = g_value_dup_string (value); + break; + case PROP_PATH_RV40: + if (dec->path_rv40) + g_free (dec->path_rv40); + dec->path_rv40 = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_real_video_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRealVideoDec *dec = GST_REAL_VIDEO_DEC (object); + + switch (prop_id) { + case PROP_PATH_RV20: + g_value_set_string (value, dec->path_rv20 ? dec->path_rv20 : + DEFAULT_PATH_RV20); + break; + case PROP_PATH_RV30: + g_value_set_string (value, dec->path_rv30 ? dec->path_rv30 : + DEFAULT_PATH_RV30); + break; + case PROP_PATH_RV40: + g_value_set_string (value, dec->path_rv40 ? dec->path_rv40 : + DEFAULT_PATH_RV40); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_real_video_dec_class_init (GstRealVideoDecClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gst_real_video_dec_set_property; + object_class->get_property = gst_real_video_dec_get_property; + object_class->finalize = gst_real_video_dec_finalize; + + g_object_class_install_property (object_class, PROP_PATH_RV20, + g_param_spec_string ("path_rv20", "Path to rv20 driver", + "Path to rv20 driver", DEFAULT_PATH_RV20, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_PATH_RV30, + g_param_spec_string ("path_rv30", "Path to rv30 driver", + "Path to rv30 driver", DEFAULT_PATH_RV30, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_PATH_RV40, + g_param_spec_string ("path_rv40", "Path to rv40 driver", + "Path to rv40 driver", DEFAULT_PATH_RV40, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT (realvideode_debug, "realvideodec", 0, + "RealVideo decoder"); +} diff --git a/gst/real/gstrealvideodec.h b/gst/real/gstrealvideodec.h new file mode 100644 index 00000000..d4251223 --- /dev/null +++ b/gst/real/gstrealvideodec.h @@ -0,0 +1,90 @@ +/* GStreamer + * + * Copyright (C) 2006 Lutz Mueller <lutz@topfrose.de> + * 2006 Edward Hervey <bilboed@bilbod.com> + * + * 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. + */ + +#ifndef __GST_REAL_VIDEO_DEC_H__ +#define __GST_REAL_VIDEO_DEC_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_REAL_VIDEO_DEC (gst_real_video_dec_get_type()) +#define GST_REAL_VIDEO_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_REAL_VIDEO_DEC,GstRealVideoDec)) +#define GST_REAL_VIDEO_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_REAL_VIDEO_DEC,GstRealVideoDecClass)) + +typedef struct _GstRealVideoDec GstRealVideoDec; +typedef struct _GstRealVideoDecClass GstRealVideoDecClass; +typedef enum _GstRealVideoDecVersion GstRealVideoDecVersion; + +enum _GstRealVideoDecVersion +{ + GST_REAL_VIDEO_DEC_VERSION_2 = 2, + GST_REAL_VIDEO_DEC_VERSION_3 = 3, + GST_REAL_VIDEO_DEC_VERSION_4 = 4 +}; + +struct _GstRealVideoDec +{ + GstElement parent; + + GstPad *src, *snk; + + /* Caps */ + GstRealVideoDecVersion version; + guint width, height; + gint format, subformat; + gint framerate_num, framerate_denom; + + /* Variables needed for fixing timestamps. */ + GstClockTime next_ts, last_ts; + guint16 next_seq, last_seq; + + /* Hooks */ + gpointer handle; + guint32 (*custom_message) (gpointer, gpointer); + guint32 (*free) (gpointer); + guint32 (*init) (gpointer, gpointer); + guint32 (*transform) (gchar *, gchar *, gpointer, gpointer, gpointer); + + /* Used by the REAL library. */ + gpointer context; + + /* State */ + GstAdapter *adapter; + guint8 seqnum, subseq; + guint16 length; + guint32 fragment_count; + guint32 fragments[256]; + + /* Properties */ + gchar *path_rv20, *path_rv30, *path_rv40; +}; + +struct _GstRealVideoDecClass +{ + GstElementClass parent_class; +}; + +GType gst_real_video_dec_get_type (void); + +G_END_DECLS +#endif /* __GST_REAL_VIDEO_DEC_H__ */ |