summaryrefslogtreecommitdiffstats
path: root/ext/annodex/gstskeldec.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/annodex/gstskeldec.c')
-rw-r--r--ext/annodex/gstskeldec.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/ext/annodex/gstskeldec.c b/ext/annodex/gstskeldec.c
new file mode 100644
index 00000000..6e72c799
--- /dev/null
+++ b/ext/annodex/gstskeldec.c
@@ -0,0 +1,380 @@
+/*
+ * gstskeldec.c - GStreamer annodex skeleton decoder
+ * Copyright (C) 2005 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include "gstskeldec.h"
+#include "gstskeltag.h"
+#include "gstannodex.h"
+
+GST_DEBUG_CATEGORY (skeldec);
+#define GST_CAT_DEFAULT skeldec
+
+#define FISHEAD_SIZE 64
+#define FISBONE_MIN_SIZE 52
+
+enum
+{
+ GST_SKEL_TAG_FISHEAD_UTC_LEN = 19
+};
+
+enum
+{
+ LAST_SIGNAL
+};
+
+static GstElementDetails gst_skel_dec_details = {
+ "skeldec: Annodex skeleton stream decoder",
+ "Codec/Decoder",
+ "Decodes ogg skeleton streams",
+ "Alessandro Decina <alessandro@nnva.org>",
+};
+
+static GstStaticPadTemplate gst_skel_dec_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-ogg-skeleton, parsed=(boolean)TRUE")
+ );
+
+static GstStaticPadTemplate gst_skel_dec_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-ogg-skeleton, parsed=(boolean)FALSE")
+ );
+
+/* GstSkelDec prototypes */
+GST_BOILERPLATE (GstSkelDec, gst_skel_dec, GstElement, GST_TYPE_ELEMENT);
+static gboolean gst_skel_dec_sink_query (GstPad * pad, GstQuery * query);
+static GstStateChangeReturn gst_skel_dec_change_state (GstElement * element,
+ GstStateChange transition);
+static GstFlowReturn gst_skel_dec_chain (GstPad * pad, GstBuffer * buffer);
+static GstFlowReturn gst_skel_dec_parse_fishead (GstSkelDec * dec,
+ GstBuffer * buffer);
+static GstFlowReturn gst_skel_dec_parse_fisbone (GstSkelDec * dec,
+ GstBuffer * buffer);
+
+/* GstSkelDec code */
+static void
+gst_skel_dec_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_skel_dec_sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_skel_dec_src_factory));
+ gst_element_class_set_details (element_class, &gst_skel_dec_details);
+}
+
+static void
+gst_skel_dec_class_init (GstSkelDecClass * klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ element_class->change_state = gst_skel_dec_change_state;
+}
+
+static void
+gst_skel_dec_init (GstSkelDec * dec, GstSkelDecClass * klass)
+{
+ dec->sinkpad =
+ gst_pad_new_from_static_template (&gst_skel_dec_sink_factory, "sink");
+ gst_pad_set_query_function (dec->sinkpad, gst_skel_dec_sink_query);
+ gst_pad_set_chain_function (dec->sinkpad, gst_skel_dec_chain);
+ gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
+
+ dec->srcpad =
+ gst_pad_new_from_static_template (&gst_skel_dec_src_factory, "src");
+ gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
+
+}
+
+static gboolean
+gst_skel_dec_sink_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CONVERT:
+ {
+ GstFormat src_format, dest_format;
+ gint64 src_val, dest_val;
+
+ gst_query_parse_convert (query, &src_format, &src_val,
+ &dest_format, &dest_val);
+
+ if (dest_format == GST_FORMAT_TIME) {
+ dest_val = GST_CLOCK_TIME_NONE;
+ res = TRUE;
+ }
+
+ if (res) {
+ gst_query_set_convert (query, src_format, src_val,
+ dest_format, dest_val);
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return res;
+}
+
+static GstStateChangeReturn
+gst_skel_dec_change_state (GstElement * element, GstStateChange transition)
+{
+ GstSkelDec *dec = GST_SKEL_DEC (element);
+ GstStateChangeReturn res;
+
+ /* handle the upward state changes */
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ dec->major = 0;
+ dec->minor = 0;
+ break;
+ default:
+ break;
+ }
+
+ res = parent_class->change_state (element, transition);
+
+ /* no need to handle downward state changes */
+
+ return res;
+}
+
+static GstFlowReturn
+gst_skel_dec_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstSkelDec *dec = GST_SKEL_DEC (GST_PAD_PARENT (pad));
+ GstFlowReturn ret;
+
+ gint size = GST_BUFFER_SIZE (buffer);
+ guint8 *data = GST_BUFFER_DATA (buffer);
+
+ if (GST_BUFFER_SIZE (buffer) == 0) {
+ /* the skeleton EOS has no packet data */
+ return GST_FLOW_OK;
+ }
+
+ if (size >= GST_SKEL_OGG_FISHEAD_SIZE && !memcmp (data, "fishead\0", 8)) {
+ ret = gst_skel_dec_parse_fishead (dec, buffer);
+ } else if (size >= 8 && !memcmp (data, "fisbone\0", 8)) {
+ ret = gst_skel_dec_parse_fisbone (dec, buffer);
+ } else {
+ /* maybe it would be better to ignore the packet */
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("unknown packet type"));
+ ret = GST_FLOW_UNEXPECTED;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_skel_dec_parse_fishead (GstSkelDec * dec, GstBuffer * buffer)
+{
+ GstTagList *tags;
+ guint8 *data = GST_BUFFER_DATA (buffer);
+ GstSkelTagFishead *fishead;
+
+ if (GST_BUFFER_SIZE (buffer) != FISHEAD_SIZE)
+ goto wrong_size;
+
+ fishead = g_object_new (GST_TYPE_SKEL_TAG_FISHEAD, NULL);
+
+ data += 8;
+ fishead->major = GST_READ_UINT16_LE (data);
+ data += 2;
+ fishead->minor = GST_READ_UINT16_LE (data);
+ data += 2;
+ fishead->prestime_n = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ fishead->prestime_d = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ fishead->basetime_n = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ fishead->basetime_d = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ fishead->utc = g_strndup ((gchar *) data, GST_SKEL_TAG_FISHEAD_UTC_LEN);
+
+ GST_INFO_OBJECT (dec, "fishead parsed ("
+ "major: %" G_GUINT16_FORMAT " minor: %" G_GUINT16_FORMAT
+ " prestime_n: %" G_GINT64_FORMAT " prestime_d: %" G_GINT64_FORMAT
+ " basetime_n: %" G_GINT64_FORMAT " basetime_d: %" G_GINT64_FORMAT
+ " utc: %s)", fishead->major, fishead->minor, fishead->prestime_n,
+ fishead->prestime_d, fishead->basetime_n, fishead->basetime_d,
+ fishead->utc);
+
+ /* send the TAG_MESSAGE */
+ tags = gst_tag_list_new ();
+ gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
+ GST_TAG_SKELETON_FISHEAD, fishead, NULL);
+ gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, tags);
+
+ g_object_unref (fishead);
+
+ /* forward the fishead */
+ gst_buffer_set_caps (buffer,
+ gst_static_pad_template_get_caps (&gst_skel_dec_src_factory));
+ return gst_pad_push (dec->srcpad, buffer);
+
+wrong_size:
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE,
+ (NULL), ("wrong fishead packet size: %d", GST_BUFFER_SIZE (buffer)));
+
+ gst_buffer_unref (buffer);
+ return GST_FLOW_ERROR;
+}
+
+static GstFlowReturn
+gst_skel_dec_parse_fisbone (GstSkelDec * dec, GstBuffer * buffer)
+{
+ GstSkelTagFisbone *fisbone;
+ guint8 *data;
+ gchar *headers;
+ gchar **tokens;
+ const gchar *content_type;
+ GstTagList *tags;
+ GValue *val;
+
+ if (GST_BUFFER_SIZE (buffer) < FISBONE_MIN_SIZE)
+ goto wrong_size;
+
+ data = GST_BUFFER_DATA (buffer);
+ fisbone = g_object_new (GST_TYPE_SKEL_TAG_FISBONE, NULL);
+
+ data += 8;
+ fisbone->hdr_offset = GST_READ_UINT32_LE (data);
+ data += 4;
+ fisbone->serialno = GST_READ_UINT32_LE (data);
+ data += 4;
+ fisbone->hdr_num = GST_READ_UINT32_LE (data);
+ data += 4;
+ fisbone->granulerate_n = GST_READ_UINT64_LE (data);
+ data += 8;
+ fisbone->granulerate_d = GST_READ_UINT64_LE (data);
+ data += 8;
+ fisbone->start_granule = GST_READ_UINT64_LE (data);
+ data += 8;
+ fisbone->preroll = GST_READ_UINT32_LE (data);
+ data += 4;
+ fisbone->granuleshift = GST_READ_UINT8 (data);
+ data += 1;
+ data += 3; /* padding */
+
+ /* 8 = strlen ("fishead\0") */
+ headers = g_strndup ((gchar *) data,
+ GST_BUFFER_SIZE (buffer) - 8 - fisbone->hdr_offset);
+ fisbone->headers = gst_annodex_parse_headers (headers);
+ g_free (headers);
+
+ if (!fisbone->headers)
+ goto bad_headers;
+
+ /* check for the mandatory Content-Type header: it MUST be the first
+ * header */
+ if (fisbone->headers->n_values < 2 ||
+ (val = g_value_array_get_nth (fisbone->headers, 0)) == NULL ||
+ strcmp (g_value_get_string (val), "Content-Type"))
+ goto no_content_type;
+
+ /* get the Content-Type value */
+ val = g_value_array_get_nth (fisbone->headers, 1);
+ content_type = g_value_get_string (val);
+
+ /* Content-Type must not be empty */
+ if (*content_type == '\0')
+ goto bad_content_type;
+
+ /* split the content-type field into "content_type; encoding" */
+ tokens = g_strsplit (content_type, ";", 2);
+ fisbone->content_type = g_strdup (g_strstrip (tokens[0]));
+ fisbone->encoding = tokens[1] == NULL ? NULL :
+ g_strdup (g_strstrip (tokens[1]));
+ g_strfreev (tokens);
+
+ GST_INFO_OBJECT (dec, "fisbone parsed ("
+ "serialno %" G_GUINT32_FORMAT " granulerate_n: %" G_GINT64_FORMAT
+ " granulerate_d: %" G_GINT64_FORMAT " start_granule: %" G_GINT64_FORMAT
+ " preroll: %" G_GUINT32_FORMAT " granuleshift: %d"
+ " content-type: %s)",
+ fisbone->serialno, fisbone->granulerate_n, fisbone->granulerate_d,
+ fisbone->start_granule, fisbone->preroll, fisbone->granuleshift,
+ fisbone->content_type);
+
+ /* send the tag message */
+ tags = gst_tag_list_new ();
+ gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
+ GST_TAG_SKELETON_FISBONE, fisbone, NULL);
+ gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, tags);
+
+ g_object_unref (fisbone);
+
+ gst_buffer_set_caps (buffer,
+ gst_static_pad_template_get_caps (&gst_skel_dec_src_factory));
+ return gst_pad_push (dec->srcpad, buffer);
+
+wrong_size:
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE,
+ (NULL), ("wrong fisbone size (%d)", GST_BUFFER_SIZE (buffer)));
+ gst_buffer_unref (buffer);
+ return GST_FLOW_ERROR;
+
+bad_headers:
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("bad fisbone headers"));
+ gst_buffer_unref (buffer);
+ g_object_unref (fisbone);
+ return GST_FLOW_ERROR;
+
+no_content_type:
+bad_content_type:
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE,
+ (NULL), ("missing or bad fisbone content-type"));
+ gst_buffer_unref (buffer);
+ g_object_unref (fisbone);
+ return GST_FLOW_ERROR;
+}
+
+gboolean
+gst_skel_dec_plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "skeldec", GST_RANK_PRIMARY,
+ gst_skel_dec_get_type ()))
+ return FALSE;
+
+ gst_tag_register (GST_TAG_SKELETON_FISHEAD, GST_TAG_FLAG_META,
+ GST_TYPE_SKEL_TAG_FISHEAD, "skeleton-fishead",
+ "annodex skeleton fishead tag", NULL);
+
+ gst_tag_register (GST_TAG_SKELETON_FISBONE, GST_TAG_FLAG_META,
+ GST_TYPE_SKEL_TAG_FISBONE, "skeleton-fisbone",
+ "annodex skeleton fisbone tag", NULL);
+
+ GST_DEBUG_CATEGORY_INIT (skeldec, "skeldec", 0,
+ "annodex skeleton decoding element");
+
+ return TRUE;
+}