SUBDIRS = common
+if GST_EXT_USE_EXT_PIFFDEMUX
+#SUBDIRS += piffdemux
+endif
+
if GST_EXT_USE_EXT_ENCODEBIN
SUBDIRS += encodebin
endif
SUBDIRS += pdpushsrc
endif
+if GST_EXT_USE_EXT_AUDIOTP
+SUBDIRS += audiotp
+endif
+
+if GST_EXT_USE_EXT_SSDEMUX
+#SUBDIRS += ssdemux
+endif
+
+
+
DIST_SUBDIRS = common
+if GST_EXT_USE_EXT_PIFFDEMUX
+#DIST_SUBDIRS += piffdemux
+endif
+
if GST_EXT_USE_EXT_ENCODEBIN
DIST_SUBDIRS += encodebin
endif
DIST_SUBDIRS += toggle
endif
+if GST_EXT_USE_EXT_SSDEMUX
+#DIST_SUBDIRS += ssdemux
+endif
+
+
EXTRA_DIST = \
gstreamer.spec gstreamer.spec.in \
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+# plugindir is set in configure
+
+##############################################################################
+# change libgstplugin.la to something more suitable, e.g. libmysomething.la #
+##############################################################################
+plugin_LTLIBRARIES = libgstaudiotp.la
+
+##############################################################################
+# for the next set of variables, rename the prefix if you renamed the .la, #
+# e.g. libgstplugin_la_SOURCES => libmysomething_la_SOURCES #
+# libgstplugin_la_CFLAGS => libmysomething_la_CFLAGS #
+# libgstplugin_la_LIBADD => libmysomething_la_LIBADD #
+# libgstplugin_la_LDFLAGS => libmysomething_la_LDFLAGS #
+##############################################################################
+
+# sources used to compile this plug-in
+libgstaudiotp_la_SOURCES = gstaudiotp.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstaudiotp_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS)
+libgstaudiotp_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS)
+libgstaudiotp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gstaudiotp.h
\ No newline at end of file
--- /dev/null
+/*
+ * audiotp
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstaudiotp.h"
+
+/* Plugin Detaills for gstreamer */
+static const GstElementDetails gst_audiotp_plugin_details = GST_ELEMENT_DETAILS (
+ "Audio timestamp reversal plugin",
+ "Utility/Audio",
+ "Reverses audio timestamps for reverse playback",
+ "Samsung Electronics <www.samsung.com>"
+ );
+
+/*** GSTREAMER PROTOTYPES *****************************************************/
+
+#define STATIC_CAPS \
+GST_STATIC_CAPS ( \
+ "audio/x-raw-float, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 64;" \
+ "audio/x-raw-float, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 32;" \
+ "audio/x-raw-int, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 32, " \
+ "depth = (int) [ 1, 32 ], " \
+ "signed = (boolean) { true, false }; " \
+ "audio/x-raw-int, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 24, " \
+ "depth = (int) [ 1, 24 ], " "signed = (boolean) { true, false }; " \
+ "audio/x-raw-int, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 16, " \
+ "depth = (int) [ 1, 16 ], " \
+ "signed = (boolean) { true, false }; " \
+ "audio/x-raw-int, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
+ "width = (int) 8, " \
+ "depth = (int) [ 1, 8 ], " \
+ "signed = (boolean) { true, false } " \
+)
+
+/* Element sink pad template */
+static GstStaticPadTemplate gst_audiotp_sink_template = GST_STATIC_PAD_TEMPLATE (
+ "sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ STATIC_CAPS);
+
+/* Element Source Pad template */
+static GstStaticPadTemplate gst_audiotp_src_template = GST_STATIC_PAD_TEMPLATE (
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ STATIC_CAPS);
+
+
+////////////////////////////////////////////////////////
+// Gstreamer Base Prototype //
+////////////////////////////////////////////////////////
+
+GST_DEBUG_CATEGORY_STATIC(gst_audiotp_debug);
+#define GST_CAT_DEFAULT gst_audiotp_debug
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT(GST_CAT_DEFAULT, "audiotp", 0, "Audio trickplay plugin"); \
+ GST_DEBUG("audiotp is registered");
+
+GST_BOILERPLATE_FULL(Gstaudiotp, gst_audiotp, GstElement, GST_TYPE_ELEMENT, _do_init);
+
+static void gst_audiotp_base_init(gpointer klass);
+static void gst_audiotp_class_init(GstaudiotpClass *klass);
+static void gst_audiotp_init(Gstaudiotp *dec, GstaudiotpClass *klass);
+static GstFlowReturn gst_audiotp_chain(GstPad *pad, GstBuffer *buf);
+static GstStateChangeReturn gst_audiotp_change_state(GstElement *element, GstStateChange transition);
+static void gst_audiotp_finalize(GObject *object);
+static gboolean gst_audiotp_sink_event (GstPad *pad, GstEvent *event);
+static GstFlowReturn gst_audiotp_push_silent_frame (Gstaudiotp *audiotp, GstBuffer *MetaDataBuf);
+
+
+
+////////////////////////////////////////////////////////
+// Gstreamer Base Functions //
+////////////////////////////////////////////////////////
+
+/**
+ **
+ ** Description: The element details and pad templates are registered with the plugin
+ ** In Params : @ gclass instance of Element class
+ ** return : None
+ ** Comments : 1. Adding templates of source and sink pad to element
+ ** 2. Setting element class deatils to element
+ **
+ */
+static void
+gst_audiotp_base_init(gpointer klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_audiotp_sink_template));
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_audiotp_src_template));
+ gst_element_class_set_details(element_class, &gst_audiotp_plugin_details);
+}
+
+
+/**
+ **
+ ** Description: Initialization of the Element Class
+ ** In Param : @ gclass instance of Element class
+ ** return : None
+ ** Comments : 1. Overwriting base class virtual functions
+ ** 2. Installing the properties of the element
+ **
+ */
+static void
+gst_audiotp_class_init(GstaudiotpClass *klass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = gst_audiotp_finalize;
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_audiotp_change_state);
+}
+
+
+/**
+ **
+ ** Description: Initialization of the Element instance
+ ** In Params : @ audio tp element instance
+ ** @ gclass instance of Element class
+ ** return : None
+ ** Comments : 1. Creating new source & sink pads using templates
+ ** 2. Setting the callback functions to the pads
+ ** 3. Local data initialization.
+ **
+ */
+static void
+gst_audiotp_init(Gstaudiotp *audiotp, GstaudiotpClass *klass)
+{
+
+ audiotp->sinkpad = gst_pad_new_from_static_template(&gst_audiotp_sink_template, "sink");
+ audiotp->srcpad = gst_pad_new_from_static_template(&gst_audiotp_src_template, "src");
+
+ gst_pad_set_chain_function (audiotp->sinkpad, GST_DEBUG_FUNCPTR(gst_audiotp_chain));
+ gst_pad_set_event_function (audiotp->sinkpad, GST_DEBUG_FUNCPTR(gst_audiotp_sink_event));
+
+ gst_pad_use_fixed_caps(audiotp->srcpad);
+
+ gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->sinkpad);
+ gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->srcpad);
+
+ audiotp->reverse = g_queue_new ();
+ audiotp->head_prev = GST_CLOCK_TIME_NONE;
+ audiotp->tail_prev = GST_CLOCK_TIME_NONE;
+
+}
+
+
+/**
+ **
+ ** Description: Finalization of the Element instance (object)
+ ** In Params : @ audiotp element instance in the form of GObject
+ ** return : None
+ ** Comments : 1. Local data Deinitialization.
+ **
+ **
+ */
+static void
+gst_audiotp_finalize(GObject *object)
+{
+ Gstaudiotp *audiotp = GST_AUDIOTP(object);
+
+ while (!g_queue_is_empty (audiotp->reverse)) {
+ GstMiniObject *data = g_queue_pop_head (audiotp->reverse);
+ gst_mini_object_unref (data);
+ }
+ /* freeing dealy queue */
+ g_queue_free(audiotp->reverse);
+ audiotp->reverse = NULL;
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+
+/**
+ **
+ ** Description: Callback function when the element's state gets changed
+ ** In Params : @ audiotp plugin element
+ ** @ type of state change
+ ** return : status of the state change processing
+ ** Comments :
+ **
+ **
+ */
+static GstStateChangeReturn
+gst_audiotp_change_state(GstElement *element, GstStateChange transition)
+{
+ GstStateChangeReturn res = GST_FLOW_ERROR;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ res = parent_class->change_state(element, transition);
+ if ( res != GST_STATE_CHANGE_SUCCESS ) {
+ GST_ERROR ("change state error in parent class\n");
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ **
+ ** Description: Callback function when sinkpad gets an event
+ ** In Params : @ Sinkpad on which the event arrives
+ ** @ event type
+ ** return : TRUE/FALSE on success/failure of the event processing.
+ ** Comments : 1. Process the event and push it to the source pad.
+ **
+ **
+ */
+static gboolean
+gst_audiotp_sink_event (GstPad *pad, GstEvent *event)
+{
+ Gstaudiotp *audiotp = NULL;
+ gboolean res = FALSE;
+
+ audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
+
+ switch (GST_EVENT_TYPE(event)) {
+ /* Arrives whenever there is a jump in the normal playback. Ex:SEEK */
+ case GST_EVENT_NEWSEGMENT: {
+ GstFormat format;
+ gdouble rate, arate;
+ gint64 start, stop, time;
+ gboolean update;
+
+ GST_INFO_OBJECT (audiotp, "GST_EVENT_NEWSEGMENT");
+ gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &time);
+
+ if (format != GST_FORMAT_TIME) {
+ GST_ERROR("Format is not supported\n");
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ goto done;
+ }
+
+ GST_INFO_OBJECT (audiotp, "update: %d, rate: %0.3f, arate: %0.3f\n", update, rate, arate);
+ GST_INFO_OBJECT (audiotp, "start : %" GST_TIME_FORMAT, GST_TIME_ARGS(start));
+ GST_INFO_OBJECT (audiotp, "stop : %" GST_TIME_FORMAT, GST_TIME_ARGS(stop));
+ GST_INFO_OBJECT (audiotp, "time : %" GST_TIME_FORMAT, GST_TIME_ARGS(time));
+
+ /* If we receive new_segment without FLUSH events, then we will push all the frame in queue */
+ while (!g_queue_is_empty (audiotp->reverse)) {
+ GstBuffer *MetaDataBuf;
+ GstFlowReturn ret = GST_FLOW_OK;
+ if(audiotp->is_reversed)
+ MetaDataBuf = g_queue_pop_head (audiotp->reverse);
+ else
+ MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
+ ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
+ if (GST_FLOW_OK != ret)
+ {
+ GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
+ }
+ }
+ gst_segment_set_newsegment_full(&audiotp->segment, update, rate, arate, format, start, stop, time);
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ break;
+ }
+
+ /* Indication of the end of the stream */
+ case GST_EVENT_EOS: {
+ /* queue all buffer timestamps till we receive next discontinuity */
+ while (!g_queue_is_empty (audiotp->reverse)) {
+ GstBuffer *MetaDataBuf;
+ GstFlowReturn ret = GST_FLOW_OK;
+ if(audiotp->is_reversed)
+ MetaDataBuf = g_queue_pop_head (audiotp->reverse);
+ else
+ MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
+ ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
+ if (GST_FLOW_OK != ret) {
+ GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
+ }
+ }
+
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ break;
+ }
+
+ /* Indication of the SEEK operation start */
+ case GST_EVENT_FLUSH_START: {
+ GST_INFO_OBJECT (audiotp, "GST_EVENT_FLUSH_START");
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ break;
+ }
+
+ /* Indication of the SEEK operation stop */
+ case GST_EVENT_FLUSH_STOP: {
+ GST_INFO_OBJECT (audiotp, "GST_EVENT_FLUSH_STOP");
+ /* make sure that we empty the queue */
+ while (!g_queue_is_empty (audiotp->reverse)) {
+ GST_DEBUG_OBJECT (audiotp, "Flushing buffers in reverse queue....");
+ gst_buffer_unref(g_queue_pop_head (audiotp->reverse));
+ }
+
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ break;
+ }
+
+ default: {
+ res = gst_pad_push_event(audiotp->srcpad, event);
+ break;
+ }
+ }
+
+ done:
+ return res;
+}
+
+
+/**
+ **
+ ** Description: Callback function when sinkpad gets a buffer (from the previous element)
+ ** In Params : @ Sinkpad on which the buffer arrives
+ ** @ input buffer
+ ** return : status of the buffer processing.
+ ** Comments : 1. Handle the buffer discontinuity ( in terms of tmestamp)
+ ** 2. Push or pop buffer based on discontinuity.
+ **
+ **
+ */
+static GstFlowReturn
+gst_audiotp_chain(GstPad *pad, GstBuffer *buf)
+{
+ Gstaudiotp *audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if(buf == NULL) {
+ ret = GST_FLOW_ERROR;
+ goto error_exit;
+ }
+
+ GST_LOG_OBJECT (audiotp, "Input buffer : ts =%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT ", size=%d",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf)),
+ GST_TIME_ARGS(GST_BUFFER_DURATION(buf)),
+ GST_BUFFER_SIZE(buf), GST_BUFFER_IS_DISCONT (buf) ? " - discont" :"");
+
+ if (audiotp->segment.rate < 0.0) {
+ goto send_reverse;
+ }
+
+
+ /* Push the input data to the next element */
+ ret = gst_pad_push(audiotp->srcpad, buf);
+ if (ret != GST_FLOW_OK ) {
+ GST_WARNING("failed to push buffer %p. reason: %s", buf, gst_flow_get_name (ret));
+ buf = NULL;
+ goto error_exit;
+ }
+ return ret;
+
+send_reverse:
+ {
+ GstBuffer *MetaDataBuf = NULL;
+ GstClockTime headbuf_ts = GST_CLOCK_TIME_NONE;
+ GstClockTime tailbuf_ts = GST_CLOCK_TIME_NONE;
+
+ /* Discont buffers is mostly due to seek, when buffers of seeked timestamp gets pushed */
+ if (GST_BUFFER_IS_DISCONT(buf)) {
+ if(!g_queue_is_empty (audiotp->reverse)) {
+ GstBuffer *headbuf = (GstBuffer*) (audiotp->reverse->head->data);
+ GstBuffer *tailbuf = (GstBuffer*) (audiotp->reverse->tail->data);
+
+ headbuf_ts = GST_BUFFER_TIMESTAMP(headbuf);
+ tailbuf_ts = GST_BUFFER_TIMESTAMP(tailbuf);
+
+ GST_DEBUG_OBJECT(audiotp,"Headbuf ts =%" GST_TIME_FORMAT ", TailBuf ts =%" GST_TIME_FORMAT "",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(headbuf)),
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(tailbuf)));
+
+ /* Check if the decoder is already having the reversal logic */
+ if(GST_BUFFER_TIMESTAMP(headbuf) > GST_BUFFER_TIMESTAMP(tailbuf)) {
+ GST_INFO_OBJECT (audiotp, "Buffers arrived in reverse order, audiotp NO NEED to reverse...");
+ audiotp->is_reversed = TRUE;
+ } else {
+ GST_INFO_OBJECT (audiotp, "Buffers arrived in forward order, audiotp NEED to reverse...");
+ audiotp->is_reversed = FALSE;
+ }
+ }
+
+ while (!g_queue_is_empty (audiotp->reverse)) {
+
+ if(audiotp->is_reversed)
+ MetaDataBuf = g_queue_pop_head (audiotp->reverse);
+ else
+ MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
+
+ if (NULL == MetaDataBuf) {
+ GST_ERROR_OBJECT (audiotp, "NULL pointer...");
+ ret = GST_FLOW_ERROR;
+ goto error_exit;
+ }
+
+ /* If buffers arrive in forward order, compare the MetaDatabuf with
+ * previous head buffer timestamp.
+ * If buffers arrive in reverse order, compare the MetaDataBuf with
+ * previous tail buffer timestamp */
+ if((GST_BUFFER_TIMESTAMP(MetaDataBuf) < audiotp->head_prev && !audiotp->is_reversed)
+ || (GST_BUFFER_TIMESTAMP(MetaDataBuf) < audiotp->tail_prev && audiotp->is_reversed)) {
+ ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
+ if (MetaDataBuf) {
+ gst_buffer_unref (MetaDataBuf);
+ MetaDataBuf = NULL;
+ }
+
+ if (GST_FLOW_OK != ret) {
+ GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
+ if (buf) {
+ gst_buffer_unref (buf);
+ buf = NULL;
+ }
+ return ret;
+ }
+ } else {
+ GST_DEBUG_OBJECT(audiotp, "Dropping the buffer out of segment with time-stamp %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(MetaDataBuf)));
+ if (MetaDataBuf) {
+ gst_buffer_unref (MetaDataBuf);
+ MetaDataBuf = NULL;
+ }
+ }
+ }
+
+ audiotp->head_prev = headbuf_ts;
+ audiotp->tail_prev = tailbuf_ts;
+ }
+
+ MetaDataBuf = gst_buffer_new ();
+ if (NULL == MetaDataBuf) {
+ GST_ERROR_OBJECT (audiotp, "Failed to create memory...");
+ ret = GST_FLOW_ERROR;
+ goto error_exit;
+ }
+
+ /* copy buffer timestamps & FLAGS to metadata buffer */
+ gst_buffer_copy_metadata (MetaDataBuf, buf, GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_CAPS);
+ GST_BUFFER_SIZE(MetaDataBuf) = GST_BUFFER_SIZE(buf);
+ GST_DEBUG_OBJECT (audiotp, "Pushing into reverse queue data of size: %d", GST_BUFFER_SIZE(MetaDataBuf));
+
+ /* queue all buffer timestamps till we receive next discontinuity */
+ g_queue_push_tail (audiotp->reverse, MetaDataBuf);
+ if (buf) {
+ gst_buffer_unref (buf);
+ buf = NULL;
+ }
+ return GST_FLOW_OK;
+ }
+
+send_dummy:
+ {
+
+ /* Resetting the buffer data to zero */
+ memset(GST_BUFFER_DATA(buf), 0, GST_BUFFER_SIZE(buf));
+ gst_buffer_set_caps(buf, GST_PAD_CAPS(audiotp->srcpad));
+
+ ret = gst_pad_push(audiotp->srcpad, buf);
+ if (ret != GST_FLOW_OK) {
+ GST_ERROR("Failed to push buffer. reason: %s\n", gst_flow_get_name(ret));
+ buf = NULL;
+ goto error_exit;
+ }
+ return GST_FLOW_OK;
+ }
+
+error_exit:
+
+ GST_WARNING_OBJECT(audiotp, "Returning from audiotp's chain with reason - %s", gst_flow_get_name (ret));
+ if (buf) {
+ gst_buffer_unref (buf);
+ buf = NULL;
+ }
+ return ret;
+}
+
+
+static GstFlowReturn
+gst_audiotp_push_silent_frame (Gstaudiotp *audiotp, GstBuffer *MetaDataBuf)
+{
+
+ GstBuffer *out = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ out = gst_buffer_new_and_alloc(GST_BUFFER_SIZE(MetaDataBuf));
+ if(out == NULL) {
+ GST_ERROR_OBJECT (audiotp, "Failed to allocate memory...");
+ return GST_FLOW_ERROR;
+ }
+
+ /* Memset the data of the out buffer so that silent frame is sent */
+ memset(GST_BUFFER_DATA(out), 0, GST_BUFFER_SIZE(out));
+
+ gst_buffer_copy_metadata (out, MetaDataBuf, GST_BUFFER_COPY_FLAGS);
+ GST_BUFFER_OFFSET (out) = GST_BUFFER_OFFSET_END (out) = 0;
+ GST_BUFFER_SIZE(out) = GST_BUFFER_SIZE(MetaDataBuf);
+ GST_BUFFER_TIMESTAMP(out) = GST_BUFFER_TIMESTAMP(MetaDataBuf);
+ GST_BUFFER_DURATION(out) = GST_BUFFER_DURATION(MetaDataBuf);
+
+ GST_LOG_OBJECT(audiotp, "Out buffer ts =%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT ", size=%d",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(out)),
+ GST_TIME_ARGS(GST_BUFFER_DURATION(out)),
+ GST_BUFFER_SIZE(out));
+
+ gst_buffer_set_caps(out, GST_PAD_CAPS(audiotp->srcpad));
+
+ ret = gst_pad_push(audiotp->srcpad, out);
+ if (ret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (audiotp, "Failed to push buffer. reason: %s\n", gst_flow_get_name(ret));
+ out = NULL;
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_audiotp_plugin_init (GstPlugin *plugin)
+{
+ if (!gst_element_register (plugin, "audiotp", GST_RANK_PRIMARY, gst_audiotp_get_type())) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "audiotp",
+ "Audio trickplay plugin",
+ gst_audiotp_plugin_init,
+ VERSION,
+ "LGPL",
+ "Samsung Electronics Co",
+ "http://www.samsung.com")
--- /dev/null
+/*
+ * audiotp
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GST_AUDIOTP_H__
+#define __GST_AUDIOTP_H__
+
+#include <gst/gst.h>
+#include <stdlib.h>
+#include <string.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AUDIOTP (gst_audiotp_get_type())
+#define GST_AUDIOTP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOTP,Gstaudiotp))
+#define GST_AUDIOTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOTP,GstaudiotpClass))
+#define GST_AUDIOTP_GET_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS((klass),GST_TYPE_AUDIOTP,GstaudiotpClass))
+#define GST_IS_AUDIOTP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOTP))
+#define GST_IS_AUDIOTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOTP))
+#define GST_AUDIOTP_CAST(obj) ((Gstaudiotp *)(obj))
+
+typedef struct _Gstaudiotp Gstaudiotp;
+typedef struct _GstaudiotpClass GstaudiotpClass;
+
+struct _Gstaudiotp
+{
+ GstElement element;
+ GstPad *sinkpad;
+ GstPad *srcpad;
+ GQueue *reverse; /* used in reverse trickplay */
+ GstSegment segment;
+
+ /* Flag to indicate the new buffer recieved is discountinued in
+ its time-stamp */
+ gboolean discont;
+ gboolean is_reversed;
+ GstClockTime head_prev;
+ GstClockTime tail_prev;
+};
+
+struct _GstaudiotpClass
+{
+ GstElementClass parent_class;
+};
+
+GType gst_audiotp_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_AUDIOTP_H__ */
\ No newline at end of file
*/
static void
-_restore_sound_path (GstAvsysAudioSink *avsys_audio)
-{
- avsys_audio_route_policy_t cur_policy;
- if (avsys_audio_get_route_policy(&cur_policy) == AVSYS_STATE_SUCCESS) {
- GST_ERROR ("audio policy = %d, cur policy = %d", avsys_audio->audio_route_policy, cur_policy);
-
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_KEYTONE, AVSYS_AUDIO_PATH_EX_SPK, AVSYS_AUDIO_PATH_EX_NONE,
- (cur_policy == AVSYS_AUDIO_ROUTE_POLICY_HANDSET_ONLY)? AVSYS_AUDIO_PATH_OPTION_NONE : AVSYS_AUDIO_PATH_OPTION_JACK_AUTO))) {
- GST_ERROR_OBJECT(avsys_audio, "audio route set to current policy failed");
-
- }
- } else {
- GST_ERROR ("avsys_audio_get_route_policy() failed");
- }
-}
-
-static void
gst_avsysaudiosink_finalise (GObject * object)
{
GstAvsysAudioSink *sink = NULL;
g_mutex_free (sink->avsys_audio_lock);
g_mutex_free (sink->avsys_audio_reset_lock);
- /* restore to default path */
- if (sink->audio_route_policy == AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT) {
- _restore_sound_path (sink);
- }
G_OBJECT_CLASS (parent_class)->finalize (object);
}
case AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING:
GST_INFO_OBJECT(sink, "use external audio route setting");
break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_NORMAL:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_SPK,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_JACK_AUTO)))
- {
- GST_ERROR_OBJECT(sink, "audio route set to normal failed");
- }
- break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_SPK,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_DUAL_OUT)))
- {
- GST_ERROR_OBJECT(sink, "audio route set to dual output failed");
- }
- break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_HEADSET,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_NONE)))
- {
- GST_ERROR_OBJECT(sink, "audio route set to headset failed");
- }
- break;
default:
g_print("AVSYSAUDIOSINK :: Unknown audio route option %d\n", sink->audio_route_policy);
GST_ERROR_OBJECT(sink, "Unknown audio route option %d", sink->audio_route_policy);
/* set software volume table type */
avsys_audio->audio_param.vol_type = avsys_audio->volume_type;
avsys_audio->audio_param.priority = avsys_audio->sound_priority;
- avsys_audio->audio_param.bluetooth = avsys_audio->user_route_policy;
+ avsys_audio->audio_param.handle_route = avsys_audio->user_route_policy;
return TRUE;
}
case AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING:
GST_INFO_OBJECT(avsys_audio, "audio route uses external setting");
break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_NORMAL:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_SPK,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_JACK_AUTO)))
- {
- GST_ERROR_OBJECT(avsys_audio, "audio route set to normal failed");
- return GST_STATE_CHANGE_FAILURE;
- }
- break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_SPK,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_DUAL_OUT)))
- {
- GST_ERROR_OBJECT(avsys_audio, "audio route set to dual output failed");
- return GST_STATE_CHANGE_FAILURE;
- }
- break;
- case AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY:
- if(AVSYS_FAIL(avsys_audio_set_path_ex(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_HEADSET,
- AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_NONE)))
- {
- GST_ERROR_OBJECT(avsys_audio, "audio route set to headset failed");
- return GST_STATE_CHANGE_FAILURE;
- }
- break;
default:
GST_ERROR_OBJECT(avsys_audio, "Unknown audio route option %d\n", avsys_audio->audio_route_policy);
break;
switch (transition)
{
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- /* restore to default path */
- if (avsys_audio->audio_route_policy == AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT) {
- _restore_sound_path (avsys_audio);
- }
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
PROP_AUDIO_LATENCY,
};
+enum {
+ CAPTURE_UNCORK = 0,
+ CAPTURE_CORK,
+};
+
GType
gst_avsysaudiosrc_audio_latency_get_type (void)
{
static void gst_avsysaudiosrc_reset (GstAudioSrc * asrc);
static gboolean gst_avsysaudiosrc_avsys_close (GstAvsysAudioSrc *src);
static gboolean gst_avsysaudiosrc_avsys_open (GstAvsysAudioSrc *src);
+static gboolean gst_avsysaudiosrc_avsys_cork (GstAvsysAudioSrc *avsysaudiosrc, int cork);
+static gboolean gst_avsysaudiosrc_avsys_start (GstAvsysAudioSrc *src);
+static gboolean gst_avsysaudiosrc_avsys_stop (GstAvsysAudioSrc *src);
#if defined(_USE_CAPS_)
static GstCaps *gst_avsysaudiosrc_detect_rates (GstObject * obj, avsys_pcm_hw_params_t * hw_params, GstCaps * in_caps);
static GstCaps *gst_avsysaudiosrc_detect_channels (GstObject * obj,avsys_pcm_hw_params_t * hw_params, GstCaps * in_caps);
{
GstAvsysAudioSrc *avsysaudiosrc = NULL;
guint p_time = 0, b_time = 0;
-// gint avsys_result = 0;
avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
GST_ERROR("avsysaudiosrc_parse_spec failed");
return FALSE;
}
- //g_print("AVSYSAUDIOSRC :: avsysaudiosrc_parse_spec() success\n");
/*open avsys audio*/
if (!gst_avsysaudiosrc_avsys_open (avsysaudiosrc))
/* Ring buffer size */
if (AVSYS_STATE_SUCCESS ==
avsys_audio_get_period_buffer_time(avsysaudiosrc->audio_handle, &p_time, &b_time))
- {
+ {
if(p_time == 0 || b_time == 0)
return FALSE;
avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
- /*close*/
+ /*close*/
GST_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
if(!gst_avsysaudiosrc_avsys_close(avsysaudiosrc))
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
{
GST_DEBUG ("GST_STATE_CHANGE_PAUSED_TO_PLAYING\n") ;
+ /* Capture Start */
+ if (!gst_avsysaudiosrc_avsys_start (avsys)) {
+ GST_ERROR("gst_avsysaudiosrc_avsys_start failed");
+ }
break ;
}
default:
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
{
GST_DEBUG ("GST_STATE_CHANGE_PLAYING_TO_PAUSED\n") ;
+ /* Capture Stop */
+ if (!gst_avsysaudiosrc_avsys_stop (avsys)) {
+ GST_ERROR("gst_avsysaudiosrc_avsys_stop failed");
+ }
break ;
}
case GST_STATE_CHANGE_PAUSED_TO_READY:
}
static gboolean
+gst_avsysaudiosrc_avsys_cork (GstAvsysAudioSrc *avsysaudiosrc, int cork)
+{
+ int avsys_result = avsys_audio_cork (avsysaudiosrc->audio_handle, cork);
+ if (avsys_result != AVSYS_STATE_SUCCESS) {
+ GST_ERROR_OBJECT(avsysaudiosrc, "avsys_audio_cork() error. [0x%x]\n", avsys_result);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_avsysaudiosrc_avsys_start (GstAvsysAudioSrc *avsysaudiosrc)
+{
+ gboolean result;
+
+ GST_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
+ result = gst_avsysaudiosrc_avsys_cork(avsysaudiosrc, CAPTURE_UNCORK);
+ GST_AVSYS_AUDIO_SRC_UNLOCK (avsysaudiosrc);
+
+ return result;
+}
+
+static gboolean
+gst_avsysaudiosrc_avsys_stop (GstAvsysAudioSrc *avsysaudiosrc)
+{
+ gboolean result;
+
+ GST_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
+ result = gst_avsysaudiosrc_avsys_cork(avsysaudiosrc, CAPTURE_CORK);
+ GST_AVSYS_AUDIO_SRC_UNLOCK (avsysaudiosrc);
+
+ return result;
+}
+
+static gboolean
gst_avsysaudiosrc_avsys_open (GstAvsysAudioSrc *avsysaudiosrc)
{
int avsys_result = 0;
GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (
"video/x-raw-yuv, "
+ "format = (fourcc){YV12}, "
"framerate = (fraction) [ 0, MAX ], "
"width = (int) [ 1, MAX ], "
- "height = (int) [ 1, MAX ], "
- "format = (fourcc) {YV12};"
- "video/x-raw-rgb,"
- "bpp = (int)32,"
- "depth = (int)24")
+ "height = (int) [ 1, MAX ]; "
+ "video/x-raw-yuv, "
+ "format = (fourcc){I420}, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], "
+ "height = (int) [ 1, MAX ]; "
+ "video/x-raw-rgb, "
+ "bpp = (int)32, "
+ "depth = (int)24 ")
);
static GstElementDetails AvsysMemSink_details = {
gboolean error;
/*register the exact name you can find in the framework*/
error = gst_element_register (plugin, "avsysaudiosink",
- GST_RANK_NONE,
+ GST_RANK_PRIMARY + 100,
GST_TYPE_AVSYS_AUDIO_SINK);
error = gst_element_register (plugin, "avsysmemsink",
],
[GST_EXT_USE_EXT_DRMSRC=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_DRMSRC, test "x$GST_EXT_USE_EXT_DRMSRC" = "xyes")
-
-if test "x$GST_EXT_USE_EXT_DRMSRC" = "xyes"; then
- PKG_CHECK_MODULES(DRM_SERVICE, drm-service)
- AC_SUBST(DRM_SERVICE_CFLAGS)
- AC_SUBST(DRM_SERVICE_LIBS)
-fi
-
dnl use ext-toggle --------------------------------------------------------------------------
AC_ARG_ENABLE(ext-toggle, AC_HELP_STRING([--enable-ext-toggle], [using toggle]),
[
],
[GST_EXT_USE_EXT_PD_PUSHSRC=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_PD_PUSHSRC, test "x$GST_EXT_USE_EXT_PD_PUSHSRC" = "xyes")
+dnl use ext-audiotp --------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-audiotp, AC_HELP_STRING([--enable-ext-audiotp], [using audiotp]),
+[
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_AUDIOTP=yes ;;
+ no) GST_EXT_USE_EXT_AUDIOTP=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-audiotp) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_AUDIOTP=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_AUDIOTP, test "x$GST_EXT_USE_EXT_AUDIOTP" = "xyes")
+
+dnl use ext-piffdemux --------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-piffdemux, AC_HELP_STRING([--enable-ext-piffdemux], [using piffdemux]),
+[
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_PIFFDEMUX=yes ;;
+ no) GST_EXT_USE_EXT_PIFFDEMUX=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-piffdemux) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_PIFFDEMUX=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_PIFFDEMUX, test "x$GST_EXT_USE_EXT_PIFFDEMUX" = "xyes")
+
+dnl use ext-ssdemux --------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-ssdemux, AC_HELP_STRING([--enable-ext-ssdemux], [using ssdemux]),
+[
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_SSDEMUX=yes ;;
+ no) GST_EXT_USE_EXT_SSDEMUX=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-ssdemux) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_SSDEMUX=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_SSDEMUX, test "x$GST_EXT_USE_EXT_SSDEMUX" = "xyes")
+
AC_OUTPUT(
Makefile
toggle/src/Makefile
drmsrc/Makefile
drmsrc/src/Makefile
+audiotp/Makefile
+audiotp/src/Makefile
+piffdemux/Makefile
+piffdemux/src/Makefile
+ssdemux/Makefile
+ssdemux/src/Makefile
)
+++ /dev/null
-gst-plugins-ext0.10 (0.1.5-1) unstable; urgency=low
-
- * Initial Release
- * Git: pkgs/g/gst-plugins-ext0.10
- * Tag: gst-plugins-ext0.10_0.1.5-1
-
- -- Sangchul Lee <sc11.lee@samsung.com> Wed, 07 Dec 2011 12:40:00 +0900
-
+++ /dev/null
-Source: gst-plugins-ext0.10
-Section: libs
-Priority: extra
-Maintainer: JongHyuk Choi <jhchoi.choi@samsung.com>, Seungbae Shin <seungbae.shin@samsung.com>, Younghwan Ahn <younghwan_.an@samsung.com>, Jeongmo Yang <jm80.yang@samsung.com>, Sangchul Lee <sc11.lee@samsung.com>
-Build-Depends: debhelper (>= 5), libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, libavsystem-dev, libmm-ta-dev, drm-service-dev, libecore-dev, libevas-dev
-Standards-Version: 3.7.2
-
-Package: gstreamer0.10-plugins-ext
-Section: libs
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, libgstreamer0.10-0, libgstreamer-plugins-base0.10-0, libavsystem-0, libmm-ta, drm-service-0, libcamsrcjpegenc
-Description: GStreamer extra plugins (common)
-
-Package: gstreamer0.10-plugins-ext-dbg
-Section: debug
-Architecture: armel
-Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer0.10-plugins-ext (= ${Source-Version})
-Description: GStreamer extra plugins (all) (unstripped)
+++ /dev/null
-Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
-
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 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 Lesser General Public
-License for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc., 51
-Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
+++ /dev/null
-@PREFIX@/lib/gstreamer-0.10/libgstavsyssink.so
-@PREFIX@/lib/gstreamer-0.10/libgstencodebin.so
-@PREFIX@/lib/gstreamer-0.10/libgsttoggle.so
-@PREFIX@/lib/gstreamer-0.10/libgstavsysaudiosrc.so
-@PREFIX@/lib/gstreamer-0.10/libgstevasimagesink.so
-@PREFIX@/lib/gstreamer-0.10/libgstdrmsrc.so
-@PREFIX@/lib/gstreamer-0.10/libgstpdpushsrc.so
+++ /dev/null
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
-DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_GNU_OS)
-
-CFLAGS += -Wall -g
-LDFLAGS ?=
-PREFIX ?= /usr
-DATADIR ?= /opt
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -O0
-else
- CFLAGS += -O2
-endif
-
-MODULES=
-# --disable-ext-evasimagesink
-
-LDFLAGS += -Wl,--rpath=$(PREFIX)/lib -Wl,--as-needed
-
-config.status: configure
- dh_testdir
- # Add here commands to configure the package.
- ./autogen.sh
- CFLAGS="$(CFLAGS) -DGST_EXT_TIME_ANALYSIS -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" " LDFLAGS="$(LDFLAGS)" ./configure $(MODULES) $(OPTION) --prefix=$(PREFIX)
-
-build: build-stamp
-
-build-stamp: config.status
- dh_testdir
-
- # Add here commands to compile the package.
- $(MAKE)
- #docbook-to-man debian/ncurses.sgml > ncurses.1
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- cat $$f > $${f%.in}; \
- sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
- sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
- done
-
- touch $@
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp
-
- # Add here commands to clean up after the build process.
- -$(MAKE) distclean
-ifneq "$(wildcard /usr/share/misc/config.sub)" ""
- cp -f /usr/share/misc/config.sub config.sub
-endif
-ifneq "$(wildcard /usr/share/misc/config.guess)" ""
- cp -f /usr/share/misc/config.guess config.guess
-endif
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- rm -f $${f%.in}; \
- done
-
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs -s
-
- # Add here commands to install the package into debian/ncurses.
- $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
-#ifneq (, $(findstring arm, $(DEB_HOST_ARCH)))
-# execstack -c $(EXECSTACK_FILES)
-#endif
-
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_installchangelogs -s
-# dh_installdocs
-# dh_installexamples
- dh_install -s --list-missing --sourcedir=debian/tmp
-# dh_installmenu
-# dh_installdebconf
-# dh_installlogrotate
-# dh_installemacsen
-# dh_installpam
-# dh_installmime
-# dh_python
-# dh_installinit
-# dh_installcron
-# dh_installinfo
-# dh_installman
- dh_link -s
- dh_strip -s --dbg-package=gstreamer0.10-plugins-ext-dbg
- dh_compress -s
- dh_fixperms -s
-# dh_perl
- dh_makeshlibs -s
- dh_installdeb -s
- dh_shlibdeps -s
- dh_gencontrol -s
- dh_md5sums -s
- dh_builddeb -s
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
-# plugindir is set in configure\r
-\r
-##############################################################################\r
-# change libgstplugin.la to something more suitable, e.g. libmysomething.la #\r
-##############################################################################\r
-plugin_LTLIBRARIES = libgstdrmsrc.la\r
-\r
-##############################################################################\r
-# for the next set of variables, rename the prefix if you renamed the .la, #\r
-# e.g. libgstplugin_la_SOURCES => libmysomething_la_SOURCES #\r
-# libgstplugin_la_CFLAGS => libmysomething_la_CFLAGS #\r
-# libgstplugin_la_LIBADD => libmysomething_la_LIBADD #\r
-# libgstplugin_la_LDFLAGS => libmysomething_la_LDFLAGS #\r
-##############################################################################\r
-\r
-# sources used to compile this plug-in\r
-libgstdrmsrc_la_SOURCES = gstdrmsrc.c\r
-\r
-# flags used to compile this plugin\r
-# add other _CFLAGS and _LIBS as needed\r
-libgstdrmsrc_la_CFLAGS = $(GST_CFLAGS) $(DRM_SERVICE_CFLAGS) $(MMTA_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64\r
-libgstdrmsrc_la_LIBADD = $(GST_LIBS) $(DRM_SERVICE_LIBS) $(GST_BASE_LIBS) $(MMTA_LIBS)\r
-libgstdrmsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)\r
-\r
-# headers we need but don't want installed\r
-noinst_HEADERS = gstdrmsrc.h\r
-\r
+# plugindir is set in configure
+
+##############################################################################
+# change libgstplugin.la to something more suitable, e.g. libmysomething.la #
+##############################################################################
+plugin_LTLIBRARIES = libgstdrmsrc.la
+
+##############################################################################
+# for the next set of variables, rename the prefix if you renamed the .la, #
+# e.g. libgstplugin_la_SOURCES => libmysomething_la_SOURCES #
+# libgstplugin_la_CFLAGS => libmysomething_la_CFLAGS #
+# libgstplugin_la_LIBADD => libmysomething_la_LIBADD #
+# libgstplugin_la_LDFLAGS => libmysomething_la_LDFLAGS #
+##############################################################################
+
+# sources used to compile this plug-in
+libgstdrmsrc_la_SOURCES = gstdrmsrc.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstdrmsrc_la_CFLAGS = $(GST_CFLAGS) $(MMTA_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+libgstdrmsrc_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(MMTA_LIBS)
+libgstdrmsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gstdrmsrc.h
+
#endif
#include "gstdrmsrc.h"
-
#define LOG_TRACE(message) //g_print("DRM_SRC: %s: %d: %s - %s \n", __FILE__, __LINE__, __FUNCTION__, message);
#define GST_TAG_PLAYREADY "playready_file_path"
{
ARG_0,
ARG_LOCATION,
- ARG_FD,
- IS_DRM
+ ARG_FD
};
static void gst_drm_src_finalize (GObject * object);
static void gst_drm_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static gboolean gst_drm_src_get_size (GstBaseSrc * src, guint64 * size);
static GstFlowReturn gst_drm_src_create (GstBaseSrc * src, guint64 offset, guint length, GstBuffer ** buffer);
static void gst_drm_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
+
/**
* This function does the following:
* 1. Initializes GstDrmSrc ( defines gst_drm_get_type)
g_object_class_install_property (gobject_class, ARG_LOCATION,
g_param_spec_string ("location", "File Location",
"Location of the file to read", NULL, G_PARAM_READWRITE));
- g_object_class_install_property (gobject_class, IS_DRM,
- g_param_spec_boolean ("is-drm", "whether selected file type is drm or not",
- "true, false", FALSE, G_PARAM_READWRITE));
+
// 2. Assigns the function pointers GObject class attributes
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_drm_src_finalize);
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_drm_src_start);
src->fd = 0;
src->uri = NULL;
src->is_regular = FALSE;
- src->drm_file = FALSE;
src->seekable = FALSE;
- src->hfile = NULL;
- src->event_posted = FALSE;
- src->is_playready = FALSE;
PROFILE_INIT;
}
/**
case ARG_FD:
g_value_set_int (value, src->fd);
break;
- case IS_DRM:
- g_value_set_boolean(value, src->drm_file);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
-/**
- * This function does the following:
- * 1. Seeks to the specified position for DRM file.
- * 2. Allocates a buffer to push the data for DRM file.
- * 3. Reads from the file and sets the related params for DRM file.
- *
- * @param i_pDrmSrc [in] GstDrmSrc Structure
- * @param i_uiOffset [in] offset of the file to seek
- * @param length [in] size of the data in bytes
- * @param o_pBbuffer [out] GstBuffer to hold the contents
- *
- * @return GstFlowReturn Returns GST_FLOW_OK on success and ERROR on failure
- */
-static GstFlowReturn gst_drm_src_create_read_drm_file (GstDrmSrc* i_pDrmSrc, guint64 i_uiOffset, guint length, GstBuffer ** o_pBbuffer)
-{
- GstBuffer *buf = NULL;
- unsigned int readSize;
- DRM_RESULT in_res = DRM_RESULT_SUCCESS;
-PROFILE_FUNC_BEGIN;
- // 1. Seeks to the specified position for DRM file.
- if (G_UNLIKELY (i_pDrmSrc->read_position != i_uiOffset))
- {
- in_res =drm_svc_seek_file(i_pDrmSrc->hfile, i_uiOffset, DRM_SEEK_SET);
-
- if(in_res != DRM_RESULT_SUCCESS)
- goto FAILED;
-
- i_pDrmSrc->read_position = i_uiOffset;
- }
- // 2. Allocates a buffer to push the data for DRM file.
- buf = gst_buffer_new_and_alloc (length);
- if(buf == NULL)
- {
- LOG_TRACE("Exit on error");
- return GST_FLOW_ERROR;
- }
- // 3. Reads from the file and sets the related params for DRM file.
- in_res = drm_svc_read_file(i_pDrmSrc->hfile, GST_BUFFER_DATA(buf), length, &readSize);
-
- if (in_res != DRM_RESULT_SUCCESS)
- goto FAILED;
-
- if(readSize <= 0)
- {
- LOG_TRACE("Exit on error");
- return GST_FLOW_ERROR;
- }
-
- #if 0 // Drm service can give lesser size block than requested thing.
- if (G_UNLIKELY ((guint) readSize < length && i_pDrmSrc->seekable))
- {
- GST_ELEMENT_ERROR (i_pDrmSrc, RESOURCE, READ, (NULL),("unexpected end of file."));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
- #endif
-
- if (G_UNLIKELY (readSize == 0 && length > 0))
- {
- GST_DEBUG ("non-regular file hits EOS");
- gst_buffer_unref (buf);
- return GST_FLOW_UNEXPECTED;
- }
- length = readSize;
- GST_BUFFER_SIZE (buf) = length;
- GST_BUFFER_OFFSET (buf) = i_uiOffset;
- GST_BUFFER_OFFSET_END (buf) = i_uiOffset + length;
- *o_pBbuffer = buf;
- i_pDrmSrc->read_position += length;
-PROFILE_FUNC_END;
-
- return GST_FLOW_OK;
-FAILED:
- {
- GST_ELEMENT_ERROR (i_pDrmSrc, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
- return GST_FLOW_ERROR;
- }
-}
/**
* This function does the following:
* 1. Seeks to the specified position.
{
GstDrmSrc *src = GST_DRM_SRC (basesrc);
- if (src->is_playready && src->event_posted == FALSE) {
- GstTagList *tags = NULL;
- GST_DEBUG_OBJECT (src, "posting playready tags");
- tags = gst_tag_list_new_full (GST_TAG_PLAYREADY, src->filename, NULL);
- if (tags) {
- GstPad* src_pad = gst_element_get_static_pad (src, "src");
- if (src_pad) {
- src->event_posted = gst_pad_push_event (src_pad, gst_event_new_tag (tags) );
- GST_DEBUG_OBJECT (src, "posting tags returns [%d]", src->event_posted);
- gst_object_unref (src_pad);
- }
- }
- }
-
// 1. Calls DRM file read chain method for drm files.
- if(src->drm_file == TRUE)
- return gst_drm_src_create_read_drm_file (src, offset, length, buffer);
+
// 2. Calls normal file read chain method for standard files.
return gst_drm_src_create_read (src, offset, length, buffer);
}
static gboolean gst_drm_src_get_size (GstBaseSrc * basesrc, guint64 * size)
{
struct stat stat_results;
- GstDrmSrc *src;
- src = GST_DRM_SRC (basesrc);
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ unsigned int offset;
+
// 1. Gets the filesize for drm file by using seek oprations
- if(src->drm_file==TRUE)
- {
- drm_svc_seek_file(src->hfile, 0, DRM_SEEK_END);
- *size = drm_svc_tell_file(src->hfile);
- drm_svc_seek_file(src->hfile, 0, DRM_SEEK_SET);
- src->read_position = 0;
- return TRUE;
- }
- if (!src->seekable)
- {
- return FALSE;
- }
+
// 2. Gets the file size for standard file by using statistics
if (fstat (src->fd, &stat_results) < 0)
return FALSE;
* This function does the following:
* 1. Checks the filename
* 2. Opens the file and check statistics of the file
- * 3. Checks whether DRM file or not.
- * 4. Checks the DRM file type (supports only for OMA) if it is DRM
- * 5. Opens the DRM file if it is DRM
- * 6. Gets the DRM_FILE_HANDLE and sets the drm, seekable and regular flag.
* 7. Checks the seeking for standard files
*
* @param basesrc [in] BaseSrc Structure
{
GstDrmSrc *src = GST_DRM_SRC (basesrc);
struct stat stat_results;
- DRM_BOOL res = DRM_TRUE;
- DRM_FILE_TYPE type = DRM_FILE_TYPE_NONE;
- DRM_RESULT in_res = DRM_RESULT_SUCCESS;
off_t ret;
PROFILE_FUNC_BEGIN;
// 1. Checks the filename
return FALSE;
}
src->read_position = 0;
- /* DRM Related code */
- // 3. Checks whether DRM file or not.
-PROFILE_BLOCK_BEGIN("drmstart");
- res = drm_svc_is_drm_file(src->filename);
- if(res == DRM_TRUE)
- type = drm_svc_get_drm_type(src->filename);
- GST_LOG_OBJECT (src, "is_drm=[%d], drm_type=[%d]", res, type);
-
- /* We handles as DRM file if it is drm with OMA type */
- if(res == DRM_TRUE && type == DRM_FILE_TYPE_OMA)
- {
-#if 0 // Do not check here.
- // 4. Checks the DRM file type (supports only for OMA) if it is DRM
- if(drm_svc_get_drm_type(src->filename) != DRM_FILE_TYPE_OMA)
- {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("File \"%s\" is not OMA DRM.", src->filename), (NULL));
- return FALSE;
- }
-#endif
-
- // 5. Opens the DRM file if it is DRM
- PROFILE_BLOCK_BEGIN("drmopen");
- in_res=drm_svc_open_file(src->filename, DRM_PERMISSION_PLAY, &(src->hfile));
- if(in_res != DRM_RESULT_SUCCESS)
- {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("File \"%s\" open fail.", src->filename), (NULL));
- return FALSE;
- }
- PROFILE_BLOCK_END("drmopen");
-
- // 6. Gets the DRM_FILE_HANDLE and sets the drm, seekable and regular flags.
- res = drm_svc_is_drm_file_handle(src->hfile);
- if(res == DRM_TRUE)
- {
- drm_svc_seek_file(src->hfile, 0, DRM_SEEK_END);
- drm_svc_seek_file(src->hfile, 0, DRM_SEEK_SET);
- }
- src->seekable = TRUE;
- src->is_regular = TRUE;
- src->drm_file = TRUE;
-PROFILE_BLOCK_END("drmstart");
-
- LOG_TRACE("Exit");
- return TRUE;
- }
-
- if(res == DRM_TRUE && type == DRM_FILE_TYPE_PLAYREADY) {
- src->is_playready = TRUE;
- src->event_posted = FALSE;
- }
// 7. Checks the seeking for standard files
if (S_ISREG (stat_results.st_mode))
src->is_regular = TRUE;
ret = lseek (src->fd, 0, SEEK_END);
- if (res < 0)
+ if (ret < 0)
{
GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek "
"failed: %s", g_strerror (errno));
{
GstDrmSrc *src = GST_DRM_SRC (basesrc);
- if (src->hfile) {
- drm_svc_close_file(src->hfile);
- src->hfile = NULL;
- }
// 1. Closes the file desciptor and resets the flags
if(src->fd > 0)
close (src->fd);
src->fd = 0;
src->is_regular = FALSE;
- src->event_posted = FALSE;
- src->is_playready = FALSE;
// PROFILE_SHOW_RESULT;
return TRUE;
}
#include <sys/types.h>
#include <gst/gst.h>
#include <gst/base/gstbasesrc.h>
-#include "drm-service.h"
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
guint64 read_position;
gboolean seekable;
gboolean is_regular;
- gboolean drm_file; // flag indicating drm file
- DRM_FILE_HANDLE hfile; // drm file handler
-
- gboolean is_playready;
- gboolean event_posted;
};
struct _GstDrmSrcClass
libgstevasimagesink_la_SOURCES = gstevasimagesink.c gstevasimagesink.h
# compiler and linker flags used to compile this plugin, set in configure.ac
-libgstevasimagesink_la_CFLAGS = $(GST_CFLAGS) $(GST_VIDEO_CFLAGS) $(EFL_CFLAGS)
-libgstevasimagesink_la_LIBADD = $(GST_LIBS) $(GST_VIDEO_LIBS) $(EFL_LIBS)
+libgstevasimagesink_la_CFLAGS = $(GST_CFLAGS) $(GST_VIDEO_CFLAGS) $(EFL_CFLAGS) $(MMTA_CFLAGS)
+libgstevasimagesink_la_LIBADD = $(GST_LIBS) $(GST_VIDEO_LIBS) $(EFL_LIBS) $(MMTA_LIBS)
libgstevasimagesink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstevasimagesink_la_LIBTOOLFLAGS = --tag=disable-static
*/
#ifdef HAVE_CONFIG_H
-# include <config.h>
+#include <config.h>
#endif
#include <sys/types.h>
#define COLOR_DEPTH 4
#define GL_X11_ENGINE "gl_x11"
+GMutex *instance_lock;
+guint instance_lock_count;
+
static inline gboolean
is_evas_image_object (Evas_Object *obj)
{
GST_BOILERPLATE (GstEvasImageSink, gst_evas_image_sink, GstVideoSink, GST_TYPE_VIDEO_SINK);
-static void gst_evas_image_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_evas_image_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
-static gboolean gst_evas_image_sink_set_caps (GstBaseSink * base_sink, GstCaps * caps);
-static GstFlowReturn gst_evas_image_sink_show_frame (GstVideoSink * video_sink, GstBuffer * buf);
+static void gst_evas_image_sink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gst_evas_image_sink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static gboolean gst_evas_image_sink_set_caps (GstBaseSink *base_sink, GstCaps *caps);
+static GstFlowReturn gst_evas_image_sink_show_frame (GstVideoSink *video_sink, GstBuffer *buf);
+static gboolean gst_evas_image_sink_event (GstBaseSink *sink, GstEvent *event);
+static GstStateChangeReturn gst_evas_image_sink_change_state (GstElement *element, GstStateChange transition);
+static void evas_image_sink_cb_del_eo (void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void evas_image_sink_cb_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
static void
gst_evas_image_sink_base_init (gpointer gclass)
}
static void
-gst_evas_image_sink_class_init (GstEvasImageSinkClass * klass)
+gst_evas_image_sink_class_init (GstEvasImageSinkClass *klass)
{
GObjectClass *gobject_class;
GstBaseSinkClass *gstbasesink_class;
GstVideoSinkClass *gstvideosink_class;
+ GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstbasesink_class = GST_BASE_SINK_CLASS (klass);
gstvideosink_class = GST_VIDEO_SINK_CLASS (klass);
+ gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = gst_evas_image_sink_set_property;
gobject_class->get_property = gst_evas_image_sink_get_property;
g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT_SHOW,
g_param_spec_boolean ("visible", "Show Evas Object", "When disabled, evas object does not show", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- gstvideosink_class->show_frame = gst_evas_image_sink_show_frame;
- gstbasesink_class->set_caps = gst_evas_image_sink_set_caps;
+ gstvideosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_evas_image_sink_show_frame);
+ gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_evas_image_sink_set_caps);
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_evas_image_sink_event);
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_evas_image_sink_change_state);
}
static void
gst_evas_image_sink_fini (gpointer data, GObject *obj)
{
+ GST_INFO ("enter");
+
GstEvasImageSink *esink = GST_EVASIMAGESINK (obj);
if (!esink) {
return;
}
- if (esink->epipe) {
- ecore_pipe_del (esink->epipe);
- }
if (esink->oldbuf) {
gst_buffer_unref (esink->oldbuf);
}
+
+ g_mutex_lock (instance_lock);
+ instance_lock_count--;
+ g_mutex_unlock (instance_lock);
+ if (instance_lock_count == 0) {
+ g_mutex_free (instance_lock);
+ instance_lock = NULL;
+ }
}
static void
if (!esink->eo) {
return;
}
+ if (GST_STATE(esink) < GST_STATE_PAUSED) {
+ GST_WARNING ("WRONG-STATE(%d) for rendering, skip this frame", GST_STATE(esink));
+ return;
+ }
memcpy (&buf, buffer, sizeof (GstBuffer *));
if (!buf) {
- GST_ERROR ("There is no buffer\n");
+ GST_ERROR ("There is no buffer");
+ return;
+ }
+ if (esink->present_data_addr == -1) {
+ /* if present_data_addr is -1, we don't use this member variable */
+ } else if (esink->present_data_addr != GST_BUFFER_DATA (buf)) {
+ GST_WARNING ("skip rendering this buffer, present_data_addr:%x, GST_BUFFER_DATA(buf):%x", esink->present_data_addr,GST_BUFFER_DATA(buf));
return;
}
+ MMTA_ACUM_ITEM_BEGIN("eavsimagesink _cb_pipe total", FALSE);
+
+ if ( !esink->is_evas_object_size_set && esink->w > 0 && esink->h > 0) {
+ evas_object_image_size_set (esink->eo, esink->w, esink->h);
+ GST_DEBUG("evas_object_image_size_set(), width(%d),height(%d)",esink->w,esink->h);
+ esink->is_evas_object_size_set = TRUE;
+ }
if (esink->gl_zerocopy) {
img_data = evas_object_image_data_get (esink->eo, EINA_TRUE);
if (!img_data || !GST_BUFFER_DATA(buf)) {
- GST_WARNING ("Cannot get image data from evas object or cannot get gstbuffer data\n");
+ GST_WARNING ("Cannot get image data from evas object or cannot get gstbuffer data");
evas_object_image_data_set(esink->eo, img_data);
} else {
GST_DEBUG ("img_data(%x), GST_BUFFER_DATA(buf):%x, esink->w(%d),esink->h(%d)",img_data,GST_BUFFER_DATA(buf),esink->w,esink->h);
- memcpy (img_data, GST_BUFFER_DATA (buf), esink->w * esink->h * COLOR_DEPTH);
+ __ta__("evasimagesink memcpy in _cb_pipe", memcpy (img_data, GST_BUFFER_DATA (buf), esink->w * esink->h * COLOR_DEPTH););
evas_object_image_pixels_dirty_set (esink->eo, 1);
evas_object_image_data_set(esink->eo, img_data);
}
gst_buffer_unref (buf);
} else {
+ GST_DEBUG ("GST_BUFFER_DATA(buf):%x",GST_BUFFER_DATA(buf));
evas_object_image_data_set (esink->eo, GST_BUFFER_DATA (buf));
evas_object_image_pixels_dirty_set (esink->eo, 1);
if (esink->oldbuf) {
}
esink->oldbuf = buf;
}
+
+ MMTA_ACUM_ITEM_END("eavsimagesink _cb_pipe total", FALSE);
}
static void
-gst_evas_image_sink_init (GstEvasImageSink * esink, GstEvasImageSinkClass * gclass)
+gst_evas_image_sink_init (GstEvasImageSink *esink, GstEvasImageSinkClass *gclass)
{
+ GST_INFO ("enter");
+
esink->eo = NULL;
+ esink->epipe = NULL;
+ esink->object_show = FALSE;
+ esink->gl_zerocopy = FALSE;
+ esink->is_evas_object_size_set = FALSE;
+ esink->present_data_addr = -1;
+
+ if(!instance_lock) {
+ instance_lock = g_mutex_new();
+ }
+ g_mutex_lock (instance_lock);
+ instance_lock_count++;
+ g_mutex_unlock (instance_lock);
+
g_object_weak_ref (G_OBJECT (esink), gst_evas_image_sink_fini, NULL);
}
if (!esink) {
return;
}
+
+ evas_object_event_callback_del (esink->eo, EVAS_CALLBACK_RESIZE, evas_image_sink_cb_resize_event);
if (esink->oldbuf) {
gst_buffer_unref (esink->oldbuf);
esink->oldbuf = NULL;
esink->eo = NULL;
}
- ecore_pipe_del (esink->epipe);
- esink->epipe = NULL;
+}
+
+static void
+evas_image_sink_cb_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ int w = 0;
+ int h = 0;
+ GstEvasImageSink *esink = data;
+ if (!esink) {
+ return;
+ }
+
+ evas_object_geometry_get(esink->eo, NULL, NULL, &w, &h);
+ if (!w || !h) {
+ GST_WARNING ("evas object size (w:%d,h:%d) was not set",w,h);
+ } else {
+ evas_object_image_fill_set(esink->eo, 0, 0, w, h);
+ GST_DEBUG ("evas object fill set (w:%d,h:%d)",w,h);
+ }
}
static int
return FALSE;
}
+static int
+evas_image_sink_event_parse_data (GstEvasImageSink *esink, GstEvent *event)
+{
+ const GstStructure *st;
+ guint st_data_addr = 0;
+ gint st_data_width = 0;
+ gint st_data_height = 0;
+
+ g_return_val_if_fail (event != NULL, FALSE);
+ g_return_val_if_fail (esink != NULL, FALSE);
+
+ if (GST_EVENT_TYPE (event) != GST_EVENT_CUSTOM_DOWNSTREAM_OOB) {
+ GST_WARNING ("it's not a custom downstream oob event");
+ return -1;
+ }
+ st = gst_event_get_structure (event);
+ if (st == NULL || !gst_structure_has_name (st, "GstStructureForCustomEvent")) {
+ GST_WARNING ("structure in a given event is not proper");
+ return -1;
+ }
+ if (!gst_structure_get_uint (st, "data-addr", &st_data_addr)) {
+ GST_WARNING ("parsing data-addr failed");
+ return -1;
+ }
+ esink->present_data_addr = st_data_addr;
+
+ return 0;
+}
+
+static gboolean
+gst_evas_image_sink_event (GstBaseSink *sink, GstEvent *event)
+{
+ GstEvasImageSink *esink = GST_EVASIMAGESINK (sink);
+ GstMessage *msg;
+ gchar *str;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ GST_DEBUG ("GST_EVENT_FLUSH_START");
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ GST_DEBUG ("GST_EVENT_FLUSH_STOP");
+ break;
+ case GST_EVENT_EOS:
+ GST_DEBUG ("GST_EVENT_EOS");
+ break;
+ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
+ if(!evas_image_sink_event_parse_data(esink, event)) {
+ GST_DEBUG ("GST_EVENT_CUSTOM_DOWNSTREAM_OOB, present_data_addr:%x",esink->present_data_addr);
+ } else {
+ GST_ERROR ("evas_image_sink_event_parse_data() failed");
+ }
+ break;
+ default:
+ break;
+ }
+ if (GST_BASE_SINK_CLASS (parent_class)->event) {
+ return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
+ } else {
+ return TRUE;
+ }
+}
+
+static GstStateChangeReturn
+gst_evas_image_sink_change_state (GstElement *element, GstStateChange transition)
+{
+ GstStateChangeReturn ret_state = GST_STATE_CHANGE_SUCCESS;
+ GstEvasImageSink *esink = NULL;
+ esink = GST_EVASIMAGESINK(element);
+ int ret = 0;
+
+ if(!esink) {
+ GST_ERROR("can not get evasimagesink from element");
+ }
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ GST_INFO ("*** STATE_CHANGE_NULL_TO_READY ***");
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_INFO ("*** STATE_CHANGE_READY_TO_PAUSED ***");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ GST_INFO ("*** STATE_CHANGE_PAUSED_TO_PLAYING ***");
+ break;
+ default:
+ break;
+ }
+
+ ret_state = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_INFO ("*** STATE_CHANGE_PLAYING_TO_PAUSED ***");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_INFO ("*** STATE_CHANGE_PAUSED_TO_READY ***");
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_INFO ("*** STATE_CHANGE_READY_TO_NULL ***");
+ evas_object_event_callback_del(esink->eo, EVAS_CALLBACK_DEL, evas_image_sink_cb_del_eo);
+ evas_object_event_callback_del(esink->eo, EVAS_CALLBACK_RESIZE, evas_image_sink_cb_resize_event);
+ if (esink->epipe) {
+ ecore_pipe_del (esink->epipe);
+ esink->epipe = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret_state;
+}
+
static void
-gst_evas_image_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+gst_evas_image_sink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
GstEvasImageSink *esink = GST_EVASIMAGESINK (object);
Evas_Object *eo;
+ g_mutex_lock (instance_lock);
+
switch (prop_id) {
case PROP_EVAS_OBJECT:
eo = g_value_get_pointer (value);
if (is_evas_image_object (eo)) {
- esink->eo = eo;
- evas_object_event_callback_add (esink->eo, EVAS_CALLBACK_DEL, evas_image_sink_cb_del_eo, esink);
- if (esink->w > 0 && esink->h >0) {
- evas_object_image_size_set (esink->eo, esink->w, esink->h);
- }
- esink->gl_zerocopy = is_zerocopy_supported (evas_object_evas_get (eo));
- if (esink->gl_zerocopy) {
- evas_object_image_content_hint_set (esink->eo, EVAS_IMAGE_CONTENT_HINT_DYNAMIC);
- GST_DEBUG("Enable gl zerocopy");
- }
- if (!esink->epipe) {
- esink->epipe = ecore_pipe_add (evas_image_sink_cb_pipe, esink);
+ if (eo != esink->eo) {
+ /* delete evas object callbacks registrated on a former evas image object */
+ evas_object_event_callback_del (esink->eo, EVAS_CALLBACK_DEL, evas_image_sink_cb_del_eo);
+ evas_object_event_callback_del (esink->eo, EVAS_CALLBACK_RESIZE, evas_image_sink_cb_resize_event);
+
+ esink->eo = eo;
+
+ /* add evas object callbacks on a new evas image object */
+ evas_object_event_callback_add (esink->eo, EVAS_CALLBACK_DEL, evas_image_sink_cb_del_eo, esink);
+ evas_object_event_callback_add (esink->eo, EVAS_CALLBACK_RESIZE, evas_image_sink_cb_resize_event, esink);
+
+ esink->gl_zerocopy = is_zerocopy_supported (evas_object_evas_get (eo));
+ if (esink->gl_zerocopy) {
+ evas_object_image_content_hint_set (esink->eo, EVAS_IMAGE_CONTENT_HINT_DYNAMIC);
+ GST_DEBUG("Enable gl zerocopy");
+ }
+ GST_DEBUG("Evas Image Object(%x) is set",esink->eo);
+ evas_object_show(esink->eo);
+ esink->object_show = TRUE;
}
- if (!esink->epipe) {
- GST_ERROR ("Cannot set evas-object property: pipe create failed");
- }
- GST_DEBUG("property set, Evas Object is set");
- evas_object_show(esink->eo);
- esink->object_show = TRUE;
- GST_INFO ("object show..");
} else {
GST_ERROR ("Cannot set evas-object property: value is not an evas image object");
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
+
+ g_mutex_unlock (instance_lock);
}
static void
-gst_evas_image_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
+gst_evas_image_sink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
GstEvasImageSink *esink = GST_EVASIMAGESINK (object);
}
static gboolean
-gst_evas_image_sink_set_caps (GstBaseSink * base_sink, GstCaps * caps)
+gst_evas_image_sink_set_caps (GstBaseSink *base_sink, GstCaps *caps)
{
int r;
int w, h;
GstEvasImageSink *esink = GST_EVASIMAGESINK (base_sink);
+ esink->is_evas_object_size_set = FALSE;
r = evas_image_sink_get_size_from_caps (caps, &w, &h);
if (!r) {
- if (esink->eo) {
- evas_object_image_size_set (esink->eo, w, h);
- GST_DEBUG ("evas_object_image_size_set: w(%d) h(%d)", w, h);
- }
esink->w = w;
esink->h = h;
+ GST_DEBUG ("set size w(%d), h(%d)", w, h);
}
return TRUE;
}
static GstFlowReturn
-gst_evas_image_sink_show_frame (GstVideoSink * video_sink, GstBuffer * buf)
+gst_evas_image_sink_show_frame (GstVideoSink *video_sink, GstBuffer *buf)
{
GstEvasImageSink *esink = GST_EVASIMAGESINK (video_sink);
Eina_Bool r;
- if (esink->epipe && esink->object_show) {
+
+ g_mutex_lock (instance_lock);
+ if (esink->present_data_addr == -1) {
+ /* if present_data_addr is -1, we don't use this member variable */
+ } else if (esink->present_data_addr != GST_BUFFER_DATA (buf)) {
+ GST_WARNING ("skip rendering this buffer, present_data_addr:%x, GST_BUFFER_DATA(buf):%x", esink->present_data_addr,GST_BUFFER_DATA(buf));
+ g_mutex_unlock (instance_lock);
+ return;
+ }
+ if (!esink->epipe) {
+ esink->epipe = ecore_pipe_add (evas_image_sink_cb_pipe, esink);
+ if (!esink->epipe) {
+ GST_ERROR ("ecore-pipe create failed");
+ return GST_FLOW_ERROR;
+ }
+ }
+ if (esink->object_show) {
gst_buffer_ref (buf);
- r = ecore_pipe_write (esink->epipe, &buf, sizeof (GstBuffer *));
+ __ta__("evasimagesink ecore_pipe_write", r = ecore_pipe_write (esink->epipe, &buf, sizeof (GstBuffer *)););
if (r == EINA_FALSE) {
gst_buffer_unref (buf);
}
GST_DEBUG ("after ecore_pipe_write()");
+ } else {
+ GST_DEBUG ("skip ecore_pipe_write()");
}
+ g_mutex_unlock (instance_lock);
return GST_FLOW_OK;
}
static gboolean
-evas_image_sink_init (GstPlugin * evasimagesink)
+evas_image_sink_init (GstPlugin *evasimagesink)
{
GST_DEBUG_CATEGORY_INIT (gst_evas_image_sink_debug, "evasimagesink", 0, "Evas image object based videosink");
#include <gst/video/gstvideosink.h>
#include <Evas.h>
#include <Ecore.h>
+#include <mm_ta.h>
G_BEGIN_DECLS
struct _GstEvasImageSink
{
GstVideoSink element;
- GstPad *sinkpad, *srcpad;
Evas_Object *eo;
Ecore_Pipe *epipe;
Evas_Coord w;
Evas_Coord h;
- GstBuffer *oldbuf;
- gboolean gl_zerocopy;
gboolean object_show;
+ gboolean gl_zerocopy;
+
+ GstBuffer *oldbuf;
+
+ gboolean is_evas_object_size_set;
+ guint present_data_addr;
};
struct _GstEvasImageSinkClass
+#sbs-git:slp/pkgs/g/gst-plugins-ext0.10 gst-plugins-ext0.10 0.1.7 1784a2d9c9fb90be7fe99857215c2a6154adaacb
Name: gst-plugins-ext0.10
-Version: 0.1.5
-Summary: GStreamer extra plugins (common) Version: 1.0
-Release: 2
+Version: 0.2.3
+Summary: GStreamer extra plugins (common)
+Release: 5
Group: TO_BE/FILLED_IN
-License: LGPLv2+
+License: TO BE FILLED IN
Source0: %{name}-%{version}.tar.gz
BuildRequires: pkgconfig(avsysaudio)
BuildRequires: pkgconfig(camsrcjpegenc)
-BuildRequires: pkgconfig(drm-service)
BuildRequires: pkgconfig(ecore)
BuildRequires: pkgconfig(ecore-x)
BuildRequires: pkgconfig(evas)
BuildRequires: pkgconfig(mm-ta)
BuildRequires: pkgconfig(gstreamer-plugins-base-0.10)
-BuildRequires: pkgconfig(gstreamer-0.10)
+BuildRequires: pkgconfig(gstreamer-0.10)
BuildRequires: pkgconfig(libexif)
%description
%files
-%defattr(-,root,root,-)
+%defattr(-,root,root,-)
%{_libdir}/gstreamer-0.10/*.so
static gboolean
gst_pd_pushsrc_plugin_init (GstPlugin *plugin)
{
- if (!gst_element_register (plugin, "pdpushsrc", GST_RANK_PRIMARY, gst_pd_pushsrc_get_type()))
+ if (!gst_element_register (plugin, "pdpushsrc", GST_RANK_NONE, gst_pd_pushsrc_get_type()))
{
return FALSE;
}
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+
+plugin_LTLIBRARIES = libgstpiff.la
+
+libgstpiffincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/
+
+libgstpiff_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+
+libgstpiff_la_LIBADD = \
+ $(GST_PLUGINS_BASE_LIBS) \
+ -lgstriff-@GST_MAJORMINOR@ \
+ -lgstaudio-@GST_MAJORMINOR@ \
+ -lgsttag-@GST_MAJORMINOR@ \
+ -lgstpbutils-@GST_MAJORMINOR@ \
+ $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS)
+
+libgstpiff_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
+
+libgstpiff_la_SOURCES = piff-plugin.c \
+ piffdemux.c \
+ piffdemux_types.c \
+ piffdemux_dump.c
+
+libgstpiff_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = \
+ piffatomparser.h \
+ piffdemux_types.h \
+ piffdemux_dump.h \
+ piffdemux_fourcc.h \
+ piffpalette.h
+
+libgstpiffinclude_HEADERS = piffcommon.h
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+ androgenizer \
+ -:PROJECT libgstpiff -:SHARED libgstpiff \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstpiff_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(CPPFLAGS) $(libgstpiff_la_CFLAGS) \
+ -:LDFLAGS $(libgstpiff_la_LDFLAGS) \
+ $(libgstpiff_la_LIBADD) \
+ -ldl \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
+ > $@
--- /dev/null
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+//#include "gst/gst-i18n-plugin.h"
+#include "piffdemux.h"
+#include <gst/pbutils/pbutils.h>
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "piffdemux", GST_RANK_PRIMARY, GST_TYPE_PIFFDEMUX))
+ return FALSE;
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "piffdemux",
+ "ISO base media file format support (PIFF)",
+ plugin_init, VERSION, "Proprietary", "Samsung Electronics Co", "http://www.samsung.com");
--- /dev/null
+/* GStreamer QuickTime atom parser
+ * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.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 QT_ATOM_PARSER_H
+#define QT_ATOM_PARSER_H
+
+#include <gst/base/gstbytereader.h>
+
+/* our inlined version of GstByteReader */
+
+static inline gboolean
+piff_atom_parser_has_remaining (GstByteReader * parser, guint64 bytes_needed)
+{
+ return G_LIKELY (parser->size >= bytes_needed) &&
+ G_LIKELY ((parser->size - bytes_needed) >= parser->byte);
+}
+
+static inline gboolean
+piff_atom_parser_has_chunks (GstByteReader * parser, guint32 n_chunks,
+ guint32 chunk_size)
+{
+ /* assumption: n_chunks and chunk_size are 32-bit, we cast to 64-bit here
+ * to avoid overflows, to handle e.g. (guint32)-1 * size correctly */
+ return piff_atom_parser_has_remaining (parser, (guint64) n_chunks * chunk_size);
+}
+
+static inline gboolean
+piff_atom_parser_peek_sub (GstByteReader * parser, guint offset, guint size,
+ GstByteReader * sub)
+{
+ *sub = *parser;
+
+ if (G_UNLIKELY (!gst_byte_reader_skip (sub, offset)))
+ return FALSE;
+
+ return (gst_byte_reader_get_remaining (sub) >= size);
+}
+
+static inline gboolean
+piff_atom_parser_skipn_and_get_uint32 (GstByteReader * parser,
+ guint bytes_to_skip, guint32 * val)
+{
+ if (G_UNLIKELY (gst_byte_reader_get_remaining (parser) < (bytes_to_skip + 4)))
+ return FALSE;
+
+ gst_byte_reader_skip_unchecked (parser, bytes_to_skip);
+ *val = gst_byte_reader_get_uint32_be_unchecked (parser);
+ return TRUE;
+}
+
+/* off_size must be either 4 or 8 */
+static inline gboolean
+piff_atom_parser_get_offset (GstByteReader * parser, guint off_size,
+ guint64 * val)
+{
+ if (G_UNLIKELY (gst_byte_reader_get_remaining (parser) < off_size))
+ return FALSE;
+
+ if (off_size == sizeof (guint64)) {
+ *val = gst_byte_reader_get_uint64_be_unchecked (parser);
+ } else {
+ *val = gst_byte_reader_get_uint32_be_unchecked (parser);
+ }
+ return TRUE;
+}
+
+/* off_size must be either 4 or 8 */
+static inline guint64
+piff_atom_parser_get_offset_unchecked (GstByteReader * parser, guint off_size)
+{
+ if (off_size == sizeof (guint64)) {
+ return gst_byte_reader_get_uint64_be_unchecked (parser);
+ } else {
+ return gst_byte_reader_get_uint32_be_unchecked (parser);
+ }
+}
+
+/* size must be from 1 to 4 */
+static inline guint32
+piff_atom_parser_get_uint_with_size_unchecked (GstByteReader * parser,
+ guint size)
+{
+ switch (size) {
+ case 1:
+ return gst_byte_reader_get_uint8_unchecked (parser);
+ case 2:
+ return gst_byte_reader_get_uint16_be_unchecked (parser);
+ case 3:
+ return gst_byte_reader_get_uint24_be_unchecked (parser);
+ case 4:
+ return gst_byte_reader_get_uint32_be_unchecked (parser);
+ default:
+ g_assert_not_reached ();
+ gst_byte_reader_skip_unchecked (parser, size);
+ break;
+ }
+ return 0;
+}
+
+static inline gboolean
+piff_atom_parser_get_fourcc (GstByteReader * parser, guint32 * fourcc)
+{
+ guint32 f_be;
+
+ if (G_UNLIKELY (gst_byte_reader_get_remaining (parser) < 4))
+ return FALSE;
+
+ f_be = gst_byte_reader_get_uint32_be_unchecked (parser);
+ *fourcc = GUINT32_SWAP_LE_BE (f_be);
+ return TRUE;
+}
+
+static inline guint32
+piff_atom_parser_get_fourcc_unchecked (GstByteReader * parser)
+{
+ guint32 fourcc;
+
+ fourcc = gst_byte_reader_get_uint32_be_unchecked (parser);
+ return GUINT32_SWAP_LE_BE (fourcc);
+}
+
+#endif /* QT_ATOM_PARSER_H */
--- /dev/null
+
+#ifndef __GST_PIFFCOMMON_H__
+#define __GST_PIFFCOMMON_H__
+
+G_BEGIN_DECLS
+
+typedef struct _piff_fragment_longtime_info_t
+{
+ guint64 ts;
+ guint64 duration;
+}piff_fragment_longtime_info;
+
+typedef struct _piff_fragment_time_info_t
+{
+ guint32 ts;
+ guint32 duration;
+}piff_fragment_time_info;
+
+typedef struct _live_param_t
+{
+ gboolean is_eos; /* is live session ended */
+ guint count; /* fragment parameters count */
+ gchar *media_type;
+ piff_fragment_time_info *info;
+ piff_fragment_longtime_info *long_info;
+}piff_live_param_t;
+G_END_DECLS
+
+#endif /* __GST_PIFFPALETTE_H__ */
--- /dev/null
+\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "piffdemux.h"\r
+#include <glib/gprintf.h>\r
+#include <gst/tag/tag.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <piffatomparser.h>\r
+#include <piffdemux_fourcc.h>\r
+#include <piffpalette.h>\r
+#include <piffdemux_types.h>\r
+#include <piffdemux_dump.h>\r
+\r
+#define PIFF_DEFAULT_TRACKID -1\r
+#define PIFF_DEFAULT_FOURCC 0\r
+#define PIFF_DEFAULT_TIMESCALE 10000000\r
+#define PIFF_DEFAULT_DURATION -1\r
+#define PIFF_DEFAULT_START_TS 0\r
+#define PIFF_DEFAULT_START_TS 0\r
+\r
+#define PIFF_DEFAULT_WIDTH 16\r
+#define PIFF_DEFAULT_HEIGHT 16\r
+#define PIFF_DEFAULT_BPS 16\r
+\r
+#undef DEC_OUT_FRAME_DUMP\r
+\r
+#ifdef DEC_OUT_FRAME_DUMP\r
+#include <stdio.h>\r
+FILE *piffdump = NULL;\r
+#endif\r
+\r
+#define PIFFDEMUX_RB16(x) ((((const unsigned char*)(x))[0] << 8) | ((const unsigned char*)(x))[1])\r
+/* max. size considered 'sane' for non-mdat atoms */\r
+#define PIFFDEMUX_MAX_ATOM_SIZE (25*1024*1024)\r
+\r
+/* if the sample index is larger than this, something is likely wrong */\r
+#define PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)\r
+\r
+GST_DEBUG_CATEGORY (piffdemux_debug);\r
+\r
+typedef struct _PiffDemuxSegment PiffDemuxSegment;\r
+typedef struct _PiffDemuxSample PiffDemuxSample;\r
+typedef struct _PiffDemuxSubSampleEncryption PiffDemuxSubSampleEncryption;\r
+typedef struct _PiffDemuxSubSampleEntryInfo PiffDemuxSubSampleEntryInfo;\r
+\r
+enum\r
+{\r
+ PROR_PIFF_0,\r
+ PROP_PIFF_MEDIA_CAPS,\r
+ PROP_PIFF_MEDIA_TIMESCALE,\r
+ PROP_PIFF_MEDIA_DURATION,\r
+ PROP_PIFF_MEDIA_START_TIMESTAMP,\r
+ PROP_PIFF_IS_LIVE,\r
+ PROP_PIFF_LOOKAHEAD_COUNT,\r
+ PROP_PIFF_AVG_FRAME_DUR,\r
+};\r
+\r
+enum\r
+{\r
+ SIGNAL_LIVE_PARAM,\r
+ LAST_SIGNAL\r
+};\r
+\r
+static guint gst_piffdemux_signals[LAST_SIGNAL] = { 0 };\r
+\r
+struct _PiffDemuxSubSampleEntryInfo\r
+{\r
+ guint16 LenofClearData;\r
+ guint32 LenofEncryptData;\r
+};\r
+\r
+struct _PiffDemuxSubSampleEncryption\r
+{\r
+ guint16 n_entries;\r
+ PiffDemuxSubSampleEntryInfo *sub_entry;\r
+};\r
+\r
+struct _PiffDemuxSample\r
+{\r
+ guint32 size;\r
+ gint32 pts_offset; /* Add this value to timestamp to get the pts */\r
+ guint64 offset;\r
+ guint64 timestamp; /* DTS In mov time */\r
+ guint32 duration; /* In mov time */\r
+ gboolean keyframe; /* TRUE when this packet is a keyframe */\r
+ guint8 *iv; /* initialization vector for decryption*/\r
+ PiffDemuxSubSampleEncryption *sub_encry;\r
+};\r
+\r
+/* timestamp is the DTS */\r
+#define PIFFSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\\r
+ GST_SECOND, (stream)->timescale)\r
+/* timestamp + offset is the PTS */\r
+#define PIFFSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \\r
+ (sample)->pts_offset, GST_SECOND, (stream)->timescale)\r
+/* timestamp + duration - dts is the duration */\r
+#define PIFFSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \\r
+ (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));\r
+/* timestamp + offset + duration - pts is the duration */\r
+#define PIFFSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \\r
+ (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));\r
+\r
+#define PIFFSAMPLE_KEYFRAME(stream,sample) ((sample)->keyframe);\r
+\r
+typedef char uuid_t[16];\r
+\r
+static const uuid_t tfxd_uuid = { 0x6d, 0x1d, 0x9b, 0x05,\r
+ 0x42, 0xd5, 0x44, 0xe6,\r
+ 0x80, 0xe2, 0x14, 0x1d,\r
+ 0xaf, 0xf7, 0x57, 0xb2 };\r
+\r
+static const uuid_t tfrf_uuid = { 0xd4, 0x80, 0x7e, 0xf2,\r
+ 0xca, 0x39, 0x46, 0x95,\r
+ 0x8e, 0x54, 0x26, 0xcb,\r
+ 0x9e, 0x46, 0xa7, 0x9f };\r
+\r
+static const uuid_t encrypt_uuid = { 0xa2, 0x39, 0x4f, 0x52,\r
+ 0x5a, 0x9b, 0x4f, 0x14,\r
+ 0xa2, 0x44, 0x6c, 0x42,\r
+ 0x7c, 0x64, 0x8d, 0xf4 };\r
+\r
+#define SE_OVERRIDE_TE_FLAGS 0x000001\r
+#define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002\r
+\r
+typedef enum\r
+{\r
+ UUID_UNKNOWN = -1,\r
+ UUID_TFXD,\r
+ UUID_TFRF,\r
+ UUID_SAMPLE_ENCRYPT,\r
+}uuid_type_t;\r
+\r
+struct _PiffDemuxSegment\r
+{\r
+ /* global time and duration, all gst time */\r
+ guint64 time;\r
+ guint64 stop_time;\r
+ guint64 duration;\r
+ /* media time of trak, all gst time */\r
+ guint64 media_start;\r
+ guint64 media_stop;\r
+ gdouble rate;\r
+};\r
+\r
+\r
+struct _PiffDemuxStream\r
+{\r
+ /* stream type */\r
+ guint32 subtype;\r
+ GstCaps *caps;\r
+ guint32 fourcc;\r
+\r
+ /* duration/scale */\r
+ guint64 duration; /* in timescale */\r
+ guint32 timescale;\r
+\r
+ /* our samples */\r
+ guint32 n_samples;\r
+ PiffDemuxSample *samples;\r
+ guint32 min_duration; /* duration in timescale of first sample, used for figuring out\r
+ the framerate, in timescale units */\r
+\r
+ /* if we use chunks or samples */\r
+ gboolean sampled;\r
+ guint padding;\r
+\r
+ /* when a discontinuity is pending */\r
+ gboolean discont;\r
+\r
+ /* list of buffers to push first */\r
+ GSList *buffers;\r
+\r
+ /* buffer needs some custom processing, e.g. subtitles */\r
+ gboolean need_process;\r
+\r
+ /* current position */\r
+ guint32 segment_index;\r
+ guint32 sample_index;\r
+ guint64 time_position; /* in gst time */\r
+\r
+ /* the Gst segment we are processing out, used for clipping */\r
+ GstSegment segment;\r
+\r
+ /* last GstFlowReturn */\r
+ GstFlowReturn last_ret;\r
+\r
+\r
+ /* quicktime segments */\r
+ guint32 n_segments;\r
+ PiffDemuxSegment *segments;\r
+ guint32 from_sample;\r
+ guint32 to_sample;\r
+\r
+ gboolean sent_eos;\r
+ GstTagList *pending_tags;\r
+ gboolean send_global_tags;\r
+\r
+ GstEvent *pending_event;\r
+\r
+ gboolean sent_nsevent;\r
+\r
+ guint64 start_ts;\r
+\r
+ guint64 avg_dur; /* average frame duration */\r
+};\r
+\r
+\r
+enum PiffDemuxState\r
+{\r
+ PIFFDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */\r
+ PIFFDEMUX_STATE_HEADER, /* Parsing the header */\r
+ PIFFDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */\r
+ PIFFDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */\r
+};\r
+\r
+\r
+static GNode *piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);\r
+static GNode *piffdemux_tree_get_child_by_type_full (GNode * node,\r
+ guint32 fourcc, GstByteReader * parser);\r
+static GNode *piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);\r
+static GNode *piffdemux_tree_get_sibling_by_type_full (GNode * node,\r
+ guint32 fourcc, GstByteReader * parser);\r
+\r
+static GstStaticPadTemplate gst_piffdemux_sink_template =\r
+ GST_STATIC_PAD_TEMPLATE ("sink",\r
+ GST_PAD_SINK,\r
+ GST_PAD_ALWAYS,\r
+ GST_STATIC_CAPS ("application/x-piff")\r
+ );\r
+\r
+static GstStaticPadTemplate gst_piffdemux_src_template =\r
+GST_STATIC_PAD_TEMPLATE ("src",\r
+ GST_PAD_SRC,\r
+ GST_PAD_ALWAYS,\r
+ GST_STATIC_CAPS_ANY);\r
+\r
+\r
+GST_BOILERPLATE (GstPiffDemux, gst_piffdemux, GstPiffDemux, GST_TYPE_ELEMENT);\r
+\r
+static void gst_piffdemux_dispose (GObject * object);\r
+\r
+static GstStateChangeReturn gst_piffdemux_change_state (GstElement * element,\r
+ GstStateChange transition);\r
+static void\r
+gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);\r
+static void\r
+gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);\r
+static GstFlowReturn gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);\r
+static gboolean gst_piffdemux_handle_sink_event (GstPad * pad, GstEvent * event);\r
+static gboolean piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer, guint length);\r
+static gboolean piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream);\r
+static gboolean piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd);\r
+static gboolean gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event);\r
+static const GstQueryType *gst_piffdemux_get_src_query_types (GstPad * pad);\r
+static gboolean gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query);\r
+\r
+\r
+static gboolean\r
+ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size);\r
+void\r
+__gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,\r
+ GValue *return_value G_GNUC_UNUSED,\r
+ guint n_param_values,\r
+ const GValue *param_values,\r
+ gpointer invocation_hint G_GNUC_UNUSED,\r
+ gpointer marshal_data);\r
+\r
+static void\r
+gst_piffdemux_base_init (gpointer klass)\r
+{\r
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);\r
+\r
+ gst_element_class_add_pad_template (element_class,\r
+ gst_static_pad_template_get (&gst_piffdemux_sink_template));\r
+ gst_element_class_add_pad_template (element_class,\r
+ gst_static_pad_template_get (&gst_piffdemux_src_template));\r
+ gst_element_class_set_details_simple (element_class, "PIFF demuxer",\r
+ "Codec/Parser",\r
+ "Parser for PIFF file format",\r
+ "naveen ch <naveen.ch@samsung.com>");\r
+\r
+ GST_DEBUG_CATEGORY_INIT (piffdemux_debug, "piffdemux", 0, "piffdemux plugin");\r
+}\r
+\r
+static void\r
+gst_piffdemux_class_init (GstPiffDemuxClass * klass)\r
+{\r
+ GObjectClass *gobject_class;\r
+ GstElementClass *gstelement_class;\r
+\r
+ gobject_class = (GObjectClass *) klass;\r
+ gstelement_class = (GstElementClass *) klass;\r
+\r
+ parent_class = g_type_class_peek_parent (klass);\r
+\r
+ gobject_class->dispose = gst_piffdemux_dispose;\r
+ gobject_class->set_property = gst_piffdemux_set_property;\r
+ gobject_class->get_property = gst_piffdemux_get_property;\r
+\r
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_piffdemux_change_state);\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_CAPS,\r
+ g_param_spec_boxed ("caps", "Caps",\r
+ "The allowed caps for the src pad", GST_TYPE_CAPS,\r
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));\r
+ \r
+ /* timescale of media to be set by application */\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_TIMESCALE,\r
+ g_param_spec_uint64 ("timescale", "media timescale",\r
+ "media timescale in PIFF Manifest", 0, G_MAXUINT64,\r
+ PIFF_DEFAULT_TIMESCALE,\r
+ G_PARAM_READWRITE));\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_DURATION,\r
+ g_param_spec_int64 ("duration", "Duration of media",\r
+ "Total duration of the content", -1, G_MAXINT64,\r
+ PIFF_DEFAULT_DURATION,\r
+ G_PARAM_READWRITE));\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_START_TIMESTAMP,\r
+ g_param_spec_uint64 ("start-ts", "expected start timestamp",\r
+ "expected start timestamp to avoid reset", 0, G_MAXUINT64,\r
+ PIFF_DEFAULT_START_TS,\r
+ G_PARAM_READWRITE));\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_IS_LIVE,\r
+ g_param_spec_boolean ("is-live", "Is presentation is Live or VOD",\r
+ "If Presentation is Live (true) else VOD (false)",\r
+ FALSE,\r
+ G_PARAM_READWRITE));\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_LOOKAHEAD_COUNT,\r
+ g_param_spec_uint ("lookahead-count", "Lookahead count value",\r
+ "Look ahead count used in case of Live presentation", 0, G_MAXUINT,\r
+ 0,\r
+ G_PARAM_READWRITE));\r
+\r
+ g_object_class_install_property (gobject_class, PROP_PIFF_AVG_FRAME_DUR,\r
+ g_param_spec_uint64 ("frame-dur", "Average frame duration",\r
+ "Average frame duration", 0, G_MAXUINT64,\r
+ G_MAXUINT64,\r
+ G_PARAM_READABLE));\r
+\r
+ gst_piffdemux_signals[SIGNAL_LIVE_PARAM] =\r
+ g_signal_new ("live-param", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,\r
+ G_STRUCT_OFFSET (GstPiffDemuxClass, live_param), NULL, NULL,\r
+ g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);\r
+\r
+}\r
+\r
+static void\r
+gst_piffdemux_init (GstPiffDemux * piffdemux, GstPiffDemuxClass * klass)\r
+{\r
+ /* sink pad */\r
+ piffdemux->sinkpad = gst_pad_new_from_static_template (&gst_piffdemux_sink_template, "sink");\r
+ gst_pad_set_chain_function (piffdemux->sinkpad, gst_piffdemux_chain);\r
+ gst_pad_set_event_function (piffdemux->sinkpad, gst_piffdemux_handle_sink_event);\r
+ gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->sinkpad);\r
+\r
+ /* source pad */\r
+ piffdemux->srcpad = gst_pad_new_from_static_template (&gst_piffdemux_src_template, "src");\r
+ gst_pad_set_event_function (piffdemux->srcpad, gst_piffdemux_handle_src_event);\r
+ gst_pad_use_fixed_caps (piffdemux->srcpad);\r
+ gst_pad_set_query_type_function (piffdemux->srcpad, gst_piffdemux_get_src_query_types);\r
+ gst_pad_set_query_function (piffdemux->srcpad, gst_piffdemux_handle_src_query);\r
+ gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->srcpad);\r
+\r
+ piffdemux->stream = g_new0 (PiffDemuxStream, 1);\r
+ piffdemux->stream->fourcc = PIFF_DEFAULT_FOURCC;\r
+ piffdemux->stream->timescale = PIFF_DEFAULT_TIMESCALE;\r
+ piffdemux->stream->duration = PIFF_DEFAULT_DURATION;\r
+ piffdemux->stream->caps = NULL;\r
+ piffdemux->stream->discont = TRUE;\r
+ piffdemux->stream->need_process = FALSE;\r
+ piffdemux->stream->segment_index = -1;\r
+ piffdemux->stream->time_position = 0;\r
+ piffdemux->stream->sample_index = -1;\r
+ piffdemux->stream->last_ret = GST_FLOW_OK;\r
+ piffdemux->stream->sent_nsevent = FALSE;\r
+ piffdemux->stream->start_ts = PIFF_DEFAULT_START_TS;\r
+ piffdemux->stream->avg_dur = -1;\r
+\r
+ piffdemux->state = PIFFDEMUX_STATE_INITIAL;\r
+ piffdemux->neededbytes = 16;\r
+ piffdemux->todrop = 0;\r
+ piffdemux->adapter = gst_adapter_new ();\r
+ piffdemux->offset = 0;\r
+ piffdemux->first_mdat = -1;\r
+ piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;\r
+ piffdemux->mdatbuffer = NULL;\r
+ piffdemux->moof_rcvd = FALSE;\r
+ piffdemux->is_live = FALSE;\r
+ piffdemux->lookahead_cnt = 0;\r
+\r
+#ifdef DEC_OUT_FRAME_DUMP\r
+ piffdump = fopen ("/opt/media/piff_out_dump.dmp", "w+");\r
+ if (piffdump == NULL)\r
+ {\r
+ g_print ("\nNot able to create frame dump file\n");\r
+ }\r
+#endif\r
+\r
+ gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);\r
+}\r
+\r
+static void\r
+gst_piffdemux_dispose (GObject * object)\r
+{\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
+\r
+ if (piffdemux->adapter) {\r
+ g_object_unref (G_OBJECT (piffdemux->adapter));\r
+ piffdemux->adapter = NULL;\r
+ }\r
+\r
+#ifdef DEC_OUT_FRAME_DUMP\r
+ {\r
+ fclose (piffdump);\r
+ piffdump = NULL;\r
+ }\r
+#endif\r
+ G_OBJECT_CLASS (parent_class)->dispose (object);\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)\r
+{\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
+\r
+ switch (prop_id) {\r
+ case PROP_PIFF_MEDIA_CAPS: {\r
+ if (piffdemux->stream->caps)\r
+ gst_caps_unref(piffdemux->stream->caps);\r
+ piffdemux->stream->caps = gst_caps_copy (gst_value_get_caps (value));\r
+ GST_DEBUG_OBJECT (piffdemux, "stream caps = %s", gst_caps_to_string(piffdemux->stream->caps));\r
+ if (!gst_pad_set_caps(piffdemux->srcpad, piffdemux->stream->caps)) {\r
+ GST_ERROR_OBJECT (piffdemux, "not able to set caps...");\r
+ }\r
+ break;\r
+ }\r
+ case PROP_PIFF_MEDIA_TIMESCALE:\r
+ piffdemux->stream->timescale = g_value_get_uint64(value);\r
+ break;\r
+ case PROP_PIFF_MEDIA_DURATION:\r
+ piffdemux->stream->duration = g_value_get_int64(value);\r
+ break;\r
+ case PROP_PIFF_MEDIA_START_TIMESTAMP:\r
+ piffdemux->stream->start_ts = g_value_get_uint64(value);\r
+ GST_INFO_OBJECT (piffdemux, "start_ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(piffdemux->stream->start_ts));\r
+ break;\r
+ case PROP_PIFF_IS_LIVE:\r
+ piffdemux->is_live = g_value_get_boolean(value);\r
+ break;\r
+ case PROP_PIFF_LOOKAHEAD_COUNT:\r
+ piffdemux->lookahead_cnt = g_value_get_uint(value);\r
+ GST_DEBUG_OBJECT (piffdemux, "Look ahead count = %d", piffdemux->lookahead_cnt);\r
+ break;\r
+ default:\r
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)\r
+{\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);\r
+\r
+ switch (prop_id) {\r
+ case PROP_PIFF_MEDIA_CAPS:\r
+ gst_value_set_caps (value, piffdemux->stream->caps);\r
+ break;\r
+ case PROP_PIFF_MEDIA_TIMESCALE:\r
+ g_value_set_uint64 (value, piffdemux->stream->timescale);\r
+ break;\r
+ case PROP_PIFF_MEDIA_DURATION:\r
+ g_value_set_int64 (value, piffdemux->stream->duration);\r
+ break;\r
+ case PROP_PIFF_MEDIA_START_TIMESTAMP:\r
+ g_value_set_uint64 (value, piffdemux->stream->start_ts);\r
+ break;\r
+ case PROP_PIFF_IS_LIVE:\r
+ g_value_set_boolean(value, piffdemux->is_live);\r
+ break;\r
+ case PROP_PIFF_LOOKAHEAD_COUNT:\r
+ g_value_set_uint (value, piffdemux->lookahead_cnt);\r
+ break;\r
+ case PROP_PIFF_AVG_FRAME_DUR:\r
+ g_value_set_uint64 (value, piffdemux->stream->avg_dur);\r
+ break;\r
+ default:\r
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_post_no_playable_stream_error (GstPiffDemux * piffdemux)\r
+{\r
+ if (piffdemux->posted_redirect) {\r
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
+ ("This file contains no playable streams."),\r
+ ("no known streams found, a redirect message has been posted"));\r
+ } else {\r
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
+ ("This file contains no playable streams."),\r
+ ("no known streams found"));\r
+ }\r
+\r
+}\r
+\r
+static gboolean\r
+gst_piffdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,\r
+ GstFormat dest_format, gint64 * dest_value)\r
+{\r
+ gboolean res = TRUE;\r
+ PiffDemuxStream *stream = gst_pad_get_element_private (pad);\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));\r
+\r
+ if (stream->subtype != FOURCC_vide) {\r
+ res = FALSE;\r
+ goto done;\r
+ }\r
+\r
+ switch (src_format) {\r
+ case GST_FORMAT_TIME:\r
+ switch (dest_format) {\r
+ case GST_FORMAT_BYTES:{\r
+\r
+ break;\r
+ }\r
+ default:\r
+ res = FALSE;\r
+ break;\r
+ }\r
+ break;\r
+ case GST_FORMAT_BYTES:\r
+ switch (dest_format) {\r
+ case GST_FORMAT_TIME:{\r
+\r
+ break;\r
+ }\r
+ default:\r
+ res = FALSE;\r
+ break;\r
+ }\r
+ break;\r
+ default:\r
+ res = FALSE;\r
+ }\r
+\r
+done:\r
+ gst_object_unref (piffdemux);\r
+\r
+ return res;\r
+}\r
+\r
+static const GstQueryType *\r
+gst_piffdemux_get_src_query_types (GstPad * pad)\r
+{\r
+ static const GstQueryType src_types[] = {\r
+ GST_QUERY_POSITION,\r
+ GST_QUERY_DURATION,\r
+ GST_QUERY_CONVERT,\r
+ GST_QUERY_FORMATS,\r
+ GST_QUERY_SEEKING,\r
+ 0\r
+ };\r
+\r
+ return src_types;\r
+}\r
+\r
+static gboolean\r
+gst_piffdemux_get_duration (GstPiffDemux * piffdemux, gint64 * duration)\r
+{\r
+ gboolean res = TRUE;\r
+\r
+ *duration = GST_CLOCK_TIME_NONE;\r
+\r
+ if (piffdemux->stream->duration != 0) {\r
+ if (piffdemux->stream->duration != G_MAXINT64 && piffdemux->stream->timescale != 0) {\r
+ *duration = gst_util_uint64_scale (piffdemux->stream->duration,\r
+ GST_SECOND, piffdemux->stream->timescale);\r
+ }\r
+ }\r
+ return res;\r
+}\r
+\r
+static gboolean\r
+gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query)\r
+{\r
+ gboolean res = FALSE;\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));\r
+\r
+ GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));\r
+\r
+ switch (GST_QUERY_TYPE (query)) {\r
+ case GST_QUERY_POSITION:\r
+ GST_ERROR ("Querying POSITION from piffdemux....");\r
+ if (GST_CLOCK_TIME_IS_VALID (piffdemux->segment.last_stop)) {\r
+ gst_query_set_position (query, GST_FORMAT_TIME,\r
+ piffdemux->segment.last_stop);\r
+ res = TRUE;\r
+ }\r
+ break;\r
+ case GST_QUERY_DURATION:{\r
+ GstFormat fmt;\r
+ GST_ERROR ("Querying DURATION from piffdemux....");\r
+\r
+ gst_query_parse_duration (query, &fmt, NULL);\r
+ if (fmt == GST_FORMAT_TIME) {\r
+ gint64 duration = -1;\r
+\r
+ gst_piffdemux_get_duration (piffdemux, &duration);\r
+ if (duration > 0) {\r
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);\r
+ res = TRUE;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ case GST_QUERY_CONVERT:{\r
+ GstFormat src_fmt, dest_fmt;\r
+ gint64 src_value, dest_value = 0;\r
+\r
+ gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);\r
+\r
+ res = gst_piffdemux_src_convert (pad,\r
+ src_fmt, src_value, dest_fmt, &dest_value);\r
+ if (res) {\r
+ gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);\r
+ res = TRUE;\r
+ }\r
+ break;\r
+ }\r
+ case GST_QUERY_FORMATS:\r
+ gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);\r
+ res = TRUE;\r
+ break;\r
+ case GST_QUERY_SEEKING:{\r
+\r
+ break;\r
+ }\r
+ default:\r
+ res = gst_pad_query_default (pad, query);\r
+ break;\r
+ }\r
+\r
+ gst_object_unref (piffdemux);\r
+\r
+ return res;\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_push_tags (GstPiffDemux * piffdemux, PiffDemuxStream * stream)\r
+{\r
+ if (G_UNLIKELY (stream->pending_tags)) {\r
+ GST_DEBUG_OBJECT (piffdemux, "Sending tags %" GST_PTR_FORMAT,\r
+ stream->pending_tags);\r
+ gst_pad_push_event (piffdemux->srcpad,\r
+ gst_event_new_tag (stream->pending_tags));\r
+ stream->pending_tags = NULL;\r
+ }\r
+\r
+ if (G_UNLIKELY (stream->send_global_tags && piffdemux->tag_list)) {\r
+ GST_DEBUG_OBJECT (piffdemux, "Sending global tags %" GST_PTR_FORMAT,\r
+ piffdemux->tag_list);\r
+ gst_pad_push_event (piffdemux->srcpad,\r
+ gst_event_new_tag (gst_tag_list_copy (piffdemux->tag_list)));\r
+ stream->send_global_tags = FALSE;\r
+ }\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_push_event (GstPiffDemux * piffdemux, GstEvent * event)\r
+{\r
+ GstEventType etype = GST_EVENT_TYPE (event);\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "pushing %s event on source pad",\r
+ GST_EVENT_TYPE_NAME (event));\r
+\r
+ if (piffdemux->stream->sent_eos) {\r
+ GST_INFO_OBJECT (piffdemux, "already sent eos");\r
+ return;\r
+ }\r
+\r
+ if (!gst_pad_push_event (piffdemux->srcpad, event)) {\r
+ GST_ERROR_OBJECT (piffdemux, "error in sending event to srcpad...");\r
+ }\r
+\r
+ if (etype == GST_EVENT_EOS)\r
+ piffdemux->stream->sent_eos = TRUE;\r
+}\r
+\r
+\r
+/* find the segment for @time_position for @stream\r
+ *\r
+ * Returns -1 if the segment cannot be found.\r
+ */\r
+static guint32\r
+gst_piffdemux_find_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,\r
+ guint64 time_position)\r
+{\r
+ gint i;\r
+ guint32 seg_idx;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "finding segment for %" GST_TIME_FORMAT,\r
+ GST_TIME_ARGS (time_position));\r
+\r
+ /* find segment corresponding to time_position if we are looking\r
+ * for a segment. */\r
+ seg_idx = -1;\r
+ for (i = 0; i < stream->n_segments; i++) {\r
+ PiffDemuxSegment *segment = &stream->segments[i];\r
+\r
+ GST_LOG_OBJECT (piffdemux,\r
+ "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,\r
+ GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));\r
+\r
+ /* For the last segment we include stop_time in the last segment */\r
+ if (i < stream->n_segments - 1) {\r
+ if (segment->time <= time_position && time_position < segment->stop_time) {\r
+ GST_LOG_OBJECT (piffdemux, "segment %d matches", i);\r
+ seg_idx = i;\r
+ break;\r
+ }\r
+ } else {\r
+ if (segment->time <= time_position && time_position <= segment->stop_time) {\r
+ GST_LOG_OBJECT (piffdemux, "segment %d matches", i);\r
+ seg_idx = i;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return seg_idx;\r
+}\r
+\r
+\r
+static gboolean\r
+gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event)\r
+{\r
+ gboolean res = TRUE;\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));\r
+\r
+ switch (GST_EVENT_TYPE (event)) {\r
+ case GST_EVENT_QOS:\r
+ case GST_EVENT_NAVIGATION:\r
+ res = FALSE;\r
+ gst_event_unref (event);\r
+ break;\r
+ case GST_EVENT_SEEK:\r
+ default:\r
+ res = gst_pad_event_default (pad, event);\r
+ break;\r
+ }\r
+\r
+ gst_object_unref (piffdemux);\r
+\r
+ return res;\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_move_stream (GstPiffDemux * piffdemux, PiffDemuxStream * str,\r
+ guint32 index)\r
+{\r
+ /* no change needed */\r
+ if (index == str->sample_index)\r
+ return;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "moving to sample %u of %u", index,\r
+ str->n_samples);\r
+\r
+ /* position changed, we have a discont */\r
+ str->sample_index = index;\r
+ /* Each time we move in the stream we store the position where we are\r
+ * starting from */\r
+ str->from_sample = index;\r
+ str->discont = TRUE;\r
+}\r
+\r
+// TODO: need to check more on this below function\r
+/* stream/index return sample that is min/max w.r.t. byte position,\r
+ * time is min/max w.r.t. time of samples,\r
+ * the latter need not be time of the former sample */\r
+static void\r
+gst_piffdemux_find_sample (GstPiffDemux * piffdemux, gint64 byte_pos, gboolean fw,\r
+ gboolean set, PiffDemuxStream ** _stream, gint * _index, gint64 * _time)\r
+{\r
+ gint i, index;\r
+ gint64 time, min_time;\r
+ PiffDemuxStream *stream;\r
+ PiffDemuxStream *str = piffdemux->stream;\r
+ gint inc;\r
+ gboolean set_sample;\r
+\r
+ min_time = -1;\r
+ stream = NULL;\r
+ index = -1;\r
+\r
+ set_sample = !set;\r
+ if (fw) {\r
+ i = 0;\r
+ inc = 1;\r
+ } else {\r
+ i = str->n_samples - 1;\r
+ inc = -1;\r
+ }\r
+\r
+ for (; (i >= 0) && (i < str->n_samples); i += inc) {\r
+ if (str->samples[i].size &&\r
+ ((fw && (str->samples[i].offset >= byte_pos)) ||\r
+ (!fw &&\r
+ (str->samples[i].offset + str->samples[i].size <=\r
+ byte_pos)))) {\r
+ /* move stream to first available sample */\r
+ if (set) {\r
+ gst_piffdemux_move_stream (piffdemux, str, i);\r
+ set_sample = TRUE;\r
+ }\r
+ /* determine min/max time */\r
+ time = str->samples[i].timestamp + str->samples[i].pts_offset;\r
+ time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);\r
+ if (min_time == -1 || (!fw && time > min_time) ||\r
+ (fw && time < min_time)) {\r
+ min_time = time;\r
+ }\r
+ index = i;\r
+ break;\r
+ }\r
+ }\r
+ /* no sample for this stream, mark eos */\r
+ if (!set_sample)\r
+ gst_piffdemux_move_stream (piffdemux, str, str->n_samples);\r
+\r
+ if (_time)\r
+ *_time = min_time;\r
+ if (_stream)\r
+ *_stream = str;\r
+ if (_index)\r
+ *_index = index;\r
+}\r
+\r
+\r
+static gboolean\r
+gst_piffdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)\r
+{\r
+ GstPiffDemux *demux = GST_PIFFDEMUX (GST_PAD_PARENT (sinkpad));\r
+ gboolean res;\r
+\r
+ GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));\r
+\r
+ switch (GST_EVENT_TYPE (event)) {\r
+ case GST_EVENT_NEWSEGMENT:\r
+ {\r
+ GstFormat format;\r
+ gdouble rate, arate;\r
+ gint64 start, stop, time, offset = 0;\r
+ PiffDemuxStream *stream;\r
+ gint idx;\r
+ gboolean update;\r
+ GstSegment segment;\r
+\r
+ /* some debug output */\r
+ gst_segment_init (&segment, GST_FORMAT_UNDEFINED);\r
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,\r
+ &start, &stop, &time);\r
+ gst_segment_set_newsegment_full (&segment, update, rate, arate, format,\r
+ start, stop, time);\r
+ GST_ERROR_OBJECT (demux,\r
+ "received format %d newsegment %" GST_SEGMENT_FORMAT, format,\r
+ &segment);\r
+\r
+ /* chain will send initial newsegment after pads have been added */\r
+ if (demux->state != PIFFDEMUX_STATE_MOVIE ) {\r
+ GST_DEBUG_OBJECT (demux, "still starting, eating event");\r
+ goto exit;\r
+ }\r
+\r
+ /* we only expect a BYTE segment, e.g. following a seek */\r
+ if (format == GST_FORMAT_BYTES) {\r
+ if (start > 0) {\r
+ gint64 requested_seek_time;\r
+ guint64 seek_offset;\r
+\r
+ offset = start;\r
+\r
+ GST_OBJECT_LOCK (demux);\r
+ requested_seek_time = demux->requested_seek_time;\r
+ seek_offset = demux->seek_offset;\r
+ demux->requested_seek_time = -1;\r
+ demux->seek_offset = -1;\r
+ GST_OBJECT_UNLOCK (demux);\r
+\r
+ if (offset == seek_offset) {\r
+ start = requested_seek_time;\r
+ } else {\r
+ gst_piffdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,\r
+ &start);\r
+ start = MAX (start, 0);\r
+ }\r
+ }\r
+ if (stop > 0) {\r
+ gst_piffdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,\r
+ &stop);\r
+ /* keyframe seeking should already arrange for start >= stop,\r
+ * but make sure in other rare cases */\r
+ stop = MAX (stop, start);\r
+ }\r
+ }\r
+#if 0\r
+ else if (format == GST_FORMAT_TIME) {\r
+ // Supporting TIME_FORMAT for new_segment\r
+ //gst_piffdemux_push_event (demux,event);\r
+ PiffDemuxStream *stream = NULL;\r
+ int i = -1;\r
+\r
+ demux->neededbytes = 16;\r
+ demux->state = PIFFDEMUX_STATE_INITIAL;\r
+ demux->offset = 0;\r
+\r
+ /* Figure out which stream this is packet belongs to */\r
+ for (i = 0; i < demux->n_streams; i++) {\r
+ stream = demux->streams[i];\r
+ stream->last_ts = start;\r
+ stream->discont = TRUE;\r
+ stream->sample_index = stream->n_samples;\r
+ }\r
+\r
+ /* accept upstream's notion of segment and distribute along */\r
+ gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,\r
+ GST_FORMAT_TIME, start, stop, start);\r
+ GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "\r
+ "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "\r
+ "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,\r
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));\r
+\r
+ gst_piffdemux_push_event (demux,\r
+ gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, start, stop, start));\r
+\r
+ /* clear leftover in current segment, if any */\r
+ gst_adapter_clear (demux->adapter);\r
+\r
+ goto exit;\r
+ }\r
+#endif\r
+ else {\r
+ GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");\r
+ goto exit;\r
+ }\r
+\r
+ /* accept upstream's notion of segment and distribute along */\r
+ gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,\r
+ GST_FORMAT_TIME, start, stop, start);\r
+ GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "\r
+ "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "\r
+ "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,\r
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));\r
+\r
+ gst_piffdemux_push_event (demux,\r
+ gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,\r
+ start, stop, start));\r
+\r
+ /* clear leftover in current segment, if any */\r
+ gst_adapter_clear (demux->adapter);\r
+ /* set up streaming thread */\r
+ gst_piffdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);\r
+ demux->offset = offset;\r
+ if (stream) {\r
+ demux->todrop = stream->samples[idx].offset - offset;\r
+ demux->neededbytes = demux->todrop + stream->samples[idx].size;\r
+ } else {\r
+ /* set up for EOS */\r
+ demux->neededbytes = -1;\r
+ demux->todrop = 0;\r
+ }\r
+ exit:\r
+ gst_event_unref (event);\r
+ res = TRUE;\r
+ goto drop;\r
+ break;\r
+ }\r
+ case GST_EVENT_FLUSH_STOP:\r
+ {\r
+ /* clean up, force EOS if no more info follows */\r
+ gst_adapter_clear (demux->adapter);\r
+ demux->offset = 0;\r
+ demux->neededbytes = -1;\r
+ /* reset flow return, e.g. following seek */\r
+ demux->stream->last_ret = GST_FLOW_OK;\r
+ demux->stream->sent_eos = FALSE;\r
+ break;\r
+ }\r
+ case GST_EVENT_EOS:\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ res = gst_pad_event_default (demux->sinkpad, event);\r
+\r
+drop:\r
+ return res;\r
+}\r
+\r
+\r
+static void\r
+gst_piffdemux_stream_free (GstPiffDemux * piffdemux, PiffDemuxStream * stream)\r
+{\r
+ while (stream->buffers) {\r
+ gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));\r
+ stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);\r
+ }\r
+ g_free (stream->samples);\r
+ if (stream->caps)\r
+ gst_caps_unref (stream->caps);\r
+ g_free (stream->segments);\r
+ if (stream->pending_tags)\r
+ gst_tag_list_free (stream->pending_tags);\r
+ g_free (stream);\r
+}\r
+\r
+\r
+static GstStateChangeReturn\r
+gst_piffdemux_change_state (GstElement * element, GstStateChange transition)\r
+{\r
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (element);\r
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;\r
+\r
+ switch (transition) {\r
+ case GST_STATE_CHANGE_PAUSED_TO_READY:\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);\r
+\r
+ switch (transition) {\r
+ case GST_STATE_CHANGE_PAUSED_TO_READY:{\r
+ piffdemux->state = PIFFDEMUX_STATE_INITIAL;\r
+ piffdemux->neededbytes = 16;\r
+ piffdemux->todrop = 0;\r
+ piffdemux->posted_redirect = FALSE;\r
+ piffdemux->offset = 0;\r
+ piffdemux->first_mdat = -1;\r
+ piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;\r
+ if (piffdemux->mdatbuffer)\r
+ gst_buffer_unref (piffdemux->mdatbuffer);\r
+ piffdemux->mdatbuffer = NULL;\r
+ if (piffdemux->tag_list)\r
+ gst_tag_list_free (piffdemux->tag_list);\r
+ piffdemux->tag_list = NULL;\r
+ gst_adapter_clear (piffdemux->adapter);\r
+ gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);\r
+ break;\r
+ }\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+static void\r
+piffdemux_post_global_tags (GstPiffDemux * piffdemux)\r
+{\r
+ if (piffdemux->tag_list) {\r
+ /* all header tags ready and parsed, push them */\r
+ GST_INFO_OBJECT (piffdemux, "posting global tags: %" GST_PTR_FORMAT,\r
+ piffdemux->tag_list);\r
+ /* post now, send event on pads later */\r
+ gst_element_post_message (GST_ELEMENT (piffdemux),\r
+ gst_message_new_tag (GST_OBJECT (piffdemux),\r
+ gst_tag_list_copy (piffdemux->tag_list)));\r
+ }\r
+}\r
+\r
+\r
+/* caller verifies at least 8 bytes in buf */\r
+static void\r
+extract_initial_length_and_fourcc (const guint8 * data, guint size,\r
+ guint64 * plength, guint32 * pfourcc)\r
+{\r
+ guint64 length;\r
+ guint32 fourcc;\r
+\r
+ length = PIFF_UINT32 (data);\r
+ GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);\r
+ fourcc = PIFF_FOURCC (data + 4);\r
+ GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));\r
+\r
+ if (length == 0) {\r
+ length = G_MAXUINT32;\r
+ } else if (length == 1 && size >= 16) {\r
+ /* this means we have an extended size, which is the 64 bit value of\r
+ * the next 8 bytes */\r
+ length = PIFF_UINT64 (data + 8);\r
+ GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);\r
+ }\r
+\r
+ if (plength)\r
+ *plength = length;\r
+ if (pfourcc)\r
+ *pfourcc = fourcc;\r
+}\r
+\r
+static gboolean\r
+piffdemux_update_sample_offset (GstPiffDemux * piffdemu, PiffDemuxStream * stream, gint64 uuid_offset)\r
+{\r
+ PiffDemuxSample *sample;\r
+ gint i;\r
+\r
+ sample = stream->samples ;\r
+ for (i = 0; i < stream->n_samples; i++)\r
+ {\r
+ sample->offset = sample->offset + uuid_offset;\r
+ sample++;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+static uuid_type_t\r
+piffdemux_get_uuid_type(GstPiffDemux * piffdemux, GstByteReader *uuid_data, gint64 *uuid_offset)\r
+{\r
+ uuid_type_t uuid_type = UUID_UNKNOWN;\r
+ guint32 box_len = 0;\r
+ guint64 box_long_len = 0;\r
+ gchar uuid[16] = {0,};\r
+ int i = 0;\r
+\r
+ if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len))\r
+ goto invalid_uuid;\r
+\r
+ /* Skipping fourcc */\r
+ if (!gst_byte_reader_skip (uuid_data, 4))\r
+ goto invalid_uuid;\r
+\r
+ if (box_len == 1)\r
+ {\r
+ GST_WARNING ("TfxdBoxLongLength field is present...");\r
+ if (!gst_byte_reader_get_uint64_be (uuid_data, &box_long_len))\r
+ goto invalid_uuid;\r
+ GST_DEBUG ("tfxd long length = %llu", box_long_len);\r
+\r
+ *uuid_offset = box_long_len;\r
+ }\r
+ else\r
+ {\r
+ GST_DEBUG ("Box Len = %d", box_len);\r
+ *uuid_offset = box_len;\r
+ }\r
+\r
+ //g_print ("\n\n\n 0x");\r
+ for (i = 0; i < sizeof (uuid); i++)\r
+ {\r
+ if (!gst_byte_reader_get_uint8 (uuid_data, &(uuid[i])))\r
+ goto invalid_uuid;\r
+ //g_print ("%02x", uuid[i]);\r
+ }\r
+ //g_print ("\n\n\n");\r
+\r
+ if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t)))\r
+ {\r
+ GST_INFO ("Found TFXD box");\r
+ return UUID_TFXD;\r
+ }\r
+ else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t)))\r
+ {\r
+ GST_INFO ("Found TFRF box");\r
+ return UUID_TFRF;\r
+ }\r
+ else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t)))\r
+ {\r
+ GST_INFO ("Found sample encryption box");\r
+ return UUID_SAMPLE_ENCRYPT;\r
+ }\r
+ else\r
+ {\r
+ GST_WARNING ("Not an valid UUID box..");\r
+ goto invalid_uuid;\r
+ }\r
+ return uuid_type;\r
+\r
+invalid_uuid:\r
+ GST_ERROR ("Error in parsing UUID atom...");\r
+ return UUID_UNKNOWN;\r
+}\r
+\r
+static gboolean\r
+piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream)\r
+{\r
+ guint32 flags = 0;\r
+ guint32 sample_count = 0;\r
+ guint32 i = 0;\r
+ guint32 algo_id;\r
+ guint8 iv_size = 0;\r
+\r
+ if (!gst_byte_reader_skip (sample_encrypt, 1) ||\r
+ !gst_byte_reader_get_uint24_be (sample_encrypt, &flags))\r
+ goto invalid_encryption;\r
+\r
+ if (flags & SE_OVERRIDE_TE_FLAGS) {\r
+ /* get algorithm id */\r
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &algo_id))\r
+ goto invalid_encryption;\r
+\r
+ /* get IV size */\r
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &iv_size))\r
+ goto invalid_encryption;\r
+\r
+ // TODO: need to add reading of KID\r
+ } else {\r
+ GST_INFO_OBJECT (piffdemux, "Override flags are not present... taking default IV_Size = 8");\r
+ iv_size = 8;\r
+ }\r
+\r
+ /* Get sample count*/\r
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))\r
+ goto invalid_encryption;\r
+\r
+ GST_INFO_OBJECT (piffdemux, "Sample count = %d", sample_count);\r
+\r
+ if (sample_count != stream->n_samples) {\r
+ GST_ERROR_OBJECT (piffdemux, "Not all samples has IV vectors... Don't know how to handle. sample_cnt = %d and stream->n_samples = %d",\r
+ sample_count, stream->n_samples);\r
+ goto invalid_encryption;\r
+ }\r
+\r
+ for (i = 0; i < stream->n_samples; i++) {\r
+ guint8 iv_idx = iv_size;\r
+\r
+ /* resetting entire IV array */\r
+ stream->samples[i].iv = (guint8 *)malloc (iv_size);\r
+ if (NULL == stream->samples[i].iv) {\r
+ GST_ERROR ("Failed to allocate memory...\n");\r
+ goto invalid_encryption;\r
+ }\r
+\r
+ memset (stream->samples[i].iv, 0x00, iv_size);\r
+\r
+ iv_idx = 0;\r
+ while (iv_idx < iv_size) {\r
+ /* get IV byte */\r
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &(stream->samples[i].iv[iv_idx])))\r
+ goto invalid_encryption;\r
+\r
+ iv_idx++;\r
+ }\r
+\r
+#ifdef DEBUG_IV\r
+ {\r
+ guint8 tmp_idx = 0;\r
+ g_print ("sample[%d] : 0x ", i);\r
+\r
+ while (tmp_idx < iv_size ) {\r
+ g_print ("%02x ", stream->samples[i].iv[tmp_idx]);\r
+ tmp_idx++;\r
+ }\r
+ g_print ("\n");\r
+ }\r
+#endif\r
+\r
+ if (flags & SE_USE_SUBSAMPLE_ENCRYPTION) {\r
+ guint16 n_entries;\r
+ guint16 n_idx;\r
+\r
+ /* NumberofEntries in SubSampleEncryption */\r
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &n_entries))\r
+ goto invalid_encryption;\r
+\r
+ stream->samples[i].sub_encry = (PiffDemuxSubSampleEncryption *)malloc (sizeof (PiffDemuxSubSampleEncryption));\r
+ if (NULL == stream->samples[i].sub_encry) {\r
+ GST_ERROR ("Failed to allocate memory...\n");\r
+ goto invalid_encryption;\r
+ }\r
+\r
+ stream->samples[i].sub_encry->sub_entry = g_try_new0 (PiffDemuxSubSampleEntryInfo, n_entries);\r
+ if (NULL == stream->samples[i].sub_encry->sub_entry)\r
+ {\r
+ GST_ERROR_OBJECT (piffdemux, "Failed to allocate memory...");\r
+ goto invalid_encryption;\r
+ }\r
+\r
+ stream->samples[i].sub_encry->n_entries = n_entries;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux,"No. of subsample entries = %d", stream->samples[i].sub_encry->n_entries);\r
+\r
+ for (n_idx = 0; n_idx < n_entries; n_idx++) {\r
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData)))\r
+ goto invalid_encryption;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofClearData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData);\r
+\r
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData)))\r
+ goto invalid_encryption;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofEncryptData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData);\r
+ }\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+\r
+invalid_encryption:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "invalid sample encryption header");\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+\r
+static gboolean\r
+piffdemux_parse_trun (GstPiffDemux * piffdemux, GstByteReader * trun,\r
+ PiffDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,\r
+ guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,\r
+ gint64 * base_offset, gint64 * running_offset)\r
+{\r
+ guint64 timestamp;\r
+ gint32 data_offset = 0;\r
+ guint32 flags = 0, first_flags = 0, samples_count = 0;\r
+ gint i;\r
+ guint8 *data;\r
+ guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;\r
+ PiffDemuxSample *sample;\r
+ gboolean ismv = FALSE;\r
+ guint64 total_duration = 0;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "parsing trun stream ; "\r
+ "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,\r
+ d_sample_duration, d_sample_size, d_sample_flags,\r
+ *base_offset);\r
+\r
+ //Resetting the samples\r
+ stream->n_samples = 0;\r
+\r
+ if (!gst_byte_reader_skip (trun, 1) ||\r
+ !gst_byte_reader_get_uint24_be (trun, &flags))\r
+ goto fail;\r
+\r
+ if (!gst_byte_reader_get_uint32_be (trun, &samples_count))\r
+ goto fail;\r
+\r
+ if (flags & TR_DATA_OFFSET) {\r
+ /* note this is really signed */\r
+ if (!gst_byte_reader_get_int32_be (trun, &data_offset))\r
+ goto fail;\r
+ GST_LOG_OBJECT (piffdemux, "trun data offset %d", data_offset);\r
+ /* default base offset = first byte of moof */\r
+ if (*base_offset == -1) {\r
+ GST_LOG_OBJECT (piffdemux, "base_offset at moof and moof_offset = %"G_GINT64_FORMAT, moof_offset);\r
+ *base_offset = moof_offset;\r
+ }\r
+ *running_offset = *base_offset + data_offset;\r
+ } else {\r
+ /* if no offset at all, that would mean data starts at moof start,\r
+ * which is a bit wrong and is ismv crappy way, so compensate\r
+ * assuming data is in mdat following moof */\r
+ if (*base_offset == -1) {\r
+ *base_offset = moof_offset + moof_length + 8;\r
+ GST_LOG_OBJECT (piffdemux, "base_offset assumed in mdat after moof");\r
+ ismv = TRUE;\r
+ }\r
+ if (*running_offset == -1)\r
+ *running_offset = *base_offset;\r
+ }\r
+\r
+ GST_LOG_OBJECT (piffdemux, "running offset now %" G_GINT64_FORMAT,\r
+ *running_offset);\r
+ GST_LOG_OBJECT (piffdemux, "trun offset %d, flags 0x%x, entries %d",\r
+ data_offset, flags, samples_count);\r
+\r
+ if (flags & TR_FIRST_SAMPLE_FLAGS) {\r
+ if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {\r
+ GST_DEBUG_OBJECT (piffdemux,\r
+ "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");\r
+ flags ^= TR_FIRST_SAMPLE_FLAGS;\r
+ } else {\r
+ if (!gst_byte_reader_get_uint32_be (trun, &first_flags))\r
+ goto fail;\r
+ GST_LOG_OBJECT (piffdemux, "first flags: 0x%x", first_flags);\r
+ }\r
+ }\r
+\r
+ /* FIXME ? spec says other bits should also be checked to determine\r
+ * entry size (and prefix size for that matter) */\r
+ entry_size = 0;\r
+ dur_offset = size_offset = 0;\r
+ if (flags & TR_SAMPLE_DURATION) {\r
+ GST_LOG_OBJECT (piffdemux, "entry duration present");\r
+ dur_offset = entry_size;\r
+ entry_size += 4;\r
+ }\r
+ if (flags & TR_SAMPLE_SIZE) {\r
+ GST_LOG_OBJECT (piffdemux, "entry size present");\r
+ size_offset = entry_size;\r
+ entry_size += 4;\r
+ }\r
+ if (flags & TR_SAMPLE_FLAGS) {\r
+ GST_LOG_OBJECT (piffdemux, "entry flags present");\r
+ flags_offset = entry_size;\r
+ entry_size += 4;\r
+ }\r
+ if (flags & TR_COMPOSITION_TIME_OFFSETS) {\r
+ GST_LOG_OBJECT (piffdemux, "entry ct offset present");\r
+ ct_offset = entry_size;\r
+ entry_size += 4;\r
+ }\r
+\r
+ if (!piff_atom_parser_has_chunks (trun, samples_count, entry_size))\r
+ goto fail;\r
+ data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);\r
+\r
+ if (stream->n_samples >=\r
+ PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (PiffDemuxSample))\r
+ goto index_too_big;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "allocating n_samples %u * %u (%.2f MB)",\r
+ stream->n_samples, (guint) sizeof (PiffDemuxSample),\r
+ stream->n_samples * sizeof (PiffDemuxSample) / (1024.0 * 1024.0));\r
+\r
+ /* create a new array of samples if it's the first sample parsed */\r
+ if (stream->n_samples == 0)\r
+ stream->samples = g_try_new0 (PiffDemuxSample, samples_count);\r
+ /* or try to reallocate it with space enough to insert the new samples */\r
+ else\r
+ stream->samples = g_try_renew (PiffDemuxSample, stream->samples,\r
+ stream->n_samples + samples_count);\r
+ if (stream->samples == NULL)\r
+ goto out_of_memory;\r
+\r
+ if (G_UNLIKELY (stream->n_samples == 0)) {\r
+ /* the timestamp of the first sample is also provided by the tfra entry\r
+ * but we shouldn't rely on it as it is at the end of files */\r
+ timestamp = 0;\r
+ } else {\r
+ /* subsequent fragments extend stream */\r
+ timestamp =\r
+ stream->samples[stream->n_samples - 1].timestamp +\r
+ stream->samples[stream->n_samples - 1].duration;\r
+ }\r
+ sample = stream->samples + stream->n_samples;\r
+ for (i = 0; i < samples_count; i++) {\r
+ guint32 dur, size, sflags, ct;\r
+\r
+ /* first read sample data */\r
+ if (flags & TR_SAMPLE_DURATION) {\r
+ dur = PIFF_UINT32 (data + dur_offset);\r
+ } else {\r
+ dur = d_sample_duration;\r
+ }\r
+ if (flags & TR_SAMPLE_SIZE) {\r
+ size = PIFF_UINT32 (data + size_offset);\r
+ } else {\r
+ size = d_sample_size;\r
+ }\r
+\r
+ GST_DEBUG_OBJECT(piffdemux,"Size of sample %d is %d", i, size);\r
+\r
+ if (flags & TR_FIRST_SAMPLE_FLAGS) {\r
+ if (i == 0) {\r
+ sflags = first_flags;\r
+ } else {\r
+ sflags = d_sample_flags;\r
+ }\r
+ } else if (flags & TR_SAMPLE_FLAGS) {\r
+ sflags = PIFF_UINT32 (data + flags_offset);\r
+ } else {\r
+ sflags = d_sample_flags;\r
+ }\r
+ if (flags & TR_COMPOSITION_TIME_OFFSETS) {\r
+ ct = PIFF_UINT32 (data + ct_offset);\r
+ } else {\r
+ ct = 0;\r
+ }\r
+ data += entry_size;\r
+\r
+ /* fill the sample information */\r
+ sample->offset = *running_offset;\r
+ sample->pts_offset = ct;\r
+ sample->size = size;\r
+ sample->timestamp = timestamp;\r
+ sample->duration = dur;\r
+ /* sample-is-difference-sample */\r
+ /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,\r
+ * now idea how it relates to bitfield other than massive LE/BE confusion */\r
+ sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);\r
+ sample->iv = NULL;\r
+ sample->sub_encry = NULL;\r
+\r
+ stream->samples[i] = *sample;\r
+\r
+ *running_offset += size;\r
+ timestamp += dur;\r
+ sample++;\r
+\r
+ /* calculate total duration of the present fragment */\r
+ total_duration += gst_util_uint64_scale (dur, GST_SECOND, stream->timescale);\r
+ }\r
+\r
+ stream->sample_index = 0;\r
+\r
+ stream->n_samples += samples_count;\r
+\r
+ /* calculate avg fps based on avg frame duration */\r
+ stream->avg_dur = total_duration/samples_count;\r
+ g_print ("total dur = %"GST_TIME_FORMAT", avg_dur = %"GST_TIME_FORMAT"count = %d\n",\r
+ GST_TIME_ARGS(total_duration), GST_TIME_ARGS(stream->avg_dur), samples_count);\r
+\r
+ return TRUE;\r
+\r
+fail:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "failed to parse trun");\r
+ return FALSE;\r
+ }\r
+out_of_memory:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "failed to allocate %d samples",\r
+ stream->n_samples);\r
+ return FALSE;\r
+ }\r
+index_too_big:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "not allocating index of %d samples, would "\r
+ "be larger than %uMB (broken file?)", stream->n_samples,\r
+ PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+static gboolean\r
+piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd)\r
+{\r
+ guint32 seq_num = 0;\r
+\r
+ if (!gst_byte_reader_skip (mfhd, 4))\r
+ goto invalid_mfhd;\r
+\r
+ if (!gst_byte_reader_get_uint32_be (mfhd, &seq_num))\r
+ goto invalid_mfhd;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "sequence number present in mfhd = %d", seq_num);\r
+\r
+ return TRUE;\r
+\r
+invalid_mfhd:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "invalid movie fragment header");\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+\r
+static gboolean\r
+piffdemux_parse_tfhd (GstPiffDemux * piffdemux, GstByteReader * tfhd,\r
+ guint32 * default_sample_duration,\r
+ guint32 * default_sample_size, guint32 * default_sample_flags,\r
+ gint64 * base_offset)\r
+{\r
+ guint32 flags = 0;\r
+ guint32 track_id = 0;\r
+\r
+ if (!gst_byte_reader_skip (tfhd, 1) ||\r
+ !gst_byte_reader_get_uint24_be (tfhd, &flags))\r
+ goto invalid_track;\r
+\r
+ if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))\r
+ goto invalid_track;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "trackID = %d", track_id);\r
+\r
+ if (flags & TF_BASE_DATA_OFFSET) {\r
+ if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))\r
+ goto invalid_track;\r
+ GST_DEBUG ("BaseData Offset = %"G_GUINT64_FORMAT, base_offset);\r
+ }\r
+\r
+ /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */\r
+ if (flags & TF_SAMPLE_DESCRIPTION_INDEX)\r
+ if (!gst_byte_reader_skip (tfhd, 4))\r
+ goto invalid_track;\r
+\r
+ if (flags & TF_DEFAULT_SAMPLE_DURATION)\r
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))\r
+ goto invalid_track;\r
+\r
+ if (flags & TF_DEFAULT_SAMPLE_SIZE)\r
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))\r
+ goto invalid_track;\r
+\r
+ if (flags & TF_DEFAULT_SAMPLE_FLAGS)\r
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))\r
+ goto invalid_track;\r
+\r
+ return TRUE;\r
+\r
+invalid_track:\r
+ {\r
+ GST_WARNING_OBJECT (piffdemux, "invalid track fragment header");\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+static gboolean\r
+piffdemux_parse_tfxd (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfxd)\r
+{\r
+ guint8 version = 0;\r
+\r
+ // TODO: In my opinion, tfxd will be mainly useful when lookahead count = 0. In this case, based on this duration, next fragment timstamp can be calculted.. Need to test this using our server\r
+\r
+ if (!gst_byte_reader_get_uint8 (tfxd, &version))\r
+ goto invalid_tfxd;\r
+\r
+ if (!gst_byte_reader_skip (tfxd, 3))\r
+ goto invalid_tfxd;\r
+\r
+ if (!piffdemux->lookahead_cnt) {\r
+ piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));\r
+ if (NULL == piffdemux->param) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+ piffdemux->param->count = 1;\r
+ piffdemux->param->long_info = NULL;\r
+ piffdemux->param->info = NULL;\r
+ piffdemux->param->is_eos = FALSE;\r
+\r
+ // TODO: presentation will be ended based on timeout in souphttpsrc in lookaheadcnt = 0 case\r
+ }\r
+\r
+ if (version == 1) {\r
+ guint64 duration = 0;\r
+ guint64 timestamp = 0;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");\r
+ if (!gst_byte_reader_get_uint64_be (tfxd, ×tamp))\r
+ goto invalid_tfxd;\r
+ if (!gst_byte_reader_get_uint64_be (tfxd, &duration))\r
+ goto invalid_tfxd;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT,\r
+ timestamp, duration);\r
+\r
+ if (!piffdemux->lookahead_cnt) {\r
+ piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));\r
+ if (NULL == piffdemux->param->long_info) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ /* Calculate next fragment's timestamp using current fragment's timestamp + duration */\r
+ piffdemux->param->long_info->duration = GST_CLOCK_TIME_NONE;\r
+ piffdemux->param->long_info->ts = timestamp +duration;\r
+ }\r
+ } else if (version == 0) {\r
+ guint32 duration = 0;\r
+ guint32 timestamp = 0;\r
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");\r
+\r
+ if (!gst_byte_reader_get_uint32_be (tfxd, ×tamp))\r
+ goto invalid_tfxd;\r
+\r
+ if (!gst_byte_reader_get_uint32_be (tfxd, &duration))\r
+ goto invalid_tfxd;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,\r
+ timestamp, duration);\r
+\r
+ if (!piffdemux->lookahead_cnt) {\r
+ piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));\r
+ if (NULL == piffdemux->param->info) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+ /* Calculate next fragment's timestamp using current fragment's timestamp + duration */\r
+ piffdemux->param->info->duration = GST_CLOCK_TIME_NONE;\r
+ piffdemux->param->info->ts = timestamp +duration;\r
+ }\r
+ } else {\r
+ GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfxd...");\r
+ return FALSE;\r
+ }\r
+\r
+ if (!piffdemux->lookahead_cnt) {\r
+ GST_DEBUG_OBJECT (piffdemux, "Emitting live-param signal...");\r
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);\r
+ }\r
+\r
+ return TRUE;\r
+\r
+invalid_tfxd:\r
+ GST_ERROR ("Invalid TFXD atom...");\r
+ return FALSE;\r
+}\r
+\r
+\r
+static gboolean\r
+piffdemux_parse_tfrf (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfrf)\r
+{\r
+ guint8 version = 0;\r
+ guint8 frag_cnt = 0;\r
+ guint8 i = 0;\r
+\r
+ /* Getting version info */\r
+ if (!gst_byte_reader_get_uint8 (tfrf, &version))\r
+ goto invalid_tfrf;\r
+\r
+ /* skipping reserved flags */\r
+ if (!gst_byte_reader_skip (tfrf, 3))\r
+ goto invalid_tfrf;\r
+\r
+ if (!gst_byte_reader_get_uint8 (tfrf, &frag_cnt))\r
+ goto invalid_tfrf;\r
+\r
+ GST_INFO_OBJECT (piffdemux, "Subsequent fragments info count = %d", frag_cnt);\r
+\r
+ piffdemux->param = (piff_live_param_t *)malloc(sizeof (piff_live_param_t));\r
+ if (NULL == piffdemux->param) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ piffdemux->param->count = frag_cnt;\r
+ piffdemux->param->long_info = NULL;\r
+ piffdemux->param->info = NULL;\r
+ piffdemux->param->is_eos = FALSE;\r
+\r
+ // TODO: Duration and timestamp values need to be posted to msl using g_signal_emit\r
+\r
+ if (version == 1) {\r
+ guint64 duration = 0;\r
+ guint64 timestamp = 0;\r
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");\r
+\r
+ piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));\r
+ if (NULL == piffdemux->param->long_info) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ for (i = 0; i < frag_cnt; i++) {\r
+ if (!gst_byte_reader_get_uint64_be (tfrf, ×tamp))\r
+ goto invalid_tfrf;\r
+ if (!gst_byte_reader_get_uint64_be (tfrf, &duration))\r
+ goto invalid_tfrf;\r
+ GST_DEBUG_OBJECT (piffdemux, "tfrf long: absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT"\n",\r
+ timestamp, duration);\r
+ (piffdemux->param->long_info[i]).ts = timestamp;\r
+ (piffdemux->param->long_info[i]).duration = duration;\r
+ }\r
+ } else if (version == 0) {\r
+ guint32 duration = 0;\r
+ guint32 timestamp = 0;\r
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");\r
+\r
+ piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));\r
+ if (NULL == piffdemux->param->info) {\r
+ GST_ERROR ("Memory not available...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ for (i = 0; i < frag_cnt; i++) {\r
+ if (!gst_byte_reader_get_uint32_be (tfrf, ×tamp))\r
+ goto invalid_tfrf;\r
+ if (!gst_byte_reader_get_uint32_be (tfrf, &duration))\r
+ goto invalid_tfrf;\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "tfrf int: absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,\r
+ timestamp, duration);\r
+ (piffdemux->param->info[i]).ts = timestamp;\r
+ (piffdemux->param->info[i]).duration = duration;\r
+ }\r
+ } else {\r
+ GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfrf...");\r
+ return FALSE;\r
+ }\r
+\r
+ g_print ("Signalling from TFRF box..\n");\r
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);\r
+\r
+ return TRUE;\r
+\r
+invalid_tfrf:\r
+ GST_ERROR_OBJECT (piffdemux, "Invalid TFRF atom...");\r
+ return FALSE;\r
+}\r
+\r
+\r
+static gboolean\r
+piffdemux_parse_moof (GstPiffDemux * piffdemux, const guint8 * buffer, guint length,\r
+ guint64 moof_offset, PiffDemuxStream * stream)\r
+{\r
+ GNode *moof_node, *mfhd_node, *traf_node, *tfhd_node, *trun_node, *uuid_node;\r
+ GstByteReader mfhd_data, trun_data, tfhd_data, uuid_data;\r
+ guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;\r
+ gint64 base_offset, running_offset;\r
+ gint64 uuid_offset = 0;\r
+ gboolean found_tfxd = FALSE;\r
+ gboolean found_tfrf = FALSE;\r
+\r
+ /* NOTE @stream ignored */\r
+\r
+ moof_node = g_node_new ((guint8 *) buffer);\r
+ piffdemux_parse_node (piffdemux, moof_node, buffer, length);\r
+ //piffdemux_node_dump (piffdemux, moof_node);\r
+\r
+ /* unknown base_offset to start with */\r
+ base_offset = running_offset = -1;\r
+\r
+ mfhd_node = piffdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);\r
+ if (!mfhd_node)\r
+ goto missing_mfhd;\r
+\r
+ if (!piffdemux_parse_mfhd (piffdemux, &mfhd_data))\r
+ goto missing_mfhd;\r
+\r
+ traf_node = piffdemux_tree_get_child_by_type (moof_node, FOURCC_traf);\r
+ while (traf_node) {\r
+ /* Fragment Header node */\r
+ tfhd_node =\r
+ piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,\r
+ &tfhd_data);\r
+ if (!tfhd_node)\r
+ goto missing_tfhd;\r
+ if (!piffdemux_parse_tfhd (piffdemux, &tfhd_data, &ds_duration,\r
+ &ds_size, &ds_flags, &base_offset))\r
+ goto missing_tfhd;\r
+\r
+ if (G_UNLIKELY (base_offset < -1))\r
+ goto lost_offset;\r
+\r
+ /* Track Run node */\r
+ trun_node =\r
+ piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,\r
+ &trun_data);\r
+ while (trun_node) {\r
+ piffdemux_parse_trun (piffdemux, &trun_data, stream,\r
+ ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,\r
+ &running_offset);\r
+ /* iterate all siblings */\r
+ trun_node = piffdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,\r
+ &trun_data);\r
+ }\r
+\r
+ uuid_node = piffdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);\r
+ while (uuid_node) {\r
+ uuid_type_t uuid_type;\r
+ guint8 *buffer = (guint8 *) uuid_node->data;\r
+\r
+ gst_byte_reader_init (&uuid_data, buffer, PIFF_UINT32 (buffer));\r
+\r
+ uuid_type = piffdemux_get_uuid_type (piffdemux, &uuid_data, &uuid_offset);\r
+\r
+ if ((UUID_TFXD == uuid_type) && piffdemux->is_live) {\r
+ // TODO: Dont know, why we should not consider tfxd offset...if we use tfxd offset also, not working.. PIFF doc does not say anything :(\r
+ found_tfxd = TRUE;\r
+ if (!piffdemux_parse_tfxd (piffdemux, stream, &uuid_data))\r
+ goto fail;\r
+ } else if ((UUID_TFRF == uuid_type) && piffdemux->is_live && piffdemux->lookahead_cnt) {\r
+ found_tfrf = TRUE;\r
+ if (!piffdemux_parse_tfrf (piffdemux, stream, &uuid_data))\r
+ goto fail;\r
+ piffdemux_update_sample_offset (piffdemux, stream, uuid_offset);\r
+ running_offset += uuid_offset;\r
+ } else if (UUID_SAMPLE_ENCRYPT == uuid_type) {\r
+ if (!piffdemux_parse_sample_encryption (piffdemux, &uuid_data, stream))\r
+ goto fail;\r
+ } else {\r
+ GST_WARNING_OBJECT (piffdemux, "Ignoring Wrong UUID...");\r
+ }\r
+\r
+ /* iterate all siblings */\r
+ uuid_node = piffdemux_tree_get_sibling_by_type (uuid_node, FOURCC_uuid);\r
+ }\r
+\r
+ if (piffdemux->is_live) {\r
+ if (!found_tfxd) {\r
+ GST_ERROR_OBJECT (piffdemux, "TFXD box is not present for live stream");\r
+ goto fail;\r
+ }\r
+\r
+ if (!found_tfrf && piffdemux->lookahead_cnt) {\r
+ /* when lookahead count is non-zero in manifest & if tfrf box is not present., means EOS */\r
+ GST_INFO_OBJECT (piffdemux, "Reached Endof Live presentation..");\r
+\r
+ piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));\r
+ if (NULL == piffdemux->param) {\r
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");\r
+ goto fail;\r
+ }\r
+ piffdemux->param->count = 0;\r
+ piffdemux->param->long_info = NULL;\r
+ piffdemux->param->info = NULL;\r
+ piffdemux->param->is_eos = TRUE; /* marking EOS */\r
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);\r
+ }\r
+ }\r
+\r
+ /* if no new base_offset provided for next traf,\r
+ * base is end of current traf */\r
+ base_offset = running_offset;\r
+ running_offset = -1;\r
+\r
+ /* iterate all siblings */\r
+ traf_node = piffdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);\r
+ }\r
+ g_node_destroy (moof_node);\r
+ return TRUE;\r
+\r
+missing_mfhd:\r
+ {\r
+ GST_DEBUG_OBJECT (piffdemux, "missing mfhd box");\r
+ goto fail;\r
+ }\r
+\r
+missing_tfhd:\r
+ {\r
+ GST_DEBUG_OBJECT (piffdemux, "missing tfhd box");\r
+ goto fail;\r
+ }\r
+lost_offset:\r
+ {\r
+ GST_DEBUG_OBJECT (piffdemux, "lost offset");\r
+ goto fail;\r
+ }\r
+fail:\r
+ {\r
+ g_node_destroy (moof_node);\r
+\r
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
+ ("This file is corrupt and cannot be played."), (NULL));\r
+\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+\r
+/* activate the given segment number @seg_idx of @stream at time @offset.\r
+ * @offset is an absolute global position over all the segments.\r
+ *\r
+ * This will push out a NEWSEGMENT event with the right values and\r
+ * position the stream index to the first decodable sample before\r
+ * @offset.\r
+ */\r
+static gboolean\r
+gst_piffdemux_activate_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,\r
+ guint32 seg_idx, guint64 offset)\r
+{\r
+ GstEvent *event;\r
+ PiffDemuxSegment *segment;\r
+ guint64 seg_time;\r
+ guint64 start, stop, time;\r
+ gdouble rate;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,\r
+ seg_idx, offset);\r
+\r
+ /* update the current segment */\r
+ stream->segment_index = seg_idx;\r
+\r
+ /* get the segment */\r
+ segment = &stream->segments[seg_idx];\r
+\r
+ if (G_UNLIKELY (offset < segment->time)) {\r
+ GST_WARNING_OBJECT (piffdemux, "offset < segment->time %" G_GUINT64_FORMAT,\r
+ segment->time);\r
+ return FALSE;\r
+ }\r
+\r
+ /* segment lies beyond total indicated duration */\r
+ if (G_UNLIKELY (piffdemux->segment.duration != -1 &&\r
+ segment->time > piffdemux->segment.duration)) {\r
+ GST_WARNING_OBJECT (piffdemux, "file duration %" G_GINT64_FORMAT\r
+ " < segment->time %" G_GUINT64_FORMAT, piffdemux->segment.duration,\r
+ segment->time);\r
+ return FALSE;\r
+ }\r
+\r
+ /* get time in this segment */\r
+ seg_time = offset - segment->time;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "seg_time %" GST_TIME_FORMAT,\r
+ GST_TIME_ARGS (seg_time));\r
+\r
+ if (G_UNLIKELY (seg_time > segment->duration)) {\r
+ GST_LOG_OBJECT (piffdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,\r
+ GST_TIME_ARGS (segment->duration));\r
+ return FALSE;\r
+ }\r
+\r
+ /* piffdemux->segment.stop is in outside-time-realm, whereas\r
+ * segment->media_stop is in track-time-realm.\r
+ *\r
+ * In order to compare the two, we need to bring segment.stop\r
+ * into the track-time-realm */\r
+\r
+ stop = piffdemux->segment.stop;\r
+ if (stop == -1)\r
+ stop = piffdemux->segment.duration;\r
+ if (stop == -1)\r
+ stop = segment->media_stop;\r
+ else\r
+ stop =\r
+ MIN (segment->media_stop, stop - segment->time + segment->media_start);\r
+\r
+ if (piffdemux->segment.rate >= 0) {\r
+ start = MIN (segment->media_start + seg_time, stop);\r
+ time = offset;\r
+ } else {\r
+ if (segment->media_start >= piffdemux->segment.start) {\r
+ start = segment->media_start;\r
+ time = segment->time;\r
+ } else {\r
+ start = piffdemux->segment.start;\r
+ time = segment->time + (piffdemux->segment.start - segment->media_start);\r
+ }\r
+\r
+ start = MAX (segment->media_start, piffdemux->segment.start);\r
+ stop = MIN (segment->media_start + seg_time, stop);\r
+ }\r
+\r
+ GST_DEBUG_OBJECT (piffdemux, "newsegment %d from %" GST_TIME_FORMAT\r
+ " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,\r
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));\r
+\r
+ /* combine global rate with that of the segment */\r
+ rate = segment->rate * piffdemux->segment.rate;\r
+\r
+ /* update the segment values used for clipping */\r
+ gst_segment_init (&stream->segment, GST_FORMAT_TIME);\r
+ gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,\r
+ start, stop, time);\r
+\r
+ /* now prepare and send the segment */\r
+ event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,\r
+ start, stop, time);\r
+ gst_pad_push_event (piffdemux->srcpad, event);\r
+ /* assume we can send more data now */\r
+ stream->last_ret = GST_FLOW_OK;\r
+ /* clear to send tags on this pad now */\r
+ gst_piffdemux_push_tags (piffdemux, stream);\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/* prepare to get the current sample of @stream, getting essential values.\r
+ *\r
+ * This function will also prepare and send the segment when needed.\r
+ *\r
+ * Return FALSE if the stream is EOS.\r
+ */\r
+static gboolean\r
+gst_piffdemux_prepare_current_sample (GstPiffDemux * piffdemux,\r
+ PiffDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,\r
+ guint64 * duration, gboolean * keyframe)\r
+{\r
+ PiffDemuxSample *sample;\r
+ guint64 time_position;\r
+ guint32 seg_idx;\r
+\r
+ g_return_val_if_fail (stream != NULL, FALSE);\r
+\r
+ time_position = stream->time_position;\r
+ if (G_UNLIKELY (time_position == -1))\r
+ goto eos;\r
+\r
+ seg_idx = stream->segment_index;\r
+ if (G_UNLIKELY (seg_idx == -1)) {\r
+ /* find segment corresponding to time_position if we are looking\r
+ * for a segment. */\r
+ seg_idx = gst_piffdemux_find_segment (piffdemux, stream, time_position);\r
+\r
+ /* nothing found, we're really eos */\r
+ if (seg_idx == -1)\r
+ goto eos;\r
+ }\r
+\r
+ /* different segment, activate it, sample_index will be set. */\r
+ if (G_UNLIKELY (stream->segment_index != seg_idx))\r
+ gst_piffdemux_activate_segment (piffdemux, stream, seg_idx, time_position);\r
+\r
+ GST_LOG_OBJECT (piffdemux, "segment active, index = %u of %u",\r
+ stream->sample_index, stream->n_samples);\r
+\r
+ if (G_UNLIKELY (stream->sample_index >= stream->n_samples))\r
+ goto eos;\r
+\r
+\r
+ /* now get the info for the sample we're at */\r
+ sample = &stream->samples[stream->sample_index];\r
+\r
+ *timestamp = PIFFSAMPLE_PTS (stream, sample);\r
+ *offset = sample->offset;\r
+ *size = sample->size;\r
+ *duration = PIFFSAMPLE_DUR_PTS (stream, sample, *timestamp);\r
+ *keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);\r
+\r
+ return TRUE;\r
+\r
+ /* special cases */\r
+eos:\r
+ {\r
+ stream->time_position = -1;\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+/* the input buffer metadata must be writable,\r
+ * but time/duration etc not yet set and need not be preserved */\r
+static GstBuffer *\r
+gst_piffdemux_process_buffer (GstPiffDemux * piffdemux, PiffDemuxStream * stream,\r
+ GstBuffer * buf)\r
+{\r
+ guint8 *data;\r
+ guint size, nsize = 0;\r
+ gchar *str;\r
+\r
+ data = GST_BUFFER_DATA (buf);\r
+ size = GST_BUFFER_SIZE (buf);\r
+\r
+ if (G_UNLIKELY (stream->subtype != FOURCC_text)) {\r
+ return buf;\r
+ }\r
+\r
+ if (G_LIKELY (size >= 2)) {\r
+ nsize = GST_READ_UINT16_BE (data);\r
+ nsize = MIN (nsize, size - 2);\r
+ }\r
+\r
+ GST_LOG_OBJECT (piffdemux, "3GPP timed text subtitle: %d/%d", nsize, size);\r
+\r
+ /* takes care of UTF-8 validation or UTF-16 recognition,\r
+ * no other encoding expected */\r
+ str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);\r
+ if (str) {\r
+ gst_buffer_unref (buf);\r
+ buf = gst_buffer_new ();\r
+ GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;\r
+ GST_BUFFER_SIZE (buf) = strlen (str);\r
+ } else {\r
+ /* may be 0-size subtitle, which is also sent to keep pipeline going */\r
+ GST_BUFFER_DATA (buf) = data + 2;\r
+ GST_BUFFER_SIZE (buf) = nsize;\r
+ }\r
+\r
+ /* FIXME ? convert optional subsequent style info to markup */\r
+\r
+ return buf;\r
+}\r
+\r
+/* Sets a buffer's attributes properly and pushes it downstream.\r
+ * Also checks for additional actions and custom processing that may\r
+ * need to be done first.\r
+ */\r
+static gboolean\r
+gst_piffdemux_decorate_and_push_buffer (GstPiffDemux * piffdemux,\r
+ PiffDemuxStream * stream, GstBuffer * buf,\r
+ guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,\r
+ guint64 byte_position)\r
+{\r
+ GstFlowReturn ret = GST_FLOW_OK;\r
+\r
+ if (!stream->caps) {\r
+ GST_WARNING_OBJECT (piffdemux, "caps are empty...creat any caps");\r
+ stream->caps = gst_caps_new_any();\r
+ if (!stream->caps) {\r
+ GST_ERROR_OBJECT (piffdemux, "failed to create caps...");\r
+ ret = GST_FLOW_ERROR;\r
+ goto exit;\r
+ }\r
+ }\r
+\r
+ /* position reporting */\r
+ if (piffdemux->segment.rate >= 0) {\r
+ // TODO: Segment fault is coming here for Audio stream.. need to check\r
+ gst_segment_set_last_stop (&piffdemux->segment, GST_FORMAT_TIME, position);\r
+ //gst_piffdemux_sync_streams (piffdemux);\r
+ }\r
+\r
+ /* send out pending buffers */\r
+ while (stream->buffers) {\r
+ GstBuffer *buffer = (GstBuffer *) stream->buffers->data;\r
+\r
+ if (G_UNLIKELY (stream->discont)) {\r
+ GST_LOG_OBJECT (piffdemux, "marking discont buffer");\r
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);\r
+ stream->discont = FALSE;\r
+ }\r
+ gst_buffer_set_caps (buffer, stream->caps);\r
+\r
+ gst_pad_push (piffdemux->srcpad, buffer);\r
+\r
+ stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);\r
+ }\r
+\r
+ /* we're going to modify the metadata */\r
+ buf = gst_buffer_make_metadata_writable (buf);\r
+\r
+ /* for subtitle processing */\r
+ if (G_UNLIKELY (stream->need_process))\r
+ buf = gst_piffdemux_process_buffer (piffdemux, stream, buf);\r
+\r
+ GST_BUFFER_TIMESTAMP (buf) = timestamp;\r
+ GST_BUFFER_DURATION (buf) = duration;\r
+ GST_BUFFER_OFFSET (buf) = -1;\r
+ GST_BUFFER_OFFSET_END (buf) = -1;\r
+\r
+ if (G_UNLIKELY (stream->padding)) {\r
+ GST_BUFFER_DATA (buf) += stream->padding;\r
+ GST_BUFFER_SIZE (buf) -= stream->padding;\r
+ }\r
+\r
+ if (G_UNLIKELY (buf == NULL))\r
+ goto exit;\r
+\r
+ if (G_UNLIKELY (stream->discont)) {\r
+ GST_LOG_OBJECT (piffdemux, "marking discont buffer");\r
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);\r
+ stream->discont = FALSE;\r
+ }\r
+\r
+ if (!keyframe)\r
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);\r
+\r
+ //g_print ("\n\npad caps : %s\n\n", gst_caps_to_string (gst_pad_get_caps (stream->pad)));\r
+\r
+ //gst_buffer_set_caps (buf, stream->caps); // commenting to avoid caps by setting properties\r
+\r
+\r
+ // TODO: need to see how resolution switch will work\r
+ gst_buffer_set_caps (buf, stream->caps);\r
+\r
+ GST_LOG_OBJECT (piffdemux,\r
+ "Pushing buffer of size = %d with time %" GST_TIME_FORMAT ", duration %"\r
+ GST_TIME_FORMAT, GST_BUFFER_SIZE(buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),\r
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));\r
+\r
+#ifdef DEC_OUT_FRAME_DUMP\r
+ {\r
+ int written = 0;\r
+ written = fwrite (GST_BUFFER_DATA (buf), sizeof (unsigned char), GST_BUFFER_SIZE (buf), piffdump);\r
+ g_print ("PIFFDEMUX: written = %d\n", written);\r
+ fflush (piffdump);\r
+ }\r
+#endif\r
+\r
+ ret = gst_pad_push (piffdemux->srcpad, buf);\r
+\r
+exit:\r
+ return ret;\r
+}\r
+\r
+\r
+/*\r
+ * next_entry_size\r
+ *\r
+ * Returns the size of the first entry at the current offset.\r
+ * If -1, there are none (which means EOS or empty file).\r
+ */\r
+static guint64\r
+next_entry_size (GstPiffDemux * demux)\r
+{\r
+ PiffDemuxStream *stream = demux->stream;\r
+ PiffDemuxSample *sample;\r
+\r
+ GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,\r
+ demux->offset);\r
+\r
+ GST_DEBUG_OBJECT (demux, "demux->sample_index = %d", stream->sample_index);\r
+\r
+ if (stream->sample_index == -1)\r
+ stream->sample_index = 0;\r
+\r
+ if (stream->sample_index >= stream->n_samples) {\r
+ GST_LOG_OBJECT (demux, "stream %d samples exhausted n_samples = %d",\r
+ stream->sample_index, stream->n_samples);\r
+ return -1;\r
+ }\r
+\r
+ sample = &stream->samples[stream->sample_index];\r
+\r
+ GST_LOG_OBJECT (demux,\r
+ "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT\r
+ " / size:%" G_GUINT32_FORMAT ")", stream->sample_index, stream->sample_index,\r
+ sample->offset, sample->size);\r
+\r
+ GST_LOG_OBJECT (demux, "stream : demux->offset :%"G_GUINT64_FORMAT, demux->offset);\r
+\r
+ stream = demux->stream;\r
+ sample = &stream->samples[stream->sample_index];\r
+\r
+ if (sample->offset >= demux->offset) {\r
+ demux->todrop = sample->offset - demux->offset;\r
+ return sample->size + demux->todrop;\r
+ }\r
+\r
+ GST_DEBUG_OBJECT (demux,\r
+ "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);\r
+ return -1;\r
+}\r
+\r
+static void\r
+gst_piffdemux_post_progress (GstPiffDemux * demux, gint num, gint denom)\r
+{\r
+ gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);\r
+\r
+ gst_element_post_message (GST_ELEMENT_CAST (demux),\r
+ gst_message_new_element (GST_OBJECT_CAST (demux),\r
+ gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));\r
+}\r
+\r
+static gboolean\r
+piffdemux_seek_offset (GstPiffDemux * demux, guint64 offset)\r
+{\r
+ GstEvent *event;\r
+ gboolean res = 0;\r
+\r
+ GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);\r
+\r
+ event =\r
+ gst_event_new_seek (1.0, GST_FORMAT_BYTES,\r
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,\r
+ GST_SEEK_TYPE_NONE, -1);\r
+\r
+ res = gst_pad_push_event (demux->sinkpad, event);\r
+\r
+ return res;\r
+}\r
+\r
+static GstFlowReturn\r
+gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)\r
+{\r
+ GstPiffDemux *demux;\r
+ GstFlowReturn ret = GST_FLOW_OK;\r
+ demux = GST_PIFFDEMUX (gst_pad_get_parent (sinkpad));\r
+\r
+ gst_adapter_push (demux->adapter, inbuf);\r
+\r
+ /* we never really mean to buffer that much */\r
+ if (demux->neededbytes == -1)\r
+ goto eos;\r
+\r
+ GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",\r
+ inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));\r
+\r
+ while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&\r
+ (ret == GST_FLOW_OK)) {\r
+\r
+ GST_DEBUG_OBJECT (demux,\r
+ "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,\r
+ demux->state, demux->neededbytes, demux->offset);\r
+\r
+ switch (demux->state) {\r
+ case PIFFDEMUX_STATE_INITIAL:{\r
+ const guint8 *data;\r
+ guint32 fourcc;\r
+ guint64 size;\r
+\r
+ data = gst_adapter_peek (demux->adapter, demux->neededbytes);\r
+\r
+ /* get fourcc/length, set neededbytes */\r
+ extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,\r
+ &size, &fourcc);\r
+ GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "\r
+ "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);\r
+ if (size == 0) {\r
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,\r
+ ("This file is invalid and cannot be played."),\r
+ ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",\r
+ GST_FOURCC_ARGS (fourcc)));\r
+\r
+ ret = GST_FLOW_ERROR;\r
+ break;\r
+ }\r
+\r
+ if (fourcc == FOURCC_mdat) {\r
+ if (demux->moof_rcvd) {\r
+ /* we have the headers, start playback */\r
+ demux->state = PIFFDEMUX_STATE_MOVIE;\r
+ demux->neededbytes = next_entry_size (demux);\r
+ demux->mdatleft = size;\r
+\r
+ /* Only post, event on pads is done after newsegment */\r
+ piffdemux_post_global_tags (demux);\r
+ } else {\r
+ GST_ERROR_OBJECT (demux, "mdata received before moof.. not handled");\r
+ goto unknown_stream;\r
+ }\r
+ } else if (G_UNLIKELY (size > PIFFDEMUX_MAX_ATOM_SIZE)) {\r
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,\r
+ ("This file is invalid and cannot be played."),\r
+ ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,\r
+ GST_FOURCC_ARGS (fourcc), size));\r
+ ret = GST_FLOW_ERROR;\r
+ break;\r
+ } else {\r
+ demux->neededbytes = size;\r
+ demux->state = PIFFDEMUX_STATE_HEADER;\r
+ }\r
+ break;\r
+ }\r
+ case PIFFDEMUX_STATE_HEADER:{\r
+ const guint8 *data;\r
+ guint32 fourcc;\r
+\r
+ GST_DEBUG_OBJECT (demux, "In header");\r
+\r
+ data = gst_adapter_peek (demux->adapter, demux->neededbytes);\r
+\r
+ /* parse the header */\r
+ extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,\r
+ &fourcc);\r
+ if (fourcc == FOURCC_moof) {\r
+ GST_DEBUG_OBJECT (demux, "Parsing [moof]");\r
+ if (!piffdemux_parse_moof (demux, data, demux->neededbytes,\r
+ demux->offset, demux->stream)) {\r
+ ret = GST_FLOW_ERROR;\r
+ goto done;\r
+ }\r
+ demux->moof_rcvd = TRUE;\r
+ } else {\r
+ GST_WARNING_OBJECT (demux,\r
+ "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,\r
+ GST_FOURCC_ARGS (fourcc));\r
+ /* Let's jump that one and go back to initial state */\r
+ }\r
+\r
+ if (demux->mdatbuffer) {\r
+ /* the mdat was before the header */\r
+ GST_DEBUG_OBJECT (demux, "We have mdatbuffer:%p",\r
+ demux->mdatbuffer);\r
+ gst_adapter_clear (demux->adapter);\r
+ demux->mdatbuffer = NULL;\r
+ demux->offset = demux->mdatoffset;\r
+ demux->neededbytes = next_entry_size (demux);\r
+ demux->state = PIFFDEMUX_STATE_MOVIE;\r
+ demux->mdatleft = gst_adapter_available (demux->adapter);\r
+\r
+ /* Only post, event on pads is done after newsegment */\r
+ piffdemux_post_global_tags (demux);\r
+ } else {\r
+ GST_DEBUG_OBJECT (demux, "Carrying on normally");\r
+ gst_adapter_flush (demux->adapter, demux->neededbytes);\r
+ demux->offset += demux->neededbytes;\r
+ demux->neededbytes = 16;\r
+ demux->state = PIFFDEMUX_STATE_INITIAL;\r
+ }\r
+\r
+ break;\r
+ }\r
+ case PIFFDEMUX_STATE_BUFFER_MDAT:{\r
+ GstBuffer *buf;\r
+\r
+ GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,\r
+ demux->offset);\r
+ buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);\r
+ GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,\r
+ GST_FOURCC_ARGS (PIFF_FOURCC (GST_BUFFER_DATA (buf) + 4)));\r
+ if (demux->mdatbuffer)\r
+ demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);\r
+ else\r
+ demux->mdatbuffer = buf;\r
+ demux->offset += demux->neededbytes;\r
+ demux->neededbytes = 16;\r
+ demux->state = PIFFDEMUX_STATE_INITIAL;\r
+ gst_piffdemux_post_progress (demux, 1, 1);\r
+\r
+ break;\r
+ }\r
+ case PIFFDEMUX_STATE_MOVIE:{\r
+ GstBuffer *outbuf;\r
+ PiffDemuxStream *stream = demux->stream;\r
+ PiffDemuxSample *sample;\r
+ guint64 timestamp, duration, position;\r
+ gboolean keyframe;\r
+\r
+ GST_DEBUG_OBJECT (demux,\r
+ "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);\r
+\r
+ if (demux->fragmented) {\r
+ GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,\r
+ demux->mdatleft);\r
+ if (G_LIKELY (demux->todrop < demux->mdatleft)) {\r
+ /* if needed data starts within this atom,\r
+ * then it should not exceed this atom */\r
+ if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {\r
+\r
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,\r
+ ("This file is invalid and cannot be played."),\r
+ ("sample data crosses atom boundary"));\r
+\r
+ ret = GST_FLOW_ERROR;\r
+ break;\r
+ }\r
+ demux->mdatleft -= demux->neededbytes;\r
+ } else {\r
+ GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");\r
+ /* so we are dropping more than left in this atom */\r
+ demux->todrop -= demux->mdatleft;\r
+ demux->neededbytes -= demux->mdatleft;\r
+ demux->mdatleft = 0;\r
+ /* need to resume atom parsing so we do not miss any other pieces */\r
+ demux->state = PIFFDEMUX_STATE_INITIAL;\r
+ demux->neededbytes = 16;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (demux->todrop) {\r
+ GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);\r
+ gst_adapter_flush (demux->adapter, demux->todrop);\r
+ demux->neededbytes -= demux->todrop;\r
+ demux->offset += demux->todrop;\r
+ }\r
+\r
+ if ( !stream->sent_nsevent) {\r
+ //TODO: better to parse sink event function and send that new_segment\r
+#if 1\r
+ demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,\r
+ demux->stream->start_ts, -1, demux->stream->start_ts);\r
+#else\r
+ demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,\r
+ 0, gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale), 0);\r
+#endif\r
+\r
+ GST_INFO_OBJECT (demux, "New segment event : start = %"GST_TIME_FORMAT", stop = %" GST_TIME_FORMAT,\r
+ GST_TIME_ARGS (demux->stream->start_ts), GST_TIME_ARGS(gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale)));\r
+\r
+ if (!gst_pad_push_event (demux->srcpad, demux->pending_newsegment)) {\r
+ GST_ERROR_OBJECT (demux, "failding to send new segment...");\r
+ goto newsegment_error;\r
+ }\r
+ stream->sent_nsevent = TRUE;\r
+ }\r
+\r
+ /* Put data in a buffer, set timestamps, caps, ... */\r
+ outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);\r
+\r
+ GST_DEBUG_OBJECT (demux, "Taken %d size buffer from adapter...", outbuf ? GST_BUFFER_SIZE (outbuf) : 0);\r
+ GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (stream->fourcc));\r
+\r
+ g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);\r
+\r
+ sample = &stream->samples[stream->sample_index];\r
+\r
+ GST_DEBUG_OBJECT (demux, "start_ts = %"GST_TIME_FORMAT" ts : %"GST_TIME_FORMAT" ts = %llu, pts_offset = %u, scale = %d\n",\r
+ GST_TIME_ARGS(stream->start_ts),GST_TIME_ARGS(sample->timestamp), sample->timestamp,\r
+ sample->pts_offset,stream->timescale);\r
+\r
+ position = PIFFSAMPLE_DTS (stream, sample);\r
+ timestamp = PIFFSAMPLE_PTS (stream, sample) + stream->start_ts; // Adding to avoid resetting of timestamp\r
+ duration = PIFFSAMPLE_DUR_DTS (stream, sample, position);\r
+ keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);\r
+\r
+ ret = gst_piffdemux_decorate_and_push_buffer (demux, stream, outbuf,\r
+ timestamp, duration, keyframe, position, demux->offset);\r
+\r
+ stream->sample_index++;\r
+\r
+ /* update current offset and figure out size of next buffer */\r
+ GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",\r
+ demux->offset, demux->neededbytes);\r
+ demux->offset += demux->neededbytes;\r
+ GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,\r
+ demux->offset);\r
+\r
+ if ((demux->neededbytes = next_entry_size (demux)) == -1) {\r
+ GST_DEBUG_OBJECT (demux, "finished parsing mdat, need to search next moof atom");\r
+ demux->neededbytes = 16;\r
+ demux->state = PIFFDEMUX_STATE_INITIAL;\r
+ GST_DEBUG ("\n\n Storing %s last_ts %"GST_TIME_FORMAT"\n\n", stream->subtype == FOURCC_vide ? "video" : "audio", GST_TIME_ARGS(timestamp));\r
+ stream->start_ts = timestamp + duration;\r
+ //goto eos;\r
+ }\r
+ break;\r
+ }\r
+ default:\r
+ goto invalid_state;\r
+ }\r
+ }\r
+\r
+ /* when buffering movie data, at least show user something is happening */\r
+ if (ret == GST_FLOW_OK && demux->state == PIFFDEMUX_STATE_BUFFER_MDAT &&\r
+ gst_adapter_available (demux->adapter) <= demux->neededbytes) {\r
+ gst_piffdemux_post_progress (demux, gst_adapter_available (demux->adapter),\r
+ demux->neededbytes);\r
+ }\r
+done:\r
+ gst_object_unref (demux);\r
+\r
+ return ret;\r
+\r
+ /* ERRORS */\r
+unknown_stream:\r
+ {\r
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));\r
+ ret = GST_FLOW_ERROR;\r
+ goto done;\r
+ }\r
+eos:\r
+ {\r
+ GST_DEBUG_OBJECT (demux, "no next entry, EOS");\r
+ ret = GST_FLOW_UNEXPECTED;\r
+ goto done;\r
+ }\r
+invalid_state:\r
+ {\r
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,\r
+ (NULL), ("piffdemuxer invalid state %d", demux->state));\r
+ ret = GST_FLOW_ERROR;\r
+ goto done;\r
+ }\r
+newsegment_error:\r
+ {\r
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,\r
+ (NULL), ("could not send newsegment event"));\r
+ ret = GST_FLOW_ERROR;\r
+ goto done;\r
+ }\r
+}\r
+\r
+static gboolean\r
+piffdemux_parse_container (GstPiffDemux * piffdemux, GNode * node, const guint8 * buf,\r
+ const guint8 * end)\r
+{\r
+ while (G_UNLIKELY (buf < end)) {\r
+ GNode *child;\r
+ guint32 len;\r
+\r
+ if (G_UNLIKELY (buf + 4 > end)) {\r
+ GST_LOG_OBJECT (piffdemux, "buffer overrun");\r
+ break;\r
+ }\r
+ len = PIFF_UINT32 (buf);\r
+ if (G_UNLIKELY (len == 0)) {\r
+ GST_LOG_OBJECT (piffdemux, "empty container");\r
+ break;\r
+ }\r
+ if (G_UNLIKELY (len < 8)) {\r
+ GST_WARNING_OBJECT (piffdemux, "length too short (%d < 8)", len);\r
+ break;\r
+ }\r
+ if (G_UNLIKELY (len > (end - buf))) {\r
+ GST_WARNING_OBJECT (piffdemux, "length too long (%d > %d)", len,\r
+ (gint) (end - buf));\r
+ break;\r
+ }\r
+\r
+ child = g_node_new ((guint8 *) buf);\r
+ g_node_append (node, child);\r
+ GST_LOG_OBJECT (piffdemux, "adding new node of len %d", len);\r
+ piffdemux_parse_node (piffdemux, child, buf, len);\r
+\r
+ buf += len;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+\r
+static gboolean\r
+piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer,\r
+ guint length)\r
+{\r
+ guint32 fourcc = 0;\r
+ guint32 node_length = 0;\r
+ const PiffNodeType *type;\r
+ const guint8 *end;\r
+\r
+ GST_LOG_OBJECT (piffdemux, "piffdemux_parse buffer %p length %u", buffer, length);\r
+\r
+ if (G_UNLIKELY (length < 8))\r
+ goto not_enough_data;\r
+\r
+ node_length = PIFF_UINT32 (buffer);\r
+ fourcc = PIFF_FOURCC (buffer + 4);\r
+\r
+ /* ignore empty nodes */\r
+ if (G_UNLIKELY (fourcc == 0 || node_length == 8))\r
+ return TRUE;\r
+\r
+ type = piffdemux_type_get (fourcc);\r
+\r
+ end = buffer + length;\r
+\r
+ GST_LOG_OBJECT (piffdemux,\r
+ "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",\r
+ GST_FOURCC_ARGS (fourcc), node_length, type->name);\r
+\r
+ if (node_length > length)\r
+ goto broken_atom_size;\r
+\r
+ if (type->flags & PIFF_FLAG_CONTAINER) {\r
+ piffdemux_parse_container (piffdemux, node, buffer + 8, end);\r
+ }\r
+ GST_LOG_OBJECT (piffdemux, "parsed '%" GST_FOURCC_FORMAT "'",\r
+ GST_FOURCC_ARGS (fourcc));\r
+ return TRUE;\r
+\r
+/* ERRORS */\r
+not_enough_data:\r
+ {\r
+\r
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
+ ("This file is corrupt and cannot be played."),\r
+ ("Not enough data for an atom header, got only %u bytes", length));\r
+\r
+ return FALSE;\r
+ }\r
+broken_atom_size:\r
+ {\r
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,\r
+ ("This file is corrupt and cannot be played."),\r
+ ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "\r
+ "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,\r
+ length));\r
+\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+\r
+static GNode *\r
+piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)\r
+{\r
+ GNode *child;\r
+ guint8 *buffer;\r
+ guint32 child_fourcc;\r
+\r
+ for (child = g_node_first_child (node); child;\r
+ child = g_node_next_sibling (child)) {\r
+ buffer = (guint8 *) child->data;\r
+\r
+ child_fourcc = PIFF_FOURCC (buffer + 4);\r
+\r
+ if (G_UNLIKELY (child_fourcc == fourcc)) {\r
+ return child;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+static GNode *\r
+piffdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,\r
+ GstByteReader * parser)\r
+{\r
+ GNode *child;\r
+ guint8 *buffer;\r
+ guint32 child_fourcc, child_len;\r
+\r
+ for (child = g_node_first_child (node); child;\r
+ child = g_node_next_sibling (child)) {\r
+ buffer = (guint8 *) child->data;\r
+\r
+ child_len = PIFF_UINT32 (buffer);\r
+ child_fourcc = PIFF_FOURCC (buffer + 4);\r
+\r
+ if (G_UNLIKELY (child_fourcc == fourcc)) {\r
+ if (G_UNLIKELY (child_len < (4 + 4)))\r
+ return NULL;\r
+ /* FIXME: must verify if atom length < parent atom length */\r
+ gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));\r
+ return child;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+static GNode *\r
+piffdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,\r
+ GstByteReader * parser)\r
+{\r
+ GNode *child;\r
+ guint8 *buffer;\r
+ guint32 child_fourcc, child_len;\r
+\r
+ for (child = g_node_next_sibling (node); child;\r
+ child = g_node_next_sibling (child)) {\r
+ buffer = (guint8 *) child->data;\r
+\r
+ child_fourcc = PIFF_FOURCC (buffer + 4);\r
+\r
+ if (child_fourcc == fourcc) {\r
+ if (parser) {\r
+ child_len = PIFF_UINT32 (buffer);\r
+ if (G_UNLIKELY (child_len < (4 + 4)))\r
+ return NULL;\r
+ /* FIXME: must verify if atom length < parent atom length */\r
+ gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));\r
+ }\r
+ return child;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+static GNode *\r
+piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)\r
+{\r
+ return piffdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);\r
+}\r
+\r
+#define _codec(name) \\r
+ do { \\r
+ if (codec_name) { \\r
+ *codec_name = g_strdup (name); \\r
+ } \\r
+ } while (0)\r
+\r
+void\r
+gst_piffdemux_set_video_params (GstPiffDemux * piffdemux, guint fourcc,\r
+ guint width, guint height,\r
+ guint fps_n, guint fps_d, unsigned char *codec_data, unsigned int codec_data_len)\r
+{\r
+ GstCaps *caps = NULL;\r
+ GstBuffer *dci = NULL;\r
+\r
+ if (codec_data && codec_data_len) {\r
+ dci = gst_buffer_new_and_alloc (codec_data_len);\r
+ if (!dci) {\r
+ GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");\r
+ } else {\r
+ memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);\r
+ }\r
+ }\r
+\r
+ switch (fourcc) {\r
+\r
+ case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):\r
+ caps = gst_caps_new_simple ("video/x-h264",\r
+ "width", G_TYPE_INT, width,\r
+ "height", G_TYPE_INT, height,\r
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,\r
+ "stream-format", G_TYPE_STRING, "avc",\r
+ "alignment", G_TYPE_STRING, "au",\r
+ "codec_data", GST_TYPE_BUFFER, dci,\r
+ NULL);\r
+ break;\r
+\r
+ case FOURCC_ovc1:\r
+ caps = gst_caps_new_simple ("video/x-wmv",\r
+ "width", G_TYPE_INT, width,\r
+ "height", G_TYPE_INT, height,\r
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,\r
+ "wmvversion", G_TYPE_INT, 3,\r
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),\r
+ "codec_data", GST_TYPE_BUFFER, dci,\r
+ NULL);\r
+ break;\r
+\r
+ default: {\r
+ char *s;\r
+ s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,\r
+ GST_FOURCC_ARGS (fourcc));\r
+ caps = gst_caps_new_simple (s,\r
+ "width", G_TYPE_INT, width,\r
+ "height", G_TYPE_INT, height,\r
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,\r
+ "codec_data", GST_TYPE_BUFFER, dci,\r
+ NULL);\r
+ break;\r
+ }\r
+ }\r
+\r
+ piffdemux->stream->caps = caps;\r
+ GST_INFO_OBJECT (piffdemux, "prepared video caps : %s", gst_caps_to_string(caps));\r
+}\r
+\r
+void\r
+gst_piffdemux_set_audio_params (GstPiffDemux * piffdemux, guint fourcc,\r
+ guint sampling_rate, guint bps, guint channels, unsigned char *codec_data, unsigned int codec_data_len)\r
+{\r
+ GstCaps *caps = NULL;\r
+ GstBuffer *dci = NULL;\r
+\r
+ if (codec_data && codec_data_len) {\r
+ dci = gst_buffer_new_and_alloc (codec_data_len);\r
+ if (!dci) {\r
+ GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");\r
+ } else {\r
+ memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);\r
+ }\r
+ }\r
+\r
+ switch (fourcc) {\r
+\r
+ case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):\r
+ caps = gst_caps_new_simple ("audio/mpeg",\r
+ "mpegversion", G_TYPE_INT, 4,\r
+ "framed", G_TYPE_BOOLEAN, TRUE,\r
+ "stream-format", G_TYPE_STRING, "raw",\r
+ "rate", G_TYPE_INT, (int) sampling_rate,\r
+ "channels", G_TYPE_INT, channels,\r
+ NULL);\r
+ break;\r
+\r
+ case FOURCC_owma:\r
+ caps = gst_caps_new_simple ("audio/x-wma",\r
+ "rate", G_TYPE_INT, (int) sampling_rate,\r
+ "channels", G_TYPE_INT, channels,\r
+ NULL);\r
+ break;\r
+\r
+ default: {\r
+ char *s;\r
+ s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,\r
+ GST_FOURCC_ARGS (fourcc));\r
+ caps = gst_caps_new_simple (s,\r
+ "rate", G_TYPE_INT, (int) sampling_rate,\r
+ "channels", G_TYPE_INT, channels,\r
+ NULL);\r
+ break;\r
+ }\r
+ }\r
+\r
+ piffdemux->stream->caps = caps;\r
+ GST_INFO_OBJECT (piffdemux, "prepared audio caps : %s", gst_caps_to_string(caps));\r
+\r
+}\r
+\r
+#define g_marshal_value_peek_object(v) g_value_get_object (v)\r
+\r
+void\r
+__gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,\r
+ GValue *return_value G_GNUC_UNUSED,\r
+ guint n_param_values,\r
+ const GValue *param_values,\r
+ gpointer invocation_hint G_GNUC_UNUSED,\r
+ gpointer marshal_data)\r
+{\r
+ typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1,\r
+ gpointer arg_1,\r
+ gpointer data2);\r
+ register GMarshalFunc_BOOLEAN__OBJECT callback;\r
+ register GCClosure *cc = (GCClosure*) closure;\r
+ register gpointer data1, data2;\r
+ gboolean v_return;\r
+\r
+ g_return_if_fail (return_value != NULL);\r
+ g_return_if_fail (n_param_values == 2);\r
+\r
+ if (G_CCLOSURE_SWAP_DATA (closure))\r
+ {\r
+ data1 = closure->data;\r
+ data2 = g_value_peek_pointer (param_values + 0);\r
+ }\r
+ else\r
+ {\r
+ data1 = g_value_peek_pointer (param_values + 0);\r
+ data2 = closure->data;\r
+ }\r
+ callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);\r
+\r
+ v_return = callback (data1,\r
+ g_marshal_value_peek_object (param_values + 1),\r
+ data2);\r
+\r
+ g_value_set_boolean (return_value, v_return);\r
+}\r
+\r
+#define PIFFDEMUX_SPSPPS_LENGTH_SIZE 2\r
+\r
+static gboolean\r
+ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size)\r
+{\r
+ unsigned short unit_size = 0;\r
+ unsigned int total_size = 0;\r
+ unsigned char unit_nb = 0;\r
+ unsigned char sps_done = 0;\r
+ const unsigned char *extradata = NULL;\r
+ unsigned int h264_nal_length_size = 0;\r
+ unsigned char *out = NULL;\r
+ //g_print ("\n\nConvertH264_MetaDCI_to_3GPPDCI Entering.............\n");\r
+\r
+ /* nothing to filter */\r
+ if ((dci_meta_buf == NULL) || (dci_meta_size < 6))\r
+ {\r
+ GST_ERROR ("Insufficient codec data...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ /* Removing unnecessary info in meta data */\r
+ extradata = (unsigned char *)dci_meta_buf + 4;\r
+\r
+ /* retrieve Length of Length*/\r
+ h264_nal_length_size = (*extradata++ & 0x03) + 1;\r
+\r
+ GST_LOG ("Length Of Length is %d\n", h264_nal_length_size);\r
+ if (h264_nal_length_size == 3)\r
+ {\r
+ GST_ERROR ("LengthOfLength is WRONG...\n");\r
+ return FALSE;\r
+ }\r
+\r
+ /* retrieve sps and pps unit(s) */\r
+ unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */\r
+ GST_LOG ("No. of SPS units = %u\n", unit_nb);\r
+\r
+ if (!unit_nb)\r
+ {\r
+ GST_ERROR ("SPS is not present....\n");\r
+ return FALSE;\r
+ }\r
+\r
+ while (unit_nb--)\r
+ {\r
+ /* get SPS/PPS data Length*/\r
+ unit_size = PIFFDEMUX_RB16(extradata);\r
+\r
+ GST_LOG ("SPS size = %d", unit_size);\r
+\r
+ /* Extra 4 bytes for adding size of the packet */\r
+ total_size += unit_size + h264_nal_length_size;\r
+\r
+ /* Check if SPS/PPS Data Length crossed buffer Length */\r
+ if ((extradata + 2 + unit_size) > (dci_meta_buf + dci_meta_size))\r
+ {\r
+ GST_ERROR ("SPS Length is wrong in DCI...\n");\r
+ return FALSE;\r
+ }\r
+ out = realloc(out, total_size);\r
+ if (!out)\r
+ {\r
+ GST_ERROR ("realloc FAILED...\n");\r
+ return FALSE;\r
+ }\r
+ /* Copy length of SPS header */\r
+ // tmp = (unsigned int *)(out + total_size - unit_size - h264_nal_length_size);\r
+ // *tmp = unit_size; \r
+ (out + total_size - unit_size - h264_nal_length_size)[0] = 0;\r
+ (out + total_size - unit_size - h264_nal_length_size)[1] = 0;\r
+ (out + total_size - unit_size - h264_nal_length_size)[2] = 0;\r
+ (out + total_size - unit_size - h264_nal_length_size)[3] = (unsigned char)unit_size;\r
+\r
+ // memcpy(out + total_size - unit_size - h264_nal_length_size, &unit_size, h264_nal_length_size);\r
+ //g_print ("out[0] = %02x, out[1] = %02x, out[2] = %02x = out[3] = %02x\n",\r
+ // out[total_size - unit_size - h264_nal_length_size], out[total_size - unit_size - h264_nal_length_size+1],\r
+ // out[total_size - unit_size - h264_nal_length_size + 2], out[total_size - unit_size - h264_nal_length_size + 3]);\r
+\r
+ /* Copy SPS/PPS Length and data */\r
+ memcpy(out + total_size - unit_size, extradata + PIFFDEMUX_SPSPPS_LENGTH_SIZE, unit_size);\r
+\r
+ extradata += (PIFFDEMUX_SPSPPS_LENGTH_SIZE + unit_size);\r
+\r
+ if (!unit_nb && !sps_done++)\r
+ {\r
+ /* Completed reading SPS data, now read PPS data */\r
+ unit_nb = *extradata++; /* number of pps unit(s) */\r
+ GST_DEBUG ("No. of PPS units = %d\n", unit_nb);\r
+ }\r
+ }\r
+\r
+ *dci_3gpp_buf = malloc (total_size);\r
+ if (NULL == *dci_3gpp_buf)\r
+ {\r
+ GST_ERROR ("Memory Allocation FAILED...\n");\r
+ free (out);\r
+ return FALSE;\r
+ }\r
+\r
+ memcpy(*dci_3gpp_buf, out, total_size);\r
+ *dci_3gpp_size = total_size;\r
+\r
+ GST_DEBUG ("SPS_PPS size = %d\n", total_size);\r
+\r
+ if (out)\r
+ {\r
+ free(out);\r
+ }\r
+ return TRUE;\r
+ }\r
+\r
--- /dev/null
+/* GStreamer\r
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Library General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Library General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Library General Public\r
+ * License along with this library; if not, write to the\r
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
+ * Boston, MA 02111-1307, USA.\r
+ */\r
+\r
+\r
+#ifndef __GST_PIFFDEMUX_H__\r
+#define __GST_PIFFDEMUX_H__\r
+\r
+#include <gst/gst.h>\r
+#include <gst/base/gstadapter.h>\r
+#include "piffcommon.h"\r
+\r
+G_BEGIN_DECLS\r
+\r
+GST_DEBUG_CATEGORY_EXTERN (piffdemux_debug);\r
+#define GST_CAT_DEFAULT piffdemux_debug\r
+\r
+#define GST_TYPE_PIFFDEMUX \\r
+ (gst_piffdemux_get_type())\r
+#define GST_PIFFDEMUX(obj) \\r
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIFFDEMUX,GstPiffDemux))\r
+#define GST_PIFFDEMUX_CLASS(klass) \\r
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIFFDEMUX,GstPiffDemuxClass))\r
+#define GST_IS_PIFFDEMUX(obj) \\r
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIFFDEMUX))\r
+#define GST_IS_PIFFDEMUX_CLASS(klass) \\r
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIFFDEMUX))\r
+\r
+#define GST_PIFFDEMUX_CAST(obj) ((GstPiffDemux *)(obj))\r
+\r
+/* piffdemux produces these for atoms it cannot parse */\r
+#define GST_PIFF_DEMUX_PRIVATE_TAG "private-piff-tag"\r
+#define GST_PIFF_DEMUX_CLASSIFICATION_TAG "classification"\r
+\r
+#define GST_PIFFDEMUX_MAX_STREAMS 8\r
+\r
+typedef struct _GstPiffDemux GstPiffDemux;\r
+typedef struct _GstPiffDemuxClass GstPiffDemuxClass;\r
+typedef struct _PiffDemuxStream PiffDemuxStream;\r
+\r
+struct _GstPiffDemux {\r
+ GstElement element;\r
+\r
+ /* pads */\r
+ GstPad *sinkpad;\r
+ GstPad *srcpad;\r
+\r
+ PiffDemuxStream *stream;\r
+\r
+ guint32 timescale;\r
+ gint64 duration;\r
+\r
+ gboolean fragmented;\r
+\r
+ guint64 moof_offset;\r
+\r
+ gint state;\r
+\r
+ gboolean posted_redirect;\r
+\r
+ /* push based variables */\r
+ guint neededbytes;\r
+ guint todrop;\r
+ GstAdapter *adapter;\r
+ GstBuffer *mdatbuffer;\r
+ guint64 mdatleft;\r
+\r
+ /* offset of the media data (i.e.: Size of header) */\r
+ guint64 offset;\r
+ /* offset of the mdat atom */\r
+ guint64 mdatoffset;\r
+ guint64 first_mdat;\r
+\r
+ GstTagList *tag_list;\r
+\r
+ /* configured playback region */\r
+ GstSegment segment;\r
+ gboolean segment_running;\r
+ GstEvent *pending_newsegment;\r
+\r
+ /* gst index support */\r
+ GstIndex *element_index;\r
+ gint index_id;\r
+\r
+ gint64 requested_seek_time;\r
+ guint64 seek_offset;\r
+ gboolean moof_rcvd;\r
+\r
+ /* live specific params */\r
+ piff_live_param_t *param;\r
+ guint lookahead_cnt;\r
+ gboolean is_live;\r
+};\r
+\r
+struct _GstPiffDemuxClass {\r
+ GstElementClass parent_class;\r
+ void (*live_param) (GstPiffDemux *piff, const piff_live_param_t *param);\r
+};\r
+\r
+GType gst_piffdemux_get_type (void);\r
+\r
+/* prepares video caps based on input params */\r
+void gst_piffdemux_set_video_params (GstPiffDemux * piffdemux, guint fourcc, guint width, guint height, guint fps_n, guint fps_d, unsigned char *codec_data, unsigned int codec_data_len);\r
+/* prepares audio caps based on input params */\r
+void gst_piffdemux_set_audio_params (GstPiffDemux * piffdemux, guint fourcc,\r
+ guint sampling_rate, guint bps, guint channels, unsigned char *codec_data, unsigned int codec_data_len);\r
+G_END_DECLS\r
+\r
+#endif /* __GST_PIFFDEMUX_H__ */\r
+\r
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.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.
+ */
+
+#include "piffdemux_types.h"
+#include "piffdemux_dump.h"
+
+#include "piffatomparser.h"
+
+#include <string.h>
+
+#define GET_UINT8(data) gst_byte_reader_get_uint8_unchecked(data)
+#define GET_UINT16(data) gst_byte_reader_get_uint16_be_unchecked(data)
+#define GET_UINT32(data) gst_byte_reader_get_uint32_be_unchecked(data)
+#define GET_UINT64(data) gst_byte_reader_get_uint64_be_unchecked(data)
+#define GET_FP32(data) (gst_byte_reader_get_uint32_be_unchecked(data)/65536.0)
+#define GET_FP16(data) (gst_byte_reader_get_uint16_be_unchecked(data)/256.0)
+#define GET_FOURCC(data) piff_atom_parser_get_fourcc_unchecked(data)
+
+gboolean
+piffdemux_dump_vmhd (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ if (!piff_atom_parser_has_remaining (data, 4 + 4))
+ return FALSE;
+
+ GST_LOG ("%*s version/flags: %08x", depth, "", GET_UINT32 (data));
+ GST_LOG ("%*s mode/color: %08x", depth, "", GET_UINT32 (data));
+ return TRUE;
+}
+
+
+gboolean
+piffdemux_dump_mfro (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ if (!piff_atom_parser_has_remaining (data, 4))
+ return FALSE;
+
+ GST_LOG ("%*s size: %d", depth, "", GET_UINT32 (data));
+ return TRUE;
+}
+
+gboolean
+piffdemux_dump_tfra (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ guint64 time = 0, moof_offset = 0;
+ guint32 len = 0, num_entries = 0, ver_flags = 0, track_id = 0, i;
+ guint value_size, traf_size, trun_size, sample_size;
+
+ if (!gst_byte_reader_get_uint32_be (data, &ver_flags))
+ return FALSE;
+
+ GST_LOG ("%*s version/flags: %08x", depth, "", ver_flags);
+
+ if (!gst_byte_reader_get_uint32_be (data, &track_id) ||
+ gst_byte_reader_get_uint32_be (data, &len) ||
+ gst_byte_reader_get_uint32_be (data, &num_entries))
+ return FALSE;
+
+ GST_LOG ("%*s track ID: %u", depth, "", track_id);
+ GST_LOG ("%*s length: 0x%x", depth, "", len);
+ GST_LOG ("%*s n entries: %u", depth, "", num_entries);
+
+ value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
+ sample_size = (len & 3) + 1;
+ trun_size = ((len & 12) >> 2) + 1;
+ traf_size = ((len & 48) >> 4) + 1;
+
+ if (!piff_atom_parser_has_chunks (data, num_entries,
+ value_size + value_size + traf_size + trun_size + sample_size))
+ return FALSE;
+
+ for (i = 0; i < num_entries; i++) {
+ piff_atom_parser_get_offset (data, value_size, &time);
+ piff_atom_parser_get_offset (data, value_size, &moof_offset);
+ GST_LOG ("%*s time: %" G_GUINT64_FORMAT, depth, "", time);
+ GST_LOG ("%*s moof_offset: %" G_GUINT64_FORMAT,
+ depth, "", moof_offset);
+ GST_LOG ("%*s traf_number: %u", depth, "",
+ piff_atom_parser_get_uint_with_size_unchecked (data, traf_size));
+ GST_LOG ("%*s trun_number: %u", depth, "",
+ piff_atom_parser_get_uint_with_size_unchecked (data, trun_size));
+ GST_LOG ("%*s sample_number: %u", depth, "",
+ piff_atom_parser_get_uint_with_size_unchecked (data, sample_size));
+ }
+
+ return TRUE;
+}
+
+gboolean
+piffdemux_dump_tfhd (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ guint32 flags = 0, n = 0, track_id = 0;
+ guint64 base_data_offset = 0;
+
+ if (!gst_byte_reader_skip (data, 1) ||
+ !gst_byte_reader_get_uint24_be (data, &flags))
+ return FALSE;
+ GST_LOG ("%*s flags: %08x", depth, "", flags);
+
+ if (!gst_byte_reader_get_uint32_be (data, &track_id))
+ return FALSE;
+ GST_LOG ("%*s track_id: %u", depth, "", track_id);
+
+ if (flags & TF_BASE_DATA_OFFSET) {
+ if (!gst_byte_reader_get_uint64_be (data, &base_data_offset))
+ return FALSE;
+ GST_LOG ("%*s base-data-offset: %" G_GUINT64_FORMAT,
+ depth, "", base_data_offset);
+ }
+
+ if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
+ if (!gst_byte_reader_get_uint32_be (data, &n))
+ return FALSE;
+ GST_LOG ("%*s sample-description-index: %u", depth, "", n);
+ }
+
+ if (flags & TF_DEFAULT_SAMPLE_DURATION) {
+ if (!gst_byte_reader_get_uint32_be (data, &n))
+ return FALSE;
+ GST_LOG ("%*s default-sample-duration: %u", depth, "", n);
+ }
+
+ if (flags & TF_DEFAULT_SAMPLE_SIZE) {
+ if (!gst_byte_reader_get_uint32_be (data, &n))
+ return FALSE;
+ GST_LOG ("%*s default-sample-size: %u", depth, "", n);
+ }
+
+ if (flags & TF_DEFAULT_SAMPLE_FLAGS) {
+ if (!gst_byte_reader_get_uint32_be (data, &n))
+ return FALSE;
+ GST_LOG ("%*s default-sample-flags: %u", depth, "", n);
+ }
+
+ GST_LOG ("%*s duration-is-empty: %s", depth, "",
+ flags & TF_DURATION_IS_EMPTY ? "yes" : "no");
+
+ return TRUE;
+}
+
+gboolean
+piffdemux_dump_trun (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ guint32 flags = 0, samples_count = 0, data_offset = 0, first_sample_flags = 0;
+ guint32 sample_duration = 0, sample_size = 0, sample_flags =
+ 0, composition_time_offsets = 0;
+ int i = 0;
+
+ if (!gst_byte_reader_skip (data, 1) ||
+ !gst_byte_reader_get_uint24_be (data, &flags))
+ return FALSE;
+
+ GST_LOG ("%*s flags: %08x", depth, "", flags);
+
+ if (!gst_byte_reader_get_uint32_be (data, &samples_count))
+ return FALSE;
+ GST_LOG ("%*s samples_count: %u", depth, "", samples_count);
+
+ if (flags & TR_DATA_OFFSET) {
+ if (!gst_byte_reader_get_uint32_be (data, &data_offset))
+ return FALSE;
+ GST_LOG ("%*s data-offset: %u", depth, "", data_offset);
+ }
+
+ if (flags & TR_FIRST_SAMPLE_FLAGS) {
+ if (!gst_byte_reader_get_uint32_be (data, &first_sample_flags))
+ return FALSE;
+ GST_LOG ("%*s first-sample-flags: %u", depth, "", first_sample_flags);
+ }
+
+ for (i = 0; i < samples_count; i++) {
+ if (flags & TR_SAMPLE_DURATION) {
+ if (!gst_byte_reader_get_uint32_be (data, &sample_duration))
+ return FALSE;
+ GST_LOG ("%*s sample-duration: %u", depth, "", sample_duration);
+ }
+
+ if (flags & TR_SAMPLE_SIZE) {
+ if (!gst_byte_reader_get_uint32_be (data, &sample_size))
+ return FALSE;
+ GST_LOG ("%*s sample-size: %u", depth, "", sample_size);
+ }
+
+ if (flags & TR_SAMPLE_FLAGS) {
+ if (!gst_byte_reader_get_uint32_be (data, &sample_flags))
+ return FALSE;
+ GST_LOG ("%*s sample-flags: %u", depth, "", sample_flags);
+ }
+
+ if (flags & TR_COMPOSITION_TIME_OFFSETS) {
+ if (!gst_byte_reader_get_uint32_be (data, &composition_time_offsets))
+ return FALSE;
+ GST_LOG ("%*s composition_time_offsets: %u", depth, "",
+ composition_time_offsets);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+piffdemux_dump_trex (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ if (!piff_atom_parser_has_remaining (data, 4 + 4 + 4 + 4 + 4 + 4))
+ return FALSE;
+
+ GST_LOG ("%*s version/flags: %08x", depth, "", GET_UINT32 (data));
+ GST_LOG ("%*s track ID: %08x", depth, "", GET_UINT32 (data));
+ GST_LOG ("%*s default sample desc. index: %08x", depth, "",
+ GET_UINT32 (data));
+ GST_LOG ("%*s default sample duration: %08x", depth, "",
+ GET_UINT32 (data));
+ GST_LOG ("%*s default sample size: %08x", depth, "",
+ GET_UINT32 (data));
+ GST_LOG ("%*s default sample flags: %08x", depth, "",
+ GET_UINT32 (data));
+
+ return TRUE;
+}
+
+
+gboolean
+piffdemux_dump_sdtp (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ guint32 version;
+ guint8 val;
+ guint i = 1;
+
+ version = GET_UINT32 (data);
+ GST_LOG ("%*s version/flags: %08x", depth, "", version);
+
+ /* the sample_count is specified in the stsz or stz2 box.
+ * the information for a sample is stored in a single byte,
+ * so we read until there are no remaining bytes */
+ while (piff_atom_parser_has_remaining (data, 1)) {
+ val = GET_UINT8 (data);
+ GST_LOG ("%*s sample number: %d", depth, "", i);
+ GST_LOG ("%*s sample_depends_on: %d", depth, "",
+ ((guint16) (val)) & 0x3);
+ GST_LOG ("%*s sample_is_depended_on: %d", depth, "",
+ ((guint16) (val >> 2)) & 0x3);
+ GST_LOG ("%*s sample_has_redundancy: %d", depth, "",
+ ((guint16) (val >> 4)) & 0x3);
+ ++i;
+ }
+ return TRUE;
+}
+
+gboolean
+piffdemux_dump_unknown (GstPiffDemux * piffdemux, GstByteReader * data, int depth)
+{
+ int len;
+
+ len = gst_byte_reader_get_remaining (data);
+ GST_LOG ("%*s length: %d", depth, "", len);
+
+ GST_MEMDUMP_OBJECT (piffdemux, "unknown atom data",
+ gst_byte_reader_peek_data_unchecked (data), len);
+ return TRUE;
+}
+
+static gboolean
+piffdemux_node_dump_foreach (GNode * node, gpointer piffdemux)
+{
+ GstByteReader parser;
+ guint8 *buffer = (guint8 *) node->data; /* FIXME: move to byte reader */
+ guint32 node_length;
+ guint32 fourcc;
+ const PiffNodeType *type;
+ int depth;
+
+ node_length = GST_READ_UINT32_BE (buffer);
+ fourcc = GST_READ_UINT32_LE (buffer + 4);
+
+ g_warn_if_fail (node_length >= 8);
+
+ gst_byte_reader_init (&parser, buffer + 8, node_length - 8);
+
+ type = piffdemux_type_get (fourcc);
+
+ depth = (g_node_depth (node) - 1) * 2;
+ GST_LOG ("%*s'%" GST_FOURCC_FORMAT "', [%d], %s",
+ depth, "", GST_FOURCC_ARGS (fourcc), node_length, type->name);
+
+ if (type->dump) {
+ gboolean ret;
+
+ ret = type->dump (GST_PIFFDEMUX_CAST (piffdemux), &parser, depth);
+
+ if (!ret) {
+ GST_WARNING ("%*s not enough data parsing atom %" GST_FOURCC_FORMAT,
+ depth, "", GST_FOURCC_ARGS (fourcc));
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+piffdemux_node_dump (GstPiffDemux * piffdemux, GNode * node)
+{
+ if (__gst_debug_min < GST_LEVEL_LOG)
+ return TRUE;
+
+ g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ piffdemux_node_dump_foreach, piffdemux);
+ return TRUE;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.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_PIFFDEMUX_DUMP_H__
+#define __GST_PIFFDEMUX_DUMP_H__
+
+#include <gst/gst.h>
+#include <piffdemux.h>
+
+G_BEGIN_DECLS
+
+gboolean piffdemux_dump_vmhd (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_mfro (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_tfra (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_tfhd (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_trun (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_trex (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_sdtp (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+gboolean piffdemux_dump_unknown (GstPiffDemux * piffdemux, GstByteReader * data,
+ int depth);
+
+gboolean piffdemux_node_dump (GstPiffDemux * piffdemux, GNode * node);
+
+G_END_DECLS
+#endif /* __GST_PIFFDEMUX_DUMP_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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_QTDEMUX_FOURCC_H__
+#define __GST_QTDEMUX_FOURCC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+
+#define FOURCC_vide GST_MAKE_FOURCC('v','i','d','e')
+#define FOURCC_soun GST_MAKE_FOURCC('s','o','u','n')
+#define FOURCC_subp GST_MAKE_FOURCC('s','u','b','p')
+#define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t')
+#define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a')
+#define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v')
+#define FOURCC_MP4V GST_MAKE_FOURCC('M','P','4','V')
+#define FOURCC_fmp4 GST_MAKE_FOURCC('f','m','p','4')
+#define FOURCC_FMP4 GST_MAKE_FOURCC('F','M','P','4')
+
+#define FOURCC_meta GST_MAKE_FOURCC('m','e','t','a')
+
+#define FOURCC_____ GST_MAKE_FOURCC('-','-','-','-')
+
+#define FOURCC_free GST_MAKE_FOURCC('f','r','e','e')
+
+#define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s')
+#define FOURCC_drmi GST_MAKE_FOURCC('d','r','m','i')
+#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
+#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C')
+
+#define FOURCC_ulaw GST_MAKE_FOURCC('u','l','a','w')
+#define FOURCC_alaw GST_MAKE_FOURCC('a','l','a','w')
+
+#define FOURCC_raw_ GST_MAKE_FOURCC('r','a','w',' ')
+
+#define FOURCC_alac GST_MAKE_FOURCC('a','l','a','c')
+#define FOURCC_samr GST_MAKE_FOURCC('s','a','m','r')
+#define FOURCC_sawb GST_MAKE_FOURCC('s','a','w','b')
+#define FOURCC_mdat GST_MAKE_FOURCC('m','d','a','t')
+#define FOURCC_in24 GST_MAKE_FOURCC('i','n','2','4')
+
+#define FOURCC_text GST_MAKE_FOURCC('t','e','x','t')
+#define FOURCC_tx3g GST_MAKE_FOURCC('t','x','3','g')
+#define FOURCC_mp4s GST_MAKE_FOURCC('m','p','4','s')
+#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d')
+
+/* Fragmented MP4 */
+
+#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d')
+#define FOURCC_mfra GST_MAKE_FOURCC('m','f','r','a')
+#define FOURCC_mfro GST_MAKE_FOURCC('m','f','r','o')
+#define FOURCC_moof GST_MAKE_FOURCC('m','o','o','f')
+#define FOURCC_mvex GST_MAKE_FOURCC('m','v','e','x')
+#define FOURCC_sdtp GST_MAKE_FOURCC('s','d','t','p')
+#define FOURCC_tfhd GST_MAKE_FOURCC('t','f','h','d')
+#define FOURCC_tfxd GST_MAKE_FOURCC('t','f','x','d')
+#define FOURCC_tfra GST_MAKE_FOURCC('t','f','r','a')
+#define FOURCC_traf GST_MAKE_FOURCC('t','r','a','f')
+#define FOURCC_trex GST_MAKE_FOURCC('t','r','e','x')
+#define FOURCC_trun GST_MAKE_FOURCC('t','r','u','n')
+#define FOURCC_ovc1 GST_MAKE_FOURCC('o','v','c','1')
+#define FOURCC_owma GST_MAKE_FOURCC('o','w','m','a')
+#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d')
+#define FOURCC_tfrf GST_MAKE_FOURCC('t','f','r','f')
+
+G_END_DECLS
+
+#endif /* __GST_QTDEMUX_FOURCC_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 "piffdemux_types.h"
+#include "piffdemux_dump.h"
+#include "piffdemux_fourcc.h"
+
+static const PiffNodeType piff_node_types[] = {
+ {FOURCC_vide, "video media", 0},
+ {FOURCC_hint, "hint", 0,},
+ {FOURCC_mp4a, "mp4a", 0,},
+ {FOURCC_mp4v, "mp4v", 0,},
+ {FOURCC_alac, "alac", 0,},
+ {FOURCC_meta, "meta", 0, piffdemux_dump_unknown},
+ {FOURCC_____, "----", PIFF_FLAG_CONTAINER,},
+ {FOURCC_free, "free", 0,},
+ {FOURCC_mfra, "movie fragment random access",
+ PIFF_FLAG_CONTAINER,},
+ {FOURCC_tfra, "track fragment random access", 0,
+ piffdemux_dump_tfra},
+ {FOURCC_mfro, "movie fragment random access offset", 0,
+ piffdemux_dump_mfro},
+ {FOURCC_moof, "movie fragment", PIFF_FLAG_CONTAINER,},
+ {FOURCC_mfhd, "movie fragment header", 0,},
+ {FOURCC_traf, "track fragment", PIFF_FLAG_CONTAINER,},
+ {FOURCC_tfhd, "track fragment header", 0,
+ piffdemux_dump_tfhd},
+ {FOURCC_sdtp, "independent and disposable samples", 0,
+ piffdemux_dump_sdtp},
+ {FOURCC_trun, "track fragment run", 0, piffdemux_dump_trun},
+ {FOURCC_mdat, "moovie data", 0, piffdemux_dump_unknown},
+ {FOURCC_trex, "moovie data", 0, piffdemux_dump_trex},
+ {FOURCC_mvex, "mvex", PIFF_FLAG_CONTAINER,},
+ {FOURCC_ovc1, "ovc1", 0},
+ {FOURCC_owma, "owma", 0},
+ {FOURCC_tfxd, "tfxd", 0},
+ {FOURCC_tfrf, "tfrf", 0},
+ {FOURCC_uuid, "uuid", 0},
+
+ {0, "unknown", 0,},
+};
+
+static const int n_piff_node_types =
+ sizeof (piff_node_types) / sizeof (piff_node_types[0]);
+
+const PiffNodeType *
+piffdemux_type_get (guint32 fourcc)
+{
+ int i;
+
+ for (i = 0; i < n_piff_node_types; i++) {
+ if (G_UNLIKELY (piff_node_types[i].fourcc == fourcc))
+ return piff_node_types + i;
+ }
+
+ GST_WARNING ("unknown QuickTime node type %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+
+ return piff_node_types + n_piff_node_types - 1;
+}
--- /dev/null
+
+#ifndef __GST_PIFFDEMUX_TYPES_H__
+#define __GST_PIFFDEMUX_TYPES_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbytereader.h>
+
+#include "piffdemux.h"
+
+G_BEGIN_DECLS
+
+typedef gboolean (*PiffDumpFunc) (GstPiffDemux * piffdemux, GstByteReader * data, int depth);
+
+typedef struct _PiffNodeType PiffNodeType;
+
+#define PIFF_UINT32(a) (GST_READ_UINT32_BE(a))
+#define PIFF_UINT24(a) (GST_READ_UINT32_BE(a) >> 8)
+#define PIFF_UINT16(a) (GST_READ_UINT16_BE(a))
+#define PIFF_UINT8(a) (GST_READ_UINT8(a))
+#define PIFF_FP32(a) ((GST_READ_UINT32_BE(a))/65536.0)
+#define PIFF_SFP32(a) (((gint)(GST_READ_UINT32_BE(a)))/65536.0)
+#define PIFF_FP16(a) ((GST_READ_UINT16_BE(a))/256.0)
+#define PIFF_FOURCC(a) (GST_READ_UINT32_LE(a))
+#define PIFF_UINT64(a) ((((guint64)PIFF_UINT32(a))<<32)|PIFF_UINT32(((guint8 *)a)+4))
+
+typedef enum {
+ PIFF_FLAG_NONE = (0),
+ PIFF_FLAG_CONTAINER = (1 << 0)
+} PiffFlags;
+
+struct _PiffNodeType {
+ guint32 fourcc;
+ const gchar *name;
+ PiffFlags flags;
+ PiffDumpFunc dump;
+};
+
+enum TfFlags
+{
+ TF_BASE_DATA_OFFSET = 0x000001, /* base-data-offset-present */
+ TF_SAMPLE_DESCRIPTION_INDEX = 0x000002, /* sample-description-index-present */
+ TF_DEFAULT_SAMPLE_DURATION = 0x000008, /* default-sample-duration-present */
+ TF_DEFAULT_SAMPLE_SIZE = 0x000010, /* default-sample-size-present */
+ TF_DEFAULT_SAMPLE_FLAGS = 0x000020, /* default-sample-flags-present */
+ TF_DURATION_IS_EMPTY = 0x100000 /* duration-is-empty */
+};
+
+enum TrFlags
+{
+ TR_DATA_OFFSET = 0x000001, /* data-offset-present */
+ TR_FIRST_SAMPLE_FLAGS = 0x000004, /* first-sample-flags-present */
+ TR_SAMPLE_DURATION = 0x000100, /* sample-duration-present */
+ TR_SAMPLE_SIZE = 0x000200, /* sample-size-present */
+ TR_SAMPLE_FLAGS = 0x000400, /* sample-flags-present */
+ TR_COMPOSITION_TIME_OFFSETS = 0x000800 /* sample-composition-time-offsets-presents */
+};
+
+enum SEFlags
+{
+ SE_OVERRIDE_TE_FLAGS = 0x000001, /* override existing track encryption parameters */
+ SE_USE_SUBSAMPLE_ENCRYPTION = 0x000002, /* Use SubSample Encryption */
+};
+const PiffNodeType *piffdemux_type_get (guint32 fourcc);
+
+G_END_DECLS
+
+#endif /* __GST_PIFFDEMUX_TYPES_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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_PIFFPALLETE_H__
+#define __GST_PIFFPALLETE_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+static const guint32 ff_piff_default_palette_2[2] = {
+ 0xffffff, 0x000000
+};
+
+static const guint32 ff_piff_default_palette_4[4] = {
+ 0x93655e, 0xffffff, 0xdfd0ab, 0x000000
+};
+
+static const guint32 ff_piff_default_palette_16[16] = {
+ 0xfffbff, 0xefd9bb, 0xe8c9b1, 0x93655e,
+ 0xfcdee8, 0x9d8891, 0xffffff, 0xffffff,
+ 0xffffff, 0x474837, 0x7a5e55, 0xdfd0ab,
+ 0xfffbf9, 0xe8cac5, 0x8a7c77, 0x000000
+};
+static const guint32 ff_piff_default_palette_256[256] = {
+ 0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00,
+ 0xFFCCFF, 0xFFCCCC, 0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00,
+ 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966, 0xFF9933, 0xFF9900,
+ 0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600,
+ 0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300,
+ 0xFF00FF, 0xFF00CC, 0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000,
+ 0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66, 0xCCFF33, 0xCCFF00,
+ 0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00,
+ 0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900,
+ 0xCC66FF, 0xCC66CC, 0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600,
+ 0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366, 0xCC3333, 0xCC3300,
+ 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
+ 0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00,
+ 0x99CCFF, 0x99CCCC, 0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00,
+ 0x9999FF, 0x9999CC, 0x999999, 0x999966, 0x999933, 0x999900,
+ 0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600,
+ 0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300,
+ 0x9900FF, 0x9900CC, 0x990099, 0x990066, 0x990033, 0x990000,
+ 0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66, 0x66FF33, 0x66FF00,
+ 0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00,
+ 0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900,
+ 0x6666FF, 0x6666CC, 0x666699, 0x666666, 0x666633, 0x666600,
+ 0x6633FF, 0x6633CC, 0x663399, 0x663366, 0x663333, 0x663300,
+ 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
+ 0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00,
+ 0x33CCFF, 0x33CCCC, 0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00,
+ 0x3399FF, 0x3399CC, 0x339999, 0x339966, 0x339933, 0x339900,
+ 0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600,
+ 0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300,
+ 0x3300FF, 0x3300CC, 0x330099, 0x330066, 0x330033, 0x330000,
+ 0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66, 0x00FF33, 0x00FF00,
+ 0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00,
+ 0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900,
+ 0x0066FF, 0x0066CC, 0x006699, 0x006666, 0x006633, 0x006600,
+ 0x0033FF, 0x0033CC, 0x003399, 0x003366, 0x003333, 0x003300,
+ 0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000,
+ 0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000,
+ 0x440000, 0x220000, 0x110000, 0x00EE00, 0x00DD00, 0x00BB00,
+ 0x00AA00, 0x008800, 0x007700, 0x005500, 0x004400, 0x002200,
+ 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088,
+ 0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE,
+ 0xDDDDDD, 0xBBBBBB, 0xAAAAAA, 0x888888, 0x777777, 0x555555,
+ 0x444444, 0x222222, 0x111111, 0x000000
+};
+
+static const guint32 ff_piff_grayscale_palette_16[16] = {
+ 0xffffff, 0xeeeeee, 0xdddddd, 0xcccccc,
+ 0xbbbbbb, 0xaaaaaa, 0x999999, 0x888888,
+ 0x777777, 0x666666, 0x555555, 0x444444,
+ 0x333333, 0x222222, 0x111111, 0x000000
+};
+
+static const guint32 ff_piff_grayscale_palette_256[256] = {
+ 0xffffff, 0xfefefe, 0xfdfdfd, 0xfcfcfc, 0xfbfbfb, 0xfafafa, 0xf9f9f9,
+ 0xf8f8f8, 0xf7f7f7, 0xf6f6f6, 0xf5f5f5, 0xf4f4f4, 0xf3f3f3, 0xf2f2f2,
+ 0xf1f1f1, 0xf0f0f0, 0xefefef, 0xeeeeee, 0xededed, 0xececec, 0xebebeb,
+ 0xeaeaea, 0xe9e9e9, 0xe8e8e8, 0xe7e7e7, 0xe6e6e6, 0xe5e5e5, 0xe4e4e4,
+ 0xe3e3e3, 0xe2e2e2, 0xe1e1e1, 0xe0e0e0, 0xdfdfdf, 0xdedede, 0xdddddd,
+ 0xdcdcdc, 0xdbdbdb, 0xdadada, 0xd9d9d9, 0xd8d8d8, 0xd7d7d7, 0xd6d6d6,
+ 0xd5d5d5, 0xd4d4d4, 0xd3d3d3, 0xd2d2d2, 0xd1d1d1, 0xd0d0d0, 0xcfcfcf,
+ 0xcecece, 0xcdcdcd, 0xcccccc, 0xcbcbcb, 0xcacaca, 0xc9c9c9, 0xc8c8c8,
+ 0xc7c7c7, 0xc6c6c6, 0xc5c5c5, 0xc4c4c4, 0xc3c3c3, 0xc2c2c2, 0xc1c1c1,
+ 0xc0c0c0, 0xbfbfbf, 0xbebebe, 0xbdbdbd, 0xbcbcbc, 0xbbbbbb, 0xbababa,
+ 0xb9b9b9, 0xb8b8b8, 0xb7b7b7, 0xb6b6b6, 0xb5b5b5, 0xb4b4b4, 0xb3b3b3,
+ 0xb2b2b2, 0xb1b1b1, 0xb0b0b0, 0xafafaf, 0xaeaeae, 0xadadad, 0xacacac,
+ 0xababab, 0xaaaaaa, 0xa9a9a9, 0xa8a8a8, 0xa7a7a7, 0xa6a6a6, 0xa5a5a5,
+ 0xa4a4a4, 0xa3a3a3, 0xa2a2a2, 0xa1a1a1, 0xa0a0a0, 0x9f9f9f, 0x9e9e9e,
+ 0x9d9d9d, 0x9c9c9c, 0x9b9b9b, 0x9a9a9a, 0x999999, 0x989898, 0x979797,
+ 0x969696, 0x959595, 0x949494, 0x939393, 0x929292, 0x919191, 0x909090,
+ 0x8f8f8f, 0x8e8e8e, 0x8d8d8d, 0x8c8c8c, 0x8b8b8b, 0x8a8a8a, 0x898989,
+ 0x888888, 0x878787, 0x868686, 0x858585, 0x848484, 0x838383, 0x828282,
+ 0x818181, 0x808080, 0x7f7f7f, 0x7e7e7e, 0x7d7d7d, 0x7c7c7c, 0x7b7b7b,
+ 0x7a7a7a, 0x797979, 0x787878, 0x777777, 0x767676, 0x757575, 0x747474,
+ 0x737373, 0x727272, 0x717171, 0x707070, 0x6f6f6f, 0x6e6e6e, 0x6d6d6d,
+ 0x6c6c6c, 0x6b6b6b, 0x6a6a6a, 0x696969, 0x686868, 0x676767, 0x666666,
+ 0x656565, 0x646464, 0x636363, 0x626262, 0x616161, 0x606060, 0x5f5f5f,
+ 0x5e5e5e, 0x5d5d5d, 0x5c5c5c, 0x5b5b5b, 0x5a5a5a, 0x595959, 0x585858,
+ 0x575757, 0x565656, 0x555555, 0x545454, 0x535353, 0x525252, 0x515151,
+ 0x505050, 0x4f4f4f, 0x4e4e4e, 0x4d4d4d, 0x4c4c4c, 0x4b4b4b, 0x4a4a4a,
+ 0x494949, 0x484848, 0x474747, 0x464646, 0x454545, 0x444444, 0x434343,
+ 0x424242, 0x414141, 0x404040, 0x3f3f3f, 0x3e3e3e, 0x3d3d3d, 0x3c3c3c,
+ 0x3b3b3b, 0x3a3a3a, 0x393939, 0x383838, 0x373737, 0x363636, 0x353535,
+ 0x343434, 0x333333, 0x323232, 0x313131, 0x303030, 0x2f2f2f, 0x2e2e2e,
+ 0x2d2d2d, 0x2c2c2c, 0x2b2b2b, 0x2a2a2a, 0x292929, 0x282828, 0x272727,
+ 0x262626, 0x252525, 0x242424, 0x232323, 0x222222, 0x212121, 0x202020,
+ 0x1f1f1f, 0x1e1e1e, 0x1d1d1d, 0x1c1c1c, 0x1b1b1b, 0x1a1a1a, 0x191919,
+ 0x181818, 0x171717, 0x161616, 0x151515, 0x141414, 0x131313, 0x121212,
+ 0x111111, 0x101010, 0x0f0f0f, 0x0e0e0e, 0x0d0d0d, 0x0c0c0c, 0x0b0b0b,
+ 0x0a0a0a, 0x090909, 0x080808, 0x070707, 0x060606, 0x050505, 0x040404,
+ 0x030303, 0x020202, 0x010101, 0x000000
+};
+
+G_END_DECLS
+
+#endif /* __GST_PIFFPALETTE_H__ */
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+# plugindir is set in configure
+
+##############################################################################
+# change libgstplugin.la to something more suitable, e.g. libmysomething.la #
+##############################################################################
+plugin_LTLIBRARIES = libgstssdemux.la
+
+##############################################################################
+# for the next set of variables, rename the prefix if you renamed the .la, #
+# e.g. libgstplugin_la_SOURCES => libmysomething_la_SOURCES #
+# libgstplugin_la_CFLAGS => libmysomething_la_CFLAGS #
+# libgstplugin_la_LIBADD => libmysomething_la_LIBADD #
+# libgstplugin_la_LDFLAGS => libmysomething_la_LDFLAGS #
+##############################################################################
+
+# sources used to compile this plug-in
+libgstssdemux_la_SOURCES = gstssdemux.c ssmanifestparse.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstssdemux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(SOUP_CFLAGS) $(XML2_CFLAGS) $(GST_APP_CFLAGS) -I$(srcdir)/../../piffdemux/src
+libgstssdemux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(SOUP_LIBS) $(XML2_LIBS) $(GST_APP_LIBS) -lgstapp-0.10
+libgstssdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gstssdemux.h ssmanifestparse.h
--- /dev/null
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <string.h>
+//#include <gst/glib-compat-private.h>
+#include "gstssdemux.h"
+
+enum
+{
+ PROP_0,
+ PROP_COOKIES,
+ PROP_ALLOW_AUDIO_ONLY,
+ PROP_FRAGMENTS_CACHE,
+ PROP_BITRATE_SWITCH_TOLERANCE,
+ PROP_LAST
+};
+
+static GstStaticPadTemplate ssdemux_videosrc_template =
+GST_STATIC_PAD_TEMPLATE ("video",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate ssdemux_audiosrc_template =
+GST_STATIC_PAD_TEMPLATE ("audio",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate ssdemux_subsrc_template =
+GST_STATIC_PAD_TEMPLATE ("subtitle",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate ssdemux_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-ss")); // Need to decide source mimetype
+
+GST_DEBUG_CATEGORY_STATIC (gst_ss_demux_debug);
+#define GST_CAT_DEFAULT gst_ss_demux_debug
+
+#undef SIMULATE_AUDIO_ONLY /* enable to simulate audio only case forcibly */
+
+static void
+_do_init (GType type)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_ss_demux_debug, "ssdemux", 0,
+ "ssdemux element");
+}
+
+GST_BOILERPLATE_FULL (GstSSDemux, gst_ss_demux, GstElement,
+ GST_TYPE_ELEMENT, _do_init);
+
+#define DEFAULT_FRAGMENTS_CACHE 0
+#define DEFAULT_BITRATE_SWITCH_TOLERANCE 0.4
+
+struct _GstSSDemuxStream
+{
+ /* Streaming task */
+ void *parent;
+ GstPad *pad;
+ gchar *name;
+ SS_STREAM_TYPE type;
+ GstTask *stream_task;
+ GStaticRecMutex stream_lock;
+ GstElement *pipe;
+ GstElement *urisrc;
+ GstElement *parser;
+ GstElement *sink;
+ GstBus *bus;
+ GMutex *lock;
+ GCond *cond;
+ guint frag_cnt;
+ GQueue *queue;
+ gchar *uri;
+ guint64 start_ts;
+ gboolean sent_ns;
+ GstCaps *caps;
+ guint64 switch_ts;
+ guint64 avg_dur;
+};
+
+static void gst_ss_demux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_ss_demux_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static gboolean gst_ss_demux_sink_event (GstPad * pad, GstEvent * event);
+static GstStateChangeReturn gst_ss_demux_change_state (GstElement * element, GstStateChange transition);
+static void gst_ss_demux_dispose (GObject * obj);
+static GstFlowReturn gst_ss_demux_chain (GstPad * pad, GstBuffer * buf);
+static void gst_ss_demux_stream_loop (GstSSDemux * demux);
+static gboolean gst_ss_demux_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data);
+static void gst_ss_demux_stream_init (GstSSDemux *demux, GstSSDemuxStream *stream, SS_STREAM_TYPE stream_type);
+static void gst_ss_demux_stream_free (GstSSDemux * demux, GstSSDemuxStream * stream);
+static void gst_ssm_demux_on_new_buffer (GstElement * appsink, void* data);
+static gboolean gst_ss_demux_download_fragment (GstSSDemux *demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts);
+static gboolean gst_ss_demux_create_download_pipe (GstSSDemux * demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts);
+static void gst_ss_demux_stop (GstSSDemux * demux, GstSSDemuxStream *stream);
+static gboolean gst_ss_demux_create_dummy_pipe (GstSSDemux * demux, GstSSDemuxStream *stream);
+static gboolean gst_ss_demux_create_dummy_sender(GstSSDemux *demux, GstSSDemuxStream *stream);
+
+static void
+gst_ss_demux_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 (&ssdemux_videosrc_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&ssdemux_audiosrc_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&ssdemux_subsrc_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&ssdemux_sink_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "SS Demuxer",
+ "Demuxer/URIList",
+ "Smooth Streaming demuxer",
+ "Naveen Cherukuri<naveen.ch@samsung.com>");
+}
+
+static void
+gst_ss_demux_class_init (GstSSDemuxClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_ss_demux_set_property;
+ gobject_class->get_property = gst_ss_demux_get_property;
+ gobject_class->dispose = gst_ss_demux_dispose;
+
+ /* to share cookies with other sessions */
+ g_object_class_install_property (gobject_class, PROP_COOKIES,
+ g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies",
+ G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /* will be considered only in LIVE case */
+ g_object_class_install_property (gobject_class, PROP_ALLOW_AUDIO_ONLY,
+ g_param_spec_boolean ("allow-audio-only", "Allow audio only when downloadrate is less in live case",
+ "Allow audio only stream download in live case when download rate is less",
+ TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /* no.of fragments to cache */
+ g_object_class_install_property (gobject_class, PROP_FRAGMENTS_CACHE,
+ g_param_spec_uint ("fragments-cache", "Fragments cache",
+ "Number of fragments needed to be cached to start playing",
+ 0, G_MAXUINT, DEFAULT_FRAGMENTS_CACHE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BITRATE_SWITCH_TOLERANCE,
+ g_param_spec_float ("bitrate-switch-tolerance",
+ "Bitrate switch tolerance",
+ "Tolerance with respect of the fragment duration to switch to "
+ "a different bitrate if the client is too slow/fast.",
+ 0, 1, DEFAULT_BITRATE_SWITCH_TOLERANCE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_ss_demux_change_state);
+}
+
+static void
+gst_ss_demux_init (GstSSDemux * demux, GstSSDemuxClass * klass)
+{
+ /* sink pad */
+ demux->sinkpad = gst_pad_new_from_static_template (&ssdemux_sink_template, "sink");
+ gst_pad_set_chain_function (demux->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_ss_demux_chain));
+ gst_pad_set_event_function (demux->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_ss_demux_sink_event));
+ gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
+
+ demux->need_cache = TRUE;
+ demux->fragments_cache = DEFAULT_FRAGMENTS_CACHE;
+ demux->cancelled = FALSE;
+ demux->cookies = NULL;
+ demux->ss_mode = SS_MODE_NO_SWITCH;
+ demux->switch_eos = FALSE;
+}
+
+static void
+gst_ss_demux_dispose (GObject * obj)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (obj);
+ int n =0;
+
+ for (n = 0; n < SS_STREAM_NUM; n++) {
+ if (demux->streams[n]) {
+ gst_ss_demux_stream_free (demux, demux->streams[n]);
+ demux->streams[n] = NULL;
+ }
+ }
+
+ if (demux->parser) {
+ gst_ssm_parse_free (demux->parser);
+ demux->parser = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+gst_ss_demux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (object);
+
+ switch (prop_id) {
+ case PROP_COOKIES:
+ g_strfreev (demux->cookies);
+ demux->cookies = g_strdupv (g_value_get_boxed (value));
+ break;
+ case PROP_ALLOW_AUDIO_ONLY:
+ demux->allow_audio_only = g_value_get_boolean (value);
+ break;
+ case PROP_FRAGMENTS_CACHE:
+ demux->fragments_cache = g_value_get_uint (value);
+ break;
+ case PROP_BITRATE_SWITCH_TOLERANCE:
+ demux->bitrate_switch_tol = g_value_get_float (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_ss_demux_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (object);
+
+ switch (prop_id) {
+ case PROP_COOKIES:
+ g_value_set_boxed (value, g_strdupv (demux->cookies));
+ break;
+ case PROP_ALLOW_AUDIO_ONLY:
+ g_value_set_boolean (value, demux->allow_audio_only);
+ break;
+ case PROP_FRAGMENTS_CACHE:
+ g_value_set_uint (value, demux->fragments_cache);
+ break;
+ case PROP_BITRATE_SWITCH_TOLERANCE:
+ g_value_set_float (value, demux->bitrate_switch_tol);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_ss_demux_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (gst_pad_get_parent (pad));
+ GstQuery *query;
+ gboolean ret;
+ gchar *uri;
+
+ switch (event->type) {
+ case GST_EVENT_EOS: {
+ int i = 0;
+ if (demux->manifest == NULL) {
+ GST_WARNING_OBJECT (demux, "Received EOS without a manifest.");
+ break;
+ }
+
+ GST_DEBUG_OBJECT (demux, "Got EOS on the sink pad: mainifest file fetched");
+
+ query = gst_query_new_uri ();
+ ret = gst_pad_peer_query (demux->sinkpad, query);
+ if (ret) {
+ gst_query_parse_uri (query, &uri);
+ demux->parser = gst_ssm_parse_new (uri);
+ g_free (uri);
+ } else {
+ GST_ERROR_OBJECT (demux, "failed to query URI from upstream");
+ return FALSE;
+ }
+ gst_query_unref (query);
+
+ GST_LOG_OBJECT (demux, "data = %p & size = %d", GST_BUFFER_DATA(demux->manifest), GST_BUFFER_SIZE(demux->manifest));
+ if (!gst_ssm_parse_manifest (demux->parser, (char *)GST_BUFFER_DATA(demux->manifest), GST_BUFFER_SIZE(demux->manifest))) {
+ /* In most cases, this will happen if we set a wrong url in the
+ * source element and we have received the 404 HTML response instead of
+ * the playlist */
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."),
+ (NULL));
+ return FALSE;
+ }
+
+ for( i = 0; i < SS_STREAM_NUM; i++) {
+ if (gst_ssm_parse_check_stream (demux->parser, i)) {
+ GstSSDemuxStream *stream = g_new0 (GstSSDemuxStream, 1);
+
+ // Add pad emission of the stream
+ gst_ss_demux_stream_init (demux, stream, i);
+ g_static_rec_mutex_init (&stream->stream_lock);
+ stream->stream_task = gst_task_create ((GstTaskFunction) gst_ss_demux_stream_loop, demux);
+ gst_task_set_lock (stream->stream_task, &stream->stream_lock);
+ demux->streams[i] = stream;
+ g_print ("Starting stream - %d task loop...\n", i);
+ gst_task_start (stream->stream_task);
+ }
+ }
+
+ gst_event_unref (event);
+ return TRUE;
+ }
+ case GST_EVENT_NEWSEGMENT:
+ /* Swallow newsegments, we'll push our own */
+ gst_event_unref (event);
+ return TRUE;
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+}
+
+static gboolean
+gst_ss_demux_handle_src_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = FALSE;
+ GstSSDemux *ssdemux = GST_SS_DEMUX (gst_pad_get_parent (pad));
+
+ GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
+
+ // TODO: need to add other query types as well
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_DURATION:{
+ GstFormat fmt;
+
+ gst_query_parse_duration (query, &fmt, NULL);
+ if (fmt == GST_FORMAT_TIME) {
+ gint64 duration = -1;
+
+ duration = gst_util_uint64_scale (GST_SSM_PARSE_GET_DURATION(ssdemux->parser), GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(ssdemux->parser));
+ if (duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+
+ gst_object_unref (ssdemux);
+
+ return res;
+}
+
+
+static gboolean
+gst_ss_demux_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (gst_pad_get_parent (pad));
+
+ switch (event->type) {
+ case GST_EVENT_SEEK:
+ {
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ gint i = 0;
+ GstSSDemuxStream *stream = NULL;
+
+ GST_INFO_OBJECT (demux, "Received GST_EVENT_SEEK");
+
+ // TODO: should be able to seek in DVR window
+ if (GST_SSM_PARSE_IS_LIVE_PRESENTATION (demux->parser)) {
+ GST_WARNING_OBJECT (demux, "Received seek event for live stream");
+ return FALSE;
+ }
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+ &stop_type, &stop);
+
+ if (format != GST_FORMAT_TIME) {
+ GST_WARNING_OBJECT (demux, "Only time format is supported in seek");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT
+ " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
+ GST_TIME_ARGS (stop));
+
+
+ for( i = 0; i < SS_STREAM_NUM; i++) {
+ if (stream = demux->streams[i]) {
+ g_cond_signal (stream->cond);
+ gst_task_stop (stream->stream_task);
+ }
+ }
+
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ GST_INFO_OBJECT (demux, "sending flush start");
+
+ for( i = 0; i < SS_STREAM_NUM; i++) {
+ if (stream = demux->streams[i]) {
+ gst_pad_push_event (stream->pad, gst_event_new_flush_start ());
+ }
+ }
+ }
+
+ gst_ssm_parse_seek_manifest (demux->parser, start);
+
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ GST_INFO_OBJECT (demux, "sending flush stop");
+ for( i = 0; i < SS_STREAM_NUM; i++) {
+ if (stream = demux->streams[i]) {
+ gst_pad_push_event (stream->pad, gst_event_new_flush_stop ());
+ GST_LOG_OBJECT (stream->pad, "Starting pad TASK again...\n");
+ stream->sent_ns = FALSE;
+ stream->frag_cnt = 0; /*resetting to start buffering on SEEK */
+ gst_task_start (stream->stream_task);
+ }
+ }
+ }
+
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+}
+
+static GstStateChangeReturn
+gst_ss_demux_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+static GstFlowReturn
+gst_ss_demux_chain (GstPad * pad, GstBuffer * buf)
+{
+ GstSSDemux *demux = GST_SS_DEMUX (gst_pad_get_parent (pad));
+
+ if (demux->manifest == NULL)
+ demux->manifest = buf;
+ else
+ demux->manifest = gst_buffer_join (demux->manifest, buf);
+ gst_object_unref (demux);
+
+ return GST_FLOW_OK;
+}
+
+
+static gboolean
+gst_ss_demux_get_next_fragment (GstSSDemux * demux, SS_STREAM_TYPE stream_type)
+{
+ GstSSDemuxStream *stream = demux->streams[stream_type];
+ gchar *next_fragment_uri = NULL;
+ guint64 start_ts = 0;
+
+ if (!gst_ssm_parse_get_next_fragment_url (demux->parser, stream_type, &next_fragment_uri, &start_ts )) {
+ GST_INFO_OBJECT (demux, "This Manifest does not contain more fragments");
+ goto end_of_list;
+ }
+
+ GST_ERROR_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri);
+
+ stream->uri = g_strdup(next_fragment_uri);
+ stream->start_ts = start_ts;
+
+ if (!gst_ss_demux_download_fragment (demux, stream, next_fragment_uri, start_ts)) {
+ GST_ERROR_OBJECT (demux, "failed to download fragment...");
+ goto error;
+ }
+
+ return TRUE;
+
+error:
+ {
+ gst_ss_demux_stop (demux, stream);
+ return FALSE;
+ }
+end_of_list:
+ {
+ GST_INFO_OBJECT (demux, "Reached end of playlist, sending EOS");
+ gst_pad_push_event (stream->pad, gst_event_new_eos ());
+ gst_ss_demux_stop (demux, stream);
+ return TRUE;
+ }
+}
+
+static void
+gst_ss_demux_stream_loop (GstSSDemux * demux)
+{
+ GThread *self = NULL;
+ int stream_type = 0;
+ GstSSDemuxStream *stream = NULL;
+
+ self = g_thread_self ();
+
+ for (stream_type = 0; stream_type < SS_STREAM_NUM; stream_type++) {
+ if (demux->streams[stream_type]->stream_task->abidata.ABI.thread == self) {
+ stream = demux->streams[stream_type];
+ break;
+ }
+ }
+
+ /* download next fragment of stream_type */
+ if (!gst_ss_demux_get_next_fragment (demux, stream_type)) {
+ GST_ERROR_OBJECT (demux, "failed to get next fragment...");
+ goto error;
+ }
+
+ return;
+
+error:
+ {
+ gst_task_pause (stream->stream_task);
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND,
+ ("could not download fragments"), (NULL));
+ gst_ss_demux_stop (demux, stream);
+ return;
+ }
+}
+
+static gboolean
+gst_ss_demux_download_fragment (GstSSDemux *demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts)
+{
+ GstStateChangeReturn ret;
+
+ g_print ("Going to download fragment : %s\n", uri);
+ if (!gst_ss_demux_create_download_pipe (demux, stream, uri, start_ts)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ return FALSE;
+ }
+
+ ret = gst_element_set_state (stream->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (demux, "set_state failed...");
+ return FALSE;
+ }
+
+ if (stream->pipe && demux->ss_mode == SS_MODE_AONLY &&
+ stream->type == SS_STREAM_VIDEO) {
+
+ GST_DEBUG_OBJECT (demux, "Waiting to fetch the URI");
+ g_mutex_lock (stream->lock);
+ g_cond_wait (stream->cond, stream->lock);
+ GST_INFO_OBJECT (stream->pad, "Recived signal to shutdown...");
+ g_mutex_unlock (stream->lock);
+
+ /* put live pipeline to PAUSED state to unlink urisrc & piffdemux */
+ gst_element_set_state (stream->pipe, GST_STATE_NULL);
+ gst_element_get_state (stream->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+
+ stream->pipe = NULL;
+
+ stream->switch_ts = stream->start_ts;
+
+ /* create dummy frame sender */
+ if (!gst_ss_demux_create_dummy_sender (demux, stream)) {
+ GST_ERROR_OBJECT (demux, "failed to create dummy sender pipeline...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("Unable to create dummy pipe."), (NULL));
+ return FALSE;
+ }
+ }
+
+ /* wait until:
+ * - the download succeed (EOS)
+ * - the download failed (Error message on the fetcher bus)
+ * - the download was canceled
+ */
+ GST_DEBUG_OBJECT (demux, "Waiting to fetch the URI");
+ g_mutex_lock (stream->lock);
+ g_cond_wait (stream->cond, stream->lock);
+ GST_INFO_OBJECT (stream->pad, "Recived signal to shutdown...");
+ g_mutex_unlock (stream->lock);
+
+ gst_element_set_state (stream->pipe, GST_STATE_NULL);
+ gst_element_get_state (stream->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ stream->pipe = NULL;
+
+ return TRUE;
+}
+
+static gboolean
+gst_ss_demux_create_dummy_sender(GstSSDemux *demux, GstSSDemuxStream *stream)
+{
+ GstStateChangeReturn ret;
+
+ if (!gst_ss_demux_create_dummy_pipe (demux, stream)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ return FALSE;
+ }
+
+ ret = gst_element_set_state (stream->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (demux, "set_state failed...");
+ return FALSE;
+ }
+
+#if 0
+ GST_DEBUG_OBJECT (demux, "Waiting to download next video URI");
+ g_mutex_lock (stream->lock);
+ g_cond_wait (stream->cond, stream->lock);
+ if (stream->pipe) {
+ gst_element_set_state (stream->pipe, GST_STATE_NULL);
+ gst_element_get_state (stream->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ stream->pipe = NULL;
+ }
+ g_mutex_unlock (stream->lock);
+#endif
+
+ return TRUE;
+}
+
+static void
+gst_ss_demux_append_live_params(GstElement *piffparser, piff_live_param_t *param, gpointer data)
+{
+ GstSSDemuxStream *stream = (GstSSDemuxStream *)data;
+ GstSSDemux *demux = stream->parent;
+ int i =0;
+ guint64 timestamp = 0;
+ guint64 duration = 0;
+
+ GST_LOG_OBJECT (demux, "received signal structs count = %d\n", param->count);
+
+ for (i = 0 ; i< param->count; i++) {
+ if (param->long_info) {
+ piff_fragment_longtime_info *info = &(param->long_info[i]);
+ timestamp = info->ts;
+ duration = info->duration;
+ } else if (param->info) {
+ piff_fragment_time_info *info = &(param->info[i]);
+ timestamp = info->ts;
+ duration = info->duration;
+ }
+
+ GST_LOG_OBJECT (demux, "Received ts = %llu and dur = %llu\n", timestamp, duration);
+
+ if (!gst_ssm_parse_append_next_fragment (demux->parser, stream->type, timestamp, duration)) {
+ GST_ERROR_OBJECT (demux, "failed to append new fragment");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("fragment allocation failed..."), (NULL));
+ return;
+ }
+ }
+
+ if (param->long_info) {
+ free (param->long_info);
+ param->long_info = NULL;
+ }
+
+ if (param->info) {
+ free (param->info);
+ param->info = NULL;
+ }
+
+ free (param);
+
+ if ((stream->type == SS_STREAM_VIDEO) && (demux->ss_mode == SS_MODE_AONLY)) {
+ g_print ("\n\n\t\tSignalling download pipe shutdonw....\n\n");
+
+ g_object_get (stream->parser, "frame-dur", &stream->avg_dur, NULL);
+ g_print ("frame duration = %"GST_TIME_FORMAT"\n\n\n", GST_TIME_ARGS(stream->avg_dur));
+ g_cond_signal (stream->cond);
+ }
+
+}
+
+static gboolean
+gst_ss_demux_create_download_pipe (GstSSDemux * demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts)
+{
+ gchar *name = NULL;
+ GstCaps *caps = NULL;
+
+ if (!gst_uri_is_valid (uri))
+ return FALSE;
+
+ name = g_strdup_printf("%s-%s", stream->name, "downloader");
+
+ stream->pipe = gst_pipeline_new (name);
+ if (!stream->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+ g_free(name);
+
+ name = g_strdup_printf("%s-%s", stream->name, "httpsrc");
+ GST_DEBUG ("Creating source element for the URI:%s", uri);
+ stream->urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, name);
+ if (!stream->urisrc) {
+ GST_ERROR_OBJECT (demux, "failed to create urisrc");
+ return FALSE;
+ }
+ g_free(name);
+
+ if (GST_SSM_PARSE_IS_LIVE_PRESENTATION(demux->parser))
+ g_object_set (G_OBJECT (stream->urisrc), "is-live", TRUE, NULL);
+ else
+ g_object_set (G_OBJECT (stream->urisrc), "is-live", FALSE, NULL);
+
+ name = g_strdup_printf("%s-%s", stream->name, "parser");
+ stream->parser = gst_element_factory_make ("piffdemux", name);
+ if (!stream->parser) {
+ GST_ERROR_OBJECT (demux, "failed to create piffdemux element");
+ return FALSE;
+ }
+
+ caps = ssm_parse_get_stream_caps (demux->parser, stream->type);
+ GST_INFO_OBJECT (stream->pad, "prepare caps = %s", gst_caps_to_string(caps));
+
+ g_object_set (G_OBJECT (stream->parser), "caps", caps, NULL);
+ g_object_set (G_OBJECT (stream->parser), "start-ts", start_ts, NULL);
+ g_object_set (G_OBJECT (stream->parser), "duration", GST_SSM_PARSE_GET_DURATION(demux->parser), NULL);
+ g_object_set (G_OBJECT (stream->parser), "is-live", GST_SSM_PARSE_IS_LIVE_PRESENTATION(demux->parser), NULL);
+ g_object_set (G_OBJECT (stream->parser), "lookahead-count", GST_SSM_PARSE_LOOKAHEAD_COUNT(demux->parser), NULL);
+ g_signal_connect (stream->parser, "live-param", G_CALLBACK (gst_ss_demux_append_live_params), stream);
+
+ g_free(name);
+
+ name = g_strdup_printf("%s-%s", stream->name, "sink");
+ stream->sink = gst_element_factory_make ("appsink", name);
+ if (!stream->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create appsink element");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (stream->sink), "emit-signals", TRUE, "sync", FALSE, NULL);
+ g_signal_connect (stream->sink, "new-buffer", G_CALLBACK (gst_ssm_demux_on_new_buffer), stream);
+
+ g_free(name);
+
+ gst_bin_add_many (GST_BIN (stream->pipe), stream->urisrc, stream->parser, stream->sink, NULL);
+ if (!gst_element_link_many (stream->urisrc, stream->parser, stream->sink, NULL)) {
+ GST_ERROR ("failed to link elements...");
+ return FALSE;
+ }
+
+ stream->bus = gst_pipeline_get_bus (GST_PIPELINE (stream->pipe));
+ gst_bus_add_watch (stream->bus, (GstBusFunc)gst_ss_demux_download_bus_cb, stream);
+ gst_object_unref (stream->bus);
+
+ return TRUE;
+}
+
+#if 0
+static gboolean
+gst_ss_demux_create_dummy_pipe (GstSSDemux * demux, GstSSDemuxStream *stream)
+{
+ gchar *name = NULL;
+ GstCaps *caps = NULL;
+ GstElement *capsfilter = NULL;
+ GstElement *enc = NULL;
+ guint64 avg_dur = -1;
+ guint frame_rate = 0;
+
+ name = g_strdup_printf("%s-%s", stream->name, "dummy");
+
+ stream->pipe = gst_pipeline_new (name);
+ if (!stream->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+ g_free(name);
+
+ /* create dummy sender source */
+ name = g_strdup_printf("%s-%s", stream->name, "dummysrc");
+ stream->urisrc = gst_element_factory_make ("imagereader", name);
+ if (!stream->urisrc) {
+ GST_ERROR_OBJECT (demux,"failed to create filesrc element");
+ return FALSE;
+ }
+ g_free(name);
+ g_object_set (G_OBJECT (stream->urisrc), "location", "/opt/home/root/aonly_VGA_1frame_I420.yuv", NULL);
+ g_object_set (G_OBJECT (stream->urisrc), "framerate", 25, NULL);
+ g_object_set (G_OBJECT (stream->urisrc), "num-buffers", 60, NULL);
+
+ /* caps filter */
+ capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ if (!capsfilter) {
+ GST_ERROR_OBJECT (demux, "failed to create capsfilter element");
+ return FALSE;
+ }
+ caps = gst_caps_new_simple ("video/x-raw-yuv",
+ "width", G_TYPE_INT, 640,
+ "height", G_TYPE_INT, 480,
+ "framerate",GST_TYPE_FRACTION, 25,1,
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
+ NULL);
+ g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
+
+ /* create h264parse element */
+ enc = gst_element_factory_make ("savsenc_h264", "H264 encoder");
+ if (!enc) {
+ GST_ERROR_OBJECT (demux, "failed to create h264 parse element");
+ return FALSE;
+ }
+ name = g_strdup_printf("%s-%s", stream->name, "sink");
+ stream->sink = gst_element_factory_make ("appsink", name);
+ if (!stream->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create appsink element");
+ return FALSE;
+ }
+ g_free(name);
+ g_object_set (G_OBJECT (stream->sink), "emit-signals", TRUE, "sync", FALSE, NULL);
+ g_signal_connect (stream->sink, "new-buffer", G_CALLBACK (gst_ssm_demux_on_new_buffer), stream);
+
+ /* add to pipeline & link all elements */
+ gst_bin_add_many (GST_BIN (stream->pipe), stream->urisrc, capsfilter, enc, stream->sink, NULL);
+
+ if (!gst_element_link_many (stream->urisrc, capsfilter, enc, stream->sink, NULL)) {
+ GST_ERROR_OBJECT (demux,"failed to link dummy pipe elements...");
+ return FALSE;
+ }
+
+ stream->bus = gst_pipeline_get_bus (GST_PIPELINE (stream->pipe));
+ gst_bus_add_watch (stream->bus, (GstBusFunc)gst_ss_demux_download_bus_cb, stream);
+ gst_object_unref (stream->bus);
+
+ return TRUE;
+}
+#else
+static gboolean
+gst_ss_demux_create_dummy_pipe (GstSSDemux * demux, GstSSDemuxStream *stream)
+{
+ gchar *name = NULL;
+ GstBus *bus = NULL;
+ GstCaps *caps = NULL;
+
+ name = g_strdup_printf("%s-%s", stream->name, "dummy");
+
+ stream->pipe = gst_pipeline_new (name);
+ if (!stream->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+ g_free(name);
+
+ /* create dummy sender source */
+ name = g_strdup_printf("%s-%s", stream->name, "dummysrc");
+ stream->urisrc = gst_element_factory_make ("filesrc", name);
+ if (!stream->urisrc) {
+ GST_ERROR_OBJECT (demux,"failed to create filesrc element");
+ return FALSE;
+ }
+ g_free(name);
+ g_object_set (G_OBJECT (stream->urisrc), "location", "/opt/home/root/sound_2sec.264", NULL);
+
+ /* create appsink element */
+ name = g_strdup_printf("%s-%s", stream->name, "parser");
+ stream->parser= gst_element_factory_make ("legacyh264parse", name);
+ if (!stream->parser) {
+ GST_ERROR_OBJECT (demux, "failed to create h264 parse element");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (stream->parser), "output-format", 1, NULL);
+
+ /* create appsink element */
+ name = g_strdup_printf("%s-%s", stream->name, "sink");
+ stream->sink = gst_element_factory_make ("appsink", name);
+ if (!stream->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create appsink element");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (stream->sink), "emit-signals", TRUE, "sync", FALSE, NULL);
+
+ caps = gst_caps_new_simple ("video/x-h264",
+ "width", G_TYPE_INT, 640,
+ "height", G_TYPE_INT, 480,
+ "stream-format", G_TYPE_STRING, "byte-stream",
+ NULL);
+ g_object_set (G_OBJECT (stream->sink), "caps", caps, NULL);
+
+ g_signal_connect (stream->sink, "new-buffer", G_CALLBACK (gst_ssm_demux_on_new_buffer), stream);
+ g_free(name);
+
+ /* add to pipeline & link all elements */
+ gst_bin_add_many (GST_BIN (stream->pipe), stream->urisrc, stream->parser, stream->sink, NULL);
+ if (!gst_element_link_many (stream->urisrc, stream->parser, stream->sink, NULL)) {
+ GST_ERROR_OBJECT (demux,"failed to link elements...");
+ return FALSE;
+ }
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (stream->pipe));
+ gst_bus_add_watch (bus, (GstBusFunc)gst_ss_demux_download_bus_cb, stream);
+ gst_object_unref (bus);
+
+ return TRUE;
+}
+
+
+#endif
+static gboolean
+gst_ss_demux_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstSSDemuxStream *stream = (GstSSDemuxStream *)data;
+ GstSSDemux *demux = stream->parent;
+
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ guint64 download_rate = -1;
+
+ GST_INFO_OBJECT (stream->pad, "received EOS on download pipe..");
+ // increase the fragment count on EOS
+ stream->frag_cnt++;
+
+ if (g_strrstr (gst_element_get_name(stream->urisrc), "http")) {
+ g_object_get (stream->urisrc, "download-rate", &download_rate, NULL);
+ g_print("*********** '%s' download rate = %d bps **************\n", stream->name, download_rate);
+ }
+
+ // TODO: need to remove download_rate> 0 check.. make it generic
+ if ((stream->type == SS_STREAM_VIDEO) && (demux->ss_mode != SS_MODE_AONLY) && (download_rate >= 0)) {
+ if (stream->frag_cnt >= demux->fragments_cache) {
+ /* for switching, we are considering video download rate only */
+ demux->ss_mode = gst_ssm_parse_switch_qualitylevel (demux->parser, download_rate);
+ }
+ } else if (stream->type == SS_STREAM_AUDIO && (demux->ss_mode == SS_MODE_AONLY)) {
+ /* when video is not present using audio download rate to calculate switching */
+ demux->ss_mode = gst_ssm_parse_switch_qualitylevel (demux->parser, download_rate);
+ if (demux->ss_mode != SS_MODE_AONLY) {
+ g_print ("\n\nMoving to AV mode by audio considering audio download rate\n\n\n\n");
+ }
+ }
+
+ g_cond_signal (stream->cond);
+
+#ifdef SIMULATE_AUDIO_ONLY
+ /* when fragment count is multiple of 4, switch to audio only case */
+ if ((stream->frag_cnt % 4 == 0) && (stream->type == SS_STREAM_VIDEO) &&
+ GST_SSM_PARSE_IS_LIVE_PRESENTATION(demux->parser)) {
+ g_print ("\n\t ######## Forcibly switching to audio only for testing ##########\n");
+ demux->ss_mode = SS_MODE_AONLY;
+ }
+ #endif
+ GST_DEBUG_OBJECT (stream->pad, "Signalling eos condition...");
+
+ GST_DEBUG_OBJECT (demux, "number of fragments downloaded = %d", stream->frag_cnt);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GError *error = NULL;
+ gchar* debug = NULL;
+
+ g_print ("Error from %s\n", gst_element_get_name (GST_MESSAGE_SRC(msg)));
+
+ gst_message_parse_error( msg, &error, &debug );
+ if (error)
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: error= %s\n", error->message);
+
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: debug = %s\n", debug);
+
+ /* handling error, when client requests url, which is yet to be prepared by server */
+ if ((!strncmp(error->message, "Precondition Failed", strlen("Precondition Failed"))) && (5 == error->code)) {
+ GstStateChangeReturn ret;
+
+ /* wait for 1sec & request the url again */
+ // TODO: need to make wait time as generic or Adding loop count to request again & again
+ GST_INFO_OBJECT (demux, "ERROR : code = %d, msg = %s, NEED to request again", error->code, error->message);
+ usleep (1000000); // 1 sec
+
+ /* put the current pipeline to NULL state */
+ gst_element_set_state (stream->pipe, GST_STATE_NULL);
+ gst_element_get_state (stream->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ stream->pipe = stream->urisrc = stream->parser = stream->sink = NULL;
+
+ g_print ("Going to download fragment AGAIN : %s\n", stream->uri);
+ if (!gst_ss_demux_create_download_pipe (demux, stream, stream->uri, stream->start_ts)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ if (!gst_element_post_message (GST_ELEMENT(demux), msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ return FALSE;
+ }
+ }
+
+ ret = gst_element_set_state (stream->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ if (!gst_element_post_message (GST_ELEMENT(demux), msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ return FALSE;
+ }
+ }
+
+ } else {
+ if (error)
+ g_print ("GST_MESSAGE_ERROR: error= %s\n", error->message);
+
+ g_print ("GST_MESSAGE_ERROR: debug = %s\n", debug);
+ if (!gst_element_post_message (GST_ELEMENT(demux), msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ gst_ss_demux_stop (demux, stream);
+ g_free( debug);
+ debug = NULL;
+ g_error_free( error);
+ return FALSE;
+ }
+ gst_ss_demux_stop (demux, stream);
+ }
+
+ g_free( debug);
+ debug = NULL;
+ g_error_free( error);
+ break;
+ }
+ case GST_MESSAGE_WARNING: {
+ char* debug = NULL;
+ GError* error = NULL;
+ gst_message_parse_warning(msg, &error, &debug);
+ GST_WARNING_OBJECT(demux, "warning : %s\n", error->message);
+ GST_WARNING_OBJECT(demux, "debug : %s\n", debug);
+ g_error_free( error );
+ g_free( debug);
+ break;
+ }
+ default : {
+ GST_LOG_OBJECT(demux, "unhandled message : %s\n", gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+gst_ssm_demux_on_new_buffer (GstElement * appsink, void* data)
+{
+ GstSSDemuxStream *stream = (GstSSDemuxStream *)data;
+ GstSSDemux *demux = stream->parent;
+ GstBuffer *inbuf = NULL;
+ GstFlowReturn fret = GST_FLOW_OK;
+
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_WARNING_OBJECT (demux, "Input buffer not available.,..\n");
+ return;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "Inbuf : size = %d, ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_BUFFER_SIZE(inbuf), GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)), GST_TIME_ARGS(GST_BUFFER_DURATION(inbuf)));
+
+ // Queue the buffers till fragment cache reached... after reaching start pushing data to respective port
+ if ( stream->frag_cnt < demux->fragments_cache ) {
+ GST_LOG_OBJECT (demux, "queuing data till caching finished...");
+ g_queue_push_tail (stream->queue, inbuf);
+ return;
+ }
+
+ if (!stream->sent_ns) {
+ guint64 duration = GST_CLOCK_TIME_NONE;
+ guint64 start = GST_CLOCK_TIME_NONE;
+ GstEvent *event = NULL;
+
+ duration = gst_util_uint64_scale (GST_SSM_PARSE_GET_DURATION(demux->parser), GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(demux->parser));
+
+ start = gst_util_uint64_scale (GST_SSM_PARSE_NS_START(demux->parser), GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(demux->parser));
+
+ event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ start, duration, start);
+
+ GST_DEBUG_OBJECT(demux," new_segment start = %"GST_TIME_FORMAT, GST_TIME_ARGS(start));
+
+ if (!gst_pad_push_event (stream->pad, event)) {
+ GST_ERROR_OBJECT (demux, "failed to push newsegment event");
+ return;
+ }
+ stream->sent_ns = TRUE;
+ }
+
+ while (!g_queue_is_empty(stream->queue)) {
+ GstBuffer *cache_buf = g_queue_pop_head (stream->queue);
+
+ fret = gst_pad_push (stream->pad, cache_buf);
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (demux, "failed to push data, reason : %s", gst_flow_get_name (fret));
+ goto error;
+ }
+ }
+
+ if (stream->type == SS_STREAM_VIDEO && demux->ss_mode == SS_MODE_AONLY) {
+ GST_BUFFER_TIMESTAMP (inbuf) = stream->switch_ts;
+ GST_BUFFER_DURATION (inbuf) = ((float)1/25) * GST_SECOND;
+ stream->switch_ts = GST_BUFFER_TIMESTAMP (inbuf) + GST_BUFFER_DURATION (inbuf);
+ g_print ("Dummy buffers ts : %"GST_TIME_FORMAT" and dur : %"GST_TIME_FORMAT"\n",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (inbuf)), GST_TIME_ARGS(GST_BUFFER_DURATION (inbuf)));
+
+ g_print ("caps : %s\n", gst_caps_to_string(GST_BUFFER_CAPS(inbuf)));
+ }
+
+ /* push data to downstream*/
+ fret = gst_pad_push (stream->pad, inbuf);
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (demux, "failed to push data, reason : %s", gst_flow_get_name (fret));
+ goto error;
+ }
+
+error:
+ return;
+}
+
+static void
+gst_ss_demux_stop (GstSSDemux * demux, GstSSDemuxStream *stream)
+{
+ if (GST_TASK_STATE (stream->stream_task) != GST_TASK_STOPPED)
+ gst_task_stop (stream->stream_task);
+}
+
+static void
+gst_ss_demux_stream_init (GstSSDemux *demux, GstSSDemuxStream *stream, SS_STREAM_TYPE stream_type)
+{
+ stream->cond = g_cond_new ();
+ stream->lock = g_mutex_new ();
+ stream->queue = g_queue_new ();
+ stream->parent = demux;
+ stream->pipe = NULL;
+ stream->urisrc = NULL;
+ stream->parser = NULL;
+ stream->sink = NULL;
+ stream->frag_cnt = 0;
+ stream->type = stream_type ;
+ stream->uri = NULL;
+ stream->start_ts = -1;
+ stream->sent_ns = FALSE;
+ stream->switch_ts = GST_CLOCK_TIME_NONE;
+ stream->avg_dur = GST_CLOCK_TIME_NONE;
+
+ if (stream->type == SS_STREAM_VIDEO) {
+ stream->pad = gst_pad_new_from_static_template (&ssdemux_videosrc_template, "video");
+ stream->name = g_strdup("video");
+ } else if (stream->type == SS_STREAM_AUDIO) {
+ stream->pad = gst_pad_new_from_static_template (&ssdemux_audiosrc_template, "audio");
+ stream->name = g_strdup("audio");
+ } else if (stream->type == SS_STREAM_TEXT) {
+ stream->pad = gst_pad_new_from_static_template (&ssdemux_subsrc_template, "subtitle");
+ stream->name = g_strdup("text");
+ }
+
+ GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
+
+ gst_pad_use_fixed_caps (stream->pad);
+ gst_pad_set_event_function (stream->pad, gst_ss_demux_handle_src_event);
+ gst_pad_set_query_function (stream->pad, gst_ss_demux_handle_src_query);
+
+ stream->caps = ssm_parse_get_stream_caps (demux->parser, stream->type);
+ g_print ("prepare video caps = %s", gst_caps_to_string(stream->caps));
+
+ GST_DEBUG_OBJECT (demux, "setting caps %" GST_PTR_FORMAT, stream->caps);
+ gst_pad_set_caps (stream->pad, stream->caps);
+
+ GST_DEBUG_OBJECT (demux, "adding pad %s %p to demux %p", GST_OBJECT_NAME (stream->pad), stream->pad, demux);
+ gst_pad_set_active (stream->pad, TRUE);
+ gst_element_add_pad (GST_ELEMENT_CAST (demux), stream->pad);
+}
+
+static void
+gst_ss_demux_stream_free (GstSSDemux * demux, GstSSDemuxStream * stream)
+{
+ if (stream->queue) {
+ while (!g_queue_is_empty(stream->queue)) {
+ gst_buffer_unref (g_queue_pop_head (stream->queue));
+ }
+ g_queue_free (stream->queue);
+ stream->queue = NULL;
+ }
+
+ if (stream->pad) {
+ gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
+ stream->pad = NULL;
+ }
+ if (stream->cond) {
+ g_cond_free (stream->cond);
+ stream->cond = NULL;
+ }
+ if (stream->lock) {
+ g_mutex_free (stream->lock);
+ stream->lock = NULL;
+ }
+
+ g_free (stream);
+}
+static gboolean
+ssdemux_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "ssdemux", GST_RANK_PRIMARY,
+ GST_TYPE_SS_DEMUX) || FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "ssdemux",
+ "Smooth streaming demux plugin",
+ ssdemux_init, VERSION, "LGPL", PACKAGE_NAME, "http://www.samsung.com/")
+
--- /dev/null
+\r
+#ifndef __GST_SS_DEMUX_H__\r
+#define __GST_SS_DEMUX_H__\r
+\r
+#include <gst/gst.h>\r
+#include <gst/base/gstadapter.h>\r
+#include <gst/app/gstappsrc.h>\r
+#include <gst/app/gstappsink.h>\r
+#include "ssmanifestparse.h"\r
+#include "piffcommon.h"\r
+\r
+G_BEGIN_DECLS\r
+#define GST_TYPE_SS_DEMUX \\r
+ (gst_ss_demux_get_type())\r
+#define GST_SS_DEMUX(obj) \\r
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_SS_DEMUX, GstSSDemux))\r
+#define GST_SS_DEMUX_CLASS(klass) \\r
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SS_DEMUX,GstSSDemuxClass))\r
+#define GST_IS_SS_DEMUX(obj) \\r
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SS_DEMUX))\r
+#define GST_IS_SS_DEMUX_CLASS(klass) \\r
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SS_DEMUX))\r
+\r
+typedef struct _GstSSDemux GstSSDemux;\r
+typedef struct _GstSSDemuxClass GstSSDemuxClass;\r
+typedef struct _GstSSDemuxStream GstSSDemuxStream;\r
+\r
+\r
+/**\r
+ * GstSSDemux:\r
+ *\r
+ * Opaque #GstSSDemux data structure.\r
+ */\r
+struct _GstSSDemux\r
+{\r
+ GstElement parent;\r
+\r
+ GstPad *sinkpad;\r
+\r
+ /* Properties */\r
+ gchar **cookies; /* HTTP request cookies. */\r
+ gboolean allow_audio_only; /*In LIVE case, allow audio only download when downloadrate is less */\r
+ guint fragments_cache; /* number of fragments needed to be cached to start playing */\r
+ gfloat bitrate_switch_tol; /* tolerance with respect to the fragment duration to switch the bitarate*/\r
+ gboolean need_cache;\r
+ gboolean cancelled;\r
+ guint download_rate;\r
+ GstBuffer *manifest;\r
+ GstSSMParse *parser; /* manifest parser */\r
+\r
+ GstSSDemuxStream *streams[SS_STREAM_NUM];\r
+ SS_BW_MODE ss_mode;\r
+ gboolean switch_eos;\r
+};\r
+\r
+struct _GstSSDemuxClass\r
+{\r
+ GstElementClass parent_class;\r
+};\r
+\r
+GType gst_ss_demux_get_type (void);\r
+\r
+G_END_DECLS\r
+#endif /* __GST_SS_DEMUX_H__ */\r
+\r
+\r
+\r
+\r
--- /dev/null
+#include "ssmanifestparse.h"
+
+static gboolean ssm_parse_root_node (GstSSMParse *parser, xmlNodePtr root_node);
+GstCaps * ssm_prepare_video_caps (GstSSMParse *parser, GstSSMStreamNode *stream);
+GstCaps * ssm_prepare_audio_caps (GstSSMParse *parser, GstSSMStreamNode *stream);
+GstCaps *ssm_prepare_text_caps (GstSSMParse *parser, GstSSMStreamNode *stream);
+static gboolean convert_NALUnitDCI_to_PacktizedDCI (unsigned char *nalu_dci, unsigned char **packetized_dci, unsigned int *packetized_dci_len);
+
+#define MANIFEST_LOCK(parser) g_mutex_lock(parser->lock)
+#define MANIFEST_UNLOCK(parser) g_mutex_unlock(parser->lock)
+#define START_TS_MAX(a,b) ((a)>(b) ? (a) : (b))
+
+const gchar *
+ssm_parse_get_stream_name(SS_STREAM_TYPE type)
+{
+ if (type == SS_STREAM_VIDEO) return "video"; \
+ else if (type == SS_STREAM_AUDIO) return "audio"; \
+ else if (type == SS_STREAM_TEXT) return "text"; \
+ else return "unknown";
+}
+
+
+static gboolean
+int_from_string (gchar * ptr, gchar ** endptr, gint * val, gint base)
+{
+ gchar *end;
+ g_return_val_if_fail (ptr != NULL, FALSE);
+ g_return_val_if_fail (val != NULL, FALSE);
+
+ errno = 0;
+ *val = strtol (ptr, &end, base);
+ if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) || (errno != 0 && *val == 0)) {
+ g_print ("Error in strtol : %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ if (endptr)
+ *endptr = end;
+
+ return end != ptr;
+}
+
+static gboolean
+ssm_parse_get_xml_prop_boolean (GstSSMParse *parser, xmlNode * node,
+ const gchar * property)
+{
+ xmlChar *prop_string;
+ gboolean prop_bool = FALSE;
+
+ prop_string = xmlGetProp (node, (const xmlChar *) property);
+ if (prop_string) {
+ if ((xmlStrcmp (prop_string, (xmlChar *) "false") == 0) ||
+ (xmlStrcmp (prop_string, (xmlChar *) "FALSE") == 0)) {
+ GST_LOG (" - %s: false", property);
+ } else if ((xmlStrcmp (prop_string, (xmlChar *) "true") == 0) ||
+ (xmlStrcmp (prop_string, (xmlChar *) "TRUE") == 0)) {
+ GST_LOG(" - %s: true", property);
+ prop_bool = TRUE;
+ } else {
+ GST_WARNING("failed to parse boolean property %s from xml string %s", property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_bool;
+}
+
+static guint
+ssm_parse_get_xml_prop_uint (GstSSMParse *parser,
+ xmlNode * node, const gchar * property, guint default_val)
+{
+ xmlChar *prop_string;
+ guint prop_uint = default_val;
+
+ prop_string = xmlGetProp (node, (const xmlChar *) property);
+ if (prop_string) {
+ if (sscanf ((gchar *) prop_string, "%u", &prop_uint)) {
+ GST_LOG (" - %s: %u", property, prop_uint);
+ } else {
+ GST_WARNING("failed to parse unsigned integer property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_uint;
+}
+
+static guint64
+ssm_parse_get_xml_prop_uint64 (GstSSMParse *parser,
+ xmlNode * node, const gchar * property, guint64 default_val)
+{
+ xmlChar *prop_string;
+ guint64 prop_uint64 = default_val;
+
+ prop_string = xmlGetProp (node, (const xmlChar *) property);
+ if (prop_string) {
+ if (sscanf ((gchar *) prop_string, "%llu", &prop_uint64)) {
+ GST_LOG (" - %s: %s[%"G_GUINT64_FORMAT"]", property, prop_string, prop_uint64);
+ } else {
+ GST_WARNING("failed to parse unsigned integer property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_uint64;
+}
+
+static gint
+ssm_parser_sort_qualitylevels_by_bitrate (gconstpointer a, gconstpointer b)
+{
+ return ((GstSSMQualityNode *) (a))->bitrate - ((GstSSMQualityNode *) (b))->bitrate; //sorting in ascending order
+ //return ((GstSSMQualityNode *) (b))->bitrate - ((GstSSMQualityNode *) (a))->bitrate; // sorting in descending order
+}
+
+
+static void
+gst_ssm_parse_free_quality_node (GstSSMQualityNode *qualitynode)
+{
+ if (qualitynode) {
+ g_free (qualitynode->codec_data);
+ g_free (qualitynode->fourcc);
+ g_slice_free (GstSSMQualityNode, qualitynode);
+ }
+}
+
+static void
+gst_ssm_parse_free_fragment_node (GstSSMFragmentNode*fragnode)
+{
+ if (fragnode) {
+ g_slice_free (GstSSMFragmentNode, fragnode);
+ }
+}
+
+
+static void
+gst_ssm_parse_free_stream_node (GstSSMStreamNode *streamnode)
+{
+ if (streamnode) {
+ if (streamnode->quality_lists) {
+ streamnode->quality_lists = g_list_first (streamnode->quality_lists);
+ g_list_foreach (streamnode->quality_lists, (GFunc) gst_ssm_parse_free_quality_node, NULL);
+ g_list_free (streamnode->quality_lists);
+ streamnode->quality_lists = NULL;
+ }
+ if (streamnode->fragment_lists) {
+ streamnode->fragment_lists = g_list_first (streamnode->fragment_lists);
+ g_list_foreach (streamnode->fragment_lists, (GFunc) gst_ssm_parse_free_fragment_node, NULL);
+ g_list_free (streamnode->fragment_lists);
+ streamnode->fragment_lists = NULL;
+ }
+ if (streamnode->StreamType) {
+ g_free(streamnode->StreamType);
+ streamnode->StreamType = NULL;
+ }
+ if (streamnode->StreamUrl) {
+ g_free(streamnode->StreamUrl);
+ streamnode->StreamUrl = NULL;
+ }
+ if (streamnode->StreamSubType) {
+ g_free(streamnode->StreamSubType);
+ streamnode->StreamSubType = NULL;
+ }
+ if (streamnode->StreamName) {
+ g_free(streamnode->StreamName);
+ streamnode->StreamName = NULL;
+ }
+ if (streamnode->frag_cond) {
+ g_cond_free (streamnode->frag_cond);
+ streamnode->frag_cond = NULL;
+ }
+ if (streamnode->frag_lock) {
+ g_mutex_free (streamnode->frag_lock);
+ streamnode->frag_lock = NULL;
+ }
+ g_slice_free (GstSSMStreamNode, streamnode);
+ }
+}
+
+GstSSMParse *
+gst_ssm_parse_new (const gchar * uri)
+{
+ GstSSMParse *parser;
+ gchar *tmp = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ parser = g_new0 (GstSSMParse, 1);
+
+ parser->uri = g_strdup (uri);
+ parser->lock = g_mutex_new();
+ tmp = strrchr(uri, '/');
+ tmp = tmp+1;
+ parser->presentation_uri = (gchar *) malloc (tmp - uri + 1);
+ if (NULL == parser->presentation_uri) {
+ GST_ERROR ("Failed to allocate memory..\n");
+ return NULL;
+ }
+
+ strncpy (parser->presentation_uri, uri, tmp - uri);
+ parser->presentation_uri[tmp-uri] = '\0';
+ parser->ns_start = GST_CLOCK_TIME_NONE;
+ GST_INFO ("Presentation URI : %s", parser->presentation_uri);
+ return parser;
+}
+
+void
+gst_ssm_parse_free (GstSSMParse *parser)
+{
+ int i =0;
+
+ g_return_if_fail (parser != NULL);
+
+ g_mutex_free(parser->lock);
+
+ // TODO: cleanup memory allocated.....
+ if (parser->RootNode) {
+ for (i = 0; i < SS_STREAM_NUM; i++) {
+ if (parser->RootNode->streams[i]) {
+ g_list_foreach (parser->RootNode->streams[i], (GFunc) gst_ssm_parse_free_stream_node, NULL);
+ parser->RootNode->streams[i] = NULL;
+ }
+ }
+ if (parser->RootNode->ProtectNode) {
+ g_slice_free (GstSSMProtectionNode, parser->RootNode->ProtectNode);
+ parser->RootNode->ProtectNode = NULL;
+ }
+ g_slice_free (GstSSMRootNode, parser->RootNode);
+ parser->RootNode = NULL;
+ }
+
+ g_free (parser);
+}
+
+
+static gboolean
+ssm_parse_quality_node (GstSSMParse *parser, GstSSMStreamNode *stream, xmlNodePtr quality_node)
+{
+ GstSSMQualityNode *quality_level = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (quality_node != NULL, FALSE);
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ quality_level = g_slice_new0 (GstSSMQualityNode);
+ if (quality_level == NULL) {
+ GST_ERROR ("Allocation of quality_level node failed!");
+ return FALSE;
+ }
+
+ quality_level->codec_data = NULL;
+ quality_level->fourcc = NULL;
+
+ quality_level->index = ssm_parse_get_xml_prop_uint (parser, quality_node, "Index", -1);
+ GST_DEBUG("Quality Index = %d", quality_level->index);
+ if (SS_STREAM_VIDEO == stream->type && (quality_level->index == -1)) {
+ GST_ERROR ("Index attribute is not present for VIDEO stream...");
+ return FALSE;
+ }
+
+ /* MANDATORY : parsing Bitrate attribute */
+ quality_level->bitrate = ssm_parse_get_xml_prop_uint (parser, quality_node, "Bitrate", -1);
+ GST_DEBUG("Bitrate = %d", quality_level->bitrate);
+ if (quality_level->bitrate == -1) {
+ GST_ERROR ("bitrate attribute is not present...");
+ return FALSE;
+ }
+
+ /* MANDATORY for video: parsing MaxWidth attribute */
+ quality_level->max_width = ssm_parse_get_xml_prop_uint (parser, quality_node, "MaxWidth", -1);
+ GST_DEBUG("MaxWidth = %d", quality_level->max_width);
+ if (SS_STREAM_VIDEO == stream->type && (quality_level->max_width == -1)) {
+ GST_ERROR ("max_width attribute is not present in VIDEO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for video: parsing MaxHeight attribute */
+ quality_level->max_height = ssm_parse_get_xml_prop_uint (parser, quality_node, "MaxHeight", -1);
+ GST_DEBUG("MaxWidth = %d", quality_level->max_height);
+ if (SS_STREAM_VIDEO == stream->type && (quality_level->max_height == -1)) {
+ GST_ERROR ("max_height attribute is not present in VIDEO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for audio: parsing SamplingRate attribute */
+ quality_level->samplingrate = ssm_parse_get_xml_prop_uint (parser, quality_node, "SamplingRate", -1);
+ GST_DEBUG("SamplingRate = %d", quality_level->samplingrate);
+ if (SS_STREAM_AUDIO == stream->type && (quality_level->samplingrate == -1)) {
+ GST_ERROR ("SamplingRate attribute is not present for AUDIO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for audio: parsing Channels attribute */
+ quality_level->channels = ssm_parse_get_xml_prop_uint (parser, quality_node, "Channels", -1);
+ GST_DEBUG("SamplingRate = %d", quality_level->channels);
+ if (SS_STREAM_AUDIO == stream->type && (quality_level->channels == -1)) {
+ GST_ERROR ("Channels attribute is not present for AUDIO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for audio: parsing BitsPerSample attribute */
+ quality_level->bps = ssm_parse_get_xml_prop_uint (parser, quality_node, "BitsPerSample", -1);
+ GST_DEBUG("BitsPerSample = %d", quality_level->bps);
+ if (SS_STREAM_AUDIO == stream->type && (quality_level->bps == -1)) {
+ GST_ERROR ("BitsPerSample attribute is not present for AUDIO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for audio: parsing PacketSize attribute */
+ quality_level->packet_size = ssm_parse_get_xml_prop_uint (parser, quality_node, "PacketSize", -1);
+ GST_DEBUG("PacketSize = %d", quality_level->packet_size);
+ if (SS_STREAM_AUDIO == stream->type && (quality_level->packet_size == -1)) {
+ GST_ERROR ("PacketSize attribute is not present for AUDIO...");
+ return FALSE;
+ }
+
+
+ /* MANDATORY for audio: parsing AudioTag attribute */
+ quality_level->audio_tag = ssm_parse_get_xml_prop_uint (parser, quality_node, "AudioTag", -1);
+ GST_DEBUG("AudioTag = %d", quality_level->audio_tag);
+ if (SS_STREAM_AUDIO == stream->type && (quality_level->audio_tag == -1)) {
+ GST_ERROR ("AudioTag attribute is not present for AUDIO...");
+ return FALSE;
+ }
+
+ /* MANDATORY for audio & video: parsing FourCC attribute */
+ quality_level->fourcc = (gchar *)xmlGetProp(quality_node, (const xmlChar *) "FourCC");
+ if (!quality_level->fourcc && ((SS_STREAM_AUDIO == stream->type) || (SS_STREAM_VIDEO == stream->type))) {
+ GST_ERROR ("failed to parse fourcc from quality node");
+ return FALSE;
+ }
+
+ if (!((!strncmp ((char *)quality_level->fourcc, "AACL", 4)) || !strncmp ((char *)quality_level->fourcc, "WMAP", 4) ||
+ (!strncmp ((char *)quality_level->fourcc, "H264", 4)) || !strncmp ((char *)quality_level->fourcc, "WVC1", 4) ||
+ (!strncmp ((char *)quality_level->fourcc, "TTML", 4)))) {
+ GST_INFO ("Not a proper Fourcc Code...If possible take from SubType\n\n\n");
+ if (quality_level->fourcc) {
+ free (quality_level->fourcc);
+ quality_level->fourcc = NULL;
+ }
+ GST_DEBUG ("Subtype = %s\n\n",stream->StreamSubType);
+ quality_level->fourcc = g_strdup (stream->StreamSubType);
+ if (!((!strncmp ((char *)quality_level->fourcc, "AACL", 4)) || !strncmp ((char *)quality_level->fourcc, "WMAP", 4) ||
+ (!strncmp ((char *)quality_level->fourcc, "H264", 4)) || !strncmp ((char *)quality_level->fourcc, "WVC1", 4))) {
+ GST_ERROR ("Subtype also does not contain valid fourcc code...ERROR\n");
+ return FALSE;
+ }
+ }
+
+ GST_DEBUG("FourCC = %s", quality_level->fourcc);
+
+ // TODO: need to check whether it is required for all video & audio
+
+ /* MANDATORY for audio & video: parsing CodecPrivateData attribute */
+ quality_level->codec_data = (gchar *)xmlGetProp(quality_node, (const xmlChar *) "CodecPrivateData");
+ if (!quality_level->codec_data && ((SS_STREAM_AUDIO == stream->type) || (SS_STREAM_VIDEO == stream->type))) {
+ GST_ERROR ("failed to get codec data from quality node");
+ return FALSE;
+ }
+
+ /* Optional for VIDEO H264: parsing NALUnitLengthField attribute */
+ quality_level->NALULengthofLength = ssm_parse_get_xml_prop_uint (parser, quality_node, "NALUnitLengthField", 4);
+ GST_DEBUG("NALUnitLengthField = %d", quality_level->NALULengthofLength);
+
+ stream->quality_lists = g_list_append (stream->quality_lists, quality_level);
+
+ GST_LOG ("Appened quality level to stream...");
+
+ return TRUE;
+
+}
+
+static gboolean
+ssm_parse_fragment_node (GstSSMParse *parser, GstSSMStreamNode *stream, xmlNodePtr fragment_node)
+{
+ GstSSMFragmentNode *fragment = NULL;
+ gboolean found_time = FALSE;
+ gboolean found_duration = FALSE;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (fragment_node != NULL, FALSE);
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ fragment = g_slice_new0 (GstSSMFragmentNode);
+ if (fragment == NULL) {
+ GST_ERROR ("Allocation of fragment failed!");
+ return FALSE;
+ }
+
+ /*parsing fragmentnumber attribute */
+ fragment->num = ssm_parse_get_xml_prop_uint (parser, fragment_node, "n", -1);
+ GST_DEBUG ("fragment number = %d", fragment->num);
+
+ /*parsing duration attribute */
+ fragment->dur = ssm_parse_get_xml_prop_uint64 (parser, fragment_node, "d", -1);
+ if (fragment->dur == -1 && !fragment_node->next) {
+ GST_ERROR ("Fragment Duration for last fragment is mandatory");
+ return FALSE;
+ } else if (fragment->dur != -1) {
+ GST_DEBUG ("Fragment duration = %"GST_TIME_FORMAT, GST_TIME_ARGS (fragment->dur));
+ found_duration = TRUE;
+ }
+
+ /*parsing time attribute */
+ fragment->time = ssm_parse_get_xml_prop_uint64 (parser, fragment_node, "t", -1);
+ if (fragment->time != -1) {
+ GST_DEBUG ("Fragment time = %"GST_TIME_FORMAT, GST_TIME_ARGS(fragment->time));
+ found_time = TRUE;
+ }
+
+ if (!found_time && !found_duration) {
+ GST_ERROR ("Both time & duration attributes are NOT present.. ERROR");
+ return FALSE;
+ } else if (!found_time && found_duration) {
+ GList *prev_fragment_list = NULL;
+ GstSSMFragmentNode *prev_fragment = NULL;
+
+ GST_DEBUG ("Only Duration attribute is present...");
+ if (stream->fragment_lists) {
+ prev_fragment_list = g_list_last(stream->fragment_lists);
+ if (NULL == prev_fragment_list) {
+ GST_ERROR ("Error last fragment lists are not present..\n");
+ return FALSE;
+ }
+ prev_fragment = (GstSSMFragmentNode *)prev_fragment_list->data;
+ fragment->time = prev_fragment->time + prev_fragment->dur;
+ GST_DEBUG ("Fragment time = %llu", fragment->time);
+ } else {
+ GST_INFO ("Frament list is empty, assuming it as first fragment...");
+ fragment->time = 0;
+ }
+ } else if (found_time && !found_duration) {
+ xmlNodePtr next_fragment_node = NULL;
+ guint64 next_ts = 0;
+
+ GST_DEBUG ("Only time attribute is present...");
+
+ next_fragment_node = fragment_node->next;
+ if (next_fragment_node) {
+ next_ts = ssm_parse_get_xml_prop_uint64 (parser, fragment_node, "t", -1);
+ if (next_ts == -1) {
+ GST_ERROR ("Next fragment time not present to calculate duration of present fragment...");
+ return FALSE;
+ } else {
+ fragment->dur = next_ts - fragment->time;
+ GST_DEBUG ("Current fragment duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(fragment->dur));
+ }
+ } else {
+ GST_ERROR ("Next fragment not present to calculate duration of present fragment...");
+ return FALSE;
+ }
+ }
+
+#if 1
+ /*error check for timestamps as specified in spec */
+ {
+ xmlNodePtr next_fragment_node = NULL;
+ unsigned long long next_ts = 0;
+
+ next_fragment_node = fragment_node->next;
+ if (next_fragment_node) {
+ next_ts = ssm_parse_get_xml_prop_uint64 (parser, fragment_node, "t", -1);
+ if (next_ts != -1) {
+ GST_DEBUG ("Next Fragment time = %llu and Current fragment time = %llu\n", next_ts, fragment->time);
+
+ if ( next_ts < fragment->time) {
+ GST_ERROR ("Error in timestamp sequence...");
+ return FALSE;
+ }
+ }
+ }
+ }
+#endif
+
+ stream->stream_duration += fragment->dur;
+
+ stream->fragment_lists = g_list_append (stream->fragment_lists, fragment);
+ GST_DEBUG ("Added fragment node to list...");
+ // TODO: Need to invetigate on TrackFragmentIndex Attribute
+
+ return TRUE;
+}
+
+static gboolean
+ssm_parse_stream_index_node (GstSSMParse *parser, xmlNodePtr stream_node)
+{
+ GstSSMStreamNode *stream = NULL;
+ xmlNodePtr stream_children = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (stream_node != NULL, FALSE);
+
+ stream = g_slice_new0 (GstSSMStreamNode);
+ if (stream == NULL) {
+ GST_WARNING ("Allocation of stream node failed!");
+ return FALSE;
+ }
+
+ stream->frag_lock = g_mutex_new ();
+ stream->frag_cond = g_cond_new ();
+
+ /* Type, Chunks, QualityLevels are MUST attributes */
+ stream->StreamType = (gchar *)xmlGetProp(stream_node, (const xmlChar *) "Type");
+ if (NULL == stream->StreamType) {
+ GST_ERROR ("Type attribute is not present");
+ return FALSE;
+ }
+ GST_DEBUG ("Type = %s", stream->StreamType);
+
+ if (!strncmp ((char *)stream->StreamType, "video", 5))
+ stream->type = SS_STREAM_VIDEO;
+ else if (!strncmp ((char *)stream->StreamType, "audio", 5))
+ stream->type = SS_STREAM_AUDIO;
+ else if (!strncmp ((char *)stream->StreamType, "text", 4))
+ stream->type = SS_STREAM_TEXT;
+ else {
+ GST_ERROR ("UnKnown Stream type...");
+ return FALSE;
+ }
+
+ /* Optional SubType Attribute */
+ stream->StreamSubType = (gchar *)xmlGetProp(stream_node, (const xmlChar *) "Subtype");
+ if (stream->StreamSubType)
+ GST_DEBUG ("StreamSubType = %s", stream->StreamSubType);
+
+ stream->StreamTimeScale = ssm_parse_get_xml_prop_uint64 (parser, stream_node, "TimeScale", parser->RootNode->TimeScale);
+ GST_LOG("StreamTimeScale = %"G_GUINT64_FORMAT, stream->StreamTimeScale);
+
+ /* Optional StreamName Attribute */
+ stream->StreamName = (gchar *)xmlGetProp(stream_node, (const xmlChar *) "Name");
+ if (stream->StreamName)
+ GST_DEBUG ("StreamName = %s", stream->StreamName);
+
+ // TODO: need to understand more on this chunks whether mandatory or not in LIVE case
+ stream->nChunks = ssm_parse_get_xml_prop_uint (parser, stream_node, "Chunks", 0);
+ if (!stream->nChunks && !parser->RootNode->PresentationIsLive) {
+ GST_ERROR ("nChunks is zero in VOD case...ERROR");
+ return FALSE;
+ }
+ GST_DEBUG("nChunks = %d", stream->nChunks);
+
+ stream->nQualityLevels = ssm_parse_get_xml_prop_uint (parser, stream_node, "QualityLevels", 0);
+ GST_DEBUG("nQualityLevels = %d", stream->nQualityLevels);
+
+ stream->StreamUrl = (gchar *)xmlGetProp(stream_node, (const xmlChar *) "Url");
+ if (NULL == stream->StreamUrl) {
+ GST_ERROR ("Url Pattern attribute is not present");
+ return FALSE;
+ }
+ GST_DEBUG ("Url = %s", stream->StreamUrl);
+
+ if (stream->type == SS_STREAM_VIDEO) {
+ /* Video stream specific attributes */
+ stream->MaxWidth = ssm_parse_get_xml_prop_uint (parser, stream_node, "MaxWidth", 0);
+ GST_DEBUG("MaxWidth = %d", stream->MaxWidth);
+
+ stream->MaxHeight = ssm_parse_get_xml_prop_uint (parser, stream_node, "MaxHeight", 0);
+ GST_DEBUG("MaxHeight = %d", stream->MaxHeight);
+
+ stream->DisplayWidth = ssm_parse_get_xml_prop_uint (parser, stream_node, "DisplayWidth", 0);
+ GST_DEBUG("DisplayWidth = %d", stream->DisplayWidth);
+
+ stream->DisplayHeight = ssm_parse_get_xml_prop_uint (parser, stream_node, "DisplayHeight", 0);
+ GST_DEBUG("DisplayHeight = %d", stream->DisplayHeight);
+ }
+
+ /* get the children nodes */
+ stream_children = stream_node->xmlChildrenNode;
+ if (NULL == stream_children) {
+ GST_ERROR ("No Children for StreamIndex Node...\n");
+ return FALSE;
+ }
+
+ /* Parse StreamIndex Children Nodes i.e. QualityLevel */
+ while (stream_children) {
+ if ( xmlIsBlankNode (stream_children)) {/* skip blank nodes */
+ stream_children = stream_children->next;
+ continue;
+ }
+ if (!xmlStrcmp(stream_children->name, (const xmlChar *) "QualityLevel")) {
+ if (!ssm_parse_quality_node (parser, stream, stream_children)) {
+ GST_ERROR ("failed to parse quality node");
+ return FALSE;
+ }
+ } else if (!xmlStrcmp(stream_children->name, (const xmlChar *) "c")) {
+ if (!ssm_parse_fragment_node (parser, stream, stream_children)) {
+ GST_ERROR ("failed to parse fragment node");
+ return FALSE;
+ }
+ } else {
+ GST_WARNING (" ==== >>>>>> Unknow ChildrenNode in StreamNode : %s", stream_children->name);
+ }
+ stream_children = stream_children->next;
+ }
+
+ /* sort the quality lists */
+ if (stream->quality_lists && (g_list_length(stream->quality_lists) > 1)) {
+ stream->quality_lists = g_list_sort (stream->quality_lists, (GCompareFunc) ssm_parser_sort_qualitylevels_by_bitrate);
+ }
+
+ parser->RootNode->streams[stream->type] = g_list_append (parser->RootNode->streams[stream->type], stream);
+
+ return TRUE;
+
+}
+
+
+static gboolean
+ssm_parse_protection_node (GstSSMParse *parser, xmlNodePtr protection_node)
+{
+ xmlChar *xml_string = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (protection_node != NULL, FALSE);
+
+ parser->RootNode->ProtectNode = g_slice_new0 (GstSSMFragmentNode);
+ if (NULL == parser->RootNode->ProtectNode) {
+ GST_ERROR ("Failed to allocate memory...\n");
+ return FALSE;
+ }
+
+ parser->RootNode->ProtectNode->SystemID = NULL;
+ parser->RootNode->ProtectNode->Content = NULL;
+ parser->RootNode->ProtectNode->ContentSize = 0;
+
+ if (!xmlStrcmp(protection_node->name, (const xmlChar *) "ProtectionHeader")) {
+ parser->RootNode->ProtectNode->SystemID = (unsigned char *) xmlGetProp(protection_node, (const xmlChar *) "SystemID");
+ if (NULL == parser->RootNode->ProtectNode->SystemID) {
+ GST_ERROR ("System ID is not present... need to decide ERROR or NOT... returning ERROR now\n");
+ return FALSE;
+ }
+
+ GST_DEBUG ("system ID = %s\n", parser->RootNode->ProtectNode->SystemID);
+
+ if (!strncasecmp ((char *)parser->RootNode->ProtectNode->SystemID,
+ "9A04F079-9840-4286-AB92-E65BE0885F95",
+ 36)) {
+ g_print ("======== >>>>>>>. Content is encrypted using PLAYREADY\n");
+ } else {
+ GST_ERROR ("\n\n ******** UN-supported encrypted content... *********\n\n");
+ return FALSE;
+ }
+
+ xml_string = xmlNodeGetContent(protection_node);
+ if (NULL == xml_string) {
+ GST_ERROR ("Content is not present... need to decide ERROR or NOT\n");
+ return FALSE;
+ } else {
+ gsize content_size = 0;
+
+ g_print ("Content = %s\n", xml_string);
+
+ parser->RootNode->ProtectNode->Content = g_base64_decode ((gchar*)xml_string, &content_size);
+ if (NULL == parser->RootNode->ProtectNode->Content) {
+ GST_ERROR ("Failed to do base64 decoding...\n");
+ free (xml_string);
+ xml_string = NULL;
+ return FALSE;
+ }
+
+ parser->RootNode->ProtectNode->ContentSize = content_size;
+ free (xml_string);
+ xml_string = NULL;
+
+ GST_DEBUG ("ProtectionNode content = %s and size = %d\n", parser->RootNode->ProtectNode->Content, content_size);
+ }
+ } else {
+ GST_ERROR ("ProtectionHeader is NOT PRESENT in Protection node...ERROR\n");
+ return FALSE;
+ }
+
+ GST_LOG ("successfully parsed protectionheader node...");
+ return TRUE;
+}
+
+static gboolean
+ssm_parse_root_node (GstSSMParse *parser, xmlNodePtr root_node)
+{
+ int i = 0;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (root_node != NULL, FALSE);
+
+ parser->RootNode = g_slice_new0 (GstSSMRootNode);
+ if (parser->RootNode == NULL) {
+ GST_WARNING ("Allocation of root node failed!");
+ return FALSE;
+ }
+
+ for (i = 0; i < SS_STREAM_NUM; i++) {
+ parser->RootNode->streams[i] = NULL;
+ }
+
+ parser->RootNode->ProtectNode = NULL;
+
+ /* MANDATORY : parsing MajorVersion attribute */
+ parser->RootNode->MajorVersion = ssm_parse_get_xml_prop_uint (parser, root_node, "MajorVersion", -1);
+ if (parser->RootNode->MajorVersion != 2) {
+ GST_ERROR("Majorversion should be 2");
+ return FALSE;
+ }
+ GST_LOG("SmoothStreamingMedia :: Majorversion = %d", parser->RootNode->MajorVersion);
+
+ /* MANDATORY : parsing MinorVersion attribute */
+ parser->RootNode->MinorVersion = ssm_parse_get_xml_prop_uint (parser, root_node, "MinorVersion", 0);
+ GST_LOG("SmoothStreamingMedia :: MinorVersion = %d", parser->RootNode->MinorVersion);
+
+ parser->RootNode->TimeScale = ssm_parse_get_xml_prop_uint64 (parser, root_node, "TimeScale", 10000000);
+ GST_LOG("SmoothStreamingMedia :: TimeScale = %"G_GUINT64_FORMAT, parser->RootNode->TimeScale);
+
+ parser->RootNode->Duration = ssm_parse_get_xml_prop_uint64 (parser, root_node, "Duration", -1);
+ GST_LOG("SmoothStreamingMedia :: Duration = %"G_GUINT64_FORMAT, parser->RootNode->Duration);
+
+ parser->RootNode->PresentationIsLive = ssm_parse_get_xml_prop_boolean (parser, root_node, "IsLive");
+ GST_LOG("SmoothStreamingMedia :: IsLive = %d", parser->RootNode->PresentationIsLive);
+
+ /* valid for live presentation only*/
+ if (parser->RootNode->PresentationIsLive) {
+
+ parser->RootNode->LookAheadCount = ssm_parse_get_xml_prop_uint (parser, root_node, "LookaheadCount", -1);
+ GST_LOG("SmoothStreamingMedia :: LookaheadCount = %d", parser->RootNode->LookAheadCount);
+
+ if (parser->RootNode->LookAheadCount == -1) {
+ GST_INFO ("fallback case of lookaheadcount...");
+ parser->RootNode->LookAheadCount = ssm_parse_get_xml_prop_uint (parser, root_node, "LookAheadFragmentCount", -1);
+ GST_LOG("SmoothStreamingMedia :: LookAheadFragmentCount = %d", parser->RootNode->LookAheadCount);
+ }
+
+ parser->RootNode->DVRWindowLength = ssm_parse_get_xml_prop_uint64 (parser, root_node, "DVRWindowLength", 0);
+ GST_LOG("SmoothStreamingMedia :: DVRWindowLength = %"G_GUINT64_FORMAT, parser->RootNode->DVRWindowLength);
+ if (parser->RootNode->DVRWindowLength == 0)
+ GST_INFO ("DVR Window Length is zero...means INFINITE");
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+gst_ssm_parse_manifest (GstSSMParse *parser, char *data, unsigned int size)
+{
+ xmlNodePtr root = NULL;
+ xmlNodePtr curnode = NULL;
+ xmlDocPtr doc;
+ int i;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (size != 0, FALSE);
+
+ /* parse manifest xml file */
+ doc = xmlReadMemory ((gchar *) data, size, NULL, NULL, 0);
+ if (doc == NULL ) {
+ GST_ERROR("failed to parse manifest file...");
+ goto error;
+ }
+
+ /* Get the root element */
+ root = xmlDocGetRootElement(doc);
+ if (root == NULL) {
+ GST_ERROR("no ROOT node in manifest file...");
+ goto error;
+ }
+
+ if (!xmlStrcmp(root->name, (const xmlChar *) "SmoothStreamingMedia")) {
+ /* parsing of ROOT Node SmoothStreamingMedia attributes */
+ if (!ssm_parse_root_node (parser, root)) {
+ GST_ERROR("failed to parse root node...");
+ goto error;
+ }
+ GST_DEBUG ("Parsing ROOT element is successful");
+ } else {
+ GST_ERROR ("SmoothStreamingMedia ROOT element is not present...");
+ goto error;
+ }
+
+ /* moving to children nodes */
+ curnode = root->xmlChildrenNode;
+
+ while (curnode) {
+ if (xmlIsBlankNode (curnode)) {/* skip blank node */
+ curnode = curnode->next;
+ continue;
+ }
+
+ if (!xmlStrcmp(curnode->name, (const xmlChar *)"StreamIndex")) {
+ /* Parsing Stream Node */
+ if (!ssm_parse_stream_index_node (parser, curnode)) {
+ GST_ERROR ("failed to parse stream index node...");
+ return FALSE;
+ }
+ } else if (!xmlStrcmp(curnode->name, (const xmlChar *) "Protection")) {
+ xmlNodePtr protect_node;
+
+ /* get the children */
+ protect_node = curnode->xmlChildrenNode;
+ if (NULL == protect_node) {
+ GST_ERROR ("No Children for Protection Node...\n");
+ return FALSE;
+ } else {
+ if (!ssm_parse_protection_node (parser, protect_node)) {
+ GST_ERROR ("failed to parse protectionheader node");
+ return FALSE;
+ }
+ }
+ }
+ curnode = curnode->next;
+ }
+
+ /* Move to last fragment when presentation is LIVE */
+ if (parser->RootNode->PresentationIsLive) {
+ for (i = 0; i < SS_STREAM_NUM; i++) {
+ if (parser->RootNode->streams[i]) {
+ GST_INFO ("Live presentation, so moved to last node...");
+ ((GstSSMStreamNode *)(parser->RootNode->streams[i]->data))->fragment_lists =
+ g_list_last (((GstSSMStreamNode *)(parser->RootNode->streams[i]->data))->fragment_lists);
+ }
+ }
+ }
+
+ parser->ns_start = 0;
+
+ /* get new segment start */
+ for (i = 0; i < SS_STREAM_NUM; i++) {
+ GstSSMStreamNode *stream = NULL;
+ GList *frag_list = NULL;
+ guint64 start_ts = GST_CLOCK_TIME_NONE;
+
+ if (parser->RootNode->streams[i]) {
+ /* Move to last fragment when presentation is LIVE */
+ if (parser->RootNode->PresentationIsLive) {
+ GST_INFO ("Live presentation, so moved to last node...");
+ ((GstSSMStreamNode *)(parser->RootNode->streams[i]->data))->fragment_lists =
+ g_list_last (((GstSSMStreamNode *)(parser->RootNode->streams[i]->data))->fragment_lists);
+ }
+
+ stream = (parser->RootNode->streams[i])->data;
+ frag_list = stream->fragment_lists;
+ start_ts = ((GstSSMFragmentNode *)frag_list->data)->time;
+
+ GST_LOG ("ns_start = %"G_GUINT64_FORMAT" and start_ts[%s] = %"G_GUINT64_FORMAT,
+ parser->ns_start, ssm_parse_get_stream_name(i), start_ts);
+
+ /* take MAX of stream's start as new_segment start */
+ parser->ns_start = START_TS_MAX (parser->ns_start, start_ts);
+ }
+ }
+
+ GST_INFO ("ns_start = %"G_GUINT64_FORMAT, parser->ns_start);
+
+ if (doc) {
+ xmlFreeDoc(doc);
+ doc = NULL;
+ }
+
+ GST_DEBUG ("successfully parsed manifest");
+
+ return TRUE;
+
+error:
+ if (doc) {
+ xmlFreeDoc(doc);
+ doc = NULL;
+ }
+
+ return FALSE;
+}
+
+/* Only supporting url in 'QualityLevels({bitrate})/Fragments(xxxxxx={start time})'
+ * FIXME : Add support for any */
+gboolean
+gst_ssm_parse_get_next_fragment_url (GstSSMParse *parser, SS_STREAM_TYPE stream_type, gchar **uri, guint64 *start_ts)
+{
+ GstSSMStreamNode *stream = NULL;
+ GList *frag_list = NULL;
+ GPtrArray *strs;
+ guint64 time = 0;
+ gchar **splitter1 = NULL;
+ gchar **splitter2 = NULL;
+ gchar **splitter3 = NULL;
+
+ MANIFEST_LOCK(parser);
+ stream = (parser->RootNode->streams[stream_type])->data; //get current video stream
+ g_mutex_lock (stream->frag_lock);
+
+ if (stream->fraglist_eos) {
+ if (parser->RootNode->PresentationIsLive) {
+ /* Live Presentation need to wait for next uri */
+ g_print ("waiting for next URI in LIVE presentation...\n");
+ g_cond_wait (stream->frag_cond, stream->frag_lock);
+ g_print ("Received signal after appending new URI...move to next now\n");
+ ((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists =
+ g_list_next (((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists);
+ } else {
+ /* VOD presentation reached EOS */
+ GST_INFO("Fragment list is empty in VOD case...");
+ g_mutex_unlock (stream->frag_lock);
+ MANIFEST_UNLOCK(parser);
+ return FALSE;
+ }
+ }
+
+ stream = (parser->RootNode->streams[stream_type])->data; //get current video stream
+
+ frag_list = stream->fragment_lists;
+
+ strs = g_ptr_array_new ();
+
+ /* adding presentation uri */
+ g_ptr_array_add (strs, g_strdup(parser->presentation_uri));
+
+ splitter1 = g_strsplit (stream->StreamUrl, "{", 3);
+
+ /* add stream url till first '{' */
+ g_ptr_array_add (strs, g_strdup(splitter1[0]));
+
+ /* adding bitrate param */
+ g_ptr_array_add (strs, g_strdup_printf ("%d", ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate));
+
+ /* tokenize based on '}' */
+ splitter2 = g_strsplit (splitter1[1], "}", 2);
+
+ g_ptr_array_add (strs, g_strdup(splitter2[1]));
+
+ time = ((GstSSMFragmentNode *)frag_list->data)->time;
+
+ /* adding fragment time */
+ g_ptr_array_add (strs, g_strdup_printf ("%llu", time));
+
+ *start_ts = gst_util_uint64_scale (time, GST_SECOND, parser->RootNode->TimeScale);
+
+ /* tokenize based on '}' */
+ splitter3 = g_strsplit (splitter1[2], "}", 2);
+
+ g_ptr_array_add (strs, g_strdup(splitter3[1]));
+
+ /* add a terminating NULL */
+ g_ptr_array_add (strs, NULL);
+
+ if (frag_list = g_list_next (((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists)) {
+ ((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists = frag_list;
+ } else {
+ GST_INFO ("Reached end of fragment list...\n");
+ ((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fraglist_eos = TRUE;
+ }
+
+ *uri = g_strjoinv (NULL, (gchar **) strs->pdata);
+ g_strfreev ((gchar **) g_ptr_array_free (strs, FALSE));
+
+ g_mutex_unlock (stream->frag_lock);
+
+ MANIFEST_UNLOCK(parser);
+
+ return TRUE;
+}
+
+GstCaps *
+ssm_parse_get_stream_caps (GstSSMParse *parser, SS_STREAM_TYPE stream_type)
+{
+ GstSSMStreamNode *stream = NULL;
+ MANIFEST_LOCK(parser);
+
+ stream = parser->RootNode->streams[stream_type]->data;
+ GstCaps *caps = NULL;
+ if (stream_type == SS_STREAM_VIDEO) {
+ caps = ssm_prepare_video_caps (parser, stream);
+ } else if (stream_type == SS_STREAM_AUDIO) {
+ caps = ssm_prepare_audio_caps (parser, stream);
+ } else if (stream_type == SS_STREAM_TEXT) {
+ caps = ssm_prepare_text_caps (parser, stream);
+ }
+ MANIFEST_UNLOCK(parser);
+ return caps;
+}
+
+GstCaps *
+ssm_prepare_video_caps (GstSSMParse *parser, GstSSMStreamNode *stream)
+{
+ GstSSMQualityNode *cur_quality_node = (GstSSMQualityNode *)(stream->quality_lists->data);
+ GstBuffer *codec_data = NULL;
+ GstCaps *caps = NULL;
+
+ codec_data = gst_buffer_new ();
+ if (!codec_data) {
+ GST_ERROR ("failed to allocate buffer");
+ return NULL;
+ }
+
+ if (!strncmp ((char *)cur_quality_node->fourcc, "H264", 4)) {
+ /* converting NALU codec data to 3GPP codec data format */
+ if (!convert_NALUnitDCI_to_PacktizedDCI (cur_quality_node->codec_data, &(GST_BUFFER_DATA(codec_data)), &GST_BUFFER_SIZE(codec_data))) {
+ GST_ERROR ("Error in converting NALUDCI to Packetized DCI...\n");
+ return NULL;
+ }
+ GST_BUFFER_MALLOCDATA(codec_data) = GST_BUFFER_DATA(codec_data);
+ } else if (!strncmp ((char *)cur_quality_node->fourcc, "WVC1", 4)) {
+ unsigned char *codec_data = cur_quality_node->codec_data;
+ unsigned int DCI_len = strlen ((char *)cur_quality_node->codec_data);
+ gchar tmp[3] = {0, };
+ gint val = 0;
+ gint codec_data_len = 0;
+ gint idx = 0;
+
+ codec_data_len = (DCI_len >>1);
+
+ codec_data = gst_buffer_new_and_alloc (codec_data_len);
+ if (!codec_data) {
+ GST_ERROR ("Failed to allocate memory..\n");
+ return FALSE;
+ }
+
+ /* copy codec data */
+ while (DCI_len) {
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)codec_data, 2);
+ tmp[2] = '\0';
+ if (!int_from_string ((char*)tmp, NULL, &val, 16)) {
+ GST_ERROR ("Failed to int from string...");
+ return NULL;
+ }
+ (GST_BUFFER_DATA(codec_data))[idx] = val;
+ codec_data += 2;
+ DCI_len = DCI_len - 2;
+ idx++;
+ }
+ }
+
+ if (!strncmp ((char *)cur_quality_node->fourcc, "H264", 4)) {
+ /* prepare H264 caps */
+ caps = gst_caps_new_simple ("video/x-h264",
+ "width", G_TYPE_INT, cur_quality_node->max_width,
+ "height", G_TYPE_INT, cur_quality_node->max_height,
+ "framerate", GST_TYPE_FRACTION, 30, 1,
+ "stream-format", G_TYPE_STRING, "avc",
+ "alignment", G_TYPE_STRING, "au",
+ "codec_data", GST_TYPE_BUFFER, codec_data,
+ NULL);
+ } else if (!strncmp ((char *)cur_quality_node->fourcc, "WVC1", 4)) {
+ /* prepare VC1 caps */
+ caps = gst_caps_new_simple ("video/x-wmv",
+ "width", G_TYPE_INT, cur_quality_node->max_width,
+ "height", G_TYPE_INT, cur_quality_node->max_height,
+ "framerate", GST_TYPE_FRACTION, 30, 1,
+ "wmvversion", G_TYPE_INT, 3,
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
+ "codec_data", GST_TYPE_BUFFER, codec_data,
+ NULL);
+ } else {
+ char *s;
+ /* prepare gst generic caps caps */
+ GST_ERROR ("Wrong VIDEO fourcc...");
+ s = g_strdup_printf ("video/x-gst-fourcc-%s", "unknown");
+ caps = gst_caps_new_simple (s,
+ "width", G_TYPE_INT, cur_quality_node->max_width,
+ "height", G_TYPE_INT, cur_quality_node->max_height,
+ "framerate", GST_TYPE_FRACTION, 30, 1,
+ "codec_data", GST_TYPE_BUFFER, codec_data,
+ NULL);
+ }
+
+ GST_INFO ( "prepared video caps : %s\n", gst_caps_to_string(caps));
+
+ return caps;
+}
+
+GstCaps *
+ssm_prepare_audio_caps (GstSSMParse *parser, GstSSMStreamNode *stream)
+{
+ GstSSMQualityNode *cur_quality_node = (GstSSMQualityNode *)(stream->quality_lists->data);
+ GstBuffer *codec_data = NULL;
+ GstCaps *caps = NULL;
+
+ if ((!strncmp ((char *)cur_quality_node->fourcc, "AACL", 4)) || !strncmp ((char *)cur_quality_node->fourcc, "WMAP", 4)) {
+ guint DCI_len = strlen ((char *)cur_quality_node->codec_data);
+ gchar tmp[3] = {0, };
+ gint val = 0;
+ gint codec_data_len = (DCI_len >>1);
+ gint idx = 0;
+
+ codec_data = gst_buffer_new_and_alloc (codec_data_len);
+ if (NULL == codec_data) {
+ GST_ERROR ("Failed to allocate memory..\n");
+ return NULL;
+ }
+
+ /* copy codec data */
+ while (DCI_len) {
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)cur_quality_node->codec_data, 2);
+ tmp[2] = '\0';
+ if (!int_from_string ((char*)tmp, NULL, &val , 16)) {
+ GST_ERROR ("Failed to int from string...");
+ return NULL;
+ }
+ (GST_BUFFER_DATA(codec_data))[idx] = val;
+ cur_quality_node->codec_data += 2;
+ DCI_len = DCI_len - 2;
+ //g_print ("val = 0x%02x, codec_data_length = %d, idx = %d\n", val, DCI_len, idx);
+ idx++;
+ }
+ } else {
+ GST_ERROR ("\n\n\nUnsupported Audio Codec Fourcc...\n\n\n");
+ return NULL;
+ }
+
+ if (!strncmp ((char *)cur_quality_node->fourcc, "AACL", 4)) {
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 4,
+ "framed", G_TYPE_BOOLEAN, TRUE,
+ "stream-format", G_TYPE_STRING, "raw",
+ "rate", G_TYPE_INT, (int) cur_quality_node->samplingrate,
+ "channels", G_TYPE_INT, cur_quality_node->channels,
+ NULL);
+ } else if (!strncmp ((char *)cur_quality_node->fourcc, "WMAP", 4)) {
+ caps = gst_caps_new_simple ("audio/x-wma",
+ "rate", G_TYPE_INT, (int) cur_quality_node->samplingrate,
+ "channels", G_TYPE_INT, cur_quality_node->channels,
+ NULL);
+ } else {
+ char *s;
+
+ GST_ERROR ("Wrong Audio fourcc...");
+ s = g_strdup_printf ("audio/x-gst-fourcc-%s", "unknown");
+ caps = gst_caps_new_simple (s,
+ "rate", G_TYPE_INT, (int) cur_quality_node->samplingrate,
+ "channels", G_TYPE_INT, cur_quality_node->channels,
+ NULL);
+ }
+ GST_INFO ( "prepared video caps : %s\n", gst_caps_to_string(caps));
+
+ return caps;
+}
+
+GstCaps *
+ssm_prepare_text_caps (GstSSMParse *parser, GstSSMStreamNode *stream)
+{
+ // TODO: Yet to add support for text caps
+ return NULL;
+}
+
+SS_BW_MODE
+gst_ssm_parse_switch_qualitylevel (GstSSMParse *parser, guint drate)
+{
+ GstSSMStreamNode *stream = parser->RootNode->streams[SS_STREAM_VIDEO]->data;
+ guint bitrate = ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate;
+ SS_BW_MODE ret = SS_MODE_NO_SWITCH;
+
+ /* check for upward transition */
+ while (drate > (bitrate + BITRATE_SWITCH_UPPER_THRESHOLD * bitrate)) {
+ if (g_list_next (stream->quality_lists)) {
+ stream->quality_lists = g_list_next (stream->quality_lists);
+ g_print ("Move to next quality level : drate = %d and bitrate = %d\n", drate, ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate);
+ ret = SS_MODE_AV;
+ } else {
+ g_print ("Already at MAX Bitrate possible...\n");
+ ret = SS_MODE_NO_SWITCH;
+ break;
+ }
+ }
+
+ /* check for downward transition */
+ while (drate < (bitrate + BITRATE_SWITCH_LOWER_THRESHOLD * bitrate)) {
+ if (g_list_previous (stream->quality_lists)) {
+ stream->quality_lists = g_list_previous (stream->quality_lists);
+ g_print ("Move to previous quality level : drate = %d and bitrate = %d\n", drate, ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate);
+ } else {
+ g_print ("Reached MIN video bitrate possible...\n");
+ if (GST_SSM_PARSE_IS_LIVE_PRESENTATION(parser)) {
+ g_print ("Going to audio-only because of LIVE...\n");
+ ret = SS_MODE_AONLY;
+ } else {
+ ret = SS_MODE_AV;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+gboolean
+gst_ssm_parse_append_next_fragment (GstSSMParse *parser, SS_STREAM_TYPE stream_type, guint64 timestamp, guint64 duration)
+{
+ GstSSMStreamNode *stream = NULL;
+ GstSSMFragmentNode *new_fragment = NULL;
+ GstSSMFragmentNode *last_fragment = NULL;
+ GList *last_node = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+
+ /*get current stream based on stream_type */
+ stream = (parser->RootNode->streams[stream_type])->data;
+
+ g_return_val_if_fail (stream->fragment_lists, FALSE);
+
+ g_mutex_lock (stream->frag_lock);
+
+ last_node = g_list_last(stream->fragment_lists);
+
+ last_fragment = (GstSSMFragmentNode *)(last_node->data);
+
+ if (last_fragment->time < timestamp) {
+
+ GST_LOG ("+++++ last_fragment time = %llu and current recd = %llu +++++\n", last_fragment->time, timestamp);
+
+ /* allocate new fragment node */
+ new_fragment = g_slice_new0 (GstSSMFragmentNode);
+ if (new_fragment == NULL) {
+ GST_ERROR ("Allocation of fragment failed!");
+ return FALSE;
+ }
+
+ if (duration == GST_CLOCK_TIME_NONE) {
+ /* useful when lookahead count is zero */
+ // TODO: need to check how to handle, when there is discontinuity
+ duration = timestamp - last_fragment->time;
+ }
+
+ // TODO: need to handle when either time or duration present OR if both are non proper values
+ new_fragment->dur = duration;
+ new_fragment->time = timestamp;
+ new_fragment->num = 0;
+
+ /* add the new fragment duration to total stream duration */
+ stream->stream_duration += duration;
+
+ /* append new fragment list to stream fragment list */
+ ((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists =
+ g_list_append (((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists, new_fragment);
+
+ GST_DEBUG ("+++++ Appened new '%s' URL and signaling the condition and duration = %llu ++++++\n",
+ ssm_parse_get_stream_name(stream_type), stream->stream_duration);
+
+ if (stream->fraglist_eos) {
+ GST_INFO ("Need to move the list now to next.. as we received new one\n");
+ ((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists =
+ g_list_next (((GstSSMStreamNode *)(parser->RootNode->streams[stream_type]->data))->fragment_lists);
+ stream->fraglist_eos = FALSE;
+ }
+ /* signal fragment wait */
+ g_cond_signal (stream->frag_cond);
+
+ } else {
+ g_print ("--------- Received '%s' fragment is less than Last fragment in the list -----------\n", ssm_parse_get_stream_name(stream_type));
+ }
+
+ g_mutex_unlock (stream->frag_lock);
+
+ return TRUE;
+}
+
+gboolean
+gst_ssm_parse_seek_manifest (GstSSMParse *parser, guint64 seek_time)
+{
+ gint i = 0;
+ GstSSMStreamNode *stream = NULL;
+ guint64 stream_time = -1;
+ guint64 start_ts = -1;
+
+ parser->ns_start = 0;
+
+ for (i = 0; i < SS_STREAM_NUM; i++) {
+ if (parser->RootNode->streams[i]) {
+ stream = parser->RootNode->streams[i]->data; // get current stream
+ stream_time = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
+ stream_time = gst_util_uint64_scale (stream_time, GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(parser));
+
+ if (seek_time > stream_time) {
+ /* forward seek */
+ while (seek_time > stream_time) {
+ stream->fragment_lists = g_list_next (stream->fragment_lists);
+ stream_time = gst_util_uint64_scale (((GstSSMFragmentNode *)stream->fragment_lists->data)->time, GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(parser));
+ GST_LOG ("seek time = %"GST_TIME_FORMAT", cur_time = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(seek_time), GST_TIME_ARGS(stream_time));
+ }
+
+ /* moving to fragment before our seeked time */
+ stream->fragment_lists = g_list_previous (stream->fragment_lists);
+ start_ts = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
+ } else if (seek_time < stream_time) {
+ /* backward seek */
+ while (seek_time < stream_time) {
+ stream->fragment_lists = g_list_previous (stream->fragment_lists);
+ stream_time = gst_util_uint64_scale (((GstSSMFragmentNode *)stream->fragment_lists->data)->time, GST_SECOND,
+ GST_SSM_PARSE_GET_TIMESCALE(parser));
+ GST_LOG ("seek time = %"GST_TIME_FORMAT", cur_time = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(seek_time), GST_TIME_ARGS(stream_time));
+ }
+ start_ts = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
+ } else {
+ /* rare case */
+ start_ts = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
+ }
+
+ if (stream->type == SS_STREAM_VIDEO) {
+ /* move to least possible bitrate variant*/
+ stream->quality_lists = g_list_first (stream->quality_lists);
+ }
+
+ GST_INFO ("SEEK : ns_start = %"G_GUINT64_FORMAT" and start_ts[%s] = %"G_GUINT64_FORMAT,
+ parser->ns_start, ssm_parse_get_stream_name(i), start_ts);
+
+ /* take max of stream's start as new_segment start */
+ parser->ns_start = START_TS_MAX (parser->ns_start, start_ts);
+ }
+ }
+
+ GST_INFO ("ns_start = %"G_GUINT64_FORMAT, parser->ns_start);
+
+ return TRUE;
+}
+
+static gboolean
+convert_NALUnitDCI_to_PacktizedDCI (unsigned char *nalu_dci, unsigned char **packetized_dci, unsigned int *packetized_dci_len)
+{
+#ifndef CHECK_NALU_OUT
+ gboolean bret = TRUE;
+ unsigned char *pps_start = NULL;
+ unsigned char *pps_end = NULL;
+ unsigned int sps_len = 0;
+ unsigned int pps_len = 0;
+ unsigned char *sps_data = NULL;
+ unsigned char *pps_data = NULL;
+ unsigned char *tmp_sps_ptr = NULL;
+ unsigned char *tmp_pps_ptr = NULL;
+ unsigned char tmp[3] = {0, };
+ int val;
+ unsigned int idx = 0;
+
+ // TODO: need to generalize this logic for finding multiple SPS and PPS sets and finding SPS/PPS by ANDing 0x1F
+
+ pps_start = strstr ((char*)nalu_dci, "0000000168");
+ pps_end = nalu_dci + strlen ((char *)nalu_dci);
+ GST_DEBUG ("nalu_dci length= %d\n", strlen ((char *)nalu_dci));
+
+ sps_len = pps_start - nalu_dci;
+
+ GST_DEBUG ("sps length = %d\n", sps_len);
+
+ sps_data = (unsigned char*) malloc (sps_len + 1);
+ if (NULL == sps_data)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ /* Copy SPS data */
+ strncpy ((char*)sps_data, (char *)nalu_dci, sps_len);
+ sps_data[sps_len] = '\0';
+
+ tmp_sps_ptr = sps_data;
+
+ GST_DEBUG ("SPS data = %s\n", sps_data);
+
+ pps_len = pps_end - pps_start;
+ GST_DEBUG ("pps length = %d\n", pps_len);
+
+ pps_data = (unsigned char*)malloc (pps_len + 1);
+ if (NULL == pps_data)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ /* Copy PPS data */
+ strncpy ((char*)pps_data, (char*)pps_start, pps_len);
+ pps_data[pps_len] = '\0';
+ tmp_pps_ptr = pps_data;
+
+ GST_DEBUG ("PPS data = %s\n", pps_data);
+
+ /* 6 bytes of metadata */
+ *packetized_dci_len = 8;
+
+ *packetized_dci = (unsigned char*) malloc (*packetized_dci_len);
+ if (NULL == *packetized_dci)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ /* configurationVersion */
+ (*packetized_dci)[0] = 0x01;
+
+ /* AVCProfileIndication */
+ memset (tmp, 0x00, 3);
+
+ strncpy ((char*)tmp, (char*)sps_data+10, 2);
+ tmp[2] = '\0';
+
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+
+ (*packetized_dci)[1] = val;
+
+ /* profile_compatibility*/
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)sps_data+12, 2);
+ tmp[2] = '\0';
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+ (*packetized_dci)[2] = val;
+
+ /* AVCLevelIndication */
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)sps_data+14, 2);
+ tmp[2] = '\0';
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+
+ (*packetized_dci)[3] = val;
+
+ /* Reserver (6) + LengthSizeMinusOne (2) */
+ (*packetized_dci)[4] = 0xff; // hardcoding lengthoflength = 4 for present
+
+ /* Reserver (3) + numOfSequenceParameterSets (5) */
+ (*packetized_dci)[5] = 0xe1; // hardcoding numOfSequenceParameterSets = 1 for present
+
+ /* avoiding NALU start code 0x00 00 00 01 */
+ sps_len = sps_len -8;
+ sps_data = sps_data + 8;
+
+ (*packetized_dci)[6] = (sps_len >>1) >> 16;
+ (*packetized_dci)[7] = (sps_len >>1);
+
+ *packetized_dci_len += (sps_len/2);
+
+ *packetized_dci = (unsigned char*) realloc (*packetized_dci, *packetized_dci_len);
+ if (NULL == *packetized_dci)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ idx = 8;
+
+ /* convert SPS data and store*/
+ while (sps_len)
+ {
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)sps_data, 2);
+ tmp[2] = '\0';
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+ (*packetized_dci)[idx] = val;
+ sps_data += 2;
+ sps_len = sps_len - 2;
+ //g_print ("val = 0x%02x, sps_len = %d, idx = %d\n", val, sps_len, idx);
+ idx++;
+ }
+
+ /* avoiding NALU start code 0x00 00 00 01 */
+ pps_len = pps_len -8;
+ pps_data = pps_data + 8;
+
+ /* no.of PPS sets (1 byte) + Lengthof PPS (2 bytes) + PPS length */
+ *packetized_dci_len = *packetized_dci_len + 1 + 2 + (pps_len/2) ;
+ *packetized_dci = (unsigned char*)realloc (*packetized_dci, *packetized_dci_len);
+ if (NULL == *packetized_dci)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ (*packetized_dci)[idx++] = 0x01; // Harding no.of PPS sets
+ (*packetized_dci)[idx++] = (pps_len>>1) >> 16;
+ (*packetized_dci)[idx++] = (pps_len >>1);
+
+ /* convert PPS data and store */
+ while (pps_len)
+ {
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)pps_data, 2);
+ tmp[2] = '\0';
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+
+ (*packetized_dci)[idx] = val;
+ pps_data += 2;
+ pps_len = pps_len - 2;
+ //g_print ("val = 0x%02x, pps_len = %d, idx = %d\n", val, pps_len, idx);
+ idx++;
+ }
+ idx = 0;
+
+#if 0
+ g_print ("\n\n");
+
+ g_print ("Complete VIDEO packetized_dci: 0x");
+ while (idx < *packetized_dci_len)
+ {
+ g_print ("%02x", (*packetized_dci)[idx]);
+ idx++;
+ }
+
+ g_print ("\n\n");
+#endif
+
+exit:
+ if (tmp_sps_ptr)
+ {
+ free (tmp_sps_ptr);
+ tmp_sps_ptr = NULL;
+ }
+ if (tmp_pps_ptr)
+ {
+ free (tmp_pps_ptr);
+ tmp_pps_ptr = NULL;
+ }
+ if ((FALSE == bret) && *packetized_dci)
+ {
+ free (*packetized_dci);
+ *packetized_dci = NULL;
+ }
+
+ return bret;
+
+#else
+ /* code for sending NALU DCI as it is */
+ guint nalu_dci_len = strlen ((char *)nalu_dci);
+ unsigned char *nalu = nalu_dci;
+ guint idx = 0;
+ unsigned char tmp[3] = {0, };
+ gboolean bret = TRUE;
+ int val = 0;
+
+ *packetized_dci_len = (nalu_dci_len/2);
+
+ *packetized_dci = (unsigned char*) malloc (*packetized_dci_len);
+ if (NULL == *packetized_dci)
+ {
+ GST_ERROR ("Failed to allocate memory..\n");
+ bret = FALSE;
+ goto exit;
+ }
+
+ /* copy entire DCI*/
+ while (nalu_dci_len)
+ {
+ memset (tmp, 0x00, 3);
+ strncpy ((char*)tmp, (char*)nalu, 2);
+ tmp[2] = '\0';
+ bret = int_from_string ((char*)tmp, NULL, &val , 16);
+ if (FALSE == bret)
+ {
+ GST_ERROR ("Failed to int from string...");
+ goto exit;
+ }
+ (*packetized_dci)[idx] = val;
+ nalu += 2;
+ nalu_dci_len = nalu_dci_len - 2;
+ //g_print ("val = 0x%02x, dci_len = %d, idx = %d\n", val, nalu_dci_len, idx);
+ idx++;
+ }
+
+ idx = 0;
+
+ g_print ("\n\n NEW DCI : 0x ");
+
+ while (idx < *packetized_dci_len)
+ {
+ g_print ("%02x", (*packetized_dci)[idx] );
+ idx++;
+ }
+
+ g_print ("\n\n\n");
+
+exit:
+
+ if ((FALSE == bret) && *packetized_dci)
+ {
+ free (*packetized_dci);
+ *packetized_dci = NULL;
+ }
+
+ return bret;
+#endif
+}
+
--- /dev/null
+
+
+#ifndef __SS_MANIFEST_PARSE_H__
+#define __SS_MANIFEST_PARSE_H__
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <libxml2/libxml/tree.h>
+
+G_BEGIN_DECLS
+typedef struct _GstSSMParse GstSSMParse;
+
+#define GST_SSMPARESE(m) ((GstSSMParse*)m)
+#define XML_MAKE_FOURCC(a,b,c,d) ((guint32)((a)|(b)<<8|(c)<<16|(d)<<24))
+#define BITRATE_SWITCH_UPPER_THRESHOLD 0.4
+#define BITRATE_SWITCH_LOWER_THRESHOLD 0.1
+
+
+typedef enum
+{
+ SS_STREAM_UNKNOWN = -1,
+ SS_STREAM_VIDEO = 0,
+ SS_STREAM_AUDIO,
+ SS_STREAM_TEXT,
+ SS_STREAM_NUM,
+}SS_STREAM_TYPE;
+
+typedef enum
+{
+ SS_MODE_NO_SWITCH, /* no switch is need */
+ SS_MODE_AONLY, /* switch to audio only */
+ SS_MODE_AV,
+}SS_BW_MODE;
+
+typedef struct
+{
+ guint64 StreamTimeScale;
+ guint64 stream_duration;
+ SS_STREAM_TYPE type;
+ guint nChunks;
+ guint nQualityLevels;
+ guint MaxWidth;
+ guint MaxHeight;
+ guint DisplayWidth;
+ guint DisplayHeight;
+ GList *quality_lists;
+ GList *fragment_lists;
+ gchar *StreamType;
+ gchar *StreamUrl;
+ gchar *StreamSubType;
+ gchar *StreamName;
+ gboolean fraglist_eos;
+ GMutex *frag_lock;
+ GCond *frag_cond;
+}GstSSMStreamNode;
+
+typedef struct
+{
+ guint index;
+ guint bitrate;
+ guint max_width;
+ guint max_height;
+ guint samplingrate;
+ guint channels;
+ guint bps; /* bits per sample */
+ guint packet_size;
+ guint audio_tag;
+ guint NALULengthofLength;
+ gchar *fourcc;
+ gchar *codec_data;
+}GstSSMQualityNode;
+
+typedef struct
+{
+ guint num;
+ guint64 dur;
+ guint64 time;
+ guint media_type;
+}GstSSMFragmentNode;
+
+typedef struct
+{
+ gchar *SystemID;
+ gchar *Content;
+ guint ContentSize;
+}GstSSMProtectionNode;
+
+typedef struct
+{
+ guint MajorVersion;
+ guint MinorVersion;
+ guint64 TimeScale;
+ guint64 Duration;
+ guint LookAheadCount;
+ guint64 DVRWindowLength;
+ gboolean PresentationIsLive;
+ GList *streams[SS_STREAM_NUM];
+ GstSSMProtectionNode *ProtectNode;
+}GstSSMRootNode;
+
+struct _GstSSMParse
+{
+ gchar *uri; /* manifest URI */
+ gchar *presentation_uri;
+ GstSSMRootNode *RootNode;
+ GMutex *lock;
+ guint64 ns_start;
+};
+
+#define gst_ssm_parse_check_stream(parser, stream_type) (parser->RootNode->streams[stream_type])
+#define GST_SSM_PARSE_GET_DURATION(parser) (parser->RootNode->Duration)
+#define GST_SSM_PARSE_GET_TIMESCALE(parser) (parser->RootNode->TimeScale)
+#define GST_SSM_PARSE_IS_LIVE_PRESENTATION(parser) (parser->RootNode->PresentationIsLive)
+#define GST_SSM_PARSE_LOOKAHEAD_COUNT(parser) (parser->RootNode->LookAheadCount)
+#define GST_SSM_PARSE_NS_START(parser) (parser->ns_start)
+
+const gchar *ssm_parse_get_stream_name(SS_STREAM_TYPE type);
+GstSSMParse *gst_ssm_parse_new (const gchar * uri);
+void gst_ssm_parse_free (GstSSMParse *parser);
+gboolean gst_ssm_parse_manifest (GstSSMParse *parser, char *data, unsigned int size);
+gboolean gst_ssm_parse_get_next_fragment_url (GstSSMParse *parser, SS_STREAM_TYPE stream_type, gchar **uri, guint64 *start_ts);
+gboolean gst_ssm_parse_append_next_fragment (GstSSMParse *parser, SS_STREAM_TYPE stream_type, guint64 timestamp, guint64 duration);
+GstCaps *ssm_parse_get_stream_caps (GstSSMParse *parser, SS_STREAM_TYPE stream_type);
+SS_BW_MODE
+gst_ssm_parse_switch_qualitylevel (GstSSMParse *parser, guint drate);
+gboolean gst_ssm_parse_seek_manifest (GstSSMParse *parser, guint64 seek_time);
+G_END_DECLS
+#endif /* __SS_MANIFEST_PARSE_H__ */
+