SUBDIRS = common
-if GST_EXT_USE_EXT_ENCODEBIN
-SUBDIRS += encodebin
+if GST_EXT_USE_EXT_PIFFDEMUX
+SUBDIRS += piffdemux
endif
-if GST_EXT_USE_EXT_AVSYSTEM
-SUBDIRS += avsystem
+if GST_EXT_USE_EXT_ENCODEBIN
+SUBDIRS += encodebin
endif
if GST_EXT_USE_EXT_EVASIMAGESINK
SUBDIRS += evasimagesink
endif
+if GST_EXT_USE_EXT_EVASPIXMAPSINK
+SUBDIRS += evaspixmapsink
+endif
+
+if GST_EXT_USE_EXT_XVIMAGESRC
+SUBDIRS += xvimagesrc
+endif
+
if GST_EXT_USE_EXT_DRMSRC
SUBDIRS += drmsrc
endif
-
+if GST_EXT_USE_EXT_SUBMUX
+SUBDIRS += submux
+endif
if GST_EXT_USE_EXT_TOGGLE
SUBDIRS += toggle
endif
-if GST_EXT_USE_EXT_AVSYSTEM
+if GST_EXT_USE_EXT_PD_PUSHSRC
SUBDIRS += pdpushsrc
endif
SUBDIRS += audiotp
endif
+if GST_EXT_USE_EXT_AUDIOEQ
+SUBDIRS += audioeq
+endif
+
if GST_EXT_USE_EXT_SSDEMUX
-#SUBDIRS += ssdemux
+SUBDIRS += ssdemux
endif
+if GST_EXT_USE_EXT_DASHDEMUX
+SUBDIRS += dashdemux
+endif
+if GST_EXT_USE_EXT_HLSDEMUX2
+SUBDIRS += hlsdemux2
+endif
DIST_SUBDIRS = common
-if GST_EXT_USE_EXT_ENCODEBIN
-DIST_SUBDIRS += encodebin
+if GST_EXT_USE_EXT_PIFFDEMUX
+DIST_SUBDIRS += piffdemux
endif
-if GST_EXT_USE_EXT_AVSYSTEM
-DIST_SUBDIRS += avsystem
+if GST_EXT_USE_EXT_ENCODEBIN
+DIST_SUBDIRS += encodebin
endif
if GST_EXT_USE_EXT_EVASIMAGESINK
DIST_SUBDIRS += evasimagesink
endif
+if GST_EXT_USE_EXT_XVIMAGESRC
+DIST_SUBDIRS += xvimagesrc
+endif
+
if GST_EXT_USE_EXT_DRMSRC
DIST_SUBDIRS += drmsrc
endif
-
+if GST_EXT_USE_EXT_SUBMUX
+DIST_SUBDIRS += submux
+endif
if GST_EXT_USE_EXT_TOGGLE
DIST_SUBDIRS += toggle
endif
if GST_EXT_USE_EXT_SSDEMUX
-#DIST_SUBDIRS += ssdemux
+DIST_SUBDIRS += ssdemux
endif
+if GST_EXT_USE_EXT_DASHDEMUX
+DIST_SUBDIRS += dashdemux
+endif
+if GST_EXT_USE_EXT_HLSDEMUX2
+DIST_SUBDIRS += hlsdemux2
+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 = libgstaudioeq.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
+libgstaudioeq_la_SOURCES = gstaudioeq.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstaudioeq_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS)
+libgstaudioeq_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(LIBM) -lgstbase-0.10 -lglib-2.0 -lgstcontroller-0.10
+libgstaudioeq_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+libgstequalizer_la_LIBTOOLFLAGS = --tag=disable-static
+
+# headers we need but don't want installed
+noinst_HEADERS = gstaudioeq.h
--- /dev/null
+/*
+ * audioeq
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Aditi Narula <aditi.n@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 "gstaudioeq.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_audioeq_debug);
+#define GST_CAT_DEFAULT gst_audioeq_debug
+
+#define AUDIOEQ_ENABLE_DUMP
+#define AUDIOEQ_REDUCE_MEMCPY
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_FILTER_ACTION,
+ PROP_CUSTOM_EQ,
+ PROP_CUSTOM_EQ_NUM,
+ PROP_CUSTOM_EQ_FREQ,
+ PROP_CUSTOM_EQ_WIDTH,
+};
+
+enum FilterActionType
+{
+ FILTER_NONE,
+ FILTER_PRESET,
+ FILTER_ADVANCED_SETTING
+};
+
+enum SampleRate
+{
+ SAMPLERATE_48000Hz,
+ SAMPLERATE_44100Hz,
+ SAMPLERATE_32000Hz,
+ SAMPLERATE_24000Hz,
+ SAMPLERATE_22050Hz,
+ SAMPLERATE_16000Hz,
+ SAMPLERATE_12000Hz,
+ SAMPLERATE_11025Hz,
+ SAMPLERATE_8000Hz,
+
+ SAMPLERATE_NUM
+};
+
+#define DEFAULT_SAMPLE_SIZE 2
+#define DEFAULT_VOLUME 15
+#define DEFAULT_GAIN 1
+#define DEFAULT_SAMPLE_RATE SAMPLERATE_44100Hz
+#define DEAFULT_CHANNELS 2
+#define DEFAULT_FILTER_ACTION FILTER_NONE
+#define DEFAULT_CUSTOM_EQ_NUM 7
+
+static GstStaticPadTemplate sinktemplate =
+ GST_STATIC_PAD_TEMPLATE(
+ "sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (
+ "audio/x-raw-int, "
+ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+ "signed = (boolean) true, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "channels = (int) [1,2]"
+ )
+ );
+
+static GstStaticPadTemplate srctemplate =
+ GST_STATIC_PAD_TEMPLATE(
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (
+ "audio/x-raw-int, "
+ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+ "signed = (boolean) true, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "channels = (int) [1,2]"
+ )
+ );
+
+static void gst_iir_equalizer_child_proxy_interface_init (gpointer g_iface,
+ gpointer iface_data);
+
+static void gst_iir_equalizer_finalize (GObject * object);
+
+static gboolean gst_iir_equalizer_setup (GstAudioFilter * filter,
+ GstRingBufferSpec * fmt);
+
+static void
+_do_init (GType object_type)
+{
+ const GInterfaceInfo child_proxy_interface_info = {
+ (GInterfaceInitFunc) gst_iir_equalizer_child_proxy_interface_init,
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY,
+ &child_proxy_interface_info);
+}
+
+GST_BOILERPLATE_FULL(Gstaudioeq, gst_audioeq, GstBaseTransform, GST_TYPE_BASE_TRANSFORM,_do_init);
+
+static void gst_audioeq_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_audioeq_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
+#ifdef AUDIOEQ_REDUCE_MEMCPY
+static GstFlowReturn gst_audioeq_transform_ip (GstBaseTransform * base, GstBuffer * buf);
+#else
+static GstFlowReturn gst_audioeq_transform (GstBaseTransform * base, GstBuffer * inbuf, GstBuffer * outbuf);
+#endif
+static gboolean gst_audioeq_set_caps (GstBaseTransform * base, GstCaps * incaps, GstCaps * outcaps);
+
+static GstStateChangeReturn
+gst_audioeq_change_state (GstElement * element, GstStateChange transition)
+{
+ GST_DEBUG ("gst_audioeq_change_state");
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ Gstaudioeq *audioeq = GST_AUDIOEQ (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ audioeq->need_update_filter = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+gst_audioeq_base_init (gpointer gclass)
+{
+
+ GST_DEBUG ("gst_audioeq_base_init");
+ static GstElementDetails element_details = {
+ "Audio Equalizer",
+ "Filter/Effect/Audio",
+ "Set equalisation effect on audio/raw streams",
+ "Samsung Electronics <www.samsung.com>"
+ };
+
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get (&srctemplate));
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get (&sinktemplate));
+
+ gst_element_class_set_details(element_class, &element_details);
+}
+
+static void
+gst_audioeq_class_init (GstaudioeqClass * klass)
+{
+ GST_DEBUG ("gst_audioeq_class_init");
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseTransformClass *basetransform_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+ basetransform_class = GST_BASE_TRANSFORM_CLASS(klass);
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_audioeq_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_audioeq_get_property);
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_audioeq_change_state);
+
+ g_object_class_install_property(gobject_class, PROP_FILTER_ACTION,
+ g_param_spec_uint("filter-action", "filter action", "(0)none (1)preset (2)advanced setting",
+ 0, 2, DEFAULT_FILTER_ACTION, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_CUSTOM_EQ,
+ g_param_spec_pointer("custom-eq", "custom eq",
+ "pointer for 9 bands of EQ array", G_PARAM_READWRITE));
+
+ g_object_class_install_property(gobject_class, PROP_CUSTOM_EQ_NUM,
+ g_param_spec_uint("custom-eq-num", "custom eq num", "number of custom EQ bands",
+ 0, 9, DEFAULT_CUSTOM_EQ_NUM, G_PARAM_READABLE));
+
+ g_object_class_install_property(gobject_class, PROP_CUSTOM_EQ_FREQ,
+ g_param_spec_pointer("custom-eq-freq", "custom eq freq", "pointer for EQ bands central frequency(Hz) array",
+ G_PARAM_READABLE));
+
+ g_object_class_install_property(gobject_class, PROP_CUSTOM_EQ_WIDTH,
+ g_param_spec_pointer("custom-eq-width", "custom eq width", "pointer for EQ bands width(Hz) array",
+ G_PARAM_READABLE));
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_iir_equalizer_finalize);
+
+/* It is possible to reduce memcpy by setting output same as input of AudioEq_InOutConfig */
+#ifdef AUDIOEQ_REDUCE_MEMCPY
+ basetransform_class->transform_ip = GST_DEBUG_FUNCPTR(gst_audioeq_transform_ip);
+#endif
+ basetransform_class->set_caps = GST_DEBUG_FUNCPTR(gst_audioeq_set_caps);
+}
+
+static void
+gst_audioeq_init (Gstaudioeq * audioeq, GstaudioeqClass * gclass)
+{
+ GST_DEBUG ("gst_audioeq_init");
+ audioeq->samplerate = DEFAULT_SAMPLE_RATE;
+ audioeq->channels = DEAFULT_CHANNELS;
+
+ audioeq->filter_action = DEFAULT_FILTER_ACTION;
+ memset(audioeq->custom_eq, 0x00, sizeof(gint) * CUSTOM_EQ_BAND_MAX);
+ audioeq->need_update_filter = TRUE;
+ audioeq->equ.bands_lock = g_mutex_new ();
+ audioeq->equ.need_new_coefficients = TRUE;
+ gst_iir_equalizer_compute_frequencies (audioeq, DEFAULT_CUSTOM_EQ_NUM);
+}
+/* equalizer implementation */
+
+static void
+gst_iir_equalizer_finalize (GObject * object)
+{
+ GST_DEBUG ("gst_iir_equalizer_finalize");
+ Gstaudioeq *audioeq = GST_AUDIOEQ(object);
+ GstIirEqualizer *equ = &audioeq->equ;
+ gint i;
+
+ for (i = 0; i < equ->freq_band_count; i++) {
+ if (equ->bands[i])
+ gst_object_unparent (GST_OBJECT (equ->bands[i]));
+ equ->bands[i] = NULL;
+ }
+ equ->freq_band_count = 0;
+
+ g_free (equ->bands);
+ g_free (equ->history);
+
+ g_mutex_free (equ->bands_lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+#define BANDS_LOCK(equ) g_mutex_lock(equ->bands_lock)
+#define BANDS_UNLOCK(equ) g_mutex_unlock(equ->bands_lock)
+
+/* child object */
+
+enum
+{
+ PROP_GAIN = 1,
+ PROP_FREQ,
+ PROP_BANDWIDTH,
+ PROP_TYPE
+};
+
+typedef enum
+{
+ BAND_TYPE_PEAK = 0,
+ BAND_TYPE_LOW_SHELF,
+ BAND_TYPE_HIGH_SHELF
+} GstIirEqualizerBandType;
+
+#define GST_TYPE_IIR_EQUALIZER_BAND_TYPE (gst_iir_equalizer_band_type_get_type ())
+static GType
+gst_iir_equalizer_band_type_get_type (void)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_type_get_type");
+ static GType gtype = 0;
+
+ if (gtype == 0) {
+ static const GEnumValue values[] = {
+ {BAND_TYPE_PEAK, "Peak filter (default for inner bands)", "peak"},
+ {BAND_TYPE_LOW_SHELF, "Low shelf filter (default for first band)",
+ "low-shelf"},
+ {BAND_TYPE_HIGH_SHELF, "High shelf filter (default for last band)",
+ "high-shelf"},
+ {0, NULL, NULL}
+ };
+
+ gtype = g_enum_register_static ("GstIirEqualizerBandType", values);
+ }
+ return gtype;
+}
+
+
+typedef struct _GstIirEqualizerBandClass GstIirEqualizerBandClass;
+
+#define GST_TYPE_IIR_EQUALIZER_BAND \
+ (gst_iir_equalizer_band_get_type())
+#define GST_IIR_EQUALIZER_BAND(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_IIR_EQUALIZER_BAND,GstIirEqualizerBand))
+#define GST_IIR_EQUALIZER_BAND_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_IIR_EQUALIZER_BAND,GstIirEqualizerBandClass))
+#define GST_IS_IIR_EQUALIZER_BAND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_IIR_EQUALIZER_BAND))
+#define GST_IS_IIR_EQUALIZER_BAND_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_IIR_EQUALIZER_BAND))
+
+struct _GstIirEqualizerBand
+{
+ GstObject object;
+
+ /*< private > */
+ /* center frequency and gain */
+ gdouble freq;
+ gdouble gain;
+ gdouble width;
+ GstIirEqualizerBandType type;
+
+ /* second order iir filter */
+ gdouble b1, b2; /* IIR coefficients for outputs */
+ gdouble a0, a1, a2; /* IIR coefficients for inputs */
+};
+
+struct _GstIirEqualizerBandClass
+{
+ GstObjectClass parent_class;
+};
+
+static GType gst_iir_equalizer_band_get_type (void);
+
+static void
+gst_iir_equalizer_band_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_set_property");
+ GstIirEqualizerBand *band = GST_IIR_EQUALIZER_BAND (object);
+ Gstaudioeq *audioeq = GST_AUDIOEQ (gst_object_get_parent (GST_OBJECT (band)));
+ GstIirEqualizer *equ = &audioeq->equ;
+
+ switch (prop_id) {
+ case PROP_GAIN:{
+ gdouble gain;
+
+ gain = g_value_get_double (value);
+ GST_DEBUG_OBJECT (band, "gain = %lf -> %lf", band->gain, gain);
+ if (gain != band->gain) {
+ BANDS_LOCK (equ);
+ equ->need_new_coefficients = TRUE;
+ band->gain = gain;
+ BANDS_UNLOCK (equ);
+ GST_DEBUG_OBJECT (band, "changed gain = %lf ", band->gain);
+ }
+ break;
+ }
+ case PROP_FREQ:{
+ gdouble freq;
+
+ freq = g_value_get_double (value);
+ GST_DEBUG_OBJECT (band, "freq = %lf -> %lf", band->freq, freq);
+ if (freq != band->freq) {
+ BANDS_LOCK (equ);
+ equ->need_new_coefficients = TRUE;
+ band->freq = freq;
+ BANDS_UNLOCK (equ);
+ GST_DEBUG_OBJECT (band, "changed freq = %lf ", band->freq);
+ }
+ break;
+ }
+ case PROP_BANDWIDTH:{
+ gdouble width;
+
+ width = g_value_get_double (value);
+ GST_DEBUG_OBJECT (band, "width = %lf -> %lf", band->width, width);
+ if (width != band->width) {
+ BANDS_LOCK (equ);
+ equ->need_new_coefficients = TRUE;
+ band->width = width;
+ BANDS_UNLOCK (equ);
+ GST_DEBUG_OBJECT (band, "changed width = %lf ", band->width);
+ }
+ break;
+ }
+ case PROP_TYPE:{
+ GstIirEqualizerBandType type;
+
+ type = g_value_get_enum (value);
+ GST_DEBUG_OBJECT (band, "type = %d -> %d", band->type, type);
+ if (type != band->type) {
+ BANDS_LOCK (equ);
+ equ->need_new_coefficients = TRUE;
+ band->type = type;
+ BANDS_UNLOCK (equ);
+ GST_DEBUG_OBJECT (band, "changed type = %d ", band->type);
+ }
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ gst_object_unref (audioeq);
+}
+
+static void
+gst_iir_equalizer_band_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_get_property");
+ GstIirEqualizerBand *band = GST_IIR_EQUALIZER_BAND (object);
+
+ switch (prop_id) {
+ case PROP_GAIN:
+ g_value_set_double (value, band->gain);
+ break;
+ case PROP_FREQ:
+ g_value_set_double (value, band->freq);
+ break;
+ case PROP_BANDWIDTH:
+ g_value_set_double (value, band->width);
+ break;
+ case PROP_TYPE:
+ g_value_set_enum (value, band->type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_iir_equalizer_band_class_init (GstIirEqualizerBandClass * klass)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_class_init");
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gst_iir_equalizer_band_set_property;
+ gobject_class->get_property = gst_iir_equalizer_band_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_GAIN,
+ g_param_spec_double ("gain", "gain",
+ "gain for the frequency band ranging from -12.0 dB to +12.0 dB",
+ -12.0, 12.0, 0.0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+
+ g_object_class_install_property (gobject_class, PROP_FREQ,
+ g_param_spec_double ("freq", "freq",
+ "center frequency of the band",
+ 0.0, 100000.0, 0.0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+
+ g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
+ g_param_spec_double ("bandwidth", "bandwidth",
+ "difference between bandedges in Hz",
+ 0.0, 100000.0, 1.0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+
+ g_object_class_install_property (gobject_class, PROP_TYPE,
+ g_param_spec_enum ("type", "Type",
+ "Filter type", GST_TYPE_IIR_EQUALIZER_BAND_TYPE,
+ BAND_TYPE_PEAK,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+}
+
+static void
+gst_iir_equalizer_band_init (GstIirEqualizerBand * band,
+ GstIirEqualizerBandClass * klass)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_init");
+ band->freq = 0.0;
+ band->gain = 0.0;
+ band->width = 1.0;
+ band->type = BAND_TYPE_PEAK;
+}
+
+static GType
+gst_iir_equalizer_band_get_type (void)
+{
+ GST_DEBUG ("gst_iir_equalizer_band_get_type");
+ static GType type = 0;
+
+ if (G_UNLIKELY (!type)) {
+ const GTypeInfo type_info = {
+ sizeof (GstIirEqualizerBandClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_iir_equalizer_band_class_init,
+ NULL,
+ NULL,
+ sizeof (GstIirEqualizerBand),
+ 0,
+ (GInstanceInitFunc) gst_iir_equalizer_band_init,
+ };
+ type =
+ g_type_register_static (GST_TYPE_OBJECT, "GstIirEqualizerBand",
+ &type_info, 0);
+ }
+ return (type);
+}
+
+
+/* child proxy iface */
+static GstObject *
+gst_iir_equalizer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
+ guint index)
+{
+ GST_DEBUG ("gst_iir_equalizer_child_proxy_get_child_by_index");
+ Gstaudioeq *audioeq = GST_AUDIOEQ(child_proxy);
+ GstIirEqualizer *equ = &audioeq->equ;
+ GstObject *ret;
+
+ BANDS_LOCK (equ);
+ if (G_UNLIKELY (index >= equ->freq_band_count)) {
+ BANDS_UNLOCK (equ);
+ g_return_val_if_fail (index < equ->freq_band_count, NULL);
+ }
+
+ ret = gst_object_ref (equ->bands[index]);
+ BANDS_UNLOCK (equ);
+
+ GST_LOG_OBJECT (equ, "return child[%d] %" GST_PTR_FORMAT, index, ret);
+ return ret;
+}
+
+static guint
+gst_iir_equalizer_child_proxy_get_children_count (GstChildProxy * child_proxy)
+{
+ GST_DEBUG ("gst_iir_equalizer_child_proxy_get_children_count");
+ Gstaudioeq *audioeq = GST_AUDIOEQ(child_proxy);
+ GstIirEqualizer *equ = &audioeq->equ;
+
+ GST_LOG ("we have %d children", equ->freq_band_count);
+ return equ->freq_band_count;
+}
+
+static void
+gst_iir_equalizer_child_proxy_interface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ GstChildProxyInterface *iface = g_iface;
+
+ GST_DEBUG ("initializing iface");
+
+ iface->get_child_by_index = gst_iir_equalizer_child_proxy_get_child_by_index;
+ iface->get_children_count = gst_iir_equalizer_child_proxy_get_children_count;
+}
+static void
+gst_iir_equalizer_class_init (GstIirEqualizerClass * klass)
+{
+ GST_DEBUG ("gst_iir_equalizer_class_init");
+}
+
+static void
+gst_iir_equalizer_init (GstIirEqualizer * eq, GstIirEqualizerClass * g_class)
+{
+ GST_DEBUG ("gst_iir_equalizer_init");
+ eq->bands_lock = g_mutex_new ();
+ eq->need_new_coefficients = TRUE;
+}
+
+GType
+gst_iir_equalizer_get_type (void)
+{
+ GST_DEBUG ("gst_iir_equalizer_get_type");
+ static GType type = 0;
+
+ if (G_UNLIKELY (!type)) {
+ const GTypeInfo type_info = {
+ sizeof (GstIirEqualizerClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_iir_equalizer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstIirEqualizer),
+ 0,
+ (GInstanceInitFunc) gst_iir_equalizer_init,
+ };
+ type =
+ g_type_register_static (GST_TYPE_OBJECT, "GstIirEqualizer",
+ &type_info, 0);
+ }
+ return (type);
+}
+/* Filter taken from
+ *
+ * The Equivalence of Various Methods of Computing
+ * Biquad Coefficients for Audio Parametric Equalizers
+ *
+ * by Robert Bristow-Johnson
+ *
+ * http://www.aes.org/e-lib/browse.cfm?elib=6326
+ * http://www.musicdsp.org/files/EQ-Coefficients.pdf
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+ *
+ * The bandwidth method that we use here is the preferred
+ * one from this article transformed from octaves to frequency
+ * in Hz.
+ */
+static inline gdouble
+arg_to_scale (gdouble arg)
+{
+ GST_DEBUG ("arg_to_scale");
+ return (pow (10.0, arg / 40.0));
+}
+
+static gdouble
+calculate_omega (gdouble freq, gint rate)
+{
+ GST_DEBUG ("calculate_omega");
+ gdouble omega;
+
+ if (freq / rate >= 0.5)
+ omega = G_PI;
+ else if (freq <= 0.0)
+ omega = 0.0;
+ else
+ omega = 2.0 * G_PI * (freq / rate);
+
+ return omega;
+}
+
+static gdouble
+calculate_bw (GstIirEqualizerBand * band, gint rate)
+{
+ GST_DEBUG ("calculate_bw");
+ gdouble bw = 0.0;
+
+ if (band->width / rate >= 0.5) {
+ /* If bandwidth == 0.5 the calculation below fails as tan(G_PI/2)
+ * is undefined. So set the bandwidth to a slightly smaller value.
+ */
+ bw = G_PI - 0.00000001;
+ } else if (band->width <= 0.0) {
+ /* If bandwidth == 0 this band won't change anything so set
+ * the coefficients accordingly. The coefficient calculation
+ * below would create coefficients that for some reason amplify
+ * the band.
+ */
+ band->a0 = 1.0;
+ band->a1 = 0.0;
+ band->a2 = 0.0;
+ band->b1 = 0.0;
+ band->b2 = 0.0;
+ } else {
+ bw = 2.0 * G_PI * (band->width / rate);
+ }
+ return bw;
+}
+
+static void
+setup_peak_filter (Gstaudioeq* audioeq, GstIirEqualizerBand * band)
+{
+ GST_DEBUG ("setup_peak_filter");
+ //g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);
+
+ {
+ gdouble gain, omega, bw;
+ gdouble alpha, alpha1, alpha2, b0;
+
+ gain = arg_to_scale (band->gain);
+ omega = calculate_omega (band->freq, audioeq->samplerate);
+ bw = calculate_bw (band, audioeq->samplerate);
+ if (bw == 0.0)
+ goto out;
+
+ alpha = tan (bw / 2.0);
+
+ alpha1 = alpha * gain;
+ alpha2 = alpha / gain;
+
+ b0 = (1.0 + alpha2);
+
+ band->a0 = (1.0 + alpha1) / b0;
+ band->a1 = (-2.0 * cos (omega)) / b0;
+ band->a2 = (1.0 - alpha1) / b0;
+ band->b1 = (2.0 * cos (omega)) / b0;
+ band->b2 = -(1.0 - alpha2) / b0;
+
+ out:
+ GST_INFO
+ ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
+ band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
+ band->b1, band->b2);
+ }
+}
+
+static void
+setup_low_shelf_filter (Gstaudioeq* audioeq, GstIirEqualizerBand * band)
+{
+ GST_DEBUG ("setup_low_shelf_filter");
+ //g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);
+
+ {
+ gdouble gain, omega, bw;
+ gdouble alpha, delta, b0;
+ gdouble egp, egm;
+
+ gain = arg_to_scale (band->gain);
+ omega = calculate_omega (band->freq, audioeq->samplerate);
+ bw = calculate_bw (band, audioeq->samplerate);
+ if (bw == 0.0)
+ goto out;
+
+ egm = gain - 1.0;
+ egp = gain + 1.0;
+ alpha = tan (bw / 2.0);
+
+ delta = 2.0 * sqrt (gain) * alpha;
+ b0 = egp + egm * cos (omega) + delta;
+
+ band->a0 = ((egp - egm * cos (omega) + delta) * gain) / b0;
+ band->a1 = ((egm - egp * cos (omega)) * 2.0 * gain) / b0;
+ band->a2 = ((egp - egm * cos (omega) - delta) * gain) / b0;
+ band->b1 = ((egm + egp * cos (omega)) * 2.0) / b0;
+ band->b2 = -((egp + egm * cos (omega) - delta)) / b0;
+
+
+ out:
+ GST_INFO
+ ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
+ band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
+ band->b1, band->b2);
+ }
+}
+
+static void
+setup_high_shelf_filter (Gstaudioeq* audioeq, GstIirEqualizerBand * band)
+{
+ GST_DEBUG ("setup_high_shelf_filter");
+ {
+ gdouble gain, omega, bw;
+ gdouble alpha, delta, b0;
+ gdouble egp, egm;
+
+ gain = arg_to_scale (band->gain);
+ omega = calculate_omega (band->freq, audioeq->samplerate);
+ bw = calculate_bw (band, audioeq->samplerate);
+ if (bw == 0.0)
+ goto out;
+
+ egm = gain - 1.0;
+ egp = gain + 1.0;
+ alpha = tan (bw / 2.0);
+
+ delta = 2.0 * sqrt (gain) * alpha;
+ b0 = egp - egm * cos (omega) + delta;
+
+ band->a0 = ((egp + egm * cos (omega) + delta) * gain) / b0;
+ band->a1 = ((egm + egp * cos (omega)) * -2.0 * gain) / b0;
+ band->a2 = ((egp + egm * cos (omega) - delta) * gain) / b0;
+ band->b1 = ((egm - egp * cos (omega)) * -2.0) / b0;
+ band->b2 = -((egp - egm * cos (omega) - delta)) / b0;
+
+
+ out:
+ GST_INFO
+ ("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
+ band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
+ band->b1, band->b2);
+ }
+}
+
+/* Must be called with bands_lock and transform lock! */
+static void
+set_passthrough (Gstaudioeq* audioeq)
+{
+ GST_DEBUG ("set_passthrough");
+ GstIirEqualizer* equ=&audioeq->equ;
+ gint i;
+ gboolean passthrough = TRUE;
+
+ for (i = 0; i < equ->freq_band_count; i++) {
+ passthrough = passthrough && (equ->bands[i]->gain == 0.0);
+ }
+
+ gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (audioeq), passthrough);
+ GST_DEBUG ("Passthrough mode: %d\n", passthrough);
+}
+
+/* Must be called with bands_lock and transform lock! */
+static void
+update_coefficients (Gstaudioeq* audioeq)
+{
+ GST_DEBUG ("update_coefficients");
+ GstIirEqualizer* equ=&audioeq->equ;
+ gint i, n = equ->freq_band_count;
+
+ for (i = 0; i < n; i++) {
+ if (equ->bands[i]->type == BAND_TYPE_PEAK)
+ setup_peak_filter (audioeq, equ->bands[i]);
+ else if (equ->bands[i]->type == BAND_TYPE_LOW_SHELF)
+ setup_low_shelf_filter (audioeq, equ->bands[i]);
+ else
+ setup_high_shelf_filter (audioeq, equ->bands[i]);
+ }
+
+ equ->need_new_coefficients = FALSE;
+}
+
+/* Must be called with transform lock! */
+static void
+alloc_history (GstIirEqualizer * equ)
+{
+ GST_DEBUG ("alloc_history");
+ /* free + alloc = no memcpy */
+ g_free (equ->history);
+ equ->history =
+ g_malloc0 (equ->history_size * equ->audiofilter.format.channels *
+ equ->freq_band_count);
+}
+void
+gst_audioeq_band_set_property(Gstaudioeq * audioeq)
+{
+ GST_DEBUG ("gst_audioeq_band_set_property");
+ GstIirEqualizer *equ = &audioeq->equ;
+ gshort i ;
+ for ( i = 0; i < DEFAULT_CUSTOM_EQ_NUM; i++){
+ GST_DEBUG ("gain = %lf -> %d", equ->bands[i]->gain, audioeq->custom_eq[i] );
+ if (audioeq->custom_eq[i] != equ->bands[i]->gain) {
+ equ->bands[i]->gain = audioeq->custom_eq[i];
+ GST_DEBUG("changed gain = %lf ", equ->bands[i]->gain);
+ g_object_notify (G_OBJECT (equ->bands[i]), "gain");
+ }
+ }
+}
+
+void
+gst_iir_equalizer_compute_frequencies (Gstaudioeq * audioeq, guint new_count)
+{
+ GST_DEBUG ("gst_iir_equalizer_compute_frequencies");
+
+ GstIirEqualizer *equ = &audioeq->equ;
+ guint old_count, i;
+ gdouble freq0, freq1, step;
+ gchar name[20];
+ GST_DEBUG ("gst_iir_equalizer_compute_frequencies before calling equalizer object");
+ if (equ->freq_band_count == new_count)
+ return;
+
+ GST_DEBUG ("gst_iir_equalizer_compute_frequencies equalizer object");
+
+ BANDS_LOCK (equ);
+ GST_DEBUG ("gst_iir_equalizer_compute_frequencies 1");
+ if (G_UNLIKELY (equ->freq_band_count == new_count)) {
+ BANDS_UNLOCK (equ);
+ return;
+ }
+ GST_DEBUG ("gst_iir_equalizer_compute_frequencies 2");
+ old_count = equ->freq_band_count;
+ equ->freq_band_count = new_count;
+ GST_DEBUG ("bands %u -> %u", old_count, new_count);
+
+ if (old_count < new_count) {
+ /* add new bands */
+ equ->bands = g_realloc (equ->bands, sizeof (GstObject *) * new_count);
+ for (i = old_count; i < new_count; i++) {
+ equ->bands[i] = g_object_new (GST_TYPE_IIR_EQUALIZER_BAND, NULL);
+ /* otherwise they get names like 'iirequalizerband5' */
+ snprintf (name, sizeof(name), "band%u", i);
+ gst_object_set_name (GST_OBJECT (equ->bands[i]), name);
+ GST_DEBUG ("adding band[%d]=%p", i, equ->bands[i]);
+
+ gst_object_set_parent (GST_OBJECT (equ->bands[i]), GST_OBJECT (audioeq));
+ gst_child_proxy_child_added (GST_OBJECT (audioeq),
+ GST_OBJECT (equ->bands[i]));
+ }
+ } else {
+ /* free unused bands */
+ for (i = new_count; i < old_count; i++) {
+ GST_DEBUG ("removing band[%d]=%p", i, equ->bands[i]);
+ gst_child_proxy_child_removed (GST_OBJECT (audioeq),
+ GST_OBJECT (equ->bands[i]));
+ gst_object_unparent (GST_OBJECT (equ->bands[i]));
+ equ->bands[i] = NULL;
+ }
+ }
+
+ alloc_history (equ);
+
+ /* set center frequencies and name band objects
+ * FIXME: arg! we can't change the name of parented objects :(
+ * application should read band->freq to get the name
+ */
+
+ step = pow (HIGHEST_FREQ / LOWEST_FREQ, 1.0 / new_count);
+ freq0 = LOWEST_FREQ;
+ for (i = 0; i < new_count; i++) {
+ freq1 = freq0 * step;
+
+ if (i == 0)
+ equ->bands[i]->type = BAND_TYPE_LOW_SHELF;
+ else if (i == new_count - 1)
+ equ->bands[i]->type = BAND_TYPE_HIGH_SHELF;
+ else
+ equ->bands[i]->type = BAND_TYPE_PEAK;
+
+ equ->bands[i]->freq = freq0 + ((freq1 - freq0) / 2.0);
+ equ->bands[i]->width = freq1 - freq0;
+ GST_DEBUG ("band[%2d] = '%lf'", i, equ->bands[i]->freq);
+
+ g_object_notify (G_OBJECT (equ->bands[i]), "bandwidth");
+ g_object_notify (G_OBJECT (equ->bands[i]), "freq");
+ g_object_notify (G_OBJECT (equ->bands[i]), "type");
+
+ freq0 = freq1;
+ }
+
+ equ->need_new_coefficients = TRUE;
+
+ BANDS_UNLOCK (equ);
+}
+/* start of code that is type specific */
+
+#define CREATE_OPTIMIZED_FUNCTIONS_INT(TYPE,BIG_TYPE,MIN_VAL,MAX_VAL) \
+typedef struct { \
+ BIG_TYPE x1, x2; /* history of input values for a filter */ \
+ BIG_TYPE y1, y2; /* history of output values for a filter */ \
+} SecondOrderHistory ## TYPE; \
+ \
+static inline BIG_TYPE \
+one_step_ ## TYPE (GstIirEqualizerBand *filter, \
+ SecondOrderHistory ## TYPE *history, BIG_TYPE input) \
+{ \
+ /* calculate output */ \
+ BIG_TYPE output = filter->a0 * input + \
+ filter->a1 * history->x1 + filter->a2 * history->x2 + \
+ filter->b1 * history->y1 + filter->b2 * history->y2; \
+ /* update history */ \
+ history->y2 = history->y1; \
+ history->y1 = output; \
+ history->x2 = history->x1; \
+ history->x1 = input; \
+ \
+ return output; \
+} \
+ \
+static const guint \
+history_size_ ## TYPE = sizeof (SecondOrderHistory ## TYPE); \
+ \
+static void \
+gst_iir_equ_process_ ## TYPE (GstIirEqualizer *equ, guint8 *data, \
+guint size, guint channels) \
+{ \
+ guint frames = size / channels / sizeof (TYPE); \
+ guint i, c, f, nf = equ->freq_band_count; \
+ BIG_TYPE cur; \
+ GstIirEqualizerBand **filters = equ->bands; \
+ \
+ for (i = 0; i < frames; i++) { \
+ SecondOrderHistory ## TYPE *history = equ->history; \
+ for (c = 0; c < channels; c++) { \
+ cur = *((TYPE *) data); \
+ for (f = 0; f < nf; f++) { \
+ cur = one_step_ ## TYPE (filters[f], history, cur); \
+ history++; \
+ } \
+ cur = CLAMP (cur, MIN_VAL, MAX_VAL); \
+ *((TYPE *) data) = (TYPE) floor (cur); \
+ data += sizeof (TYPE); \
+ } \
+ } \
+}
+
+#define CREATE_OPTIMIZED_FUNCTIONS(TYPE) \
+typedef struct { \
+ TYPE x1, x2; /* history of input values for a filter */ \
+ TYPE y1, y2; /* history of output values for a filter */ \
+} SecondOrderHistory ## TYPE; \
+ \
+static inline TYPE \
+one_step_ ## TYPE (GstIirEqualizerBand *filter, \
+ SecondOrderHistory ## TYPE *history, TYPE input) \
+{ \
+ /* calculate output */ \
+ TYPE output = filter->a0 * input + filter->a1 * history->x1 + \
+ filter->a2 * history->x2 + filter->b1 * history->y1 + \
+ filter->b2 * history->y2; \
+ /* update history */ \
+ history->y2 = history->y1; \
+ history->y1 = output; \
+ history->x2 = history->x1; \
+ history->x1 = input; \
+ \
+ return output; \
+} \
+ \
+static const guint \
+history_size_ ## TYPE = sizeof (SecondOrderHistory ## TYPE); \
+ \
+static void \
+gst_iir_equ_process_ ## TYPE (GstIirEqualizer *equ, guint8 *data, \
+guint size, guint channels) \
+{ \
+ guint frames = size / channels / sizeof (TYPE); \
+ guint i, c, f, nf = equ->freq_band_count; \
+ TYPE cur; \
+ GstIirEqualizerBand **filters = equ->bands; \
+ \
+ for (i = 0; i < frames; i++) { \
+ SecondOrderHistory ## TYPE *history = equ->history; \
+ for (c = 0; c < channels; c++) { \
+ cur = *((TYPE *) data); \
+ for (f = 0; f < nf; f++) { \
+ cur = one_step_ ## TYPE (filters[f], history, cur); \
+ history++; \
+ } \
+ *((TYPE *) data) = (TYPE) cur; \
+ data += sizeof (TYPE); \
+ } \
+ } \
+}
+
+CREATE_OPTIMIZED_FUNCTIONS_INT (gint16, gfloat, -32768.0, 32767.0);
+CREATE_OPTIMIZED_FUNCTIONS (gfloat);
+CREATE_OPTIMIZED_FUNCTIONS (gdouble);
+
+#ifdef AUDIOEQ_REDUCE_MEMCPY
+static GstFlowReturn
+gst_audioeq_transform_ip (GstBaseTransform * base, GstBuffer * buf)
+{
+ GST_DEBUG ("gst_audioeq_transform_ip");
+ Gstaudioeq *audioeq = GST_AUDIOEQ(base);
+ GstIirEqualizer *equ = &audioeq->equ;
+
+ equ->history_size = history_size_gint16;
+ equ->process = gst_iir_equ_process_gint16;
+ g_free (equ->history);
+ equ->history = g_malloc0 (equ->history_size * audioeq->channels * equ->freq_band_count);
+ GstClockTime timestamp;
+
+ if (G_UNLIKELY (audioeq->channels < 1 || equ->process == NULL)) {
+ GST_DEBUG ("gst_audioeq_transform_ip return GST_FLOW_NOT_NEGOTIATED;");
+ if (G_UNLIKELY (equ->process == NULL))
+ GST_DEBUG ("gst_audioeq_transform_ip equ->process ");
+ if (G_UNLIKELY (audioeq->channels < 1))
+ GST_DEBUG ("gst_audioeq_transform_ip audioeq->channels");
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+ GST_DEBUG ("gst_audioeq_transform_ip BANDS_LOCK (equ);");
+ BANDS_LOCK (equ);
+ if (equ->need_new_coefficients) {
+ GST_DEBUG ("gst_audioeq_transform_ip update_coefficients");
+ update_coefficients (audioeq);
+ set_passthrough (audioeq);
+ }
+ BANDS_UNLOCK (equ);
+
+ if (gst_base_transform_is_passthrough (base)) {
+ GST_DEBUG ("gst_audioeq_transform_ip gst_base_transform_is_passthrough return GST_FLOW_OK;");
+ return GST_FLOW_OK;
+ }
+ timestamp = GST_BUFFER_TIMESTAMP (buf);
+ timestamp =
+ gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
+
+ if (GST_CLOCK_TIME_IS_VALID (timestamp))
+ gst_object_sync_values (G_OBJECT (audioeq), timestamp);
+ GST_DEBUG (" equ->process ++");
+ equ->process (equ, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
+ audioeq->channels);
+ GST_DEBUG (" equ->process --");
+ GST_DEBUG ("gst_audioeq_transform_ip return GST_FLOW_OK;");
+ return GST_FLOW_OK;
+}
+#endif
+
+static gboolean
+gst_audioeq_set_caps (GstBaseTransform * base, GstCaps * incaps,
+ GstCaps * outcaps)
+{
+ GST_DEBUG ("gst_audioeq_set_caps");
+ Gstaudioeq *audioeq = GST_AUDIOEQ(base);
+ GstStructure *ins;
+ GstPad *pad;
+ gint samplerate;
+ gint channels;
+ gshort old_samplerate;
+ gshort old_channels;
+
+ pad = gst_element_get_static_pad(GST_ELEMENT(audioeq), "src");
+
+ /* forward-negotiate */
+ if(!gst_pad_set_caps(pad, incaps)) {
+ gst_object_unref(pad);
+ return FALSE;
+ }
+
+ /* negotiation succeeded, so now configure ourselves */
+ ins = gst_caps_get_structure(incaps, 0);
+
+ /* get samplerate from caps & convert */
+ old_samplerate = audioeq->samplerate;
+ old_channels = audioeq->channels;
+ gst_structure_get_int(ins, "rate", &samplerate);
+ switch (samplerate) {
+ case 48000:
+ audioeq->samplerate = SAMPLERATE_48000Hz;
+ break;
+ case 44100:
+ audioeq->samplerate = SAMPLERATE_44100Hz;
+ break;
+ case 32000:
+ audioeq->samplerate = SAMPLERATE_32000Hz;
+ break;
+ case 24000:
+ audioeq->samplerate = SAMPLERATE_24000Hz;
+ break;
+ case 22050:
+ audioeq->samplerate = SAMPLERATE_22050Hz;
+ break;
+ case 16000:
+ audioeq->samplerate = SAMPLERATE_16000Hz;
+ break;
+ case 12000:
+ audioeq->samplerate = SAMPLERATE_12000Hz;
+ break;
+ case 11025:
+ audioeq->samplerate = SAMPLERATE_11025Hz;
+ break;
+ case 8000:
+ audioeq->samplerate = SAMPLERATE_8000Hz;
+ break;
+ default:
+ if (samplerate < 8000) {
+ audioeq->samplerate = SAMPLERATE_8000Hz;
+ } else if (samplerate > 48000) {
+ audioeq->samplerate = SAMPLERATE_48000Hz;
+ }
+ break;
+ }
+ /* get number of channels from caps */
+ gst_structure_get_int(ins, "channels", &channels);
+ audioeq->channels = (gshort)channels;
+
+ if ((old_samplerate != audioeq->samplerate)
+ || (old_channels != audioeq->channels)) {
+ audioeq->need_update_filter = TRUE;
+ }
+
+ gst_object_unref (pad);
+
+ return TRUE;
+}
+
+static void
+gst_audioeq_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GST_DEBUG ("gst_audioeq_set_property");
+ Gstaudioeq *audioeq = GST_AUDIOEQ (object);
+ GstIirEqualizer *equ = &audioeq->equ;
+ gshort *pointer;
+
+ switch (prop_id) {
+
+ case PROP_FILTER_ACTION:
+ audioeq->filter_action = g_value_get_uint(value);
+ BANDS_LOCK(equ);
+ equ->need_new_coefficients = TRUE;
+ BANDS_UNLOCK(equ);
+ break;
+
+ case PROP_CUSTOM_EQ:
+ pointer = g_value_get_pointer(value);
+ if (pointer) {
+ memcpy(audioeq->custom_eq, pointer, sizeof(gint) * CUSTOM_EQ_BAND_MAX);
+ if (audioeq->filter_action == FILTER_ADVANCED_SETTING) {
+ BANDS_LOCK(equ);
+ equ->need_new_coefficients = TRUE;
+ gst_audioeq_band_set_property(audioeq);
+ BANDS_UNLOCK(equ);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ GST_DEBUG ("gst_audioeq_set_property need_update_filter %d", audioeq->need_update_filter);
+}
+
+static void
+gst_audioeq_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
+{
+GST_DEBUG ("gst_audioeq_get_property");
+
+ Gstaudioeq *audioeq = GST_AUDIOEQ (object);
+ GstIirEqualizer *equ = &audioeq->equ;
+ gshort i;
+ gdouble widtharr[DEFAULT_CUSTOM_EQ_NUM],freqarr[DEFAULT_CUSTOM_EQ_NUM];
+
+ switch (prop_id) {
+ case PROP_FILTER_ACTION:
+ g_value_set_uint(value, audioeq->filter_action);
+ break;
+
+ case PROP_CUSTOM_EQ:
+ g_value_set_pointer(value, audioeq->custom_eq);
+ break;
+
+ case PROP_CUSTOM_EQ_NUM:
+ g_value_set_uint(value, DEFAULT_CUSTOM_EQ_NUM);
+ break;
+
+ case PROP_CUSTOM_EQ_FREQ:
+ for(i=0;i<DEFAULT_CUSTOM_EQ_NUM;i++) {
+ freqarr[i] = equ->bands[i]->freq;
+ }
+ g_value_set_pointer(value, &freqarr);
+ break;
+
+ case PROP_CUSTOM_EQ_WIDTH:
+ for(i=0;i<DEFAULT_CUSTOM_EQ_NUM;i++) {
+ widtharr[i] = equ->bands[i]->width;
+ }
+ g_value_set_pointer(value, &widtharr);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static gboolean
+gst_iir_equalizer_setup (GstAudioFilter * audio, GstRingBufferSpec * fmt)
+{
+GST_DEBUG ("gst_iir_equalizer_setup");
+ GstIirEqualizer *equ = GST_IIR_EQUALIZER (audio);
+
+ switch (fmt->type) {
+ case GST_BUFTYPE_LINEAR:
+ switch (fmt->width) {
+ case 16:
+ equ->history_size = history_size_gint16;
+ equ->process = gst_iir_equ_process_gint16;
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ case GST_BUFTYPE_FLOAT:
+ switch (fmt->width) {
+ case 32:
+ equ->history_size = history_size_gfloat;
+ equ->process = gst_iir_equ_process_gfloat;
+ break;
+ case 64:
+ equ->history_size = history_size_gdouble;
+ equ->process = gst_iir_equ_process_gdouble;
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ alloc_history (equ);
+ return TRUE;
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG ("audioeq plugin_init ");
+ GST_DEBUG_CATEGORY_INIT(gst_audioeq_debug, "audioeq", 0, "Audio Equalizer Plugin");
+ return gst_element_register(plugin, "audioeq", GST_RANK_NONE, GST_TYPE_AUDIOEQ);
+}
+
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "audioeq",
+ "Audio Equalizer Plugin",
+ plugin_init,
+ VERSION,
+ "LGPL",
+ "gst-plugins-ext",
+ "https://www.tizen.org/")
--- /dev/null
+/*
+ * audioeq
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Aditi Narula <aditi.n@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_AUDIOEQ_H__
+#define __GST_AUDIOEQ_H__
+
+#include <stdio.h>
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/audio/gstaudiofilter.h>
+#include <gst/audio/gstringbuffer.h>
+#include <gst/controller/gstcontroller.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AUDIOEQ \
+ (gst_audioeq_get_type())
+#define GST_AUDIOEQ(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOEQ,Gstaudioeq))
+#define GST_audioeq_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOEQ,GstaudioeqClass))
+#define GST_IS_AUDIOEQ(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOEQ))
+#define GST_IS_AUDIOEQ_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOEQ))
+#define GST_TYPE_IIR_EQUALIZER \
+ (gst_iir_equalizer_get_type())
+#define GST_IIR_EQUALIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_IIR_EQUALIZER,GstIirEqualizer))
+#define GST_IIR_EQUALIZER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_IIR_EQUALIZER,GstIirEqualizerClass))
+#define GST_IS_IIR_EQUALIZER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_IIR_EQUALIZER))
+#define GST_IS_IIR_EQUALIZER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_IIR_EQUALIZER))
+
+typedef struct _Gstaudioeq Gstaudioeq;
+typedef struct _GstaudioeqClass GstaudioeqClass;
+typedef struct _GstIirEqualizer GstIirEqualizer;
+typedef struct _GstIirEqualizerClass GstIirEqualizerClass;
+typedef struct _GstIirEqualizerBand GstIirEqualizerBand;
+
+#define CUSTOM_EQ_BAND_MAX 9
+
+#define LOWEST_FREQ (20.0)
+#define HIGHEST_FREQ (20000.0)
+
+typedef void (*ProcessFunc) (GstIirEqualizer * eq, guint8 * data, guint size,
+ guint channels);
+
+struct _GstIirEqualizer
+{
+ GstAudioFilter audiofilter;
+
+ /*< private >*/
+
+ GMutex *bands_lock;
+ GstIirEqualizerBand **bands;
+
+ /* properties */
+ guint freq_band_count;
+ /* for each band and channel */
+ gpointer history;
+ guint history_size;
+
+ gboolean need_new_coefficients;
+
+ ProcessFunc process;
+};
+
+struct _GstIirEqualizerClass
+{
+ GstAudioFilterClass audiofilter_class;
+};
+
+
+
+struct _Gstaudioeq
+{
+ GstBaseTransform element;
+
+ guint samplerate;
+ guint channels;
+
+ /* properties */
+ guint filter_action;
+ gint custom_eq[CUSTOM_EQ_BAND_MAX];
+ gboolean need_update_filter;
+ GstIirEqualizer equ;
+};
+
+struct _GstaudioeqClass
+{
+ GstAudioFilterClass parent_class;
+};
+
+void gst_iir_equalizer_compute_frequencies (Gstaudioeq * audioeq, guint new_count);
+
+GType gst_iir_equalizer_get_type(void);
+
+GType gst_audioeq_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_AUDIOEQ_H__ */
+
return GST_FLOW_OK;
}
+#if 0
send_dummy:
{
}
return GST_FLOW_OK;
}
+#endif
error_exit:
+++ /dev/null
-
-# plugindir is set in configure
-
-plugin_LTLIBRARIES = libgstavsysaudiosrc.la
-
-# sources used to compile this plug-in
-libgstavsysaudiosrc_la_SOURCES = gstavsyssrc.c \
- gstavsysaudiosrc.c
-
-libgstavsysaudiosrc_la_CFLAGS = $(GST_CFLAGS) \
- $(GST_BASE_CFLAGS) \
- $(AVSYSTEM_CFLAGS) \
- $(GST_VIDEO_FLAGS) \
- -I$(includedir)/mmf \
- $(MMTA_CFLAGS) \
- $(GST_AUDIO_CFLAGS) \
- $(VCONF_CFLAGS) \
- $(AVSYSAUDIO_CFLAGS)
-
-libgstavsysaudiosrc_la_LIBADD = $(GST_LIBS) \
- $(GST_BASE_LIBS) \
- $(DATACOMLIB_LIBS) \
- $(HTTPLIB_LIBS) \
- $(AVSYSTEM_LIBS) \
- $(GST_VIDEO_LIBS) \
- -lgstaudio-0.10 \
- $(MMTA_LIBS) \
- $(GST_AUDIO_LIBS) \
- -ldl \
- $(VCONF_LIBS) \
- $(AVSYSAUDIO_LIBS)
-
-libgstavsysaudiosrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-
-if IS_I386
-libgstavsysaudiosrc_la_CFLAGS += -DI386_SIMULATOR
-else
-endif
-
-plugin_LTLIBRARIES += libgstavsyssink.la
-
-## sources used to compile this plug-in
-libgstavsyssink_la_SOURCES = gstavsyssink.c \
- gstavsysmemsink.c
-
-libgstavsyssink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(AVSYSVIDEO_CFLAGS) $(AVSYSTEM_CFLAGS) -I$(includedir)/mmf
-libgstavsyssink_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(DATACOMLIB_LIBS) $(HTTPLIB_LIBS) $(AVSYSVIDEO_LIBS) $(AVSYSTEM_LIBS) $(GST_VIDEO_LIBS) -lgstaudio-0.10 -ldl
-libgstavsyssink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-
-
-libgstavsyssink_la_SOURCES += gstavsysaudiosink.c
-libgstavsyssink_la_CFLAGS += $(AVSYSAUDIO_CFLAGS)
-libgstavsyssink_la_LIBADD += $(AVSYSAUDIO_LIBS)
-
-if IS_I386
-libgstavsyssink_la_CFLAGS += -DI386_SIMULATOR
-else
-endif
-
-
+++ /dev/null
-/*
- * avsystem
- *
- * 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
- *
- */
-
-
-
-#include <gst/gst.h>
-#include <gst/gstutils.h>
-
-#include <string.h>
-
-#include "gstavsysaudiosink.h"
-
-#define _ALSA_DAPM_
-#define __REPLACE_RESET_WITH_CLOSE_AND_REOPEN__
-
-#define CONVERT_MUTE_VALUE(_mute) ((_mute) ? AVSYS_AUDIO_MUTE : AVSYS_AUDIO_UNMUTE)
-
-GST_DEBUG_CATEGORY_EXTERN (avsystem_sink_debug);
-#define GST_CAT_DEFAULT avsystem_sink_debug
-
-#define DEFAULT_USER_ROUTE AVSYSAUDIOSINK_USERROUTE_AUTO
-#define DEFAULT_AUDIO_ROUTE AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING
-#define DEFAULT_VOLUME_TYPE AVSYS_AUDIO_VOLUME_TYPE_MEDIA
-#define DEFAULT_MEDIACALL_MODE AVSYS_AUDIO_ECHO_MODE_NONE
-#define DEFAULT_FADEUP_VOLUME FALSE
-#define DEFAULT_AUDIO_MUTE AVSYSAUDIOSINK_AUDIO_UNMUTE
-#define DEFAULT_AUDIO_LATENCY AVSYSAUDIOSINK_LATENCY_MID
-
-
-//GST_DEBUG_CATEGORY_STATIC (gst_avsystemsink_debug);
-
-/* element factory information */
-static const GstElementDetails gst_avsysaudiosink_details =
- GST_ELEMENT_DETAILS ("AV-system Audio OUT",
- "Sink/Audio",
- "Output to AV System",
- "Samsung Electronics co., ltd");
-
-enum
-{
- PROP_0,
- PROP_AUDIO_MUTE,
- PROP_AUDIO_VOLUME_TYPE,
- PROP_AUDIO_PRIORITY,
- PROP_AUDIO_FADEUPVOLUME,
- PROP_AUDIO_ROUTE_POLICY,
- PROP_AUDIO_USER_ROUTE,
- PROP_AUDIO_LATENCY,
- PROP_AUDIO_HANDLE,
- PROP_AUDIO_CALLBACK
-};
-
-GType
-gst_avsysaudiosink_audio_mute_get_type (void)
-{
- static GType avaudio_mute_type = 0;
- static const GEnumValue avaudio_mute[] = {
- {AVSYSAUDIOSINK_AUDIO_UNMUTE, "Unmute", "unmute"},
- {AVSYSAUDIOSINK_AUDIO_MUTE, "Mute immediately", "mute"},
- {AVSYSAUDIOSINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT, "Mute with fadedown effect", "fadedown"},
- {0, NULL, NULL},
- };
-
- if (!avaudio_mute_type) {
- avaudio_mute_type =
- g_enum_register_static ("GstAvsysAudioSinkAudioMute", avaudio_mute);
- }
- return avaudio_mute_type;
-}
-
-GType
-gst_avsysaudiosink_user_route_get_type (void)
-{
- static GType user_route_type = 0;
- static const GEnumValue user_route[] = {
- {AVSYSAUDIOSINK_USERROUTE_AUTO, "Route automatically", "auto"},
- {AVSYSAUDIOSINK_USERROUTE_PHONE, "Route to phone only", "phone"},
- {0, NULL, NULL},
- };
-
- if (!user_route_type) {
- user_route_type =
- g_enum_register_static ("GstAvsysAudioSinkUserRoutePolicy",user_route);
- }
- return user_route_type;
-}
-
-GType
-gst_avsysaudiosink_audio_route_get_type (void)
-{
- static GType playback_audio_route_type = 0;
- static const GEnumValue playback_audio_route[] = {
- {AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING, "Use external sound path", "external"},
- {AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_NORMAL, "Auto change between speaker & earphone", "normal"},
- {AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT, "Play via both speaker & earphone", "alert"},
- {AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY, "Play via earphone only", "headset"},
- {0, NULL, NULL},
- };
-
- if (!playback_audio_route_type) {
- playback_audio_route_type =
- g_enum_register_static ("GstAvsysAudioSinkAudioRoutePolicy", playback_audio_route);
- }
- return playback_audio_route_type;
-}
-
-
-GType
-gst_avsysaudiosink_volume_table_get_type (void)
-{
- static GType avsysaudio_volume_table_type = 0;
- static const GEnumValue avsysaudio_volume_table[] = {
- {AVSYS_AUDIO_VOLUME_TYPE_SYSTEM, "System Volume", "system"},
- {AVSYS_AUDIO_VOLUME_TYPE_NOTIFICATION, "Notification Volume", "notification"},
- {AVSYS_AUDIO_VOLUME_TYPE_ALARM, "Alarm Volume", "alarm"},
- {AVSYS_AUDIO_VOLUME_TYPE_RINGTONE, "Ringtone Volume", "ringtone"},
- {AVSYS_AUDIO_VOLUME_TYPE_MEDIA, "Media Volume", "media"},
- {AVSYS_AUDIO_VOLUME_TYPE_CALL, "Call Volume", "call"},
- {AVSYS_AUDIO_VOLUME_TYPE_FIXED, "Fixed Volume", "fixed"},
- {AVSYS_AUDIO_VOLUME_TYPE_EXT_SYSTEM_JAVA, "Java Volume", "java"},
- {0, NULL, NULL},
- };
-
- if (!avsysaudio_volume_table_type) {
- avsysaudio_volume_table_type =
- g_enum_register_static ("GstAvsysAudioSinkVolumeTable", avsysaudio_volume_table);
- }
- return avsysaudio_volume_table_type;
-}
-
-GType
-gst_avsysaudiosink_latency_get_type (void)
-{
- static GType avsysaudio_latency_type = 0;
- static const GEnumValue avsysaudio_latency[] = {
- {AVSYSAUDIOSINK_LATENCY_LOW, "Low latency", "low"},
- {AVSYSAUDIOSINK_LATENCY_MID, "Mid latency", "mid"},
- {AVSYSAUDIOSINK_LATENCY_HIGH, "High latency", "high"},
- {0, NULL, NULL},
- };
-
- if (!avsysaudio_latency_type) {
- avsysaudio_latency_type =
- g_enum_register_static ("GstAvsysAudioSinkLatency", avsysaudio_latency);
- }
- return avsysaudio_latency_type;
-}
-
-static void gst_avsysaudiosink_init_interfaces (GType type);
-
-//#define GST_BOILERPLATE_FULL(type, type_as_function, parent_type, parent_type_macro, additional_initializations)
-
-GST_BOILERPLATE_FULL (GstAvsysAudioSink, gst_avsysaudiosink, GstAudioSink,
- GST_TYPE_AUDIO_SINK, gst_avsysaudiosink_init_interfaces);
-
-
-static void gst_avsysaudiosink_finalise (GObject * object);
-static void gst_avsysaudiosink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_avsysaudiosink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
-#if 0 /*not use*/
-static GstCaps *gst_avsysaudiosink_getcaps (GstBaseSink * bsink);
-#endif
-
-static gboolean gst_avsysaudiosink_avsys_close(GstAvsysAudioSink *avsys_audio);
-static gboolean gst_avsysaudiosink_avsys_open(GstAvsysAudioSink *avsys_audio);
-
-static gboolean gst_avsysaudiosink_open (GstAudioSink * asink);
-static gboolean gst_avsysaudiosink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec);
-static gboolean gst_avsysaudiosink_unprepare (GstAudioSink * asink);
-static gboolean gst_avsysaudiosink_close (GstAudioSink * asink);
-static guint gst_avsysaudiosink_write (GstAudioSink * asink, gpointer data, guint length);
-static guint gst_avsysaudiosink_delay (GstAudioSink * asink);
-static void gst_avsysaudiosink_reset (GstAudioSink * asink);
-static gboolean avsysaudiosink_post_message(GstAvsysAudioSink* self,int errorcode);
-
-
-#define AVSYS_AUDIO_FACTORY_ENDIANNESS "LITTLE_ENDIAN"
-
-
-static GstStaticPadTemplate avsysaudiosink_sink_factory =
- GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-int, "
- "endianness = (int) { " AVSYS_AUDIO_FACTORY_ENDIANNESS " }, "
- "signed = (boolean) { TRUE }, "
- "width = (int) 16, "
- "depth = (int) 16, "
- "rate = (int) [ 1, MAX ], "
- "channels = (int) [ 1, 2 ]; "
- "audio/x-raw-int, "
- "signed = (boolean) { FALSE }, "
- "width = (int) 8, "
- "depth = (int) 8, "
- "rate = (int) [ 1, MAX ], "
- "channels = (int) [ 1, 2 ] "
- )
- );
-/*
-static inline guint _time_to_sample(GstAvsysAudioSink * asink, GstClockTime diff)
-{
- guint result = 0;
- result =(GST_TIME_AS_USECONDS(diff) * asink->audio_param.samplerate)/1000000;
- return result;
-}
-*/
-
-static void
-gst_avsysaudiosink_finalise (GObject * object)
-{
- GstAvsysAudioSink *sink = NULL;
-
- sink = GST_AVSYS_AUDIO_SINK (object);
- gst_avsysaudiosink_avsys_close(sink);
- g_mutex_free (sink->avsys_audio_lock);
- g_mutex_free (sink->avsys_audio_reset_lock);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_avsysaudiosink_init_interfaces (GType type)
-{
- /* None */
-}
-
-static void
-gst_avsysaudiosink_base_init (gpointer g_class)
-{
- GstElementClass *element_class = NULL;
-
- element_class = GST_ELEMENT_CLASS (g_class);
- gst_element_class_set_details (element_class, &gst_avsysaudiosink_details);
- gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&avsysaudiosink_sink_factory));
-}
-
-static GstStateChangeReturn
-gst_avsyssudiosink_change_state (GstElement *element, GstStateChange transition);
-
-
-static void
-gst_avsysaudiosink_class_init (GstAvsysAudioSinkClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
- GstBaseSinkClass *gstbasesink_class;
- GstBaseAudioSinkClass *gstbaseaudiosink_class;
- GstAudioSinkClass *gstaudiosink_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
- gstbasesink_class = (GstBaseSinkClass *) klass;
- gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
- gstaudiosink_class = (GstAudioSinkClass *) klass;
-
- parent_class = g_type_class_peek_parent (klass);
- gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_avsyssudiosink_change_state);
-
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_finalise);
- gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_get_property);
- gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_set_property);
-
-// gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_getcaps);
-
- gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_open);
- gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_prepare);
- gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_unprepare);
- gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_close);
- gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_write);
- gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_delay);
- gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_avsysaudiosink_reset);
-
- g_object_class_install_property ( gobject_class, PROP_AUDIO_VOLUME_TYPE,
- g_param_spec_enum ("volumetype", "Avsystem Volume Type",
- "Select avsystem audio software volume type",
- GST_AVSYS_AUDIO_SINK_VOLUME_TYPE, DEFAULT_VOLUME_TYPE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property ( gobject_class, PROP_AUDIO_PRIORITY,
- g_param_spec_int ("priority", "Avsystem Sound Priority", "Avsystem sound priority",
- AVSYS_AUDIO_PRIORITY_NORMAL, AVSYS_AUDIO_PRIORITY_SOLO_WITH_TRANSITION_EFFECT,
- AVSYS_AUDIO_PRIORITY_NORMAL, G_PARAM_READWRITE));
-
- g_object_class_install_property ( gobject_class, PROP_AUDIO_HANDLE,
- g_param_spec_pointer("audio-handle", "Avsystem handle",
- "Avsystem audio handle",
- G_PARAM_READWRITE));
-
- g_object_class_install_property ( gobject_class, PROP_AUDIO_CALLBACK,
- g_param_spec_pointer("audio-callback", "Avsystem callback",
- "Avsystem audio callback",
- G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_AUDIO_FADEUPVOLUME,
- g_param_spec_boolean ("fadeup", "Avsystem fadeup volume",
- "Enable avsystem audio fadeup volume when pause to play",
- DEFAULT_FADEUP_VOLUME, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class ,PROP_AUDIO_MUTE,
- g_param_spec_enum("mute", "Avsystem mute",
- "Avsystem audio mute",
- GST_AVSYS_AUDIO_SINK_MUTE, DEFAULT_AUDIO_MUTE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
-
- g_object_class_install_property (gobject_class ,PROP_AUDIO_ROUTE_POLICY,
- g_param_spec_enum("audio-route", "Audio Route Policy",
- "Audio route policy of system",
- GST_AVSYS_AUDIO_SINK_AUDIO_ROUTE, DEFAULT_AUDIO_ROUTE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
-
- g_object_class_install_property (gobject_class ,PROP_AUDIO_USER_ROUTE,
- g_param_spec_enum("user-route", "User Route Policy",
- "User route policy",
- GST_AVSYS_AUDIO_SINK_USER_ROUTE, DEFAULT_USER_ROUTE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
-
- g_object_class_install_property (gobject_class ,PROP_AUDIO_LATENCY,
- g_param_spec_enum("latency", "Audio Backend Latency",
- "Audio backend latency",
- GST_AVSYS_AUDIO_SINK_LATENCY_TYPE, DEFAULT_AUDIO_LATENCY,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
-}
-
-static void
-gst_avsysaudiosink_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstAvsysAudioSink *sink = NULL;
- int nvalue = 0;
- gboolean nbool = FALSE;
-
- sink = GST_AVSYS_AUDIO_SINK (object);
-
- switch (prop_id)
- {
- case PROP_AUDIO_HANDLE:
- sink->cbHandle = g_value_get_pointer(value);
- break;
- case PROP_AUDIO_CALLBACK:
- sink->audio_stream_cb = g_value_get_pointer(value);
- break;
-
- case PROP_AUDIO_VOLUME_TYPE:
- nvalue = g_value_get_enum(value);
- sink->volume_type = nvalue;
- break;
-
- case PROP_AUDIO_PRIORITY:
- nvalue = g_value_get_int(value);
- sink->sound_priority = nvalue;
- break;
-
- case PROP_AUDIO_MUTE:
- nvalue = g_value_get_enum(value);
- if(sink->audio_handle != (avsys_handle_t)-1)
- {
- if(AVSYS_SUCCESS(avsys_audio_set_mute_fadedown(sink->audio_handle)))
- sink->mute = nvalue;
- }
- else
- {
- sink->mute = nvalue;
- }
- break;
- case PROP_AUDIO_FADEUPVOLUME:
- nbool = g_value_get_boolean(value);
- sink->use_fadeup_volume = nbool;
- break;
-
- case PROP_AUDIO_ROUTE_POLICY:
- nvalue = g_value_get_enum(value);
- sink->audio_route_policy = nvalue;
- switch(sink->audio_route_policy)
- {
- case AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING:
- GST_INFO_OBJECT(sink, "use external audio route setting");
- 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);
- break;
- }
- break;
- case PROP_AUDIO_USER_ROUTE:
- nvalue = g_value_get_enum(value);
- sink->user_route_policy = nvalue;
- break;
- case PROP_AUDIO_LATENCY:
- nvalue = g_value_get_enum(value);
- sink->latency = nvalue;
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_avsysaudiosink_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- GstAvsysAudioSink *sink = NULL;
-
- sink = GST_AVSYS_AUDIO_SINK (object);
-
- switch (prop_id) {
- case PROP_AUDIO_VOLUME_TYPE:
- g_value_set_enum(value, sink->volume_type);
- break;
- case PROP_AUDIO_PRIORITY:
- g_value_set_int(value, sink->sound_priority);
- break;
- case PROP_AUDIO_MUTE:
- g_value_set_enum(value, sink->mute);
- break;
- case PROP_AUDIO_FADEUPVOLUME:
- g_value_set_boolean(value, sink->use_fadeup_volume);
- break;
-
- case PROP_AUDIO_ROUTE_POLICY:
- g_value_set_enum(value, sink->audio_route_policy);
- break;
- case PROP_AUDIO_USER_ROUTE:
- g_value_set_enum(value, sink->user_route_policy);
- break;
- case PROP_AUDIO_LATENCY:
- g_value_set_enum(value, sink->latency);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_avsysaudiosink_init (GstAvsysAudioSink * avsysaudiosink, GstAvsysAudioSinkClass * g_class)
-{
- GST_DEBUG_OBJECT (avsysaudiosink, "initializing avsysaudiosink");
-
- avsysaudiosink->audio_handle = (avsys_handle_t)-1;
- avsysaudiosink->cached_caps = NULL;
- avsysaudiosink->avsys_audio_lock = g_mutex_new ();
- avsysaudiosink->avsys_audio_reset_lock = g_mutex_new ();
- avsysaudiosink->volume_type = DEFAULT_VOLUME_TYPE;
- avsysaudiosink->sound_priority = AVSYS_AUDIO_PRIORITY_NORMAL;
- avsysaudiosink->mute = DEFAULT_AUDIO_MUTE;
- avsysaudiosink->use_fadeup_volume = DEFAULT_FADEUP_VOLUME;
- avsysaudiosink->latency = DEFAULT_AUDIO_LATENCY;
- avsysaudiosink->audio_route_policy = DEFAULT_AUDIO_ROUTE;
- avsysaudiosink->bytes_per_sample = 1;
-#if defined (LPCM_DUMP_SUPPORT)
- avsysaudiosink->dumpFp = NULL;
-#endif
-
-}
-#if 0
-static GstCaps *
-gst_avsysaudiosink_getcaps (GstBaseSink * bsink)
-{
- GstElementClass *element_class = NULL;
- GstPadTemplate *pad_template = NULL;
- GstAvsysAudioSink *sink = GST_AVSYS_AUDIO_SINK (bsink);
- GstCaps *caps;
-
-// debug_fenter();
-
- sink = GST_AVSYS_AUDIO_SINK (bsink);
- if (sink->audio_handle == -1)
- {
- GST_DEBUG_OBJECT (sink, "avsystem audio not open, using template caps");
- return NULL; /* base class will get template caps for us */
- }
-
- if (sink->cached_caps)
- {
- GST_LOG_OBJECT (sink, "Returning cached caps");
- return gst_caps_ref (sink->cached_caps);
- }
-
- element_class = GST_ELEMENT_GET_CLASS (sink);
- pad_template = gst_element_class_get_pad_template (element_class, "sink");
- g_return_val_if_fail (pad_template != NULL, NULL);
-
- // todo : get supported format.
- //caps = gst_avsysaudio_probe_supported_formats (GST_OBJECT (sink), sink->,
- //gst_pad_template_get_caps (pad_template));
-
- //if (caps) {
- //sink->cached_caps = gst_caps_ref (caps);
- //}
-
- GST_INFO_OBJECT (sink, "returning caps %" GST_PTR_FORMAT, caps);
-
- return caps;
-}
-#endif
-
-static gboolean
-avsysaudiosink_parse_spec (GstAvsysAudioSink * avsys_audio, GstRingBufferSpec * spec)
-{
- /* Check param */
- if (spec->type != GST_BUFTYPE_LINEAR ||
- spec->channels > 2 || spec->channels < 1 ||
- !(spec->format == GST_S8 || spec->format == GST_S16_LE) )
- return FALSE;
-
- switch(spec->format)
- {
- case GST_S8:
- avsys_audio->audio_param.format = AVSYS_AUDIO_FORMAT_8BIT;
- avsys_audio->bytes_per_sample = 1;
- break;
- case GST_S16_LE:
- avsys_audio->audio_param.format = AVSYS_AUDIO_FORMAT_16BIT;
- avsys_audio->bytes_per_sample = 2;
- break;
- default:
- return FALSE;
- }
-
- /// set audio parameter for avsys audio open
- switch(avsys_audio->latency)
- {
- case AVSYSAUDIOSINK_LATENCY_LOW:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_OUTPUT_VIDEO;
- break;
- case AVSYSAUDIOSINK_LATENCY_MID:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_OUTPUT;
- break;
- case AVSYSAUDIOSINK_LATENCY_HIGH:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_OUTPUT_CLOCK;
- break;
- }
-
-
-
- avsys_audio->audio_param.priority = 0;
- avsys_audio->audio_param.samplerate = spec->rate;
- avsys_audio->audio_param.channels = spec->channels;
-
- if(spec->channels == 2)
- avsys_audio->bytes_per_sample *= 2;
- else if(spec->channels > 2)
- {
- GST_ERROR_OBJECT(avsys_audio,"Unsupported channel number %d", spec->channels);
- return FALSE;
- }
-
- /* 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.handle_route = avsys_audio->user_route_policy;
-
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosink_open (GstAudioSink * asink)
-{
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
- guint p_time = 0, b_time = 0;
-
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
-
- // set avsys audio param
- if (!avsysaudiosink_parse_spec (avsys_audio, spec))
- goto spec_parse;
-
- if (gst_avsysaudiosink_avsys_open(avsys_audio) == FALSE)
- {
- GST_ERROR_OBJECT(avsys_audio, "gst_avsysaudiosink_avsys_open() failed");
- return FALSE;
- }
-
-
- /* Ring buffer size */
- if (AVSYS_STATE_SUCCESS ==
- avsys_audio_get_period_buffer_time(avsys_audio->audio_handle, &p_time, &b_time))
- {
- if(p_time == 0 || b_time == 0)
- return FALSE;
-
- spec->latency_time = (guint64)p_time;
- spec->buffer_time = (guint64)b_time;
- }
- else
- {
- return FALSE;
- }
- spec->segsize = avsys_audio->avsys_size; /* '/16' see avsys_audio_open */
- spec->segtotal = (b_time / p_time) + (((b_time % p_time)/p_time > 0.5) ? 1: 0);
- //spec->segtotal+2;
-
- GST_WARNING_OBJECT (avsys_audio, "latency time %u, buffer time %u, seg total %u\n",
- (unsigned int)(spec->latency_time/1000), (unsigned int)(spec->buffer_time/1000), spec->segtotal);
- return TRUE;
-
-spec_parse:
- {
- GST_ELEMENT_ERROR (avsys_audio, RESOURCE, SETTINGS, (NULL),
- ("Setting of swparams failed: " ));
- return FALSE;
- }
-}
-
-static gboolean
-gst_avsysaudiosink_unprepare (GstAudioSink * asink)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
- gboolean result = TRUE;
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
-
- if(!gst_avsysaudiosink_avsys_close(avsys_audio))
- {
- GST_ERROR_OBJECT(avsys_audio, "gst_avsysaudiosink_avsys_close() failed");
- result = FALSE;
- }
-
- return result;
-}
-
-static gboolean
-gst_avsysaudiosink_close (GstAudioSink * asink)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
-
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
- gst_caps_replace (&avsys_audio->cached_caps, NULL);
-
- return TRUE;
-}
-
-
-/*
- * Underrun and suspend recovery
- */
-
-static guint
-gst_avsysaudiosink_write (GstAudioSink * asink, gpointer data, guint length)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
- gint write_len = 0;
-
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
- GST_AVSYS_AUDIO_SINK_LOCK (asink);
-
- if(avsys_audio->audio_stream_cb == NULL)
- {
- write_len = avsys_audio_write(avsys_audio->audio_handle, data, length);
-
-#if defined (LPCM_DUMP_SUPPORT)
- fwrite(data, 1, write_len, avsys_audio->dumpFp); //This is for original data (no volume convert)
-#endif
- if(write_len != length)
- {
- goto write_error;
- }
-
- GST_AVSYS_AUDIO_SINK_UNLOCK (asink);
-
- return write_len;
-write_error:
- {
- GST_AVSYS_AUDIO_SINK_UNLOCK (asink);
-
- if(AVSYS_FAIL(write_len))
- {
- GST_ERROR_OBJECT(avsys_audio, "avsys_audio_write() failed with %d\n", write_len);
- }
- return length; /* skip one period */
-
- }
- }
- else
- {
- gboolean result;
- result = avsys_audio->audio_stream_cb(data, length, avsys_audio->cbHandle);
- if(!result)
- {
- GST_ERROR_OBJECT(avsys_audio,"auido stream callback failed\n");
- }
- GST_AVSYS_AUDIO_SINK_UNLOCK (asink);
- return length;
- }
-}
-
-static guint
-gst_avsysaudiosink_delay (GstAudioSink * asink)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
- int delay = 0;
- guint retValue = 0;
-
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
- GST_AVSYS_AUDIO_SINK_RESET_LOCK (asink);
- if((int)avsys_audio->audio_handle != -1) {
- if(AVSYS_STATE_SUCCESS == avsys_audio_delay(avsys_audio->audio_handle, &delay)) {
- retValue = delay;
- }
- }
- GST_AVSYS_AUDIO_SINK_RESET_UNLOCK (asink);
- return retValue;
-}
-
-static void
-gst_avsysaudiosink_reset (GstAudioSink * asink)
-{
- GstAvsysAudioSink *avsys_audio = NULL;
- int avsys_result = AVSYS_STATE_SUCCESS;
-
- GST_AVSYS_AUDIO_SINK_LOCK (asink);
- avsys_audio = GST_AVSYS_AUDIO_SINK (asink);
-
-#if defined(__REPLACE_RESET_WITH_CLOSE_AND_REOPEN__)
- GST_AVSYS_AUDIO_SINK_RESET_LOCK (asink);
- avsys_result = avsys_audio_close (avsys_audio->audio_handle);
- if(AVSYS_FAIL(avsys_result))
- {
- GST_ERROR_OBJECT (avsys_audio, "avsys_audio_close: internal error: ");
- }
- else
- {
- avsys_audio->audio_handle =(avsys_handle_t) -1;
- }
- GST_AVSYS_AUDIO_SINK_RESET_UNLOCK (asink);
-#else
- if(AVSYS_STATE_SUCCESS != avsys_audio_reset(avsys_audio->audio_handle))
- {
- GST_ERROR_OBJECT (avsys_audio, "avsys-reset: internal error: ");
- }
-#endif
-
- GST_AVSYS_AUDIO_SINK_UNLOCK (asink);
-
- return;
-}
-
-
-static gboolean
-gst_avsysaudiosink_avsys_open(GstAvsysAudioSink *avsys_audio)
-{
- int avsys_result;
-
- GST_AVSYS_AUDIO_SINK_LOCK(avsys_audio);
- if(avsys_audio->audio_handle == (avsys_handle_t)-1) {
-
- GST_LOG_OBJECT (avsys_audio, "avsys_audio_open() with user policy [%d] ", avsys_audio->user_route_policy);
-
- avsys_result = avsys_audio_open(&avsys_audio->audio_param, &avsys_audio->audio_handle, &avsys_audio->avsys_size);
- if(avsys_result != AVSYS_STATE_SUCCESS)
- {
- GST_AVSYS_AUDIO_SINK_UNLOCK (avsys_audio);
- avsysaudiosink_post_message(avsys_audio, avsys_result);
- return FALSE;
- }
-
- GST_LOG_OBJECT (avsys_audio, "Opened av system ");
-
- GST_AVSYS_AUDIO_SINK_UNLOCK (avsys_audio);
-
- }
- else {
- GST_WARNING_OBJECT(avsys_audio, "audio handle has already opened");
- GST_AVSYS_AUDIO_SINK_UNLOCK (avsys_audio);
- return FALSE;
- }
-#if defined (LPCM_DUMP_SUPPORT)
- if(avsys_audio->dumpFp == NULL)
- {
- avsys_audio->dumpFp = fopen("/root/dump.lpcm","w");
- }
-#endif
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosink_avsys_close(GstAvsysAudioSink *avsys_audio)
-{
- int avsys_result = AVSYS_STATE_SUCCESS;
-
- GST_AVSYS_AUDIO_SINK_LOCK (avsys_audio);
-
- if(avsys_audio->audio_handle != (avsys_handle_t)-1 )
- {
- avsys_result = avsys_audio_close(avsys_audio->audio_handle);
-
- if (AVSYS_FAIL(avsys_result))
- {
- GST_ERROR_OBJECT(avsys_audio,
- "avsys_audio_close() failed with 0x%x", avsys_result);
- GST_AVSYS_AUDIO_SINK_UNLOCK (avsys_audio);
- return FALSE;
- }
- else
- {
- avsys_audio->audio_handle = (avsys_handle_t) -1;
- GST_INFO_OBJECT(avsys_audio, "avsys_audio_close() success");
- }
-
- GST_LOG_OBJECT (avsys_audio, "Closed av system ");
- }
- else
- {
- GST_WARNING_OBJECT(avsys_audio, "audio handle has already closed");
- }
-
- GST_AVSYS_AUDIO_SINK_UNLOCK (avsys_audio);
-#if defined (LPCM_DUMP_SUPPORT)
- if(avsys_audio->dumpFp != NULL)
- {
- fclose(avsys_audio->dumpFp);
- avsys_audio->dumpFp = NULL;
- }
-#endif
- return TRUE;
-}
-
-static GstStateChangeReturn
-gst_avsyssudiosink_change_state (GstElement *element, GstStateChange transition)
-{
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- GstAvsysAudioSink *avsys_audio = GST_AVSYS_AUDIO_SINK (element);
-
- int avsys_result = AVSYS_STATE_SUCCESS;
-
- 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:
-#if defined(_ALSA_DAPM_)
- switch(avsys_audio->audio_route_policy)
- {
- case AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING:
- GST_INFO_OBJECT(avsys_audio, "audio route uses external setting");
- break;
- default:
- GST_ERROR_OBJECT(avsys_audio, "Unknown audio route option %d\n", avsys_audio->audio_route_policy);
- break;
- }
-#endif
-#if defined(__REPLACE_RESET_WITH_CLOSE_AND_REOPEN__)
- if(avsys_audio->audio_handle == (avsys_handle_t)-1)
- {
- avsys_result = avsys_audio_open(&avsys_audio->audio_param, &avsys_audio->audio_handle, &avsys_audio->avsys_size);
- if(AVSYS_FAIL(avsys_result))
- {
- GST_ERROR_OBJECT (avsys_audio, "avsys_audio_open: internal error: ");
- return GST_STATE_CHANGE_FAILURE;
- }
- }
-#endif
-
- if(avsys_audio->use_fadeup_volume) {
- GST_INFO_OBJECT(avsys_audio, "Set fadeup volume");
- avsys_audio_set_volume_fadeup(avsys_audio->audio_handle);
- }
-
- if(AVSYS_STATE_SUCCESS != avsys_audio_set_mute(avsys_audio->audio_handle, CONVERT_MUTE_VALUE(avsys_audio->mute)))
- {
- GST_ERROR_OBJECT(avsys_audio, "Set mute failed %d", CONVERT_MUTE_VALUE(avsys_audio->mute));
- }
- break;
- default:
- break;
- }
- ret = GST_ELEMENT_CLASS(parent_class)->change_state (element, transition);
- if (ret == GST_STATE_CHANGE_FAILURE)
- {
- return ret;
- }
- 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 ret;
-}
-
-
-static gboolean
-avsysaudiosink_post_message(GstAvsysAudioSink* self,int errorcode)
-{
- GST_DEBUG("avsysaudiosink_post_message\n");
- gboolean ret = TRUE;
- GstMessage *Msg = NULL;
- GQuark domain;
- gboolean status = FALSE;
- GError *error = NULL;
- gint error_code;
- /*
- if(errorcode>0)
- error_code = errorcode;
- else
- error_code = GST_STREAM_ERROR_TYPE_NOT_FOUND; */
- error_code = GST_RESOURCE_ERROR_FAILED;
- domain = gst_resource_error_quark();
- error = g_error_new (domain, error_code, "AVSYSAUDIOSINK_RESOURCE_ERROR");
- Msg = gst_message_new_error(GST_ELEMENT(self), error, "AVSYSAUDIOSINK_ERROR");
- status = gst_element_post_message (GST_ELEMENT(self), Msg);
- if (status == FALSE)
- {
- GST_ERROR("Error in posting message on the bus ...\n");
- ret = FALSE;
- }
-
- return ret;
-}
+++ /dev/null
-/*
- * avsystem
- *
- * 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_AVSYSAUDIOSINK_H__
-#define __GST_AVSYSAUDIOSINK_H__
-
-#include <gst/gst.h>
-#include <gst/audio/gstaudiosink.h>
-
-#include <avsys-audio.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_AVSYS_AUDIO_SINK (gst_avsysaudiosink_get_type())
-#define GST_AVSYS_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVSYS_AUDIO_SINK,GstAvsysAudioSink))
-#define GST_AVSYS_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVSYS_AUDIO_SINK,GstAvsysAudioSinkClass))
-#define GST_IS_AVSYS_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVSYS_AUDIO_SINK))
-#define GST_IS_AVSYS_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVSYS_AUDIO_SINK))
-//#define GST_AVSYS_AUDIO_SINK_CAST(obj) ((GstAvsysAudioSink *) (obj))
-
-typedef struct _GstAvsysAudioSink GstAvsysAudioSink;
-typedef struct _GstAvsysAudioSinkClass GstAvsysAudioSinkClass;
-
-#define GST_AVSYS_AUDIO_SINK_GET_LOCK(obj) (GST_AVSYS_AUDIO_SINK (obj)->avsys_audio_lock)
-#define GST_AVSYS_AUDIO_SINK_LOCK(obj) (g_mutex_lock (GST_AVSYS_AUDIO_SINK_GET_LOCK(obj)))
-#define GST_AVSYS_AUDIO_SINK_UNLOCK(obj) (g_mutex_unlock (GST_AVSYS_AUDIO_SINK_GET_LOCK(obj)))
-
-#define GST_AVSYS_AUDIO_SINK_GET_RESET_LOCK(obj) (GST_AVSYS_AUDIO_SINK (obj)->avsys_audio_reset_lock)
-#define GST_AVSYS_AUDIO_SINK_RESET_LOCK(obj) (g_mutex_lock (GST_AVSYS_AUDIO_SINK_GET_RESET_LOCK(obj)))
-#define GST_AVSYS_AUDIO_SINK_RESET_UNLOCK(obj) (g_mutex_unlock (GST_AVSYS_AUDIO_SINK_GET_RESET_LOCK(obj)))
-
-
-typedef enum {
- AVSYSAUDIOSINK_AUDIO_UNMUTE = 0,
- AVSYSAUDIOSINK_AUDIO_MUTE,
- AVSYSAUDIOSINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT,
-}GstAvsysAudioSinkAudioMute;
-
-
-typedef enum {
- AVSYSAUDIOSINK_AUDIOROUTE_USE_EXTERNAL_SETTING = -1,
- AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_NORMAL,
- AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_ALERT,
- AVSYSAUDIOSINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY
-}GstAvsysAudioSinkAudioRoutePolicy;
-
-
-typedef enum {
- AVSYSAUDIOSINK_USERROUTE_AUTO = 0,
- AVSYSAUDIOSINK_USERROUTE_PHONE
-}GstAvsysAudioSinkUserRoutePolicy;
-
-typedef enum {
- AVSYSAUDIOSINK_LATENCY_LOW = 0,
- AVSYSAUDIOSINK_LATENCY_MID,
- AVSYSAUDIOSINK_LATENCY_HIGH,
-}GstAvsysAudioSinkLatency;
-
-#define GST_AVSYS_AUDIO_SINK_USER_ROUTE (gst_avsysaudiosink_user_route_get_type ())
-#define GST_AVSYS_AUDIO_SINK_AUDIO_ROUTE (gst_avsysaudiosink_audio_route_get_type ())
-#define GST_AVSYS_AUDIO_SINK_VOLUME_TABLE (gst_avsysaudiosink_volume_table_get_type ())
-#define GST_AVSYS_AUDIO_SINK_VOLUME_TYPE (gst_avsysaudiosink_volume_table_get_type ())
-#define GST_AVSYS_AUDIO_SINK_MUTE (gst_avsysaudiosink_audio_mute_get_type())
-#define GST_AVSYS_AUDIO_SINK_LATENCY_TYPE (gst_avsysaudiosink_latency_get_type ())
-
-//this define if for debugging
-//#define LPCM_DUMP_SUPPORT
-struct _GstAvsysAudioSink {
- GstAudioSink sink;
-
- avsys_handle_t audio_handle;
- avsys_audio_param_t audio_param;
- gint avsys_size;
- gint mute;
- gint use_fadeup_volume;
- gint audio_route_policy;
- gint user_route_policy;
- gint latency;
-
- GstCaps *cached_caps;
-
- GMutex *avsys_audio_lock;
- GMutex *avsys_audio_reset_lock;
- gint volume_type;
- gint sound_priority;
- gint bytes_per_sample;
-
- gpointer cbHandle;
- gboolean (*audio_stream_cb) (void *stream, int stream_size, void *user_param);
-};
-
-
-struct _GstAvsysAudioSinkClass {
- GstAudioSinkClass parent_class;
-};
-
-GType gst_avsysaudiosink_get_type (void);
-GType gst_avsysaudiosink_audio_route_get_type (void);
-GType gst_avsysaudiosink_volume_table_get_type (void);
-GType gst_avsysaudiosink_audio_mute_get_type(void);
-GType gst_avsysaudiosink_latency_get_type (void);
-
-G_END_DECLS
-
-#endif /* __GST_AVSYSAUDIOSINK_H__ */
+++ /dev/null
-/*
- * avsystem
- *
- * 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
- *
- */
-
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <gst/gst.h>
-#include <gst/gstutils.h>
-//#include <mm_debug.h>
-
-#include <sys/time.h> /*gettimeofday*/
-#include <unistd.h>
-
-#include "gstavsysaudiosrc.h"
-
-/**
- * Define for Release
- *
- * _ENABLE_FAKE_READ: enable fake read instead of avsys_audio_read(). (default: 0)
- */
-#define _ENABLE_FAKE_READ 0
-//#define USE_GPT8
-
-
-#define _MIN_RATE 8000
-#define _MAX_RATE 48000
-#define _MIN_CHANNEL 1
-#define _MAX_CHANNEL 2
-
-
-#define DEFAULT_GPT8_FREQ 26
-
-#if _ENABLE_FAKE_READ
-static long g_delay_time;
-#endif
-
-#define DEFAULT_MEDIACALL_MODE AVSYS_AUDIO_ECHO_MODE_NONE
-#define DEFAULT_AUDIO_LATENCY AVSYSAUDIOSRC_LATENCY_MID
-
-GST_DEBUG_CATEGORY_EXTERN (avsystem_src_debug);
-#define GST_CAT_DEFAULT avsystem_src_debug
-
-/* elementfactory information */
-static const GstElementDetails gst_avsysaudiosrc_details =
- GST_ELEMENT_DETAILS ("AV system source",
- "Source/Audio",
- "Read from a AV system audio in",
- "Samsung Electronics co., ltd.");
-
-
-enum {
- PROP_0,
-#if 0
-#if !defined(I386_SIMULATOR)
- PROP_AUDIO_MEDIA_CALL,
-#endif
-#endif
- PROP_AUDIO_LATENCY,
-};
-
-enum {
- CAPTURE_UNCORK = 0,
- CAPTURE_CORK,
-};
-
-GType
-gst_avsysaudiosrc_audio_latency_get_type (void)
-{
- static GType capture_audio_latency_type = 0;
- static const GEnumValue capture_audio_latency[] = {
- {AVSYSAUDIOSRC_LATENCY_LOW, "Set capture latency as low", "low"},
- {AVSYSAUDIOSRC_LATENCY_MID, "Set capture latency as mid", "mid"},
- {AVSYSAUDIOSRC_LATENCY_HIGH, "Set capture latency as high", "high"},
- {0, NULL, NULL},
- };
-
- if (!capture_audio_latency_type) {
- capture_audio_latency_type =
- g_enum_register_static ("GstAvsysAudioSrcAudioLatency", capture_audio_latency);
- }
- return capture_audio_latency_type;
-}
-
-
-GST_BOILERPLATE (GstAvsysAudioSrc, gst_avsysaudiosrc, GstAudioSrc, GST_TYPE_AUDIO_SRC);
-
-#if _ENABLE_FAKE_READ
-static unsigned long fake_delay (unsigned long usec);
-#endif
-
-static guint gst_avsysaudiosrc_delay (GstAudioSrc *asrc);
-
-static void gst_avsysaudiosrc_finalize (GObject * object);
-static void gst_avsysaudiosrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_avsysaudiosrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstCaps* gst_avsysaudiosrc_getcaps (GstBaseSrc * bsrc);
-
-static gboolean gst_avsysaudiosrc_open (GstAudioSrc * asrc);
-static gboolean gst_avsysaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec);
-static gboolean gst_avsysaudiosrc_unprepare (GstAudioSrc * asrc);
-static gboolean gst_avsysaudiosrc_close (GstAudioSrc * asrc);
-static guint gst_avsysaudiosrc_read (GstAudioSrc * asrc, gpointer data, guint length);
-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);
-static GstCaps *gst_avsysaudiosrc_detect_formats (GstObject * obj,avsys_pcm_hw_params_t * hw_params, GstCaps * in_caps);
-static GstCaps *gst_avsysaudiosrc_probe_supported_formats (GstObject * obj, avsys_handle_t handle, const GstCaps * template_caps);
-#endif
-static GstStateChangeReturn gst_avsys_src_change_state (GstElement * element, GstStateChange transition) ;
-
-
-
-/* AvsysAudioSrc signals and args */
-enum {
- LAST_SIGNAL
-};
-
-# define AVSYS_AUDIO_SRC_FACTORY_ENDIANNESS "LITTLE_ENDIAN"
-
-static GstStaticPadTemplate avsysaudiosrc_src_factory =
- GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (
- "audio/x-raw-int, "
- "endianness = (int) { " AVSYS_AUDIO_SRC_FACTORY_ENDIANNESS " }, "
- "signed = (boolean) { TRUE }, "
- "width = (int) 16, "
- "depth = (int) 16, "
- "rate = (int) [ 8000, 48000 ], "
- "channels = (int) [ 1, 2 ]; "
- "audio/x-raw-int, "
- "signed = (boolean) { FALSE }, "
- "width = (int) 8, "
- "depth = (int) 8, "
- "rate = (int) [ 8000, 48000 ], "
- "channels = (int) [ 1, 2 ]")
- );
-
-static inline guint _time_to_sample(GstAvsysAudioSrc *avsyssrc, GstClockTime diff)
-{
- guint result = 0;
- // sample rate : asrc->audio_param.samplerate
- result =(GST_TIME_AS_USECONDS(diff) * avsyssrc->audio_param.samplerate)/1000000;
- return result;
-}
-
-
-static void
-gst_avsysaudiosrc_finalize (GObject * object)
-{
- GstAvsysAudioSrc *src = NULL;
-
-
- src = GST_AVSYS_AUDIO_SRC (object);
- g_mutex_free (src->avsysaudio_lock);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_avsysaudiosrc_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_details (element_class, &gst_avsysaudiosrc_details);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&avsysaudiosrc_src_factory));
-}
-
-static void
-gst_avsysaudiosrc_class_init (GstAvsysAudioSrcClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
- GstBaseSrcClass *gstbasesrc_class;
- GstBaseAudioSrcClass *gstbaseaudiosrc_class;
- GstAudioSrcClass *gstaudiosrc_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
- gstbasesrc_class = (GstBaseSrcClass *) klass;
- gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
- gstaudiosrc_class = (GstAudioSrcClass *) klass;
-
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_avsys_src_change_state);
-
- parent_class = g_type_class_peek_parent (klass);
-
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_finalize);
- gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_get_property);
- gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_set_property);
-#if defined(_USE_CAPS_)
- gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_getcaps);
-#endif
- gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_open);
- gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_prepare);
- gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_unprepare);
- gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_close);
- gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_read);
- gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_delay);
- gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_avsysaudiosrc_reset);
-
- g_object_class_install_property (gobject_class ,PROP_AUDIO_LATENCY,
- g_param_spec_enum("latency", "Audio Latency",
- "Audio latency",
- GST_AVSYS_AUDIO_SRC_LATENCY, DEFAULT_AUDIO_LATENCY,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
-}
-
-
-
-/**
- *@note useful range 0 - 1000000 microsecond
- */
-#if _ENABLE_FAKE_READ
-static unsigned long
-fake_delay (unsigned long usec)
-{
- struct timeval s_time;
- struct timeval c_time;
- long s_sec = 0L;
- long s_usec = 0L;
- long c_sec = 0L;
- long c_usec = 0L;
- long t_sec = 0L;
- long t_usec = 0L;
- unsigned long total_usec = 0UL;
- unsigned long t = 0UL;
-
- if (usec == 0UL) {
- usleep (0);
- return 0;
- }
-
- /*get start time*/
- if (gettimeofday ((struct timeval *)&s_time, NULL) == 0) {
- s_sec = s_time.tv_sec;
- s_usec = s_time.tv_usec;
- } else {
- return 0;
- }
-
- for (;;) {
- /*get current time*/
- if (gettimeofday ((struct timeval *)&c_time, NULL) == 0) {
- c_sec = c_time.tv_sec;
- c_usec = c_time.tv_usec;
- } else {
- return 0;
- }
-
- /*get elasped sec*/
- t_sec = c_sec - s_sec;
-
- /*get elapsed usec*/
- if ((s_usec) > (c_usec)) {
- t_usec = 1000000L - (s_usec) + (c_usec);
- t_sec--;
- } else {
- t_usec = (c_usec) - (s_usec);
- }
-
- /*get total elapsed time*/
- total_usec = (t_sec * 1000000UL) + t_usec;
-
- t = usec - total_usec;
-
- if (total_usec >= usec) {
- break;
- } else {
- if (t > 10000UL) {
- /*this function does not work in precision*/
- usleep (1);
- }
- }
- }
-
- return total_usec;
-}
-#endif
-
-static guint
-gst_avsysaudiosrc_delay (GstAudioSrc *asrc)
-{
- GstAvsysAudioSrc *avsys_audio = NULL;
- int delay;
- guint retValue = 0;
-
- avsys_audio = GST_AVSYS_AUDIO_SRC (asrc);
- if(AVSYS_STATE_SUCCESS == avsys_audio_delay(avsys_audio->audio_handle, &delay))
- retValue = delay;
-
- return retValue;
-}
-
-
-
-static void
-gst_avsysaudiosrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstAvsysAudioSrc *src = NULL;
-// gint getvalue = 0;
- src = GST_AVSYS_AUDIO_SRC (object);
-
- if (src->cached_caps == NULL)
- {
- switch (prop_id)
- {
- case PROP_AUDIO_LATENCY:
- src->latency = g_value_get_enum(value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
- }
-}
-
-static void
-gst_avsysaudiosrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
-{
- GstAvsysAudioSrc *src;
-
- src = GST_AVSYS_AUDIO_SRC (object);
-
- switch (prop_id)
- {
- case PROP_AUDIO_LATENCY:
- g_value_set_enum(value, src->latency);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_avsysaudiosrc_init (GstAvsysAudioSrc * avsysaudiosrc, GstAvsysAudioSrcClass * g_class)
-{
- GST_DEBUG_OBJECT (avsysaudiosrc, "initializing");
-
- avsysaudiosrc->cached_caps = NULL;
- avsysaudiosrc->audio_handle = (avsys_handle_t)-1;
- avsysaudiosrc->buffer_size = 0;
- avsysaudiosrc->avsysaudio_lock = g_mutex_new ();
- avsysaudiosrc->latency = DEFAULT_AUDIO_LATENCY;
-}
-
-#if defined(_USE_CAPS_)
-static GstCaps *
-gst_avsysaudiosrc_getcaps (GstBaseSrc * bsrc)
-{
- GstElementClass *element_class;
- GstPadTemplate *pad_template;
- GstAvsysAudioSrc *src;
- GstCaps *caps = NULL;
-
- src = GST_AVSYS_AUDIO_SRC (bsrc);
-
- if(src->audio_handle==(avsys_handle_t)-1){
- GST_DEBUG_OBJECT(src,"device not open, using template caps");
- return NULL;
- }
-
- if (src->cached_caps)
- {
- GST_LOG_OBJECT (src, "Returning cached caps");
- return gst_caps_ref(src->cached_caps);
- }
- element_class = GST_ELEMENT_GET_CLASS (src);
- pad_template = gst_element_class_get_pad_template (element_class, "src");
- g_return_val_if_fail (pad_template != NULL, NULL);
-
- caps = gst_avsysaudiosrc_probe_supported_formats (GST_OBJECT (src), src->audio_handle,
- gst_pad_template_get_caps (pad_template));
-
- if (caps) {
- src->cached_caps = gst_caps_ref (caps);
- }
-
- GST_INFO_OBJECT (src, "returning caps %" GST_PTR_FORMAT, caps);
-
- return caps;
-}
-#endif
-static gboolean
-gst_avsysaudiosrc_open (GstAudioSrc * asrc)
-{
- return TRUE;
-}
-
-static gboolean
-avsysaudiosrc_parse_spec (GstAvsysAudioSrc *avsys_audio, GstRingBufferSpec * spec)
-{
- /* Check param */
- if (spec->type != GST_BUFTYPE_LINEAR ||
- spec->channels > 2 || spec->channels < 1 ||
- !(spec->format == GST_S8 || spec->format == GST_S16_LE) )
- return FALSE;
-
- switch(spec->format)
- {
- case GST_S8:
- avsys_audio->audio_param.format = AVSYS_AUDIO_FORMAT_8BIT;
- avsys_audio->bytes_per_sample = 1;
- break;
- case GST_S16_LE:
- avsys_audio->audio_param.format = AVSYS_AUDIO_FORMAT_16BIT;
- avsys_audio->bytes_per_sample = 2;
- break;
- default:
- GST_DEBUG_OBJECT(avsys_audio, "Only support S8, S16LE format");
- return FALSE;
- }
-
- // set audio parameter for avsys audio open
-
- switch(avsys_audio->latency)
- {
- case AVSYSAUDIOSRC_LATENCY_LOW:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_INPUT_LOW_LATENCY;
- break;
- case AVSYSAUDIOSRC_LATENCY_MID:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_INPUT;
- break;
- case AVSYSAUDIOSRC_LATENCY_HIGH:
- avsys_audio->audio_param.mode = AVSYS_AUDIO_MODE_INPUT_HIGH_LATENCY;
- break;
- default:
- break;
- }
-
- avsys_audio->audio_param.priority = 0;
- avsys_audio->audio_param.samplerate = spec->rate;
- avsys_audio->audio_param.channels = spec->channels;
-
- if(spec->channels == 2)
- avsys_audio->bytes_per_sample *= 2;
- else if(spec->channels > 2)
- {
- GST_ERROR_OBJECT(avsys_audio,"Unsupported channel number %d", spec->channels);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
-{
- GstAvsysAudioSrc *avsysaudiosrc = NULL;
- guint p_time = 0, b_time = 0;
-
- avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
-
- if (!avsysaudiosrc_parse_spec(avsysaudiosrc, spec))
- {
- GST_ERROR("avsysaudiosrc_parse_spec failed");
- return FALSE;
- }
-
- /*open avsys audio*/
- if (!gst_avsysaudiosrc_avsys_open (avsysaudiosrc))
- {
- GST_ERROR("gst_avsysaudiosrc_avsys_open failed");
- return FALSE;
- }
-
- /* 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;
-
- spec->latency_time = (guint64)p_time;
- spec->buffer_time = (guint64)b_time;
- }
- else
- {
- return FALSE;
- }
- spec->segsize = avsysaudiosrc->buffer_size;
- spec->segtotal = (b_time / p_time) + (((b_time % p_time)/p_time > 0.5) ? 1: 0);
- spec->segtotal += 2;
-
- GST_INFO_OBJECT(avsysaudiosrc, "audio buffer spec : latency_time(%llu), buffer_time(%llu), segtotal(%d), segsize(%d)\n",
- spec->latency_time, spec->buffer_time, spec->segtotal, spec->segsize);
-
-
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosrc_unprepare (GstAudioSrc * asrc)
-{
- GstAvsysAudioSrc *avsysaudiosrc = NULL;
- gboolean ret = TRUE;
-
- avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
-
- /*close*/
- GST_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
-
- if(!gst_avsysaudiosrc_avsys_close(avsysaudiosrc))
- {
- GST_ERROR_OBJECT(avsysaudiosrc, "gst_avsysaudiosrc_avsys_close failed");
- ret = FALSE;
- }
- GST_AVSYS_AUDIO_SRC_UNLOCK (asrc);
-
-
-
- return ret;
-}
-
-static gboolean
-gst_avsysaudiosrc_close (GstAudioSrc * asrc)
-{
- GstAvsysAudioSrc *avsysaudiosrc = NULL;
-
- avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
- gst_caps_replace (&avsysaudiosrc->cached_caps, NULL);
-
- return TRUE;
-}
-
-static void
-gst_avsysaudiosrc_reset (GstAudioSrc * asrc)
-{
- GstAvsysAudioSrc *avsys_audio = NULL;
-
- avsys_audio = GST_AVSYS_AUDIO_SRC (asrc);
- GST_AVSYS_AUDIO_SRC_LOCK (asrc);
-
- if(AVSYS_STATE_SUCCESS != avsys_audio_reset(avsys_audio->audio_handle))
- {
- GST_ERROR_OBJECT (avsys_audio, "avsys-reset: internal error: ");
- }
-
- GST_AVSYS_AUDIO_SRC_UNLOCK (asrc);
-
- return;
-}
-
-
-static GstStateChangeReturn
-gst_avsys_src_change_state (GstElement * element, GstStateChange transition)
-{
- GstAvsysAudioSrc *avsys = NULL;
- GstStateChangeReturn ret ;
-
- avsys = GST_AVSYS_AUDIO_SRC (element);
- GST_DEBUG("gst_avsys_src_change_state");
-
- switch (transition)
- {
- case GST_STATE_CHANGE_NULL_TO_READY:
- {
- GST_DEBUG ("GST_STATE_CHANGE_NULL_TO_READY\n") ;
- break ;
- }
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- {
- GST_DEBUG ("GST_STATE_CHANGE_READY_TO_PAUSED\n") ;
- break ;
- }
- 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:
- break ;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- GST_DEBUG ("After parent_class->change_state...\n") ;
- switch (transition)
- {
- 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:
- {
- GST_DEBUG ("GST_STATE_CHANGE_PAUSED_TO_READY\n") ;
- break ;
- }
- case GST_STATE_CHANGE_READY_TO_NULL:
- {
- GST_DEBUG ("GST_STATE_CHANGE_READY_TO_NULL\n") ;
- break ;
- }
- default:
- break ;
- }
-
- return ret;
-}
-
-
-
-static guint
-gst_avsysaudiosrc_read (GstAudioSrc * asrc, gpointer data, guint length)
-{
- GstAvsysAudioSrc *avsysaudiosrc = NULL;
- gint readed = 0;
- gpointer ptr = NULL;
- guint used_length = 0;
-
- avsysaudiosrc = GST_AVSYS_AUDIO_SRC (asrc);
-
- ptr = data;
-
- GST_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
-#if _ENABLE_FAKE_READ
- readed = avsysaudiosrc->buffer_size;
- memset (ptr, 10, avsysaudiosrc->buffer_size); /*maybe can't hear*/
- fake_delay (1000000UL / g_delay_time);
-#else
- ptr = data;
- readed = avsys_audio_read (avsysaudiosrc->audio_handle, ptr, length);
- if (readed < 0)
- goto _READ_ERROR;
-#endif //_ENABLE_FAKE_READ
- GST_AVSYS_AUDIO_SRC_UNLOCK (avsysaudiosrc);
-
- return readed;
-
-_READ_ERROR:
- {
- GST_AVSYS_AUDIO_SRC_UNLOCK (asrc);
- return length; /* skip one period */
- }
-}
-
-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_AVSYS_AUDIO_SRC_LOCK (avsysaudiosrc);
-
- avsys_result = avsys_audio_open(&avsysaudiosrc->audio_param, &avsysaudiosrc->audio_handle, &avsysaudiosrc->buffer_size);
-
- if (avsys_result != AVSYS_STATE_SUCCESS) {
- GST_ERROR_OBJECT(avsysaudiosrc, "avsys_audio_open() error. [0x%x]\n", avsys_result);
- GST_AVSYS_AUDIO_SRC_UNLOCK (avsysaudiosrc);
- return FALSE;
- }
-
-#if _ENABLE_FAKE_READ
- g_delay_time = (unsigned long)((avsysaudiosrc->samplerate * (avsysaudiosrc->format / 8) * avsysaudiosrc->channels) / avsysaudiosrc->buffer_size);
-#endif
-
- GST_AVSYS_AUDIO_SRC_UNLOCK (avsysaudiosrc);
- return TRUE;
-}
-
-static gboolean
-gst_avsysaudiosrc_avsys_close(GstAvsysAudioSrc *src)
-{
- int ret;
-
- if (src->audio_handle != (avsys_handle_t)-1)
- {
- /*close avsys audio*/
- ret = avsys_audio_close (src->audio_handle);
- if (AVSYS_FAIL(ret))
- {
- GST_ERROR_OBJECT(src, "avsys_audio_close() error 0x%x", ret);
- return FALSE;
- }
- src->audio_handle = (avsys_handle_t)-1;
- GST_DEBUG_OBJECT(src, "AVsys audio handle closed");
- }
- else
- {
- GST_WARNING_OBJECT(src,"avsys audio handle has already closed");
- }
-
- return TRUE;
-}
-#if defined(_USE_CAPS_)
-static GstCaps *
-gst_avsysaudiosrc_detect_rates(GstObject *obj,
- avsys_pcm_hw_params_t *hw_params, GstCaps *in_caps)
-{
- GstCaps *caps;
- guint min, max;
- gint err, dir, min_rate, max_rate, i;
-
- GST_LOG_OBJECT(obj, "probing sample rates ...");
-
- if ((err = avsys_pcm_hw_params_get_rate_min(hw_params, &min, &dir)) < 0)
- goto min_rate_err;
-
- if ((err = avsys_pcm_hw_params_get_rate_max(hw_params, &max, &dir)) < 0)
- goto max_rate_err;
-
- min_rate = min;
- max_rate = max;
-
- if (min_rate < _MIN_RATE)
- min_rate = _MIN_RATE; /* random 'sensible minimum' */
-
- if (max_rate <= 0)
- max_rate = _MAX_RATE; /* or maybe just use 192400 or so? */
- else if (max_rate > 0 && max_rate < _MIN_RATE)
- max_rate = MAX (_MIN_RATE, min_rate);
-
- GST_DEBUG_OBJECT(obj, "Min. rate = %u (%d)", min_rate, min);
- GST_DEBUG_OBJECT(obj, "Max. rate = %u (%d)", max_rate, max);
-
- caps = gst_caps_make_writable(in_caps);
-
- for (i = 0; i < gst_caps_get_size(caps); ++i)
- {
- GstStructure *s;
-
- s = gst_caps_get_structure(caps, i);
- if (min_rate == max_rate)
- {
- gst_structure_set(s, "rate", G_TYPE_INT, min_rate, NULL);
- }
- else
- {
- gst_structure_set(s, "rate", GST_TYPE_INT_RANGE, min_rate,
- max_rate, NULL);
- }
- }
-
- return caps;
- /* ERRORS */
- min_rate_err:
- {
- GST_ERROR_OBJECT(obj, "failed to query minimum sample rate");
- gst_caps_unref(in_caps);
- return NULL;
- }
- max_rate_err:
- {
- GST_ERROR_OBJECT(obj, "failed to query maximum sample rate");
- gst_caps_unref(in_caps);
- return NULL;
- }
-}
-
-
-static GstCaps *
-gst_avsysaudiosrc_detect_formats(GstObject * obj,
- avsys_pcm_hw_params_t * hw_params, GstCaps * in_caps)
-{
- avsys_pcm_format_mask_t *mask;
- GstStructure *s;
- GstCaps *caps;
- gint i;
-
- avsys_pcm_format_mask_malloc(&mask);
- avsys_pcm_hw_params_get_format_mask(hw_params, mask);
-
- caps = gst_caps_new_empty();
-
- for (i = 0; i < gst_caps_get_size(in_caps); ++i)
- {
- GstStructure *scopy;
- //gint w;
- gint width = 0, depth = 0;
- gint sndformat;
-
- s = gst_caps_get_structure(in_caps, i);
- if (!gst_structure_has_name(s, "audio/x-raw-int"))
- {
- GST_WARNING_OBJECT(obj, "skipping non-int format");
- continue;
- }
- if (!gst_structure_get_int(s, "width", &width)
- || !gst_structure_get_int(s, "depth", &depth))
- continue;
-
- GST_DEBUG_OBJECT(obj, "width = %d height = %d", width, depth);
- if (width == 8)
- sndformat = 0;//SND_PCM_FORMAT_S8
- else
- //width==16
- sndformat = 2; //SND_PCM_FORMAT_S16_LE
- if (avsys_pcm_format_mask_test(mask, sndformat))
- { //must be implemented
- /* template contains { true, false } or just one, leave it as it is */
- scopy = gst_structure_copy(s);
-
- }
- else
- {
- scopy = NULL;
- }
- if (scopy)
- {
- /* TODO: proper endianness detection, for now it's CPU endianness only */
- gst_structure_set(scopy, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
- gst_structure_set(scopy, "endianness", G_TYPE_INT, G_BYTE_ORDER,
- NULL);
- gst_caps_append_structure(caps, scopy);
- }
-
- }
-
- avsys_pcm_format_mask_free(mask);
- gst_caps_unref(in_caps);
- return caps;
-}
-
-
-static GstCaps *
-gst_avsysaudiosrc_detect_channels(GstObject * obj,
- avsys_pcm_hw_params_t * hw_params, GstCaps * in_caps)
-{
- GstCaps *caps;
- guint min, max;
- gint err, min_channel, max_channel, i;
-// gint dir;
-
- GST_LOG_OBJECT(obj, "probing sample rates ...");
-
- if ((err = avsys_pcm_hw_params_get_channels_min(hw_params, &min)) < 0)
- goto min_chan_err;
-
- if ((err = avsys_pcm_hw_params_get_channels_max(hw_params, &max)) < 0)
- goto max_chan_err;
-
- min_channel = min;
- max_channel = max;
-
- if (min_channel < _MIN_CHANNEL)
- min_channel = _MIN_CHANNEL; /* random 'sensible minimum' */
-
- if (max_channel <= 0)
- max_channel = _MAX_CHANNEL; /* or maybe just use 192400 or so? */
- else if (max_channel > 0 && max_channel < _MIN_CHANNEL)
- max_channel = MAX (_MAX_CHANNEL, min_channel);
-
- GST_DEBUG_OBJECT(obj, "Min. channel = %u (%d)", min_channel, min);
- GST_DEBUG_OBJECT(obj, "Max. channel = %u (%d)", max_channel, max);
-
- caps = gst_caps_make_writable(in_caps);
-
- for (i = 0; i < gst_caps_get_size(caps); ++i)
- {
- GstStructure *s;
-
- s = gst_caps_get_structure(caps, i);
- if (min_channel == max_channel)
- {
- gst_structure_set(s, "channels", G_TYPE_INT, _MIN_CHANNEL, NULL);
- }
- else
- {
- gst_structure_set(s, "channels", GST_TYPE_INT_RANGE, min_channel,
- max_channel, NULL);
- }
- }
-
- return caps;
-
- /* ERRORS */
- min_chan_err:
- {
- GST_ERROR_OBJECT(obj, "failed to query minimum sample rate");
- gst_caps_unref(in_caps);
- return NULL;
- }
- max_chan_err:
- {
- GST_ERROR_OBJECT(obj, "failed to query maximum sample rate:");
- gst_caps_unref(in_caps);
- return NULL;
- }
-}
-
-/*
- * gst_avsys_probe_supported_formats:
- *
- * Takes the template caps and returns the subset which is actually
- * supported by this device.
- *
- */
-
-static GstCaps *
-gst_avsysaudiosrc_probe_supported_formats(GstObject * obj,
- avsys_handle_t handle, const GstCaps * template_caps)
-{
-
- avsys_pcm_hw_params_t *hw_params;
- GstCaps *caps;
- gint err;
-
- avsys_pcm_hw_params_malloc(&hw_params);
- if ((err = avsys_pcm_hw_params_any(handle, hw_params)) < 0)
- goto error;
-
- caps = gst_caps_copy(template_caps);
-
- if (!(caps = gst_avsysaudiosrc_detect_formats(obj, hw_params, caps)))
- goto subroutine_error;
-
- if (!(caps = gst_avsysaudiosrc_detect_rates(obj, hw_params, caps)))
- goto subroutine_error;
-
- if (!(caps = gst_avsysaudiosrc_detect_channels(obj, hw_params, caps)))
- goto subroutine_error;
-
- avsys_pcm_hw_params_free(hw_params);
- return caps;
-
- /* ERRORS */
- error:
- {
- GST_ERROR_OBJECT(obj, "failed to query formats");
- return NULL;
- }
- subroutine_error:
- {
- GST_ERROR_OBJECT(obj, "failed to query formats");
- return NULL;
- }
-}
-#endif
+++ /dev/null
-/*
- * avsystem
- *
- * 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_AVSYS_AUDIO_SRC_H__
-#define __GST_AVSYS_AUDIO_SRC_H__
-
-//#undef _MMCAMCORDER_MERGE_TEMP
-
-#include <gst/audio/gstaudiosrc.h>
-#include <glib.h>
-
-#include <avsys-audio.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_AVSYS_AUDIO_SRC (gst_avsysaudiosrc_get_type())
-#define GST_AVSYS_AUDIO_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVSYS_AUDIO_SRC,GstAvsysAudioSrc))
-#define GST_AVSYS_AUDIO_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVSYS_AUDIO_SRC,GstAvsysAudioSrcClass))
-#define GST_IS_AVSYS_AUDIO_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVSYS_AUDIO_SRC))
-#define GST_IS_AVSYS_AUDIO_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVSYS_AUDIO_SRC))
-#define GST_AVSYS_AUDIO_SRC_CAST(obj) ((GstAvsysAudioSrc *)(obj))
-
-#define GST_AVSYS_AUDIO_SRC_GET_LOCK(obj) (GST_AVSYS_AUDIO_SRC_CAST (obj)->avsysaudio_lock)
-#define GST_AVSYS_AUDIO_SRC_LOCK(obj) (g_mutex_lock (GST_AVSYS_AUDIO_SRC_GET_LOCK (obj)))
-#define GST_AVSYS_AUDIO_SRC_UNLOCK(obj) (g_mutex_unlock (GST_AVSYS_AUDIO_SRC_GET_LOCK (obj)))
-
-typedef struct _GstAvsysAudioSrc GstAvsysAudioSrc;
-typedef struct _GstAvsysAudioSrcClass GstAvsysAudioSrcClass;
-
-typedef enum {
- AVSYSAUDIOSRC_LATENCY_LOW = 0,
- AVSYSAUDIOSRC_LATENCY_MID,
- AVSYSAUDIOSRC_LATENCY_HIGH,
-}GstAvsysAudioSrcAudioLatency;
-
-#define GST_AVSYS_AUDIO_SRC_LATENCY (gst_avsysaudiosrc_audio_latency_get_type())
-
-
-
-/**
- * GstAvsysAudioSrc:
- *
- * Opaque data structure
- */
-struct _GstAvsysAudioSrc {
- GstAudioSrc src;
-
- avsys_handle_t audio_handle;
- avsys_audio_param_t audio_param;
-
- gint buffer_size;
- GstCaps *cached_caps;
- GMutex *avsysaudio_lock;
-
- gint media_call;
- gint bytes_per_sample;
- gint latency;
-
-};
-
-struct _GstAvsysAudioSrcClass {
- GstAudioSrcClass parent_class;
-};
-
-
-GType gst_avsysaudiosrc_get_type (void);
-GType gst_avsysaudiosrc_audio_latency_get_type(void);
-
-G_END_DECLS
-
-#endif /* __GST_AVSYS_AUDIO_SRC_H__ */
+++ /dev/null
-/*
- * avsystem
- *
- * 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
- *
- */
-
-
-#if defined(_MM_PROJECT_FLOATER) || defined(_MM_PROJECT_PROTECTOR) || defined(_MM_PROJECT_VOLANS)
-#define VERSION "0.10.19" // sec
-#define PACKAGE "gstreamer"
-#elif defined(_MM_PROJECT_ADORA)
-#define VERSION "0.10.9" // mavell
-#define PACKAGE "gstreamer"
-#else
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "gstavsysmemsink.h"
-
-#define debug_enter g_print
-#define debug_leave g_print
-#define debug_fenter() g_print("enter: %s\n",__FUNCTION__)
-#define debug_fleave() g_print("leave: %s\n",__FUNCTION__)
-#define debug_msg g_print
-#define debug_verbose g_print
-#define debug_warning g_print
-#define debug_error g_print
-
-#define GST_CAT_DEFAULT avsysmemsink_debug
-
-#define DISABLE_YUV_FORMAT_ON_SINK_CAPS
-
-GST_DEBUG_CATEGORY_STATIC (avsysmemsink_debug);
-
-enum
-{
- SIGNAL_VIDEO_STREAM,
- LAST_SIGNAL
-};
-
-enum
-{
- PROP_0,
- PROP_WIDTH,
- PROP_HEIGHT,
- PROP_ROTATE,
-};
-
-static GstStaticPadTemplate sink_factory =
- GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK, GST_PAD_ALWAYS,
- GST_STATIC_CAPS (
-#ifndef DISABLE_YUV_FORMAT_ON_SINK_CAPS
- "video/x-raw-yuv, "
- "format = (fourcc){YV12}, "
- "framerate = (fraction) [ 0, MAX ], "
- "width = (int) [ 1, MAX ], "
- "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; "
-#else /* BGRA */
- "video/x-raw-rgb, "
- "bpp = (int)32, "
- "depth = (int)32, "
- "endianness = (int)4321, "
- "red_mask = (int)65280, "
- "green_mask = (int)16711680, "
- "blue_mask = (int)-16777216, "
- "alpha_mask = (int)255, "
- "width = (int) [ 1, MAX ], "
- "height = (int) [ 1, MAX ], "
- "framerate = (fraction) [ 0, MAX ]; "
-#endif
- )
- );
-
-static GstElementDetails AvsysMemSink_details = {
- "AV-system Stream callback",
- "Sink/Video",
- "Stream sink for AV-System GStreamer Plug-in",
- ""
-};
-
-static guint gst_avsysmemsink_signals[LAST_SIGNAL] = { 0 };
-
-
-#ifdef G_ENABLE_DEBUG
-#define g_marshal_value_peek_int(v) g_value_get_int (v)
-#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
-#else /* !G_ENABLE_DEBUG */
-#define g_marshal_value_peek_int(v) (v)->data[0].v_int
-#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
-#endif /* !G_ENABLE_DEBUG */
-
-
-#define GET_PIXEL(buf, x, y, stride) (*((unsigned char*)(buf) + (x) + (y)*(stride)))
-
-#define GET_RED(Y,U,V) ((9535 * (Y - 16) + 13074 * (V - 128)) >> 13)
-#define GET_GREEN(Y,U,V) ((9535 * (Y - 16) - 6660 * (V - 128) - 3203 * (U - 128)) >> 13 )
-#define GET_BLUE(Y,U,V) ((9535 * (Y - 16) + 16531 * (U - 128)) >> 13 )
-
-#define UCLIP(a) (((a)<0)?0:((a)>255)?255:(a))
-
-
-static void
-yuv420toargb(unsigned char *src, unsigned char *dst, int width, int height)
-{
- int h,w;
- int y=0,u=0,v=0;
- int a=0,r=0,g=0,b=0;
-
- unsigned char* pixel;
-
- int index=0;
-
- unsigned char *pY;
- unsigned char *pU;
- unsigned char *pV;
-
- GST_DEBUG ("converting yuv420 to argb");
-
- pY = src ;
- pU = src + (width * height) ;
- pV = src + (width * height) + (width * height /4) ;
-
- a = 255;
-
- for(h = 0 ; h < height; h++)
- {
- for(w = 0 ; w < width; w++)
- {
- y = GET_PIXEL(pY,w,h,width);
- u = GET_PIXEL(pU,w/2,h/2,width/2);
- v = GET_PIXEL(pV,w/2,h/2,width/2);
-
- r = GET_RED(y,u,v);
- g = GET_GREEN(y,u,v);
- b = GET_BLUE(y,u,v);
-
- r = UCLIP(r);
- g = UCLIP(g);
- b = UCLIP(b);
-
- index = (w + (h* width)) * 4;
- dst[index] = r;
- dst[index+1] = g;
- dst[index+2] = b;
- dst[index+3] = a;
- }
- }
-}
-
-static void
-rotate_pure(unsigned char *src, unsigned char *dst, int width,int height,int angle,int bpp)
-{
-
- int size;
- int new_x,new_y;
- int org_x,org_y;
- int dst_width;
- int src_idx, dst_idx;
-
- size = width * height * bpp;
-
- if(angle == 0)
- {
- memcpy(dst,src,size);
- return;
- }
-
- for(org_y =0; org_y < height; org_y++)
- {
- for(org_x = 0; org_x < width ; org_x++)
- {
- if(angle == 90)
- {
- new_x = height - org_y;
- new_y = org_x;
-
- dst_width = height;
- }
- else if(angle == 180)
- {
- new_x = width - org_x;
- new_y = height - org_y;
- dst_width = width;
- }
- else if(angle == 270)
- {
- new_x = org_y;
- new_y = width - org_x;
- dst_width = height;
- }
- else
- {
- g_print("Not support Rotate : %d\n",angle);
- return;
- }
-
- src_idx = org_x + (org_y * width);
- dst_idx = new_x + (new_y * dst_width);
-
- memcpy(dst + (dst_idx*bpp), src+(src_idx *bpp),bpp);
- }
- }
-
-}
-
-static void
-resize_pure(unsigned char *src, unsigned char *dst, int src_width, int src_height, int dst_width,int dst_height, int bpp)
-{
- float xFactor,yFactor;
-
- float org_fx,org_fy;
- int org_x,org_y;
-
- int x,y;
-
- int src_index,dst_index;
-
- unsigned short *pshortSrc;
- unsigned short *pshortDst;
-
- if(bpp == 2)
- {
- pshortSrc = (unsigned short*)src;
- pshortDst = (unsigned short*)dst;
- }
-
- xFactor = (float)((dst_width<<16) / src_width);
- yFactor = (float)((dst_height<<16) / src_height);
-
- for(y = 0; y < dst_height; y++)
- {
- for(x = 0; x < dst_width; x++)
- {
- org_fx = (float)((x<<16)/xFactor);
- org_fy = (float)((y<<16)/yFactor);
-
- org_x = (int)(org_fx);
- org_y = (int)(org_fy);
-
- src_index = org_x + (org_y * src_width);
- dst_index = x + (y*dst_width);
-
- memcpy(dst+(dst_index *bpp ),src+(src_index *bpp),bpp);
- }
- }
-}
-
-/* BOOLEAN:POINTER,INT,INT (avsysvideosink.c:1) */
-void
-gst_avsysmemsink_BOOLEAN__POINTER_INT_INT (GClosure *closure,
- GValue *return_value G_GNUC_UNUSED,
- guint n_param_values,
- const GValue *param_values,
- gpointer invocation_hint G_GNUC_UNUSED,
- gpointer marshal_data)
-{
- typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_INT_INT) (gpointer data1,
- gpointer arg_1,
- gint arg_2,
- gint arg_3,
- gpointer data2);
- register GMarshalFunc_BOOLEAN__POINTER_INT_INT callback;
- register GCClosure *cc = (GCClosure*) closure;
- register gpointer data1, data2;
-
- gboolean v_return;
-
- g_return_if_fail (return_value != NULL);
- g_return_if_fail (n_param_values == 4);
-
- if (G_CCLOSURE_SWAP_DATA (closure))
- {
- data1 = closure->data;
- data2 = g_value_peek_pointer (param_values + 0);
- }
- else
- {
- data1 = g_value_peek_pointer (param_values + 0);
- data2 = closure->data;
- }
- callback = (GMarshalFunc_BOOLEAN__POINTER_INT_INT) (marshal_data ? marshal_data : cc->callback);
-
- v_return = callback (data1,
- g_marshal_value_peek_pointer (param_values + 1),
- g_marshal_value_peek_int (param_values + 2),
- g_marshal_value_peek_int (param_values + 3),
- data2);
-
- g_value_set_boolean (return_value, v_return);
-}
-
-static void gst_avsysmemsink_init_interfaces (GType type);
-
-
-GST_BOILERPLATE_FULL (GstAvsysMemSink, gst_avsysmemsink, GstVideoSink, GST_TYPE_VIDEO_SINK, gst_avsysmemsink_init_interfaces);
-
-
-static void
-gst_avsysmemsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
- GstAvsysMemSink *AvsysMemSink = GST_AVSYS_MEM_SINK (object);
- switch (prop_id) {
- case PROP_0:
- break;
- case PROP_WIDTH:
- if(AvsysMemSink->dst_width != g_value_get_int (value))
- {
- AvsysMemSink->dst_width = g_value_get_int (value);
- AvsysMemSink->dst_changed = 1;
- }
- break;
- case PROP_HEIGHT:
- if(AvsysMemSink->dst_height != g_value_get_int (value))
- {
- AvsysMemSink->dst_height = g_value_get_int (value);
- AvsysMemSink->dst_changed = 1;
- }
- break;
- case PROP_ROTATE:
- if(AvsysMemSink->rotate != g_value_get_int(value))
- {
- AvsysMemSink->rotate = g_value_get_int(value);
- AvsysMemSink->dst_changed = 1;
- }
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- g_print ("invalid property id\n");
- break;
- };
-}
-
-static void
-gst_avsysmemsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
- GstAvsysMemSink *AvsysMemSink = GST_AVSYS_MEM_SINK (object);
-
- switch (prop_id) {
- case PROP_0:
- break;
- case PROP_WIDTH:
- g_value_set_int (value, AvsysMemSink->dst_width);
- break;
- case PROP_HEIGHT:
- g_value_set_int (value, AvsysMemSink->dst_height);
- break;
- case PROP_ROTATE:
- g_value_set_int (value, AvsysMemSink->rotate);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- debug_warning ("invalid property id\n");
- break;
- }
-}
-
-static void
-free_buffer(GstAvsysMemSink *AvsysMemSink)
-{
- if(AvsysMemSink->con_buf)
- free(AvsysMemSink->con_buf);
-
- if(AvsysMemSink->rot_buf)
- free(AvsysMemSink->rot_buf);
-
- if(AvsysMemSink->rsz_buf)
- free(AvsysMemSink->rsz_buf);
-
- AvsysMemSink->con_buf = NULL;
- AvsysMemSink->rot_buf = NULL;
- AvsysMemSink->rsz_buf = NULL;
-
-}
-static GstStateChangeReturn
-gst_avsysmemsink_change_state (GstElement *element, GstStateChange transition)
-{
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- GstAvsysMemSink *AvsysMemSink = GST_AVSYS_MEM_SINK (element);
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- debug_msg ("GST AVSYS DISPLAY SINK: NULL -> READY\n");
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- debug_msg ("GST AVSYS DISPLAY SINK: READY -> PAUSED\n");
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- debug_msg ("GST AVSYS DISPLAY SINK: PAUSED -> PLAYING\n");
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS(parent_class)->change_state (element, transition);
- if (ret == GST_STATE_CHANGE_FAILURE) {
- return ret;
- }
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- debug_msg ("GST AVSYS MEM SINK: PLAYING -> PAUSED\n");
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- debug_msg ("GST AVSYS MEM SINK: PAUSED -> READY\n");
- free_buffer(AvsysMemSink);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- debug_msg ("GST AVSYS MEM SINK: READY -> NULL\n");
- break;
- default:
- break;
- }
-
- return ret;
-
-
-}
-
-
-static gboolean
-gst_avsysmemsink_set_caps (GstBaseSink * bs, GstCaps * caps)
-{
- GstAvsysMemSink *s = GST_AVSYS_MEM_SINK (bs);
- GstStructure *structure;
- gint width = 0;
- gint height = 0;
-
- if (caps != NULL)
- {
- guint32 fourcc;
- char *name = NULL;
- int bpp = 0, depth = 0;
-
- structure = gst_caps_get_structure (caps, 0);
-
- /**/
- name = (char *) gst_structure_get_name (structure);
- GST_DEBUG_OBJECT (s, "CAPS NAME: %s", name);
-
- if (gst_structure_has_name (structure, "video/x-raw-rgb"))
- {
- s->is_rgb = TRUE;
- }
- else if (gst_structure_has_name (structure, "video/x-raw-yuv"))
- {
- s->is_rgb = FALSE;
- }
-
- /* get source size */
- gst_structure_get_int (structure, "height", &height);
- gst_structure_get_int (structure, "width", &width);
-
- if (gst_structure_get_fourcc (structure, "format", &fourcc))
- {
- switch (fourcc)
- {
- case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
- debug_warning("set format AVSYS_VIDEO_FORMAT_UYVY\n");
- break;
- case GST_MAKE_FOURCC ('Y', 'V', '1', '6'):
- case GST_MAKE_FOURCC ('Y', '4', '2', 'B'):
- debug_warning("set format AVSYS_VIDEO_FORMAT_YUV422\n");
- break;
- case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
- debug_warning("set format AVSYS_VIDEO_FORMAT_YUV420\n");
- break;
- case GST_MAKE_FOURCC ('R', 'G', 'B', ' '):
- debug_warning("set format AVSYS_VIDEO_FORMAT_RGB565\n");
- break;
- default:
- debug_warning("set default format AVSYS_VIDEO_FORMAT_RGB565\n");
- break;
- }
- }
-
- if( s->src_width != width ||
- s->src_height != height)
- {
- debug_warning ("DISPLAY: Video Source Changed! [%d x %d] -> [%d x %d]\n",
- s->src_width, s->src_height, width, height);
- s->src_changed = TRUE;
- }
-
- s->src_width = width;
- s->src_height = height;
-
- if(s->dst_width ==0)
- {
- s->dst_width = width;
- s->dst_changed = 1;
- }
-
- if(s->dst_height == 0)
- {
- s->dst_height = height;
- s->dst_changed = 1;
- }
- debug_msg ("SRC CAPS: width:%d, height:%d \n", width, height);
- }
- else
- {
- debug_warning ("caps is NULL.\n");
- }
-
- debug_fleave ();
-
- return TRUE;
-}
-
-static GstFlowReturn
-gst_avsysmemsink_preroll (GstBaseSink * bsink, GstBuffer * buf)
-{
- GstAvsysMemSink *AvsysMemSink = GST_AVSYS_MEM_SINK (bsink);
-
- AvsysMemSink->src_length = GST_BUFFER_SIZE (buf);
- debug_msg ("SRC LENGTH: %d\n", AvsysMemSink->src_length);
-
- return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-gst_avsysmemsink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
-{
- GstAvsysMemSink *s = GST_AVSYS_MEM_SINK (bsink);
- gboolean res = FALSE;
- int f_size;
- f_size = GST_BUFFER_SIZE (buf);
- unsigned char *dst_buf;
-
- if ( ! s->is_rgb )
- {
- GST_DEBUG_OBJECT (s, "src format is not rgb");
- if (s->dst_changed == TRUE)
- {
- if(s->con_buf)
- {
- free(s->con_buf);
- s->con_buf = NULL;
- }
-
- if(s->rot_buf)
- {
- free(s->rot_buf);
- s->rot_buf = NULL;
- }
-
- if(s->rsz_buf)
- {
- free(s->rsz_buf);
- s->rsz_buf = NULL;
- }
-
- s->con_buf = malloc(s->src_width * s->src_height * 4);
- if(s->rotate != 0)
- {
- s->rot_buf = malloc(s->src_width * s->src_height * 4);
- }
-
- s->rsz_buf = malloc(s->dst_width * s->dst_height *4);
-
- s->dst_changed = FALSE;
- }
-
- yuv420toargb(GST_BUFFER_DATA (buf),s->con_buf,s->src_width, s->src_height);
- if(s->rotate != 0)
- {
- rotate_pure(s->con_buf,s->rot_buf,s->src_width, s->src_height,s->rotate,4);
- if(s->rotate == 90 || s->rotate == 270)
- {
- resize_pure(s->rot_buf,s->rsz_buf,s->src_height,s->src_width,
- s->dst_width, s->dst_height,4);
- }
- else
- {
- resize_pure(s->rot_buf,s->rsz_buf,s->src_width,s->src_height,
- s->dst_width, s->dst_height,4);
- }
- }
- else
- {
- resize_pure(s->con_buf,s->rsz_buf,s->src_width,s->src_height,
- s->dst_width, s->dst_height,4);
- }
-
- /* emit signal for video-stream */
- g_signal_emit (s,gst_avsysmemsink_signals[SIGNAL_VIDEO_STREAM],
- 0,s->rsz_buf,
- s->dst_width,s->dst_height,
- &res);
- }
- else
- {
- GST_DEBUG_OBJECT (s, "src format is rgb");
-
- /* NOTE : video can be resized by convert plugin's set caps on running time.
- * So, it should notice it to application through callback func.
- */
- g_signal_emit (s, gst_avsysmemsink_signals[SIGNAL_VIDEO_STREAM],
- 0, GST_BUFFER_DATA (buf),
- s->src_width, s->src_height,
- &res);
- }
- GST_DEBUG_OBJECT (s, "g_signal_emit : src_width=%d, src_height=%d, GST_BUFFER_SIZE=%d", s->src_width,s->src_height,GST_BUFFER_SIZE(buf));
-
- /*check video stream callback result.*/
- if (res)
- {
- //debug_verbose("Video stream is called.\n");
- return GST_FLOW_OK;
- }
-
- return GST_FLOW_OK;
-}
-
-static void
-gst_avsysmemsink_init_interfaces (GType type)
-{
- /*void*/
-}
-
-
-static void
-gst_avsysmemsink_base_init (gpointer klass)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&sink_factory));
- gst_element_class_set_details (element_class, &AvsysMemSink_details);
-}
-
-static void
-gst_avsysmemsink_class_init (GstAvsysMemSinkClass *klass)
-{
- GObjectClass *gobject_class = (GObjectClass*) klass;
- GstElementClass *gstelement_class = (GstElementClass*) klass;
- GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
-
-
- parent_class = g_type_class_peek_parent (klass);
-
- gobject_class->set_property = gst_avsysmemsink_set_property;
- gobject_class->get_property = gst_avsysmemsink_get_property;
-
-
- g_object_class_install_property (gobject_class, PROP_WIDTH,
- g_param_spec_int ("width",
- "Width",
- "Width of display",
- 0, G_MAXINT, 176,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-
- g_object_class_install_property (gobject_class, PROP_HEIGHT,
- g_param_spec_int ("height",
- "Height",
- "Height of display",
- 0, G_MAXINT, 144,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_ROTATE,
- g_param_spec_int ("rotate",
- "Rotate",
- "Rotate of display",
- 0, G_MAXINT, 0,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-
- /**
- * GstAvsysVideoSink::video-stream:
- */
- gst_avsysmemsink_signals[SIGNAL_VIDEO_STREAM] = g_signal_new (
- "video-stream",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- gst_avsysmemsink_BOOLEAN__POINTER_INT_INT,
- G_TYPE_BOOLEAN,
- 3,
- G_TYPE_POINTER, G_TYPE_INT, G_TYPE_INT);
-
- gstelement_class->change_state = gst_avsysmemsink_change_state;
-
- gstbasesink_class->set_caps = gst_avsysmemsink_set_caps;
- gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_avsysmemsink_preroll);
- gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_avsysmemsink_show_frame);
-
-
- GST_DEBUG_CATEGORY_INIT (avsysmemsink_debug,
- "avsysmemsink",
- 0,
- "AV system based GStreamer Plug-in");
-}
-
-
-static void
-gst_avsysmemsink_init (GstAvsysMemSink *AvsysMemSink, GstAvsysMemSinkClass *klass)
-{
- /*private*/
- AvsysMemSink->src_width = 0;
- AvsysMemSink->src_height = 0;
-
- AvsysMemSink->src_changed = 0;
-
- /*property*/
- AvsysMemSink->dst_width = 0;
- AvsysMemSink->dst_height = 0;
-
- AvsysMemSink->dst_changed = 0;
-
- AvsysMemSink->rotate = 0;
-
- AvsysMemSink->con_buf = NULL;
- AvsysMemSink->rot_buf = NULL;
- AvsysMemSink->rsz_buf = NULL;
-
- AvsysMemSink->is_rgb = FALSE;
-}
-
-
-/* EOF */
+++ /dev/null
-/*
- * avsystem
- *
- * 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_AVSYSMEMSINK_H__
-#define __GST_AVSYSMEMSINK_H__
-
-#include <gst/gst.h>
-#include <gst/video/gstvideosink.h>
-#include <gst/interfaces/xoverlay.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_AVSYS_MEM_SINK (gst_avsysmemsink_get_type())
-#define GST_AVSYS_MEM_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVSYS_MEM_SINK, GstAvsysMemSink))
-#define GST_AVSYS_MEM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVSYS_MEM_SINK, GstAvsysMemSinkClass))
-#define GST_IS_AVSYS_MEM_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVSYS_MEM_SINK))
-#define GST_IS_AVSYS_MEM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVSYS_MEM_SINK))
-
-
-typedef struct _GstAvsysMemSink GstAvsysMemSink;
-typedef struct _GstAvsysMemSinkClass GstAvsysMemSinkClass;
-
-struct _GstAvsysMemSink
-{
- GstVideoSink videosink;
-
- int src_width;
- int src_height;
-
- int src_changed;
- int src_length;
-
-
- int dst_width;
- int dst_height;
-
- int dst_length;
- int dst_changed;
-
- unsigned char *con_buf;
- unsigned char *rot_buf;
- unsigned char *rsz_buf;
-
- int rotate;
-
- int is_rgb;
-};
-
-struct _GstAvsysMemSinkClass
-{
- GstVideoSinkClass parent_class;
-};
-
-GType gst_avsysmemsink_get_type (void);
-
-G_END_DECLS
-
-
-#endif /* __GST_AVSYSMEMSINK_H__ */
-
-/* EOF */
+++ /dev/null
-/*
- * avsystem
- *
- * 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 <gst/gst.h>
-#include "gstavsysaudiosink.h"
-#include "gstavsysmemsink.h"
-
-GST_DEBUG_CATEGORY (avsystem_sink_debug);
-
-static gboolean
-plugin_init (GstPlugin *plugin)
-{
- gboolean error;
- /*register the exact name you can find in the framework*/
- error = gst_element_register (plugin, "avsysaudiosink",
- GST_RANK_PRIMARY + 100,
- GST_TYPE_AVSYS_AUDIO_SINK);
-
- error = gst_element_register (plugin, "avsysmemsink",
- GST_RANK_NONE,
- GST_TYPE_AVSYS_MEM_SINK);
-
- if (!error)
- goto failed;
-
- GST_DEBUG_CATEGORY_INIT (avsystem_sink_debug, "avsystemsink", 0, "avsystem sink plugins");
- return TRUE;
-
-failed:
-
- return FALSE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "avsyssink",
- "AV system video/audio sink plug-in",
- plugin_init,
- PACKAGE_VERSION,
- "LGPL",
- "AV System Sink",
- "http://www.samsung.com")
+++ /dev/null
-/*
- * avsystem
- *
- * 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 <gst/gst.h>
-
-#include "gstavsysaudiosrc.h"
-
-GST_DEBUG_CATEGORY (avsystem_src_debug);
-
-static gboolean
-plugin_init (GstPlugin *plugin)
-{
- gboolean error;
- /*register the exact name you can find in the framework*/
- error = gst_element_register (plugin, "avsysaudiosrc",
- GST_RANK_NONE,
- GST_TYPE_AVSYS_AUDIO_SRC);
- if (!error)
- goto failed;
-
- GST_DEBUG_CATEGORY_INIT (avsystem_src_debug, "avsystemsrc", 0, "avsystem src plugins");
- return TRUE;
-
-failed:
-
- return FALSE;
-}
-
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "avsysaudiosrc",
- "AV system audio source plug-in",
- plugin_init,
- PACKAGE_VERSION,
- "LGPL",
- "AV System Source",
- "http://www.samsung.com")
-
--- /dev/null
+dnl macros for X-related detections
+dnl AC_SUBST's HAVE_X, X_CFLAGS, X_LIBS
+AC_DEFUN([AG_GST_CHECK_X],
+[
+ AC_PATH_XTRA
+ ac_cflags_save="$CFLAGS"
+ ac_cppflags_save="$CPPFLAGS"
+ CFLAGS="$CFLAGS $X_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+
+ dnl now try to find the HEADER
+ AC_CHECK_HEADER(X11/Xlib.h, HAVE_X="yes", HAVE_X="no")
+
+ if test "x$HAVE_X" = "xno"
+ then
+ AC_MSG_NOTICE([cannot find X11 development files])
+ else
+ dnl this is much more than we want
+ X_LIBS="$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS"
+ dnl AC_PATH_XTRA only defines the path needed to find the X libs,
+ dnl it does not add the libs; therefore we add them here
+ X_LIBS="$X_LIBS -lX11"
+ AC_SUBST(X_CFLAGS)
+ AC_SUBST(X_LIBS)
+ fi
+ AC_SUBST(HAVE_X)
+
+ CFLAGS="$ac_cflags_save"
+ CPPFLAGS="$ac_cppflags_save"
+])
+
+dnl *** XVideo ***
+dnl Look for the PIC library first, Debian requires it.
+dnl Check debian-devel archives for gory details.
+dnl 20020110:
+dnl At the moment XFree86 doesn't distribute shared libXv due
+dnl to unstable API. On many platforms you CAN NOT link a shared
+dnl lib to a static non-PIC lib. This is what the xvideo GStreamer
+dnl plug-in wants to do. So Debian distributes a PIC compiled
+dnl version of the static lib for plug-ins to link to when it is
+dnl inappropriate to link the main application to libXv directly.
+dnl FIXME: add check if this platform can support linking to a
+dnl non-PIC libXv, if not then don not use Xv.
+dnl FIXME: perhaps warn user if they have a shared libXv since
+dnl this is an error until XFree86 starts shipping one
+AC_DEFUN([AG_GST_CHECK_XV],
+[
+ if test x$HAVE_X = xyes; then
+ AC_CHECK_LIB(Xv_pic, XvQueryExtension,
+ HAVE_XVIDEO="yes", HAVE_XVIDEO="no",
+ $X_LIBS -lXext)
+
+ if test x$HAVE_XVIDEO = xyes; then
+ XVIDEO_LIBS="-lXv_pic -lXext"
+ AC_SUBST(XVIDEO_LIBS)
+ else
+ dnl try again using something else if we didn't find it first
+ if test x$HAVE_XVIDEO = xno; then
+ AC_CHECK_LIB(Xv, XvQueryExtension,
+ HAVE_XVIDEO="yes", HAVE_XVIDEO="no",
+ $X_LIBS -lXext)
+
+ if test x$HAVE_XVIDEO = xyes; then
+ XVIDEO_LIBS="-lXv -lXext"
+ AC_SUBST(XVIDEO_LIBS)
+ fi
+ fi
+ fi
+ fi
+])
--- /dev/null
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2005-05-14.22
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "compile $scriptversion"
+ exit $?
+ ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+ if test -n "$eat"; then
+ eat=
+ else
+ case $1 in
+ -o)
+ # configure might choose to run compile as `compile cc -o foo foo.c'.
+ # So we strip `-o arg' only if arg is an object.
+ eat=1
+ case $2 in
+ *.o | *.obj)
+ ofile=$2
+ ;;
+ *)
+ set x "$@" -o "$2"
+ shift
+ ;;
+ esac
+ ;;
+ *.c)
+ cfile=$1
+ set x "$@" "$1"
+ shift
+ ;;
+ *)
+ set x "$@" "$1"
+ shift
+ ;;
+ esac
+ fi
+ shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+ # If no `-o' option was seen then we might have been invoked from a
+ # pattern rule where we don't need one. That is ok -- this is a
+ # normal compilation that the losing compiler can handle. If no
+ # `.c' file was seen then we are probably linking. That is also
+ # ok.
+ exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/.-]' here to ensure that we don't use the same name
+# that we are using for the .o file. Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d
+while true; do
+ if mkdir "$lockdir" >/dev/null 2>&1; then
+ break
+ fi
+ sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+ mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+ mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+# 2011 Free Software Foundation, Inc.
+
+timestamp='2011-05-11'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner. Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free
+Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ 8664:Windows_NT:*)
+ echo x86_64-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-tilera-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define the version */
+#undef GST_PLUGIN_VERSION
+
+/* Define the release version */
+#undef GST_PLUGIN_VERSION_RELEASE
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
AC_INIT(extension, 1.0)
-
+
dnl versions of gstreamer and plugins-base
GST_MAJORMINOR=0.10
GST_REQUIRED=0.10.0
dnl decide on error flags
AS_COMPILER_FLAG(-Wall, GST_WALL="yes", GST_WALL="no")
-
+
if test "x$GST_WALL" = "xyes"; then
GST_ERROR="$GST_ERROR -Wall"
-
+
# if test "x$GST_PLUGIN_CVS" = "xyes"; then
# AS_COMPILER_FLAG(-Werror,GST_ERROR="$GST_ERROR -Werror",GST_ERROR="$GST_ERROR")
# fi
AC_SUBST(GST_INTERFACES_CFLAGS)
AC_SUBST(GST_INTERFACES_LIBS)
+dnl xvimagesrc _CFLAGS and _LIB available
+
+PKG_CHECK_MODULES(DRI2, libdri2)
+AC_SUBST(DRI2_CFLAGS)
+AC_SUBST(DRI2_LIBS)
+
+PKG_CHECK_MODULES(X11, x11)
+AC_SUBST(X11_CFLAGS)
+AC_SUBST(X11_LIBS)
+
+PKG_CHECK_MODULES(XEXT, xext)
+AC_SUBST(XEXT_CFLAGS)
+AC_SUBST(XEXT_LIBS)
+
+PKG_CHECK_MODULES(XV, xv)
+AC_SUBST(XV_CFLAGS)
+AC_SUBST(XV_LIBS)
+
+PKG_CHECK_MODULES(XDAMAGE, xdamage)
+AC_SUBST(XDAMAGE_CFLAGS)
+AC_SUBST(XDAMAGE_LIBS)
+
+PKG_CHECK_MODULES(DRM, libdrm)
+AC_SUBST(DRM_CFLAGS)
+AC_SUBST(DRM_LIBS)
+
+PKG_CHECK_MODULES(DRM, libdrm-devel)
+AC_SUBST(DRM_DEVEL_CFLAGS)
+AC_SUBST(DRM_DEVEL_LIBS)
+
+PKG_CHECK_MODULES(TBM, libtbm)
+AC_SUBST(TBM_CFLAGS)
+AC_SUBST(TBM_LIBS)
+
dnl use time analysis module
-PKG_CHECK_MODULES(MMTA, mm-ta)
-AC_SUBST(MMTA_CFLAGS)
-AC_SUBST(MMTA_LIBS)
-dnl required package for evasimagesink
+PKG_CHECK_MODULES(XML2, libxml-2.0)
+AC_SUBST(XML2_CFLAGS)
+AC_SUBST(XML2_LIBS)
+
+dnl required package for evasimagesink/evaspixmapsink
PKG_CHECK_MODULES(EFL, [
evas >= $EFL_REQUIRED
ecore >= $EFL_REQUIRED
])
])
+dnl *** belows are related to evaspixmapsink plug-ins ***
+AG_GST_ARG_WITH_PACKAGE_NAME
+AG_GST_ARG_WITH_PACKAGE_ORIGIN
+
+dnl set license and copyright notice
+GST_LICENSE="LGPL"
+AC_DEFINE_UNQUOTED(GST_LICENSE, "$GST_LICENSE", [GStreamer license])
+AC_SUBST(GST_LICENSE)
+
+echo
+AC_MSG_NOTICE([Checking libraries for evaspixmapsink plugin])
+echo
+dnl *** X11 ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_X, true)
+AG_GST_CHECK_FEATURE(X, [X libraries and plugins],
+ [evaspixmapsink], [
+ AC_PATH_XTRA
+ ac_cflags_save="$CFLAGS"
+ ac_cppflags_save="$CPPFLAGS"
+ CFLAGS="$CFLAGS $X_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+
+ dnl now try to find the HEADER
+ AC_CHECK_HEADER(X11/Xlib.h, HAVE_X="yes", HAVE_X="no")
+
+ if test "x$HAVE_X" = "xno"
+ then
+ AC_MSG_NOTICE([cannot find X11 development files])
+ else
+ dnl this is much more than we want
+ X_LIBS="$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS"
+ dnl AC_PATH_XTRA only defines the path needed to find the X libs,
+ dnl it does not add the libs; therefore we add them here
+ X_LIBS="$X_LIBS -lX11"
+ AC_SUBST(X_CFLAGS)
+ AC_SUBST(X_LIBS)
+ fi
+ AC_SUBST(HAVE_X)
+ CFLAGS="$ac_cflags_save"
+ CPPFLAGS="$ac_cppflags_save"
+])
+
+dnl Check for Xv extension
+translit(dnm, m, l) AM_CONDITIONAL(USE_XVIDEO, true)
+AG_GST_CHECK_FEATURE(XVIDEO, [X11 XVideo extensions],
+ [evaspixmapsink], [
+AG_GST_CHECK_XV
+])
+
+dnl check for X Shm
+translit(dnm, m, l) AM_CONDITIONAL(USE_XSHM, true)
+AG_GST_CHECK_FEATURE(XSHM, [X Shared Memory extension], , [
+ if test x$HAVE_X = xyes; then
+ AC_CHECK_LIB(Xext, XShmAttach,
+ HAVE_XSHM="yes", HAVE_XSHM="no",
+ $X_LIBS)
+ if test "x$HAVE_XSHM" = "xyes"; then
+ XSHM_LIBS="-lXext"
+ else
+ dnl On AIX, it is in XextSam instead, but we still need -lXext
+ AC_CHECK_LIB(XextSam, XShmAttach,
+ HAVE_XSHM="yes", HAVE_XSHM="no",
+ $X_LIBS)
+ if test "x$HAVE_XSHM" = "xyes"; then
+ XSHM_LIBS="-lXext -lXextSam"
+ fi
+ fi
+ fi
+], , [
+ AC_SUBST(HAVE_XSHM)
+ AC_SUBST(XSHM_LIBS)
+])
+
+dnl use tbm
+PKG_CHECK_MODULES(TBM, libtbm)
+AC_SUBST(TBM_CFLAGS)
+AC_SUBST(TBM_LIBS)
+
+PKG_CHECK_MODULES(VCONF, vconf)
+AC_SUBST(VCONF_CFLAGS)
+AC_SUBST(VCONF_LIBS)
+
dnl PKG_CHECK_MODULES(UDEVMGR, unified-dev-mgr)
dnl AC_SUBST(UDEVMGR_CFLAGS)
dnl AC_SUBST(UDEVMGR_LIBS)
[GST_EXT_USE_EXT_ENCODEBIN=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_ENCODEBIN, test "x$GST_EXT_USE_EXT_ENCODEBIN" = "xyes")
-dnl use ext-avsystem --------------------------------------------------------------------------
-AC_ARG_ENABLE(ext-avsystem, AC_HELP_STRING([--enable-ext-avsystem], [using avsystem]),
- [
- case "${enableval}" in
- yes) GST_EXT_USE_EXT_AVSYSTEM=yes ;;
- no) GST_EXT_USE_EXT_AVSYSTEM=no ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-avsystem) ;;
- esac
- ],
- [GST_EXT_USE_EXT_AVSYSTEM=yes])
-AM_CONDITIONAL(GST_EXT_USE_EXT_AVSYSTEM, test "x$GST_EXT_USE_EXT_AVSYSTEM" = "xyes")
-
dnl use ext-evasimagesink --------------------------------------------------------------------------
AC_ARG_ENABLE(ext-evasimagesink, AC_HELP_STRING([--enable-ext-evasimagesink], [using evasimagesink]),
[
[GST_EXT_USE_EXT_EVASIMAGESINK=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_EVASIMAGESINK, test "x$GST_EXT_USE_EXT_EVASIMAGESINK" = "xyes")
-dnl use ext-gstreamer-audio -------------------------------------------------------------------
-AC_ARG_ENABLE(ext-gstreamer-audio, AC_HELP_STRING([--enable-ext-gstreamer-audio], [using gstreamer-audio]),
+dnl use evaspixmapsink ---------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-evaspixmapsink, AC_HELP_STRING([--enable-ext-evaspixmapsink], [using evaspixmapsink]),
+ [
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_EVASPIXMAPSINK=yes ;;
+ no) GST_EXT_USE_EXT_EVASPIXMAPSINK=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-evaspixmapsink) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_EVASPIXMAPSINK=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_EVASPIXMAPSINK, test "x$GST_EXT_USE_EXT_EVASPIXMAPSINK" = "xyes")
+
+dnl use ext-xvimagesrc--------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-xvimagesrc, AC_HELP_STRING([--enable-ext-xvimagesrc], [using xvimagesrc]),
[
case "${enableval}" in
- yes) GST_EXT_USE_EXT_AVSYSAUDIO=yes ;;
- no) GST_EXT_USE_EXT_AVSYSAUDIO=no ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-gstreamer-audio) ;;
+ yes) GST_EXT_USE_EXT_XVIMAGESRC=yes ;;
+ no) GST_EXT_USE_EXT_XVIMAGESRC=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-xvimagesrc) ;;
esac
],
- [GST_EXT_USE_EXT_AVSYSAUDIO=yes])
-AM_CONDITIONAL(GST_EXT_USE_EXT_AVSYSAUDIO, test "x$GST_EXT_USE_EXT_AVSYSAUDIO" = "xyes")
-
-if test "x$GST_EXT_USE_EXT_AVSYSAUDIO" = "xyes"; then
- HAVE_AVSYSAUDIO=NO
- PKG_CHECK_MODULES(AVSYSAUDIO, avsysaudio, HAVE_AVSYSAUDIO="yes", [
- HAVE_AVSYSAUDIO="no"
- AC_MSG_RESULT(no)
- ])
- if test "x$HAVE_AVSYSAUDIO" = "xno"; then
- AC_MSG_ERROR(no avsysaudio package found)
- fi
- AC_SUBST(AVSYSAUDIO_CFLAGS)
- AC_SUBST(AVSYSAUDIO_LIBS)
-fi
+ [GST_EXT_USE_EXT_XVIMAGESRC=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_XVIMAGESRC, test "x$GST_EXT_USE_EXT_XVIMAGESRC" = "xyes")
+
+AC_ARG_ENABLE(pcmdump, AC_HELP_STRING([--enable-pcmdump], [pcm dump]),
+ [
+ case "${enableval}" in
+ yes) PCM_DUMP_ENABLE=yes ;;
+ no) PCM_DUMP_ENABLE=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-pcmdump) ;;
+ esac
+ ],
+ [PCM_DUMP_ENABLE=no])
+AM_CONDITIONAL([PCM_DUMP_ENABLE], [test "x$PCM_DUMP_ENABLE" = "xyes"])
dnl use ext-drmsrc --------------------------------------------------------------------------
AC_ARG_ENABLE(ext-drmsrc, AC_HELP_STRING([--enable-ext-drmsrc], [using drmsrc]),
],
[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_CLIENT, drm-client)
+ AC_SUBST(DRM_CLIENT_CFLAGS)
+ AC_SUBST(DRM_CLIENT_LIBS)
+ PKG_CHECK_MODULES(DRM_TRUSTED, drm-trusted)
+ AC_SUBST(DRM_TRUSTED_CFLAGS)
+ AC_SUBST(DRM_TRUSTED_LIBS)
+fi
+
+dnl use ext-submux --------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-submux, AC_HELP_STRING([--enable-ext-submux], [using submux]),
+ [
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_SUBMUX=yes ;;
+ no) GST_EXT_USE_EXT_SUBMUX=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-submux) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_SUBMUX=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_SUBMUX, test "x$GST_EXT_USE_EXT_SUBMUX" = "xyes")
+
dnl use ext-toggle --------------------------------------------------------------------------
AC_ARG_ENABLE(ext-toggle, AC_HELP_STRING([--enable-ext-toggle], [using toggle]),
[
[GST_EXT_USE_EXT_AUDIOTP=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_AUDIOTP, test "x$GST_EXT_USE_EXT_AUDIOTP" = "xyes")
+dnl use ext-audioeq --------------------------------------------------------------------------
+AC_ARG_ENABLE(ext-audioeq, AC_HELP_STRING([--enable-ext-audioeq], [using audioeq]),
+[
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_AUDIOEQ=yes ;;
+ no) GST_EXT_USE_EXT_AUDIOEQ=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-audioeq) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_AUDIOEQ=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_AUDIOEQ, test "x$GST_EXT_USE_EXT_AUDIOEQ" = "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")
+
+#if test "x$GST_EXT_USE_EXT_PIFFDEMUX" = "xyes"; then
+# PKG_CHECK_MODULES(DRM_CLIENT, drm-client)
+# AC_SUBST(DRM_CLIENT_CFLAGS)
+# AC_SUBST(DRM_CLIENT_LIBS)
+# PKG_CHECK_MODULES(DRM_TRUSTED, drm-trusted)
+# AC_SUBST(DRM_TRUSTED_CFLAGS)
+# AC_SUBST(DRM_TRUSTED_LIBS)
+#fi
+
dnl use ext-ssdemux --------------------------------------------------------------------------
AC_ARG_ENABLE(ext-ssdemux, AC_HELP_STRING([--enable-ext-ssdemux], [using ssdemux]),
[
[GST_EXT_USE_EXT_SSDEMUX=yes])
AM_CONDITIONAL(GST_EXT_USE_EXT_SSDEMUX, test "x$GST_EXT_USE_EXT_SSDEMUX" = "xyes")
+dnl use ext-dashdemux -----------------------------------------------------------------------
+AC_ARG_ENABLE(ext-dashdemux, AC_HELP_STRING([--enable-ext-dashdemux], [using dashdemux]),
+ [
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_DASHDEMUX=yes ;;
+ no) GST_EXT_USE_EXT_DASHDEMUX=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-dashdemux) ;;
+ esac
+ ],
+ [GST_EXT_USE_EXT_DASHDEMUX=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_DASHDEMUX, test "x$GST_EXT_USE_EXT_DASHDEMUX" = "xyes")
+
+dnl use ext-hlsdemux2 -----------------------------------------------------------------------
+AC_ARG_ENABLE(ext-hlsdemux2, AC_HELP_STRING([--enable-ext-hlsdemux2], [using hlsdemux2]),
+ [
+ case "${enableval}" in
+ yes) GST_EXT_USE_EXT_HLSDEMUX2=yes ;;
+ no) GST_EXT_USE_EXT_HLSDEMUX2=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ext-hlsdemux2) ;;
+ esac
+ ]
+ [GST_EXT_USE_EXT_HLSDEMUX2=yes])
+AM_CONDITIONAL(GST_EXT_USE_EXT_HLSDEMUX2, test "x$GST_EXT_USE_EXT_HLSDEMUX2" = "xyes")
+
+if test "x$GST_EXT_USE_EXT_HLSDEMUX2" = "xyes"; then
+ dnl crypto for hlsdemux
+ PKG_CHECK_MODULES(CRYPTO, libcrypto)
+ AC_SUBST(CRYPTO_CFLAGS)
+ AC_SUBST(CRYPTO_LIBS)
+fi
AC_OUTPUT(
Makefile
common/Makefile
common/m4/Makefile
-avsystem/Makefile
pdpushsrc/Makefile
pdpushsrc/src/Makefile
-avsystem/src/Makefile
encodebin/Makefile
encodebin/src/Makefile
evasimagesink/Makefile
evasimagesink/src/Makefile
+evaspixmapsink/Makefile
+xvimagesrc/Makefile
+xvimagesrc/src/Makefile
toggle/Makefile
toggle/src/Makefile
drmsrc/Makefile
drmsrc/src/Makefile
+submux/Makefile
+submux/src/Makefile
audiotp/Makefile
audiotp/src/Makefile
+audioeq/Makefile
+audioeq/src/Makefile
+piffdemux/Makefile
+piffdemux/src/Makefile
ssdemux/Makefile
ssdemux/src/Makefile
+dashdemux/Makefile
+dashdemux/src/Makefile
+hlsdemux2/Makefile
+hlsdemux2/src/Makefile
)
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+
+plugin_LTLIBRARIES = libgstdashdemux.la
+
+libgstdashdemux_la_SOURCES = \
+ gstmpdparser.c \
+ gstdashdemux.c \
+ gstfragment.c \
+ gsturidownloader.c \
+ gstdownloadrate.c \
+ gstplugin.c
+
+# headers we need but don't want installed
+noinst_HEADERS = \
+ gstmpdparser.h \
+ gstfragmented.h \
+ gstfragment.h \
+ gstdashdemux.h \
+ gsturidownloader.h \
+ gstdownloadrate.h
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgstdashdemux_la_CFLAGS = $(GST_CFLAGS)
+libgstdashdemux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgstdashdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdashdemux_la_LIBTOOLFLAGS = --tag=disable-static
--- /dev/null
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(plugindir)"
+LTLIBRARIES = $(plugin_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgstdashdemux_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_libgstdashdemux_la_OBJECTS = libgstdashdemux_la-gstmpdparser.lo \
+ libgstdashdemux_la-gstdashdemux.lo \
+ libgstdashdemux_la-gstfragment.lo \
+ libgstdashdemux_la-gsturidownloader.lo \
+ libgstdashdemux_la-gstplugin.lo
+libgstdashdemux_la_OBJECTS = $(am_libgstdashdemux_la_OBJECTS)
+libgstdashdemux_la_LINK = $(LIBTOOL) --tag=CC \
+ $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) \
+ $(libgstdashdemux_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libgstdashdemux_la_SOURCES)
+DIST_SOURCES = $(libgstdashdemux_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+GST_CFLAGS = @GST_CFLAGS@
+GST_LIBS = @GST_LIBS@
+GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@
+HAVE_PKGCONFIG = @HAVE_PKGCONFIG@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+plugin_LTLIBRARIES = libgstdashdemux.la
+libgstdashdemux_la_SOURCES = \
+ gstmpdparser.c \
+ gstdashdemux.c \
+ gstfragment.c \
+ gsturidownloader.c \
+ gstplugin.c
+
+
+# headers we need but don't want installed
+noinst_HEADERS = \
+ gstmpdparser.h \
+ gstfragmented.h \
+ gstfragment.h \
+ gstdashdemux.h \
+ gsturidownloader.h
+
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgstdashdemux_la_CFLAGS = $(GST_CFLAGS)
+libgstdashdemux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgstdashdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdashdemux_la_LIBTOOLFLAGS = --tag=disable-static
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+ }
+
+uninstall-pluginLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+ done
+
+clean-pluginLTLIBRARIES:
+ -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+ @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgstdashdemux.la: $(libgstdashdemux_la_OBJECTS) $(libgstdashdemux_la_DEPENDENCIES) $(EXTRA_libgstdashdemux_la_DEPENDENCIES)
+ $(libgstdashdemux_la_LINK) -rpath $(plugindir) $(libgstdashdemux_la_OBJECTS) $(libgstdashdemux_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdashdemux_la-gstdashdemux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdashdemux_la-gstfragment.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdashdemux_la-gstmpdparser.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdashdemux_la-gstplugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdashdemux_la-gsturidownloader.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libgstdashdemux_la-gstmpdparser.lo: gstmpdparser.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -MT libgstdashdemux_la-gstmpdparser.lo -MD -MP -MF $(DEPDIR)/libgstdashdemux_la-gstmpdparser.Tpo -c -o libgstdashdemux_la-gstmpdparser.lo `test -f 'gstmpdparser.c' || echo '$(srcdir)/'`gstmpdparser.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgstdashdemux_la-gstmpdparser.Tpo $(DEPDIR)/libgstdashdemux_la-gstmpdparser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmpdparser.c' object='libgstdashdemux_la-gstmpdparser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -c -o libgstdashdemux_la-gstmpdparser.lo `test -f 'gstmpdparser.c' || echo '$(srcdir)/'`gstmpdparser.c
+
+libgstdashdemux_la-gstdashdemux.lo: gstdashdemux.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -MT libgstdashdemux_la-gstdashdemux.lo -MD -MP -MF $(DEPDIR)/libgstdashdemux_la-gstdashdemux.Tpo -c -o libgstdashdemux_la-gstdashdemux.lo `test -f 'gstdashdemux.c' || echo '$(srcdir)/'`gstdashdemux.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgstdashdemux_la-gstdashdemux.Tpo $(DEPDIR)/libgstdashdemux_la-gstdashdemux.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstdashdemux.c' object='libgstdashdemux_la-gstdashdemux.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -c -o libgstdashdemux_la-gstdashdemux.lo `test -f 'gstdashdemux.c' || echo '$(srcdir)/'`gstdashdemux.c
+
+libgstdashdemux_la-gstfragment.lo: gstfragment.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -MT libgstdashdemux_la-gstfragment.lo -MD -MP -MF $(DEPDIR)/libgstdashdemux_la-gstfragment.Tpo -c -o libgstdashdemux_la-gstfragment.lo `test -f 'gstfragment.c' || echo '$(srcdir)/'`gstfragment.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgstdashdemux_la-gstfragment.Tpo $(DEPDIR)/libgstdashdemux_la-gstfragment.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstfragment.c' object='libgstdashdemux_la-gstfragment.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -c -o libgstdashdemux_la-gstfragment.lo `test -f 'gstfragment.c' || echo '$(srcdir)/'`gstfragment.c
+
+libgstdashdemux_la-gsturidownloader.lo: gsturidownloader.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -MT libgstdashdemux_la-gsturidownloader.lo -MD -MP -MF $(DEPDIR)/libgstdashdemux_la-gsturidownloader.Tpo -c -o libgstdashdemux_la-gsturidownloader.lo `test -f 'gsturidownloader.c' || echo '$(srcdir)/'`gsturidownloader.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgstdashdemux_la-gsturidownloader.Tpo $(DEPDIR)/libgstdashdemux_la-gsturidownloader.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsturidownloader.c' object='libgstdashdemux_la-gsturidownloader.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -c -o libgstdashdemux_la-gsturidownloader.lo `test -f 'gsturidownloader.c' || echo '$(srcdir)/'`gsturidownloader.c
+
+libgstdashdemux_la-gstplugin.lo: gstplugin.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -MT libgstdashdemux_la-gstplugin.lo -MD -MP -MF $(DEPDIR)/libgstdashdemux_la-gstplugin.Tpo -c -o libgstdashdemux_la-gstplugin.lo `test -f 'gstplugin.c' || echo '$(srcdir)/'`gstplugin.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgstdashdemux_la-gstplugin.Tpo $(DEPDIR)/libgstdashdemux_la-gstplugin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstplugin.c' object='libgstdashdemux_la-gstplugin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(libgstdashdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdashdemux_la_CFLAGS) $(CFLAGS) -c -o libgstdashdemux_la-gstplugin.lo `test -f 'gstplugin.c' || echo '$(srcdir)/'`gstplugin.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(plugindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pluginLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-pluginLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-pluginLTLIBRARIES \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-pluginLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
--- /dev/null
+/*\r
+ * glibcompat.h\r
+ *\r
+ * Copyright (C) 2013 Douglas Gore <doug@ssonic.co.uk>\r
+ *\r
+ * Authors:\r
+ * Douglas Gore <doug@ssonic.co.uk>\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
+#ifndef GLIBCOMPAT_H_\r
+#define GLIBCOMPAT_H_\r
+\r
+/* The GLib threading API changed in 2.32, these macros are used to\r
+ * provide backwards compatibility with older versions without\r
+ * complicating the code. */\r
+\r
+#include <glib.h>\r
+\r
+#if (GLIB_CHECK_VERSION (2, 32, 0))\r
+ #define G_MUTEX GMutex\r
+ #define G_MUTEX_INIT(mutex) g_mutex_init(&mutex)\r
+ #define G_MUTEX_CLEAR(mutex) g_mutex_clear (&mutex);\r
+ #define G_MUTEX_LOCK(mutex) g_mutex_lock (&mutex)\r
+ #define G_MUTEX_UNLOCK(mutex) g_mutex_unlock (&mutex)\r
+#else\r
+ #define G_MUTEX GMutex*\r
+ #define G_MUTEX_INIT(mutex) mutex = g_mutex_new()\r
+ #define G_MUTEX_CLEAR(mutex) g_mutex_free (mutex)\r
+ #define G_MUTEX_LOCK(mutex) g_mutex_lock (mutex)\r
+ #define G_MUTEX_UNLOCK(mutex) g_mutex_unlock (mutex)\r
+#endif\r
+\r
+#endif /* GLIBCOMPAT_H_ */\r
--- /dev/null
+/*
+ * DASH demux plugin for GStreamer
+ *
+ * gstdashdemux.c
+ *
+ * Copyright (C) 2012 Orange
+ *
+ * Authors:
+ * David Corvoysier <david.corvoysier@orange.com>
+ * Hamid Zakari <hamid.zakari@gmail.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.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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION:element-dashdemux
+ *
+ * DASH demuxer element.
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch playbin2 uri="http://www-itec.uni-klu.ac.at/ftp/datasets/mmsys12/RedBullPlayStreets/redbull_4s/RedBullPlayStreets_4s_isoffmain_DIS_23009_1_v_2_1c2_2011_08_30.mpd"
+ * ]|
+ */
+
+/* Implementation notes:
+ *
+ * The following section describes how dashdemux works internally.
+ *
+ * Introduction:
+ *
+ * dashdemux is a "fake" demux, as unlike traditional demux elements, it
+ * doesn't split data streams contained in an enveloppe to expose them
+ * to downstream decoding elements.
+ *
+ * Instead, it parses an XML file called a manifest to identify a set of
+ * individual stream fragments it needs to fetch and expose to the actual
+ * demux elements that will handle them (this behavior is sometimes
+ * referred as the "demux after a demux" scenario).
+ *
+ * For a given section of content, several representations corresponding
+ * to different bitrates may be available: dashdemux will select the most
+ * appropriate representation based on local conditions (typically the
+ * available bandwidth and the amount of buffering available, capped by
+ * a maximum allowed bitrate).
+ *
+ * The representation selection algorithm can be configured using
+ * specific properties: max bitrate, min/max buffering, bandwidth ratio.
+ *
+ *
+ * General Design:
+ *
+ * dashdemux has a single sink pad that accepts the data corresponding
+ * to the manifest, typically fetched from an HTTP or file source.
+ *
+ * dashdemux exposes the streams it recreates based on the fragments it
+ * fetches through dedicated src pads corresponding to the caps of the
+ * fragments container (ISOBMFF/MP4 or MPEG2TS).
+ *
+ * During playback, new representations will typically be exposed as a
+ * new set of pads (see 'Switching between representations' below).
+ *
+ * Fragments downloading is performed using a dedicated task that fills
+ * an internal queue. Another task is in charge of popping fragments
+ * from the queue and pushing them downstream.
+ *
+ * Switching between representations:
+ *
+ * Decodebin supports scenarios allowing to seamlessly switch from one
+ * stream to another inside the same "decoding chain".
+ *
+ * To achieve that, it combines the elements it autoplugged in chains
+ * and groups, allowing only one decoding group to be active at a given
+ * time for a given chain.
+ *
+ * A chain can signal decodebin that it is complete by sending a
+ * no-more-pads event, but even after that new pads can be added to
+ * create new subgroups, providing that a new no-more-pads event is sent.
+ *
+ * We take advantage of that to dynamically create a new decoding group
+ * in order to select a different representation during playback.
+ *
+ * Typically, assuming that each fragment contains both audio and video,
+ * the following tree would be created:
+ *
+ * chain "DASH Demux"
+ * |_ group "Representation set 1"
+ * | |_ chain "Qt Demux 0"
+ * | |_ group "Stream 0"
+ * | |_ chain "H264"
+ * | |_ chain "AAC"
+ * |_ group "Representation set 2"
+ * |_ chain "Qt Demux 1"
+ * |_ group "Stream 1"
+ * |_ chain "H264"
+ * |_ chain "AAC"
+ *
+ * Or, if audio and video are contained in separate fragments:
+ *
+ * chain "DASH Demux"
+ * |_ group "Representation set 1"
+ * | |_ chain "Qt Demux 0"
+ * | | |_ group "Stream 0"
+ * | | |_ chain "H264"
+ * | |_ chain "Qt Demux 1"
+ * | |_ group "Stream 1"
+ * | |_ chain "AAC"
+ * |_ group "Representation set 2"
+ * |_ chain "Qt Demux 3"
+ * | |_ group "Stream 2"
+ * | |_ chain "H264"
+ * |_ chain "Qt Demux 4"
+ * |_ group "Stream 3"
+ * |_ chain "AAC"
+ *
+ * In both cases, when switching from Set 1 to Set 2 an EOS is sent on
+ * each end pad corresponding to Rep 0, triggering the "drain" state to
+ * propagate upstream.
+ * Once both EOS have been processed, the "Set 1" group is completely
+ * drained, and decodebin2 will switch to the "Set 2" group.
+ *
+ * Note: nothing can be pushed to the new decoding group before the
+ * old one has been drained, which means that in order to be able to
+ * adapt quickly to bandwidth changes, we will not be able to rely
+ * on downstream buffering, and will instead manage an internal queue.
+ *
+ */
+
+#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 <inttypes.h>
+#include <gst/base/gsttypefindhelper.h>
+#include "gstdashdemux.h"
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src%d",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/dash+xml"));
+
+GST_DEBUG_CATEGORY_STATIC (gst_dash_demux_debug);
+#define GST_CAT_DEFAULT gst_dash_demux_debug
+
+enum
+{
+ PROP_0,
+
+ PROP_MAX_BUFFERING_TIME,
+ PROP_BANDWIDTH_USAGE,
+ PROP_MAX_BITRATE,
+ PROP_LAST
+};
+
+/* Default values for properties */
+#define DEFAULT_MAX_BUFFERING_TIME 30 /* in seconds */
+#define DEFAULT_BANDWIDTH_USAGE 0.8 /* 0 to 1 */
+#define DEFAULT_MAX_BITRATE 24000000 /* in bit/s */
+
+#define DEFAULT_FAILED_COUNT 3
+#define DOWNLOAD_RATE_HISTORY_MAX 3
+#define DOWNLOAD_RATE_TIME_MAX 3 * GST_SECOND
+
+/* Custom internal event to signal end of period */
+#define GST_EVENT_DASH_EOP GST_EVENT_MAKE_TYPE(81, GST_EVENT_TYPE_DOWNSTREAM | GST_EVENT_TYPE_SERIALIZED)
+static GstEvent *
+gst_event_new_dash_eop (void)
+{
+ return gst_event_new_custom (GST_EVENT_DASH_EOP, NULL);
+}
+
+
+/* GObject */
+static void gst_dash_demux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_dash_demux_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_dash_demux_dispose (GObject * obj);
+
+/* GstElement */
+static GstStateChangeReturn
+gst_dash_demux_change_state (GstElement * element, GstStateChange transition);
+
+/* GstDashDemux */
+static GstFlowReturn gst_dash_demux_pad (GstPad * pad, GstBuffer * buf);
+static gboolean gst_dash_demux_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_dash_demux_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_dash_demux_src_query (GstPad * pad, GstQuery * query);
+static void gst_dash_demux_stream_loop (GstDashDemux * demux);
+static void gst_dash_demux_download_loop (GstDashDemux * demux);
+static void gst_dash_demux_stop (GstDashDemux * demux);
+static void gst_dash_demux_resume_stream_task (GstDashDemux * demux);
+static void gst_dash_demux_resume_download_task (GstDashDemux * demux);
+static gboolean gst_dash_demux_setup_all_streams (GstDashDemux * demux);
+static gboolean gst_dash_demux_select_representations (GstDashDemux * demux);
+static GstCaps *gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream);
+static gboolean gst_dash_demux_get_next_fragment (GstDashDemux * demux, GstActiveStream **fragment_stream, GstClockTime *selected_ts);
+
+static void gst_dash_demux_clear_streams(GstDashDemux * demux);
+static void gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose);
+static GstClockTime gst_dash_demux_get_buffering_time (GstDashDemux * demux);
+
+static void
+_do_init (GType type)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_dash_demux_debug, "dashdemux", 0,
+ "dashdemux element");
+}
+
+GST_BOILERPLATE_FULL (GstDashDemux, gst_dash_demux, GstElement,
+ GST_TYPE_ELEMENT, _do_init);
+
+static void
+gst_dash_demux_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_static_pad_template (element_class, &srctemplate);
+
+ gst_element_class_add_static_pad_template (element_class, &sinktemplate);
+
+ gst_element_class_set_details_simple (element_class,
+ "DASH Demuxer",
+ "Codec/Demuxer",
+ "Dynamic Adaptive Streaming over HTTP demuxer",
+ "David Corvoysier <david.corvoysier@orange.com>\n\
+ Hamid Zakari <hamid.zakari@gmail.com>\n\
+ Gianluca Gennari <gennarone@gmail.com>");
+}
+
+static void
+gst_dash_demux_dispose (GObject * obj)
+{
+ GstDashDemux *demux = GST_DASH_DEMUX (obj);
+
+ if (demux->stream_task) {
+ if (GST_TASK_STATE (demux->stream_task) != GST_TASK_STOPPED) {
+ GST_DEBUG_OBJECT (demux, "Leaving streaming task");
+ gst_task_stop (demux->stream_task);
+ gst_task_join (demux->stream_task);
+ }
+ gst_object_unref (demux->stream_task);
+ g_static_rec_mutex_free (&demux->stream_lock);
+ g_mutex_free(demux->stream_timed_lock);
+ demux->stream_task = NULL;
+ }
+
+ if (demux->download_task) {
+ if (GST_TASK_STATE (demux->download_task) != GST_TASK_STOPPED) {
+ GST_DEBUG_OBJECT (demux, "Leaving download task");
+ gst_task_stop (demux->download_task);
+ gst_task_join (demux->download_task);
+ }
+ gst_object_unref (demux->download_task);
+ g_static_rec_mutex_free (&demux->download_lock);
+ demux->download_task = NULL;
+ }
+
+ g_cond_clear (&demux->download_cond);
+ g_mutex_clear (&demux->download_mutex);
+
+ if (demux->downloader != NULL) {
+ g_object_unref (demux->downloader);
+ demux->downloader = NULL;
+ }
+
+ gst_dash_demux_reset (demux, TRUE);
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+gst_dash_demux_class_init (GstDashDemuxClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_dash_demux_set_property;
+ gobject_class->get_property = gst_dash_demux_get_property;
+ gobject_class->dispose = gst_dash_demux_dispose;
+
+ g_object_class_install_property (gobject_class, PROP_MAX_BUFFERING_TIME,
+ g_param_spec_uint ("max-buffering-time", "Maximum buffering time",
+ "Maximum number of seconds of buffer accumulated during playback",
+ 2, G_MAXUINT, DEFAULT_MAX_BUFFERING_TIME,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BANDWIDTH_USAGE,
+ g_param_spec_float ("bandwidth-usage",
+ "Bandwidth usage [0..1]",
+ "Percentage of the available bandwidth to use when selecting representations",
+ 0, 1, DEFAULT_BANDWIDTH_USAGE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
+ g_param_spec_uint ("max-bitrate", "Max bitrate",
+ "Max of bitrate supported by target decoder",
+ 1000, G_MAXUINT, DEFAULT_MAX_BITRATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_dash_demux_change_state);
+}
+
+static gboolean
+_check_queue_full (GstDataQueue * q, guint visible, guint bytes, guint64 time,
+ GstDashDemux *demux)
+{
+ return time > demux->max_buffering_time;
+}
+
+static void
+_data_queue_item_destroy (GstDataQueueItem * item)
+{
+ gst_mini_object_unref (item->object);
+ g_free (item);
+}
+
+static void
+gst_dash_demux_stream_push_event (GstDashDemuxStream * stream,
+ GstEvent *event)
+{
+ GstDataQueueItem *item = g_new0 (GstDataQueueItem, 1);
+
+ item->object = GST_MINI_OBJECT_CAST (event);
+ item->destroy = (GDestroyNotify) _data_queue_item_destroy;
+
+ gst_data_queue_push (stream->queue, item);
+}
+
+static void
+gst_dash_demux_stream_push_data (GstDashDemuxStream * stream,
+ GstBuffer * buffer)
+{
+ GstDataQueueItem *item = g_new0 (GstDataQueueItem, 1);
+
+ item->object = GST_MINI_OBJECT_CAST (buffer);
+ item->duration = GST_BUFFER_DURATION (buffer);
+ item->visible = TRUE;
+ item->size = GST_BUFFER_SIZE (buffer);
+
+ item->destroy = (GDestroyNotify) _data_queue_item_destroy;
+
+ gst_data_queue_push (stream->queue, item);
+}
+
+static void
+gst_dash_demux_init (GstDashDemux * demux, GstDashDemuxClass * klass)
+{
+ /* sink pad */
+ demux->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ gst_pad_set_chain_function (demux->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_pad));
+ gst_pad_set_event_function (demux->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_sink_event));
+ gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
+
+ /* Downloader */
+ demux->downloader = gst_uri_downloader_new ();
+
+ /* Properties */
+ demux->max_buffering_time = DEFAULT_MAX_BUFFERING_TIME * GST_SECOND;
+ demux->bandwidth_usage = DEFAULT_BANDWIDTH_USAGE;
+ demux->max_bitrate = DEFAULT_MAX_BITRATE;
+
+ demux->max_video_width = 0;
+ demux->max_video_height = 0;
+
+ /* Updates task */
+ g_static_rec_mutex_init (&demux->download_lock);
+ demux->download_task =
+ gst_task_create ((GstTaskFunction) gst_dash_demux_download_loop, demux);
+ gst_task_set_lock (demux->download_task, &demux->download_lock);
+ g_cond_init (&demux->download_cond);
+ g_mutex_init (&demux->download_mutex);
+
+ /* Streaming task */
+ g_static_rec_mutex_init (&demux->stream_lock);
+ demux->stream_task =
+ gst_task_create ((GstTaskFunction) gst_dash_demux_stream_loop, demux);
+ gst_task_set_lock (demux->stream_task, &demux->stream_lock);
+ demux->stream_timed_lock = g_mutex_new ();
+}
+
+static void
+gst_dash_demux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDashDemux *demux = GST_DASH_DEMUX (object);
+
+ switch (prop_id) {
+ case PROP_MAX_BUFFERING_TIME:
+ demux->max_buffering_time = g_value_get_uint (value) * GST_SECOND;
+ break;
+ case PROP_BANDWIDTH_USAGE:
+ demux->bandwidth_usage = g_value_get_float (value);
+ break;
+ case PROP_MAX_BITRATE:
+ demux->max_bitrate = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_dash_demux_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstDashDemux *demux = GST_DASH_DEMUX (object);
+
+ switch (prop_id) {
+ case PROP_MAX_BUFFERING_TIME:
+ g_value_set_uint (value, demux->max_buffering_time / GST_SECOND);
+ break;
+ case PROP_BANDWIDTH_USAGE:
+ g_value_set_float (value, demux->bandwidth_usage);
+ break;
+ case PROP_MAX_BITRATE:
+ g_value_set_uint (value, demux->max_bitrate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_dash_demux_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstDashDemux *demux = GST_DASH_DEMUX (element);
+
+ GST_DEBUG_OBJECT (demux, "changing state %s - %s",
+ gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+ gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_dash_demux_reset (demux, FALSE);
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ demux->cancelled = TRUE;
+ gst_dash_demux_stop (demux);
+ gst_task_join (demux->stream_task);
+ gst_task_join (demux->download_task);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void
+gst_dash_demux_flush_stream_queues (GstDashDemux * demux)
+{
+ GSList *it;
+ GstDashDemuxStream *stream;
+ for(it = demux->streams; it; it=it->next)
+ {
+ stream = it->data;
+ gst_data_queue_set_flushing(stream->queue, TRUE);
+ gst_data_queue_flush(stream->queue);
+ }
+}
+
+static gboolean
+gst_dash_demux_src_event (GstPad * pad, GstEvent * event)
+{
+ GstDashDemux *demux;
+
+ demux = GST_DASH_DEMUX (gst_pad_get_element_private (pad));
+
+ switch (event->type) {
+ case GST_EVENT_SEEK:
+ {
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ GList *list;
+ GstClockTime current_pos = GST_CLOCK_TIME_NONE;
+ GstClockTime target_pos;
+ guint current_period;
+ GstActiveStream *stream;
+ GstStreamPeriod *period = NULL;
+ guint nb_active_stream;
+ guint stream_idx = 0;
+ guint *seek_idx = NULL; /*Seek positions on each stream*/
+ gboolean end_of_mpd = FALSE;
+
+ if (gst_mpd_client_is_live (demux->client)) {
+ 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)
+ return FALSE;
+
+ GST_DEBUG_OBJECT (demux,
+ "seek event, rate: %f type: %d start: %" GST_TIME_FORMAT " stop: %"
+ GST_TIME_FORMAT, rate, start_type, GST_TIME_ARGS (start),
+ GST_TIME_ARGS (stop));
+
+ //GST_MPD_CLIENT_LOCK (demux->client);
+
+ /* select the requested Period in the Media Presentation */
+ target_pos = (GstClockTime) start;
+ current_period = 0;
+ for (list = g_list_first (demux->client->periods); list;
+ list = g_list_next (list)) {
+ period = list->data;
+ current_pos = period->start;
+ current_period = period->number;
+ if (current_pos <= target_pos
+ && target_pos < current_pos + period->duration) {
+ break;
+ }
+ }
+ if(target_pos == current_pos + period->duration) {
+ /*Seeking to the end of MPD*/
+ end_of_mpd = TRUE;
+ goto seeking;
+ }
+ if (list == NULL) {
+ GST_WARNING_OBJECT (demux, "Could not find seeked Period");
+ return FALSE;
+ }
+ if (current_period != gst_mpd_client_get_period_index (demux->client)) {
+ GST_DEBUG_OBJECT (demux, "Seeking to Period %d", current_period);
+ /* setup video, audio and subtitle streams, starting from the new Period */
+ if (!gst_mpd_client_set_period_index (demux->client, current_period) ||
+ !gst_dash_demux_setup_all_streams (demux))
+ return FALSE;
+ }
+
+ /*select the requested segments for all streams*/
+ nb_active_stream = gst_mpdparser_get_nb_active_stream (demux->client);
+ seek_idx = g_malloc0(sizeof(gint)*nb_active_stream);
+ gint video_idx = gst_mpd_client_get_video_active_stream_id(demux->client);
+ if(video_idx >= 0) {
+ /*Seeking on video stream firstly.*/
+ GstClockTime segment_start;
+ segment_start = gst_mpd_client_stream_find_segment(demux->client, video_idx,
+ target_pos, &seek_idx[video_idx]);
+ if(!GST_CLOCK_TIME_IS_VALID(segment_start))
+ goto no_segment;
+ target_pos = segment_start;
+ }
+ /*Seeking on non video streams*/
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ if (video_idx != stream_idx) {
+ GstClockTime stream_start = gst_mpd_client_stream_find_segment(demux->client,
+ stream_idx, target_pos, &seek_idx[stream_idx]);
+ if(!GST_CLOCK_TIME_IS_VALID (stream_start)) {
+ goto no_segment;
+ }
+ }
+ }
+
+seeking:
+ /* We can actually perform the seek */
+ nb_active_stream = gst_mpdparser_get_nb_active_stream (demux->client);
+
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ GST_DEBUG_OBJECT (demux, "sending flush start");
+ stream_idx = 0;
+ while (stream_idx < nb_active_stream) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ dash_stream->need_header = TRUE;
+ gst_pad_push_event (dash_stream->srcpad,
+ gst_event_new_flush_start ());
+ stream_idx++;
+ }
+ }
+
+ /* Stop the demux */
+ demux->cancelled = TRUE;
+ gst_dash_demux_stop (demux);
+ GST_DEBUG_OBJECT (demux, "joining tasks");
+ gst_task_join (demux->stream_task);
+ gst_task_join (demux->download_task);
+ GST_DEBUG_OBJECT (demux, "tasks was joined");
+
+ /* Wait for streaming to finish */
+ g_static_rec_mutex_lock (&demux->stream_lock);
+
+ //GST_MPD_CLIENT_LOCK (demux->client);
+ demux->end_of_period = end_of_mpd;
+ //GST_MPD_CLIENT_UNLOCK (demux->client);
+
+
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ GstCaps *caps = gst_pad_get_negotiated_caps (dash_stream->srcpad);
+ if(caps) {
+ gst_caps_replace (&dash_stream->input_caps, NULL);
+ gst_caps_unref (caps);
+ }
+ if(!end_of_mpd) {
+ GST_DEBUG_OBJECT (demux, "Seeking to sequence %d on stream %d", seek_idx[stream_idx], stream_idx);
+ stream = gst_mpdparser_get_active_stream_by_index (demux->client, stream_idx);
+ gst_mpd_client_set_segment_index(stream, seek_idx[stream_idx]);
+ }
+ gst_data_queue_set_flushing(dash_stream->queue, FALSE);
+ dash_stream->start_time = target_pos;
+ dash_stream->download_end_of_period = end_of_mpd;
+ dash_stream->stream_end_of_period = end_of_mpd;
+ dash_stream->stream_eos = end_of_mpd;
+ dash_stream->need_segment = TRUE;
+ }
+ if(!end_of_mpd)
+ g_free(seek_idx);
+
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ GST_DEBUG_OBJECT (demux, "Sending flush stop on all pad");
+
+ for (stream_idx = 0; stream_idx < nb_active_stream; stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ gst_pad_push_event (dash_stream->srcpad,
+ gst_event_new_flush_stop ());
+ }
+ }
+
+ /* Restart the demux */
+ demux->cancelled = FALSE;
+ gst_dash_demux_resume_download_task (demux);
+ gst_dash_demux_resume_stream_task (demux);
+ g_static_rec_mutex_unlock (&demux->stream_lock);
+
+ return TRUE;
+no_segment:
+ {
+ GST_WARNING_OBJECT (demux, "Could not find seeked fragment on stream %d", stream_idx);
+ g_free(seek_idx);
+ return FALSE;
+ }
+ }
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+}
+
+static gboolean
+gst_dash_demux_setup_mpdparser_streams (GstDashDemux * demux, GstMpdClient *client)
+{
+ GList *listLang = NULL;
+ guint i, nb_audio;
+ gchar *lang;
+
+ GST_MPD_CLIENT_LOCK (client);
+ /* clean old active stream list, if any */
+ gst_active_streams_free (client);
+
+ if (!gst_mpd_client_setup_streaming (client, GST_STREAM_VIDEO, "")) {
+ GST_INFO_OBJECT (demux, "No video adaptation set found");
+ } else {
+ gst_mpd_client_get_max_video_dimensions(client, &demux->max_video_width,
+ &demux->max_video_height);
+ }
+
+ nb_audio =
+ gst_mpdparser_get_list_and_nb_of_audio_language (client,
+ &listLang);
+ if (nb_audio == 0)
+ nb_audio = 1;
+ GST_INFO_OBJECT (demux, "Number of language is=%d", nb_audio);
+
+ for (i = 0; i < nb_audio; i++) {
+ lang = (gchar *) g_list_nth_data (listLang, i);
+ if (gst_mpdparser_get_nb_adaptationSet (client) > 1)
+ if (!gst_mpd_client_setup_streaming (client, GST_STREAM_AUDIO,
+ lang))
+ GST_INFO_OBJECT (demux, "No audio adaptation set found");
+
+ if (gst_mpdparser_get_nb_adaptationSet (client) > nb_audio)
+ if (!gst_mpd_client_setup_streaming (client,
+ GST_STREAM_APPLICATION, lang))
+ GST_INFO_OBJECT (demux, "No application adaptation set found");
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+ return TRUE;
+}
+
+static gboolean
+gst_dash_demux_setup_all_streams (GstDashDemux * demux)
+{
+ guint i;
+ if( !gst_dash_demux_setup_mpdparser_streams(demux, demux->client))
+ return FALSE;
+
+ GST_DEBUG_OBJECT (demux, "Creating dashdemux streams");
+ gst_dash_demux_clear_streams(demux);
+ for ( i =0; i < gst_mpdparser_get_nb_active_stream (demux->client); i++) {
+ GstDashDemuxStream *dash_stream;
+ GstCaps *caps;
+ GstActiveStream *active_stream;
+ dash_stream = g_new0(GstDashDemuxStream, 1);
+ demux->streams = g_slist_append(demux->streams, dash_stream);
+ dash_stream->idx = i;
+ dash_stream->queue = gst_data_queue_new ((GstDataQueueCheckFullFunction) _check_queue_full, demux);
+ dash_stream->need_header = TRUE;
+ dash_stream->need_segment = TRUE;
+ dash_stream->start_time = GST_CLOCK_TIME_NONE;
+ gst_download_rate_init (&dash_stream->dnl_rate);
+ gst_download_rate_set_max_length (&dash_stream->dnl_rate,
+ DOWNLOAD_RATE_HISTORY_MAX);
+ gst_download_rate_set_aver_period (&dash_stream->dnl_rate,
+ DOWNLOAD_RATE_TIME_MAX);
+ /*Create stream pad*/
+ active_stream = gst_mpdparser_get_active_stream_by_index(demux->client, i);
+ caps = gst_dash_demux_get_input_caps(demux, active_stream);
+ dash_stream->srcpad = gst_pad_new_from_static_template (&srctemplate, NULL);
+ gst_pad_set_event_function (dash_stream->srcpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_src_event));
+ gst_pad_set_query_function (dash_stream->srcpad,
+ GST_DEBUG_FUNCPTR (gst_dash_demux_src_query));
+ gst_pad_set_element_private (dash_stream->srcpad, demux);
+ gst_pad_set_active (dash_stream->srcpad, TRUE);
+ gst_pad_set_caps (dash_stream->srcpad, caps);
+ gst_caps_unref(caps);
+ gst_element_add_pad (GST_ELEMENT (demux), gst_object_ref (dash_stream->srcpad));
+ }
+ /* Send 'no-more-pads' to have decodebin create the new group */
+ gst_element_no_more_pads (GST_ELEMENT (demux));
+
+ return TRUE;
+}
+
+static gboolean
+gst_dash_demux_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstDashDemux *demux = GST_DASH_DEMUX (gst_pad_get_parent (pad));
+
+ switch (event->type) {
+ case GST_EVENT_EOS:{
+ gchar *manifest;
+ GstQuery *query;
+ gboolean res;
+
+ if (demux->manifest == NULL) {
+ GST_WARNING_OBJECT (demux, "Received EOS without a manifest.");
+ break;
+ }
+
+ GST_DEBUG_OBJECT (demux, "Got EOS on the sink pad: manifest fetched");
+
+ if (demux->client)
+ gst_mpd_client_free (demux->client);
+ demux->client = gst_mpd_client_new ();
+
+ query = gst_query_new_uri ();
+ res = gst_pad_peer_query (pad, query);
+ if (res) {
+ gst_query_parse_uri (query, &demux->client->mpd_uri);
+ GST_DEBUG_OBJECT (demux, "Fetched MPD file at URI: %s",
+ demux->client->mpd_uri);
+ } else {
+ GST_WARNING_OBJECT (demux, "MPD URI query failed.");
+ }
+ gst_query_unref (query);
+
+ manifest = (gchar *) GST_BUFFER_DATA (demux->manifest);
+ if (manifest == NULL) {
+ GST_WARNING_OBJECT (demux, "Error validating the manifest.");
+ } else if (!gst_mpd_parse (demux->client, 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 manifest */
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid manifest."),
+ (NULL));
+ return FALSE;
+ }
+
+ gst_buffer_unref (demux->manifest);
+ demux->manifest = NULL;
+
+ if (!gst_mpd_client_setup_media_presentation (demux->client)) {
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE,
+ ("Incompatible manifest file."), (NULL));
+ return FALSE;
+ }
+
+ /* setup video, audio and subtitle streams, starting from first Period */
+ if (!gst_mpd_client_set_period_index (demux->client, 0) ||
+ !gst_dash_demux_setup_all_streams (demux))
+ return FALSE;
+
+ /* start playing from the first segment */
+ gst_mpd_client_set_segment_index_for_all_streams (demux->client, 0);
+
+ /* Send duration message */
+ if (!gst_mpd_client_is_live (demux->client)) {
+ GstClockTime duration =
+ gst_mpd_client_get_media_presentation_duration (demux->client);
+
+ if (duration != GST_CLOCK_TIME_NONE) {
+ GST_DEBUG_OBJECT (demux,
+ "Sending duration message : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration));
+ gst_element_post_message (GST_ELEMENT (demux),
+ gst_message_new_duration (GST_OBJECT (demux), GST_FORMAT_TIME,
+ duration));
+ } else {
+ GST_DEBUG_OBJECT (demux,
+ "mediaPresentationDuration unknown, can not send the duration message");
+ }
+ }
+ gst_dash_demux_resume_download_task (demux);
+ gst_dash_demux_resume_stream_task (demux);
+ 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_dash_demux_src_query (GstPad * pad, GstQuery * query)
+{
+ GstDashDemux *dashdemux;
+ gboolean ret = FALSE;
+
+ if (query == NULL)
+ return FALSE;
+
+ dashdemux = GST_DASH_DEMUX (gst_pad_get_element_private (pad));
+
+ switch (query->type) {
+ case GST_QUERY_DURATION:{
+ GstClockTime duration = -1;
+ GstFormat fmt;
+
+ gst_query_parse_duration (query, &fmt, NULL);
+ if (fmt == GST_FORMAT_TIME) {
+ duration =
+ gst_mpd_client_get_media_presentation_duration (dashdemux->client);
+ if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ ret = TRUE;
+ }
+ }
+ GST_DEBUG_OBJECT (dashdemux,
+ "GST_QUERY_DURATION returns %s with duration %" GST_TIME_FORMAT,
+ ret ? "TRUE" : "FALSE", GST_TIME_ARGS (duration));
+ break;
+ }
+ case GST_QUERY_SEEKING:{
+ GstFormat fmt;
+ gint64 stop = -1;
+
+ gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+ GST_DEBUG_OBJECT (dashdemux, "Received GST_QUERY_SEEKING with format %d",
+ fmt);
+ if (fmt == GST_FORMAT_TIME) {
+ GstClockTime duration;
+
+ duration =
+ gst_mpd_client_get_media_presentation_duration (dashdemux->client);
+ if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0)
+ stop = duration;
+
+ gst_query_set_seeking (query, fmt,
+ !gst_mpd_client_is_live (dashdemux->client), 0, stop);
+ ret = TRUE;
+ GST_DEBUG_OBJECT (dashdemux, "GST_QUERY_SEEKING returning with stop : %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (stop));
+ }
+ break;
+ }
+ case GST_QUERY_URI: {
+ /* forwarding uri */
+ GST_DEBUG("URI query recevied in DASH demux.....");
+ gboolean res;
+ res = gst_pad_query_default (pad,query);
+ if(res)
+ GST_DEBUG("forwarding URI is done successfully!!...");
+ ret = TRUE;
+ break;
+ }
+ default:{
+ // By default, do not forward queries upstream
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_dash_demux_pad (GstPad * pad, GstBuffer * buf)
+{
+ GstDashDemux *demux = GST_DASH_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 void
+gst_dash_demux_stop (GstDashDemux * demux)
+{
+ gst_uri_downloader_cancel (demux->downloader);
+ gst_dash_demux_flush_stream_queues (demux);
+
+ if (GST_TASK_STATE (demux->download_task) != GST_TASK_STOPPED) {
+ GST_TASK_SIGNAL (demux->download_task);
+ gst_task_stop (demux->download_task);
+ g_mutex_lock (&demux->download_mutex);
+ g_cond_signal (&demux->download_cond);
+ g_mutex_unlock (&demux->download_mutex);
+ }
+ if (GST_TASK_STATE (demux->stream_task) != GST_TASK_STOPPED) {
+ GST_TASK_SIGNAL (demux->stream_task);
+ gst_task_stop (demux->stream_task);
+ }
+}
+
+/* gst_dash_demux_stream_loop:
+ *
+ * Loop for the "stream' task that pushes fragments to the src pads.
+ *
+ * Startup:
+ * The task is started as soon as we have received the manifest and
+ * waits for the first fragment to be downloaded and pushed in the
+ * queue. Once this fragment has been pushed, the task pauses itself
+ * until actual playback begins.
+ *
+ * During playback:
+ * The task pushes fragments downstream at regular intervals based on
+ * the fragment duration. If it detects a queue underrun, it sends
+ * a buffering event to tell the main application to pause.
+ *
+ * Teardown:
+ * The task is stopped when we have reached the end of the manifest
+ * and emptied our queue.
+ *
+ */
+static void
+gst_dash_demux_stream_loop (GstDashDemux * demux)
+{
+ GstFlowReturn ret;
+ GstActiveStream *active_stream;
+ GstDashDemuxStream *selected_stream = NULL;
+ GstClockTime min_ts = GST_CLOCK_TIME_NONE;
+ guint i = 0;
+ gboolean eos = TRUE;
+ gboolean eop = TRUE;
+
+ for (i = 0; i < g_slist_length (demux->streams); i++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ GstBuffer *buffer;
+ GstDataQueueItem *item;
+
+ if (dash_stream->stream_eos) {
+ GST_DEBUG_OBJECT (demux, "Stream %d is eos, skipping", dash_stream->idx);
+ continue;
+ }
+
+ if (dash_stream->stream_end_of_period) {
+ GST_DEBUG_OBJECT (demux, "Stream %d is eop, skipping", dash_stream->idx);
+ eos = FALSE;
+ continue;
+ }
+ eos = FALSE;
+ eop = FALSE;
+
+ if (!gst_data_queue_peek (dash_stream->queue, &item))
+ goto flushing;
+
+ if(GST_IS_BUFFER(item->object)) {
+ buffer = GST_BUFFER(item->object);
+ if(GST_BUFFER_TIMESTAMP(buffer) < min_ts ||
+ !GST_CLOCK_TIME_IS_VALID(min_ts)) {
+ min_ts = GST_BUFFER_TIMESTAMP(buffer);
+ selected_stream = dash_stream;
+ } else if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (item->object))) {
+ selected_stream = dash_stream;
+ break;
+ }
+ } else {
+ selected_stream = dash_stream;
+ break;
+ }
+ }
+
+ if(selected_stream) {
+ GstBuffer *buffer;
+ GstDataQueueItem *item;
+
+ if (!gst_data_queue_pop (selected_stream->queue, &item))
+ goto end;
+ if ( GST_IS_BUFFER (item->object)) {
+ buffer = GST_BUFFER(item->object);
+ active_stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
+
+ if (selected_stream->need_segment) {
+ if(!GST_CLOCK_TIME_IS_VALID (selected_stream->start_time)) {
+ if(GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))){
+ selected_stream->start_time = GST_BUFFER_TIMESTAMP (buffer);
+ } else {
+ selected_stream->start_time = 0;
+ }
+ }
+ /* And send a newsegment */
+ GST_DEBUG_OBJECT (demux, "Sending new-segment stream #%d. segment start:%"
+ GST_TIME_FORMAT, selected_stream->idx, GST_TIME_ARGS (selected_stream->start_time));
+ gst_pad_push_event (selected_stream->srcpad,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ selected_stream->start_time, GST_CLOCK_TIME_NONE, selected_stream->start_time));
+ selected_stream->need_segment = FALSE;
+ }
+
+ GST_DEBUG_OBJECT (demux, "Pushing fragment #%llu (stream %d) ts=%"GST_TIME_FORMAT, GST_BUFFER_OFFSET (buffer),
+ selected_stream->idx, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+ ret = gst_pad_push (selected_stream->srcpad, gst_buffer_ref(buffer) );
+ item->destroy (item);
+ if ((ret != GST_FLOW_OK) && (active_stream->mimeType == GST_STREAM_VIDEO))
+ goto error_pushing;
+ } else {
+ GstEvent *event = GST_EVENT (item->object);
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ selected_stream->stream_eos = TRUE;
+ selected_stream->stream_end_of_period = TRUE;
+ } else if (GST_EVENT_TYPE (event) == GST_EVENT_DASH_EOP) {
+ selected_stream->stream_end_of_period = TRUE;
+ }
+
+ if (GST_EVENT_TYPE (item->object) != GST_EVENT_DASH_EOP) {
+ gst_pad_push_event (selected_stream->srcpad,
+ gst_event_ref (GST_EVENT_CAST (item->object)));
+ }
+
+ item->destroy (item);
+ }
+ } else {
+ if (eos) {
+ goto end_of_manifest;
+ } else if (eop) {
+ /*TODO Switch to next period*/
+ }
+ }
+
+end:
+ return;
+
+flushing:
+ {
+ GST_INFO_OBJECT (demux, "Queue is flushing. Stopped streaming task");
+ gst_task_stop (demux->stream_task);
+ return;
+ }
+
+end_of_manifest:
+ {
+ GST_INFO_OBJECT (demux, "Reached end of manifest, sending EOS");
+ guint i = 0;
+ for (i = 0; i < gst_mpdparser_get_nb_active_stream (demux->client); i++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ gst_pad_push_event (dash_stream->srcpad, gst_event_new_eos ());
+ }
+ GST_INFO_OBJECT (demux, "Stopped streaming task");
+ gst_task_stop (demux->stream_task);
+ return;
+ }
+
+error_pushing:
+ {
+ /* FIXME: handle error */
+ GST_ERROR_OBJECT (demux,
+ "Error pushing buffer: %s... terminating the demux",
+ gst_flow_get_name (ret));
+ gst_dash_demux_stop (demux);
+ return;
+ }
+}
+
+static void
+gst_dash_demux_clear_streams(GstDashDemux * demux) {
+ guint i = 0;
+ gst_dash_demux_flush_stream_queues (demux);
+ for (i = 0; i < g_slist_length(demux->streams); i++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, i);
+ gst_download_rate_deinit (&dash_stream->dnl_rate);
+ if (dash_stream->input_caps) {
+ gst_caps_unref (dash_stream->input_caps);
+ dash_stream->input_caps = NULL;
+ }
+ if (dash_stream->srcpad) {
+ gst_object_unref (dash_stream->srcpad);
+ dash_stream->srcpad = NULL;
+ }
+ /*TODO consider unref stream->output_caps*/
+ g_object_unref (dash_stream->queue);
+ }
+ if(demux->streams) {
+ g_slist_free(demux->streams);
+ demux->streams = NULL;
+ }
+}
+
+static void
+gst_dash_demux_reset (GstDashDemux * demux, gboolean dispose)
+{
+ gint stream_idx;
+ demux->end_of_period = FALSE;
+ demux->cancelled = FALSE;
+
+ gst_dash_demux_clear_streams(demux);
+
+ if (demux->manifest) {
+ gst_buffer_unref (demux->manifest);
+ demux->manifest = NULL;
+ }
+ if (demux->client) {
+ gst_mpd_client_free (demux->client);
+ demux->client = NULL;
+ }
+ if (!dispose) {
+ demux->client = gst_mpd_client_new ();
+ }
+
+ demux->last_manifest_update = GST_CLOCK_TIME_NONE;
+ for (stream_idx = 0; stream_idx < g_slist_length (demux->streams); stream_idx++) {
+ GstDashDemuxStream *dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+ dash_stream->need_segment = TRUE;
+ }
+}
+
+static GstClockTime
+gst_dash_demux_get_buffering_time (GstDashDemux * demux)
+{
+ GstClockTime buffer_time = 0;
+ GSList *it;
+ GstDashDemuxStream *stream;
+ GstDataQueueSize queue_size;
+
+ for(it=demux->streams; it; it=it->next) {
+ stream = it->data;
+ gst_data_queue_get_level(stream->queue, &queue_size);
+
+ if (queue_size.time > 0) {
+ buffer_time = queue_size.time;
+ break;
+ }
+ }
+
+ return buffer_time;
+}
+
+static gboolean
+gst_dash_demux_update_manifest(GstDashDemux *demux) {
+ GstFragment *download;
+ GstBuffer *buffer;
+ GstClockTime duration, now = gst_util_get_timestamp();
+ gint64 update_period = demux->client->mpd_node->minimumUpdatePeriod;
+
+ if (update_period == -1) {
+ GST_DEBUG_OBJECT (demux, "minimumUpdatePeriod unspecified, will not update MPD");
+ return TRUE;
+ }
+
+ /* init reference time for manifest file updates */
+ if (!GST_CLOCK_TIME_IS_VALID (demux->last_manifest_update))
+ demux->last_manifest_update = now;
+
+ /* update the manifest file */
+ if (now >= demux->last_manifest_update + update_period * GST_MSECOND) {
+ GST_DEBUG_OBJECT (demux, "Updating manifest file from URL %s",
+ demux->client->mpd_uri);
+ download =
+ gst_uri_downloader_fetch_uri (demux->downloader,
+ demux->client->mpd_uri);
+ if (download == NULL) {
+ GST_WARNING_OBJECT (demux,
+ "Failed to update the manifest file from URL %s",
+ demux->client->mpd_uri);
+ } else {
+ GstMpdClient *new_client = NULL;
+ guint period_idx;
+ const gchar *period_id;
+ GSList *iter;
+
+ buffer = gst_fragment_get_buffer(download);
+ g_object_unref (download);
+ /* parse the manifest file */
+ if (buffer == NULL) {
+ GST_WARNING_OBJECT (demux, "Error validating the manifest.");
+ return TRUE;
+ }
+
+ new_client = gst_mpd_client_new ();
+ new_client->mpd_uri = g_strdup (demux->client->mpd_uri);
+ if (!gst_mpd_parse (new_client,
+ (gchar *) GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) {
+ /* 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 manifest */
+ GST_WARNING_OBJECT (demux, "Error parsing the manifest.");
+ gst_buffer_unref (buffer);
+ return TRUE;
+ }
+
+ gst_buffer_unref (buffer);
+ GST_DEBUG_OBJECT (demux, "Updating manifest");
+
+ period_id = gst_mpd_client_get_period_id (demux->client);
+ period_idx = gst_mpd_client_get_period_index (demux->client);
+
+ /* setup video, audio and subtitle streams, starting from current Period */
+ if (!gst_mpd_client_setup_media_presentation (new_client)) {
+ /* TODO */
+ }
+
+ if (period_idx) {
+ /*If more than one period exists.*/
+ if (!gst_mpd_client_set_period_id (new_client, period_id)) {
+ GST_DEBUG_OBJECT (demux,
+ "Error setting up the updated manifest file");
+ return FALSE;
+ }
+ } else {
+ if (!gst_mpd_client_set_period_index (new_client, period_idx)) {
+ GST_DEBUG_OBJECT (demux,
+ "Error setting up the updated manifest file");
+ return FALSE;
+ }
+ }
+
+ if (!gst_dash_demux_setup_mpdparser_streams (demux, new_client)) {
+ GST_ERROR_OBJECT (demux, "Failed to setup streams on manifest "
+ "update");
+ return FALSE;
+ }
+
+ /* update the streams to play from the next segment */
+ for (iter = demux->streams; iter; iter = g_slist_next (iter)) {
+ GstDashDemuxStream *demux_stream = iter->data;
+ GstActiveStream *new_stream;
+ GstClockTime ts;
+
+ new_stream = gst_mpdparser_get_active_stream_by_index (new_client,
+ demux_stream->idx);
+
+ if (!new_stream) {
+ GST_DEBUG_OBJECT (demux,
+ "Stream of index %d is missing from manifest update",
+ demux_stream->idx);
+ return FALSE;
+ }
+
+ if (gst_mpd_client_get_next_fragment_timestamp (demux->client,
+ demux_stream->idx, &ts)) {
+ gst_mpd_client_stream_seek (new_client, demux_stream->idx, ts);
+
+ } else
+ if (gst_mpd_client_get_last_fragment_timestamp (demux->client,
+ demux_stream->idx, &ts)) {
+ /* try to set to the old timestamp + 1 */
+ gst_mpd_client_stream_seek (new_client, demux_stream->idx, ts+1);
+ }
+ }
+
+ /*Remember download failed count*/
+ new_client->download_failed_count = demux->client->download_failed_count;
+
+ gst_mpd_client_free (demux->client);
+ demux->client = new_client;
+
+ /* Send an updated duration message */
+ duration =
+ gst_mpd_client_get_media_presentation_duration (demux->client);
+
+ if (duration != GST_CLOCK_TIME_NONE) {
+ GST_DEBUG_OBJECT (demux,
+ "Sending duration message : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration));
+ gst_element_post_message (GST_ELEMENT (demux),
+ gst_message_new_duration(GST_OBJECT (demux), GST_FORMAT_TIME, duration));
+ } else {
+ GST_DEBUG_OBJECT (demux,
+ "mediaPresentationDuration unknown, can not send the duration message");
+ }
+ demux->last_manifest_update = gst_util_get_timestamp ();
+ GST_DEBUG_OBJECT (demux, "Manifest file successfully updated");
+ }
+ }
+ return TRUE;
+}
+
+static void
+gst_dash_demux_download_wait (GstDashDemux * demux, GstClockTime time_diff)
+{
+ gint64 end_time = g_get_monotonic_time () + time_diff / GST_USECOND;
+
+ GST_DEBUG_OBJECT (demux, "Download waiting for %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time_diff));
+ g_cond_wait_until (&demux->download_cond, &demux->download_mutex, end_time);
+ GST_DEBUG_OBJECT (demux, "Download finished waiting");
+}
+
+static void
+gst_dash_demux_check_live(GstDashDemux* demux, GstActiveStream *fragment_stream,
+ GstClockTime fragment_ts)
+{
+ gint64 time_diff;
+ gint pos;
+
+ pos =
+ gst_mpd_client_check_time_position (demux->client, fragment_stream,
+ fragment_ts, &time_diff);
+ GST_DEBUG_OBJECT (demux,
+ "Checked position for fragment ts %" GST_TIME_FORMAT
+ ", res: %d, diff: %" G_GINT64_FORMAT, GST_TIME_ARGS (fragment_ts),
+ pos, time_diff);
+
+ time_diff *= GST_USECOND;
+ if (pos < 0) {
+ /* we're behind, try moving to the 'present' */
+ GDateTime *now = g_date_time_new_now_utc ();
+
+ GST_DEBUG_OBJECT (demux,
+ "Falling behind live stream, moving forward");
+ gst_mpd_client_seek_to_time(demux->client, now);
+ g_date_time_unref (now);
+
+ } else if (pos > 0) {
+ /* we're ahead, wait a little */
+
+ GST_DEBUG_OBJECT (demux, "Waiting for next segment to be created");
+ gst_dash_demux_download_wait (demux, time_diff);
+ } else {
+ demux->client->download_failed_count++;
+ }
+}
+
+/* gst_dash_demux_download_loop:
+ *
+ * Loop for the "download' task that fetches fragments based on the
+ * selected representations.
+ *
+ * Startup:
+ *
+ * The task is started from the stream loop.
+ *
+ * During playback:
+ *
+ * It sequentially fetches fragments corresponding to the current
+ * representations and pushes them into a queue.
+ *
+ * It tries to maintain the number of queued items within a predefined
+ * range: if the queue is full, it will pause, checking every 100 ms if
+ * it needs to restart downloading fragments.
+ *
+ * When a new set of fragments has been downloaded, it evaluates the
+ * download time to check if we can or should switch to a different
+ * set of representations.
+ *
+ * Teardown:
+ *
+ * The task will exit when it encounters an error or when the end of the
+ * manifest has been reached.
+ *
+ */
+void
+gst_dash_demux_download_loop (GstDashDemux * demux)
+{
+ GstActiveStream *fragment_stream = NULL;
+ GstClockTime fragment_ts;
+ if ( gst_mpd_client_is_live (demux->client) && demux->client->mpd_uri != NULL ) {
+ if (!gst_dash_demux_update_manifest(demux))
+ goto end_of_manifest;
+ }
+
+ /* try to switch to another set of representations if needed */
+ gst_dash_demux_select_representations (demux);
+
+ /* fetch the next fragment */
+ while (!gst_dash_demux_get_next_fragment (demux, &fragment_stream, &fragment_ts)) {
+ if (demux->end_of_period) {
+ GST_INFO_OBJECT (demux, "Reached the end of the Period");
+ /* setup video, audio and subtitle streams, starting from the next Period */
+ if (!gst_mpd_client_set_period_index (demux->client,
+ gst_mpd_client_get_period_index (demux->client) + 1)
+ || !gst_dash_demux_setup_all_streams (demux)) {
+ GST_INFO_OBJECT (demux, "Reached the end of the manifest file");
+ gst_task_start (demux->stream_task);
+ goto end_of_manifest;
+ }
+ /* start playing from the first segment of the new period */
+ gst_mpd_client_set_segment_index_for_all_streams (demux->client, 0);
+ demux->end_of_period = FALSE;
+ } else if (!demux->cancelled) {
+ gst_uri_downloader_reset (demux->downloader);
+ if(gst_mpd_client_is_live (demux->client)) {
+ gst_dash_demux_check_live (demux, fragment_stream, fragment_ts);
+ } else {
+ demux->client->download_failed_count++;
+ }
+
+ if (demux->client->download_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not fetch the next fragment");
+ goto quit;
+ } else if (gst_mpd_client_set_next_baseURL_for_stream (demux->client)) {
+ GST_INFO_OBJECT (demux, "Current baseURL is %s. Trying to select another",
+ gst_mpdparser_get_baseURL (demux->client));
+ demux->client->download_failed_count = 0;
+ } else {
+ goto error_downloading;
+ }
+ } else {
+ goto quit;
+ }
+ }
+ GST_INFO_OBJECT (demux, "Internal buffering : %" PRIu64 " s",
+ gst_dash_demux_get_buffering_time (demux) / GST_SECOND);
+ demux->client->download_failed_count = 0;
+
+
+quit:
+ {
+ return;
+ }
+
+end_of_manifest:
+ {
+ GST_INFO_OBJECT (demux, "Stopped download task");
+ gst_task_stop (demux->download_task);
+ return;
+ }
+
+error_downloading:
+ {
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND,
+ ("Could not fetch the next fragment"), (NULL));
+ gst_dash_demux_stop (demux);
+ return;
+ }
+}
+
+static void
+gst_dash_demux_resume_stream_task (GstDashDemux * demux)
+{
+ gst_task_start (demux->stream_task);
+}
+
+static void
+gst_dash_demux_resume_download_task (GstDashDemux * demux)
+{
+ gst_uri_downloader_reset(demux->downloader);
+ gst_task_start (demux->download_task);
+}
+
+/* gst_dash_demux_select_representations:
+ *
+ * Select the most appropriate media representations based on a target
+ * bitrate.
+ *
+ * FIXME: all representations are selected against the same bitrate, but
+ * they will share the same bandwidth. This only works today because the
+ * audio representations bitrate usage is negligible as compared to the
+ * video representation one.
+ *
+ * Returns TRUE if a new set of representations has been selected
+ */
+static gboolean
+gst_dash_demux_select_representations (GstDashDemux * demux)
+{
+ GstDashDemuxStream *stream = NULL;
+ GstActiveStream *active_stream = NULL;
+ GList *rep_list = NULL;
+ GSList *iter;
+ gint new_index;
+ gboolean ret = FALSE;
+
+ GST_MPD_CLIENT_LOCK (demux->client);
+ for (iter = demux->streams; iter; iter = g_slist_next (iter)) {
+ guint64 bitrate;
+
+ stream = iter->data;
+ active_stream =
+ gst_mpdparser_get_active_stream_by_index (demux->client, stream->idx);
+ if (!active_stream)
+ return FALSE;
+
+ /* retrieve representation list */
+ if (active_stream->cur_adapt_set)
+ rep_list = active_stream->cur_adapt_set->Representations;
+ if (!rep_list)
+ return FALSE;
+
+ bitrate = gst_download_rate_get_current_rate (&stream->dnl_rate) *
+ demux->bandwidth_usage;
+
+ GST_DEBUG_OBJECT (demux, "Trying to change bitrate to %" G_GUINT64_FORMAT, bitrate);
+
+ /* get representation index with current max_bandwidth */
+ new_index =
+ gst_mpdparser_get_rep_idx_with_max_bandwidth (rep_list, bitrate);
+
+ /* if no representation has the required bandwidth, take the lowest one */
+ if (new_index == -1)
+ new_index = 0;
+
+ if (new_index != active_stream->representation_idx) {
+ GstRepresentationNode *rep = g_list_nth_data (rep_list, new_index);
+ GST_INFO_OBJECT (demux, "Changing representation idx: %d %d %u",
+ stream->idx, new_index, rep->bandwidth);
+ if (gst_mpd_client_setup_representation (demux->client, active_stream,
+ rep)) {
+ ret = TRUE;
+ stream->need_header = TRUE;
+ GST_INFO_OBJECT (demux, "Switching bitrate to %d",
+ active_stream->cur_representation->bandwidth);
+ } else {
+ GST_WARNING_OBJECT (demux,
+ "Can not switch representation, aborting...");
+ }
+ }
+ }
+ GST_MPD_CLIENT_UNLOCK (demux->client);
+ return ret;
+}
+
+static GstFragment *
+gst_dash_demux_get_next_header (GstDashDemux * demux, guint stream_idx)
+{
+ const gchar *initializationURL;
+ gchar *next_header_uri;
+ GstFragment *fragment;
+
+ if (!gst_mpd_client_get_next_header (demux->client, &initializationURL,
+ stream_idx))
+ return NULL;
+
+ if (strncmp (initializationURL, "http://", 7) != 0) {
+ next_header_uri =
+ g_strconcat (gst_mpdparser_get_baseURL (demux->client),
+ initializationURL, NULL);
+ } else {
+ next_header_uri = g_strdup (initializationURL);
+ }
+
+ GST_INFO_OBJECT (demux, "Fetching header %s", next_header_uri);
+
+ fragment = gst_uri_downloader_fetch_uri (demux->downloader, next_header_uri);
+ g_free (next_header_uri);
+ g_free (initializationURL);
+
+ return fragment;
+}
+
+static GstCaps *
+gst_dash_demux_get_video_input_caps (GstDashDemux * demux,
+ GstActiveStream * stream)
+{
+ guint width = 0, height = 0, bandwidth = 0;
+ const gchar *mimeType = NULL;
+ GstCaps *caps = NULL;
+
+ if (stream == NULL)
+ return NULL;
+#ifdef DASHDEMUX_MODIFICATION
+ /* caps need to inlcude resolution and bandwidth */
+ width = gst_mpd_client_get_video_stream_width (stream);
+ height = gst_mpd_client_get_video_stream_height (stream);
+ bandwidth = gst_mpd_client_get_video_stream_bandwidth (stream);
+#else
+ /* if bitstreamSwitching is true we dont need to swich pads on resolution change */
+ if (!gst_mpd_client_get_bitstream_switching_flag (stream)) {
+ width = gst_mpd_client_get_video_stream_width (stream);
+ height = gst_mpd_client_get_video_stream_height (stream);
+ }
+#endif
+ mimeType = gst_mpd_client_get_stream_mimeType (stream);
+ if (mimeType == NULL)
+ return NULL;
+
+ caps = gst_caps_new_simple (mimeType, NULL);
+ if (width > 0 && height > 0) {
+ gst_caps_set_simple (caps, "width", G_TYPE_INT, width, "height",
+ G_TYPE_INT, height, NULL);
+ }
+
+ if (bandwidth > 0)
+ gst_caps_set_simple (caps, "bandwidth", G_TYPE_INT, bandwidth, NULL);
+
+ gst_caps_set_simple (caps, "max-width", G_TYPE_INT, demux->max_video_width, "max-height",
+ G_TYPE_INT, demux->max_video_height, NULL);
+
+/* add ContentProtection to caps */
+ if ( stream->cur_adapt_set->RepresentationBase->ContentProtection != NULL){
+ GList *list;
+ GstDescriptorType *ContentProtectionDesc;
+
+ list = g_list_first (stream->cur_adapt_set->RepresentationBase->ContentProtection);
+ ContentProtectionDesc = (GstDescriptorType *) list->data;
+ gchar *schemeIdUri = ContentProtectionDesc->schemeIdUri;
+ gchar *msprPro = ContentProtectionDesc->msprPro;
+
+ if ( (schemeIdUri != NULL) && (msprPro != NULL) ){
+ gst_caps_set_simple (caps, "contentprotection_scheme_iduri", G_TYPE_STRING, schemeIdUri, "mspr:pro", G_TYPE_STRING, msprPro, NULL);
+ }
+ }
+ return caps;
+}
+
+static GstCaps *
+gst_dash_demux_get_audio_input_caps (GstDashDemux * demux,
+ GstActiveStream * stream)
+{
+ guint rate = 0, channels = 0;
+ const gchar *mimeType;
+ GstCaps *caps = NULL;
+
+ if (stream == NULL)
+ return NULL;
+
+ /* if bitstreamSwitching is true we dont need to swich pads on rate/channels change */
+ if (!gst_mpd_client_get_bitstream_switching_flag (stream)) {
+ channels = gst_mpd_client_get_audio_stream_num_channels (stream);
+ rate = gst_mpd_client_get_audio_stream_rate (stream);
+ }
+ mimeType = gst_mpd_client_get_stream_mimeType (stream);
+ if (mimeType == NULL)
+ return NULL;
+
+ caps = gst_caps_new_simple (mimeType, NULL);
+ if (rate > 0) {
+ gst_caps_set_simple (caps, "rate", G_TYPE_INT, rate, NULL);
+ }
+ if (channels > 0) {
+ gst_caps_set_simple (caps, "channels", G_TYPE_INT, channels, NULL);
+ }
+
+/* add ContentProtection to caps */
+ if ( stream->cur_adapt_set->RepresentationBase->ContentProtection != NULL){
+ GList *list;
+ GstDescriptorType *ContentProtectionDesc;
+
+ list = g_list_first (stream->cur_adapt_set->RepresentationBase->ContentProtection);
+ ContentProtectionDesc = (GstDescriptorType *) list->data;
+ gchar *schemeIdUri = ContentProtectionDesc->schemeIdUri;
+ gchar *msprPro = ContentProtectionDesc->msprPro;
+
+ if ( (schemeIdUri != NULL) && (msprPro != NULL) ){
+ gst_caps_set_simple (caps, "contentprotection_scheme_iduri", G_TYPE_STRING, schemeIdUri, "mspr:pro", G_TYPE_STRING, msprPro, NULL);
+ }
+ }
+ return caps;
+}
+
+static GstCaps *
+gst_dash_demux_get_application_input_caps (GstDashDemux * demux,
+ GstActiveStream * stream)
+{
+ const gchar *mimeType;
+ GstCaps *caps = NULL;
+
+ if (stream == NULL)
+ return NULL;
+
+ mimeType = gst_mpd_client_get_stream_mimeType (stream);
+ if (mimeType == NULL)
+ return NULL;
+
+ caps = gst_caps_new_simple (mimeType, NULL);
+
+ return caps;
+}
+
+static GstCaps *
+gst_dash_demux_get_input_caps (GstDashDemux * demux, GstActiveStream * stream)
+{
+ GstCaps *caps;
+ switch (stream->mimeType) {
+ case GST_STREAM_VIDEO:
+ caps = gst_dash_demux_get_video_input_caps (demux, stream);
+ break;
+ case GST_STREAM_AUDIO:
+ caps = gst_dash_demux_get_audio_input_caps (demux, stream);
+ break;
+ case GST_STREAM_APPLICATION:
+ caps = gst_dash_demux_get_application_input_caps (demux, stream);
+ break;
+ default:
+ return GST_CAPS_NONE;
+ }
+ /*Need to signal downstream elements about dash*/
+ gst_caps_set_simple(caps, "variant", G_TYPE_STRING, "dash-fragmented", NULL);
+ return caps;
+}
+
+/* gst_dash_demux_get_next_fragment_set:
+ *
+ * Get the next set of fragments for the current representations.
+ *
+ * This function uses the generic URI downloader API.
+ *
+ * Returns FALSE if an error occured while downloading fragments
+ *
+ */
+static gboolean
+gst_dash_demux_get_next_fragment (GstDashDemux * demux,GstActiveStream **fragment_stream,
+ GstClockTime *selected_ts)
+{
+ GstActiveStream *stream;
+ GstDashDemuxStream *dash_stream;
+ GstDashDemuxStream *selected_stream = NULL;
+ GstFragment *download, *header;
+ gchar *next_fragment_uri;
+ GstClockTime duration;
+ GstClockTime timestamp;
+ GstClockTime min_timestamp = GST_CLOCK_TIME_NONE;
+ gboolean discont;
+ GTimeVal now;
+ GTimeVal start;
+ GstClockTime diff;
+ guint64 size_buffer = 0;
+ GstBuffer *buffer;
+ guint stream_idx;
+ gboolean end_of_period = TRUE;
+
+ /*Select stream with smallest progress*/
+ for (stream_idx = 0; stream_idx < g_slist_length (demux->streams); stream_idx++) {
+ dash_stream = g_slist_nth_data (demux->streams, stream_idx);
+
+ if (dash_stream->download_end_of_period)
+ continue;
+
+ if (gst_mpd_client_get_next_fragment_timestamp (demux->client, stream_idx, ×tamp)) {
+ if( timestamp < min_timestamp || !GST_CLOCK_TIME_IS_VALID(min_timestamp) ) {
+ selected_stream = dash_stream;
+ min_timestamp = timestamp;
+ }
+ } else {
+ GstEvent *event = NULL;
+
+ GST_INFO_OBJECT (demux,
+ "This Period doesn't contain more fragments for stream %u",
+ dash_stream->idx);
+
+ /* check if this is live and we should wait for more data */
+ if (gst_mpd_client_is_live (demux->client)
+ && demux->client->mpd_node->minimumUpdatePeriod != -1) {
+ end_of_period = FALSE;
+ continue;
+ }
+
+ if (gst_mpd_client_has_next_period (demux->client)) {
+ event = gst_event_new_dash_eop ();
+ } else {
+ GST_DEBUG_OBJECT (demux,
+ "No more fragments or periods for this stream, setting EOS");
+ event = gst_event_new_eos ();
+ }
+ dash_stream->download_end_of_period = TRUE;
+ gst_dash_demux_stream_push_event (dash_stream, event);
+ }
+ }
+
+ if (selected_ts)
+ *selected_ts = min_timestamp;
+ if (fragment_stream && selected_stream)
+ *fragment_stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
+ /* Fetch next fragment from selected stream */
+ if(selected_stream) {
+
+ if (!gst_mpd_client_get_next_fragment (demux->client,
+ selected_stream->idx, &discont, &next_fragment_uri, &duration, ×tamp)) {
+ GST_WARNING_OBJECT (demux, "Failed to download fragment for stream %d", selected_stream->idx);
+ } else {
+
+ g_get_current_time (&start);
+ GST_INFO_OBJECT (demux, "Fetching next fragment stream=%d ts=%"GST_TIME_FORMAT" url=%s",
+ selected_stream->idx, GST_TIME_ARGS(timestamp), next_fragment_uri);
+
+ stream = gst_mpdparser_get_active_stream_by_index (demux->client, selected_stream->idx);
+
+ end_of_period = FALSE;
+
+ download = gst_uri_downloader_fetch_uri (demux->downloader,
+ next_fragment_uri);
+ g_free (next_fragment_uri);
+
+ if (stream == NULL)
+ return FALSE;
+
+ if (download == NULL) {
+ guint segment_idx = gst_mpd_client_get_segment_index ( stream );
+ if(segment_idx > 0) {
+ /*Move to previous segment if download failed*/
+ gst_mpd_client_set_segment_index (stream, segment_idx - 1);
+ }
+ return FALSE;
+ }
+
+ download->start_time = timestamp;
+ download->stop_time = timestamp + duration;
+
+ download->index = gst_mpd_client_get_segment_index (stream) - 1;
+
+ GstCaps *caps = gst_dash_demux_get_input_caps (demux, stream);
+ buffer = gst_fragment_get_buffer (download);
+ g_return_val_if_fail (buffer != NULL, FALSE);
+
+ if (selected_stream->need_header) {
+ /* Store the new input caps for that stream */
+ gst_caps_replace (&dash_stream->input_caps, caps);
+ GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT,
+ dash_stream->input_caps);
+
+ /* We need to fetch a new header */
+ if ((header = gst_dash_demux_get_next_header (demux, selected_stream->idx)) == NULL) {
+ GST_INFO_OBJECT (demux, "Unable to fetch header");
+ } else {
+ /* Replace fragment buffer with a new one including the header */
+ GstBuffer *header_buffer = gst_fragment_get_buffer(header);
+ buffer = gst_buffer_join(header_buffer, buffer);
+ g_object_unref (header);
+ selected_stream->need_header = FALSE;
+ }
+ } else {
+ gst_caps_unref (caps);
+ }
+
+ g_get_current_time (&now);
+ g_object_unref (download);
+
+ gst_buffer_set_caps(buffer, dash_stream->input_caps);
+ GST_BUFFER_TIMESTAMP(buffer) = timestamp;
+ GST_BUFFER_DURATION(buffer) = duration;
+ GST_BUFFER_OFFSET(buffer) = gst_mpd_client_get_segment_index (stream) - 1;
+ size_buffer = GST_BUFFER_SIZE (buffer);
+ /* Push fragment into the queue */
+ gst_dash_demux_stream_push_data (selected_stream, buffer);
+ diff = (GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (start));
+ gst_download_rate_add_rate (&selected_stream->dnl_rate, size_buffer, diff, duration);
+ GST_DEBUG_OBJECT (demux,
+ "Stream: %d Download rate = %" G_GUINT64_FORMAT " Kbits/s (%" G_GUINT64_FORMAT
+ " Ko in %.2f s)\n", selected_stream->idx,
+ gst_download_rate_get_current_rate (&selected_stream->dnl_rate) / 1000,
+ size_buffer / 1024,
+ ((double) diff / GST_SECOND));
+ }
+ }
+
+ demux->end_of_period = end_of_period;
+
+ return !end_of_period;
+}
--- /dev/null
+/*
+ * DASH demux plugin for GStreamer
+ *
+ * gstdashdemux.h
+ *
+ * Copyright (C) 2012 Orange
+ *
+ * Authors:
+ * David Corvoysier <david.corvoysier@orange.com>
+ * Hamid Zakari <hamid.zakari@gmail.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.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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_DASH_DEMUX_H__
+#define __GST_DASH_DEMUX_H__
+#define DASHDEMUX_MODIFICATION
+#include <gst/gst.h>
+#include <gst/base/gstdataqueue.h>
+#include "gstmpdparser.h"
+#include "gstfragmented.h"
+#include "gsturidownloader.h"
+#include "gstdownloadrate.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_DASH_DEMUX \
+ (gst_dash_demux_get_type())
+#define GST_DASH_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DASH_DEMUX,GstDashDemux))
+#define GST_DASH_DEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DASH_DEMUX,GstDashDemuxClass))
+#define GST_IS_DASH_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DASH_DEMUX))
+#define GST_IS_DASH_DEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DASH_DEMUX))
+//
+typedef struct _GstDashDemux GstDashDemux;
+typedef struct _GstDashDemuxClass GstDashDemuxClass;
+typedef struct _GstDashDemuxStream GstDashDemuxStream;
+
+struct _GstDashDemuxStream {
+ guint idx;
+ GstPad *srcpad;
+ GstCaps *output_caps;
+ GstCaps *input_caps;
+ GstDataQueue *queue;
+ GstClockTime start_time;
+ gboolean download_end_of_period;
+ gboolean stream_end_of_period;
+ gboolean stream_eos;
+ gboolean need_header;
+ gboolean need_segment;
+ GstDownloadRate dnl_rate;
+};
+
+#define MAX_LANGUAGES 20
+/**
+ * GstDashDemux:
+ *
+ * Opaque #GstDashDemux data structure.
+ */
+struct _GstDashDemux
+{
+ GstElement parent;
+ GstPad *sinkpad;
+ GSList *streams;
+ gint max_video_width;
+ gint max_video_height;
+
+ GstBuffer *manifest;
+ GstUriDownloader *downloader;
+ GstMpdClient *client; /* MPD client */
+ gboolean end_of_period;
+ gboolean end_of_manifest;
+
+ /* Properties */
+ GstClockTime max_buffering_time; /* Maximum buffering time accumulated during playback */
+ gfloat bandwidth_usage; /* Percentage of the available bandwidth to use */
+ guint64 max_bitrate; /* max of bitrate supported by target decoder */
+
+ /* Streaming task */
+ GstTask *stream_task;
+ GStaticRecMutex stream_lock;
+ GMutex *stream_timed_lock;
+
+ /* Download task */
+ GstTask *download_task;
+ GStaticRecMutex download_lock;
+ volatile gboolean cancelled;
+ GMutex download_mutex;
+ GCond download_cond;
+
+ /* Manifest update */
+ GstClockTime last_manifest_update;
+};
+
+struct _GstDashDemuxClass
+{
+ GstElementClass parent_class;
+};
+
+GType gst_dash_demux_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_DASH_DEMUX_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
+ * Copyright (C) 2012 Smart TV Alliance
+ * Author: Louis-Francis Ratté-Boulianne <lfrb@collabora.com>, Collabora Ltd.
+ *
+ * gstfragment.c:
+ *
+ * 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 <glib.h>
+#include "gstdownloadrate.h"
+
+static void
+_gst_download_rate_check_remove_rates (GstDownloadRate * rate)
+{
+ if (rate->max_length == 0)
+ return;
+
+ while (g_queue_get_length (&rate->queue) > rate->max_length) {
+ guint bitrate = GPOINTER_TO_UINT (g_queue_pop_head (&rate->queue));
+
+ rate->total -= bitrate;
+ }
+}
+
+void
+gst_download_rate_init (GstDownloadRate * rate)
+{
+ g_queue_init (&rate->queue);
+ g_mutex_init (&rate->mutex);
+ rate->total = 0;
+ rate->max_length = 0;
+ rate->aver_period = 0;
+}
+
+void
+gst_download_rate_deinit (GstDownloadRate * rate)
+{
+ gst_download_rate_clear (rate);
+ g_mutex_clear (&rate->mutex);
+}
+
+void
+gst_download_rate_set_max_length (GstDownloadRate * rate, gint max_length)
+{
+ g_mutex_lock (&rate->mutex);
+ rate->max_length = max_length;
+ _gst_download_rate_check_remove_rates (rate);
+ g_mutex_unlock (&rate->mutex);
+}
+
+gint
+gst_download_rate_get_max_length (GstDownloadRate * rate)
+{
+ guint ret;
+ g_mutex_lock (&rate->mutex);
+ ret = rate->max_length;
+ g_mutex_unlock (&rate->mutex);
+
+ return ret;
+}
+
+void
+ gst_download_rate_set_aver_period (GstDownloadRate * rate, guint64 aver_period)
+{
+ g_mutex_lock (&rate->mutex);
+ rate->aver_period = aver_period;
+ _gst_download_rate_check_remove_rates (rate);
+ g_mutex_unlock (&rate->mutex);
+}
+
+GstClockTime
+gst_download_rate_get_aver_period (GstDownloadRate * rate)
+{
+ guint64 ret;
+ g_mutex_lock (&rate->mutex);
+ ret = rate->aver_period;
+ g_mutex_unlock (&rate->mutex);
+
+ return ret;
+}
+
+void
+gst_download_rate_clear (GstDownloadRate * rate)
+{
+ g_mutex_lock (&rate->mutex);
+ g_queue_clear (&rate->queue);
+ rate->total = 0;
+ g_mutex_unlock (&rate->mutex);
+}
+
+void
+gst_download_rate_add_rate (GstDownloadRate * rate, guint bytes, guint64 time, GstClockTime duration)
+{
+ guint64 bitrate;
+ g_mutex_lock (&rate->mutex);
+
+ /* convert from bytes / nanoseconds to bits per second */
+ bitrate = G_GUINT64_CONSTANT (8000000000) * bytes / ((double) time);
+ g_queue_push_tail (&rate->queue, GUINT_TO_POINTER ((guint) bitrate));
+ rate->total += bitrate;
+
+ if ( duration * rate->max_length > rate->aver_period )
+ rate->max_length =
+ duration < rate->aver_period ? rate->aver_period / duration : 1;
+
+ _gst_download_rate_check_remove_rates (rate);
+ g_mutex_unlock (&rate->mutex);
+}
+
+guint64
+gst_download_rate_get_current_rate (GstDownloadRate * rate)
+{
+ guint64 ret;
+ g_mutex_lock (&rate->mutex);
+ if (g_queue_get_length (&rate->queue))
+ ret = rate->total / g_queue_get_length (&rate->queue);
+ else
+ ret = 0;
+ g_mutex_unlock (&rate->mutex);
+
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2012 Smart TV Alliance
+ * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
+ *
+ * gstdownloadrate.h:
+ *
+ * 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_DOWNLOAD_RATE_H__
+#define __GST_DOWNLOAD_RATE_H__
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstDownloadRate GstDownloadRate;
+
+struct _GstDownloadRate
+{
+ GQueue queue;
+ GMutex mutex;
+
+ gint max_length;
+ guint64 aver_period;
+ guint64 total;
+};
+
+void gst_download_rate_init (GstDownloadRate * rate);
+void gst_download_rate_deinit (GstDownloadRate * rate);
+
+void gst_download_rate_set_max_length (GstDownloadRate * rate, gint max_length);
+gint gst_download_rate_get_max_length (GstDownloadRate * rate);
+
+void gst_download_rate_set_aver_period (GstDownloadRate * rate, GstClockTime aver_period);
+GstClockTime gst_download_rate_get_aver_period (GstDownloadRate * rate);
+
+void gst_download_rate_clear (GstDownloadRate * rate);
+void gst_download_rate_add_rate (GstDownloadRate * rate, guint bytes, guint64 time, GstClockTime duration);
+
+guint64 gst_download_rate_get_current_rate (GstDownloadRate * rate);
+
+G_END_DECLS
+#endif /* __GST_DOWNLOAD_RATE_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
+ *
+ * gstfragment.c:
+ *
+ * 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 <glib.h>
+#include <gst/base/gsttypefindhelper.h>
+#include <gst/base/gstadapter.h>
+#include "glibcompat.h"
+#include "gstfragmented.h"
+#include "gstfragment.h"
+
+#define GST_CAT_DEFAULT fragmented_debug
+
+#define GST_FRAGMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_FRAGMENT, GstFragmentPrivate))
+
+enum
+{
+ PROP_0,
+ PROP_INDEX,
+ PROP_NAME,
+ PROP_DURATION,
+ PROP_DISCONTINOUS,
+ PROP_BUFFER_LIST,
+ PROP_CAPS,
+ PROP_LAST
+};
+
+struct _GstFragmentPrivate
+{
+ GstBuffer *buffer;
+ GstAdapter *adapter;
+};
+
+G_DEFINE_TYPE (GstFragment, gst_fragment, G_TYPE_OBJECT);
+
+static void gst_fragment_dispose (GObject * object);
+static void gst_fragment_finalize (GObject * object);
+
+static void
+gst_fragment_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec)
+{
+ GstFragment *fragment = GST_FRAGMENT (object);
+
+ switch (property_id) {
+ case PROP_INDEX:
+ g_value_set_uint (value, fragment->index);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, fragment->name);
+ break;
+
+ case PROP_DURATION:
+ g_value_set_uint64 (value, fragment->stop_time - fragment->start_time);
+ break;
+
+ case PROP_DISCONTINOUS:
+ g_value_set_boolean (value, fragment->discontinuous);
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+gst_fragment_class_init (GstFragmentClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GstFragmentPrivate));
+
+ gobject_class->get_property = gst_fragment_get_property;
+ gobject_class->dispose = gst_fragment_dispose;
+ gobject_class->finalize = gst_fragment_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_INDEX,
+ g_param_spec_uint ("index", "Index", "Index of the fragment", 0,
+ G_MAXUINT, 0, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_NAME,
+ g_param_spec_string ("name", "Name",
+ "Name of the fragment (eg:fragment-12.ts)", NULL, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_DISCONTINOUS,
+ g_param_spec_boolean ("discontinuous", "Discontinous",
+ "Whether this fragment has a discontinuity or not",
+ FALSE, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_DURATION,
+ g_param_spec_uint64 ("duration", "Fragment duration",
+ "Duration of the fragment", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+
+}
+
+static void
+gst_fragment_init (GstFragment * fragment)
+{
+ GstFragmentPrivate *priv;
+
+ fragment->priv = priv = GST_FRAGMENT_GET_PRIVATE (fragment);
+
+ priv->adapter = gst_adapter_new ();
+ fragment->download_start_time = g_get_real_time ();
+ fragment->start_time = 0;
+ fragment->stop_time = 0;
+ fragment->index = 0;
+ fragment->name = g_strdup ("");
+ fragment->completed = FALSE;
+ fragment->discontinuous = FALSE;
+}
+
+GstFragment *
+gst_fragment_new (void)
+{
+ return GST_FRAGMENT (g_object_new (GST_TYPE_FRAGMENT, NULL));
+}
+
+static void
+gst_fragment_finalize (GObject * gobject)
+{
+ GstFragment *fragment = GST_FRAGMENT (gobject);
+
+ g_free (fragment->name);
+
+ G_OBJECT_CLASS (gst_fragment_parent_class)->finalize (gobject);
+}
+
+void
+gst_fragment_dispose (GObject * object)
+{
+ GstFragmentPrivate *priv = GST_FRAGMENT (object)->priv;
+
+ if (priv->buffer != NULL) {
+ gst_buffer_unref (priv->buffer);
+ priv->buffer = NULL;
+ }
+
+ if (priv->adapter != NULL) {
+ g_object_unref (priv->adapter);
+ priv->adapter = NULL;
+ }
+
+ G_OBJECT_CLASS (gst_fragment_parent_class)->dispose (object);
+}
+
+GstBuffer *
+gst_fragment_get_buffer (GstFragment * fragment)
+{
+ g_return_val_if_fail (fragment != NULL, NULL);
+
+ if (!fragment->completed)
+ return NULL;
+
+ if(!fragment->priv->buffer) {
+ fragment->priv->buffer = gst_adapter_take_buffer(fragment->priv->adapter,
+ gst_adapter_available(fragment->priv->adapter));
+ }
+
+ return gst_buffer_ref(fragment->priv->buffer);
+}
+
+guint64
+gst_fragment_get_total_size (GstFragment * fragment)
+{
+ g_return_val_if_fail (fragment != NULL, 0);
+
+ if (fragment->priv->buffer)
+ return GST_BUFFER_SIZE(fragment->priv->buffer);
+
+ return gst_adapter_available(fragment->priv->adapter);
+}
+
+
+
+gboolean
+gst_fragment_add_buffer (GstFragment * fragment, GstBuffer * buffer)
+{
+ g_return_val_if_fail (fragment != NULL, FALSE);
+ g_return_val_if_fail (buffer != NULL, FALSE);
+
+ if (fragment->completed) {
+ GST_WARNING ("Fragment is completed, could not add more buffers");
+ return FALSE;
+ }
+
+ gst_adapter_push(fragment->priv->adapter, buffer);
+ return TRUE;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
+ *
+ * gstfragment.h:
+ *
+ * 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 __GSTFRAGMENT_H__
+#define __GSTFRAGMENT_H__
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_FRAGMENT (gst_fragment_get_type())
+#define GST_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FRAGMENT,GstFragment))
+#define GST_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FRAGMENT,GstFragmentClass))
+#define GST_IS_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FRAGMENT))
+#define GST_IS_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FRAGMENT))
+
+typedef struct _GstFragment GstFragment;
+typedef struct _GstFragmentPrivate GstFragmentPrivate;
+typedef struct _GstFragmentClass GstFragmentClass;
+
+struct _GstFragment
+{
+ GObject parent;
+
+ gchar * name; /* Name of the fragment */
+ gboolean completed; /* Whether the fragment is complete or not */
+ guint64 download_start_time; /* Epoch time when the download started */
+ guint64 download_stop_time; /* Epoch time when the download finished */
+ guint64 start_time; /* Start time of the fragment */
+ guint64 stop_time; /* Stop time of the fragment */
+ gboolean index; /* Index of the fragment */
+ gboolean discontinuous; /* Whether this fragment is discontinuous or not */
+
+ GstFragmentPrivate *priv;
+};
+
+struct _GstFragmentClass
+{
+ GObjectClass parent_class;
+};
+
+GType gst_fragment_get_type (void);
+
+guint64 gst_fragment_get_total_size (GstFragment * fragment);
+GstBuffer *gst_fragment_get_buffer(GstFragment *fragment);
+gboolean gst_fragment_add_buffer (GstFragment *fragment, GstBuffer *buffer);
+GstFragment * gst_fragment_new (void);
+
+G_END_DECLS
+#endif /* __GSTFRAGMENT_H__ */
--- /dev/null
+#ifndef __GST_FRAGMENTED_H__
+#define __GST_FRAGMENTED_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN (fragmented_debug);
+
+#define LOG_CAPS(obj, caps) GST_DEBUG_OBJECT (obj, "%s: %" GST_PTR_FORMAT, #caps, caps)
+
+G_END_DECLS
+
+#endif /* __GST_FRAGMENTED_H__ */
+
--- /dev/null
+/*
+ * DASH MPD parsing library
+ *
+ * gstmpdparser.c
+ *
+ * Copyright (C) 2012 STMicroelectronics
+ *
+ * Authors:
+ * Gianluca Gennari <gennarone@gmail.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.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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <libxml/tree.h>
+#include "gstfragmented.h"
+#include "gstmpdparser.h"
+
+#define GST_CAT_DEFAULT fragmented_debug
+
+/* Property parsing */
+static gchar *gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
+ const gchar * property);
+static gchar **gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
+ const gchar * property);
+static guint gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
+ const gchar * property, guint default_val);
+static guint *gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
+ const gchar * property, guint * size);
+static gdouble gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
+ const gchar * property);
+static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
+ const gchar * property);
+static GstMPDFileType gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
+ const gchar * property);
+static GstSAPType gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
+ const gchar * property);
+static GstRange *gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
+ const gchar * property);
+static GstRatio *gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
+ const gchar * property);
+static GstFrameRate *gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
+ const gchar * property);
+static GstConditionalUintType *gst_mpdparser_get_xml_prop_cond_uint (xmlNode *
+ a_node, const gchar * property);
+static GstDateTime *gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
+ const gchar * property);
+static gint64 gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
+ const gchar * property);
+static gchar *gst_mpdparser_get_xml_node_content (xmlNode * a_node);
+static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
+ const gchar * prefix);
+
+/* XML node parsing */
+static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_content_component_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_segment_url_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType **
+ pointer, xmlNode * a_node);
+static void gst_mpdparser_parse_s_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
+ pointer, xmlNode * a_node);
+static void gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType
+ ** pointer, xmlNode * a_node);
+static void gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
+ pointer, xmlNode * a_node);
+static void
+gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
+ pointer, xmlNode * a_node);
+static void gst_mpdparser_parse_representation_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_adaptation_set_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode **
+ pointer, xmlNode * a_node);
+static void gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_program_info_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_metrics_range_node (GList ** list,
+ xmlNode * a_node);
+static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
+static void gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
+ xmlNode * a_node);
+
+/* Helper functions */
+static gint convert_to_millisecs (gint decimals, gint pos);
+static int strncmp_ext (const char *s1, const char *s2);
+static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
+static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client);
+static gchar *gst_mpdparser_get_segmentURL_for_range (const gchar *url,
+ GstRange * range);
+static gchar *gst_mpdparser_get_mediaURL (GstMpdClient * client,
+ GstSegmentURLNode * segmentURL);
+static gchar *gst_mpdparser_get_initializationURL (GstURLType *
+ InitializationURL);
+static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
+ const gchar * id, guint number, guint bandwidth, guint time);
+static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
+ GstSegmentURLNode * url_node, guint number, guint start,
+ GstClockTime start_time, GstClockTime duration);
+static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
+static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client, GstActiveStream * stream);
+
+/* Adaptation Set */
+static GstAdaptationSetNode
+ *gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets,
+ const gchar * mimeType);
+static GstAdaptationSetNode
+ *gst_mpdparser_get_adapt_set_with_mimeType_and_idx (GList * AdaptationSets,
+ const gchar * mimeType, gint idx);
+static GstAdaptationSetNode
+ *gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (GList *
+ AdaptationSets, const gchar * mimeType, const gchar * lang);
+
+/* Representation */
+static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
+ Representations);
+#if 0
+static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
+ Representations);
+static GstRepresentationNode
+ *gst_mpdparser_get_representation_with_max_bandwidth (GList *
+ Representations, guint64 max_bandwidth);
+#endif
+static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
+ Period, GstAdaptationSetNode * AdaptationSet,
+ GstRepresentationNode * Representation);
+static GstSegmentListNode *gst_mpdparser_get_segment_list (GstPeriodNode *
+ Period, GstAdaptationSetNode * AdaptationSet,
+ GstRepresentationNode * Representation);
+
+/* Memory management */
+static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
+static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
+ prog_info_node);
+static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
+static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
+ metrics_range_node);
+static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
+static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
+static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
+ segment_template_node);
+static void
+gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
+ representation_base);
+static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
+ adaptation_set_node);
+static void gst_mpdparser_free_representation_node (GstRepresentationNode *
+ representation_node);
+static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
+ * subrep_node);
+static void gst_mpdparser_free_s_node (GstSNode * s_node);
+static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
+ seg_timeline);
+static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
+static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
+ seg_base_type);
+static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
+ mult_seg_base_type);
+static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
+ segment_list_node);
+static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
+ segment_url);
+static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
+static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
+ descriptor_type);
+static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
+ content_component_node);
+static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
+static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
+static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
+
+/* functions to parse node namespaces, content and properties */
+static gchar *
+gst_mpdparser_get_xml_prop_string (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ GST_LOG (" - %s: %s", property, prop_string);
+ }
+
+ return (gchar *) prop_string;
+}
+
+static gchar **
+gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
+ const gchar * property)
+{
+ xmlChar *prop_string;
+ gchar **prop_string_vector = NULL;
+ guint i = 0;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
+ if (!prop_string_vector) {
+ GST_WARNING ("Scan of string vector property failed!");
+ return NULL;
+ }
+ GST_LOG (" - %s:", property);
+ while (prop_string_vector[i]) {
+ GST_LOG (" %s", prop_string_vector[i]);
+ i++;
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_string_vector;
+}
+
+static guint
+gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
+ const gchar * property, guint default_val)
+{
+ xmlChar *prop_string;
+ guint prop_unsigned_integer = default_val;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ if (sscanf ((gchar *) prop_string, "%u", &prop_unsigned_integer)) {
+ GST_LOG (" - %s: %u", property, prop_unsigned_integer);
+ } else {
+ GST_WARNING
+ ("failed to parse unsigned integer property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_unsigned_integer;
+}
+
+static guint *
+gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
+ const gchar * property, guint * size)
+{
+ xmlChar *prop_string;
+ gchar **str_vector;
+ guint *prop_uint_vector = NULL, i;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
+ if (!str_vector) {
+ GST_WARNING ("Scan of uint vector property failed!");
+ return NULL;
+ }
+ *size = g_strv_length (str_vector);
+ prop_uint_vector = g_malloc (*size * sizeof (guint));
+ if (!prop_uint_vector) {
+ GST_WARNING ("Array allocation failed!");
+ } else {
+ GST_LOG (" - %s:", property);
+ for (i = 0; i < *size; i++) {
+ if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i])) {
+ GST_LOG (" %u", prop_uint_vector[i]);
+ } else {
+ GST_WARNING
+ ("failed to parse uint vector type property %s from xml string %s",
+ property, str_vector[i]);
+ }
+ }
+ }
+ xmlFree (prop_string);
+ g_strfreev (str_vector);
+ }
+
+ return prop_uint_vector;
+}
+
+static gdouble
+gst_mpdparser_get_xml_prop_double (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ gdouble prop_double = 0;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ if (sscanf ((gchar *) prop_string, "%lf", &prop_double)) {
+ GST_LOG (" - %s: %lf", property, prop_double);
+ } else {
+ GST_WARNING ("failed to parse double property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_double;
+}
+
+static gboolean
+gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ gboolean prop_bool = FALSE;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
+ GST_LOG (" - %s: false", property);
+ } else if (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 GstMPDFileType
+gst_mpdparser_get_xml_prop_type (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ GstMPDFileType prop_type = GST_MPD_FILE_TYPE_STATIC; /* default */
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
+ || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
+ GST_LOG (" - %s: static", property);
+ prop_type = GST_MPD_FILE_TYPE_STATIC;
+ } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
+ || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
+ GST_LOG (" - %s: dynamic", property);
+ prop_type = GST_MPD_FILE_TYPE_DYNAMIC;
+ } else {
+ GST_WARNING ("failed to parse MPD type property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return prop_type;
+}
+
+static GstSAPType
+gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ guint prop_SAP_type = 0;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type)
+ && prop_SAP_type <= 6) {
+ GST_LOG (" - %s: %u", property, prop_SAP_type);
+ } else {
+ GST_WARNING
+ ("failed to parse unsigned integer property %s from xml string %s",
+ property, prop_string);
+ }
+ xmlFree (prop_string);
+ }
+
+ return (GstSAPType) prop_SAP_type;
+}
+
+static GstRange *
+gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ GstRange *prop_range = NULL;
+ guint64 first_byte_pos = 0, last_byte_pos = 0;
+ guint len, pos;
+ gchar *str;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ len = xmlStrlen (prop_string);
+ str = (gchar *) prop_string;
+ GST_TRACE ("range: %s, len %d", str, len);
+
+ /* read "-" */
+ pos = strcspn (str, "-");
+ if (pos >= len) {
+ GST_TRACE ("pos %d >= len %d", pos, len);
+ goto error;
+ }
+ /* read first_byte_pos */
+ if (pos != 0) {
+ if (sscanf (str, "%llu", &first_byte_pos) != 1) {
+ goto error;
+ }
+ }
+ /* read last_byte_pos */
+ if (pos < (len - 1)) {
+ if (sscanf (str + pos + 1, "%llu", &last_byte_pos) != 1) {
+ goto error;
+ }
+ }
+ /* malloc return data structure */
+ prop_range = g_slice_new0 (GstRange);
+ if (prop_range == NULL) {
+ GST_WARNING ("Allocation of GstRange failed!");
+ goto error;
+ }
+ prop_range->first_byte_pos = first_byte_pos;
+ prop_range->last_byte_pos = last_byte_pos;
+ GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
+ property, first_byte_pos, last_byte_pos);
+ xmlFree (prop_string);
+ }
+
+ return prop_range;
+
+error:
+ GST_WARNING ("failed to parse property %s from xml string %s", property,
+ prop_string);
+ return NULL;
+}
+
+static GstRatio *
+gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ GstRatio *prop_ratio = NULL;
+ guint num = 0, den = 1;
+ guint len, pos;
+ gchar *str;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ len = xmlStrlen (prop_string);
+ str = (gchar *) prop_string;
+ GST_TRACE ("ratio: %s, len %d", str, len);
+
+ /* read ":" */
+ pos = strcspn (str, ":");
+ if (pos >= len) {
+ GST_TRACE ("pos %d >= len %d", pos, len);
+ goto error;
+ }
+ /* read num */
+ if (pos != 0) {
+ if (sscanf (str, "%u", &num) != 1) {
+ goto error;
+ }
+ }
+ /* read den */
+ if (pos < (len - 1)) {
+ if (sscanf (str + pos + 1, "%u", &den) != 1) {
+ goto error;
+ }
+ }
+ /* malloc return data structure */
+ prop_ratio = g_slice_new0 (GstRatio);
+ if (prop_ratio == NULL) {
+ GST_WARNING ("Allocation of GstRatio failed!");
+ goto error;
+ }
+ prop_ratio->num = num;
+ prop_ratio->den = den;
+ GST_LOG (" - %s: %u:%u", property, num, den);
+ xmlFree (prop_string);
+ }
+
+ return prop_ratio;
+
+error:
+ GST_WARNING ("failed to parse property %s from xml string %s", property,
+ prop_string);
+ return NULL;
+}
+
+static GstFrameRate *
+gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ GstFrameRate *prop_framerate = NULL;
+ guint num = 0, den = 1;
+ guint len, pos;
+ gchar *str;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ len = xmlStrlen (prop_string);
+ str = (gchar *) prop_string;
+ GST_TRACE ("framerate: %s, len %d", str, len);
+
+ /* read "/" if available */
+ pos = strcspn (str, "/");
+ /* read num */
+ if (pos != 0) {
+ if (sscanf (str, "%u", &num) != 1) {
+ goto error;
+ }
+ }
+ /* read den (if available) */
+ if (pos < (len - 1)) {
+ if (sscanf (str + pos + 1, "%u", &den) != 1) {
+ goto error;
+ }
+ }
+ /* alloc return data structure */
+ prop_framerate = g_slice_new0 (GstFrameRate);
+ if (prop_framerate == NULL) {
+ GST_WARNING ("Allocation of GstFrameRate failed!");
+ goto error;
+ }
+ prop_framerate->num = num;
+ prop_framerate->den = den;
+ if (den == 1)
+ GST_LOG (" - %s: %u", property, num);
+ else
+ GST_LOG (" - %s: %u/%u", property, num, den);
+ xmlFree (prop_string);
+ }
+
+ return prop_framerate;
+
+error:
+ GST_WARNING ("failed to parse property %s from xml string %s", property,
+ prop_string);
+ return NULL;
+}
+
+static GstConditionalUintType *
+gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ GstConditionalUintType *prop_cond_uint = NULL;
+ gchar *str;
+ gboolean flag;
+ guint val;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ str = (gchar *) prop_string;
+ GST_TRACE ("conditional uint: %s", str);
+
+ if (strcmp (str, "false") == 0) {
+ flag = FALSE;
+ val = 0;
+ } else if (strcmp (str, "true") == 0) {
+ flag = TRUE;
+ val = 0;
+ } else {
+ flag = TRUE;
+ if (sscanf (str, "%u", &val) != 1)
+ goto error;
+ }
+
+ /* alloc return data structure */
+ prop_cond_uint = g_slice_new0 (GstConditionalUintType);
+ if (prop_cond_uint == NULL) {
+ GST_WARNING ("Allocation of GstConditionalUintType failed!");
+ goto error;
+ }
+ prop_cond_uint->flag = flag;
+ prop_cond_uint->value = val;
+ GST_LOG (" - %s: flag=%s val=%u", property, flag ? "true" : "false", val);
+ xmlFree (prop_string);
+ }
+
+ return prop_cond_uint;
+
+error:
+ GST_WARNING ("failed to parse property %s from xml string %s", property,
+ prop_string);
+ return NULL;
+}
+
+/*
+ DateTime Data Type
+
+ The dateTime data type is used to specify a date and a time.
+
+ The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:
+
+ * YYYY indicates the year
+ * MM indicates the month
+ * DD indicates the day
+ * T indicates the start of the required time section
+ * hh indicates the hour
+ * mm indicates the minute
+ * ss indicates the second
+
+ Note: All components are required!
+*/
+
+static GstDateTime *
+gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ gchar *str;
+ gint ret, len, pos;
+ gint year, month, day, hour, minute, second;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ len = xmlStrlen (prop_string);
+ str = (gchar *) prop_string;
+ GST_TRACE ("dateTime: %s, len %d", str, len);
+ /* parse year */
+ ret = sscanf (str, "%d", &year);
+ if (ret != 1)
+ goto error;
+ pos = strcspn (str, "-");
+ str += (pos + 1);
+ GST_TRACE (" - year %d", year);
+ /* parse month */
+ ret = sscanf (str, "%d", &month);
+ if (ret != 1)
+ goto error;
+ pos = strcspn (str, "-");
+ str += (pos + 1);
+ GST_TRACE (" - month %d", month);
+ /* parse day */
+ ret = sscanf (str, "%d", &day);
+ if (ret != 1)
+ goto error;
+ pos = strcspn (str, "T");
+ str += (pos + 1);
+ GST_TRACE (" - day %d", day);
+ /* parse hour */
+ ret = sscanf (str, "%d", &hour);
+ if (ret != 1)
+ goto error;
+ pos = strcspn (str, ":");
+ str += (pos + 1);
+ GST_TRACE (" - hour %d", hour);
+ /* parse minute */
+ ret = sscanf (str, "%d", &minute);
+ if (ret != 1)
+ goto error;
+ pos = strcspn (str, ":");
+ str += (pos + 1);
+ GST_TRACE (" - minute %d", minute);
+ /* parse second */
+ ret = sscanf (str, "%d", &second);
+ if (ret != 1)
+ goto error;
+ GST_TRACE (" - second %d", second);
+
+ GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%02d", property,
+ year, month, day, hour, minute, second);
+
+ return gst_date_time_new (0, year, month, day, hour, minute, second);
+ }
+
+ return NULL;
+
+error:
+ GST_WARNING ("failed to parse property %s from xml string %s", property,
+ prop_string);
+ return NULL;
+}
+
+/*
+ Duration Data Type
+
+ The duration data type is used to specify a time interval.
+
+ The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
+
+ * - indicates the negative sign (optional)
+ * P indicates the period (required)
+ * nY indicates the number of years
+ * nM indicates the number of months
+ * nD indicates the number of days
+ * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
+ * nH indicates the number of hours
+ * nM indicates the number of minutes
+ * nS indicates the number of seconds
+*/
+
+/* this function computes decimals * 10 ^ (3 - pos) */
+static gint
+convert_to_millisecs (gint decimals, gint pos)
+{
+ gint num = 1, den = 1, i = 3 - pos;
+
+ while (i < 0) {
+ den *= 10;
+ i++;
+ }
+ while (i > 0) {
+ num *= 10;
+ i--;
+ }
+ /* if i == 0 we have exactly 3 decimals and nothing to do */
+ return decimals * num / den;
+}
+
+static gint64
+gst_mpdparser_get_xml_prop_duration (xmlNode * a_node, const gchar * property)
+{
+ xmlChar *prop_string;
+ gchar *str;
+ gint64 prop_duration = -1;
+ gint ret, read, len, pos, posT;
+ gint years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds =
+ 0, decimals = 0;
+ gint sign = 1;
+ gboolean have_ms = FALSE;
+
+ prop_string = xmlGetProp (a_node, (const xmlChar *) property);
+ if (prop_string) {
+ len = xmlStrlen (prop_string);
+ str = (gchar *) prop_string;
+ GST_TRACE ("duration: %s, len %d", str, len);
+ /* read "-" for sign, if present */
+ pos = strcspn (str, "-");
+ if (pos < len) { /* found "-" */
+ if (pos != 0) {
+ GST_WARNING ("sign \"-\" non at the beginning of the string");
+ return -1;
+ }
+ GST_TRACE ("found - sign at the beginning");
+ sign = -1;
+ str++;
+ len--;
+ }
+ /* read "P" for period */
+ pos = strcspn (str, "P");
+ if (pos != 0) {
+ GST_WARNING ("P not found at the beginning of the string!");
+ return -1;
+ }
+ str++;
+ len--;
+ /* read "T" for time (if present) */
+ posT = strcspn (str, "T");
+ len -= posT;
+ if (posT > 0) {
+ /* there is some room between P and T, so there must be a period section */
+ /* read years, months, days */
+ do {
+ GST_TRACE ("parsing substring %s", str);
+ pos = strcspn (str, "YMD");
+ ret = sscanf (str, "%d", &read);
+ if (ret != 1) {
+ GST_WARNING ("can not read integer value from string %s!", str);
+ return -1;
+ }
+ switch (str[pos]) {
+ case 'Y':
+ years = read;
+ break;
+ case 'M':
+ months = read;
+ break;
+ case 'D':
+ days = read;
+ break;
+ default:
+ GST_WARNING ("unexpected char %c!", str[pos]);
+ return -1;
+ break;
+ }
+ GST_TRACE ("read number %d type %c", read, str[pos]);
+ str += (pos + 1);
+ posT -= (pos + 1);
+ } while (posT > 0);
+
+ GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
+ }
+ /* read "T" for time (if present) */
+ /* here T is at pos == 0 */
+ str++;
+ len--;
+ pos = 0;
+ if (pos < len) {
+ /* T found, there is a time section */
+ /* read hours, minutes, seconds, cents of second */
+ do {
+ GST_TRACE ("parsing substring %s", str);
+ pos = strcspn (str, "HMS,.");
+ ret = sscanf (str, "%d", &read);
+ if (ret != 1) {
+ GST_WARNING ("can not read integer value from string %s!", str);
+ return -1;
+ }
+ switch (str[pos]) {
+ case 'H':
+ hours = read;
+ break;
+ case 'M':
+ minutes = read;
+ break;
+ case 'S':
+ if (have_ms) {
+ /* we have read the decimal part of the seconds */
+ decimals = convert_to_millisecs (read, pos);
+ GST_TRACE ("decimal number %d (%d digits) -> %d ms", read, pos,
+ decimals);
+ } else {
+ /* no decimals */
+ seconds = read;
+ }
+ break;
+ case '.':
+ case ',':
+ /* we have read the integer part of a decimal number in seconds */
+ seconds = read;
+ have_ms = TRUE;
+ break;
+ default:
+ GST_WARNING ("unexpected char %c!", str[pos]);
+ return -1;
+ break;
+ }
+ GST_TRACE ("read number %d type %c", read, str[pos]);
+ str += pos + 1;
+ len -= (pos + 1);
+ } while (len > 0);
+
+ GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
+ }
+
+ xmlFree (prop_string);
+ prop_duration =
+ sign * ((((((gint64) years * 365 + months * 30 + days) * 24 +
+ hours) * 60 + minutes) * 60 + seconds) * 1000 + decimals);
+ GST_LOG (" - %s: %" G_GINT64_FORMAT, property, prop_duration);
+ }
+
+ return prop_duration;
+}
+
+static gchar *
+gst_mpdparser_get_xml_node_content (xmlNode * a_node)
+{
+ xmlChar *content = NULL;
+
+ content = xmlNodeGetContent (a_node);
+ if (content) {
+ GST_LOG (" - %s: %s", a_node->name, content);
+ }
+
+ return (gchar *) content;
+}
+
+static gchar *
+gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
+{
+ xmlNs *curr_ns;
+ gchar *namespace = NULL;
+
+ if (prefix == NULL) {
+ /* return the default namespace */
+ namespace = g_strdup ((gchar *) a_node->ns->href);
+ if (namespace) {
+ GST_LOG (" - default namespace: %s", namespace);
+ }
+ } else {
+ /* look for the specified prefix in the namespace list */
+ for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
+ if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
+ namespace = g_strdup ((gchar *) curr_ns->href);
+ if (namespace) {
+ GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
+ }
+ }
+ }
+ }
+
+ return namespace;
+}
+
+static void
+gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
+{
+ GstBaseURL *new_base_url;
+
+ new_base_url = g_slice_new0 (GstBaseURL);
+ if (new_base_url == NULL) {
+ GST_WARNING ("Allocation of BaseURL node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_base_url);
+
+ GST_LOG ("content of BaseURL node:");
+ new_base_url->baseURL = gst_mpdparser_get_xml_node_content (a_node);
+ GST_LOG ("attributes of BaseURL node:");
+ new_base_url->serviceLocation =
+ gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation");
+ new_base_url->byteRange =
+ gst_mpdparser_get_xml_prop_string (a_node, "byteRange");
+}
+
+static void
+gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
+{
+ GstDescriptorType *new_descriptor;
+
+ new_descriptor = g_slice_new0 (GstDescriptorType);
+ if (new_descriptor == NULL) {
+ GST_WARNING ("Allocation of DescriptorType node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_descriptor);
+
+ GST_LOG ("attributes of %s node:", a_node->name);
+ new_descriptor->schemeIdUri =
+ gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri");
+ new_descriptor->value = gst_mpdparser_get_xml_prop_string (a_node, "value");
+
+ /* For adding ContentProtection to caps , get string of <mspr:pro> */
+ if(xmlStrcmp (a_node->name,(xmlChar *) "ContentProtection") == 0) {
+ if (a_node->children->next){
+ if(xmlStrcmp (a_node->children->next->name,(xmlChar *) "pro") == 0){
+ if (a_node->children->next->type == XML_ELEMENT_NODE) {
+ new_descriptor->msprPro = gst_mpdparser_get_xml_node_content (a_node->children->next);
+ }
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstContentComponentNode *new_content_component;
+
+ new_content_component = g_slice_new0 (GstContentComponentNode);
+ if (new_content_component == NULL) {
+ GST_WARNING ("Allocation of ContentComponent node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_content_component);
+
+ GST_LOG ("attributes of ContentComponent node:");
+ new_content_component->id =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0);
+ new_content_component->lang =
+ gst_mpdparser_get_xml_prop_string (a_node, "lang");
+ new_content_component->contentType =
+ gst_mpdparser_get_xml_prop_string (a_node, "contentType");
+ new_content_component->par = gst_mpdparser_get_xml_prop_ratio (a_node, "par");
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_content_component->
+ Accessibility, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_content_component->
+ Rating, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_content_component->
+ Viewpoint, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
+{
+ gchar *location;
+
+ GST_LOG ("content of Location node:");
+ location = gst_mpdparser_get_xml_node_content (a_node);
+
+ *list = g_list_append (*list, location);
+}
+
+static void
+gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
+{
+ GstSubRepresentationNode *new_subrep;
+
+ new_subrep = g_slice_new0 (GstSubRepresentationNode);
+ if (new_subrep == NULL) {
+ GST_WARNING ("Allocation of SubRepresentation node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_subrep);
+
+ GST_LOG ("attributes of SubRepresentation node:");
+ new_subrep->level =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0);
+ new_subrep->dependencyLevel =
+ gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
+ &new_subrep->size);
+ new_subrep->bandwidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0);
+ new_subrep->contentComponent =
+ gst_mpdparser_get_xml_prop_string_vector_type (a_node,
+ "contentComponent");
+
+ /* RepresentationBase extension */
+ gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
+ a_node);
+}
+
+static void
+gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
+{
+ GstSegmentURLNode *new_segment_url;
+
+ new_segment_url = g_slice_new0 (GstSegmentURLNode);
+ if (new_segment_url == NULL) {
+ GST_WARNING ("Allocation of SegmentURL node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_segment_url);
+
+ GST_LOG ("attributes of SegmentURL node:");
+ new_segment_url->media = gst_mpdparser_get_xml_prop_string (a_node, "media");
+ new_segment_url->mediaRange =
+ gst_mpdparser_get_xml_prop_range (a_node, "mediaRange");
+ new_segment_url->index = gst_mpdparser_get_xml_prop_string (a_node, "index");
+ new_segment_url->indexRange =
+ gst_mpdparser_get_xml_prop_range (a_node, "indexRange");
+}
+
+static void
+gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
+{
+ GstURLType *new_url_type;
+
+ gst_mpdparser_free_url_type_node (*pointer);
+ *pointer = new_url_type = g_slice_new0 (GstURLType);
+ if (new_url_type == NULL) {
+ GST_WARNING ("Allocation of URLType node failed!");
+ return;
+ }
+
+ GST_LOG ("attributes of URLType node:");
+ new_url_type->sourceURL =
+ gst_mpdparser_get_xml_prop_string (a_node, "sourceURL");
+ new_url_type->range = gst_mpdparser_get_xml_prop_range (a_node, "range");
+}
+
+static void
+gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
+ xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstSegmentBaseType *seg_base_type;
+
+ gst_mpdparser_free_seg_base_type_ext (*pointer);
+ *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
+ if (seg_base_type == NULL) {
+ GST_WARNING ("Allocation of SegmentBaseType node failed!");
+ return;
+ }
+
+ GST_LOG ("attributes of SegmentBaseType extension:");
+ seg_base_type->timescale =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 0);
+ seg_base_type->presentationTimeOffset =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node,
+ "presentationTimeOffset", 0);
+ seg_base_type->indexRange =
+ gst_mpdparser_get_xml_prop_string (a_node, "indexRange");
+ seg_base_type->indexRangeExact =
+ gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact");
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
+ xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
+ gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "RepresentationIndex") == 0) {
+ gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
+ cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_s_node (GList ** list, xmlNode * a_node)
+{
+ GstSNode *new_s_node;
+
+ new_s_node = g_slice_new0 (GstSNode);
+ if (new_s_node == NULL) {
+ GST_WARNING ("Allocation of S node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_s_node);
+
+ GST_LOG ("attributes of S node:");
+ new_s_node->t = gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "t", 0);
+ new_s_node->d = gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "d", 0);
+ new_s_node->r = gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "r", 0);
+}
+
+static void
+gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
+ xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstSegmentTimelineNode *new_seg_timeline;
+
+ gst_mpdparser_free_segment_timeline_node (*pointer);
+ *pointer = new_seg_timeline = g_slice_new0 (GstSegmentTimelineNode);
+ if (new_seg_timeline == NULL) {
+ GST_WARNING ("Allocation of SegmentTimeline node failed!");
+ return;
+ }
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
+ gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
+ xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstMultSegmentBaseType *mult_seg_base_type;
+
+ gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
+ *pointer = mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
+ if (mult_seg_base_type == NULL) {
+ GST_WARNING ("Allocation of MultipleSegmentBaseType node failed!");
+ return;
+ }
+
+ GST_LOG ("attributes of MultipleSegmentBaseType extension:");
+ mult_seg_base_type->duration =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0);
+ mult_seg_base_type->startNumber =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1);
+
+ GST_LOG ("extension of MultipleSegmentBaseType extension:");
+ gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
+ a_node);
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
+ gst_mpdparser_parse_segment_timeline_node (&mult_seg_base_type->
+ SegmentTimeline, cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "BitstreamSwitching") == 0) {
+ gst_mpdparser_parse_url_type_node (&mult_seg_base_type->
+ BitstreamSwitching, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
+ xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstSegmentListNode *new_segment_list;
+
+ gst_mpdparser_free_segment_list_node (*pointer);
+ *pointer = new_segment_list = g_slice_new0 (GstSegmentListNode);
+ if (new_segment_list == NULL) {
+ GST_WARNING ("Allocation of SegmentList node failed!");
+ return;
+ }
+
+ GST_LOG ("extension of SegmentList node:");
+ gst_mpdparser_parse_mult_seg_base_type_ext (&new_segment_list->
+ MultSegBaseType, a_node);
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
+ gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
+ cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
+ pointer, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstRepresentationBaseType *representation_base;
+
+ gst_mpdparser_free_representation_base_type (*pointer);
+ *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
+ if (representation_base == NULL) {
+ GST_WARNING ("Allocation of RepresentationBaseType node failed!");
+ return;
+ }
+
+ GST_LOG ("attributes of RepresentationBaseType extension:");
+ representation_base->profiles =
+ gst_mpdparser_get_xml_prop_string (a_node, "profiles");
+ representation_base->width =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0);
+ representation_base->height =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0);
+ representation_base->bandwidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0);
+ representation_base->sar = gst_mpdparser_get_xml_prop_ratio (a_node, "sar");
+ representation_base->frameRate =
+ gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate");
+ representation_base->audioSamplingRate =
+ gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate");
+ representation_base->mimeType =
+ gst_mpdparser_get_xml_prop_string (a_node, "mimeType");
+ representation_base->segmentProfiles =
+ gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles");
+ representation_base->codecs =
+ gst_mpdparser_get_xml_prop_string (a_node, "codecs");
+ representation_base->maximumSAPPeriod =
+ gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod");
+ representation_base->startWithSAP =
+ gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP");
+ representation_base->maxPlayoutRate =
+ gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate");
+ representation_base->codingDependency =
+ gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency");
+ representation_base->scanType =
+ gst_mpdparser_get_xml_prop_string (a_node, "scanType");
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&representation_base->
+ FramePacking, cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "AudioChannelConfiguration") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&representation_base->
+ AudioChannelConfiguration, cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "ContentProtection") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&representation_base->
+ ContentProtection, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstRepresentationNode *new_representation;
+
+ new_representation = g_slice_new0 (GstRepresentationNode);
+ if (new_representation == NULL) {
+ GST_WARNING ("Allocation of Representation node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_representation);
+
+ GST_LOG ("attributes of Representation node:");
+ new_representation->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
+ new_representation->bandwidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0);
+ new_representation->qualityRanking =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0);
+ new_representation->dependencyId =
+ gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId");
+ new_representation->mediaStreamStructureId =
+ gst_mpdparser_get_xml_prop_string_vector_type (a_node,
+ "mediaStreamStructureId");
+
+ /* RepresentationBase extension */
+ gst_mpdparser_parse_representation_base_type (&new_representation->
+ RepresentationBase, a_node);
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
+ gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
+ gst_mpdparser_parse_segment_template_node (&new_representation->
+ SegmentTemplate, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
+ gst_mpdparser_parse_segment_list_node (&new_representation->SegmentList,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
+ gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "SubRepresentation") == 0) {
+ gst_mpdparser_parse_subrepresentation_node (&new_representation->
+ SubRepresentations, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstAdaptationSetNode *new_adap_set;
+
+ new_adap_set = g_slice_new0 (GstAdaptationSetNode);
+ if (new_adap_set == NULL) {
+ GST_WARNING ("Allocation of AdaptationSet node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_adap_set);
+
+ GST_LOG ("attributes of AdaptationSet node:");
+ new_adap_set->id =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0);
+ new_adap_set->group =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0);
+ new_adap_set->lang = gst_mpdparser_get_xml_prop_string (a_node, "lang");
+ new_adap_set->contentType =
+ gst_mpdparser_get_xml_prop_string (a_node, "contentType");
+ new_adap_set->par = gst_mpdparser_get_xml_prop_ratio (a_node, "par");
+ new_adap_set->minBandwidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0);
+ new_adap_set->maxBandwidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0);
+ new_adap_set->minWidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0);
+ new_adap_set->maxWidth =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0);
+ new_adap_set->minHeight =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0);
+ new_adap_set->maxHeight =
+ gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0);
+ new_adap_set->minFrameRate =
+ gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate");
+ new_adap_set->maxFrameRate =
+ gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate");
+ new_adap_set->segmentAlignment =
+ gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment");
+ new_adap_set->subsegmentAlignment =
+ gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment");
+ new_adap_set->subsegmentStartsWithSAP =
+ gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP");
+ new_adap_set->bitstreamSwitching =
+ gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching");
+
+ /* RepresentationBase extension */
+ gst_mpdparser_parse_representation_base_type (&new_adap_set->
+ RepresentationBase, a_node);
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Accessibility,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Role,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Rating,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
+ gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Viewpoint,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
+ gst_mpdparser_parse_representation_node (&new_adap_set->Representations,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
+ gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
+ gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
+ gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "ContentComponent") == 0) {
+ gst_mpdparser_parse_content_component_node (&new_adap_set->
+ ContentComponents, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
+ gst_mpdparser_parse_segment_template_node (&new_adap_set->
+ SegmentTemplate, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
+{
+ GstSubsetNode *new_subset;
+
+ new_subset = g_slice_new0 (GstSubsetNode);
+ if (new_subset == NULL) {
+ GST_WARNING ("Allocation of Subset node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_subset);
+
+ GST_LOG ("attributes of Subset node:");
+ new_subset->contains =
+ gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
+ &new_subset->size);
+}
+
+static void
+gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
+ xmlNode * a_node)
+{
+ GstSegmentTemplateNode *new_segment_template;
+
+ gst_mpdparser_free_segment_template_node (*pointer);
+ *pointer = new_segment_template = g_slice_new0 (GstSegmentTemplateNode);
+ if (new_segment_template == NULL) {
+ GST_WARNING ("Allocation of SegmentTemplate node failed!");
+ return;
+ }
+
+ GST_LOG ("extension of SegmentTemplate node:");
+ gst_mpdparser_parse_mult_seg_base_type_ext (&new_segment_template->
+ MultSegBaseType, a_node);
+
+ GST_LOG ("attributes of SegmentTemplate node:");
+ new_segment_template->media =
+ gst_mpdparser_get_xml_prop_string (a_node, "media");
+ new_segment_template->index =
+ gst_mpdparser_get_xml_prop_string (a_node, "index");
+ new_segment_template->initialization =
+ gst_mpdparser_get_xml_prop_string (a_node, "initialization");
+ new_segment_template->bitstreamSwitching =
+ gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching");
+}
+
+static void
+gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstPeriodNode *new_period;
+
+ new_period = g_slice_new0 (GstPeriodNode);
+ if (new_period == NULL) {
+ GST_WARNING ("Allocation of Period node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_period);
+
+ GST_LOG ("attributes of Period node:");
+ new_period->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
+ new_period->start = gst_mpdparser_get_xml_prop_duration (a_node, "start");
+ new_period->duration =
+ gst_mpdparser_get_xml_prop_duration (a_node, "duration");
+ new_period->bitstreamSwitching =
+ gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching");
+
+ /* explore children nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
+ gst_mpdparser_parse_adaptation_set_node (&new_period->AdaptationSets,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
+ gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
+ gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
+ gst_mpdparser_parse_segment_template_node (&new_period->SegmentTemplate,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
+ gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
+ gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstProgramInformationNode *new_prog_info;
+
+ new_prog_info = g_slice_new0 (GstProgramInformationNode);
+ if (new_prog_info == NULL) {
+ GST_WARNING ("Allocation of ProgramInfo node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_prog_info);
+
+ GST_LOG ("attributes of ProgramInformation node:");
+ new_prog_info->lang = gst_mpdparser_get_xml_prop_string (a_node, "lang");
+ new_prog_info->moreInformationURL =
+ gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL");
+
+ /* explore children nodes */
+ GST_LOG ("children of ProgramInformation node:");
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
+ new_prog_info->Title = gst_mpdparser_get_xml_node_content (cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
+ new_prog_info->Source = gst_mpdparser_get_xml_node_content (cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
+ new_prog_info->Copyright =
+ gst_mpdparser_get_xml_node_content (cur_node);
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
+{
+ GstMetricsRangeNode *new_metrics_range;
+
+ new_metrics_range = g_slice_new0 (GstMetricsRangeNode);
+ if (new_metrics_range == NULL) {
+ GST_WARNING ("Allocation of Metrics Range node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_metrics_range);
+
+ GST_LOG ("attributes of Metrics Range node:");
+ new_metrics_range->starttime =
+ gst_mpdparser_get_xml_prop_duration (a_node, "starttime");
+ new_metrics_range->duration =
+ gst_mpdparser_get_xml_prop_duration (a_node, "duration");
+}
+
+static void
+gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstMetricsNode *new_metrics;
+
+ new_metrics = g_slice_new0 (GstMetricsNode);
+ if (new_metrics == NULL) {
+ GST_WARNING ("Allocation of Metrics node failed!");
+ return;
+ }
+ *list = g_list_append (*list, new_metrics);
+
+ GST_LOG ("attributes of Metrics node:");
+ new_metrics->metrics = gst_mpdparser_get_xml_prop_string (a_node, "metrics");
+
+ /* explore children nodes */
+ GST_LOG ("children of Metrics node:");
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
+ gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
+ cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
+ /* No reporting scheme is specified in this part of ISO/IEC 23009.
+ * It is expected that external specifications may define formats
+ * and delivery for the reporting data. */
+ GST_LOG (" - Reporting node found (unknown structure)");
+ }
+ }
+ }
+}
+
+static void
+gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ GstMPDNode *new_mpd;
+
+ gst_mpdparser_free_mpd_node (*pointer);
+ *pointer = new_mpd = g_slice_new0 (GstMPDNode);
+ if (new_mpd == NULL) {
+ GST_WARNING ("Allocation of MPD node failed!");
+ return;
+ }
+
+ GST_LOG ("namespaces of root MPD node:");
+ new_mpd->default_namespace =
+ gst_mpdparser_get_xml_node_namespace (a_node, NULL);
+ new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
+ new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
+
+ GST_LOG ("attributes of root MPD node:");
+ new_mpd->schemaLocation =
+ gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation");
+ new_mpd->id = gst_mpdparser_get_xml_prop_string (a_node, "id");
+ new_mpd->profiles = gst_mpdparser_get_xml_prop_string (a_node, "profiles");
+ new_mpd->type = gst_mpdparser_get_xml_prop_type (a_node, "type");
+ new_mpd->availabilityStartTime =
+ gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime");
+ new_mpd->availabilityEndTime =
+ gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime");
+ new_mpd->mediaPresentationDuration =
+ gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration");
+ new_mpd->minimumUpdatePeriod =
+ gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod");
+ new_mpd->minBufferTime =
+ gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime");
+ new_mpd->timeShiftBufferDepth =
+ gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth");
+ new_mpd->suggestedPresentationDelay =
+ gst_mpdparser_get_xml_prop_duration (a_node,
+ "suggestedPresentationDelay");
+ new_mpd->maxSegmentDuration =
+ gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration");
+ new_mpd->maxSubsegmentDuration =
+ gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration");
+
+ /* explore children Period nodes */
+ for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
+ gst_mpdparser_parse_period_node (&new_mpd->Periods, cur_node);
+ } else if (xmlStrcmp (cur_node->name,
+ (xmlChar *) "ProgramInformation") == 0) {
+ gst_mpdparser_parse_program_info_node (&new_mpd->ProgramInfo, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
+ gst_mpdparser_parse_baseURL_node (&new_mpd->BaseURLs, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
+ gst_mpdparser_parse_location_node (&new_mpd->Locations, cur_node);
+ } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
+ gst_mpdparser_parse_metrics_node (&new_mpd->Metrics, cur_node);
+ }
+ }
+ }
+}
+
+/* comparison functions */
+static int
+strncmp_ext (const char *s1, const char *s2)
+{
+ if (s1 == NULL && s2 == NULL)
+ return 0;
+ if (s1 == NULL && s2 != NULL)
+ return 1;
+ if (s2 == NULL && s1 != NULL)
+ return 1;
+ return strncmp (s1, s2, strlen (s2));
+}
+
+/* navigation functions */
+static GstAdaptationSetNode *
+gst_mpdparser_get_first_adapt_set_with_mimeType (GList * AdaptationSets,
+ const gchar * mimeType)
+{
+ GList *list;
+ GstAdaptationSetNode *adapt_set;
+
+ if (AdaptationSets == NULL)
+ return NULL;
+
+ for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
+ adapt_set = (GstAdaptationSetNode *) list->data;
+ if (adapt_set) {
+ gchar *this_mimeType = NULL;
+ GstRepresentationNode *rep;
+ rep =
+ gst_mpdparser_get_lowest_representation (adapt_set->Representations);
+ if (!rep) {
+ GST_WARNING ("No valid representation in the MPD file, aborting...");
+ return NULL;
+ }
+ if (rep->RepresentationBase)
+ this_mimeType = rep->RepresentationBase->mimeType;
+ if (!this_mimeType && adapt_set->RepresentationBase) {
+ this_mimeType = adapt_set->RepresentationBase->mimeType;
+ }
+ if (strncmp_ext (this_mimeType, mimeType) == 0)
+ return adapt_set;
+ }
+ }
+
+ return NULL;
+}
+
+/* if idx < 0, returns the highest adaptation set with the given mimeType
+ * if idx >= 0, returns the highest adaptation set with the given mimeType and an index <= idx
+ */
+static GstAdaptationSetNode *
+gst_mpdparser_get_adapt_set_with_mimeType_and_idx (GList * AdaptationSets,
+ const gchar * mimeType, gint idx)
+{
+ GList *list;
+ GstAdaptationSetNode *adapt_set, *selected = NULL;
+ gint i = 0;
+
+ if (AdaptationSets == NULL)
+ return NULL;
+
+ for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
+ adapt_set = (GstAdaptationSetNode *) list->data;
+ if (adapt_set) {
+ gchar *this_mimeType = NULL;
+ GstRepresentationNode *rep;
+ rep =
+ gst_mpdparser_get_lowest_representation (adapt_set->Representations);
+ if (!rep) {
+ GST_WARNING ("No valid representation in the MPD file, aborting...");
+ return NULL;
+ }
+ if (rep->RepresentationBase)
+ this_mimeType = rep->RepresentationBase->mimeType;
+ if (!this_mimeType && adapt_set->RepresentationBase) {
+ this_mimeType = adapt_set->RepresentationBase->mimeType;
+ }
+ if (strncmp_ext (this_mimeType, mimeType) == 0) {
+ if (idx < 0 || i <= idx)
+ selected = adapt_set;
+ i++;
+ }
+ }
+ }
+
+ return selected;
+}
+
+static GstAdaptationSetNode *
+gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang (GList *
+ AdaptationSets, const gchar * mimeType, const gchar * lang)
+{
+ GList *list;
+ GstAdaptationSetNode *adapt_set;
+
+ if (AdaptationSets == NULL)
+ return NULL;
+
+ for (list = g_list_first (AdaptationSets); list; list = g_list_next (list)) {
+ adapt_set = (GstAdaptationSetNode *) list->data;
+ if (adapt_set) {
+ GstRepresentationNode *rep;
+ gchar *this_lang = adapt_set->lang;
+ gchar *this_mimeType = NULL;
+ rep =
+ gst_mpdparser_get_lowest_representation (adapt_set->Representations);
+ if (!rep) {
+ GST_WARNING ("No valid representation in the MPD file, aborting...");
+ return NULL;
+ }
+ if (rep->RepresentationBase)
+ this_mimeType = rep->RepresentationBase->mimeType;
+ if (!this_mimeType && adapt_set->RepresentationBase) {
+ this_mimeType = adapt_set->RepresentationBase->mimeType;
+ }
+ if (strncmp_ext (this_mimeType, mimeType) == 0
+ && strncmp_ext (this_lang, lang) == 0)
+ return adapt_set;
+ }
+ }
+
+ return NULL;
+}
+
+static GstRepresentationNode *
+gst_mpdparser_get_lowest_representation (GList * Representations)
+{
+ GList *list = NULL;
+
+ if (Representations == NULL)
+ return NULL;
+
+ list = g_list_first (Representations);
+
+ return list ? (GstRepresentationNode *) list->data : NULL;
+}
+
+#if 0
+static GstRepresentationNode *
+gst_mpdparser_get_highest_representation (GList * Representations)
+{
+ GList *list = NULL;
+
+ if (Representations == NULL)
+ return NULL;
+
+ list = g_list_last (Representations);
+
+ return list ? (GstRepresentationNode *) list->data : NULL;
+}
+
+static GstRepresentationNode *
+gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
+ gint max_bandwidth)
+{
+ GList *list = NULL;
+ GstRepresentationNode *representation, *best_rep = NULL;
+
+ if (Representations == NULL)
+ return NULL;
+
+ if (max_bandwidth <= 0) /* 0 => get highest representation available */
+ return gst_mpdparser_get_highest_representation (Representations);
+
+ for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+ representation = (GstRepresentationNode *) list->data;
+ if (representation && representation->bandwidth <= max_bandwidth) {
+ best_rep = representation;
+ }
+ }
+
+ return best_rep;
+}
+#endif
+
+static GstSegmentBaseType *
+gst_mpdparser_get_segment_base (GstPeriodNode * Period,
+ GstAdaptationSetNode * AdaptationSet,
+ GstRepresentationNode * Representation)
+{
+ GstSegmentBaseType *SegmentBase = NULL;
+
+ if (Representation && Representation->SegmentBase
+ && Representation->SegmentBase->Initialization) {
+ SegmentBase = Representation->SegmentBase;
+ } else if (AdaptationSet && AdaptationSet->SegmentBase
+ && AdaptationSet->SegmentBase->Initialization) {
+ SegmentBase = AdaptationSet->SegmentBase;
+ } else if (Period && Period->SegmentBase
+ && Period->SegmentBase->Initialization) {
+ SegmentBase = Period->SegmentBase;
+ }
+ /* the SegmentBase element could be encoded also inside a SegmentList element */
+ if (SegmentBase == NULL) {
+ if (Representation && Representation->SegmentList
+ && Representation->SegmentList->MultSegBaseType
+ && Representation->SegmentList->MultSegBaseType->SegBaseType
+ && Representation->SegmentList->MultSegBaseType->SegBaseType->
+ Initialization) {
+ SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
+ } else if (AdaptationSet && AdaptationSet->SegmentList
+ && AdaptationSet->SegmentList->MultSegBaseType
+ && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType
+ && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType->
+ Initialization) {
+ SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
+ } else if (Period && Period->SegmentList
+ && Period->SegmentList->MultSegBaseType
+ && Period->SegmentList->MultSegBaseType->SegBaseType
+ && Period->SegmentList->MultSegBaseType->SegBaseType->Initialization) {
+ SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
+ }
+ }
+
+ return SegmentBase;
+}
+
+gint
+gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
+ guint64 max_bandwidth)
+{
+ GList *list = NULL, *best = NULL;
+ GstRepresentationNode *representation;
+ guint64 best_bandwidth = 0;
+
+ GST_DEBUG ("max bandwidth %" G_GUINT64_FORMAT, max_bandwidth);
+
+ if (Representations == NULL)
+ return -1;
+
+ if (max_bandwidth <= 0) /* 0 => get lowest representation available */
+ return 0;
+
+ for (list = g_list_first (Representations); list; list = g_list_next (list)) {
+ representation = (GstRepresentationNode *) list->data;
+ if (representation && representation->bandwidth <= max_bandwidth &&
+ representation->bandwidth > best_bandwidth) {
+ best = list;
+ best_bandwidth = representation->bandwidth;
+ }
+ }
+
+ return best ? g_list_position (Representations, best) : -1;
+}
+
+static GstSegmentListNode *
+gst_mpdparser_get_segment_list (GstPeriodNode * Period,
+ GstAdaptationSetNode * AdaptationSet,
+ GstRepresentationNode * Representation)
+{
+ GstSegmentListNode *SegmentList = NULL;
+
+ if (Representation && Representation->SegmentList) {
+ SegmentList = Representation->SegmentList;
+ } else if (AdaptationSet && AdaptationSet->SegmentList) {
+ SegmentList = AdaptationSet->SegmentList;
+ } else {
+ SegmentList = Period->SegmentList;
+ }
+
+ return SegmentList;
+}
+
+/* memory management functions */
+static void
+gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
+{
+ if (mpd_node) {
+ g_free (mpd_node->default_namespace);
+ g_free (mpd_node->namespace_xsi);
+ g_free (mpd_node->namespace_ext);
+ g_free (mpd_node->schemaLocation);
+ g_free (mpd_node->id);
+ g_free (mpd_node->profiles);
+ if (mpd_node->availabilityStartTime)
+ gst_date_time_unref (mpd_node->availabilityStartTime);
+ if (mpd_node->availabilityEndTime)
+ gst_date_time_unref (mpd_node->availabilityEndTime);
+ g_list_foreach (mpd_node->ProgramInfo,
+ (GFunc) gst_mpdparser_free_prog_info_node, NULL);
+ g_list_free (mpd_node->ProgramInfo);
+ g_list_foreach (mpd_node->BaseURLs,
+ (GFunc) gst_mpdparser_free_base_url_node, NULL);
+ g_list_free (mpd_node->BaseURLs);
+ g_list_foreach (mpd_node->Locations, (GFunc) g_free, NULL);
+ g_list_free (mpd_node->Locations);
+ g_list_foreach (mpd_node->Periods, (GFunc) gst_mpdparser_free_period_node,
+ NULL);
+ g_list_free (mpd_node->Periods);
+ g_list_foreach (mpd_node->Metrics, (GFunc) gst_mpdparser_free_metrics_node,
+ NULL);
+ g_list_free (mpd_node->Metrics);
+ g_slice_free (GstMPDNode, mpd_node);
+ }
+}
+
+static void
+gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
+{
+ if (prog_info_node) {
+ g_free (prog_info_node->lang);
+ g_free (prog_info_node->moreInformationURL);
+ g_free (prog_info_node->Title);
+ g_free (prog_info_node->Source);
+ g_free (prog_info_node->Copyright);
+ g_slice_free (GstProgramInformationNode, prog_info_node);
+ }
+}
+
+static void
+gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
+{
+ if (metrics_node) {
+ g_free (metrics_node->metrics);
+ g_list_foreach (metrics_node->MetricsRanges,
+ (GFunc) gst_mpdparser_free_metrics_range_node, NULL);
+ g_list_free (metrics_node->MetricsRanges);
+ }
+}
+
+static void
+gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
+{
+ if (metrics_range_node) {
+ g_slice_free (GstMetricsRangeNode, metrics_range_node);
+ }
+}
+
+static void
+gst_mpdparser_free_period_node (GstPeriodNode * period_node)
+{
+ if (period_node) {
+ g_free (period_node->id);
+ gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
+ gst_mpdparser_free_segment_list_node (period_node->SegmentList);
+ gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
+ g_list_foreach (period_node->AdaptationSets,
+ (GFunc) gst_mpdparser_free_adaptation_set_node, NULL);
+ g_list_free (period_node->AdaptationSets);
+ g_list_foreach (period_node->Subsets,
+ (GFunc) gst_mpdparser_free_subset_node, NULL);
+ g_list_free (period_node->Subsets);
+ g_list_foreach (period_node->BaseURLs,
+ (GFunc) gst_mpdparser_free_base_url_node, NULL);
+ g_list_free (period_node->BaseURLs);
+ g_slice_free (GstPeriodNode, period_node);
+ }
+}
+
+static void
+gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
+{
+ if (subset_node) {
+ g_free (subset_node->contains);
+ g_slice_free (GstSubsetNode, subset_node);
+ }
+}
+
+static void
+gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
+ segment_template_node)
+{
+ if (segment_template_node) {
+ g_free (segment_template_node->media);
+ g_free (segment_template_node->index);
+ g_free (segment_template_node->initialization);
+ g_free (segment_template_node->bitstreamSwitching);
+ /* MultipleSegmentBaseType extension */
+ gst_mpdparser_free_mult_seg_base_type_ext
+ (segment_template_node->MultSegBaseType);
+ g_slice_free (GstSegmentTemplateNode, segment_template_node);
+ }
+}
+
+static void
+gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
+ representation_base)
+{
+ if (representation_base) {
+ g_free (representation_base->profiles);
+ g_slice_free (GstRatio, representation_base->sar);
+ g_slice_free (GstFrameRate, representation_base->frameRate);
+ g_free (representation_base->audioSamplingRate);
+ g_free (representation_base->mimeType);
+ g_free (representation_base->segmentProfiles);
+ g_free (representation_base->codecs);
+ g_free (representation_base->scanType);
+ g_list_foreach (representation_base->FramePacking,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (representation_base->FramePacking);
+ g_list_foreach (representation_base->AudioChannelConfiguration,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (representation_base->AudioChannelConfiguration);
+ g_list_foreach (representation_base->ContentProtection,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (representation_base->ContentProtection);
+ g_slice_free (GstRepresentationBaseType, representation_base);
+ }
+}
+
+static void
+gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
+ adaptation_set_node)
+{
+ if (adaptation_set_node) {
+ g_free (adaptation_set_node->lang);
+ g_free (adaptation_set_node->contentType);
+ g_slice_free (GstRatio, adaptation_set_node->par);
+ g_slice_free (GstFrameRate, adaptation_set_node->minFrameRate);
+ g_slice_free (GstFrameRate, adaptation_set_node->maxFrameRate);
+ g_slice_free (GstConditionalUintType,
+ adaptation_set_node->segmentAlignment);
+ g_slice_free (GstConditionalUintType,
+ adaptation_set_node->subsegmentAlignment);
+ g_list_foreach (adaptation_set_node->Accessibility,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (adaptation_set_node->Accessibility);
+ g_list_foreach (adaptation_set_node->Role,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (adaptation_set_node->Role);
+ g_list_foreach (adaptation_set_node->Rating,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (adaptation_set_node->Rating);
+ g_list_foreach (adaptation_set_node->Viewpoint,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (adaptation_set_node->Viewpoint);
+ gst_mpdparser_free_representation_base_type
+ (adaptation_set_node->RepresentationBase);
+ gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
+ gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
+ gst_mpdparser_free_segment_template_node (adaptation_set_node->
+ SegmentTemplate);
+ g_list_foreach (adaptation_set_node->BaseURLs,
+ (GFunc) gst_mpdparser_free_base_url_node, NULL);
+ g_list_free (adaptation_set_node->BaseURLs);
+ g_list_foreach (adaptation_set_node->Representations,
+ (GFunc) gst_mpdparser_free_representation_node, NULL);
+ g_list_free (adaptation_set_node->Representations);
+ g_list_foreach (adaptation_set_node->ContentComponents,
+ (GFunc) gst_mpdparser_free_content_component_node, NULL);
+ g_list_free (adaptation_set_node->ContentComponents);
+ g_slice_free (GstAdaptationSetNode, adaptation_set_node);
+ }
+}
+
+static void
+gst_mpdparser_free_representation_node (GstRepresentationNode *
+ representation_node)
+{
+ if (representation_node) {
+ g_free (representation_node->id);
+ g_strfreev (representation_node->dependencyId);
+ g_strfreev (representation_node->mediaStreamStructureId);
+ gst_mpdparser_free_representation_base_type
+ (representation_node->RepresentationBase);
+ g_list_foreach (representation_node->SubRepresentations,
+ (GFunc) gst_mpdparser_free_subrepresentation_node, NULL);
+ g_list_free (representation_node->SubRepresentations);
+ gst_mpdparser_free_seg_base_type_ext (representation_node->SegmentBase);
+ gst_mpdparser_free_segment_template_node (representation_node->
+ SegmentTemplate);
+ gst_mpdparser_free_segment_list_node (representation_node->SegmentList);
+ g_list_foreach (representation_node->BaseURLs,
+ (GFunc) gst_mpdparser_free_base_url_node, NULL);
+ g_list_free (representation_node->BaseURLs);
+ g_slice_free (GstRepresentationNode, representation_node);
+ }
+}
+
+static void
+gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
+ subrep_node)
+{
+ if (subrep_node) {
+ gst_mpdparser_free_representation_base_type (subrep_node->
+ RepresentationBase);
+ g_free (subrep_node->dependencyLevel);
+ g_strfreev (subrep_node->contentComponent);
+ }
+}
+
+static void
+gst_mpdparser_free_s_node (GstSNode * s_node)
+{
+ if (s_node) {
+ g_slice_free (GstSNode, s_node);
+ }
+}
+
+static void
+gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
+{
+ if (seg_timeline) {
+ g_list_foreach (seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
+ g_list_free (seg_timeline->S);
+ g_slice_free (GstSegmentTimelineNode, seg_timeline);
+ }
+}
+
+static void
+gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
+{
+ if (url_type_node) {
+ g_free (url_type_node->sourceURL);
+ g_slice_free (GstRange, url_type_node->range);
+ g_slice_free (GstURLType, url_type_node);
+ }
+}
+
+static void
+gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
+{
+ if (seg_base_type) {
+ g_free (seg_base_type->indexRange);
+ gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
+ gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
+ g_slice_free (GstSegmentBaseType, seg_base_type);
+ }
+}
+
+static void
+gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
+ mult_seg_base_type)
+{
+ if (mult_seg_base_type) {
+ /* SegmentBaseType extension */
+ gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
+ gst_mpdparser_free_segment_timeline_node
+ (mult_seg_base_type->SegmentTimeline);
+ gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
+ g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
+ }
+}
+
+static void
+gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
+{
+ if (segment_list_node) {
+ g_list_foreach (segment_list_node->SegmentURL,
+ (GFunc) gst_mpdparser_free_segment_url_node, NULL);
+ g_list_free (segment_list_node->SegmentURL);
+ /* MultipleSegmentBaseType extension */
+ gst_mpdparser_free_mult_seg_base_type_ext
+ (segment_list_node->MultSegBaseType);
+ g_slice_free (GstSegmentListNode, segment_list_node);
+ }
+}
+
+static void
+gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
+{
+ if (segment_url) {
+ g_free (segment_url->media);
+ g_slice_free (GstRange, segment_url->mediaRange);
+ g_free (segment_url->index);
+ g_slice_free (GstRange, segment_url->indexRange);
+ g_slice_free (GstSegmentURLNode, segment_url);
+ }
+}
+
+static void
+gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
+{
+ if (base_url_node) {
+ g_free (base_url_node->baseURL);
+ g_free (base_url_node->serviceLocation);
+ g_free (base_url_node->byteRange);
+ g_slice_free (GstBaseURL, base_url_node);
+ }
+}
+
+static void
+gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
+{
+ if (descriptor_type) {
+ g_free (descriptor_type->schemeIdUri);
+ g_free (descriptor_type->value);
+ }
+}
+
+static void
+gst_mpdparser_free_content_component_node (GstContentComponentNode *
+ content_component_node)
+{
+ if (content_component_node) {
+ g_free (content_component_node->lang);
+ g_free (content_component_node->contentType);
+ g_slice_free (GstRatio, content_component_node->par);
+ g_list_foreach (content_component_node->Accessibility,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (content_component_node->Accessibility);
+ g_list_foreach (content_component_node->Role,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (content_component_node->Role);
+ g_list_foreach (content_component_node->Rating,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (content_component_node->Rating);
+ g_list_foreach (content_component_node->Viewpoint,
+ (GFunc) gst_mpdparser_free_descriptor_type_node, NULL);
+ g_list_free (content_component_node->Viewpoint);
+ g_slice_free (GstContentComponentNode, content_component_node);
+ }
+}
+
+static void
+gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
+{
+ if (stream_period) {
+ g_slice_free (GstStreamPeriod, stream_period);
+ }
+}
+
+static void
+gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
+{
+ if (media_segment) {
+ g_slice_free (GstMediaSegment, media_segment);
+ }
+}
+
+static void
+gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
+{
+ if (active_stream) {
+ g_list_foreach (active_stream->segments,
+ (GFunc) gst_mpdparser_free_media_segment, NULL);
+ if (active_stream->segments)
+ g_list_free (active_stream->segments);
+ g_slice_free (GstActiveStream, active_stream);
+ }
+}
+
+static gchar *
+gst_mpdparser_get_segmentURL_for_range (const gchar * url, GstRange * range)
+{
+ gchar *segmentURL;
+
+ if (range) {
+ gchar *range_suffix;
+ range_suffix =
+ g_strdup_printf ("?range=%llu-%llu", range->first_byte_pos,
+ range->last_byte_pos);
+ segmentURL = g_strconcat (url, range_suffix, NULL);
+ g_free (range_suffix);
+ } else {
+ segmentURL = g_strdup (url);
+ }
+
+ return segmentURL;
+}
+
+static gchar *
+gst_mpdparser_get_mediaURL (GstMpdClient * client,
+ GstSegmentURLNode * segmentURL)
+{
+ const gchar *url_prefix;
+
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (segmentURL != NULL, NULL);
+
+ url_prefix =
+ segmentURL->media ? segmentURL->
+ media : gst_mpdparser_get_baseURL (client);
+ g_return_val_if_fail (url_prefix != NULL, NULL);
+
+ return gst_mpdparser_get_segmentURL_for_range (url_prefix,
+ segmentURL->mediaRange);//if not present @media attribute, it is mapped to baseURL
+}
+
+static gchar *
+gst_mpdparser_get_initializationURL (GstURLType * InitializationURL)
+{
+ g_return_val_if_fail (InitializationURL != NULL, NULL);
+ g_return_val_if_fail (InitializationURL->sourceURL != NULL, NULL);
+
+ return gst_mpdparser_get_segmentURL_for_range (InitializationURL->sourceURL,
+ InitializationURL->range);
+}
+
+static gchar *
+gst_mpdparser_build_URL_from_template (const gchar * url_template,
+ const gchar * id, guint number, guint bandwidth, guint time)
+{
+ static gchar default_format[] = "%01d";
+ gchar **tokens, *token, *ret, *format;
+ gint i, num_tokens;
+ gboolean last_token_par = TRUE; /* last token was a parameter */
+
+ g_return_val_if_fail (url_template != NULL, NULL);
+ tokens = g_strsplit_set (url_template, "$", -1);
+ if (!tokens) {
+ GST_WARNING ("Scan of URL template failed!");
+ return NULL;
+ }
+ num_tokens = g_strv_length (tokens);
+
+ for (i = 0; i < num_tokens; i++) {
+ token = tokens[i];
+ format = default_format;
+
+ if (!g_strcmp0 (token, "RepresentationID")) {
+ tokens[i] = g_strdup_printf ("%s", id);
+ g_free (token);
+ last_token_par = TRUE;
+ } else if (!strncmp (token, "Number", 6)) {
+ if (strlen (token) > 6) {
+ format = token + 6; /* format tag */
+ }
+ tokens[i] = g_strdup_printf (format, number);
+ g_free (token);
+ last_token_par = TRUE;
+ } else if (!strncmp (token, "Bandwidth", 9)) {
+ if (strlen (token) > 9) {
+ format = token + 9; /* format tag */
+ }
+ tokens[i] = g_strdup_printf (format, bandwidth);
+ g_free (token);
+ last_token_par = TRUE;
+ } else if (!strncmp (token, "Time", 4)) {
+ if (strlen (token) > 4) {
+ format = token + 4; /* format tag */
+ }
+ tokens[i] = g_strdup_printf (format, time);
+ g_free (token);
+ last_token_par = TRUE;
+ } else if (!g_strcmp0 (token, "")) {
+ if (!last_token_par) {
+ tokens[i] = g_strdup_printf ("%s", "$");
+ g_free (token);
+ last_token_par = TRUE;
+ }
+ } else {
+ last_token_par = FALSE;
+ }
+ }
+
+ ret = g_strjoinv (NULL, tokens);
+ g_strfreev (tokens);
+
+ return ret;
+}
+
+static GstStreamPeriod *
+gst_mpdparser_get_stream_period (GstMpdClient * client)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (client->periods != NULL, NULL);
+
+ return g_list_nth_data (client->periods, client->period_idx);
+}
+
+/* select a stream and extract the baseURL (if present) */
+static gchar *
+gst_mpdparser_parse_baseURL (GstMpdClient * client)
+{
+ GstActiveStream *stream;
+ GstStreamPeriod *stream_period;
+ GstBaseURL *baseURL;
+ GList *list;
+ static gchar *baseURL_array[5];
+ static gchar empty[] = "";
+ gchar *ret = NULL;
+
+ stream =
+ gst_mpdparser_get_active_stream_by_index (client, client->stream_idx);
+ g_return_val_if_fail (stream != NULL, empty);
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, empty);
+ g_return_val_if_fail (stream_period->period != NULL, empty);
+
+ baseURL_array[0] = baseURL_array[1] = baseURL_array[2] = baseURL_array[3] =
+ empty;
+ baseURL_array[4] = NULL;
+
+ /* FIXME: this simple implementation is not fully compliant with RFC 3986 */
+ if ((list = client->mpd_node->BaseURLs) != NULL) {
+ baseURL = g_list_nth_data (list, stream->mpd_baseURL_idx);
+ if (!baseURL) {
+ baseURL = list->data;
+ }
+ baseURL_array[0] = baseURL->baseURL;
+ }
+ if ((list = stream_period->period->BaseURLs) != NULL) {
+ baseURL = g_list_nth_data (list, stream->period_baseURL_idx);
+ if (!baseURL) {
+ baseURL = list->data;
+ }
+ baseURL_array[1] = baseURL->baseURL;
+ }
+ if ((list = stream->cur_adapt_set->BaseURLs) != NULL) {
+ baseURL = g_list_nth_data (list, stream->adaptset_baseURL_idx);
+ if (!baseURL) {
+ baseURL = list->data;
+ }
+ baseURL_array[2] = baseURL->baseURL;
+ }
+ if ((list = stream->cur_representation->BaseURLs) != NULL) {
+ baseURL = g_list_nth_data (list, stream->repr_baseURL_idx);
+ if (!baseURL) {
+ baseURL = list->data;
+ }
+ baseURL_array[3] = baseURL->baseURL;
+ }
+
+ ret = g_strjoinv (NULL, baseURL_array);
+ /* get base URI from MPD file URI, if the "http" scheme is missing */
+ if (client->mpd_uri != NULL && strncmp (ret, "http://", 7) != 0) {
+ gchar *last_sep, *tmp1, *tmp2;
+ last_sep = strrchr (client->mpd_uri, '/');
+ if (last_sep) {
+ tmp1 = g_strndup (client->mpd_uri, last_sep - client->mpd_uri + 1);
+ tmp2 = ret;
+ GST_DEBUG ("Got base URI from MPD file URI %s", tmp1);
+ ret = g_strconcat (tmp1, tmp2, NULL);
+ g_free (tmp1);
+ g_free (tmp2);
+ }
+ }
+
+ return ret;
+}
+
+static GstClockTime
+gst_mpd_client_get_segment_duration (GstMpdClient * client, GstActiveStream *stream)
+{
+ GstStreamPeriod *stream_period;
+ GstMultSegmentBaseType *base = NULL;
+ GstClockTime duration;
+ guint timescale;
+
+ g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
+
+ if (stream->cur_segment_list) {
+ base = stream->cur_segment_list->MultSegBaseType;
+ } else if (stream->cur_seg_template) {
+ base = stream->cur_seg_template->MultSegBaseType;
+ }
+
+ if (base == NULL || base->SegBaseType == NULL) {
+ /* this may happen when we have a single segment */
+ duration = stream_period->duration;
+ } else {
+ duration = base->duration * GST_SECOND;
+ timescale = base->SegBaseType->timescale;
+
+ if (timescale > 1)
+ duration /= timescale;
+ }
+
+ return duration;
+}
+
+/*****************************/
+/******* API functions *******/
+/*****************************/
+
+GstMpdClient *
+gst_mpd_client_new ()
+{
+ GstMpdClient *client;
+
+ client = g_new0 (GstMpdClient, 1);
+ client->lock = g_mutex_new ();
+
+ client->download_failed_count = 0;
+
+ return client;
+}
+
+void
+gst_active_streams_free (GstMpdClient * client)
+{
+ if (client->active_streams) {
+ g_list_foreach (client->active_streams,
+ (GFunc) gst_mpdparser_free_active_stream, NULL);
+ g_list_free (client->active_streams);
+ client->active_streams = NULL;
+ }
+}
+
+void
+gst_mpd_client_free (GstMpdClient * client)
+{
+ g_return_if_fail (client != NULL);
+
+ if (client->mpd_node)
+ gst_mpdparser_free_mpd_node (client->mpd_node);
+
+ if (client->periods) {
+ g_list_foreach (client->periods,
+ (GFunc) gst_mpdparser_free_stream_period, NULL);
+ g_list_free (client->periods);
+ }
+
+ gst_active_streams_free (client);
+
+ if (client->lock)
+ g_mutex_free (client->lock);
+
+ g_free (client->mpd_uri);
+
+ g_free (client);
+}
+
+gboolean
+gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
+{
+ if (data) {
+ xmlDocPtr doc;
+ xmlNode *root_element = NULL;
+
+ GST_DEBUG ("MPD file fully buffered, start parsing...");
+
+ GST_MPD_CLIENT_LOCK (client);
+ /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
+
+ /* this initialize the library and check potential ABI mismatches
+ * between the version it was compiled for and the actual shared
+ * library used
+ */
+ LIBXML_TEST_VERSION
+ /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
+ doc = xmlReadMemory (data, size, "noname.xml", NULL, 0);
+ if (doc == NULL) {
+ GST_ERROR ("failed to parse the MPD file");
+ GST_MPD_CLIENT_UNLOCK (client);
+ return FALSE;
+ } else {
+ /* get the root element node */
+ root_element = xmlDocGetRootElement (doc);
+
+ if (root_element->type != XML_ELEMENT_NODE
+ || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
+ GST_ERROR
+ ("can not find the root element MPD, failed to parse the MPD file");
+ } else {
+ /* now we can parse the MPD root node and all children nodes, recursively */
+ gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
+ }
+ /* free the document */
+ xmlFreeDoc (doc);
+ /* dump XML library memory for debugging */
+ xmlMemoryDump ();
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const gchar *
+gst_mpdparser_get_baseURL (GstMpdClient * client)
+{
+ GstActiveStream *stream;
+
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (client->active_streams != NULL, NULL);
+ stream = g_list_nth_data (client->active_streams, client->stream_idx);
+ g_return_val_if_fail (stream != NULL, NULL);
+
+ return stream->baseURL;
+}
+
+gboolean
+gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream,
+ guint indexChunk, GstMediaSegment *segment)
+{
+ GstActiveStream *stream;
+
+ /* select stream */
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (segment != NULL, FALSE);
+ g_return_val_if_fail (client->active_streams != NULL, FALSE);
+ stream = g_list_nth_data (client->active_streams, indexStream);
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ if(stream->segments) {
+ GstMediaSegment *list_segment;
+ if ( indexChunk >= g_list_length (stream->segments))
+ return FALSE;
+ list_segment = g_list_nth_data (stream->segments, indexChunk);
+ memcpy (segment, list_segment, sizeof (GstMediaSegment));
+ } else {
+ GstClockTime duration;
+ GstStreamPeriod *stream_period;
+ GstClockTime start_time;
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->SegmentTimeline == NULL, FALSE);
+ g_return_val_if_fail (stream_period != NULL, FALSE);
+ g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+ duration = gst_mpd_client_get_segment_duration (client, stream);
+ if (!GST_CLOCK_TIME_IS_VALID (duration))
+ return FALSE;
+
+ start_time = duration * indexChunk;
+ if (GST_CLOCK_TIME_IS_VALID (stream_period->start) && GST_CLOCK_TIME_IS_VALID (stream_period->duration)
+ && start_time >= stream_period->start + stream_period->duration)
+ return FALSE;
+
+ segment->number = indexChunk + stream->cur_seg_template->MultSegBaseType->startNumber;
+ segment->start_time = start_time;
+ segment->duration = duration;
+ segment->SegmentURL = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpd_client_add_media_segment (GstActiveStream * stream,
+ GstSegmentURLNode * url_node, guint number, guint start,
+ GstClockTime start_time, GstClockTime duration)
+{
+ GstMediaSegment *media_segment;
+
+ media_segment = g_slice_new0 (GstMediaSegment);
+ if (media_segment == NULL) {
+ GST_WARNING ("Allocation of GstMediaSegment struct failed!");
+ return FALSE;
+ }
+ stream->segments = g_list_append (stream->segments, media_segment);
+ media_segment->SegmentURL = url_node;
+ media_segment->number = number;
+ media_segment->start = start;
+ media_segment->start_time = start_time;
+ media_segment->duration = duration;
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_setup_representation (GstMpdClient * client,
+ GstActiveStream * stream, GstRepresentationNode * representation)
+{
+ GstStreamPeriod *stream_period;
+ GList *rep_list;
+ GstClockTime PeriodStart, PeriodEnd, start_time, duration;
+ GstMediaSegment *last_media_segment;
+ guint i, start;
+
+ if (stream->cur_adapt_set == NULL) {
+ GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
+ return FALSE;
+ }
+
+ rep_list = stream->cur_adapt_set->Representations;
+ stream->cur_representation = representation;
+ stream->representation_idx = g_list_index (rep_list, representation);
+
+ /* clean the old segment list, if any */
+ if (stream->segments) {
+ g_list_foreach (stream->segments,
+ (GFunc) gst_mpdparser_free_media_segment, NULL);
+ g_list_free (stream->segments);
+ stream->segments = NULL;
+ }
+
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, FALSE);
+ g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+ PeriodStart = stream_period->start;
+ if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
+ PeriodEnd = stream_period->start + stream_period->duration;
+ else
+ PeriodEnd = GST_CLOCK_TIME_NONE;
+
+ GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
+
+ if (representation->SegmentBase != NULL
+ || representation->SegmentList != NULL) {
+ GList *SegmentURL;
+
+ /* get the first segment_base of the selected representation */
+ if ((stream->cur_segment_base =
+ gst_mpdparser_get_segment_base (stream_period->period,
+ stream->cur_adapt_set, representation)) == NULL) {
+ GST_DEBUG ("No useful SegmentBase node for the current Representation");
+ }
+
+ /* get the first segment_list of the selected representation */
+ if ((stream->cur_segment_list =
+ gst_mpdparser_get_segment_list (stream_period->period,
+ stream->cur_adapt_set, representation)) == NULL) {
+ GST_DEBUG ("No useful SegmentList node for the current Representation");
+ /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
+ if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, PeriodStart,
+ PeriodEnd)) {
+ return FALSE;
+ }
+ } else {
+ /* build the list of GstMediaSegment nodes from the SegmentList node */
+ SegmentURL = stream->cur_segment_list->SegmentURL;
+ if (SegmentURL == NULL) {
+ GST_WARNING
+ ("No valid list of SegmentURL nodes in the MPD file, aborting...");
+ return FALSE;
+ }
+
+ /* build segment list */
+ i = stream->cur_segment_list->MultSegBaseType->startNumber;
+ start = 0;
+ start_time = PeriodStart;
+
+ GST_LOG ("Building media segment list using a SegmentList node");
+ if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
+ GstSegmentTimelineNode *timeline;
+ GstSNode *S;
+ GList *list;
+
+ timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
+ for (list = g_list_first (timeline->S); list; list = g_list_next (list)) {
+ guint j, timescale;
+
+ S = (GstSNode *) list->data;
+ GST_LOG ("Processing S node: d=%d r=%d t=%d", S->d, S->r, S->t);
+ duration = S->d * GST_SECOND;
+ timescale =
+ stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
+ if (timescale > 1)
+ duration /= timescale;
+ if (S->t > 0) {
+ start = S->t;
+ start_time = S->t * GST_SECOND;
+ if (timescale > 1)
+ start_time /= timescale;
+ }
+
+ for (j = 0; j <= S->r && SegmentURL != NULL; j++) {
+ if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
+ start, start_time, duration)) {
+ return FALSE;
+ }
+ i++;
+ start += S->d;
+ start_time += duration;
+ SegmentURL = g_list_next (SegmentURL);
+ }
+ }
+ } else {
+ duration =gst_mpd_client_get_segment_duration (client, stream);
+ if (!GST_CLOCK_TIME_IS_VALID (duration))
+ return FALSE;
+
+ while (SegmentURL) {
+ if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i, 0,
+ start_time, duration)) {
+ return FALSE;
+ }
+ i++;
+ start_time += duration;
+ SegmentURL = g_list_next (SegmentURL);
+ }
+ }
+ }
+ } else {
+ if (representation->SegmentTemplate != NULL) {
+ stream->cur_seg_template = representation->SegmentTemplate;
+ } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
+ stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
+ } else if (stream_period->period->SegmentTemplate != NULL) {
+ stream->cur_seg_template = stream_period->period->SegmentTemplate;
+ }
+
+ if (stream->cur_seg_template == NULL
+ || stream->cur_seg_template->MultSegBaseType == NULL) {
+ /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
+ if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0, PeriodEnd)) {
+ return FALSE;
+ }
+ } else {
+ /* build segment list */
+ i = stream->cur_seg_template->MultSegBaseType->startNumber;
+ start = 0;
+ start_time = PeriodStart;
+
+ GST_LOG ("Building media segment list using this template: %s",
+ stream->cur_seg_template->media);
+ if (stream->cur_seg_template->MultSegBaseType->SegmentTimeline) {
+ GstSegmentTimelineNode *timeline;
+ GstSNode *S;
+ GList *list;
+
+ timeline = stream->cur_seg_template->MultSegBaseType->SegmentTimeline;
+ for (list = g_list_first (timeline->S); list; list = g_list_next (list)) {
+ guint j, timescale;
+
+ S = (GstSNode *) list->data;
+ GST_LOG ("Processing S node: d=%d r=%d t=%d", S->d, S->r, S->t);
+ duration = S->d * GST_SECOND;
+ timescale =
+ stream->cur_seg_template->MultSegBaseType->SegBaseType->timescale;
+ if (timescale > 1)
+ duration /= timescale;
+ if (S->t > 0) {
+ start = S->t;
+ start_time = S->t * GST_SECOND;
+ if (timescale > 1)
+ start_time /= timescale;
+ }
+
+ for (j = 0; j <= S->r; j++) {
+ if (!gst_mpd_client_add_media_segment (stream, NULL, i, start,
+ start_time, duration)) {
+ return FALSE;
+ }
+ i++;
+ start += S->d;
+ start_time += duration;
+ }
+ }
+ } else {
+ /*The segment is created on demand with the template, no need to build a list */
+ }
+ }
+ }
+
+ /* check duration of last segment */
+ if(stream->segments)
+ last_media_segment = g_list_last (stream->segments)->data;
+ else
+ last_media_segment = NULL;
+ if (last_media_segment && GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
+ if (last_media_segment->start_time + last_media_segment->duration >
+ PeriodEnd) {
+ last_media_segment->duration = PeriodEnd - last_media_segment->start_time;
+ GST_LOG ("Fixed duration of last segment: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (last_media_segment->duration));
+ }
+ GST_LOG ("Built a list of %d segments", last_media_segment->number);
+ }
+
+ g_free (stream->baseURL);
+ stream->baseURL = gst_mpdparser_parse_baseURL (client);
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_setup_media_presentation (GstMpdClient * client)
+{
+ GstStreamPeriod *stream_period;
+ GstPeriodNode *period_node;
+ GstClockTime start, duration;
+ GList *list, *next;
+ guint idx;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->mpd_node != NULL, FALSE);
+
+ GST_DEBUG ("Building the list of Periods in the Media Presentation");
+ GST_MPD_CLIENT_LOCK (client);
+ /* clean the old period list, if any */
+ if (client->periods) {
+ g_list_foreach (client->periods,
+ (GFunc) gst_mpdparser_free_stream_period, NULL);
+ g_list_free (client->periods);
+ client->periods = NULL;
+ }
+
+ idx = 0;
+ start = 0;
+ duration = GST_CLOCK_TIME_NONE;
+ for (list = g_list_first (client->mpd_node->Periods); list;
+ list = g_list_next (list)) {
+ period_node = (GstPeriodNode *) list->data;
+ if (period_node->start != -1) {
+ /* we have a regular period */
+ start = period_node->start * GST_MSECOND;
+ } else if (duration != GST_CLOCK_TIME_NONE) {
+ /* start time inferred from previous period, this is still a regular period */
+ start += duration;
+ } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
+ /* first period of a static MPD file, start time is 0 */
+ start = 0;
+ } else if (gst_mpd_client_is_live (client)) {
+ /*it should be a live stream, let this pass.*/
+ } else {
+ /* this is an 'Early Available Period' */
+ goto early;
+ }
+
+ if (period_node->duration != -1) {
+ duration = period_node->duration * GST_MSECOND;
+ } else if ((next = g_list_next (list)) != NULL) {
+ /* try to infer this period duration from the start time of the next period */
+ GstPeriodNode *next_period_node = next->data;
+ if (next_period_node->start != -1) {
+ duration = next_period_node->start * GST_MSECOND - start;
+ } else {
+ /* Invalid MPD file! */
+ goto syntax_error;
+ }
+ } else if (client->mpd_node->mediaPresentationDuration != -1) {
+ /* last Period of the Media Presentation */
+ duration =
+ client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
+ } else if (gst_mpd_client_is_live (client)) {
+ /*it should be a live stream, let this pass.*/
+ } else {
+ /* Invalid MPD file! */
+ goto syntax_error;
+ }
+
+ stream_period = g_slice_new0 (GstStreamPeriod);
+ if (stream_period == NULL) {
+ goto no_mem;
+ }
+ client->periods = g_list_append (client->periods, stream_period);
+ stream_period->period = period_node;
+ stream_period->number = idx++;
+ stream_period->start = start;
+ stream_period->duration = duration;
+ ret = TRUE;
+ GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
+ GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
+ }
+
+ GST_MPD_CLIENT_UNLOCK (client);
+ GST_DEBUG ("Found a total of %d valid Periods in the Media Presentation",
+ idx);
+ return ret;
+
+early:
+ GST_MPD_CLIENT_UNLOCK (client);
+ GST_WARNING
+ ("Found an Early Available Period, skipping the rest of the Media Presentation");
+ return ret;
+
+syntax_error:
+ GST_MPD_CLIENT_UNLOCK (client);
+ GST_WARNING
+ ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
+ idx);
+ return ret;
+
+no_mem:
+ GST_MPD_CLIENT_UNLOCK (client);
+ GST_WARNING ("Allocation of GstStreamPeriod struct failed!");
+ return FALSE;
+}
+
+gboolean
+gst_mpd_client_setup_streaming (GstMpdClient * client,
+ GstStreamMimeType mimeType, gchar * lang)
+{
+ GstActiveStream *stream;
+ GstStreamPeriod *stream_period;
+ GstAdaptationSetNode *adapt_set;
+ GstRepresentationNode *representation;
+ GList *rep_list = NULL;
+
+ stream_period = gst_mpdparser_get_stream_period (client);
+ if (stream_period == NULL || stream_period->period == NULL) {
+ GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
+ return FALSE;
+ }
+
+ switch (mimeType) {
+ case GST_STREAM_VIDEO:
+ /* select the adaptation set for the video pipeline */
+ adapt_set =
+ gst_mpdparser_get_adapt_set_with_mimeType_and_idx (stream_period->
+ period->AdaptationSets, "video", 0);
+ if (!adapt_set) {
+ GST_INFO ("No video adaptation set found");
+ return FALSE;
+ }
+ /* retrive the list of representations */
+ rep_list = adapt_set->Representations;
+ if (!rep_list) {
+ GST_WARNING ("Can not retrieve any representation, aborting...");
+ return FALSE;
+ }
+ break;
+ case GST_STREAM_AUDIO:
+ adapt_set =
+ gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang
+ (stream_period->period->AdaptationSets, "audio", lang);
+ /* if we did not found the requested audio language, get the first one */
+ if (!adapt_set)
+ adapt_set =
+ gst_mpdparser_get_first_adapt_set_with_mimeType (stream_period->
+ period->AdaptationSets, "audio");
+ if (!adapt_set) {
+ GST_INFO ("No audio adaptation set found");
+ return FALSE;
+ }
+ rep_list = adapt_set->Representations;
+ if (!rep_list) {
+ GST_WARNING ("Can not retrieve any representation, aborting...");
+ return FALSE;
+ }
+ break;
+ case GST_STREAM_APPLICATION:
+ adapt_set =
+ gst_mpdparser_get_first_adapt_set_with_mimeType_and_lang
+ (stream_period->period->AdaptationSets, "application", lang);
+ /* if we did not found the requested subtitles language, get the first one */
+ if (!adapt_set)
+ adapt_set =
+ gst_mpdparser_get_first_adapt_set_with_mimeType (stream_period->
+ period->AdaptationSets, "application");
+ if (!adapt_set) {
+ GST_INFO ("No application adaptation set found");
+ return FALSE;
+ }
+ rep_list = adapt_set->Representations;
+ if (!rep_list) {
+ GST_WARNING ("Can not retrieve any representation, aborting...");
+ return FALSE;
+ }
+ break;
+ default:
+ GST_WARNING ("Unsupported mimeType %d", mimeType);
+ return FALSE;
+ }
+
+ stream = g_slice_new0 (GstActiveStream);
+ if (stream == NULL) {
+ GST_WARNING ("Allocation of active stream struct failed!");
+ return FALSE;
+ }
+ client->active_streams = g_list_append (client->active_streams, stream);
+
+ stream->mpd_baseURL_idx = 0;
+ stream->period_baseURL_idx = 0;
+ stream->adaptset_baseURL_idx = 0;
+ stream->repr_baseURL_idx = 0;
+
+ stream->mimeType = mimeType;
+ stream->cur_adapt_set = adapt_set;
+
+ /* retrive representation list */
+ if (stream->cur_adapt_set != NULL)
+ rep_list = stream->cur_adapt_set->Representations;
+#if 0
+ /* fast start */
+ representation =
+ gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
+ stream->max_bandwidth);
+
+ if (!representation) {
+ GST_WARNING
+ ("Can not retrieve a representation with the requested bandwidth");
+ representation = gst_mpdparser_get_lowest_representation (rep_list);
+ }
+#else
+ /* slow start */
+ representation = gst_mpdparser_get_lowest_representation (rep_list);
+#endif
+
+ if (!representation) {
+ GST_WARNING ("No valid representation in the MPD file, aborting...");
+ return FALSE;
+ }
+
+ if (!gst_mpd_client_setup_representation (client, stream, representation))
+ return FALSE;
+
+ GST_INFO ("Successfully setup the download pipeline for mimeType %d",
+ mimeType);
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
+ guint stream_idx, GstClockTime * ts)
+{
+ GstActiveStream *stream;
+ gint segment_idx;
+ GstMediaSegment currentChunk;
+
+ GST_DEBUG ("Stream index: %i", stream_idx);
+ stream = g_list_nth_data (client->active_streams, stream_idx);
+ g_return_val_if_fail (stream != NULL, 0);
+
+ GST_MPD_CLIENT_LOCK (client);
+ segment_idx = gst_mpd_client_get_segment_index (stream);
+ GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
+
+
+ if (!gst_mpdparser_get_chunk_by_index (client, stream_idx, segment_idx, ¤tChunk)) {
+ GST_MPD_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+
+ *ts = currentChunk.start_time;
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_get_last_fragment_timestamp (GstMpdClient * client,
+ guint stream_idx, GstClockTime * ts)
+{
+ GstActiveStream *stream;
+ gint segment_idx = 0;
+ GstMediaSegment currentChunk;
+
+ GST_DEBUG ("Stream index: %i", stream_idx);
+ stream = g_list_nth_data (client->active_streams, stream_idx);
+ g_return_val_if_fail (stream != NULL, 0);
+
+ GST_MPD_CLIENT_LOCK (client);
+ if (stream->segments)
+ segment_idx = g_list_length (stream->segments) - 1;
+ GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
+
+ if (!gst_mpdparser_get_chunk_by_index (client, stream_idx, segment_idx,
+ ¤tChunk)) {
+ GST_MPD_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+
+ *ts = currentChunk.start_time;
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_get_next_fragment (GstMpdClient * client,
+ guint indexStream, gboolean * discontinuity, gchar ** uri,
+ GstClockTime * duration, GstClockTime * timestamp)
+{
+ GstActiveStream *stream = NULL;
+ GstMediaSegment currentChunk;
+ gchar *mediaURL = NULL;
+ guint segment_idx;
+
+ /* select stream */
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->active_streams != NULL, FALSE);
+ stream = g_list_nth_data (client->active_streams, indexStream);
+ g_return_val_if_fail (stream != NULL, FALSE);
+ g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
+ g_return_val_if_fail (discontinuity != NULL, FALSE);
+
+ GST_MPD_CLIENT_LOCK (client);
+ segment_idx = gst_mpd_client_get_segment_index (stream);
+ GST_DEBUG ("Looking for fragment sequence chunk %d", segment_idx);
+
+ if (!gst_mpdparser_get_chunk_by_index (client, indexStream, segment_idx, ¤tChunk)) {
+ GST_MPD_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+
+ if (currentChunk.SegmentURL != NULL) {
+ mediaURL = gst_mpdparser_get_mediaURL (client, currentChunk.SegmentURL);
+ } else if (stream->cur_seg_template != NULL) {
+ mediaURL =
+ gst_mpdparser_build_URL_from_template (stream->cur_seg_template->media,
+ stream->cur_representation->id, currentChunk.number,
+ stream->cur_representation->bandwidth, currentChunk.start);
+ }
+
+ *timestamp = currentChunk.start_time;
+ *duration = currentChunk.duration;
+ *discontinuity = segment_idx != currentChunk.number;
+ if (mediaURL == NULL) {
+ /* single segment with URL encoded in the baseURL syntax element */
+ *uri = g_strdup (gst_mpdparser_get_baseURL (client));
+ } else if (strncmp (mediaURL, "http://", 7) != 0) {
+ *uri = g_strconcat (gst_mpdparser_get_baseURL (client), mediaURL, NULL);
+ g_free (mediaURL);
+ } else {
+ *uri = mediaURL;
+ }
+ gst_mpd_client_set_segment_index (stream, segment_idx + 1);
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ GST_DEBUG ("Loading chunk with URL %s", *uri);
+
+ return TRUE;
+}
+
+gboolean
+gst_mpd_client_get_next_header (GstMpdClient * client, const gchar ** uri,
+ guint stream_idx)
+{
+ GstActiveStream *stream;
+ GstStreamPeriod *stream_period;
+
+ stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
+ g_return_val_if_fail (stream != NULL, FALSE);
+ g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, FALSE);
+ g_return_val_if_fail (stream_period->period != NULL, FALSE);
+
+ GST_DEBUG ("Looking for current representation header");
+ GST_MPD_CLIENT_LOCK (client);
+ *uri = NULL;
+ if (stream->cur_segment_base && stream->cur_segment_base->Initialization) {
+
+ if (stream->cur_segment_base->Initialization->sourceURL == NULL) {
+ stream->cur_segment_base->Initialization->sourceURL = gst_mpdparser_get_baseURL (client);
+ }//if not present initialization @sourceURL attribute, it's mapped to baseURL
+
+ *uri =
+ gst_mpdparser_get_initializationURL (stream->cur_segment_base->
+ Initialization);
+ } else if (stream->cur_seg_template) {
+ const gchar *initialization = NULL;
+ if (stream->cur_seg_template->initialization) {
+ initialization = stream->cur_seg_template->initialization;
+ } else if (stream->cur_adapt_set->SegmentTemplate
+ && stream->cur_adapt_set->SegmentTemplate->initialization) {
+ initialization = stream->cur_adapt_set->SegmentTemplate->initialization;
+ } else if (stream_period->period->SegmentTemplate
+ && stream_period->period->SegmentTemplate->initialization) {
+ initialization = stream_period->period->SegmentTemplate->initialization;
+ }
+ *uri = gst_mpdparser_build_URL_from_template (initialization,
+ stream->cur_representation->id, 0,
+ stream->cur_representation->bandwidth, 0);
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return *uri == NULL ? FALSE : TRUE;
+}
+
+gboolean
+gst_mpd_client_get_stream_current_position (GstMpdClient * client, guint stream_idx, GstClockTime *ts)
+{
+ GstActiveStream *stream;
+ GstMediaSegment media_segment;
+ stream = gst_mpdparser_get_active_stream_by_index( client, stream_idx);
+ g_return_val_if_fail (stream != NULL, FALSE);
+ if(!gst_mpdparser_get_chunk_by_index(client, stream_idx,
+ gst_mpd_client_get_segment_index (stream), &media_segment))
+ {
+ return FALSE;
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (media_segment.start_time)) {
+ *ts = media_segment.start_time;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+GstClockTime
+gst_mpd_client_get_next_fragment_duration (GstMpdClient * client)
+{
+ GstActiveStream *stream;
+ GstMediaSegment *media_segment;
+ gint seg_idx;
+
+ stream = g_list_nth_data (client->active_streams, client->stream_idx);
+ g_return_val_if_fail (stream != NULL, 0);
+ seg_idx = gst_mpd_client_get_segment_index (stream);
+
+ if(stream->segments) {
+ media_segment =
+ g_list_nth_data (stream->segments, seg_idx);
+ return media_segment == NULL ? 0 : media_segment->duration;
+ } else {
+ GstClockTime duration =
+ gst_mpd_client_get_segment_duration (client, stream);
+ g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
+ SegmentTimeline == NULL, 0);
+
+ if (GST_CLOCK_TIME_IS_VALID (duration))
+ return duration;
+ return 0;
+ }
+}
+
+GstClockTime
+gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
+{
+ GstClockTime duration;
+
+ g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+
+ GST_MPD_CLIENT_LOCK (client);
+ if (client->mpd_node->mediaPresentationDuration != -1) {
+ duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
+ } else {
+ /* We can only get the duration for on-demand streams */
+ duration = GST_CLOCK_TIME_NONE;
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return duration;
+}
+
+gboolean
+gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
+{
+ GstStreamPeriod *next_stream_period;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->periods != NULL, FALSE);
+
+ GST_MPD_CLIENT_LOCK (client);
+ next_stream_period = g_list_nth_data (client->periods, period_idx);
+ if (next_stream_period != NULL) {
+ client->period_idx = period_idx;
+ ret = TRUE;
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return ret;
+}
+
+guint
+gst_mpd_client_get_period_index (GstMpdClient * client)
+{
+ guint period_idx;
+
+ g_return_val_if_fail (client != NULL, 0);
+ GST_MPD_CLIENT_LOCK (client);
+ period_idx = client->period_idx;
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return period_idx;
+}
+
+gboolean
+gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
+{
+ GstStreamPeriod *next_stream_period;
+ gboolean ret = FALSE;
+ GList *iter;
+ gint index = 0;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->periods != NULL, FALSE);
+ g_return_val_if_fail (period_id != NULL, FALSE);
+
+ GST_MPD_CLIENT_LOCK (client);
+ for (iter = client->periods; iter; iter = g_list_next (iter)) {
+ next_stream_period = iter->data;
+
+ if (next_stream_period->period->id
+ && strcmp (next_stream_period->period->id, period_id) == 0) {
+ client->period_idx = index;
+ ret = TRUE;
+ break;
+ }
+ index++;
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return ret;
+}
+
+const gchar *
+gst_mpd_client_get_period_id (GstMpdClient * client)
+{
+ GstStreamPeriod *period;
+ gchar *period_id = NULL;
+
+ g_return_val_if_fail (client != NULL, 0);
+ GST_MPD_CLIENT_LOCK (client);
+ period = g_list_nth_data (client->periods, client->period_idx);
+ if (period && period->period)
+ period_id = period->period->id;
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return period_id;
+}
+
+gboolean
+gst_mpd_client_has_next_period (GstMpdClient * client)
+{
+ GList *next_stream_period;
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->periods != NULL, FALSE);
+
+ GST_MPD_CLIENT_LOCK (client);
+ next_stream_period =
+ g_list_nth_data (client->periods, client->period_idx + 1);
+ GST_MPD_CLIENT_UNLOCK (client);
+
+ return next_stream_period != NULL;
+}
+
+void
+gst_mpd_client_set_segment_index_for_all_streams (GstMpdClient * client,
+ guint segment_idx)
+{
+ GList *list;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (client->active_streams != NULL);
+
+ /* FIXME: support multiple streams with different segment duration */
+ for (list = g_list_first (client->active_streams); list;
+ list = g_list_next (list)) {
+ GstActiveStream *stream = (GstActiveStream *) list->data;
+ if (stream) {
+ stream->segment_idx = segment_idx;
+ }
+ }
+}
+
+gboolean
+gst_mpd_client_set_next_baseURL_for_stream (GstMpdClient * client)
+{
+ GstActiveStream *stream;
+ GstStreamPeriod *stream_period;
+ GList *list;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->active_streams != NULL, FALSE);
+
+ stream = g_list_nth_data (client->active_streams,
+ client->stream_idx);
+ g_return_val_if_fail (stream != NULL, FALSE);
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, FALSE);
+
+ if ((list = stream->cur_representation->BaseURLs) != NULL &&
+ stream->repr_baseURL_idx < g_list_length (list) - 1) {
+ stream->repr_baseURL_idx++;
+ } else if ((list = stream->cur_adapt_set->BaseURLs) != NULL &&
+ stream->adaptset_baseURL_idx < g_list_length (list) - 1) {
+ stream->adaptset_baseURL_idx++;
+ stream->repr_baseURL_idx = 0;
+ } else if ((list = stream_period->period->BaseURLs) != NULL &&
+ stream->period_baseURL_idx < g_list_length (list) - 1) {
+ stream->period_baseURL_idx++;
+ stream->adaptset_baseURL_idx = 0;
+ stream->repr_baseURL_idx = 0;
+ } else if ((list = client->mpd_node->BaseURLs) != NULL &&
+ stream->mpd_baseURL_idx < g_list_length (list) - 1) {
+ stream->mpd_baseURL_idx++;
+ stream->period_baseURL_idx = 0;
+ stream->adaptset_baseURL_idx = 0;
+ stream->repr_baseURL_idx = 0;
+ } else {
+ GST_WARNING ("Unable to change URI: No alternative baseURL available");
+ return FALSE;
+ }
+
+ stream->baseURL = gst_mpdparser_parse_baseURL (client);
+
+ return TRUE;
+}
+
+void
+gst_mpd_client_set_segment_index (GstActiveStream * stream, guint segment_idx)
+{
+ g_return_if_fail (stream != NULL);
+
+ stream->segment_idx = segment_idx;
+}
+
+guint
+gst_mpd_client_get_segment_index (GstActiveStream * stream)
+{
+ g_return_val_if_fail (stream != NULL, 0);
+
+ return stream->segment_idx;
+}
+
+gboolean
+gst_mpd_client_is_live (GstMpdClient * client)
+{
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->mpd_node != NULL, FALSE);
+
+ return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
+}
+
+guint
+gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
+{
+ g_return_val_if_fail (client != NULL, 0);
+
+ return g_list_length (client->active_streams);
+}
+
+guint
+gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
+{
+ GstStreamPeriod *stream_period;
+
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, 0);
+ g_return_val_if_fail (stream_period->period != NULL, 0);
+
+ return g_list_length (stream_period->period->AdaptationSets);
+}
+
+GstActiveStream *
+gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
+ guint stream_idx)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (client->active_streams != NULL, NULL);
+
+ return g_list_nth_data (client->active_streams, stream_idx);
+}
+
+gint
+gst_mpd_client_get_video_active_stream_id(GstMpdClient *client) {
+ guint idx;
+ g_return_val_if_fail (client != NULL, -1);
+ g_return_val_if_fail (client->active_streams != NULL, -1);
+ for (idx = 0; idx < gst_mpdparser_get_nb_active_stream(client); idx++) {
+ GstActiveStream *stream = gst_mpdparser_get_active_stream_by_index(client, idx);
+ if( strncmp_ext (gst_mpd_client_get_stream_mimeType (stream), "video") == 0 )
+ return idx;
+ }
+ return -1;
+}
+
+gboolean
+gst_mpd_client_stream_seek (GstMpdClient * client, gint stream_idx, GstClockTime ts)
+{
+ guint segment_idx = 0;
+ GstActiveStream *stream;
+ GST_MPD_CLIENT_LOCK (client);
+ GST_DEBUG ("mpd client seeking to %"GST_TIME_FORMAT" on stream %d", GST_TIME_ARGS(ts), stream_idx);
+ stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ if ( GST_CLOCK_TIME_IS_VALID (gst_mpd_client_stream_find_segment (client,
+ stream_idx, ts, &segment_idx))) {
+ gst_mpd_client_set_segment_index(stream, segment_idx);
+ }
+ GST_MPD_CLIENT_UNLOCK (client);
+ return TRUE;
+}
+
+static GDateTime *
+_gst_date_time_to_g_date_time (GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, NULL);
+ return g_date_time_new_utc ( gst_date_time_get_year (datetime), gst_date_time_get_month(datetime),
+ gst_date_time_get_day(datetime), gst_date_time_get_hour(datetime),
+ gst_date_time_get_minute(datetime),gst_date_time_get_second(datetime));
+}
+
+gboolean
+gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
+{
+ GDateTime *start =
+ _gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
+ GTimeSpan ts_microseconds;
+ GstClockTime ts;
+ gboolean ret = TRUE;
+ gint i;
+
+ g_return_val_if_fail (gst_mpd_client_is_live (client), 0);
+
+ ts_microseconds = g_date_time_difference (time, start);
+ g_date_time_unref (start);
+
+ ts = ts_microseconds * GST_USECOND;
+ for (i=0; i < g_list_length (client->active_streams); i++) {
+ ret = ret & gst_mpd_client_stream_seek (client, i, ts);
+ }
+ return ret;
+}
+
+gint
+gst_mpd_client_check_time_position (GstMpdClient * client,
+ GstActiveStream * stream, GstClockTime ts, gint64 * diff)
+{
+ GDateTime *now = g_date_time_new_now_utc ();
+ GDateTime *start =
+ _gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
+ GTimeSpan stream_now;
+ GTimeSpan ts_microseconds;
+ GstClockTime duration;
+
+ g_return_val_if_fail (gst_mpd_client_is_live (client), 0);
+
+ duration = gst_mpd_client_get_segment_duration (client, stream);
+ stream_now = g_date_time_difference (now, start);
+ g_date_time_unref (now);
+ g_date_time_unref (start);
+
+ /* sum duration to check if the segment is fully ready */
+ ts_microseconds = (ts + duration) / GST_USECOND;
+
+ /*
+ * This functions checks if a given ts is in the 'available range' of
+ * a DASH presentation. This only makes sense for live streams, which
+ * are continuously adding new segments and removing old ones.
+ *
+ * Note: Both the client and the server should use UTC as a time reference.
+ *
+ * @ts is the time since the beginning of the stream and we need to find out
+ * if it is currently available. The server should be hosting segments
+ *
+ * * ---------------- ... --- * ----------- * ---- ...
+ * |
+ * | past(unavailable) | | available | future(unavailable yet)
+ * |
+ * * ---------------- ... --- * ----------- * ---- ...
+ * | | |
+ * availabilitStartTime | UTC now
+ * UTC now - timeShiftBufferDepth
+ *
+ * This function should return 0 if @ts is in the 'available' area, 1 for
+ * 'future' and '-1' for past and the corresponding distance to the
+ * 'available' area is set to @diff
+ *
+ * TODO untested with live presentations with multiple periods as no
+ * examples for it could be found/generated
+ */
+
+ if (ts_microseconds > stream_now) {
+ *diff = ts_microseconds - stream_now;
+ return 1;
+ }
+ if (GST_CLOCK_TIME_IS_VALID (client->mpd_node->timeShiftBufferDepth)
+ && ts_microseconds <
+ stream_now - client->mpd_node->timeShiftBufferDepth * GST_USECOND) {
+ *diff = ts_microseconds - stream_now;
+ return -1;
+ }
+
+ *diff = 0;
+ return 0;
+}
+
+/*Finds the segment with appropriate time*/
+GstClockTime
+gst_mpd_client_stream_find_segment(GstMpdClient *client, guint stream_idx,
+ GstClockTime seek_pos, guint *seg_num)
+{
+ guint cur_segment_idx=0;
+ GstMediaSegment *chunk;
+ GList *list = NULL;
+ GstClockTime current_pos = 0;
+ GstActiveStream *stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
+ g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
+ if(stream->segments != NULL) {
+ for (list = g_list_first (stream->segments); list;
+ list = g_list_next (list)) {
+ chunk = list->data;
+ current_pos = chunk->start_time;
+ if (current_pos <= seek_pos
+ && seek_pos < current_pos + chunk->duration) {
+ break;
+ }
+ cur_segment_idx++;
+ }
+ if(list != NULL) {
+ *seg_num = cur_segment_idx;
+ return current_pos;
+ } else {
+ return GST_CLOCK_TIME_NONE;
+ }
+ } else {
+ GstClockTime duration;
+ duration = gst_mpd_client_get_segment_duration (client, stream);
+ if( !GST_CLOCK_TIME_IS_VALID (duration) )
+ return FALSE;
+ *seg_num = seek_pos / duration;
+ return *seg_num * duration;
+ }
+}
+
+static const gchar *
+gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
+{
+ if (mimeType == NULL)
+ return NULL;
+ if (strcmp (mimeType, "video/mp2t") == 0) {
+ return "video/mpegts";
+ } else if (strcmp (mimeType, "video/mp4") == 0) {
+ return "video/quicktime";
+ } else if (strcmp (mimeType, "audio/mp4") == 0) {
+ return "audio/x-m4a";
+ } else
+ return mimeType;
+}
+
+const gchar *
+gst_mpd_client_get_stream_mimeType (GstActiveStream * stream)
+{
+ const gchar *mimeType;
+
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return NULL;
+
+ mimeType = stream->cur_representation->RepresentationBase->mimeType;
+ if (mimeType == NULL) {
+ mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
+ }
+
+ return gst_mpdparser_mimetype_to_caps (mimeType);
+}
+
+const gboolean
+gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
+{
+ if (stream == NULL || stream->cur_adapt_set == NULL)
+ return FALSE;
+
+ return stream->cur_adapt_set->bitstreamSwitching;
+}
+
+gboolean
+gst_mpd_client_get_max_video_dimensions (GstMpdClient *client, gint *max_width, gint *max_height)
+{
+ GList *period_it, *repr_it;
+ GstStreamPeriod *period;
+ GstAdaptationSetNode *adaptset;
+ GstRepresentationNode *repr;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (max_height != NULL, FALSE);
+ g_return_val_if_fail (max_width != NULL, FALSE);
+
+ *max_width = 0;
+ *max_height = 0;
+
+ period_it = client->periods;
+ while(period_it) {
+ period = period_it->data;
+ adaptset = gst_mpdparser_get_adapt_set_with_mimeType_and_idx (
+ period->period->AdaptationSets, "video", 0);
+ if(adaptset == NULL)
+ continue;
+ repr_it = adaptset->Representations;
+
+ if(adaptset->RepresentationBase->width > *max_width)
+ *max_width = adaptset->RepresentationBase->width;
+ if(adaptset->RepresentationBase->height > *max_height)
+ *max_height = adaptset->RepresentationBase->height;
+
+ while(repr_it) {
+ repr = repr_it->data;
+ if(repr->RepresentationBase->width > *max_width)
+ *max_width = repr->RepresentationBase->width;
+ if(repr->RepresentationBase->height > *max_height)
+ *max_height = repr->RepresentationBase->height;
+ repr_it = repr_it->next;
+ }
+
+ period_it = period_it->next;
+ }
+ if(*max_width == 0 && *max_height == 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+guint
+gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
+{
+ guint width;
+
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return 0;
+
+ width = stream->cur_representation->RepresentationBase->width;
+ if (width == 0) {
+ width = stream->cur_adapt_set->RepresentationBase->width;
+ }
+
+ return width;
+}
+
+guint
+gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
+{
+ guint height;
+
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return 0;
+
+ height = stream->cur_representation->RepresentationBase->height;
+ if (height == 0) {
+ height = stream->cur_adapt_set->RepresentationBase->height;
+ }
+
+ return height;
+}
+
+guint
+gst_mpd_client_get_video_stream_bandwidth (GstActiveStream * stream)
+{
+ guint bandwidth;
+
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return 0;
+
+ bandwidth = stream->cur_representation->RepresentationBase->bandwidth;
+ if (bandwidth == 0) {
+ bandwidth = stream->cur_adapt_set->RepresentationBase->bandwidth;
+ }
+
+ return bandwidth;
+}
+
+guint
+gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
+{
+ const gchar *rate;
+
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return 0;
+
+ rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
+ if (rate == NULL) {
+ rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
+ }
+
+ return rate ? atoi (rate) : 0;
+}
+
+guint
+gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
+{
+ if (stream == NULL || stream->cur_adapt_set == NULL
+ || stream->cur_representation == NULL)
+ return 0;
+ /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
+ return 0;
+}
+
+guint
+gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
+ GList ** lang)
+{
+ GstStreamPeriod *stream_period;
+ GstAdaptationSetNode *adapt_set;
+ GList *list;
+ gchar *this_mimeType = "audio";
+ gchar *mimeType = NULL;
+ guint nb_adapatation_set = 0;
+
+ stream_period = gst_mpdparser_get_stream_period (client);
+ g_return_val_if_fail (stream_period != NULL, 0);
+ g_return_val_if_fail (stream_period->period != NULL, 0);
+
+ for (list = g_list_first (stream_period->period->AdaptationSets); list;
+ list = g_list_next (list)) {
+ adapt_set = (GstAdaptationSetNode *) list->data;
+ if (adapt_set) {
+ gchar *this_lang = adapt_set->lang;
+ GstRepresentationNode *rep;
+ rep =
+ gst_mpdparser_get_lowest_representation (adapt_set->Representations);
+ if (!rep) {
+ GST_WARNING ("No valid representation in the MPD file, aborting...");
+ return 0;
+ }
+ if (rep->RepresentationBase)
+ mimeType = rep->RepresentationBase->mimeType;
+ if (!mimeType && adapt_set->RepresentationBase) {
+ mimeType = adapt_set->RepresentationBase->mimeType;
+ }
+
+ if (strncmp_ext (mimeType, this_mimeType) == 0) {
+ if (this_lang) {
+ nb_adapatation_set++;
+ *lang = g_list_append (*lang, this_lang);
+ }
+ }
+ }
+ }
+
+ return nb_adapatation_set;
+}
--- /dev/null
+/*
+ * DASH MPD parsing library
+ *
+ * gstmpdparser.h
+ *
+ * Copyright (C) 2012 STMicroelectronics
+ *
+ * Authors:
+ * Gianluca Gennari <gennarone@gmail.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.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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library (COPYING); if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_MPDPARSER_H__
+#define __GST_MPDPARSER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstMpdClient GstMpdClient;
+typedef struct _GstActiveStream GstActiveStream;
+typedef struct _GstStreamPeriod GstStreamPeriod;
+typedef struct _GstMediaSegment GstMediaSegment;
+typedef struct _GstMPDNode GstMPDNode;
+typedef struct _GstPeriodNode GstPeriodNode;
+typedef struct _GstRepresentationBaseType GstRepresentationBaseType;
+typedef struct _GstDescriptorType GstDescriptorType;
+typedef struct _GstContentComponentNode GstContentComponentNode;
+typedef struct _GstAdaptationSetNode GstAdaptationSetNode;
+typedef struct _GstRepresentationNode GstRepresentationNode;
+typedef struct _GstSubRepresentationNode GstSubRepresentationNode;
+typedef struct _GstSegmentListNode GstSegmentListNode;
+typedef struct _GstSegmentTemplateNode GstSegmentTemplateNode;
+typedef struct _GstSegmentURLNode GstSegmentURLNode;
+typedef struct _GstBaseURL GstBaseURL;
+typedef struct _GstRange GstRange;
+typedef struct _GstRatio GstRatio;
+typedef struct _GstFrameRate GstFrameRate;
+typedef struct _GstConditionalUintType GstConditionalUintType;
+typedef struct _GstSubsetNode GstSubsetNode;
+typedef struct _GstProgramInformationNode GstProgramInformationNode;
+typedef struct _GstMetricsRangeNode GstMetricsRangeNode;
+typedef struct _GstMetricsNode GstMetricsNode;
+typedef struct _GstSNode GstSNode;
+typedef struct _GstSegmentTimelineNode GstSegmentTimelineNode;
+typedef struct _GstSegmentBaseType GstSegmentBaseType;
+typedef struct _GstURLType GstURLType;
+typedef struct _GstMultSegmentBaseType GstMultSegmentBaseType;
+
+#define GST_MPD_CLIENT_LOCK(c) g_mutex_lock (c->lock);
+#define GST_MPD_CLIENT_UNLOCK(c) g_mutex_unlock (c->lock);
+
+typedef enum
+{
+ GST_STREAM_VIDEO, /* video stream (the main one) */
+ GST_STREAM_AUDIO, /* audio stream (optional) */
+ GST_STREAM_APPLICATION /* application stream (optional): for timed text/subtitles */
+} GstStreamMimeType;
+
+typedef enum
+{
+ GST_MPD_FILE_TYPE_STATIC,
+ GST_MPD_FILE_TYPE_DYNAMIC
+} GstMPDFileType;
+
+typedef enum
+{
+ GST_SAP_TYPE_0 = 0,
+ GST_SAP_TYPE_1,
+ GST_SAP_TYPE_2,
+ GST_SAP_TYPE_3,
+ GST_SAP_TYPE_4,
+ GST_SAP_TYPE_5,
+ GST_SAP_TYPE_6
+} GstSAPType;
+
+struct _GstBaseURL
+{
+ gchar *baseURL;
+ gchar *serviceLocation;
+ gchar *byteRange;
+};
+
+struct _GstRange
+{
+ guint64 first_byte_pos;
+ guint64 last_byte_pos;
+};
+
+struct _GstRatio
+{
+ guint num;
+ guint den;
+};
+
+struct _GstFrameRate
+{
+ guint num;
+ guint den;
+};
+
+struct _GstConditionalUintType
+{
+ gboolean flag;
+ guint value;
+};
+
+struct _GstSNode
+{
+ guint t;
+ guint d;
+ guint r;
+};
+
+struct _GstSegmentTimelineNode
+{
+ /* list of S nodes */
+ GList *S;
+};
+
+struct _GstURLType
+{
+ gchar *sourceURL;
+ GstRange *range;
+};
+
+struct _GstSegmentBaseType
+{
+ guint timescale;
+ guint presentationTimeOffset;
+ gchar *indexRange;
+ gboolean indexRangeExact;
+ /* Initialization node */
+ GstURLType *Initialization;
+ /* RepresentationIndex node */
+ GstURLType *RepresentationIndex;
+};
+
+struct _GstMultSegmentBaseType
+{
+ guint duration; /* in seconds */
+ guint startNumber;
+ /* SegmentBaseType extension */
+ GstSegmentBaseType *SegBaseType;
+ /* SegmentTimeline node */
+ GstSegmentTimelineNode *SegmentTimeline;
+ /* BitstreamSwitching node */
+ GstURLType *BitstreamSwitching;
+};
+
+struct _GstSegmentListNode
+{
+ /* extension */
+ GstMultSegmentBaseType *MultSegBaseType;
+ /* list of SegmentURL nodes */
+ GList *SegmentURL;
+};
+
+struct _GstSegmentTemplateNode
+{
+ /* extension */
+ GstMultSegmentBaseType *MultSegBaseType;
+ gchar *media;
+ gchar *index;
+ gchar *initialization;
+ gchar *bitstreamSwitching;
+};
+
+struct _GstSegmentURLNode
+{
+ gchar *media;
+ GstRange *mediaRange;
+ gchar *index;
+ GstRange *indexRange;
+};
+
+struct _GstRepresentationBaseType
+{
+ gchar *profiles;
+ guint width;
+ guint height;
+ guint bandwidth;
+ GstRatio *sar;
+ GstFrameRate *frameRate;
+ gchar *audioSamplingRate;
+ gchar *mimeType;
+ gchar *segmentProfiles;
+ gchar *codecs;
+ gdouble maximumSAPPeriod;
+ GstSAPType startWithSAP;
+ gdouble maxPlayoutRate;
+ gboolean codingDependency;
+ gchar *scanType;
+ /* list of FramePacking DescriptorType nodes */
+ GList *FramePacking;
+ /* list of AudioChannelConfiguration DescriptorType nodes */
+ GList *AudioChannelConfiguration;
+ /* list of ContentProtection DescriptorType nodes */
+ GList *ContentProtection;
+};
+
+struct _GstSubRepresentationNode
+{
+ /* RepresentationBase extension */
+ GstRepresentationBaseType *RepresentationBase;
+ guint level;
+ guint *dependencyLevel; /* UIntVectorType */
+ guint size; /* size of "dependencyLevel" array */
+ guint64 bandwidth;
+ gchar **contentComponent; /* StringVectorType */
+};
+
+struct _GstRepresentationNode
+{
+ gchar *id;
+ guint bandwidth;
+ guint qualityRanking;
+ gchar **dependencyId; /* StringVectorType */
+ gchar **mediaStreamStructureId; /* StringVectorType */
+ /* RepresentationBase extension */
+ GstRepresentationBaseType *RepresentationBase;
+ /* list of BaseURL nodes */
+ GList *BaseURLs;
+ /* list of SubRepresentation nodes */
+ GList *SubRepresentations;
+ /* SegmentBase node */
+ GstSegmentBaseType *SegmentBase;
+ /* SegmentTemplate node */
+ GstSegmentTemplateNode *SegmentTemplate;
+ /* SegmentList node */
+ GstSegmentListNode *SegmentList;
+};
+
+struct _GstDescriptorType
+{
+ gchar *schemeIdUri;
+ gchar *value;
+ /* For adding ContentProtection to caps , get string of <mspr:pro> */
+ gchar *msprPro;
+};
+
+struct _GstContentComponentNode
+{
+ guint id;
+ gchar *lang; /* LangVectorType RFC 5646 */
+ gchar *contentType;
+ GstRatio *par;
+ /* list of Accessibility DescriptorType nodes */
+ GList *Accessibility;
+ /* list of Role DescriptorType nodes */
+ GList *Role;
+ /* list of Rating DescriptorType nodes */
+ GList *Rating;
+ /* list of Viewpoint DescriptorType nodes */
+ GList *Viewpoint;
+};
+
+struct _GstAdaptationSetNode
+{
+ guint id;
+ guint group;
+ gchar *lang; /* LangVectorType RFC 5646 */
+ gchar *contentType;
+ GstRatio *par;
+ guint minBandwidth;
+ guint maxBandwidth;
+ guint minWidth;
+ guint maxWidth;
+ guint minHeight;
+ guint maxHeight;
+ GstFrameRate *minFrameRate;
+ GstFrameRate *maxFrameRate;
+ GstConditionalUintType *segmentAlignment;
+ GstConditionalUintType *subsegmentAlignment;
+ GstSAPType subsegmentStartsWithSAP;
+ gboolean bitstreamSwitching;
+ /* list of Accessibility DescriptorType nodes */
+ GList *Accessibility;
+ /* list of Role DescriptorType nodes */
+ GList *Role;
+ /* list of Rating DescriptorType nodes */
+ GList *Rating;
+ /* list of Viewpoint DescriptorType nodes */
+ GList *Viewpoint;
+ /* RepresentationBase extension */
+ GstRepresentationBaseType *RepresentationBase;
+ /* SegmentBase node */
+ GstSegmentBaseType *SegmentBase;
+ /* SegmentList node */
+ GstSegmentListNode *SegmentList;
+ /* SegmentTemplate node */
+ GstSegmentTemplateNode *SegmentTemplate;
+ /* list of BaseURL nodes */
+ GList *BaseURLs;
+ /* list of Representation nodes */
+ GList *Representations;
+ /* list of ContentComponent nodes */
+ GList *ContentComponents;
+};
+
+struct _GstSubsetNode
+{
+ guint *contains; /* UIntVectorType */
+ guint size; /* size of the "contains" array */
+};
+
+struct _GstPeriodNode
+{
+ gchar *id;
+ gint64 start; /* [ms] */
+ gint64 duration; /* [ms] */
+ gboolean bitstreamSwitching;
+ /* SegmentBase node */
+ GstSegmentBaseType *SegmentBase;
+ /* SegmentList node */
+ GstSegmentListNode *SegmentList;
+ /* SegmentTemplate node */
+ GstSegmentTemplateNode *SegmentTemplate;
+ /* list of Adaptation Set nodes */
+ GList *AdaptationSets;
+ /* list of Representation nodes */
+ GList *Subsets;
+ /* list of BaseURL nodes */
+ GList *BaseURLs;
+};
+
+struct _GstProgramInformationNode
+{
+ gchar *lang; /* LangVectorType RFC 5646 */
+ gchar *moreInformationURL;
+ /* children nodes */
+ gchar *Title;
+ gchar *Source;
+ gchar *Copyright;
+};
+
+struct _GstMetricsRangeNode
+{
+ gint64 starttime; /* [ms] */
+ gint64 duration; /* [ms] */
+};
+
+struct _GstMetricsNode
+{
+ gchar *metrics;
+ /* list of Metrics Range nodes */
+ GList *MetricsRanges;
+ /* list of Reporting nodes */
+ GList *Reportings;
+};
+
+struct _GstMPDNode
+{
+ gchar *default_namespace;
+ gchar *namespace_xsi;
+ gchar *namespace_ext;
+ gchar *schemaLocation;
+ gchar *id;
+ gchar *profiles;
+ GstMPDFileType type;
+ GstDateTime *availabilityStartTime;
+ GstDateTime *availabilityEndTime;
+ gint64 mediaPresentationDuration; /* [ms] */
+ gint64 minimumUpdatePeriod; /* [ms] */
+ gint64 minBufferTime; /* [ms] */
+ gint64 timeShiftBufferDepth; /* [ms] */
+ gint64 suggestedPresentationDelay; /* [ms] */
+ gint64 maxSegmentDuration; /* [ms] */
+ gint64 maxSubsegmentDuration; /* [ms] */
+ /* list of BaseURL nodes */
+ GList *BaseURLs;
+ /* list of Location nodes */
+ GList *Locations;
+ /* List of ProgramInformation nodes */
+ GList *ProgramInfo;
+ /* list of Periods nodes */
+ GList *Periods;
+ /* list of Metrics nodes */
+ GList *Metrics;
+};
+
+/**
+ * GstStreamPeriod:
+ *
+ * Stream period data structure
+ */
+struct _GstStreamPeriod
+{
+ GstPeriodNode *period; /* Stream period */
+ guint number; /* Period number */
+ GstClockTime start; /* Period start time */
+ GstClockTime duration; /* Period duration */
+};
+
+/**
+ * GstMediaSegment:
+ *
+ * Media segment data structure
+ */
+struct _GstMediaSegment
+{
+ GstSegmentURLNode *SegmentURL; /* this is NULL when using a SegmentTemplate */
+ guint number; /* segment number */
+ guint start; /* segment start time in timescale units */
+ GstClockTime start_time; /* segment start time */
+ GstClockTime duration; /* segment duration */
+};
+
+/**
+ * GstActiveStream:
+ *
+ * Active stream data structure
+ */
+struct _GstActiveStream
+{
+ GstStreamMimeType mimeType; /* video/audio/application */
+
+ guint mpd_baseURL_idx; /* MPD baseURL index */
+ guint period_baseURL_idx; /* Period baseURL index */
+ guint adaptset_baseURL_idx; /* AdaptationSet baseURL index */
+ guint repr_baseURL_idx; /* Representation baseURL index */
+
+ gchar *baseURL; /* active baseURL used for last request */
+ guint max_bandwidth; /* max bandwidth allowed for this mimeType */
+
+ GstAdaptationSetNode *cur_adapt_set; /* active adaptation set */
+ gint representation_idx; /* index of current representation */
+ GstRepresentationNode *cur_representation; /* active representation */
+ GstSegmentBaseType *cur_segment_base; /* active segment base */
+ GstSegmentListNode *cur_segment_list; /* active segment list */
+ GstSegmentTemplateNode *cur_seg_template; /* active segment template */
+ guint segment_idx; /* index of next sequence chunk */
+ GList *segments; /* list of GstMediaSegment */
+};
+
+struct _GstMpdClient
+{
+ GstMPDNode *mpd_node; /* active MPD manifest file */
+
+ GList *periods; /* list of GstStreamPeriod */
+ guint period_idx; /* index of current Period */
+
+ GList *active_streams; /* list of GstActiveStream */
+ guint stream_idx; /* currently active stream */
+
+ guint download_failed_count;
+ gchar *mpd_uri; /* manifest file URI */
+ GMutex *lock;
+};
+
+/* Basic initialization/deinitialization functions */
+GstMpdClient *gst_mpd_client_new ();
+void gst_active_streams_free (GstMpdClient * client);
+void gst_mpd_client_free (GstMpdClient * client);
+
+/* MPD file parsing */
+gboolean gst_mpd_parse (GstMpdClient *client, const gchar *data, gint size);
+
+/* Streaming management */
+gboolean gst_mpd_client_setup_media_presentation (GstMpdClient *client);
+gboolean gst_mpd_client_setup_streaming (GstMpdClient *client, GstStreamMimeType mimeType, gchar* lang);
+gboolean gst_mpd_client_setup_representation (GstMpdClient *client, GstActiveStream *stream, GstRepresentationNode *representation);
+gboolean gst_mpd_client_get_stream_current_position (GstMpdClient * client, guint stream_idx, GstClockTime *ts);
+GstClockTime gst_mpd_client_get_next_fragment_duration (GstMpdClient * client);
+GstClockTime gst_mpd_client_get_media_presentation_duration (GstMpdClient *client);
+gboolean gst_mpd_client_get_next_fragment (GstMpdClient *client, guint indexStream, gboolean *discontinuity, gchar **uri, GstClockTime *duration, GstClockTime *timestamp);
+gboolean gst_mpd_client_get_last_fragment_timestamp (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
+gboolean gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client, guint stream_idx, GstClockTime * ts);
+gboolean gst_mpd_client_get_next_header (GstMpdClient *client, const gchar **uri, guint stream_idx);
+gboolean gst_mpd_client_is_live (GstMpdClient * client);
+
+/* Period selection */
+gboolean gst_mpd_client_set_period_index (GstMpdClient *client, guint period_idx);
+guint gst_mpd_client_get_period_index (GstMpdClient *client);
+const gchar* gst_mpd_client_get_period_id (GstMpdClient * client);
+gboolean gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id);
+gboolean gst_mpd_client_has_next_period (GstMpdClient * client);
+
+/* Representation selection */
+gint gst_mpdparser_get_rep_idx_with_max_bandwidth (GList *Representations, guint64 max_bandwidth);
+
+/* URL management */
+const gchar *gst_mpdparser_get_baseURL (GstMpdClient *client);
+gboolean gst_mpdparser_get_chunk_by_index (GstMpdClient * client, guint indexStream, guint indexChunk, GstMediaSegment *segment);
+gboolean gst_mpd_client_set_next_baseURL_for_stream (GstMpdClient * client);
+
+/* Active stream */
+guint gst_mpdparser_get_nb_active_stream (GstMpdClient *client);
+GstActiveStream *gst_mpdparser_get_active_stream_by_index (GstMpdClient *client, guint stream_idx);
+gint gst_mpd_client_get_video_active_stream_id(GstMpdClient *client);
+gboolean gst_mpd_client_stream_seek (GstMpdClient * client, gint stream_idx, GstClockTime ts);
+gboolean gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time);
+gint gst_mpd_client_check_time_position (GstMpdClient * client, GstActiveStream * stream, GstClockTime ts, gint64 * diff);
+GstClockTime gst_mpd_client_stream_find_segment(GstMpdClient *client, guint stream_idx, GstClockTime seek_pos, guint *seg_num);
+
+/* AdaptationSet */
+guint gst_mpdparser_get_nb_adaptationSet (GstMpdClient *client);
+
+/* Segment */
+void gst_mpd_client_set_segment_index_for_all_streams (GstMpdClient * client, guint segment_idx);
+void gst_mpd_client_set_segment_index (GstActiveStream * stream, guint segment_idx);
+guint gst_mpd_client_get_segment_index (GstActiveStream * stream);
+
+/* Get audio/video stream parameters (mimeType, width, height, rate, number of channels) */
+const gchar *gst_mpd_client_get_stream_mimeType (GstActiveStream * stream);
+const gboolean gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream);
+gboolean gst_mpd_client_get_max_video_dimensions(GstMpdClient *client, gint *max_width, gint *max_height);
+guint gst_mpd_client_get_video_stream_width (GstActiveStream * stream);
+guint gst_mpd_client_get_video_stream_height (GstActiveStream * stream);
+guint gst_mpd_client_get_video_stream_bandwidth (GstActiveStream * stream);
+guint gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream);
+guint gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream);
+
+/* Support multi language */
+guint gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient *client, GList **lang);
+G_END_DECLS
+
+#endif /* __GST_MPDPARSER_H__ */
--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "gstfragmented.h"
+#include "gstdashdemux.h"
+
+GST_DEBUG_CATEGORY (fragmented_debug);
+
+#define XML_BUFFER_SIZE 16
+#define XML_INC_BUFFER { \
+ pos++; \
+ if (pos == XML_BUFFER_SIZE) { \
+ pos = 0; \
+ offset += XML_BUFFER_SIZE; \
+ data = gst_type_find_peek (tf, offset, XML_BUFFER_SIZE); \
+ if (data == NULL) return FALSE; \
+ } else { \
+ data++; \
+ } \
+}
+
+static gboolean
+xml_check_first_element (GstTypeFind * tf, const gchar * element, guint elen,
+ gboolean strict)
+{
+ gboolean got_xmldec;
+ const guint8 *data;
+ guint offset = 0;
+ guint pos = 0;
+
+ data = gst_type_find_peek (tf, 0, XML_BUFFER_SIZE);
+ if (!data)
+ return FALSE;
+
+ /* look for the XMLDec
+ * see XML spec 2.8, Prolog and Document Type Declaration
+ * http://www.w3.org/TR/2004/REC-xml-20040204/#sec-prolog-dtd */
+ got_xmldec = (memcmp (data, "<?xml", 5) == 0);
+
+ if (strict && !got_xmldec)
+ return FALSE;
+
+ /* skip XMLDec in any case if we've got one */
+ if (got_xmldec) {
+ pos += 5;
+ data += 5;
+ }
+
+ /* look for the first element, it has to be the requested element. Bail
+ * out if it is not within the first 4kB. */
+ while (data && (offset + pos) < 4096) {
+ while (*data != '<' && (offset + pos) < 4096) {
+ XML_INC_BUFFER;
+ }
+
+ XML_INC_BUFFER;
+ if (!g_ascii_isalpha (*data)) {
+ /* if not alphabetic, it's a PI or an element / attribute declaration
+ * like <?xxx or <!xxx */
+ XML_INC_BUFFER;
+ continue;
+ }
+
+ /* the first normal element, check if it's the one asked for */
+ data = gst_type_find_peek (tf, offset + pos, elen + 1);
+ return (data && element && strncmp ((char *) data, element, elen) == 0);
+ }
+
+ return FALSE;
+}
+
+/*** application/dash+xml typefind helper ***/
+
+static GstStaticCaps dash_caps = GST_STATIC_CAPS ("application/dash+xml");
+
+#define DASH_CAPS gst_static_caps_get (&dash_caps)
+
+static void
+dash_type_find (GstTypeFind * tf, gpointer unused)
+{
+ if (xml_check_first_element (tf, "MPD", 3, FALSE) ||
+ xml_check_first_element (tf, "mpd", 3, FALSE)) {
+ gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, DASH_CAPS);
+ }
+}
+
+static gboolean
+fragmented_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (fragmented_debug, "dashdemux", 0, "dashdemux");
+ if (!gst_element_register (plugin, "dashdemux", GST_RANK_PRIMARY,
+ GST_TYPE_DASH_DEMUX) || FALSE)
+ return FALSE;
+ if(!gst_type_find_register (plugin, "application/dash+xml",
+ GST_RANK_SECONDARY, dash_type_find, NULL, DASH_CAPS, NULL, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "dashdemux",
+ "DASH demuxer plugin",
+ fragmented_init, VERSION, "LGPL", PACKAGE_NAME, "http://www.gstreamer.org/")
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
+ *
+ * gstfragment.c:
+ *
+ * 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 <glib.h>
+#include "gstfragmented.h"
+#include "gstfragment.h"
+#include "gsturidownloader.h"
+
+GST_DEBUG_CATEGORY_STATIC (uridownloader_debug);
+#define GST_CAT_DEFAULT (uridownloader_debug)
+
+#define GST_URI_DOWNLOADER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ GST_TYPE_URI_DOWNLOADER, GstUriDownloaderPrivate))
+
+struct _GstUriDownloaderPrivate
+{
+ /* Fragments fetcher */
+ GstElement *urisrc;
+ GstBus *bus;
+ GstPad *pad;
+ GTimeVal *timeout;
+ GstFragment *download;
+ GMutex *lock;
+ GCond *cond;
+ gboolean canceled;
+/* add setting UA,cookies */
+ gchar *user_agent;
+ gchar **cookies;
+};
+
+static void gst_uri_downloader_finalize (GObject * object);
+static void gst_uri_downloader_dispose (GObject * object);
+
+static GstFlowReturn gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf);
+static gboolean gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event);
+static GstBusSyncReply gst_uri_downloader_bus_handler (GstBus * bus,
+ GstMessage * message, gpointer data);
+
+static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
+ _do_init);
+
+static void
+gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
+
+ gobject_class->dispose = gst_uri_downloader_dispose;
+ gobject_class->finalize = gst_uri_downloader_finalize;
+}
+
+static void
+gst_uri_downloader_init (GstUriDownloader * downloader)
+{
+ downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
+
+ /* Initialize the sink pad. This pad will be connected to the src pad of the
+ * element created with gst_element_make_from_uri and will handle the download */
+ downloader->priv->pad =
+ gst_pad_new_from_static_template (&sinkpadtemplate, "sink");
+ gst_pad_set_chain_function (downloader->priv->pad,
+ GST_DEBUG_FUNCPTR (gst_uri_downloader_chain));
+ gst_pad_set_event_function (downloader->priv->pad,
+ GST_DEBUG_FUNCPTR (gst_uri_downloader_sink_event));
+ gst_pad_set_element_private (downloader->priv->pad, downloader);
+ gst_pad_activate_push (downloader->priv->pad, TRUE);
+
+ /* Create a bus to handle error and warning message from the source element */
+ downloader->priv->bus = gst_bus_new ();
+
+ downloader->priv->lock = g_mutex_new ();
+ downloader->priv->cond = g_cond_new ();
+/* add setting UA, cookies */
+ downloader->priv->user_agent = NULL;
+ downloader->priv->cookies = NULL;
+ downloader->priv->canceled = FALSE;
+}
+
+void
+gst_uri_downloader_reset(GstUriDownloader *downloader)
+{
+ downloader->priv->canceled = FALSE;
+}
+
+static void
+gst_uri_downloader_dispose (GObject * object)
+{
+ GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
+
+ if (downloader->priv->urisrc != NULL) {
+ gst_object_unref (downloader->priv->urisrc);
+ downloader->priv->urisrc = NULL;
+ }
+
+ if (downloader->priv->bus != NULL) {
+ gst_object_unref (downloader->priv->bus);
+ downloader->priv->bus = NULL;
+ }
+
+ if (downloader->priv->pad) {
+ gst_object_unref (downloader->priv->pad);
+ downloader->priv->pad = NULL;
+ }
+
+ if (downloader->priv->download) {
+ g_object_unref (downloader->priv->download);
+ downloader->priv->download = NULL;
+ }
+
+/* add setting UA, cookies */
+ if (downloader->priv->user_agent) {
+ g_free (downloader->priv->user_agent);
+ downloader->priv->user_agent = NULL;
+ }
+ if (downloader->priv->cookies) {
+ g_strfreev (downloader->priv->cookies);
+ downloader->priv->cookies = NULL;
+ }
+ G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
+}
+
+static void
+gst_uri_downloader_finalize (GObject * object)
+{
+ GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
+
+ g_mutex_free (downloader->priv->lock);
+ g_cond_free (downloader->priv->cond);
+
+ G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
+}
+
+GstUriDownloader *
+gst_uri_downloader_new (void)
+{
+ return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
+}
+
+static gboolean
+gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstUriDownloader *downloader =
+ (GstUriDownloader *) (gst_pad_get_element_private (pad));
+
+ switch (event->type) {
+ case GST_EVENT_EOS:{
+ GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
+ g_mutex_lock (downloader->priv->lock);
+ if (downloader->priv->download != NULL) {
+ /* signal we have fetched the URI */
+ downloader->priv->download->completed = TRUE;
+ downloader->priv->download->download_stop_time = g_get_real_time ();
+ GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
+ g_cond_signal (downloader->priv->cond);
+ }
+ g_mutex_unlock (downloader->priv->lock);
+ break;
+ }
+ default:
+ break;
+ }
+
+ gst_event_unref (event);
+ return FALSE;
+}
+
+static GstBusSyncReply
+gst_uri_downloader_bus_handler (GstBus * bus,
+ GstMessage * message, gpointer data)
+{
+ GstUriDownloader *downloader = (GstUriDownloader *) (data);
+
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR ||
+ GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
+ GST_WARNING_OBJECT (downloader,
+ "Received error in bus from the source element, "
+ "the download will be cancelled");
+ /* remove the sync handler to avoid duplicated messages */
+ gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL);
+ gst_uri_downloader_cancel (downloader);
+ }
+
+ gst_message_unref (message);
+ return GST_BUS_DROP;
+}
+
+static GstFlowReturn
+gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf)
+{
+ GstUriDownloader *downloader =
+ (GstUriDownloader *) gst_pad_get_element_private (pad);
+
+ /* HTML errors (404, 500, etc...) are also pushed through this pad as
+ * response but the source element will also post a warning or error message
+ * in the bus, which is handled synchronously cancelling the download.
+ */
+ g_mutex_lock (downloader->priv->lock);
+ if (downloader->priv->download == NULL) {
+ /* Download cancelled, quit */
+ goto done;
+ }
+
+ GST_LOG_OBJECT (downloader,
+ "The uri fetcher received a new buffer of size %u",
+ GST_BUFFER_SIZE (buf));
+ if (!gst_fragment_add_buffer (downloader->priv->download, buf))
+ GST_WARNING_OBJECT (downloader, "Could not add buffer to fragment");
+
+done:
+ {
+ g_mutex_unlock (downloader->priv->lock);
+ return GST_FLOW_OK;
+ }
+}
+
+static void
+gst_uri_downloader_stop (GstUriDownloader * downloader)
+{
+ GstPad *pad;
+ GST_DEBUG_OBJECT (downloader, "Stopping source element");
+
+ /* remove the bus' sync handler */
+ gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL);
+ /* unlink the source element from the internal pad */
+ pad = gst_pad_get_peer (downloader->priv->pad);
+ if (pad) {
+ gst_pad_unlink (pad, downloader->priv->pad);
+ gst_object_unref (pad);
+ }
+ /* set the element state to NULL */
+ if (downloader->priv->urisrc)
+ {
+ gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
+ gst_element_get_state (downloader->priv->urisrc, NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ }
+}
+
+void
+gst_uri_downloader_cancel (GstUriDownloader * downloader)
+{
+ g_mutex_lock (downloader->priv->lock);
+ if (downloader->priv->download != NULL) {
+ GST_DEBUG_OBJECT (downloader, "Cancelling download");
+ g_object_unref (downloader->priv->download);
+ downloader->priv->download = NULL;
+ } else {
+ GST_DEBUG_OBJECT (downloader,
+ "Trying to cancell a download that was alredy cancelled");
+ }
+
+ GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
+ downloader->priv->canceled = TRUE;
+ g_cond_signal (downloader->priv->cond);
+ g_mutex_unlock (downloader->priv->lock);
+}
+
+static gboolean
+gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri)
+{
+ GstPad *pad;
+
+ if (!gst_uri_is_valid (uri))
+ return FALSE;
+
+ if (downloader->priv->urisrc == NULL) {
+ GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s", uri);
+ downloader->priv->urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
+ if (!downloader->priv->urisrc)
+ return FALSE;
+ } else {
+ GST_DEBUG_OBJECT (downloader, "Reusing existing source element for the URI:%s", uri);
+ if (!gst_uri_handler_set_uri (GST_URI_HANDLER (downloader->priv->urisrc), uri))
+ return FALSE;
+ }
+ g_object_set(downloader->priv->urisrc,"ahs-streaming", TRUE,NULL);
+
+ /* add a sync handler for the bus messages to detect errors in the download */
+ gst_element_set_bus (GST_ELEMENT (downloader->priv->urisrc),
+ downloader->priv->bus);
+ gst_bus_set_sync_handler (downloader->priv->bus,
+ gst_uri_downloader_bus_handler, downloader);
+
+ pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
+ if (!pad)
+ return FALSE;
+ gst_pad_link (pad, downloader->priv->pad);
+ gst_object_unref (pad);
+ return TRUE;
+}
+
+GstFragment *
+gst_uri_downloader_fetch_uri (GstUriDownloader * downloader, const gchar * uri)
+{
+ GstStateChangeReturn ret;
+ GstFragment *download = NULL;
+/* add setting UA,cookies */
+ gchar **cookies;
+
+ g_mutex_lock (downloader->priv->lock);
+
+ if(downloader->priv->canceled) {
+ GST_DEBUG_OBJECT (downloader, "Uridownloader was canceled.");
+ goto quit;
+ }
+
+ if (!gst_uri_downloader_set_uri (downloader, uri)) {
+ goto quit;
+ }
+
+ /* add setting UA,cookies */
+ g_object_set (downloader->priv->urisrc, "cookies", downloader->priv->cookies, NULL);
+ g_object_set (downloader->priv->urisrc, "user-agent", downloader->priv->user_agent, NULL);
+
+ downloader->priv->download = gst_fragment_new ();
+
+ ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ g_object_unref (downloader->priv->download);
+ downloader->priv->download = NULL;
+ goto quit;
+ }
+
+ /* wait until:
+ * - the download succeed (EOS in the src pad)
+ * - the download failed (Error message on the fetcher bus)
+ * - the download was canceled
+ */
+ GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI");
+ g_cond_wait (downloader->priv->cond, downloader->priv->lock);
+
+/* add setting UA,cookies */
+ g_object_get (downloader->priv->urisrc, "cookies", &cookies, NULL);
+ gst_uri_downloader_set_cookies (downloader, cookies);
+
+ download = downloader->priv->download;
+ downloader->priv->download = NULL;
+
+ if (download != NULL)
+ GST_INFO_OBJECT (downloader, "URI fetched successfully");
+ else
+ GST_INFO_OBJECT (downloader, "Error fetching URI");
+
+quit:
+ {
+ g_mutex_unlock (downloader->priv->lock);
+ gst_uri_downloader_stop (downloader);
+ return download;
+ }
+}
+
+/* add setting UA,cookies */
+void
+gst_uri_downloader_set_user_agent (GstUriDownloader * downloader, gchar * user_agent)
+{
+ if (downloader->priv->user_agent)
+ g_free (downloader->priv->user_agent);
+
+ downloader->priv->user_agent = user_agent;
+}
+
+void
+gst_uri_downloader_set_cookies (GstUriDownloader * downloader, gchar ** cookies)
+{
+ if (downloader->priv->cookies)
+ g_strfreev (downloader->priv->cookies);
+
+ downloader->priv->cookies = cookies;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
+ *
+ * gsturidownloader.h:
+ *
+ * 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.
+ *
+ * Youshould 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 __GSTURI_DOWNLOADER_H__
+#define __GSTURI_DOWNLOADER_H__
+
+#include <glib-object.h>
+#include <gst/gst.h>
+#include "gstfragment.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_URI_DOWNLOADER (gst_uri_downloader_get_type())
+#define GST_URI_DOWNLOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DOWNLOADER,GstUriDownloader))
+#define GST_URI_DOWNLOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DOWNLOADER,GstUriDownloaderClass))
+#define GST_IS_URI_DOWNLOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DOWNLOADER))
+#define GST_IS_URI_DOWNLOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DOWNLOADER))
+
+typedef struct _GstUriDownloader GstUriDownloader;
+typedef struct _GstUriDownloaderPrivate GstUriDownloaderPrivate;
+typedef struct _GstUriDownloaderClass GstUriDownloaderClass;
+
+struct _GstUriDownloader
+{
+ GstObject parent;
+
+ GstUriDownloaderPrivate *priv;
+};
+
+struct _GstUriDownloaderClass
+{
+ GstObjectClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_uri_downloader_get_type (void);
+
+GstUriDownloader * gst_uri_downloader_new (void);
+GstFragment * gst_uri_downloader_fetch_uri (GstUriDownloader * downloader, const gchar * uri);
+void gst_uri_downloader_cancel (GstUriDownloader *downloader);
+/* add setting UA,cookies */
+void gst_uri_downloader_set_user_agent (GstUriDownloader * downloader, gchar * user_agent);
+void gst_uri_downloader_set_cookies (GstUriDownloader * downloader, gchar ** cookies);
+void gst_uri_downloader_reset(GstUriDownloader *downloader);
+G_END_DECLS
+#endif /* __GSTURIDOWNLOADER_H__ */
--- /dev/null
+gst-plugins-ext0.10 (0.2.22-0) unstable; urgency=low
+
+ * [encodebin] Add error log for element link failure
+ * Tag: gst-plugins-ext0.10_0.2.22-0
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Sat, 02 Feb 2013 14:07:59 +0900
+
+gst-plugins-ext0.10 (0.2.21-0) unstable; urgency=low
+
+ * [evasimagesink] enhance code for setting a new evas image object during playing
+ * Tag: gst-plugins-ext0.10_0.2.21-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 15 Jan 2013 21:24:33 +0900
+
+gst-plugins-ext0.10 (0.2.19-2) unstable; urgency=low
+
+ * [evasimagesink] add defense code to avoid seg.fault
+ * Tag: gst-plugins-ext0.10_0.2.19-2
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Mon, 07 Jan 2013 22:55:11 +0900
+
+gst-plugins-ext0.10 (0.2.16-0) unstable; urgency=low
+
+ * Fix prevent issue
+ * Tag: gst-plugins-ext0.10_0.2.16-0
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Sat, 22 Dec 2012 14:53:41 +0900
+
+gst-plugins-ext0.10 (0.2.15-0) unstable; urgency=low
+
+ * [evasimagesink] fix prevent defects
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.15-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Sat, 22 Dec 2012 14:03:42 +0900
+
+gst-plugins-ext0.10 (0.2.14-0) unstable; urgency=low
+
+ * Solving Prevent issues.
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.14-0
+
+ -- Abhishek Bajaj <abhi.bajaj@samsung.com> Fri, 21 Dec 2012 15:30:27 +0530
+
+gst-plugins-ext0.10 (0.2.12-0) unstable; urgency=low
+
+ * drm_trusted was changed.
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.12-0
+
+ -- Haejeong Kim <backto.kim@samsung.com> Thu, 06 Dec 2012 14:34:27 +0900
+
+gst-plugins-ext0.10 (0.2.9-0) unstable; urgency=low
+
+ * [evasimagesink] modify present_data_addr, not using vir_addr
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.9-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 16 Oct 2012 18:37:08 +0900
+
+gst-plugins-ext0.10 (0.2.8-1) unstable; urgency=low
+
+ * add default smack manifest
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.8-1
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Fri, 21 Sep 2012 11:27:42 +0900
+
+gst-plugins-ext0.10 (0.2.5-0) unstable; urgency=low
+
+ * [encodebin] Add new property to select video converting element
+ * Git: gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.5-0
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Thu, 13 Sep 2012 15:26:46 +0900
+
+gst-plugins-ext0.10 (0.2.4-0) unstable; urgency=low
+
+ * [avsysmemsink] change sink pad's capabilities to bgra8888
+ * Git: framework/multimedia/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.4-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Thu, 06 Sep 2012 22:23:15 +0900
+
+gst-plugins-ext0.10 (0.2.3-0) unstable; urgency=low
+
+ * [evasimagesink] fix code to show frame if evas image object does not support gl zerocopy
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.3-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Mon, 09 Jul 2012 16:26:40 +0900
+
+gst-plugins-ext0.10 (0.2.2-0) unstable; urgency=low
+
+ * [evasimagesink] add evas_object_event_callback_del() for EVAS_CALLBACK_DEL and EVAS_CALLBACK_RESIZE event
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.2-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Mon, 18 Jun 2012 14:19:04 +0900
+
+gst-plugins-ext0.10 (0.2.1-0) unstable; urgency=low
+
+ * [evasimagesink] 1.handling GST_EVENT_CUSTOM_DOWNSTREAM_OOB event to skip memory copy in ecore pipe callback 2.remove redundancy ecore_pipe_del() 3.add evas image object resize callback
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.1-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 05 Jun 2012 12:03:39 +0900
+
+gst-plugins-ext0.10 (0.2.0-1) unstable; urgency=low
+
+ * Apply new drm
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.0-1
+
+ -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 23 May 2012 11:59:22 +0900
+
+gst-plugins-ext0.10 (0.2.0-0) unstable; urgency=low
+
+ * [AudioRoute] remove sound path in avsysaudiosink
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.2.0-0
+
+ -- Seungbae Shin <seungbae.shin@samsung.com> Mon, 23 Apr 2012 14:10:06 +0900
+
+gst-plugins-ext0.10 (0.1.10-1) unstable; urgency=low
+
+ * Enable trickplay audio in case of rate > 0
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.10-1
+
+ -- Dowan Kim <dowan2171.kim@samsung.com> Tue, 24 Apr 2012 14:44:26 +0900
+
+gst-plugins-ext0.10 (0.1.10-0) unstable; urgency=low
+
+ * [evasimagesink] 1. enhance code for multi-instance usage 2. apply MMTA for time analysis
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.10-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Thu, 19 Apr 2012 19:35:13 +0900
+
+gst-plugins-ext0.10 (0.1.9-0) unstable; urgency=low
+
+ * [avsysmemsink] update caps format
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.9-0
+
+ -- Younghwan Ahn <younghwan_.an@samsung.com> Mon, 02 Apr 2012 19:03:39 +0900
+
+gst-plugins-ext0.10 (0.1.8-0) unstable; urgency=low
+
+ * Audiotp plugin added
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.8-0
+
+ -- Santhoshi.KS <santhoshi.ks@samsung.com> Fri, 2 Mar 2012 18:52:26 +0900
+
+gst-plugins-ext0.10 (0.1.7-0) unstable; urgency=low
+
+ * [avsysaudiosink] remove memcpy when pcm write
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.7-0
+
+ -- Seungbae Shin <seungbae.shin@samsung.com> Mon, 13 Feb 2012 13:24:37 +0900
+
+gst-plugins-ext0.10 (0.1.6-0) unstable; urgency=low
+
+ * [evasimagesink] 1. add visible property 2. add GST_DEBUG log 3. code cleanup
+ * [avsystem] revise a parameter of g_signal_emit() in gstavsysmemsink.c
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.6-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Wed, 08 Feb 2012 14:34:10 +0900
+
+gst-plugins-ext0.10 (0.1.5-1) unstable; urgency=low
+
+ * Remove unused build dependency
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.5-1
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 20 Dec 2011 16:26:58 +0900
+
+gst-plugins-ext0.10 (0.1.5-0) unstable; urgency=low
+
+ * Remove avsysvideosrc plugin, gettext related and unused files
+ * Change license from Apache-2.0 to LGPL
+ * Git: slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.5-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 20 Dec 2011 14:41:14 +0900
+
+gst-plugins-ext0.10 (0.1.4-2) unstable; urgency=low
+
+ * Add missing files : gettext.h gst-i18n-lib.h for drmsrc
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.4-2
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Wed, 07 Dec 2011 13:55:23 +0900
+
+gst-plugins-ext0.10 (0.1.4-1) unstable; urgency=low
+
+ * Replace boilerplate - APACHE v2.0
+ * Delete unused files : gstavsysvideosink, encodetest, gstpreviewbin
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.4-1
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Fri, 02 Dec 2011 14:43:21 +0900
+
+gst-plugins-ext0.10 (0.1.4-0) unstable; urgency=low
+
+ * [avsysaudiosrc] remove audio-route property
+ * [pdpushsrc] fix Makefile.am
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.4-0
+
+ -- Seungbae Shin <seungbae.shin@samsung.com> Mon, 31 Oct 2011 18:15:01 +0900
+
+gst-plugins-ext0.10 (0.1.3-1) unstable; urgency=low
+
+ * [pdpushsrc] initial version
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.3-1
+
+ -- Younghwan Ahn <younghwan_.an@samsung.com> Mon, 24 Oct 2011 12:51:49 +0900
+
+gst-plugins-ext0.10 (0.1.3-0) unstable; urgency=low
+
+ * 1. [avsysaudiosink] restore sound path according to current policy
+ * 2. [avsysaudiosink] remove unused function/variables
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.3-0
+
+ -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 20 Oct 2011 18:03:39 +0900
+
+gst-plugins-ext0.10 (0.1.2-1) unstable; urgency=low
+
+ * Fix bugs resulted from prevent test (delete unnecessary compare codes and add preventing memory leak codes)
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.2-1
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Tue, 11 Oct 2011 15:50:08 +0900
+
+gst-plugins-ext0.10 (0.1.2-0) unstable; urgency=low
+
+ * [avsysvideosrc] Update code for metadata of samsung extension format
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.2-0
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Tue, 11 Oct 2011 15:39:10 +0900
+
+gst-plugins-ext0.10 (0.1.1-2) unstable; urgency=low
+
+ * [evasimagesink] Fix BS on U1 - add defensive code for gl extension error
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.1-2
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Thu, 06 Oct 2011 20:14:01 +0900
+
+gst-plugins-ext0.10 (0.1.1-1) unstable; urgency=low
+
+ * [avsysvideosrc] Correct thumbnail size when JPEG capture
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.1-1
+
+ -- Jeongmo Yang <jm80.yang@samsung.com> Mon, 26 Sep 2011 15:27:25 +0900
+
+gst-plugins-ext0.10 (0.1.1-0) unstable; urgency=low
+
+ * Initial Release.
+ * Git: slp-source.sec.samsung.net:slp/pkgs/g/gst-plugins-ext0.10
+ * Tag: gst-plugins-ext0.10_0.1.1-0
+
+ -- Sangchul Lee <sc11.lee@samsung.com> Wed, 24 Aug 2011 09:58:05 +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>, Santhoshi KS <santhoshi.ks@samsung.com>
+Build-Depends: debhelper (>= 5), libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, libavsystem-dev, libmm-ta-dev, drm-client-dev, drm-trusted-dev, libecore-dev, libevas-dev, libmm-ta-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-client-0, drm-trusted-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
+@PREFIX@/lib/gstreamer-0.10/libgstaudiotp.so
+@PREFIX@/lib/gstreamer-0.10/libgstaudioeq.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
##############################################################################
# sources used to compile this plug-in
-libgstdrmsrc_la_SOURCES = gstdrmsrc.c
+libgstdrmsrc_la_SOURCES = gstdrmsrc.c drm_util.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_CFLAGS = $(GST_CFLAGS) $(DRM_CLIENT_CFLAGS) $(DRM_TRUSTED_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+libgstdrmsrc_la_LIBADD = $(GST_LIBS) $(DRM_CLIENT_LIBS) $(DRM_TRUSTED_LIBS) $(GST_BASE_LIBS)
libgstdrmsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
# headers we need but don't want installed
-noinst_HEADERS = gstdrmsrc.h
+noinst_HEADERS = gstdrmsrc.h drm_util.h
--- /dev/null
+/*
+ * drm-util.c
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Seungbae Shin <seungbae.shin@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
+ *
+ */
+#include <string.h>
+#include <drm_trusted_client.h>
+#include "drm_util.h"
+
+gboolean drm_util_open (DRM_DECRYPT_HANDLE *phandle, char* file_path, int file_type)
+{
+ drm_trusted_result_e drm_trusted_result;
+ drm_trusted_open_decrypt_info_s open_decrypt_info;
+ drm_trusted_open_decrypt_resp_data_s open_decrypt_resp;
+ drm_trusted_set_consumption_state_info_s decrypt_state_data = { DRM_CONSUMPTION_STARTED };
+
+ if (phandle == NULL || file_path == NULL) {
+ GST_ERROR ("Invalid parameter, phandle=%p, file_path=%p", phandle, file_path);
+ return FALSE;
+ }
+
+ /* Fill parameter structure for opening decrypt session */
+ memset (&open_decrypt_info, 0, sizeof (drm_trusted_open_decrypt_info_s));
+ memset (&open_decrypt_resp, 0, sizeof (drm_trusted_open_decrypt_resp_data_s));
+
+ strncpy (open_decrypt_info.filePath, file_path, sizeof (open_decrypt_info.filePath)-1);
+ open_decrypt_info.file_type = file_type;
+ open_decrypt_info.permission = DRM_TRUSTED_PERMISSION_TYPE_PLAY;
+
+ /* Open decrypt session */
+ drm_trusted_result = drm_trusted_open_decrypt_session(&open_decrypt_info, &open_decrypt_resp, phandle);
+ if (drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS || *phandle == NULL) {
+ GST_ERROR ("Error in drm_trusted_open_decrypt_session(), error=%x, *phandle=%p,", drm_trusted_result, *phandle);
+ return FALSE;
+ }
+ GST_DEBUG ("drm_trusted_open_decrypt_session () success!!, ret=%x, *phandle=%p", drm_trusted_result, *phandle);
+
+ /* Set decryption state to STARTED */
+ drm_trusted_result = drm_trusted_set_decrypt_state(*phandle, &decrypt_state_data);
+ if (drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_set_decrypt_state(), error=%x", drm_trusted_result);
+ drm_trusted_close_decrypt_session (phandle);
+ *phandle = NULL;
+ return FALSE;
+ }
+ GST_DEBUG ("drm_trusted_set_decrypt_state () success!!, ret=%x, *phandle=%p", drm_trusted_result, *phandle);
+
+ return TRUE;
+}
+
+gboolean drm_util_read (DRM_DECRYPT_HANDLE handle, unsigned char* buf, unsigned int buf_length, unsigned int *read_size)
+{
+ drm_trusted_result_e drm_trusted_result;
+ drm_trusted_payload_info_s payload_info;
+ drm_trusted_read_decrypt_resp_data_s decrypt_resp;
+
+ if (handle == NULL || buf == NULL) {
+ GST_ERROR ("Invalid parameter, handle=%p, buf=%p", handle, buf);
+ return FALSE;
+ }
+
+ GST_DEBUG ("Enter [%s] buf=%p, length=%d, read_size=%p", __func__, buf, buf_length, read_size);
+
+ /* fill input/output data */
+ memset (&payload_info, 0, sizeof (drm_trusted_payload_info_s));
+ memset (&decrypt_resp, 0, sizeof (drm_trusted_read_decrypt_resp_data_s));
+
+ payload_info.payload_data = buf;
+ payload_info.payload_data_len = buf_length;
+ payload_info.payload_data_output = buf;
+
+ drm_trusted_result = drm_trusted_read_decrypt_session (handle, &payload_info, &decrypt_resp);
+ if(drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_read_decrypt_session() [%x]", drm_trusted_result);
+ return FALSE;
+ }
+
+ GST_DEBUG ("Leave [%s], drm_trusted_read_decrypt_session() success, requested=%d, read_size=%d", __func__, buf_length, decrypt_resp.read_size);
+ if (read_size)
+ *read_size = decrypt_resp.read_size;
+
+ return TRUE;
+}
+
+gboolean drm_util_seek (DRM_DECRYPT_HANDLE handle, int offset, int mode)
+{
+ drm_trusted_result_e drm_trusted_result;
+ drm_trusted_seek_decrypt_info_s seek_decrypt_info;
+ memset (&seek_decrypt_info, 0, sizeof(drm_trusted_seek_decrypt_info_s));
+
+ GST_DEBUG ("Enter [%s] offset=%d, mode=%d", __func__, offset, mode);
+
+ seek_decrypt_info.offset = offset;
+ seek_decrypt_info.seek_mode = mode;
+
+ drm_trusted_result = drm_trusted_seek_decrypt_session(handle, &seek_decrypt_info);
+ if (drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_seek_decrypt_session(), error=%x", drm_trusted_result);
+ return FALSE;
+ }
+
+ GST_DEBUG ("Leave [%s], drm_trusted_seek_decrypt_session () success!!", __func__);
+ return TRUE;
+}
+
+gboolean drm_util_tell (DRM_DECRYPT_HANDLE handle, unsigned int *offset)
+{
+ drm_trusted_result_e drm_trusted_result;
+ drm_trusted_tell_decrypt_resp_data_s tell_decrypt_resp_data;
+
+ if (handle == NULL || offset == NULL) {
+ GST_ERROR ("Invalid parameter, handle=%p, offset=%p", handle, offset);
+ return FALSE;
+ }
+
+ GST_DEBUG ("Enter [%s] offset=%p", __func__, offset);
+
+ memset (&tell_decrypt_resp_data, 0, sizeof(drm_trusted_tell_decrypt_resp_data_s));
+
+ drm_trusted_result = drm_trusted_tell_decrypt_session(handle, &tell_decrypt_resp_data);
+ if (drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_tell_decrypt_session(), error=%x", drm_trusted_result);
+ return FALSE;
+ }
+
+ if (offset)
+ *offset = tell_decrypt_resp_data.offset;
+
+ GST_DEBUG ("Leave [%s], drm_trusted_tell_decrypt_session () success!!", __func__);
+
+ return TRUE;
+}
+
+gboolean drm_util_close (DRM_DECRYPT_HANDLE *phandle)
+{
+ drm_trusted_result_e drm_trusted_result;
+ drm_trusted_set_consumption_state_info_s decrypt_state_data = { DRM_CONSUMPTION_STOPPED };
+
+ if (phandle == NULL) {
+ GST_ERROR ("Invalid parameter, phandle=%p", phandle);
+ return FALSE;
+ }
+
+ /* Set decryption state to STOPPED */
+ drm_trusted_result = drm_trusted_set_decrypt_state(*phandle, &decrypt_state_data);
+ if (drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_set_decrypt_state(), error=%x", drm_trusted_result);
+ } else {
+ GST_DEBUG ("drm_trusted_set_decrypt_state () success!!");
+ }
+
+ /* Close decrypt session */
+ drm_trusted_result = drm_trusted_close_decrypt_session(phandle);
+ if(drm_trusted_result != DRM_TRUSTED_RETURN_SUCCESS) {
+ GST_ERROR ("Error in drm_trusted_close_decrypt_session() error=%x", drm_trusted_result);
+ return FALSE;
+ }
+ GST_DEBUG ("drm_trusted_close_decrypt_session() success!!!");
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * drm_util.h
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Seungbae Shin <seungbae.shin@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 __DRM_UTIL_H__
+#define __DRM_UTIL_H__
+
+#include <gst/gst.h>
+
+#include <drm_trusted_client_types.h>
+
+gboolean drm_util_open (DRM_DECRYPT_HANDLE *phandle, char* file_path, int file_type);
+gboolean drm_util_read (DRM_DECRYPT_HANDLE handle, unsigned char* buf, unsigned int buf_length, unsigned int *read_size);
+gboolean drm_util_seek (DRM_DECRYPT_HANDLE handle, int offset, int mode);
+gboolean drm_util_tell (DRM_DECRYPT_HANDLE handle, unsigned int *offset);
+gboolean drm_util_close (DRM_DECRYPT_HANDLE *phandle);
+
+#endif /* __DRM_UTIL_H__ */
#endif
#include "gstdrmsrc.h"
+#include "drm_util.h"
+#include <drm_client.h>
+#include <drm_client_types.h>
+
+#ifdef CONTROL_PAGECACHE
+#include <fcntl.h>
+#define DEFAULT_DO_FADVISE_THRESHOLD (100 * 1024 * 1024) /* 100 MB */
+#endif
+
#define LOG_TRACE(message) //g_print("DRM_SRC: %s: %d: %s - %s \n", __FILE__, __LINE__, __FUNCTION__, message);
#define GST_TAG_PLAYREADY "playready_file_path"
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,GST_STATIC_CAPS_ANY);
+#ifdef CONTROL_PAGECACHE
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#define DRMSRC_FADVISE_DONT(x_fd, x_offset, x_length) \
+ do \
+ { \
+ if (posix_fadvise64(x_fd, x_offset, x_length, POSIX_FADV_DONTNEED) != 0) \
+ { \
+ GST_ERROR("Set posix_fadvise with POSIX_FADV_DONTNEED failed"); \
+ } \
+ }while (0);
+#else
+#define DRMSRC_FADVISE_DONT(x_fd, x_offset, x_length) \
+ do \
+ { \
+ if (posix_fadvise(x_fd, x_offset, x_length, POSIX_FADV_DONTNEED) != 0) \
+ { \
+ GST_ERROR("Set posix_fadvise with POSIX_FADV_DONTNEED failed"); \
+ } \
+ }while (0);
+#endif
+#endif
GST_DEBUG_CATEGORY_STATIC (gst_drm_src_debug);
#define GST_CAT_DEFAULT gst_drm_src_debug
enum
{
- ARG_0,
- ARG_LOCATION,
- ARG_FD
+ ARG_0,
+ ARG_LOCATION,
+ ARG_FD,
+ IS_DRM
};
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);
-
+static gboolean gst_drm_src_query (GstBaseSrc * src, GstQuery * query);
+static GstStateChangeReturn gst_drm_src_change_state (GstElement * element,
+ GstStateChange transition);
/**
* This function does the following:
* 1. Initializes GstDrmSrc ( defines gst_drm_get_type)
*/
static void _do_init (GType drmsrc_type)
{
- // 1. Initializes GstDrmSrc ( defines gst_drm_get_type)
- static const GInterfaceInfo urihandler_info = {
- gst_drm_src_uri_handler_init,
- NULL,
- NULL
- };
-
- g_type_add_interface_static (drmsrc_type, GST_TYPE_URI_HANDLER, &urihandler_info);
- GST_DEBUG_CATEGORY_INIT (gst_drm_src_debug, "drmsrc", 0, "drmsrc element");
+ // 1. Initializes GstDrmSrc ( defines gst_drm_get_type)
+ static const GInterfaceInfo urihandler_info = {
+ gst_drm_src_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ g_type_add_interface_static (drmsrc_type, GST_TYPE_URI_HANDLER, &urihandler_info);
+ GST_DEBUG_CATEGORY_INIT (gst_drm_src_debug, "drmsrc", 0, "drmsrc element");
}
GST_BOILERPLATE_FULL (GstDrmSrc, gst_drm_src, GstBaseSrc, GST_TYPE_BASE_SRC, _do_init);
/**
*/
static void gst_drm_src_base_init (gpointer g_class)
{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
- // 1. Sets the class details
- gst_element_class_set_details_simple (gstelement_class,
- "DRM Source",
- "Source/File",
- "Read from arbitrary point in a standard/DRM file",
- "Kishore Arepalli <kishore.a@samsung.com> and Sadanand Dodawadakar <sadanand.d@samsung.com>");
- // 2. Adds the source pad template
- gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&srctemplate));
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ /*Sets the class details */
+ gst_element_class_set_details_simple (gstelement_class,
+ "DRM Source",
+ "Source/File",
+ "Read from arbitrary point in a standard/DRM file",
+ "Kishore Arepalli <kishore.a@samsung.com> and Sadanand Dodawadakar <sadanand.d@samsung.com>");
+
+ /*Adds the source pad template */
+ gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&srctemplate));
}
/**
* This function does the following:
*/
static void gst_drm_src_class_init (GstDrmSrcClass * klass)
{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
- GstBaseSrcClass *gstbasesrc_class;
- gobject_class = G_OBJECT_CLASS (klass);
- gstelement_class = GST_ELEMENT_CLASS (klass);
- gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
- // Assigns the function pointers GObject class attributes
- gobject_class->set_property = gst_drm_src_set_property;
- gobject_class->get_property = gst_drm_src_get_property;
- // 1. Installs the properties
- g_object_class_install_property (gobject_class, ARG_FD,
- g_param_spec_int ("fd", "File-descriptor",
- "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
- G_PARAM_READABLE));
- 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));
-
- // 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);
- gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_drm_src_stop);
- gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_drm_src_is_seekable);
- gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_drm_src_get_size);
- gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_drm_src_create);
-
-
- gst_tag_register (GST_TAG_PLAYREADY, GST_TAG_FLAG_META,
- G_TYPE_STRING,
- "PlayReady File Path",
- "a tag that is specific to PlayReady File",
- NULL);
+ GObjectClass *gobject_class;
+ GstBaseSrcClass *gstbasesrc_class;
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+ GstElementClass *gstelement_class;
+ gstelement_class = (GstElementClass *) klass;
+ /* Assigns the function pointers GObject class attributes */
+ gobject_class->set_property = gst_drm_src_set_property;
+ gobject_class->get_property = gst_drm_src_get_property;
+
+ /* Installs the properties*/
+ g_object_class_install_property (gobject_class, ARG_FD,
+ g_param_spec_int ("fd", "File-descriptor",
+ "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+
+ 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_READABLE));
+
+ /*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);
+ gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_drm_src_stop);
+ gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_drm_src_is_seekable);
+ gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_drm_src_get_size);
+ gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_drm_src_create);
+ gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_drm_src_query);
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_drm_src_change_state);
+ gst_tag_register (GST_TAG_PLAYREADY, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ "PlayReady File Path",
+ "a tag that is specific to PlayReady File",
+ NULL);
}
/**
* This function does the following:
*/
static void gst_drm_src_init (GstDrmSrc * src, GstDrmSrcClass * g_class)
{
- // 1. Initilizes the parameters of GstDrmSrc
- src->filename = NULL;
- src->fd = 0;
- src->uri = NULL;
- src->is_regular = FALSE;
- src->seekable = FALSE;
- PROFILE_INIT;
+ /* Initilizes the parameters of GstDrmSrc */
+ src->filename = NULL;
+ src->fd = 0;
+ src->uri = NULL;
+ src->is_regular = FALSE;
+ src->is_oma = FALSE;
+ src->seekable = FALSE;
+ src->hfile = NULL;
+ src->event_posted = FALSE;
+ src->is_playready = FALSE;
+ src->is_drm = FALSE;
+ src->isopen = FALSE;
+#ifdef CONTROL_PAGECACHE
+ src->accum = 0;
+#endif
}
/**
* This function does the following:
*/
static void gst_drm_src_finalize (GObject * object)
{
- GstDrmSrc *src;
-
- src = GST_DRM_SRC (object);
- // 1. deallocates the filename and uri
- g_free (src->filename);
- g_free (src->uri);
- // 2. calls the parent class->finalize
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ GstDrmSrc *src;
+
+ src = GST_DRM_SRC (object);
+ /*. deallocates the filename and uri */
+ g_free (src->filename);
+ g_free (src->uri);
+ /* calls the parent class->finalize */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* This function does the following:
*/
static gboolean gst_drm_src_set_location (GstDrmSrc * src, const gchar * location)
{
- GstState state;
-
- GST_OBJECT_LOCK (src);
- // 1. Checks the state
- state = GST_STATE (src);
- if (state != GST_STATE_READY && state != GST_STATE_NULL)
- {
- GST_DEBUG_OBJECT (src, "setting location in wrong state");
- GST_OBJECT_UNLOCK (src);
- return FALSE;
- }
- GST_OBJECT_UNLOCK (src);
- g_free (src->filename);
- g_free (src->uri);
- // 2. Checks the filename
- if (location == NULL)
- {
- src->filename = NULL;
- src->uri = NULL;
- }
- else
- {
- // 3. Sets the filename
- src->filename = g_strdup (location);
- src->uri = gst_uri_construct ("file", src->filename);
- }
- g_object_notify (G_OBJECT (src), "location");
- gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
- return TRUE;
+ GstState state;
+
+ GST_OBJECT_LOCK (src);
+
+ /* Checks the state */
+ state = GST_STATE (src);
+ if (state != GST_STATE_READY && state != GST_STATE_NULL) {
+ GST_DEBUG_OBJECT (src, "setting location in wrong state");
+ GST_OBJECT_UNLOCK (src);
+ return FALSE;
+ }
+ GST_OBJECT_UNLOCK (src);
+
+ g_free (src->filename);
+ g_free (src->uri);
+
+ /* Checks the filename */
+ if (location == NULL) {
+ src->filename = NULL;
+ src->uri = NULL;
+ } else {
+ /*. Sets the filename */
+ src->filename = g_strdup (location);
+#if 0
+ /*This below API is changing the filename in case of punctuation marks in filename*/
+ src->uri = gst_filename_to_uri (location, NULL);
+#else
+ src->uri = g_strdup_printf ("%s://%s", "file", src->filename);
+
+ drm_bool_type_e is_drm = DRM_FALSE;
+ if (drm_is_drm_file(src->filename, &is_drm) == DRM_RETURN_SUCCESS) {
+ if (is_drm == DRM_TRUE) {
+ src->is_drm = TRUE;
+ }
+ }
+ GST_DEBUG_OBJECT (src, "is drm : %d", src->is_drm);
+#endif
+ GST_INFO_OBJECT(src, "filename : %s", src->filename);
+ GST_INFO_OBJECT(src, "uri : %s", src->uri);
+ }
+ g_object_notify (G_OBJECT (src), "location");
+ gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
+
+ return TRUE;
}
/**
* This function does the following:
*/
static void gst_drm_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
{
- GstDrmSrc *src;
-
- g_return_if_fail (GST_IS_DRM_SRC (object));
- src = GST_DRM_SRC (object);
- switch (prop_id)
- {
- // 1. Sets the location of the file.
- case ARG_LOCATION:
- gst_drm_src_set_location (src, g_value_get_string (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ GstDrmSrc *src;
+
+ g_return_if_fail (GST_IS_DRM_SRC (object));
+
+ src = GST_DRM_SRC (object);
+
+ switch (prop_id) {
+ // 1. Sets the location of the file.
+ case ARG_LOCATION:
+ gst_drm_src_set_location (src, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
/**
* This function does the following:
*/
static void gst_drm_src_get_property (GObject * object, guint prop_id, GValue * value,GParamSpec * pspec)
{
- GstDrmSrc *src;
-
- g_return_if_fail (GST_IS_DRM_SRC (object));
- src = GST_DRM_SRC (object);
- switch (prop_id)
- {
- // 1. Provides the location of the file.
- case ARG_LOCATION:
- g_value_set_string (value, src->filename);
- break;
- // 2. Provides the file descriptor.
- case ARG_FD:
- g_value_set_int (value, src->fd);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ GstDrmSrc *src;
+
+ g_return_if_fail (GST_IS_DRM_SRC (object));
+ src = GST_DRM_SRC (object);
+ switch (prop_id) {
+ case ARG_LOCATION:
+ g_value_set_string (value, src->filename);
+ break;
+ case ARG_FD:
+ g_value_set_int (value, src->fd);
+ break;
+ case IS_DRM:
+ g_value_set_boolean(value, src->is_drm);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_drm_src_change_state (GstElement * element, GstStateChange transition)
+{
+
+ GstDrmSrc *src = GST_DRM_SRC (element);
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+ gboolean ret = FALSE;
+ drm_file_type_e file_type = DRM_TYPE_UNDEFINED;
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:{
+ GST_INFO_OBJECT(src,"change state from NULL to ready");
+ drm_result_e drm_result;
+ drm_result = drm_get_file_type (src->filename, &file_type);
+ if (file_type == DRM_TYPE_OMA_V1) {
+ /* Opens the DRM file if it is DRM */
+ if (!src->isopen) {
+ GST_DEBUG_OBJECT (src, "trying to open drm util");
+ if (drm_util_open (&src->hfile, src->filename, file_type) == FALSE) {
+ GST_ERROR_OBJECT (src, "failed to open drm util");
+ return FALSE;
+ }
+ src->isopen=TRUE;
+ }
+ }
+ break;
+ }
+ case GST_STATE_CHANGE_READY_TO_PAUSED:{
+ GST_INFO_OBJECT(src,"change state from ready to paused");
+ drm_result_e drm_result;
+ drm_result = drm_get_file_type (src->filename, &file_type);
+ if (file_type == DRM_TYPE_OMA_V1){
+ ret = drm_process_request(DRM_REQUEST_TYPE_CLIENT_CLEAN_UP, NULL, NULL);
+ if (DRM_RETURN_SUCCESS == ret) {
+ GST_INFO("Clean Up successful!!");
+ } else {
+ GST_ERROR("Clean Up Failed!!, ret = 0x%x", ret);
+ }
+ ret = drm_trusted_handle_request(DRM_TRUSTED_REQ_TYPE_CLIENT_CLEAN_UP, NULL, NULL);
+ if (DRM_RETURN_SUCCESS == ret) {
+ GST_INFO("Clean Up successful!!");
+ } else {
+ GST_ERROR("Clean Up Failed!!, ret = 0x%x", ret);
+ }
+ }
+ break;
+ }
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
+ GST_INFO_OBJECT(src,"change state from paused to playing");
+ break;
+ }
+ default:
+ break;
+ }
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
+ GST_INFO_OBJECT(src,"change state from playing to paused");
+ break;
+ }
+ case GST_STATE_CHANGE_PAUSED_TO_READY:{
+ GST_INFO_OBJECT(src,"change state from paused to ready");
+ if (src->hfile) {
+ if (src->isopen)
+ if (drm_util_close(&src->hfile))
+ src->isopen=FALSE;
+ src->hfile = NULL;
+#ifdef CONTROL_PAGECACHE
+ DRMSRC_FADVISE_DONT(src->hfile, 0, 0);
+ src->accum = 0;
+#endif
+ }
+ break;
+ }
+ case GST_STATE_CHANGE_READY_TO_NULL:{
+ GST_INFO_OBJECT(src,"change state from ready to null");
+ break;
+ }
+ default:
+ break;
+
+ }
+ return result;
}
+
+
+
+/**
+ * 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* src, guint64 i_uiOffset, guint length, GstBuffer ** o_pBbuffer)
+{
+ GstBuffer *buf = NULL;
+ unsigned int readSize;
+
+ /* Seeks to the specified position for DRM file. */
+ if (G_UNLIKELY (src->read_position != i_uiOffset)) {
+ if (drm_util_seek (src->hfile, i_uiOffset, DRM_SEEK_SET) == FALSE)
+ goto FAILED;
+
+ src->read_position = i_uiOffset;
+ }
+
+ /* 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;
+ }
+
+ /*. Reads from the file and sets the related params for DRM file. */
+ if (drm_util_read (src->hfile, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf), &readSize) == FALSE)
+ 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;
+ }
+
+#ifdef CONTROL_PAGECACHE
+ src->accum += readSize;
+ if (src->accum >= DEFAULT_DO_FADVISE_THRESHOLD) {
+ DRMSRC_FADVISE_DONT(src->hfile, 0, 0);
+ src->accum = 0;
+ }
+#endif
+
+ length = readSize;
+ GST_BUFFER_SIZE (buf) = length;
+ GST_BUFFER_OFFSET (buf) = i_uiOffset;
+ GST_BUFFER_OFFSET_END (buf) = i_uiOffset + length;
+ *o_pBbuffer = buf;
+ src->read_position += length;
+
+ return GST_FLOW_OK;
+
+FAILED:
+{
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
+ return GST_FLOW_ERROR;
+}
+}
/**
* This function does the following:
* 1. Seeks to the specified position.
*/
static GstFlowReturn gst_drm_src_create_read (GstDrmSrc * src, guint64 offset, guint length, GstBuffer ** buffer)
{
- int ret;
- GstBuffer *buf;
- // 1. Seeks to the specified position.
- if (G_UNLIKELY (src->read_position != offset))
- {
- off_t res;
- res = lseek (src->fd, offset, SEEK_SET);
- if (G_UNLIKELY (res < 0 || res != offset))
- {
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
- return GST_FLOW_ERROR;
- }
- src->read_position = offset;
- }
- // 2. Allocates a buffer to push the data
- buf = gst_buffer_new_and_alloc (length);
- GST_LOG_OBJECT (src, "Reading %d bytes", length);
- // 3. Reads from the file and sets the related params
- ret = read (src->fd, GST_BUFFER_DATA (buf), length);
- if (G_UNLIKELY (ret < 0))
- {
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
- if (G_UNLIKELY ((guint) ret < length && src->seekable))
- {
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),("unexpected end of file."));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
- if (G_UNLIKELY (ret == 0 && length > 0))
- {
- GST_DEBUG ("non-regular file hits EOS");
- gst_buffer_unref (buf);
- return GST_FLOW_UNEXPECTED;
+ int ret;
+ GstBuffer *buf;
+
+ /* Seeks to the specified position. */
+ if (G_UNLIKELY (src->read_position != offset)) {
+ off_t res;
+ res = lseek (src->fd, offset, SEEK_SET);
+ if (G_UNLIKELY (res < 0 || res != offset)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
+ return GST_FLOW_ERROR;
+ }
+ src->read_position = offset;
+ }
+
+ /* Allocates a buffer to push the data */
+ buf = gst_buffer_new_and_alloc (length);
+ if (NULL == buf) {
+ GST_ERROR_OBJECT (src, "failed to allocate memory..");
+ GST_ELEMENT_ERROR (src, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return GST_FLOW_ERROR;
+ }
+
+ GST_LOG_OBJECT (src, "Reading %d bytes", length);
+
+ /* Reads from the file and sets the related params */
+ ret = read (src->fd, GST_BUFFER_DATA (buf), length);
+ if (G_UNLIKELY (ret < 0)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
+
+ if (G_UNLIKELY ((guint) ret < length && src->seekable)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),("unexpected end of file."));
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
+
+ if (G_UNLIKELY (ret == 0 && length > 0)) {
+ GST_DEBUG ("non-regular file hits EOS");
+ gst_buffer_unref (buf);
+ return GST_FLOW_UNEXPECTED;
+ }
+
+#ifdef CONTROL_PAGECACHE
+ src->accum += ret;
+ if (src->accum >= DEFAULT_DO_FADVISE_THRESHOLD) {
+ DRMSRC_FADVISE_DONT(src->fd, 0, 0);
+ src->accum = 0;
}
- length = ret;
- GST_BUFFER_SIZE (buf) = length;
- GST_BUFFER_OFFSET (buf) = offset;
- GST_BUFFER_OFFSET_END (buf) = offset + length;
- *buffer = buf;
- src->read_position += length;
- return GST_FLOW_OK;
+ //DRMSRC_FADVISE_DONT(src->fd, offset, ret);
+#endif
+
+ length = ret;
+ GST_BUFFER_SIZE (buf) = length;
+ GST_BUFFER_OFFSET (buf) = offset;
+ GST_BUFFER_OFFSET_END (buf) = offset + length;
+ *buffer = buf;
+ src->read_position += length;
+
+ return GST_FLOW_OK;
}
/**
* This function does the following:
*/
static GstFlowReturn gst_drm_src_create (GstBaseSrc * basesrc, guint64 offset, guint length, GstBuffer ** buffer)
{
- GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
- // 1. Calls DRM file read chain method for drm files.
+ 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) {
+ if(!gst_pad_push_event (src_pad, gst_event_new_tag (tags))) {
+ GST_ERROR_OBJECT (src, "failed to push tags..");
+ gst_object_unref (src_pad);
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG_OBJECT (src, "posting tags returns [%d]", src->event_posted);
+ src->event_posted = TRUE;
+ gst_object_unref (src_pad);
+ }
+ }
+ }
- // 2. Calls normal file read chain method for standard files.
- return gst_drm_src_create_read (src, offset, length, buffer);
+ if(src->is_oma == TRUE) /* Calls DRM file read chain method for drm files. */
+ return gst_drm_src_create_read_drm_file (src, offset, length, buffer);
+ else /* Calls normal file read chain method for standard files. */
+ return gst_drm_src_create_read (src, offset, length, buffer);
}
+
+static gboolean
+gst_drm_src_query (GstBaseSrc * basesrc, GstQuery * query)
+{
+ gboolean ret = FALSE;
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_URI:
+ gst_query_set_uri (query, src->uri);
+ ret = TRUE;
+ break;
+ case GST_QUERY_CUSTOM:
+ {
+ GstStructure *s;
+ guint64 size = 0;
+ GValue v = { 0, { { 0 } } };
+ g_value_init(&v, G_TYPE_UINT64);
+
+ s = gst_query_get_structure (query);
+ if (gst_structure_has_name (s, "dynamic-size")) {
+ if (gst_drm_src_get_size (basesrc, &size)){
+ /* succedded. take size */
+ g_value_set_uint64 (&v, size);
+ gst_structure_set_value(s, "size", &v);
+ ret = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ ret = FALSE;
+ break;
+ }
+
+ if (!ret)
+ ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
+
+ return ret;
+}
+
/**
*
* @param basesrc [in] BaseSrc Structure
*/
static gboolean gst_drm_src_is_seekable (GstBaseSrc * basesrc)
{
- GstDrmSrc *src = GST_DRM_SRC (basesrc);
- return src->seekable;
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ return src->seekable;
}
/**
* This function does the following:
*/
static gboolean gst_drm_src_get_size (GstBaseSrc * basesrc, guint64 * size)
{
- struct stat stat_results;
- GstDrmSrc *src = GST_DRM_SRC (basesrc);
- unsigned int offset;
+ struct stat stat_results;
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ unsigned int offset;
+
+ /* Gets the filesize for drm file by using seek oprations */
+ if(src->is_oma==TRUE) {
+ drm_util_seek (src->hfile, 0, DRM_SEEK_END);
+ if (drm_util_tell(src->hfile, &offset) == TRUE) {
+ /* FIXME : drm doesn't support 64 */
+ *size = offset;
+ }
+ drm_util_seek (src->hfile, 0, DRM_SEEK_SET);
+ src->read_position = 0;
+ return TRUE;
+ }
+
+ if (!src->seekable) {
+ GST_DEBUG_OBJECT (src, "non-seekable");
+ return FALSE;
+ }
- // 1. Gets the filesize for drm file by using seek oprations
+ /* Gets the file size for standard file by using statistics */
+ if (fstat (src->fd, &stat_results) < 0)
+ return FALSE;
- // 2. Gets the file size for standard file by using statistics
- if (fstat (src->fd, &stat_results) < 0)
- return FALSE;
- *size = stat_results.st_size;
- return TRUE;
+ *size = stat_results.st_size;
+ GST_DEBUG_OBJECT (src, "size : %"G_GUINT64_FORMAT, *size);
+ return TRUE;
}
/**
* 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
*/
static gboolean gst_drm_src_start (GstBaseSrc * basesrc)
{
- GstDrmSrc *src = GST_DRM_SRC (basesrc);
- struct stat stat_results;
- off_t ret;
-PROFILE_FUNC_BEGIN;
- // 1. Checks the filename
- if (src->filename == NULL || src->filename[0] == '\0')
- {
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,("No file name specified for reading."), (NULL));
- return FALSE;
- }
- // 2. Opens the file and check statistics of the file
- GST_INFO_OBJECT (src, "opening file %s", src->filename);
- src->fd = open (src->filename, O_RDONLY | O_BINARY);
- if (src->fd < 0)
- {
- if(errno == ENOENT)
- {
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),("No such file \"%s\"", src->filename));
- return FALSE;
- }
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Could not open file \"%s\" for reading.", src->filename), GST_ERROR_SYSTEM);
- return FALSE;
- }
- if (fstat (src->fd, &stat_results) < 0)
- {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Could not get info on \"%s\".", src->filename), (NULL));
- close (src->fd);
- return FALSE;
- }
- if (S_ISDIR (stat_results.st_mode))
- {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("\"%s\" is a directory.", src->filename), (NULL));
- close (src->fd);
- return FALSE;
- }
- if (S_ISSOCK (stat_results.st_mode))
- {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("File \"%s\" is a socket.", src->filename), (NULL));
- close (src->fd);
- return FALSE;
- }
- src->read_position = 0;
-
- // 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 (ret < 0)
- {
- GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek "
- "failed: %s", g_strerror (errno));
- src->seekable = FALSE;
- }
- else
- {
- src->seekable = TRUE;
- }
- lseek (src->fd, 0, SEEK_SET);
- src->seekable = src->seekable && src->is_regular;
- PROFILE_FUNC_END;
- return TRUE;
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ struct stat stat_results;
+ drm_result_e drm_result;
+ drm_file_type_e file_type;
+ off_t ret;
+
+ /* Checks the filename */
+ if (src->filename == NULL || src->filename[0] == '\0') {
+ GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,("No file name specified for reading."), (NULL));
+ return FALSE;
+ }
+
+ /* Opens the file and check statistics of the file */
+ GST_INFO_OBJECT (src, "opening file %s", src->filename);
+ src->fd = open (src->filename, O_RDONLY | O_BINARY);
+ if (src->fd < 0) {
+ if(errno == ENOENT) {
+ GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),("No such file \"%s\"", src->filename));
+ return FALSE;
+ }
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Could not open file \"%s\" for reading.", src->filename), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+
+ if (fstat (src->fd, &stat_results) < 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Could not get info on \"%s\".", src->filename), (NULL));
+ close (src->fd);
+ return FALSE;
+ }
+
+ if (S_ISDIR (stat_results.st_mode)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("\"%s\" is a directory.", src->filename), (NULL));
+ close (src->fd);
+ return FALSE;
+ }
+
+ if (S_ISSOCK (stat_results.st_mode)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("File \"%s\" is a socket.", src->filename), (NULL));
+ close (src->fd);
+ return FALSE;
+ }
+
+ src->read_position = 0;
+
+
+ /* Checks whether DRM file or not.*/
+ drm_result = drm_get_file_type (src->filename, &file_type);
+ if (drm_result != DRM_RETURN_SUCCESS) {
+ GST_ERROR_OBJECT (src,"Error in drm_get_file_type(), error=%d", drm_result);
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT (src, "file_path = [%s], file_type = [%d]", src->filename, file_type);
+
+#if 0
+ if (DRM_TYPE_UNDEFINED != file_type) {
+ drm_bool_type_e is_drm = DRM_FALSE;
+ if (drm_is_drm_file(src->filename, &is_drm) == DRM_RETURN_SUCCESS) {
+ if (is_drm == DRM_TRUE) {
+ src->is_drm = TRUE;
+ }
+ }
+ GST_DEBUG_OBJECT (src, "is drm = [%d]", src->is_drm);
+ }
+#endif
+
+ /* We handles as DRM file if it is drm with OMA type */
+ if (file_type == DRM_TYPE_OMA_V1) { /* FIMXE: what about DRM_TYPE_OMA_V2 */
+ // Gets the DRM_FILE_HANDLE and sets the drm, seekable and regular flags.
+ drm_util_seek (src->hfile, 0, DRM_SEEK_END);
+ drm_util_seek (src->hfile, 0, DRM_SEEK_SET);
+
+ src->seekable = TRUE;
+ src->is_regular = TRUE;
+ src->is_oma = TRUE;
+
+ LOG_TRACE("Exit");
+ return TRUE;
+ }
+
+ if (file_type == DRM_TYPE_PLAYREADY || file_type == DRM_TYPE_PLAYREADY_ENVELOPE) { /* FIXME: what is envelope?? */
+ src->is_playready = TRUE;
+ src->event_posted = FALSE;
+ }
+
+ /* 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 (ret < 0) {
+ GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek failed: %s", g_strerror (errno));
+ src->seekable = FALSE;
+ } else {
+ src->seekable = TRUE;
+ }
+
+ lseek (src->fd, 0, SEEK_SET);
+ src->seekable = src->seekable && src->is_regular;
+
+
+ return TRUE;
}
/**
* This function does the following:
*/
static gboolean gst_drm_src_stop (GstBaseSrc * basesrc)
{
- GstDrmSrc *src = GST_DRM_SRC (basesrc);
-
- // 1. Closes the file desciptor and resets the flags
- if(src->fd > 0)
- close (src->fd);
- src->fd = 0;
- src->is_regular = FALSE;
-// PROFILE_SHOW_RESULT;
- return TRUE;
+ GstDrmSrc *src = GST_DRM_SRC (basesrc);
+ // 1. Closes the file desciptor and resets the flags
+ if(src->fd > 0) {
+#ifdef CONTROL_PAGECACHE
+ DRMSRC_FADVISE_DONT(src->fd, 0, 0);
+ src->accum = 0;
+#endif
+ close (src->fd);
+ }
+
+ src->fd = 0;
+ src->is_regular = FALSE;
+ src->event_posted = FALSE;
+ src->is_playready = FALSE;
+
+ return TRUE;
}
/**
*
static GstURIType gst_drm_src_uri_get_type (void)
{
- return GST_URI_SRC;
+ return GST_URI_SRC;
}
/**
static gchar ** gst_drm_src_uri_get_protocols (void)
{
- static gchar *protocols[] = { "file", NULL };
- return protocols;
+ static gchar *protocols[] = { "file", NULL };
+ return protocols;
}
/**
*
*/
static const gchar * gst_drm_src_uri_get_uri (GstURIHandler *handler)
{
- GstDrmSrc *src = GST_DRM_SRC (handler);
- return src->uri;
+ GstDrmSrc *src = GST_DRM_SRC (handler);
+ return src->uri;
}
/**
* This function does the following:
*/
static gboolean gst_drm_src_uri_set_uri (GstURIHandler *handler, const gchar * uri)
{
- gchar *protocol, *location;
- gboolean ret;
- GstDrmSrc *src = GST_DRM_SRC (handler);
- // 1. Checks the protocol
- protocol = gst_uri_get_protocol (uri);
- if (strcmp (protocol, "file") != 0)
- {
- g_free (protocol);
- return FALSE;
- }
- g_free (protocol);
- if (g_str_has_prefix (uri, "file://localhost/"))
- {
- char *tmp;
- tmp = g_strconcat ("file://", uri + 16, NULL);
- location = gst_uri_get_location (tmp);
- g_free (tmp);
- }
- else if (strcmp (uri, "file://") == 0)
- {
- gst_drm_src_set_location (src, NULL);
- return TRUE;
- }
- else
- {
- location = gst_uri_get_location (uri);
- }
- if (!location)
- return FALSE;
- // 2. Checks the whether it is absolute or not
- if (!g_path_is_absolute (location))
- {
- g_free (location);
- return FALSE;
- }
- // 3 sets the location
- ret = gst_drm_src_set_location (src, location);
- g_free (location);
- return ret;
+ gchar *location, *hostname = NULL;
+ gboolean ret = FALSE;
+ GstDrmSrc *src = GST_DRM_SRC (handler);
+ GError *error = NULL;
+
+ if (strcmp (uri, "file://") == 0) {
+ /* Special case for "file://" as this is used by some applications
+ * to test with gst_element_make_from_uri if there's an element
+ * that supports the URI protocol. */
+ gst_drm_src_set_location (src, NULL);
+ return TRUE;
+ }
+
+ location = g_filename_from_uri (uri, &hostname, &error);
+
+ if (!location || error) {
+ if (error) {
+ GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc: %s", uri,
+ error->message);
+ g_error_free (error);
+ } else {
+ GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc", uri);
+ }
+ goto beach;
+ }
+
+ if ((hostname) && (strcmp (hostname, "localhost"))) {
+ /* Only 'localhost' is permitted */
+ GST_WARNING_OBJECT (src, "Invalid hostname '%s' for filesrc", hostname);
+ goto beach;
+ }
+
+ ret = gst_drm_src_set_location (src, location);
+
+beach:
+ if (location)
+ g_free (location);
+ if (hostname)
+ g_free (hostname);
+
+ return ret;
}
+
+
/**
* This function does the following:
* 1. Assignes the function pointer for URI related stuff
*/
static void gst_drm_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
- GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
- // 1. Assigning the function pointer for URI related stuff
- iface->get_type = gst_drm_src_uri_get_type;
- iface->get_protocols = gst_drm_src_uri_get_protocols;
- iface->get_uri = gst_drm_src_uri_get_uri;
- iface->set_uri = gst_drm_src_uri_set_uri;
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ /* Assigning the function pointer for URI related stuff */
+ iface->get_type = gst_drm_src_uri_get_type;
+ iface->get_protocols = gst_drm_src_uri_get_protocols;
+ iface->get_uri = gst_drm_src_uri_get_uri;
+ iface->set_uri = gst_drm_src_uri_set_uri;
}
/**
* This function does the following:
*/
static gboolean plugin_init(GstPlugin* i_pPlugin)
{
- return gst_element_register(i_pPlugin, "drmsrc", GST_RANK_NONE, GST_TYPE_DRM_SRC);;
+ return gst_element_register(i_pPlugin, "drmsrc", GST_RANK_NONE, GST_TYPE_DRM_SRC);
}
/**
* This function does the following:
*
*/
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "drmsrc",
- "Plugin to read data from standad/DRM File",
- plugin_init,
- VERSION,
- "LGPL",
- "Samsung Electronics Co",
- "http://www.samsung.com/")
+ GST_VERSION_MINOR,
+ "drmsrc",
+ "Plugin to read data from standad/DRM File",
+ plugin_init,
+ VERSION,
+ "LGPL",
+ "Samsung Electronics Co",
+ "http://www.samsung.com/")
#include <sys/types.h>
#include <gst/gst.h>
#include <gst/base/gstbasesrc.h>
+#include <drm_trusted_client_types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define O_BINARY (0)
#endif
-#define ENABLE_PROFILING_INFO
-#ifdef ENABLE_PROFILING_INFO
-#define PROFILE_FUNC_BEGIN\
- char *newname; \
- newname=__FUNCTION__; \
- gst_ta_accum_item_begin(newname,FALSE,__FILE__,__LINE__);
-
-#define PROFILE_FUNC_END \
- gst_ta_accum_item_end(newname,FALSE,__FILE__,__LINE__);
-
-#define PROFILE_BLOCK_BEGIN(name) gst_ta_accum_item_begin(name,FALSE,__FILE__,__LINE__);
-#define PROFILE_BLOCK_END(name) gst_ta_accum_item_end(name,FALSE,__FILE__,__LINE__);
-#define PROFILE_INIT gst_ta_init();
-#define PROFILE_SHOW_RESULT gst_ta_accum_show_result(0);//MMTA_SHOW_FILE);
-#define PROFILE_CLEAR_DATA gst_ta_release();
-#else
-#define PROFILE_FUNC_BEGIN
-#define PROFILE_FUNC_END
-#define PROFILE_BLOCK_BEGIN(name)
-#define PROFILE_BLOCK_END(name)
-#define PROFILE_INIT
-#define PROFILE_SHOW_RESULT
-#define PROFILE_CLEAR_DATA
-#endif
G_BEGIN_DECLS
#define GST_TYPE_DRM_SRC (gst_drm_src_get_type())
gchar *uri;
gint fd;
guint64 read_position;
- gboolean seekable;
+ gboolean seekable;
gboolean is_regular;
+ gboolean is_drm; // flag indicating drm file
+ DRM_DECRYPT_HANDLE hfile;
+ gboolean is_playready;
+ gboolean is_oma;
+ gboolean event_posted;
+ gboolean isopen;
+#ifdef CONTROL_PAGECACHE
+ guint64 accum;
+#endif
};
struct _GstDrmSrcClass
{\
g_object_set(G_OBJECT(x_queue), \
"max-size-bytes", (guint)x_byte, \
- "max-size-buffers", (guint)x_buffer, \
+ "max-size-buffers", (guint)x_buffer, \
"max-size-time", (guint64)(x_time*GST_SECOND), \
NULL); \
GST_INFO("Set to [%s], max [%d] byte, max [%d] buffer, max [%d] time(sec) ", GST_OBJECT_NAME(x_queue), x_byte, x_buffer, x_time);\
}
#else
-#define ENCODER_QUEUE_SET(x_queue, x_byte, x_buffer, x_time)
+#define ENCODER_QUEUE_SET(x_queue, x_byte, x_buffer, x_time)
#endif
-#define _GST_PAD_LINK_UNREF( srcpad, sinkpad, if_fail_goto )\
+#define _GST_PAD_LINK_UNREF(srcpad, sinkpad, if_fail_goto)\
{\
- GstPadLinkReturn ret = _GST_PAD_LINK( srcpad, sinkpad );\
- gst_object_unref( srcpad ); srcpad = NULL;\
- gst_object_unref( sinkpad ); sinkpad = NULL;\
- if( ret != GST_PAD_LINK_OK) goto if_fail_goto;\
+ GstPadLinkReturn ret = _GST_PAD_LINK(srcpad, sinkpad);\
+ if(ret != GST_PAD_LINK_OK) { \
+ GstObject *src_parent = gst_pad_get_parent(srcpad);\
+ GstObject *sink_parent = gst_pad_get_parent(sinkpad);\
+ char *src_name = NULL;\
+ char *sink_name = NULL;\
+ g_object_get((GObject *)src_parent, "name", &src_name, NULL);\
+ g_object_get((GObject *)sink_parent, "name", &sink_name, NULL);\
+ GST_ERROR("src[%s] - sink[%s] link failed", src_name, sink_name);\
+ gst_object_unref(src_parent); src_parent = NULL;\
+ gst_object_unref(sink_parent); sink_parent = NULL;\
+ if (src_name) {\
+ free(src_name); src_name = NULL;\
+ }\
+ if (sink_name) {\
+ free(sink_name); sink_name = NULL;\
+ }\
+ gst_object_unref(srcpad); srcpad = NULL;\
+ gst_object_unref(sinkpad); sinkpad = NULL;\
+ goto if_fail_goto;\
+ }\
+ gst_object_unref(srcpad); srcpad = NULL;\
+ gst_object_unref(sinkpad); sinkpad = NULL;\
}
-#define _GST_PAD_UNLINK_UNREF( srcpad, sinkpad)\
+#define _GST_PAD_UNLINK_UNREF(srcpad, sinkpad)\
{\
- gst_pad_unlink( srcpad, sinkpad );\
- gst_object_unref( srcpad ); srcpad = NULL;\
- gst_object_unref( sinkpad ); sinkpad = NULL;\
+ gst_pad_unlink(srcpad, sinkpad);\
+ gst_object_unref(srcpad); srcpad = NULL;\
+ gst_object_unref(sinkpad); sinkpad = NULL;\
}
-#define DEFAULT_PROP_PROFILE 0
-#define DEFAULT_PROP_HIGH_SPEED 0
-#define DEFAULT_PROP_VENC_NAME "ffenc_h263"
-#define DEFAULT_PROP_AENC_NAME "secenc_amr"
-#define DEFAULT_PROP_IENC_NAME "jpegenc"
-#define DEFAULT_PROP_MUX_NAME "ffmux_3gp"
+#define DEFAULT_PROP_PROFILE 0
+#define DEFAULT_PROP_HIGH_SPEED 0
+#define DEFAULT_PROP_VENC_NAME "ffenc_h263"
+#define DEFAULT_PROP_AENC_NAME "secenc_amr"
+#define DEFAULT_PROP_IENC_NAME "jpegenc"
+#define DEFAULT_PROP_MUX_NAME "ffmux_3gp"
+#define DEFAULT_PROP_VCONV_NAME "ffmpegcolorspace"
/* props */
enum
//elements name
PROP_VENC_NAME,
PROP_AENC_NAME,
- PROP_IENC_NAME,
- PROP_MUX_NAME,
+ PROP_IENC_NAME,
+ PROP_MUX_NAME,
+ PROP_VCONV_NAME,
//caps
PROP_VCAPS,
PROP_ACAPS,
PROP_AUTO_COLORSPACE,
PROP_BLOCK,
PROP_PAUSE,
- PROP_VENC_QUEUE,
+ PROP_VENC_QUEUE,
PROP_AENC_QUEUE,
//elements pointer
PROP_VIDEO_ENC,
PROP_AUDIO_ENC,
PROP_IMAGE_ENC,
PROP_MUX,
+ PROP_VIDEO_CONV,
//options
PROP_USE_VIDEO_TOGGLE,
};
enum
{
SIGNAL_STREAM_BLOCK,
- SIGNAL_STREAM_UNBLOCK,
+ SIGNAL_STREAM_UNBLOCK,
SIGNAL_STREAM_PAUSE,
- SIGNAL_STREAM_RESUME,
+ SIGNAL_STREAM_RESUME,
LAST_SIGNAL
};
#endif
ENCODEBIN_ELEMENT_VENC,
ENCODEBIN_ELEMENT_AENC,
ENCODEBIN_ELEMENT_IENC,
- ENCODEBIN_ELEMENT_MUX
+ ENCODEBIN_ELEMENT_MUX,
+ ENCODEBIN_ELEMENT_VIDEO_CONV
}GstEncodeBinElement;
typedef enum {
"height = (int) [ 1, 2147483647 ]," \
"framerate = (fraction) [ 0/1, 2147483647/1 ]," \
"format = (fourcc) AYUV " \
-)
+)
static GstStaticPadTemplate encoder_bin_src_template =
GstClockTime now = gst_util_get_timestamp ();
- g_object_get(G_OBJECT(queue), "current-level-bytes", &queue_size,
+ g_object_get(G_OBJECT(queue), "current-level-bytes", &queue_size,
"current-level-buffers", &queue_bufnum,
// "current-level-time", &queue_time,
NULL);
GST_ELEMENT_WARNING (encodebin, STREAM, TOO_LAZY,
- ("[%" GST_TIME_FORMAT "][%s], [%u b], [%u]",
- GST_TIME_ARGS(now), GST_OBJECT_NAME(queue), queue_size, queue_bufnum), (NULL));
+ ("[%" GST_TIME_FORMAT "][%s], [%u b], [%u]",
+ GST_TIME_ARGS(now), GST_OBJECT_NAME(queue), queue_size, queue_bufnum), (NULL));
#else
GST_ELEMENT_WARNING (encodebin, STREAM, TOO_LAZY,
- ("%s overrun", GST_OBJECT_NAME(queue)), (NULL));
+ ("%s overrun", GST_OBJECT_NAME(queue)), (NULL));
#endif
}
break;
case PROP_IENC_NAME:
g_value_set_string (value, encodebin->ienc_name);
- break;
+ break;
case PROP_MUX_NAME:
g_value_set_string (value, encodebin->mux_name);
- break;
+ break;
+ case PROP_VCONV_NAME:
+ g_value_set_string (value, encodebin->vconv_name);
+ break;
//caps
case PROP_VCAPS:
gst_value_set_caps (value, encodebin->vcaps);
// g_value_set_boolean (value, encodebin->use_venc_queue);
if((encodebin->video_encode_queue == NULL) && (encodebin->profile == GST_ENCODE_BIN_PROFILE_AV)) {
encodebin->video_encode_queue = gst_element_factory_make ("queue", "video_encode_queue");
- if(encodebin->video_encode_queue != NULL)
+ if(encodebin->video_encode_queue != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->video_encode_queue);
}
- g_value_set_object (value, encodebin->video_encode_queue);
- break;
+ g_value_set_object (value, encodebin->video_encode_queue);
+ break;
case PROP_AENC_QUEUE:
// g_value_set_boolean (value, encodebin->use_aenc_queue);
if((encodebin->audio_encode_queue == NULL) && (encodebin->profile <= GST_ENCODE_BIN_PROFILE_AUDIO)) {
encodebin->audio_encode_queue = gst_element_factory_make ("queue", "audio_encode_queue");
- if(encodebin->audio_encode_queue != NULL)
+ if(encodebin->audio_encode_queue != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->audio_encode_queue);
}
- g_value_set_object (value, encodebin->audio_encode_queue);
- break;
+ g_value_set_object (value, encodebin->audio_encode_queue);
+ break;
//elements pointer
case PROP_VIDEO_ENC:
if((encodebin->video_encode == NULL) && (encodebin->profile == GST_ENCODE_BIN_PROFILE_AV)) {
- encodebin->video_encode = gst_element_factory_make (encodebin->venc_name, "video_encode");
- if(encodebin->video_encode != NULL)
+ encodebin->video_encode = gst_element_factory_make (encodebin->venc_name, NULL);
+ if(encodebin->video_encode != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->video_encode);
}
g_value_set_object (value, encodebin->video_encode);
case PROP_AUDIO_ENC:
if(encodebin->audio_encode == NULL && (encodebin->profile <= GST_ENCODE_BIN_PROFILE_AUDIO)) {
encodebin->audio_encode = gst_element_factory_make (encodebin->aenc_name, "audio_encode");
- if(encodebin->audio_encode != NULL)
+ if(encodebin->audio_encode != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->audio_encode);
}
g_value_set_object (value, encodebin->audio_encode);
case PROP_IMAGE_ENC:
if(encodebin->image_encode == NULL && (encodebin->profile == GST_ENCODE_BIN_PROFILE_IMAGE)) {
encodebin->image_encode = gst_element_factory_make (encodebin->ienc_name, "image_encode");
- if(encodebin->image_encode != NULL)
+ if(encodebin->image_encode != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->image_encode);
}
g_value_set_object (value, encodebin->image_encode);
- break;
+ break;
case PROP_MUX:
if(encodebin->mux == NULL && (encodebin->profile <= GST_ENCODE_BIN_PROFILE_AUDIO)) {
encodebin->mux = gst_element_factory_make (encodebin->mux_name, "mux");
- if(encodebin->mux != NULL)
+ if(encodebin->mux != NULL)
gst_bin_add(GST_BIN(encodebin), encodebin->mux);
}
g_value_set_object (value, encodebin->mux);
break;
+ case PROP_VIDEO_CONV:
+ if(encodebin->color_space == NULL && (encodebin->profile != GST_ENCODE_BIN_PROFILE_AUDIO)) {
+ encodebin->color_space = gst_element_factory_make (encodebin->vconv_name, "video_convert");
+ if(encodebin->color_space != NULL)
+ gst_bin_add(GST_BIN(encodebin), encodebin->color_space);
+ }
+ g_value_set_object (value, encodebin->color_space);
+ break;
case PROP_USE_VIDEO_TOGGLE:
g_value_set_boolean( value, encodebin->use_video_toggle );
break;
if(encodebin->profile != newprofile) {
gst_encode_bin_change_profile(encodebin, newprofile);
encodebin->profile = newprofile;
- }
+ }
*/
break;
case PROP_HIGH_SPEED:
new_name = g_value_get_string (value);
if(encodebin->video_encode == NULL) {
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VENC, new_name))
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VENC, new_name))
encodebin->venc_name = g_strdup (new_name);
} else {
if(strcmp (encodebin->venc_name, new_name)) {
gst_encode_bin_remove_element(encodebin, encodebin->video_encode);
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VENC, new_name))
- encodebin->venc_name = g_strdup (new_name);
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VENC, new_name))
+ encodebin->venc_name = g_strdup (new_name);
}
}
break;
- }
+ }
case PROP_AENC_NAME: {
const gchar *new_name;
if(encodebin->profile > GST_ENCODE_BIN_PROFILE_AUDIO) {
new_name = g_value_get_string (value);
if(encodebin->audio_encode == NULL) {
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_AENC, new_name))
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_AENC, new_name))
encodebin->aenc_name = g_strdup (new_name);
} else {
if(strcmp (encodebin->aenc_name, new_name)) {
gst_encode_bin_remove_element(encodebin, encodebin->audio_encode);
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_AENC, new_name))
- encodebin->aenc_name = g_strdup (new_name);
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_AENC, new_name))
+ encodebin->aenc_name = g_strdup (new_name);
}
}
break;
new_name = g_value_get_string (value);
if(encodebin->image_encode == NULL) {
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_IENC, new_name))
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_IENC, new_name))
encodebin->ienc_name = g_strdup (new_name);
} else {
if(strcmp (encodebin->ienc_name, new_name)) {
gst_encode_bin_remove_element(encodebin, encodebin->image_encode);
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_IENC, new_name))
- encodebin->ienc_name = g_strdup (new_name);
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_IENC, new_name))
+ encodebin->ienc_name = g_strdup (new_name);
}
}
- break;
+ break;
}
case PROP_MUX_NAME: {
const gchar *new_name;
new_name = g_value_get_string (value);
if(encodebin->mux == NULL) {
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_MUX, new_name))
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_MUX, new_name))
encodebin->mux_name = g_strdup (new_name);
} else {
if(strcmp (encodebin->mux_name, new_name)) {
gst_encode_bin_remove_element(encodebin, encodebin->mux);
- if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_MUX, new_name))
- encodebin->mux_name = g_strdup (new_name);
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_MUX, new_name))
+ encodebin->mux_name = g_strdup (new_name);
+ }
+ }
+ break;
+ }
+ case PROP_VCONV_NAME: {
+ const gchar *new_name;
+ if (encodebin->profile == GST_ENCODE_BIN_PROFILE_AUDIO) {
+ GST_WARNING_OBJECT(encodebin, "Profile isn't match");
+ break;
+ }
+ new_name = g_value_get_string(value);
+
+ if (encodebin->color_space == NULL) {
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VIDEO_CONV, new_name))
+ encodebin->vconv_name = g_strdup (new_name);
+ } else {
+ if(strcmp (encodebin->vconv_name, new_name)) {
+ gst_encode_bin_remove_element(encodebin, encodebin->color_space);
+ if(gst_encode_bin_add_element_by_name(encodebin, ENCODEBIN_ELEMENT_VIDEO_CONV, new_name))
+ encodebin->vconv_name = g_strdup (new_name);
}
}
- break;
+ break;
}
//caps
case PROP_VCAPS: {
gboolean newval = g_value_get_boolean (value);
if(encodebin->block != newval) {
if(!gst_encode_bin_block(encodebin, newval)) {
-#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
+#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
if(newval) {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_BLOCK], 0, FALSE);
} else {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_UNBLOCK], 0, FALSE);
}
-#endif
+#endif
break;
- }
+ }
}
-#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
+#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
if(newval) {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_BLOCK], 0, TRUE);
} else {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_UNBLOCK], 0, TRUE);
}
-#endif
+#endif
break;
}
case PROP_PAUSE: {
if(!gst_encode_bin_pause(encodebin, newval))
break;
}
-#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
+#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
if(newval) {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_PAUSE], 0, TRUE);
} else {
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_RESUME], 0, TRUE);
}
-#endif
+#endif
break;
}
case PROP_VENC_QUEUE:
}
break;
}
- break;
+ break;
case PROP_AENC_QUEUE:
// encodebin->use_aenc_queue = g_value_get_boolean (value);
{
}
break;
}
- break;
- case PROP_VIDEO_ENC:
+ break;
+ case PROP_VIDEO_ENC:
{
GstElement *newelement = g_value_get_object (value);
if(encodebin->profile > GST_ENCODE_BIN_PROFILE_AV) {
}
break;
}
- case PROP_AUDIO_ENC:
+ case PROP_AUDIO_ENC:
{
GstElement *newelement = g_value_get_object (value);
if(encodebin->profile > GST_ENCODE_BIN_PROFILE_AUDIO) {
}
break;
}
+ case PROP_VIDEO_CONV: {
+ GstElement *newelement = g_value_get_object (value);
+ if(encodebin->profile == GST_ENCODE_BIN_PROFILE_AUDIO) {
+ GST_WARNING_OBJECT(encodebin, "Profile isn't match, change profile first!");
+ break;
+ }
+ if(newelement != NULL) {
+ gst_encode_bin_remove_element(encodebin, encodebin->color_space);
+ encodebin->color_space = newelement;
+ gst_object_ref (encodebin->color_space);
+ gst_object_sink (GST_OBJECT_CAST (encodebin->color_space));
+ gst_bin_add(GST_BIN(encodebin), encodebin->color_space);
+ }
+ break;
+ }
case PROP_USE_VIDEO_TOGGLE:
encodebin->use_video_toggle = g_value_get_boolean( value );
break;
g_return_val_if_fail (GST_IS_ENCODE_BIN (element), NULL);
encodebin = GST_ENCODE_BIN (element);
-
+
/* FIXME */
if (templ == gst_element_class_get_pad_template (klass, "audio")) {
if (encodebin->profile <= GST_ENCODE_BIN_PROFILE_AUDIO) {
gst_encode_bin_init_audio_elements(element, NULL); //??
-
+
if(encodebin->audio_sinkpad == NULL)
{
pad = gst_element_get_static_pad (encodebin->audio_queue, "sink");
}
else
{
- GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: audio pad is aleady existed, return existing audio pad\n");
+ GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: audio pad is aleady existed, return existing audio pad\n");
return encodebin->audio_sinkpad;
}
-
+
gst_element_add_pad (element, encodebin->audio_sinkpad);
gst_pad_set_setcaps_function (encodebin->audio_sinkpad,
GST_DEBUG_FUNCPTR (gst_encode_bin_audsink_set_caps));
if(encodebin->video_sinkpad == NULL)
{
pad = gst_element_get_static_pad (encodebin->video_queue, "sink");
- encodebin->video_sinkpad = gst_ghost_pad_new ("video", pad);
+ encodebin->video_sinkpad = gst_ghost_pad_new ("video", pad);
gst_object_unref(pad);
pad = NULL;
}
else
{
- GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: video pad is aleady existed, return existing video pad\n");
+ GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: video pad is aleady existed, return existing video pad\n");
return encodebin->video_sinkpad;
}
-
+
gst_element_add_pad (element, encodebin->video_sinkpad);
gst_pad_set_setcaps_function (encodebin->video_sinkpad,
GST_DEBUG_FUNCPTR (gst_encode_bin_vidsink_set_caps));
pad = gst_element_get_static_pad (encodebin->image_queue, "sink");
encodebin->image_sinkpad = gst_ghost_pad_new ("image", pad);
gst_object_unref(pad);
- pad = NULL;
+ pad = NULL;
}
else
{
- GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: image pad is aleady existed, return existing image pad\n");
+ GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: image pad is aleady existed, return existing image pad\n");
return encodebin->image_sinkpad;
}
-
+
gst_element_add_pad (element, encodebin->image_sinkpad);
gst_pad_set_setcaps_function (encodebin->image_sinkpad,
GST_DEBUG_FUNCPTR (gst_encode_bin_imgsink_set_caps));
} else {
if (encodebin->profile == GST_ENCODE_BIN_PROFILE_IMAGE) {
gst_encode_bin_init_image_elements(element, NULL); //??
-
+
if(encodebin->image_sinkpad == NULL)
{
pad = gst_element_get_static_pad (encodebin->image_queue, "sink");
encodebin->image_sinkpad = gst_ghost_pad_new ("image", pad);
gst_object_unref(pad);
- pad = NULL;
+ pad = NULL;
}
else
{
- GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: image pad is aleady existed, return existing image pad\n");
+ GST_WARNING_OBJECT (GST_IS_ENCODE_BIN (element), "encodebin: image pad is aleady existed, return existing image pad\n");
return encodebin->image_sinkpad;
}
-
+
gst_element_add_pad (element, encodebin->image_sinkpad);
gst_pad_set_setcaps_function (encodebin->image_sinkpad,
GST_DEBUG_FUNCPTR (gst_encode_bin_imgsink_set_caps));
return encodebin->image_sinkpad;
} else
- return NULL;
- }
+ return NULL;
+ }
}
static void
gst_encode_bin_class_init (GstEncodeBinClass *klass)
{
-
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gstbin_klass = (GstBinClass *) klass;
-
+
parent_class = g_type_class_peek_parent (klass);
gobject_klass->get_property = gst_encode_bin_get_property;
gobject_klass->set_property = gst_encode_bin_set_property;
- gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_encode_bin_dispose);
+ gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_encode_bin_dispose);
gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_encode_bin_finalize);
G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, PROP_HIGH_SPEED,
- g_param_spec_int ("high-speed-fps", "high speed rec. fps", "framerate for high speed recording", 0, G_MAXINT,
+ g_param_spec_int ("high-speed-fps", "high speed rec. fps", "framerate for high speed recording", 0, G_MAXINT,
DEFAULT_PROP_HIGH_SPEED, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, PROP_VENC_NAME,
g_param_spec_string ("mux-name", "muxer name", "the name of muxer to use",
DEFAULT_PROP_MUX_NAME, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, PROP_VCONV_NAME,
+ g_param_spec_string ("vconv-name", "Video converter name", "the name of video color converter to use",
+ DEFAULT_PROP_VCONV_NAME, G_PARAM_READWRITE));
+
g_object_class_install_property (gobject_klass, PROP_VCAPS,
g_param_spec_boxed ("vcaps", "caps for video","caps for video recording",
GST_TYPE_CAPS, G_PARAM_READWRITE));
#if 0
g_object_class_install_property (gobject_klass, PROP_VENC_QUEUE,
g_param_spec_boolean ("use-venc-queue", "use queue between venc and mux",
- "add queue between venc and mux(only for custom optimization)", FALSE, G_PARAM_READWRITE));
+ "add queue between venc and mux(only for custom optimization)", FALSE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, PROP_AENC_QUEUE,
g_param_spec_boolean ("use-aenc-queue", "use queue between aenc and mux",
- "add queue between aenc and mux(only for custom optimization)", FALSE, G_PARAM_READWRITE));
+ "add queue between aenc and mux(only for custom optimization)", FALSE, G_PARAM_READWRITE));
#else
g_object_class_install_property (gobject_klass, PROP_VENC_QUEUE,
g_param_spec_object ("use-venc-queue", "Video Encoder queue",
"the muxer element to use",
GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, PROP_VIDEO_CONV,
+ g_param_spec_object ("video-convert", "Video converter",
+ "the video converter element to use",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE));
+
g_object_class_install_property (gobject_klass, PROP_USE_VIDEO_TOGGLE,
g_param_spec_boolean ("use-video-toggle", "Use video toggle",
"Use video toggle while AV recording", TRUE, G_PARAM_READWRITE));
#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
gst_encode_bin_signals[SIGNAL_STREAM_BLOCK] =
g_signal_new ("stream-block", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_block),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_block),
NULL, NULL, gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
gst_encode_bin_signals[SIGNAL_STREAM_UNBLOCK] =
g_signal_new ("stream-unblock", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_unblock),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_unblock),
NULL, NULL, gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
gst_encode_bin_signals[SIGNAL_STREAM_PAUSE] =
g_signal_new ("stream-pause", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_pause),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_pause),
NULL, NULL, gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
gst_encode_bin_signals[SIGNAL_STREAM_RESUME] =
g_signal_new ("stream-resume", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_resume),
- NULL, NULL, gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstEncodeBinClass, stream_resume),
+ NULL, NULL, gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
#endif
gst_element_class_add_pad_template (gstelement_klass,
gstelement_klass->request_new_pad =
GST_DEBUG_FUNCPTR (gst_encode_bin_request_new_pad);
- gstelement_klass->release_pad =
+ gstelement_klass->release_pad =
GST_DEBUG_FUNCPTR (gst_encode_bin_release_pad);
gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_encode_bin_change_state);
{
encodebin->mutex = g_mutex_new();
- if(encodebin->srcpad == NULL) {
+ if (encodebin->srcpad == NULL) {
encodebin->srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
gst_element_add_pad (GST_ELEMENT(encodebin), encodebin->srcpad);
- }
+ }
encodebin->video_sinkpad = NULL;
encodebin->audio_sinkpad = NULL;
encodebin->image_sinkpad = NULL;
encodebin->mux_audio_sinkpad = NULL;
encodebin->mux_video_sinkpad = NULL;
-
+
encodebin->profile = DEFAULT_PROP_PROFILE;
encodebin->fps = 0;
encodebin->high_speed_fps = DEFAULT_PROP_HIGH_SPEED;
encodebin->auto_audio_resample = TRUE;
encodebin->auto_color_space = TRUE;
encodebin->block = FALSE;
- encodebin->pause= FALSE;
+ encodebin->pause= FALSE;
encodebin->use_video_toggle = TRUE;
- encodebin->use_venc_queue= FALSE;
- encodebin->use_aenc_queue= FALSE;
+ encodebin->use_venc_queue= FALSE;
+ encodebin->use_aenc_queue= FALSE;
encodebin->venc_name = g_strdup(DEFAULT_PROP_VENC_NAME);
encodebin->aenc_name = g_strdup(DEFAULT_PROP_AENC_NAME);
- encodebin->ienc_name = g_strdup(DEFAULT_PROP_IENC_NAME);
- encodebin->mux_name = g_strdup(DEFAULT_PROP_MUX_NAME);
+ encodebin->ienc_name = g_strdup(DEFAULT_PROP_IENC_NAME);
+ encodebin->mux_name = g_strdup(DEFAULT_PROP_MUX_NAME);
+ encodebin->vconv_name = g_strdup(DEFAULT_PROP_VCONV_NAME);
encodebin->vcaps = gst_caps_new_any ();
encodebin->acaps = gst_caps_new_any ();
encodebin->icaps = gst_caps_new_any ();
encodebin->audio_queue = NULL;
- encodebin->video_queue = NULL;
- encodebin->video_encode_queue = NULL;
- encodebin->image_queue = NULL;
+ encodebin->video_queue = NULL;
+ encodebin->video_encode_queue = NULL;
+ encodebin->image_queue = NULL;
encodebin->audio_encode = NULL;
encodebin->video_encode = NULL;
- encodebin->image_encode = NULL;
+ encodebin->image_encode = NULL;
- encodebin->vcapsfilter = NULL;
- encodebin->acapsfilter = NULL;
- encodebin->icapsfilter = NULL;
+ encodebin->vcapsfilter = NULL;
+ encodebin->acapsfilter = NULL;
+ encodebin->icapsfilter = NULL;
- encodebin->video_toggle = NULL;
+ encodebin->video_toggle = NULL;
encodebin->image_toggle = NULL;
- encodebin->color_space = NULL;
- encodebin->audio_conv = NULL;
+ encodebin->color_space = NULL;
+ encodebin->audio_conv = NULL;
encodebin->audio_sample = NULL;
encodebin->mux = NULL;
encodebin->vsink_hs_probeid = 0;
encodebin->asink_probeid = 0;
encodebin->veque_sig_id = 0;
- encodebin->aeque_sig_id = 0;
+ encodebin->aeque_sig_id = 0;
}
-static void
-gst_encode_bin_dispose (GObject * object)
+static void
+gst_encode_bin_dispose (GObject * object)
{
GstEncodeBin *encodebin = GST_ENCODE_BIN (object);
encodebin->venc_name = NULL;
g_free(encodebin->aenc_name);
- encodebin->aenc_name = NULL;
+ encodebin->aenc_name = NULL;
g_free(encodebin->ienc_name);
encodebin->ienc_name = NULL;
g_free(encodebin->mux_name);
encodebin->mux_name = NULL;
+ g_free(encodebin->vconv_name);
+ encodebin->vconv_name = NULL;
gst_caps_replace (&encodebin->vcaps, NULL);
gst_caps_replace (&encodebin->acaps, NULL);
gst_caps_replace (&encodebin->icaps, NULL);
- if(encodebin->srcpad != NULL)
- {
- gst_element_remove_pad (GST_ELEMENT(encodebin), encodebin->srcpad);
+ if (encodebin->srcpad != NULL) {
+ gst_element_remove_pad(GST_ELEMENT(encodebin), encodebin->srcpad);
encodebin->srcpad = NULL;
}
-
G_OBJECT_CLASS (parent_class)->dispose (object);
-
-
encodebin->video_sinkpad = NULL;
encodebin->audio_sinkpad = NULL;
encodebin->image_sinkpad = NULL;
encodebin->mux_video_sinkpad = NULL;
encodebin->audio_queue = NULL;
- encodebin->video_queue = NULL;
- encodebin->image_queue = NULL;
+ encodebin->video_queue = NULL;
+ encodebin->image_queue = NULL;
encodebin->audio_encode = NULL;
encodebin->video_encode = NULL;
- encodebin->video_encode_queue = NULL;
- encodebin->image_encode = NULL;
+ encodebin->video_encode_queue = NULL;
+ encodebin->image_encode = NULL;
- encodebin->vcapsfilter = NULL;
- encodebin->acapsfilter = NULL;
- encodebin->icapsfilter = NULL;
+ encodebin->vcapsfilter = NULL;
+ encodebin->acapsfilter = NULL;
+ encodebin->icapsfilter = NULL;
- encodebin->video_toggle = NULL;
+ encodebin->video_toggle = NULL;
encodebin->image_toggle = NULL;
- encodebin->color_space = NULL;
- encodebin->audio_conv = NULL;
+ encodebin->color_space = NULL;
+ encodebin->audio_conv = NULL;
encodebin->audio_sample = NULL;
-
- if(encodebin->mux && GST_IS_ELEMENT(encodebin->mux))
- {
- int remain_count= 0 ;
+
+ if (encodebin->mux && GST_IS_ELEMENT(encodebin->mux)) {
+ int remain_count= 0;
remain_count = GST_OBJECT_REFCOUNT_VALUE(encodebin->mux);
- while(remain_count)
- {
+ while (remain_count) {
gst_object_unref(encodebin->mux);
remain_count--;
- }
+ }
}
+
encodebin->mux = NULL;
}
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_NULL:
- gst_encode_bin_unlink_elements(encode_bin);
- break;
+ gst_encode_bin_unlink_elements(encode_bin);
+ break;
default:
break;
}
{
GstEncodeBin *encodebin = GST_ENCODE_BIN (element);
GstPad *muxpad = NULL;
-
+
if(!pad_compare_name(pad, "video")) {
-#if 0
+#if 0
gst_encode_bin_remove_element(encodebin, encodebin->video_queue);
encodebin->video_queue = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->video_toggle);
gst_encode_bin_remove_element(encodebin, encodebin->vcapsfilter);
encodebin->vcapsfilter = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->video_encode_queue);
- encodebin->video_encode_queue = NULL;
+ encodebin->video_encode_queue = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->video_encode);
encodebin->video_encode = NULL;
- gst_element_release_request_pad(encodebin->mux, encodebin->mux_video_sinkpad);
+ gst_element_release_request_pad(encodebin->mux, encodebin->mux_video_sinkpad);
encodebin->mux_video_sinkpad = NULL;
if(encodebin->mux_audio_sinkpad == NULL) {
gst_encode_bin_remove_element(encodebin, encodebin->mux);
encodebin->mux = NULL;
}
- else
+ else
{
encodebin->mux_audio_sinkpad = NULL;
}
-#endif
+#endif
if(encodebin->mux_video_sinkpad != NULL)
- {
+ {
gst_element_release_request_pad(encodebin->mux, encodebin->mux_video_sinkpad);
encodebin->mux_video_sinkpad = NULL;
}
-
+
gst_pad_set_active (pad, FALSE); //??
gst_element_remove_pad(element, pad);
encodebin->video_sinkpad = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->acapsfilter);
encodebin->acapsfilter = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->audio_encode_queue);
- encodebin->audio_encode_queue = NULL;
+ encodebin->audio_encode_queue = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->audio_encode);
encodebin->audio_encode = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->mux);
encodebin->mux = NULL;
}
- else
+ else
{
encodebin->mux_video_sinkpad = NULL;
}
-#endif
+#endif
if(encodebin->mux_audio_sinkpad != NULL)
{
gst_element_release_request_pad(encodebin->mux, encodebin->mux_audio_sinkpad);
encodebin->mux_audio_sinkpad = NULL;
}
- gst_pad_set_active (pad, FALSE); //??
+ gst_pad_set_active (pad, FALSE); //??
gst_element_remove_pad(element, pad);
encodebin->audio_sinkpad = NULL;
} else {
encodebin->icapsfilter = NULL;
gst_encode_bin_remove_element(encodebin, encodebin->image_encode);
encodebin->image_encode = NULL;
-#endif
- gst_pad_set_active (pad, FALSE); //??
+#endif
+ gst_pad_set_active (pad, FALSE); //??
gst_element_remove_pad(element, pad);
encodebin->image_sinkpad = NULL;
}
return result;
}
-static gboolean
+static gboolean
gst_encode_bin_add_element_by_name (GstEncodeBin *encodebin, GstEncodeBinElement type, const gchar *name)
{
switch(type) {
- case ENCODEBIN_ELEMENT_VENC :
- encodebin->video_encode = gst_element_factory_make (name, "video_encode");
+ case ENCODEBIN_ELEMENT_VENC:
+ encodebin->video_encode = gst_element_factory_make (name, NULL);
if(encodebin->video_encode != NULL) {
gst_bin_add(GST_BIN(encodebin), encodebin->video_encode);
g_free(encodebin->venc_name);
+ encodebin->venc_name = NULL;
} else {
goto element_make_fail;
}
break;
- case ENCODEBIN_ELEMENT_AENC :
- encodebin->audio_encode = gst_element_factory_make (name, "audio_encode");
+ case ENCODEBIN_ELEMENT_AENC:
+ encodebin->audio_encode = gst_element_factory_make (name, "audio_encode");
if(encodebin->audio_encode != NULL) {
gst_bin_add(GST_BIN(encodebin), encodebin->audio_encode);
g_free(encodebin->aenc_name);
+ encodebin->aenc_name = NULL;
} else {
goto element_make_fail;
- }
+ }
break;
- case ENCODEBIN_ELEMENT_IENC :
- encodebin->image_encode = gst_element_factory_make (name, "image_encode");
+ case ENCODEBIN_ELEMENT_IENC:
+ encodebin->image_encode = gst_element_factory_make (name, "image_encode");
if(encodebin->image_encode != NULL) {
gst_bin_add(GST_BIN(encodebin), encodebin->image_encode);
g_free(encodebin->ienc_name);
+ encodebin->ienc_name = NULL;
} else {
goto element_make_fail;
- }
+ }
break;
- case ENCODEBIN_ELEMENT_MUX :
- encodebin->mux = gst_element_factory_make (name, "mux");
+ case ENCODEBIN_ELEMENT_MUX:
+ encodebin->mux = gst_element_factory_make (name, "mux");
if(encodebin->mux != NULL) {
gst_bin_add(GST_BIN(encodebin), encodebin->mux);
g_free(encodebin->mux_name);
+ encodebin->mux_name = NULL;
} else {
goto element_make_fail;
- }
+ }
break;
- default :
+ case ENCODEBIN_ELEMENT_VIDEO_CONV:
+ encodebin->color_space = gst_element_factory_make(name, "video_convert");
+ if (encodebin->color_space != NULL) {
+ gst_bin_add(GST_BIN(encodebin), encodebin->color_space);
+ g_free(encodebin->vconv_name);
+ encodebin->vconv_name = NULL;
+ } else {
+ goto element_make_fail;
+ }
+ break;
+ default:
GST_WARNING_OBJECT(encodebin, "Invalid element type = %d", type);
- break;
+ break;
}
return TRUE;
-
+
element_make_fail:
GST_WARNING_OBJECT(encodebin, "no such element factory \"%s\"!", name);
return FALSE;
}
#if 0 //disable unused function
-static gboolean
+static gboolean
gst_encode_bin_change_profile(GstEncodeBin *encodebin, gboolean newprofile)
{
}
-static void
+static void
gst_encode_bin_replace_element (GstEncodeBin *encodebin, gint type, GstElement * newelement)
{
if(newelement == NULL) {
gst_object_ref (encodebin->audio_encode);
gst_object_sink (GST_OBJECT_CAST (encodebin->audio_encode));
gst_bin_add(GST_BIN(encodebin), encodebin->audio_encode);
- break;
+ break;
case PROP_IMAGE_ENC:
gst_encode_bin_remove_element(encodebin, encodebin->image_encode);
encodebin->image_encode = newelement;
}
}
-static gboolean
+static gboolean
gst_encode_bin_replace_element_by_name(GstEncodeBin *encodebin, GstEncodeBinElement type, const gchar *name)
{
GstPad *sink1, *sink2, *src, *peersink1, *peersink2, *peersrc;
-
+
switch(type) {
case ENCODEBIN_ELEMENT_VENC:
if(encodebin->video_encode == NULL) {
}
}
}
-
+
if(gst_encode_bin_remove_element(encodebin, encodebin->video_encode)) {
if(encodebin->video_encode = gst_element_factory_make (name, "video_encode") != NULL) {
gst_bin_add(GST_BIN(encodebin), encodebin->video_encode);
goto link_fail;
}
}
-
+
if(peersrc != NULL) {
if(!gst_pad_link(gst_element_get_pad(encodebin->video_encode, "src"), peersrc)) {
goto link_fail;
}
} else {
GST_ERROR_OBJECT(encodebin, "gst_encode_bin_replace_element_by_name() new element[%d] make fail\n", type);
- return FALSE;
+ return FALSE;
}
} else {
GST_ERROR_OBJECT(encodebin, "gst_encode_bin_replace_element_by_name() old element[%d] remove fail\n", type);
return FALSE;
- }
- }
+ }
+ }
break;
case ENCODEBIN_ELEMENT_AENC:
break;
gst_object_unref(sink1);
gst_object_unref(sink2);
gst_object_unref(src);
- gst_object_unref(peersink1);
- gst_object_unref(peersink2);
- gst_object_unref(peersrc);
+ gst_object_unref(peersink1);
+ gst_object_unref(peersink2);
+ gst_object_unref(peersrc);
return TRUE;
unlink_fail:
gst_object_unref(sink1);
gst_object_unref(sink2);
gst_object_unref(src);
- gst_object_unref(peersink1);
- gst_object_unref(peersink2);
- gst_object_unref(peersrc);
+ gst_object_unref(peersink1);
+ gst_object_unref(peersink2);
+ gst_object_unref(peersrc);
GST_ERROR_OBJECT(encodebin, "gst_encode_bin_replace_element_by_name() old element[%d] unlink fail\n", type);
return FALSE;
-
+
link_fail:
gst_object_unref(sink1);
gst_object_unref(sink2);
gst_object_unref(src);
- gst_object_unref(peersink1);
- gst_object_unref(peersink2);
- gst_object_unref(peersrc);
+ gst_object_unref(peersink1);
+ gst_object_unref(peersink2);
+ gst_object_unref(peersrc);
GST_ERROR_OBJECT(encodebin, "gst_encode_bin_replace_element_by_name() new element[%d] link fail\n", type);
- return FALSE;
+ return FALSE;
}
-static gboolean
+static gboolean
gst_encode_bin_replace_element_by_object(GstEncodeBin *encodebin, GstEncodeBinElement type, GstElement * element)
{
GstPad *sink1, *sink2, *src, *peersink1, *peersink2, *peersrc;
-
+
switch(type)
case ENCODEBIN_ELEMENT_VENC:
if(encodebin->video_encode == NULL) {
}
#endif //disable unused function
-static gboolean
+static gboolean
gst_encode_bin_remove_element (GstEncodeBin *encodebin, GstElement * element)
{
GstObject *parent;
gchar *ename = NULL;
- GST_DEBUG_OBJECT (encodebin, "gst_encode_bin_remove_element");
+ GST_INFO_OBJECT (encodebin, "gst_encode_bin_remove_element");
- if(element == NULL)
+ if (element == NULL) {
+ GST_INFO_OBJECT (encodebin, "element is already NULL");
return TRUE;
+ }
gst_element_set_state (element, GST_STATE_NULL);
parent = gst_element_get_parent (element);
GST_ERROR_OBJECT (encodebin, "gst_encode_bin_remove_element() [%s] remove fail", ename);
g_free (ename);
return FALSE;
- } else
- gst_object_unref (parent);
+ } else {
+ gst_object_unref(parent);
+ }
} else {
gst_object_unref(element);
}
+
return TRUE;
}
-static gboolean
+static gboolean
gst_encode_bin_link_elements (GstEncodeBin *encodebin) // need to return ????
{
GstPad *srcpad = NULL, *sinkpad = NULL;
switch(encodebin->profile) {
case GST_ENCODE_BIN_PROFILE_AV :
- if(!gst_caps_is_any(encodebin->vcaps))
- {
+ if (!gst_caps_is_any(encodebin->vcaps)) {
+ gchar *caps_str = NULL;
+ caps_str = gst_caps_to_string(encodebin->vcaps);
+ if (caps_str) {
+ GST_INFO_OBJECT(encodebin, "vconv caps [%s]", caps_str);
+ g_free(caps_str);
+ caps_str = NULL;
+ }
+
g_object_set(encodebin->vcapsfilter, "caps", encodebin->vcaps, NULL);
}
-
+
if (encodebin->auto_color_space) {
if(encodebin->color_space == NULL) {
- encodebin->color_space = gst_element_factory_make ("ffmpegcolorspace","color_space");
- gst_bin_add (GST_BIN (encodebin), encodebin->color_space);
+ encodebin->color_space = gst_element_factory_make (encodebin->vconv_name, "video_convert");
+ gst_bin_add (GST_BIN (encodebin), encodebin->color_space);
}
-
- srcpad = gst_element_get_static_pad(encodebin->video_queue, "src");
+
+ srcpad = gst_element_get_static_pad(encodebin->video_queue, "src");
if( encodebin->video_toggle )
{
sinkpad = gst_element_get_static_pad(encodebin->video_toggle, "sink");
_GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
-
+
srcpad = gst_element_get_static_pad(encodebin->video_toggle, "src");
}
sinkpad = gst_element_get_static_pad(encodebin->color_space, "sink");
srcpad = gst_element_get_static_pad(encodebin->color_space, "src");
sinkpad = gst_element_get_static_pad(encodebin->vcapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
srcpad = gst_element_get_static_pad(encodebin->vcapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
#if 0
if(encodebin->use_venc_queue)
{
if(encodebin->video_encode_queue == NULL) {
encodebin->video_encode_queue = gst_element_factory_make ("queue","video_encode_queue");
gst_bin_add (GST_BIN (encodebin), encodebin->video_encode_queue);
-
+
ENCODER_QUEUE_SET(encodebin->video_encode_queue, 0, 0, VIDEO_ENC_QUE_TIME);
- encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
+ encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
G_CALLBACK(queue_overun_cb), encodebin);
-
- }
+
+ }
srcpad = gst_element_get_static_pad(encodebin->video_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
#else
if(encodebin->video_encode_queue)
{
ENCODER_QUEUE_SET(encodebin->video_encode_queue, 0, 0, VIDEO_ENC_QUE_TIME);
- encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
+ encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
G_CALLBACK(queue_overun_cb), encodebin);
#if 0
- g_object_set(G_OBJECT(encodebin->video_queue),
- "max-size-bytes", (guint)0,
- "max-size-buffers", (guint)1,
- "max-size-time", (guint64)0,
- NULL);
+ g_object_set(G_OBJECT(encodebin->video_queue),
+ "max-size-bytes", (guint)0,
+ "max-size-buffers", (guint)1,
+ "max-size-time", (guint64)0,
+ NULL);
#endif
srcpad = gst_element_get_static_pad(encodebin->video_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
#endif
-
+
}
else {
srcpad = gst_element_get_static_pad(encodebin->video_queue, "src");
if( encodebin->video_toggle )
{
sinkpad = gst_element_get_static_pad(encodebin->video_toggle, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
srcpad = gst_element_get_static_pad(encodebin->video_toggle, "src");
}
sinkpad = gst_element_get_static_pad(encodebin->vcapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
srcpad = gst_element_get_static_pad(encodebin->vcapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
#if 0
if(encodebin->use_venc_queue)
{
if(encodebin->video_encode_queue == NULL) {
encodebin->video_encode_queue = gst_element_factory_make ("queue","video_encode_queue");
gst_bin_add (GST_BIN (encodebin), encodebin->video_encode_queue);
-
+
ENCODER_QUEUE_SET(encodebin->video_encode_queue, 0, 0, VIDEO_ENC_QUE_TIME);
- encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
}
-
+
srcpad = gst_element_get_static_pad(encodebin->video_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode_queue, "sink");
_GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
-
- }
+
+ }
#else
if(encodebin->video_encode_queue)
{
ENCODER_QUEUE_SET(encodebin->video_encode_queue, 0, 0, VIDEO_ENC_QUE_TIME);
- encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
+ encodebin->veque_sig_id = g_signal_connect( G_OBJECT(encodebin->video_encode_queue), "overrun",
G_CALLBACK(queue_overun_cb), encodebin);
-#if 0
- g_object_set(G_OBJECT(encodebin->video_queue),
- "max-size-bytes", (guint)0,
- "max-size-buffers", (guint)1,
- "max-size-time", (guint64)0,
- NULL);
+#if 0
+ g_object_set(G_OBJECT(encodebin->video_queue),
+ "max-size-bytes", (guint)0,
+ "max-size-buffers", (guint)1,
+ "max-size-time", (guint64)0,
+ NULL);
#endif
srcpad = gst_element_get_static_pad(encodebin->video_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->video_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
-#endif
-
+#endif
+
}
// gst_element_get_request_pad (encodebin->mux, "video_%d");
{
srcpad = gst_element_get_static_pad(encodebin->video_encode_queue, "src");
sinkpad = encodebin->mux_video_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_VIDEO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
#else
if(encodebin->video_encode_queue)
{
srcpad = gst_element_get_static_pad(encodebin->video_encode_queue, "src");
sinkpad = encodebin->mux_video_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_VIDEO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
#endif
else
{
srcpad = gst_element_get_static_pad(encodebin->video_encode, "src");
sinkpad = encodebin->mux_video_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_VIDEO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, video_link_fail);
}
srcpad = gst_element_get_static_pad(encodebin->mux, "src");
if(gst_ghost_pad_get_target(GST_GHOST_PAD (encodebin->srcpad)) != srcpad)
gst_ghost_pad_set_target(GST_GHOST_PAD (encodebin->srcpad), srcpad);
gst_object_unref(srcpad);
- srcpad = NULL;
-
+ srcpad = NULL;
+
/* For pause/resume control */
// encodebin->vsink_probeid = gst_pad_add_data_probe (gst_element_get_static_pad (encodebin->video_queue, "sink"),
sinkpad = gst_element_get_static_pad (encodebin->video_queue, "sink");
encodebin->vsink_probeid = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (gst_encode_bin_video_probe), encodebin);
gst_object_unref(sinkpad);
sinkpad = NULL;
-
+
if(encodebin->high_speed_fps > DEFAULT_PROP_HIGH_SPEED)
{
// encodebin->vsink_hs_probeid = gst_pad_add_data_probe (gst_element_get_static_pad (encodebin->video_encode, "sink"),
sinkpad = gst_element_get_static_pad (encodebin->video_encode, "sink");
encodebin->vsink_hs_probeid = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (gst_encode_bin_video_probe_hs), encodebin);
gst_object_unref(sinkpad);
- sinkpad = NULL;
- }
-
+ sinkpad = NULL;
+ }
+
if(encodebin->audio_queue == NULL)
{
GST_WARNING_OBJECT(encodebin, "Audio pad isn't requested, recording video only mode");
}
case GST_ENCODE_BIN_PROFILE_AUDIO :
if(!gst_caps_is_any(encodebin->acaps))
- {
+ {
g_object_set(encodebin->acapsfilter, "caps", encodebin->acaps, NULL);
}
if (encodebin->auto_audio_convert ||encodebin->auto_audio_resample) {
srcpad = gst_element_get_static_pad(encodebin->acapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-#if 0
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+#if 0
if(encodebin->use_aenc_queue)
{
if(encodebin->audio_encode_queue == NULL) {
encodebin->audio_encode_queue = gst_element_factory_make ("queue","audio_encode_queue");
gst_bin_add (GST_BIN (encodebin), encodebin->audio_encode_queue);
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
- }
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
+ }
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
- }
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ }
#else
if(encodebin->audio_encode_queue)
{
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
#endif
-
+
} else if (!encodebin->auto_audio_resample) {
if (encodebin->audio_conv == NULL) {
encodebin->audio_conv = gst_element_factory_make ("audioconvert","audio_conv");
gst_bin_add (GST_BIN (encodebin), encodebin->audio_conv);
- }
+ }
srcpad = gst_element_get_static_pad(encodebin->audio_queue, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_conv, "sink");
srcpad = gst_element_get_static_pad(encodebin->acapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-#if 0
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+#if 0
if(encodebin->use_aenc_queue)
{
if(encodebin->audio_encode_queue == NULL) {
gst_bin_add (GST_BIN (encodebin), encodebin->audio_encode_queue);
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
}
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-
- }
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+
+ }
#else
if(encodebin->audio_encode_queue)
{
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
-#endif
-
+#endif
+
} else {
if(encodebin->audio_sample == NULL) {
encodebin->audio_sample = gst_element_factory_make ("audioresample","audio_sample");
srcpad = gst_element_get_static_pad(encodebin->audio_queue, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_conv, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
srcpad = gst_element_get_static_pad(encodebin->audio_conv, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_sample, "sink");
srcpad = gst_element_get_static_pad(encodebin->audio_sample, "src");
sinkpad = gst_element_get_static_pad(encodebin->acapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
srcpad = gst_element_get_static_pad(encodebin->acapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-#if 0
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+#if 0
if(encodebin->use_aenc_queue)
{
if(encodebin->audio_encode_queue == NULL) {
encodebin->audio_encode_queue = gst_element_factory_make ("queue","audio_encode_queue");
gst_bin_add (GST_BIN (encodebin), encodebin->audio_encode_queue);
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
- }
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
+ }
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
_GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-
- }
+
+ }
#else
if(encodebin->audio_encode_queue)
{
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
-#endif
-
+#endif
+
}
}else {
srcpad = gst_element_get_static_pad(encodebin->audio_queue, "src");
sinkpad = gst_element_get_static_pad(encodebin->acapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
srcpad = gst_element_get_static_pad(encodebin->acapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
#if 0
if(encodebin->use_aenc_queue)
{
if(encodebin->audio_encode_queue == NULL) {
encodebin->audio_encode_queue = gst_element_factory_make ("queue","audio_encode_queue");
gst_bin_add (GST_BIN (encodebin), encodebin->audio_encode_queue);
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
- }
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
+ }
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
- }
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ }
#else
if(encodebin->audio_encode_queue)
{
-
+
ENCODER_QUEUE_SET(encodebin->audio_encode_queue, 0, 0, AUDIO_ENC_QUE_TIME);
- encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
- G_CALLBACK(queue_overun_cb), encodebin);
+ encodebin->aeque_sig_id = g_signal_connect( G_OBJECT(encodebin->audio_encode_queue), "overrun",
+ G_CALLBACK(queue_overun_cb), encodebin);
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
-#endif
-
+#endif
+
}
-#if 0
+#if 0
if(encodebin->use_aenc_queue)
{
srcpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "src");
sinkpad = encodebin->mux_audio_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_AUDIO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
#else
{
srcpad = gst_element_get_static_pad(encodebin->audio_encode_queue, "src");
sinkpad = encodebin->mux_audio_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_AUDIO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
}
#endif
else
- {
+ {
srcpad = gst_element_get_static_pad(encodebin->audio_encode, "src");
sinkpad = encodebin->mux_audio_sinkpad = gst_encode_bin_get_mux_sink_pad(encodebin->mux, ENCODEBIN_MUX_AUDIO_SINK);
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
-
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, audio_link_fail);
+
}
srcpad = gst_element_get_static_pad(encodebin->mux, "src");
break;
case GST_ENCODE_BIN_PROFILE_IMAGE :
if(!gst_caps_is_any(encodebin->icaps))
- {
+ {
g_object_set(encodebin->icapsfilter, "caps", encodebin->icaps, NULL);
}
-
+
if (encodebin->auto_color_space) {
if(encodebin->color_space == NULL) {
encodebin->color_space = gst_element_factory_make ("ffmpegcolorspace","color_space");
srcpad = gst_element_get_static_pad(encodebin->image_toggle, "src");
sinkpad = gst_element_get_static_pad(encodebin->color_space, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
srcpad = gst_element_get_static_pad(encodebin->color_space, "src");
sinkpad = gst_element_get_static_pad(encodebin->icapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
srcpad = gst_element_get_static_pad(encodebin->icapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->image_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
}
else {
-
+
srcpad = gst_element_get_static_pad(encodebin->image_queue, "src");
sinkpad = gst_element_get_static_pad(encodebin->image_toggle, "sink");
_GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
srcpad = gst_element_get_static_pad(encodebin->image_toggle, "src");
sinkpad = gst_element_get_static_pad(encodebin->icapsfilter, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
srcpad = gst_element_get_static_pad(encodebin->icapsfilter, "src");
sinkpad = gst_element_get_static_pad(encodebin->image_encode, "sink");
- _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
+ _GST_PAD_LINK_UNREF(srcpad, sinkpad, image_link_fail);
}
srcpad = gst_element_get_static_pad (encodebin->image_encode, "src");
return FALSE;
break;
}
-// gst_pad_set_active(encodebin->srcpad, TRUE);
+// gst_pad_set_active(encodebin->srcpad, TRUE);
return TRUE;
video_link_fail:
gst_encode_bin_remove_element(encodebin, encodebin->color_space);
GST_WARNING_OBJECT(encodebin, "encodebin link video elements fail");
return FALSE;
-
+
audio_link_fail:
- // remove element
- gst_encode_bin_remove_element(encodebin, encodebin->audio_conv);
- gst_encode_bin_remove_element(encodebin, encodebin->audio_sample);
+ // remove element
+ gst_encode_bin_remove_element(encodebin, encodebin->audio_conv);
+ gst_encode_bin_remove_element(encodebin, encodebin->audio_sample);
GST_WARNING_OBJECT(encodebin, "encodebin link audio elements fail");
return FALSE;
image_link_fail:
- // remove element
- gst_encode_bin_remove_element(encodebin, encodebin->color_space);
+ // remove element
+ gst_encode_bin_remove_element(encodebin, encodebin->color_space);
GST_WARNING_OBJECT(encodebin, "encodebin link image elements fail");
return FALSE;
-
+
}
-static gboolean
+static gboolean
gst_encode_bin_unlink_elements (GstEncodeBin *encodebin)
{
GstPad *pad = NULL, *muxpad = NULL;
-
+
switch(encodebin->profile) {
case GST_ENCODE_BIN_PROFILE_AV :
if (encodebin->auto_color_space) {
- if( encodebin->video_toggle )
- {
- gst_element_unlink_many (
+ if (encodebin->video_toggle) {
+ gst_element_unlink_many(
encodebin->video_queue,
encodebin->video_toggle,
encodebin->color_space,
- encodebin->vcapsfilter,
+ encodebin->vcapsfilter,
encodebin->video_encode,
- //encodebin->video_encode_queue,
+ //encodebin->video_encode_queue,
NULL);
- }
- else
- {
+ } else {
gst_element_unlink_many(
encodebin->video_queue,
encodebin->color_space,
- encodebin->vcapsfilter,
+ encodebin->vcapsfilter,
encodebin->video_encode,
- //encodebin->video_encode_queue,
+ //encodebin->video_encode_queue,
NULL);
}
- }
- else {
- if( encodebin->video_toggle )
- {
- gst_element_unlink_many (
+ } else {
+ if (encodebin->video_toggle) {
+ gst_element_unlink_many(
encodebin->video_queue,
encodebin->video_toggle,
- encodebin->vcapsfilter,
+ encodebin->vcapsfilter,
encodebin->video_encode,
- //encodebin->video_encode_queue,
+ //encodebin->video_encode_queue,
NULL);
- }
- else
- {
+ } else {
gst_element_unlink_many(
encodebin->video_queue,
- encodebin->vcapsfilter,
+ encodebin->vcapsfilter,
encodebin->video_encode,
- //encodebin->video_encode_queue,
+ //encodebin->video_encode_queue,
NULL);
}
}
if(encodebin->mux_video_sinkpad != NULL)
{
-#if 0
+#if 0
if(encodebin->use_venc_queue)
{
gst_element_unlink(encodebin->video_encode, encodebin->video_encode_queue);
gst_pad_remove_buffer_probe(pad, encodebin->vsink_probeid);
encodebin->vsink_probeid = 0;
gst_object_unref(pad);
- pad = NULL;
+ pad = NULL;
}
-
+
if(encodebin->vsink_hs_probeid)
{
encodebin->audio_sample,
encodebin->acapsfilter,
encodebin->audio_encode,
- NULL);
+ NULL);
} else if (!encodebin->auto_audio_resample) {
gst_element_unlink_many (
encodebin->audio_queue,
- encodebin->audio_conv,
+ encodebin->audio_conv,
encodebin->acapsfilter,
encodebin->audio_encode,
- NULL);
+ NULL);
} else {
gst_element_unlink_many (
encodebin->audio_queue,
encodebin->audio_conv,
- encodebin->audio_sample,
+ encodebin->audio_sample,
encodebin->acapsfilter,
encodebin->audio_encode,
- NULL);
+ NULL);
}
}
else {
encodebin->audio_queue,
encodebin->acapsfilter,
encodebin->audio_encode,
- NULL);
+ NULL);
}
if(encodebin->mux_audio_sinkpad != NULL)
{
-#if 0
+#if 0
if(encodebin->use_aenc_queue)
{
gst_element_unlink(encodebin->audio_encode, encodebin->audio_encode_queue);
pad = gst_element_get_static_pad (encodebin->audio_encode_queue, "src");
gst_pad_unlink(pad, muxpad);
gst_object_unref(pad);
- pad = NULL;
+ pad = NULL;
if ( g_signal_handler_is_connected ( encodebin->audio_encode_queue, encodebin->veque_sig_id) )
{
g_signal_handler_disconnect ( encodebin->audio_encode_queue, encodebin->veque_sig_id );
- }
+ }
}
#else
if(encodebin->audio_encode_queue)
pad = gst_element_get_static_pad (encodebin->audio_encode_queue, "src");
gst_pad_unlink(pad, encodebin->mux_audio_sinkpad);
gst_object_unref(pad);
- pad = NULL;
+ pad = NULL;
if ( g_signal_handler_is_connected ( encodebin->audio_encode_queue, encodebin->veque_sig_id) )
{
g_signal_handler_disconnect ( encodebin->audio_encode_queue, encodebin->veque_sig_id );
- }
+ }
}
#endif
else
gst_pad_unlink(pad, encodebin->mux_audio_sinkpad);
gst_object_unref(pad);
pad = NULL;
- }
+ }
gst_element_release_request_pad(encodebin->mux, encodebin->mux_audio_sinkpad);
// gst_object_unref(encodebin->mux_audio_sinkpad); //***
if(encodebin->asink_probeid)
{
- pad = gst_element_get_static_pad (encodebin->audio_queue, "sink");
+ pad = gst_element_get_static_pad (encodebin->audio_queue, "sink");
gst_pad_remove_buffer_probe(pad, encodebin->asink_probeid);
encodebin->asink_probeid =0;
gst_object_unref(pad);
encodebin->image_queue,
encodebin->image_toggle,
encodebin->color_space,
- encodebin->icapsfilter,
+ encodebin->icapsfilter,
encodebin->image_encode,
NULL);
}
else {
gst_element_unlink_many (
encodebin->image_queue,
- encodebin->image_toggle,
- encodebin->icapsfilter,
+ encodebin->image_toggle,
+ encodebin->icapsfilter,
encodebin->image_encode,
- NULL);
+ NULL);
}
break;
default:
return FALSE;
break;
}
- // gst_pad_set_active(encodebin->srcpad, TRUE);
+ // gst_pad_set_active(encodebin->srcpad, TRUE);
return TRUE;
}
-static gboolean
+static gboolean
gst_encode_bin_init_video_elements (GstElement *element, gpointer user_data)
{
GstEncodeBin *encodebin = GST_ENCODE_BIN (element);
if(encodebin->vcapsfilter == NULL) {
encodebin->vcapsfilter = gst_element_factory_make ("capsfilter","vcapsfilter");
gst_bin_add (GST_BIN (element), encodebin->vcapsfilter);
- }
+ }
#if 0
encodebin->vcaps = gst_caps_new_simple("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
"width", G_TYPE_INT, 320,
"height", G_TYPE_INT, 240,
"framerate", GST_TYPE_FRACTION, 30, 1,
- NULL);
+ NULL);
#endif
if(encodebin->video_encode == NULL) {
- encodebin->video_encode = gst_element_factory_make (encodebin->venc_name, "video_encode");
+ encodebin->video_encode = gst_element_factory_make (encodebin->venc_name, NULL);
gst_bin_add (GST_BIN (element), encodebin->video_encode);
}
gst_bin_add (GST_BIN (element), encodebin->mux);
}
- if (!encodebin->video_encode
-// || !encodebin->video_encode_queue
- || !encodebin->mux
- || !encodebin->video_queue
+ if (!encodebin->video_encode
+// || !encodebin->video_encode_queue
+ || !encodebin->mux
+ || !encodebin->video_queue
|| !encodebin->vcapsfilter
|| !encodebin->srcpad )
{
GST_ERROR_OBJECT(encodebin, "Faild create element \n");
return FALSE;
}
-
+
if( encodebin->use_video_toggle && !encodebin->video_toggle )
{
GST_ERROR_OBJECT(encodebin, "Faild create video toggle element \n");
return FALSE;
}
-#if 0
+#if 0
if (encodebin->auto_color_space && (encodebin->color_space == NULL)) {
encodebin->color_space = gst_element_factory_make ("ffmpegcolorspace","color_space");
gst_bin_add (GST_BIN (element), encodebin->color_space);
- }
-#endif
+ }
+#endif
return TRUE;
}
-static gboolean
+static gboolean
gst_encode_bin_init_audio_elements (GstElement *element, gpointer user_data)
{
GstEncodeBin *encodebin = GST_ENCODE_BIN (element);
if(encodebin->acapsfilter == NULL) {
encodebin->acapsfilter = gst_element_factory_make ("capsfilter","acapsfilter");
gst_bin_add (GST_BIN (element), encodebin->acapsfilter);
- }
+ }
#if 0
encodebin->acaps = gst_caps_new_simple("audio/x-raw-int",
"rate", G_TYPE_INT, 8000,
gst_bin_add (GST_BIN (element), encodebin->mux);
}
- if (!encodebin->audio_encode
+ if (!encodebin->audio_encode
|| !encodebin->audio_queue
|| !encodebin->mux
- || !encodebin->acapsfilter
+ || !encodebin->acapsfilter
|| !encodebin->srcpad )
{
GST_ERROR_OBJECT(encodebin, "Faild create element \n");
if (encodebin->auto_audio_convert && (encodebin->audio_conv == NULL)) {
encodebin->audio_conv = gst_element_factory_make ("audioconvert","audio_conv");
gst_bin_add (GST_BIN (element), encodebin->audio_conv);
- }
+ }
if (encodebin->auto_audio_resample && (encodebin->audio_sample == NULL)) {
encodebin->audio_sample = gst_element_factory_make ("audioresample","audio_sample");
gst_bin_add (GST_BIN (element), encodebin->audio_sample);
- }
+ }
#endif
return TRUE;
}
-static gboolean
+static gboolean
gst_encode_bin_init_image_elements (GstElement *element, gpointer user_data)
{
GstEncodeBin *encodebin = GST_ENCODE_BIN (element);
if(encodebin->icapsfilter == NULL) {
encodebin->icapsfilter = gst_element_factory_make ("capsfilter","icapsfilter");
gst_bin_add (GST_BIN (element), encodebin->icapsfilter);
- }
+ }
if(encodebin->image_encode == NULL) {
encodebin->image_encode = gst_element_factory_make (encodebin->ienc_name, "image_encode");
gst_bin_add (GST_BIN (element), encodebin->image_encode);
- }
+ }
- if (!encodebin->image_encode
+ if (!encodebin->image_encode
|| !encodebin->image_queue
|| !encodebin->image_toggle
|| !encodebin->icapsfilter
if (encodebin->auto_color_space && (encodebin->color_space == NULL)) {
encodebin->color_space = gst_element_factory_make ("ffmpegcolorspace","color_space");
gst_bin_add (GST_BIN (element), encodebin->color_space);
- }
+ }
#endif
return TRUE;
}
static gboolean gst_encode_bin_block(GstEncodeBin *encodebin, gboolean value)
{
-
+
if(value) { //block stream
switch(encodebin->profile) {
case GST_ENCODE_BIN_PROFILE_AV:
if(encodebin->audio_queue == NULL && encodebin->video_queue == NULL) {
goto block_fail;
} else {
- if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->video_queue)),
+ if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->video_queue)),
"empty-buffers") == NULL) {
GST_ERROR_OBJECT(encodebin, "The queue element doesn't support 'empty-buffers' property");
goto block_fail;
g_object_set(encodebin->video_toggle, "block-data", TRUE , NULL);
GST_INFO_OBJECT( encodebin, "video_toggle block-data TRUE" );
}
-
+
g_object_set(encodebin->video_queue, "empty-buffers", TRUE , NULL);
GST_INFO_OBJECT( encodebin, "video_queue empty-buffers TRUE" );
if(encodebin->audio_queue != NULL)
break;
case GST_ENCODE_BIN_PROFILE_AUDIO:
if(encodebin->audio_queue == NULL) {
- goto block_fail;
+ goto block_fail;
} else {
- if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->audio_queue)),
+ if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->audio_queue)),
"empty-buffers") == NULL) {
GST_ERROR_OBJECT(encodebin, "The queue element doesn't support 'empty-buffers' property");
goto block_fail;
- }
+ }
g_object_set(encodebin->audio_queue, "empty-buffers", TRUE , NULL);
GST_INFO_OBJECT( encodebin, "audio_queue empty-buffers TRUE" );
}
g_object_set(encodebin->image_toggle, "block_data", TRUE, NULL);
GST_INFO_OBJECT( encodebin, "image_toggle block_data TRUE" );
}
- break;
+ break;
default:
GST_WARNING_OBJECT (encodebin,"Invalid profile number = %d", encodebin->profile);
goto block_fail;
- break;
+ break;
}
} else { //release blocked-stream
switch(encodebin->profile) {
if(encodebin->audio_queue == NULL && encodebin->video_queue == NULL) {
goto unblock_fail;
} else {
- if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->video_queue)),
+ if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->video_queue)),
"empty-buffers") == NULL) {
GST_ERROR_OBJECT(encodebin, "The queue element doesn't support 'empty-buffers' property");
goto unblock_fail;
- }
+ }
if( encodebin->video_toggle )
{
g_object_set(encodebin->video_toggle, "block-data", FALSE , NULL);
GST_INFO_OBJECT( encodebin, "video_toggle block-data FALSE" );
}
-
+
if(encodebin->audio_queue != NULL)
- {
+ {
g_object_set(encodebin->audio_queue, "empty-buffers", FALSE , NULL);
GST_INFO_OBJECT( encodebin, "audio_queue empty-buffers FALSE" );
}
break;
case GST_ENCODE_BIN_PROFILE_AUDIO:
if(encodebin->audio_queue == NULL) {
- goto unblock_fail;
- } else {
- if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->audio_queue)),
+ goto unblock_fail;
+ } else {
+ if(g_object_class_find_property(G_OBJECT_GET_CLASS(GST_OBJECT(encodebin->audio_queue)),
"empty-buffers") == NULL) {
GST_ERROR_OBJECT(encodebin, "The queue element doesn't support 'empty-buffers' property");
goto unblock_fail;
- }
+ }
g_object_set(encodebin->audio_queue, "empty-buffers", FALSE , NULL);
GST_INFO_OBJECT( encodebin, "audio_queue empty-buffers FALSE" );
}
case GST_ENCODE_BIN_PROFILE_IMAGE:
if(encodebin->image_toggle == NULL) {
goto unblock_fail;
- } else {
+ } else {
g_object_set(encodebin->image_toggle, "block_data", FALSE, NULL);
GST_INFO_OBJECT( encodebin, "image_toggle block_data FALSE" );
}
- break;
+ break;
default:
GST_WARNING_OBJECT (encodebin,"Invalid profile number = %d", encodebin->profile);
goto unblock_fail;
- break;
- }
+ break;
+ }
}
encodebin->block = value;
return TRUE;
block_fail:
GST_ERROR_OBJECT(encodebin, "encodebin block failed");
return FALSE;
-
-unblock_fail:
- GST_ERROR_OBJECT(encodebin, "encodebin unblock failed");
- return FALSE;
+
+unblock_fail:
+ GST_ERROR_OBJECT(encodebin, "encodebin unblock failed");
+ return FALSE;
}
static gboolean gst_encode_bin_pause(GstEncodeBin *encodebin, gboolean value)
{
GstClock *clock = NULL;
- if(value) {
+ if(value) {
/* pause stream*/
//Block src of encode bin
if (!gst_encode_bin_block(encodebin, TRUE))
base_time = gst_element_get_base_time(GST_ELEMENT(encodebin));
encodebin->paused_time = current_time - base_time;
-
+
GST_INFO_OBJECT (encodebin, "Encodebin is in running-pause at [%"GST_TIME_FORMAT"]."
, GST_TIME_ARGS(encodebin->paused_time));
}
{
encodebin->paused_time = 0;
encodebin->total_offset_time = 0;
-
+
GST_WARNING_OBJECT (encodebin, "There is no clock in Encodebin.");
}
}
-#if 0 //def GST_ENCODE_BIN_SIGNAL_ENABLE
+#if 0 //def GST_ENCODE_BIN_SIGNAL_ENABLE
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_PAUSE], 0, TRUE);
-#endif
+#endif
}
- else {
+ else {
/* release paused-stream*/
if (encodebin->paused_time != 0)
{
current_time = gst_clock_get_time(clock);
base_time = gst_element_get_base_time(GST_ELEMENT(encodebin));
paused_gap = current_time - base_time - encodebin->paused_time;
-
+
encodebin->total_offset_time += paused_gap;
encodebin->paused_time = 0;
-
+
GST_INFO_OBJECT (encodebin, "Encodebin now resumes. Offset delay [%"GST_TIME_FORMAT"], Total offset delay [%"GST_TIME_FORMAT"]"
, GST_TIME_ARGS(paused_gap) , GST_TIME_ARGS(encodebin->total_offset_time));
}
{
encodebin->paused_time = 0;
encodebin->total_offset_time = 0;
-
+
GST_WARNING_OBJECT (encodebin, "There is no clock in Encodebin.");
}
}
GST_WARNING_OBJECT (encodebin, "Fail to Unblock Encodebin.");
goto resume_fail;
}
-#if 0 //def GST_ENCODE_BIN_SIGNAL_ENABLE
+#if 0 //def GST_ENCODE_BIN_SIGNAL_ENABLE
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_RESUME], 0, TRUE);
-#endif
+#endif
}
encodebin->pause = value;
return TRUE;
pause_fail:
GST_WARNING_OBJECT (encodebin, "Fail to pause Encodebin");
-#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
+#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_PAUSE], 0, FALSE);
-#endif
+#endif
return FALSE;
-resume_fail:
+resume_fail:
GST_WARNING_OBJECT (encodebin, "Fail to resume Encodebin");
-#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
+#ifdef GST_ENCODE_BIN_SIGNAL_ENABLE
g_signal_emit (G_OBJECT (encodebin), gst_encode_bin_signals[SIGNAL_STREAM_RESUME], 0, FALSE);
-#endif
- return FALSE;
+#endif
+ return FALSE;
}
static gboolean
gst_element_unlink (encodebin->video_queue, encodebin->video_encode);
}
- gst_pad_unlink (gst_element_get_pad (encodebin->audio_encode, "src"),
+ gst_pad_unlink (gst_element_get_pad (encodebin->audio_encode, "src"),
encodebin->mux_audio_sinkpad);
- gst_pad_unlink (gst_element_get_pad (encodebin->video_encode, "src"),
+ gst_pad_unlink (gst_element_get_pad (encodebin->video_encode, "src"),
encodebin->mux_video_sinkpad);
gst_bin_remove_many (GST_BIN (element),
// const GValue *codec_data;
gint channels, rate;
- encodebin = GST_ENCODE_BIN (gst_pad_get_parent (pad));
+ encodebin = GST_ENCODE_BIN (gst_pad_get_parent (pad));
structure = gst_caps_get_structure (vscaps, 0);
// mimetype = gst_structure_get_name (structure);
encodebin->fps = gst_value_get_fraction_numerator(fps);
- if(encodebin->high_speed_fps > 0)
+ if(encodebin->high_speed_fps > 0 && encodebin->fps > 0)
{
encodebin->multiple =(encodebin->high_speed_fps)/(encodebin->fps);
}
-
+
{
guint32 format;
break;
}
}
- gst_object_unref (encodebin);
+ gst_object_unref (encodebin);
return TRUE;
-
+
refuse_caps:
{
GST_WARNING_OBJECT (encodebin, "refused caps %" GST_PTR_FORMAT, vscaps);
gst_object_unref (encodebin);
return FALSE;
- }
+ }
}
GST_WARNING_OBJECT (encodebin, "encodebin is Null.");
return TRUE;
}
-
+
//Adjusting timestamp of video source
GST_BUFFER_TIMESTAMP(buffer) -= encodebin->total_offset_time;
GST_WARNING_OBJECT (encodebin, "encodebin is Null.");
return TRUE;
}
-
+
GST_BUFFER_TIMESTAMP(buffer) *= encodebin->multiple;
return TRUE;
}
GST_WARNING_OBJECT (encodebin, "encodebin is Null.");
return TRUE;
}
-
+
//Adjusting timestamp of video source
GST_BUFFER_TIMESTAMP(buffer) -= encodebin->total_offset_time;
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"encodebin",
- "EXT encoder bin",
+ "EXT encoder bin",
plugin_init, VERSION, "LGPL", "Samsung Electronics Co", "http://www.samsung.com/")
GMutex *mutex;
/* pads */
- GstPad *srcpad;
- GstPad *video_sinkpad;
- GstPad *audio_sinkpad;
- GstPad *image_sinkpad;
- GstPad *mux_audio_sinkpad;
- GstPad *mux_video_sinkpad;
+ GstPad *srcpad;
+ GstPad *video_sinkpad;
+ GstPad *audio_sinkpad;
+ GstPad *image_sinkpad;
+ GstPad *mux_audio_sinkpad;
+ GstPad *mux_video_sinkpad;
/* sinkpads, video first */
- GSList *sinkpads;
+ GSList *sinkpads;
/* video restricted to 1 pad */
- guint video_pads, audio_pads;
-
- gint profile;
- gint fps;
- gint high_speed_fps;
- gint multiple;
- gchar *venc_name;
- gchar *aenc_name;
- gchar *ienc_name;
- gchar *mux_name;
-
-
- GstCaps *vcaps;
- GstCaps *acaps;
- GstCaps *icaps;
-
- gboolean auto_audio_convert;
- gboolean auto_audio_resample;
- gboolean auto_color_space;
- gboolean block;
- gboolean pause;
- gboolean use_video_toggle;
- gboolean use_venc_queue;
- gboolean use_aenc_queue;
-
+ guint video_pads, audio_pads;
+
+ gint profile;
+ gint fps;
+ gint high_speed_fps;
+ gint multiple;
+ gchar *venc_name;
+ gchar *aenc_name;
+ gchar *ienc_name;
+ gchar *mux_name;
+ gchar *vconv_name;
+
+ GstCaps *vcaps;
+ GstCaps *acaps;
+ GstCaps *icaps;
+
+ gboolean auto_audio_convert;
+ gboolean auto_audio_resample;
+ gboolean auto_color_space;
+ gboolean block;
+ gboolean pause;
+ gboolean use_video_toggle;
+ gboolean use_venc_queue;
+ gboolean use_aenc_queue;
+
GstElement *audio_queue;
- GstElement *video_queue;
- GstElement *video_encode_queue;
- GstElement *audio_encode_queue;
- GstElement *image_queue;
-
+ GstElement *video_queue;
+ GstElement *video_encode_queue;
+ GstElement *audio_encode_queue;
+ GstElement *image_queue;
+
GstElement *audio_encode;
GstElement *video_encode;
- GstElement *image_encode;
+ GstElement *image_encode;
+
+ GstElement *vcapsfilter;
+ GstElement *acapsfilter;
+ GstElement *icapsfilter;
- GstElement *vcapsfilter;
- GstElement *acapsfilter;
- GstElement *icapsfilter;
-
GstElement *video_toggle;
- GstElement *image_toggle;
- GstElement *color_space;
- GstElement *audio_conv;
+ GstElement *image_toggle;
+ GstElement *color_space;
+ GstElement *audio_conv;
GstElement *audio_sample;
-
+
GstElement *mux;
/* pause/resume variables */
- GstClockTime paused_time; /* pipeline time when pausing */
- GstClockTime total_offset_time; /* delayed time which is due to pause */
- gulong vsink_probeid;
- gulong vsink_hs_probeid;
- gulong asink_probeid;
- gulong veque_sig_id;
- gulong aeque_sig_id;
+ GstClockTime paused_time; /* pipeline time when pausing */
+ GstClockTime total_offset_time; /* delayed time which is due to pause */
+ gulong vsink_probeid;
+ gulong vsink_hs_probeid;
+ gulong asink_probeid;
+ gulong veque_sig_id;
+ gulong aeque_sig_id;
};
struct _GstEncodeBinClass
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) $(MMTA_CFLAGS)
-libgstevasimagesink_la_LIBADD = $(GST_LIBS) $(GST_VIDEO_LIBS) $(EFL_LIBS) $(MMTA_LIBS)
+libgstevasimagesink_la_CFLAGS = $(GST_CFLAGS) $(GST_VIDEO_CFLAGS) $(EFL_CFLAGS) $(NATIVE_BUFFER_CFLAGS) $(TBM_CFLAGS)
+libgstevasimagesink_la_LIBADD = $(GST_LIBS) $(GST_VIDEO_LIBS) $(EFL_LIBS) $(NATIVE_BUFFER_LIBS) $(TBM_LIBS)
libgstevasimagesink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstevasimagesink_la_LIBTOOLFLAGS = --tag=disable-static
#define CAP_WIDTH "width"
#define CAP_HEIGHT "height"
+//#define DUMP_IMG
GST_DEBUG_CATEGORY_STATIC (gst_evas_image_sink_debug);
#define GST_CAT_DEFAULT gst_evas_image_sink_debug
PROP_0,
PROP_EVAS_OBJECT,
PROP_EVAS_OBJECT_SHOW,
+#ifdef USE_TBM_SURFACE
+ PROP_ROTATE_ANGLE,
+ PROP_DISPLAY_GEOMETRY_METHOD,
+ PROP_ENABLE_FLUSH_BUFFER,
+ PROP_FLIP
+#endif
+};
+
+enum
+{
+ UPDATE_FALSE,
+ UPDATE_TRUE
};
#define COLOR_DEPTH 4
#define GL_X11_ENGINE "gl_x11"
+#define DO_RENDER_FROM_FIMC 1
+#define SIZE_FOR_UPDATE_VISIBILITY sizeof(gchar)
+
+#ifdef USE_TBM_SURFACE
+#define MAX_ECOREPIPE_BUFFER_CNT 4
+#define DEBUGLOG_DEFAULT_COUNT 8
+#define SIZE_FOR_NATIVE_INDEX sizeof(gint)
+
+/* max channel count *********************************************************/
+#define SCMN_IMGB_MAX_PLANE (4)
+
+/* image buffer definition ***************************************************
+
+ +------------------------------------------+ ---
+ | | ^
+ | a[], p[] | |
+ | +---------------------------+ --- | |
+ | | | ^ | |
+ | |<---------- w[] ---------->| | | |
+ | | | | | |
+ | | | |
+ | | | h[] | e[]
+ | | | |
+ | | | | | |
+ | | | | | |
+ | | | v | |
+ | +---------------------------+ --- | |
+ | | v
+ +------------------------------------------+ ---
+
+ |<----------------- s[] ------------------>|
+*/
+
+typedef struct
+{
+ /* width of each image plane */
+ int w[SCMN_IMGB_MAX_PLANE];
+ /* height of each image plane */
+ int h[SCMN_IMGB_MAX_PLANE];
+ /* stride of each image plane */
+ int s[SCMN_IMGB_MAX_PLANE];
+ /* elevation of each image plane */
+ int e[SCMN_IMGB_MAX_PLANE];
+ /* user space address of each image plane */
+ void *a[SCMN_IMGB_MAX_PLANE];
+ /* physical address of each image plane, if needs */
+ void *p[SCMN_IMGB_MAX_PLANE];
+ /* color space type of image */
+ int cs;
+ /* left postion, if needs */
+ int x;
+ /* top position, if needs */
+ int y;
+ /* to align memory */
+ int __dummy2;
+ /* arbitrary data */
+ int data[16];
+ /* dma buf fd */
+ int fd[SCMN_IMGB_MAX_PLANE];
+ /* buffer share method */
+ int buf_share_method;
+ /* Y plane size in case of ST12 */
+ int y_size;
+ /* UV plane size in case of ST12 */
+ int uv_size;
+ /* Tizen buffer object */
+ void *bo[SCMN_IMGB_MAX_PLANE];
+ /* JPEG data */
+ void *jpeg_data;
+ /* JPEG size */
+ int jpeg_size;
+ /* TZ memory buffer */
+ int tz_enable;
+} SCMN_IMGB;
+#endif
+
+#define EVASIMAGESINK_SET_EVAS_OBJECT_EVENT_CALLBACK( x_evas_image_object, x_usr_data ) \
+do \
+{ \
+ if (x_evas_image_object) { \
+ GST_LOG("object callback add"); \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_DEL, evas_callback_del_event, x_usr_data); \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_RESIZE, evas_callback_resize_event, x_usr_data); \
+ } \
+}while(0)
+
+#define EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK( x_evas_image_object ) \
+do \
+{ \
+ if (x_evas_image_object) { \
+ GST_LOG("object callback del"); \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_DEL, evas_callback_del_event); \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_RESIZE, evas_callback_resize_event); \
+ } \
+}while(0)
+
+#ifdef USE_TBM_SURFACE
+#define EVASIMAGESINK_SET_EVAS_EVENT_CALLBACK( x_evas, x_usr_data ) \
+do \
+{ \
+ if (x_evas) { \
+ GST_DEBUG("callback add... evas_callback_render_pre.. evas : %p esink : %p", x_evas, x_usr_data); \
+ evas_event_callback_add (x_evas, EVAS_CALLBACK_RENDER_PRE, evas_callback_render_pre, x_usr_data); \
+ } \
+}while(0)
+
+#define EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK( x_evas ) \
+do \
+{ \
+ if (x_evas) { \
+ GST_DEBUG("callback del... evas_callback_render_pre"); \
+ evas_event_callback_del (x_evas, EVAS_CALLBACK_RENDER_PRE, evas_callback_render_pre); \
+ } \
+}while(0)
+#endif
GMutex *instance_lock;
guint instance_lock_count;
return FALSE;
}
+#ifdef USE_TBM_SURFACE
+gint
+gst_evas_image_sink_ref_count (GstBuffer * buf)
+{
+ return GST_OBJECT_REFCOUNT_VALUE(GST_BUFFER_CAST(buf));
+}
+#endif
+
/* the capabilities of the inputs.
*
* BGRx format
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
+#ifdef USE_FIMCC
GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx));
-
+#endif
+#ifdef USE_TBM_SURFACE
+ GST_STATIC_CAPS(GST_VIDEO_CAPS_YUV ("I420") ";"
+ GST_VIDEO_CAPS_YUV ("NV12") ";"
+ GST_VIDEO_CAPS_YUV ("SN12"))
+);
+#endif
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 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 evas_callback_del_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void evas_callback_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+#ifdef USE_TBM_SURFACE
+static void evas_callback_render_pre (void *data, Evas *e, void *event_info);
+static GstFlowReturn gst_esink_epipe_reset(GstEvasImageSink *esink);
+static gboolean gst_esink_make_flush_buffer(GstEvasImageSink *esink);
+static void _release_flush_buffer(GstEvasImageSink *esink);
+static void gst_evas_image_sink_update_geometry (GstEvasImageSink *esink, GstVideoRectangle *result);
+static void gst_evas_image_sink_apply_geometry (GstEvasImageSink *esink);
+#endif
+#ifdef DUMP_IMG
+int util_write_rawdata (const char *file, const void *data, unsigned int size);
+int g_cnt = 0;
+#endif
static void
gst_evas_image_sink_base_init (gpointer gclass)
"EvasImageSink",
"VideoSink",
"Video sink element for evas image object",
- "Wonguk Jeong <wonguk.jeong@samsung.com>");
+ "Samsung Electronics <www.samsung.com>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
g_param_spec_pointer ("evas-object", "Destination Evas Object", "Destination evas image object", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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));
-
+#ifdef USE_TBM_SURFACE
+ g_object_class_install_property(gobject_class, PROP_ROTATE_ANGLE,
+ g_param_spec_int("rotate", "Rotate angle", "Rotate angle of display output", DEGREE_0, DEGREE_NUM, DEGREE_0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property(gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
+ g_param_spec_int("display-geometry-method", "Display geometry method",
+ "Geometrical method for display", DISP_GEO_METHOD_LETTER_BOX, DISP_GEO_METHOD_NUM, DISP_GEO_METHOD_LETTER_BOX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_ENABLE_FLUSH_BUFFER,
+ g_param_spec_boolean("enable-flush-buffer", "Enable flush buffer mechanism",
+ "Enable flush buffer mechanism when state change(PAUSED_TO_READY)",
+ TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property(gobject_class, PROP_FLIP,
+ g_param_spec_int("flip", "Display flip",
+ "Flip for display", FLIP_NONE, FLIP_NUM, FLIP_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
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);
static void
gst_evas_image_sink_fini (gpointer data, GObject *obj)
{
- GST_INFO ("enter");
+ GST_DEBUG ("[ENTER]");
GstEvasImageSink *esink = GST_EVASIMAGESINK (obj);
if (!esink) {
return;
}
+#ifdef USE_FIMCC
if (esink->oldbuf) {
gst_buffer_unref (esink->oldbuf);
}
+#endif
+ if (esink->eo) {
+#ifdef USE_TBM_SURFACE
+ EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK (evas_object_evas_get(esink->eo));
+ GST_DEBUG("unset EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK esink : %p, esink->eo : %x", esink, esink->eo);
+#endif
+ EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK (esink->eo);
+ GST_DEBUG("unset EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK esink : %p, esink->eo : %x", esink, esink->eo);
+ evas_object_image_data_set(esink->eo, NULL);
+ }
+#ifdef USE_TBM_SURFACE
+ if (esink->display_buffer_lock) {
+ g_mutex_free (esink->display_buffer_lock);
+ esink->display_buffer_lock = NULL;
+ }
+ if (esink->flow_lock) {
+ g_mutex_free (esink->flow_lock);
+ esink->flow_lock = NULL;
+ }
+ esink->eo = NULL;
+ esink->epipe = NULL;
+#endif
g_mutex_lock (instance_lock);
instance_lock_count--;
g_mutex_unlock (instance_lock);
g_mutex_free (instance_lock);
instance_lock = NULL;
}
+
+ GST_DEBUG ("[LEAVE]");
+}
+
+#ifdef USE_TBM_SURFACE
+static void
+gst_evas_image_sink_reset (GstEvasImageSink *esink)
+{
+ int i = 0;
+ tbm_bo_handle bo_handle;
+
+ GST_DEBUG("gst_evas_image_sink_reset start");
+
+ g_mutex_lock(esink->display_buffer_lock);
+
+ for(i=0; i<NATIVE_BUFFER_NUM; i++)
+ {
+ if(esink->displaying_buffer[i].tbm_surf)
+ {
+ tbm_surface_destroy(esink->displaying_buffer[i].tbm_surf);
+ esink->displaying_buffer[i].tbm_surf = NULL;
+ }
+ if(esink->displaying_buffer[i].buffer)
+ {
+ if(esink->displaying_buffer[i].ref_count)
+ {
+ if(esink->displaying_buffer[i].bo) {
+ bo_handle = tbm_bo_map(esink->displaying_buffer[i].bo, TBM_DEVICE_CPU, TBM_OPTION_READ|TBM_OPTION_WRITE);
+ if (!bo_handle.ptr) {
+ GST_WARNING("failed to map bo [%p]", esink->displaying_buffer[i].bo);
+ }
+ else
+ tbm_bo_unmap(esink->displaying_buffer[i].bo);
+ }
+ else
+ GST_WARNING("there is no bo information. so skip to map bo");
+
+ GST_WARNING("[reset] unreffing gst %p", esink->displaying_buffer[i].buffer);
+
+ while(esink->displaying_buffer[i].ref_count)
+ {
+ GST_WARNING("index[%d]'s buffer ref count=%d",i,gst_evas_image_sink_ref_count (esink->displaying_buffer[i].buffer));
+ esink->displaying_buffer[i].ref_count--;
+ gst_buffer_unref(esink->displaying_buffer[i].buffer);
+ }
+ }
+ esink->displaying_buffer[i].buffer = NULL;
+ }
+ if(esink->displaying_buffer[i].bo)
+ esink->displaying_buffer[i].bo = NULL;
+ }
+ esink->prev_buf = NULL;
+ esink->prev_index = -1;
+ esink->cur_index = -1;
+
+ esink->eo_size.x = esink->eo_size.y =
+ esink->eo_size.w = esink->eo_size.h = 0;
+ esink->use_ratio = FALSE;
+ esink->sent_buffer_cnt = 0;
+
+ g_mutex_unlock(esink->display_buffer_lock);
}
+#endif
+#ifdef USE_FIMCC
static void
evas_image_sink_cb_pipe (void *data, void *buffer, unsigned int nbyte)
+#endif
+#ifdef USE_TBM_SURFACE
+static void
+evas_image_sink_cb_pipe (void *data, int *buffer_index, unsigned int nbyte)
+#endif
{
- GstBuffer *buf;
GstEvasImageSink *esink = data;
+#ifdef USE_FIMCC
+ GstBuffer *buf;
void *img_data;
-
- if (!data || !buffer) {
+#endif
+ GST_DEBUG ("[ENTER]");
+ if (!esink || !esink->eo) {
+ GST_WARNING ("esink : %p, or eo is NULL returning", esink);
return;
}
- if (nbyte != sizeof (GstBuffer *)) {
+ GST_LOG("esink : %p, esink->eo : %x", esink, esink->eo);
+ if (nbyte == SIZE_FOR_UPDATE_VISIBILITY) {
+ if(!esink->object_show) {
+ evas_object_hide(esink->eo);
+ GST_INFO ("object hide..");
+ } else {
+ evas_object_show(esink->eo);
+ GST_INFO ("object show..");
+ }
+ GST_DEBUG ("[LEAVE]");
return;
}
- if (!esink->eo) {
+#ifdef USE_FIMCC
+ if (!buffer || nbyte != sizeof (GstBuffer *)) {
+ GST_WARNING ("buffer %p, nbyte : %d, sizeof(GstBuffer *) : %d", buffer, nbyte, sizeof (GstBuffer *));
return;
}
+#endif
+#ifdef USE_TBM_SURFACE
+ int index = 0;
+ index = *buffer_index;
+ if ((index<0 || index>=NATIVE_BUFFER_NUM) || nbyte != SIZE_FOR_NATIVE_INDEX) {
+ GST_WARNING ("index : %d, nbyte : %d", index, nbyte);
+ return;
+ }
+#endif
if (GST_STATE(esink) < GST_STATE_PAUSED) {
GST_WARNING ("WRONG-STATE(%d) for rendering, skip this frame", GST_STATE(esink));
return;
}
+#ifdef USE_FIMCC
memcpy (&buf, buffer, sizeof (GstBuffer *));
if (!buf) {
GST_ERROR ("There is no buffer");
}
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));
+ } else if (esink->present_data_addr != DO_RENDER_FROM_FIMC) {
+ GST_WARNING ("skip rendering this buffer, present_data_addr:%d, DO_RENDER_FROM_FIMC:%d", esink->present_data_addr, DO_RENDER_FROM_FIMC);
return;
}
+#endif
+#ifdef USE_TBM_SURFACE
+ g_mutex_lock(esink->display_buffer_lock);
+ if(esink->tbm_surface_format == TBM_FORMAT_NV12) {
+ if (!esink->displaying_buffer[index].tbm_surf) {
+ GST_ERROR("the index's nbuffer was already NULL, so return");
+ g_mutex_unlock(esink->display_buffer_lock);
+ return;
+ }
+ GST_LOG("received (bo %p, gst %p) index num : %d", esink->displaying_buffer[index].bo, esink->displaying_buffer[index].buffer, index);
+ } else if(esink->tbm_surface_format == TBM_FORMAT_YUV420) {
+ GST_LOG("received (bo %p) index num : %d", esink->displaying_buffer[index].bo, index);
+ }
- MMTA_ACUM_ITEM_BEGIN("eavsimagesink _cb_pipe total", FALSE);
+ Evas_Native_Surface surf;
+ surf.type = EVAS_NATIVE_SURFACE_TBM;
+ surf.version = EVAS_NATIVE_SURFACE_VERSION;
+ surf.data.tizen.buffer = esink->displaying_buffer[index].tbm_surf;
+ surf.data.tizen.rot = esink->rotate_angle;
+ surf.data.tizen.flip = esink->flip;
+ GST_LOG("received (bo %p, gst %p) index num : %d", esink->displaying_buffer[index].bo, esink->displaying_buffer[index].buffer, index);
+
+ GstVideoRectangle result = {0};
+
+ evas_object_geometry_get(esink->eo, &esink->eo_size.x, &esink->eo_size.y, &esink->eo_size.w, &esink->eo_size.h);
+ if (!esink->eo_size.w || !esink->eo_size.h) {
+ GST_ERROR ("there is no information for evas object size");
+ g_mutex_unlock(esink->display_buffer_lock);
+ return;
+ }
+ gst_evas_image_sink_update_geometry(esink, &result);
+ if(!result.w || !result.h)
+ {
+ GST_ERROR("no information about geometry (%d, %d)", result.w, result.h);
+ g_mutex_unlock(esink->display_buffer_lock);
+ return;
+ }
+
+ if(esink->use_ratio)
+ {
+ surf.data.tizen.ratio = (float) esink->w / esink->h;
+ GST_LOG("set ratio for letter mode");
+ }
+ evas_object_size_hint_align_set(esink->eo, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(esink->eo, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ if ( !esink->is_evas_object_size_set && esink->w > 0 && esink->h > 0) {
+ evas_object_image_size_set(esink->eo, esink->w, esink->h);
+ esink->is_evas_object_size_set = TRUE;
+ }
+#ifdef DUMP_IMG
+ int ret = 0;
+ char file_name[128];
+ char *dump_data;
+ guint size = esink->w*esink->h + (esink->w/2*esink->h/2)*2; //this size is for I420 format
+ tbm_bo_handle vaddr_dump;
+ if(g_cnt<20)
+ {
+ GST_WARNING ("DUMP IMG_%2.2d : buffer size(%d)", g_cnt, size);
+ sprintf(file_name, "DUMP_IMG_%2.2d.dump", g_cnt++);
+
+ dump_data = g_malloc(size);
+ vaddr_dump = tbm_bo_map(esink->displaying_buffer[index].bo, TBM_DEVICE_CPU, TBM_OPTION_READ|TBM_OPTION_WRITE);
+ if (vaddr_dump.ptr) {
+ tbm_bo_unmap(esink->displaying_buffer[index].bo);
+ }
+ memcpy (dump_data, vaddr_dump.ptr, size);
+
+ ret = util_write_rawdata(file_name, dump_data, size);
+ if (ret) {
+ GST_ERROR_OBJECT (esink, "util_write_rawdata() failed");
+ }
+ g_free(dump_data);
+ }
+#endif
+ evas_object_image_native_surface_set(esink->eo, &surf);
+ GST_DEBUG("native surface set finish");
+
+ if(result.x || result.y)
+ GST_LOG("coordinate x, y (%d, %d) for locating video to center", result.x, result.y);
+ evas_object_image_fill_set(esink->eo, result.x, result.y, result.w, result.h);
+
+ evas_object_image_pixels_dirty_set(esink->eo, EINA_TRUE);
+ GST_DEBUG_OBJECT (esink, "GEO_METHOD : src(%dx%d), dst(%dx%d), dst_x(%d), dst_y(%d), rotate(%d), flip(%d)",
+ esink->w, esink->h, esink->eo_size.w, esink->eo_size.h, esink->eo_size.x, esink->eo_size.y, esink->rotate_angle, esink->flip);
+
+ /* unref previous buffer */
+ if(esink->tbm_surface_format == TBM_FORMAT_NV12) {
+ if(esink->prev_buf && esink->displaying_buffer[esink->prev_index].ref_count)
+ {
+ GST_DEBUG("before index %d's ref_count =%d, gst_buf %p",esink->prev_index,esink->displaying_buffer[esink->prev_index].ref_count, esink->prev_buf);
+ esink->displaying_buffer[esink->prev_index].ref_count--;
+ GST_DEBUG("after index %d's ref_count =%d, gst_buf %p",esink->prev_index,esink->displaying_buffer[esink->prev_index].ref_count, esink->prev_buf);
+ /* Print debug log for 8 frame */
+ if(esink->debuglog_cnt_ecoreCbPipe > 0)
+ {
+ GST_WARNING("(%d) ecore_cb_pipe unref index[%d] .. gst_buf %p", DEBUGLOG_DEFAULT_COUNT-(esink->debuglog_cnt_ecoreCbPipe), esink->prev_index, esink->prev_buf);
+ esink->debuglog_cnt_ecoreCbPipe--;
+ }
+ if (esink->sent_buffer_cnt == MAX_ECOREPIPE_BUFFER_CNT)
+ GST_WARNING("sent buffer cnt 4->3 so skip will be stop");
+
+ esink->sent_buffer_cnt--;
+ GST_DEBUG("prev gst_buffer %p's unref Start!!", esink->prev_buf);
+ gst_buffer_unref(esink->prev_buf);
+ GST_DEBUG("prev gst_buffer %p's unref End!!", esink->prev_buf);
+ } else {
+ GST_DEBUG("ref_count=%d unref prev gst_buffer %p", esink->displaying_buffer[esink->prev_index].ref_count,esink->prev_buf);
+ }
+
+ GST_DEBUG("Current gst_buf %p and index=%d is overwrited to Prev gst_buf %p & index %d",
+ esink->displaying_buffer[index].buffer, index, esink->prev_buf, esink->prev_index );
+ esink->prev_buf = esink->displaying_buffer[index].buffer;
+ esink->prev_index = index;
+ }else if(esink->tbm_surface_format == TBM_FORMAT_YUV420) {
+ /* Print debug log for 8 frame */
+ if(esink->debuglog_cnt_ecoreCbPipe > 0)
+ {
+ GST_WARNING("(%d) ecore_cb_pipe set native surface [%d] tbm_surf[%p]",
+ DEBUGLOG_DEFAULT_COUNT-(esink->debuglog_cnt_ecoreCbPipe), index, esink->displaying_buffer[index].tbm_surf);
+ esink->debuglog_cnt_ecoreCbPipe--;
+ }
+ if (esink->sent_buffer_cnt == MAX_ECOREPIPE_BUFFER_CNT)
+ GST_WARNING("sent buffer cnt 4->3 so skip will be stop");
+
+ esink->sent_buffer_cnt--;
+ }
+#endif
+#ifdef USE_FIMCC
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);
+ 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) {
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);
- __ta__("evasimagesink memcpy in _cb_pipe", memcpy (img_data, GST_BUFFER_DATA (buf), esink->w * esink->h * COLOR_DEPTH););
+ GST_DEBUG ("img_data(%x), GST_BUFFER_DATA(buf):%x, esink->w(%d),esink->h(%d), esink->eo(%x)",img_data,GST_BUFFER_DATA(buf),esink->w,esink->h,esink->eo);
+ 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));
+ GST_DEBUG ("GST_BUFFER_DATA(buf):%x, esink->eo(%x)",GST_BUFFER_DATA(buf),esink->eo);
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;
}
+#endif
+#ifdef USE_TBM_SURFACE
+ g_mutex_unlock(esink->display_buffer_lock);
+#endif
+ GST_DEBUG ("[LEAVE]");
+}
+#ifdef USE_TBM_SURFACE
+static void
+gst_evas_image_sink_update_geometry (GstEvasImageSink *esink, GstVideoRectangle *result)
+{
+ if (!esink || !esink->eo) {
+ GST_WARNING("there is no esink");
+ return;
+ }
- MMTA_ACUM_ITEM_END("eavsimagesink _cb_pipe total", FALSE);
+ result->x = 0;
+ result->y = 0;
+
+ switch (esink->display_geometry_method)
+ {
+ case DISP_GEO_METHOD_LETTER_BOX: // 0
+ /* set black padding for letter box mode */
+ GST_DEBUG("letter box mode");
+ esink->use_ratio = TRUE;
+ result->w = esink->eo_size.w;
+ result->h = esink->eo_size.h;
+ break;
+ case DISP_GEO_METHOD_ORIGIN_SIZE: // 1
+ GST_DEBUG("origin size mode");
+ esink->use_ratio = FALSE;
+ /* set coordinate for each case */
+ result->x = (esink->eo_size.w-esink->w) / 2;
+ result->y = (esink->eo_size.h-esink->h) / 2;
+ result->w = esink->w;
+ result->h = esink->h;
+ break;
+ case DISP_GEO_METHOD_FULL_SCREEN: // 2
+ GST_DEBUG("full screen mode");
+ esink->use_ratio = FALSE;
+ result->w = esink->eo_size.w;
+ result->h = esink->eo_size.h;
+ break;
+ case DISP_GEO_METHOD_CROPPED_FULL_SCREEN: // 3
+ GST_DEBUG("cropped full screen mode");
+ esink->use_ratio = FALSE;
+ /* compare evas object's ratio with video's */
+ if((esink->eo_size.w/esink->eo_size.h) > (esink->w/esink->h))
+ {
+ result->w = esink->eo_size.w;
+ result->h = esink->eo_size.w * esink->h / esink->w;
+ result->y = -(result->h-esink->eo_size.h) / 2;
+ }
+ else
+ {
+ result->w = esink->eo_size.h * esink->w / esink->h;
+ result->h = esink->eo_size.h;
+ result->x = -(result->w-esink->eo_size.w) / 2;
+ }
+ break;
+ case DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX: // 4
+ GST_DEBUG("origin size or letter box mode");
+ /* if video size is smaller than evas object's, it will be set to origin size mode */
+ if((esink->eo_size.w > esink->w) && (esink->eo_size.h > esink->h))
+ {
+ GST_DEBUG("origin size mode");
+ esink->use_ratio = FALSE;
+ /* set coordinate for each case */
+ result->x = (esink->eo_size.w-esink->w) / 2;
+ result->y = (esink->eo_size.h-esink->h) / 2;
+ result->w = esink->w;
+ result->h = esink->h;
+ }
+ else
+ {
+ GST_DEBUG("letter box mode");
+ esink->use_ratio = TRUE;
+ result->w = esink->eo_size.w;
+ result->h = esink->eo_size.h;
+ }
+ break;
+ default:
+ GST_WARNING("unsupported mode.");
+ break;
+ }
+ GST_DEBUG("geometry result [%d, %d, %d, %d]", result->x, result->y, result->w, result->h);
}
+static void
+gst_evas_image_sink_apply_geometry (GstEvasImageSink *esink)
+{
+ if (!esink || !esink->eo) {
+ GST_WARNING("there is no esink");
+ return;
+ }
+
+ Evas_Native_Surface *surf = evas_object_image_native_surface_get(esink->eo);
+ GstVideoRectangle result = {0};
+
+ if(surf)
+ {
+ GST_DEBUG("native surface exists");
+ surf->data.tizen.rot = esink->rotate_angle;
+ surf->data.tizen.flip = esink->flip;
+ evas_object_image_native_surface_set(esink->eo, surf);
+
+ gst_evas_image_sink_update_geometry(esink, &result);
+
+ if(esink->use_ratio)
+ {
+ surf->data.tizen.ratio = (float) esink->w / esink->h;
+ GST_LOG("set ratio for letter mode");
+ }
+
+ if(result.x || result.y)
+ GST_LOG("coordinate x, y (%d, %d) for locating video to center", result.x, result.y);
+
+ evas_object_image_fill_set(esink->eo, result.x, result.y, result.w, result.h);
+ }
+ else
+ GST_WARNING("there is no surf");
+}
+#endif
static void
gst_evas_image_sink_init (GstEvasImageSink *esink, GstEvasImageSinkClass *gclass)
{
- GST_INFO ("enter");
+ GST_DEBUG ("[ENTER]");
esink->eo = NULL;
esink->epipe = NULL;
esink->object_show = FALSE;
- esink->gl_zerocopy = FALSE;
+ esink->update_visibility = UPDATE_FALSE;
esink->is_evas_object_size_set = FALSE;
+#ifdef USE_FIMCC
+ esink->gl_zerocopy = FALSE;
esink->present_data_addr = -1;
-
+#endif
+#ifdef USE_TBM_SURFACE
+ esink->display_buffer_lock = g_mutex_new ();
+ esink->flow_lock = g_mutex_new ();
+ int i = 0;
+ for (i=0; i<NATIVE_BUFFER_NUM; i++)
+ {
+ esink->displaying_buffer[i].tbm_surf = NULL;
+ esink->displaying_buffer[i].buffer = NULL;
+ esink->displaying_buffer[i].bo = NULL;
+ esink->displaying_buffer[i].ref_count = 0;
+ }
+ esink->prev_buf = NULL;
+ esink->prev_index = -1;
+ esink->cur_index = -1;
+ esink->enable_flush_buffer = TRUE;
+ esink->need_flush = FALSE;
+ esink->display_geometry_method = DISP_GEO_METHOD_LETTER_BOX;
+ esink->flip = FLIP_NONE;
+ esink->eo_size.x = esink->eo_size.y =
+ esink->eo_size.w = esink->eo_size.h = 0;
+ esink->use_ratio = FALSE;
+ esink->sent_buffer_cnt = 0;
+ esink->debuglog_cnt_showFrame = DEBUGLOG_DEFAULT_COUNT;
+ esink->debuglog_cnt_ecoreCbPipe = DEBUGLOG_DEFAULT_COUNT;
+
+ esink->src_buf_idx = 0;
+ esink->is_buffer_allocated = FALSE;
+#endif
if(!instance_lock) {
instance_lock = g_mutex_new();
}
g_mutex_unlock (instance_lock);
g_object_weak_ref (G_OBJECT (esink), gst_evas_image_sink_fini, NULL);
+
+ GST_DEBUG ("[LEAVE]");
}
static void
-evas_image_sink_cb_del_eo (void *data, Evas *e, Evas_Object *obj, void *event_info)
+evas_callback_del_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
{
+ GST_DEBUG ("[ENTER]");
+
GstEvasImageSink *esink = data;
if (!esink) {
return;
}
-
- evas_object_event_callback_del (esink->eo, EVAS_CALLBACK_RESIZE, evas_image_sink_cb_resize_event);
+#ifdef USE_FIMCC
if (esink->oldbuf) {
gst_buffer_unref (esink->oldbuf);
esink->oldbuf = NULL;
+ }
+#endif
+ if (esink->eo) {
+#ifdef USE_TBM_SURFACE
+ EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK (evas_object_evas_get(esink->eo));
+ GST_DEBUG("unset EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK esink : %p, esink->eo : %x", esink, esink->eo);
+#endif
+ EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK (esink->eo);
+ GST_DEBUG("unset EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK esink : %p, esink->eo : %x", esink, esink->eo);
+
+ evas_object_image_data_set(esink->eo, NULL);
esink->eo = NULL;
}
+ GST_DEBUG ("[LEAVE]");
}
static void
-evas_image_sink_cb_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+evas_callback_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
{
- int w = 0;
- int h = 0;
+ int x, y, w, h;
+ x = y = w = h = 0;
+
+ GST_DEBUG ("[ENTER]");
+
GstEvasImageSink *esink = data;
- if (!esink) {
+ if (!esink || !esink->eo) {
return;
}
- evas_object_geometry_get(esink->eo, NULL, NULL, &w, &h);
+ evas_object_geometry_get(esink->eo, &x, &y, &w, &h);
if (!w || !h) {
- GST_WARNING ("evas object size (w:%d,h:%d) was not set",w,h);
+ GST_WARNING ("evas object size (w:%d,h:%d) was not set", w, h);
} else {
+#ifdef USE_TBM_SURFACE
+ esink->eo_size.x = x;
+ esink->eo_size.y = y;
+ esink->eo_size.w = w;
+ esink->eo_size.h = h;
+ GST_WARNING ("resize (x:%d, y:%d, w:%d, h:%d)", x, y, w, h);
+ gst_evas_image_sink_apply_geometry(esink);
+#endif
+#ifdef USE_FIMCC
evas_object_image_fill_set(esink->eo, 0, 0, w, h);
- GST_DEBUG ("evas object fill set (w:%d,h:%d)",w,h);
+ GST_DEBUG ("evas object fill set (w:%d,h:%d)", w, h);
+#endif
}
+ GST_DEBUG ("[LEAVE]");
}
+#ifdef USE_TBM_SURFACE
+static void
+evas_callback_render_pre (void *data, Evas *e, void *event_info)
+{
+ GstEvasImageSink *esink = data;
+ if (!esink || !esink->eo) {
+ GST_WARNING("there is no esink info.... esink : %p, or eo is NULL returning", esink);
+ return;
+ }
+ if(esink->need_flush && esink->flush_buffer)
+ {
+ g_mutex_lock(esink->display_buffer_lock);
+ Evas_Native_Surface surf;
+ GstVideoRectangle result = {0};
+
+ evas_object_geometry_get(esink->eo, &esink->eo_size.x, &esink->eo_size.y, &esink->eo_size.w, &esink->eo_size.h);
+ if (!esink->eo_size.w || !esink->eo_size.h) {
+ GST_ERROR ("there is no information for evas object size");
+ return;
+ }
+ gst_evas_image_sink_update_geometry(esink, &result);
+ if(!result.w || !result.h)
+ {
+ GST_ERROR("no information about geometry (%d, %d)", result.w, result.h);
+ return;
+ }
+
+ if(esink->use_ratio)
+ {
+ surf.data.tizen.ratio = (float) esink->w / esink->h;
+ GST_LOG("set ratio for letter mode");
+ }
+ evas_object_size_hint_align_set(esink->eo, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_weight_set(esink->eo, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ if ( !esink->is_evas_object_size_set && esink->w > 0 && esink->h > 0) {
+ evas_object_image_size_set(esink->eo, esink->w, esink->h);
+ esink->is_evas_object_size_set = TRUE;
+ }
+
+ if(result.x || result.y)
+ GST_LOG("coordinate x, y (%d, %d) for locating video to center", result.x, result.y);
+
+ evas_object_image_fill_set(esink->eo, result.x, result.y, result.w, result.h);
+
+ surf.type = EVAS_NATIVE_SURFACE_TBM;
+ surf.version = EVAS_NATIVE_SURFACE_VERSION;
+ surf.data.tizen.buffer = esink->flush_buffer->tbm_surf;
+ surf.data.tizen.rot = esink->rotate_angle;
+ surf.data.tizen.flip = esink->flip;
+ GST_DEBUG("use esink->flush buffer->tbm_surf (%p), rotate(%d), flip(%d)",
+ esink->flush_buffer->tbm_surf, esink->rotate_angle, esink->flip);
+ evas_object_image_native_surface_set(esink->eo, &surf);
+ g_mutex_unlock(esink->display_buffer_lock);
+ esink->need_flush = FALSE;
+ }
+ evas_object_image_pixels_dirty_set (esink->eo, EINA_TRUE);
+ GST_LOG("dirty set finish");
+}
+
+static gboolean
+gst_evas_image_sink_release_source_buffer(GstEvasImageSink *esink)
+{
+ int i = 0;
+ tbm_bo_handle bo_handle;
+ g_mutex_lock(esink->display_buffer_lock);
+
+ for(i = 0; i < NATIVE_BUFFER_NUM; i++) {
+ GST_WARNING("[reset] reset gst %p", esink->displaying_buffer[i].buffer);
+ esink->displaying_buffer[i].bo = NULL;
+ esink->displaying_buffer[i].tbm_surf = NULL;
+ esink->displaying_buffer[i].ref_count = 0;
+ }
+
+ for(i = 0; i < SOURCE_BUFFER_NUM; i++) {
+ if(esink->src_buffer_info[i].bo) {
+ tbm_bo_unmap(esink->src_buffer_info[i].bo);
+ esink->src_buffer_info[i].bo = NULL;
+ }
+
+ if (esink->src_buffer_info[i].tbm_surf) {
+ tbm_surface_destroy(esink->src_buffer_info[i].tbm_surf);
+ esink->src_buffer_info[i].tbm_surf = NULL;
+ }
+ }
+
+ esink->is_buffer_allocated = FALSE;
+ esink->src_buf_idx = 0;
+ esink->prev_buf = NULL;
+ esink->prev_index = -1;
+ esink->cur_index = -1;
+
+ esink->eo_size.x = esink->eo_size.y =
+ esink->eo_size.w = esink->eo_size.h = 0;
+ esink->use_ratio = FALSE;
+ esink->sent_buffer_cnt = 0;
+
+ g_mutex_unlock(esink->display_buffer_lock);
+
+ return TRUE;
+}
+
+static gboolean
+gst_evas_image_sink_allocate_source_buffer(GstEvasImageSink *esink)
+{
+ int size = 0;
+ int idx = 0;
+ tbm_bo bo;
+ tbm_bo_handle vaddr;
+ GstFlowReturn ret=GST_FLOW_OK;
+
+ if (esink == NULL) {
+ GST_ERROR("handle is NULL");
+ return FALSE;
+ }
+
+ for(idx=0; idx < SOURCE_BUFFER_NUM; idx++) {
+ if(!esink->src_buffer_info[idx].tbm_surf) {
+ /* create tbm surface */
+ esink->src_buffer_info[idx].tbm_surf = tbm_surface_create(esink->w, esink->h, esink->tbm_surface_format);
+ }
+ if(!esink->src_buffer_info[idx].tbm_surf)
+ {
+ GST_ERROR("tbm_surf is NULL!!");
+ goto ALLOC_FAILED;
+ }
+
+ /* get bo and size */
+ bo = tbm_surface_internal_get_bo(esink->src_buffer_info[idx].tbm_surf, 0);
+ size = tbm_bo_size(bo);
+ if(!bo || !size)
+ {
+ GST_ERROR("bo(%p), size(%d)", bo, size);
+ goto ALLOC_FAILED;
+ }
+ esink->src_buffer_info[idx].bo = bo;
+
+ vaddr = tbm_bo_map(bo, TBM_DEVICE_CPU, TBM_OPTION_READ|TBM_OPTION_WRITE);
+ if (!vaddr.ptr) {
+ GST_WARNING_OBJECT(esink, "get vaddr failed pointer = %p", vaddr.ptr);
+ tbm_bo_unmap(bo);
+ goto ALLOC_FAILED;
+ } else {
+ memset (vaddr.ptr, 0x0, size);
+ GST_LOG_OBJECT (esink, "tbm_bo_map(VADDR) finished, bo(%p), vaddr(%p)", bo, vaddr.ptr);
+ }
+
+ esink->src_buffer_info[idx].usr_addr = vaddr.ptr;
+
+ GST_WARNING_OBJECT(esink, "src buffer index:%d , tbm surface : %p", idx, esink->src_buffer_info[idx].tbm_surf);
+ }
+
+ return TRUE;
+
+ALLOC_FAILED:
+ gst_evas_image_sink_release_source_buffer(esink);
+ return FALSE;
+}
+#endif
static int
evas_image_sink_get_size_from_caps (GstCaps *caps, int *w, int *h)
r = gst_structure_get_int (s, CAP_WIDTH, &width);
if (r == FALSE) {
+ GST_DEBUG("fail to get width from caps");
return -1;
}
r = gst_structure_get_int (s, CAP_HEIGHT, &height);
if (r == FALSE) {
+ GST_DEBUG("fail to get height from caps");
return -1;
}
*w = width;
*h = height;
+ GST_DEBUG ("size w(%d), h(%d)", width, height);
+
return 0;
}
-
+#ifdef USE_FIMCC
static gboolean
is_zerocopy_supported (Evas *e)
{
return 0;
}
-
+#endif
static gboolean
gst_evas_image_sink_event (GstBaseSink *sink, GstEvent *event)
{
+#ifdef USE_FIMCC
GstEvasImageSink *esink = GST_EVASIMAGESINK (sink);
GstMessage *msg;
gchar *str;
-
+#endif
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
GST_DEBUG ("GST_EVENT_FLUSH_START");
case GST_EVENT_EOS:
GST_DEBUG ("GST_EVENT_EOS");
break;
+#ifdef USE_FIMCC
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);
GST_ERROR ("evas_image_sink_event_parse_data() failed");
}
break;
+#endif
default:
break;
}
{
GstStateChangeReturn ret_state = GST_STATE_CHANGE_SUCCESS;
GstEvasImageSink *esink = NULL;
+ GstFlowReturn ret=GST_FLOW_OK;
esink = GST_EVASIMAGESINK(element);
- int ret = 0;
if(!esink) {
GST_ERROR("can not get evasimagesink from element");
+ return GST_STATE_CHANGE_FAILURE;
}
+
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
- GST_INFO ("*** STATE_CHANGE_NULL_TO_READY ***");
+ GST_WARNING ("*** STATE_CHANGE_NULL_TO_READY ***");
+#ifdef USE_TBM_SURFACE
+ g_mutex_lock (esink->flow_lock);
+ if (!is_evas_image_object (esink->eo)) {
+ GST_ERROR_OBJECT (esink, "There is no evas image object..");
+ g_mutex_unlock (esink->flow_lock);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+ g_mutex_unlock (esink->flow_lock);
+#endif
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_INFO ("*** STATE_CHANGE_READY_TO_PAUSED ***");
+ GST_WARNING ("*** STATE_CHANGE_READY_TO_PAUSED ***");
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- GST_INFO ("*** STATE_CHANGE_PAUSED_TO_PLAYING ***");
+#ifdef USE_TBM_SURFACE
+ /* Print debug log for 8 frame */
+ esink->debuglog_cnt_showFrame = DEBUGLOG_DEFAULT_COUNT;
+ esink->debuglog_cnt_ecoreCbPipe = DEBUGLOG_DEFAULT_COUNT;
+#endif
+ GST_WARNING ("*** STATE_CHANGE_PAUSED_TO_PLAYING ***");
break;
default:
break;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- GST_INFO ("*** STATE_CHANGE_PLAYING_TO_PAUSED ***");
+ GST_WARNING ("*** STATE_CHANGE_PLAYING_TO_PAUSED ***");
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_INFO ("*** STATE_CHANGE_PAUSED_TO_READY ***");
+ GST_WARNING ("*** STATE_CHANGE_PAUSED_TO_READY ***");
+#ifdef USE_TBM_SURFACE
+ Eina_Bool r;
+ /* flush buffer, we will copy last buffer to keep image data and reset buffer list */
+ GST_WARNING("esink->enable_flush_buffer : %d", esink->enable_flush_buffer);
+ if(esink->enable_flush_buffer && esink->tbm_surface_format == TBM_FORMAT_NV12)
+ {
+ if (gst_esink_make_flush_buffer(esink) == FALSE) {
+ ret = gst_esink_epipe_reset(esink);
+ if(ret) {
+ GST_ERROR_OBJECT(esink, "evas epipe reset ret=%d, need to check",ret);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+ gst_evas_image_sink_reset(esink);
+ }
+ }
+ else {
+ ret = gst_esink_epipe_reset(esink);
+ if(ret) {
+ GST_ERROR_OBJECT(esink, "evas epipe reset ret=%d, need to check",ret);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+ if(esink->tbm_surface_format == TBM_FORMAT_NV12) {
+ gst_evas_image_sink_reset(esink);
+ } else if(esink->tbm_surface_format == TBM_FORMAT_YUV420) {
+ gst_evas_image_sink_release_source_buffer(esink);
+ }
+ }
+#endif
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);
+ GST_WARNING ("*** STATE_CHANGE_READY_TO_NULL ***");
+#ifdef USE_TBM_SURFACE
+ if(esink->flush_buffer)
+ _release_flush_buffer(esink);
+ EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK (evas_object_evas_get(esink->eo));
+#endif
+ EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK (esink->eo);
if (esink->epipe) {
+ GST_DEBUG("ecore-pipe will delete");
ecore_pipe_del (esink->epipe);
esink->epipe = NULL;
}
eo = g_value_get_pointer (value);
if (is_evas_image_object (eo)) {
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);
-
+ Eina_Bool r;
+#ifdef USE_TBM_SURFACE
+ EVASIMAGESINK_UNSET_EVAS_EVENT_CALLBACK (evas_object_evas_get(esink->eo));
+#endif
+ /* delete evas object callbacks registrated on a previous evas image object */
+ EVASIMAGESINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK (esink->eo);
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);
-
+ EVASIMAGESINK_SET_EVAS_OBJECT_EVENT_CALLBACK (esink->eo, esink);
+#ifdef USE_TBM_SURFACE
+ GST_WARNING("register render callback [esink : %p, esink->eo : %x]", esink, esink->eo);
+ EVASIMAGESINK_SET_EVAS_EVENT_CALLBACK (evas_object_evas_get(esink->eo), esink);
+ evas_object_geometry_get(esink->eo, &esink->eo_size.x, &esink->eo_size.y, &esink->eo_size.w, &esink->eo_size.h);
+ GST_WARNING ("evas object size (x:%d, y:%d, w:%d, h:%d)", esink->eo_size.x, esink->eo_size.y, esink->eo_size.w, esink->eo_size.h);
+#endif
+#ifdef USE_FIMCC
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);
+#endif
+ GST_DEBUG("Evas Image Object(%x) is set", esink->eo);
+ esink->is_evas_object_size_set = FALSE;
esink->object_show = TRUE;
+ esink->update_visibility = UPDATE_TRUE;
+ if (esink->epipe) {
+ r = ecore_pipe_write (esink->epipe, &esink->update_visibility, SIZE_FOR_UPDATE_VISIBILITY);
+ if (r == EINA_FALSE) {
+ GST_WARNING ("Failed to ecore_pipe_write() for updating visibility\n");
+ }
+ }
}
} else {
GST_ERROR ("Cannot set evas-object property: value is not an evas image object");
break;
case PROP_EVAS_OBJECT_SHOW:
+ {
+ Eina_Bool r;
esink->object_show = g_value_get_boolean (value);
if( !is_evas_image_object(esink->eo) ) {
GST_WARNING ("Cannot apply visible(show-object) property: cannot get an evas object\n");
break;
}
- if(!esink->object_show) {
- evas_object_hide(esink->eo);
- GST_INFO ("object hide..");
- } else {
- evas_object_show(esink->eo);
- GST_INFO ("object show..");
+ esink->update_visibility = UPDATE_TRUE;
+ GST_LOG("esink->update_visibility : %d", esink->update_visibility);
+ if (esink->epipe) {
+ r = ecore_pipe_write (esink->epipe, &esink->update_visibility, SIZE_FOR_UPDATE_VISIBILITY);
+ if (r == EINA_FALSE) {
+ GST_WARNING ("Failed to ecore_pipe_write() for updating visibility)\n");
+ }
+ }
+ break;
+ }
+#ifdef USE_TBM_SURFACE
+ case PROP_ROTATE_ANGLE:
+ {
+ int rotate = 0;
+ rotate = g_value_get_int (value);
+ switch(rotate)
+ {
+ case DEGREE_0:
+ esink->rotate_angle = 0;
+ break;
+ case DEGREE_90:
+ esink->rotate_angle = 90;
+ break;
+ case DEGREE_180:
+ esink->rotate_angle = 180;
+ break;
+ case DEGREE_270:
+ esink->rotate_angle = 270;
+ break;
+ default:
+ break;
}
+ GST_INFO("update rotate_angle : %d", esink->rotate_angle);
break;
+ }
+ case PROP_DISPLAY_GEOMETRY_METHOD:
+ {
+ Eina_Bool r;
+ guint geometry = g_value_get_int (value);
+ if (esink->display_geometry_method != geometry) {
+ esink->display_geometry_method = geometry;
+ GST_INFO_OBJECT (esink, "Overlay geometry method update, display_geometry_method(%d)", esink->display_geometry_method);
+ }
+ g_mutex_lock(esink->display_buffer_lock);
+
+ if(esink->cur_index!=-1 && esink->epipe)
+ {
+ GST_WARNING("apply property esink->cur_index =%d",esink->cur_index);
+ esink->displaying_buffer[esink->cur_index].ref_count++;
+ gst_buffer_ref(esink->displaying_buffer[esink->cur_index].buffer);
+ esink->sent_buffer_cnt++;
+ r = ecore_pipe_write (esink->epipe, &esink->cur_index, SIZE_FOR_NATIVE_INDEX);
+
+ if (r == EINA_FALSE) {
+ GST_WARNING("ecore_pipe_write fail");
+ esink->displaying_buffer[esink->cur_index].ref_count--;
+ gst_buffer_unref(esink->displaying_buffer[esink->cur_index].buffer);
+ esink->sent_buffer_cnt--;
+ }
+ }
+ g_mutex_unlock(esink->display_buffer_lock);
+ break;
+ }
+ case PROP_ENABLE_FLUSH_BUFFER:
+ esink->enable_flush_buffer = g_value_get_boolean(value);
+ break;
+ case PROP_FLIP:
+ {
+ esink->flip = g_value_get_int(value);
+ GST_INFO("update flip : %d", esink->flip);
+ break;
+ }
+#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_EVAS_OBJECT_SHOW:
g_value_set_boolean (value, esink->object_show);
break;
+#ifdef USE_TBM_SURFACE
+ case PROP_ROTATE_ANGLE:
+ g_value_set_int (value, esink->rotate_angle);
+ break;
+ case PROP_DISPLAY_GEOMETRY_METHOD:
+ g_value_set_int (value, esink->display_geometry_method);
+ break;
+ case PROP_ENABLE_FLUSH_BUFFER:
+ g_value_set_boolean(value, esink->enable_flush_buffer);
+ break;
+ case PROP_FLIP:
+ g_value_set_int(value, esink->flip);
+ break;
+#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
int r;
int w, h;
GstEvasImageSink *esink = GST_EVASIMAGESINK (base_sink);
+ GstStructure *structure = NULL;
+ guint32 format = 0;
esink->is_evas_object_size_set = FALSE;
r = evas_image_sink_get_size_from_caps (caps, &w, &h);
esink->h = h;
GST_DEBUG ("set size w(%d), h(%d)", w, h);
}
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (!structure) {
+ return FALSE;
+ }
+
+ if (!gst_structure_get_fourcc (structure, "format", &format)) {
+ GST_DEBUG ("format(dst) is not set...it may be rgb series");
+ }
+
+ GST_DEBUG_OBJECT(esink, "source color format is %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (format));
+
+#ifdef USE_TBM_SURFACE
+ if (format == GST_MAKE_FOURCC('S','N','1','2') || format == GST_MAKE_FOURCC('N','V','1','2') ) {
+ esink->tbm_surface_format = TBM_FORMAT_NV12;
+ } else if (format == GST_MAKE_FOURCC('I','4','2','0')){
+ esink->tbm_surface_format = TBM_FORMAT_YUV420;
+ } else {
+ GST_ERROR("cannot parse fourcc format from caps.");
+ return FALSE;
+ }
+#endif
+
return TRUE;
}
GstEvasImageSink *esink = GST_EVASIMAGESINK (video_sink);
Eina_Bool r;
+ GST_LOG("[ENTER] show frame");
+#ifdef USE_TBM_SURFACE
+ if (!gst_evas_image_sink_ref_count (buf))
+ {
+ GST_WARNING("ref count is 0.. skip show frame");
+ return GST_FLOW_OK;
+ }
+#endif
g_mutex_lock (instance_lock);
+#ifdef USE_FIMCC
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));
+ } else if (esink->present_data_addr != DO_RENDER_FROM_FIMC) {
+ GST_WARNING ("skip rendering this buffer, present_data_addr:%d, DO_RENDER_FROM_FIMC:%d", esink->present_data_addr, DO_RENDER_FROM_FIMC);
g_mutex_unlock (instance_lock);
- return;
+ return GST_FLOW_OK;
}
+#endif
if (!esink->epipe) {
- esink->epipe = ecore_pipe_add (evas_image_sink_cb_pipe, esink);
+ esink->epipe = ecore_pipe_add ((Ecore_Pipe_Cb)evas_image_sink_cb_pipe, esink);
if (!esink->epipe) {
GST_ERROR ("ecore-pipe create failed");
+ g_mutex_unlock (instance_lock);
return GST_FLOW_ERROR;
}
+ GST_DEBUG("ecore-pipe create success");
}
- if (esink->object_show) {
+#ifdef USE_TBM_SURFACE
+ int i = 0;
+ int index = -1;
+ SCMN_IMGB *scmn_imgb = NULL;
+ gboolean exist_bo = FALSE;
+
+ GST_LOG ("BUF: gst_buf= %p ts= %" GST_TIME_FORMAT " size= %lu, mallocdata= %p",
+ buf, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf), GST_BUFFER_MALLOCDATA(buf));
+
+ if(esink->tbm_surface_format == TBM_FORMAT_NV12) {
+ /* get received buffer informations */
+ scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
+ if (!scmn_imgb) {
+ GST_WARNING("scmn_imgb is NULL. Skip..." );
+ g_mutex_unlock (instance_lock);
+ return GST_FLOW_OK;
+ }
+ if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_TIZEN_BUFFER)
+ {
+ /* check whether bo is new or not */
+ for(i=0; i<NATIVE_BUFFER_NUM; i++)
+ {
+ if(esink->displaying_buffer[i].bo==scmn_imgb->bo[0])
+ {
+ GST_DEBUG("it is already saved bo %p (index num : %d)", esink->displaying_buffer[i].bo, i);
+ index = i;
+ exist_bo = TRUE;
+ break;
+ }
+ else
+ exist_bo = FALSE;
+ }
+ /* keep bo */
+ if(!exist_bo)
+ {
+ /* find empty buffer space for indexing */
+ for(i=0; i<NATIVE_BUFFER_NUM; i++)
+ {
+ if(!esink->displaying_buffer[i].bo)
+ {
+ index = i;
+ break;
+ }
+ }
+ if(index!=-1)
+ {
+ /* keep informations */
+ esink->displaying_buffer[index].buffer = buf;
+ esink->displaying_buffer[index].bo = scmn_imgb->bo[0];
+ GST_WARNING("TBM bo %p / gst_buf %p", esink->displaying_buffer[index].bo, esink->displaying_buffer[index].buffer);
+
+ /* create new tbm surface */
+ esink->displaying_buffer[index].tbm_surf = tbm_surface_internal_create_with_bos(esink->w, esink->h,
+ TBM_FORMAT_NV12, esink->displaying_buffer[index].bo, TBM_BO_DEFAULT);
+ if(!esink->displaying_buffer[index].tbm_surf)
+ {
+ GST_WARNING("there is no tbm surface.. bo : %p, gst_buf : %p", esink->displaying_buffer[index].bo, esink->displaying_buffer[index].buffer);
+ g_mutex_unlock (instance_lock);
+ return GST_FLOW_OK;
+ }
+
+ GST_WARNING("create tbm surface : %p", esink->displaying_buffer[index].tbm_surf);
+ }
+ }
+ /* because it has same bo, use existing tbm surface */
+ else
+ {
+ if(index!=-1)
+ {
+ esink->displaying_buffer[index].buffer = buf;
+ GST_DEBUG("existing tbm surface %p.. bo %p, gst_buf %p", esink->displaying_buffer[index].tbm_surf, esink->displaying_buffer[index].bo, esink->displaying_buffer[index].buffer);
+ exist_bo = FALSE;
+ }
+ }
+
+ /* if it couldn't find proper index */
+ if(index == -1)
+ GST_WARNING("all spaces are using!!!");
+ else
+ GST_DEBUG("selected buffer index %d", index);
+
+ }
+ else if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_FLUSH_BUFFER)
+ {
+ /* flush buffer, we will copy last buffer to keep image data and reset buffer list */
+ GST_WARNING_OBJECT(esink, "FLUSH_BUFFER: gst_buf= %p ts= %" GST_TIME_FORMAT " size= %lu, mallocdata= %p",
+ buf, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf), GST_BUFFER_MALLOCDATA(buf));
+ gst_buffer_ref (buf);
+ if (gst_esink_make_flush_buffer(esink))
+ GST_WARNING("made flush buffer");
+ gst_buffer_unref (buf);
+ }
+ else
+ {
+ GST_ERROR("it is not TBM buffer.. %d", scmn_imgb->buf_share_method);
+ }
+ } else if(esink->tbm_surface_format == TBM_FORMAT_YUV420) {
+ static int skip_count_i420=0;
+ if(esink->sent_buffer_cnt >= MAX_ECOREPIPE_BUFFER_CNT) {
+ if(!(skip_count_i420++%20)) {
+ GST_WARNING("[%d]EA buffer was already sent to ecore pipe, and %d frame skipped", esink->sent_buffer_cnt,skip_count_i420);
+ }
+ g_mutex_unlock (instance_lock);
+ return GST_FLOW_OK;
+ }
+
+ if(!esink->is_buffer_allocated) {
+ /* Allocate TBM buffer for non-zero copy case */
+ if(!gst_evas_image_sink_allocate_source_buffer(esink)) {
+ GST_ERROR_OBJECT(esink, "Buffer allocation failed");
+ g_mutex_unlock (instance_lock);
+ return GST_FLOW_ERROR;
+ }
+ esink->is_buffer_allocated = TRUE;
+ }
+
+ skip_count_i420 = 0; //for skip log in I420
+
+ /* check whether bo is new or not */
+ for(i=0; i<NATIVE_BUFFER_NUM; i++) {
+ if(esink->displaying_buffer[i].bo == esink->src_buffer_info[esink->src_buf_idx].bo) {
+ GST_DEBUG_OBJECT(esink, "it is already saved bo %p (index num : %d)", esink->displaying_buffer[i].bo, i);
+ index = i;
+ exist_bo = TRUE;
+ break;
+ }
+ else
+ exist_bo = FALSE;
+ }
+
+ /* keep bo */
+ if(!exist_bo) {
+ /* find empty buffer space for indexing */
+ for(i=0; i<NATIVE_BUFFER_NUM; i++) {
+ if(!esink->displaying_buffer[i].bo) {
+ index = i;
+ break;
+ }
+ }
+
+ if(index!=-1) {
+ /* keep informations */
+ esink->displaying_buffer[index].bo = esink->src_buffer_info[esink->src_buf_idx].bo;
+ esink->displaying_buffer[index].tbm_surf = esink->src_buffer_info[esink->src_buf_idx].tbm_surf;
+
+ GST_DEBUG_OBJECT(esink, "gst_buf %p, TBM bo %p.. gst_buf vaddr %p .. src data size(%lu)",
+ buf, esink->displaying_buffer[index].bo, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
+
+ memcpy(esink->src_buffer_info[esink->src_buf_idx].usr_addr, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
+ }
+ } else {
+ /* because it has same bo, use existing tbm surface */
+ if(index!=-1) {
+ memcpy(esink->src_buffer_info[esink->src_buf_idx].usr_addr, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
+
+ GST_DEBUG_OBJECT(esink, "existing tbm surface %p.. gst_buf %p, bo %p, gst_buf vaddr %p",
+ esink->displaying_buffer[index].tbm_surf, buf, esink->displaying_buffer[index].bo, GST_BUFFER_DATA(buf));
+ exist_bo = FALSE;
+ }
+ }
+
+ /* if it couldn't find proper index */
+ if(index == -1)
+ GST_WARNING_OBJECT(esink, "all spaces are being used!!!");
+ else
+ GST_DEBUG_OBJECT(esink, "selected buffer index %d", index);
+
+ } else {
+ GST_ERROR_OBJECT(esink, "unsupported color format");
+ g_mutex_unlock (instance_lock);
+ return GST_FLOW_ERROR;
+ }
+#endif
+ if (esink->object_show && index != -1) {
+#ifdef USE_FIMCC
gst_buffer_ref (buf);
- __ta__("evasimagesink ecore_pipe_write", r = ecore_pipe_write (esink->epipe, &buf, sizeof (GstBuffer *)););
+ r = ecore_pipe_write (esink->epipe, &buf, sizeof (GstBuffer *));
if (r == EINA_FALSE) {
+ //add error handling
+ GST_WARNING("ecore_pipe_write fail");
gst_buffer_unref (buf);
}
- GST_DEBUG ("after ecore_pipe_write()");
+#endif
+#ifdef USE_TBM_SURFACE
+ int old_curidx=esink->cur_index ;
+ static int skip_count=0;
+ g_mutex_lock(esink->display_buffer_lock);
+
+ if(esink->tbm_surface_format == TBM_FORMAT_NV12 && scmn_imgb->buf_share_method != BUF_SHARE_METHOD_FLUSH_BUFFER) {
+ if (esink->sent_buffer_cnt < MAX_ECOREPIPE_BUFFER_CNT) {
+ GST_LOG("[show_frame] before refcount : %d .. gst_buf : %p", gst_evas_image_sink_ref_count (buf), buf);
+ gst_buffer_ref (buf);
+ esink->displaying_buffer[index].ref_count++;
+ esink->cur_index = index;
+ GST_LOG("index %d set refcount increase as %d", index,esink->displaying_buffer[index].ref_count);
+ GST_LOG("[show_frame] after refcount : %d .. gst_buf : %p", gst_evas_image_sink_ref_count (buf), buf);
+
+ /* Print debug log for 8 frame */
+ if(esink->debuglog_cnt_showFrame > 0)
+ {
+ GST_WARNING("(%d) ecore_pipe_write index[%d] gst_buf : %p", DEBUGLOG_DEFAULT_COUNT-(esink->debuglog_cnt_showFrame),
+ esink->cur_index, esink->displaying_buffer[esink->cur_index].buffer);
+ esink->debuglog_cnt_showFrame--;
+ }
+
+ esink->sent_buffer_cnt++;
+ skip_count =0 ;
+
+ r = ecore_pipe_write (esink->epipe, &esink->cur_index , SIZE_FOR_NATIVE_INDEX);
+
+ if (r == EINA_FALSE) {
+ GST_LOG("[show_frame] before refcount : %d .. gst_buf : %p", gst_evas_image_sink_ref_count (buf), buf);
+ esink->cur_index = old_curidx;
+ if(esink->displaying_buffer[index].ref_count)
+ {
+ esink->displaying_buffer[index].ref_count--;
+ esink->sent_buffer_cnt--;
+ gst_buffer_unref(buf);
+ GST_ERROR("finish unreffing");
+ }
+ }
+ } else {
+ /* If buffer count which is sent to ecore pipe, is upper 3, Print Error log */
+ if(!(skip_count++%20)) {
+ GST_WARNING("[%d]EA buffer was already sent to ecore pipe, and %d frame skipped", esink->sent_buffer_cnt,skip_count);
+ }
+ }
+ } else if (esink->tbm_surface_format == TBM_FORMAT_YUV420) {
+ esink->cur_index = index;
+ GST_LOG("index %d", index);
+ GST_LOG("[show_frame] gst_buf vaddr %p", esink->src_buffer_info[esink->src_buf_idx].usr_addr);
+
+ /* Print debug log for 8 frame */
+ if(esink->debuglog_cnt_showFrame > 0)
+ {
+ GST_WARNING("(%d) ecore_pipe_write : index[%d], bo[%p], tbm_surf[%p], gst_buf[%p], gst_buf vaddr [%p]", DEBUGLOG_DEFAULT_COUNT-(esink->debuglog_cnt_showFrame),
+ esink->cur_index, esink->displaying_buffer[index].bo, esink->displaying_buffer[index].tbm_surf, buf, esink->src_buffer_info[esink->src_buf_idx].usr_addr);
+ esink->debuglog_cnt_showFrame--;
+ }
+
+ esink->sent_buffer_cnt++;
+
+ esink->src_buf_idx++;
+ r = ecore_pipe_write (esink->epipe, &esink->cur_index , SIZE_FOR_NATIVE_INDEX);
+
+ if (r == EINA_FALSE) {
+ esink->cur_index = old_curidx;
+ esink->sent_buffer_cnt--;
+ esink->src_buf_idx--;
+ GST_ERROR("ecore_pipe_write is failed. index[%d], bo[%p], tbm_surf[%p], gst_buf vaddr [%p]",
+ index, esink->displaying_buffer[index].bo, esink->displaying_buffer[index].tbm_surf, esink->src_buffer_info[esink->src_buf_idx].usr_addr);
+ }
+ esink->src_buf_idx = esink->src_buf_idx % SOURCE_BUFFER_NUM;
+ }
+
+ g_mutex_unlock(esink->display_buffer_lock);
+#endif
+ GST_DEBUG ("ecore_pipe_write() was called with gst_buf= %p.. gst_buf Vaddr=%p, mallocdata= %p", buf, GST_BUFFER_DATA(buf), GST_BUFFER_MALLOCDATA(buf));
} else {
- GST_DEBUG ("skip ecore_pipe_write()");
+ GST_WARNING ("skip ecore_pipe_write(). becuase of esink->object_show(%d) index(%d)", esink->object_show, index);
}
g_mutex_unlock (instance_lock);
+ GST_DEBUG ("Leave");
return GST_FLOW_OK;
}
+#ifdef USE_TBM_SURFACE
+static GstFlowReturn
+gst_esink_epipe_reset(GstEvasImageSink *esink)
+{
+ if (esink == NULL) {
+ GST_ERROR("handle is NULL");
+ return GST_FLOW_ERROR;
+ }
+
+ if (esink->epipe) {
+ GST_DEBUG("ecore-pipe will delete");
+ ecore_pipe_del (esink->epipe);
+ esink->epipe = NULL;
+ }
+
+ if (!esink->epipe) {
+ esink->epipe = ecore_pipe_add ((Ecore_Pipe_Cb)evas_image_sink_cb_pipe, esink);
+ if (!esink->epipe) {
+ GST_ERROR ("ecore-pipe create failed");
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG("ecore-pipe create success");
+ }
+
+ return GST_FLOW_OK;
+}
+
+
+static gboolean gst_esink_make_flush_buffer(GstEvasImageSink *esink)
+{
+ GstEvasImageFlushBuffer *flush_buffer = NULL;
+ GstEvasImageDisplayingBuffer *display_buffer = NULL;
+ int size = 0;
+ tbm_bo bo;
+ tbm_bo_handle vaddr_src;
+ tbm_bo_handle vaddr_dst;
+ GstFlowReturn ret=GST_FLOW_OK;
+ int src_size = 0;
+
+ if (esink == NULL) {
+ GST_ERROR("handle is NULL");
+ return FALSE;
+ }
+
+ if (esink->cur_index == -1) {
+ GST_WARNING_OBJECT(esink, "there is no remained buffer");
+ return FALSE;
+ }
+
+ if(esink->flush_buffer)
+ _release_flush_buffer(esink);
+
+ /* malloc buffer */
+ flush_buffer = (GstEvasImageFlushBuffer *)malloc(sizeof(GstEvasImageFlushBuffer));
+ if (flush_buffer == NULL) {
+ GST_ERROR_OBJECT(esink, "GstEvasImageFlushBuffer alloc failed");
+ return FALSE;
+ }
+
+ memset(flush_buffer, 0x0, sizeof(GstEvasImageFlushBuffer));
+
+ display_buffer = &(esink->displaying_buffer[esink->cur_index]);
+ GST_WARNING_OBJECT(esink, "cur_index [%d]",
+ esink->cur_index);
+ if(!display_buffer || !display_buffer->bo)
+ {
+ GST_WARNING("display_buffer(%p) or bo (%p) is NULL!!", display_buffer, display_buffer->bo);
+ goto FLUSH_BUFFER_FAILED;
+ }
+
+ /* create tbm surface */
+ flush_buffer->tbm_surf = tbm_surface_create(esink->w, esink->h, TBM_FORMAT_NV12);
+
+ if(!flush_buffer->tbm_surf)
+ {
+ GST_ERROR("tbm_surf is NULL!!");
+ goto FLUSH_BUFFER_FAILED;
+ }
+
+ /* get bo and size */
+ bo = tbm_surface_internal_get_bo(flush_buffer->tbm_surf, 0);
+ size = tbm_bo_size(bo);
+ if(!bo || !size)
+ {
+ GST_ERROR("bo(%p), size(%d)", bo, size);
+ goto FLUSH_BUFFER_FAILED;
+ }
+ flush_buffer->bo = bo;
+
+ src_size = display_buffer->buffer->size;
+
+ vaddr_src = tbm_bo_map(display_buffer->bo, TBM_DEVICE_CPU, TBM_OPTION_READ|TBM_OPTION_WRITE);
+ vaddr_dst = tbm_bo_map(bo, TBM_DEVICE_CPU, TBM_OPTION_READ|TBM_OPTION_WRITE);
+ if (!vaddr_src.ptr || !vaddr_dst.ptr) {
+ GST_WARNING_OBJECT(esink, "get vaddr failed src %p, dst %p", vaddr_src.ptr, vaddr_dst.ptr);
+ if (vaddr_src.ptr) {
+ tbm_bo_unmap(display_buffer->bo);
+ }
+ if (vaddr_dst.ptr) {
+ tbm_bo_unmap(bo);
+ }
+ goto FLUSH_BUFFER_FAILED;
+ } else {
+ memset (vaddr_dst.ptr, 0x0, size);
+ GST_WARNING_OBJECT (esink, "tbm_bo_map(VADDR) finished, bo(%p), vaddr(%p)", bo, vaddr_dst.ptr);
+ }
+
+ /* copy buffer */
+ memcpy(vaddr_dst.ptr, vaddr_src.ptr, src_size);
+
+ tbm_bo_unmap(display_buffer->bo);
+ tbm_bo_unmap(bo);
+
+ GST_WARNING_OBJECT(esink, "copy done.. tbm surface : %p src_size : %d", flush_buffer->tbm_surf, src_size);
+
+ esink->flush_buffer = flush_buffer;
+
+ /* initialize buffer list */
+ if (esink->object_show)
+ esink->need_flush = TRUE;
+
+ ret = gst_esink_epipe_reset(esink);
+ if(ret) {
+ GST_ERROR_OBJECT(esink, "evas epipe reset ret=%d, need to check",ret);
+ return FALSE;
+ } else {
+ GST_ERROR_OBJECT(esink, "evas epipe reset success");
+ }
+
+ gst_evas_image_sink_reset(esink);
+
+
+ return TRUE;
+
+FLUSH_BUFFER_FAILED:
+ if (flush_buffer) {
+ if(flush_buffer->tbm_surf)
+ {
+ tbm_surface_destroy(flush_buffer->tbm_surf);
+ flush_buffer->tbm_surf = NULL;
+ }
+
+ free(flush_buffer);
+ flush_buffer = NULL;
+ }
+ return FALSE;
+}
+
+ static void _release_flush_buffer(GstEvasImageSink *esink)
+{
+ if (esink == NULL ||
+ esink->flush_buffer == NULL) {
+ GST_WARNING("handle is NULL");
+ return;
+ }
+
+ GST_WARNING_OBJECT(esink, "release FLUSH BUFFER start");
+ if (esink->flush_buffer->bo) {
+ esink->flush_buffer->bo = NULL;
+ }
+ if(esink->flush_buffer->tbm_surf) {
+ tbm_surface_destroy(esink->flush_buffer->tbm_surf);
+ esink->flush_buffer->tbm_surf = NULL;
+ }
+
+ GST_WARNING_OBJECT(esink, "release FLUSH BUFFER done");
+
+ free(esink->flush_buffer);
+ esink->flush_buffer = NULL;
+
+ return;
+}
+#endif
+#ifdef DUMP_IMG
+int util_write_rawdata(const char *file, const void *data, unsigned int size)
+{
+ FILE *fp;
+ fp = fopen(file, "wb");
+ if (fp == NULL)
+ return -1;
+ fwrite((char*)data, sizeof(char), size, fp);
+ fclose(fp);
+ return 0;
+}
+#endif
+
static gboolean
evas_image_sink_init (GstPlugin *evasimagesink)
{
#ifndef __GST_EVASIMAGESINK_H__
#define __GST_EVASIMAGESINK_H__
+//#define USE_TBM_SURFACE
+#define USE_FIMCC
+
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <Evas.h>
#include <Ecore.h>
-#include <mm_ta.h>
-
+#ifdef USE_TBM_SURFACE
+#include <tbm_surface.h>
+#include <tbm_bufmgr.h>
+#endif
G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
typedef struct _GstEvasImageSink GstEvasImageSink;
typedef struct _GstEvasImageSinkClass GstEvasImageSinkClass;
+#ifdef USE_TBM_SURFACE
+typedef struct _GstEvasImageDisplayingBuffer GstEvasImageDisplayingBuffer;
+typedef struct _GstEvasImageFlushBuffer GstEvasImageFlushBuffer;
+typedef enum {
+ BUF_SHARE_METHOD_NONE = -1,
+ BUF_SHARE_METHOD_PADDR = 0,
+ BUF_SHARE_METHOD_FD,
+ BUF_SHARE_METHOD_TIZEN_BUFFER,
+ BUF_SHARE_METHOD_FLUSH_BUFFER
+} buf_share_method_t;
+
+enum {
+ DEGREE_0 = 0,
+ DEGREE_90,
+ DEGREE_180,
+ DEGREE_270,
+ DEGREE_NUM,
+};
+
+enum {
+ DISP_GEO_METHOD_LETTER_BOX = 0,
+ DISP_GEO_METHOD_ORIGIN_SIZE,
+ DISP_GEO_METHOD_FULL_SCREEN,
+ DISP_GEO_METHOD_CROPPED_FULL_SCREEN,
+ DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX,
+ DISP_GEO_METHOD_CUSTOM_DST_ROI,
+ DISP_GEO_METHOD_NUM,
+};
+
+enum {
+ FLIP_NONE = 0,
+ FLIP_HORIZONTAL,
+ FLIP_VERTICAL,
+ FLIP_BOTH,
+ FLIP_NUM
+};
+struct buffer_info {
+ uint64_t usr_addr;
+ uint64_t size;
+ void *bo;
+ tbm_surface_h tbm_surf;
+};
+
+/* _GstEvasDisplayingBuffer
+ *
+ * buffer : manage ref count by got index through comparison
+ * bo : compare with buffer from codec for getting index
+ * n_buffer : compare with buffer from evas for getting index
+ * ref_count : decide whether it unref buffer or not in gst_evas_image_sink_fini/reset
+ */
+struct _GstEvasImageDisplayingBuffer {
+ GstBuffer *buffer;
+ void *bo;
+ tbm_surface_h tbm_surf;
+ int ref_count;
+};
+
+struct _GstEvasImageFlushBuffer {
+ void *bo;
+ tbm_surface_h tbm_surf;
+};
+#define NATIVE_BUFFER_NUM 20
+#define SOURCE_BUFFER_NUM 8
+#endif
struct _GstEvasImageSink
{
GstVideoSink element;
Evas_Coord w;
Evas_Coord h;
gboolean object_show;
+ gchar update_visibility;
gboolean gl_zerocopy;
GstBuffer *oldbuf;
gboolean is_evas_object_size_set;
guint present_data_addr;
+#ifdef USE_TBM_SURFACE
+ GMutex *display_buffer_lock;
+ GMutex *flow_lock;
+ GstEvasImageDisplayingBuffer displaying_buffer[NATIVE_BUFFER_NUM];
+ GstVideoRectangle eo_size;
+ gboolean use_ratio;
+ guint rotate_angle;
+ guint display_geometry_method;
+ guint flip;
+ GstBuffer *prev_buf;
+ gint prev_index;
+ gint cur_index;
+ gboolean need_flush;
+ gboolean enable_flush_buffer;
+ GstEvasImageFlushBuffer *flush_buffer;
+ gint sent_buffer_cnt;
+ gint debuglog_cnt_showFrame;
+ gint debuglog_cnt_ecoreCbPipe;
+
+ tbm_format tbm_surface_format;
+ struct buffer_info src_buffer_info[NATIVE_BUFFER_NUM];
+ guint src_buf_idx;
+ gboolean is_buffer_allocated;
+#endif
};
struct _GstEvasImageSinkClass
--- /dev/null
+plugin_LTLIBRARIES = libgstevaspixmapsink.la
+
+libgstevaspixmapsink_la_SOURCES = evaspixmapsink.c
+libgstevaspixmapsink_la_CFLAGS = $(GST_CFLAGS) $(X_CFLAGS) $(EFL_CFLAGS) \
+ $(XFIXES_CFLAGS) $(DRI2PROTO_CFLAGS) $(DRI2_CFLAGS) $(X11_CFLAGS) $(XDAMAGE_CFLAGS) $(XV_CFLAGS) $(TBM_CFLAGS) $(DRM_CFLAGS) $(DRM_DEVEL_CFLAGS)
+libgstevaspixmapsink_la_LIBADD = \
+ $(GST_LIBS) -lgstvideo-$(GST_MAJORMINOR) -lgstinterfaces-$(GST_MAJORMINOR) \
+ $(X_LIBS) $(XVIDEO_LIBS) $(XSHM_LIBS) $(LIBM) \
+ $(EFL_LIBS) \
+ $(XFIXES_LIBS) $(DRI2PROTO_LIBS) $(DRI2_LIBS) $(X11_LIBS) $(XDAMAGE_LIBS) $(XV_LIBS) $(TBM_LIBS) $(DRM_LIBS) $(DRM_DEVEL_LIBS)
+libgstevaspixmapsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstxvimagesink_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = evaspixmapsink.h
--- /dev/null
+/*
+ * EvasPixmapSink
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Sangchul Lee <sc11.lee@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
+
+/* Our interfaces */
+#include <gst/interfaces/navigation.h>
+#include <gst/interfaces/colorbalance.h>
+#include <gst/interfaces/propertyprobe.h>
+/* Helper functions */
+#include <gst/video/video.h>
+
+/* Object header */
+#include "evaspixmapsink.h"
+
+#include <time.h>
+
+/* Samsung extension headers */
+/* For xv extension header for buffer transfer (output) */
+#include "xv_types.h"
+
+/* headers for drm */
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <X11/Xmd.h>
+#include <dri2/dri2.h>
+#include <libdrm/drm.h>
+
+/* max channel count *********************************************************/
+#define SCMN_IMGB_MAX_PLANE (4)
+
+/* image buffer definition ***************************************************
+
+ +------------------------------------------+ ---
+ | | ^
+ | a[], p[] | |
+ | +---------------------------+ --- | |
+ | | | ^ | |
+ | |<---------- w[] ---------->| | | |
+ | | | | | |
+ | | | |
+ | | | h[] | e[]
+ | | | |
+ | | | | | |
+ | | | | | |
+ | | | v | |
+ | +---------------------------+ --- | |
+ | | v
+ +------------------------------------------+ ---
+
+ |<----------------- s[] ------------------>|
+*/
+
+typedef struct
+{
+ /* width of each image plane */
+ int w[SCMN_IMGB_MAX_PLANE];
+ /* height of each image plane */
+ int h[SCMN_IMGB_MAX_PLANE];
+ /* stride of each image plane */
+ int s[SCMN_IMGB_MAX_PLANE];
+ /* elevation of each image plane */
+ int e[SCMN_IMGB_MAX_PLANE];
+ /* user space address of each image plane */
+ void *a[SCMN_IMGB_MAX_PLANE];
+ /* physical address of each image plane, if needs */
+ void *p[SCMN_IMGB_MAX_PLANE];
+ /* color space type of image */
+ int cs;
+ /* left postion, if needs */
+ int x;
+ /* top position, if needs */
+ int y;
+ /* to align memory */
+ int __dummy2;
+ /* arbitrary data */
+ int data[16];
+ /* dma buf fd */
+ int dma_buf_fd[SCMN_IMGB_MAX_PLANE];
+ /* buffer share method */
+ int buf_share_method;
+ /* Y plane size in case of ST12 */
+ int y_size;
+ /* UV plane size in case of ST12 */
+ int uv_size;
+ /* Tizen buffer object */
+ void *bo[SCMN_IMGB_MAX_PLANE];
+ /* JPEG data */
+ void *jpeg_data;
+ /* JPEG size */
+ int jpeg_size;
+ /* TZ memory buffer */
+ int tz_enable;
+} SCMN_IMGB;
+
+/* Debugging category */
+#include <gst/gstinfo.h>
+GST_DEBUG_CATEGORY_STATIC (gst_debug_evaspixmapsink);
+#define GST_CAT_DEFAULT gst_debug_evaspixmapsink
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
+
+enum {
+ UPDATE_FALSE,
+ UPDATE_TRUE
+};
+
+enum {
+ DEGREE_0,
+ DEGREE_90,
+ DEGREE_180,
+ DEGREE_270,
+ DEGREE_NUM,
+};
+
+enum {
+ DISP_GEO_METHOD_LETTER_BOX = 0,
+ DISP_GEO_METHOD_ORIGIN_SIZE,
+ DISP_GEO_METHOD_FULL_SCREEN,
+ DISP_GEO_METHOD_CROPPED_FULL_SCREEN,
+ DISP_GEO_METHOD_CUSTOM_ROI,
+ DISP_GEO_METHOD_NUM,
+};
+
+enum {
+ FLIP_NONE = 0,
+ FLIP_HORIZONTAL,
+ FLIP_VERTICAL,
+ FLIP_BOTH,
+ FLIP_NUM,
+};
+
+#define DEF_DISPLAY_GEOMETRY_METHOD DISP_GEO_METHOD_LETTER_BOX
+#define DEF_DISPLAY_FLIP FLIP_NONE
+#define GST_TYPE_EVASPIXMAPSINK_FLIP (gst_evaspixmapsink_flip_get_type())
+#define GST_TYPE_EVASPIXMAPSINK_ROTATE_ANGLE (gst_evaspixmapsink_rotate_angle_get_type())
+#define GST_TYPE_EVASPIXMAPSINK_DISPLAY_GEOMETRY_METHOD (gst_evaspixmapsink_display_geometry_method_get_type())
+#define SIZE_FOR_UPDATE_VISIBILITY sizeof(gchar)
+#define EPIPE_REQUEST_LIMIT 20
+
+#ifdef DUMP_IMG
+int util_write_rawdata (const char *file, const void *data, unsigned int size);
+typedef struct _BufferInfo
+{
+ Display *dpy;
+ Pixmap pixmap;
+ int width;
+ int height;
+
+ /* Dri2 */
+ int drm_fd;
+ tbm_bufmgr bufmgr;
+ void *virtual;
+ DRI2Buffer *dri2_buffers;
+ tbm_bo bo;
+} BufferInfo;
+
+static BufferInfo *g_bufinfo[NUM_OF_PIXMAP];
+void * gst_evaspixmapsink_get_buffer (Display *dpy, Pixmap pixmap, int width, int height, int i);
+void gst_evaspixmapsink_free_buffer (int i);
+static Bool gst_evaspixmapsink_init_dri2 (BufferInfo *bufinfo);
+int g_cnt;
+#endif
+
+/* macro ******************************************************/
+#define EVASPIXMAPSINK_SET_PIXMAP_ID_TO_GEM_INFO( x_evaspixmapsink, x_pixmap_id ) \
+do \
+{ \
+ int i = 0; \
+ XV_DATA_PTR data = (XV_DATA_PTR)x_evaspixmapsink->evas_pixmap_buf->xvimage->data; \
+ if (data->YBuf > 0) { \
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) { \
+ if (evaspixmapsink->gem_info[i].gem_name == data->YBuf) { \
+ evaspixmapsink->gem_info[i].ref_pixmap = x_pixmap_id; \
+ GST_LOG_OBJECT (x_evaspixmapsink,"pixmap(%d) is marked at index(%d) of gem_info(YBuf)->gem_handle(%d)", x_pixmap_id, i, evaspixmapsink->gem_info[i].gem_handle); \
+ break; \
+ } \
+ } \
+ } \
+ if (data->CbBuf > 0) { \
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) { \
+ if (evaspixmapsink->gem_info[i].gem_name == data->CbBuf) { \
+ evaspixmapsink->gem_info[i].ref_pixmap = x_pixmap_id; \
+ GST_LOG_OBJECT (x_evaspixmapsink,"pixmap(%d) is marked at index(%d) of gem_info(CbBuf)->gem_handle(%d)", x_pixmap_id, i, evaspixmapsink->gem_info[i].gem_handle); \
+ break; \
+ } \
+ } \
+ } \
+ if (data->CrBuf > 0) { \
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) { \
+ if (evaspixmapsink->gem_info[i].gem_name == data->CrBuf) { \
+ evaspixmapsink->gem_info[i].ref_pixmap = x_pixmap_id; \
+ GST_LOG_OBJECT (x_evaspixmapsink,"pixmap(%d) is marked at index(%d) of gem_info(CrBuf)->gem_handle(%d)", x_pixmap_id, i, evaspixmapsink->gem_info[i].gem_handle); \
+ break; \
+ } \
+ } \
+ } \
+}while(0)
+
+#define EVASPIXMAPSINK_SET_EVAS_OBJECT_EVENT_CALLBACK( x_evas_image_object, x_usr_data ) \
+do \
+{ \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_DEL, evas_callback_del_event, x_usr_data); \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_RESIZE, evas_callback_resize_event, x_usr_data); \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_SHOW, evas_callback_show_event, x_usr_data); \
+ evas_object_event_callback_add (x_evas_image_object, EVAS_CALLBACK_HIDE, evas_callback_hide_event, x_usr_data); \
+}while(0)
+
+#define EVASPIXMAPSINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK( x_evas_image_object ) \
+do \
+{ \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_DEL, evas_callback_del_event); \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_RESIZE, evas_callback_resize_event); \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_SHOW, evas_callback_show_event); \
+ evas_object_event_callback_del (x_evas_image_object, EVAS_CALLBACK_HIDE, evas_callback_hide_event); \
+}while(0)
+
+static GType
+gst_evaspixmapsink_flip_get_type(void)
+{
+ static GType evaspixmapsink_flip_type = 0;
+ static const GEnumValue flip_type[] = {
+ { FLIP_NONE, "Flip NONE", "FLIP_NONE"},
+ { FLIP_HORIZONTAL, "Flip HORIZONTAL", "FLIP_HORIZONTAL"},
+ { FLIP_VERTICAL, "Flip VERTICAL", "FLIP_VERTICAL"},
+ { FLIP_BOTH, "Flip BOTH", "FLIP_BOTH"},
+ { FLIP_NUM, NULL, NULL},
+ };
+
+ if (!evaspixmapsink_flip_type) {
+ evaspixmapsink_flip_type = g_enum_register_static("GstEvasPixmapSinkFlipType", flip_type);
+ }
+
+ return evaspixmapsink_flip_type;
+}
+
+static GType
+gst_evaspixmapsink_rotate_angle_get_type(void)
+{
+ static GType evaspixmapsink_rotate_angle_type = 0;
+ static const GEnumValue rotate_angle_type[] = {
+ { 0, "No rotate", "DEGREE_0"},
+ { 1, "Rotate 90 degree", "DEGREE_90"},
+ { 2, "Rotate 180 degree", "DEGREE_180"},
+ { 3, "Rotate 270 degree", "DEGREE_270"},
+ { 4, NULL, NULL},
+ };
+
+ if (!evaspixmapsink_rotate_angle_type) {
+ evaspixmapsink_rotate_angle_type = g_enum_register_static("GstEvasPixmapSinkRotateAngleType", rotate_angle_type);
+ }
+
+ return evaspixmapsink_rotate_angle_type;
+}
+
+static GType
+gst_evaspixmapsink_display_geometry_method_get_type(void)
+{
+ static GType evaspixmapsink_display_geometry_method_type = 0;
+ static const GEnumValue display_geometry_method_type[] = {
+ { 0, "Letter box", "LETTER_BOX"},
+ { 1, "Origin size", "ORIGIN_SIZE"},
+ { 2, "Full-screen", "FULL_SCREEN"},
+ { 3, "Cropped Full-screen", "CROPPED_FULL_SCREEN"},
+ { 4, "Explicitely described destination ROI", "CUSTOM_ROI"},
+ { 5, NULL, NULL},
+ };
+
+ if (!evaspixmapsink_display_geometry_method_type) {
+ evaspixmapsink_display_geometry_method_type = g_enum_register_static("GstEvasPixmapSinkDisplayGeometryMethodType", display_geometry_method_type);
+ }
+
+ return evaspixmapsink_display_geometry_method_type;
+}
+
+typedef struct
+{
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long input_mode;
+ unsigned long status;
+}
+MotifWmHints, MwmHints;
+
+#define MWM_HINTS_DECORATIONS (1L << 1)
+
+static void gst_evaspixmapsink_reset (GstEvasPixmapSink *evaspixmapsink);
+static GstBufferClass *evaspixmap_buffer_parent_class = NULL;
+static void gst_evaspixmap_buffer_finalize (GstEvasPixmapBuffer *evaspixmapbuf);
+static void gst_evaspixmapsink_xcontext_clear (GstEvasPixmapSink *evaspixmapsink);
+static void gst_evaspixmapsink_xpixmap_destroy (GstEvasPixmapSink *evaspixmapsink, GstXPixmap *xpixmap);
+static void gst_evaspixmapsink_xpixmap_update_geometry (GstEvasPixmapSink *evaspixmapsink, int idx);
+static gboolean gst_evaspixmap_buffer_put (GstEvasPixmapSink *evaspixmapsink, GstEvasPixmapBuffer *evaspixmapbuf);
+static gboolean gst_evaspixmapsink_xpixmap_link (GstEvasPixmapSink *evaspixmapsink);
+static void gst_evaspixmapsink_xpixmap_clear (GstEvasPixmapSink *evaspixmapsink, GstXPixmap *xpixmap);
+static gint gst_evaspixmapsink_get_format_from_caps (GstEvasPixmapSink *evaspixmapsink, GstCaps *caps);
+static void drm_close_gem(GstEvasPixmapSink *evaspixmapsink, unsigned int gem_handle);
+static void drm_fini_close_gem_handle(GstEvasPixmapSink *evaspixmapsink, Pixmap pixmap_id);
+static void evas_callback_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void evas_callback_del_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void evas_callback_show_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void evas_callback_hide_event (void *data, Evas *e, Evas_Object *obj, void *event_info);
+
+/* Default template - initiated with class struct to allow gst-register to work
+ without X running */
+static GstStaticPadTemplate gst_evaspixmapsink_sink_template_factory =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw-rgb, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], "
+ "height = (int) [ 1, MAX ]; "
+ "video/x-raw-yuv, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
+ );
+
+enum
+{
+ PROP_0,
+ PROP_CONTRAST,
+ PROP_BRIGHTNESS,
+ PROP_HUE,
+ PROP_SATURATION,
+ PROP_DISPLAY,
+ PROP_SYNCHRONOUS,
+ PROP_PIXEL_ASPECT_RATIO,
+ PROP_DEVICE,
+ PROP_DEVICE_NAME,
+ PROP_DOUBLE_BUFFER,
+ PROP_AUTOPAINT_COLORKEY,
+ PROP_COLORKEY,
+ PROP_PIXMAP_WIDTH,
+ PROP_PIXMAP_HEIGHT,
+ PROP_FLIP,
+ PROP_ROTATE_ANGLE,
+ PROP_DISPLAY_GEOMETRY_METHOD,
+ PROP_ZOOM,
+ PROP_DST_ROI_X,
+ PROP_DST_ROI_Y,
+ PROP_DST_ROI_W,
+ PROP_DST_ROI_H,
+ PROP_STOP_VIDEO,
+ PROP_EVAS_OBJECT,
+ PROP_VISIBLE,
+ PROP_ORIGIN_SIZE,
+};
+
+static GstVideoSinkClass *parent_class = NULL;
+
+/* ============================================================= */
+/* */
+/* Private Methods */
+/* */
+/* ============================================================= */
+
+/* evaspixmap buffers */
+
+#define GST_TYPE_EVASPIXMAP_BUFFER (gst_evaspixmap_buffer_get_type())
+
+#define GST_IS_EVASPIXMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_EVASPIXMAP_BUFFER))
+#define GST_EVASPIXMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_EVASPIXMAP_BUFFER, GstEvasPixmapBuffer))
+
+static int log_warn_count = 0;
+
+Display *g_my_disp;
+
+static int get_millis_time()
+{
+ struct timespec tp;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
+}
+
+static void
+ecore_pipe_callback_handler (void *data, void *buffer, unsigned int nbyte)
+{
+ if (!data ) {
+ GST_WARNING ("data is NULL..");
+ return;
+ }
+
+ GstEvasPixmapSink *evaspixmapsink = (GstEvasPixmapSink*)data;
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START] Evas_Object(0x%x)", evaspixmapsink->eo);
+ int i = 0;
+ guint idx = 0;
+ GstXPixmap *xpixmap = NULL;
+
+ g_mutex_lock (evaspixmapsink->flow_lock);
+
+ evaspixmapsink->epipe_request_count--;
+ if (!evaspixmapsink->eo) {
+ GST_WARNING_OBJECT (evaspixmapsink,"evas object is NULL..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return;
+ }
+
+ if (!evaspixmapsink->xcontext) {
+ GST_WARNING_OBJECT (evaspixmapsink,"xcontext is NULL..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return;
+ }
+
+ /* setting evas object hide and show */
+ if (nbyte == SIZE_FOR_UPDATE_VISIBILITY) {
+ if(!evaspixmapsink->visible) {
+ evas_object_hide(evaspixmapsink->eo);
+ GST_INFO_OBJECT (evaspixmapsink, "object hide");
+ } else {
+ if (!evas_object_image_native_surface_get(evaspixmapsink->eo)) {
+ GST_WARNING_OBJECT (evaspixmapsink, "native surface is not set, skip evas_object_show()..");
+ } else {
+ evas_object_show(evaspixmapsink->eo);
+ GST_INFO_OBJECT (evaspixmapsink, "object show");
+ }
+ }
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return;
+ }
+
+ /* mapping evas object with xpixmap */
+ if (evaspixmapsink->do_link) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"do link");
+ evas_object_image_size_set(evaspixmapsink->eo, evaspixmapsink->w, evaspixmapsink->h);
+ if (evaspixmapsink->xpixmap[idx]->pixmap) {
+ Evas_Native_Surface surf;
+ surf.version = EVAS_NATIVE_SURFACE_VERSION;
+ surf.type = EVAS_NATIVE_SURFACE_X11;
+ surf.data.x11.visual = ecore_x_default_visual_get(ecore_x_display_get(), ecore_x_default_screen_get());
+ surf.data.x11.pixmap = evaspixmapsink->xpixmap[idx]->pixmap;
+ evas_object_image_native_surface_set(evaspixmapsink->eo, &surf);
+ evaspixmapsink->do_link = FALSE;
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink,"pixmap is NULL..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return;
+ }
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]->prev_pixmap && evaspixmapsink->xpixmap[i]->prev_gc && evaspixmapsink->prev_damage[i]) {
+ GST_LOG_OBJECT (evaspixmapsink,"Free pixmap(%d), gc(%x), destroy previous damage(%d)",
+ evaspixmapsink->xpixmap[i]->prev_pixmap, evaspixmapsink->xpixmap[i]->prev_gc, evaspixmapsink->prev_damage[i]);
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XFreePixmap(evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->prev_pixmap);
+ XFreeGC (evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->prev_gc);
+ XDamageDestroy(evaspixmapsink->xcontext->disp, evaspixmapsink->prev_damage[i]);
+ XSync(evaspixmapsink->xcontext->disp, FALSE);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ evaspixmapsink->xpixmap[i]->prev_pixmap = 0;
+ evaspixmapsink->xpixmap[i]->prev_gc = 0;
+ evaspixmapsink->prev_damage[i] = NULL;
+ }
+ }
+ if (evaspixmapsink->visible && !evas_object_visible_get(evaspixmapsink->eo)) {
+ evas_object_show(evaspixmapsink->eo);
+ GST_WARNING_OBJECT (evaspixmapsink, "object show (lazy)");
+ }
+
+ } else {
+ GST_DEBUG_OBJECT (evaspixmapsink,"update");
+ /* update evas image object size */
+ if (evaspixmapsink->use_origin_size) {
+ evas_object_geometry_get(evaspixmapsink->eo, NULL, NULL, &evaspixmapsink->w, &evaspixmapsink->h);
+ }
+
+ g_mutex_lock (evaspixmapsink->pixmap_ref_lock);
+
+ /* find a oldest damaged pixmap */
+ int temp_time = 0;
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->last_updated_idx == i) {
+ continue;
+ } else {
+ xpixmap = evaspixmapsink->xpixmap[i];
+ if (xpixmap->ref > 0 && xpixmap->damaged_time) {
+ if (temp_time == 0) {
+ temp_time = xpixmap->damaged_time;
+ idx = i;
+ } else {
+ if (temp_time > xpixmap->damaged_time) {
+ temp_time = xpixmap->damaged_time;
+ idx = i;
+ }
+ }
+ }
+ }
+ }
+
+ xpixmap = evaspixmapsink->xpixmap[idx];
+ if (xpixmap->pixmap) {
+ if (evaspixmapsink->last_updated_idx != idx) {
+ Evas_Native_Surface surf;
+ surf.version = EVAS_NATIVE_SURFACE_VERSION;
+ surf.type = EVAS_NATIVE_SURFACE_X11;
+ surf.data.x11.visual = ecore_x_default_visual_get(ecore_x_display_get(), ecore_x_default_screen_get());
+ surf.data.x11.pixmap = xpixmap->pixmap;
+ if (evaspixmapsink->eo) {
+ evas_object_image_native_surface_set(evaspixmapsink->eo, NULL);
+ }
+ evas_object_image_native_surface_set(evaspixmapsink->eo, &surf);
+ GST_LOG_OBJECT (evaspixmapsink,"update, native_surface_set of xpixmap[%d]",idx);
+ if (evaspixmapsink->last_updated_idx == -1) {
+ xpixmap->damaged_time = 0;
+ GST_INFO_OBJECT (evaspixmapsink,"this is the first time to request to update(do not DECREASE ref-count) : pixmap(%d), refcount(%d), damaged_time(%d), idx(%d)",
+ xpixmap->pixmap, xpixmap->ref, xpixmap->damaged_time, idx);
+ } else {
+ xpixmap = evaspixmapsink->xpixmap[evaspixmapsink->last_updated_idx];
+ if(xpixmap->ref > 0 && xpixmap->damaged_time > 0) {
+ xpixmap->ref--;
+ xpixmap->damaged_time = 0;
+ GST_INFO_OBJECT (evaspixmapsink,"pixmap ref-count DECREASED : pixmap(%d), refcount(%d), damaged_time(%d), idx(%d)",
+ xpixmap->pixmap, xpixmap->ref, xpixmap->damaged_time, evaspixmapsink->last_updated_idx);
+ }
+ }
+ evaspixmapsink->last_updated_idx = idx;
+ }
+
+ evas_object_image_fill_set(evaspixmapsink->eo, 0, 0, evaspixmapsink->w, evaspixmapsink->h);
+ evas_object_image_data_update_add(evaspixmapsink->eo, 0, 0, evaspixmapsink->w, evaspixmapsink->h);
+ //GST_LOG_OBJECT (evaspixmapsink,"request to update : pixmap idx(%d), ref(%d)", idx, xpixmap->ref);
+ } else {
+ GST_ERROR_OBJECT (evaspixmapsink,"pixmap is NULL..");
+ }
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+ }
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+}
+
+static inline gboolean
+is_evas_image_object (Evas_Object *obj)
+{
+ const char *type;
+ if (!obj) {
+ GST_ERROR ("evas image object is NULL..");
+ return FALSE;
+ }
+ type = evas_object_type_get (obj);
+ if (!type) {
+ GST_ERROR ("could not find type of the evas object..");
+ return FALSE;
+ }
+ if (strcmp (type, "image") == 0) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+evas_callback_resize_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ int w = 0;
+ int h = 0;
+ float former_ratio = 0;
+ float ratio = 0;
+ int i = 0;
+#ifdef COMPARE_RATIO
+ float abs_margin = 0;
+#endif
+
+ GstEvasPixmapSink *evaspixmapsink = (GstEvasPixmapSink *)data;
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START] evas_image_object(%x), tid(%u)", obj, (unsigned int)pthread_self());
+
+ evas_object_geometry_get(evaspixmapsink->eo, NULL, NULL, &w, &h);
+ GST_DEBUG_OBJECT (evaspixmapsink,"resized : w(%d), h(%d)", w, h);
+ if (!evaspixmapsink->use_origin_size &&
+ (evaspixmapsink->w != w || evaspixmapsink->h != h)) {
+ former_ratio = (float)evaspixmapsink->w / evaspixmapsink->h;
+ ratio = (float)w / h;
+ evaspixmapsink->w = w;
+ evaspixmapsink->h = h;
+
+#ifdef COMPARE_RATIO
+ GST_DEBUG_OBJECT (evaspixmapsink,"resized : ratio(%.3f=>%.3f)", former_ratio, ratio);
+ if ( former_ratio >= ratio ) {
+ abs_margin = former_ratio - ratio;
+ } else {
+ abs_margin = ratio - former_ratio;
+ }
+ /* re-link_pixmap can only be set when ratio is changed */
+ if ( abs_margin >= MARGIN_OF_ERROR ) {
+#endif
+ if (!gst_evaspixmapsink_xpixmap_link(evaspixmapsink)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"link evas image object with pixmap failed...");
+ return;
+ }
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) {
+ if (evaspixmapsink->gem_info[i].ref_pixmap > 0) {
+ GST_LOG_OBJECT (evaspixmapsink,"set ref_pixmap(%d) to 0 in gem_info[%d]", evaspixmapsink->gem_info[i].ref_pixmap, i);
+ evaspixmapsink->gem_info[i].ref_pixmap = 0;
+ }
+ }
+
+ /**
+ * Pixmap is reallocated.
+ * But native surface has old pixmap.
+ * In this case, evas can rendering with old pixmap.
+ * That makes broken image in the window.
+ * So I just unset evas native surface.
+ */
+ if (evaspixmapsink->eo) {
+ evas_object_image_native_surface_set(evaspixmapsink->eo, NULL);
+ evas_object_image_data_set(evaspixmapsink->eo, NULL);
+ }
+#ifdef COMPARE_RATIO
+ }
+#endif
+ }
+
+ if (GST_STATE(evaspixmapsink) >= GST_STATE_PAUSED) {
+ gst_evaspixmap_buffer_put (evaspixmapsink, evaspixmapsink->evas_pixmap_buf);
+ }
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+}
+
+static void
+evas_callback_del_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ GstEvasPixmapSink *evaspixmapsink = data;
+ if (!evaspixmapsink) {
+ GST_WARNING ("evaspixmapsink is NULL..");
+ return;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START]");
+
+ EVASPIXMAPSINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK( evaspixmapsink->eo );
+ if (evaspixmapsink->eo) {
+ evas_object_image_native_surface_set(evaspixmapsink->eo, NULL);
+ evaspixmapsink->eo = NULL;
+ }
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+}
+
+static void
+evas_callback_show_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ GstEvasPixmapSink *evaspixmapsink = (GstEvasPixmapSink *)data;
+ GST_INFO_OBJECT (evaspixmapsink,"show evas_image_object(%x) from pid(%d), tid(%u)", obj, getpid(), (unsigned int)pthread_self());
+}
+
+static void
+evas_callback_hide_event (void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ GstEvasPixmapSink *evaspixmapsink = (GstEvasPixmapSink *)data;
+ GST_INFO_OBJECT (evaspixmapsink,"hide evas_image_object(%x) from pid(%d), tid(%u)", obj, getpid(), (unsigned int)pthread_self());
+}
+
+/* X11 stuff */
+static gboolean error_caught = FALSE;
+
+static int
+gst_evaspixmapsink_handle_xerror (Display * display, XErrorEvent * xevent)
+{
+ char error_msg[1024];
+
+ XGetErrorText (display, xevent->error_code, error_msg, 1024);
+ if (display != g_my_disp) {
+ GST_WARNING ("evaspixmapsink triggered an XError. but display(%p) is not ours(%p), just skip it. error(%s), xevent->request_code(%d)", display, g_my_disp, error_msg, xevent->request_code);
+ } else {
+ GST_ERROR ("evaspixmapsink triggered an XError. error(%s), display(%p), xevent->request_code(%d)", error_msg, display, xevent->request_code);
+ error_caught = TRUE;
+ }
+ return 0;
+}
+
+#ifdef DUMP_IMG
+int util_write_rawdata(const char *file, const void *data, unsigned int size)
+{
+ FILE *fp;
+
+ fp = fopen(file, "wb");
+ if (fp == NULL)
+ {
+ GST_WARNING("fopen fail... size : %d", size);
+ return -1;
+ }
+ fwrite((char*)data, sizeof(char), size, fp);
+ fclose(fp);
+
+ return 0;
+}
+
+void *
+gst_evaspixmapsink_get_buffer (Display *dpy, Pixmap pixmap, int width, int height, int i)
+{
+ if (g_bufinfo[i]!=NULL)
+ {
+ GST_ERROR("g_bufinfo!=NULL %p", g_bufinfo[i]);
+ exit (-1);
+ }
+
+ g_bufinfo[i] = calloc (1, sizeof (BufferInfo));
+
+ g_bufinfo[i]->dpy = dpy;
+ g_bufinfo[i]->pixmap = pixmap;
+ g_bufinfo[i]->width = width;
+ g_bufinfo[i]->height = height;
+
+ if (!gst_evaspixmapsink_init_dri2 (g_bufinfo[i]))
+ {
+ free (g_bufinfo[i]);
+ g_bufinfo[i] = NULL;
+ return NULL;
+ }
+
+ if (g_bufinfo[i]->virtual == NULL)
+ {
+ GST_ERROR("g_bufinfo->virtual == NULL");
+ exit (-1);
+ }
+
+ return g_bufinfo[i]->virtual;
+}
+
+void
+gst_evaspixmapsink_free_buffer (int i)
+{
+ if (g_bufinfo[i] == NULL)
+ {
+ GST_ERROR("g_bufinfo == NULL");
+ exit (-1);
+ }
+
+ if (g_bufinfo[i]==NULL)
+ {
+ GST_ERROR("g_bufinfo==NULL");
+ exit (-1);
+ }
+
+ if (g_bufinfo[i])
+ {
+ if (g_bufinfo[i]->bo)
+ tbm_bo_unref(g_bufinfo[i]->bo);
+ if (g_bufinfo[i]->dri2_buffers)
+ free(g_bufinfo[i]->dri2_buffers);
+ if (g_bufinfo[i]->bufmgr)
+ tbm_bufmgr_deinit (g_bufinfo[i]->bufmgr);
+ if (g_bufinfo[i]->drm_fd >= 0)
+ close (g_bufinfo[i]->drm_fd);
+ }
+
+ XSync (g_bufinfo[i]->dpy, 0);
+ free (g_bufinfo[i]);
+ g_bufinfo[i] = NULL;
+}
+
+static Bool gst_evaspixmapsink_init_dri2 (BufferInfo *bufinfo)
+{
+ int screen;
+ int dri2_base = 0;
+ int dri2_err_base = 0;
+ int dri2Major, dri2Minor;
+ char *driverName = NULL, *deviceName = NULL;
+ unsigned int attachments[1];
+ int dri2_count, dri2_out_count;
+ int dri2_width, dri2_height;
+ drm_magic_t magic;
+ tbm_bo_handle bo_handle;
+ screen = DefaultScreen(bufinfo->dpy);
+ if (!DRI2QueryExtension (bufinfo->dpy, &dri2_base, &dri2_err_base))
+ {
+ GST_ERROR ("no DRI2 extension. !!\n");
+ goto fail_init_dri2;
+ }
+ if (!DRI2QueryVersion (bufinfo->dpy, &dri2Major, &dri2Minor))
+ {
+ GST_ERROR ("fail : DRI2QueryVersion !!\n");
+ goto fail_init_dri2;
+ }
+ if (!DRI2Connect (bufinfo->dpy, RootWindow(bufinfo->dpy, screen), &driverName, &deviceName))
+ {
+ GST_ERROR ("fail : DRI2Connect !!\n");
+ goto fail_init_dri2;
+ } /* drm_fd */
+ bufinfo->drm_fd = open (deviceName, O_RDWR);
+ if (bufinfo->drm_fd < 0)
+ {
+ GST_ERROR ("fail : open drm device (%s)\n", deviceName);
+ goto fail_init_dri2;
+ }
+ /* get the drm magic */
+ drmGetMagic(bufinfo->drm_fd, &magic);
+ if (!DRI2Authenticate(bufinfo->dpy, RootWindow(bufinfo->dpy, screen), magic))
+ {
+ GST_ERROR ("fail : DRI2Authenticate (%d)\n", magic);
+ goto fail_init_dri2;
+ }
+ /* bufmgr */
+ bufinfo->bufmgr = tbm_bufmgr_init (bufinfo->drm_fd);
+ if (!bufinfo->bufmgr)
+ {
+ GST_ERROR ("fail : init buffer manager \n");
+ goto fail_init_dri2;
+ }
+ DRI2CreateDrawable (bufinfo->dpy, bufinfo->pixmap);
+ attachments[0] = DRI2BufferFrontLeft;
+ dri2_count = 1;
+ bufinfo->dri2_buffers = DRI2GetBuffers (bufinfo->dpy, bufinfo->pixmap, &dri2_width, &dri2_height,
+ attachments, dri2_count, &dri2_out_count);
+ if (!bufinfo->dri2_buffers)
+ {
+ GST_ERROR ("fail : get buffers\n");
+ goto fail_init_dri2;
+ }
+ if (!bufinfo->dri2_buffers[0].name)
+ {
+ GST_ERROR ("fail : a handle of the dri2 buffer is null \n ");
+ goto fail_init_dri2;
+ }
+ bufinfo->bo = tbm_bo_import (bufinfo->bufmgr, bufinfo->dri2_buffers[0].name);
+ if (!bufinfo->bo)
+ {
+ GST_ERROR ("fail : import bo (key:%d)\n", bufinfo->dri2_buffers[0].name);
+ goto fail_init_dri2;
+ }
+ /* virtual */
+ bo_handle = tbm_bo_map(bufinfo->bo, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ if(!bo_handle.ptr)
+ {
+ GST_ERROR ("fail : tbm_bo_map");
+ goto fail_init_dri2;
+ }
+ tbm_bo_unmap (bufinfo->bo);
+
+ bufinfo->virtual = (void *)bo_handle.ptr;
+ if (!bufinfo->virtual)
+ {
+ GST_ERROR ("fail : map \n");
+ goto fail_init_dri2;
+ }
+
+ free (driverName);
+ free (deviceName);
+
+ return True;
+
+ fail_init_dri2:
+ if (driverName)
+ free (driverName);
+ if (deviceName)
+ free (deviceName);
+ if (bufinfo->bo)
+ tbm_bo_unref(bufinfo->bo);
+ if (bufinfo->dri2_buffers)
+ free(bufinfo->dri2_buffers);
+ if (bufinfo->bufmgr)
+ tbm_bufmgr_deinit (bufinfo->bufmgr);
+ if (bufinfo->drm_fd >= 0)
+ close (bufinfo->drm_fd);
+ return False;
+}
+#endif
+
+#ifdef HAVE_XSHM
+/* This function checks that it is actually really possible to create an image
+ using XShm */
+static gboolean
+gst_evaspixmapsink_check_xshm_calls (GstXContext * xcontext)
+{
+ XvImage *xvimage;
+ XShmSegmentInfo SHMInfo;
+ gint size;
+ int (*handler) (Display *, XErrorEvent *);
+ gboolean result = FALSE;
+ gboolean did_attach = FALSE;
+
+ g_return_val_if_fail (xcontext != NULL, FALSE);
+
+ /* Sync to ensure any older errors are already processed */
+ XSync (xcontext->disp, FALSE);
+
+ /* Set defaults so we don't free these later unnecessarily */
+ SHMInfo.shmaddr = ((void *) -1);
+ SHMInfo.shmid = -1;
+
+ /* Setting an error handler to catch failure */
+ error_caught = FALSE;
+ handler = XSetErrorHandler (gst_evaspixmapsink_handle_xerror);
+
+ /* Trying to create a 1x1 picture */
+ GST_DEBUG ("XvShmCreateImage of 1x1");
+ xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
+ xcontext->im_format, NULL, 1, 1, &SHMInfo);
+
+ /* Might cause an error, sync to ensure it is noticed */
+ XSync (xcontext->disp, FALSE);
+ if (!xvimage || error_caught) {
+ GST_WARNING ("could not XvShmCreateImage a 1x1 image");
+ goto beach;
+ }
+ size = xvimage->data_size;
+
+ SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
+ if (SHMInfo.shmid == -1) {
+ GST_WARNING ("could not get shared memory of %d bytes", size);
+ goto beach;
+ }
+
+ SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
+ if (SHMInfo.shmaddr == ((void *) -1)) {
+ GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
+ /* Clean up the shared memory segment */
+ shmctl (SHMInfo.shmid, IPC_RMID, NULL);
+ goto beach;
+ }
+
+ xvimage->data = SHMInfo.shmaddr;
+ SHMInfo.readOnly = FALSE;
+
+ if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
+ GST_WARNING ("Failed to XShmAttach");
+ /* Clean up the shared memory segment */
+ shmctl (SHMInfo.shmid, IPC_RMID, NULL);
+ goto beach;
+ }
+
+ /* Sync to ensure we see any errors we caused */
+ XSync (xcontext->disp, FALSE);
+
+ /* Delete the shared memory segment as soon as everyone is attached.
+ * This way, it will be deleted as soon as we detach later, and not
+ * leaked if we crash. */
+ shmctl (SHMInfo.shmid, IPC_RMID, NULL);
+
+ if (!error_caught) {
+ GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
+ SHMInfo.shmseg);
+
+ did_attach = TRUE;
+ /* store whether we succeeded in result */
+ result = TRUE;
+ } else {
+ GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
+ "Not using shared memory.");
+ }
+
+beach:
+ /* Sync to ensure we swallow any errors we caused and reset error_caught */
+ XSync (xcontext->disp, FALSE);
+
+ error_caught = FALSE;
+ XSetErrorHandler (handler);
+
+ if (did_attach) {
+ GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
+ SHMInfo.shmid, SHMInfo.shmseg);
+ XShmDetach (xcontext->disp, &SHMInfo);
+ XSync (xcontext->disp, FALSE);
+ }
+ if (SHMInfo.shmaddr != ((void *) -1))
+ shmdt (SHMInfo.shmaddr);
+ if (xvimage)
+ XFree (xvimage);
+ return result;
+}
+#endif /* HAVE_XSHM */
+
+/* This function destroys a GstEvasPixmap handling XShm availability */
+static void
+gst_evaspixmap_buffer_destroy (GstEvasPixmapBuffer *evaspixmapbuf)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+
+ evaspixmapsink = evaspixmapbuf->evaspixmapsink;
+ if (G_UNLIKELY (evaspixmapsink == NULL)) {
+ goto no_sink;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink, "Destroying buffer");
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ GST_OBJECT_LOCK (evaspixmapsink);
+
+ /* We might have some buffers destroyed after changing state to NULL */
+ if (evaspixmapsink->xcontext == NULL) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Destroying XvImage after Xcontext");
+#ifdef HAVE_XSHM
+ /* Need to free the shared memory segment even if the x context
+ * was already cleaned up */
+ if (evaspixmapbuf->SHMInfo.shmaddr != ((void *) -1)) {
+ shmdt (evaspixmapbuf->SHMInfo.shmaddr);
+ }
+#endif
+ goto beach;
+ }
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+#ifdef HAVE_XSHM
+ if (evaspixmapsink->xcontext->use_xshm) {
+ if (evaspixmapbuf->SHMInfo.shmaddr != ((void *) -1)) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"XServer ShmDetaching from 0x%x id 0x%lx", evaspixmapbuf->SHMInfo.shmid, evaspixmapbuf->SHMInfo.shmseg);
+ XShmDetach (evaspixmapsink->xcontext->disp, &evaspixmapbuf->SHMInfo);
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+ shmdt (evaspixmapbuf->SHMInfo.shmaddr);
+ }
+ if (evaspixmapbuf->xvimage)
+ XFree (evaspixmapbuf->xvimage);
+ } else
+#endif /* HAVE_XSHM */
+ {
+ if (evaspixmapbuf->xvimage) {
+ if (evaspixmapbuf->xvimage->data) {
+ g_free (evaspixmapbuf->xvimage->data);
+ }
+ XFree (evaspixmapbuf->xvimage);
+ }
+ }
+
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+beach:
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+ evaspixmapbuf->evaspixmapsink = NULL;
+ gst_object_unref (evaspixmapsink);
+
+ GST_MINI_OBJECT_CLASS (evaspixmap_buffer_parent_class)->finalize (GST_MINI_OBJECT(evaspixmapbuf));
+
+ return;
+
+ no_sink:
+ {
+ GST_WARNING ("no sink found");
+ return;
+ }
+}
+
+static void
+gst_evaspixmap_buffer_finalize (GstEvasPixmapBuffer *evaspixmapbuf)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+
+ evaspixmapsink = evaspixmapbuf->evaspixmapsink;
+ if (G_UNLIKELY (evaspixmapsink == NULL)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"no sink found");
+ return;
+ }
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ /* If our geometry changed we can't reuse that image. */
+ GST_LOG_OBJECT (evaspixmapsink,"destroy image as sink is shutting down");
+ gst_evaspixmap_buffer_destroy (evaspixmapbuf);
+}
+
+static void
+gst_evaspixmap_buffer_free (GstEvasPixmapBuffer *evaspixmapbuf)
+{
+ /* make sure it is not recycled */
+ evaspixmapbuf->width = -1;
+ evaspixmapbuf->height = -1;
+ gst_buffer_unref (GST_BUFFER (evaspixmapbuf));
+}
+
+static void
+gst_evaspixmap_buffer_init (GstEvasPixmapBuffer *evaspixmapbuf, gpointer g_class)
+{
+#ifdef HAVE_XSHM
+ evaspixmapbuf->SHMInfo.shmaddr = ((void *) -1);
+ evaspixmapbuf->SHMInfo.shmid = -1;
+#endif
+}
+
+static void
+gst_evaspixmap_buffer_class_init (gpointer g_class, gpointer class_data)
+{
+ GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
+
+ evaspixmap_buffer_parent_class = g_type_class_peek_parent (g_class);
+
+ mini_object_class->finalize = (GstMiniObjectFinalizeFunction) gst_evaspixmap_buffer_finalize;
+}
+
+static GType
+gst_evaspixmap_buffer_get_type (void)
+{
+ static GType _gst_evaspixmap_buffer_type;
+
+ if (G_UNLIKELY (_gst_evaspixmap_buffer_type == 0)) {
+ static const GTypeInfo evaspixmap_buffer_info = {
+ sizeof (GstBufferClass),
+ NULL,
+ NULL,
+ gst_evaspixmap_buffer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstEvasPixmapBuffer),
+ 0,
+ (GInstanceInitFunc) gst_evaspixmap_buffer_init,
+ NULL
+ };
+ _gst_evaspixmap_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
+ "GstEvasPixmapBuffer", &evaspixmap_buffer_info, 0);
+ }
+ return _gst_evaspixmap_buffer_type;
+}
+
+/* This function handles GstEvasPixmapBuffer creation depending on XShm availability */
+static GstEvasPixmapBuffer*
+gst_evaspixmap_buffer_new (GstEvasPixmapSink *evaspixmapsink, GstCaps *caps)
+{
+ GstEvasPixmapBuffer *evaspixmapbuf = NULL;
+ GstStructure *structure = NULL;
+ gboolean succeeded = FALSE;
+ int (*handler) (Display *, XErrorEvent *);
+
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), NULL);
+
+ if (caps == NULL) {
+ return NULL;
+ }
+
+ evaspixmapbuf = (GstEvasPixmapBuffer*) gst_mini_object_new (GST_TYPE_EVASPIXMAP_BUFFER);
+ GST_DEBUG_OBJECT (evaspixmapsink,"Creating new EvasPixmapBuffer");
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ if (!gst_structure_get_int (structure, "width", &evaspixmapbuf->width) || !gst_structure_get_int (structure, "height", &evaspixmapbuf->height)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+ }
+
+ GST_LOG_OBJECT (evaspixmapsink,"creating %dx%d", evaspixmapbuf->width, evaspixmapbuf->height);
+
+ GST_LOG_OBJECT (evaspixmapsink,"aligned size %dx%d", evaspixmapsink->aligned_width, evaspixmapsink->aligned_height);
+ if (evaspixmapsink->aligned_width == 0 || evaspixmapsink->aligned_height == 0) {
+ GST_INFO_OBJECT (evaspixmapsink,"aligned size is zero. set size of caps.");
+ evaspixmapsink->aligned_width = evaspixmapbuf->width;
+ evaspixmapsink->aligned_height = evaspixmapbuf->height;
+ }
+
+ evaspixmapbuf->im_format = gst_evaspixmapsink_get_format_from_caps (evaspixmapsink, caps);
+ if (evaspixmapbuf->im_format == -1) {
+ GST_WARNING_OBJECT (evaspixmapsink,"failed to get format from caps %"GST_PTR_FORMAT, caps);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,("Failed to create output image buffer of %dx%d pixels",
+ evaspixmapbuf->width, evaspixmapbuf->height), ("Invalid input caps"));
+ goto beach_unlocked;
+ }
+
+ GST_INFO_OBJECT (evaspixmapsink, "FOURCC format : %c%c%c%c", evaspixmapbuf->im_format, evaspixmapbuf->im_format>>8,
+ evaspixmapbuf->im_format>>16, evaspixmapbuf->im_format>>24);
+
+ evaspixmapbuf->evaspixmapsink = gst_object_ref (evaspixmapsink);
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ /* Sync to ensure we swallow any errors we caused and reset error_caught */
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ /* Setting an error handler to catch failure */
+ error_caught = FALSE;
+ handler = XSetErrorHandler (gst_evaspixmapsink_handle_xerror);
+
+#ifdef HAVE_XSHM
+ if (evaspixmapsink->xcontext->use_xshm) {
+ int expected_size;
+ evaspixmapbuf->xvimage = XvShmCreateImage (evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, evaspixmapbuf->im_format, NULL,
+ evaspixmapsink->aligned_width, evaspixmapsink->aligned_height, &evaspixmapbuf->SHMInfo);
+ if (!evaspixmapbuf->xvimage || error_caught) {
+ if (error_caught) {
+ GST_ERROR_OBJECT (evaspixmapsink,"error_caught!");
+ }
+ if(!evaspixmapbuf->xvimage) {
+ GST_ERROR_OBJECT (evaspixmapsink,"XvShmCreateImage() failed");
+ }
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ /* Reset error handler */
+ error_caught = FALSE;
+ XSetErrorHandler (handler);
+ /* Push an error */
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,("Failed to create output image buffer of %dx%d pixels",evaspixmapbuf->width,
+ evaspixmapbuf->height),("could not XvShmCreateImage a %dx%d image",evaspixmapbuf->width, evaspixmapbuf->height));
+ goto beach_unlocked;
+ }
+
+ /* we have to use the returned data_size for our shm size */
+ evaspixmapbuf->size = evaspixmapbuf->xvimage->data_size;
+ GST_LOG_OBJECT (evaspixmapsink,"XShm image size is %" G_GSIZE_FORMAT, evaspixmapbuf->size);
+
+ /* calculate the expected size. This is only for sanity checking the
+ * number we get from X. */
+ switch (evaspixmapbuf->im_format) {
+ case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+ case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
+ {
+ gint pitches[3];
+ gint offsets[3];
+ guint plane;
+
+ offsets[0] = 0;
+ pitches[0] = GST_ROUND_UP_4 (evaspixmapbuf->width);
+ offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (evaspixmapbuf->height);
+ pitches[1] = GST_ROUND_UP_8 (evaspixmapbuf->width) / 2;
+ offsets[2] =
+ offsets[1] + pitches[1] * GST_ROUND_UP_2 (evaspixmapbuf->height) / 2;
+ pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
+
+ expected_size = offsets[2] + pitches[2] * GST_ROUND_UP_2 (evaspixmapbuf->height) / 2;
+
+ for (plane = 0; plane < evaspixmapbuf->xvimage->num_planes; plane++) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Plane %u has a expected pitch of %d bytes, " "offset of %d",
+ plane, pitches[plane], offsets[plane]);
+ }
+ break;
+ }
+ case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+ case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
+ expected_size = evaspixmapbuf->height * GST_ROUND_UP_4 (evaspixmapbuf->width * 2);
+ break;
+ case GST_MAKE_FOURCC ('S', 'T', '1', '2'):
+ case GST_MAKE_FOURCC ('S', 'N', '1', '2'):
+ case GST_MAKE_FOURCC ('S', 'U', 'Y', 'V'):
+ case GST_MAKE_FOURCC ('S', 'U', 'Y', '2'):
+ case GST_MAKE_FOURCC ('S', '4', '2', '0'):
+ case GST_MAKE_FOURCC ('S', 'Y', 'V', 'Y'):
+ case GST_MAKE_FOURCC ('I', 'T', 'L', 'V'):
+ case GST_MAKE_FOURCC ('S', 'R', '3', '2'):
+ expected_size = sizeof(SCMN_IMGB);
+ break;
+ default:
+ expected_size = 0;
+ break;
+ }
+ if (expected_size != 0 && evaspixmapbuf->size != expected_size) {
+ GST_WARNING_OBJECT (evaspixmapsink,"unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)", evaspixmapbuf->size, expected_size);
+ }
+
+ /* Be verbose about our XvImage stride */
+ {
+ guint plane;
+ for (plane = 0; plane < evaspixmapbuf->xvimage->num_planes; plane++) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Plane %u has a pitch of %d bytes, ""offset of %d", plane,
+ evaspixmapbuf->xvimage->pitches[plane], evaspixmapbuf->xvimage->offsets[plane]);
+ }
+ }
+
+ evaspixmapbuf->SHMInfo.shmid = shmget (IPC_PRIVATE, evaspixmapbuf->size,IPC_CREAT | 0777);
+ if (evaspixmapbuf->SHMInfo.shmid == -1) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,
+ ("Failed to create output image buffer of %dx%d pixels", evaspixmapbuf->width, evaspixmapbuf->height),
+ ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",evaspixmapbuf->size));
+ goto beach_unlocked;
+ }
+
+ evaspixmapbuf->SHMInfo.shmaddr = shmat (evaspixmapbuf->SHMInfo.shmid, NULL, 0);
+ if (evaspixmapbuf->SHMInfo.shmaddr == ((void *) -1)) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,
+ ("Failed to create output image buffer of %dx%d pixels",
+ evaspixmapbuf->width, evaspixmapbuf->height),
+ ("Failed to shmat: %s", g_strerror (errno)));
+ /* Clean up the shared memory segment */
+ shmctl (evaspixmapbuf->SHMInfo.shmid, IPC_RMID, NULL);
+ goto beach_unlocked;
+ }
+
+ evaspixmapbuf->xvimage->data = evaspixmapbuf->SHMInfo.shmaddr;
+ evaspixmapbuf->SHMInfo.readOnly = FALSE;
+
+ if (XShmAttach (evaspixmapsink->xcontext->disp, &evaspixmapbuf->SHMInfo) == 0) {
+ /* Clean up the shared memory segment */
+ shmctl (evaspixmapbuf->SHMInfo.shmid, IPC_RMID, NULL);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,
+ ("Failed to create output image buffer of %dx%d pixels",
+ evaspixmapbuf->width, evaspixmapbuf->height), ("Failed to XShmAttach"));
+ goto beach_unlocked;
+ }
+
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ /* Delete the shared memory segment as soon as we everyone is attached.
+ * This way, it will be deleted as soon as we detach later, and not
+ * leaked if we crash. */
+ shmctl (evaspixmapbuf->SHMInfo.shmid, IPC_RMID, NULL);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"XServer ShmAttached to 0x%x, id 0x%lx", evaspixmapbuf->SHMInfo.shmid, evaspixmapbuf->SHMInfo.shmseg);
+ } else
+#endif /* HAVE_XSHM */
+ {
+ evaspixmapbuf->xvimage = XvCreateImage (evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id,
+ evaspixmapbuf->im_format, NULL, evaspixmapsink->aligned_width, evaspixmapsink->aligned_height);
+ if (!evaspixmapbuf->xvimage || error_caught) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ /* Reset error handler */
+ error_caught = FALSE;
+ XSetErrorHandler (handler);
+ /* Push an error */
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE,
+ ("Failed to create outputimage buffer of %dx%d pixels",
+ evaspixmapbuf->width, evaspixmapbuf->height),
+ ("could not XvCreateImage a %dx%d image",
+ evaspixmapbuf->width, evaspixmapbuf->height));
+ goto beach_unlocked;
+ }
+
+ /* we have to use the returned data_size for our image size */
+ evaspixmapbuf->size = evaspixmapbuf->xvimage->data_size;
+ evaspixmapbuf->xvimage->data = g_malloc (evaspixmapbuf->size);
+
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+ }
+
+ /* Reset error handler */
+ error_caught = FALSE;
+ XSetErrorHandler (handler);
+
+ succeeded = TRUE;
+
+ GST_BUFFER_DATA (evaspixmapbuf) = (guchar *) evaspixmapbuf->xvimage->data;
+ GST_BUFFER_SIZE (evaspixmapbuf) = evaspixmapbuf->size;
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+beach_unlocked:
+ if (!succeeded) {
+ gst_evaspixmap_buffer_free (evaspixmapbuf);
+ evaspixmapbuf = NULL;
+ }
+
+ return evaspixmapbuf;
+}
+
+/* This function puts a GstEvasPixmapBuffer on a GstEvasPixmapSink's pixmap. Returns FALSE
+ * if no pixmap was available */
+static gboolean
+gst_evaspixmap_buffer_put (GstEvasPixmapSink *evaspixmapsink, GstEvasPixmapBuffer *evaspixmapbuf)
+{
+ GstVideoRectangle result;
+
+ GstVideoRectangle src_origin = { 0, 0, 0, 0};
+ GstVideoRectangle src_input = { 0, 0, 0, 0};
+ GstVideoRectangle src = { 0, 0, 0, 0};
+ GstVideoRectangle dst = { 0, 0, 0, 0};
+ int rotate = 0;
+ int ret = 0;
+ int idx = 0;
+ GstXPixmap *xpixmap = NULL;
+ int i = 0;
+
+ /* We take the flow_lock. If expose is in there we don't want to run
+ concurrently from the data flow thread */
+ g_mutex_lock (evaspixmapsink->flow_lock);
+
+ if (G_UNLIKELY (evaspixmapsink->xpixmap[idx] == NULL)) {
+ GST_WARNING_OBJECT (evaspixmapsink, "xpixmap is NULL. Skip buffer_put." );
+ g_mutex_unlock(evaspixmapsink->flow_lock);
+ return TRUE;
+ }
+ if (evaspixmapsink->visible == FALSE) {
+ GST_WARNING_OBJECT (evaspixmapsink, "visible is FALSE. Skip buffer_put." );
+ g_mutex_unlock(evaspixmapsink->flow_lock);
+ return TRUE;
+ }
+ if (!evaspixmapbuf) {
+ GST_WARNING_OBJECT (evaspixmapsink, "evaspixmapbuf is NULL. Skip buffer_put." );
+ g_mutex_unlock(evaspixmapsink->flow_lock);
+ return TRUE;
+ }
+
+ g_mutex_lock (evaspixmapsink->pixmap_ref_lock);
+ if (evaspixmapsink->last_updated_idx == -1) {
+ /* if it has never been updated any frame in Ecore thread, do below */
+ idx = 0;
+ xpixmap = evaspixmapsink->xpixmap[idx];
+ xpixmap->ref = 1;
+ GST_LOG_OBJECT (evaspixmapsink, "last_updated_idx(%d), we use index[%d] of pixmap buffers : pixmap(%d), ref(%d)",
+ evaspixmapsink->last_updated_idx, idx, xpixmap->pixmap, xpixmap->ref);
+ } else {
+ for (idx = 0; idx < evaspixmapsink->num_of_pixmaps; idx++) {
+ if (idx == evaspixmapsink->last_updated_idx) {
+ continue;
+ } else {
+ xpixmap = evaspixmapsink->xpixmap[idx];
+ if (xpixmap->ref == 0 && xpixmap->damaged_time == 0) {
+ xpixmap->ref++;
+ GST_LOG_OBJECT (evaspixmapsink, "found an available pixmap(%d) : xpixmap[%d]", xpixmap->pixmap, idx);
+ GST_INFO_OBJECT (evaspixmapsink,"pixmap ref-count INCREASED : pixmap(%d), refcount(%d)", xpixmap->pixmap, xpixmap->ref);
+ break;
+ }
+ }
+ }
+ if (idx == evaspixmapsink->num_of_pixmaps) {
+ GST_LOG_OBJECT (evaspixmapsink, "Could not find a pixmap with idle state, skip buffer_put." );
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+ g_mutex_unlock(evaspixmapsink->flow_lock);
+ return TRUE;
+ }
+ }
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+
+ gst_evaspixmapsink_xpixmap_update_geometry(evaspixmapsink, idx);
+
+ src.x = src.y = 0;
+ src_origin.x = src_origin.y = src_input.x = src_input.y = 0;
+ src_input.w = src_origin.w = evaspixmapsink->video_width;
+ src_input.h = src_origin.h = evaspixmapsink->video_height;
+ if (evaspixmapsink->use_origin_size ||
+ (evaspixmapsink->rotate_angle == DEGREE_0 ||
+ evaspixmapsink->rotate_angle == DEGREE_180)) {
+ src.w = src_origin.w;
+ src.h = src_origin.h;
+ } else {
+ src.w = src_origin.h;
+ src.h = src_origin.w;
+ }
+
+ dst.w = evaspixmapsink->render_rect.w; /* pixmap width */
+ dst.h = evaspixmapsink->render_rect.h; /* pixmap height */
+
+ if (!evaspixmapsink->use_origin_size) {
+ static Atom atom_rotation = None;
+ static Atom atom_hflip = None;
+ static Atom atom_vflip = None;
+ gboolean set_hflip = FALSE;
+ gboolean set_vflip = FALSE;
+
+ /* compensation of size information (between evas image object's and pixmap's) */
+ if (evaspixmapsink->sizediff_width > 1) {
+ if (evaspixmapsink->sizediff_height > 1) {
+ dst.w -= (evaspixmapsink->sizediff_width >> 1) << 1;
+ dst.h -= (evaspixmapsink->sizediff_height >> 1) << 1;
+ } else {
+ dst.w -= (evaspixmapsink->sizediff_width >> 1) << 1;
+ }
+ } else if (evaspixmapsink->sizediff_height > 1) {
+ dst.h -= (evaspixmapsink->sizediff_height >> 1) << 1;
+ }
+
+ switch (evaspixmapsink->display_geometry_method) {
+ case DISP_GEO_METHOD_LETTER_BOX:
+ gst_video_sink_center_rect (src, dst, &result, TRUE);
+ result.x += evaspixmapsink->render_rect.x;
+ result.y += evaspixmapsink->render_rect.y;
+ GST_DEBUG_OBJECT (evaspixmapsink, "GEO_METHOD : letter box");
+ break;
+ case DISP_GEO_METHOD_ORIGIN_SIZE:
+ gst_video_sink_center_rect (src, dst, &result, FALSE);
+ gst_video_sink_center_rect (dst, src, &src_input, FALSE);
+ GST_DEBUG_OBJECT (evaspixmapsink, "GEO_METHOD : origin size");
+ if (evaspixmapsink->rotate_angle == DEGREE_90 ||
+ evaspixmapsink->rotate_angle == DEGREE_270) {
+ src_input.x = src_input.x ^ src_input.y;
+ src_input.y = src_input.x ^ src_input.y;
+ src_input.x = src_input.x ^ src_input.y;
+ src_input.w = src_input.w ^ src_input.h;
+ src_input.h = src_input.w ^ src_input.h;
+ src_input.w = src_input.w ^ src_input.h;
+ }
+ break;
+ case DISP_GEO_METHOD_FULL_SCREEN:
+ result.x = result.y = 0;
+ result.w = evaspixmapsink->xpixmap[idx]->width;
+ result.h = evaspixmapsink->xpixmap[idx]->height;
+ GST_DEBUG_OBJECT (evaspixmapsink, "GEO_METHOD : full screen");
+ break;
+ case DISP_GEO_METHOD_CROPPED_FULL_SCREEN:
+ GST_DEBUG_OBJECT (evaspixmapsink, "GEO_METHOD : cropped full screen");
+ gst_video_sink_center_rect(dst, src, &src_input, TRUE);
+ result.x = result.y = 0;
+ result.w = dst.w;
+ result.h = dst.h;
+ if (evaspixmapsink->rotate_angle == DEGREE_90 ||
+ evaspixmapsink->rotate_angle == DEGREE_270) {
+ src_input.x = src_input.x ^ src_input.y;
+ src_input.y = src_input.x ^ src_input.y;
+ src_input.x = src_input.x ^ src_input.y;
+ src_input.w = src_input.w ^ src_input.h;
+ src_input.h = src_input.w ^ src_input.h;
+ src_input.w = src_input.w ^ src_input.h;
+ }
+ break;
+ case DISP_GEO_METHOD_CUSTOM_ROI:
+ switch (evaspixmapsink->rotate_angle) {
+ case DEGREE_90:
+ result.w = evaspixmapsink->dst_roi.h;
+ result.h = evaspixmapsink->dst_roi.w;
+ result.x = evaspixmapsink->dst_roi.y;
+ result.y = evaspixmapsink->xpixmap[idx]->height - evaspixmapsink->dst_roi.x - evaspixmapsink->dst_roi.w;
+ break;
+ case DEGREE_180:
+ result.w = evaspixmapsink->dst_roi.w;
+ result.h = evaspixmapsink->dst_roi.h;
+ result.x = evaspixmapsink->xpixmap[idx]->width - result.w - evaspixmapsink->dst_roi.x;
+ result.y = evaspixmapsink->xpixmap[idx]->height - result.h - evaspixmapsink->dst_roi.y;
+ break;
+ case DEGREE_270:
+ result.w = evaspixmapsink->dst_roi.h;
+ result.h = evaspixmapsink->dst_roi.w;
+ result.x = evaspixmapsink->xpixmap[idx]->width - evaspixmapsink->dst_roi.y - evaspixmapsink->dst_roi.h;
+ result.y = evaspixmapsink->dst_roi.x;
+ break;
+ default:
+ result.x = evaspixmapsink->dst_roi.x;
+ result.y = evaspixmapsink->dst_roi.y;
+ result.w = evaspixmapsink->dst_roi.w;
+ result.h = evaspixmapsink->dst_roi.h;
+ break;
+ }
+ GST_LOG_OBJECT(evaspixmapsink, "rotate[%d], ROI input[%d,%d,%dx%d] > result[%d,%d,%dx%d]",
+ evaspixmapsink->rotate_angle,
+ evaspixmapsink->dst_roi.x, evaspixmapsink->dst_roi.y, evaspixmapsink->dst_roi.w, evaspixmapsink->dst_roi.h,
+ result.x, result.y, result.w, result.h);
+ break;
+ default:
+ break;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink, "GEO_METHOD : src(%dx%d), dst(%dx%d), result(%dx%d), result_x(%d), result_y(%d)",
+ src.w,src.h,dst.w,dst.h,result.w,result.h,result.x,result.y);
+
+ switch( evaspixmapsink->rotate_angle ) {
+ case DEGREE_0:
+ break;
+ case DEGREE_90:
+ rotate = 270;
+ break;
+ case DEGREE_180:
+ rotate = 180;
+ break;
+ case DEGREE_270:
+ rotate = 90;
+ break;
+ default:
+ GST_WARNING_OBJECT( evaspixmapsink, "Unsupported rotation [%d]... set DEGREE 0.",
+ evaspixmapsink->rotate_angle );
+ break;
+ }
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ /* set display rotation */
+ if (atom_rotation == None) {
+ atom_rotation = XInternAtom(evaspixmapsink->xcontext->disp, "_USER_WM_PORT_ATTRIBUTE_ROTATION", False);
+ }
+ ret = XvSetPortAttribute(evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_rotation, rotate);
+ if (ret != Success) {
+ GST_ERROR_OBJECT( evaspixmapsink, "XvSetPortAttribute failed[%d]. disp[%x],xv_port_id[%d],atom[%x],rotate[%d]",
+ ret, evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_rotation, rotate );
+ }
+
+ /* set display flip */
+ if (atom_hflip == None) {
+ atom_hflip = XInternAtom(evaspixmapsink->xcontext->disp, "_USER_WM_PORT_ATTRIBUTE_HFLIP", False);
+ }
+ if (atom_vflip == None) {
+ atom_vflip = XInternAtom(evaspixmapsink->xcontext->disp, "_USER_WM_PORT_ATTRIBUTE_VFLIP", False);
+ }
+
+ switch (evaspixmapsink->flip) {
+ case FLIP_HORIZONTAL:
+ set_hflip = TRUE;
+ set_vflip = FALSE;
+ break;
+ case FLIP_VERTICAL:
+ set_hflip = FALSE;
+ set_vflip = TRUE;
+ break;
+ case FLIP_BOTH:
+ set_hflip = TRUE;
+ set_vflip = TRUE;
+ break;
+ case FLIP_NONE:
+ default:
+ set_hflip = FALSE;
+ set_vflip = FALSE;
+ break;
+ }
+ GST_INFO_OBJECT(evaspixmapsink, "set rotate %d HFLIP %d, VFLIP %d", rotate, set_hflip, set_vflip);
+
+ ret = XvSetPortAttribute(evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_hflip, set_hflip);
+ if (ret != Success) {
+ GST_WARNING("set HFLIP failed[%d]. disp[%x],xv_port_id[%d],atom[%x],hflip[%d]",
+ ret, evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_hflip, set_hflip);
+ }
+ ret = XvSetPortAttribute(evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_vflip, set_vflip);
+ if (ret != Success) {
+ GST_WARNING("set VFLIP failed[%d]. disp[%x],xv_port_id[%d],atom[%x],vflip[%d]",
+ ret, evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, atom_vflip, set_vflip);
+ }
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ } else {
+ result.x = result.y = 0;
+ result.w = evaspixmapsink->xpixmap[idx]->width;
+ result.h = evaspixmapsink->xpixmap[idx]->height;
+ GST_INFO_OBJECT (evaspixmapsink, "USE ORIGIN SIZE, no geometry method, no rotation/flip" );
+ }
+
+ if((evaspixmapsink->result_prev.w != 0 && evaspixmapsink->result_prev.h != 0)
+ && (evaspixmapsink->src_prev.w==src_origin.w && evaspixmapsink->src_prev.h==src_origin.h)
+ && (evaspixmapsink->result_prev.w!=result.w || evaspixmapsink->result_prev.h!=result.h))
+ {
+ GST_WARNING("It will execute XFillRectangle..... evaspixmapsink->result_prev.w : %d, evaspixmapsink->result_prev.h: %d, result.w : %d, result.h : %d",
+ evaspixmapsink->result_prev.w, evaspixmapsink->result_prev.h, result.w, result.h);
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ XFillRectangle (evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->pixmap, evaspixmapsink->xpixmap[i]->gc,
+ 0, 0, evaspixmapsink->xpixmap[i]->width, evaspixmapsink->xpixmap[i]->height);
+ GST_LOG_OBJECT (evaspixmapsink,"fill black to xpixmap[%d] with size(w:%d,h:%d)", evaspixmapsink->xpixmap[i]->pixmap,
+ evaspixmapsink->xpixmap[i]->width, evaspixmapsink->xpixmap[i]->height);
+ }
+ }
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ /* We scale to the pixmap's geometry */
+#ifdef HAVE_XSHM
+ if (evaspixmapsink->xcontext->use_xshm) {
+ GST_LOG_OBJECT (evaspixmapsink,"XvShmPutImage with image %dx%d and pixmap %dx%d, from xvimage %"
+ GST_PTR_FORMAT,
+ evaspixmapbuf->width, evaspixmapbuf->height,
+ evaspixmapsink->render_rect.w, evaspixmapsink->render_rect.h, evaspixmapbuf);
+
+ /* Trim as proper size */
+ if (src_input.w % 2 == 1) {
+ src_input.w += 1;
+ }
+ if (src_input.h % 2 == 1) {
+ src_input.h += 1;
+ }
+
+ GST_LOG_OBJECT (evaspixmapsink, "screen[%dx%d],pixmap[%d,%d,%dx%d],method[%d],rotate[%d],src[%dx%d],dst[%d,%d,%dx%d],input[%d,%d,%dx%d],result[%d,%d,%dx%d]",
+ evaspixmapsink->scr_w, evaspixmapsink->scr_h,
+ evaspixmapsink->xpixmap[idx]->x, evaspixmapsink->xpixmap[idx]->y, evaspixmapsink->xpixmap[idx]->width, evaspixmapsink->xpixmap[idx]->height,
+ evaspixmapsink->display_geometry_method, rotate,
+ src_origin.w, src_origin.h,
+ dst.x, dst.y, dst.w, dst.h,
+ src_input.x, src_input.y, src_input.w, src_input.h,
+ result.x, result.y, result.w, result.h );
+
+ if (evaspixmapsink->visible) {
+ if (evaspixmapsink->buf_shared_type == BUF_SHARE_METHOD_FD) {
+ EVASPIXMAPSINK_SET_PIXMAP_ID_TO_GEM_INFO (evaspixmapsink, evaspixmapsink->xpixmap[idx]->pixmap);
+ }
+ ret = XvShmPutImage (evaspixmapsink->xcontext->disp,
+ evaspixmapsink->xcontext->xv_port_id,
+ evaspixmapsink->xpixmap[idx]->pixmap,
+ evaspixmapsink->xpixmap[idx]->gc, evaspixmapbuf->xvimage,
+ src_input.x, src_input.y, src_input.w, src_input.h,
+ result.x, result.y, result.w, result.h, FALSE);
+ GST_LOG_OBJECT (evaspixmapsink, "XvShmPutImage return value [%d]", ret );
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink, "visible is FALSE. skip this image..." );
+ }
+ } else
+#endif /* HAVE_XSHM */
+ {
+ if (evaspixmapsink->visible) {
+ XvPutImage (evaspixmapsink->xcontext->disp,
+ evaspixmapsink->xcontext->xv_port_id,
+ evaspixmapsink->xpixmap[idx]->pixmap,
+ evaspixmapsink->xpixmap[idx]->gc, evaspixmapbuf->xvimage,
+ evaspixmapsink->disp_x, evaspixmapsink->disp_y,
+ evaspixmapsink->disp_width, evaspixmapsink->disp_height,
+ result.x, result.y, result.w, result.h);
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink, "visible is FALSE. skip this image..." );
+ }
+ }
+
+ evaspixmapsink->src_prev.w = src_origin.w;
+ evaspixmapsink->src_prev.h = src_origin.h;
+
+ evaspixmapsink->result_prev.w = result.w;
+ evaspixmapsink->result_prev.h = result.h;
+
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+
+ return TRUE;
+}
+
+static int
+drm_init(GstEvasPixmapSink *evaspixmapsink)
+{
+ Display *dpy;
+ int i = 0;
+ int eventBase = 0;
+ int errorBase = 0;
+ int dri2Major = 0;
+ int dri2Minor = 0;
+ char *driverName = NULL;
+ char *deviceName = NULL;
+ struct drm_auth auth_arg = {0};
+
+ evaspixmapsink->drm_fd = -1;
+
+ dpy = XOpenDisplay(0);
+
+ /* DRI2 */
+ if (!DRI2QueryExtension(dpy, &eventBase, &errorBase)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"failed to DRI2QueryExtension()");
+ goto ERROR_CASE;
+ }
+
+ if (!DRI2QueryVersion(dpy, &dri2Major, &dri2Minor)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"failed to DRI2QueryVersion");
+ goto ERROR_CASE;
+ }
+
+ if (!DRI2Connect(dpy, RootWindow(dpy, DefaultScreen(dpy)), &driverName, &deviceName)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"failed to DRI2Connect");
+ goto ERROR_CASE;
+ }
+
+ if (!driverName || !deviceName) {
+ GST_ERROR_OBJECT (evaspixmapsink,"driverName or deviceName is not valid");
+ goto ERROR_CASE;
+ }
+
+ GST_INFO_OBJECT (evaspixmapsink,"Open drm device : %s", deviceName);
+
+ /* get the drm_fd though opening the deviceName */
+ evaspixmapsink->drm_fd = open(deviceName, O_RDWR);
+ if (evaspixmapsink->drm_fd < 0) {
+ GST_ERROR_OBJECT (evaspixmapsink,"cannot open drm device (%s)", deviceName);
+ goto ERROR_CASE;
+ }
+
+ /* get magic from drm to authentication */
+ if (ioctl(evaspixmapsink->drm_fd, DRM_IOCTL_GET_MAGIC, &auth_arg)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"cannot get drm auth magic");
+ close(evaspixmapsink->drm_fd);
+ evaspixmapsink->drm_fd = -1;
+ goto ERROR_CASE;
+ }
+
+ if (!DRI2Authenticate(dpy, RootWindow(dpy, DefaultScreen(dpy)), auth_arg.magic)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"cannot get drm authentication from X");
+ close(evaspixmapsink->drm_fd);
+ evaspixmapsink->drm_fd = -1;
+ goto ERROR_CASE;
+ }
+
+ /* init gem handle */
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) {
+ evaspixmapsink->gem_info[i].dmabuf_fd = 0;
+ evaspixmapsink->gem_info[i].gem_handle = 0;
+ evaspixmapsink->gem_info[i].gem_name = 0;
+ evaspixmapsink->gem_info[i].bo = 0;
+ evaspixmapsink->gem_info[i].ref_pixmap = 0;
+ }
+
+ XCloseDisplay(dpy);
+ free(driverName);
+ free(deviceName);
+
+ return 0;
+
+ERROR_CASE:
+ XCloseDisplay(dpy);
+ if (driverName) {
+ free(driverName);
+ }
+ if (deviceName) {
+ free(deviceName);
+ }
+
+ return -1;
+}
+
+static void
+drm_fini(GstEvasPixmapSink *evaspixmapsink)
+{
+ if (evaspixmapsink->drm_fd >= 0) {
+ GST_INFO_OBJECT (evaspixmapsink,"close drm_fd(%d)", evaspixmapsink->drm_fd);
+ close(evaspixmapsink->drm_fd);
+ evaspixmapsink->drm_fd = -1;
+ }
+}
+
+static unsigned int
+drm_init_convert_dmabuf_gemname(GstEvasPixmapSink *evaspixmapsink, int dmabuf_fd)
+{
+ struct drm_prime_handle prime_arg = {0,};
+ struct drm_gem_flink flink_arg = {0,};
+ int i = 0;
+
+ if (evaspixmapsink->drm_fd < 0) {
+ GST_ERROR_OBJECT (evaspixmapsink,"DRM is not opened");
+ return 0;
+ }
+
+ if (dmabuf_fd <= 0) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Ignore wrong dmabuf fd(%d)", dmabuf_fd); /* temporarily change log level to DEBUG for reducing WARNING level log */
+ return 0;
+ }
+
+ /* check duplicated dmabuf fd */
+ for (i = 0 ; i < MAX_GEM_BUFFER_NUM ; i++) {
+ if (evaspixmapsink->gem_info[i].dmabuf_fd == dmabuf_fd) {
+ GST_LOG_OBJECT (evaspixmapsink,"already got fd(%u) with name(%u)", dmabuf_fd, evaspixmapsink->gem_info[i].gem_name);
+ return evaspixmapsink->gem_info[i].gem_name;
+ }
+
+ if (evaspixmapsink->gem_info[i].dmabuf_fd == 0) {
+ GST_LOG_OBJECT (evaspixmapsink,"empty gem_info[%d] found", i);
+ break;
+ }
+ }
+
+ if (i == MAX_GEM_BUFFER_NUM) {
+ GST_WARNING_OBJECT (evaspixmapsink,"too many buffers[dmabuf_fd(%d). skip it]", dmabuf_fd);
+ return 0;
+ }
+
+ evaspixmapsink->gem_info[i].dmabuf_fd = dmabuf_fd;
+ prime_arg.fd = dmabuf_fd;
+ if (ioctl(evaspixmapsink->drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_arg)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"non dmabuf fd(%d)", dmabuf_fd);
+ return 0;
+ }
+
+ evaspixmapsink->gem_info[i].gem_handle = prime_arg.handle;
+ GST_LOG_OBJECT (evaspixmapsink,"gem_info[%d].gem_handle = %u", i, prime_arg.handle);
+
+ flink_arg.handle = prime_arg.handle;
+ if (ioctl(evaspixmapsink->drm_fd, DRM_IOCTL_GEM_FLINK, &flink_arg)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"cannot convert drm handle to name");
+ return 0;
+ }
+
+ evaspixmapsink->gem_info[i].gem_name = flink_arg.name;
+ GST_LOG_OBJECT (evaspixmapsink,"gem_info[%d].gem_name = %u", i, flink_arg.name);
+
+ return flink_arg.name;
+}
+
+static unsigned int
+tbm_init_convert_bo_gemname(GstEvasPixmapSink *evaspixmapsink, tbm_bo bo)
+{
+ int i = 0;
+
+ if (bo == NULL) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Ignore wrong bo(%u)", bo); /* temporarily change log level to DEBUG for reducing WARNING level log */
+ return 0;
+ }
+
+ /* check duplicated dmabuf bo */
+ for (i = 0 ; i < MAX_GEM_BUFFER_NUM ; i++) {
+ if (evaspixmapsink->gem_info[i].bo == bo) {
+ GST_LOG_OBJECT (evaspixmapsink,"already got bo(%u) with name(%u)", bo, evaspixmapsink->gem_info[i].gem_name);
+ return evaspixmapsink->gem_info[i].gem_name;
+ }
+
+ if (evaspixmapsink->gem_info[i].bo == 0) {
+ GST_LOG_OBJECT (evaspixmapsink,"empty gem_info[%d] found", i);
+ break;
+ }
+ }
+
+ if (i == MAX_GEM_BUFFER_NUM) {
+ GST_WARNING_OBJECT (evaspixmapsink,"too many buffers[dmabuf_bo(%d). skip it]", bo);
+ return 0;
+ }
+
+ evaspixmapsink->gem_info[i].bo = bo;
+ evaspixmapsink->gem_info[i].gem_name = tbm_bo_export(bo);
+ GST_LOG_OBJECT (evaspixmapsink,"gem_info[%d].gem_name = %u", i, evaspixmapsink->gem_info[i].gem_name);
+
+ return evaspixmapsink->gem_info[i].gem_name;
+}
+
+static void
+drm_fini_close_gem_handle(GstEvasPixmapSink *evaspixmapsink, Pixmap pixmap_id)
+{
+ int i = 0;
+ if (evaspixmapsink->drm_fd >= 0) {
+ if (pixmap_id == 0) {
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) {
+ if (evaspixmapsink->gem_info[i].dmabuf_fd > 0 || evaspixmapsink->gem_info[i].bo > 0) {
+ if (evaspixmapsink->buf_shared_type == BUF_SHARE_METHOD_FD) {
+ GST_INFO_OBJECT (evaspixmapsink,"close gem_handle(%u)", evaspixmapsink->gem_info[i].gem_handle);
+ drm_close_gem(evaspixmapsink, evaspixmapsink->gem_info[i].gem_handle);
+ }
+ evaspixmapsink->gem_info[i].dmabuf_fd = 0;
+ evaspixmapsink->gem_info[i].gem_handle = 0;
+ evaspixmapsink->gem_info[i].gem_name = 0;
+ evaspixmapsink->gem_info[i].bo = 0;
+ evaspixmapsink->gem_info[i].ref_pixmap = 0;
+ GST_LOG_OBJECT (evaspixmapsink,"gem_info[%d] is cleared",i);
+ }
+ }
+ } else {
+ for (i = 0; i < MAX_GEM_BUFFER_NUM; i++) {
+ if (evaspixmapsink->gem_info[i].ref_pixmap == pixmap_id) {
+ if (evaspixmapsink->buf_shared_type == BUF_SHARE_METHOD_FD) {
+ GST_INFO_OBJECT (evaspixmapsink,"close gem_handle(%u) for pixmap_id(%d)",
+ evaspixmapsink->gem_info[i].gem_handle, pixmap_id);
+ drm_close_gem(evaspixmapsink, evaspixmapsink->gem_info[i].gem_handle);
+ }
+ evaspixmapsink->gem_info[i].dmabuf_fd = 0;
+ evaspixmapsink->gem_info[i].gem_handle = 0;
+ evaspixmapsink->gem_info[i].gem_name = 0;
+ evaspixmapsink->gem_info[i].bo = 0;
+ evaspixmapsink->gem_info[i].ref_pixmap = 0;
+ GST_LOG_OBJECT (evaspixmapsink,"gem_info[%d] is cleared",i);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+drm_close_gem(GstEvasPixmapSink *evaspixmapsink, unsigned int gem_handle)
+{
+ struct drm_gem_close close_arg = {0,};
+
+ if (evaspixmapsink->drm_fd < 0) {
+ GST_ERROR_OBJECT (evaspixmapsink,"DRM is not opened");
+ return;
+ }
+
+ if (gem_handle == 0) {
+ GST_ERROR_OBJECT (evaspixmapsink,"invalid gem_handle(%d)",gem_handle);
+ return;
+ }
+
+ close_arg.handle = gem_handle;
+ if (gem_handle > 0 && ioctl(evaspixmapsink->drm_fd, DRM_IOCTL_GEM_CLOSE, &close_arg)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"cannot close drm gem handle(%d)", gem_handle);
+ return;
+ }
+
+ return;
+}
+
+/* This function destroys a GstXPixmap */
+static void
+gst_evaspixmapsink_xpixmap_destroy (GstEvasPixmapSink *evaspixmapsink, GstXPixmap *xpixmap)
+{
+ g_return_if_fail (xpixmap != NULL);
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ if(xpixmap->pixmap) {
+ GST_LOG_OBJECT (evaspixmapsink,"Free pixmap(%d)", xpixmap->pixmap);
+ XFreePixmap(evaspixmapsink->xcontext->disp, xpixmap->pixmap);
+ xpixmap->pixmap = 0;
+ }
+
+ if (xpixmap->gc) {
+ XFreeGC (evaspixmapsink->xcontext->disp, xpixmap->gc);
+ }
+
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ g_free (xpixmap);
+}
+
+static void
+gst_evaspixmapsink_xpixmap_update_geometry (GstEvasPixmapSink *evaspixmapsink, int idx)
+{
+ Window root_window;
+ XWindowAttributes root_attr;
+
+ int cur_pixmap_x = 0;
+ int cur_pixmap_y = 0;
+ unsigned int cur_pixmap_width = 0;
+ unsigned int cur_pixmap_height = 0;
+ unsigned int cur_pixmap_border_width = 0;
+ unsigned int cur_pixmap_depth = 0;
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ /* Update the window geometry */
+ g_mutex_lock (evaspixmapsink->x_lock);
+ if (G_UNLIKELY (evaspixmapsink->xpixmap[idx] == NULL)) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ return;
+ }
+
+ /* Get root window and size of current pixmap */
+ XGetGeometry( evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[idx]->pixmap, &root_window,
+ &cur_pixmap_x, &cur_pixmap_y, /* relative x, y, for pixmap these are alway 0 */
+ &cur_pixmap_width, &cur_pixmap_height,
+ &cur_pixmap_border_width, &cur_pixmap_depth ); /* cur_pixmap_border_width, cur_pixmap_depth are not used */
+
+ evaspixmapsink->xpixmap[idx]->width = cur_pixmap_width;
+ evaspixmapsink->xpixmap[idx]->height = cur_pixmap_height;
+
+ evaspixmapsink->xpixmap[idx]->x = cur_pixmap_x;
+ evaspixmapsink->xpixmap[idx]->y = cur_pixmap_y;
+
+ /* Get size of root window == size of screen */
+ XGetWindowAttributes(evaspixmapsink->xcontext->disp, root_window, &root_attr);
+
+ evaspixmapsink->scr_w = root_attr.width;
+ evaspixmapsink->scr_h = root_attr.height;
+
+ if (!evaspixmapsink->have_render_rect) {
+ evaspixmapsink->render_rect.x = evaspixmapsink->render_rect.y = 0;
+ evaspixmapsink->render_rect.w = cur_pixmap_width;
+ evaspixmapsink->render_rect.h = cur_pixmap_height;
+ }
+
+ GST_LOG_OBJECT (evaspixmapsink,"screen size %dx%d, current pixmap geometry %d,%d,%dx%d, render_rect %d,%d,%dx%d",
+ evaspixmapsink->scr_w, evaspixmapsink->scr_h,
+ evaspixmapsink->xpixmap[idx]->x, evaspixmapsink->xpixmap[idx]->y,
+ evaspixmapsink->xpixmap[idx]->width, evaspixmapsink->xpixmap[idx]->height,
+ evaspixmapsink->render_rect.x, evaspixmapsink->render_rect.y,
+ evaspixmapsink->render_rect.w, evaspixmapsink->render_rect.h);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+}
+
+static void
+gst_evaspixmapsink_xpixmap_clear (GstEvasPixmapSink *evaspixmapsink, GstXPixmap *xpixmap)
+{
+ g_return_if_fail (xpixmap != NULL);
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ if (!xpixmap->pixmap) {
+ GST_WARNING_OBJECT (evaspixmapsink,"pixmap was not created..");
+ return;
+ }
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ if (!evaspixmapsink->xcontext) {
+ GST_WARNING_OBJECT (evaspixmapsink,"xcontext is null..");
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ return;
+ }
+
+ if (evaspixmapsink->stop_video) {
+ XvStopVideo (evaspixmapsink->xcontext->disp, evaspixmapsink->xcontext->xv_port_id, xpixmap->pixmap);
+ }
+ XSync (evaspixmapsink->xcontext->disp, FALSE);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ g_mutex_lock (evaspixmapsink->pixmap_ref_lock);
+ evaspixmapsink->last_updated_idx = -1;
+ xpixmap->ref = 0;
+ xpixmap->damaged_time = 0;
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+}
+
+/* This function commits our internal colorbalance settings to our grabbed Xv
+ port. If the xcontext is not initialized yet it simply returns */
+static void
+gst_evaspixmapsink_update_colorbalance (GstEvasPixmapSink *evaspixmapsink)
+{
+ GList *channels = NULL;
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ /* If we haven't initialized the X context we can't update anything */
+ if (evaspixmapsink->xcontext == NULL)
+ return;
+
+ /* Don't set the attributes if they haven't been changed, to avoid
+ * rounding errors changing the values */
+ if (!evaspixmapsink->cb_changed)
+ return;
+
+ /* For each channel of the colorbalance we calculate the correct value
+ doing range conversion and then set the Xv port attribute to match our
+ values. */
+ channels = evaspixmapsink->xcontext->channels_list;
+
+ while (channels) {
+ if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
+ GstColorBalanceChannel *channel = NULL;
+ Atom prop_atom;
+ gint value = 0;
+ gdouble convert_coef;
+
+ channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
+ g_object_ref (channel);
+
+ /* Our range conversion coef */
+ convert_coef = (channel->max_value - channel->min_value) / 2000.0;
+
+ if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
+ value = evaspixmapsink->hue;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
+ value = evaspixmapsink->saturation;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
+ value = evaspixmapsink->contrast;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
+ value = evaspixmapsink->brightness;
+ } else {
+ g_warning ("got an unknown channel %s", channel->label);
+ g_object_unref (channel);
+ return;
+ }
+
+ /* Committing to Xv port */
+ g_mutex_lock (evaspixmapsink->x_lock);
+ prop_atom =
+ XInternAtom (evaspixmapsink->xcontext->disp, channel->label, True);
+ if (prop_atom != None) {
+ int xv_value;
+ xv_value =
+ floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
+ XvSetPortAttribute (evaspixmapsink->xcontext->disp,
+ evaspixmapsink->xcontext->xv_port_id, prop_atom, xv_value);
+ }
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ g_object_unref (channel);
+ }
+ channels = g_list_next (channels);
+ }
+}
+
+static void
+gst_lookup_xv_port_from_adaptor (GstXContext *xcontext, XvAdaptorInfo *adaptors, int adaptor_no)
+{
+ gint j;
+ gint res;
+
+ /* Do we support XvImageMask ? */
+ if (!(adaptors[adaptor_no].type & XvImageMask)) {
+ GST_DEBUG ("XV Adaptor %s has no support for XvImageMask", adaptors[adaptor_no].name);
+ return;
+ }
+
+ /* We found such an adaptor, looking for an available port */
+ for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
+ /* We try to grab the port */
+ res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
+ if (Success == res) {
+ xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
+ GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name, adaptors[adaptor_no].num_ports);
+ } else {
+ GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j, adaptors[adaptor_no].name, res);
+ }
+ }
+}
+
+/* This function generates a caps with all supported format by the first
+ Xv grabable port we find. We store each one of the supported formats in a
+ format list and append the format to a newly created caps that we return
+ If this function does not return NULL because of an error, it also grabs
+ the port via XvGrabPort */
+static GstCaps*
+gst_evaspixmapsink_get_xv_support (GstEvasPixmapSink *evaspixmapsink, GstXContext *xcontext)
+{
+ gint i;
+ XvAdaptorInfo *adaptors;
+ gint nb_formats;
+ XvImageFormatValues *formats = NULL;
+ guint nb_encodings;
+ XvEncodingInfo *encodings = NULL;
+ gulong max_w = G_MAXINT, max_h = G_MAXINT;
+ GstCaps *caps = NULL;
+ GstCaps *rgb_caps = NULL;
+
+ g_return_val_if_fail (xcontext != NULL, NULL);
+
+ /* First let's check that XVideo extension is available */
+ if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, SETTINGS,
+ ("Could not initialise Xv output"),
+ ("XVideo extension is not available"));
+ return NULL;
+ }
+
+ /* Then we get adaptors list */
+ if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
+ &xcontext->nb_adaptors, &adaptors)) {
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, SETTINGS,
+ ("Could not initialise Xv output"),
+ ("Failed getting XV adaptors list"));
+ return NULL;
+ }
+
+ xcontext->xv_port_id = 0;
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"Found %u XV adaptor(s)", xcontext->nb_adaptors);
+
+ xcontext->adaptors =
+ (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
+
+ /* Now fill up our adaptor name array */
+ for (i = 0; i < xcontext->nb_adaptors; i++) {
+ xcontext->adaptors[i] = g_strdup (adaptors[i].name);
+ }
+
+ if (evaspixmapsink->adaptor_no < xcontext->nb_adaptors) {
+ /* Find xv port from user defined adaptor */
+ gst_lookup_xv_port_from_adaptor (xcontext, adaptors, evaspixmapsink->adaptor_no);
+ }
+
+ if (!xcontext->xv_port_id) {
+ /* Now search for an adaptor that supports XvImageMask */
+ for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
+ gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
+ evaspixmapsink->adaptor_no = i;
+ }
+ }
+
+ XvFreeAdaptorInfo (adaptors);
+
+ if (!xcontext->xv_port_id) {
+ evaspixmapsink->adaptor_no = -1;
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, BUSY,
+ ("Could not initialise Xv output"), ("No port available"));
+ return NULL;
+ }
+
+ /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
+ {
+ int count, todo = 3;
+ XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
+ xcontext->xv_port_id, &count);
+ static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
+ static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
+ static const char colorkey[] = "XV_COLORKEY";
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"Checking %d Xv port attributes", count);
+
+ evaspixmapsink->have_autopaint_colorkey = FALSE;
+ evaspixmapsink->have_double_buffer = FALSE;
+ evaspixmapsink->have_colorkey = FALSE;
+
+ for (i = 0; ((i < count) && todo); i++)
+ if (!strcmp (attr[i].name, autopaint)) {
+ const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
+
+ /* turn on autopaint colorkey */
+ XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
+ (evaspixmapsink->autopaint_colorkey ? 1 : 0));
+ todo--;
+ evaspixmapsink->have_autopaint_colorkey = TRUE;
+ } else if (!strcmp (attr[i].name, dbl_buffer)) {
+ const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
+
+ XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
+ (evaspixmapsink->double_buffer ? 1 : 0));
+ todo--;
+ evaspixmapsink->have_double_buffer = TRUE;
+ } else if (!strcmp (attr[i].name, colorkey)) {
+ /* Set the colorkey, default is something that is dark but hopefully
+ * won't randomly appear on the screen elsewhere (ie not black or greys)
+ * can be overridden by setting "colorkey" property
+ */
+ const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
+ guint32 ckey = 0;
+ gboolean set_attr = TRUE;
+ guint cr, cg, cb;
+
+ /* set a colorkey in the right format RGB565/RGB888
+ * We only handle these 2 cases, because they're the only types of
+ * devices we've encountered. If we don't recognise it, leave it alone
+ */
+ cr = (evaspixmapsink->colorkey >> 16);
+ cg = (evaspixmapsink->colorkey >> 8) & 0xFF;
+ cb = (evaspixmapsink->colorkey) & 0xFF;
+ switch (xcontext->depth) {
+ case 16: /* RGB 565 */
+ cr >>= 3;
+ cg >>= 2;
+ cb >>= 3;
+ ckey = (cr << 11) | (cg << 5) | cb;
+ break;
+ case 24:
+ case 32: /* RGB 888 / ARGB 8888 */
+ ckey = (cr << 16) | (cg << 8) | cb;
+ break;
+ default:
+ GST_DEBUG_OBJECT (evaspixmapsink,"Unknown bit depth %d for Xv Colorkey - not adjusting", xcontext->depth);
+ set_attr = FALSE;
+ break;
+ }
+
+ if (set_attr) {
+ ckey = CLAMP (ckey, (guint32) attr[i].min_value,
+ (guint32) attr[i].max_value);
+ GST_LOG_OBJECT (evaspixmapsink,"Setting color key for display depth %d to 0x%x", xcontext->depth, ckey);
+
+ XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
+ (gint) ckey);
+ }
+ todo--;
+ evaspixmapsink->have_colorkey = TRUE;
+ }
+
+ XFree (attr);
+ }
+
+ /* Get the list of encodings supported by the adapter and look for the
+ * XV_IMAGE encoding so we can determine the maximum width and height
+ * supported */
+ XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
+ &encodings);
+
+ for (i = 0; i < nb_encodings; i++) {
+ GST_LOG_OBJECT (evaspixmapsink,
+ "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
+ i, encodings[i].name, encodings[i].width, encodings[i].height,
+ encodings[i].rate.numerator, encodings[i].rate.denominator);
+ if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
+ max_w = encodings[i].width;
+ max_h = encodings[i].height;
+ evaspixmapsink->scr_w = max_w;
+ evaspixmapsink->scr_h = max_h;
+ }
+ }
+
+ XvFreeEncodingInfo (encodings);
+
+ /* We get all image formats supported by our port */
+ formats = XvListImageFormats (xcontext->disp,
+ xcontext->xv_port_id, &nb_formats);
+ caps = gst_caps_new_empty ();
+ for (i = 0; i < nb_formats; i++) {
+ GstCaps *format_caps = NULL;
+ gboolean is_rgb_format = FALSE;
+
+ /* We set the image format of the xcontext to an existing one. This
+ is just some valid image format for making our xshm calls check before
+ caps negotiation really happens. */
+ xcontext->im_format = formats[i].id;
+
+ switch (formats[i].type) {
+ case XvRGB:
+ {
+ XvImageFormatValues *fmt = &(formats[i]);
+ gint endianness = G_BIG_ENDIAN;
+
+ if (fmt->byte_order == LSBFirst) {
+ /* our caps system handles 24/32bpp RGB as big-endian. */
+ if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
+ fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
+ fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
+ fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
+
+ if (fmt->bits_per_pixel == 24) {
+ fmt->red_mask >>= 8;
+ fmt->green_mask >>= 8;
+ fmt->blue_mask >>= 8;
+ }
+ } else
+ endianness = G_LITTLE_ENDIAN;
+ }
+
+ format_caps = gst_caps_new_simple ("video/x-raw-rgb",
+ "format", GST_TYPE_FOURCC, formats[i].id,
+ "endianness", G_TYPE_INT, endianness,
+ "depth", G_TYPE_INT, fmt->depth,
+ "bpp", G_TYPE_INT, fmt->bits_per_pixel,
+ "red_mask", G_TYPE_INT, fmt->red_mask,
+ "green_mask", G_TYPE_INT, fmt->green_mask,
+ "blue_mask", G_TYPE_INT, fmt->blue_mask,
+ "width", GST_TYPE_INT_RANGE, 1, max_w,
+ "height", GST_TYPE_INT_RANGE, 1, max_h,
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
+
+ is_rgb_format = TRUE;
+ break;
+ }
+ case XvYUV:
+ format_caps = gst_caps_new_simple ("video/x-raw-yuv",
+ "format", GST_TYPE_FOURCC, formats[i].id,
+ "width", GST_TYPE_INT_RANGE, 1, max_w,
+ "height", GST_TYPE_INT_RANGE, 1, max_h,
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (format_caps) {
+ GstEvasPixmapFormat *format = NULL;
+
+ format = g_new0 (GstEvasPixmapFormat, 1);
+ if (format) {
+ format->format = formats[i].id;
+ format->caps = gst_caps_copy (format_caps);
+ xcontext->formats_list = g_list_append (xcontext->formats_list, format);
+ }
+
+ if (is_rgb_format) {
+ if (rgb_caps == NULL)
+ rgb_caps = format_caps;
+ else
+ gst_caps_append (rgb_caps, format_caps);
+ } else
+ gst_caps_append (caps, format_caps);
+ }
+ }
+
+ /* Collected all caps into either the caps or rgb_caps structures.
+ * Append rgb_caps on the end of YUV, so that YUV is always preferred */
+ if (rgb_caps)
+ gst_caps_append (caps, rgb_caps);
+
+ if (formats)
+ XFree (formats);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"Generated the following caps: %" GST_PTR_FORMAT, caps);
+
+ if (gst_caps_is_empty (caps)) {
+ gst_caps_unref (caps);
+ XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
+ GST_ELEMENT_ERROR (evaspixmapsink, STREAM, WRONG_TYPE, (NULL),
+ ("No supported format found"));
+ return NULL;
+ }
+
+ return caps;
+}
+
+static gpointer
+gst_evaspixmapsink_event_thread (GstEvasPixmapSink * evaspixmapsink)
+{
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), NULL);
+ int damage_base = 0;
+ int damage_err_base = 0;
+ int damage_case = 0;
+ XEvent e;
+ int i = 0;
+ Display *disp = NULL;
+
+#ifdef DUMP_IMG
+ int ret = 0;
+ char file_name[128];
+ char *dump_data;
+#endif
+
+
+ GST_OBJECT_LOCK (evaspixmapsink);
+
+ if (evaspixmapsink->xcontext && evaspixmapsink->xcontext->disp) {
+ disp = evaspixmapsink->xcontext->disp;
+ } else {
+ GST_ERROR_OBJECT (evaspixmapsink,"evaspixmapsink->xcontext(->disp) is not ready");
+ return NULL;
+ }
+
+ if (!XDamageQueryExtension(evaspixmapsink->xcontext->disp, &damage_base, &damage_err_base)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"XDamageQueryExtension() failed");
+ return NULL;
+ }
+ damage_case = (int)damage_base + XDamageNotify;
+
+ while (evaspixmapsink->running) {
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+ while (XPending (disp)) {
+ XNextEvent (disp, &e);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ if (e.type == damage_case ) {
+ XDamageNotifyEvent *damage_ev = (XDamageNotifyEvent *)&e;
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ GstXPixmap *xpixmap = evaspixmapsink->xpixmap[i];
+ if (xpixmap && damage_ev->drawable == xpixmap->pixmap) {
+ g_mutex_lock(evaspixmapsink->pixmap_ref_lock);
+ if (xpixmap->ref) {
+ /* set it only if damage event comes from _buffer_put() */
+ xpixmap->damaged_time = (gint)get_millis_time();
+ }
+ g_mutex_unlock(evaspixmapsink->pixmap_ref_lock);
+ if (GST_STATE(evaspixmapsink) < GST_STATE_PLAYING || log_warn_count < 20) {
+ GST_WARNING_OBJECT (evaspixmapsink,"event_handler : got a damage event for pixmap(%d), refcount(%d), damaged_time(%d)",
+ xpixmap->pixmap, xpixmap->ref, xpixmap->damaged_time);
+ } else {
+ GST_DEBUG_OBJECT (evaspixmapsink,"event_handler : got a damage event for pixmap(%d), refcount(%d), damaged_time(%d)",
+ xpixmap->pixmap, xpixmap->ref, xpixmap->damaged_time);
+ }
+ if (evaspixmapsink->epipe_request_count > EPIPE_REQUEST_LIMIT) {
+ GST_WARNING_OBJECT (evaspixmapsink,"event_handler : epipe_request_count(%d), skip ecore_pipe_write()", evaspixmapsink->epipe_request_count);
+ } else {
+ ecore_pipe_write(evaspixmapsink->epipe, evaspixmapsink, sizeof(GstEvasPixmapSink));
+ evaspixmapsink->epipe_request_count++;
+ if (GST_STATE(evaspixmapsink) < GST_STATE_PLAYING || log_warn_count < 20 ) {
+ GST_WARNING_OBJECT (evaspixmapsink,"event_handler : after call ecore_pipe_write() for pixmap(%d)", xpixmap->pixmap);
+ log_warn_count++;
+ } else {
+ GST_DEBUG_OBJECT (evaspixmapsink,"event_handler : after call ecore_pipe_write() for pixmap(%d)", xpixmap->pixmap);
+ }
+ }
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XDamageSubtract (evaspixmapsink->xcontext->disp, evaspixmapsink->damage[i], None, None );
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+#ifdef DUMP_IMG
+ int dump_w = evaspixmapsink->w + (32 - (evaspixmapsink->w%32)); // for multiples of 32
+ if(g_cnt<100)
+ {
+ evaspixmapsink->pixmap_addr[i] = gst_evaspixmapsink_get_buffer(evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->pixmap, dump_w, evaspixmapsink->h, i);
+ GST_ERROR("<DUMP_%2.2d> pixmap[%d] addr %p (w : %d, h : %d)", g_cnt, i, evaspixmapsink->pixmap_addr[i], dump_w, evaspixmapsink->h);
+ sprintf(file_name, "/opt/usr/media/DUMP_%2.2d.dump", g_cnt);
+
+ dump_data = g_malloc(dump_w * evaspixmapsink->h * 4);
+ memcpy (dump_data, evaspixmapsink->pixmap_addr[i], dump_w * evaspixmapsink->h * 4);
+
+ ret = util_write_rawdata(file_name, dump_data, dump_w * evaspixmapsink->h * 4);
+ if (ret) {
+ GST_ERROR_OBJECT (evaspixmapsink, "util_write_rawdata() failed");
+ }
+ else
+ g_cnt++;
+ g_free(dump_data);
+
+ if (g_bufinfo[i]) {
+ gst_evaspixmapsink_free_buffer (i);
+ g_bufinfo[i] = NULL;
+ }
+ evaspixmapsink->pixmap_addr[i] = NULL;
+ }
+#endif
+ if (evaspixmapsink->buf_shared_type == BUF_SHARE_METHOD_FD) {
+ if (GST_STATE(evaspixmapsink) == GST_STATE_PLAYING) {
+ drm_fini_close_gem_handle(evaspixmapsink, xpixmap->pixmap);
+ }
+ }
+ break;
+ }
+ }
+ if (i == evaspixmapsink->num_of_pixmaps) {
+ GST_WARNING_OBJECT (evaspixmapsink,"event_handler : could not find corresponding pixmap with this damage event(%d)", damage_ev->drawable);
+ }
+ } else {
+ GST_LOG_OBJECT (evaspixmapsink,"event_handler : unidentified event(%d)", e.type);
+ g_mutex_lock (evaspixmapsink->x_lock);
+ continue;
+ }
+ g_mutex_lock (evaspixmapsink->x_lock);
+ }
+ //XSync (disp, FALSE);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ g_usleep (G_USEC_PER_SEC / 40);
+ GST_OBJECT_LOCK (evaspixmapsink);
+ }
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+ return NULL;
+}
+
+static void
+gst_evaspixmapsink_manage_event_thread (GstEvasPixmapSink *evaspixmapsink)
+{
+ /* don't start the thread too early */
+ if (evaspixmapsink->xcontext == NULL) {
+ GST_ERROR_OBJECT (evaspixmapsink,"xcontext is NULL..");
+ return;
+ }
+
+ GST_OBJECT_LOCK (evaspixmapsink);
+
+ if (!evaspixmapsink->event_thread) {
+ /* Setup our event listening thread */
+ GST_DEBUG_OBJECT (evaspixmapsink,"run xevent thread");
+ evaspixmapsink->running = TRUE;
+ evaspixmapsink->event_thread = g_thread_create ( (GThreadFunc) gst_evaspixmapsink_event_thread, evaspixmapsink, TRUE, NULL);
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink,"there already existed the event_thread.. keep going");
+ /* Do not finalize the thread in here, Only finalize the thread by calling gst_evaspixmapsink_reset() */
+ }
+
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+}
+
+
+/* This function calculates the pixel aspect ratio based on the properties
+ * in the xcontext structure and stores it there. */
+static void
+gst_evaspixmapsink_calculate_pixel_aspect_ratio (GstXContext *xcontext)
+{
+ static const gint par[][2] = {
+ {1, 1}, /* regular screen */
+ {16, 15}, /* PAL TV */
+ {11, 10}, /* 525 line Rec.601 video */
+ {54, 59}, /* 625 line Rec.601 video */
+ {64, 45}, /* 1280x1024 on 16:9 display */
+ {5, 3}, /* 1280x1024 on 4:3 display */
+ {4, 3} /* 800x600 on 16:9 display */
+ };
+ gint i;
+ gint index;
+ gdouble ratio;
+ gdouble delta;
+
+#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
+
+ /* first calculate the "real" ratio based on the X values;
+ * which is the "physical" w/h divided by the w/h in pixels of the display */
+ ratio = (gdouble) (xcontext->widthmm * xcontext->height)
+ / (xcontext->heightmm * xcontext->width);
+
+ /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
+ * override here */
+ if (xcontext->width == 720 && xcontext->height == 576) {
+ ratio = 4.0 * 576 / (3.0 * 720);
+ }
+ GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
+ /* now find the one from par[][2] with the lowest delta to the real one */
+ delta = DELTA (0);
+ index = 0;
+
+ for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
+ gdouble this_delta = DELTA (i);
+
+ if (this_delta < delta) {
+ index = i;
+ delta = this_delta;
+ }
+ }
+
+ GST_DEBUG ("Decided on index %d (%d/%d)", index,
+ par[index][0], par[index][1]);
+
+ g_free (xcontext->par);
+ xcontext->par = g_new0 (GValue, 1);
+ g_value_init (xcontext->par, GST_TYPE_FRACTION);
+ gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
+ GST_DEBUG ("set xcontext PAR to %d/%d",
+ gst_value_get_fraction_numerator (xcontext->par),
+ gst_value_get_fraction_denominator (xcontext->par));
+}
+
+/* This function gets the X Display and global info about it. Everything is
+ stored in our object and will be cleaned when the object is disposed. Note
+ here that caps for supported format are generated without any window or
+ image creation */
+static GstXContext*
+gst_evaspixmapsink_xcontext_get (GstEvasPixmapSink *evaspixmapsink)
+{
+ GstXContext *xcontext = NULL;
+ XPixmapFormatValues *px_formats = NULL;
+ gint nb_formats = 0, i, j, N_attr;
+ XvAttribute *xv_attr;
+ Atom prop_atom;
+ const char *channels[4] = { "XV_HUE", "XV_SATURATION", "XV_BRIGHTNESS", "XV_CONTRAST"};
+
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), NULL);
+
+ xcontext = g_new0 (GstXContext, 1);
+ xcontext->im_format = 0;
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ xcontext->disp = XOpenDisplay (evaspixmapsink->display_name);
+ if (!xcontext->disp) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_free (xcontext);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE, ("Could not initialise Xv output"), ("Could not open display"));
+ return NULL;
+ }
+ GST_WARNING_OBJECT (evaspixmapsink,"Display(0x%x) is opened", xcontext->disp);
+ g_my_disp = xcontext->disp;
+
+ xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
+ xcontext->screen_num = DefaultScreen (xcontext->disp);
+ xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
+ xcontext->root = DefaultRootWindow (xcontext->disp);
+ xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
+ xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
+
+ xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
+ xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
+ xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
+ xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"X reports %dx%d pixels and %d mm x %d mm", xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
+
+ gst_evaspixmapsink_calculate_pixel_aspect_ratio (xcontext);
+ /* We get supported pixmap formats at supported depth */
+ px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
+
+ if (!px_formats) {
+ XCloseDisplay (xcontext->disp);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_free (xcontext->par);
+ g_free (xcontext);
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, SETTINGS,
+ ("Could not initialise Xv output"), ("Could not get pixel formats"));
+ g_my_disp = NULL;
+ return NULL;
+ }
+
+ /* We get bpp value corresponding to our running depth */
+ for (i = 0; i < nb_formats; i++) {
+ if (px_formats[i].depth == xcontext->depth)
+ xcontext->bpp = px_formats[i].bits_per_pixel;
+ }
+
+ XFree (px_formats);
+
+ xcontext->endianness = (ImageByteOrder (xcontext->disp) == LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
+
+ /* our caps system handles 24/32bpp RGB as big-endian. */
+ if ((xcontext->bpp == 24 || xcontext->bpp == 32) && xcontext->endianness == G_LITTLE_ENDIAN) {
+ xcontext->endianness = G_BIG_ENDIAN;
+ xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
+ xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
+ xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
+ if (xcontext->bpp == 24) {
+ xcontext->visual->red_mask >>= 8;
+ xcontext->visual->green_mask >>= 8;
+ xcontext->visual->blue_mask >>= 8;
+ }
+ }
+
+ xcontext->caps = gst_evaspixmapsink_get_xv_support (evaspixmapsink, xcontext);
+
+ if (!xcontext->caps) {
+ XCloseDisplay (xcontext->disp);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_free (xcontext->par);
+ g_free (xcontext);
+ /* GST_ELEMENT_ERROR is thrown by gst_evaspixmapsink_get_xv_support */
+ g_my_disp = NULL;
+ return NULL;
+ }
+#ifdef HAVE_XSHM
+ /* Search for XShm extension support */
+ if (XShmQueryExtension (xcontext->disp) && gst_evaspixmapsink_check_xshm_calls (xcontext)) {
+ xcontext->use_xshm = TRUE;
+ GST_DEBUG_OBJECT (evaspixmapsink,"evaspixmapsink is using XShm extension");
+ } else
+#endif /* HAVE_XSHM */
+ {
+ xcontext->use_xshm = FALSE;
+ GST_DEBUG_OBJECT (evaspixmapsink,"evaspixmapsink is not using XShm extension");
+ }
+
+ xv_attr = XvQueryPortAttributes (xcontext->disp, xcontext->xv_port_id, &N_attr);
+
+ /* Generate the channels list */
+ for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
+ XvAttribute *matching_attr = NULL;
+
+ /* Retrieve the property atom if it exists. If it doesn't exist,
+ * the attribute itself must not either, so we can skip */
+ prop_atom = XInternAtom (xcontext->disp, channels[i], True);
+ if (prop_atom == None) {
+ continue;
+ }
+
+ if (xv_attr != NULL) {
+ for (j = 0; j < N_attr && matching_attr == NULL; ++j) {
+ if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name)) {
+ matching_attr = xv_attr + j;
+ }
+ }
+ }
+
+ if (matching_attr) {
+ GstColorBalanceChannel *channel;
+ channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
+ channel->label = g_strdup (channels[i]);
+ channel->min_value = matching_attr->min_value;
+ channel->max_value = matching_attr->max_value;
+
+ xcontext->channels_list = g_list_append (xcontext->channels_list, channel);
+
+ /* If the colorbalance settings have not been touched we get Xv values
+ as defaults and update our internal variables */
+ if (!evaspixmapsink->cb_changed) {
+ gint val;
+ XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id, prop_atom, &val);
+ /* Normalize val to [-1000, 1000] */
+ val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) / (double) (channel->max_value - channel->min_value));
+
+ if (!g_ascii_strcasecmp (channels[i], "XV_HUE")) {
+ evaspixmapsink->hue = val;
+ } else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION")) {
+ evaspixmapsink->saturation = val;
+ } else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS")) {
+ evaspixmapsink->brightness = val;
+ } else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST")) {
+ evaspixmapsink->contrast = val;
+ }
+ }
+ }
+ }
+
+ if (xv_attr) {
+ XFree (xv_attr);
+ }
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ return xcontext;
+}
+
+/* This function cleans the X context. Closing the Display, releasing the XV
+ port and unrefing the caps for supported formats. */
+static void
+gst_evaspixmapsink_xcontext_clear (GstEvasPixmapSink *evaspixmapsink)
+{
+ GList *formats_list, *channels_list;
+ GstXContext *xcontext;
+ gint i = 0;
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+
+ GST_OBJECT_LOCK (evaspixmapsink);
+ if (evaspixmapsink->xcontext == NULL) {
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+ return;
+ }
+
+ /* Take the XContext from the sink and clean it up */
+ xcontext = evaspixmapsink->xcontext;
+ evaspixmapsink->xcontext = NULL;
+
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+
+ formats_list = xcontext->formats_list;
+
+ while (formats_list) {
+ GstEvasPixmapFormat *format = formats_list->data;
+
+ gst_caps_unref (format->caps);
+ g_free (format);
+ formats_list = g_list_next (formats_list);
+ }
+
+ if (xcontext->formats_list)
+ g_list_free (xcontext->formats_list);
+
+ channels_list = xcontext->channels_list;
+
+ while (channels_list) {
+ GstColorBalanceChannel *channel = channels_list->data;
+
+ g_object_unref (channel);
+ channels_list = g_list_next (channels_list);
+ }
+
+ if (xcontext->channels_list)
+ g_list_free (xcontext->channels_list);
+
+ gst_caps_unref (xcontext->caps);
+
+ for (i = 0; i < xcontext->nb_adaptors; i++) {
+ g_free (xcontext->adaptors[i]);
+ }
+
+ g_free (xcontext->adaptors);
+
+ g_free (xcontext->par);
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"Closing display and freeing X Context");
+
+ XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
+
+ XCloseDisplay (xcontext->disp);
+ g_my_disp = NULL;
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+ g_free (xcontext);
+}
+
+/* Element stuff */
+
+/* This function tries to get a format matching with a given caps in the
+ supported list of formats we generated in gst_evaspixmapsink_get_xv_support */
+static gint
+gst_evaspixmapsink_get_format_from_caps (GstEvasPixmapSink *evaspixmapsink, GstCaps *caps)
+{
+ GList *list = NULL;
+
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), 0);
+
+ list = evaspixmapsink->xcontext->formats_list;
+
+ while (list) {
+ GstEvasPixmapFormat *format = list->data;
+
+ if (format) {
+ if (gst_caps_can_intersect (caps, format->caps)) {
+ return format->format;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ return -1;
+}
+
+static GstCaps *
+gst_evaspixmapsink_getcaps (GstBaseSink *bsink)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (bsink);
+
+ if (evaspixmapsink->xcontext)
+ return gst_caps_ref (evaspixmapsink->xcontext->caps);
+
+ return
+ gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
+ (evaspixmapsink)));
+}
+
+static gboolean
+gst_evaspixmapsink_setcaps (GstBaseSink *bsink, GstCaps *caps)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+ GstStructure *structure;
+ guint32 im_format = 0;
+ gboolean ret;
+ gint video_width, video_height;
+ gint disp_x, disp_y;
+ gint disp_width, disp_height;
+ gint video_par_n, video_par_d; /* video's PAR */
+ gint display_par_n, display_par_d; /* display's PAR */
+ const GValue *caps_par;
+ const GValue *caps_disp_reg;
+ const GValue *fps;
+ guint num, den;
+ gboolean enable_last_buffer;
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (bsink);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %" GST_PTR_FORMAT, evaspixmapsink->xcontext->caps, caps);
+
+ if (!gst_caps_can_intersect (evaspixmapsink->xcontext->caps, caps)) {
+ goto incompatible_caps;
+ }
+
+ structure = gst_caps_get_structure (caps, 0);
+ ret = gst_structure_get_int (structure, "width", &video_width);
+ ret &= gst_structure_get_int (structure, "height", &video_height);
+ fps = gst_structure_get_value (structure, "framerate");
+ ret &= (fps != NULL);
+
+ if (!ret) {
+ goto incomplete_caps;
+ }
+
+ evaspixmapsink->aligned_width = video_width;
+ evaspixmapsink->aligned_height = video_height;
+
+ /* get combine prop of hevc */
+ if(gst_structure_get_int (structure, "yuvcombine", &(evaspixmapsink->need_combine_data)))
+ {
+ GST_INFO_OBJECT(evaspixmapsink, "need combine data : %d", evaspixmapsink->need_combine_data);
+ }
+ else
+ {
+ /*Not need to combine data, just directly copy*/
+ evaspixmapsink->need_combine_data = 0;
+ }
+
+ /* get enable-last-buffer */
+ g_object_get(G_OBJECT(evaspixmapsink), "enable-last-buffer", &enable_last_buffer, NULL);
+ GST_INFO_OBJECT (evaspixmapsink,"current enable-last-buffer : %d", enable_last_buffer);
+ /* flush if enable-last-buffer is TRUE */
+ if (enable_last_buffer) {
+ GST_INFO_OBJECT (evaspixmapsink,"flush last-buffer");
+ g_object_set(G_OBJECT(evaspixmapsink), "enable-last-buffer", FALSE, NULL);
+ g_object_set(G_OBJECT(evaspixmapsink), "enable-last-buffer", TRUE, NULL);
+ }
+
+ evaspixmapsink->fps_n = gst_value_get_fraction_numerator (fps);
+ evaspixmapsink->fps_d = gst_value_get_fraction_denominator (fps);
+
+ if (evaspixmapsink->video_width != video_width || evaspixmapsink->video_height != video_height) {
+ evaspixmapsink->src_prev.w = evaspixmapsink->video_width = video_width;
+ evaspixmapsink->src_prev.h = evaspixmapsink->video_height = video_height;
+ evaspixmapsink->need_to_fill_black = TRUE;
+ }
+
+ im_format = gst_evaspixmapsink_get_format_from_caps (evaspixmapsink, caps);
+ if (im_format == -1) {
+ goto invalid_format;
+ }
+
+ /* get aspect ratio from caps if it's present, and
+ * convert video width and height to a display width and height
+ * using wd / hd = wv / hv * PARv / PARd */
+
+ /* get video's PAR */
+ caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
+ if (caps_par) {
+ video_par_n = gst_value_get_fraction_numerator (caps_par);
+ video_par_d = gst_value_get_fraction_denominator (caps_par);
+ } else {
+ video_par_n = 1;
+ video_par_d = 1;
+ }
+ /* get display's PAR */
+ if (evaspixmapsink->par) {
+ display_par_n = gst_value_get_fraction_numerator (evaspixmapsink->par);
+ display_par_d = gst_value_get_fraction_denominator (evaspixmapsink->par);
+ } else {
+ display_par_n = 1;
+ display_par_d = 1;
+ }
+
+ /* get the display region */
+ caps_disp_reg = gst_structure_get_value (structure, "display-region");
+ if (caps_disp_reg) {
+ disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
+ disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
+ disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
+ disp_height = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
+ } else {
+ disp_x = disp_y = 0;
+ disp_width = video_width;
+ disp_height = video_height;
+ }
+
+ if (!gst_video_calculate_display_ratio (&num, &den, video_width, video_height, video_par_n, video_par_d, display_par_n, display_par_d)) {
+ goto no_disp_ratio;
+ }
+
+ evaspixmapsink->disp_x = disp_x;
+ evaspixmapsink->disp_y = disp_y;
+ evaspixmapsink->disp_width = disp_width;
+ evaspixmapsink->disp_height = disp_height;
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"video width/height: %dx%d, calculated display ratio: %d/%d", video_width, video_height, num, den);
+
+ /* now find a width x height that respects this display ratio.
+ * prefer those that have one of w/h the same as the incoming video
+ * using wd / hd = num / den */
+
+ /* start with same height, because of interlaced video */
+ /* check hd / den is an integer scale factor, and scale wd with the PAR */
+ if (video_height % den == 0) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"keeping video height");
+ GST_VIDEO_SINK_WIDTH (evaspixmapsink) = (guint) gst_util_uint64_scale_int (video_height, num, den);
+ GST_VIDEO_SINK_HEIGHT (evaspixmapsink) = video_height;
+ } else if (video_width % num == 0) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"keeping video width");
+ GST_VIDEO_SINK_WIDTH (evaspixmapsink) = video_width;
+ GST_VIDEO_SINK_HEIGHT (evaspixmapsink) = (guint) gst_util_uint64_scale_int (video_width, den, num);
+ } else {
+ GST_DEBUG_OBJECT (evaspixmapsink,"approximating while keeping video height");
+ GST_VIDEO_SINK_WIDTH (evaspixmapsink) = (guint) gst_util_uint64_scale_int (video_height, num, den);
+ GST_VIDEO_SINK_HEIGHT (evaspixmapsink) = video_height;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink,"scaling to %dx%d", GST_VIDEO_SINK_WIDTH (evaspixmapsink), GST_VIDEO_SINK_HEIGHT (evaspixmapsink));
+
+ /* Creating our window and our image with the display size in pixels */
+ if (GST_VIDEO_SINK_WIDTH (evaspixmapsink) <= 0 || GST_VIDEO_SINK_HEIGHT (evaspixmapsink) <= 0) {
+ goto no_display_size;
+ }
+
+ g_mutex_lock (evaspixmapsink->flow_lock);
+
+ /* We renew our evaspixmap buffer only if size or format changed;
+ * the evaspixmap buffer is the same size as the video pixel size */
+ if ((evaspixmapsink->evas_pixmap_buf) && ((im_format != evaspixmapsink->evas_pixmap_buf->im_format)
+ || (video_width != evaspixmapsink->evas_pixmap_buf->width) || (video_height != evaspixmapsink->evas_pixmap_buf->height))) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (evaspixmapsink->evas_pixmap_buf->im_format), GST_FOURCC_ARGS (im_format));
+ GST_DEBUG_OBJECT (evaspixmapsink,"renewing evaspixmap buffer");
+ gst_buffer_unref (GST_BUFFER (evaspixmapsink->evas_pixmap_buf));
+ evaspixmapsink->evas_pixmap_buf = NULL;
+ }
+
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+
+ if (evaspixmapsink->eo) {
+ if (!gst_evaspixmapsink_xpixmap_link (evaspixmapsink)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"link evas image object with pixmap failed...");
+ return FALSE;
+ } else {
+ gst_evaspixmapsink_manage_event_thread (evaspixmapsink);
+ }
+ } else {
+ GST_ERROR_OBJECT (evaspixmapsink,"setcaps success, but there is no evas image object..");
+ return FALSE;
+ }
+
+ return TRUE;
+
+ /* ERRORS */
+ incompatible_caps:
+ {
+ GST_ERROR_OBJECT (evaspixmapsink,"caps incompatible");
+ return FALSE;
+ }
+ incomplete_caps:
+ {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Failed to retrieve either width, ""height or framerate from intersected caps");
+ return FALSE;
+ }
+ invalid_format:
+ {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Could not locate image format from caps %" GST_PTR_FORMAT, caps);
+ return FALSE;
+ }
+ no_disp_ratio:
+ {
+ GST_ELEMENT_ERROR (evaspixmapsink, CORE, NEGOTIATION, (NULL), ("Error calculating the output display ratio of the video."));
+ return FALSE;
+ }
+ no_display_size:
+ {
+ GST_ELEMENT_ERROR (evaspixmapsink, CORE, NEGOTIATION, (NULL), ("Error calculating the output display ratio of the video."));
+ return FALSE;
+ }
+}
+
+static GstStateChangeReturn
+gst_evaspixmapsink_change_state (GstElement *element, GstStateChange transition)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstEvasPixmapSink *evaspixmapsink;
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ g_mutex_lock (evaspixmapsink->flow_lock);
+ GST_WARNING_OBJECT (evaspixmapsink,"GST_STATE_CHANGE_NULL_TO_READY");
+
+ /* open drm to use gem */
+ if (drm_init(evaspixmapsink)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"drm_init() failure");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ /* check if there exist evas image object, need to write code related to making internal evas image object */
+ if (!is_evas_image_object (evaspixmapsink->eo)) {
+ GST_ERROR_OBJECT (evaspixmapsink,"There is no evas image object..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ /* Set xcontext and display */
+ if (!evaspixmapsink->xcontext) {
+ evaspixmapsink->xcontext = gst_evaspixmapsink_xcontext_get (evaspixmapsink);
+ if (!evaspixmapsink->xcontext) {
+ GST_ERROR_OBJECT (evaspixmapsink,"could not get xcontext..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+ }
+
+ /* update object's par with calculated one if not set yet */
+ if (!evaspixmapsink->par) {
+ evaspixmapsink->par = g_new0 (GValue, 1);
+ gst_value_init_and_copy (evaspixmapsink->par, evaspixmapsink->xcontext->par);
+ GST_DEBUG_OBJECT (evaspixmapsink,"set calculated PAR on object's PAR");
+ }
+
+ /* call XSynchronize with the current value of synchronous */
+ GST_DEBUG_OBJECT (evaspixmapsink,"XSynchronize called with %s", evaspixmapsink->synchronous ? "TRUE" : "FALSE");
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XSynchronize (evaspixmapsink->xcontext->disp, evaspixmapsink->synchronous);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ break;
+
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_WARNING_OBJECT (evaspixmapsink,"GST_STATE_CHANGE_READY_TO_PAUSED");
+ break;
+
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ GST_WARNING_OBJECT (evaspixmapsink,"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:
+ GST_WARNING_OBJECT (evaspixmapsink,"GST_STATE_CHANGE_PLAYING_TO_PAUSED");
+ log_warn_count = 0;
+ break;
+
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
+ int i = 0;
+ GST_WARNING_OBJECT (evaspixmapsink,"GST_STATE_CHANGE_PAUSED_TO_READY");
+ evaspixmapsink->fps_n = 0;
+ evaspixmapsink->fps_d = 1;
+ GST_VIDEO_SINK_WIDTH (evaspixmapsink) = 0;
+ GST_VIDEO_SINK_HEIGHT (evaspixmapsink) = 0;
+ drm_fini_close_gem_handle(evaspixmapsink, 0);
+ }
+ break;
+
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_WARNING_OBJECT (evaspixmapsink,"GST_STATE_CHANGE_READY_TO_NULL");
+ g_mutex_lock (evaspixmapsink->flow_lock);
+ gst_evaspixmapsink_reset(evaspixmapsink);
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ /* close drm */
+ drm_fini(evaspixmapsink);
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void
+gst_evaspixmapsink_get_times (GstBaseSink *bsink, GstBuffer *buf, GstClockTime *start, GstClockTime *end)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (bsink);
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+ *start = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_BUFFER_DURATION_IS_VALID (buf)) {
+ *end = *start + GST_BUFFER_DURATION (buf);
+ } else {
+ if (evaspixmapsink->fps_n > 0) {
+ *end = *start +
+ gst_util_uint64_scale_int (GST_SECOND, evaspixmapsink->fps_d,
+ evaspixmapsink->fps_n);
+ }
+ }
+ }
+}
+
+static void
+gst_evaspixmapsink_combine_i420_scmn_data(GstEvasPixmapSink *evaspixmapsink, GstBuffer *buf)
+{
+ unsigned int outsize = 0;
+ unsigned char *temp_outbuf = evaspixmapsink->evas_pixmap_buf->xvimage->data;
+ int cnt = 0, j = 0;
+ unsigned char *temp_imgb;
+ SCMN_IMGB *imgb = NULL;
+ int stride, w, h, i;
+
+ GST_DEBUG("Begin");
+
+ imgb = (SCMN_IMGB *)GST_BUFFER_DATA(buf);
+ if(imgb == NULL || imgb->a[0] == NULL)
+ {
+ return;
+ }
+
+ stride = GST_ROUND_UP_4 (imgb->w[0]);
+ h = imgb->h[0];
+ w = imgb->w[0];
+ /*Y plane copy */
+ for (i = 0; i < h; i++)
+ {
+ memcpy (temp_outbuf + i * stride, imgb->a[0] + i * imgb->s[0], w);
+ }
+
+ temp_outbuf += GST_ROUND_UP_4 (imgb->w[0]) * GST_ROUND_UP_2 (imgb->h[0]);
+
+ stride = GST_ROUND_UP_4 (GST_ROUND_UP_2 (imgb->w[0]) / 2);
+ h = GST_ROUND_UP_2 (imgb->h[0]) / 2;
+ w = GST_ROUND_UP_2 (imgb->w[0]) / 2;
+ /* Cb plane copy */
+ for (i = 0; i < h; i++)
+ {
+ memcpy (temp_outbuf + i * stride, imgb->a[1] + i * imgb->s[1], w);
+ }
+
+ temp_outbuf += GST_ROUND_UP_4 (GST_ROUND_UP_2 (imgb->w[0]) / 2) * (GST_ROUND_UP_2 (imgb->h[0]) / 2);
+ /* Same stride, height, width as above */
+ /* Cr plane copy */
+ for (i = 0; i < h; i++)
+ {
+ memcpy (temp_outbuf + i * stride, imgb->a[2] + i * imgb->s[2], w);
+ }
+
+ outsize = imgb->w[0] * imgb->h[0]* 3 >> 1;
+ evaspixmapsink->evas_pixmap_buf->size = MIN(outsize, evaspixmapsink->evas_pixmap_buf->size);
+
+ GST_DEBUG("End");
+}
+
+static GstFlowReturn
+gst_evaspixmapsink_show_frame (GstVideoSink *vsink, GstBuffer *buf)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+ XV_DATA_PTR img_data = NULL;
+ SCMN_IMGB *scmn_imgb = NULL;
+ gint format = 0;
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (vsink);
+
+ if (GST_STATE(evaspixmapsink) < GST_STATE_PLAYING || log_warn_count < 20) {
+ GST_WARNING_OBJECT (evaspixmapsink, "enter >>>" );
+ }
+
+ if( evaspixmapsink->stop_video ) {
+ GST_INFO_OBJECT (evaspixmapsink, "Stop video is TRUE. so skip show frame..." );
+ goto skip_frame;
+ }
+
+ if (!evaspixmapsink->evas_pixmap_buf) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"creating our evaspixmap buffer");
+ format = gst_evaspixmapsink_get_format_from_caps(evaspixmapsink, GST_BUFFER_CAPS(buf));
+ switch (format) {
+ case GST_MAKE_FOURCC('S', 'T', '1', '2'):
+ case GST_MAKE_FOURCC('S', 'N', '1', '2'):
+ case GST_MAKE_FOURCC('S', '4', '2', '0'):
+ case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
+ case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
+ case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
+ case GST_MAKE_FOURCC('I', 'T', 'L', 'V'):
+ case GST_MAKE_FOURCC('S', 'R', '3', '2'):
+ scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
+ if(scmn_imgb == NULL) {
+ GST_DEBUG_OBJECT (evaspixmapsink, "scmn_imgb is NULL. Skip buffer put..." );
+ goto skip_frame;
+ }
+ /* skip buffer if aligned size is smaller than size of caps */
+ if (scmn_imgb->s[0] < evaspixmapsink->video_width || scmn_imgb->e[0] < evaspixmapsink->video_height) {
+ GST_WARNING_OBJECT (evaspixmapsink,"invalid size[caps:%dx%d,aligned:%dx%d]. Skip this buffer...",
+ evaspixmapsink->video_width, evaspixmapsink->video_height, scmn_imgb->s[0], scmn_imgb->e[0]);
+ goto skip_frame;
+ }
+ evaspixmapsink->aligned_width = scmn_imgb->s[0];
+ evaspixmapsink->aligned_height = scmn_imgb->e[0];
+ GST_DEBUG_OBJECT (evaspixmapsink,"video width,height[%dx%d]",evaspixmapsink->video_width, evaspixmapsink->video_height);
+ GST_INFO_OBJECT (evaspixmapsink,"Use aligned width,height[%dx%d]",evaspixmapsink->aligned_width, evaspixmapsink->aligned_height);
+ break;
+ default:
+ GST_INFO_OBJECT (evaspixmapsink,"Use original width,height of caps");
+ break;
+ }
+ evaspixmapsink->evas_pixmap_buf = gst_evaspixmap_buffer_new (evaspixmapsink, GST_BUFFER_CAPS (buf));
+ if (!evaspixmapsink->evas_pixmap_buf) {
+ /* The create method should have posted an informative error */
+ goto no_image;
+ }
+ if (evaspixmapsink->evas_pixmap_buf->size < GST_BUFFER_SIZE (buf)) {
+ GST_ELEMENT_ERROR (evaspixmapsink, RESOURCE, WRITE, ("Failed to create output image buffer of %dx%d pixels", evaspixmapsink->evas_pixmap_buf->width, evaspixmapsink->evas_pixmap_buf->height),("XServer allocated buffer size did not match input buffer"));
+ gst_evaspixmap_buffer_destroy (evaspixmapsink->evas_pixmap_buf);
+ evaspixmapsink->evas_pixmap_buf = NULL;
+ goto no_image;
+ }
+ }
+
+ switch (evaspixmapsink->evas_pixmap_buf->im_format) {
+ /* Cases for specified formats of Samsung extension */
+ case GST_MAKE_FOURCC('S', 'T', '1', '2'):
+ case GST_MAKE_FOURCC('S', 'N', '1', '2'):
+ case GST_MAKE_FOURCC('S', '4', '2', '0'):
+ case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
+ case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
+ case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
+ case GST_MAKE_FOURCC('I', 'T', 'L', 'V'):
+ case GST_MAKE_FOURCC('S', 'R', '3', '2'):
+ {
+ GST_DEBUG_OBJECT (evaspixmapsink,"Samsung extension display format activated. fourcc:%d", evaspixmapsink->evas_pixmap_buf->im_format);
+
+ if (evaspixmapsink->evas_pixmap_buf->xvimage->data) {
+ img_data = (XV_DATA_PTR) evaspixmapsink->evas_pixmap_buf->xvimage->data;
+ XV_INIT_DATA(img_data);
+ scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
+ if (scmn_imgb == NULL) {
+ GST_DEBUG_OBJECT (evaspixmapsink, "scmn_imgb is NULL. Skip buffer put..." );
+ goto skip_frame;
+ }
+
+ if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_PADDR) {
+ img_data->YBuf = (unsigned int)scmn_imgb->p[0];
+ img_data->CbBuf = (unsigned int)scmn_imgb->p[1];
+ img_data->CrBuf = (unsigned int)scmn_imgb->p[2];
+ img_data->BufType = XV_BUF_TYPE_LEGACY;
+
+ } else if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_FD) {
+ /* set gem information to gem_info structure of handle and convert dma-buf fd into drm gem name */
+ img_data->YBuf = drm_init_convert_dmabuf_gemname(evaspixmapsink, (int)scmn_imgb->dma_buf_fd[0]);
+ img_data->CbBuf = drm_init_convert_dmabuf_gemname(evaspixmapsink, (int)scmn_imgb->dma_buf_fd[1]);
+ img_data->CrBuf = drm_init_convert_dmabuf_gemname(evaspixmapsink, (int)scmn_imgb->dma_buf_fd[2]);
+ img_data->BufType = XV_BUF_TYPE_DMABUF;
+ if (evaspixmapsink->buf_shared_type != BUF_SHARE_METHOD_FD) {
+ evaspixmapsink->buf_shared_type = BUF_SHARE_METHOD_FD;
+ GST_WARNING_OBJECT (evaspixmapsink, "BUF SHARED TYPE : FD");
+ }
+
+ } else if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_TIZEN_BUFFER) {
+ img_data->bo[0] = scmn_imgb->bo[0];
+ img_data->bo[1] = scmn_imgb->bo[1];
+ img_data->bo[2] = scmn_imgb->bo[2];
+ GST_DEBUG("TBM bo %p %p %p", img_data->bo[0], img_data->bo[1], img_data->bo[2]);
+ /* export bo */
+ if (img_data->bo[0]) {
+ img_data->YBuf = tbm_init_convert_bo_gemname(evaspixmapsink, (tbm_bo)img_data->bo[0]);
+ }
+ if (img_data->bo[1]) {
+ img_data->CbBuf = tbm_init_convert_bo_gemname(evaspixmapsink, (tbm_bo)img_data->bo[1]);
+ }
+ if (img_data->bo[2]) {
+ img_data->CrBuf = tbm_init_convert_bo_gemname(evaspixmapsink, (tbm_bo)img_data->bo[2]);
+ }
+ if (evaspixmapsink->buf_shared_type != BUF_SHARE_METHOD_TIZEN_BUFFER) {
+ evaspixmapsink->buf_shared_type = BUF_SHARE_METHOD_TIZEN_BUFFER;
+ GST_WARNING_OBJECT (evaspixmapsink, "BUF SHARED TYPE : TIZEN BUFFER");
+ }
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink, "Not supported, buf_share_method(%d)", scmn_imgb->buf_share_method);
+ goto skip_frame;
+ }
+ if (!img_data->YBuf) {
+ GST_WARNING_OBJECT (evaspixmapsink, "img_data->YBuf is NULL. skip buffer put..." );
+ goto skip_frame;
+ }
+ GST_LOG_OBJECT(evaspixmapsink, "YBuf[%d], CbBuf[%d], CrBuf[%d]",
+ img_data->YBuf, img_data->CbBuf, img_data->CrBuf );
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink, "xvimage->data is NULL. skip buffer put..." );
+ goto skip_frame;
+ }
+ break;
+ }
+ default:
+ {
+ if (evaspixmapsink->buf_shared_type != BUF_SHARE_METHOD_NONE) {
+ evaspixmapsink->buf_shared_type = BUF_SHARE_METHOD_NONE;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink,"Normal format activated. fourcc = %d", evaspixmapsink->evas_pixmap_buf->im_format);
+ if(evaspixmapsink->need_combine_data == 1)
+ {
+ gst_evaspixmapsink_combine_i420_scmn_data(evaspixmapsink, buf);
+ }
+ else
+ {
+ memcpy (evaspixmapsink->evas_pixmap_buf->xvimage->data,
+ GST_BUFFER_DATA (buf),
+ MIN (GST_BUFFER_SIZE (buf), evaspixmapsink->evas_pixmap_buf->size));
+ }
+ break;
+ }
+ }
+ gst_evaspixmap_buffer_put (evaspixmapsink, evaspixmapsink->evas_pixmap_buf);
+
+skip_frame:
+
+ if (GST_STATE(evaspixmapsink) < GST_STATE_PLAYING || log_warn_count < 20) {
+ GST_WARNING_OBJECT (evaspixmapsink, "leave <<<" );
+ }
+
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+ no_image:
+ {
+ /* No image available. That's very bad ! */
+ GST_WARNING_OBJECT (evaspixmapsink,"could not create image");
+ return GST_FLOW_ERROR;
+ }
+}
+
+static gboolean
+gst_evaspixmapsink_event (GstBaseSink *sink, GstEvent *event)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (sink);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ GST_DEBUG_OBJECT (evaspixmapsink,"GST_EVENT_FLUSH_START");
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ GST_DEBUG_OBJECT (evaspixmapsink,"GST_EVENT_FLUSH_STOP");
+ break;
+ default:
+ break;
+ }
+ if (GST_BASE_SINK_CLASS (parent_class)->event) {
+ return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
+ } else {
+ return TRUE;
+ }
+}
+
+/* Interfaces stuff */
+
+static gboolean
+gst_evaspixmapsink_interface_supported (GstImplementsInterface *iface, GType type)
+{
+ g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
+ return TRUE;
+}
+
+static void
+gst_evaspixmapsink_interface_init (GstImplementsInterfaceClass *klass)
+{
+ klass->supported = gst_evaspixmapsink_interface_supported;
+}
+
+static void
+gst_evaspixmapsink_navigation_send_event (GstNavigation *navigation, GstStructure *structure)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (navigation);
+ GstPad *peer;
+ int i = 0;
+
+ if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (evaspixmapsink)))) {
+ GstEvent *event;
+ GstVideoRectangle result;
+ gdouble x, y, xscale = 1.0, yscale = 1.0;
+
+ event = gst_event_new_navigation (structure);
+
+ /* We take the flow_lock while we look at the window */
+ g_mutex_lock (evaspixmapsink->flow_lock);
+
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i ++) {
+ if (!evaspixmapsink->xpixmap[i]) {
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return;
+ }
+ }
+
+ memcpy (&result, &evaspixmapsink->render_rect, sizeof (GstVideoRectangle));
+
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+
+ /* We calculate scaling using the original video frames geometry to include
+ pixel aspect ratio scaling. */
+ xscale = (gdouble) evaspixmapsink->video_width / result.w;
+ yscale = (gdouble) evaspixmapsink->video_height / result.h;
+
+ /* Converting pointer coordinates to the non scaled geometry */
+ if (gst_structure_get_double (structure, "pointer_x", &x)) {
+ x = MIN (x, result.x + result.w);
+ x = MAX (x - result.x, 0);
+ gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
+ (gdouble) x * xscale, NULL);
+ }
+ if (gst_structure_get_double (structure, "pointer_y", &y)) {
+ y = MIN (y, result.y + result.h);
+ y = MAX (y - result.y, 0);
+ gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
+ (gdouble) y * yscale, NULL);
+ }
+
+ gst_pad_send_event (peer, event);
+ gst_object_unref (peer);
+ }
+}
+
+static void
+gst_evaspixmapsink_navigation_init (GstNavigationInterface *iface)
+{
+ iface->send_event = gst_evaspixmapsink_navigation_send_event;
+}
+
+static const GList*
+gst_evaspixmapsink_colorbalance_list_channels (GstColorBalance *balance)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (balance);
+
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), NULL);
+
+ if (evaspixmapsink->xcontext)
+ return evaspixmapsink->xcontext->channels_list;
+ else
+ return NULL;
+}
+
+static void
+gst_evaspixmapsink_colorbalance_set_value (GstColorBalance *balance, GstColorBalanceChannel *channel, gint value)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (balance);
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink));
+ g_return_if_fail (channel->label != NULL);
+
+ evaspixmapsink->cb_changed = TRUE;
+
+ /* Normalize val to [-1000, 1000] */
+ value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
+ (double) (channel->max_value - channel->min_value));
+
+ if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
+ evaspixmapsink->hue = value;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
+ evaspixmapsink->saturation = value;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
+ evaspixmapsink->contrast = value;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
+ evaspixmapsink->brightness = value;
+ } else {
+ g_warning ("got an unknown channel %s", channel->label);
+ return;
+ }
+
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+}
+
+static gint
+gst_evaspixmapsink_colorbalance_get_value (GstColorBalance *balance, GstColorBalanceChannel *channel)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (balance);
+ gint value = 0;
+
+ g_return_val_if_fail (GST_IS_EVASPIXMAPSINK (evaspixmapsink), 0);
+ g_return_val_if_fail (channel->label != NULL, 0);
+
+ if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
+ value = evaspixmapsink->hue;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
+ value = evaspixmapsink->saturation;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
+ value = evaspixmapsink->contrast;
+ } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
+ value = evaspixmapsink->brightness;
+ } else {
+ g_warning ("got an unknown channel %s", channel->label);
+ }
+
+ /* Normalize val to [channel->min_value, channel->max_value] */
+ value = channel->min_value + (channel->max_value - channel->min_value) * (value + 1000) / 2000;
+
+ return value;
+}
+
+static void
+gst_evaspixmapsink_colorbalance_init (GstColorBalanceClass *iface)
+{
+ GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
+ iface->list_channels = gst_evaspixmapsink_colorbalance_list_channels;
+ iface->set_value = gst_evaspixmapsink_colorbalance_set_value;
+ iface->get_value = gst_evaspixmapsink_colorbalance_get_value;
+}
+
+static const GList *
+gst_evaspixmapsink_probe_get_properties (GstPropertyProbe *probe)
+{
+ GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
+ static GList *list = NULL;
+
+ if (!list) {
+ list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
+ list = g_list_append (list, g_object_class_find_property (klass, "autopaint-colorkey"));
+ list = g_list_append (list, g_object_class_find_property (klass, "double-buffer"));
+ list = g_list_append (list, g_object_class_find_property (klass, "colorkey"));
+ }
+
+ return list;
+}
+
+static void
+gst_evaspixmapsink_probe_probe_property (GstPropertyProbe *probe, guint prop_id, const GParamSpec *pspec)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (probe);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ case PROP_AUTOPAINT_COLORKEY:
+ case PROP_DOUBLE_BUFFER:
+ case PROP_COLORKEY:
+ GST_DEBUG_OBJECT (evaspixmapsink,"probing device list and get capabilities");
+ if (!evaspixmapsink->xcontext) {
+ GST_DEBUG_OBJECT (evaspixmapsink,"generating xcontext");
+ evaspixmapsink->xcontext = gst_evaspixmapsink_xcontext_get (evaspixmapsink);
+ if (!evaspixmapsink->xcontext) {
+ GST_ERROR_OBJECT (evaspixmapsink,"could not get xcontext..");
+ }
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_evaspixmapsink_probe_needs_probe (GstPropertyProbe *probe, guint prop_id, const GParamSpec *pspec)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (probe);
+ gboolean ret = FALSE;
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ case PROP_AUTOPAINT_COLORKEY:
+ case PROP_DOUBLE_BUFFER:
+ case PROP_COLORKEY:
+ if (evaspixmapsink->xcontext != NULL) {
+ ret = FALSE;
+ } else {
+ ret = TRUE;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
+ break;
+ }
+
+ return ret;
+}
+
+static GValueArray *
+gst_evaspixmapsink_probe_get_values (GstPropertyProbe *probe, guint prop_id, const GParamSpec *pspec)
+{
+ GstEvasPixmapSink *evaspixmapsink = GST_EVASPIXMAPSINK (probe);
+ GValueArray *array = NULL;
+
+ if (G_UNLIKELY (!evaspixmapsink->xcontext)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"we don't have any xcontext, can't "
+ "get values");
+ goto beach;
+ }
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ {
+ guint i;
+ GValue value = { 0 };
+
+ array = g_value_array_new (evaspixmapsink->xcontext->nb_adaptors);
+ g_value_init (&value, G_TYPE_STRING);
+
+ for (i = 0; i < evaspixmapsink->xcontext->nb_adaptors; i++) {
+ gchar *adaptor_id_s = g_strdup_printf ("%u", i);
+
+ g_value_set_string (&value, adaptor_id_s);
+ g_value_array_append (array, &value);
+ g_free (adaptor_id_s);
+ }
+ g_value_unset (&value);
+ break;
+ }
+ case PROP_AUTOPAINT_COLORKEY:
+ if (evaspixmapsink->have_autopaint_colorkey) {
+ GValue value = { 0 };
+
+ array = g_value_array_new (2);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, FALSE);
+ g_value_array_append (array, &value);
+ g_value_set_boolean (&value, TRUE);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+ }
+ break;
+ case PROP_DOUBLE_BUFFER:
+ if (evaspixmapsink->have_double_buffer) {
+ GValue value = { 0 };
+
+ array = g_value_array_new (2);
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, FALSE);
+ g_value_array_append (array, &value);
+ g_value_set_boolean (&value, TRUE);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+ }
+ break;
+ case PROP_COLORKEY:
+ if (evaspixmapsink->have_colorkey) {
+ GValue value = { 0 };
+
+ array = g_value_array_new (1);
+ g_value_init (&value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (&value, 0, 0xffffff);
+ g_value_array_append (array, &value);
+ g_value_unset (&value);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
+ break;
+ }
+
+beach:
+ return array;
+}
+
+static void
+gst_evaspixmapsink_property_probe_interface_init (GstPropertyProbeInterface *iface)
+{
+ iface->get_properties = gst_evaspixmapsink_probe_get_properties;
+ iface->probe_property = gst_evaspixmapsink_probe_probe_property;
+ iface->needs_probe = gst_evaspixmapsink_probe_needs_probe;
+ iface->get_values = gst_evaspixmapsink_probe_get_values;
+}
+
+static gboolean
+gst_evaspixmapsink_xpixmap_link (GstEvasPixmapSink *evaspixmapsink)
+{
+ Display *dpy;
+ Pixmap *pixmap_id[NUM_OF_PIXMAP];
+ int evas_object_width = 0;
+ int evas_object_height = 0;
+ int pixmap_width = 0;
+ int pixmap_height = 0;
+ unsigned int xw = 0;
+ unsigned int xh = 0;
+ int i = 0;
+ XGCValues gc_values_black;
+ gc_values_black.foreground = 0xff000000;
+#ifdef DUMP_IMG
+ int ret = 0;
+ char file_name[128];
+ char *dump_data;
+ int dump_w = 0;
+#endif
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START]");
+
+ if (!evaspixmapsink) {
+ GST_ERROR_OBJECT (evaspixmapsink,"could not get evaspixmapsink..");
+ return FALSE;
+ }
+ g_mutex_lock (evaspixmapsink->flow_lock);
+
+ /* Set xcontext and display */
+ if (!evaspixmapsink->xcontext) {
+ GST_WARNING_OBJECT (evaspixmapsink,"there's no xcontext, try to get one..");
+ evaspixmapsink->xcontext = gst_evaspixmapsink_xcontext_get (evaspixmapsink);
+ if (!evaspixmapsink->xcontext) {
+ GST_ERROR_OBJECT (evaspixmapsink,"could not get xcontext..");
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return FALSE;
+ }
+ }
+
+ /* Set evas image object size */
+ evas_object_geometry_get(evaspixmapsink->eo, NULL, NULL, &evas_object_width, &evas_object_height);
+
+ /* check if it is redundant request */
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]) {
+ if (!evaspixmapsink->use_origin_size && evaspixmapsink->xpixmap[i]->width && evaspixmapsink->xpixmap[i]->height) {
+ if (evaspixmapsink->xpixmap[i]->width == evas_object_width && evaspixmapsink->xpixmap[i]->height == evas_object_height) {
+ GST_WARNING_OBJECT (evaspixmapsink,"pixmap was already created(w:%d, h:%d), skip it..",
+ evaspixmapsink->xpixmap[i]->width, evaspixmapsink->xpixmap[i]->height);
+ if (evaspixmapsink->need_to_fill_black) {
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XFillRectangle (evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->pixmap, evaspixmapsink->xpixmap[i]->gc,
+ 0, 0, evaspixmapsink->xpixmap[i]->width, evaspixmapsink->xpixmap[i]->height);
+ GST_LOG_OBJECT (evaspixmapsink,"fill black to xpixmap[%d] with size(w:%d,h:%d)", evaspixmapsink->xpixmap[i]->pixmap,
+ evaspixmapsink->xpixmap[i]->width, evaspixmapsink->xpixmap[i]->height);
+#ifdef DUMP_IMG
+ dump_w = evaspixmapsink->xpixmap[i]->width + (32 - (evaspixmapsink->xpixmap[i]->width%32)); // for multiples of 32
+ if(g_cnt<100)
+ {
+ evaspixmapsink->pixmap_addr[i] = gst_evaspixmapsink_get_buffer(evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->pixmap, dump_w, evaspixmapsink->xpixmap[i]->height, i);
+ GST_ERROR("<DUMP_%2.2d> pixmap[%d] addr %p (w : %d, h : %d)", g_cnt, i, evaspixmapsink->pixmap_addr[i], dump_w, evaspixmapsink->xpixmap[i]->height);
+ sprintf(file_name, "/opt/usr/media/DUMP_%2.2d.dump", g_cnt);
+
+ dump_data = g_malloc(dump_w * evaspixmapsink->xpixmap[i]->height * 4);
+ memcpy (dump_data, evaspixmapsink->pixmap_addr[i], dump_w * evaspixmapsink->xpixmap[i]->height * 4);
+
+ ret = util_write_rawdata(file_name, dump_data, dump_w * evaspixmapsink->xpixmap[i]->height * 4);
+ if (ret) {
+ GST_ERROR_OBJECT (evaspixmapsink, "util_write_rawdata() failed");
+ }
+ else
+ g_cnt++;
+ g_free(dump_data);
+
+ if (g_bufinfo[i]) {
+ gst_evaspixmapsink_free_buffer (i);
+ g_bufinfo[i] = NULL;
+ }
+ evaspixmapsink->pixmap_addr[i] = NULL;
+ }
+#endif
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ }
+ evaspixmapsink->need_to_fill_black = FALSE;
+ }
+ goto SKIP_LINK;
+ }
+ }
+ }
+ }
+
+ dpy = evaspixmapsink->xcontext->disp;
+
+ if (evaspixmapsink->use_origin_size || !evas_object_width || !evas_object_height) {
+ pixmap_width = evaspixmapsink->video_width;
+ pixmap_height = evaspixmapsink->video_height;
+ GST_INFO_OBJECT (evaspixmapsink,"set size to media src size(%dx%d)", pixmap_width, pixmap_height);
+ }
+
+ g_mutex_lock (evaspixmapsink->x_lock);
+ evaspixmapsink->sizediff_width = 0;
+ evaspixmapsink->sizediff_height = 0;
+ if (evaspixmapsink->use_origin_size || !evas_object_width || !evas_object_height) {
+ XvQueryBestSize(dpy, evaspixmapsink->xcontext->xv_port_id,0,0,0, pixmap_width, pixmap_height, &xw, &xh);
+ if (!evas_object_width || !evas_object_height) {
+ evaspixmapsink->w = xw;
+ evaspixmapsink->h = xh;
+ } else {
+ evaspixmapsink->w = evas_object_width;
+ evaspixmapsink->h = evas_object_height;
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink,"XvQueryBestSize : xv_port_id(%d), w(%d),h(%d) => xw(%d),xh(%d)", evaspixmapsink->xcontext->xv_port_id, pixmap_width, pixmap_height, xw, xh);
+ } else {
+ XvQueryBestSize(dpy, evaspixmapsink->xcontext->xv_port_id,0,0,0, evas_object_width, evas_object_height, &xw, &xh);
+ GST_DEBUG_OBJECT (evaspixmapsink,"XvQueryBestSize : xv_port_id(%d), w(%d),h(%d) => xw(%d),xh(%d)", evaspixmapsink->xcontext->xv_port_id, evas_object_width, evas_object_height, xw, xh);
+ evaspixmapsink->w = xw;
+ evaspixmapsink->h = xh;
+ /* update difference of size information (between evas image object's and pixmap's) */
+ evaspixmapsink->sizediff_width = xw - evas_object_width;
+ evaspixmapsink->sizediff_height = xh - evas_object_height;
+ }
+
+ /* create xpixmap structure */
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (!evaspixmapsink->xpixmap[i]) {
+ /* xpixmap can be created in this function only */
+ evaspixmapsink->xpixmap[i] = g_new0 (GstXPixmap, 1);
+ if(!evaspixmapsink->xpixmap[i]) {
+ GST_ERROR_OBJECT (evaspixmapsink,"xpixmap is not valid..");
+ int j = 0;
+ for (j = 0; j < i; j++) {
+ g_free(evaspixmapsink->xpixmap[j]);
+ }
+ goto GO_OUT_OF_FUNC;
+ }
+ }
+ }
+
+ /* create pixmap */
+ if (!xw || !xh) {
+ GST_WARNING_OBJECT (evaspixmapsink,"skip creating pixmap..xw(%d),xh(%d)",xw,xh);
+ goto GO_OUT_OF_FUNC;
+ } else {
+ /* multiple pixmaps creation */
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ pixmap_id[i] = XCreatePixmap(dpy, DefaultRootWindow(dpy), xw, xh, DefaultDepth(dpy, DefaultScreen(dpy)));
+ if ( (int)pixmap_id[i] == BadAlloc || (int)pixmap_id[i] == BadDrawable || (int)pixmap_id[i] == BadValue ) {
+ GST_ERROR_OBJECT (evaspixmapsink,"pixmap[%d] allocation error..", i);
+ int j = 0;
+ for (j = 0; j < i; j++) {
+ XFreePixmap(dpy, pixmap_id[j]);
+ }
+ goto GO_OUT_OF_FUNC;
+ }
+ evaspixmapsink->need_to_fill_black = FALSE;
+ GST_INFO_OBJECT (evaspixmapsink,"creation pixmap_id[%d]:%d success", i, pixmap_id[i]);
+ GST_DEBUG_OBJECT (evaspixmapsink,"evas_object_width(%d),evas_object_height(%d),pixmap:%d,depth:%d",
+ evas_object_width,evas_object_height,pixmap_id[i],DefaultDepth(dpy, DefaultScreen(dpy)));
+ }
+ }
+
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]->pixmap && pixmap_id[i] != evaspixmapsink->xpixmap[i]->pixmap) {
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_mutex_lock (evaspixmapsink->pixmap_ref_lock);
+
+ /* If we reset another pixmap, do below */
+ if (evaspixmapsink->xpixmap[i]->prev_pixmap) {
+ GST_LOG_OBJECT (evaspixmapsink,"Free pixmap(%d), gc(%x), destroy previous damage(%d)",
+ evaspixmapsink->xpixmap[i]->prev_pixmap, evaspixmapsink->xpixmap[i]->prev_gc, evaspixmapsink->prev_damage[i]);
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XFreePixmap(evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->prev_pixmap);
+ XFreeGC (evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->prev_gc);
+ XDamageDestroy(evaspixmapsink->xcontext->disp, evaspixmapsink->prev_damage[i]);
+ XSync(evaspixmapsink->xcontext->disp, FALSE);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ evaspixmapsink->prev_damage[i] = NULL;
+ }
+ evaspixmapsink->xpixmap[i]->prev_pixmap = evaspixmapsink->xpixmap[i]->pixmap;
+ evaspixmapsink->xpixmap[i]->prev_gc = evaspixmapsink->xpixmap[i]->gc;
+ evaspixmapsink->xpixmap[i]->pixmap = 0;
+ evaspixmapsink->xpixmap[i]->ref = 0;
+ evaspixmapsink->xpixmap[i]->damaged_time = 0;
+
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+ g_mutex_lock (evaspixmapsink->x_lock);
+ }
+ }
+
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ /* Set pixmap id and create GC */
+ evaspixmapsink->xpixmap[i]->pixmap = pixmap_id[i];
+ evaspixmapsink->xpixmap[i]->gc = XCreateGC(dpy, evaspixmapsink->xpixmap[i]->pixmap, GCForeground, &gc_values_black);
+ evaspixmapsink->xpixmap[i]->width = xw;
+ evaspixmapsink->xpixmap[i]->height = xh;
+
+ /* Create XDamage */
+ if (evaspixmapsink->damage[i]) {
+ evaspixmapsink->prev_damage[i] = evaspixmapsink->damage[i];
+ }
+ evaspixmapsink->damage[i] = XDamageCreate (dpy, evaspixmapsink->xpixmap[i]->pixmap, XDamageReportRawRectangles);
+
+ GST_WARNING_OBJECT (evaspixmapsink,"xpixmap[%d]->(pixmap:%d,gc:%p), damage[%d]:%d",
+ i, evaspixmapsink->xpixmap[i]->pixmap, evaspixmapsink->xpixmap[i]->gc, i, evaspixmapsink->damage[i]);
+
+ /* Fill blackcolor for the new pixmap */
+ XFillRectangle (evaspixmapsink->xcontext->disp, pixmap_id[i], evaspixmapsink->xpixmap[i]->gc,
+ 0, 0, evaspixmapsink->w, evaspixmapsink->h);
+ GST_LOG_OBJECT (evaspixmapsink,"fill black to xpixmap[%d] with size(w:%d,h:%d)", pixmap_id[i],
+ evaspixmapsink->w, evaspixmapsink->h);
+
+#ifdef DUMP_IMG
+ dump_w = evaspixmapsink->w + (32 - (evaspixmapsink->w%32)); // for multiples of 32
+ if(g_cnt<100)
+ {
+ evaspixmapsink->pixmap_addr[i] = gst_evaspixmapsink_get_buffer(evaspixmapsink->xcontext->disp, evaspixmapsink->xpixmap[i]->pixmap, dump_w, evaspixmapsink->h, i);
+ GST_ERROR("<DUMP_%2.2d> pixmap[%d] addr %p (w : %d, h : %d)", g_cnt, i, evaspixmapsink->pixmap_addr[i], dump_w, evaspixmapsink->h);
+ sprintf(file_name, "/opt/usr/media/DUMP_%2.2d.dump", g_cnt);
+
+ dump_data = g_malloc(dump_w * evaspixmapsink->h * 4);
+ memcpy (dump_data, evaspixmapsink->pixmap_addr[i], dump_w * evaspixmapsink->h * 4);
+
+ ret = util_write_rawdata(file_name, dump_data, dump_w * evaspixmapsink->h * 4);
+ if (ret) {
+ GST_ERROR_OBJECT (evaspixmapsink, "util_write_rawdata() failed");
+ }
+ else
+ g_cnt++;
+ g_free(dump_data);
+
+ if (g_bufinfo[i]) {
+ gst_evaspixmapsink_free_buffer (i);
+ g_bufinfo[i] = NULL;
+ }
+ evaspixmapsink->pixmap_addr[i] = NULL;
+ }
+#endif
+ }
+
+ XSync(dpy, FALSE);
+
+ /* Set flag for mapping evas object with xpixmap */
+ evaspixmapsink->do_link = TRUE;
+ if (evaspixmapsink->epipe_request_count > EPIPE_REQUEST_LIMIT) {
+ GST_WARNING_OBJECT (evaspixmapsink,"epipe_request_count(%d), skip ecore_pipe_write()", evaspixmapsink->epipe_request_count);
+ } else {
+ ecore_pipe_write(evaspixmapsink->epipe, evaspixmapsink, sizeof(GstEvasPixmapSink));
+ evaspixmapsink->epipe_request_count++;
+ }
+
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+
+ g_mutex_unlock (evaspixmapsink->x_lock);
+
+
+SKIP_LINK:
+ g_mutex_lock (evaspixmapsink->pixmap_ref_lock);
+ evaspixmapsink->last_updated_idx = -1;
+ g_mutex_unlock (evaspixmapsink->pixmap_ref_lock);
+
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+
+ return TRUE;
+
+GO_OUT_OF_FUNC:
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ g_mutex_unlock (evaspixmapsink->flow_lock);
+ return FALSE;
+}
+
+static void
+gst_evaspixmapsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (object));
+ evaspixmapsink = GST_EVASPIXMAPSINK (object);
+
+ switch (prop_id) {
+ case PROP_HUE:
+ evaspixmapsink->hue = g_value_get_int (value);
+ evaspixmapsink->cb_changed = TRUE;
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+ break;
+ case PROP_CONTRAST:
+ evaspixmapsink->contrast = g_value_get_int (value);
+ evaspixmapsink->cb_changed = TRUE;
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+ break;
+ case PROP_BRIGHTNESS:
+ evaspixmapsink->brightness = g_value_get_int (value);
+ evaspixmapsink->cb_changed = TRUE;
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+ break;
+ case PROP_SATURATION:
+ evaspixmapsink->saturation = g_value_get_int (value);
+ evaspixmapsink->cb_changed = TRUE;
+ gst_evaspixmapsink_update_colorbalance (evaspixmapsink);
+ break;
+ case PROP_DISPLAY:
+ evaspixmapsink->display_name = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_SYNCHRONOUS:
+ evaspixmapsink->synchronous = g_value_get_boolean (value);
+ if (evaspixmapsink->xcontext) {
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XSynchronize (evaspixmapsink->xcontext->disp, evaspixmapsink->synchronous);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ GST_DEBUG_OBJECT (evaspixmapsink,"XSynchronize called with %s", evaspixmapsink->synchronous ? "TRUE" : "FALSE");
+ }
+ break;
+ case PROP_PIXEL_ASPECT_RATIO:
+ g_free (evaspixmapsink->par);
+ evaspixmapsink->par = g_new0 (GValue, 1);
+ g_value_init (evaspixmapsink->par, GST_TYPE_FRACTION);
+ if (!g_value_transform (value, evaspixmapsink->par)) {
+ g_warning ("Could not transform string to aspect ratio");
+ gst_value_set_fraction (evaspixmapsink->par, 1, 1);
+ }
+ GST_DEBUG_OBJECT (evaspixmapsink,"set PAR to %d/%d", gst_value_get_fraction_numerator (evaspixmapsink->par), gst_value_get_fraction_denominator (evaspixmapsink->par));
+ break;
+ case PROP_DEVICE:
+ evaspixmapsink->adaptor_no = atoi (g_value_get_string (value));
+ break;
+ case PROP_DOUBLE_BUFFER:
+ evaspixmapsink->double_buffer = g_value_get_boolean (value);
+ break;
+ case PROP_AUTOPAINT_COLORKEY:
+ evaspixmapsink->autopaint_colorkey = g_value_get_boolean (value);
+ break;
+ case PROP_COLORKEY:
+ evaspixmapsink->colorkey = g_value_get_int (value);
+ break;
+ case PROP_PIXMAP_WIDTH:
+ {
+ /* To do : code related to pixmap re-link */
+ GST_LOG_OBJECT (evaspixmapsink, "Not supported");
+ break;
+ }
+ case PROP_PIXMAP_HEIGHT:
+ {
+ /* To do : code related to pixmap re-link */
+ GST_LOG_OBJECT (evaspixmapsink, "Not supported");
+ break;
+ }
+ case PROP_DISPLAY_GEOMETRY_METHOD:
+ {
+ guint new_val = g_value_get_enum (value);
+ if (evaspixmapsink->display_geometry_method != new_val) {
+ evaspixmapsink->display_geometry_method = new_val;
+ GST_INFO_OBJECT (evaspixmapsink,"Overlay geometry method update, display_geometry_method(%d)",evaspixmapsink->display_geometry_method);
+ if( evaspixmapsink->display_geometry_method != DISP_GEO_METHOD_FULL_SCREEN &&
+ evaspixmapsink->display_geometry_method != DISP_GEO_METHOD_CROPPED_FULL_SCREEN ) {
+ if( evaspixmapsink->xcontext ) {
+ g_mutex_lock( evaspixmapsink->flow_lock );
+ int i = 0;
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]) {
+ gst_evaspixmapsink_xpixmap_clear (evaspixmapsink, evaspixmapsink->xpixmap[i]);
+ }
+ }
+ g_mutex_unlock( evaspixmapsink->flow_lock );
+ }
+ }
+ if (evaspixmapsink->xcontext) {
+ gst_evaspixmap_buffer_put (evaspixmapsink, evaspixmapsink->evas_pixmap_buf);
+ }
+ }
+ break;
+ }
+ case PROP_DST_ROI_X:
+ evaspixmapsink->dst_roi.x = g_value_get_int (value);
+ GST_INFO_OBJECT (evaspixmapsink, "ROI_X(%d)",evaspixmapsink->dst_roi.x );
+ break;
+ case PROP_DST_ROI_Y:
+ evaspixmapsink->dst_roi.y = g_value_get_int (value);
+ GST_INFO_OBJECT (evaspixmapsink, "ROI_Y(%d)",evaspixmapsink->dst_roi.y );
+ break;
+ case PROP_DST_ROI_W:
+ evaspixmapsink->dst_roi.w = g_value_get_int (value);
+ GST_INFO_OBJECT (evaspixmapsink, "ROI_W(%d)",evaspixmapsink->dst_roi.w );
+ break;
+ case PROP_DST_ROI_H:
+ evaspixmapsink->dst_roi.h = g_value_get_int (value);
+ GST_INFO_OBJECT (evaspixmapsink, "ROI_H(%d)",evaspixmapsink->dst_roi.h );
+ break;
+ case PROP_STOP_VIDEO:
+ evaspixmapsink->stop_video = g_value_get_int (value);
+ g_mutex_lock( evaspixmapsink->flow_lock );
+ if( evaspixmapsink->stop_video ) {
+ GST_INFO_OBJECT (evaspixmapsink, "XPixmap CLEAR when set video-stop property" );
+ int i = 0;
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]) {
+ gst_evaspixmapsink_xpixmap_clear (evaspixmapsink, evaspixmapsink->xpixmap[i]);
+ }
+ }
+ }
+ GST_INFO_OBJECT (evaspixmapsink, "video-stop property(%d)", evaspixmapsink->stop_video);
+
+ g_mutex_unlock( evaspixmapsink->flow_lock );
+ break;
+ case PROP_EVAS_OBJECT:
+ {
+ Evas_Object *eo = g_value_get_pointer (value);
+ if ( is_evas_image_object (eo)) {
+ if (!evaspixmapsink->epipe) {
+ evaspixmapsink->epipe = ecore_pipe_add (ecore_pipe_callback_handler, evaspixmapsink);
+ if (!evaspixmapsink->epipe) {
+ GST_ERROR_OBJECT (evaspixmapsink,"Cannot set evas-object property: ecore_pipe_add() failed");
+ break;
+ }
+ }
+ if (evaspixmapsink->eo == NULL) {
+ evaspixmapsink->eo = eo;
+ /* add evas object callbacks on a new evas image object */
+ EVASPIXMAPSINK_SET_EVAS_OBJECT_EVENT_CALLBACK (eo, evaspixmapsink);
+ if (GST_STATE(evaspixmapsink) < GST_STATE_PAUSED) {
+ GST_INFO_OBJECT (evaspixmapsink,"It's the first time the new evas image object(%x) is set, skip gst_evaspixmapsink_xpixmap_link()..", eo);
+ break;
+ } else {
+ if (!gst_evaspixmapsink_xpixmap_link(evaspixmapsink)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"link evas image object with pixmap failed...");
+ evaspixmapsink->eo = NULL;
+ break;
+ }
+ }
+ }
+ if (eo == evaspixmapsink->eo) {
+ GST_LOG_OBJECT (evaspixmapsink,"new evas image object(%x) is same as the previous one(%x)", eo, evaspixmapsink->eo);
+ } else {
+ GST_INFO_OBJECT (evaspixmapsink,"new evas image object(%x), previous one(%x)", eo, evaspixmapsink->eo);
+ /* delete evas object callbacks registrated on a former evas image object */
+ EVASPIXMAPSINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK (evaspixmapsink->eo);
+ evaspixmapsink->eo = eo;
+ if (!gst_evaspixmapsink_xpixmap_link(evaspixmapsink)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"link evas image object with pixmap failed...");
+ evaspixmapsink->eo = NULL;
+ break;
+ }
+ /* add evas object callbacks on a new evas image object */
+ EVASPIXMAPSINK_SET_EVAS_OBJECT_EVENT_CALLBACK (eo, evaspixmapsink);
+ }
+ GST_INFO_OBJECT (evaspixmapsink,"Evas image object(%x) is set", evaspixmapsink->eo);
+ } else {
+ GST_ERROR_OBJECT (evaspixmapsink,"Cannot set evas-object property: value is not an evas image object");
+ }
+ break;
+ }
+ case PROP_FLIP:
+ evaspixmapsink->flip = g_value_get_enum(value);
+ break;
+ case PROP_ROTATE_ANGLE:
+ evaspixmapsink->rotate_angle = g_value_get_enum (value);
+ break;
+ case PROP_VISIBLE:
+ {
+ Eina_Bool r = EINA_FALSE;
+ gboolean visible = g_value_get_boolean (value);
+ GST_INFO_OBJECT (evaspixmapsink,"evaspixmapsink->visible(%d), new value of visible(%d)", evaspixmapsink->visible, visible);
+ if (evaspixmapsink->visible != visible) {
+ evaspixmapsink->visible = visible;
+ if (evaspixmapsink->eo) {
+ evaspixmapsink->update_visibility = UPDATE_TRUE;
+ if (evaspixmapsink->epipe_request_count > EPIPE_REQUEST_LIMIT) {
+ GST_WARNING_OBJECT (evaspixmapsink,"skip ecore_pipe_write()", evaspixmapsink->epipe_request_count);
+ if (visible) {
+ evas_object_show(evaspixmapsink->eo);
+ GST_WARNING_OBJECT (evaspixmapsink, "object show (forcely)");
+ }
+ } else {
+ r = ecore_pipe_write (evaspixmapsink->epipe, &evaspixmapsink->update_visibility, SIZE_FOR_UPDATE_VISIBILITY);
+ if (r == EINA_FALSE) {
+ GST_WARNING ("Failed to ecore_pipe_write() for updating visibility)\n");
+ }
+ evaspixmapsink->epipe_request_count++;
+ }
+
+ if (!visible) {
+ int i = 0;
+ g_mutex_lock( evaspixmapsink->flow_lock );
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if (evaspixmapsink->xpixmap[i]) {
+ gst_evaspixmapsink_xpixmap_clear (evaspixmapsink, evaspixmapsink->xpixmap[i]);
+ }
+ }
+ evas_object_hide(evaspixmapsink->eo);
+ GST_INFO_OBJECT (evaspixmapsink, "object hide (forcely)");
+ g_mutex_unlock( evaspixmapsink->flow_lock );
+ } else {
+ gst_evaspixmap_buffer_put (evaspixmapsink, evaspixmapsink->evas_pixmap_buf);
+ }
+ } else {
+ GST_WARNING_OBJECT (evaspixmapsink,"evas image object was not set");
+ }
+ }
+ break;
+ }
+ case PROP_ORIGIN_SIZE:
+ evaspixmapsink->use_origin_size = g_value_get_boolean (value);
+ GST_INFO_OBJECT (evaspixmapsink,"set origin-size (%d)",evaspixmapsink->use_origin_size);
+ if (evaspixmapsink->previous_origin_size != evaspixmapsink->use_origin_size) {
+ if (!gst_evaspixmapsink_xpixmap_link(evaspixmapsink)) {
+ GST_WARNING_OBJECT (evaspixmapsink,"link evas image object with pixmap failed...");
+ }
+ evaspixmapsink->previous_origin_size = evaspixmapsink->use_origin_size;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_evaspixmapsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+
+ g_return_if_fail (GST_IS_EVASPIXMAPSINK (object));
+
+ evaspixmapsink = GST_EVASPIXMAPSINK (object);
+
+ switch (prop_id) {
+ case PROP_HUE:
+ g_value_set_int (value, evaspixmapsink->hue);
+ break;
+ case PROP_CONTRAST:
+ g_value_set_int (value, evaspixmapsink->contrast);
+ break;
+ case PROP_BRIGHTNESS:
+ g_value_set_int (value, evaspixmapsink->brightness);
+ break;
+ case PROP_SATURATION:
+ g_value_set_int (value, evaspixmapsink->saturation);
+ break;
+ case PROP_DISPLAY:
+ g_value_set_string (value, evaspixmapsink->display_name);
+ break;
+ case PROP_SYNCHRONOUS:
+ g_value_set_boolean (value, evaspixmapsink->synchronous);
+ break;
+ case PROP_PIXEL_ASPECT_RATIO:
+ if (evaspixmapsink->par) {
+ if (!g_value_transform (evaspixmapsink->par, value)) {
+ g_warning ("g_value_transform() failure");
+ }
+ }
+ break;
+ case PROP_DEVICE:
+ {
+ char *adaptor_no_s = g_strdup_printf ("%u", evaspixmapsink->adaptor_no);
+ g_value_set_string (value, adaptor_no_s);
+ g_free (adaptor_no_s);
+ break;
+ }
+ case PROP_DEVICE_NAME:
+ if (evaspixmapsink->xcontext && evaspixmapsink->xcontext->adaptors) {
+ g_value_set_string (value,
+ evaspixmapsink->xcontext->adaptors[evaspixmapsink->adaptor_no]);
+ } else {
+ g_value_set_string (value, NULL);
+ }
+ break;
+ case PROP_DOUBLE_BUFFER:
+ g_value_set_boolean (value, evaspixmapsink->double_buffer);
+ break;
+ case PROP_AUTOPAINT_COLORKEY:
+ g_value_set_boolean (value, evaspixmapsink->autopaint_colorkey);
+ break;
+ case PROP_COLORKEY:
+ g_value_set_int (value, evaspixmapsink->colorkey);
+ break;
+ case PROP_PIXMAP_WIDTH:
+ {
+ GST_LOG_OBJECT (evaspixmapsink, "Not supported");
+ break;
+ }
+ case PROP_PIXMAP_HEIGHT:
+ {
+ GST_LOG_OBJECT (evaspixmapsink, "Not supported");
+ break;
+ }
+ case PROP_DISPLAY_GEOMETRY_METHOD:
+ g_value_set_enum (value, evaspixmapsink->display_geometry_method);
+ break;
+ case PROP_DST_ROI_X:
+ g_value_set_int (value, evaspixmapsink->dst_roi.x);
+ break;
+ case PROP_DST_ROI_Y:
+ g_value_set_int (value, evaspixmapsink->dst_roi.y);
+ break;
+ case PROP_DST_ROI_W:
+ g_value_set_int (value, evaspixmapsink->dst_roi.w);
+ break;
+ case PROP_DST_ROI_H:
+ g_value_set_int (value, evaspixmapsink->dst_roi.h);
+ break;
+ case PROP_STOP_VIDEO:
+ g_value_set_int (value, evaspixmapsink->stop_video);
+ break;
+ case PROP_EVAS_OBJECT:
+ g_value_set_pointer (value, evaspixmapsink->eo);
+ break;
+ case PROP_FLIP:
+ g_value_set_enum(value, evaspixmapsink->flip);
+ break;
+ case PROP_ROTATE_ANGLE:
+ g_value_set_enum (value, evaspixmapsink->rotate_angle);
+ break;
+ case PROP_VISIBLE:
+ g_value_set_boolean (value, evaspixmapsink->visible);
+ break;
+ case PROP_ORIGIN_SIZE:
+ g_value_set_boolean (value, evaspixmapsink->use_origin_size);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_evaspixmapsink_reset (GstEvasPixmapSink *evaspixmapsink)
+{
+ GST_WARNING_OBJECT (evaspixmapsink,"[START]");
+
+ GThread *thread;
+ GST_OBJECT_LOCK (evaspixmapsink);
+ evaspixmapsink->running = FALSE;
+ int i = 0;
+
+ /* grab thread and mark it as NULL */
+ thread = evaspixmapsink->event_thread;
+ evaspixmapsink->event_thread = NULL;
+ GST_OBJECT_UNLOCK (evaspixmapsink);
+
+ /* Wait for our event thread to finish before we clean up our stuff. */
+ if (thread) {
+ g_thread_join (thread);
+ }
+
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+ if(evaspixmapsink->damage[i]) {
+ g_mutex_lock (evaspixmapsink->x_lock);
+ XDamageDestroy(evaspixmapsink->xcontext->disp, evaspixmapsink->damage[i]);
+ g_mutex_unlock (evaspixmapsink->x_lock);
+ evaspixmapsink->damage[i] = NULL;
+ }
+ }
+ EVASPIXMAPSINK_UNSET_EVAS_OBJECT_EVENT_CALLBACK( evaspixmapsink->eo );
+
+ if (evaspixmapsink->evas_pixmap_buf) {
+ gst_buffer_unref (GST_BUFFER_CAST (evaspixmapsink->evas_pixmap_buf));
+ evaspixmapsink->evas_pixmap_buf = NULL;
+ }
+
+ for (i = 0; i < evaspixmapsink->num_of_pixmaps; i++) {
+#ifdef DUMP_IMG
+ if (g_bufinfo[i]) {
+ gst_evaspixmapsink_free_buffer (i);
+ g_bufinfo[i] = NULL;
+ }
+ evaspixmapsink->pixmap_addr[i] = NULL;
+#endif
+ gst_evaspixmapsink_xpixmap_clear (evaspixmapsink, evaspixmapsink->xpixmap[i]);
+ gst_evaspixmapsink_xpixmap_destroy (evaspixmapsink, evaspixmapsink->xpixmap[i]);
+ evaspixmapsink->xpixmap[i] = NULL;
+ }
+ if (evaspixmapsink->eo) {
+ evas_object_image_native_surface_set(evaspixmapsink->eo, NULL);
+ evas_object_image_data_set(evaspixmapsink->eo, NULL);
+ evaspixmapsink->eo = NULL;
+ }
+
+ if (evaspixmapsink->epipe) {
+ ecore_pipe_del (evaspixmapsink->epipe);
+ evaspixmapsink->epipe = NULL;
+ }
+
+ evaspixmapsink->render_rect.x = evaspixmapsink->render_rect.y =
+ evaspixmapsink->render_rect.w = evaspixmapsink->render_rect.h = 0;
+ evaspixmapsink->have_render_rect = FALSE;
+
+ evaspixmapsink->epipe_request_count = 0;
+
+ gst_evaspixmapsink_xcontext_clear (evaspixmapsink);
+
+ GST_WARNING_OBJECT (evaspixmapsink,"[END]");
+}
+
+/* Finalize is called only once, dispose can be called multiple times.
+ * We use mutexes and don't reset stuff to NULL here so let's register
+ * as a finalize. */
+static void
+gst_evaspixmapsink_finalize (GObject *object)
+{
+ GstEvasPixmapSink *evaspixmapsink;
+ evaspixmapsink = GST_EVASPIXMAPSINK (object);
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START]");
+
+ if (evaspixmapsink->display_name) {
+ g_free (evaspixmapsink->display_name);
+ evaspixmapsink->display_name = NULL;
+ }
+ if (evaspixmapsink->par) {
+ g_free (evaspixmapsink->par);
+ evaspixmapsink->par = NULL;
+ }
+ if (evaspixmapsink->x_lock) {
+ g_mutex_free (evaspixmapsink->x_lock);
+ evaspixmapsink->x_lock = NULL;
+ }
+ if (evaspixmapsink->flow_lock) {
+ g_mutex_free (evaspixmapsink->flow_lock);
+ evaspixmapsink->flow_lock = NULL;
+ }
+ if (evaspixmapsink->pixmap_ref_lock) {
+ g_mutex_free (evaspixmapsink->pixmap_ref_lock);
+ evaspixmapsink->pixmap_ref_lock = NULL;
+ }
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+
+}
+
+static void
+gst_evaspixmapsink_init (GstEvasPixmapSink *evaspixmapsink)
+{
+ int i = 0;
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[START]");
+
+ evaspixmapsink->display_name = NULL;
+ evaspixmapsink->adaptor_no = 0;
+ evaspixmapsink->xcontext = NULL;
+
+ for (i = 0; i < NUM_OF_PIXMAP; i++) {
+ evaspixmapsink->xpixmap[i] = NULL;
+ evaspixmapsink->damage[i] = 0;
+#ifdef DUMP_IMG
+ g_bufinfo[i] = NULL;
+ evaspixmapsink->pixmap_addr[i] = NULL;
+#endif
+ }
+
+ evaspixmapsink->evas_pixmap_buf = NULL;
+
+ evaspixmapsink->hue = evaspixmapsink->saturation = 0;
+ evaspixmapsink->contrast = evaspixmapsink->brightness = 0;
+ evaspixmapsink->cb_changed = FALSE;
+
+ evaspixmapsink->fps_n = 0;
+ evaspixmapsink->fps_d = 0;
+ evaspixmapsink->video_width = 0;
+ evaspixmapsink->video_height = 0;
+ evaspixmapsink->need_to_fill_black = FALSE;
+
+ evaspixmapsink->x_lock = g_mutex_new ();
+ evaspixmapsink->flow_lock = g_mutex_new ();
+ evaspixmapsink->pixmap_ref_lock = g_mutex_new();
+
+ evaspixmapsink->synchronous = FALSE;
+ evaspixmapsink->double_buffer = TRUE;
+ evaspixmapsink->par = NULL;
+ evaspixmapsink->autopaint_colorkey = TRUE;
+ evaspixmapsink->running = FALSE;
+
+ /* on 16bit displays this becomes r,g,b = 1,2,3
+ * on 24bit displays this becomes r,g,b = 8,8,16
+ * as a port atom value
+ */
+ evaspixmapsink->colorkey = (8 << 16) | (8 << 8) | 16;
+
+ evaspixmapsink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
+ evaspixmapsink->dst_roi.x = 0;
+ evaspixmapsink->dst_roi.y = 0;
+ evaspixmapsink->dst_roi.w = 0;
+ evaspixmapsink->dst_roi.h = 0;
+ evaspixmapsink->scr_w = 0;
+ evaspixmapsink->scr_h = 0;
+ evaspixmapsink->aligned_width = 0;
+ evaspixmapsink->aligned_height = 0;
+ evaspixmapsink->stop_video = FALSE;
+ evaspixmapsink->eo = NULL;
+ evaspixmapsink->epipe = NULL;
+ evaspixmapsink->epipe_request_count = 0;
+ evaspixmapsink->do_link = FALSE;
+ evaspixmapsink->flip = DEF_DISPLAY_FLIP;
+ evaspixmapsink->rotate_angle = DEGREE_0;
+ evaspixmapsink->visible = TRUE;
+ evaspixmapsink->update_visibility = UPDATE_FALSE;
+ evaspixmapsink->use_origin_size = FALSE;
+ evaspixmapsink->previous_origin_size = FALSE;
+
+ evaspixmapsink->num_of_pixmaps = NUM_OF_PIXMAP;
+
+ evaspixmapsink->buf_shared_type = BUF_SHARE_METHOD_NONE;
+
+ memset(&evaspixmapsink->src_prev, 0, sizeof (GstVideoRectangle));
+ memset(&evaspixmapsink->result_prev, 0, sizeof (GstVideoRectangle));
+
+#ifdef DUMP_IMG
+ g_cnt = 0;
+#endif
+
+ GST_DEBUG_OBJECT (evaspixmapsink,"[END]");
+ }
+
+static void
+gst_evaspixmapsink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details_simple (element_class,
+ "EvasPixmapSink", "Sink/Video",
+ "evas image object videosink based on Xv extension", "Sangchul Lee <sc11.lee@samsung.com>");
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_evaspixmapsink_sink_template_factory));
+}
+
+static void
+gst_evaspixmapsink_class_init (GstEvasPixmapSinkClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+ GstVideoSinkClass *videosink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+ videosink_class = (GstVideoSinkClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gst_evaspixmapsink_set_property;
+ gobject_class->get_property = gst_evaspixmapsink_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CONTRAST,
+ g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
+ -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
+ g_param_spec_int ("brightness", "Brightness",
+ "The brightness of the video", -1000, 1000, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_HUE,
+ g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SATURATION,
+ g_param_spec_int ("saturation", "Saturation",
+ "The saturation of the video", -1000, 1000, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_DISPLAY,
+ g_param_spec_string ("display", "Display", "X Display name", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
+ g_param_spec_boolean ("synchronous", "Synchronous",
+ "When enabled, runs "
+ "the X display in synchronous mode. (used only for debugging)", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
+ g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
+ "The pixel aspect ratio of the device", "1/1",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_DEVICE,
+ g_param_spec_string ("device", "Adaptor number",
+ "The number of the video adaptor", "0",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
+ g_param_spec_string ("device-name", "Adaptor name",
+ "The name of the video adaptor", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:double-buffer
+ *
+ * Whether to double-buffer the output.
+ *
+ * Since: 0.10.14
+ */
+ g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
+ g_param_spec_boolean ("double-buffer", "Double-buffer",
+ "Whether to double-buffer the output", TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstEvasPixmapSink:autopaint-colorkey
+ *
+ * Whether to autofill overlay with colorkey
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
+ g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
+ "Whether to autofill overlay with colorkey", TRUE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstEvasPixmapSink:colorkey
+ *
+ * Color to use for the overlay mask.
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_COLORKEY,
+ g_param_spec_int ("colorkey", "Colorkey",
+ "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:pixmap-width
+ *
+ * Actual width of the pixmap.
+ */
+ g_object_class_install_property (gobject_class, PROP_PIXMAP_WIDTH,
+ g_param_spec_uint64 ("pixmap-width", "pixmap-width", "Width of the pixmap", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:pixmap-height
+ *
+ * Actual height of the pixmap.
+ */
+ g_object_class_install_property (gobject_class, PROP_PIXMAP_HEIGHT,
+ g_param_spec_uint64 ("pixmap-height", "pixmap-height", "Height of the pixmap", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:display-geometry-method
+ *
+ * Display geometrical method setting
+ */
+ g_object_class_install_property(gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
+ g_param_spec_enum("display-geometry-method", "Display geometry method",
+ "Geometrical method for display",
+ GST_TYPE_EVASPIXMAPSINK_DISPLAY_GEOMETRY_METHOD, DEF_DISPLAY_GEOMETRY_METHOD,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:dst-roi-x
+ *
+ * X value of Destination ROI
+ */
+ g_object_class_install_property (gobject_class, PROP_DST_ROI_X,
+ g_param_spec_int ("dst-roi-x", "Dst-ROI-X",
+ "X value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:dst-roi-y
+ *
+ * Y value of Destination ROI
+ */
+ g_object_class_install_property (gobject_class, PROP_DST_ROI_Y,
+ g_param_spec_int ("dst-roi-y", "Dst-ROI-Y",
+ "Y value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:dst-roi-w
+ *
+ * W value of Destination ROI
+ */
+ g_object_class_install_property (gobject_class, PROP_DST_ROI_W,
+ g_param_spec_int ("dst-roi-w", "Dst-ROI-W",
+ "W value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:dst-roi-h
+ *
+ * H value of Destination ROI
+ */
+ g_object_class_install_property (gobject_class, PROP_DST_ROI_H,
+ g_param_spec_int ("dst-roi-h", "Dst-ROI-H",
+ "H value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:stop-video
+ *
+ * Stop video for releasing video source buffer
+ */
+ g_object_class_install_property (gobject_class, PROP_STOP_VIDEO,
+ g_param_spec_int ("stop-video", "Stop-Video", "Stop video for releasing video source buffer", 0, 1, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:evas-object
+ *
+ * Evas image object for rendering
+ */
+ g_object_class_install_property (gobject_class, PROP_EVAS_OBJECT,
+ g_param_spec_pointer ("evas-object", "Destination Evas Object", "Destination evas image object", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:display-flip
+ *
+ * Display flip setting
+ */
+ g_object_class_install_property(gobject_class, PROP_FLIP,
+ g_param_spec_enum("flip", "Display flip",
+ "Flip for display",
+ GST_TYPE_EVASPIXMAPSINK_FLIP, DEF_DISPLAY_FLIP,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:rotate
+ *
+ * draw rotation angle setting
+ */
+ g_object_class_install_property(gobject_class, PROP_ROTATE_ANGLE,
+ g_param_spec_enum("rotate", "Rotate angle", "Rotate angle of display output",GST_TYPE_EVASPIXMAPSINK_ROTATE_ANGLE, DEGREE_0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:visible
+ *
+ * visible setting for a evas image object
+ */
+ g_object_class_install_property (gobject_class, PROP_VISIBLE,
+ g_param_spec_boolean ("visible", "Visible", "When setting it false, evas image object does not show", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstEvasPixmapSink:origin-size
+ *
+ * Set pixmap size with media source's width and height
+ */
+ g_object_class_install_property (gobject_class, PROP_ORIGIN_SIZE,
+ g_param_spec_boolean ("origin-size", "Origin-Size", "When setting it true, pixmap will be created with media source's width and height", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gobject_class->finalize = gst_evaspixmapsink_finalize;
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_change_state);
+ gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_getcaps);
+ gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_setcaps);
+ gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_get_times);
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_event);
+ videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_evaspixmapsink_show_frame);
+}
+
+/* Object typing & Creation */
+GType
+gst_evaspixmapsink_get_type (void)
+{
+ static GType evaspixmapsink_type = 0;
+
+ if (!evaspixmapsink_type) {
+ static const GTypeInfo evaspixmapsink_info = {
+ sizeof (GstEvasPixmapSinkClass),
+ gst_evaspixmapsink_base_init,
+ NULL,
+ (GClassInitFunc) gst_evaspixmapsink_class_init,
+ NULL,
+ NULL,
+ sizeof (GstEvasPixmapSink),
+ 0,
+ (GInstanceInitFunc) gst_evaspixmapsink_init,
+ };
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) gst_evaspixmapsink_interface_init,
+ NULL,
+ NULL,
+ };
+ static const GInterfaceInfo navigation_info = {
+ (GInterfaceInitFunc) gst_evaspixmapsink_navigation_init,
+ NULL,
+ NULL,
+ };
+ static const GInterfaceInfo colorbalance_info = {
+ (GInterfaceInitFunc) gst_evaspixmapsink_colorbalance_init,
+ NULL,
+ NULL,
+ };
+ static const GInterfaceInfo propertyprobe_info = {
+ (GInterfaceInitFunc) gst_evaspixmapsink_property_probe_interface_init,
+ NULL,
+ NULL,
+ };
+ evaspixmapsink_type = g_type_register_static (GST_TYPE_VIDEO_SINK, "GstEvasPixmapSink", &evaspixmapsink_info, 0);
+
+ g_type_add_interface_static (evaspixmapsink_type, GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
+ g_type_add_interface_static (evaspixmapsink_type, GST_TYPE_NAVIGATION, &navigation_info);
+ g_type_add_interface_static (evaspixmapsink_type, GST_TYPE_COLOR_BALANCE, &colorbalance_info);
+ g_type_add_interface_static (evaspixmapsink_type, GST_TYPE_PROPERTY_PROBE, &propertyprobe_info);
+
+ /* register type and create class in a more safe place instead of at
+ * runtime since the type registration and class creation is not
+ * threadsafe. */
+ g_type_class_ref (gst_evaspixmap_buffer_get_type ());
+ }
+
+ return evaspixmapsink_type;
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+ if (!gst_element_register (plugin, "evaspixmapsink", GST_RANK_NONE, GST_TYPE_EVASPIXMAPSINK)) {
+ return FALSE;
+ }
+ GST_DEBUG_CATEGORY_INIT (gst_debug_evaspixmapsink, "evaspixmapsink", 0, "evaspixmapsink element");
+ GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "evaspixmapsink","Evas image object render plugin using Xv extension", plugin_init,
+ VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
--- /dev/null
+/*
+ * EvasPixmapSink
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Sangchul Lee <sc11.lee@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_EVASPIXMAPSINK_H__
+#define __GST_EVASPIXMAPSINK_H__
+
+#include <gst/video/gstvideosink.h>
+
+#ifdef HAVE_XSHM
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif /* HAVE_XSHM */
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSHM
+#include <X11/extensions/XShm.h>
+#endif /* HAVE_XSHM */
+
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/damagewire.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+
+#include <Evas.h>
+#include <Ecore.h>
+#include <Ecore_X.h>
+
+#include <tbm_bufmgr.h>
+
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+//#define DUMP_IMG
+
+#define MAX_PLANE_NUM 3
+#define MAX_BUFFER_NUM 20
+#define MAX_GEM_BUFFER_NUM (MAX_PLANE_NUM * MAX_BUFFER_NUM)
+typedef struct _gem_info_t {
+ int dmabuf_fd;
+ unsigned int gem_handle;
+ unsigned int gem_name;
+ tbm_bo bo;
+ Pixmap ref_pixmap;
+} gem_info_t;
+
+typedef enum {
+ BUF_SHARE_METHOD_NONE = -1,
+ BUF_SHARE_METHOD_PADDR = 0,
+ BUF_SHARE_METHOD_FD,
+ BUF_SHARE_METHOD_TIZEN_BUFFER
+} buf_share_method_t;
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_EVASPIXMAPSINK \
+ (gst_evaspixmapsink_get_type())
+#define GST_EVASPIXMAPSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_EVASPIXMAPSINK, GstEvasPixmapSink))
+#define GST_EVASPIXMAPSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_EVASPIXMAPSINK, GstEvasPixmapSinkClass))
+#define GST_IS_EVASPIXMAPSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_EVASPIXMAPSINK))
+#define GST_IS_EVASPIXMAPSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_EVASPIXMAPSINK))
+
+#define XV_SCREEN_SIZE_WIDTH 4096
+#define XV_SCREEN_SIZE_HEIGHT 4096
+
+#define MARGIN_OF_ERROR 0.005
+#define NUM_OF_PIXMAP 3
+
+typedef struct _GstXContext GstXContext;
+typedef struct _GstXPixmap GstXPixmap;
+typedef struct _GstEvasPixmapFormat GstEvasPixmapFormat;
+typedef struct _GstEvasPixmapBuffer GstEvasPixmapBuffer;
+typedef struct _GstEvasPixmapBufferClass GstEvasPixmapBufferClass;
+typedef struct _GstEvasPixmapSink GstEvasPixmapSink;
+typedef struct _GstEvasPixmapSinkClass GstEvasPixmapSinkClass;
+
+/*
+ * GstXContext:
+ * @disp: the X11 Display of this context
+ * @screen: the default Screen of Display @disp
+ * @screen_num: the Screen number of @screen
+ * @visual: the default Visual of Screen @screen
+ * @root: the root Window of Display @disp
+ * @white: the value of a white pixel on Screen @screen
+ * @black: the value of a black pixel on Screen @screen
+ * @depth: the color depth of Display @disp
+ * @bpp: the number of bits per pixel on Display @disp
+ * @endianness: the endianness of image bytes on Display @disp
+ * @width: the width in pixels of Display @disp
+ * @height: the height in pixels of Display @disp
+ * @widthmm: the width in millimeters of Display @disp
+ * @heightmm: the height in millimeters of Display @disp
+ * @par: the pixel aspect ratio calculated from @width, @widthmm and @height,
+ * @heightmm ratio
+ * @use_xshm: used to known wether of not XShm extension is usable or not even
+ * if the Extension is present
+ * @xv_port_id: the XVideo port ID
+ * @im_format: used to store at least a valid format for XShm calls checks
+ * @formats_list: list of supported image formats on @xv_port_id
+ * @channels_list: list of #GstColorBalanceChannels
+ * @caps: the #GstCaps that Display @disp can accept
+ *
+ * Structure used to store various informations collected/calculated for a
+ * Display.
+ */
+struct _GstXContext {
+ Display *disp;
+
+ Screen *screen;
+ gint screen_num;
+
+ Visual *visual;
+
+ Window root;
+
+ gulong white;
+
+ gint depth;
+ gint bpp;
+ gint endianness;
+
+ gint width, height;
+ gint widthmm, heightmm;
+ GValue *par; /* calculated pixel aspect ratio */
+
+ gboolean use_xshm;
+
+ XvPortID xv_port_id;
+ guint nb_adaptors;
+ gchar ** adaptors;
+ gint im_format;
+
+ GList *formats_list;
+ GList *channels_list;
+
+ GstCaps *caps;
+};
+
+/*
+ * GstXPixmap:
+ * @pixmap: the pixmap ID of this X11 pixmap
+ * @width: the width in pixels of Pixmap @pixmap
+ * @height: the height in pixels of Pixmap @pixmap
+ * @gc: the Graphical Context of Pixmap @pixmap
+ *
+ * Structure used to store informations about a Pixmap.
+ */
+struct _GstXPixmap {
+ Pixmap pixmap;
+ gint x, y;
+ gint width, height;
+ GC gc;
+ guint ref;
+ gint damaged_time;
+ Pixmap prev_pixmap;
+ GC prev_gc;
+};
+
+/**
+ * GstEvasPixmapFormat:
+ * @format: the image format
+ * @caps: generated #GstCaps for this image format
+ *
+ * Structure storing image format to #GstCaps association.
+ */
+struct _GstEvasPixmapFormat {
+ gint format;
+ GstCaps *caps;
+};
+
+/**
+ * GstEvasPixmapBuffer:
+ * @evaspixmapsink: a reference to our #GstEvasPixmapSink
+ * @xvimage: the XvImage of this buffer
+ * @width: the width in pixels of XvImage @xvimage
+ * @height: the height in pixels of XvImage @xvimage
+ * @im_format: the image format of XvImage @xvimage
+ * @size: the size in bytes of XvImage @xvimage
+ *
+ * Subclass of #GstBuffer containing additional information about an XvImage.
+ */
+struct _GstEvasPixmapBuffer {
+ GstBuffer buffer;
+
+ /* Reference to the evaspixmapsink we belong to */
+ GstEvasPixmapSink *evaspixmapsink;
+ XvImage *xvimage;
+
+#ifdef HAVE_XSHM
+ XShmSegmentInfo SHMInfo;
+#endif /* HAVE_XSHM */
+
+ gint width, height;
+ gint im_format;
+ size_t size;
+};
+
+/**
+ * GstEvasPixmapSink:
+ * @display_name: the name of the Display we want to render to
+ * @xcontext: our instance's #GstXContext
+ * @xpixmap: the #GstXPixmap we are rendering to
+ * @fps_n: the framerate fraction numerator
+ * @fps_d: the framerate fraction denominator
+ * @x_lock: used to protect X calls as we are not using the XLib in threaded
+ * mode
+ * @flow_lock: used to protect data flow routines from external calls such as
+ * methods from the #GstXOverlay interface
+ * @par: used to override calculated pixel aspect ratio from @xcontext
+ * @synchronous: used to store if XSynchronous should be used or not (for
+ * debugging purpose only)
+ * @keep_aspect: used to remember if reverse negotiation scaling should respect
+ * aspect ratio
+ * @brightness: used to store the user settings for color balance brightness
+ * @contrast: used to store the user settings for color balance contrast
+ * @hue: used to store the user settings for color balance hue
+ * @saturation: used to store the user settings for color balance saturation
+ * @cb_changed: used to store if the color balance settings where changed
+ * @video_width: the width of incoming video frames in pixels
+ * @video_height: the height of incoming video frames in pixels
+ *
+ * The #GstEvasPixmapSink data structure.
+ */
+struct _GstEvasPixmapSink {
+ /* Our element stuff */
+ GstVideoSink videosink;
+
+ char *display_name;
+ guint adaptor_no;
+
+ GstXContext *xcontext;
+ GstXPixmap *xpixmap[NUM_OF_PIXMAP];
+ GstEvasPixmapBuffer *evas_pixmap_buf;
+
+ GThread *event_thread;
+ gboolean running;
+
+ gint fps_n;
+ gint fps_d;
+
+ GMutex *x_lock;
+ GMutex *flow_lock;
+ GMutex *pixmap_ref_lock;
+
+ /* object-set pixel aspect ratio */
+ GValue *par;
+
+ gboolean synchronous;
+ gboolean double_buffer;
+
+ gint brightness;
+ gint contrast;
+ gint hue;
+ gint saturation;
+ gboolean cb_changed;
+
+ /* size of incoming video, used as the size for XvImage */
+ guint video_width, video_height;
+ gboolean need_to_fill_black;
+
+ /* display sizes, used for clipping the image */
+ gint disp_x, disp_y;
+ gint disp_width, disp_height;
+
+ /* port attributes */
+ gboolean autopaint_colorkey;
+ gint colorkey;
+
+ /* port features */
+ gboolean have_autopaint_colorkey;
+ gboolean have_colorkey;
+ gboolean have_double_buffer;
+
+ /* target video rectagle */
+ GstVideoRectangle render_rect;
+ gboolean have_render_rect;
+
+ /* display */
+ guint flip;
+ guint rotate_angle;
+ guint display_geometry_method;
+ GstVideoRectangle dst_roi;
+ guint scr_w, scr_h;
+ /* needed if fourcc is one if S series */
+ guint aligned_width;
+ guint aligned_height;
+
+ gboolean stop_video;
+ buf_share_method_t buf_shared_type;
+
+ /* ecore & evas object */
+ Ecore_Pipe *epipe;
+ Evas_Object *eo;
+ Evas_Coord w;
+ Evas_Coord h;
+ gboolean visible;
+ gint last_updated_idx;
+ gchar update_visibility;
+ gint epipe_request_count;
+
+ /* pixmap */
+ gboolean do_link;
+ gboolean use_origin_size;
+ gboolean previous_origin_size;
+ gint sizediff_width;
+ gint sizediff_height;
+ guint num_of_pixmaps;
+
+ /* damage event */
+ Damage damage[NUM_OF_PIXMAP];
+ Damage prev_damage[NUM_OF_PIXMAP];
+ int damage_case;
+
+ gint drm_fd;
+ gem_info_t gem_info[MAX_GEM_BUFFER_NUM];
+
+ /* if needed combine planes data */
+ gint need_combine_data;
+
+ GstVideoRectangle src_prev;
+ GstVideoRectangle result_prev;
+
+#ifdef DUMP_IMG
+ void *pixmap_addr[NUM_OF_PIXMAP];
+#endif
+};
+
+/* max plane count *********************************************************/
+#define MPLANE_IMGB_MAX_COUNT (4)
+
+/* image buffer definition ***************************************************
+
+ +------------------------------------------+ ---
+ | | ^
+ | uaddr[], index[] | |
+ | +---------------------------+ --- | |
+ | | | ^ | |
+ | |<-------- width[] -------->| | | |
+ | | | | | |
+ | | | |
+ | | |height[]|elevation[]
+ | | | |
+ | | | | | |
+ | | | | | |
+ | | | v | |
+ | +---------------------------+ --- | |
+ | | v
+ +------------------------------------------+ ---
+
+ |<----------------- stride[] ------------------>|
+*/
+typedef struct _GstMultiPlaneImageBuffer GstMultiPlaneImageBuffer;
+struct _GstMultiPlaneImageBuffer
+{
+ GstBuffer buffer;
+
+ /* width of each image plane */
+ gint width[MPLANE_IMGB_MAX_COUNT];
+ /* height of each image plane */
+ gint height[MPLANE_IMGB_MAX_COUNT];
+ /* stride of each image plane */
+ gint stride[MPLANE_IMGB_MAX_COUNT];
+ /* elevation of each image plane */
+ gint elevation[MPLANE_IMGB_MAX_COUNT];
+ /* user space address of each image plane */
+ gpointer uaddr[MPLANE_IMGB_MAX_COUNT];
+ /* Index of real address of each image plane, if needs */
+ gpointer index[MPLANE_IMGB_MAX_COUNT];
+ /* left postion, if needs */
+ gint x;
+ /* top position, if needs */
+ gint y;
+ /* to align memory */
+ gint __dummy2;
+ /* arbitrary data */
+ gint data[16];
+};
+
+struct _GstEvasPixmapSinkClass {
+ GstVideoSinkClass parent_class;
+};
+
+GType gst_evaspixmapsink_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_EVASPIXMAPSINK_H__ */
--- /dev/null
+/**************************************************************************
+
+xserver-xorg-video-exynos
+
+Copyright 2010 - 2011 Samsung Electronics co., Ltd. All Rights Reserved.
+
+Contact: Boram Park <boram1288.park@samsung.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+/* */
+/* File name : xv_types.h */
+/* Author : Boram Park (boram1288.park@samsung.com) */
+/* Protocol Version : 1.0.1 (Dec 16th 2009) */
+/* This file is for describing Xv APIs' buffer encoding method. */
+/* */
+
+#ifndef __XV_TYPE_H__
+#define __XV_TYPE_H__
+
+#define XV_DATA_HEADER 0xDEADCD01
+#define XV_DATA_VERSION 0x00010001
+
+/* Return Values */
+#define XV_OK 0
+#define XV_HEADER_ERROR -1
+#define XV_VERSION_MISMATCH -2
+
+/* Video Mode */
+#define DISPLAY_MODE_DEFAULT 0
+#define DISPLAY_MODE_PRI_VIDEO_ON_AND_SEC_VIDEO_FULL_SCREEN 1
+#define DISPLAY_MODE_PRI_VIDEO_OFF_AND_SEC_VIDEO_FULL_SCREEN 2
+
+/* Color space range */
+#define CSC_RANGE_NARROW 0
+#define CSC_RANGE_WIDE 1
+
+/* Buffer Type */
+#define XV_BUF_TYPE_DMABUF 0
+#define XV_BUF_TYPE_LEGACY 1
+#define XV_BUF_PLANE_NUM 3
+
+/* Data structure for XvPutImage / XvShmPutImage */
+typedef struct
+{
+ unsigned int _header; /* for internal use only */
+ unsigned int _version; /* for internal use only */
+
+ unsigned int YBuf;
+ unsigned int CbBuf;
+ unsigned int CrBuf;
+ unsigned int BufType;
+ unsigned int dmabuf_fd[XV_BUF_PLANE_NUM];
+ unsigned int gem_handle[XV_BUF_PLANE_NUM];
+ void *bo[XV_BUF_PLANE_NUM];
+} XV_DATA, * XV_DATA_PTR;
+
+static void
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+XV_INIT_DATA (XV_DATA_PTR data)
+{
+ data->_header = XV_DATA_HEADER;
+ data->_version = XV_DATA_VERSION;
+}
+
+static int
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+XV_VALIDATE_DATA (XV_DATA_PTR data)
+{
+ if (data->_header != XV_DATA_HEADER)
+ return XV_HEADER_ERROR;
+ if (data->_version != XV_DATA_VERSION)
+ return XV_VERSION_MISMATCH;
+ return XV_OK;
+}
+
+#endif
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+
+plugin_LTLIBRARIES = libgsthlsdemux2.la
+
+libgsthlsdemux2_la_SOURCES = \
+ m3u8.c \
+ gsthlsdemux2.c
+
+libgsthlsdemux2_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(SOUP_CFLAGS) $(GST_APP_CFLAGS) $(CRYPTO_CFLAGS)
+libgsthlsdemux2_la_LIBADD = -lgsttag-@GST_MAJORMINOR@ \
+ $(GST_LIBS) $(GST_BASE_LIBS) $(SOUP_LIBS) $(GST_APP_LIBS) -lgstapp-0.10 $(CRYPTO_LIBS)
+libgsthlsdemux2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -no-undefined
+libgsthlsdemux2_la_LIBTOOLFLAGS = --tag=disable-static
+
+# headers we need but don't want installed
+noinst_HEADERS = \
+ gsthlsdemux2.h \
+ m3u8.h
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+ androgenizer \
+ -:PROJECT libgsthls -:SHARED libgsthls \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgsthls_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgsthls_la_CFLAGS) \
+ -:LDFLAGS $(libgsthls_la_LDFLAGS) \
+ $(libgsthls_la_LIBADD) \
+ -ldl \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
+ > $@
--- /dev/null
+/*
+ * hlsdemux2
+ *
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>, Naveen Cherukuri <naveen.ch@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* 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 <sys/types.h>
+#include <sys/stat.h>
+#include <gst/tag/tag.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <gst/gst.h>
+
+#include "gsthlsdemux2.h"
+
+enum
+{
+ PROP_0,
+ PROP_BITRATE_SWITCH_TOLERANCE,
+ PROP_FORCE_LOWER_BITRATE,
+ PROP_BLOCKSIZE,
+ PROP_LAST
+};
+
+#define HLS_VIDEO_CAPS \
+ GST_STATIC_CAPS (\
+ "video/mpeg, " \
+ "mpegversion = (int) { 1, 2, 4 }, " \
+ "systemstream = (boolean) FALSE; " \
+ "video/x-h264,stream-format=(string)byte-stream," \
+ "alignment=(string)nal;" \
+ "video/x-dirac;" \
+ "video/x-wmv," \
+ "wmvversion = (int) 3, " \
+ "format = (fourcc) WVC1" \
+ )
+
+#define HLS_AUDIO_CAPS \
+ GST_STATIC_CAPS ( \
+ "audio/mpeg, " \
+ "mpegversion = (int) { 1, 4 };" \
+ "audio/x-lpcm, " \
+ "width = (int) { 16, 20, 24 }, " \
+ "rate = (int) { 48000, 96000 }, " \
+ "channels = (int) [ 1, 8 ], " \
+ "dynamic_range = (int) [ 0, 255 ], " \
+ "emphasis = (boolean) { FALSE, TRUE }, " \
+ "mute = (boolean) { FALSE, TRUE }; " \
+ "audio/x-ac3; audio/x-eac3;" \
+ "audio/x-dts;" \
+ "audio/x-private-ts-lpcm;" \
+ "application/x-id3" \
+ )
+
+/* Can also use the subpicture pads for text subtitles? */
+#define HLS_SUBPICTURE_CAPS \
+ GST_STATIC_CAPS ("subpicture/x-pgs; video/x-dvd-subpicture")
+
+static GstStaticPadTemplate hlsdemux2_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-hls"));
+
+static GstStaticPadTemplate hlsdemux2_videosrc_template =
+GST_STATIC_PAD_TEMPLATE ("video",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ HLS_VIDEO_CAPS);
+
+static GstStaticPadTemplate hlsdemux2_audiosrc_template =
+GST_STATIC_PAD_TEMPLATE ("audio",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ HLS_AUDIO_CAPS);
+
+static GstStaticPadTemplate hlsdemux2_subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("subpicture",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ HLS_SUBPICTURE_CAPS);
+
+static GstStaticPadTemplate hlsdemux2_private_template =
+GST_STATIC_PAD_TEMPLATE ("private",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (gst_hlsdemux2_debug);
+#define GST_CAT_DEFAULT gst_hlsdemux2_debug
+
+GST_DEBUG_CATEGORY (hlsdemux2_m3u8_debug);
+
+static const float update_interval_factor[] = { 1, 0.5, 1.5, 3 };
+
+#define HLS_DEFAULT_FRAME_DURATION (0.04 * GST_SECOND) // 40ms
+#define DEFAULT_BLOCKSIZE (8 * 1024) // 8 kbytes
+#define DEFAULT_FAST_SWITCH_BUFFER_SIZE 0.7 // factor
+static void _do_init (GType type)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_hlsdemux2_debug, "hlsdemux2", 0, "hlsdemux2 element");
+ GST_DEBUG_CATEGORY_INIT (hlsdemux2_m3u8_debug, "hlsdemux2m3u8", 0, "m3u8 parser");
+}
+
+GST_BOILERPLATE_FULL (GstHLSDemux2, gst_hlsdemux2, GstElement, GST_TYPE_ELEMENT, _do_init);
+
+#define DEFAULT_BITRATE_SWITCH_TOLERANCE 0.4
+#define DEFAULT_FAILED_COUNT 3
+#define DEFAULT_TARGET_DURATION 10
+#define DEFAULT_NUM_FRAGMENTS_CACHE 3
+#define DEFAULT_TOTAL_CACHE_DURATION (DEFAULT_NUM_FRAGMENTS_CACHE * DEFAULT_TARGET_DURATION * GST_SECOND)
+
+#define PREDEFINED_VIDEO_FRAME_LOCATION "/usr/etc/sec_audio_fixed_qvga.264"
+//#define PREDEFINED_VIDEO_FRAME_LOCATION "/usr/etc/blackframe_QVGA.264"
+#define PREDEFINED_IMAGE_FRAME_LOCATION "/usr/etc/sec_audio_fixed_qvga.jpg"
+
+#define HLSDEMUX2_SOUP_FAILED_CNT 10
+#define DEFAULT_FORCE_LOWER_BITRATE FALSE
+#define FORCE_LOW_BITRATE_AFTER_CNT 4
+#define HLSDEMUX2_HTTP_TIMEOUT 30 //30sec
+
+enum {
+ HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE = 0,
+ HLSDEMUX2_HTTP_ERROR_RECOVERABLE = 1,
+};
+
+typedef struct _HLSDemux2_HTTP_error HLSDemux2_HTTP_error;
+
+typedef gboolean (*HLS_HTTP_error_handle_function) (GstHLSDemux2 *demux, gchar *element_name);
+
+struct _HLSDemux2_HTTP_error {
+ guint HTTP_error_code;
+ const gchar *error_phrase;
+ guint error_type;
+ HLS_HTTP_error_handle_function handle_error;
+};
+
+struct _GstHLSDemux2PvtStream
+{
+ void *parent;
+ GstPad *sinkpad;
+ GstElement *sink;
+ gulong sink_eprobe;
+ gulong sink_bprobe;
+ HLSDEMUX2_STREAM_TYPE type;
+ GValue codec_type;
+ GstBuffer *id3_buffer;
+ GstBuffer *image_buffer;
+ GstBuffer *video_buffer;
+ gboolean got_img_buffer;
+ GstElement *convert_pipe;
+ GCond *img_load_cond;
+ GMutex *img_load_lock;
+ GCond *convert_cond;
+ GMutex *convert_lock;
+};
+
+struct _GstHLSDemux2Stream
+{
+ void *parent;
+ GstPad *pad;
+ GQueue *queue;
+ GMutex *queue_lock;
+ GCond *queue_full;
+ GCond *queue_empty;
+ gint64 percent;
+ HLSDEMUX2_STREAM_TYPE type;
+ gboolean is_linked;
+ gboolean eos;
+ gboolean apply_disc;
+ gint64 cached_duration;
+ GThread *dummy_data_thread; /* for pushing dummy video data */
+ GstClockTime base_ts; /* base ts start */
+ GstClockTime fts; /* first valid timestamp in a fragment */
+ GstClockTime lts; /* last valid timestamp in a fragment */
+ /* expected first timestamp in next fragment : usefule for handling fragments discontinuities*/
+ GstClockTime nts;
+ GstClockTime prev_nts; /* stores last nts for handling .aac files */
+ gboolean valid_fts_rcvd;
+ GstClockTime cdisc; /* current disc time */
+ GstClockTime frame_duration;
+ guint64 total_stream_time;
+ GstClockTime total_disc;
+ GQueue *downloader_queue;
+ gboolean need_newsegment;
+ GstEvent *newsegment;
+};
+
+/* GStreamer virtual functions */
+static void gst_hlsdemux2_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_hlsdemux2_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static gboolean gst_hlsdemux2_sink_event (GstPad * pad, GstEvent * event);
+static GstStateChangeReturn gst_hlsdemux2_change_state (GstElement * element, GstStateChange transition);
+static void gst_hlsdemux2_dispose (GObject * obj);
+static GstFlowReturn gst_hlsdemux2_chain (GstPad * pad, GstBuffer * buf);
+static gboolean gst_hlsdemux2_handle_src_event (GstPad * pad, GstEvent * event);
+
+/* init & de-init functions */
+static void gst_hlsdemux2_private_init (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_private_deinit (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_playlist_downloader_init (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_playlist_downloader_deinit (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_key_downloader_init (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_key_downloader_deinit (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_fragment_downloader_init (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_fragment_downloader_deinit (GstHLSDemux2 *demux);
+
+/* helper functions */
+static void gst_hlsdemux2_stop (GstHLSDemux2 * demux);
+static gboolean gst_hlsdemux2_set_location (GstHLSDemux2 * demux, const gchar * uri);
+static gchar *gst_hlsdemux2_src_buf_to_utf8_playlist (GstBuffer * buf);
+static void gst_hlsdemux2_new_pad_added (GstElement *element, GstPad *pad, gpointer data);
+static void gst_hlsdemux2_get_cookies(GstHLSDemux2 *demux);
+static void gst_hlsdemux2_get_user_agent(GstHLSDemux2 *demux);
+static gboolean gst_hlsdemux2_update_playlist (GstHLSDemux2 * demux, gboolean update, gboolean *is_error);
+static gboolean gst_hlsdemux2_schedule (GstHLSDemux2 * demux);
+static void gst_hlsdemux2_calculate_pushed_duration (GstHLSDemux2 *demux, GstHLSDemux2Stream *stream,
+ GstBuffer *inbuf);
+
+/* stream specific functions */
+static void gst_hlsdemux2_stream_init (GstHLSDemux2 *demux, GstHLSDemux2Stream *stream,
+ HLSDEMUX2_STREAM_TYPE stream_type, gchar *name, GstCaps *src_caps);
+static void gst_hlsdemux2_stream_deinit (GstHLSDemux2Stream *stream, gpointer data);
+static void gst_hlsdemux2_stop_stream (GstHLSDemux2 *demux);
+static HLSDemux2SinkBin *gst_hlsdemux2_create_stream (GstHLSDemux2 *demux, gchar *name, GstCaps *caps);
+
+/* task functions */
+static void gst_hlsdemux2_push_loop (GstHLSDemux2Stream *stream);
+static void gst_hlsdemux2_fragment_download_loop (GstHLSDemux2 * demux);
+
+/* playlist download related functions */
+static void gst_hlsdemux2_on_playlist_buffer (GstElement * appsink, void* data);
+static GstBusSyncReply gst_hlsdemux2_playlist_download_bus_sync_cb (GstBus * bus, GstMessage *msg, gpointer data);
+
+/* key file download related functions */
+static gboolean gst_hlsdemux2_key_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data);
+static void gst_hlsdemux2_on_key_buffer (GstElement *appsink, void *data);
+
+/* fragment download related functions */
+static gboolean gst_hlsdemux2_fragment_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data);
+static void gst_hlsdemux2_downloader_new_buffer (GstElement *appsink, void *user_data);
+static void gst_hlsdemux2_downloader_eos (GstElement * appsink, void* user_data);
+
+/* probe functions */
+static gboolean gst_hlsdemux2_sink_event_handler (GstPad * pad, GstEvent * event, gpointer data);
+static gboolean gst_hlsdemux2_change_playlist (GstHLSDemux2 * demux, guint max_bitrate, gboolean *is_switched);
+static gboolean gst_hlsdemux2_download_monitor_thread (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_push_eos (GstHLSDemux2 *demux);
+static void gst_hlsdemux2_apply_disc (GstHLSDemux2 * demux);
+static gboolean gst_hlsdemux2_queue_buffer_handler (GstPad * pad, GstBuffer *buffer, gpointer data);
+static gboolean hlsdemux2_HTTP_not_found(GstHLSDemux2 *demux, gchar *element_name);
+static gboolean hlsdemux2_HTTP_time_out (GstHLSDemux2 *demux, gchar *element_name);
+static gboolean hlsdemux2_HTTP_repeat_request (GstHLSDemux2 *demux, gchar *element_name);
+static void gst_hlsdemux2_handle_private_pad (GstHLSDemux2 *demux, GstPad *srcpad);
+static void gst_hlsdemux2_private_sink_on_new_buffer (GstElement *appsink, void *user_data);
+static void gst_hlsdemux2_private_sink_on_eos (GstElement * appsink, void* user_data);
+static gboolean gst_hlsdemux2_imagebuf_pipe_bus_cb (GstBus *bus, GstMessage *msg, gpointer data);
+static gboolean gst_hlsdemux2_set_video_buffer (GstPad * srcpad, GstBuffer * buffer, gpointer user_data);
+static gboolean gst_hlsdemux2_done_video_buffer (GstPad * srcpad, GstEvent * event, gpointer user_data);
+static gboolean gst_hlsdemux2_check_fast_switch (GstHLSDemux2 *demux);
+
+static const HLSDemux2_HTTP_error http_errors[] = {
+
+ /* transport errors by libsoup */
+ { 1, "Cancelled", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 2, "Cannot resolve hostname", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 3, "Cannot resolve proxy hostname", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 4, "Cannot connect to destination", HLSDEMUX2_HTTP_ERROR_RECOVERABLE, hlsdemux2_HTTP_repeat_request },
+ { 5, "Cannot connect to proxy", HLSDEMUX2_HTTP_ERROR_RECOVERABLE, hlsdemux2_HTTP_repeat_request },
+ { 6, "SSL handshake failed", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 7, "Connection terminated unexpectedly", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 8, "Message Corrupt", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 9, "Too many redirects", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+
+ /* Client error */
+ { 400, "Bad Request", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 401, "Unauthorized", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 402, "Payment Required", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 403, "Forbidden", HLSDEMUX2_HTTP_ERROR_RECOVERABLE, hlsdemux2_HTTP_repeat_request }, // TODO: Currently taking it as recoverable for testing
+ { 404, "Not Found", HLSDEMUX2_HTTP_ERROR_RECOVERABLE, hlsdemux2_HTTP_not_found },
+ { 405, "Method Not Allowed", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 406, "Not Acceptable", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 407, "Proxy Authentication Required", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 408, "Request Timeout", HLSDEMUX2_HTTP_ERROR_RECOVERABLE, hlsdemux2_HTTP_time_out },
+ { 409, "Conflict", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 410, "Gone", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 411, "Length Required", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 412, "Precondition Failed", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 413, "Request Entity Too Large", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 414, "Request-URI Too Long", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 415, "Unsupported Media Type", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 416, "Requested Range Not Satisfiable", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 417, "Expectation Failed", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 418, "Unprocessable Entity", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 419, "Locked", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 420, "Failed Dependency", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+
+ /* Server error */
+ { 500, "Internal Server Error", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 501, "Not Implemented", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 502, "Bad Gateway", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 503, "Service Unavailable", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL }, // TODO: need to make as recoverable with timout value
+ { 504, "Gateway Timeout", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 505, "HTTP Version Not Supported", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 506, "Insufficient Storage", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+ { 507, "Not Extended", HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE, NULL },
+};
+
+
+static void
+gst_hlsdemux2_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 (&hlsdemux2_videosrc_template));
+ gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&hlsdemux2_audiosrc_template));
+ gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&hlsdemux2_subpicture_template));
+ gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&hlsdemux2_sink_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "HLS Demuxer2",
+ "Demuxer/URIList",
+ "HTTP Live Streaming (HLS) demuxer",
+ "Naveen Cherukuri<naveen.ch@samsung.com>");
+}
+
+static void
+gst_hlsdemux2_class_init (GstHLSDemux2Class * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_hlsdemux2_set_property;
+ gobject_class->get_property = gst_hlsdemux2_get_property;
+ gobject_class->dispose = gst_hlsdemux2_dispose;
+
+ 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));
+
+ g_object_class_install_property (gobject_class, PROP_FORCE_LOWER_BITRATE,
+ g_param_spec_boolean ("force-low-bitrate",
+ "forcing lower variant", "forcing lower variant after every few fragments [for debugging purpose only]",
+ DEFAULT_FORCE_LOWER_BITRATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
+ g_param_spec_ulong ("blocksize", "Block size",
+ "Size in bytes to read per buffer (-1 = default)", 0, G_MAXULONG,
+ DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_hlsdemux2_change_state);
+}
+
+static void
+gst_hlsdemux2_init (GstHLSDemux2 * demux, GstHLSDemux2Class * klass)
+{
+ /* sink pad */
+ demux->sinkpad = gst_pad_new_from_static_template (&hlsdemux2_sink_template, "sink");
+ gst_pad_set_chain_function (demux->sinkpad, GST_DEBUG_FUNCPTR (gst_hlsdemux2_chain));
+ gst_pad_set_event_function (demux->sinkpad, GST_DEBUG_FUNCPTR (gst_hlsdemux2_sink_event));
+ gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
+
+ gst_hlsdemux2_private_init (demux);
+
+ /* fragment downloader init */
+ gst_hlsdemux2_fragment_downloader_init (demux);
+
+ /* playlist downloader init */
+ gst_hlsdemux2_playlist_downloader_init (demux);
+
+ /* key downloader init */
+ gst_hlsdemux2_key_downloader_init (demux);
+
+}
+
+static void
+gst_hlsdemux2_dispose (GObject * obj)
+{
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (obj);
+
+ GST_INFO_OBJECT (demux,"entering");
+
+ gst_hlsdemux2_stop (demux);
+
+ gst_hlsdemux2_private_deinit (demux);
+
+ gst_hlsdemux2_fragment_downloader_deinit (demux);
+
+ gst_hlsdemux2_playlist_downloader_deinit (demux);
+
+ gst_hlsdemux2_key_downloader_deinit (demux);
+
+ GST_INFO_OBJECT (demux,"leaving");
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+gst_hlsdemux2_private_init (GstHLSDemux2 *demux)
+{
+ demux->is_live = TRUE;
+ demux->bitrate_switch_tol = DEFAULT_BITRATE_SWITCH_TOLERANCE;
+ demux->percent = 100;
+ demux->active_stream_cnt = 0;
+ demux->download_task = NULL;
+ demux->pl_update_lock = g_mutex_new ();
+ demux->pl_update_cond = g_cond_new ();
+ demux->cancelled = FALSE;
+ demux->total_cache_duration = DEFAULT_TOTAL_CACHE_DURATION;
+ demux->target_duration = DEFAULT_TARGET_DURATION;
+ demux->keyCookie = NULL;
+ demux->playlistCookie = NULL;
+ demux->fragCookie = NULL;
+ demux->lastCookie = NULL;
+ demux->keyDomain = NULL;
+ demux->playlistDomain = NULL;
+ demux->fragDomain = NULL;
+ demux->lastDomain = NULL;
+ demux->user_agent = NULL;
+ demux->buffering_lock = g_mutex_new ();
+ demux->soup_request_fail_cnt = HLSDEMUX2_SOUP_FAILED_CNT;
+ demux->force_lower_bitrate = DEFAULT_FORCE_LOWER_BITRATE;
+ demux->cfrag_dur = 0;
+ demux->error_posted = FALSE;
+ demux->playlist_uri = NULL;
+ demux->frag_uri = NULL;
+ demux->key_uri = NULL;
+ demux->blocksize = DEFAULT_BLOCKSIZE;
+ demux->flushing = FALSE;
+ demux->ns_start = 0;
+ demux->cur_audio_fts = GST_CLOCK_TIME_NONE;
+ demux->is_buffering = TRUE;
+ demux->buffering_posting_thread = NULL;
+ demux->post_msg_lock = g_mutex_new ();
+ demux->post_msg_start = g_cond_new ();
+ demux->post_msg_exit = g_cond_new ();
+ demux->stream_config = HLSDEMUX2_SINGLE_VARIANT;
+ demux->has_image_buffer = FALSE;
+ demux->prev_image_buffer = NULL;
+ demux->prev_video_buffer = NULL;
+ demux->private_stream = NULL;
+}
+
+static void
+gst_hlsdemux2_private_deinit (GstHLSDemux2 *demux)
+{
+ demux->end_of_playlist = FALSE;
+
+ if (demux->pl_update_lock) {
+ g_mutex_free (demux->pl_update_lock);
+ demux->pl_update_lock = NULL;
+ }
+
+ if (demux->pl_update_cond) {
+ g_cond_free (demux->pl_update_cond);
+ demux->pl_update_cond = NULL;
+ }
+
+ if (demux->buffering_lock) {
+ g_mutex_free (demux->buffering_lock);
+ demux->buffering_lock = NULL;
+ }
+
+ if (demux->user_agent) {
+ g_free (demux->user_agent);
+ demux->user_agent = NULL;
+ }
+
+ if (demux->playlist) {
+ gst_buffer_unref (demux->playlist);
+ demux->playlist = NULL;
+ }
+
+ if (demux->fragCookie) {
+ g_strfreev (demux->fragCookie);
+ demux->fragCookie = NULL;
+ }
+
+ if (demux->keyCookie) {
+ g_strfreev (demux->keyCookie);
+ demux->keyCookie = NULL;
+ }
+
+ if (demux->lastCookie) {
+ g_strfreev (demux->lastCookie);
+ demux->lastCookie = NULL;
+ }
+
+ if (demux->playlistCookie) {
+ g_strfreev (demux->playlistCookie);
+ demux->playlistCookie = NULL;
+ }
+
+ if (demux->fragDomain) {
+ g_free (demux->fragDomain);
+ demux->fragDomain = NULL;
+ }
+
+ if (demux->keyDomain) {
+ g_free (demux->keyDomain);
+ demux->keyDomain = NULL;
+ }
+
+ if (demux->lastDomain) {
+ g_free (demux->lastDomain);
+ demux->lastDomain = NULL;
+ }
+
+ if (demux->playlistDomain) {
+ g_free (demux->playlistDomain);
+ demux->playlistDomain = NULL;
+ }
+
+ if (demux->playlist_uri) {
+ g_free (demux->playlist_uri);
+ demux->playlist_uri = NULL;
+ }
+
+ if (demux->key_uri) {
+ g_free (demux->key_uri);
+ demux->key_uri = NULL;
+ }
+
+ if (demux->frag_uri) {
+ g_free (demux->frag_uri);
+ demux->frag_uri = NULL;
+ }
+
+ if (demux->prev_image_buffer) {
+ gst_buffer_unref (demux->prev_image_buffer);
+ demux->prev_image_buffer = NULL;
+ }
+
+ if (demux->prev_video_buffer) {
+ gst_buffer_unref (demux->prev_video_buffer);
+ demux->prev_video_buffer = NULL;
+ }
+
+ if (demux->private_stream) {
+ g_free (demux->private_stream);
+ demux->private_stream = NULL;
+ }
+
+ if (demux->client) {
+ gst_m3u8_client_free (demux->client);
+ demux->client = NULL;
+ }
+}
+
+static void
+gst_hlsdemux2_fragment_downloader_init (GstHLSDemux2 *demux)
+{
+ demux->fdownloader = g_new0 (HLSDemux2FragDownloader, 1);
+ demux->fdownloader->pipe = NULL;
+ demux->fdownloader->urisrc = NULL;
+ demux->fdownloader->queue = NULL;
+ demux->fdownloader->typefind = NULL;
+ demux->fdownloader->demuxer = NULL;
+ demux->fdownloader->sinkbins = NULL;
+ demux->fdownloader->lock = g_mutex_new ();
+ demux->fdownloader->cond = g_cond_new ();
+ demux->fdownloader->is_encrypted = FALSE;
+ demux->fdownloader->content_size = 0;
+ demux->fdownloader->get_next_frag = FALSE;
+ demux->fdownloader->applied_fast_switch = FALSE;
+ demux->fdownloader->remaining_data = NULL;
+ demux->fdownloader->remaining_size = 0;
+ demux->fdownloader->first_buffer = TRUE;
+ demux->fdownloader->cur_stream_cnt = 0;
+ demux->fdownloader->force_timestamps = FALSE;
+ demux->fdownloader->error_rcvd = FALSE;
+ demux->fdownloader->find_mediaseq = FALSE;
+ demux->fdownloader->seeked_pos = 0;
+ demux->fdownloader->cur_running_dur = 0;
+ demux->fdownloader->download_rate = -1;
+ demux->fdownloader->download_start_ts = 0;
+ demux->fdownloader->download_stop_ts = 0;
+ demux->fdownloader->src_downloaded_size = 0;
+ demux->fdownloader->queue_downloaded_size = 0;
+ demux->fdownloader->ndownloaded = 0;
+ demux->fdownloader->chunk_downloaded_size = 0;
+ demux->fdownloader->chunk_start_ts = 0;
+ demux->fdownloader->avg_chunk_drates = g_array_new(FALSE, TRUE, sizeof(guint64));
+ demux->fdownloader->avg_frag_drates = g_array_new(FALSE, TRUE, sizeof(guint64));
+}
+
+static void
+gst_hlsdemux2_fragment_downloader_deinit (GstHLSDemux2 *demux)
+{
+ if (!demux->fdownloader)
+ return;
+
+ /* free fragment downloader structure */
+ if (demux->fdownloader->lock) {
+ g_mutex_free (demux->fdownloader->lock);
+ demux->fdownloader->lock = NULL;
+ }
+
+ if (demux->fdownloader->cond) {
+ g_cond_free (demux->fdownloader->cond);
+ demux->fdownloader->cond = NULL;
+ }
+
+ if (demux->fdownloader->remaining_data) {
+ g_free (demux->fdownloader->remaining_data);
+ demux->fdownloader->remaining_data = NULL;
+ }
+
+ if (demux->fdownloader->avg_chunk_drates) {
+ g_array_free(demux->fdownloader->avg_chunk_drates, TRUE);
+ demux->fdownloader->avg_chunk_drates = NULL;
+ }
+
+ if (demux->fdownloader->avg_frag_drates) {
+ g_array_free(demux->fdownloader->avg_frag_drates, TRUE);
+ demux->fdownloader->avg_frag_drates = NULL;
+ }
+
+ g_free (demux->fdownloader);
+ demux->fdownloader = NULL;
+}
+
+static void
+gst_hlsdemux2_playlist_downloader_init (GstHLSDemux2 *demux)
+{
+ demux->pldownloader = g_new0 (HLSDemux2PLDownloader, 1);
+ demux->pldownloader->pipe = NULL;
+ demux->pldownloader->bus = NULL;
+ demux->pldownloader->urisrc = NULL;
+ demux->pldownloader->sink = NULL;
+ demux->pldownloader->lock = g_mutex_new ();
+ demux->pldownloader->cond = g_cond_new ();
+ demux->pldownloader->playlist = NULL;
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+}
+
+static void
+gst_hlsdemux2_playlist_downloader_deinit (GstHLSDemux2 *demux)
+{
+ if (!demux->pldownloader)
+ return;
+
+ /* free playlist downloader structure */
+ if (demux->pldownloader->lock) {
+ g_mutex_free (demux->pldownloader->lock);
+ demux->pldownloader->lock = NULL;
+ }
+
+ if (demux->pldownloader->cond) {
+ g_cond_free (demux->pldownloader->cond);
+ demux->pldownloader->cond = NULL;
+ }
+
+ if (demux->pldownloader->playlist){
+ gst_buffer_unref (demux->pldownloader->playlist);
+ demux->pldownloader->playlist = NULL;
+ }
+
+ g_free (demux->pldownloader);
+ demux->pldownloader = NULL;
+}
+
+static void
+gst_hlsdemux2_key_downloader_init (GstHLSDemux2 *demux)
+{
+ demux->kdownloader = g_new0 (HLSDemux2KeyDownloader, 1);
+ demux->kdownloader->pipe = NULL;
+ demux->kdownloader->bus = NULL;
+ demux->kdownloader->urisrc = NULL;
+ demux->kdownloader->sink = NULL;
+ demux->kdownloader->lock = g_mutex_new ();
+ demux->kdownloader->cond = g_cond_new ();
+ demux->kdownloader->key = NULL;
+ demux->kdownloader->prev_key_uri = NULL;
+}
+
+static void
+gst_hlsdemux2_key_downloader_deinit (GstHLSDemux2 *demux)
+{
+ /* free key file downloader structure */
+ if (!demux->kdownloader)
+ return;
+
+ if (demux->kdownloader->lock) {
+ g_mutex_free (demux->kdownloader->lock);
+ demux->kdownloader->lock = NULL;
+ }
+
+ if (demux->kdownloader->cond) {
+ g_cond_free (demux->kdownloader->cond);
+ demux->kdownloader->cond = NULL;
+ }
+
+ if (demux->kdownloader->key){
+ gst_buffer_unref (demux->kdownloader->key);
+ demux->kdownloader->key = NULL;
+ }
+
+ if (demux->kdownloader->prev_key_uri) {
+ g_free (demux->kdownloader->prev_key_uri);
+ demux->kdownloader->prev_key_uri = NULL;
+ }
+
+ g_free (demux->kdownloader);
+ demux->kdownloader = NULL;
+}
+
+static void
+gst_hlsdemux2_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (object);
+
+ switch (prop_id) {
+ case PROP_BITRATE_SWITCH_TOLERANCE:
+ demux->bitrate_switch_tol = g_value_get_float (value);
+ break;
+ case PROP_FORCE_LOWER_BITRATE:
+ demux->force_lower_bitrate = g_value_get_boolean (value);
+ break;
+ case PROP_BLOCKSIZE:
+ demux->blocksize = g_value_get_ulong (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_hlsdemux2_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (object);
+
+ switch (prop_id) {
+ case PROP_BITRATE_SWITCH_TOLERANCE:
+ g_value_set_float (value, demux->bitrate_switch_tol);
+ break;
+ case PROP_FORCE_LOWER_BITRATE:
+ g_value_set_boolean (value, demux->force_lower_bitrate);
+ break;
+ case PROP_BLOCKSIZE:
+ g_value_set_ulong (value, demux->blocksize);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_hlsdemux2_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (gst_pad_get_parent (pad));
+ GstQuery *query = NULL;
+ gboolean ret = FALSE;
+ gchar *uri = NULL;
+
+ GST_LOG_OBJECT (demux, "Received event '%s' on sink pad...", GST_EVENT_TYPE_NAME (event));
+
+ switch (event->type) {
+ case GST_EVENT_EOS: {
+ gchar *playlist = NULL;
+ GError *err = NULL;
+
+ if (demux->playlist == NULL) {
+ GST_ERROR_OBJECT (demux, "Received EOS without a playlist.");
+ goto error;
+ }
+
+ GST_INFO_OBJECT (demux, "Got EOS on the sink pad: playlist file fetched");
+
+ query = gst_query_new_uri ();
+
+ /* query the location from upstream element for absolute path */
+ ret = gst_pad_peer_query (demux->sinkpad, query);
+ if (ret) {
+ gst_query_parse_uri (query, &uri);
+ gst_hlsdemux2_set_location (demux, uri);
+ g_free (uri);
+ } else {
+ GST_ERROR_OBJECT (demux, "failed to query URI from upstream");
+ goto error;
+ }
+ gst_query_unref (query);
+ query = NULL;
+
+ /* get cookies from upstream httpsrc */
+ gst_hlsdemux2_get_cookies (demux);
+
+ /* get user-agent from upstream httpsrc */
+ gst_hlsdemux2_get_user_agent (demux);
+
+ playlist = gst_hlsdemux2_src_buf_to_utf8_playlist (demux->playlist);
+ demux->playlist = NULL;
+
+ if (playlist == NULL) {
+ GST_ERROR_OBJECT (demux, "Error validating first playlist.");
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), (NULL));
+ goto error;
+ }
+
+ if (!gst_m3u8_client_update (demux->client, playlist)) {
+ /* 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_ERROR_OBJECT (demux, "failed to parse playlist...");
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), (NULL));
+ goto error;
+ }
+
+ /* Loop to download the fragments & updates playlist */
+ g_static_rec_mutex_init (&demux->download_lock);
+ demux->download_task = gst_task_create ((GstTaskFunction) gst_hlsdemux2_fragment_download_loop, demux);
+ if (NULL == demux->download_task) {
+ GST_ERROR_OBJECT (demux, "failed to create download task...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to fragment download task"), (NULL));
+ goto error;
+ }
+ gst_task_set_lock (demux->download_task, &demux->download_lock);
+ gst_task_start (demux->download_task);
+
+ /* create thread to send dummy data */
+ demux->buffering_posting_thread = g_thread_create (
+ (GThreadFunc) gst_hlsdemux2_download_monitor_thread, demux, TRUE, &err);
+
+ /* Swallow EOS, we'll push our own */
+ gst_event_unref (event);
+ gst_object_unref (demux);
+ return TRUE;
+ }
+ case GST_EVENT_NEWSEGMENT:
+ /* Swallow newsegments, we'll push our own */
+ gst_event_unref (event);
+ gst_object_unref (demux);
+ return TRUE;
+ default:
+ gst_object_unref (demux);
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+
+error:
+ gst_hlsdemux2_stop (demux);
+ gst_event_unref (event);
+ gst_object_unref (demux);
+
+ if (query)
+ gst_query_unref (query);
+
+ GST_LOG_OBJECT (demux,"Returning from sink event...");
+ return FALSE;
+}
+
+static GstStateChangeReturn
+ gst_hlsdemux2_change_state (GstElement * element, GstStateChange transition)
+ {
+ GstStateChangeReturn ret;
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (element);
+ gboolean bret = FALSE;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ GST_INFO_OBJECT (demux,"PAUSED->PLAYING");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_INFO_OBJECT (demux,"PAUSED->READY before parent");
+ gst_hlsdemux2_stop (demux);
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_INFO_OBJECT (demux,"PLAYING->PAUSED");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_INFO_OBJECT (demux,"PAUSED->READY after parent");
+ if (demux->download_task) {
+ bret = gst_task_join (demux->download_task);
+ GST_DEBUG_OBJECT (demux,"Joining download task : %d", bret);
+ }
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_INFO_OBJECT (demux,"READY->NULL");
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+
+
+static GstFlowReturn
+gst_hlsdemux2_chain (GstPad * pad, GstBuffer * buf)
+{
+ GstHLSDemux2 *demux = GST_HLSDEMUX2 (gst_pad_get_parent (pad));
+
+ if (demux->playlist == NULL)
+ demux->playlist = buf;
+ else
+ demux->playlist = gst_buffer_join (demux->playlist, buf);
+ gst_object_unref (demux);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_hlsdemux2_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ GstHLSDemux2 *hlsdemux2 = NULL;
+ GstHLSDemux2Stream *stream = NULL;
+
+ stream = (GstHLSDemux2Stream *) (gst_pad_get_element_private (pad));
+ hlsdemux2 = stream->parent;
+
+ switch (event->type) {
+ case GST_EVENT_SEEK: {
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ gint idx = 0;
+
+ if (gst_m3u8_client_is_live (hlsdemux2->client)) {
+ GST_WARNING_OBJECT (stream->pad, "SEEK is NOT Supported in LIVE");
+ return FALSE;
+ }
+
+ GST_INFO_OBJECT (stream->pad, "received SEEK event..");
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+ &stop_type, &stop);
+
+ if (format != GST_FORMAT_TIME) {
+ GST_WARNING_OBJECT (stream->pad, "received seek with unsupported format");
+ return FALSE;
+ }
+
+ // TODO: Validate requested SEEK time is within the duration or not
+
+ hlsdemux2->end_of_playlist = FALSE; // resent end of playlist
+
+ GST_DEBUG_OBJECT (stream->pad, "SEEK event with rate: %f start: %" GST_TIME_FORMAT
+ " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
+ GST_TIME_ARGS (stop));
+
+ if (!(flags & GST_SEEK_FLAG_FLUSH)) {
+ GST_WARNING_OBJECT (stream->pad, "non-flush seek is not supported yet");
+ return FALSE;
+ }
+
+ hlsdemux2->flushing = TRUE;
+
+ /* signal queue full condition to come out */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream) {
+ cur_stream->eos = FALSE; // resent stream EOS
+ if (cur_stream->queue) {
+ GST_INFO_OBJECT (cur_stream->pad, "signalling stream queue");
+ g_cond_signal (cur_stream->queue_full); /* to signal downloader eos blocking */
+ g_cond_signal (cur_stream->queue_empty); /* incase push_loop blocked on condition */
+ }
+ }
+ }
+
+ /* send FLUSH_START event to all source pads */
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+
+ GST_INFO_OBJECT (hlsdemux2, "sending flush start on all the stream pads");
+
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream && GST_PAD_TASK(cur_stream->pad)) {
+ gboolean bret = FALSE;
+ bret = gst_pad_push_event (cur_stream->pad, gst_event_new_flush_start ());
+ GST_DEBUG_OBJECT (cur_stream->pad, "flush_start returned = %d", bret);
+ }
+ }
+ }
+
+ /* wait till all stream pad's task paused */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream && GST_PAD_TASK(cur_stream->pad)) {
+ GST_INFO_OBJECT (cur_stream->pad, "trying acquire stream lock");
+ GST_PAD_STREAM_LOCK (cur_stream->pad);
+ GST_INFO_OBJECT (cur_stream->pad, "acquired stream lock");
+ GST_PAD_STREAM_UNLOCK (cur_stream->pad);
+ }
+ }
+
+ /* pause fragment download loop */
+ g_mutex_lock (hlsdemux2->kdownloader->lock);
+ GST_INFO_OBJECT (stream->pad, "Signalling key downloader condition");
+ g_cond_signal (hlsdemux2->kdownloader->cond);
+ g_mutex_unlock (hlsdemux2->kdownloader->lock);
+
+ g_mutex_lock (hlsdemux2->fdownloader->lock);
+ GST_INFO_OBJECT (stream->pad, "Signalling fragment downloader condition");
+ g_cond_signal (hlsdemux2->fdownloader->cond);
+ g_mutex_unlock (hlsdemux2->fdownloader->lock);
+
+ g_mutex_lock (hlsdemux2->pldownloader->lock);
+ GST_INFO_OBJECT (stream->pad, "Signalling playlist downloader condition");
+ g_cond_signal (hlsdemux2->pldownloader->cond);
+ g_mutex_unlock (hlsdemux2->pldownloader->lock);
+
+ GST_INFO_OBJECT (stream->pad, "waiting for download task to pause...");
+ g_static_rec_mutex_lock (&hlsdemux2->download_lock);
+ g_static_rec_mutex_unlock (&hlsdemux2->download_lock);
+
+ GST_INFO_OBJECT (hlsdemux2, "Download task paused");
+
+ /* clear internal stream queues */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream && cur_stream->queue) {
+ while (!g_queue_is_empty (cur_stream->queue)) {
+ gpointer data = NULL;
+ data = g_queue_pop_head (cur_stream->queue);
+ gst_object_unref (data);
+ }
+ g_queue_clear (cur_stream->queue);
+ cur_stream->cached_duration = 0;
+ stream->percent = 0;
+
+ while (!g_queue_is_empty (cur_stream->downloader_queue)) {
+ gpointer data = NULL;
+ data = g_queue_pop_head (cur_stream->downloader_queue);
+ gst_object_unref (data);
+ }
+ g_queue_clear (cur_stream->downloader_queue);
+ }
+ }
+
+#if 0 /* useful when we want to switch to lowest variant on seek for faster buffering */
+ if (gst_m3u8_client_has_variant_playlist (hlsdemux2->client)) {
+ /* intentionally sending zero download rate, to move to least possible variant */
+ gst_hlsdemux2_change_playlist (hlsdemux2, 0, NULL);
+ }
+#endif
+
+ /* send FLUSH_STOP event to all source pads */
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+
+ GST_INFO_OBJECT (hlsdemux2, "sending flush stop");
+
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream && GST_PAD_TASK(cur_stream->pad)) {
+ gboolean bret = FALSE;
+ bret = gst_pad_push_event (cur_stream->pad, gst_event_new_flush_stop ());
+ GST_DEBUG_OBJECT (cur_stream->pad, "flush_stop returned = %d", bret);
+ }
+ }
+ }
+
+ hlsdemux2->flushing = FALSE;
+ hlsdemux2->fdownloader->find_mediaseq = TRUE;
+ hlsdemux2->fdownloader->seeked_pos = start;
+
+ /* start all streams loop tasks & fragment download task */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = hlsdemux2->streams[idx];
+
+ if (cur_stream && GST_PAD_TASK(cur_stream->pad)) {
+ GST_INFO_OBJECT (cur_stream->pad, "Starting push task");
+ cur_stream->need_newsegment = TRUE;
+ cur_stream->nts = GST_CLOCK_TIME_NONE; /*useful for audio-only seeking */
+ cur_stream->cdisc = 0;
+ cur_stream->fts = GST_CLOCK_TIME_NONE;
+ cur_stream->valid_fts_rcvd = FALSE;
+ cur_stream->total_stream_time = 0;
+
+ if (!gst_pad_start_task (cur_stream->pad, (GstTaskFunction) gst_hlsdemux2_push_loop, cur_stream)) {
+ GST_ERROR_OBJECT (hlsdemux2, "failed to start stream task...");
+ GST_ELEMENT_ERROR (hlsdemux2, RESOURCE, FAILED, ("failed to create stream push loop"), (NULL));
+ goto error;
+ }
+ }
+ }
+
+ gst_hlsdemux2_apply_disc (hlsdemux2);
+
+ GST_INFO_OBJECT (hlsdemux2, "Starting Download task..");
+
+ gst_task_start (hlsdemux2->download_task);
+
+ GST_INFO_OBJECT (hlsdemux2, "Successfully configured SEEK...");
+ gst_event_unref (event);
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+
+error:
+ GST_ERROR_OBJECT (hlsdemux2, "seeking failed...");
+ gst_event_unref (event);
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_src_query (GstPad *pad, GstQuery * query)
+{
+ GstHLSDemux2 *hlsdemux2 = NULL;
+ gboolean ret = FALSE;
+ GstHLSDemux2Stream *stream = NULL;
+
+ if (query == NULL)
+ return FALSE;
+
+ stream = (GstHLSDemux2Stream *) (gst_pad_get_element_private (pad));
+ hlsdemux2 = stream->parent;
+
+ switch (query->type) {
+ case GST_QUERY_DURATION:{
+ GstClockTime duration = -1;
+ GstFormat fmt;
+
+ gst_query_parse_duration (query, &fmt, NULL);
+ if (fmt == GST_FORMAT_TIME) {
+ duration = gst_m3u8_client_get_duration (hlsdemux2->client);
+ if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ ret = TRUE;
+ }
+ }
+ GST_LOG_OBJECT (hlsdemux2, "GST_QUERY_DURATION returns %s with duration %"
+ GST_TIME_FORMAT, ret ? "TRUE" : "FALSE", GST_TIME_ARGS (duration));
+ break;
+ }
+ case GST_QUERY_URI:
+ if (hlsdemux2->client) {
+ /* FIXME: Do we answer with the variant playlist, with the current
+ * playlist or the the uri of the least downlowaded fragment? */
+ gst_query_set_uri (query, gst_m3u8_client_get_uri (hlsdemux2->client));
+ ret = TRUE;
+ }
+ break;
+ case GST_QUERY_SEEKING:{
+ GstFormat fmt;
+ gint64 stop = -1;
+
+ gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+ GST_INFO_OBJECT (hlsdemux2, "Received GST_QUERY_SEEKING with format %d",
+ fmt);
+ if (fmt == GST_FORMAT_TIME) {
+ GstClockTime duration;
+
+ duration = gst_m3u8_client_get_duration (hlsdemux2->client);
+ if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0)
+ stop = duration;
+
+ gst_query_set_seeking (query, fmt,
+ !gst_m3u8_client_is_live (hlsdemux2->client), 0, stop);
+ ret = TRUE;
+ GST_INFO_OBJECT (hlsdemux2, "GST_QUERY_SEEKING returning with stop : %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (stop));
+ }
+ break;
+ }
+ default:
+ /* Don't fordward queries upstream because of the special nature of this
+ * "demuxer", which relies on the upstream element only to be fed with the
+ * first playlist */
+ break;
+ }
+
+ return ret;
+}
+
+static gchar *
+gst_hlsdemux2_uri_get_domain ( GstHLSDemux2 *demux, gchar * uri)
+{
+ gchar *pointer = uri;
+ gchar *domain = NULL;
+ guint flag = 0;
+ guint counter=0;
+
+ domain = g_malloc0(strlen (uri));
+ if (!domain) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ return NULL;
+ }
+
+ while(*pointer != '\0') {
+ switch(*pointer) {
+ case '.':
+ flag = 1;
+ break;
+ case '/':
+ if(flag==1)
+ flag = 2;
+ break;
+ }
+ if(flag==1) {
+ domain[counter]=*pointer;
+ counter++;
+ } else if(flag == 2) {
+ domain[counter]='\0';
+ break;
+ }
+ pointer++;
+ }
+
+ GST_DEBUG_OBJECT (demux, "uri = %s and domain = %s", uri, domain);
+ return domain;
+}
+
+static void
+gst_hlsdemux2_get_cookies(GstHLSDemux2 *demux)
+{
+ GstQuery *cquery;
+ GstStructure *structure;
+ gboolean bret = FALSE;
+
+ structure = gst_structure_new ("HTTPCookies",
+ "cookies", G_TYPE_STRING, NULL, NULL);
+
+ cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
+
+ bret = gst_pad_peer_query (demux->sinkpad, cquery);
+ if (bret) {
+ const GstStructure *s;
+ const GValue *value;
+
+ s = gst_query_get_structure (cquery);
+ value = gst_structure_get_value (s, "cookies");
+ demux->playlistCookie = g_strdupv (g_value_get_boxed (value));
+
+ GST_INFO_OBJECT (demux, "Received playlist cookies from upstream element : %s", demux->playlistCookie ? *(demux->playlistCookie) : NULL);
+ if(demux->playlistCookie){
+ demux->lastCookie = g_strdupv(demux->playlistCookie);
+ }
+ } else {
+ GST_WARNING_OBJECT (demux, "Failed to get cookies");
+ }
+
+ gst_query_unref (cquery);
+}
+
+static void
+gst_hlsdemux2_get_user_agent(GstHLSDemux2 *demux)
+{
+ GstQuery *cquery;
+ GstStructure *structure;
+ gboolean bret = FALSE;
+
+ structure = gst_structure_new ("HTTPUserAgent",
+ "user-agent", G_TYPE_STRING, NULL, NULL);
+
+ cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
+
+ bret = gst_pad_peer_query (demux->sinkpad, cquery);
+ if (bret) {
+ const GstStructure *s;
+ const GValue *value;
+
+ GST_INFO_OBJECT (demux, "Received user-agent from upstream element...");
+
+ s = gst_query_get_structure (cquery);
+ value = gst_structure_get_value (s, "user-agent");
+ demux->user_agent = g_strdup (g_value_get_string (value));
+
+ GST_INFO_OBJECT (demux,"User agent received from query : %s", demux->user_agent);
+ } else {
+ GST_WARNING_OBJECT (demux, "Failed to get user-agent");
+ }
+
+ gst_query_unref (cquery);
+}
+
+static gboolean
+gst_hlsdemux2_create_playlist_download (GstHLSDemux2 *demux, const gchar *playlist_uri)
+{
+ if (!gst_uri_is_valid (playlist_uri)) {
+ GST_ERROR_OBJECT (demux, "invalid uri : %s...", playlist_uri == NULL ? "NULL" : playlist_uri);
+ return FALSE;
+ }
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (demux,"returning from download playlist due to cancel or flushing..");
+ return FALSE;
+ }
+
+ /* create playlist downloader pipeline */
+ demux->pldownloader->pipe = gst_pipeline_new ("playlist-downloader");
+ if (!demux->pldownloader->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+
+ demux->pldownloader->bus = gst_pipeline_get_bus (GST_PIPELINE (demux->pldownloader->pipe));
+ gst_bus_set_sync_handler(demux->pldownloader->bus, gst_hlsdemux2_playlist_download_bus_sync_cb, demux);
+
+ gst_object_unref (demux->pldownloader->bus);
+
+ GST_INFO_OBJECT (demux, "Creating source element for the URI : %s", playlist_uri);
+
+ /* creating source element */
+ demux->pldownloader->urisrc = gst_element_make_from_uri (GST_URI_SRC, playlist_uri, "playlisturisrc");
+ if (!demux->pldownloader->urisrc) {
+ GST_ERROR_OBJECT (demux, "failed to create urisrc");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (demux->pldownloader->urisrc), "timeout", HLSDEMUX2_HTTP_TIMEOUT, NULL);
+ g_object_set (G_OBJECT (demux->pldownloader->urisrc), "user-agent", demux->user_agent, NULL);
+ g_object_set (G_OBJECT (demux->pldownloader->urisrc), "ahs-streaming", TRUE, NULL);
+
+ demux->playlistDomain = gst_hlsdemux2_uri_get_domain(demux, playlist_uri);
+ if(!g_strcmp0(demux->playlistDomain, demux->lastDomain) && demux->lastCookie) {
+ g_strfreev (demux->playlistCookie);
+ demux->playlistCookie = g_strdupv (demux->lastCookie);
+ }
+
+ if (demux->playlistCookie) {
+ GST_DEBUG_OBJECT (demux, "Setting cookies before PLAYLIST download, before PLAYING: %s", *(demux->playlistCookie));
+ /* setting cookies */
+ g_object_set (demux->pldownloader->urisrc, "cookies", demux->playlistCookie, NULL);
+ }
+
+ /* create sink element */
+ demux->pldownloader->sink = gst_element_factory_make ("appsink", "playlistsink");
+ if (!demux->pldownloader->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create playlist sink element");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (demux->pldownloader->sink), "sync", FALSE, "emit-signals", TRUE, NULL);
+ g_signal_connect (demux->pldownloader->sink, "new-buffer", G_CALLBACK (gst_hlsdemux2_on_playlist_buffer), demux);
+
+ gst_bin_add_many (GST_BIN (demux->pldownloader->pipe), demux->pldownloader->urisrc, demux->pldownloader->sink, NULL);
+
+ if (!gst_element_link_many (demux->pldownloader->urisrc, demux->pldownloader->sink, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link src & demux elements...");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_destroy_playlist_download (GstHLSDemux2 * demux)
+{
+ if (demux->pldownloader->pipe) {
+ GST_DEBUG_OBJECT (demux, "Shutting down playlist download pipeline");
+ gst_element_set_state (demux->pldownloader->pipe, GST_STATE_NULL);
+ gst_element_get_state (demux->pldownloader->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ gst_object_unref (demux->pldownloader->pipe);
+ demux->pldownloader->pipe = NULL;
+ }
+}
+
+static gboolean
+gst_hlsdemux2_download_playlist (GstHLSDemux2 *demux, const gchar * uri)
+{
+ GstStateChangeReturn ret;
+
+ if (demux->playlist_uri) {
+ g_free (demux->playlist_uri);
+ }
+ demux->playlist_uri = g_strdup (uri);
+
+ demux->pldownloader->download_start_ts = gst_util_get_timestamp();
+
+ if (!gst_hlsdemux2_create_playlist_download (demux, uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create playlist download pipeline");
+ return FALSE;
+ }
+
+ ret = gst_element_set_state (demux->pldownloader->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (demux, "set_state failed...");
+ return FALSE;
+ }
+
+ /* schedule the next update of playlist */
+ if (demux->is_live)
+ gst_hlsdemux2_schedule (demux);
+
+ /* wait until:
+ * - the download succeed (EOS)
+ * - the download failed (Error message on the fetcher bus)
+ * - the download was canceled
+ */
+ GST_LOG_OBJECT (demux, "Waiting to fetch the URI");
+ g_cond_wait (demux->pldownloader->cond, demux->pldownloader->lock);
+ GST_LOG_OBJECT (demux, "Recived signal to shutdown...");
+
+ if (demux->playlistCookie)
+ g_strfreev (demux->playlistCookie);
+
+ g_object_get (demux->pldownloader->urisrc, "cookies", &demux->playlistCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after PLAYLIST download : %s", demux->playlistCookie ? *(demux->playlistCookie) : NULL);
+
+ if(demux->playlistCookie){
+ g_strfreev(demux->lastCookie);
+ g_free(demux->lastDomain);
+ demux->lastCookie = g_strdupv(demux->playlistCookie);
+ demux->lastDomain = g_strdup(demux->playlistDomain);
+ }
+
+ gst_hlsdemux2_destroy_playlist_download (demux);
+
+ if (demux->cancelled || demux->flushing)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static gboolean
+gst_hlsdemux2_update_playlist (GstHLSDemux2 * demux, gboolean update, gboolean *is_error)
+{
+ gchar *playlist = NULL;
+ gboolean updated = FALSE;
+ const gchar *playlist_uri = gst_m3u8_client_get_current_uri (demux->client);
+
+ *is_error = FALSE;
+
+ if (!gst_m3u8_client_is_playlist_download_needed (demux->client)) {
+ GST_INFO_OBJECT (demux, "Playlist download is not needed...");
+ return TRUE;
+ }
+
+ g_mutex_lock (demux->pldownloader->lock);
+
+try_again:
+
+ if (!gst_hlsdemux2_download_playlist (demux, playlist_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create playlist download");
+ goto error;
+ }
+
+ if ((demux->pldownloader->playlist == NULL) || (GST_BUFFER_DATA(demux->pldownloader->playlist) == NULL)) {
+ // TODO: don't exact reason, why eos is coming from soup without data...
+ GST_ERROR_OBJECT (demux, "Received playlist eos, without playlist data -> TRY AGAIN");
+ goto try_again;
+ }
+
+ /* reset the count */
+ demux->soup_request_fail_cnt = HLSDEMUX2_SOUP_FAILED_CNT;
+
+ playlist = gst_hlsdemux2_src_buf_to_utf8_playlist (demux->pldownloader->playlist);
+ if (!playlist) {
+ GST_ERROR_OBJECT(demux, "failed to create playlist");
+ goto error;
+ }
+ demux->pldownloader->playlist = NULL;
+
+ updated = gst_m3u8_client_update (demux->client, playlist);
+ g_mutex_unlock (demux->pldownloader->lock);
+
+ return updated;
+
+error:
+ if (demux->flushing)
+ *is_error = TRUE;
+ g_mutex_unlock (demux->pldownloader->lock);
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_create_key_download (GstHLSDemux2 *demux, const gchar *key_uri)
+{
+ if (!gst_uri_is_valid (key_uri)) {
+ GST_ERROR_OBJECT (demux, "invalid uri : %s...", key_uri == NULL ? "NULL" : key_uri);
+ return FALSE;
+ }
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (demux,"returning from download key due to cancel or flushing..");
+ return FALSE;
+ }
+
+ demux->kdownloader->pipe = gst_pipeline_new ("keydownloader");
+ if (!demux->kdownloader->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+
+ demux->kdownloader->bus = gst_pipeline_get_bus (GST_PIPELINE (demux->kdownloader->pipe));
+ gst_bus_add_watch (demux->kdownloader->bus, (GstBusFunc)gst_hlsdemux2_key_download_bus_cb, demux);
+ gst_object_unref (demux->kdownloader->bus);
+
+ GST_INFO_OBJECT (demux, "Creating source element for the URI : %s", key_uri);
+
+ demux->kdownloader->urisrc = gst_element_make_from_uri (GST_URI_SRC, key_uri, "keyurisrc");
+ if (!demux->kdownloader->urisrc) {
+ GST_ERROR_OBJECT (demux, "failed to create urisrc");
+ return FALSE;
+ }
+
+ g_object_set (G_OBJECT (demux->kdownloader->urisrc), "timeout", HLSDEMUX2_HTTP_TIMEOUT, NULL);
+ g_object_set (G_OBJECT (demux->kdownloader->urisrc), "user-agent", demux->user_agent, NULL);
+ g_object_set (G_OBJECT (demux->kdownloader->urisrc), "ahs-streaming", TRUE, NULL);
+
+ demux->keyDomain = gst_hlsdemux2_uri_get_domain(demux, key_uri);
+ if(!g_strcmp0(demux->keyDomain, demux->lastDomain) && demux->lastCookie) {
+ g_strfreev(demux->keyCookie);
+ demux->keyCookie = g_strdupv(demux->lastCookie);
+ }
+
+ if (demux->keyCookie) {
+ GST_DEBUG_OBJECT (demux, "Setting cookies before KEY download goto PLAYING : %s", *(demux->keyCookie));
+ g_object_set (demux->kdownloader->urisrc, "cookies", demux->keyCookie, NULL);
+ }
+
+ demux->kdownloader->sink = gst_element_factory_make ("appsink", "keyfilesink");
+ if (!demux->kdownloader->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create playlist sink element");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (demux->kdownloader->sink), "sync", FALSE, "emit-signals", TRUE, NULL);
+ g_signal_connect (demux->kdownloader->sink, "new-buffer", G_CALLBACK (gst_hlsdemux2_on_key_buffer), demux);
+
+ gst_bin_add_many (GST_BIN (demux->kdownloader->pipe), demux->kdownloader->urisrc, demux->kdownloader->sink, NULL);
+
+ if (!gst_element_link_many (demux->kdownloader->urisrc, demux->kdownloader->sink, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link src & demux elements...");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_destroy_key_download (GstHLSDemux2 * demux)
+{
+ if (demux->kdownloader->pipe) {
+ GST_DEBUG_OBJECT (demux, "Shutting down key download pipeline");
+ gst_element_set_state (demux->kdownloader->pipe, GST_STATE_NULL);
+ gst_element_get_state (demux->kdownloader->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ gst_object_unref (demux->kdownloader->pipe);
+ demux->kdownloader->pipe = NULL;
+ }
+}
+
+static gboolean
+gst_hlsdemux2_download_key (GstHLSDemux2 *demux, const gchar * uri)
+{
+ GstStateChangeReturn ret;
+
+ if (demux->key_uri) {
+ g_free (demux->key_uri);
+ }
+ demux->key_uri = g_strdup (uri);
+
+ g_mutex_lock (demux->kdownloader->lock);
+
+ if (!gst_hlsdemux2_create_key_download (demux, uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create key download pipeline");
+ return FALSE;
+ }
+
+ ret = gst_element_set_state (demux->kdownloader->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (demux, "set_state failed...");
+ 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 key URI");
+ g_cond_wait (demux->kdownloader->cond, demux->kdownloader->lock);
+ GST_DEBUG_OBJECT (demux, "Recived signal to shutdown key downloader...");
+ g_mutex_unlock (demux->kdownloader->lock);
+
+ if (demux->keyCookie) {
+ g_object_get (demux->kdownloader->urisrc, "cookies", &demux->keyCookie, NULL);
+ }
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after KEY download : %s", demux->keyCookie ? *(demux->keyCookie) : NULL);
+
+ if(demux->keyCookie){
+ g_strfreev(demux->lastCookie);
+ g_free(demux->lastDomain);
+ demux->lastCookie = g_strdupv(demux->keyCookie);
+ demux->lastDomain = g_strdup(demux->keyDomain);
+ }
+
+ gst_hlsdemux2_destroy_key_download (demux);
+
+ if (demux->cancelled || demux->flushing)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_have_type_cb (GstElement * typefind, guint probability,
+ GstCaps * caps, GstHLSDemux2 * demux)
+{
+ GstStructure *structure = NULL;
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ GST_DEBUG_OBJECT (demux, "typefind found caps %" GST_PTR_FORMAT, caps);
+
+ if (gst_structure_has_name (structure, "video/mpegts")) {
+ /* found ts fragment */
+ demux->fdownloader->demuxer = gst_element_factory_make ("mpegtsdemux", "tsdemux");
+ if (!demux->fdownloader->demuxer) {
+ GST_ERROR_OBJECT (demux, "failed to create mpegtsdemux element");
+ GST_ELEMENT_ERROR (demux, CORE, MISSING_PLUGIN, ("failed to create mpegtsdemuxer element"), (NULL));
+ return;
+ }
+ g_signal_connect (demux->fdownloader->demuxer, "pad-added", G_CALLBACK (gst_hlsdemux2_new_pad_added), demux);
+
+ gst_bin_add (GST_BIN (demux->fdownloader->pipe), demux->fdownloader->demuxer);
+
+ /* set queue element to PLAYING state */
+ gst_element_set_state (demux->fdownloader->demuxer, GST_STATE_PLAYING);
+
+ if (!gst_element_link (demux->fdownloader->typefind, demux->fdownloader->demuxer)) {
+ GST_ERROR_OBJECT (demux, "failed to link src and demux elements...");
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("failed to link typefind & demuxer"), (NULL));
+ return;
+ }
+ } else if (gst_structure_has_name (structure, "application/x-id3")) {
+ /* found ts fragment */
+ demux->fdownloader->demuxer = gst_element_factory_make ("id3demux", "id3demuxer");
+ if (!demux->fdownloader->demuxer) {
+ GST_ERROR_OBJECT (demux, "failed to create mpegtsdemux element");
+ GST_ELEMENT_ERROR (demux, CORE, MISSING_PLUGIN, ("failed to create id3demuxer element"), (NULL));
+ return;
+ }
+ g_signal_connect (demux->fdownloader->demuxer, "pad-added", G_CALLBACK (gst_hlsdemux2_new_pad_added), demux);
+
+ gst_bin_add (GST_BIN (demux->fdownloader->pipe), demux->fdownloader->demuxer);
+
+ /* set queue element to PLAYING state */
+ gst_element_set_state (demux->fdownloader->demuxer, GST_STATE_PLAYING);
+
+ if (!gst_element_link_many (demux->fdownloader->typefind, demux->fdownloader->demuxer, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link src and demux elements...");
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("failed to link typefind & demuxer"), (NULL));
+ return;
+ }
+
+ /* as the fragment does not contain in container format, force the timestamps based on fragment duration */
+ demux->fdownloader->force_timestamps = TRUE;
+ }
+}
+
+static gboolean
+gst_hlsdemux2_download_monitor_thread (GstHLSDemux2 *demux)
+{
+ GTimeVal post_msg_update = {0, };
+
+ g_mutex_lock (demux->post_msg_lock);
+ GST_INFO_OBJECT (demux, "waiting for streaming to start to post message...");
+ g_cond_wait (demux->post_msg_start, demux->post_msg_lock);
+ GST_INFO_OBJECT (demux, "Start posting messages...");
+ g_mutex_unlock (demux->post_msg_lock);
+
+ while (TRUE) {
+ gboolean do_post = FALSE;
+ guint64 percent = 0;
+ guint idx = 0;
+ guint64 total_percent = 0;
+ gboolean bret = FALSE;
+
+ if (demux->cancelled) {
+ GST_WARNING_OBJECT (demux, "returning msg posting thread...");
+ return TRUE;
+ }
+
+ /* schedule the next update using the target duration field of the
+ * playlist */
+ post_msg_update.tv_sec = 0;
+ post_msg_update.tv_usec = 0;
+
+ g_get_current_time (&post_msg_update);
+ g_time_val_add (&post_msg_update, 200000); // posts buffering msg after every 0.5 sec
+
+ /* verify fast switch from here */
+ g_mutex_lock (demux->fdownloader->lock);
+ if (demux->fdownloader->ndownloaded
+ && !demux->fdownloader->applied_fast_switch && demux->fdownloader->download_start_ts) {
+ gst_hlsdemux2_check_fast_switch (demux);
+ }
+ g_mutex_unlock (demux->fdownloader->lock);
+
+ /* calculate avg percent of streams' percentages */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *stream = demux->streams[idx];
+ if (stream ) {
+ guint64 stream_percent = 0;
+
+ stream_percent = ((stream->cached_duration * 100) / (demux->total_cache_duration / DEFAULT_NUM_FRAGMENTS_CACHE));
+ GST_DEBUG_OBJECT (stream->pad, "stream percent = %"G_GUINT64_FORMAT, stream_percent);
+ total_percent += stream_percent;
+ }
+ }
+ percent = total_percent / demux->active_stream_cnt;
+
+ if (demux->is_buffering) {
+ do_post = TRUE;
+ if (percent >= 100)
+ demux->is_buffering = FALSE;
+ } else {
+ if (percent < 10) {
+ /* if buffering drops below 10% lets start posting msgs */
+ demux->is_buffering = TRUE;
+ do_post = TRUE;
+ }
+ }
+
+ if (do_post) {
+ GstMessage *message;
+
+ if (percent > 100) {
+ //GST_LOG_OBJECT (demux, " exceeded percent = %d", (gint) percent);
+ percent = 100;
+ }
+
+ if (demux->end_of_playlist) {
+ GST_INFO_OBJECT (demux, "end of the playlist reached... always post 100%");
+ percent = 100;
+ }
+
+ if (percent != demux->percent) {
+ g_mutex_lock (demux->buffering_lock);
+ demux->percent = percent;
+ g_mutex_unlock (demux->buffering_lock);
+
+ GST_LOG_OBJECT (demux, "buffering %d percent", (gint) percent);
+
+ /* posting buffering to internal bus, which will take average & post to main bus */
+ message = gst_message_new_buffering (GST_OBJECT_CAST (demux), (gint) demux->percent);
+ if (!gst_element_post_message (GST_ELEMENT_CAST (demux), message)) {
+ GST_WARNING_OBJECT (demux, "failed to post buffering msg...");
+ }
+ }
+ }
+
+ g_mutex_lock (demux->post_msg_lock);
+ /* block until the next scheduled update or the exit signal */
+ bret = g_cond_timed_wait (demux->post_msg_exit, demux->post_msg_lock, &post_msg_update);
+ g_mutex_unlock (demux->post_msg_lock);
+
+ if (bret) {
+ GST_WARNING_OBJECT (demux, "signalled to exit posting msgs...");
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_hlsdemux2_fragurisrc_event_handler (GstPad * pad, GstEvent *event, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ GTimeVal time = {0, };
+
+ GST_INFO_OBJECT (demux, "received EOS from fragment downloader's httpsrc");
+
+ if (demux->fdownloader->error_rcvd) {
+ GST_WARNING_OBJECT (demux, "received EOS event after ERROR.. discard this");
+ demux->fdownloader->src_downloaded_size = 0;
+ demux->fdownloader->download_stop_ts = demux->fdownloader->download_start_ts = 0;
+ return FALSE;
+ }
+
+ /* download rate calculation : note down stop time*/
+ g_get_current_time (&time);
+ demux->fdownloader->download_stop_ts = gst_util_get_timestamp();
+
+ GST_INFO_OBJECT (demux, "download stop_ts = %"G_GUINT64_FORMAT, demux->fdownloader->download_stop_ts);
+ GST_INFO_OBJECT (demux, "time taken to download current fragment = %"G_GUINT64_FORMAT, demux->fdownloader->download_stop_ts - demux->fdownloader->download_start_ts);
+ GST_INFO_OBJECT (demux, "fragment downloaded size = %"G_GUINT64_FORMAT" and content_size = %"G_GUINT64_FORMAT,
+ demux->fdownloader->src_downloaded_size, demux->fdownloader->content_size);
+
+ if (demux->fdownloader->src_downloaded_size && (demux->fdownloader->src_downloaded_size == demux->fdownloader->content_size)) {
+ guint64 download_rate = 0;
+ guint i = 0;
+ guint64 dsum = 0;
+
+ /* calculate current fragment download rate */
+ download_rate = (demux->fdownloader->src_downloaded_size * 8 * GST_SECOND) /
+ (demux->fdownloader->download_stop_ts - demux->fdownloader->download_start_ts);
+
+ if (demux->fdownloader->avg_frag_drates->len == HLSDEMUX2_MAX_N_PAST_FRAG_DOWNLOADRATES) {
+ /* removing starting element */
+ g_array_remove_index (demux->fdownloader->avg_frag_drates, 0);
+ GST_DEBUG_OBJECT (demux, "removing index from download rates");
+ }
+ g_array_append_val (demux->fdownloader->avg_frag_drates, download_rate);
+
+ for (i = 0 ; i < demux->fdownloader->avg_frag_drates->len; i++) {
+ dsum += g_array_index (demux->fdownloader->avg_frag_drates, guint64, i);
+ }
+
+ demux->fdownloader->download_rate = dsum / demux->fdownloader->avg_frag_drates->len;
+
+ GST_INFO_OBJECT (demux, " >>>> DOWNLOAD RATE = %"G_GUINT64_FORMAT, demux->fdownloader->download_rate);
+ } else {
+ GST_WARNING_OBJECT (demux, "something wrong better not to calculate download rate with this and discard EOS...");
+ demux->fdownloader->src_downloaded_size = 0;
+ demux->fdownloader->download_stop_ts = demux->fdownloader->download_start_ts = 0;
+ return FALSE;
+ }
+
+ demux->fdownloader->src_downloaded_size = 0;
+ demux->fdownloader->download_stop_ts = demux->fdownloader->download_start_ts = 0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_hlsdemux2_check_fast_switch (GstHLSDemux2 *demux)
+{
+ GstClockTime elapsed_time = 0;
+ guint i = 0;
+ guint64 download_rate = 0;
+
+ elapsed_time = gst_util_get_timestamp();
+
+ if (gst_m3u8_client_has_variant_playlist (demux->client) &&
+ ((elapsed_time - demux->fdownloader->download_start_ts) >
+ (HLSDEMUX2_FAST_CHECK_WARNING_TIME_FACTOR * demux->target_duration))) {
+ guint64 drate_sum = 0;
+ GList *previous_variant, *current_variant;
+ gint old_bandwidth, new_bandwidth;
+
+ GST_INFO_OBJECT (demux, "Doing fast switch using array len %d...", demux->fdownloader->avg_chunk_drates->len);
+
+ for (i = 0 ; i < demux->fdownloader->avg_chunk_drates->len; i++) {
+ drate_sum += g_array_index (demux->fdownloader->avg_chunk_drates, guint64, i);
+ }
+
+ /* 0.9 factor is for compensating for connection time*/
+ download_rate = (drate_sum * 0.9) / demux->fdownloader->avg_chunk_drates->len;
+
+ GST_INFO_OBJECT (demux, "average chunk download rate = %"G_GUINT64_FORMAT, download_rate);
+
+ if ((elapsed_time - demux->fdownloader->download_start_ts) >
+ (HLSDEMUX2_FAST_CHECK_CRITICAL_TIME_FACTOR * demux->target_duration)) {
+ GST_WARNING_OBJECT (demux, "Reached FAST_SWITCH critical point...do fast_switch");
+ goto fast_switch;
+ }
+
+ previous_variant = demux->client->main->current_variant;
+ current_variant = gst_m3u8_client_get_playlist_for_bitrate (demux->client, download_rate);
+
+ old_bandwidth = GST_M3U8 (previous_variant->data)->bandwidth;
+ new_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
+
+ GST_INFO_OBJECT (demux, "variant's bandwidth got using average chunk download rate is %d", new_bandwidth);
+
+ /* Don't do anything else if the playlist is the same */
+ if (new_bandwidth == old_bandwidth) {
+ GST_WARNING_OBJECT (demux, "Either BW is recovering or NO LOWER variant available than current, DON'T DO FAST_SWITCH...");
+ return TRUE;
+ }
+
+ GST_WARNING_OBJECT (demux, "Going to do fast_switch due to BW problem...");
+ goto fast_switch;
+ }
+
+ return TRUE;
+
+fast_switch:
+
+ demux->fdownloader->download_rate = download_rate;
+
+ /* flush previous fragment downloads history */
+ for (i = 3; i ; --i) {
+ g_array_remove_index (demux->fdownloader->avg_frag_drates, i - 1);
+ }
+
+ GST_INFO_OBJECT (demux, "in fast download rate : len %d...", demux->fdownloader->avg_frag_drates->len);
+ g_array_append_val (demux->fdownloader->avg_frag_drates, demux->fdownloader->download_rate);
+
+ GST_INFO_OBJECT (demux, " >>>> DOWNLOAD RATE in fast switch = %"G_GUINT64_FORMAT, demux->fdownloader->download_rate);
+
+ demux->fdownloader->applied_fast_switch = TRUE;
+
+ /* close current fragment */
+ g_cond_signal (demux->fdownloader->cond);
+
+ return FALSE;
+
+}
+
+static gboolean
+gst_hlsdemux2_fragurisrc_buffer_handler (GstPad * pad, GstBuffer *buffer, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GstClockTime elapsed_ts = 0;
+ guint64 chunk_drate = 0;
+
+ /* if block_switch already enabled no need to cross check. IMP check*/
+ if (demux->fdownloader->applied_fast_switch) {
+ GST_WARNING_OBJECT (demux, "already applied fast_switch.. drop current buffer");
+ return FALSE;
+ }
+
+ demux->fdownloader->src_downloaded_size += GST_BUFFER_SIZE(buffer);
+
+ if (demux->fdownloader->first_buffer) {
+ /* get the content size for doing last buffer decryption & for vaidating complete download happend or not */
+ GST_INFO_OBJECT (demux, "just received first buffer...");
+ g_object_get (demux->fdownloader->urisrc, "content-size", &demux->fdownloader->content_size, NULL);
+ GST_INFO_OBJECT (demux, "content size = %"G_GUINT64_FORMAT, demux->fdownloader->content_size);
+ demux->fdownloader->first_buffer = FALSE;
+ demux->fdownloader->chunk_start_ts = gst_util_get_timestamp();
+ demux->fdownloader->chunk_downloaded_size = 0;
+ }
+
+
+ demux->fdownloader->chunk_downloaded_size += GST_BUFFER_SIZE(buffer);
+ elapsed_ts = gst_util_get_timestamp();
+
+ if ((elapsed_ts - demux->fdownloader->chunk_start_ts) > HLSDEMUX2_CHUNK_TIME_DURATION) {
+ GST_DEBUG_OBJECT (demux, "chunk downloaded time = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(elapsed_ts - demux->fdownloader->chunk_start_ts));
+
+ chunk_drate = (demux->fdownloader->chunk_downloaded_size * 8 * GST_SECOND) / (elapsed_ts - demux->fdownloader->chunk_start_ts);
+ demux->fdownloader->chunk_start_ts = elapsed_ts;
+ demux->fdownloader->chunk_downloaded_size = 0;
+
+ GST_DEBUG_OBJECT(demux, "CHUNK DOWNLOAD RATE = %"G_GUINT64_FORMAT, chunk_drate);
+
+ if (demux->fdownloader->avg_chunk_drates->len == HLSDEMUX2_NUM_CHUNK_DOWNLOADS_FAST_SWITCH) {
+ /* removing starting element */
+ g_array_remove_index (demux->fdownloader->avg_chunk_drates, 0);
+ }
+ g_array_append_val (demux->fdownloader->avg_chunk_drates, chunk_drate);
+ }
+
+ GST_LOG_OBJECT (demux, "fragment downloaded size at urisrc = %"G_GUINT64_FORMAT, demux->fdownloader->src_downloaded_size);
+
+ return TRUE;
+}
+
+static gboolean
+gst_hlsdemux2_queue_buffer_handler (GstPad *pad, GstBuffer *buffer, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ unsigned char *encry_data = NULL;
+ unsigned char *outdata = NULL;
+ unsigned char *remained_data = NULL;
+ unsigned char* total_data = NULL;
+
+ demux->fdownloader->queue_downloaded_size += GST_BUFFER_SIZE(buffer);
+
+ GST_DEBUG_OBJECT (demux, "total downloaded size = %"G_GUINT64_FORMAT, demux->fdownloader->queue_downloaded_size);
+
+ if (demux->fdownloader->is_encrypted) {
+ guint encry_data_size = 0;
+ unsigned char *indata = GST_BUFFER_DATA (buffer);
+ guint insize = GST_BUFFER_SIZE (buffer);
+ gint out_len = 0;
+
+ if (demux->fdownloader->remaining_data && demux->fdownloader->remaining_size) {
+ GST_LOG_OBJECT (demux, "remain size = %d..", demux->fdownloader->remaining_size);
+
+ encry_data = (unsigned char *) malloc (insize + demux->fdownloader->remaining_size);
+ if (!encry_data) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ goto error;
+ }
+
+ /* copy remaining data in previous iteration */
+ memcpy (encry_data, demux->fdownloader->remaining_data, demux->fdownloader->remaining_size);
+ /* copy input encrypted data of current iteration */
+ memcpy (encry_data + demux->fdownloader->remaining_size, indata, insize);
+
+ encry_data_size = insize + demux->fdownloader->remaining_size;
+
+ free (demux->fdownloader->remaining_data);
+ demux->fdownloader->remaining_data = NULL;
+ demux->fdownloader->remaining_size = 0;
+ } else {
+ encry_data_size = insize;
+ encry_data = indata;
+ }
+
+ demux->fdownloader->remaining_size = encry_data_size % GST_HLS_DEMUX_AES_BLOCK_SIZE;
+
+ if (demux->fdownloader->remaining_size) {
+ GST_LOG_OBJECT (demux, "Remining in current iteration = %d\n", demux->fdownloader->remaining_size);
+ demux->fdownloader->remaining_data = (gchar *) malloc (demux->fdownloader->remaining_size);
+ if ( demux->fdownloader->remaining_data == NULL) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ goto error;
+ }
+
+ /* copy remained portion to buffer */
+ memcpy (demux->fdownloader->remaining_data,
+ encry_data + encry_data_size - demux->fdownloader->remaining_size,
+ demux->fdownloader->remaining_size);
+ }
+
+ out_len = encry_data_size - demux->fdownloader->remaining_size + GST_HLS_DEMUX_AES_BLOCK_SIZE;
+ outdata = (unsigned char *) malloc (out_len);
+ if (!outdata) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ goto error;
+ }
+
+ if (!gst_m3u8_client_decrypt_update (demux->client, outdata, &out_len,
+ encry_data, encry_data_size - demux->fdownloader->remaining_size)) {
+ GST_ERROR_OBJECT (demux, "failed to decrypt...\n");
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED, ("failed to decrypt"), (NULL));
+ goto error;
+ }
+
+ if (encry_data != indata) {
+ g_free (encry_data);
+ encry_data = NULL;
+ }
+
+ g_free (GST_BUFFER_MALLOCDATA(buffer));
+
+ /* decrypt the final buffer */
+ if (demux->fdownloader->queue_downloaded_size == demux->fdownloader->content_size) {
+ gint remained_size = 0;
+ gint total_len = 0;
+
+ GST_LOG_OBJECT (demux, "remained data = %p & remained size = %d", demux->fdownloader->remaining_data, demux->fdownloader->remaining_size);
+
+ remained_data = (unsigned char *) malloc (GST_HLS_DEMUX_AES_BLOCK_SIZE);
+ if (!remained_data) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ goto error;
+ }
+ gst_m3u8_client_decrypt_final (demux->client, remained_data, &remained_size);
+ GST_LOG_OBJECT (demux,"remained size = %d\n", remained_size);
+
+ if (remained_size) {
+ total_len = out_len + remained_size;
+
+ total_data = (unsigned char *) malloc (total_len);
+ if (!total_data) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ goto error;
+ }
+
+ memcpy (total_data, outdata, out_len);
+ memcpy (total_data+out_len, remained_data, remained_size);
+
+ GST_BUFFER_MALLOCDATA(buffer) = GST_BUFFER_DATA(buffer) = total_data;
+ GST_BUFFER_SIZE(buffer) = total_len;
+ g_free (remained_data);
+ g_free (outdata);
+ } else {
+ GST_BUFFER_MALLOCDATA(buffer) = GST_BUFFER_DATA(buffer) = outdata;
+ GST_BUFFER_SIZE(buffer) = out_len;
+ g_free (remained_data);
+ }
+
+ if (!gst_m3u8_client_decrypt_deinit (demux->client)) {
+ GST_WARNING_OBJECT (demux, "decryption cleanup failed...");
+ }
+ } else {
+ GST_BUFFER_MALLOCDATA(buffer) = GST_BUFFER_DATA(buffer) = outdata;
+ GST_BUFFER_SIZE(buffer) = out_len;
+ }
+ }
+
+ return TRUE;
+
+error:
+ if (encry_data)
+ free (encry_data);
+
+ if (outdata)
+ free (outdata);
+
+ if (remained_data)
+ free (remained_data);
+
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_create_fragment_download (GstHLSDemux2 * demux, const gchar * uri)
+{
+ GstBus *bus = NULL;
+ GstPad *srcpad = NULL;
+
+ if (!gst_uri_is_valid (uri)) {
+ GST_ERROR_OBJECT (demux, "invalid uri : %s...", uri == NULL ? "NULL" : uri);
+ return FALSE;
+ }
+
+ /* re-initialize fragment variables */
+ demux->fdownloader->first_buffer = TRUE;
+ demux->fdownloader->cur_stream_cnt = 0;
+ demux->fdownloader->error_rcvd = FALSE;
+
+ demux->fdownloader->pipe = gst_pipeline_new ("frag-fdownloader");
+ if (!demux->fdownloader->pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ return FALSE;
+ }
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (demux->fdownloader->pipe));
+ gst_bus_add_watch (bus, (GstBusFunc)gst_hlsdemux2_fragment_download_bus_cb, demux);
+ gst_object_unref (bus);
+
+ GST_INFO_OBJECT (demux, "Creating source element for the URI : %s", uri);
+
+ demux->fdownloader->urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "fragurisrc");
+ if (!demux->fdownloader->urisrc) {
+ GST_ERROR_OBJECT (demux, "failed to create urisrc");
+ return FALSE;
+ }
+
+ g_object_set (G_OBJECT (demux->fdownloader->urisrc), "timeout", HLSDEMUX2_HTTP_TIMEOUT, NULL);
+ g_object_set (G_OBJECT (demux->fdownloader->urisrc), "user-agent", demux->user_agent, NULL);
+ g_object_set (G_OBJECT (demux->fdownloader->urisrc), "ahs-streaming", TRUE, NULL);
+ g_object_set (G_OBJECT (demux->fdownloader->urisrc), "blocksize", DEFAULT_BLOCKSIZE, NULL);
+
+ demux->fragDomain = gst_hlsdemux2_uri_get_domain(demux, uri);
+ if(!g_strcmp0 (demux->fragDomain, demux->lastDomain) && demux->lastCookie) {
+ g_strfreev(demux->fragCookie);
+ demux->fragCookie = g_strdupv (demux->lastCookie);
+ }
+
+ if (demux->fragCookie) {
+ GST_DEBUG_OBJECT (demux, "Setting cookies before FRAG download goto PLAYING: %s", *(demux->fragCookie));
+ g_object_set (demux->fdownloader->urisrc, "cookies", demux->fragCookie, NULL);
+ }
+
+ srcpad = gst_element_get_static_pad (demux->fdownloader->urisrc, "src");
+ if (!srcpad) {
+ GST_ERROR_OBJECT (demux, "failed to get source pad from urisrc...");
+ return FALSE;
+ }
+
+ gst_pad_add_event_probe (srcpad, G_CALLBACK (gst_hlsdemux2_fragurisrc_event_handler), demux);
+ gst_pad_add_buffer_probe (srcpad, G_CALLBACK (gst_hlsdemux2_fragurisrc_buffer_handler), demux);
+
+ gst_object_unref (srcpad);
+
+ demux->fdownloader->queue = gst_element_factory_make ("queue2", "frag_queue");
+ if (!demux->fdownloader->queue) {
+ GST_ERROR_OBJECT (demux, "failed to create queue2");
+ return FALSE;
+ }
+
+ g_object_set (G_OBJECT (demux->fdownloader->queue),
+ "max-size-buffers", 0,
+ "max-size-bytes", 0,
+ "max-size-time", 0 * GST_SECOND, NULL);
+
+ srcpad = gst_element_get_static_pad (demux->fdownloader->queue, "src");
+ if (!srcpad) {
+ GST_ERROR_OBJECT (demux, "failed to get source pad from fragment queue...");
+ return FALSE;
+ }
+
+ gst_pad_add_buffer_probe (srcpad, G_CALLBACK (gst_hlsdemux2_queue_buffer_handler), demux);
+
+ demux->fdownloader->typefind = gst_element_factory_make ("typefind", "typefinder");
+ if (!demux->fdownloader->typefind) {
+ GST_ERROR_OBJECT (demux, "failed to create typefind element");
+ gst_object_unref (srcpad);
+ return FALSE;
+ }
+
+ gst_bin_add_many (GST_BIN (demux->fdownloader->pipe),
+ demux->fdownloader->urisrc, demux->fdownloader->queue, demux->fdownloader->typefind, NULL);
+
+ if (!gst_element_link_many (demux->fdownloader->urisrc,
+ demux->fdownloader->queue, demux->fdownloader->typefind, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link src, queue & typefind elements...");
+ gst_object_unref (srcpad);
+ return FALSE;
+ }
+
+ /* typefind callback to determine file type */
+ g_signal_connect (demux->fdownloader->typefind, "have-type", G_CALLBACK (gst_hlsdemux2_have_type_cb), demux);
+
+ gst_object_unref (srcpad);
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_destroy_fragment_download (GstHLSDemux2 * demux)
+{
+ if (demux->fdownloader->pipe) {
+ GST_DEBUG_OBJECT (demux, "completely closing fragment downloader pipeline");
+ gst_element_set_state (demux->fdownloader->pipe, GST_STATE_NULL);
+ gst_element_get_state (demux->fdownloader->pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ GST_DEBUG_OBJECT (demux, "completely closed fragment downloader pipeline");
+ gst_object_unref (demux->fdownloader->pipe);
+ demux->has_image_buffer = FALSE;
+ demux->fdownloader->pipe = NULL;
+ demux->fdownloader->cur_stream_cnt = 0;
+ g_list_free (demux->fdownloader->sinkbins); // all sink elements will be unreffed when pipeline is unreffed
+ demux->fdownloader->sinkbins = 0;
+ }
+}
+
+static gboolean
+gst_hlsdemux2_download_fragment (GstHLSDemux2 *demux, const gchar * uri)
+{
+ GstStateChangeReturn ret;
+
+ g_mutex_lock (demux->fdownloader->lock);
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (demux,"returning from download fragment due to cancel or flushing..");
+ g_mutex_unlock (demux->fdownloader->lock);
+ return FALSE;
+ }
+
+ if (demux->frag_uri) {
+ g_free (demux->frag_uri);
+ }
+ demux->frag_uri = g_strdup (uri);
+
+ demux->fdownloader->get_next_frag = FALSE;
+
+ if (!gst_hlsdemux2_create_fragment_download (demux, uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ g_mutex_unlock (demux->fdownloader->lock);
+ return FALSE;
+ }
+
+ /* download rate calculation : note down start time*/
+ demux->fdownloader->download_start_ts = gst_util_get_timestamp();
+ GST_INFO_OBJECT (demux, "download start_ts = %"G_GUINT64_FORMAT, demux->fdownloader->download_start_ts);
+
+ ret = gst_element_set_state (demux->fdownloader->pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (demux, "set_state failed...");
+ g_mutex_unlock (demux->fdownloader->lock);
+ return FALSE;
+ }
+ gst_element_get_state (demux->fdownloader->pipe, NULL, NULL, -1);
+
+ /* 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_cond_wait (demux->fdownloader->cond, demux->fdownloader->lock);
+ GST_INFO_OBJECT (demux, "Recived signal to shutdown...");
+
+ if (demux->fragCookie)
+ g_strfreev (demux->fragCookie);
+
+ g_object_get (demux->fdownloader->urisrc, "cookies", &demux->fragCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after FRAG download : %s", demux->fragCookie ? *(demux->fragCookie) : NULL);
+
+ if (demux->fdownloader->applied_fast_switch) {
+ if (demux->fdownloader->src_downloaded_size == demux->fdownloader->content_size) {
+ /* to handle case, when both fast_switch & bus_callback signalled at same moment */
+ GST_WARNING_OBJECT (demux, "ignoring fast_switch as fragment download is completed...");
+ demux->fdownloader->applied_fast_switch = FALSE;
+ } else {
+ GST_WARNING_OBJECT (demux, "decrement the media sequence by one, due to fast_switch");
+ gst_m3u8_client_decrement_sequence (demux->client);
+ }
+ }
+
+ if (demux->cancelled || demux->flushing ||
+ demux->fdownloader->get_next_frag || demux->fdownloader->applied_fast_switch) {
+ GstBuffer *buf = NULL;
+ guint idx = 0;
+
+ /* clear stream queues if we already downloaded some data */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *stream = demux->streams[idx];
+ if (stream ) {
+ while (!g_queue_is_empty (stream->downloader_queue)) {
+ buf = g_queue_pop_head (stream->downloader_queue);
+ gst_buffer_unref (buf);
+ }
+ GST_LOG_OBJECT (stream->pad, "cleared stream download queue...");
+ g_queue_clear (stream->downloader_queue);
+ }
+ }
+ }
+
+ if(demux->fragCookie){
+ g_strfreev(demux->lastCookie);
+ g_free(demux->lastDomain);
+ demux->lastCookie = g_strdupv(demux->fragCookie);
+ demux->lastDomain = g_strdup(demux->fragDomain);
+ }
+
+ if(demux->active_stream_cnt == 1 && !demux->private_stream) {
+ gchar *image_header = NULL;
+ GValue codec_type = G_VALUE_INIT;
+ GValue image_data = G_VALUE_INIT;
+ GstMessage * tag_message = NULL;
+ struct stat stat_results;
+ gint iread = 0;
+ GstBuffer *image_buffer = NULL;
+ GstTagList * tag_list = gst_tag_list_new();
+ int dummy_fp = -1;
+
+ GST_INFO_OBJECT (demux, "Going to show fixed image");
+ dummy_fp = open (PREDEFINED_IMAGE_FRAME_LOCATION, O_RDONLY);
+ if (dummy_fp < 0) {
+ GST_WARNING_OBJECT (demux, "failed to open fixed image file : %s...", PREDEFINED_IMAGE_FRAME_LOCATION);
+ goto skip_posting_image;
+ }
+
+ GST_LOG_OBJECT (demux, "opened fixed image file %s successfully...", PREDEFINED_IMAGE_FRAME_LOCATION);
+
+ if (fstat (dummy_fp, &stat_results) < 0) {
+ GST_WARNING_OBJECT (demux, "failed to get stats of a file...");
+ close (dummy_fp);
+ goto skip_posting_image;
+ }
+
+ GST_LOG_OBJECT (demux, "size of the dummy file = %d", stat_results.st_size);
+
+ image_buffer = gst_buffer_new_and_alloc (stat_results.st_size);
+ if (!image_buffer) {
+ GST_ERROR_OBJECT (demux, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ close (dummy_fp);
+ goto error;
+ }
+
+ iread = read (dummy_fp, GST_BUFFER_DATA (image_buffer), stat_results.st_size);
+
+ GST_LOG_OBJECT (demux, "read size = %d successfully", iread);
+
+ close (dummy_fp);
+
+ /* check whether it is same as previous image */
+ if (demux->prev_image_buffer &&
+ (GST_BUFFER_SIZE(demux->prev_image_buffer) == GST_BUFFER_SIZE(image_buffer))) {
+ if (!memcmp (GST_BUFFER_DATA(demux->prev_image_buffer), GST_BUFFER_DATA(image_buffer), GST_BUFFER_SIZE(image_buffer))) {
+ GST_INFO_OBJECT (demux, "current & previous embedded images are same..no need to post image again");
+ gst_buffer_unref (image_buffer);
+ goto skip_posting_image;
+ }
+ }
+
+ if (demux->prev_image_buffer) {
+ gst_buffer_unref (demux->prev_image_buffer);
+ }
+ demux->prev_image_buffer = gst_buffer_copy(image_buffer);
+
+ g_value_init (&codec_type, G_TYPE_STRING);
+ g_value_init (&image_data, GST_TYPE_BUFFER);
+ gst_value_set_buffer(&image_data, image_buffer);
+
+ image_header = g_strndup((gchar *) image_buffer->data, 8);
+
+ if((image_header[0] == 0xFF) && (image_header[1] == 0xD8)) {
+ GST_INFO_OBJECT(demux, "Found JPEG image header");
+ g_value_set_static_string (&codec_type, "image/jpeg");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ } else if (!g_strcmp0(image_header, "\211PNG\015\012\032\012")) {
+ GST_INFO_OBJECT(demux, "Found PNG image header");
+ g_value_set_static_string (&codec_type, "image/png");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ } else {
+ g_value_set_static_string (&codec_type, "image/unknown");
+ GST_INFO_OBJECT(demux, "Unknown image header");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ }
+
+ gst_tag_list_add_value (tag_list, GST_TAG_MERGE_APPEND, GST_TAG_IMAGE, &image_data);
+
+ GST_INFO_OBJECT(demux, "Set value : %s", g_value_get_string (&codec_type));
+ g_value_unset(&codec_type);
+
+ tag_message = gst_message_new_tag (GST_OBJECT_CAST (demux), tag_list);
+ if(!gst_element_post_message (GST_ELEMENT_CAST (demux), tag_message)) {
+ GST_ERROR_OBJECT (demux, "failed to post fixed image tag");
+ }
+
+ GST_INFO_OBJECT (demux, "successfully posted image tag...");
+ gst_buffer_unref (image_buffer);
+ g_free (image_header);
+ }
+
+skip_posting_image:
+
+ gst_hlsdemux2_destroy_fragment_download (demux);
+
+ if (demux->fdownloader->remaining_data) {
+ free (demux->fdownloader->remaining_data);
+ demux->fdownloader->remaining_data = NULL;
+ }
+
+ demux->fdownloader->remaining_size = 0;
+ demux->fdownloader->src_downloaded_size = 0;
+ demux->fdownloader->queue_downloaded_size = 0;
+ demux->fdownloader->download_stop_ts = demux->fdownloader->download_start_ts = 0;
+ demux->fdownloader->applied_fast_switch = FALSE;
+ demux->fdownloader->content_size = 0;
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (demux, "returning false due to flushing/cancelled");
+ goto error;
+ } else if (demux->fdownloader->get_next_frag || demux->fdownloader->applied_fast_switch) {
+ GST_INFO_OBJECT (demux, "Requesting next fragment due to error or same sequence number from different variant");
+ goto exit;
+ }
+
+ demux->fdownloader->ndownloaded++;
+
+ GST_INFO_OBJECT (demux, "number of fragments downloaded = %d", demux->fdownloader->ndownloaded);
+
+ /* reset the count */
+ demux->soup_request_fail_cnt = HLSDEMUX2_SOUP_FAILED_CNT;
+
+exit:
+ if (demux->private_stream) {
+ g_free (demux->private_stream);
+ demux->private_stream = NULL;
+ }
+ g_mutex_unlock (demux->fdownloader->lock);
+
+ return TRUE;
+
+error:
+ if (demux->private_stream) {
+ g_free (demux->private_stream);
+ demux->private_stream = NULL;
+ }
+ g_mutex_unlock (demux->fdownloader->lock);
+
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_change_playlist (GstHLSDemux2 * demux, guint max_bitrate, gboolean *is_switched)
+{
+ GList *previous_variant, *current_variant;
+ gint old_bandwidth, new_bandwidth;
+
+ previous_variant = demux->client->main->current_variant;
+ current_variant = gst_m3u8_client_get_playlist_for_bitrate (demux->client, max_bitrate);
+
+ old_bandwidth = GST_M3U8 (previous_variant->data)->bandwidth;
+ new_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
+
+ /* Don't do anything else if the playlist is the same */
+ if (new_bandwidth == old_bandwidth) {
+ if (is_switched)
+ *is_switched = FALSE;
+ return TRUE;
+ }
+
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ demux->client->main->current_variant = current_variant;
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+
+ gst_m3u8_client_set_current (demux->client, current_variant->data);
+
+ GST_INFO_OBJECT (demux, "Client was on %dbps, max allowed is %dbps, switching"
+ " to bitrate %dbps", old_bandwidth, max_bitrate, new_bandwidth);
+
+ gst_hlsdemux2_apply_disc(demux);
+
+ if (is_switched)
+ *is_switched = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_hlsdemux2_get_next_fragment (GstHLSDemux2 * demux, gboolean *is_error)
+{
+ const gchar *next_fragment_uri = NULL;;
+ gchar *next_fragment_key_uri = NULL;
+ gchar *iv = NULL;
+ GstClockTime duration;
+ GstClockTime timestamp;
+ gboolean discont = FALSE;
+ gboolean bret = TRUE;
+ GstClockTime lookup_time = 0;
+ guint i = 0;
+ GstHLSDemux2Stream *stream = NULL;
+
+ if (!demux->is_live) {
+ for (i = 0; i< HLSDEMUX2_STREAM_NUM; i++) {
+ stream = demux->streams[i];
+ if (stream) {
+ lookup_time = stream->lts + stream->frame_duration + (0.5 * GST_SECOND);
+ GST_INFO_OBJECT (stream->pad, "Lookup time = %"GST_TIME_FORMAT, GST_TIME_ARGS(lookup_time));
+ stream->frame_duration = 0;
+ if (stream->type == HLSDEMUX2_STREAM_AUDIO)
+ break;
+ }
+ }
+ }
+
+ if (!gst_m3u8_client_get_next_fragment (demux->client, lookup_time,
+ &discont, &next_fragment_uri, &duration, ×tamp, &next_fragment_key_uri, &iv)) {
+
+ GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments");
+
+ if (demux->is_live && (demux->client->update_failed_count < DEFAULT_FAILED_COUNT)) {
+ GST_WARNING_OBJECT (demux, "Could not get next fragment, try again after updating playlist...");
+ *is_error = FALSE;
+ return FALSE;
+ } else {
+ goto end_of_list;
+ }
+ }
+
+ demux->cfrag_dur = duration;
+ demux->fdownloader->cur_running_dur += duration;
+
+ GST_LOG_OBJECT (demux, "current running duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(demux->fdownloader->cur_running_dur));
+
+ GST_DEBUG_OBJECT (demux, "Fetching next fragment = %s & key uri = %s", next_fragment_uri, next_fragment_key_uri);
+
+ /* when hls requests next fragment due to some error like NOT_FOUND, then also we need to set apply disc */
+ if (discont || demux->fdownloader->get_next_frag)
+ gst_hlsdemux2_apply_disc (demux);
+
+ if (next_fragment_key_uri) {
+ /* download key data & initialize decryption with key data & IV */
+ GST_INFO_OBJECT (demux, "Fetching next fragment key %s", next_fragment_key_uri);
+
+ if (demux->kdownloader->prev_key_uri) {
+ if (strcmp (demux->kdownloader->prev_key_uri, next_fragment_key_uri)) {
+ /* if previous & current key uris are different, download new one */
+ if (demux->kdownloader->key) {
+ gst_buffer_unref (demux->kdownloader->key);
+ demux->kdownloader->key = NULL;
+ }
+
+ if (demux->kdownloader->prev_key_uri)
+ g_free (demux->kdownloader->prev_key_uri);
+
+ demux->kdownloader->prev_key_uri = g_strdup(next_fragment_key_uri);
+
+ /* download the key data as there is a change */
+ if (!gst_hlsdemux2_download_key (demux, next_fragment_key_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to download key...");
+ goto error;
+ }
+ } else {
+ GST_INFO_OBJECT (demux, "already having the key...no need to download again");
+ }
+ } else {
+ if (demux->kdownloader->key) {
+ gst_buffer_unref (demux->kdownloader->key);
+ demux->kdownloader->key = NULL;
+ }
+
+ demux->kdownloader->prev_key_uri = g_strdup(next_fragment_key_uri);
+
+ if (!gst_hlsdemux2_download_key (demux, next_fragment_key_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to download key...");
+ goto error;
+ }
+ /* reset the count */
+ demux->soup_request_fail_cnt = HLSDEMUX2_SOUP_FAILED_CNT;
+ }
+
+ if (!demux->kdownloader->key || !GST_BUFFER_DATA (demux->kdownloader->key)) {
+ GST_ERROR_OBJECT (demux, "eos received without key content...");
+ goto error;
+ }
+
+ if (!gst_m3u8_client_decrypt_init (demux->client, GST_BUFFER_DATA (demux->kdownloader->key), iv)) {
+ GST_ERROR_OBJECT (demux, "failed to initialize AES decryption...");
+ goto error;
+ }
+
+ demux->fdownloader->is_encrypted = TRUE;
+ }
+
+ if (!gst_hlsdemux2_download_fragment (demux, next_fragment_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to download fragment...");
+ goto error;
+ }
+
+exit:
+ if (next_fragment_uri)
+ g_free ((gpointer)next_fragment_uri);
+ if (next_fragment_key_uri)
+ g_free ((gpointer)next_fragment_key_uri);
+ if (iv)
+ g_free ((gpointer)iv);
+
+ return bret;
+
+error:
+ if (!demux->flushing)
+ *is_error = TRUE;
+ bret = FALSE;
+ goto exit;
+
+ end_of_list:
+ GST_INFO_OBJECT (demux, "Reached end of playlist, sending EOS");
+
+ gst_hlsdemux2_push_eos (demux);
+
+ demux->end_of_playlist = TRUE;
+ bret = FALSE;
+ goto exit;
+}
+
+
+static gboolean
+gst_hlsdemux2_switch_playlist (GstHLSDemux2 * demux, gboolean *is_switched)
+{
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ if (!demux->client->main->lists) {
+ /* not a variant to switch */
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+ GST_INFO_OBJECT (demux, "not a variant to switch...");
+ return TRUE;
+ }
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+
+ if (demux->force_lower_bitrate && (demux->fdownloader->ndownloaded % FORCE_LOW_BITRATE_AFTER_CNT == 0)) {
+ demux->fdownloader->download_rate = 0; // Using some temp lowest value
+ GST_WARNING_OBJECT (demux, "resetting to lowest one bitrate = %"G_GUINT64_FORMAT, demux->fdownloader->download_rate);
+ }
+
+ return gst_hlsdemux2_change_playlist (demux, demux->fdownloader->download_rate, is_switched);
+}
+
+static void
+gst_hlsdemux2_apply_disc (GstHLSDemux2 * demux)
+{
+ int i = 0;
+ GstHLSDemux2Stream *stream = NULL;
+
+ for (i = 0; i< HLSDEMUX2_STREAM_NUM; i++) {
+ stream = demux->streams[i];
+
+ if (stream) {
+ stream->apply_disc = TRUE;
+ GST_INFO_OBJECT (stream->pad, "apply discontinuity...");
+ }
+ }
+}
+
+static void
+gst_hlsdemux2_fragment_download_loop (GstHLSDemux2 * demux)
+{
+ gboolean is_error = FALSE;
+ gboolean bret = FALSE;
+ GTimeVal tmp_update = {0, };
+ GTimeVal current;
+ guint64 current_time = 0;
+ guint64 nextupdate_time = 0;
+
+ /* if variant playlist, get current subplaylist first */
+ if (gst_m3u8_client_has_variant_playlist(demux->client)) {
+ gboolean is_error = FALSE;
+
+ if (!gst_hlsdemux2_update_playlist (demux, FALSE, &is_error)) {
+ GST_ERROR_OBJECT (demux, "failed to update playlist. uri : %s", gst_m3u8_client_get_current_uri(demux->client));
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not update the playlist"), (NULL));
+ goto exit;
+ }
+ } else if (demux->is_live) {
+ /* single variant live.. so schedule for starting */
+ if (demux->is_live)
+ gst_hlsdemux2_schedule (demux);
+ }
+
+ if (demux->fdownloader->find_mediaseq) {
+ GList *walk;
+ GstClockTime current_pos, target_pos;
+ gint current_sequence;
+ GstM3U8MediaFile *file;
+ guint i = 0;
+ GstHLSDemux2Stream *stream = NULL;
+
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data);
+ current_sequence = file->sequence;
+ current_pos = 0 ;
+ target_pos = demux->fdownloader->seeked_pos;
+ for (walk = demux->client->current->files; walk; walk = walk->next) {
+ file = walk->data;
+
+ current_sequence = file->sequence;
+ if (current_pos <= target_pos &&
+ target_pos < current_pos + file->duration) {
+ break;
+ }
+ current_pos += file->duration;
+ }
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+
+ if (walk == NULL) {
+ GST_WARNING_OBJECT (demux, "Could not find seeked fragment");
+ }
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ GST_INFO_OBJECT (demux, "seeking to sequence %d", current_sequence);
+ demux->client->sequence = current_sequence;
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+ demux->ns_start = demux->fdownloader->cur_running_dur = current_pos; // NON-accurate seek
+
+ for (i = 0; i< HLSDEMUX2_STREAM_NUM; i++) {
+ stream = demux->streams[i];
+#ifdef LATEST_AV_SYNC
+ if (stream) {
+ stream->lts = stream->total_stream_time = current_pos;
+ GST_INFO_OBJECT (stream->pad, "Changed stream->lts to %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->lts));
+ }
+#else
+ if (stream && stream->type == HLSDEMUX2_STREAM_AUDIO) {
+ stream->lts = current_pos;
+ }
+#endif
+ }
+
+ demux->fdownloader->find_mediaseq = FALSE;
+ }
+
+ demux->target_duration = gst_m3u8_client_get_target_duration(demux->client);
+ demux->total_cache_duration = DEFAULT_NUM_FRAGMENTS_CACHE * demux->target_duration;
+ demux->is_live = gst_m3u8_client_is_live(demux->client);
+
+ GST_INFO_OBJECT (demux, "Total cache duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(demux->total_cache_duration));
+
+ while (TRUE) {
+
+ if (demux->cancelled)
+ goto exit;
+
+ /* get the next fragement */
+ bret = gst_hlsdemux2_get_next_fragment (demux, &is_error);
+
+ if (!bret) {
+ if (is_error) {
+ GST_ERROR_OBJECT (demux, "error in getting next fragment");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("could not download fragment"), (NULL));
+ goto exit;
+ } else {
+ if (!demux->end_of_playlist) {
+ if (demux->flushing) {
+ GST_WARNING_OBJECT (demux, "SEEK is in progress.. pause the task");
+ goto exit;
+ } else if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not update fragment & playlist update failed count = %d",
+ demux->client->update_failed_count);
+ } else {
+ GST_ERROR_OBJECT (demux, "Could not get next fragment..");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not get next fragment..."), (NULL));
+ goto exit;
+ }
+ } else {
+ GST_INFO_OBJECT (demux, "end of fragment download, doing pause");
+ goto exit;
+ }
+ }
+ } else {
+ /* reset the update failed count */
+ demux->client->update_failed_count = 0;
+ }
+
+ if (demux->cancelled)
+ goto exit;
+
+ if (demux->is_live) {
+ /* get the current time */
+ current.tv_sec = current.tv_usec = 0;
+ g_get_current_time (¤t);
+
+ current_time = ((guint64)current.tv_sec * (guint64)1000000) + (guint64)current.tv_usec;
+ nextupdate_time = ((guint64)demux->next_update.tv_sec * (guint64)1000000) + (guint64)demux->next_update.tv_usec;
+ }
+
+ if (demux->is_live && ((current_time > nextupdate_time) || !bret)) {
+ gboolean is_switched = FALSE;
+
+ /* try to switch to another bitrate if needed */
+ // TODO: take care of return value
+ gst_hlsdemux2_switch_playlist (demux, &is_switched);
+
+ g_mutex_lock (demux->pl_update_lock);
+
+ /* block until the next scheduled update or the exit signal */
+ bret = g_cond_timed_wait (demux->pl_update_cond, demux->pl_update_lock, &demux->next_update);
+
+ g_mutex_unlock (demux->pl_update_lock);
+
+ GST_DEBUG_OBJECT (demux, "playlist update wait is completed. reason : %s", bret ? "Received signal" : "time-out");
+
+ tmp_update.tv_sec = 0;
+ tmp_update.tv_usec = 0;
+
+ g_get_current_time (&tmp_update);
+
+ if (bret) {
+ GST_DEBUG_OBJECT (demux, "Sombody signalled manifest waiting... going to exit and diff = %ld",
+ ((demux->next_update.tv_sec * 1000000)+ demux->next_update.tv_usec) - ((tmp_update.tv_sec * 1000000)+ tmp_update.tv_usec));
+ goto exit;
+ } else if (demux->cancelled) {
+ GST_WARNING_OBJECT (demux, "closing is in progress..exit now");
+ goto exit;
+ }
+
+ if (!gst_hlsdemux2_update_playlist (demux, FALSE, &is_error)) {
+ if (is_error) {
+ GST_ERROR_OBJECT (demux, "failed to update playlist...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to update playlist"), (NULL));
+ goto exit;
+ } else {
+ if (demux->flushing) {
+ GST_WARNING_OBJECT (demux, "SEEK is in progress.. pause the task");
+ goto exit;
+ } else if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not update playlist & playlist update failed count = %d",
+ demux->client->update_failed_count);
+ continue;
+ } else {
+ GST_ERROR_OBJECT (demux, "Could not update playlist..");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not update playlist..."), (NULL));
+ goto exit;
+ }
+ }
+ }
+
+ } else {
+ gboolean is_switched = FALSE;
+
+ /* try to switch to another bitrate if needed */
+ // TODO: take care of return value
+
+ /* when in recovery mode, do not obey download rates */
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_NO_RECOVERY)
+ gst_hlsdemux2_switch_playlist (demux, &is_switched);
+
+ if (is_switched) {
+ if (!gst_hlsdemux2_update_playlist (demux, FALSE, &is_error)) {
+ if (is_error) {
+ GST_ERROR_OBJECT (demux, "failed to update playlist...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to update playlist"), (NULL));
+ goto exit;
+ } else {
+ if (demux->flushing) {
+ GST_WARNING_OBJECT (demux, "SEEK is in progress.. pause the task");
+ goto exit;
+ } else if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not update playlist & playlist update failed count = %d",
+ demux->client->update_failed_count);
+ continue;
+ } else {
+ GST_ERROR_OBJECT (demux, "Could not update playlist..");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not update playlist..."), (NULL));
+ goto exit;
+ }
+ }
+ }
+ } else if (demux->is_live) {
+ GstClockTime current_time = gst_util_get_timestamp();
+
+ if ((current_time - demux->pldownloader->download_start_ts) <
+ (PLAYLIST_REFRESH_TIMEOUT - (HLSDEMUX2_FAST_CHECK_CRITICAL_TIME_FACTOR * demux->target_duration) - HLSDEMUX2_OVERHEAD)) {
+ GST_INFO_OBJECT (demux, "Eligible to download next fragment w/o playlist update : "
+ "elapsed time since last playlist request = %"GST_TIME_FORMAT" and max_time_limit = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(current_time - demux->pldownloader->download_start_ts),
+ GST_TIME_ARGS(PLAYLIST_REFRESH_TIMEOUT - (HLSDEMUX2_FAST_CHECK_CRITICAL_TIME_FACTOR * demux->target_duration) - HLSDEMUX2_OVERHEAD));
+ } else {
+
+ GST_INFO_OBJECT (demux, "NOT Eligible to download next fragment w/o playlist update : "
+ "elapsed time since previous request = %"GST_TIME_FORMAT" and max_time_limit = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(current_time - demux->pldownloader->download_start_ts),
+ GST_TIME_ARGS(PLAYLIST_REFRESH_TIMEOUT - (HLSDEMUX2_FAST_CHECK_CRITICAL_TIME_FACTOR * demux->target_duration) - HLSDEMUX2_OVERHEAD));
+
+ /* we are not eligible to download, next fragment.. So wait till next playlist update */
+ g_mutex_lock (demux->pl_update_lock);
+ /* block until the next scheduled update or the exit signal */
+ bret = g_cond_timed_wait (demux->pl_update_cond, demux->pl_update_lock, &demux->next_update);
+ g_mutex_unlock (demux->pl_update_lock);
+
+ GST_DEBUG_OBJECT (demux, "playlist update wait is completed. reason : %s", bret ? "Received signal" : "time-out");
+
+ tmp_update.tv_sec = 0;
+ tmp_update.tv_usec = 0;
+
+ g_get_current_time (&tmp_update);
+
+ if (bret) {
+ GST_DEBUG_OBJECT (demux, "Sombody signalled manifest waiting... going to exit and diff = %ld",
+ ((demux->next_update.tv_sec * 1000000)+ demux->next_update.tv_usec) - ((tmp_update.tv_sec * 1000000)+ tmp_update.tv_usec));
+ goto exit;
+ } else if (demux->cancelled) {
+ GST_WARNING_OBJECT (demux, "closing is in progress..exit now");
+ goto exit;
+ }
+
+ if (!gst_hlsdemux2_update_playlist (demux, FALSE, &is_error)) {
+ if (is_error) {
+ GST_ERROR_OBJECT (demux, "failed to update playlist...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to update playlist"), (NULL));
+ goto exit;
+ } else {
+ if (demux->flushing) {
+ GST_WARNING_OBJECT (demux, "SEEK is in progress.. pause the task");
+ goto exit;
+ } else if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) {
+ GST_WARNING_OBJECT (demux, "Could not update playlist & playlist update failed count = %d",
+ demux->client->update_failed_count);
+ continue;
+ } else {
+ GST_ERROR_OBJECT (demux, "Could not update playlist..");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not update playlist..."), (NULL));
+ goto exit;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+exit:
+ GST_WARNING_OBJECT (demux, "Going to PAUSE download task from self");
+ gst_task_pause (demux->download_task);
+ return;
+}
+
+static void
+gst_hlsdemux2_calculate_popped_duration (GstHLSDemux2 *demux, GstHLSDemux2Stream *stream,
+ GstBuffer *outbuf)
+{
+ gpointer data = NULL;
+ gint i = g_queue_get_length (stream->queue);
+
+ /* get the buffer with valid timestamp from tail of the queue */
+ while (i > 1) {
+ data = g_queue_peek_nth (stream->queue, (i - 1));
+ if (data == NULL) {
+ return;
+ }
+
+ if (GST_IS_BUFFER ((GstBuffer *)data)) {
+ if (GST_BUFFER_TIMESTAMP_IS_VALID(data)) {
+ /* found valid timestamp & stop searching */
+ GST_LOG_OBJECT (stream->pad, "data found buffer with valid ts = %"GST_TIME_FORMAT " len = %d & i = %d",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(data)), g_queue_get_length (stream->queue), i);
+ break;
+ }
+ }
+ i--;
+ }
+
+ if (data == NULL) {
+ return;
+ }
+
+ if (GST_BUFFER_TIMESTAMP (outbuf) <= GST_BUFFER_TIMESTAMP ((GstBuffer *)data)) {
+ stream->cached_duration = GST_BUFFER_TIMESTAMP ((GstBuffer *)data) - GST_BUFFER_TIMESTAMP (outbuf);
+ GST_DEBUG_OBJECT (stream->pad, "cache duration in popping : %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->cached_duration));
+ } else {
+ GST_ERROR_OBJECT (stream->pad, "Wrong order.. not calculating");
+ }
+
+ return;
+}
+
+static void
+gst_hlsdemux2_push_loop (GstHLSDemux2Stream *stream)
+{
+ GstHLSDemux2 *demux = stream->parent;
+ GstFlowReturn fret = GST_FLOW_OK;
+ gpointer data = NULL;
+
+ // TODO: need to take care of EOS handling....
+
+ if (demux->cancelled || demux->flushing)
+ goto error;
+
+ g_mutex_lock (stream->queue_lock);
+
+ if (g_queue_is_empty (stream->queue)) {
+ GST_LOG_OBJECT (stream->pad,"queue is empty wait till, some buffers are available...");
+ g_cond_wait (stream->queue_empty, stream->queue_lock);
+ GST_LOG_OBJECT (stream->pad,"Received queue empty signal...");
+ }
+
+ if (demux->cancelled || demux->flushing) {
+ GST_ERROR_OBJECT (stream->pad, "Shutting down push loop");
+ g_mutex_unlock (stream->queue_lock);
+ goto error;
+ }
+
+ if (g_queue_is_empty (stream->queue)) {
+ GST_ERROR_OBJECT (stream->pad, "Queue empty undesired....");
+ g_mutex_unlock (stream->queue_lock);
+ goto error;
+ }
+
+ data = g_queue_pop_head (stream->queue);
+ if (!data) {
+ GST_ERROR_OBJECT (stream->pad, "Received null data...");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Unhandled GstObjectType"), (NULL));
+ g_mutex_unlock (stream->queue_lock);
+ goto error;
+ }
+
+ /* Calculate duration only when
+ * 1. popped obj is a buffer and
+ * 2. buffer has valid ts and
+ * 3. queue length > 1
+ */
+
+ if (GST_IS_BUFFER(data) &&
+ GST_BUFFER_TIMESTAMP_IS_VALID(data) ) {
+
+ /* calculate buffering duration */
+ gst_hlsdemux2_calculate_popped_duration (demux, stream, data);
+
+ if (stream->cached_duration < 0) {
+ GST_WARNING_OBJECT (stream->pad, "Wrong cached duration in push_loop...\n");
+ stream->cached_duration = 0;
+ }
+ }
+
+ g_mutex_unlock (stream->queue_lock);
+
+ if (GST_IS_EVENT(data)) {
+ GstEvent *event = (GstEvent *)data;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NEWSEGMENT: {
+ GstFormat format;
+ gboolean update;
+ gdouble rate, arate;
+ gint64 start, stop, pos;
+ gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &pos);
+ GST_INFO_OBJECT (stream->pad, "Pushing newsegment to downstream : rate = %0.3f, start = %"GST_TIME_FORMAT
+ ", stop = %"GST_TIME_FORMAT, rate, GST_TIME_ARGS(start), GST_TIME_ARGS(stop));
+ }
+ break;
+ case GST_EVENT_EOS:
+ GST_INFO_OBJECT (stream->pad, "going to push eos event to downstream");
+ stream->eos = TRUE;
+ break;
+ default:
+ GST_ERROR_OBJECT (stream->pad, "not handled event...still pushing event");
+ break;
+ }
+ if (!gst_pad_push_event (stream->pad, event)) {
+ GST_ERROR_OBJECT (demux, "failed to push newsegment event");
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("failed to push '%s' event", GST_EVENT_TYPE_NAME(event)), (NULL));
+ goto error;
+ }
+
+ GST_INFO_OBJECT (stream->pad, "Pushed '%s' event...", GST_EVENT_TYPE_NAME(event));
+
+ if (stream->eos) {
+ //gst_element_post_message (GST_ELEMENT_CAST (stream->sink), gst_message_new_eos (GST_OBJECT_CAST(stream->sink)));
+ GST_INFO_OBJECT (stream->pad, "Pausing the task");
+ goto error;
+ }
+ } else if (GST_IS_BUFFER(data)) {
+ GstCaps *temp_caps = NULL;
+ g_cond_signal (stream->queue_full);
+
+ GST_DEBUG_OBJECT (stream->pad, "Pushing buffer : size = %d, ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_BUFFER_SIZE(data), GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(data)), GST_TIME_ARGS(GST_BUFFER_DURATION(data)));
+
+ temp_caps = gst_buffer_get_caps((GstBuffer *)data);
+ temp_caps = gst_caps_make_writable (temp_caps);
+ gst_caps_set_simple (temp_caps,"hls_streaming", G_TYPE_BOOLEAN, TRUE, NULL);
+ gst_buffer_set_caps((GstBuffer *)data, temp_caps);
+ gst_caps_unref(temp_caps);
+
+ /* push data to downstream*/
+ fret = gst_pad_push (stream->pad, data);
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (stream->pad, "failed to push data, reason : %s", gst_flow_get_name (fret));
+ goto error;
+ }
+ } else {
+ GST_ERROR_OBJECT (stream->pad, "unhandled object type..still pushing");
+ //GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Unhandled GstObjectType"), (NULL));
+ //return;
+ gst_object_unref (data);
+ }
+
+ if (demux->cancelled || demux->flushing) {
+ goto error;
+ }
+
+ return;
+
+error:
+ {
+ GST_WARNING_OBJECT (stream->pad, "Pausing the push task...");
+
+ if (fret < GST_FLOW_UNEXPECTED) {
+ GST_ERROR_OBJECT (stream->pad, "Crtical error in push loop....");
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("failed to push. reason - %s", gst_flow_get_name (fret)), (NULL));
+ }
+
+ gst_pad_pause_task (stream->pad);
+ return;
+ }
+}
+
+// TODO: need to review change_playlist API
+
+static gboolean
+gst_hlsdemux2_schedule (GstHLSDemux2 * demux)
+{
+ gfloat update_factor;
+ gint count;
+ GstClockTime last_frag_duration;
+
+ /* As defined in §6.3.4. Reloading the Playlist file:
+ * "If the client reloads a Playlist file and finds that it has not
+ * changed then it MUST wait for a period of time before retrying. The
+ * minimum delay is a multiple of the target duration. This multiple is
+ * 0.5 for the first attempt, 1.5 for the second, and 3.0 thereafter."
+ */
+ count = demux->client->update_failed_count;
+ if (count < 3)
+ update_factor = update_interval_factor[count];
+ else
+ update_factor = update_interval_factor[3];
+
+ /* schedule the next update using the target duration field of the
+ * playlist */
+ demux->next_update.tv_sec = 0;
+ demux->next_update.tv_usec = 0;
+
+ last_frag_duration = gst_m3u8_client_get_last_fragment_duration (demux->client);
+
+ GST_DEBUG_OBJECT (demux, "last fragment duration = %"GST_TIME_FORMAT" and next update = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(last_frag_duration), GST_TIME_ARGS(GST_TIME_AS_USECONDS(last_frag_duration) * 1000));
+
+ g_get_current_time (&demux->next_update);
+
+ //g_time_val_add (&demux->next_update, last_frag_duration / GST_SECOND * G_USEC_PER_SEC * update_factor);
+ g_time_val_add (&demux->next_update, GST_TIME_AS_USECONDS(last_frag_duration)* update_factor);
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_new_pad_added (GstElement *element, GstPad *srcpad, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GstPad *sinkpad = NULL;
+ GstCaps *caps = NULL;
+ HLSDemux2SinkBin *sinkbin = NULL;
+ gchar *demuxer_name = NULL;
+ gchar *src_pad_name = NULL;
+
+ GST_INFO_OBJECT (demux, "received the src_pad '%s' from mpeg2ts demuxer...", GST_PAD_NAME(srcpad));
+
+ if (strstr (GST_PAD_NAME(srcpad), "private")) {
+ /* handle private image TAG */
+ gst_hlsdemux2_handle_private_pad (demux, srcpad);
+ return;
+ }
+
+ caps = GST_PAD_CAPS (srcpad);
+ demuxer_name = GST_ELEMENT_NAME (element);
+
+ sinkbin = gst_hlsdemux2_create_stream (demux, demuxer_name, caps);
+ if (!sinkbin) {
+ return;
+ }
+
+ sinkpad = gst_element_get_pad(GST_ELEMENT(sinkbin->sinkbin), "sink");
+ if (!sinkpad) {
+ GST_ERROR_OBJECT (demux, "failed to get sinkpad from element - %s", GST_PAD_NAME(sinkbin->sinkbin));
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("could not get sink pad"), (NULL));
+ return;
+ }
+
+ /* link demuxer srcpad & sink element's sink pad */
+ if (gst_pad_link(srcpad, sinkpad) != GST_PAD_LINK_OK) {
+ GST_ERROR_OBJECT (demux, "failed to link pad '%s' & sink's sink pad", GST_PAD_NAME(srcpad));
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("Could not link pads.."), (NULL));
+ gst_object_unref(sinkpad);
+ return;
+ }
+
+ if (sinkbin->parser) {
+ GST_INFO_OBJECT (demux, "linking parser");
+ if (!gst_element_link_many (sinkbin->queue, sinkbin->parser, sinkbin->sink, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link sink bin elements");
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("Could not link pads.."), (NULL));
+ gst_object_unref(sinkpad);
+ return;
+ }
+ } else {
+ if (!gst_element_link_many (sinkbin->queue, sinkbin->sink, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link sink bin elements");
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("Could not link pads.."), (NULL));
+ gst_object_unref(sinkpad);
+ return;
+ }
+ }
+ demux->fdownloader->cur_stream_cnt++;
+
+ src_pad_name = gst_pad_get_name(srcpad);
+ GST_INFO_OBJECT (demux, "succesfully linked pad (%s) with stream pad and current stream cnt = %d",
+ src_pad_name, demux->fdownloader->cur_stream_cnt);
+ g_free(src_pad_name);
+ src_pad_name = NULL;
+
+ gst_element_set_state (GST_ELEMENT(sinkbin->sinkbin), GST_STATE_PLAYING);
+
+ gst_object_unref (sinkpad);
+}
+
+static gboolean
+gst_hlsdemux2_check_stream_type_exist (GstHLSDemux2 *demux, HLSDEMUX2_STREAM_TYPE stream_type)
+{
+ int i = 0;
+ GstHLSDemux2Stream *stream = NULL;
+
+ for (i = 0; i< HLSDEMUX2_STREAM_NUM; i++) {
+ stream = demux->streams[i];
+
+ if (stream && (stream->type == stream_type)) {
+ GST_INFO_OBJECT (demux, "stream type '%d' is already present", stream_type);
+ return TRUE;
+ }
+ }
+
+ GST_INFO_OBJECT (demux, "received new stream type [%d]", stream_type);
+
+ return FALSE;
+}
+
+static HLSDemux2SinkBin *
+gst_hlsdemux2_create_stream (GstHLSDemux2 *demux, gchar *demuxer_name, GstCaps *caps)
+{
+ GstHLSDemux2Stream *stream = NULL;
+ GstPad *sinkpad = NULL;
+ GstPad *linkpad = NULL;
+ HLSDEMUX2_STREAM_TYPE stream_type;
+ gchar *element_name = NULL;
+ gboolean stream_exist = FALSE;
+ gulong sink_probe;
+ HLSDemux2SinkBin *bin = NULL;
+ GstStructure *structure = NULL;
+ gchar *mime = NULL;
+ gchar *stream_name = NULL;
+
+ stream = g_new0 (GstHLSDemux2Stream, 1);
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ mime = gst_structure_get_name (structure);
+
+ if (strstr (mime, "video")) {
+ stream_type = HLSDEMUX2_STREAM_VIDEO;
+ GST_DEBUG_OBJECT (demux, "Its a Multi-variant, Audio only stream..");
+ demux->stream_config = HLSDEMUX2_MULTI_VARIANT;
+ stream_name = g_strdup ("video");
+ } else if (strstr (mime, "audio")) {
+ stream_type = HLSDEMUX2_STREAM_AUDIO;
+ stream_name = g_strdup ("audio");
+ } else if (strstr (mime, "subpicture")) {
+ stream_type = HLSDEMUX2_STREAM_TEXT;
+ stream_name = g_strdup ("subpicture");
+ } else if (strstr (mime, "private")) {
+ stream_type = HLSDEMUX2_STREAM_PRIVATE;
+ stream_name = g_strdup ("private");
+ } else {
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("Received unknown named pad"), (NULL));
+ goto error;
+ }
+
+ stream_exist = gst_hlsdemux2_check_stream_type_exist (demux, stream_type);
+
+ if (!stream_exist) {
+ stream = g_new0 (GstHLSDemux2Stream, 1);
+ gst_hlsdemux2_stream_init (demux, stream, stream_type, stream_name, caps);
+
+ if (!stream->is_linked) {
+ goto error;
+ }
+ } else {
+ int i = 0;
+
+ for (i = 0; i< HLSDEMUX2_STREAM_NUM; i++) {
+ stream = demux->streams[i];
+ if (stream && (stream->type == stream_type)) {
+ GST_INFO_OBJECT (demux, "found the stream - '%d'", stream->type);
+ break;
+ }
+ }
+ }
+
+ if (!stream)
+ goto error;
+
+ bin = g_new0 (HLSDemux2SinkBin, 1);
+
+ element_name = g_strdup_printf("%s-%s", stream_name, "bin");
+ bin->sinkbin = GST_BIN(gst_bin_new (element_name));
+ if (!bin->sinkbin) {
+ GST_ERROR_OBJECT (demux, "failed to create sink bin - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("failed to create bin - %s", element_name), (NULL));
+ goto error;
+ }
+ g_free (element_name);
+ element_name = NULL;
+
+ /* create queue element */
+ element_name = g_strdup_printf("%s-%s", stream_name, "queue");
+ bin->queue = gst_element_factory_make ("queue", element_name);
+ if (!bin->queue) {
+ GST_ERROR_OBJECT (demux, "failed to create queue element - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("failed to create element - %s", element_name), (NULL));
+ goto error;
+ }
+ g_free (element_name);
+ element_name = NULL;
+
+ if ((stream->type == HLSDEMUX2_STREAM_AUDIO) &&
+ g_strstr_len(demuxer_name, strlen(demuxer_name), "id3")) {
+ /* create parser element */
+
+ GST_INFO_OBJECT (stream->pad, "demuxer is id3demuxer, so create audio parser...");
+
+ element_name = g_strdup_printf("%s-%s", stream_name, "parse");
+ bin->parser = gst_element_factory_make ("aacparse", element_name);
+ if (!bin->parser) {
+ GST_ERROR_OBJECT (demux, "failed to create parser element - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("failed to create element - %s", element_name), (NULL));
+ goto error;
+ }
+ g_free (element_name);
+ element_name = NULL;
+ gst_bin_add (GST_BIN (bin->sinkbin), bin->parser);
+ }
+
+ /* create sink element */
+ element_name = g_strdup_printf("%s-%s", stream_name, "sink");
+ bin->sink = gst_element_factory_make ("appsink", element_name);
+ if (!bin->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create sink element - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("failed to create element - %s", element_name), (NULL));
+ goto error;
+ }
+
+ g_object_set (G_OBJECT (bin->sink), "sync", FALSE, "emit-signals", TRUE, NULL);
+ g_signal_connect (bin->sink, "new-buffer", G_CALLBACK (gst_hlsdemux2_downloader_new_buffer), stream);
+ g_signal_connect (bin->sink, "eos", G_CALLBACK (gst_hlsdemux2_downloader_eos), stream);
+
+ gst_bin_add_many (GST_BIN (bin->sinkbin), bin->queue, bin->sink, NULL);
+
+ gst_bin_add (GST_BIN (demux->fdownloader->pipe), GST_ELEMENT(bin->sinkbin));
+
+ /* set queue element to PLAYING state */
+ gst_element_set_state (GST_ELEMENT(bin->sinkbin), GST_STATE_READY);
+
+ sinkpad = gst_element_get_pad(bin->sink, "sink");
+ if (!sinkpad) {
+ GST_ERROR_OBJECT (demux, "failed to get sinkpad from element - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("Count not get sink pad"), (NULL));
+ goto error;
+ }
+ g_free (element_name);
+ element_name = NULL;
+
+ /* adding probe to get new segment events */
+ sink_probe = gst_pad_add_event_probe (sinkpad,
+ G_CALLBACK (gst_hlsdemux2_sink_event_handler), stream);
+
+ if (!stream_exist) {
+ /* add stream to stream_type list */
+ demux->streams[stream_type] = stream;
+ demux->active_stream_cnt++;
+ GST_INFO_OBJECT (demux, "number of active streams = %d", demux->active_stream_cnt);
+ }
+
+ linkpad = gst_element_get_pad(bin->queue, "sink");
+ if (!linkpad) {
+ element_name = gst_element_get_name (bin->queue);
+ GST_ERROR_OBJECT (demux, "failed to get sinkpad from element - %s", element_name);
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("could not get sink pad"), (NULL));
+ goto error;
+ }
+
+ gst_pad_set_active(linkpad,TRUE);
+
+ gst_element_add_pad (GST_ELEMENT(bin->sinkbin), gst_ghost_pad_new ("sink", linkpad));
+
+ GST_INFO_OBJECT (stream->pad, "successfully created stream...");
+
+ demux->fdownloader->sinkbins = g_list_append (demux->fdownloader->sinkbins, bin);
+
+ g_cond_signal (demux->post_msg_start);
+
+ g_free (stream_name);
+
+ return bin;
+
+error:
+ g_free (stream);
+ if (element_name)
+ g_free (element_name);
+
+ if (stream_name)
+ g_free (stream_name);
+
+ return NULL;
+}
+
+static gboolean
+gst_hlsdemux2_private_image_to_vid(gpointer user_data,GstBuffer *image_buffer)
+{
+ GstHLSDemux2PvtStream *pvt_stream = (GstHLSDemux2PvtStream *) user_data;
+ GstHLSDemux2 *demux = (GstHLSDemux2 *) pvt_stream->parent;
+ GstPad *pad, *srcpad;
+ GstElement *appsrc, *appsink, *dec, *videorate, *capsfilter, *enc;
+ GstCaps *scale_caps, *image_caps;
+ GstBus * bus;
+ GstFlowReturn fret = GST_FLOW_OK;
+
+ gchar *image_header = g_strndup((gchar *) image_buffer->data, 8);
+ if(!((image_header[0] == 0xFF) && (image_header[1] == 0xD8))) {
+ GST_WARNING_OBJECT(demux, "Not a valid JPEG image header : %d %d",image_header[0],image_header[1]);
+ goto error;
+ }
+
+ GST_DEBUG_OBJECT(demux, "IMAGE DATA [%d,%d]",pvt_stream->image_buffer->data[0],pvt_stream->image_buffer->data[1]);
+
+ GST_INFO_OBJECT (demux, "Creating jpeg->h264 conversion pipeline");
+
+ pvt_stream->convert_pipe = gst_pipeline_new ("jpegdec-pipe");
+ if (!pvt_stream->convert_pipe) {
+ GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ goto error;
+ }
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pvt_stream->convert_pipe));
+ gst_bus_add_watch (bus, (GstBusFunc)gst_hlsdemux2_imagebuf_pipe_bus_cb, user_data);
+ gst_object_unref (bus);
+
+ appsrc = gst_element_factory_make ("appsrc", "imgbuf-src");
+ if (!appsrc) {
+ GST_ERROR_OBJECT (demux, "failed to create appSrc element");
+ goto error;
+ }
+
+ appsink = gst_element_factory_make ("appsink", "imgbuf-sink");
+ if (!appsink) {
+ GST_ERROR_OBJECT (demux, "failed to create appSink element");
+ goto error;
+ }
+
+ dec = gst_element_factory_make ("jpegdec", "jpeg-decoder");
+ if (!dec) {
+ GST_ERROR_OBJECT (demux, "failed to create jpeg-decoder element");
+ goto error;
+ }
+
+ videorate = gst_element_factory_make ("videorate", "video-rate");
+ if (!videorate) {
+ GST_ERROR_OBJECT (demux, "failed to create ffmpeg-converter element");
+ goto error;
+ }
+
+ capsfilter = gst_element_factory_make ("capsfilter", "caps-filter");
+ if (!capsfilter) {
+ GST_ERROR_OBJECT (demux, "failed to create caps-filter element");
+ goto error;
+ }
+
+ scale_caps = gst_caps_new_simple ("video/x-raw-yuv",
+ "width" , G_TYPE_INT, 480,
+ "height", G_TYPE_INT, 270,
+ "framerate",GST_TYPE_FRACTION,30,1,
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('I','4','2','0'),
+ NULL);
+ g_object_set (capsfilter, "caps", scale_caps, NULL);
+
+ enc = gst_element_factory_make ("savsenc_h264", "h264-encoder");
+ if (!enc) {
+ GST_ERROR_OBJECT (demux, "failed to create h264-encoder element");
+ goto error;
+ }
+
+ image_caps = gst_caps_new_simple ("image/jpeg",
+ "framerate", GST_TYPE_FRACTION, 30, 1,
+ "width", G_TYPE_INT, 480,
+ "height", G_TYPE_INT, 270,
+ NULL);
+ gst_buffer_set_caps (image_buffer, image_caps);
+
+ gst_bin_add_many (GST_BIN (pvt_stream->convert_pipe), appsrc, dec, videorate, capsfilter, enc, appsink, NULL);
+ if (!gst_element_link_many ( appsrc, dec, videorate, capsfilter, enc, appsink, NULL)) {
+ GST_ERROR_OBJECT (demux, "failed to link elements...");
+ goto error;
+ }
+
+ pad = gst_element_get_static_pad(appsink,"sink");
+ gst_pad_add_buffer_probe (pad,G_CALLBACK (gst_hlsdemux2_set_video_buffer), user_data);
+ gst_pad_add_event_probe (pad,G_CALLBACK (gst_hlsdemux2_done_video_buffer), user_data);
+ gst_element_set_state (pvt_stream->convert_pipe, GST_STATE_PLAYING);
+
+ fret = gst_app_src_push_buffer ((GstAppSrc *)appsrc, pvt_stream->image_buffer);
+ if(fret != GST_FLOW_OK){
+ GST_ERROR_OBJECT (demux, "Push Failed; Error: %s[%d]", gst_flow_get_name(fret),fret);
+ goto error;
+ }
+
+ fret = gst_app_src_end_of_stream ((GstAppSrc *)appsrc);
+ if(fret != GST_FLOW_OK){
+ GST_ERROR_OBJECT (demux, "Push EOS Failed; Error: %s[%d]", gst_flow_get_name(fret),fret);
+ goto error;
+ }
+ g_free(image_header);
+ image_header = NULL;
+ return TRUE;
+
+ error:
+ GST_ERROR_OBJECT (demux, "ERROR ERROR ERROR: tut tut tut ");
+ g_free(image_header);
+ image_header = NULL;
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_push_dummy_data (GstHLSDemux2Stream *stream)
+{
+ GstHLSDemux2 *demux = stream->parent;
+ struct stat stat_results;
+ GstBuffer *buf = NULL;
+ gint iread = 0;
+ gint64 percent = 0;
+ GstCaps *caps = NULL;
+ GstBuffer *pushbuf = NULL;
+ GstClockTime stop = GST_CLOCK_TIME_NONE;
+ GstClockTime next_ts = GST_CLOCK_TIME_NONE;
+ gboolean first_frame = TRUE;
+ gint n = 0;
+ gboolean convert_success = TRUE;
+ GstHLSDemux2Stream *cur_stream = NULL;
+ int dummy_fp = -1;
+ GstClockTime cdisc = 0;
+
+ GST_INFO_OBJECT (demux, "START dummy video data thread");
+
+ /* Read dummy frame */
+ if(demux->has_image_buffer) {
+ GstHLSDemux2PvtStream *pvt_stream = demux->private_stream;
+
+ g_mutex_lock (pvt_stream->img_load_lock);
+ if(pvt_stream->got_img_buffer == FALSE)
+ g_cond_wait(pvt_stream->img_load_cond, pvt_stream->img_load_lock);
+ g_mutex_unlock (pvt_stream->img_load_lock);
+
+ GST_LOG_OBJECT (demux, "prev_image_buffer = %p and image_buffer = %p",
+ demux->prev_image_buffer, pvt_stream->image_buffer);
+
+ /* check whether it is same as previous image */
+ if (demux->prev_video_buffer && demux->prev_image_buffer &&
+ (GST_BUFFER_SIZE(demux->prev_image_buffer) == GST_BUFFER_SIZE(pvt_stream->image_buffer))) {
+ if (!memcmp (GST_BUFFER_DATA(demux->prev_image_buffer), GST_BUFFER_DATA(pvt_stream->image_buffer),
+ GST_BUFFER_SIZE(pvt_stream->image_buffer))) {
+ GST_INFO_OBJECT (demux, "current & previous embedded images are same..no need to prepare video buffer");
+ gst_buffer_unref(pvt_stream->image_buffer);
+ pvt_stream->image_buffer = NULL;
+ buf = gst_buffer_copy(demux->prev_video_buffer);
+ goto start_push;
+ }
+ }
+
+ if (demux->prev_image_buffer) {
+ gst_buffer_unref (demux->prev_image_buffer);
+ }
+ demux->prev_image_buffer = gst_buffer_copy(pvt_stream->image_buffer);
+
+ g_mutex_lock (pvt_stream->convert_lock);
+ convert_success = gst_hlsdemux2_private_image_to_vid(pvt_stream, pvt_stream->image_buffer);
+ if(convert_success)
+ g_cond_wait(pvt_stream->convert_cond, pvt_stream->convert_lock);
+ g_mutex_unlock (pvt_stream->convert_lock);
+
+ if(pvt_stream->convert_pipe){
+ pvt_stream->convert_pipe = NULL;
+ gst_object_unref(pvt_stream->convert_pipe);
+ }
+
+ if (demux->prev_video_buffer) {
+ gst_buffer_unref (demux->prev_video_buffer);
+ demux->prev_video_buffer = NULL;
+ }
+
+ if(convert_success){
+ buf = pvt_stream->video_buffer;
+ pvt_stream->video_buffer = NULL;
+ demux->prev_video_buffer = gst_buffer_copy(buf);
+ GST_DEBUG_OBJECT (demux, "Set Image from buffer - success [%d,%d]", buf->data[0], buf->data[1]);
+ GST_INFO_OBJECT (demux, "Going to show embedded image");
+ }
+ }
+
+ //load black frame if error occured while creating video buffer.
+ if(demux->has_image_buffer == FALSE || convert_success == FALSE)
+ {
+ GST_INFO_OBJECT (demux, "Going to show fixed image");
+ dummy_fp = open (PREDEFINED_VIDEO_FRAME_LOCATION, O_RDONLY);
+ if (dummy_fp < 0) {
+ GST_ERROR_OBJECT (stream->pad, "failed to open dummy data file : %s...", PREDEFINED_VIDEO_FRAME_LOCATION);
+ GST_ELEMENT_ERROR (demux, RESOURCE, OPEN_READ, ("Failed open file '%s' for reading. reason : %s",PREDEFINED_VIDEO_FRAME_LOCATION ,g_strerror(errno)), (NULL));
+ goto quit;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "opened dummy video file %s succefully...", PREDEFINED_VIDEO_FRAME_LOCATION);
+
+ if (fstat (dummy_fp, &stat_results) < 0) {
+ GST_ERROR_OBJECT (stream->pad, "failed to get stats of a file...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("Failed get stats. reason : %s", g_strerror(errno)), (NULL));
+ close (dummy_fp);
+ goto quit;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "size of the dummy file = %d\n", stat_results.st_size);
+
+ buf = gst_buffer_new_and_alloc (stat_results.st_size);
+ if (!buf) {
+ GST_ERROR_OBJECT (stream->pad, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ close (dummy_fp);
+ goto quit;
+ }
+
+ iread = read (dummy_fp, GST_BUFFER_DATA (buf), stat_results.st_size);
+
+ close (dummy_fp);
+ }
+
+start_push:
+
+ for (n = 0; n < HLSDEMUX2_STREAM_NUM; n++) {
+ cur_stream = demux->streams[n];
+
+ if (cur_stream && (stream != cur_stream)) {
+ cdisc = cur_stream->cdisc;
+ }
+ }
+
+ stream->nts = stop = stream->fts + demux->cfrag_dur;
+ stream->total_disc += cdisc;
+ stream->cdisc = cdisc;
+ next_ts = stream->fts;
+
+ GST_INFO_OBJECT (stream->pad, "Dummy video start_ts = %"GST_TIME_FORMAT" and stop_ts = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(stream->fts), GST_TIME_ARGS(stop));
+
+ /* set caps on buffer */
+ caps = gst_caps_new_simple ("video/x-h264",
+ "stream-format", G_TYPE_STRING, "byte-stream",
+ "alignment", G_TYPE_STRING, "nal",
+ NULL);
+
+ gst_buffer_set_caps (buf, caps);
+
+ if (stream->need_newsegment) {
+ GST_INFO_OBJECT (stream->pad, "need to send new segment event based on audio");
+ g_queue_push_tail (stream->queue, (demux->streams[HLSDEMUX2_STREAM_AUDIO])->newsegment);
+ stream->need_newsegment = FALSE;
+ }
+
+ while (TRUE) {
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (stream->pad, "Stopping dummy push task...");
+ goto quit;
+ }
+
+ g_mutex_lock (stream->queue_lock);
+
+ pushbuf = gst_buffer_copy (buf);
+ gst_buffer_set_caps (pushbuf, caps);
+ GST_BUFFER_TIMESTAMP (pushbuf) = stream->lts = next_ts;
+ GST_BUFFER_DURATION (pushbuf) = GST_CLOCK_TIME_NONE;
+
+ if (first_frame) {
+ GST_BUFFER_FLAG_SET (pushbuf, GST_BUFFER_FLAG_DISCONT);
+ first_frame = FALSE;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "Pushing dummy buffer : ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (pushbuf)));
+
+ /* calculate cached duration */
+ gst_hlsdemux2_calculate_pushed_duration (demux, stream, pushbuf);
+
+ if (stream->cached_duration >= 0) {
+
+ percent = (stream->cached_duration * 100) / demux->total_cache_duration;
+ GST_LOG_OBJECT (stream->pad, "percent done = %"G_GINT64_FORMAT, percent);
+
+ if (percent > 100) {
+ guint64 overall_percent = 0;
+
+ g_mutex_lock (demux->buffering_lock);
+ overall_percent = demux->percent;
+ g_mutex_unlock (demux->buffering_lock);
+
+ if (overall_percent < 100) { /* otherwise, may cause blocking while buffering*/
+ GST_DEBUG_OBJECT (stream->pad, "@@@@@@@@@@@ queue should not go to wait now @@@@@@@@");
+ } else {
+ /* update buffering & wait if space is not available */
+ GST_DEBUG_OBJECT (stream->pad, "Reached more than 100 percent, queue full & wait till free");
+ g_cond_wait(stream->queue_full, stream->queue_lock);
+ GST_DEBUG_OBJECT (stream->pad,"Received signal to add more data...");
+ }
+ }
+ }
+
+ g_cond_signal (stream->queue_empty);
+
+ g_mutex_unlock (stream->queue_lock);
+
+ /* calculate next timestamp */
+ next_ts += HLS_DEFAULT_FRAME_DURATION;
+
+ if (next_ts > stop) {
+ GST_DEBUG_OBJECT (stream->pad, "Reached Endof the fragment ....\n\n");
+ break;
+ }
+ }
+
+ if (demux->end_of_playlist) {
+ /* end of playlist, push EOS to queue */
+ GST_INFO_OBJECT (stream->pad, "pushing EOS event to stream's queue");
+ g_queue_push_tail (stream->queue, gst_event_new_eos ());
+ }
+
+#ifdef LATEST_AV_SYNC
+ if (demux->is_live) {
+ stream->total_stream_time += (stream->lts - stream->fts);
+ } else {
+ stream->total_stream_time += (stream->lts - stream->fts + HLS_DEFAULT_FRAME_DURATION);
+ stream->total_stream_time = stream->nts;
+ }
+#else
+ stream->total_stream_time += (stream->lts - stream->fts);
+#endif
+
+ GST_DEBUG_OBJECT (stream->pad, "---------------- TS VALUES ----------------");
+ GST_DEBUG_OBJECT (stream->pad, "valid start ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->fts));
+ GST_DEBUG_OBJECT (stream->pad, "valid end ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->lts));
+ GST_DEBUG_OBJECT (stream->pad, "fragment duration received = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->lts - stream->fts));
+ GST_DEBUG_OBJECT (stream->pad, "total stream time = %"GST_TIME_FORMAT,GST_TIME_ARGS(stream->total_stream_time));
+ GST_DEBUG_OBJECT (stream->pad, "total disc time = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->total_disc));
+ GST_DEBUG_OBJECT (stream->pad, "next expected start ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->nts));
+ GST_DEBUG_OBJECT (stream->pad, "---------------- DUMP VALUES END----------------");
+
+ stream->apply_disc = FALSE;
+ stream->fts = GST_CLOCK_TIME_NONE;
+ stream->valid_fts_rcvd = FALSE;
+ stream->prev_nts = stream->nts;
+
+quit:
+ {
+ GST_INFO_OBJECT (stream->pad, "Stopping dummy data thread...");
+ gst_buffer_unref (buf);
+ stream->dummy_data_thread = NULL;
+ return TRUE;
+ }
+
+}
+
+static gboolean
+gst_hlsdemux2_sink_event_handler (GstPad * pad, GstEvent * event, gpointer data)
+{
+ GstHLSDemux2Stream *stream = (GstHLSDemux2Stream *)data;
+ GstHLSDemux2 *demux = stream->parent;
+
+ if ((GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) && stream->need_newsegment) {
+ GstEvent *newsegment = NULL;
+ GstFormat format;
+ gboolean update;
+ gdouble rate, arate;
+ gint64 start, stop, pos;
+
+ newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, demux->ns_start, -1, demux->ns_start);
+
+ GST_INFO_OBJECT (stream->pad, "Received NEWSEGMENT event...");
+
+ g_mutex_lock (stream->queue_lock);
+
+ gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &pos);
+
+ if (!GST_CLOCK_TIME_IS_VALID(stream->base_ts)) {
+ GST_INFO_OBJECT (stream->pad, "storing stream's base timestamp = %"GST_TIME_FORMAT, GST_TIME_ARGS(start));
+ stream->base_ts = start;
+ }
+
+ g_queue_push_tail (stream->queue, newsegment);
+
+ GST_INFO_OBJECT (stream->pad, "Pushed new segment event with start = %"GST_TIME_FORMAT" to queue..",
+ GST_TIME_ARGS(demux->ns_start));
+
+ g_mutex_unlock (stream->queue_lock);
+
+ stream->need_newsegment = FALSE;
+ stream->prev_nts = demux->ns_start;
+
+ if (stream->newsegment)
+ gst_event_unref (stream->newsegment);
+
+ stream->newsegment = gst_event_copy (newsegment);
+ }
+ return TRUE;
+}
+
+static gboolean
+hlsdemux2_HTTP_repeat_request (GstHLSDemux2 *demux, gchar *element_name)
+{
+ if (g_strrstr(element_name, "fragurisrc")) {
+ guint idx = 0;
+ GstBuffer *buf = NULL;
+
+ /* clear stream queues if we already downloaded some data */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *stream = demux->streams[idx];
+ if (stream ) {
+ while (!g_queue_is_empty (stream->downloader_queue)) {
+ buf = g_queue_pop_head (stream->downloader_queue);
+ gst_buffer_unref (buf);
+ }
+ GST_LOG_OBJECT (stream->pad, "cleared stream download queue...");
+ g_queue_clear (stream->downloader_queue);
+ }
+ }
+
+ /* request the same fragment again */
+ if (demux->fragCookie)
+ g_strfreev (demux->fragCookie);
+
+ g_object_get (demux->fdownloader->urisrc, "cookies", &demux->fragCookie, NULL);
+ GST_DEBUG_OBJECT (demux, "Got cookies after FRAGMENT download : %s", demux->fragCookie ? *(demux->fragCookie) : NULL);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download fragment AGAIN: %s", demux->frag_uri);
+
+ gst_hlsdemux2_destroy_fragment_download (demux);
+
+ if (!gst_hlsdemux2_create_fragment_download (demux, demux->frag_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ /* download rate calculation : note down start time*/
+ demux->fdownloader->download_start_ts = gst_util_get_timestamp();
+
+ gst_element_set_state (demux->fdownloader->pipe, GST_STATE_PLAYING);
+
+ return TRUE;
+ } else if (g_strrstr(element_name, "playlisturisrc")) {
+
+ if (demux->playlistCookie)
+ g_strfreev (demux->playlistCookie);
+
+ g_object_get (demux->pldownloader->urisrc, "cookies", &demux->playlistCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after PLAYLIST download : %s", demux->playlistCookie ? *(demux->playlistCookie) : NULL);
+
+ gst_hlsdemux2_destroy_playlist_download (demux);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download playlist AGAIN: %s", demux->playlist_uri);
+ if (!gst_hlsdemux2_create_playlist_download (demux, demux->playlist_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ gst_element_set_state (demux->pldownloader->pipe, GST_STATE_PLAYING);
+ return TRUE;
+ } else if (g_strrstr(element_name, "keyurisrc")) {
+
+ if (demux->keyCookie)
+ g_strfreev (demux->keyCookie);
+
+ g_object_get (demux->kdownloader->urisrc, "cookies", &demux->keyCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after KEY download : %s", demux->keyCookie ? *(demux->keyCookie) : NULL);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download key AGAIN: %s", demux->key_uri);
+
+ gst_hlsdemux2_destroy_key_download (demux);
+
+ if (!gst_hlsdemux2_create_key_download (demux, demux->key_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ gst_element_set_state (demux->kdownloader->pipe, GST_STATE_PLAYING);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+hlsdemux2_HTTP_time_out (GstHLSDemux2 *demux, gchar *element_name)
+{
+ if (g_strrstr(element_name, "fragurisrc")) {
+ /* as it is because of timeout, there is no point in requesting the same fragment again, so request next one */
+ GST_INFO_OBJECT (demux, "signalling fragment downloader to get next fragment...");
+ demux->fdownloader->get_next_frag = TRUE;
+ g_cond_signal (demux->fdownloader->cond);
+ return TRUE;
+ } else if (g_strrstr(element_name, "playlisturisrc")) {
+
+ if (demux->playlistCookie)
+ g_strfreev (demux->playlistCookie);
+
+ g_object_get (demux->pldownloader->urisrc, "cookies", &demux->playlistCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after PLAYLIST download : %s", demux->playlistCookie ? *(demux->playlistCookie) : NULL);
+
+ gst_hlsdemux2_destroy_playlist_download (demux);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download playlist AGAIN: %s", demux->playlist_uri);
+ if (!gst_hlsdemux2_create_playlist_download (demux, demux->playlist_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ gst_element_set_state (demux->pldownloader->pipe, GST_STATE_PLAYING);
+ return TRUE;
+ } else if (g_strrstr(element_name, "keyurisrc")) {
+
+ if (demux->keyCookie)
+ g_strfreev (demux->keyCookie);
+
+ g_object_get (demux->kdownloader->urisrc, "cookies", &demux->keyCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after KEY download : %s", demux->keyCookie ? *(demux->keyCookie) : NULL);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download key AGAIN: %s", demux->key_uri);
+
+ gst_hlsdemux2_destroy_key_download (demux);
+
+ if (!gst_hlsdemux2_create_key_download (demux, demux->key_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ gst_element_set_state (demux->kdownloader->pipe, GST_STATE_PLAYING);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+hlsdemux2_HTTP_not_found(GstHLSDemux2 *demux, gchar *element_name)
+{
+ if (g_strrstr(element_name, "fragurisrc")) {
+ if (demux->is_live) {
+ /* request next fragment url */
+ GST_INFO_OBJECT (demux, "signalling fragment downloader to get next fragment...");
+ demux->fdownloader->get_next_frag = TRUE;
+ g_cond_signal (demux->fdownloader->cond);
+ return TRUE;
+ } else {
+
+#if 0 // In future enable
+ GList *next_variant = NULL;
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_NO_RECOVERY)
+ demux->pldownloader->recovery_mode = HLSDEMUX2_DOWNWARD_RECOVERY;
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_DOWNWARD_RECOVERY) {
+ /* get next available lower variant */
+ next_variant = gst_m3u8_client_get_next_lower_bw_playlist (demux->client);
+ if (!next_variant) {
+ GST_WARNING_OBJECT (demux, "no next lower variants.. Go Upward");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_UPWARD_RECOVERY;
+ }
+ }
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_UPWARD_RECOVERY) {
+ /* get next available lower variant */
+ next_variant = gst_m3u8_client_get_next_higher_bw_playlist (demux->client);
+ if (!next_variant) {
+ GST_ERROR_OBJECT (demux, "no next higher variants.. need to exit");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+ return FALSE;
+ }
+ }
+
+ /* change variant */
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ demux->client->main->current_variant = next_variant;
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+
+ gst_m3u8_client_set_current (demux->client, next_variant->data);
+
+ demux->fdownloader->get_next_frag = TRUE;
+ g_cond_signal (demux->fdownloader->cond);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+ }
+ } else if (g_strrstr(element_name, "playlisturisrc")) {
+ GList *next_variant = NULL;
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_NO_RECOVERY)
+ demux->pldownloader->recovery_mode = HLSDEMUX2_DOWNWARD_RECOVERY;
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_DOWNWARD_RECOVERY) {
+ /* get next available lower variant */
+ next_variant = gst_m3u8_client_get_next_lower_bw_playlist (demux->client);
+ if (!next_variant) {
+ GST_WARNING_OBJECT (demux, "no next lower variants.. Go Upward");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_UPWARD_RECOVERY;
+ }
+ }
+
+ if (demux->pldownloader->recovery_mode == HLSDEMUX2_UPWARD_RECOVERY) {
+ /* get next available lower variant */
+ next_variant = gst_m3u8_client_get_next_higher_bw_playlist (demux->client);
+ if (!next_variant) {
+ GST_ERROR_OBJECT (demux, "no next higher variants.. need to exit");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+ return FALSE;
+ }
+ }
+
+ /* change variant */
+ GST_M3U8_CLIENT_LOCK (demux->client);
+ demux->client->main->current_variant = next_variant;
+ GST_M3U8_CLIENT_UNLOCK (demux->client);
+
+ gst_m3u8_client_set_current (demux->client, next_variant->data);
+
+ /* get new playlist uri */
+ demux->playlist_uri = g_strdup (gst_m3u8_client_get_current_uri (demux->client));
+
+ if (demux->playlistCookie)
+ g_strfreev (demux->playlistCookie);
+
+ g_object_get (demux->pldownloader->urisrc, "cookies", &demux->playlistCookie, NULL);
+
+ GST_DEBUG_OBJECT (demux, "Got cookies after PLAYLIST download : %s", demux->playlistCookie ? *(demux->playlistCookie) : NULL);
+
+ gst_hlsdemux2_destroy_playlist_download (demux);
+
+ GST_INFO_OBJECT (demux, "========>>>>>Going to download recovery playlist : %s", demux->playlist_uri);
+ if (!gst_hlsdemux2_create_playlist_download (demux, demux->playlist_uri)) {
+ GST_ERROR_OBJECT (demux, "failed to create download pipeline");
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("Failed to create pipeline"), (NULL));
+ return FALSE;
+ }
+
+ gst_element_set_state (demux->pldownloader->pipe, GST_STATE_PLAYING);
+
+ return TRUE;
+
+ } else if (g_strrstr(element_name, "keyurisrc")) {
+ /* treating it as non-recoverable error */
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_hlsdemux2_handle_HTTP_error (GstHLSDemux2 *demux, GError *error, gchar *element_name) {
+ guint i = 0;
+ guint n_http_errors = sizeof (http_errors) / sizeof (http_errors[0]);
+
+ if(!error)
+ return HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE;
+ for (i = 0; i < n_http_errors; i++) {
+ if (G_LIKELY (!strncmp(error->message, http_errors[i].error_phrase, strlen(error->message)))) {
+ if (http_errors[i].handle_error) {
+ return http_errors[i].handle_error (demux, element_name);
+ }
+ return http_errors[i].error_type;
+ }
+ }
+ return HLSDEMUX2_HTTP_ERROR_NONRECOVERABLE;
+}
+
+static gboolean
+gst_hlsdemux2_fragment_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GError *error = NULL;
+ gchar* debug = NULL;
+ gboolean bret = TRUE;
+ GstMessage *err_msg = NULL;
+ gchar *ele_name = gst_element_get_name (GST_MESSAGE_SRC (msg));
+
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ GST_INFO_OBJECT (demux, "received EOS on download pipe from '%s'..", ele_name);
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+ g_cond_signal(demux->fdownloader->cond);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GST_ERROR_OBJECT (demux, "Error from %s\n", ele_name);
+
+ gst_message_parse_error( msg, &error, &debug );
+
+ demux->fdownloader->error_rcvd = TRUE;
+
+ if (error)
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: error->message = %s and error->code = %d", error->message);
+
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: debug = %s", debug);
+
+ if (g_strrstr(ele_name, "fragurisrc") && !demux->cancelled && demux->soup_request_fail_cnt) {
+
+ /* flush the error msgs pending.. as we are going to create new pipeline */
+ gst_bus_set_flushing (bus, TRUE);
+
+ bret = gst_hlsdemux2_handle_HTTP_error (demux, error, ele_name);
+ if (!bret)
+ goto post_error;
+ else {
+ demux->soup_request_fail_cnt--;
+ GST_WARNING_OBJECT (demux, "HTTP error count remaining = %d", demux->soup_request_fail_cnt);
+ goto exit;
+ }
+ }
+
+post_error:
+
+ GST_ERROR_OBJECT (demux, "posting error from fragment download callback");
+
+ err_msg = gst_message_new_error (GST_OBJECT(demux), error, debug);
+ if (!gst_element_post_message (GST_ELEMENT(demux), err_msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ bret = FALSE;
+ goto exit;
+ }
+ gst_hlsdemux2_stop (demux);
+ break;
+ }
+ case GST_MESSAGE_WARNING: {
+ gst_message_parse_warning(msg, &error, &debug);
+ GST_WARNING_OBJECT(demux, "warning : %s\n", error->message);
+ GST_WARNING_OBJECT(demux, "debug : %s\n", debug);
+ break;
+ }
+ case GST_MESSAGE_ELEMENT: {
+ const GstStructure *s = gst_message_get_structure (msg);
+
+ if (gst_structure_has_name (s, "cookies")) {
+ const GValue *value;
+ gchar **cookies = NULL;
+ gchar *cookie = NULL;
+ value = gst_structure_get_value (s, "cookies");
+ cookie = g_strdup_value_contents(value);
+
+ cookies = &cookie;
+ GST_ERROR_OBJECT (demux, "received cookies from soup : %s", *cookies);
+ }
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+exit:
+ if (debug)
+ g_free( debug);
+
+ if (error)
+ g_error_free( error);
+
+ if (!bret)
+ gst_hlsdemux2_stop (demux);
+
+ g_free (ele_name);
+
+ return bret;
+}
+
+static GstBusSyncReply
+gst_hlsdemux2_playlist_download_bus_sync_cb (GstBus * bus, GstMessage *msg, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GstBusSyncReply reply = GST_BUS_DROP;
+
+ GError *error = NULL;
+ gchar *debug = NULL;
+ gboolean bret = TRUE;
+ GstMessage *err_msg = NULL;
+ gchar *ele_name = gst_element_get_name (GST_MESSAGE_SRC (msg));
+
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ GST_DEBUG_OBJECT (demux, "received EOS on playlist download pipe..");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+ g_mutex_lock (demux->pldownloader->lock);
+ g_cond_broadcast (demux->pldownloader->cond);
+ g_mutex_unlock (demux->pldownloader->lock);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GST_ERROR_OBJECT (demux, "Error from %s element", ele_name);
+
+ gst_message_parse_error( msg, &error, &debug );
+ if (error)
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: error= %s", error->message);
+
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: debug = %s", debug);
+
+ if (g_strrstr(ele_name, "playlisturisrc") && !demux->cancelled && demux->soup_request_fail_cnt) {
+
+ /* flush the error msgs pending.. as we are going to create new pipeline */
+ gst_bus_set_flushing (bus, TRUE);
+
+ bret = gst_hlsdemux2_handle_HTTP_error (demux, error, ele_name);
+ if (!bret)
+ goto post_error;
+ else {
+ demux->soup_request_fail_cnt--;
+ GST_WARNING_OBJECT (demux, "HTTP error count remaining = %d", demux->soup_request_fail_cnt);
+ goto exit;
+ }
+ }
+
+post_error:
+
+ GST_ERROR_OBJECT (demux, "posting error from playlist callback...");
+
+ err_msg = gst_message_new_error (GST_OBJECT(demux), error, debug);
+ if (!gst_element_post_message (GST_ELEMENT(demux), err_msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ bret = FALSE;
+ goto exit;
+ }
+ gst_hlsdemux2_stop (demux);
+ break;
+ }
+ case GST_MESSAGE_WARNING: {
+ gst_message_parse_warning(msg, &error, &debug);
+ if (error)
+ GST_WARNING_OBJECT(demux, "warning : %s\n", error->message);
+ GST_WARNING_OBJECT(demux, "debug : %s\n", debug);
+ break;
+ }
+ case GST_MESSAGE_ELEMENT: {
+ const GstStructure *s = gst_message_get_structure (msg);
+
+ if (gst_structure_has_name (s, "cookies")) {
+ const GValue *value;
+ gchar **cookies = NULL;
+ gchar *cookie = NULL;
+ value = gst_structure_get_value (s, "cookies");
+ cookie = g_strdup_value_contents(value);
+
+ cookies = &cookie;
+ GST_ERROR_OBJECT (demux, "received cookies from soup : %s", *cookies);
+ }
+ break;
+ }
+ default : {
+ //GST_LOG_OBJECT(demux, "unhandled message : %s\n", gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
+ break;
+ }
+ }
+
+exit:
+ if (debug)
+ g_free( debug);
+
+ if (error)
+ g_error_free( error);
+
+ if (!bret)
+ gst_hlsdemux2_stop (demux);
+
+ g_free (ele_name);
+
+ return reply;
+}
+
+static gboolean
+gst_hlsdemux2_key_download_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GError *error = NULL;
+ gchar* debug = NULL;
+ gboolean bret = TRUE;
+ GstMessage *err_msg = NULL;
+ gchar *ele_name = gst_element_get_name (GST_MESSAGE_SRC (msg));
+
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ GST_DEBUG_OBJECT (demux, "received EOS on key download pipe..");
+ demux->pldownloader->recovery_mode = HLSDEMUX2_NO_RECOVERY;
+ g_mutex_lock (demux->kdownloader->lock);
+ g_cond_signal (demux->kdownloader->cond);
+ g_mutex_unlock (demux->kdownloader->lock);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GST_INFO_OBJECT (demux, "Error from %s element...", ele_name);
+
+ gst_message_parse_error(msg, &error, &debug);
+
+ if (error)
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: error= %s", error->message);
+
+ GST_ERROR_OBJECT (demux, "GST_MESSAGE_ERROR: debug = %s", debug);
+
+ if (g_strrstr(ele_name, "keyurisrc") && !demux->cancelled && demux->soup_request_fail_cnt) {
+
+ /* flush the error msgs pending.. as we are going to create new pipeline */
+ gst_bus_set_flushing (bus, TRUE);
+
+ bret = gst_hlsdemux2_handle_HTTP_error (demux, error, ele_name);
+ if (!bret)
+ goto post_error;
+ else {
+ demux->soup_request_fail_cnt--;
+ GST_WARNING_OBJECT (demux, "HTTP error count remaining = %d", demux->soup_request_fail_cnt);
+ goto exit;
+ }
+ }
+
+post_error:
+ GST_ERROR_OBJECT (demux, "Error posting from key downloader...");
+ err_msg = gst_message_new_error (GST_OBJECT(demux), error, debug);
+
+ if (!gst_element_post_message (GST_ELEMENT(demux), err_msg)) {
+ GST_ERROR_OBJECT (demux, "failed to post error");
+ gst_hlsdemux2_stop (demux);
+ bret = FALSE;
+ goto exit;
+ }
+ gst_hlsdemux2_stop (demux);
+ break;
+ }
+ case GST_MESSAGE_WARNING: {
+ gst_message_parse_warning(msg, &error, &debug);
+ if (error)
+ GST_WARNING_OBJECT(demux, "warning : %s\n", error->message);
+ GST_WARNING_OBJECT(demux, "debug : %s\n", debug);
+ break;
+ }
+ case GST_MESSAGE_ELEMENT: {
+ const GstStructure *s = gst_message_get_structure (msg);
+
+ if (gst_structure_has_name (s, "cookies")) {
+ const GValue *value;
+ gchar **cookies = NULL;
+ gchar *cookie = NULL;
+ value = gst_structure_get_value (s, "cookies");
+ cookie = g_strdup_value_contents(value);
+
+ cookies = &cookie;
+ GST_ERROR_OBJECT (demux, "received cookies from soup : %s", *cookies);
+ }
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+exit:
+
+ if (debug)
+ g_free( debug);
+
+ if (error)
+ g_error_free( error);
+
+ if (!bret)
+ gst_hlsdemux2_stop (demux);
+
+ g_free (ele_name);
+
+ return bret;
+}
+
+static void
+gst_hlsdemux2_calculate_pushed_duration (GstHLSDemux2 *demux, GstHLSDemux2Stream *stream,
+ GstBuffer *inbuf)
+{
+ gint len = 0;
+ int qidx = 0;
+ gpointer data = NULL;
+
+ // TODO: for all timestamps -1 case, we need to add max-bytes also
+
+ g_queue_push_tail (stream->queue, inbuf);
+
+ len = g_queue_get_length (stream->queue);
+
+ /* peek the head buffer having valid timestamp to calculate cached duration */
+ while (qidx < len) {
+ data = g_queue_peek_nth (stream->queue, qidx);
+
+ if (GST_IS_BUFFER (data)) {
+ if (GST_BUFFER_TIMESTAMP_IS_VALID(data)) {
+ GST_LOG_OBJECT (stream->pad, "data found buffer with valid ts = %"GST_TIME_FORMAT " len = %d & qidx = %d",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(data)), len, qidx);
+ break;
+ }
+ }
+ qidx++;
+ }
+ if(GST_IS_BUFFER (data)) {
+ if (GST_BUFFER_TIMESTAMP (inbuf) >= GST_BUFFER_TIMESTAMP ((GstBuffer *)data)) {
+ stream->cached_duration = GST_BUFFER_TIMESTAMP (inbuf) - GST_BUFFER_TIMESTAMP ((GstBuffer *)data);
+ GST_LOG_OBJECT (stream->pad, "len = %d, cached duration = %"GST_TIME_FORMAT,
+ g_queue_get_length (stream->queue), GST_TIME_ARGS(stream->cached_duration));
+ } else {
+ GST_WARNING_OBJECT (stream->pad, "Wrong order.. not calculating");
+ }
+ }
+ return;
+}
+
+static gboolean
+gst_hlsdemux2_alter_timestamps (GstHLSDemux2Stream *stream, GstBuffer *inbuf)
+{
+ GstHLSDemux2 *demux = stream->parent;
+ GstClockTime cts = 0;
+
+ /* set discontinuity only when there is real fragment discontinuity */
+ if (GST_BUFFER_IS_DISCONT(inbuf)) {
+ if (!stream->apply_disc) {
+ GST_BUFFER_FLAG_UNSET (inbuf, GST_BUFFER_FLAG_DISCONT);
+ GST_INFO_OBJECT (stream->pad, "unsetting discontinuity flag...");
+ }
+ }
+
+ if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (inbuf))) {
+ if (GST_BUFFER_TIMESTAMP (inbuf) >= stream->base_ts) {
+ cts = GST_BUFFER_TIMESTAMP (inbuf) - stream->base_ts;
+ } else {
+ /* this buffer should be dropped at sink */
+ GST_WARNING_OBJECT (stream->pad, "input timestamp [%"GST_TIME_FORMAT"] is less than base_ts [%"GST_TIME_FORMAT"]",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (inbuf)), GST_TIME_ARGS(stream->base_ts));
+ if (stream->type == HLSDEMUX2_STREAM_AUDIO) {
+ return FALSE;
+ } else {
+ GST_BUFFER_TIMESTAMP (inbuf) = GST_CLOCK_TIME_NONE;
+ return TRUE;
+ }
+ }
+ } else {
+ /* this buffer should be dropped at sink */
+ GST_LOG_OBJECT (stream->pad, "invalid input timestamp (i.e. GST_CLOCK_TIME_NONE)");
+#ifdef LATEST_AV_SYNC
+ if (!demux->is_live && (stream->type == HLSDEMUX2_STREAM_AUDIO) && !stream->valid_fts_rcvd){
+ GST_WARNING_OBJECT (stream->pad, "dropping invalid ts buffer, due to no valid first ts yet...");
+ return FALSE;
+ }
+#endif
+ return TRUE;
+ }
+
+ /* handle discontinuity in stream */
+ if(GST_CLOCK_TIME_IS_VALID (stream->nts) && (GST_CLOCK_DIFF(stream->nts,cts) > (2 * GST_SECOND)) && stream->apply_disc) {
+ GST_INFO_OBJECT (stream->pad, "cts = %"GST_TIME_FORMAT" and prev_next_ts = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(cts), GST_TIME_ARGS(stream->nts));
+ GST_INFO_OBJECT (stream->pad,"Received disc = %"GST_TIME_FORMAT, GST_TIME_ARGS(cts - stream->nts));
+ stream->cdisc = cts - stream->nts;
+ stream->total_disc += (cts - stream->nts);
+ GST_INFO_OBJECT (stream->pad, "total disc = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->total_disc));
+ stream->apply_disc = FALSE;
+ }
+
+ cts -= stream->cdisc;
+
+ /* get the max last ts, required because of frame reordering */
+ stream->lts = cts > stream->lts ? cts : stream->lts;
+
+ GST_DEBUG_OBJECT (stream->pad, "modifying buffer timestamp : %"GST_TIME_FORMAT" -> %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (inbuf)), GST_TIME_ARGS(cts));
+
+#ifndef LATEST_AV_SYNC
+ /* store first valid timestamp of a fragment */
+ if (stream->fts == GST_CLOCK_TIME_NONE) {
+ stream->fts = cts;
+ stream->nts = stream->fts + demux->cfrag_dur;
+ GST_INFO_OBJECT (stream->pad, "storing first ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->fts));
+ if (demux->is_live && stream->type == HLSDEMUX2_STREAM_AUDIO)
+ demux->cur_audio_fts = cts;
+ }
+#endif
+
+#ifdef LATEST_AV_SYNC
+ /* 10/31/2013: dont want to disturb live case at this moment */
+ if (!stream->valid_fts_rcvd) {
+ if (stream->type == HLSDEMUX2_STREAM_AUDIO) {
+ if (!demux->is_live && (GST_CLOCK_DIFF(cts, stream->prev_nts) > (10 * GST_MSECOND))) {
+ GST_WARNING_OBJECT (stream->pad, "already seen audio timestamps[%"GST_TIME_FORMAT"]"
+ " and previous_nts = %"GST_TIME_FORMAT"...drop this",
+ GST_TIME_ARGS(cts), GST_TIME_ARGS(stream->prev_nts));
+ return FALSE;
+ } else {
+ GST_INFO_OBJECT (stream->pad, "recevied valid audio first ts = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS (cts));
+ demux->cur_audio_fts = cts;
+ }
+ }
+
+ stream->valid_fts_rcvd = TRUE;
+ stream->fts = cts;
+ stream->nts = stream->fts + demux->cfrag_dur; // Approximate nts
+
+ GST_INFO_OBJECT (stream->pad, "storing first ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->fts));
+ } else if (!stream->frame_duration) {
+ stream->frame_duration = cts - stream->fts;
+ GST_DEBUG_OBJECT (stream->pad, "frame duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->frame_duration));
+ }
+
+#else
+
+ /* 10/31/2013: dont want to disturb live case at this moment */
+ if (!demux->is_live && stream->type == HLSDEMUX2_STREAM_AUDIO) {
+ if (!stream->valid_fts_rcvd) {
+ if (GST_CLOCK_DIFF(cts, stream->prev_nts) > 0) {
+ GST_WARNING_OBJECT (stream->pad, "already seen audio timestamps[%"GST_TIME_FORMAT"]"
+ " and previous_nts = %"GST_TIME_FORMAT"...drop this",
+ GST_TIME_ARGS(cts), GST_TIME_ARGS(stream->prev_nts));
+ return FALSE;
+ } else {
+ GST_INFO_OBJECT (stream->pad, "recevied valid audio first ts = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS (cts));
+ demux->cur_audio_fts = cts;
+ stream->valid_fts_rcvd = TRUE;
+ }
+ } else if (!stream->frame_duration) {
+ stream->frame_duration = cts - demux->cur_audio_fts;
+ GST_DEBUG_OBJECT (demux, "audio frame duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->frame_duration));
+ }
+ }
+#endif
+
+ GST_BUFFER_TIMESTAMP (inbuf) = cts;
+
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_downloader_new_buffer (GstElement *appsink, void *user_data)
+{
+ GstHLSDemux2Stream *stream = (GstHLSDemux2Stream *)user_data;
+ GstHLSDemux2 *demux = stream->parent;
+ GstBuffer *inbuf = NULL;
+ gboolean bret = FALSE;
+
+ if (demux->cancelled ) {
+ return;
+ }
+
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_WARNING_OBJECT (demux, "Input buffer not available...");
+ return;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "Received buffer with size = %d and ts = %"GST_TIME_FORMAT,
+ GST_BUFFER_SIZE(inbuf), GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)));
+
+ if (demux->fdownloader->force_timestamps) {
+ GST_BUFFER_TIMESTAMP(inbuf) += (stream->prev_nts + stream->base_ts);
+ GST_DEBUG_OBJECT (stream->pad, "forced timestamp = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)));
+ }
+
+ /* alter the timestamps */
+ bret = gst_hlsdemux2_alter_timestamps (stream, inbuf);
+ if (!bret) {
+ gst_buffer_unref (inbuf);
+ return;
+ }
+
+ /* store buffer in queue */
+ g_queue_push_tail (stream->downloader_queue, inbuf);
+
+ return;
+}
+
+static gboolean
+gst_hlsdemux2_imagebuf_pipe_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstHLSDemux2PvtStream *stream = (GstHLSDemux2PvtStream *)data;
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)stream->parent;
+ gboolean bret = TRUE;
+ gchar *ele_name = gst_element_get_name (GST_MESSAGE_SRC (msg));
+
+ GST_INFO_OBJECT(demux, "Message on BUS : %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ GST_INFO_OBJECT (demux, "received EOS on IMAGE pipe from '%s'.. Closing pipeline", ele_name);
+ g_cond_signal (stream->convert_cond);
+ gst_object_unref (stream->convert_pipe);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GST_ERROR_OBJECT (demux, "Error Image Pipe from %s\n", ele_name);
+ bret = FALSE;
+ break;
+ }
+ default:{
+ GST_DEBUG_OBJECT (demux, "Message on Image Pipe : %s from %s\n", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)), ele_name);
+ bret = FALSE;
+ break;
+ }
+ }
+ g_free(ele_name);
+ ele_name = NULL;
+ return bret;
+}
+
+static gboolean gst_hlsdemux2_done_video_buffer (GstPad * srcpad, GstEvent * event, gpointer user_data)
+{
+ GstHLSDemux2PvtStream *stream = (GstHLSDemux2PvtStream *)user_data;
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)stream->parent;
+ GST_LOG_OBJECT(demux,"EVENT %s on pad : %s having caps : %"GST_PTR_FORMAT,GST_EVENT_TYPE_NAME (event), GST_PAD_NAME(srcpad), srcpad->caps);
+ if(event->type == GST_EVENT_EOS){
+ GST_DEBUG_OBJECT(demux,"EVENT : %s Size : %d Pushing to demux video buffer [%d,%d]",
+ GST_EVENT_TYPE_NAME (event),GST_BUFFER_SIZE(stream->video_buffer),
+ stream->video_buffer->data[0],stream->video_buffer->data[1]);
+ g_cond_signal (stream->convert_cond);
+ }
+ return TRUE;
+}
+
+static gboolean gst_hlsdemux2_set_video_buffer (GstPad * srcpad, GstBuffer * buffer, gpointer user_data)
+{
+ GstHLSDemux2PvtStream *pvt_stream = (GstHLSDemux2PvtStream *)user_data;
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)pvt_stream->parent;
+ GST_LOG_OBJECT(demux,"BUFFER size:%d on pad having caps : %"GST_PTR_FORMAT,GST_BUFFER_SIZE(buffer), srcpad->caps);
+ GST_LOG_OBJECT(demux,"BUFFER DATA : %d %d",buffer->data[0],buffer->data[1]);
+ pvt_stream->video_buffer = buffer;
+ return TRUE;
+}
+
+static void
+gst_hlsdemux2_downloader_eos (GstElement * appsink, void* user_data)
+{
+ GstHLSDemux2Stream *stream = (GstHLSDemux2Stream *)user_data;
+ GstHLSDemux2 *demux = stream->parent;
+ GstBuffer *buf = NULL;
+ gint64 percent = 0;
+
+ /* push all data to queue specific to this stream */
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (demux, "returning due to cancelled / flushing");
+ return;
+ }
+
+ if (g_queue_is_empty (stream->downloader_queue)) {
+ GST_WARNING_OBJECT (demux, "EOS received without any buffer in downloader queue");
+ return;
+ }
+
+ if (demux->fdownloader->force_timestamps) {
+ demux->cur_audio_fts = stream->prev_nts;
+ }
+
+ if (demux->fdownloader->cur_stream_cnt < demux->active_stream_cnt) {
+ gint idx = 0;
+
+ GST_INFO_OBJECT (demux, "need to send dummy data.. check for the stream");
+
+ /* signal queue full condition to come out */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = demux->streams[idx];
+ GError *error = NULL;
+
+ if (cur_stream) {
+ if (g_queue_is_empty (cur_stream->downloader_queue)) {
+ GST_INFO_OBJECT (cur_stream->pad, "enable sending dummy data");
+
+ /* take first ts of audio as cur_stream fts */
+ cur_stream->fts = demux->cur_audio_fts;
+
+ /* create thread to send dummy data */
+ cur_stream->dummy_data_thread = g_thread_create ((GThreadFunc) gst_hlsdemux2_push_dummy_data,
+ cur_stream, TRUE, &error);
+ }
+ }
+ }
+ }
+
+ while (!g_queue_is_empty (stream->downloader_queue)) {
+
+ if (demux->cancelled || demux->flushing) {
+ GST_WARNING_OBJECT (stream->pad, "on cancel/flushing stopping pushing");
+ break;
+ }
+
+ buf = (GstBuffer *) g_queue_pop_head (stream->downloader_queue);
+
+ GST_DEBUG_OBJECT (stream->pad, "input buffer timestamp : %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)));
+
+ g_mutex_lock (stream->queue_lock);
+
+ if (!GST_BUFFER_TIMESTAMP_IS_VALID(buf)){
+ g_queue_push_tail (stream->queue, buf);
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ continue;
+ }
+
+ GST_LOG_OBJECT (stream->pad, "buffer size = %d, ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_BUFFER_SIZE(buf), GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(buf)));
+
+ /* calculate cached duration */
+ gst_hlsdemux2_calculate_pushed_duration (demux, stream, buf);
+
+ if (stream->cached_duration < 0)
+ stream->cached_duration = 0;
+
+ percent = (stream->cached_duration * 100) / demux->total_cache_duration;
+ GST_LOG_OBJECT (stream->pad, "percent done = %"G_GINT64_FORMAT, (gint)percent, percent);
+
+ if (percent > 100) {
+ guint64 overall_percent = 0;
+
+ g_mutex_lock (demux->buffering_lock);
+ overall_percent = demux->percent;
+ g_mutex_unlock (demux->buffering_lock);
+
+ if (overall_percent < 100) { /* otherwise, may cause blocking while buffering*/
+ if (percent > 400) {
+ GST_ERROR_OBJECT (stream->pad,"having worrest buffering.. exiting");
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED, ("wrong buffering.. check implementation"), (NULL));
+ }
+ GST_INFO_OBJECT (stream->pad, "@@@@@@ queue should not go to wait now @@@@@@@@");
+ } else {
+ /* update buffering & wait if space is not available */
+ GST_LOG_OBJECT (stream->pad, "Reached more than 100 percent, queue full & wait till free");
+ g_cond_wait(stream->queue_full, stream->queue_lock);
+ GST_LOG_OBJECT (stream->pad,"Received signal to add more data...");
+ }
+ }
+
+ g_cond_signal (stream->queue_empty);
+
+ g_mutex_unlock (stream->queue_lock);
+ }
+
+ /* wait for dummy threads finish their processing */
+ if (demux->fdownloader->cur_stream_cnt < demux->active_stream_cnt) {
+ gint idx = 0;
+
+ GST_INFO_OBJECT (demux, "need to close dummy threads");
+
+ /* signal queue full condition to come out */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *cur_stream = demux->streams[idx];
+
+ if (cur_stream && cur_stream->dummy_data_thread) {
+ GST_INFO_OBJECT (stream->pad, "waiting for dummy push thread to finish...");
+ g_thread_join(cur_stream->dummy_data_thread);
+ cur_stream->dummy_data_thread = NULL;
+ GST_INFO_OBJECT (stream->pad, "COMPLETED DUMMY PUSH...");
+ }
+ }
+ }
+
+#ifdef LATEST_AV_SYNC
+ if (demux->is_live) {
+ stream->total_stream_time += (stream->lts - stream->fts);
+ } else {
+ stream->total_stream_time += (stream->lts - stream->fts + stream->frame_duration);
+ stream->nts = stream->total_stream_time;
+ }
+#else
+ stream->total_stream_time += (stream->lts - stream->fts);
+#endif
+
+ GST_DEBUG_OBJECT (stream->pad, "---------------- TS VALUES ----------------");
+ GST_DEBUG_OBJECT (stream->pad, "valid start ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->fts));
+ GST_DEBUG_OBJECT (stream->pad, "valid end ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->lts));
+ GST_DEBUG_OBJECT (stream->pad, "fragment duration received = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->lts - stream->fts));
+ GST_DEBUG_OBJECT (stream->pad, "total stream time = %"GST_TIME_FORMAT,GST_TIME_ARGS(stream->total_stream_time));
+ GST_DEBUG_OBJECT (stream->pad, "total disc time = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->total_disc));
+ GST_DEBUG_OBJECT (stream->pad, "next expected start ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(stream->nts));
+ GST_DEBUG_OBJECT (stream->pad, "---------------- DUMP VALUES END----------------");
+
+ stream->apply_disc = FALSE;
+ stream->fts = GST_CLOCK_TIME_NONE;
+ stream->valid_fts_rcvd = FALSE;
+ stream->prev_nts = stream->nts;
+ demux->fdownloader->force_timestamps = FALSE;
+ return;
+}
+
+static void
+gst_hlsdemux2_push_eos (GstHLSDemux2 *demux)
+{
+ guint idx = 0;
+
+ /* signal queue full condition to come out */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ GstHLSDemux2Stream *stream = demux->streams[idx];
+
+ if (stream) {
+ g_mutex_lock (stream->queue_lock);
+ /* end of playlist, push EOS to queue */
+ GST_INFO_OBJECT (stream->pad, "pushing EOS event to stream's queue");
+ g_queue_push_tail (stream->queue, gst_event_new_eos ());
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ }
+ }
+}
+
+static void
+gst_hlsdemux2_on_playlist_buffer (GstElement *appsink, void *data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GstBuffer *inbuf = NULL;
+
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_WARNING_OBJECT (demux, "Input buffer not available...");
+ return;
+ }
+
+ if (demux->pldownloader->playlist == NULL) {
+ demux->pldownloader->playlist = gst_buffer_copy (inbuf);
+ gst_buffer_unref (inbuf);
+ } else {
+ GstBuffer *buffer = NULL;
+ guint size = GST_BUFFER_SIZE(demux->pldownloader->playlist) + GST_BUFFER_SIZE(inbuf);
+
+ buffer = gst_buffer_new_and_alloc (size);
+ if (!buffer) {
+ GST_ERROR_OBJECT (demux, "failed allocate memory...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ gst_buffer_unref (inbuf);
+ gst_buffer_unref (demux->pldownloader->playlist);
+ return;
+ }
+
+ /*copy existing playlist buffer */
+ memcpy (GST_BUFFER_DATA(buffer), GST_BUFFER_DATA(demux->pldownloader->playlist), GST_BUFFER_SIZE(demux->pldownloader->playlist));
+
+ /*copy new playlist buffer */
+ memcpy (GST_BUFFER_DATA(buffer) + GST_BUFFER_SIZE(demux->pldownloader->playlist),
+ GST_BUFFER_DATA(inbuf), GST_BUFFER_SIZE(inbuf));
+
+ gst_buffer_unref (inbuf);
+ gst_buffer_unref (demux->pldownloader->playlist);
+ demux->pldownloader->playlist = buffer;
+ }
+
+ return;
+}
+
+static void
+gst_hlsdemux2_on_key_buffer (GstElement *appsink, void *data)
+{
+ GstHLSDemux2 *demux = (GstHLSDemux2 *)data;
+ GstBuffer *inbuf = NULL;
+
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_WARNING_OBJECT (demux, "Input buffer not available...");
+ return;
+ }
+
+ if (demux->kdownloader->key == NULL) {
+ demux->kdownloader->key = gst_buffer_copy (inbuf);
+ gst_buffer_unref (inbuf);
+ } else {
+ GstBuffer *buffer = NULL;
+ guint size = GST_BUFFER_SIZE(demux->kdownloader->key) + GST_BUFFER_SIZE(inbuf);
+
+ buffer = gst_buffer_new_and_alloc (size);
+ if (!buffer) {
+ GST_ERROR_OBJECT (demux, "failed allocate memory...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("can't allocate memory"), (NULL));
+ gst_buffer_unref (inbuf);
+ gst_buffer_unref (demux->kdownloader->key);
+ return;
+ }
+
+ /*copy existing playlist buffer */
+ memcpy (GST_BUFFER_DATA(buffer), GST_BUFFER_DATA(demux->kdownloader->key), GST_BUFFER_SIZE(demux->kdownloader->key));
+
+ /*copy new playlist buffer */
+ memcpy (GST_BUFFER_DATA(buffer) + GST_BUFFER_SIZE(demux->kdownloader->key),
+ GST_BUFFER_DATA(inbuf), GST_BUFFER_SIZE(inbuf));
+
+ gst_buffer_unref (inbuf);
+ gst_buffer_unref (demux->kdownloader->key);
+ demux->kdownloader->key = buffer;
+ }
+
+ return;
+}
+
+static void
+gst_hlsdemux2_stop (GstHLSDemux2 *demux)
+{
+ GST_INFO_OBJECT (demux, "entering...");
+
+ demux->cancelled = TRUE;
+
+ g_cond_signal (demux->post_msg_start);
+ g_cond_signal (demux->post_msg_exit);
+
+ /* special case: for exiting playlist downloader created from sink event */
+ if (demux->pldownloader && demux->pldownloader->cond)
+ g_cond_broadcast (demux->pldownloader->cond);
+
+ while (demux->download_task && (GST_TASK_STATE (demux->download_task) != GST_TASK_STOPPED)) {
+ int idx = 0;
+ GstHLSDemux2Stream *stream = NULL;
+
+ GST_INFO_OBJECT (demux, "Closing Download task...");
+
+ /* signal queue full conditions */
+ for (idx = 0; idx < HLSDEMUX2_STREAM_NUM; idx++) {
+ stream = demux->streams[idx];
+
+ if (stream && GST_PAD_TASK(stream->pad)) {
+ gst_pad_push_event (stream->pad, gst_event_new_flush_start ());
+ g_cond_broadcast(stream->queue_full);
+ g_cond_broadcast(stream->queue_empty);
+ }
+ }
+
+ if (demux->pl_update_cond)
+ g_cond_broadcast (demux->pl_update_cond);
+
+ if (demux->fdownloader && demux->fdownloader->cond)
+ g_cond_broadcast (demux->fdownloader->cond);
+ if (demux->kdownloader && demux->kdownloader->cond)
+ g_cond_broadcast (demux->kdownloader->cond);
+ if (demux->pldownloader && demux->pldownloader->cond)
+ g_cond_broadcast (demux->pldownloader->cond);
+
+ GST_INFO_OBJECT (demux, "Waiting to acquire download lock");
+ g_static_rec_mutex_lock (&demux->download_lock);
+ g_static_rec_mutex_unlock (&demux->download_lock);
+
+ GST_INFO_OBJECT (demux, "Waiting download task JOIN");
+ gst_task_join (demux->download_task);
+ gst_object_unref (demux->download_task);
+ demux->download_task = NULL;
+
+ GST_INFO_OBJECT (demux, "Completed closing of download task...");
+
+ g_thread_join(demux->buffering_posting_thread);
+ GST_INFO_OBJECT (demux, "Completed closing of download monitor thread...");
+
+ if (demux->fdownloader && demux->fdownloader->pipe) {
+ GST_WARNING_OBJECT (demux, "Still fragment download exist");
+ gst_hlsdemux2_destroy_fragment_download (demux);
+ }
+ if (demux->pldownloader && demux->pldownloader->pipe) {
+ GST_WARNING_OBJECT (demux, "Still playlist download exist");
+ gst_hlsdemux2_destroy_playlist_download (demux);
+ }
+ if (demux->kdownloader && demux->kdownloader->pipe) {
+ GST_WARNING_OBJECT (demux, "Still key download exist");
+ gst_hlsdemux2_destroy_key_download (demux);
+ }
+ }
+
+ gst_hlsdemux2_stop_stream (demux);
+
+ GST_INFO_OBJECT (demux, "leaving...");
+}
+
+static void
+gst_hlsdemux2_stop_stream (GstHLSDemux2 *demux)
+{
+ int n = 0;
+ GstHLSDemux2Stream *stream = NULL;
+ gchar *pad_name = NULL;
+
+ GST_INFO_OBJECT (demux, "entering");
+
+ for (n = 0; n < HLSDEMUX2_STREAM_NUM; n++) {
+ stream = demux->streams[n];
+
+ if (stream && GST_PAD_TASK(stream->pad)) {
+ pad_name = gst_pad_get_name (stream->pad);
+ GST_INFO_OBJECT (stream->pad, "stopping pad task : %s", pad_name);
+ gst_pad_stop_task (stream->pad);
+ GST_INFO_OBJECT (stream->pad, "stopped pad task : %s", pad_name);
+ g_free(pad_name);
+ pad_name = NULL;
+ }
+ }
+
+ for (n = 0; n < HLSDEMUX2_STREAM_NUM; n++) {
+ if (demux->streams[n]) {
+ gst_hlsdemux2_stream_deinit(demux->streams[n], demux);
+ demux->streams[n] = NULL;
+ }
+ }
+ demux->active_stream_cnt = 0;
+ GST_INFO_OBJECT (demux, "leaving");
+}
+
+static void
+gst_hlsdemux2_stream_init (GstHLSDemux2 *demux, GstHLSDemux2Stream *stream, HLSDEMUX2_STREAM_TYPE stream_type, gchar *name, GstCaps *src_caps)
+{
+ stream->queue = g_queue_new ();
+ stream->queue_full = g_cond_new ();
+ stream->queue_empty = g_cond_new ();
+ stream->queue_lock = g_mutex_new ();
+ stream->parent = demux;
+ stream->type = stream_type;
+ stream->percent = 100;
+ stream->eos = FALSE;
+ stream->cached_duration = 0;
+ stream->lts = 0;
+ stream->dummy_data_thread = NULL;
+ stream->base_ts = GST_CLOCK_TIME_NONE;
+ stream->fts = GST_CLOCK_TIME_NONE;
+ stream->valid_fts_rcvd = FALSE;
+ stream->total_stream_time = 0;
+ stream->total_disc = 0;
+ stream->cdisc = 0;
+ stream->nts = GST_CLOCK_TIME_NONE;
+ stream->prev_nts = 0;
+ stream->downloader_queue = g_queue_new();
+ stream->need_newsegment = TRUE;
+ stream->newsegment = NULL;
+ stream->frame_duration = 0;
+
+ if (stream_type == HLSDEMUX2_STREAM_VIDEO) {
+ stream->pad = gst_pad_new_from_static_template (&hlsdemux2_videosrc_template, name);
+ GST_INFO_OBJECT (demux, "Its a Multi-variant stream..");
+ demux->stream_config = HLSDEMUX2_MULTI_VARIANT;
+ } else if (stream_type == HLSDEMUX2_STREAM_AUDIO) {
+ stream->pad = gst_pad_new_from_static_template (&hlsdemux2_audiosrc_template, name);
+ } else if (stream_type == HLSDEMUX2_STREAM_TEXT) {
+ stream->pad = gst_pad_new_from_static_template (&hlsdemux2_subpicture_template, name);
+ } else if (stream_type == HLSDEMUX2_STREAM_PRIVATE) {
+ stream->pad = gst_pad_new_from_static_template (&hlsdemux2_private_template, name);
+ }
+
+ GST_INFO_OBJECT (demux, "======>>>> created hlsdemux2 source pad : %s", name);
+
+ GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
+
+ gst_pad_use_fixed_caps (stream->pad);
+ gst_pad_set_event_function (stream->pad, gst_hlsdemux2_handle_src_event);
+ gst_pad_set_query_function (stream->pad, gst_hlsdemux2_src_query);
+ gst_pad_set_caps (stream->pad, src_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);
+
+ if (!gst_pad_is_linked (stream->pad)) {
+ GST_WARNING_OBJECT (stream->pad, "'%s' pad is not linked...", GST_OBJECT_NAME (stream->pad));
+ stream->is_linked = FALSE;
+ return;
+ }
+ stream->is_linked = TRUE;
+
+ if (!gst_pad_start_task (stream->pad, (GstTaskFunction) gst_hlsdemux2_push_loop, stream)) {
+ GST_ERROR_OBJECT (demux, "failed to create stream task...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to create stream push loop"), (NULL));
+ return;
+ }
+
+}
+
+static void
+gst_hlsdemux2_stream_deinit (GstHLSDemux2Stream *stream, gpointer data)
+{
+ GstHLSDemux2 * demux = (GstHLSDemux2 *)data;
+ if (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->queue_lock) {
+ g_mutex_free (stream->queue_lock);
+ stream->queue_lock = NULL;
+ }
+ if (stream->queue_full) {
+ g_cond_free (stream->queue_full);
+ stream->queue_full = NULL;
+ }
+ if (stream->queue_empty) {
+ g_cond_free (stream->queue_empty);
+ stream->queue_empty= NULL;
+ }
+ g_free (stream);
+ }
+
+}
+
+static gboolean
+gst_hlsdemux2_set_location (GstHLSDemux2 * demux, const gchar * uri)
+{
+ if (demux->client)
+ gst_m3u8_client_free (demux->client);
+ demux->client = gst_m3u8_client_new (uri);
+ GST_INFO_OBJECT (demux, "Changed location: %s", uri);
+ return TRUE;
+}
+
+static gchar *
+gst_hlsdemux2_src_buf_to_utf8_playlist (GstBuffer * buf)
+{
+ gint size;
+ gchar *data;
+ gchar *playlist;
+
+ data = (gchar *) GST_BUFFER_DATA (buf);
+ size = GST_BUFFER_SIZE (buf);
+
+#if 0 /* giving error some times, so removing*/
+ if (!g_utf8_validate (data, size, NULL))
+ goto validate_error;
+#endif
+
+ /* alloc size + 1 to end with a null character */
+ playlist = g_malloc0 (size + 1);
+ if (!playlist) {
+ GST_ERROR ("failed to create memory...");
+ gst_buffer_unref (buf);
+ return NULL;
+ }
+
+ memcpy (playlist, data, size);
+ playlist[size] = '\0';
+
+ gst_buffer_unref (buf);
+ return playlist;
+#if 0
+validate_error:
+ gst_buffer_unref (buf);
+ GST_ERROR ("failed to validate playlist");
+ return NULL;
+#endif
+}
+
+static void
+gst_hlsdemux2_handle_private_pad (GstHLSDemux2 *demux, GstPad *srcpad)
+{
+ GstHLSDemux2PvtStream *pvt_stream = NULL;
+ gchar *sinkname = NULL;
+ GstPad *sinkpad = NULL;
+
+ pvt_stream = g_new0 (GstHLSDemux2PvtStream, 1);
+
+ demux->private_stream = pvt_stream;
+ pvt_stream->parent = demux;
+ pvt_stream->type = HLSDEMUX2_STREAM_PRIVATE;
+ pvt_stream->image_buffer = NULL;
+ pvt_stream->got_img_buffer = FALSE;
+ pvt_stream->video_buffer = NULL;
+ pvt_stream->id3_buffer = NULL;
+ pvt_stream->convert_lock = g_mutex_new ();
+ pvt_stream->convert_cond = g_cond_new ();
+ pvt_stream->sink = NULL;
+ pvt_stream->img_load_lock = g_mutex_new ();
+ pvt_stream->img_load_cond = g_cond_new ();
+
+ /* create sink element */
+ sinkname = g_strdup_printf("%s-%s", GST_PAD_NAME(srcpad) , "sink");
+ pvt_stream->sink = gst_element_factory_make ("appsink", NULL);
+ if (!pvt_stream->sink) {
+ GST_ERROR_OBJECT (demux, "failed to create sink element - %s", sinkname);
+ GST_ELEMENT_ERROR (demux, CORE, FAILED, ("failed to create element - %s", sinkname), (NULL));
+ g_free(sinkname);
+ sinkname = NULL;
+ return;
+ }
+
+ /* set sink element to PLAYING state */
+ gst_bin_add (GST_BIN (demux->fdownloader->pipe), pvt_stream->sink);
+ gst_element_set_state (pvt_stream->sink, GST_STATE_READY);
+ g_object_set (G_OBJECT (pvt_stream->sink), "sync", FALSE, "emit-signals", TRUE, NULL);
+
+ sinkpad = gst_element_get_pad(pvt_stream->sink, "sink");
+ if (!sinkpad) {
+ GST_ERROR_OBJECT (demux, "failed to get sinkpad from element - %s", sinkname);
+ GST_ELEMENT_ERROR (demux, CORE, PAD, ("Count not get sink pad"), (NULL));
+ g_free(sinkname);
+ sinkname = NULL;
+ return;
+ }
+
+ if (gst_pad_link(srcpad, sinkpad) != GST_PAD_LINK_OK) {
+ GST_ERROR_OBJECT (demux, "failed to link sink bin elements");
+ GST_ELEMENT_ERROR (demux, CORE, NEGOTIATION, ("Could not link pads.."), (NULL));
+ gst_object_unref(sinkpad);
+ }
+
+ gst_element_set_state (pvt_stream->sink, GST_STATE_PLAYING);
+
+ g_signal_connect (pvt_stream->sink, "new-buffer", G_CALLBACK (gst_hlsdemux2_private_sink_on_new_buffer), pvt_stream);
+ g_signal_connect (pvt_stream->sink, "eos", G_CALLBACK (gst_hlsdemux2_private_sink_on_eos), pvt_stream);
+
+ gst_object_unref(sinkpad);
+
+ GST_INFO_OBJECT (demux, "successfully created private pad bin...");
+
+ g_free(sinkname);
+ sinkname = NULL;
+ return;
+}
+
+static void
+gst_hlsdemux2_private_sink_on_new_buffer (GstElement *appsink, void *user_data)
+{
+ GstHLSDemux2PvtStream *pvt_stream = (GstHLSDemux2PvtStream *)user_data;
+ GstHLSDemux2 *demux = pvt_stream->parent;
+ GstBuffer *inbuf = NULL;
+
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_ERROR_OBJECT (demux, "Input buffer not available....");
+ return;
+ }
+
+ GST_LOG_OBJECT (demux, "Image buffer received of size = %d", GST_BUFFER_SIZE (inbuf));
+ if(pvt_stream->id3_buffer==NULL)
+ pvt_stream->id3_buffer = gst_buffer_copy(inbuf);
+ else
+ pvt_stream->id3_buffer = gst_buffer_join (pvt_stream->id3_buffer, inbuf);
+ return;
+}
+
+static void
+gst_hlsdemux2_private_sink_on_eos (GstElement * appsink, void* user_data)
+{
+ GstHLSDemux2PvtStream *pvt_stream = (GstHLSDemux2PvtStream *)user_data;
+ GstHLSDemux2 *demux = pvt_stream->parent;
+ GstBuffer *image_buffer = NULL;
+ GstTagList *tag_list = gst_tag_list_from_id3v2_tag(pvt_stream->id3_buffer);
+
+ GST_INFO_OBJECT (demux, "Processing image buffer");
+
+ if(!gst_tag_list_get_buffer (tag_list, GST_TAG_IMAGE, &image_buffer)) {
+ GST_ERROR_OBJECT(demux, "Failed to obtain image data.");
+ return;
+ }
+
+ if(demux->stream_config == HLSDEMUX2_SINGLE_VARIANT) {
+ gchar *image_header = NULL;
+ GValue codec_type = G_VALUE_INIT;
+ GstMessage * tag_message = NULL;
+
+ /* check whether it is same as previous image */
+ if (demux->prev_image_buffer &&
+ (GST_BUFFER_SIZE(demux->prev_image_buffer) == GST_BUFFER_SIZE(image_buffer))) {
+ if (!memcmp (GST_BUFFER_DATA(demux->prev_image_buffer), GST_BUFFER_DATA(image_buffer), GST_BUFFER_SIZE(image_buffer))) {
+ GST_INFO_OBJECT (demux, "current & previous embedded images are same..no need to post image again");
+ gst_buffer_unref (image_buffer);
+ return;
+ }
+ }
+
+ if (demux->prev_image_buffer) {
+ gst_buffer_unref (demux->prev_image_buffer);
+ }
+ demux->prev_image_buffer = gst_buffer_copy(image_buffer);
+
+ image_header = g_strndup((gchar *) image_buffer->data, 8);
+
+ g_value_init (&codec_type, G_TYPE_STRING);
+
+ if((image_header[0] == 0xFF) && (image_header[1] == 0xD8)) {
+ GST_INFO_OBJECT(demux, "Found JPEG image header");
+ g_value_set_static_string (&codec_type, "image/jpeg");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ } else if (!g_strcmp0(image_header, "\211PNG\015\012\032\012")) {
+ GST_INFO_OBJECT(demux, "Found PNG image header");
+ g_value_set_static_string (&codec_type, "image/png");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ } else {
+ g_value_set_static_string (&codec_type, "image/unknown");
+ GST_INFO_OBJECT(demux, "Unknown image header");
+ gst_tag_list_add_value(tag_list, GST_TAG_MERGE_APPEND, GST_TAG_CODEC, &codec_type);
+ }
+
+ GST_INFO_OBJECT(demux, "Set value : %s", g_value_get_string (&codec_type));
+ g_value_unset(&codec_type);
+
+ tag_message = gst_message_new_tag (GST_OBJECT_CAST (demux), tag_list);
+ if(!gst_element_post_message (GST_ELEMENT_CAST (demux), tag_message)) {
+ GST_ERROR_OBJECT (demux, "failed to post image tag");
+ gst_buffer_unref (image_buffer);
+ g_free (image_header);
+ return;
+ }
+
+ GST_INFO_OBJECT (demux, "successfully posted image tag...");
+ gst_buffer_unref (image_buffer);
+ g_free (image_header);
+ return;
+ }
+
+ GST_INFO_OBJECT(demux, "image header : %d,%d", image_buffer->data[0],image_buffer->data[1]);
+
+ pvt_stream->image_buffer = gst_buffer_copy(image_buffer);
+ gst_buffer_unref (image_buffer);
+ pvt_stream->got_img_buffer = TRUE;
+
+ g_cond_signal (pvt_stream->img_load_cond);
+ demux->has_image_buffer = TRUE;
+
+}
+
+static gboolean
+hlsdemux2_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "hlsdemux2", GST_RANK_PRIMARY + 1, GST_TYPE_HLSDEMUX2) || FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "hlsdemux2",
+ "HLS demux - 2 plugin",
+ hlsdemux2_init,
+ VERSION,
+ "LGPL",
+ PACKAGE_NAME,
+ "http://www.samsung.com/")
+
--- /dev/null
+/*
+ * hlsdemux2
+ *
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>, Naveen Cherukuri <naveen.ch@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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_HLSDEMUX2_H__
+#define __GST_HLSDEMUX2_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/app/gstappsink.h>
+#include "m3u8.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_HLSDEMUX2 (gst_hlsdemux2_get_type())
+#define GST_HLSDEMUX2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_HLSDEMUX2, GstHLSDemux2))
+#define GST_HLSDEMUX2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HLSDEMUX2,GstHLSDemux2Class))
+#define GST_IS_HLSDEMUX2(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HLSDEMUX2))
+#define GST_IS_HLSDEMUX2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HLSDEMUX2))
+
+typedef struct _GstHLSDemux2 GstHLSDemux2;
+typedef struct _GstHLSDemux2Class GstHLSDemux2Class;
+typedef struct _GstHLSDemux2Stream GstHLSDemux2Stream;
+typedef struct _GstHLSDemux2PvtStream GstHLSDemux2PvtStream;
+
+#define GST_HLS_DEMUX_AES_BLOCK_SIZE 16
+
+#define ON_ERROR_REQUEST_AGAIN // Orange server giving soup errors. so, request for fail count times
+
+#define HLSDEMUX2_CHUNK_TIME_DURATION (0.5 * GST_SECOND)
+#define HLSDEMUX2_NUM_CHUNK_DOWNLOADS_FAST_SWITCH 4
+#define HLSDEMUX2_MAX_N_PAST_FRAG_DOWNLOADRATES 3
+
+#define HLSDEMUX2_FAST_CHECK_CRITICAL_TIME_FACTOR 1.2
+#define HLSDEMUX2_FAST_CHECK_WARNING_TIME_FACTOR 1
+
+#ifdef USING_AVG_PAST_DOWNLOADRATES
+#define HLSDEMUX2_MAX_N_PAST_DOWNLOADRATES 3
+#endif
+
+
+#define PLAYLIST_REFRESH_TIMEOUT (20 * GST_SECOND)
+
+#define HLSDEMUX2_OVERHEAD (1 * GST_SECOND)
+
+#define LATEST_AV_SYNC
+
+typedef enum
+{
+ HLSDEMUX2_STREAM_VIDEO = 0,
+ HLSDEMUX2_STREAM_AUDIO,
+ HLSDEMUX2_STREAM_TEXT,
+ HLSDEMUX2_STREAM_PRIVATE,
+ HLSDEMUX2_STREAM_NUM,
+}HLSDEMUX2_STREAM_TYPE;
+
+typedef enum
+{
+ HLSDEMUX2_DOWNWARD_RECOVERY = 0,
+ HLSDEMUX2_UPWARD_RECOVERY,
+ HLSDEMUX2_NO_RECOVERY,
+}HLSDEMUX2_RECOVERY_MODE;
+
+typedef enum
+{
+ HLSDEMUX2_MULTI_VARIANT = 0,
+ HLSDEMUX2_SINGLE_VARIANT
+}HLSDEMUX2_STREAM_CONFIGURATION;
+
+typedef struct
+{
+ GstBin *sinkbin;
+ GstElement *parser; /*only for .aac stream */
+ GstElement *queue;
+ GstElement *sink;
+}HLSDemux2SinkBin;
+
+typedef struct
+{
+ GstElement *pipe;
+ GstElement *urisrc;
+ GstElement *typefind;
+ GstElement *queue;
+ GstElement *demuxer;
+ GList *sinkbins;
+
+ guint64 content_size;
+ gboolean applied_fast_switch;
+
+ gchar *remaining_data;
+ guint remaining_size;
+
+ GMutex *lock;
+ GCond *cond;
+ gboolean is_encrypted;
+
+ gulong src_bprobe;
+ gboolean get_next_frag; /* on error like NOT_FOUND, request next fragment */
+
+ guint cur_stream_cnt;
+ gboolean first_buffer;
+ gboolean force_timestamps; /* useful in .aac files playback */
+ gboolean error_rcvd;
+ gboolean find_mediaseq;
+ GstClockTime seeked_pos;
+ GstClockTime cur_running_dur;
+
+ /* for fragment download rate calculation */
+ guint64 download_rate;
+ GstClockTime download_start_ts;
+ GstClockTime download_stop_ts;
+ guint64 src_downloaded_size;
+ guint64 queue_downloaded_size;
+ gint ndownloaded; /* number of fragments downloaded */
+ GArray *avg_chunk_drates; /* chunk download rates array */
+ GArray *avg_frag_drates; /* fragment download rates array */
+ guint64 chunk_downloaded_size;
+ GstClockTime chunk_start_ts;
+}HLSDemux2FragDownloader;
+
+/* playlist downloader */
+typedef struct
+{
+ GstElement *pipe;
+ GstBus *bus;
+ GstElement *urisrc;
+ GstElement *sink;
+ GMutex *lock;
+ GCond *cond;
+
+ GstBuffer *playlist;
+ HLSDEMUX2_RECOVERY_MODE recovery_mode;
+ GstClockTime download_start_ts;
+}HLSDemux2PLDownloader;
+
+/* Key downloader */
+typedef struct
+{
+ GstElement *pipe;
+ GstBus *bus;
+ GstElement *urisrc;
+ GstElement *sink;
+
+ GMutex *lock;
+ GCond *cond;
+
+ GstBuffer *key;
+ gchar *prev_key_uri;
+}HLSDemux2KeyDownloader;
+
+/**
+ * GstHLSDemux2:
+ *
+ * Opaque #GstHLSDemux2 data structure.
+ */
+struct _GstHLSDemux2
+{
+ GstElement parent;
+
+ GstPad *sinkpad;
+
+ /* M3U8 client */
+ GstM3U8Client *client;
+
+ gboolean is_live;
+
+ HLSDemux2FragDownloader *fdownloader; /* fragment downloader */
+ HLSDemux2PLDownloader *pldownloader; /* playlist downloader */
+ HLSDemux2KeyDownloader *kdownloader; /* key downloader */
+
+ /* properties */
+ gint blocksize;
+
+ /* master playlist got from upstream */
+ GstBuffer *playlist;
+
+ /* fragment download task */
+ GstTask *download_task;
+ GStaticRecMutex download_lock;
+
+ /* Playlist updates task for live session */
+ GStaticRecMutex updates_lock;
+ GTimeVal next_update; /* Time of the next update */
+
+ /* playlist update condition */
+ GCond *pl_update_cond;
+ GMutex *pl_update_lock;
+
+ /* array of pointers to each stream's list */
+ GstHLSDemux2Stream *streams[HLSDEMUX2_STREAM_NUM];
+ gint active_stream_cnt;
+
+ gint percent; /* buffering percentage posting */
+ GMutex *buffering_lock;
+ gboolean is_buffering;
+ GThread *buffering_posting_thread;
+ GMutex *post_msg_lock;
+ GCond *post_msg_start;
+ GCond *post_msg_exit;
+
+ /* HTTP request cookies. */
+ gchar **lastCookie;
+ gchar **playlistCookie;
+ gchar **keyCookie;
+ gchar **fragCookie;
+ gchar *lastDomain;
+ gchar *playlistDomain;
+ gchar *keyDomain;
+ gchar *fragDomain;
+
+ gchar *user_agent;
+
+ /* Properties */
+ gfloat bitrate_switch_tol; /* tolerance with respect to the fragment duration to switch the bitarate*/
+
+ gboolean cancelled;
+ gboolean end_of_playlist;
+
+ guint64 total_cache_duration;
+ guint64 target_duration;
+
+#ifdef ON_ERROR_REQUEST_AGAIN
+ /* requesting the same uron error */
+ gchar *playlist_uri;
+ gchar *frag_uri; /* NOT supporting YET for fragment download errors*/
+ gchar *key_uri;
+#endif
+
+ int soup_request_fail_cnt;
+ /* can be removed [mainly for debugging & testing] */
+ gboolean force_lower_bitrate;
+
+ GstClockTime cfrag_dur;
+ gboolean error_posted;
+ gboolean flushing;
+ GstClockTime ns_start;
+ GstClockTime cur_audio_fts;
+
+ GstHLSDemux2PvtStream * private_stream;
+ gboolean has_image_buffer;
+ HLSDEMUX2_STREAM_CONFIGURATION stream_config;
+ GstBuffer *prev_image_buffer;
+ GstBuffer *prev_video_buffer;
+};
+
+struct _GstHLSDemux2Class
+{
+ GstElementClass parent_class;
+};
+
+GType gst_hlsdemux2_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_HLSDEMUX2_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * m3u8.c:
+ *
+ * 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 <stdlib.h>
+#include <math.h>
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include "m3u8.h"
+
+static GstM3U8 *gst_m3u8_new (void);
+static void gst_m3u8_free (GstM3U8 * m3u8);
+static gboolean gst_m3u8_update (GstM3U8 * m3u8, gchar * data,
+ gboolean * updated);
+static GstM3U8MediaFile *
+gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
+ gchar *key_url, gchar *IV, guint sequence);
+static void gst_m3u8_media_file_free (GstM3U8MediaFile * self);
+static void gst_m3u8_key_free (GstM3U8Key * self);
+
+#define GST_CAT_DEFAULT hlsdemux2_m3u8_debug
+
+#define BITRATE_SWITCH_UPPER_THRESHOLD 0.3
+#define BITRATE_SWITCH_LOWER_THRESHOLD 0
+#define SWITCH_TRIGGER_POINT 1
+
+static GstM3U8 *
+gst_m3u8_new (void)
+{
+ GstM3U8 *m3u8;
+
+ m3u8 = g_new0 (GstM3U8, 1);
+ m3u8->last_data = NULL;
+ return m3u8;
+}
+
+static void
+gst_m3u8_set_uri (GstM3U8 * self, gchar * uri)
+{
+ g_return_if_fail (self != NULL);
+
+ if (self->uri)
+ g_free (self->uri);
+ self->uri = uri;
+}
+
+static void
+gst_m3u8_free (GstM3U8 * self)
+{
+ g_return_if_fail (self != NULL);
+
+ g_free (self->uri);
+ g_free (self->allowcache);
+ g_free (self->codecs);
+ //g_free (self->cipher_ctx);
+
+ g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
+ g_list_free (self->files);
+
+ g_free (self->last_data);
+ g_list_foreach (self->lists, (GFunc) gst_m3u8_free, NULL);
+ g_list_free (self->lists);
+
+ g_free (self);
+}
+
+static GstM3U8MediaFile *
+gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
+ gchar *key_url, gchar *IV, guint sequence)
+{
+ GstM3U8MediaFile *file;
+
+ file = g_new0 (GstM3U8MediaFile, 1);
+ file->uri = uri;
+ file->title = title;
+ file->duration = duration;
+ file->sequence = sequence;
+
+ GST_DEBUG ("Duration of the file = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
+
+ if (key_url != NULL)
+ file->key_url = g_strdup (key_url);
+ else
+ file->key_url = NULL;
+
+ file->iv = IV;
+
+ return file;
+}
+
+static void
+gst_m3u8_media_file_free (GstM3U8MediaFile * self)
+{
+ g_return_if_fail (self != NULL);
+
+ if (self->key_url) {
+ g_free (self->key_url);
+ self->key_url = NULL;
+ }
+ if (self->iv) {
+ g_free (self->iv);
+ self->iv = NULL;
+ }
+ if (self->title) {
+ g_free (self->title);
+ self->title = NULL;
+ }
+ if (self->uri){
+ g_free (self->uri);
+ self->uri = NULL;
+ }
+ g_free (self);
+}
+
+static gchar *
+gst_m3u8_getIV_from_mediasequence (GstM3U8 *self)
+{
+ gchar *IV = NULL;
+
+ IV = g_malloc0 (16);
+ if (!IV) {
+ GST_ERROR ("Failed to allocate memory...");
+ return NULL;
+ }
+
+ if (self->mediasequence > INT_MAX) {
+ GST_ERROR ("media sequnece is greater than INT_MAX...yet to handle");
+ }
+
+ IV [15] = (gchar)(self->mediasequence);
+ IV [14] = (gchar)(self->mediasequence >> 8);
+ IV [13] = (gchar)(self->mediasequence >> 16);
+ IV [12] = (gchar)(self->mediasequence >> 24);
+ return IV;
+}
+
+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)) {
+ GST_WARNING ("%s", g_strerror (errno));
+ return FALSE;
+ }
+
+ if (endptr)
+ *endptr = end;
+
+ return end != ptr;
+}
+
+static gboolean
+double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
+{
+ gchar *end;
+ gdouble ret;
+
+ g_return_val_if_fail (ptr != NULL, FALSE);
+ g_return_val_if_fail (val != NULL, FALSE);
+
+ errno = 0;
+ ret = g_strtod (ptr, &end);
+ if ((errno == ERANGE && (ret == HUGE_VAL || ret == -HUGE_VAL))
+ || (errno != 0 && ret == 0)) {
+ GST_WARNING ("%s", g_strerror (errno));
+ return FALSE;
+ }
+
+ if (!isfinite (ret)) {
+ GST_WARNING ("%s", g_strerror (ERANGE));
+ return FALSE;
+ }
+
+ if (endptr)
+ *endptr = end;
+
+ *val = (gdouble) ret;
+
+ return end != ptr;
+}
+
+static gboolean
+iv_from_string (gchar * ptr, gchar ** endptr, guint8 * val)
+{
+ guint8 idx;
+ guint8 hex;
+
+ g_return_val_if_fail (ptr != NULL, FALSE);
+ g_return_val_if_fail (val != NULL, FALSE);
+
+ if (*ptr == '0' && (*(ptr + 1) == 'x' || *(ptr + 1) == 'X')) {
+ ptr = ptr + 2; /* skip 0x or 0X */
+ }
+
+ for (idx = 0; idx < GST_M3U8_IV_LEN; idx++) {
+ hex = *ptr++ - '0';
+ if (hex >= 16)
+ return FALSE;
+ *(val + idx) = hex * 16;
+ hex = *ptr++ - '0';
+ if (hex >= 16)
+ return FALSE;
+ *(val + idx) += hex;
+ }
+
+ if (endptr)
+ *endptr = ptr;
+
+ return TRUE;
+}
+
+static gboolean
+iv_from_uint (guint uint, guint8 * val)
+{
+ guint8 idx;
+
+ g_return_val_if_fail (val != NULL, FALSE);
+
+ val = val + GST_M3U8_IV_LEN - 1;
+ for (idx = 0; idx < sizeof(guint); idx++) {
+ *val-- = (guint8)uint;
+ uint = uint >> 8;
+ }
+ return TRUE;
+}
+
+static gboolean
+make_valid_uri (gchar *prefix, gchar *suffix, gchar **uri)
+{
+ gchar *slash;
+
+ if (!prefix) {
+ GST_WARNING ("uri prefix not set, can't build a valid uri");
+ return FALSE;
+ }
+ slash = g_utf8_strrchr (prefix, -1, '/');
+ if (!slash) {
+ GST_WARNING ("Can't build a valid uri");
+ return FALSE;
+ }
+
+ *slash = '\0';
+ *uri = g_strdup_printf ("%s/%s", prefix, suffix);
+ *slash = '/';
+
+ return TRUE;
+}
+
+static gboolean
+parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
+{
+ gchar *end, *p;
+
+ g_return_val_if_fail (ptr != NULL, FALSE);
+ g_return_val_if_fail (*ptr != NULL, FALSE);
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (v != NULL, FALSE);
+
+ /* [attribute=value,]* */
+
+ *a = *ptr;
+ end = p = g_utf8_strchr (*ptr, -1, ',');
+ if (end) {
+ do {
+ end = g_utf8_next_char (end);
+ } while (end && *end == ' ');
+ *p = '\0';
+ }
+
+ *v = p = g_utf8_strchr (*ptr, -1, '=');
+ if (*v) {
+ *v = g_utf8_next_char (*v);
+ *p = '\0';
+ } else {
+ GST_WARNING ("missing = after attribute");
+ return FALSE;
+ }
+
+ *ptr = end;
+ return TRUE;
+}
+
+static gint
+_m3u8_compare_uri (GstM3U8 * a, gchar * uri)
+{
+ g_return_val_if_fail (a != NULL, 0);
+ g_return_val_if_fail (uri != NULL, 0);
+
+ return g_strcmp0 (a->uri, uri);
+}
+
+static gint
+gst_m3u8_compare_playlist_by_bitrate (gconstpointer a, gconstpointer b)
+{
+ return ((GstM3U8 *) (a))->bandwidth - ((GstM3U8 *) (b))->bandwidth;
+}
+
+/*
+ * @data: a m3u8 playlist text data, taking ownership
+ */
+static gboolean
+gst_m3u8_update (GstM3U8 * self, gchar *data, gboolean * updated)
+{
+ gint val;
+ GstClockTime duration = 0;
+ gchar *title, *end;
+// gboolean discontinuity;
+ GstM3U8 *list;
+ gchar *key_url = NULL;
+ gchar *IV = NULL;
+ gchar *actual_data = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (updated != NULL, FALSE);
+
+ *updated = TRUE;
+
+ /* check if the data changed since last update */
+ if (self->last_data && g_str_equal (self->last_data, data)) {
+ GST_DEBUG ("Playlist is the same as previous one");
+ *updated = FALSE;
+ g_free (data);
+ return TRUE;
+ }
+
+ if (!g_str_has_prefix (data, "#EXTM3U")) {
+ GST_WARNING ("Data doesn't start with #EXTM3U");
+ *updated = FALSE;
+ g_free (data);
+ return FALSE;
+ }
+
+ if (self->last_data)
+ g_free (self->last_data);
+
+ self->last_data = g_strdup(data);
+
+ actual_data = data;
+
+ if (self->files) {
+ g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
+ g_list_free (self->files);
+ self->files = NULL;
+ }
+
+ list = NULL;
+ duration = 0;
+ title = NULL;
+ data += 7;
+
+ while (TRUE) {
+
+ end = g_utf8_strchr (data, -1, '\n');
+ if (end)
+ *end = '\0';
+
+ if (data[0] != '#') {
+ gchar *r;
+ gchar *uri = NULL;
+
+ if (duration <= 0 && list == NULL) {
+ GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
+ goto next_line;
+ }
+
+ if (!gst_uri_is_valid (data)) {
+ if (!make_valid_uri(self->uri, data, &uri))
+ goto next_line;
+ } else {
+ uri = g_strdup (data);
+ }
+
+ r = g_utf8_strchr (uri, -1, '\r');
+ if (r)
+ *r = '\0';
+
+ if (list != NULL) {
+ if (g_list_find_custom (self->lists, uri, (GCompareFunc) _m3u8_compare_uri)) {
+ GST_DEBUG ("Already have a list with this URI");
+ gst_m3u8_free (list);
+ g_free (uri);
+ } else {
+ gst_m3u8_set_uri (list, uri);
+ self->lists = g_list_append (self->lists, list);
+ }
+ list = NULL;
+ } else {
+ GstM3U8MediaFile *file;
+ gchar *send_IV = NULL;
+
+ if (key_url) {
+ GST_DEBUG ("AES-128 key url = %s", key_url);
+ if (NULL == IV) {
+ /* IV is not present in EXT-X-KEY tag. Prepare IV based on mediasequence */
+ GST_DEBUG ("IV is not in EXT-X-KEY tag... generating from media_seq_num = %d", self->mediasequence);
+ send_IV = gst_m3u8_getIV_from_mediasequence (self);
+ } else {
+ int i=0;
+ send_IV = g_malloc0(16);
+ for(i=0;i<16;i++){
+ send_IV[i]=IV[i];
+ }
+ }
+ }
+
+ file =
+ gst_m3u8_media_file_new (uri, title, duration, key_url, send_IV,
+ self->mediasequence++);
+ duration = 0;
+ title = NULL;
+ send_IV = NULL;
+
+ self->files = g_list_append (self->files, file);
+ GST_DEBUG ("------------>>>>>>appending uri = %s and key = %s\n", file->uri, file->key_url);
+ }
+ } else if (g_str_has_prefix (data, "#EXT-X-ENDLIST")) {
+ GST_DEBUG ("*************** End list is present ****************");
+ self->endlist = TRUE;
+ } else if (g_str_has_prefix (data, "#EXT-X-VERSION:")) {
+ if (int_from_string (data + 15, &data, &val, 10))
+ self->version = val;
+ } else if (g_str_has_prefix (data, "#EXT-X-STREAM-INF:")) {
+ gchar *v, *a;
+
+ if (list != NULL) {
+ GST_WARNING ("Found a list without a uri..., dropping");
+ gst_m3u8_free (list);
+ }
+
+ list = gst_m3u8_new ();
+ data = data + 18;
+ while (data && parse_attributes (&data, &a, &v)) {
+ if (g_str_equal (a, "BANDWIDTH")) {
+ if (!int_from_string (v, NULL, &list->bandwidth, 10))
+ GST_WARNING ("Error while reading BANDWIDTH");
+ } else if (g_str_equal (a, "PROGRAM-ID")) {
+ if (!int_from_string (v, NULL, &list->program_id, 10))
+ GST_WARNING ("Error while reading PROGRAM-ID");
+ } else if (g_str_equal (a, "CODECS")) {
+ g_free (list->codecs);
+ list->codecs = g_strdup (v);
+ } else if (g_str_equal (a, "RESOLUTION")) {
+ if (!int_from_string (v, &v, &list->width, 10))
+ GST_WARNING ("Error while reading RESOLUTION width");
+ if (!v || *v != '=') {
+ GST_WARNING ("Missing height");
+ } else {
+ v = g_utf8_next_char (v);
+ if (!int_from_string (v, NULL, &list->height, 10))
+ GST_WARNING ("Error while reading RESOLUTION height");
+ }
+ }
+ }
+ } else if (g_str_has_prefix (data, "#EXT-X-TARGETDURATION:")) {
+ if (int_from_string (data + 22, &data, &val, 10))
+ self->targetduration = val * GST_SECOND;
+ } else if (g_str_has_prefix (data, "#EXT-X-MEDIA-SEQUENCE:")) {
+ if (int_from_string (data + 22, &data, &val, 10))
+ self->mediasequence = val;
+ } else if (g_str_has_prefix (data, "#EXT-X-DISCONTINUITY")) {
+ /* discontinuity = TRUE; */
+ } else if (g_str_has_prefix (data, "#EXT-X-PROGRAM-DATE-TIME:")) {
+ /* <YYYY-MM-DDThh:mm:ssZ> */
+ GST_DEBUG ("FIXME parse date");
+ } else if (g_str_has_prefix (data, "#EXT-X-ALLOW-CACHE:")) {
+ g_free (self->allowcache);
+ self->allowcache = g_strdup (data + 19);
+ } else if (g_str_has_prefix (data, "#EXTINF:")) {
+ gdouble fval;
+ if (!double_from_string (data + 8, &data, &fval)) {
+ GST_WARNING ("Can't read EXTINF duration");
+ goto next_line;
+ }
+ duration = fval * (gdouble) GST_SECOND;
+ if (duration > self->targetduration)
+ GST_WARNING ("EXTINF duration > TARGETDURATION");
+ if (!data || *data != ',')
+ goto next_line;
+ data = g_utf8_next_char (data);
+ if (data != end) {
+ g_free (title);
+ title = g_strdup (data);
+ }
+ } else if (g_str_has_prefix (data, "#EXT-X-KEY:")) {
+ gchar *val, *attr;
+ GST_DEBUG ("Found EXT-X-KEY tag...");
+
+ /* handling encrypted content */
+ data = data + 11; /* skipping "#EXT-X-KEY:" tag */
+
+ while (data && parse_attributes (&data, &attr, &val)) {
+ if (g_str_equal (attr, "METHOD")) {
+ if (g_str_equal (val, "NONE")) {
+ GST_LOG ("Non encrypted file...and skipping current line and going to next line\n");
+ goto next_line;
+ } else if (g_str_equal (val, "AES-128")) {
+ /* media files are encrypted */
+ GST_LOG ("media files are encrypted with AES-128\n");
+ // TODO: indicate in flag whether encrypted files or not
+ }
+ } else if (g_str_equal (attr, "URI")) {
+ gchar *end_dq = NULL;
+
+ if (!val){
+ GST_ERROR ("val is NULL");
+ break;
+ }
+
+ val = val + 1; /* eliminating first double quote in url */
+ if (!val) {
+ GST_ERROR ("val is NULL");
+ break;
+ }
+
+ end_dq = g_utf8_strrchr (val, -1, '"');
+ if (!end_dq) {
+ GST_ERROR ("end_dq is NULL");
+ break;
+ }
+
+ *end_dq = '\0';
+ GST_DEBUG ("Key URI = %s\n", val);
+
+ if (!gst_uri_is_valid (val)) {
+ gchar *slash;
+ if (!self->uri) {
+ GST_WARNING ("uri not set, can't build a valid uri");
+ goto next_line;
+ }
+ slash = g_utf8_strrchr (self->uri, -1, '/');
+ if (!slash) {
+ GST_WARNING ("Can't build a valid uri");
+ goto next_line;
+ }
+ *slash = '\0';
+ key_url = g_strdup_printf ("%s/%s", self->uri, val);
+ *slash = '/';
+ } else {
+ key_url= g_strdup (val);
+ }
+ } else if (g_str_equal (attr, "IV")) {
+ gint iv_len = 0;
+ gchar tmp_byte[3];
+ gint tmp_val = 0;
+ gint idx = 0;
+ gchar *car = NULL;
+
+ if (IV) {
+ g_free (IV);
+ IV = NULL;
+ }
+
+ IV = g_malloc0 (16);
+ if (NULL == IV) {
+ GST_ERROR ("Failed to allocate memory...\n");
+ goto error;
+ }
+
+ /* eliminating 0x/0X prefix */
+ val = val + 2;
+ car = g_utf8_strchr (val, -1, '\r');
+ if (car)
+ *car = '\0';
+
+ iv_len = g_utf8_strlen(val, -1);
+ if (iv_len < 1 || iv_len >32) {
+ GST_ERROR ("Wrong IV and iv_len = %d", iv_len);
+ goto error;
+ }
+
+ val+=iv_len;
+ idx = 15;
+ while (iv_len > 0) {
+ // TODO: val need to incremented I feel.. check again
+ val-=(iv_len==1)?1:2;
+ g_utf8_strncpy(tmp_byte, val, (iv_len==1)?1:2);
+ tmp_byte[2] = '\0';
+ tmp_val = 0;
+ if (!int_from_string (tmp_byte, NULL, &tmp_val, 16))
+ GST_WARNING ("Error while reading PROGRAM-ID");
+ IV[idx] = tmp_val;
+ idx--;
+ iv_len = iv_len - 2;
+ }
+ }
+ }
+ } else {
+ GST_LOG ("Ignored line: %s", data);
+ }
+
+ next_line:
+ if (!end)
+ break;
+ data = g_utf8_next_char (end); /* skip \n */
+ }
+
+ /* redorder playlists by bitrate */
+ if (self->lists) {
+ gchar *top_variant_uri = NULL;
+
+ if (!self->current_variant)
+ top_variant_uri = GST_M3U8 (self->lists->data)->uri;
+ else
+ top_variant_uri = GST_M3U8 (self->current_variant->data)->uri;
+
+ self->lists =
+ g_list_sort (self->lists,
+ (GCompareFunc) gst_m3u8_compare_playlist_by_bitrate);
+
+ self->current_variant = g_list_find_custom (self->lists, top_variant_uri,
+ (GCompareFunc) _m3u8_compare_uri);
+ }
+
+ g_free (actual_data);
+ actual_data = NULL;
+ g_free (key_url);
+ key_url = NULL;
+ g_free (title);
+ title = NULL;
+ return TRUE;
+
+error:
+ g_free (actual_data);
+ actual_data = NULL;
+ g_free (key_url);
+ key_url = NULL;
+ g_free (title);
+ title = NULL;
+ return FALSE;
+}
+
+
+GstM3U8Client *
+gst_m3u8_client_new (const gchar * uri)
+{
+ GstM3U8Client *client;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ client = g_new0 (GstM3U8Client, 1);
+ client->main = gst_m3u8_new ();
+ client->current = NULL;
+ client->sequence = -1;
+ client->update_failed_count = 0;
+ client->lock = g_mutex_new ();
+ gst_m3u8_set_uri (client->main, g_strdup (uri));
+
+ return client;
+}
+
+void
+gst_m3u8_client_free (GstM3U8Client * self)
+{
+ g_return_if_fail (self != NULL);
+
+ gst_m3u8_free (self->main);
+ g_mutex_free (self->lock);
+ g_free (self);
+}
+
+void
+gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8)
+{
+ g_return_if_fail (self != NULL);
+
+ GST_M3U8_CLIENT_LOCK (self);
+ if (m3u8 != self->current) {
+ self->current = m3u8;
+ self->update_failed_count = 0;
+ }
+ GST_M3U8_CLIENT_UNLOCK (self);
+}
+
+static guint
+_get_start_sequence (GstM3U8Client * client)
+{
+ GList *l;
+ GstClockTime duration_limit, duration_count = 0;
+ guint sequence = -1;
+
+ if (client->current->endlist) {
+ l = g_list_first (client->current->files);
+ sequence = GST_M3U8_MEDIA_FILE (l->data)->sequence;
+ } else {
+ duration_limit = client->current->targetduration * 3;
+ for (l = g_list_last (client->current->files); l; l = l->prev) {
+ duration_count += GST_M3U8_MEDIA_FILE (l->data)->duration;
+ sequence = GST_M3U8_MEDIA_FILE (l->data)->sequence;
+ if (duration_count >= duration_limit) {
+ break;
+ }
+ }
+ }
+ return sequence;
+}
+
+gboolean
+gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
+{
+ GstM3U8 *m3u8;
+ gboolean updated = FALSE;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (self);
+ m3u8 = self->current ? self->current : self->main;
+
+ if (!gst_m3u8_update (m3u8, data, &updated))
+ goto out;
+
+ if (!updated) {
+ self->update_failed_count++;
+ goto out;
+ }
+
+ /* select the first playlist, for now */
+ if (!self->current) {
+ if (self->main->lists) {
+ self->current = self->main->current_variant->data;
+ } else {
+ self->current = self->main;
+ }
+ }
+
+ if (m3u8->files && self->sequence == -1) {
+#if 0
+ self->sequence = GST_M3U8_MEDIA_FILE (g_list_first (m3u8->files)->data)->sequence;
+#else
+ /* (spec : 6.3.3) In live case, the client SHOULD NOT choose a segment
+ which starts less than three target durations from the end of the Playlist file */
+ self->sequence = _get_start_sequence (self);
+#endif
+ GST_DEBUG ("Setting first sequence at %d", self->sequence);
+ }
+
+ ret = TRUE;
+out:
+ GST_M3U8_CLIENT_UNLOCK (self);
+ return ret;
+}
+
+static gboolean
+_find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
+{
+ GST_DEBUG ("Found fragment %d", file->sequence);
+ if (file->sequence >= client->sequence)
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+_find_key (GstM3U8Key * key, GstM3U8Client * client)
+{
+ if (key->sequence <= client->sequence)
+ return FALSE;
+ return TRUE;
+}
+
+void
+gst_m3u8_client_get_current_position (GstM3U8Client * client,
+ GstClockTime * timestamp)
+{
+ GList *l;
+ GList *walk;
+
+ l = g_list_find_custom (client->current->files, client,
+ (GCompareFunc) _find_next);
+
+ *timestamp = 0;
+ for (walk = client->current->files; walk; walk = walk->next) {
+ if (walk == l)
+ break;
+ *timestamp += GST_M3U8_MEDIA_FILE (walk->data)->duration;
+ }
+}
+
+gboolean
+gst_m3u8_client_get_next_fragment (GstM3U8Client * client, GstClockTime cur_running_time,
+ gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
+ GstClockTime * timestamp, gchar **key_uri, gchar **iv)
+{
+ GList *l;
+ GstM3U8MediaFile *file;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (client->current != NULL, FALSE);
+ g_return_val_if_fail (discontinuity != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+
+ GST_DEBUG ("Looking for fragment %d", client->sequence);
+
+ if (client->main && client->main->lists && client->current->endlist) {
+ GList *walk = NULL;
+ GstClockTime current_pos, target_pos;
+ gint current_sequence;
+ gboolean found_sequence = FALSE;
+
+ /* VOD playlist, so find mediasequence from time */
+ // TODO: need to handle when live converts to VOD by putting END_LIST tag
+
+ file = GST_M3U8_MEDIA_FILE (client->current->files->data);
+ current_sequence = file->sequence;
+ current_pos = 0 ;
+ target_pos = cur_running_time;
+
+ for (walk = client->current->files; walk; walk = walk->next) {
+ file = walk->data;
+
+ current_sequence = file->sequence;
+ if (current_pos <= target_pos &&
+ target_pos < current_pos + file->duration) {
+ found_sequence = TRUE;
+ break;
+ }
+ current_pos += file->duration;
+ }
+
+ if (found_sequence) {
+ GST_ERROR ("changing client sequence from %d -> %d", client->sequence, current_sequence);
+ client->sequence = current_sequence;
+ } else {
+ GST_WARNING ("max file_seq = %d and client_seq = %d", current_sequence, client->sequence);
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+ }
+
+ l = g_list_find_custom (client->current->files, client, (GCompareFunc) _find_next);
+ if (l == NULL) {
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return FALSE;
+ }
+
+ gst_m3u8_client_get_current_position (client, timestamp);
+
+ file = GST_M3U8_MEDIA_FILE (l->data);
+
+ if (file->key_url)
+ *key_uri = g_strdup (file->key_url);
+
+ if (file->iv) {
+ *iv = g_malloc0 (GST_M3U8_IV_LEN);
+ //*iv = file->iv;
+ memcpy (*iv, file->iv, GST_M3U8_IV_LEN);
+ }
+
+ *discontinuity = client->sequence != file->sequence;
+ client->sequence = file->sequence + 1;
+
+ if (file->uri)
+ *uri = g_strdup (file->uri);
+
+ *duration = file->duration;
+
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return TRUE;
+}
+
+void gst_m3u8_client_decrement_sequence (GstM3U8Client * client)
+{
+ GST_M3U8_CLIENT_LOCK (client);
+ client->sequence = client->sequence - 1;
+ GST_M3U8_CLIENT_UNLOCK (client);
+}
+
+static void
+_sum_duration (GstM3U8MediaFile * self, GstClockTime * duration)
+{
+ *duration += self->duration;
+}
+
+GstClockTime
+gst_m3u8_client_get_duration (GstM3U8Client * client)
+{
+ GstClockTime duration = 0;
+
+ g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ /* We can only get the duration for on-demand streams */
+ if (!client->current->endlist) {
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration);
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return duration;
+}
+
+GstClockTime
+gst_m3u8_client_get_target_duration (GstM3U8Client * client)
+{
+ GstClockTime duration = 0;
+
+ g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ duration = client->current->targetduration;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return duration;
+}
+
+/* used for LIVE case only, to update playlist file */
+GstClockTime
+gst_m3u8_client_get_last_fragment_duration (GstM3U8Client * client)
+{
+ GstClockTime duration = 0;
+ GstM3U8MediaFile *last_file = NULL;
+ GList *last_list = NULL;
+
+ g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+
+ if (!client->current) {
+ GST_ERROR ("current variant is NULL.. return CLOCK_NONE");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ if (!client->current->files) {
+ GST_WARNING ("Due to playlist updation problem.. did not update the playlist");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return 0; /* update playlist immediately */
+ }
+
+ last_list = g_list_last (client->current->files);
+
+ if (!last_list) {
+ GST_WARNING ("list does not have last object... return target_duration");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return client->current->targetduration;
+ }
+
+ last_file = (GstM3U8MediaFile *)(last_list->data);
+ if(last_file)
+ duration = last_file->duration;
+ else
+ duration=client->current->targetduration;
+
+ GST_DEBUG ("Last file duration = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return duration;
+}
+
+const gchar *
+gst_m3u8_client_get_uri (GstM3U8Client * client)
+{
+ const gchar *uri;
+
+ g_return_val_if_fail (client != NULL, NULL);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ uri = client->main->uri;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return uri;
+}
+
+const gchar *
+gst_m3u8_client_get_current_uri (GstM3U8Client * client)
+{
+ const gchar *uri;
+
+ g_return_val_if_fail (client != NULL, NULL);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ uri = client->current->uri;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return uri;
+}
+
+gint
+gst_m3u8_client_get_current_bandwidth (GstM3U8Client * client)
+{
+ gint bandwidth;
+
+ g_return_val_if_fail (client != NULL, -1);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ bandwidth = client->current->bandwidth;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return bandwidth;
+}
+
+gboolean
+gst_m3u8_client_has_variant_playlist (GstM3U8Client * client)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ ret = (client->main->lists != NULL);
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return ret;
+}
+
+gboolean
+gst_m3u8_client_is_live (GstM3U8Client * client)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ if (!client->current || client->current->endlist)
+ ret = FALSE;
+ else
+ ret = TRUE;
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return ret;
+}
+
+GList *
+gst_m3u8_client_get_next_higher_bw_playlist (GstM3U8Client * client)
+{
+ GList *current_variant;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ current_variant = g_list_next (client->main->current_variant);
+ if (!current_variant) {
+ GST_WARNING ("no next variant available...");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return NULL;
+ }
+
+ GST_DEBUG ("next variant = %p and uri = %s", current_variant, GST_M3U8(current_variant->data)->uri);
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+
+}
+
+GList *
+gst_m3u8_client_get_next_lower_bw_playlist (GstM3U8Client * client)
+{
+ GList *current_variant;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ GST_M3U8_CLIENT_LOCK (client);
+ current_variant = g_list_previous (client->main->current_variant);
+ if (!current_variant) {
+ GST_WARNING ("no previous variant available...");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return NULL;
+ }
+
+ GST_DEBUG ("previous variant = %p and uri = %s", current_variant, GST_M3U8(current_variant->data)->uri);
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+}
+
+
+#ifndef SWITCH_TRIGGER_POINT
+GList *
+gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
+{
+ GList *list, *current_variant;
+
+ GST_M3U8_CLIENT_LOCK (client);
+ current_variant = client->main->current_variant;
+
+ /* Go to the highest possible bandwidth allowed */
+ while (GST_M3U8 (current_variant->data)->bandwidth < bitrate) {
+ list = g_list_next (current_variant);
+ if (!list)
+ break;
+ current_variant = list;
+ }
+
+ while (GST_M3U8 (current_variant->data)->bandwidth > bitrate) {
+ list = g_list_previous (current_variant);
+ if (!list)
+ break;
+ current_variant = list;
+ }
+ GST_M3U8_CLIENT_UNLOCK (client);
+
+ return current_variant;
+}
+#else
+GList *
+gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
+{
+ GList *next_variant = NULL;
+ GList *current_variant = NULL;
+ guint current_bandwidth = 0;
+
+ GST_M3U8_CLIENT_LOCK (client);
+ current_variant = client->main->current_variant;
+ current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
+
+ if ((current_bandwidth + BITRATE_SWITCH_UPPER_THRESHOLD * current_bandwidth) < bitrate) {
+ GST_DEBUG ("Switch to upper band...\n");
+
+ if (!g_list_next (current_variant)) {
+ GST_DEBUG ("no next upper fragment...\n");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+ } else {
+ GST_DEBUG (">>>>> UP : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
+ next_variant = current_variant;
+
+ /* Go to the highest possible bandwidth allowed */
+ while ((current_bandwidth + BITRATE_SWITCH_UPPER_THRESHOLD * current_bandwidth) < bitrate) {
+ current_variant = next_variant;
+ next_variant = g_list_next (next_variant);
+ if (!next_variant) {
+ GST_DEBUG ("no next upper fragment...\n");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+ }
+
+ current_bandwidth = GST_M3U8 (next_variant->data)->bandwidth;
+ GST_DEBUG ("current_bw in while= %d & URI = %s\n", current_bandwidth, GST_M3U8 (next_variant->data)->uri);
+ }
+
+ /* as condition failed fallback to previous */
+ GST_M3U8_CLIENT_UNLOCK (client);
+
+ return current_variant;
+ }
+ } else if ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
+ GST_DEBUG ("Switch to lower band...\n");
+
+ if (!g_list_previous (current_variant)) {
+ GST_DEBUG ("no previous lower fragment...to go further down\n");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+ } else {
+
+ GST_DEBUG (">>>>> LOW : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
+ next_variant = current_variant;
+
+ /* Go to the lowest possible bandwidth allowed */
+ while ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
+
+ next_variant = g_list_previous (next_variant);
+ if (!next_variant) {
+ GST_DEBUG ("no previous lower fragment...\n");
+ GST_M3U8_CLIENT_UNLOCK (client);
+ return current_variant;
+ }
+
+ current_variant = next_variant;
+
+ current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
+ GST_DEBUG ("current_bw in while= %d\n", current_bandwidth);
+ }
+
+ GST_M3U8_CLIENT_UNLOCK (client);
+
+ /* as condition failed fallback to previous */
+ return current_variant;
+ }
+ } else {
+ GST_DEBUG ("No need to switch .... returning current_variant..\n");
+ }
+
+ GST_M3U8_CLIENT_UNLOCK (client);
+
+ return current_variant;
+}
+#endif
+
+gboolean
+gst_m3u8_client_decrypt_init (GstM3U8Client * client, unsigned char *key, gchar *iv)
+{
+ //if (!client->current->cipher_ctx)
+ // client->current->cipher_ctx = g_malloc0 (sizeof(EVP_CIPHER_CTX));
+
+ EVP_CIPHER_CTX_init (&(client->cipher_ctx));
+ EVP_CIPHER_CTX_set_padding(&(client->cipher_ctx), 0); // added to check avodining final buffer decryption error
+
+ return EVP_DecryptInit_ex (&(client->cipher_ctx), EVP_aes_128_cbc(), NULL, key, iv);
+}
+
+gboolean
+gst_m3u8_client_decrypt_update (GstM3U8Client * client, guint8 * out_data,
+ gint * out_size, guint8 * in_data, gint in_size)
+{
+ //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
+
+ return EVP_DecryptUpdate (&(client->cipher_ctx), out_data, out_size,
+ in_data, in_size);
+}
+
+gboolean
+gst_m3u8_client_decrypt_final (GstM3U8Client * client, guint8 * out_data,
+ gint * out_size)
+{
+ //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
+
+ return EVP_DecryptFinal_ex (&(client->cipher_ctx), out_data, out_size);
+}
+
+gboolean
+gst_m3u8_client_decrypt_deinit (GstM3U8Client * client)
+{
+ //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
+
+ return EVP_CIPHER_CTX_cleanup (&(client->cipher_ctx));
+}
+
+
+gboolean
+gst_m3u8_client_is_playlist_download_needed (GstM3U8Client * self)
+{
+ /* download is not need, if last_data is present & not live (i.e. we can reuse last VOD child playlist)*/
+ if (self->current->last_data && !gst_m3u8_client_is_live(self))
+ return FALSE;
+ else
+ return TRUE;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
+ * Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * m3u8.h:
+ *
+ * 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 __M3U8_H__
+#define __M3U8_H__
+
+#include <glib.h>
+#include <openssl/evp.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS typedef struct _GstM3U8 GstM3U8;
+typedef struct _GstM3U8MediaFile GstM3U8MediaFile;
+typedef struct _GstM3U8Key GstM3U8Key;
+typedef struct _GstM3U8Client GstM3U8Client;
+
+#define GST_M3U8(m) ((GstM3U8*)m)
+#define GST_M3U8_KEY(k) ((GstM3U8Key*)k)
+#define GST_M3U8_MEDIA_FILE(f) ((GstM3U8MediaFile*)f)
+
+#define GST_M3U8_CLIENT_LOCK(c) g_mutex_lock (c->lock);
+#define GST_M3U8_CLIENT_UNLOCK(c) g_mutex_unlock (c->lock);
+
+#define GST_M3U8_IV_LEN 16
+
+GST_DEBUG_CATEGORY_EXTERN (hlsdemux2_m3u8_debug);
+
+typedef enum {
+ GST_M3U8_ENCRYPTED_NONE,
+ GST_M3U8_ENCRYPTED_AES_128,
+} GstM3U8EncryptionMethod;
+
+struct _GstM3U8
+{
+ gchar *uri;
+
+ gboolean endlist; /* if ENDLIST has been reached */
+ gint version; /* last EXT-X-VERSION */
+ GstClockTime targetduration; /* last EXT-X-TARGETDURATION */
+ gchar *allowcache; /* last EXT-X-ALLOWCACHE */
+
+ gint bandwidth;
+ gint program_id;
+ gchar *codecs;
+ gint width;
+ gint height;
+ GList *files;
+
+ /*< private > */
+ gchar *last_data;
+ GList *lists; /* list of GstM3U8 from the main playlist */
+ GList *current_variant; /* Current variant playlist used */
+ GstM3U8 *parent; /* main playlist (if any) */
+ guint mediasequence; /* EXT-X-MEDIA-SEQUENCE & increased with new media file */
+};
+
+struct _GstM3U8MediaFile
+{
+ gchar *title;
+ GstClockTime duration;
+ gchar *uri;
+ guint sequence; /* the sequence nb of this file */
+ gchar *key_url;
+ unsigned char *key;
+ unsigned char *iv;
+};
+
+struct _GstM3U8Key
+{
+ GstM3U8EncryptionMethod method;
+ gchar *uri;
+ guint8 *iv;
+ guint sequence;
+ guint8 *data;
+};
+
+struct _GstM3U8Client
+{
+ GstM3U8 *main; /* main playlist */
+ GstM3U8 *current;
+ guint update_failed_count;
+ gint sequence; /* the next sequence for this client */
+ GMutex *lock;
+ EVP_CIPHER_CTX cipher_ctx;
+};
+
+
+GstM3U8Client *gst_m3u8_client_new (const gchar * uri);
+void gst_m3u8_client_free (GstM3U8Client * client);
+gboolean gst_m3u8_client_update (GstM3U8Client * client, gchar * data);
+void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8);
+gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, GstClockTime cur_running_time,
+ gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
+ GstClockTime * timestamp, gchar **key_uri, gchar **iv);
+void gst_m3u8_client_get_current_position (GstM3U8Client * client,
+ GstClockTime * timestamp);
+GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client);
+GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client);
+const gchar *gst_m3u8_client_get_uri(GstM3U8Client * client);
+const gchar *gst_m3u8_client_get_current_uri(GstM3U8Client * client);
+gboolean gst_m3u8_client_has_variant_playlist(GstM3U8Client * client);
+gboolean gst_m3u8_client_is_live(GstM3U8Client * client);
+GList * gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client,
+ guint bitrate);
+gboolean gst_m3u8_client_decrypt_init (GstM3U8Client * client, unsigned char *key, gchar *iv);
+gboolean gst_m3u8_client_decrypt_update (GstM3U8Client * client,
+ guint8 * out_data, gint * out_size, guint8 * in_data, gint in_size);
+gboolean gst_m3u8_client_is_playlist_download_needed (GstM3U8Client * self);
+GstClockTime gst_m3u8_client_get_last_fragment_duration (GstM3U8Client * client);
+gint gst_m3u8_client_get_current_bandwidth (GstM3U8Client * client);
+gboolean gst_m3u8_client_decrypt_final (GstM3U8Client * client, guint8 * out_data, gint * out_size);
+gboolean gst_m3u8_client_decrypt_deinit (GstM3U8Client * client);
+void gst_m3u8_client_decrement_sequence (GstM3U8Client * client);
+GList *gst_m3u8_client_get_next_higher_bw_playlist (GstM3U8Client * client);
+GList *gst_m3u8_client_get_next_lower_bw_playlist (GstM3U8Client * client);
+
+G_END_DECLS
+#endif /* __M3U8_H__ */
Name: gst-plugins-ext0.10
-Version: 0.2.4
-Summary: GStreamer extra plugins (common)
-Release: 1
-Group: TO_BE/FILLED_IN
-License: TO BE FILLED IN
+Version: 0.4.88
+Summary: GStreamer extra plugins (common)
+Release: 0
+Group: libs
+License: LGPL-2.1+
Source0: %{name}-%{version}.tar.gz
-BuildRequires: pkgconfig(avsysaudio)
BuildRequires: pkgconfig(camsrcjpegenc)
+#BuildRequires: pkgconfig(drm-client)
+#BuildRequires: pkgconfig(drm-trusted)
BuildRequires: pkgconfig(ecore)
BuildRequires: pkgconfig(ecore-x)
BuildRequires: pkgconfig(evas)
-BuildRequires: pkgconfig(mm-ta)
+BuildRequires: gst-plugins-base-devel
BuildRequires: pkgconfig(gstreamer-plugins-base-0.10)
-BuildRequires: pkgconfig(gstreamer-0.10)
+BuildRequires: pkgconfig(gstreamer-0.10)
BuildRequires: pkgconfig(libexif)
+BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(libdri2)
+BuildRequires: pkgconfig(x11)
+BuildRequires: pkgconfig(xext)
+BuildRequires: pkgconfig(xv)
+BuildRequires: pkgconfig(xdamage)
+BuildRequires: pkgconfig(libdrm)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(dri2proto)
+BuildRequires: pkgconfig(xfixes)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(libcrypto)
%description
GStreamer extra plugins (common)
%build
-export CFLAGS+=" -DGST_EXT_TIME_ANALYSIS -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" "
+export CFLAGS+=" -DCONTROL_PAGECACHE -DGST_EXT_TIME_ANALYSIS -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" "
./autogen.sh --disable-static
-%configure --disable-static
+%configure --disable-static \
+ --enable-ext-hlsdemux2\
+ --disable-ext-drmsrc\
+%if 0%{?tizen_build_binary_release_type_eng}
+ --enable-pcmdump
+%endif
make %{?jobs:-j%jobs}
%install
rm -rf %{buildroot}
+mkdir -p %{buildroot}/usr/share/license
+cp COPYING %{buildroot}/usr/share/license/%{name}
%make_install
-
+mkdir -p %{buildroot}/usr/etc/
+install -m 755 hlsdemux2/predefined_frame/* %{buildroot}/usr/etc/
%files
-%defattr(-,root,root,-)
+%manifest gst-plugins-ext0.10.manifest
+%defattr(-,root,root,-)
%{_libdir}/gstreamer-0.10/*.so
+/usr/share/license/%{name}
+/usr/etc/blackframe_QVGA.264
+/usr/etc/sec_audio_fixed_qvga.264
+/usr/etc/sec_audio_fixed_qvga.jpg
"Location of the file to read", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
-
+
g_object_class_install_property (gobject_class, ARG_EOS,
g_param_spec_boolean ("eos",
"EOS recived on downloading pipeline",
"download of clip is over",
0,
G_PARAM_READWRITE));
-
+
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_pd_pushsrc_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_pd_pushsrc_stop);
gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_pd_pushsrc_is_seekable);
}
GST_LOG ("OUT");
-
+
}
static void
src->uri = NULL;
src->is_regular = FALSE;
src->is_eos = FALSE;
-
+
gst_pad_set_checkgetrange_function (basesrc->srcpad, GST_DEBUG_FUNCPTR (gst_pd_pushsrc_checkgetrange));
GST_LOG ("OUT");
}
g_object_notify (G_OBJECT (src), "location");
gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
-
+
GST_LOG ("OUT");
return TRUE;
GST_LOG ("IN");
int ret;
- GstBuffer *buf;
+ GstBuffer *buf = NULL;
struct stat stat_results;
GstPDPushSrc *src;
+ if(length < 1)
+ return GST_FLOW_ERROR;
+
src = GST_PD_PUSHSRC_CAST (basesrc);
-
- GST_LOG_OBJECT (src, "read position = %"G_GUINT64_FORMAT ", offset = %"G_GUINT64_FORMAT", length = %d",
- src->read_position, offset, length);
-
+
+ GST_LOG_OBJECT (src, "read position = %"G_GUINT64_FORMAT ", offset = %"G_GUINT64_FORMAT", length = %d",
+ src->read_position, offset, length);
+
memset (&stat_results, 0, sizeof (stat_results));
-
+
if (fstat (src->fd, &stat_results) < 0)
goto could_not_stat;
GST_DEBUG_OBJECT (src, "Going to wait for %ld msec", timeout.tv_usec);
ret = select (src->fd + 1, &fds, NULL, NULL, &timeout);
- if (-1 == ret)
+ if (-1 == ret)
{
- GST_ERROR_OBJECT (src, "ERROR in select () : reason - %s...\n", strerror(errno));
+ GST_ERROR_OBJECT (src, "ERROR in select () : reason - %s...\n", g_strerror(errno));
return GST_FLOW_ERROR;
}
else if (0 == ret)
else
{
memset (&stat_results, 0, sizeof (stat_results));
-
+
if (fstat (src->fd, &stat_results) < 0)
goto could_not_stat;
goto seek_failed;
src->read_position = offset;
}
-
+
buf = gst_buffer_try_new_and_alloc (length);
- if (G_UNLIKELY (buf == NULL && length > 0)) {
+ if (G_UNLIKELY (buf == NULL)) {
GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
return GST_FLOW_ERROR;
}
- /* No need to read anything if length is 0 */
- if (length > 0) {
- GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
- length, offset);
- ret = read (src->fd, GST_BUFFER_DATA (buf), length);
- if (G_UNLIKELY (ret < 0))
- goto could_not_read;
+ GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
+ length, offset);
+ ret = read (src->fd, GST_BUFFER_DATA (buf), length);
+ if (G_UNLIKELY (ret < 0))
+ goto could_not_read;
- /* seekable regular files should have given us what we expected */
- if (G_UNLIKELY ((guint) ret < length && src->seekable))
- goto unexpected_eos;
+ /* seekable regular files should have given us what we expected */
+ if (G_UNLIKELY ((guint) ret < length && src->seekable))
+ goto unexpected_eos;
- /* other files should eos if they read 0 and more was requested */
- if (G_UNLIKELY (ret == 0 && length > 0))
- goto eos;
+ /* other files should eos if they read 0 and more was requested */
+ if (G_UNLIKELY (ret == 0 && length > 0))
+ goto eos;
- length = ret;
- GST_BUFFER_SIZE (buf) = length;
- GST_BUFFER_OFFSET (buf) = offset;
- GST_BUFFER_OFFSET_END (buf) = offset + length;
+ length = ret;
+ GST_BUFFER_SIZE (buf) = length;
+ GST_BUFFER_OFFSET (buf) = offset;
+ GST_BUFFER_OFFSET_END (buf) = offset + length;
- src->read_position += length;
- }
+ src->read_position += length;
*buffer = buf;
GST_LOG ("OUT");
unexpected_eos:
{
GST_ERROR_OBJECT (src, "Unexpected EOS occured...");
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
- ("unexpected end of file."));
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("unexpected end of file."));
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
eos:
{
GST_ERROR_OBJECT (src, "non-regular file hits EOS");
- gst_buffer_unref (buf);
+ if (buf)
+ gst_buffer_unref (buf);
return GST_FLOW_UNEXPECTED;
}
}
*size = G_MAXUINT64;
GST_DEBUG ("size of the file = %"G_GUINT64_FORMAT, *size);
-
+
GST_LOG ("OUT");
return TRUE;
}
no_stat:
{
- GST_ERROR_OBJECT (src, "Could not get stat info...");
+ GST_ERROR_OBJECT (src, "Could not get stat info...");
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
("Could not get info on \"\"."), (NULL));
close (src->fd);
}
was_socket:
{
- GST_ERROR_OBJECT (src, "Is a Socket");
+ GST_ERROR_OBJECT (src, "Is a Socket");
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
("File \"\" is a socket."), (NULL));
close (src->fd);
#ifdef G_OS_WIN32
/* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths
* correctly on windows, it leaves them with an extra backslash
- * at the start if they're of the mozilla-style file://///host/path/file
+ * at the start if they're of the mozilla-style file://///host/path/file
* form. Correct this.
*/
if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\')
--- /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) $(DRM_CLIENT_CFLAGS) $(DRM_TRUSTED_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) $(DRM_CLIENT_LIBS) $(DRM_TRUSTED_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 \
+ piffcommon.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, "LGPL", "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
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "piffdemux.h"
+#include <glib/gprintf.h>
+#include <gst/tag/tag.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <piffatomparser.h>
+#include <piffdemux_fourcc.h>
+#include <piffpalette.h>
+#include <piffdemux_types.h>
+#include <piffdemux_dump.h>
+
+#define PIFF_DEFAULT_TRACKID -1
+#define PIFF_DEFAULT_FOURCC 0
+#define PIFF_DEFAULT_TIMESCALE 10000000
+#define PIFF_DEFAULT_DURATION -1
+#define PIFF_DEFAULT_START_TS 0
+#define PIFF_DEFAULT_START_TS 0
+
+#define PIFF_DEFAULT_WIDTH 16
+#define PIFF_DEFAULT_HEIGHT 16
+#define PIFF_DEFAULT_BPS 16
+
+#undef DEC_OUT_FRAME_DUMP
+
+#ifdef DEC_OUT_FRAME_DUMP
+#include <stdio.h>
+FILE *piffdump = NULL;
+#endif
+
+#define PIFFDEMUX_RB16(x) ((((const unsigned char*)(x))[0] << 8) | ((const unsigned char*)(x))[1])
+/* max. size considered 'sane' for non-mdat atoms */
+#define PIFFDEMUX_MAX_ATOM_SIZE (25*1024*1024)
+
+/* if the sample index is larger than this, something is likely wrong */
+#define PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
+
+GST_DEBUG_CATEGORY (piffdemux_debug);
+
+typedef struct _PiffDemuxSegment PiffDemuxSegment;
+typedef struct _PiffDemuxSample PiffDemuxSample;
+typedef struct _PiffDemuxSubSampleEncryption PiffDemuxSubSampleEncryption;
+typedef struct _PiffDemuxSubSampleEntryInfo PiffDemuxSubSampleEntryInfo;
+
+enum
+{
+ PROR_PIFF_0,
+ PROP_PIFF_MEDIA_CAPS,
+ PROP_PIFF_MEDIA_TIMESCALE,
+ PROP_PIFF_MEDIA_DURATION,
+ PROP_PIFF_MEDIA_START_TIMESTAMP,
+ PROP_PIFF_IS_LIVE,
+ PROP_PIFF_LOOKAHEAD_COUNT,
+ PROP_PIFF_AVG_FRAME_DUR,
+#ifdef DRM_ENABLE
+ PROP_PROTECTION_HEADER_BUFFER,
+#endif
+};
+
+enum
+{
+ SIGNAL_LIVE_PARAM,
+ LAST_SIGNAL
+};
+
+static guint gst_piffdemux_signals[LAST_SIGNAL] = { 0 };
+
+struct _PiffDemuxSubSampleEntryInfo
+{
+ guint16 LenofClearData;
+ guint32 LenofEncryptData;
+};
+
+struct _PiffDemuxSubSampleEncryption
+{
+ guint16 n_entries;
+ PiffDemuxSubSampleEntryInfo *sub_entry;
+};
+
+struct _PiffDemuxSample
+{
+ guint32 size;
+ gint32 pts_offset; /* Add this value to timestamp to get the pts */
+ guint64 offset;
+ guint64 timestamp; /* DTS In mov time */
+ guint32 duration; /* In mov time */
+ gboolean keyframe; /* TRUE when this packet is a keyframe */
+ guint8 *iv; /* initialization vector for decryption*/
+ PiffDemuxSubSampleEncryption *sub_encry;
+};
+
+/* timestamp is the DTS */
+#define PIFFSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\
+ GST_SECOND, (stream)->timescale)
+/* timestamp + offset is the PTS */
+#define PIFFSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \
+ (sample)->pts_offset, GST_SECOND, (stream)->timescale)
+/* timestamp + duration - dts is the duration */
+#define PIFFSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
+ (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
+/* timestamp + offset + duration - pts is the duration */
+#define PIFFSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
+ (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));
+
+#define PIFFSAMPLE_KEYFRAME(stream,sample) ((sample)->keyframe);
+
+typedef char uuid_t[16];
+
+static const uuid_t tfxd_uuid = { 0x6d, 0x1d, 0x9b, 0x05,
+ 0x42, 0xd5, 0x44, 0xe6,
+ 0x80, 0xe2, 0x14, 0x1d,
+ 0xaf, 0xf7, 0x57, 0xb2 };
+
+static const uuid_t tfrf_uuid = { 0xd4, 0x80, 0x7e, 0xf2,
+ 0xca, 0x39, 0x46, 0x95,
+ 0x8e, 0x54, 0x26, 0xcb,
+ 0x9e, 0x46, 0xa7, 0x9f };
+
+static const uuid_t encrypt_uuid = { 0xa2, 0x39, 0x4f, 0x52,
+ 0x5a, 0x9b, 0x4f, 0x14,
+ 0xa2, 0x44, 0x6c, 0x42,
+ 0x7c, 0x64, 0x8d, 0xf4 };
+
+#define SE_OVERRIDE_TE_FLAGS 0x000001
+#define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002
+
+typedef enum
+{
+ UUID_UNKNOWN = -1,
+ UUID_TFXD,
+ UUID_TFRF,
+ UUID_SAMPLE_ENCRYPT,
+}uuid_type_t;
+
+struct _PiffDemuxSegment
+{
+ /* global time and duration, all gst time */
+ guint64 time;
+ guint64 stop_time;
+ guint64 duration;
+ /* media time of trak, all gst time */
+ guint64 media_start;
+ guint64 media_stop;
+ gdouble rate;
+};
+
+
+struct _PiffDemuxStream
+{
+ /* stream type */
+ guint32 subtype;
+ GstCaps *caps;
+ guint32 fourcc;
+
+ /* duration/scale */
+ guint64 duration; /* in timescale */
+ guint32 timescale;
+
+ /* our samples */
+ guint32 n_samples;
+ PiffDemuxSample *samples;
+ guint32 min_duration; /* duration in timescale of first sample, used for figuring out
+ the framerate, in timescale units */
+
+ /* if we use chunks or samples */
+ gboolean sampled;
+ guint padding;
+
+ /* when a discontinuity is pending */
+ gboolean discont;
+
+ /* list of buffers to push first */
+ GSList *buffers;
+
+ /* buffer needs some custom processing, e.g. subtitles */
+ gboolean need_process;
+
+ /* current position */
+ guint32 segment_index;
+ guint32 sample_index;
+ guint64 time_position; /* in gst time */
+
+ /* the Gst segment we are processing out, used for clipping */
+ GstSegment segment;
+
+ /* last GstFlowReturn */
+ GstFlowReturn last_ret;
+
+
+ /* quicktime segments */
+ guint32 n_segments;
+ PiffDemuxSegment *segments;
+ guint32 from_sample;
+ guint32 to_sample;
+
+ gboolean sent_eos;
+ GstTagList *pending_tags;
+ gboolean send_global_tags;
+
+ GstEvent *pending_event;
+
+ gboolean sent_nsevent;
+
+ guint64 start_ts;
+
+ guint64 avg_dur; /* average frame duration */
+};
+
+
+enum PiffDemuxState
+{
+ PIFFDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
+ PIFFDEMUX_STATE_HEADER, /* Parsing the header */
+ PIFFDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
+ PIFFDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
+};
+
+
+static GNode *piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
+static GNode *piffdemux_tree_get_child_by_type_full (GNode * node,
+ guint32 fourcc, GstByteReader * parser);
+static GNode *piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
+static GNode *piffdemux_tree_get_sibling_by_type_full (GNode * node,
+ guint32 fourcc, GstByteReader * parser);
+
+static GstStaticPadTemplate gst_piffdemux_sink_template =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-piff")
+ );
+
+static GstStaticPadTemplate gst_piffdemux_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+
+GST_BOILERPLATE (GstPiffDemux, gst_piffdemux, GstPiffDemux, GST_TYPE_ELEMENT);
+
+static void gst_piffdemux_dispose (GObject * object);
+
+static GstStateChangeReturn gst_piffdemux_change_state (GstElement * element,
+ GstStateChange transition);
+static void
+gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void
+gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static GstFlowReturn gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
+static gboolean gst_piffdemux_handle_sink_event (GstPad * pad, GstEvent * event);
+static gboolean piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer, guint length);
+static gboolean piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream);
+static gboolean piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd);
+static gboolean gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event);
+static const GstQueryType *gst_piffdemux_get_src_query_types (GstPad * pad);
+static gboolean gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query);
+
+#ifdef DRM_ENABLE
+static void piffdemux_get_playready_licence (GstPiffDemux *demux);
+void test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data);
+#endif
+
+static gboolean
+ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size);
+void
+__gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data);
+
+static void
+gst_piffdemux_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_piffdemux_sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_piffdemux_src_template));
+ gst_element_class_set_details_simple (element_class, "PIFF demuxer",
+ "Codec/Parser",
+ "Parser for PIFF file format",
+ "naveen ch <naveen.ch@samsung.com>");
+
+ GST_DEBUG_CATEGORY_INIT (piffdemux_debug, "piffdemux", 0, "piffdemux plugin");
+}
+
+static void
+gst_piffdemux_class_init (GstPiffDemuxClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = gst_piffdemux_dispose;
+ gobject_class->set_property = gst_piffdemux_set_property;
+ gobject_class->get_property = gst_piffdemux_get_property;
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_piffdemux_change_state);
+
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_CAPS,
+ g_param_spec_boxed ("caps", "Caps",
+ "The allowed caps for the src pad", GST_TYPE_CAPS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /* timescale of media to be set by application */
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_TIMESCALE,
+ g_param_spec_uint64 ("timescale", "media timescale",
+ "media timescale in PIFF Manifest", 0, G_MAXUINT64,
+ PIFF_DEFAULT_TIMESCALE,
+ G_PARAM_READWRITE));
+#ifdef DRM_ENABLE
+ g_object_class_install_property (gobject_class, PROP_PROTECTION_HEADER_BUFFER,
+ gst_param_spec_mini_object ("protection-header", "protection header buffer",
+ "protection header used for playready", GST_TYPE_BUFFER,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_DURATION,
+ g_param_spec_int64 ("duration", "Duration of media",
+ "Total duration of the content", -1, G_MAXINT64,
+ PIFF_DEFAULT_DURATION,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PIFF_MEDIA_START_TIMESTAMP,
+ g_param_spec_uint64 ("start-ts", "expected start timestamp",
+ "expected start timestamp to avoid reset", 0, G_MAXUINT64,
+ PIFF_DEFAULT_START_TS,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PIFF_IS_LIVE,
+ g_param_spec_boolean ("is-live", "Is presentation is Live or VOD",
+ "If Presentation is Live (true) else VOD (false)",
+ FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PIFF_LOOKAHEAD_COUNT,
+ g_param_spec_uint ("lookahead-count", "Lookahead count value",
+ "Look ahead count used in case of Live presentation", 0, G_MAXUINT,
+ 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PIFF_AVG_FRAME_DUR,
+ g_param_spec_uint64 ("frame-dur", "Average frame duration",
+ "Average frame duration", 0, G_MAXUINT64,
+ G_MAXUINT64,
+ G_PARAM_READABLE));
+
+ gst_piffdemux_signals[SIGNAL_LIVE_PARAM] = g_signal_new ("live-param",
+ G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstPiffDemuxClass, live_param), NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+
+static void
+gst_piffdemux_init (GstPiffDemux * piffdemux, GstPiffDemuxClass * klass)
+{
+ /* sink pad */
+ piffdemux->sinkpad = gst_pad_new_from_static_template (&gst_piffdemux_sink_template, "sink");
+ gst_pad_set_chain_function (piffdemux->sinkpad, gst_piffdemux_chain);
+ gst_pad_set_event_function (piffdemux->sinkpad, gst_piffdemux_handle_sink_event);
+ gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->sinkpad);
+
+ /* source pad */
+ piffdemux->srcpad = gst_pad_new_from_static_template (&gst_piffdemux_src_template, "src");
+ gst_pad_set_event_function (piffdemux->srcpad, gst_piffdemux_handle_src_event);
+ gst_pad_use_fixed_caps (piffdemux->srcpad);
+ gst_pad_set_query_type_function (piffdemux->srcpad, gst_piffdemux_get_src_query_types);
+ gst_pad_set_query_function (piffdemux->srcpad, gst_piffdemux_handle_src_query);
+ gst_element_add_pad (GST_ELEMENT_CAST (piffdemux), piffdemux->srcpad);
+
+ piffdemux->stream = g_new0 (PiffDemuxStream, 1);
+ piffdemux->stream->fourcc = PIFF_DEFAULT_FOURCC;
+ piffdemux->stream->timescale = PIFF_DEFAULT_TIMESCALE;
+ piffdemux->stream->duration = PIFF_DEFAULT_DURATION;
+ piffdemux->stream->caps = NULL;
+ piffdemux->stream->discont = TRUE;
+ piffdemux->stream->need_process = FALSE;
+ piffdemux->stream->segment_index = -1;
+ piffdemux->stream->time_position = 0;
+ piffdemux->stream->sample_index = -1;
+ piffdemux->stream->last_ret = GST_FLOW_OK;
+ piffdemux->stream->sent_nsevent = FALSE;
+ piffdemux->stream->start_ts = PIFF_DEFAULT_START_TS;
+ piffdemux->stream->avg_dur = -1;
+
+ piffdemux->state = PIFFDEMUX_STATE_INITIAL;
+ piffdemux->neededbytes = 16;
+ piffdemux->todrop = 0;
+ piffdemux->adapter = gst_adapter_new ();
+ piffdemux->offset = 0;
+ piffdemux->first_mdat = -1;
+ piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;
+ piffdemux->mdatbuffer = NULL;
+ piffdemux->moof_rcvd = FALSE;
+ piffdemux->is_live = FALSE;
+ piffdemux->lookahead_cnt = 0;
+#ifdef DRM_ENABLE
+ piffdemux->pr_handle = NULL;
+#endif
+ piffdemux->decrypt_init = FALSE;
+ piffdemux->encrypt_content = FALSE;
+
+#ifdef DEC_OUT_FRAME_DUMP
+ piffdump = fopen ("/opt/media/piff_out_dump.dmp", "w+");
+ if (piffdump == NULL)
+ {
+ g_print ("\nNot able to create frame dump file\n");
+ }
+#endif
+
+ gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);
+}
+
+static void
+gst_piffdemux_dispose (GObject * object)
+{
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
+
+ if (piffdemux->adapter) {
+ g_object_unref (G_OBJECT (piffdemux->adapter));
+ piffdemux->adapter = NULL;
+ }
+
+#ifdef DEC_OUT_FRAME_DUMP
+ {
+ fclose (piffdump);
+ piffdump = NULL;
+ }
+#endif
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+
+static void
+gst_piffdemux_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
+
+ switch (prop_id) {
+ case PROP_PIFF_MEDIA_CAPS: {
+ if (piffdemux->stream->caps)
+ gst_caps_unref(piffdemux->stream->caps);
+ piffdemux->stream->caps = gst_caps_copy (gst_value_get_caps (value));
+ gchar *caps_string = gst_caps_to_string(piffdemux->stream->caps);
+ GST_DEBUG_OBJECT (piffdemux, "stream caps = %s", caps_string);
+ g_free(caps_string);
+ caps_string = NULL;
+ if (!gst_pad_set_caps(piffdemux->srcpad, piffdemux->stream->caps)) {
+ GST_ERROR_OBJECT (piffdemux, "not able to set caps...");
+ }
+ break;
+ }
+ case PROP_PIFF_MEDIA_TIMESCALE:
+ piffdemux->stream->timescale = g_value_get_uint64(value);
+ break;
+ case PROP_PIFF_MEDIA_DURATION:
+ piffdemux->stream->duration = g_value_get_int64(value);
+ break;
+ case PROP_PIFF_MEDIA_START_TIMESTAMP:
+ piffdemux->stream->start_ts = g_value_get_uint64(value);
+ GST_INFO_OBJECT (piffdemux, "start_ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(piffdemux->stream->start_ts));
+ break;
+ case PROP_PIFF_IS_LIVE:
+ piffdemux->is_live = g_value_get_boolean(value);
+ break;
+ case PROP_PIFF_LOOKAHEAD_COUNT:
+ piffdemux->lookahead_cnt = g_value_get_uint(value);
+ GST_DEBUG_OBJECT (piffdemux, "Look ahead count = %d", piffdemux->lookahead_cnt);
+ break;
+#ifdef DRM_ENABLE
+ case PROP_PROTECTION_HEADER_BUFFER:
+ piffdemux->protection_header = gst_value_get_buffer(value);
+ piffdemux_get_playready_licence (piffdemux);
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_piffdemux_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (object);
+
+ switch (prop_id) {
+ case PROP_PIFF_MEDIA_CAPS:
+ gst_value_set_caps (value, piffdemux->stream->caps);
+ break;
+ case PROP_PIFF_MEDIA_TIMESCALE:
+ g_value_set_uint64 (value, piffdemux->stream->timescale);
+ break;
+ case PROP_PIFF_MEDIA_DURATION:
+ g_value_set_int64 (value, piffdemux->stream->duration);
+ break;
+ case PROP_PIFF_MEDIA_START_TIMESTAMP:
+ g_value_set_uint64 (value, piffdemux->stream->start_ts);
+ break;
+ case PROP_PIFF_IS_LIVE:
+ g_value_set_boolean(value, piffdemux->is_live);
+ break;
+ case PROP_PIFF_LOOKAHEAD_COUNT:
+ g_value_set_uint (value, piffdemux->lookahead_cnt);
+ break;
+ case PROP_PIFF_AVG_FRAME_DUR:
+ g_value_set_uint64 (value, piffdemux->stream->avg_dur);
+ break;
+#ifdef DRM_ENABLE
+ case PROP_PROTECTION_HEADER_BUFFER:
+ gst_value_take_buffer (value, piffdemux->protection_header);
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_piffdemux_post_no_playable_stream_error (GstPiffDemux * piffdemux)
+{
+ if (piffdemux->posted_redirect) {
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
+ ("This file contains no playable streams."),
+ ("no known streams found, a redirect message has been posted"));
+ } else {
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
+ ("This file contains no playable streams."),
+ ("no known streams found"));
+ }
+
+}
+
+static gboolean
+gst_piffdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
+ GstFormat dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ PiffDemuxStream *stream = gst_pad_get_element_private (pad);
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
+
+ if (stream->subtype != FOURCC_vide) {
+ res = FALSE;
+ goto done;
+ }
+
+ switch (src_format) {
+ case GST_FORMAT_TIME:
+ switch (dest_format) {
+ case GST_FORMAT_BYTES:{
+
+ break;
+ }
+ default:
+ res = FALSE;
+ break;
+ }
+ break;
+ case GST_FORMAT_BYTES:
+ switch (dest_format) {
+ case GST_FORMAT_TIME:{
+
+ break;
+ }
+ default:
+ res = FALSE;
+ break;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+
+done:
+ gst_object_unref (piffdemux);
+
+ return res;
+}
+
+static const GstQueryType *
+gst_piffdemux_get_src_query_types (GstPad * pad)
+{
+ static const GstQueryType src_types[] = {
+ GST_QUERY_POSITION,
+ GST_QUERY_DURATION,
+ GST_QUERY_CONVERT,
+ GST_QUERY_FORMATS,
+ GST_QUERY_SEEKING,
+ 0
+ };
+
+ return src_types;
+}
+
+static gboolean
+gst_piffdemux_get_duration (GstPiffDemux * piffdemux, gint64 * duration)
+{
+ gboolean res = TRUE;
+
+ *duration = GST_CLOCK_TIME_NONE;
+
+ if (piffdemux->stream->duration != 0) {
+ if (piffdemux->stream->duration != G_MAXINT64 && piffdemux->stream->timescale != 0) {
+ *duration = gst_util_uint64_scale (piffdemux->stream->duration,
+ GST_SECOND, piffdemux->stream->timescale);
+ }
+ }
+ return res;
+}
+
+static gboolean
+gst_piffdemux_handle_src_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = FALSE;
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
+
+ GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ GST_ERROR ("Querying POSITION from piffdemux....");
+ if (GST_CLOCK_TIME_IS_VALID (piffdemux->segment.last_stop)) {
+ gst_query_set_position (query, GST_FORMAT_TIME,
+ piffdemux->segment.last_stop);
+ res = TRUE;
+ }
+ break;
+ case GST_QUERY_DURATION:{
+ GstFormat fmt;
+ GST_ERROR ("Querying DURATION from piffdemux....");
+
+ gst_query_parse_duration (query, &fmt, NULL);
+ if (fmt == GST_FORMAT_TIME) {
+ gint64 duration = -1;
+
+ gst_piffdemux_get_duration (piffdemux, &duration);
+ if (duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ }
+ }
+ break;
+ }
+ case GST_QUERY_CONVERT:{
+ GstFormat src_fmt, dest_fmt;
+ gint64 src_value, dest_value = 0;
+
+ gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);
+
+ res = gst_piffdemux_src_convert (pad,
+ src_fmt, src_value, dest_fmt, &dest_value);
+ if (res) {
+ gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
+ res = TRUE;
+ }
+ break;
+ }
+ case GST_QUERY_FORMATS:
+ gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
+ res = TRUE;
+ break;
+ case GST_QUERY_SEEKING:{
+
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+
+ gst_object_unref (piffdemux);
+
+ return res;
+}
+
+
+static void
+gst_piffdemux_push_tags (GstPiffDemux * piffdemux, PiffDemuxStream * stream)
+{
+ if (G_UNLIKELY (stream->pending_tags)) {
+ GST_DEBUG_OBJECT (piffdemux, "Sending tags %" GST_PTR_FORMAT,
+ stream->pending_tags);
+ gst_pad_push_event (piffdemux->srcpad,
+ gst_event_new_tag (stream->pending_tags));
+ stream->pending_tags = NULL;
+ }
+
+ if (G_UNLIKELY (stream->send_global_tags && piffdemux->tag_list)) {
+ GST_DEBUG_OBJECT (piffdemux, "Sending global tags %" GST_PTR_FORMAT,
+ piffdemux->tag_list);
+ gst_pad_push_event (piffdemux->srcpad,
+ gst_event_new_tag (gst_tag_list_copy (piffdemux->tag_list)));
+ stream->send_global_tags = FALSE;
+ }
+}
+
+
+static void
+gst_piffdemux_push_event (GstPiffDemux * piffdemux, GstEvent * event)
+{
+ GstEventType etype = GST_EVENT_TYPE (event);
+
+ GST_DEBUG_OBJECT (piffdemux, "pushing %s event on source pad",
+ GST_EVENT_TYPE_NAME (event));
+
+ if (piffdemux->stream->sent_eos) {
+ GST_INFO_OBJECT (piffdemux, "already sent eos");
+ return;
+ }
+
+ if (!gst_pad_push_event (piffdemux->srcpad, event)) {
+ GST_ERROR_OBJECT (piffdemux, "error in sending event to srcpad...");
+ }
+
+ if (etype == GST_EVENT_EOS)
+ piffdemux->stream->sent_eos = TRUE;
+}
+
+
+/* find the segment for @time_position for @stream
+ *
+ * Returns -1 if the segment cannot be found.
+ */
+static guint32
+gst_piffdemux_find_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
+ guint64 time_position)
+{
+ gint i;
+ guint32 seg_idx;
+
+ GST_LOG_OBJECT (piffdemux, "finding segment for %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time_position));
+
+ /* find segment corresponding to time_position if we are looking
+ * for a segment. */
+ seg_idx = -1;
+ for (i = 0; i < stream->n_segments; i++) {
+ PiffDemuxSegment *segment = &stream->segments[i];
+
+ GST_LOG_OBJECT (piffdemux,
+ "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));
+
+ /* For the last segment we include stop_time in the last segment */
+ if (i < stream->n_segments - 1) {
+ if (segment->time <= time_position && time_position < segment->stop_time) {
+ GST_LOG_OBJECT (piffdemux, "segment %d matches", i);
+ seg_idx = i;
+ break;
+ }
+ } else {
+ if (segment->time <= time_position && time_position <= segment->stop_time) {
+ GST_LOG_OBJECT (piffdemux, "segment %d matches", i);
+ seg_idx = i;
+ break;
+ }
+ }
+ }
+ return seg_idx;
+}
+
+
+static gboolean
+gst_piffdemux_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_QOS:
+ case GST_EVENT_NAVIGATION:
+ res = FALSE;
+ gst_event_unref (event);
+ break;
+ case GST_EVENT_SEEK:
+ default:
+ res = gst_pad_event_default (pad, event);
+ break;
+ }
+
+ gst_object_unref (piffdemux);
+
+ return res;
+}
+
+
+static void
+gst_piffdemux_move_stream (GstPiffDemux * piffdemux, PiffDemuxStream * str,
+ guint32 index)
+{
+ /* no change needed */
+ if (index == str->sample_index)
+ return;
+
+ GST_DEBUG_OBJECT (piffdemux, "moving to sample %u of %u", index,
+ str->n_samples);
+
+ /* position changed, we have a discont */
+ str->sample_index = index;
+ /* Each time we move in the stream we store the position where we are
+ * starting from */
+ str->from_sample = index;
+ str->discont = TRUE;
+}
+
+// TODO: need to check more on this below function
+/* stream/index return sample that is min/max w.r.t. byte position,
+ * time is min/max w.r.t. time of samples,
+ * the latter need not be time of the former sample */
+static void
+gst_piffdemux_find_sample (GstPiffDemux * piffdemux, gint64 byte_pos, gboolean fw,
+ gboolean set, PiffDemuxStream ** _stream, gint * _index, gint64 * _time)
+{
+ gint i, index;
+ gint64 time, min_time;
+ PiffDemuxStream *stream;
+ PiffDemuxStream *str = piffdemux->stream;
+ gint inc;
+ gboolean set_sample;
+
+ min_time = -1;
+ stream = NULL;
+ index = -1;
+
+ set_sample = !set;
+ if (fw) {
+ i = 0;
+ inc = 1;
+ } else {
+ i = str->n_samples - 1;
+ inc = -1;
+ }
+
+ for (; (i >= 0) && (i < str->n_samples); i += inc) {
+ if (str->samples[i].size &&
+ ((fw && (str->samples[i].offset >= byte_pos)) ||
+ (!fw &&
+ (str->samples[i].offset + str->samples[i].size <=
+ byte_pos)))) {
+ /* move stream to first available sample */
+ if (set) {
+ gst_piffdemux_move_stream (piffdemux, str, i);
+ set_sample = TRUE;
+ }
+ /* determine min/max time */
+ time = str->samples[i].timestamp + str->samples[i].pts_offset;
+ time = gst_util_uint64_scale (time, GST_SECOND, str->timescale);
+ /*if (min_time == -1 || (!fw && time > min_time) ||
+ (fw && time < min_time)) : Dead code*/ {
+ min_time = time;
+ }
+ index = i;
+ break;
+ }
+ }
+ /* no sample for this stream, mark eos */
+ if (!set_sample)
+ gst_piffdemux_move_stream (piffdemux, str, str->n_samples);
+
+ if (_time)
+ *_time = min_time;
+ if (_stream)
+ *_stream = str;
+ if (_index)
+ *_index = index;
+}
+
+
+static gboolean
+gst_piffdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
+{
+ GstPiffDemux *demux = GST_PIFFDEMUX (GST_PAD_PARENT (sinkpad));
+ gboolean res;
+
+ GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NEWSEGMENT:
+ {
+ GstFormat format;
+ gdouble rate, arate;
+ gint64 start, stop, time, offset = 0;
+ PiffDemuxStream *stream;
+ gint idx;
+ gboolean update;
+ GstSegment segment;
+
+ /* some debug output */
+ gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+ &start, &stop, &time);
+ gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
+ start, stop, time);
+ GST_ERROR_OBJECT (demux,
+ "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
+ &segment);
+
+ /* chain will send initial newsegment after pads have been added */
+ if (demux->state != PIFFDEMUX_STATE_MOVIE ) {
+ GST_DEBUG_OBJECT (demux, "still starting, eating event");
+ goto exit;
+ }
+
+ /* we only expect a BYTE segment, e.g. following a seek */
+ if (format == GST_FORMAT_BYTES) {
+ if (start > 0) {
+ gint64 requested_seek_time;
+ guint64 seek_offset;
+
+ offset = start;
+
+ GST_OBJECT_LOCK (demux);
+ requested_seek_time = demux->requested_seek_time;
+ seek_offset = demux->seek_offset;
+ demux->requested_seek_time = -1;
+ demux->seek_offset = -1;
+ GST_OBJECT_UNLOCK (demux);
+
+ if (offset == seek_offset) {
+ start = requested_seek_time;
+ } else {
+ gst_piffdemux_find_sample (demux, start, TRUE, FALSE, NULL, NULL,
+ &start);
+ start = MAX (start, 0);
+ }
+ }
+ if (stop > 0) {
+ gst_piffdemux_find_sample (demux, stop, FALSE, FALSE, NULL, NULL,
+ &stop);
+ /* keyframe seeking should already arrange for start >= stop,
+ * but make sure in other rare cases */
+ stop = MAX (stop, start);
+ }
+ }
+#if 0
+ else if (format == GST_FORMAT_TIME) {
+ // Supporting TIME_FORMAT for new_segment
+ //gst_piffdemux_push_event (demux,event);
+ PiffDemuxStream *stream = NULL;
+ int i = -1;
+
+ demux->neededbytes = 16;
+ demux->state = PIFFDEMUX_STATE_INITIAL;
+ demux->offset = 0;
+
+ /* Figure out which stream this is packet belongs to */
+ for (i = 0; i < demux->n_streams; i++) {
+ stream = demux->streams[i];
+ stream->last_ts = start;
+ stream->discont = TRUE;
+ stream->sample_index = stream->n_samples;
+ }
+
+ /* accept upstream's notion of segment and distribute along */
+ gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
+ GST_FORMAT_TIME, start, stop, start);
+ GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "
+ "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
+ "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+
+ gst_piffdemux_push_event (demux,
+ gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, start, stop, start));
+
+ /* clear leftover in current segment, if any */
+ gst_adapter_clear (demux->adapter);
+
+ goto exit;
+ }
+#endif
+ else {
+ GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring");
+ goto exit;
+ }
+
+ /* accept upstream's notion of segment and distribute along */
+ gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
+ GST_FORMAT_TIME, start, stop, start);
+ GST_ERROR_OBJECT (demux, "Pushing newseg update %d, rate %g, "
+ "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
+ "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+
+ gst_piffdemux_push_event (demux,
+ gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
+ start, stop, start));
+
+ /* clear leftover in current segment, if any */
+ gst_adapter_clear (demux->adapter);
+ /* set up streaming thread */
+ gst_piffdemux_find_sample (demux, offset, TRUE, TRUE, &stream, &idx, NULL);
+ demux->offset = offset;
+ if (stream) {
+ demux->todrop = stream->samples[idx].offset - offset;
+ demux->neededbytes = demux->todrop + stream->samples[idx].size;
+ } else {
+ /* set up for EOS */
+ demux->neededbytes = -1;
+ demux->todrop = 0;
+ }
+ exit:
+ gst_event_unref (event);
+ res = TRUE;
+ goto drop;
+ break;
+ }
+ case GST_EVENT_FLUSH_STOP:
+ {
+ /* clean up, force EOS if no more info follows */
+ gst_adapter_clear (demux->adapter);
+ demux->offset = 0;
+ demux->neededbytes = -1;
+ /* reset flow return, e.g. following seek */
+ demux->stream->last_ret = GST_FLOW_OK;
+ demux->stream->sent_eos = FALSE;
+ break;
+ }
+ case GST_EVENT_EOS:
+ break;
+ default:
+ break;
+ }
+
+ res = gst_pad_event_default (demux->sinkpad, event);
+
+drop:
+ return res;
+}
+
+
+static void
+gst_piffdemux_stream_free (GstPiffDemux * piffdemux, PiffDemuxStream * stream)
+{
+ int i = 0;
+
+ g_return_if_fail (stream != NULL);
+
+ while (stream->buffers) {
+ gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
+ stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
+ }
+
+ for (i = 0; i < stream->n_samples; i++) {
+ if (stream->samples[i].iv) {
+ free (stream->samples[i].iv);
+ stream->samples[i].iv = NULL;
+ }
+ if (stream->samples[i].sub_encry) {
+ if (stream->samples[i].sub_encry->sub_entry) {
+ g_free (stream->samples[i].sub_encry->sub_entry);
+ stream->samples[i].sub_encry->sub_entry = NULL;
+ }
+
+ free (stream->samples[i].sub_encry);
+ stream->samples[i].sub_encry = NULL;
+ }
+ }
+
+ if (stream->samples) {
+ g_free (stream->samples);
+ stream->samples = NULL;
+ }
+ if (stream->caps) {
+ gst_caps_unref (stream->caps);
+ stream->caps = NULL;
+ }
+ if (stream->segments) {
+ g_free (stream->segments);
+ stream->segments = NULL;
+ }
+ if (stream->pending_tags) {
+ gst_tag_list_free (stream->pending_tags);
+ stream->pending_tags = NULL;
+ }
+ g_free (stream);
+}
+
+
+static GstStateChangeReturn
+gst_piffdemux_change_state (GstElement * element, GstStateChange transition)
+{
+ GstPiffDemux *piffdemux = GST_PIFFDEMUX (element);
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ break;
+ default:
+ break;
+ }
+
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:{
+ piffdemux->state = PIFFDEMUX_STATE_INITIAL;
+ piffdemux->neededbytes = 16;
+ piffdemux->todrop = 0;
+ piffdemux->posted_redirect = FALSE;
+ piffdemux->offset = 0;
+ piffdemux->first_mdat = -1;
+ piffdemux->mdatoffset = GST_CLOCK_TIME_NONE;
+ if (piffdemux->mdatbuffer)
+ gst_buffer_unref (piffdemux->mdatbuffer);
+ piffdemux->mdatbuffer = NULL;
+ if (piffdemux->tag_list)
+ gst_tag_list_free (piffdemux->tag_list);
+ piffdemux->tag_list = NULL;
+ gst_adapter_clear (piffdemux->adapter);
+ gst_piffdemux_stream_free (piffdemux, piffdemux->stream);
+ gst_segment_init (&piffdemux->segment, GST_FORMAT_TIME);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static void
+piffdemux_post_global_tags (GstPiffDemux * piffdemux)
+{
+ if (piffdemux->tag_list) {
+ /* all header tags ready and parsed, push them */
+ GST_INFO_OBJECT (piffdemux, "posting global tags: %" GST_PTR_FORMAT,
+ piffdemux->tag_list);
+ /* post now, send event on pads later */
+ gst_element_post_message (GST_ELEMENT (piffdemux),
+ gst_message_new_tag (GST_OBJECT (piffdemux),
+ gst_tag_list_copy (piffdemux->tag_list)));
+ }
+}
+
+
+/* caller verifies at least 8 bytes in buf */
+static void
+extract_initial_length_and_fourcc (const guint8 * data, guint size,
+ guint64 * plength, guint32 * pfourcc)
+{
+ guint64 length;
+ guint32 fourcc;
+
+ length = PIFF_UINT32 (data);
+ GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
+ fourcc = PIFF_FOURCC (data + 4);
+ GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+
+ if (length == 0) {
+ length = G_MAXUINT32;
+ } else if (length == 1 && size >= 16) {
+ /* this means we have an extended size, which is the 64 bit value of
+ * the next 8 bytes */
+ length = PIFF_UINT64 (data + 8);
+ GST_DEBUG ("length 0x%08" G_GINT64_MODIFIER "x", length);
+ }
+
+ if (plength)
+ *plength = length;
+ if (pfourcc)
+ *pfourcc = fourcc;
+}
+
+static gboolean
+piffdemux_update_sample_offset (GstPiffDemux * piffdemu, PiffDemuxStream * stream, gint64 uuid_offset)
+{
+ PiffDemuxSample *sample;
+ gint i;
+
+ sample = stream->samples ;
+ for (i = 0; i < stream->n_samples; i++)
+ {
+ sample->offset = sample->offset + uuid_offset;
+ sample++;
+ }
+ return TRUE;
+}
+
+static uuid_type_t
+piffdemux_get_uuid_type(GstPiffDemux * piffdemux, GstByteReader *uuid_data, gint64 *uuid_offset)
+{
+ uuid_type_t uuid_type = UUID_UNKNOWN;
+ guint32 box_len = 0;
+ guint64 box_long_len = 0;
+ gchar uuid[16] = {0,};
+ int i = 0;
+
+ if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len))
+ goto invalid_uuid;
+
+ /* Skipping fourcc */
+ if (!gst_byte_reader_skip (uuid_data, 4))
+ goto invalid_uuid;
+
+ if (box_len == 1)
+ {
+ GST_WARNING ("TfxdBoxLongLength field is present...");
+ if (!gst_byte_reader_get_uint64_be (uuid_data, &box_long_len))
+ goto invalid_uuid;
+ GST_DEBUG ("tfxd long length = %llu", box_long_len);
+
+ *uuid_offset = box_long_len;
+ }
+ else
+ {
+ GST_DEBUG ("Box Len = %d", box_len);
+ *uuid_offset = box_len;
+ }
+
+ //g_print ("\n\n\n 0x");
+ for (i = 0; i < sizeof (uuid); i++)
+ {
+ if (!gst_byte_reader_get_uint8 (uuid_data, &(uuid[i])))
+ goto invalid_uuid;
+ //g_print ("%02x", uuid[i]);
+ }
+ //g_print ("\n\n\n");
+
+ if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t)))
+ {
+ GST_INFO ("Found TFXD box");
+ return UUID_TFXD;
+ }
+ else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t)))
+ {
+ GST_INFO ("Found TFRF box");
+ return UUID_TFRF;
+ }
+ else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t)))
+ {
+ GST_INFO ("Found sample encryption box");
+ return UUID_SAMPLE_ENCRYPT;
+ }
+ else
+ {
+ GST_WARNING ("Not an valid UUID box..");
+ goto invalid_uuid;
+ }
+ return uuid_type;
+
+invalid_uuid:
+ GST_ERROR ("Error in parsing UUID atom...");
+ return UUID_UNKNOWN;
+}
+
+static gboolean
+piffdemux_parse_sample_encryption(GstPiffDemux * piffdemux, GstByteReader *sample_encrypt, PiffDemuxStream * stream)
+{
+ guint32 flags = 0;
+ guint32 sample_count = 0;
+ guint32 i = 0;
+ guint32 algo_id;
+ guint8 iv_size = 0;
+
+ if (!gst_byte_reader_skip (sample_encrypt, 1) ||
+ !gst_byte_reader_get_uint24_be (sample_encrypt, &flags))
+ goto invalid_encryption;
+
+ if (flags & SE_OVERRIDE_TE_FLAGS) {
+ /* get algorithm id */
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &algo_id))
+ goto invalid_encryption;
+
+ /* get IV size */
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &iv_size))
+ goto invalid_encryption;
+
+ // TODO: need to add reading of KID
+ } else {
+ GST_INFO_OBJECT (piffdemux, "Override flags are not present... taking default IV_Size = 8");
+ iv_size = 8;
+ }
+
+ /* Get sample count*/
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))
+ goto invalid_encryption;
+
+ GST_INFO_OBJECT (piffdemux, "Sample count = %d", sample_count);
+
+ if (sample_count != stream->n_samples) {
+ GST_ERROR_OBJECT (piffdemux, "Not all samples has IV vectors... Don't know how to handle. sample_cnt = %d and stream->n_samples = %d",
+ sample_count, stream->n_samples);
+ goto invalid_encryption;
+ }
+
+ for (i = 0; i < stream->n_samples; i++) {
+ guint8 iv_idx = iv_size;
+
+ /* resetting entire IV array */
+ stream->samples[i].iv = (guint8 *)malloc (iv_size);
+ if (NULL == stream->samples[i].iv) {
+ GST_ERROR ("Failed to allocate memory...\n");
+ goto invalid_encryption;
+ }
+
+ memset (stream->samples[i].iv, 0x00, iv_size);
+
+ iv_idx = 0;
+ while (iv_idx < iv_size) {
+ /* get IV byte */
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &(stream->samples[i].iv[iv_idx])))
+ goto invalid_encryption;
+
+ iv_idx++;
+ }
+
+#ifdef DEBUG_IV
+ {
+ guint8 tmp_idx = 0;
+ g_print ("sample[%d] : 0x ", i);
+
+ while (tmp_idx < iv_size ) {
+ g_print ("%02x ", stream->samples[i].iv[tmp_idx]);
+ tmp_idx++;
+ }
+ g_print ("\n");
+ }
+#endif
+
+ if (flags & SE_USE_SUBSAMPLE_ENCRYPTION) {
+ guint16 n_entries;
+ guint16 n_idx;
+
+ /* NumberofEntries in SubSampleEncryption */
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &n_entries))
+ goto invalid_encryption;
+
+ stream->samples[i].sub_encry = (PiffDemuxSubSampleEncryption *)malloc (sizeof (PiffDemuxSubSampleEncryption));
+ if (NULL == stream->samples[i].sub_encry) {
+ GST_ERROR ("Failed to allocate memory...\n");
+ goto invalid_encryption;
+ }
+
+ stream->samples[i].sub_encry->sub_entry = g_try_new0 (PiffDemuxSubSampleEntryInfo, n_entries);
+ if (NULL == stream->samples[i].sub_encry->sub_entry) {
+ GST_ERROR_OBJECT (piffdemux, "Failed to allocate memory...");
+ goto invalid_encryption;
+ }
+
+ stream->samples[i].sub_encry->n_entries = n_entries;
+
+ GST_DEBUG_OBJECT (piffdemux,"No. of subsample entries = %d", stream->samples[i].sub_encry->n_entries);
+
+ for (n_idx = 0; n_idx < n_entries; n_idx++) {
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData)))
+ goto invalid_encryption;
+
+ GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofClearData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData);
+
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData)))
+ goto invalid_encryption;
+
+ GST_DEBUG_OBJECT (piffdemux,"entry[%d] and lengthofEncryptData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData);
+ }
+ }
+ }
+
+ return TRUE;
+
+invalid_encryption:
+ {
+ GST_WARNING_OBJECT (piffdemux, "invalid sample encryption header");
+ return FALSE;
+ }
+}
+
+
+static gboolean
+piffdemux_parse_trun (GstPiffDemux * piffdemux, GstByteReader * trun,
+ PiffDemuxStream * stream, guint32 d_sample_duration, guint32 d_sample_size,
+ guint32 d_sample_flags, gint64 moof_offset, gint64 moof_length,
+ gint64 * base_offset, gint64 * running_offset)
+{
+ guint64 timestamp;
+ gint32 data_offset = 0;
+ guint32 flags = 0, first_flags = 0, samples_count = 0;
+ gint i;
+ guint8 *data;
+ guint entry_size, dur_offset, size_offset, flags_offset = 0, ct_offset = 0;
+ PiffDemuxSample *sample;
+ gboolean ismv = FALSE;
+ guint64 total_duration = 0;
+
+ GST_LOG_OBJECT (piffdemux, "parsing trun stream ; "
+ "default dur %d, size %d, flags 0x%x, base offset %" G_GINT64_FORMAT,
+ d_sample_duration, d_sample_size, d_sample_flags,
+ *base_offset);
+
+ //Resetting the samples
+ stream->n_samples = 0;
+
+ if (!gst_byte_reader_skip (trun, 1) ||
+ !gst_byte_reader_get_uint24_be (trun, &flags))
+ goto fail;
+
+ if (!gst_byte_reader_get_uint32_be (trun, &samples_count))
+ goto fail;
+
+ if (flags & TR_DATA_OFFSET) {
+ /* note this is really signed */
+ if (!gst_byte_reader_get_int32_be (trun, &data_offset))
+ goto fail;
+ GST_LOG_OBJECT (piffdemux, "trun data offset %d", data_offset);
+ /* default base offset = first byte of moof */
+ if (*base_offset == -1) {
+ GST_LOG_OBJECT (piffdemux, "base_offset at moof and moof_offset = %"G_GINT64_FORMAT, moof_offset);
+ *base_offset = moof_offset;
+ }
+ *running_offset = *base_offset + data_offset;
+ } else {
+ /* if no offset at all, that would mean data starts at moof start,
+ * which is a bit wrong and is ismv crappy way, so compensate
+ * assuming data is in mdat following moof */
+ if (*base_offset == -1) {
+ *base_offset = moof_offset + moof_length + 8;
+ GST_LOG_OBJECT (piffdemux, "base_offset assumed in mdat after moof");
+ ismv = TRUE;
+ }
+ if (*running_offset == -1)
+ *running_offset = *base_offset;
+ }
+
+ GST_LOG_OBJECT (piffdemux, "running offset now %" G_GINT64_FORMAT,
+ *running_offset);
+ GST_LOG_OBJECT (piffdemux, "trun offset %d, flags 0x%x, entries %d",
+ data_offset, flags, samples_count);
+
+ if (flags & TR_FIRST_SAMPLE_FLAGS) {
+ if (G_UNLIKELY (flags & TR_SAMPLE_FLAGS)) {
+ GST_DEBUG_OBJECT (piffdemux,
+ "invalid flags; SAMPLE and FIRST_SAMPLE present, discarding latter");
+ flags ^= TR_FIRST_SAMPLE_FLAGS;
+ } else {
+ if (!gst_byte_reader_get_uint32_be (trun, &first_flags))
+ goto fail;
+ GST_LOG_OBJECT (piffdemux, "first flags: 0x%x", first_flags);
+ }
+ }
+
+ /* FIXME ? spec says other bits should also be checked to determine
+ * entry size (and prefix size for that matter) */
+ entry_size = 0;
+ dur_offset = size_offset = 0;
+ if (flags & TR_SAMPLE_DURATION) {
+ GST_LOG_OBJECT (piffdemux, "entry duration present");
+ dur_offset = entry_size;
+ entry_size += 4;
+ }
+ if (flags & TR_SAMPLE_SIZE) {
+ GST_LOG_OBJECT (piffdemux, "entry size present");
+ size_offset = entry_size;
+ entry_size += 4;
+ }
+ if (flags & TR_SAMPLE_FLAGS) {
+ GST_LOG_OBJECT (piffdemux, "entry flags present");
+ flags_offset = entry_size;
+ entry_size += 4;
+ }
+ if (flags & TR_COMPOSITION_TIME_OFFSETS) {
+ GST_LOG_OBJECT (piffdemux, "entry ct offset present");
+ ct_offset = entry_size;
+ entry_size += 4;
+ }
+
+ if (!piff_atom_parser_has_chunks (trun, samples_count, entry_size))
+ goto fail;
+ data = (guint8 *) gst_byte_reader_peek_data_unchecked (trun);
+
+ if (stream->n_samples >=
+ PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE / sizeof (PiffDemuxSample))
+ goto index_too_big;
+
+ GST_DEBUG_OBJECT (piffdemux, "allocating n_samples %u * %u (%.2f MB)",
+ stream->n_samples, (guint) sizeof (PiffDemuxSample),
+ stream->n_samples * sizeof (PiffDemuxSample) / (1024.0 * 1024.0));
+
+ /* create a new array of samples if it's the first sample parsed */
+ if (stream->n_samples == 0)
+ stream->samples = g_try_new0 (PiffDemuxSample, samples_count);
+ /* or try to reallocate it with space enough to insert the new samples */
+ else
+ stream->samples = g_try_renew (PiffDemuxSample, stream->samples,
+ stream->n_samples + samples_count);
+ if (stream->samples == NULL)
+ goto out_of_memory;
+
+ if (G_UNLIKELY (stream->n_samples == 0)) {
+ /* the timestamp of the first sample is also provided by the tfra entry
+ * but we shouldn't rely on it as it is at the end of files */
+ timestamp = 0;
+ } else {
+ /* subsequent fragments extend stream */
+ timestamp =
+ stream->samples[stream->n_samples - 1].timestamp +
+ stream->samples[stream->n_samples - 1].duration;
+ }
+ sample = stream->samples + stream->n_samples;
+ for (i = 0; i < samples_count; i++) {
+ guint32 dur, size, sflags, ct;
+
+ /* first read sample data */
+ if (flags & TR_SAMPLE_DURATION) {
+ dur = PIFF_UINT32 (data + dur_offset);
+ } else {
+ dur = d_sample_duration;
+ }
+ if (flags & TR_SAMPLE_SIZE) {
+ size = PIFF_UINT32 (data + size_offset);
+ } else {
+ size = d_sample_size;
+ }
+
+ GST_DEBUG_OBJECT(piffdemux,"Size of sample %d is %d", i, size);
+
+ if (flags & TR_FIRST_SAMPLE_FLAGS) {
+ if (i == 0) {
+ sflags = first_flags;
+ } else {
+ sflags = d_sample_flags;
+ }
+ } else if (flags & TR_SAMPLE_FLAGS) {
+ sflags = PIFF_UINT32 (data + flags_offset);
+ } else {
+ sflags = d_sample_flags;
+ }
+ if (flags & TR_COMPOSITION_TIME_OFFSETS) {
+ ct = PIFF_UINT32 (data + ct_offset);
+ } else {
+ ct = 0;
+ }
+ data += entry_size;
+
+ /* fill the sample information */
+ sample->offset = *running_offset;
+ sample->pts_offset = ct;
+ sample->size = size;
+ sample->timestamp = timestamp;
+ sample->duration = dur;
+ /* sample-is-difference-sample */
+ /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
+ * now idea how it relates to bitfield other than massive LE/BE confusion */
+ sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
+ sample->iv = NULL;
+ sample->sub_encry = NULL;
+
+ stream->samples[i] = *sample;
+
+ *running_offset += size;
+ timestamp += dur;
+ sample++;
+
+ /* calculate total duration of the present fragment */
+ total_duration += gst_util_uint64_scale (dur, GST_SECOND, stream->timescale);
+ }
+
+ stream->sample_index = 0;
+
+ stream->n_samples += samples_count;
+
+ /* calculate avg fps based on avg frame duration */
+ stream->avg_dur = total_duration/samples_count;
+ g_print ("total dur = %"GST_TIME_FORMAT", avg_dur = %"GST_TIME_FORMAT"count = %d\n",
+ GST_TIME_ARGS(total_duration), GST_TIME_ARGS(stream->avg_dur), samples_count);
+
+ return TRUE;
+
+fail:
+ {
+ GST_WARNING_OBJECT (piffdemux, "failed to parse trun");
+ return FALSE;
+ }
+out_of_memory:
+ {
+ GST_WARNING_OBJECT (piffdemux, "failed to allocate %d samples",
+ stream->n_samples);
+ return FALSE;
+ }
+index_too_big:
+ {
+ GST_WARNING_OBJECT (piffdemux, "not allocating index of %d samples, would "
+ "be larger than %uMB (broken file?)", stream->n_samples,
+ PIFFDEMUX_MAX_SAMPLE_INDEX_SIZE >> 20);
+ return FALSE;
+ }
+}
+
+static gboolean
+piffdemux_parse_mfhd (GstPiffDemux * piffdemux, GstByteReader * mfhd)
+{
+ guint32 seq_num = 0;
+
+ if (!gst_byte_reader_skip (mfhd, 4))
+ goto invalid_mfhd;
+
+ if (!gst_byte_reader_get_uint32_be (mfhd, &seq_num))
+ goto invalid_mfhd;
+
+ GST_DEBUG_OBJECT (piffdemux, "sequence number present in mfhd = %d", seq_num);
+
+ return TRUE;
+
+invalid_mfhd:
+ {
+ GST_WARNING_OBJECT (piffdemux, "invalid movie fragment header");
+ return FALSE;
+ }
+}
+
+
+static gboolean
+piffdemux_parse_tfhd (GstPiffDemux * piffdemux, GstByteReader * tfhd,
+ guint32 * default_sample_duration,
+ guint32 * default_sample_size, guint32 * default_sample_flags,
+ gint64 * base_offset)
+{
+ guint32 flags = 0;
+ guint32 track_id = 0;
+
+ if (!gst_byte_reader_skip (tfhd, 1) ||
+ !gst_byte_reader_get_uint24_be (tfhd, &flags))
+ goto invalid_track;
+
+ if (!gst_byte_reader_get_uint32_be (tfhd, &track_id))
+ goto invalid_track;
+
+ GST_DEBUG_OBJECT (piffdemux, "trackID = %d", track_id);
+
+ if (flags & TF_BASE_DATA_OFFSET) {
+ if (!gst_byte_reader_get_uint64_be (tfhd, (guint64 *) base_offset))
+ goto invalid_track;
+ GST_DEBUG ("BaseData Offset = %"G_GUINT64_FORMAT, base_offset);
+ }
+
+ /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
+ if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
+ if (!gst_byte_reader_skip (tfhd, 4))
+ goto invalid_track;
+
+ if (flags & TF_DEFAULT_SAMPLE_DURATION)
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
+ goto invalid_track;
+
+ if (flags & TF_DEFAULT_SAMPLE_SIZE)
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_size))
+ goto invalid_track;
+
+ if (flags & TF_DEFAULT_SAMPLE_FLAGS)
+ if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_flags))
+ goto invalid_track;
+
+ return TRUE;
+
+invalid_track:
+ {
+ GST_WARNING_OBJECT (piffdemux, "invalid track fragment header");
+ return FALSE;
+ }
+}
+
+static gboolean
+piffdemux_parse_tfxd (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfxd)
+{
+ guint8 version = 0;
+
+ // 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
+
+ if (!gst_byte_reader_get_uint8 (tfxd, &version))
+ goto invalid_tfxd;
+
+ if (!gst_byte_reader_skip (tfxd, 3))
+ goto invalid_tfxd;
+
+ if (!piffdemux->lookahead_cnt) {
+ piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));
+ if (NULL == piffdemux->param) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ return FALSE;
+ }
+ piffdemux->param->count = 1;
+ piffdemux->param->long_info = NULL;
+ piffdemux->param->info = NULL;
+ piffdemux->param->is_eos = FALSE;
+
+ // TODO: presentation will be ended based on timeout in souphttpsrc in lookaheadcnt = 0 case
+ }
+
+ if (version == 1) {
+ guint64 duration = 0;
+ guint64 timestamp = 0;
+
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");
+ if (!gst_byte_reader_get_uint64_be (tfxd, ×tamp))
+ goto invalid_tfxd;
+ if (!gst_byte_reader_get_uint64_be (tfxd, &duration))
+ goto invalid_tfxd;
+
+ GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT,
+ timestamp, duration);
+
+ if (!piffdemux->lookahead_cnt) {
+ piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));
+ if (NULL == piffdemux->param->long_info) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ return FALSE;
+ }
+
+ /* Calculate next fragment's timestamp using current fragment's timestamp + duration */
+ piffdemux->param->long_info->duration = GST_CLOCK_TIME_NONE;
+ piffdemux->param->long_info->ts = timestamp +duration;
+ }
+ } else if (version == 0) {
+ guint32 duration = 0;
+ guint32 timestamp = 0;
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");
+
+ if (!gst_byte_reader_get_uint32_be (tfxd, ×tamp))
+ goto invalid_tfxd;
+
+ if (!gst_byte_reader_get_uint32_be (tfxd, &duration))
+ goto invalid_tfxd;
+
+ GST_DEBUG_OBJECT (piffdemux, "tfxd : absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,
+ timestamp, duration);
+
+ if (!piffdemux->lookahead_cnt) {
+ piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));
+ if (NULL == piffdemux->param->info) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ return FALSE;
+ }
+ /* Calculate next fragment's timestamp using current fragment's timestamp + duration */
+ piffdemux->param->info->duration = GST_CLOCK_TIME_NONE;
+ piffdemux->param->info->ts = timestamp +duration;
+ }
+ } else {
+ GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfxd...");
+ return FALSE;
+ }
+
+ if (!piffdemux->lookahead_cnt) {
+ GST_DEBUG_OBJECT (piffdemux, "Emitting live-param signal...");
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
+ }
+
+ return TRUE;
+
+invalid_tfxd:
+ GST_ERROR ("Invalid TFXD atom...");
+ return FALSE;
+}
+
+
+static gboolean
+piffdemux_parse_tfrf (GstPiffDemux * piffdemux, PiffDemuxStream *stream,GstByteReader * tfrf)
+{
+ guint8 version = 0;
+ guint8 frag_cnt = 0;
+ guint8 i = 0;
+
+ /* Getting version info */
+ if (!gst_byte_reader_get_uint8 (tfrf, &version))
+ goto invalid_tfrf;
+
+ /* skipping reserved flags */
+ if (!gst_byte_reader_skip (tfrf, 3))
+ goto invalid_tfrf;
+
+ if (!gst_byte_reader_get_uint8 (tfrf, &frag_cnt))
+ goto invalid_tfrf;
+
+ GST_INFO_OBJECT (piffdemux, "Subsequent fragments info count = %d", frag_cnt);
+
+ piffdemux->param = (piff_live_param_t *)malloc(sizeof (piff_live_param_t));
+ if (NULL == piffdemux->param) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ return FALSE;
+ }
+
+ piffdemux->param->count = frag_cnt;
+ piffdemux->param->long_info = NULL;
+ piffdemux->param->info = NULL;
+ piffdemux->param->is_eos = FALSE;
+
+ // TODO: Duration and timestamp values need to be posted to msl using g_signal_emit
+
+ if (version == 1) {
+ guint64 duration = 0;
+ guint64 timestamp = 0;
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 64-bit format...");
+
+ piffdemux->param->long_info = (piff_fragment_longtime_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_longtime_info));
+ if (NULL == piffdemux->param->long_info) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ return FALSE;
+ }
+
+ for (i = 0; i < frag_cnt; i++) {
+ if (!gst_byte_reader_get_uint64_be (tfrf, ×tamp))
+ goto invalid_tfrf;
+ if (!gst_byte_reader_get_uint64_be (tfrf, &duration))
+ goto invalid_tfrf;
+ GST_DEBUG_OBJECT (piffdemux, "tfrf long: absolute timestamp = %"G_GUINT64_FORMAT" and duration of fragment = %"G_GUINT64_FORMAT"\n",
+ timestamp, duration);
+ (piffdemux->param->long_info[i]).ts = timestamp;
+ (piffdemux->param->long_info[i]).duration = duration;
+ }
+ } else if (version == 0) {
+ guint32 duration = 0;
+ guint32 timestamp = 0;
+ GST_LOG_OBJECT (piffdemux, "Time and Duration are in 32-bit format...");
+
+ piffdemux->param->info = (piff_fragment_time_info *)malloc (piffdemux->param->count * sizeof (piff_fragment_time_info));
+ if (NULL == piffdemux->param->info) {
+ GST_ERROR ("Memory not available...\n");
+ return FALSE;
+ }
+
+ for (i = 0; i < frag_cnt; i++) {
+ if (!gst_byte_reader_get_uint32_be (tfrf, ×tamp))
+ goto invalid_tfrf;
+ if (!gst_byte_reader_get_uint32_be (tfrf, &duration))
+ goto invalid_tfrf;
+
+ GST_DEBUG_OBJECT (piffdemux, "tfrf int: absolute timestamp = %"G_GUINT32_FORMAT" and duration of fragment = %"G_GUINT32_FORMAT,
+ timestamp, duration);
+ (piffdemux->param->info[i]).ts = timestamp;
+ (piffdemux->param->info[i]).duration = duration;
+ }
+ } else {
+ GST_ERROR_OBJECT (piffdemux, "Invalid Version in tfrf...");
+ return FALSE;
+ }
+
+ g_print ("Signalling from TFRF box..\n");
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
+
+ return TRUE;
+
+invalid_tfrf:
+ GST_ERROR_OBJECT (piffdemux, "Invalid TFRF atom...");
+ return FALSE;
+}
+
+
+static gboolean
+piffdemux_parse_moof (GstPiffDemux * piffdemux, const guint8 * buffer, guint length,
+ guint64 moof_offset, PiffDemuxStream * stream)
+{
+ GNode *moof_node, *mfhd_node, *traf_node, *tfhd_node, *trun_node, *uuid_node;
+ GstByteReader mfhd_data, trun_data, tfhd_data, uuid_data;
+ guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
+ gint64 base_offset, running_offset;
+ gint64 uuid_offset = 0;
+ gboolean found_tfxd = FALSE;
+ gboolean found_tfrf = FALSE;
+
+ /* NOTE @stream ignored */
+
+ moof_node = g_node_new ((guint8 *) buffer);
+ piffdemux_parse_node (piffdemux, moof_node, buffer, length);
+ //piffdemux_node_dump (piffdemux, moof_node);
+
+ /* unknown base_offset to start with */
+ base_offset = running_offset = -1;
+
+ mfhd_node = piffdemux_tree_get_child_by_type_full (moof_node, FOURCC_mfhd, &mfhd_data);
+ if (!mfhd_node)
+ goto missing_mfhd;
+
+ if (!piffdemux_parse_mfhd (piffdemux, &mfhd_data))
+ goto missing_mfhd;
+
+ traf_node = piffdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
+ while (traf_node) {
+ /* Fragment Header node */
+ tfhd_node =
+ piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
+ &tfhd_data);
+ if (!tfhd_node)
+ goto missing_tfhd;
+ if (!piffdemux_parse_tfhd (piffdemux, &tfhd_data, &ds_duration,
+ &ds_size, &ds_flags, &base_offset))
+ goto missing_tfhd;
+
+ if (G_UNLIKELY (base_offset < -1))
+ goto lost_offset;
+
+ /* Track Run node */
+ trun_node =
+ piffdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
+ &trun_data);
+ while (trun_node) {
+ piffdemux_parse_trun (piffdemux, &trun_data, stream,
+ ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
+ &running_offset);
+ /* iterate all siblings */
+ trun_node = piffdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
+ &trun_data);
+ }
+
+ uuid_node = piffdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
+ while (uuid_node) {
+ uuid_type_t uuid_type;
+ guint8 *lbuffer = (guint8 *) uuid_node->data;
+
+ gst_byte_reader_init (&uuid_data, lbuffer, PIFF_UINT32 (lbuffer));
+
+ uuid_type = piffdemux_get_uuid_type (piffdemux, &uuid_data, &uuid_offset);
+
+ if ((UUID_TFXD == uuid_type) && piffdemux->is_live) {
+ // TODO: Dont know, why we should not consider tfxd offset...if we use tfxd offset also, not working.. PIFF doc does not say anything :(
+ found_tfxd = TRUE;
+ if (!piffdemux_parse_tfxd (piffdemux, stream, &uuid_data))
+ goto fail;
+ } else if ((UUID_TFRF == uuid_type) && piffdemux->is_live && piffdemux->lookahead_cnt) {
+ found_tfrf = TRUE;
+ if (!piffdemux_parse_tfrf (piffdemux, stream, &uuid_data))
+ goto fail;
+ piffdemux_update_sample_offset (piffdemux, stream, uuid_offset);
+ running_offset += uuid_offset;
+ } else if (UUID_SAMPLE_ENCRYPT == uuid_type) {
+ if (!piffdemux_parse_sample_encryption (piffdemux, &uuid_data, stream))
+ goto fail;
+ } else {
+ GST_WARNING_OBJECT (piffdemux, "Ignoring Wrong UUID...");
+ }
+
+ /* iterate all siblings */
+ uuid_node = piffdemux_tree_get_sibling_by_type (uuid_node, FOURCC_uuid);
+ }
+
+ if (piffdemux->is_live) {
+ if (!found_tfxd) {
+ GST_ERROR_OBJECT (piffdemux, "TFXD box is not present for live stream");
+ goto fail;
+ }
+
+ if (!found_tfrf && piffdemux->lookahead_cnt) {
+ /* when lookahead count is non-zero in manifest & if tfrf box is not present., means EOS */
+ GST_INFO_OBJECT (piffdemux, "Reached Endof Live presentation..");
+
+ piffdemux->param = (piff_live_param_t *)malloc (sizeof (piff_live_param_t));
+ if (NULL == piffdemux->param) {
+ GST_ERROR_OBJECT (piffdemux, "Memory not available...\n");
+ goto fail;
+ }
+ piffdemux->param->count = 0;
+ piffdemux->param->long_info = NULL;
+ piffdemux->param->info = NULL;
+ piffdemux->param->is_eos = TRUE; /* marking EOS */
+ g_signal_emit (piffdemux, gst_piffdemux_signals[SIGNAL_LIVE_PARAM], 0, piffdemux->param);
+ }
+ }
+
+ /* if no new base_offset provided for next traf,
+ * base is end of current traf */
+ base_offset = running_offset;
+ running_offset = -1;
+
+ /* iterate all siblings */
+ traf_node = piffdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
+ }
+ g_node_destroy (moof_node);
+ return TRUE;
+
+missing_mfhd:
+ {
+ GST_DEBUG_OBJECT (piffdemux, "missing mfhd box");
+ goto fail;
+ }
+
+missing_tfhd:
+ {
+ GST_DEBUG_OBJECT (piffdemux, "missing tfhd box");
+ goto fail;
+ }
+lost_offset:
+ {
+ GST_DEBUG_OBJECT (piffdemux, "lost offset");
+ goto fail;
+ }
+fail:
+ {
+ g_node_destroy (moof_node);
+
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
+ ("This file is corrupt and cannot be played."), (NULL));
+
+ return FALSE;
+ }
+}
+
+
+/* activate the given segment number @seg_idx of @stream at time @offset.
+ * @offset is an absolute global position over all the segments.
+ *
+ * This will push out a NEWSEGMENT event with the right values and
+ * position the stream index to the first decodable sample before
+ * @offset.
+ */
+static gboolean
+gst_piffdemux_activate_segment (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
+ guint32 seg_idx, guint64 offset)
+{
+ GstEvent *event;
+ PiffDemuxSegment *segment;
+ guint64 seg_time;
+ guint64 start, stop, time;
+ gdouble rate;
+
+ GST_LOG_OBJECT (piffdemux, "activate segment %d, offset %" G_GUINT64_FORMAT,
+ seg_idx, offset);
+
+ /* update the current segment */
+ stream->segment_index = seg_idx;
+
+ /* get the segment */
+ segment = &stream->segments[seg_idx];
+
+ if (G_UNLIKELY (offset < segment->time)) {
+ GST_WARNING_OBJECT (piffdemux, "offset < segment->time %" G_GUINT64_FORMAT,
+ segment->time);
+ return FALSE;
+ }
+
+ /* segment lies beyond total indicated duration */
+ if (G_UNLIKELY (piffdemux->segment.duration != -1 &&
+ segment->time > piffdemux->segment.duration)) {
+ GST_WARNING_OBJECT (piffdemux, "file duration %" G_GINT64_FORMAT
+ " < segment->time %" G_GUINT64_FORMAT, piffdemux->segment.duration,
+ segment->time);
+ return FALSE;
+ }
+
+ /* get time in this segment */
+ seg_time = offset - segment->time;
+
+ GST_LOG_OBJECT (piffdemux, "seg_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (seg_time));
+
+ if (G_UNLIKELY (seg_time > segment->duration)) {
+ GST_LOG_OBJECT (piffdemux, "seg_time > segment->duration %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (segment->duration));
+ return FALSE;
+ }
+
+ /* piffdemux->segment.stop is in outside-time-realm, whereas
+ * segment->media_stop is in track-time-realm.
+ *
+ * In order to compare the two, we need to bring segment.stop
+ * into the track-time-realm */
+
+ stop = piffdemux->segment.stop;
+ if (stop == -1)
+ stop = piffdemux->segment.duration;
+ if (stop == -1)
+ stop = segment->media_stop;
+ else
+ stop =
+ MIN (segment->media_stop, stop - segment->time + segment->media_start);
+
+ if (piffdemux->segment.rate >= 0) {
+ start = MIN (segment->media_start + seg_time, stop);
+ time = offset;
+ } else {
+ if (segment->media_start >= piffdemux->segment.start) {
+ start = segment->media_start;
+ time = segment->time;
+ } else {
+ start = piffdemux->segment.start;
+ time = segment->time + (piffdemux->segment.start - segment->media_start);
+ }
+
+ start = MAX (segment->media_start, piffdemux->segment.start);
+ stop = MIN (segment->media_start + seg_time, stop);
+ }
+
+ GST_DEBUG_OBJECT (piffdemux, "newsegment %d from %" GST_TIME_FORMAT
+ " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
+
+ /* combine global rate with that of the segment */
+ rate = segment->rate * piffdemux->segment.rate;
+
+ /* update the segment values used for clipping */
+ gst_segment_init (&stream->segment, GST_FORMAT_TIME);
+ gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
+ start, stop, time);
+
+ /* now prepare and send the segment */
+ event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
+ start, stop, time);
+ gst_pad_push_event (piffdemux->srcpad, event);
+ /* assume we can send more data now */
+ stream->last_ret = GST_FLOW_OK;
+ /* clear to send tags on this pad now */
+ gst_piffdemux_push_tags (piffdemux, stream);
+
+ return TRUE;
+}
+
+
+/* prepare to get the current sample of @stream, getting essential values.
+ *
+ * This function will also prepare and send the segment when needed.
+ *
+ * Return FALSE if the stream is EOS.
+ */
+static gboolean
+gst_piffdemux_prepare_current_sample (GstPiffDemux * piffdemux,
+ PiffDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
+ guint64 * duration, gboolean * keyframe)
+{
+ PiffDemuxSample *sample;
+ guint64 time_position;
+ guint32 seg_idx;
+
+ g_return_val_if_fail (stream != NULL, FALSE);
+
+ time_position = stream->time_position;
+ if (G_UNLIKELY (time_position == -1))
+ goto eos;
+
+ seg_idx = stream->segment_index;
+ if (G_UNLIKELY (seg_idx == -1)) {
+ /* find segment corresponding to time_position if we are looking
+ * for a segment. */
+ seg_idx = gst_piffdemux_find_segment (piffdemux, stream, time_position);
+
+ /* nothing found, we're really eos */
+ if (seg_idx == -1)
+ goto eos;
+ }
+
+ /* different segment, activate it, sample_index will be set. */
+ if (G_UNLIKELY (stream->segment_index != seg_idx))
+ gst_piffdemux_activate_segment (piffdemux, stream, seg_idx, time_position);
+
+ GST_LOG_OBJECT (piffdemux, "segment active, index = %u of %u",
+ stream->sample_index, stream->n_samples);
+
+ if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
+ goto eos;
+
+
+ /* now get the info for the sample we're at */
+ sample = &stream->samples[stream->sample_index];
+
+ *timestamp = PIFFSAMPLE_PTS (stream, sample);
+ *offset = sample->offset;
+ *size = sample->size;
+ *duration = PIFFSAMPLE_DUR_PTS (stream, sample, *timestamp);
+ *keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);
+
+ return TRUE;
+
+ /* special cases */
+eos:
+ {
+ stream->time_position = -1;
+ return FALSE;
+ }
+}
+
+/* the input buffer metadata must be writable,
+ * but time/duration etc not yet set and need not be preserved */
+static GstBuffer *
+gst_piffdemux_process_buffer (GstPiffDemux * piffdemux, PiffDemuxStream * stream,
+ GstBuffer * buf)
+{
+ guint8 *data;
+ guint size, nsize = 0;
+ gchar *str;
+
+ data = GST_BUFFER_DATA (buf);
+ size = GST_BUFFER_SIZE (buf);
+
+ if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
+ return buf;
+ }
+
+ if (G_LIKELY (size >= 2)) {
+ nsize = GST_READ_UINT16_BE (data);
+ nsize = MIN (nsize, size - 2);
+ }
+
+ GST_LOG_OBJECT (piffdemux, "3GPP timed text subtitle: %d/%d", nsize, size);
+
+ /* takes care of UTF-8 validation or UTF-16 recognition,
+ * no other encoding expected */
+ str = gst_tag_freeform_string_to_utf8 ((gchar *) data + 2, nsize, NULL);
+ if (str) {
+ gst_buffer_unref (buf);
+ buf = gst_buffer_new ();
+ GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = (guint8 *) str;
+ GST_BUFFER_SIZE (buf) = strlen (str);
+ } else {
+ /* may be 0-size subtitle, which is also sent to keep pipeline going */
+ GST_BUFFER_DATA (buf) = data + 2;
+ GST_BUFFER_SIZE (buf) = nsize;
+ }
+
+ /* FIXME ? convert optional subsequent style info to markup */
+
+ return buf;
+}
+
+/* Sets a buffer's attributes properly and pushes it downstream.
+ * Also checks for additional actions and custom processing that may
+ * need to be done first.
+ */
+static gboolean
+gst_piffdemux_decorate_and_push_buffer (GstPiffDemux * piffdemux,
+ PiffDemuxStream * stream, GstBuffer * buf,
+ guint64 timestamp, guint64 duration, gboolean keyframe, guint64 position,
+ guint64 byte_position)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (!stream->caps) {
+ GST_WARNING_OBJECT (piffdemux, "caps are empty...creat any caps");
+ stream->caps = gst_caps_new_any();
+ if (!stream->caps) {
+ GST_ERROR_OBJECT (piffdemux, "failed to create caps...");
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+ }
+
+ /* position reporting */
+ if (piffdemux->segment.rate >= 0) {
+ // TODO: Segment fault is coming here for Audio stream.. need to check
+
+ gst_segment_set_last_stop (&piffdemux->segment, GST_FORMAT_TIME, position);
+ //gst_piffdemux_sync_streams (piffdemux);
+ }
+
+ /* send out pending buffers */
+ while (stream->buffers) {
+ GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
+
+ if (G_UNLIKELY (stream->discont)) {
+ GST_LOG_OBJECT (piffdemux, "marking discont buffer");
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+ stream->discont = FALSE;
+ }
+ gst_buffer_set_caps (buffer, stream->caps);
+
+ gst_pad_push (piffdemux->srcpad, buffer);
+
+ stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
+ }
+
+ /* we're going to modify the metadata */
+ buf = gst_buffer_make_metadata_writable (buf);
+
+ /* for subtitle processing */
+ if (G_UNLIKELY (stream->need_process))
+ buf = gst_piffdemux_process_buffer (piffdemux, stream, buf);
+
+ GST_BUFFER_TIMESTAMP (buf) = timestamp;
+ GST_BUFFER_DURATION (buf) = duration;
+ GST_BUFFER_OFFSET (buf) = -1;
+ GST_BUFFER_OFFSET_END (buf) = -1;
+
+ if (G_UNLIKELY (stream->padding)) {
+ GST_BUFFER_DATA (buf) += stream->padding;
+ GST_BUFFER_SIZE (buf) -= stream->padding;
+ }
+
+ if (G_UNLIKELY (buf == NULL))
+ goto exit;
+
+ if (G_UNLIKELY (stream->discont)) {
+ GST_LOG_OBJECT (piffdemux, "marking discont buffer");
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+ stream->discont = FALSE;
+ }
+
+ if (!keyframe)
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
+
+ //g_print ("\n\npad caps : %s\n\n", gst_caps_to_string (gst_pad_get_caps (stream->pad)));
+
+ //gst_buffer_set_caps (buf, stream->caps); // commenting to avoid caps by setting properties
+
+ // TODO: need to see how resolution switch will work
+ gst_buffer_set_caps (buf, stream->caps);
+
+#ifdef DRM_ENABLE
+ if (piffdemux->encrypt_content) {
+ drm_trusted_payload_info_s read_input_data = {0, };
+ drm_trusted_read_decrypt_resp_data_s read_output_data = {0, };
+ gint offset = 0;
+ PiffDemuxSample *sample = &stream->samples[stream->sample_index];
+
+ if (sample->sub_encry) {
+ offset = sample->sub_encry->sub_entry[0].LenofClearData;
+ }
+
+ read_input_data.media_offset = 0;
+ read_input_data.payload_data = GST_BUFFER_DATA(buf) + offset;
+ read_input_data.payload_data_len = GST_BUFFER_SIZE(buf) - offset;
+ read_input_data.payload_iv_len = 8;
+ read_input_data.payload_iv = (unsigned char *) malloc (8);
+ read_input_data.payload_data_output = GST_BUFFER_DATA(buf) + offset;
+ if (NULL == read_input_data.payload_iv) {
+ GST_ERROR ("Failed to allocate memory...");
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+ memcpy (read_input_data.payload_iv, sample->iv, 8);
+
+ ret = drm_trusted_read_decrypt_session(piffdemux->pr_handle , &read_input_data, &read_output_data);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (piffdemux, "failed to decrypt buffer...");
+ free (read_input_data.payload_iv);
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+
+ if (read_output_data.read_size != read_input_data.payload_data_len) {
+ g_print ("Decrypter did not consume data fully...\n\n\n");
+ }
+
+ free (read_input_data.payload_iv);
+ read_input_data.payload_iv = NULL;
+
+ }
+#endif
+
+ GST_LOG_OBJECT (piffdemux,
+ "Pushing buffer of size = %d with time %" GST_TIME_FORMAT ", duration %"
+ GST_TIME_FORMAT, GST_BUFFER_SIZE(buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
+
+#ifdef DEC_OUT_FRAME_DUMP
+ {
+ int written = 0;
+ written = fwrite (GST_BUFFER_DATA (buf), sizeof (unsigned char), GST_BUFFER_SIZE (buf), piffdump);
+ g_print ("PIFFDEMUX: written = %d\n", written);
+ fflush (piffdump);
+ }
+#endif
+
+ ret = gst_pad_push (piffdemux->srcpad, buf);
+
+exit:
+ return ret;
+}
+
+
+/*
+ * next_entry_size
+ *
+ * Returns the size of the first entry at the current offset.
+ * If -1, there are none (which means EOS or empty file).
+ */
+static guint64
+next_entry_size (GstPiffDemux * demux)
+{
+ PiffDemuxStream *stream = demux->stream;
+ PiffDemuxSample *sample;
+
+ GST_LOG_OBJECT (demux, "Finding entry at offset %" G_GUINT64_FORMAT,
+ demux->offset);
+
+ GST_DEBUG_OBJECT (demux, "demux->sample_index = %d", stream->sample_index);
+
+ if (stream->sample_index == -1)
+ stream->sample_index = 0;
+
+ if (stream->sample_index >= stream->n_samples) {
+ GST_LOG_OBJECT (demux, "stream %d samples exhausted n_samples = %d",
+ stream->sample_index, stream->n_samples);
+ return -1;
+ }
+
+ sample = &stream->samples[stream->sample_index];
+
+ GST_LOG_OBJECT (demux,
+ "Checking Stream %d (sample_index:%d / offset:%" G_GUINT64_FORMAT
+ " / size:%" G_GUINT32_FORMAT ")", stream->sample_index, stream->sample_index,
+ sample->offset, sample->size);
+
+ GST_LOG_OBJECT (demux, "stream : demux->offset :%"G_GUINT64_FORMAT, demux->offset);
+
+ stream = demux->stream;
+ sample = &stream->samples[stream->sample_index];
+
+ if (sample->offset >= demux->offset) {
+ demux->todrop = sample->offset - demux->offset;
+ return sample->size + demux->todrop;
+ }
+
+ GST_DEBUG_OBJECT (demux,
+ "There wasn't any entry at offset %" G_GUINT64_FORMAT, demux->offset);
+ return -1;
+}
+
+static void
+gst_piffdemux_post_progress (GstPiffDemux * demux, gint num, gint denom)
+{
+ gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);
+
+ gst_element_post_message (GST_ELEMENT_CAST (demux),
+ gst_message_new_element (GST_OBJECT_CAST (demux),
+ gst_structure_new ("progress", "percent", G_TYPE_INT, perc, NULL)));
+}
+
+static gboolean
+piffdemux_seek_offset (GstPiffDemux * demux, guint64 offset)
+{
+ GstEvent *event;
+ gboolean res = 0;
+
+ GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
+
+ event =
+ gst_event_new_seek (1.0, GST_FORMAT_BYTES,
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
+ GST_SEEK_TYPE_NONE, -1);
+
+ res = gst_pad_push_event (demux->sinkpad, event);
+
+ return res;
+}
+
+static GstFlowReturn
+gst_piffdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
+{
+ GstPiffDemux *demux;
+ GstFlowReturn ret = GST_FLOW_OK;
+ demux = GST_PIFFDEMUX (gst_pad_get_parent (sinkpad));
+#ifdef DRM_ENABLE
+ if (demux->encrypt_content && !demux->decrypt_init) {
+ int ret = -1;
+ drm_trusted_permission_type_e perm_type = DRM_TRUSTED_PERMISSION_TYPE_PLAY;
+ drm_trusted_open_decrypt_info_s open_input_data = {0, };
+ drm_trusted_open_decrypt_resp_data_s open_output_data = {0, };
+ drm_trusted_set_consumption_state_info_s state_input_data = {0, };
+
+ open_input_data.file_type = DRM_TRUSTED_TYPE_PIFF;
+ open_input_data.permission = perm_type;
+ open_input_data.operation_callback.callback = test_drm_trusted_operation_cb;
+ open_input_data.lic_header.header = GST_BUFFER_DATA(demux->protection_header);
+ open_input_data.lic_header.header_len = GST_BUFFER_SIZE (demux->protection_header);
+
+ /* Open Decrypt Session*/
+ ret = drm_trusted_open_decrypt_session(&open_input_data, &open_output_data, &(demux->pr_handle));
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (demux, "failed to open decrypt session");
+ goto unknown_stream;
+ }
+
+ /* Before Read, Appropriate state MUST be SET */
+ state_input_data.state = DRM_CONSUMPTION_STARTED;
+ ret = drm_trusted_set_decrypt_state(demux->pr_handle, &state_input_data);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (demux, "failed to set decrypt state...");
+ goto unknown_stream;
+ }
+
+ demux->decrypt_init = TRUE;
+ }
+#endif
+ gst_adapter_push (demux->adapter, inbuf);
+
+ /* we never really mean to buffer that much */
+ if (demux->neededbytes == -1)
+ goto eos;
+
+ GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
+ inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
+
+ while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
+ (ret == GST_FLOW_OK)) {
+
+ GST_DEBUG_OBJECT (demux,
+ "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
+ demux->state, demux->neededbytes, demux->offset);
+
+ switch (demux->state) {
+ case PIFFDEMUX_STATE_INITIAL:{
+ const guint8 *data;
+ guint32 fourcc;
+ guint64 size;
+
+ data = gst_adapter_peek (demux->adapter, demux->neededbytes);
+
+ if (!data) {
+ GST_ERROR_OBJECT (demux, "peeked adapter buffer is NULL");
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
+ /* get fourcc/length, set neededbytes */
+ extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
+ &size, &fourcc);
+ GST_DEBUG_OBJECT (demux, "Peeking found [%" GST_FOURCC_FORMAT "] "
+ "size: %" G_GUINT64_FORMAT, GST_FOURCC_ARGS (fourcc), size);
+ if (size == 0) {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
+ ("This file is invalid and cannot be played."),
+ ("initial atom '%" GST_FOURCC_FORMAT "' has empty length",
+ GST_FOURCC_ARGS (fourcc)));
+
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
+ if (fourcc == FOURCC_mdat) {
+ if (demux->moof_rcvd) {
+ /* we have the headers, start playback */
+ demux->state = PIFFDEMUX_STATE_MOVIE;
+ demux->neededbytes = next_entry_size (demux);
+ demux->mdatleft = size;
+
+ /* Only post, event on pads is done after newsegment */
+ piffdemux_post_global_tags (demux);
+ } else {
+ GST_ERROR_OBJECT (demux, "mdata received before moof.. not handled");
+ goto unknown_stream;
+ }
+ } else if (G_UNLIKELY (size > PIFFDEMUX_MAX_ATOM_SIZE)) {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
+ ("This file is invalid and cannot be played."),
+ ("atom %" GST_FOURCC_FORMAT " has bogus size %" G_GUINT64_FORMAT,
+ GST_FOURCC_ARGS (fourcc), size));
+ ret = GST_FLOW_ERROR;
+ break;
+ } else {
+ demux->neededbytes = size;
+ demux->state = PIFFDEMUX_STATE_HEADER;
+ }
+ break;
+ }
+ case PIFFDEMUX_STATE_HEADER:{
+ const guint8 *data;
+ guint32 fourcc;
+
+ GST_DEBUG_OBJECT (demux, "In header");
+
+ data = gst_adapter_peek (demux->adapter, demux->neededbytes);
+
+ if (!data) {
+ GST_ERROR_OBJECT (demux, "peeked adapter buffer is NULL");
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
+ /* parse the header */
+ extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
+ &fourcc);
+ if (fourcc == FOURCC_moof) {
+ GST_DEBUG_OBJECT (demux, "Parsing [moof]");
+ if (!piffdemux_parse_moof (demux, data, demux->neededbytes,
+ demux->offset, demux->stream)) {
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ demux->moof_rcvd = TRUE;
+ } else {
+ GST_WARNING_OBJECT (demux,
+ "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+ /* Let's jump that one and go back to initial state */
+ }
+
+ if (demux->mdatbuffer) {
+ /* the mdat was before the header */
+ GST_DEBUG_OBJECT (demux, "We have mdatbuffer:%p",
+ demux->mdatbuffer);
+ gst_adapter_clear (demux->adapter);
+ demux->mdatbuffer = NULL;
+ demux->offset = demux->mdatoffset;
+ demux->neededbytes = next_entry_size (demux);
+ demux->state = PIFFDEMUX_STATE_MOVIE;
+ demux->mdatleft = gst_adapter_available (demux->adapter);
+
+ /* Only post, event on pads is done after newsegment */
+ piffdemux_post_global_tags (demux);
+ } else {
+ GST_DEBUG_OBJECT (demux, "Carrying on normally");
+ gst_adapter_flush (demux->adapter, demux->neededbytes);
+ demux->offset += demux->neededbytes;
+ demux->neededbytes = 16;
+ demux->state = PIFFDEMUX_STATE_INITIAL;
+ }
+
+ break;
+ }
+ case PIFFDEMUX_STATE_BUFFER_MDAT:{
+ GstBuffer *buf;
+
+ GST_DEBUG_OBJECT (demux, "Got our buffer at offset %" G_GUINT64_FORMAT,
+ demux->offset);
+ buf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
+ GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (PIFF_FOURCC (GST_BUFFER_DATA (buf) + 4)));
+ if (demux->mdatbuffer)
+ demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
+ else
+ demux->mdatbuffer = buf;
+ demux->offset += demux->neededbytes;
+ demux->neededbytes = 16;
+ demux->state = PIFFDEMUX_STATE_INITIAL;
+ gst_piffdemux_post_progress (demux, 1, 1);
+
+ break;
+ }
+ case PIFFDEMUX_STATE_MOVIE:{
+ GstBuffer *outbuf;
+ PiffDemuxStream *stream = demux->stream;
+ PiffDemuxSample *sample;
+ guint64 timestamp, duration, position;
+ gboolean keyframe;
+
+ GST_DEBUG_OBJECT (demux,
+ "BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
+
+ if (demux->fragmented) {
+ GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
+ demux->mdatleft);
+ if (G_LIKELY (demux->todrop < demux->mdatleft)) {
+ /* if needed data starts within this atom,
+ * then it should not exceed this atom */
+ if (G_UNLIKELY (demux->neededbytes > demux->mdatleft)) {
+
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
+ ("This file is invalid and cannot be played."),
+ ("sample data crosses atom boundary"));
+
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+ demux->mdatleft -= demux->neededbytes;
+ } else {
+ GST_DEBUG_OBJECT (demux, "data atom emptied; resuming atom scan");
+ /* so we are dropping more than left in this atom */
+ demux->todrop -= demux->mdatleft;
+ demux->neededbytes -= demux->mdatleft;
+ demux->mdatleft = 0;
+ /* need to resume atom parsing so we do not miss any other pieces */
+ demux->state = PIFFDEMUX_STATE_INITIAL;
+ demux->neededbytes = 16;
+ break;
+ }
+ }
+
+ if (demux->todrop) {
+ GST_LOG_OBJECT (demux, "Dropping %d bytes", demux->todrop);
+ gst_adapter_flush (demux->adapter, demux->todrop);
+ demux->neededbytes -= demux->todrop;
+ demux->offset += demux->todrop;
+ }
+
+ if ( !stream->sent_nsevent) {
+ //TODO: better to parse sink event function and send that new_segment
+#if 1
+ demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ demux->stream->start_ts, -1, demux->stream->start_ts);
+#else
+ demux->pending_newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ 0, gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale), 0);
+#endif
+
+ GST_INFO_OBJECT (demux, "New segment event : start = %"GST_TIME_FORMAT", stop = %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (demux->stream->start_ts), GST_TIME_ARGS(gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale)));
+
+ if (!gst_pad_push_event (demux->srcpad, demux->pending_newsegment)) {
+ GST_ERROR_OBJECT (demux, "failding to send new segment...");
+ goto newsegment_error;
+ }
+ stream->sent_nsevent = TRUE;
+ }
+
+ /* Put data in a buffer, set timestamps, caps, ... */
+ outbuf = gst_adapter_take_buffer (demux->adapter, demux->neededbytes);
+
+ GST_DEBUG_OBJECT (demux, "Taken %d size buffer from adapter...", outbuf ? GST_BUFFER_SIZE (outbuf) : 0);
+
+ GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (stream->fourcc));
+
+ g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
+
+ sample = &stream->samples[stream->sample_index];
+
+ GST_DEBUG_OBJECT (demux, "start_ts = %"GST_TIME_FORMAT" ts : %"GST_TIME_FORMAT" ts = %llu, pts_offset = %u, scale = %d\n",
+ GST_TIME_ARGS(stream->start_ts),GST_TIME_ARGS(sample->timestamp), sample->timestamp,
+ sample->pts_offset,stream->timescale);
+
+ position = PIFFSAMPLE_DTS (stream, sample);
+ timestamp = PIFFSAMPLE_PTS (stream, sample) + stream->start_ts; // Adding to avoid resetting of timestamp
+ duration = PIFFSAMPLE_DUR_DTS (stream, sample, position);
+ keyframe = PIFFSAMPLE_KEYFRAME (stream, sample);
+
+ ret = gst_piffdemux_decorate_and_push_buffer (demux, stream, outbuf,
+ timestamp, duration, keyframe, position, demux->offset);
+
+ stream->sample_index++;
+
+ /* update current offset and figure out size of next buffer */
+ GST_LOG_OBJECT (demux, "increasing offset %" G_GUINT64_FORMAT " by %u",
+ demux->offset, demux->neededbytes);
+ demux->offset += demux->neededbytes;
+ GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
+ demux->offset);
+
+ if ((demux->neededbytes = next_entry_size (demux)) == -1) {
+ GST_DEBUG_OBJECT (demux, "finished parsing mdat, need to search next moof atom");
+ demux->neededbytes = 16;
+ demux->state = PIFFDEMUX_STATE_INITIAL;
+ GST_DEBUG ("\n\n Storing %s last_ts %"GST_TIME_FORMAT"\n\n", stream->subtype == FOURCC_vide ? "video" : "audio", GST_TIME_ARGS(timestamp));
+ stream->start_ts = timestamp + duration;
+ //goto eos;
+ }
+ break;
+ }
+ default:
+ goto invalid_state;
+ }
+ }
+
+ /* when buffering movie data, at least show user something is happening */
+ if (ret == GST_FLOW_OK && demux->state == PIFFDEMUX_STATE_BUFFER_MDAT &&
+ gst_adapter_available (demux->adapter) <= demux->neededbytes) {
+ gst_piffdemux_post_progress (demux, gst_adapter_available (demux->adapter),
+ demux->neededbytes);
+ }
+done:
+ gst_object_unref (demux);
+
+ return ret;
+
+ /* ERRORS */
+unknown_stream:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+eos:
+ {
+ GST_DEBUG_OBJECT (demux, "no next entry, EOS");
+ ret = GST_FLOW_UNEXPECTED;
+ goto done;
+ }
+invalid_state:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+ (NULL), ("piffdemuxer invalid state %d", demux->state));
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+newsegment_error:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+ (NULL), ("could not send newsegment event"));
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+}
+
+static gboolean
+piffdemux_parse_container (GstPiffDemux * piffdemux, GNode * node, const guint8 * buf,
+ const guint8 * end)
+{
+ while (G_UNLIKELY (buf < end)) {
+ GNode *child;
+ guint32 len;
+
+ if (G_UNLIKELY (buf + 4 > end)) {
+ GST_LOG_OBJECT (piffdemux, "buffer overrun");
+ break;
+ }
+ len = PIFF_UINT32 (buf);
+ if (G_UNLIKELY (len == 0)) {
+ GST_LOG_OBJECT (piffdemux, "empty container");
+ break;
+ }
+ if (G_UNLIKELY (len < 8)) {
+ GST_WARNING_OBJECT (piffdemux, "length too short (%d < 8)", len);
+ break;
+ }
+ if (G_UNLIKELY (len > (end - buf))) {
+ GST_WARNING_OBJECT (piffdemux, "length too long (%d > %d)", len,
+ (gint) (end - buf));
+ break;
+ }
+
+ child = g_node_new ((guint8 *) buf);
+ g_node_append (node, child);
+ GST_LOG_OBJECT (piffdemux, "adding new node of len %d", len);
+ piffdemux_parse_node (piffdemux, child, buf, len);
+
+ buf += len;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+piffdemux_parse_node (GstPiffDemux * piffdemux, GNode * node, const guint8 * buffer,
+ guint length)
+{
+ guint32 fourcc = 0;
+ guint32 node_length = 0;
+ const PiffNodeType *type;
+ const guint8 *end;
+
+ GST_LOG_OBJECT (piffdemux, "piffdemux_parse buffer %p length %u", buffer, length);
+
+ if (G_UNLIKELY (length < 8))
+ goto not_enough_data;
+
+ node_length = PIFF_UINT32 (buffer);
+ fourcc = PIFF_FOURCC (buffer + 4);
+
+ /* ignore empty nodes */
+ if (G_UNLIKELY (fourcc == 0 || node_length == 8))
+ return TRUE;
+
+ type = piffdemux_type_get (fourcc);
+
+ end = buffer + length;
+
+ GST_LOG_OBJECT (piffdemux,
+ "parsing '%" GST_FOURCC_FORMAT "', length=%u, name '%s'",
+ GST_FOURCC_ARGS (fourcc), node_length, type->name);
+
+ if (node_length > length)
+ goto broken_atom_size;
+
+ if (type->flags & PIFF_FLAG_CONTAINER) {
+ piffdemux_parse_container (piffdemux, node, buffer + 8, end);
+ }
+ GST_LOG_OBJECT (piffdemux, "parsed '%" GST_FOURCC_FORMAT "'",
+ GST_FOURCC_ARGS (fourcc));
+ return TRUE;
+
+/* ERRORS */
+not_enough_data:
+ {
+
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
+ ("This file is corrupt and cannot be played."),
+ ("Not enough data for an atom header, got only %u bytes", length));
+
+ return FALSE;
+ }
+broken_atom_size:
+ {
+ GST_ELEMENT_ERROR (piffdemux, STREAM, DEMUX,
+ ("This file is corrupt and cannot be played."),
+ ("Atom '%" GST_FOURCC_FORMAT "' has size of %u bytes, but we have only "
+ "%u bytes available.", GST_FOURCC_ARGS (fourcc), node_length,
+ length));
+
+ return FALSE;
+ }
+}
+
+
+static GNode *
+piffdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
+{
+ GNode *child;
+ guint8 *buffer;
+ guint32 child_fourcc;
+
+ for (child = g_node_first_child (node); child;
+ child = g_node_next_sibling (child)) {
+ buffer = (guint8 *) child->data;
+
+ child_fourcc = PIFF_FOURCC (buffer + 4);
+
+ if (G_UNLIKELY (child_fourcc == fourcc)) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static GNode *
+piffdemux_tree_get_child_by_type_full (GNode * node, guint32 fourcc,
+ GstByteReader * parser)
+{
+ GNode *child;
+ guint8 *buffer;
+ guint32 child_fourcc, child_len;
+
+ for (child = g_node_first_child (node); child;
+ child = g_node_next_sibling (child)) {
+ buffer = (guint8 *) child->data;
+
+ child_len = PIFF_UINT32 (buffer);
+ child_fourcc = PIFF_FOURCC (buffer + 4);
+
+ if (G_UNLIKELY (child_fourcc == fourcc)) {
+ if (G_UNLIKELY (child_len < (4 + 4)))
+ return NULL;
+ /* FIXME: must verify if atom length < parent atom length */
+ gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static GNode *
+piffdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc,
+ GstByteReader * parser)
+{
+ GNode *child;
+ guint8 *buffer;
+ guint32 child_fourcc, child_len;
+
+ for (child = g_node_next_sibling (node); child;
+ child = g_node_next_sibling (child)) {
+ buffer = (guint8 *) child->data;
+
+ child_fourcc = PIFF_FOURCC (buffer + 4);
+
+ if (child_fourcc == fourcc) {
+ if (parser) {
+ child_len = PIFF_UINT32 (buffer);
+ if (G_UNLIKELY (child_len < (4 + 4)))
+ return NULL;
+ /* FIXME: must verify if atom length < parent atom length */
+ gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
+ }
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static GNode *
+piffdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
+{
+ return piffdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
+}
+
+#define _codec(name) \
+ do { \
+ if (codec_name) { \
+ *codec_name = g_strdup (name); \
+ } \
+ } while (0)
+
+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)
+{
+ GstCaps *caps = NULL;
+ GstBuffer *dci = NULL;
+
+ if (codec_data && codec_data_len) {
+ dci = gst_buffer_new_and_alloc (codec_data_len);
+ if (!dci) {
+ GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");
+ } else {
+ memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);
+ }
+ }
+
+ switch (fourcc) {
+
+ case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
+ caps = gst_caps_new_simple ("video/x-h264",
+ "width", G_TYPE_INT, width,
+ "height", G_TYPE_INT, height,
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
+ "stream-format", G_TYPE_STRING, "avc",
+ "alignment", G_TYPE_STRING, "au",
+ "codec_data", GST_TYPE_BUFFER, dci,
+ NULL);
+ break;
+
+ case FOURCC_ovc1:
+ caps = gst_caps_new_simple ("video/x-wmv",
+ "width", G_TYPE_INT, width,
+ "height", G_TYPE_INT, height,
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
+ "wmvversion", G_TYPE_INT, 3,
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
+ "codec_data", GST_TYPE_BUFFER, dci,
+ NULL);
+ break;
+
+ default: {
+ char *s;
+ s = g_strdup_printf ("video/x-gst-fourcc-%" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+ caps = gst_caps_new_simple (s,
+ "width", G_TYPE_INT, width,
+ "height", G_TYPE_INT, height,
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
+ "codec_data", GST_TYPE_BUFFER, dci,
+ NULL);
+ break;
+ }
+ }
+
+ piffdemux->stream->caps = caps;
+ gchar *caps_string = gst_caps_to_string(caps);
+ GST_INFO_OBJECT (piffdemux, "prepared video caps : %s", caps_string);
+ g_free(caps_string);
+ caps_string = NULL;
+}
+
+void
+gst_piffdemux_set_audio_params (GstPiffDemux * piffdemux, guint fourcc,
+ guint sampling_rate, guint bps, guint channels, unsigned char *codec_data, unsigned int codec_data_len)
+{
+ GstCaps *caps = NULL;
+ GstBuffer *dci = NULL;
+
+ if (codec_data && codec_data_len) {
+ dci = gst_buffer_new_and_alloc (codec_data_len);
+ if (!dci) {
+ GST_ERROR_OBJECT (piffdemux, "failed to create codec data buffer...");
+ } else {
+ memcpy (GST_BUFFER_DATA(dci), codec_data, codec_data_len);
+ }
+ }
+
+ switch (fourcc) {
+
+ case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
+ 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) sampling_rate,
+ "channels", G_TYPE_INT, channels,
+ NULL);
+ break;
+
+ case FOURCC_owma:
+ caps = gst_caps_new_simple ("audio/x-wma",
+ "rate", G_TYPE_INT, (int) sampling_rate,
+ "channels", G_TYPE_INT, channels,
+ NULL);
+ break;
+
+ default: {
+ char *s;
+ s = g_strdup_printf ("audio/x-gst-fourcc-%" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+ caps = gst_caps_new_simple (s,
+ "rate", G_TYPE_INT, (int) sampling_rate,
+ "channels", G_TYPE_INT, channels,
+ NULL);
+ break;
+ }
+ }
+
+ piffdemux->stream->caps = caps;
+ char *tmp_caps_name = gst_caps_to_string(caps);
+ GST_INFO_OBJECT (piffdemux, "prepared audio caps : %s", tmp_caps_name);
+ g_free(tmp_caps_name);
+
+}
+
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+
+void
+__gst_piffdemux_marshal_BOOLEAN__OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer data2);
+ register GMarshalFunc_BOOLEAN__OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
+#define PIFFDEMUX_SPSPPS_LENGTH_SIZE 2
+
+static gboolean
+ConvertH264_MetaDCI_to_3GPPDCI(unsigned char *dci_meta_buf, unsigned int dci_meta_size, unsigned char **dci_3gpp_buf, unsigned int *dci_3gpp_size)
+{
+ unsigned short unit_size = 0;
+ unsigned int total_size = 0;
+ unsigned char unit_nb = 0;
+ unsigned char sps_done = 0;
+ const unsigned char *extradata = NULL;
+ unsigned int h264_nal_length_size = 0;
+ unsigned char *out = NULL;
+ //g_print ("\n\nConvertH264_MetaDCI_to_3GPPDCI Entering.............\n");
+
+ /* nothing to filter */
+ if ((dci_meta_buf == NULL) || (dci_meta_size < 6))
+ {
+ GST_ERROR ("Insufficient codec data...\n");
+ return FALSE;
+ }
+
+ /* Removing unnecessary info in meta data */
+ extradata = (unsigned char *)dci_meta_buf + 4;
+
+ /* retrieve Length of Length*/
+ h264_nal_length_size = (*extradata++ & 0x03) + 1;
+
+ GST_LOG ("Length Of Length is %d\n", h264_nal_length_size);
+ if (h264_nal_length_size == 3)
+ {
+ GST_ERROR ("LengthOfLength is WRONG...\n");
+ return FALSE;
+ }
+
+ /* retrieve sps and pps unit(s) */
+ unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
+ GST_LOG ("No. of SPS units = %u\n", unit_nb);
+
+ if (!unit_nb)
+ {
+ GST_ERROR ("SPS is not present....\n");
+ return FALSE;
+ }
+
+ while (unit_nb--)
+ {
+ /* get SPS/PPS data Length*/
+ unit_size = PIFFDEMUX_RB16(extradata);
+
+ GST_LOG ("SPS size = %d", unit_size);
+
+ /* Extra 4 bytes for adding size of the packet */
+ total_size += unit_size + h264_nal_length_size;
+
+ /* Check if SPS/PPS Data Length crossed buffer Length */
+ if ((extradata + 2 + unit_size) > (dci_meta_buf + dci_meta_size))
+ {
+ GST_ERROR ("SPS Length is wrong in DCI...\n");
+ if (out)
+ {
+ free (out);
+ }
+ return FALSE;
+ }
+ out = realloc(out, total_size);
+ if (!out)
+ {
+ GST_ERROR ("realloc FAILED...\n");
+ return FALSE;
+ }
+ /* Copy length of SPS header */
+ // tmp = (unsigned int *)(out + total_size - unit_size - h264_nal_length_size);
+ // *tmp = unit_size;
+ (out + total_size - unit_size - h264_nal_length_size)[0] = 0;
+ (out + total_size - unit_size - h264_nal_length_size)[1] = 0;
+ (out + total_size - unit_size - h264_nal_length_size)[2] = 0;
+ (out + total_size - unit_size - h264_nal_length_size)[3] = (unsigned char)unit_size;
+
+ // memcpy(out + total_size - unit_size - h264_nal_length_size, &unit_size, h264_nal_length_size);
+ //g_print ("out[0] = %02x, out[1] = %02x, out[2] = %02x = out[3] = %02x\n",
+ //out[total_size - unit_size - h264_nal_length_size], out[total_size - unit_size - h264_nal_length_size+1],
+ //out[total_size - unit_size - h264_nal_length_size + 2], out[total_size - unit_size - h264_nal_length_size + 3]);
+
+ /* Copy SPS/PPS Length and data */
+ memcpy(out + total_size - unit_size, extradata + PIFFDEMUX_SPSPPS_LENGTH_SIZE, unit_size);
+
+ extradata += (PIFFDEMUX_SPSPPS_LENGTH_SIZE + unit_size);
+
+ if (!unit_nb && !sps_done++)
+ {
+ /* Completed reading SPS data, now read PPS data */
+ unit_nb = *extradata++; /* number of pps unit(s) */
+ GST_DEBUG ("No. of PPS units = %d\n", unit_nb);
+ }
+ }
+
+ *dci_3gpp_buf = malloc (total_size);
+ if (NULL == *dci_3gpp_buf)
+ {
+ GST_ERROR ("Memory Allocation FAILED...\n");
+ free (out);
+ return FALSE;
+ }
+
+ memcpy(*dci_3gpp_buf, out, total_size);
+ *dci_3gpp_size = total_size;
+
+ GST_DEBUG ("SPS_PPS size = %d\n", total_size);
+
+ free(out);
+
+ return TRUE;
+ }
+
+#ifdef DRM_ENABLE
+static void
+piffdemux_get_playready_licence (GstPiffDemux *demux)
+{
+ int ret = -1;
+ drm_trusted_piff_get_license_info_s license_info;
+ drm_trusted_request_type_e request_type = DRM_TRUSTED_REQ_TYPE_PIFF_GET_LICENSE;
+
+ memset(&license_info, 0x00, sizeof(drm_trusted_piff_get_license_info_s));
+
+ license_info.lic_header.header = (unsigned char*) GST_BUFFER_DATA (demux->protection_header);
+ license_info.lic_header.header_len = GST_BUFFER_SIZE (demux->protection_header);
+
+ ret = drm_trusted_handle_request(request_type, (void *) &license_info, NULL);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (demux,"failed to get license...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to get license"), (NULL));
+ return;
+ }
+
+ GST_INFO_OBJECT (demux, "Got license....\n");
+
+ demux->encrypt_content = TRUE;
+
+ return;
+}
+
+void
+test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data)
+{
+ g_print ("Callback Hit:test_drm_trusted_operation_cb\n");
+ g_print ("operation_status=%d\n",operation_info->operation_status);
+ g_print ("operation_type=%d\n",operation_info->operation_type);
+}
+#endif
+
--- /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_PIFFDEMUX_H__
+#define __GST_PIFFDEMUX_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include "piffcommon.h"
+
+#ifdef DRM_ENABLE
+#include <drm_client.h>
+#include <drm_trusted_client.h>
+#include <drm_client_types.h>
+#include <drm_trusted_client_types.h>
+#endif
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN (piffdemux_debug);
+#define GST_CAT_DEFAULT piffdemux_debug
+
+#define GST_TYPE_PIFFDEMUX \
+ (gst_piffdemux_get_type())
+#define GST_PIFFDEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIFFDEMUX,GstPiffDemux))
+#define GST_PIFFDEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIFFDEMUX,GstPiffDemuxClass))
+#define GST_IS_PIFFDEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIFFDEMUX))
+#define GST_IS_PIFFDEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIFFDEMUX))
+
+#define GST_PIFFDEMUX_CAST(obj) ((GstPiffDemux *)(obj))
+
+/* piffdemux produces these for atoms it cannot parse */
+#define GST_PIFF_DEMUX_PRIVATE_TAG "private-piff-tag"
+#define GST_PIFF_DEMUX_CLASSIFICATION_TAG "classification"
+
+#define GST_PIFFDEMUX_MAX_STREAMS 8
+
+typedef struct _GstPiffDemux GstPiffDemux;
+typedef struct _GstPiffDemuxClass GstPiffDemuxClass;
+typedef struct _PiffDemuxStream PiffDemuxStream;
+
+struct _GstPiffDemux {
+ GstElement element;
+
+ /* pads */
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ PiffDemuxStream *stream;
+
+ guint32 timescale;
+ gint64 duration;
+
+ gboolean fragmented;
+ guint64 moof_offset;
+ gint state;
+ gboolean posted_redirect;
+
+ /* push based variables */
+ guint neededbytes;
+ guint todrop;
+ GstAdapter *adapter;
+ GstBuffer *mdatbuffer;
+ guint64 mdatleft;
+
+ /* offset of the media data (i.e.: Size of header) */
+ guint64 offset;
+ /* offset of the mdat atom */
+ guint64 mdatoffset;
+ guint64 first_mdat;
+
+ GstTagList *tag_list;
+
+ /* configured playback region */
+ GstSegment segment;
+ gboolean segment_running;
+ GstEvent *pending_newsegment;
+
+ /* gst index support */
+ GstIndex *element_index;
+ gint index_id;
+
+ gint64 requested_seek_time;
+ guint64 seek_offset;
+ gboolean moof_rcvd;
+
+ /* live specific params */
+ piff_live_param_t *param;
+ guint lookahead_cnt;
+ gboolean is_live;
+
+ gboolean encrypt_content;
+ gboolean decrypt_init;
+#ifdef DRM_ENABLE
+ DRM_DECRYPT_HANDLE pr_handle;
+#endif
+ GstBuffer *protection_header;
+};
+
+struct _GstPiffDemuxClass {
+ GstElementClass parent_class;
+ void (*live_param) (GstPiffDemux *piff, const piff_live_param_t *param);
+};
+
+GType gst_piffdemux_get_type (void);
+
+/* prepares video caps based on input params */
+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);
+/* prepares audio caps based on input params */
+void gst_piffdemux_set_audio_params (GstPiffDemux * piffdemux, guint fourcc, guint sampling_rate, guint bps,
+ guint channels, unsigned char *codec_data, unsigned int codec_data_len);
+G_END_DECLS
+
+#endif /* __GST_PIFFDEMUX_H__ */
+
--- /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__ */
PROP_0,
PROP_COOKIES,
PROP_ALLOW_AUDIO_ONLY,
- PROP_FRAGMENTS_CACHE,
+ PROP_CACHE_TIME,
+ PROP_LOW_PERCENTAGE,
+ PROP_HIGH_PERCENTAGE,
PROP_BITRATE_SWITCH_TOLERANCE,
PROP_LAST
};
static void
_do_init (GType type)
{
- GST_DEBUG_CATEGORY_INIT (gst_ss_demux_debug, "ssdemux", 0,
- "ssdemux element");
+ 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_CACHE_TIME 6*GST_SECOND
#define DEFAULT_BITRATE_SWITCH_TOLERANCE 0.4
+#define DEFAULT_LOW_PERCENTAGE 1
+#define DEFAULT_HIGH_PERCENTAGE 99
struct _GstSSDemuxStream
{
GstBus *bus;
GMutex *lock;
GCond *cond;
+ GMutex *queue_lock;
+ GCond *queue_full;
+ GCond *queue_empty;
guint frag_cnt;
GQueue *queue;
gchar *uri;
GstCaps *caps;
guint64 switch_ts;
guint64 avg_dur;
+ gboolean is_buffering;
+ gint64 percent;
+ gboolean rcvd_percent;
+ guint64 push_block_time;
+ gint64 cached_duration;
+
+ /* for fragment download rate calculation */
+ guint64 download_start_ts;
+ guint64 download_stop_ts;
+ guint64 download_size;
+
};
static void gst_ss_demux_set_property (GObject * object, guint prop_id,
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_push_loop (GstSSDemuxStream *stream);
+static void gst_ss_demux_update_buffering (GstSSDemuxStream *stream, guint64 percent);
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_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",
"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_object_class_install_property (gobject_class, PROP_CACHE_TIME,
+ g_param_spec_uint64 ("max-cache-time", "caching time",
+ "amount of data that can be cached in seconds", 0, G_MAXUINT64,
+ DEFAULT_CACHE_TIME,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_LOW_PERCENTAGE,
+ g_param_spec_int ("low-percent", "Low Percent",
+ "Low threshold to start buffering",
+ 1, 100, DEFAULT_LOW_PERCENTAGE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_HIGH_PERCENTAGE,
+ g_param_spec_int ("high-percent", "High percent",
+ "High threshold to complete buffering",
+ 2, 100, DEFAULT_HIGH_PERCENTAGE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BITRATE_SWITCH_TOLERANCE,
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->max_cache_time = DEFAULT_CACHE_TIME;
demux->cookies = NULL;
demux->ss_mode = SS_MODE_NO_SWITCH;
demux->switch_eos = FALSE;
+ demux->allow_audio_only = FALSE;
+ demux->percent = 100;
+ demux->low_percent = DEFAULT_LOW_PERCENTAGE;
+ demux->high_percent = DEFAULT_HIGH_PERCENTAGE;
+ demux->eos = FALSE;
}
static void
for (n = 0; n < SS_STREAM_NUM; n++) {
if (demux->streams[n]) {
+ gst_pad_stop_task ((demux->streams[n])->pad);
+ g_print ("\n\n\nstopped the TASK\n\n\n");
gst_ss_demux_stream_free (demux, demux->streams[n]);
demux->streams[n] = NULL;
}
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);
+ case PROP_CACHE_TIME:
+ demux->max_cache_time = g_value_get_uint64 (value);
+ break;
+ case PROP_LOW_PERCENTAGE:
+ demux->low_percent = g_value_get_int (value);
+ break;
+ case PROP_HIGH_PERCENTAGE:
+ demux->high_percent = g_value_get_int (value);
break;
case PROP_BITRATE_SWITCH_TOLERANCE:
demux->bitrate_switch_tol = g_value_get_float (value);
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);
+ case PROP_CACHE_TIME:
+ g_value_set_uint64 (value, demux->max_cache_time);
+ break;
+ case PROP_LOW_PERCENTAGE:
+ g_value_set_int (value, demux->low_percent);
+ break;
+ case PROP_HIGH_PERCENTAGE:
+ g_value_set_int (value, demux->high_percent);
break;
case PROP_BITRATE_SWITCH_TOLERANCE:
g_value_set_float (value, demux->bitrate_switch_tol);
gst_ss_demux_sink_event (GstPad * pad, GstEvent * event)
{
GstSSDemux *demux = GST_SS_DEMUX (gst_pad_get_parent (pad));
- GstQuery *query;
+ GstQuery *query = NULL;
gboolean ret;
gchar *uri;
case GST_EVENT_EOS: {
int i = 0;
if (demux->manifest == NULL) {
- GST_WARNING_OBJECT (demux, "Received EOS without a manifest.");
- break;
+ GST_ERROR_OBJECT (demux, "Received EOS without a manifest.");
+ goto error;
}
GST_DEBUG_OBJECT (demux, "Got EOS on the sink pad: mainifest file fetched");
g_free (uri);
} else {
GST_ERROR_OBJECT (demux, "failed to query URI from upstream");
- return FALSE;
+ goto error;
}
gst_query_unref (query);
+ query = NULL;
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;
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), (NULL));
+ goto error;
+ }
+
+ {
+ unsigned char *protection_data = NULL;
+ unsigned int protection_len = 0;
+
+ /* get protection-header from manifest parser */
+ ret = gst_ssm_parse_get_protection_header (demux->parser, &protection_data, &protection_len);
+ if (!ret) {
+ GST_ERROR_OBJECT (demux, "failed to get protection header...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, NO_SPACE_LEFT, ("fragment allocation failed..."), (NULL));
+ goto error;
+ }
+
+ if (protection_data && protection_len) {
+ g_print ("Got the protection header...\n");
+ demux->protection_header = gst_buffer_new ();
+ GST_BUFFER_DATA (demux->protection_header) = GST_BUFFER_MALLOCDATA (demux->protection_header) = protection_data;
+ GST_BUFFER_SIZE (demux->protection_header) = protection_len;
+ }
}
for( i = 0; i < SS_STREAM_NUM; i++) {
// Add pad emission of the stream
gst_ss_demux_stream_init (demux, stream, i);
+
+ if (!gst_pad_is_linked (stream->pad)) {
+ GST_WARNING_OBJECT (demux, "%s - stream pad is not linked...clean up", ssm_parse_get_stream_name(i));
+ gst_ss_demux_stream_free (demux, stream);
+ continue;
+ }
+
+ /* create stream task */
g_static_rec_mutex_init (&stream->stream_lock);
stream->stream_task = gst_task_create ((GstTaskFunction) gst_ss_demux_stream_loop, demux);
+ if (NULL == stream->stream_task) {
+ GST_ERROR_OBJECT (demux, "failed to create stream task...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to create stream task"), (NULL));
+ goto error;
+ }
gst_task_set_lock (stream->stream_task, &stream->stream_lock);
+
+ /* create stream push loop */
+ if (!gst_pad_start_task (stream->pad, (GstTaskFunction) gst_ss_demux_push_loop, stream)) {
+ GST_ERROR_OBJECT (demux, "failed to create push loop...");
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
+ goto error;
+ }
+
demux->streams[i] = stream;
g_print ("Starting stream - %d task loop...\n", i);
gst_task_start (stream->stream_task);
}
gst_event_unref (event);
+ gst_object_unref (demux);
return TRUE;
}
case GST_EVENT_NEWSEGMENT:
/* Swallow newsegments, we'll push our own */
gst_event_unref (event);
+ gst_object_unref (demux);
return TRUE;
default:
break;
}
return gst_pad_event_default (pad, event);
+
+error:
+ // TODO: add closing
+
+ //gst_ss_demux_stop (demux);
+ gst_event_unref (event);
+ gst_object_unref (demux);
+
+ if (query)
+ gst_query_unref (query);
+
+ g_print ("Returning from sink event...\n");
+ return FALSE;
+
}
static gboolean
error:
{
+ GST_ELEMENT_ERROR (demux, RESOURCE, FAILED, ("failed to download fragment"), (NULL));
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 ());
+ demux->eos = TRUE;
gst_ss_demux_stop (demux, stream);
return TRUE;
}
}
static void
+gst_ss_demux_push_loop (GstSSDemuxStream *stream)
+{
+ GstBuffer *outbuf = NULL;
+ GstSSDemux *demux = stream->parent;
+ GstFlowReturn fret = GST_FLOW_OK;
+
+ // TODO: need to take care of EOS handling....
+
+ g_mutex_lock (stream->queue_lock);
+
+ if (g_queue_is_empty (stream->queue)) {
+ GST_DEBUG_OBJECT (stream->pad,"queue is empty wait till, some buffers are available...");
+ if (demux->eos) {
+ GST_INFO_OBJECT (stream->pad, "stream EOS, pause the task");
+ gst_pad_push_event (stream->pad, gst_event_new_eos ());
+ gst_pad_pause_task (stream->pad);
+ g_print ("Paused the task");
+ return;
+ }
+ g_cond_wait (stream->queue_empty, stream->queue_lock);
+ }
+
+ outbuf = g_queue_pop_head (stream->queue);
+
+ if (GST_BUFFER_DURATION_IS_VALID (outbuf)) {
+ stream->cached_duration -= GST_BUFFER_DURATION(outbuf);
+ } else {
+ g_print ("\nDuration field is not valid.. check this issue !!!!!!!!\n");
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid duration of a frame"), (NULL));
+ g_mutex_unlock (stream->queue_lock);
+ return;
+ }
+
+ g_cond_signal (stream->queue_full);
+ //g_print ("[%s] Signalled full condition...\n", ssm_parse_get_stream_name(stream->type));
+ g_mutex_unlock (stream->queue_lock);
+
+ 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; // No need to close task for this, because sometimes pad can unlined
+ }
+ stream->sent_ns = TRUE;
+ }
+
+ if (stream->type == SS_STREAM_VIDEO && demux->ss_mode == SS_MODE_AONLY) {
+ GST_BUFFER_TIMESTAMP (outbuf) = stream->switch_ts;
+ GST_BUFFER_DURATION (outbuf) = ((float)1/25) * GST_SECOND;
+ stream->switch_ts = GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf);
+ g_print ("Dummy buffers ts : %"GST_TIME_FORMAT" and dur : %"GST_TIME_FORMAT"\n",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS(GST_BUFFER_DURATION (outbuf)));
+ gchar *caps_string = gst_caps_to_string(GST_BUFFER_CAPS(outbuf));
+ g_print ("caps : %s\n", caps_string);
+ g_free(caps_string);
+ caps_string = NULL;
+ }
+
+ /* push data to downstream*/
+ fret = gst_pad_push (stream->pad, outbuf);
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (demux, "failed to push data, reason : %s", gst_flow_get_name (fret));
+ goto error;
+ }
+
+ //g_print ("[%s] pushed buffer\n", ssm_parse_get_stream_name(stream->type));
+error:
+ // TODO: need to close task & post error to bus
+ return;
+}
+
+static void
gst_ss_demux_stream_loop (GstSSDemux * demux)
{
GThread *self = 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) {
+ if (demux->streams[stream_type] && 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;
+ if (stream) {
+ /* 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;
gst_ss_demux_download_fragment (GstSSDemux *demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts)
{
GstStateChangeReturn ret;
+ GTimeVal time = {0, };
g_print ("Going to download fragment : %s\n", uri);
if (!gst_ss_demux_create_download_pipe (demux, stream, uri, start_ts)) {
return FALSE;
}
+ /* download rate calculation : note down start time*/
+ g_get_current_time (&time);
+ stream->download_start_ts = (time.tv_sec * 1000000)+ time.tv_usec;
+
ret = gst_element_set_state (stream->pipe, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
GST_ERROR_OBJECT (demux, "set_state failed...");
gst_ss_demux_create_download_pipe (GstSSDemux * demux, GstSSDemuxStream *stream, const gchar * uri, guint64 start_ts)
{
gchar *name = NULL;
- GstCaps *caps = NULL;
+ gchar *caps_string = NULL;
if (!gst_uri_is_valid (uri))
return FALSE;
stream->pipe = gst_pipeline_new (name);
if (!stream->pipe) {
GST_ERROR_OBJECT (demux, "failed to create pipeline");
+ g_free(name);
+ name = NULL;
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");
+ g_free(name);
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);
stream->parser = gst_element_factory_make ("piffdemux", name);
if (!stream->parser) {
GST_ERROR_OBJECT (demux, "failed to create piffdemux element");
+ g_free(name);
+ name = NULL;
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));
+ if (stream->caps)
+ gst_caps_unref (stream->caps);
- g_object_set (G_OBJECT (stream->parser), "caps", caps, NULL);
+ stream->caps = ssm_parse_get_stream_caps (demux->parser, stream->type);
+ caps_string = gst_caps_to_string(stream->caps);
+ GST_INFO_OBJECT (stream->pad, "prepare caps = %s", caps_string);
+ g_free(caps_string);
+ caps_string = NULL;
+
+ g_object_set (G_OBJECT (stream->parser), "caps", stream->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);
+ if (demux->protection_header)
+ g_object_set (G_OBJECT (stream->parser), "protection-header", demux->protection_header, 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");
+ g_free(name);
+ name = NULL;
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...");
gst_bus_add_watch (stream->bus, (GstBusFunc)gst_ss_demux_download_bus_cb, stream);
gst_object_unref (stream->bus);
+ g_free(name);
+ name = NULL;
+
return TRUE;
}
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS: {
- guint64 download_rate = -1;
+ GTimeVal time = {0, };
+ gint idx = 0;
+ guint64 total_push_time = 0;
+ guint64 download_rate = 0;
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);
- }
+ /* download rate calculation : note down start time*/
+ g_get_current_time (&time);
+ stream->download_stop_ts = (time.tv_sec * 1000000)+ time.tv_usec;
- // 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) {
+ download_rate = ((stream->download_size * 8 * 1000000) / (stream->download_stop_ts - stream->download_start_ts - stream->push_block_time));
+ g_print("*********** '%s' download rate = %"G_GUINT64_FORMAT" bpssss **************\n", stream->name, download_rate);
+ stream->download_size = 0;
+ stream->download_stop_ts = stream->download_start_ts = 0;
+ stream->push_block_time = 0;
+
+ if ((stream->type == SS_STREAM_VIDEO) && (demux->ss_mode != SS_MODE_AONLY)) {
+ if (!stream->is_buffering) {
/* for switching, we are considering video download rate only */
demux->ss_mode = gst_ssm_parse_switch_qualitylevel (demux->parser, download_rate);
}
g_print ("Error from %s\n", gst_element_get_name (GST_MESSAGE_SRC(msg)));
- gst_message_parse_error( msg, &error, &debug );
+ 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)) {
+ if (GST_IS_URI_HANDLER(GST_MESSAGE_SRC(msg))) {
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);
+ if (error)
+ 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_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");
+ g_free(debug);
+ debug = NULL;
+
return FALSE;
}
}
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);
+ g_free(debug);
debug = NULL;
- g_error_free( error);
+ g_error_free(error);
return FALSE;
}
gst_ss_demux_stop (demux, stream);
g_error_free( error);
break;
}
+ case GST_MESSAGE_BUFFERING: {
+ int n =0;
+ int total_cache_perc = 0;
+ int active_stream_cnt = 0;
+ GstSSDemuxStream *cur_stream = NULL;
+ int avg_percent = 0;
+
+ /* update buffer percent */
+ gst_message_parse_buffering (msg, &stream->rcvd_percent);
+ gchar *name = gst_element_get_name (GST_MESSAGE_SRC (msg));
+ GST_LOG_OBJECT (stream->pad, "Internal bus : Buffering from %s = %d\n", name, stream->rcvd_percent);
+ g_free(name);
+ name = NULL;
+ // TODO: need to check for better logic
+ for (n = 0; n < SS_STREAM_NUM; n++) {
+ cur_stream = demux->streams[n];
+ if (cur_stream) {
+ active_stream_cnt++;
+ total_cache_perc += cur_stream->rcvd_percent;
+ }
+ }
+
+ avg_percent = total_cache_perc / active_stream_cnt;
+
+ GST_LOG_OBJECT (demux, "avg buffering completed = %d", avg_percent);
+
+ if (avg_percent > 100)
+ avg_percent = 100;
+
+ // TODO: need to add mutex for protecting percent
+ if (avg_percent != demux->percent) {
+ demux->percent = avg_percent;
+ GST_LOG_OBJECT (demux, "#########Posting %d buffering msg to main bus ###########", demux->percent);
+
+ gst_element_post_message (GST_ELEMENT (demux), gst_message_new_buffering (GST_OBJECT (demux), avg_percent));
+ }
+ }
+ break;
case GST_MESSAGE_WARNING: {
char* debug = NULL;
GError* error = NULL;
}
static void
+gst_ss_demux_update_buffering (GstSSDemuxStream *stream, guint64 percent)
+{
+ gboolean do_post = FALSE;
+ GstSSDemux *demux = stream->parent;
+
+ if (stream->is_buffering) {
+ do_post = TRUE;
+ if (percent >= demux->high_percent)
+ stream->is_buffering = FALSE;
+ } else {
+ if (percent < demux->low_percent) {
+ stream->is_buffering = TRUE;
+ do_post = TRUE;
+ }
+ }
+
+ if (do_post) {
+ GstMessage *message;
+ GstBufferingMode mode;
+ gint64 buffering_left = -1;
+
+ percent = percent * 100 / demux->high_percent;
+
+ if (percent > 100)
+ percent = 100;
+
+ if (percent != stream->percent) {
+ stream->percent = percent;
+
+ GST_DEBUG_OBJECT (stream->pad, "buffering %d percent", (gint) percent);
+ g_print ("'%s' buffering %d percent done\n", stream->name, (gint) percent);
+
+ /* posting buffering to internal bus, which will take average & post to main bus */
+ message = gst_message_new_buffering (GST_OBJECT_CAST (stream->sink), (gint) percent);
+ gst_element_post_message (GST_ELEMENT_CAST (stream->sink), message);
+ }
+ }
+
+}
+
+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;
+ GstBuffer *headbuf = NULL;
+ gint64 diff = 0;
+ gint64 percent = 0;
+ GTimeVal start = {0, };
+ GTimeVal stop = {0, };
+ guint64 push_start_time = 0;
+ guint64 push_end_time =0;
inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
if (!inbuf) {
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;
+ g_mutex_lock (stream->queue_lock);
- duration = gst_util_uint64_scale (GST_SSM_PARSE_GET_DURATION(demux->parser), GST_SECOND,
- GST_SSM_PARSE_GET_TIMESCALE(demux->parser));
+ stream->download_size += GST_BUFFER_SIZE(inbuf);
- start = gst_util_uint64_scale (GST_SSM_PARSE_NS_START(demux->parser), GST_SECOND,
- GST_SSM_PARSE_GET_TIMESCALE(demux->parser));
+ /* download rate calculation : note push_start_ts */
+ g_get_current_time (&start);
+ push_start_time = (start.tv_sec * 1000000)+ start.tv_usec;
- event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
- start, duration, start);
+ 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)));
- GST_DEBUG_OBJECT(demux," new_segment start = %"GST_TIME_FORMAT, GST_TIME_ARGS(start));
+ g_queue_push_tail (stream->queue, inbuf);
- if (!gst_pad_push_event (stream->pad, event)) {
- GST_ERROR_OBJECT (demux, "failed to push newsegment event");
- return;
- }
- stream->sent_ns = TRUE;
+ if (GST_BUFFER_DURATION_IS_VALID (inbuf)) {
+ stream->cached_duration += GST_BUFFER_DURATION(inbuf);
+ } else {
+ g_print ("\nDuration field is not valid.. check this issue !!!!!!!!\n");
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid duration of a frame"), (NULL));
+ g_mutex_unlock (stream->queue_lock);
+ return;
}
- while (!g_queue_is_empty(stream->queue)) {
- GstBuffer *cache_buf = g_queue_pop_head (stream->queue);
+ if (stream->cached_duration >= 0) {
+ percent = (stream->cached_duration * 100) / demux->max_cache_time;
+ //g_print ("[%s] percent done = %d[%"G_GINT64_FORMAT"]\n", ssm_parse_get_stream_name(stream->type), percent, percent);
- 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;
+ // TODO: need to decide, whther to call before wait or after ??
+ gst_ss_demux_update_buffering (stream, percent);
+
+ if (percent > 100) {
+ /* update buffering & wait if space is not available */
+ GST_DEBUG_OBJECT (stream->pad, "Reached more than 100 percent, queue full & wait till free");
+ g_cond_wait(stream->queue_full, stream->queue_lock);
+ GST_DEBUG_OBJECT (stream->pad,"Received signal to add more data...");
}
+ } else {
+ g_print ("cached duration can not be negative\n\n\n");
+ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid cached duration"), (NULL));
+ g_mutex_unlock (stream->queue_lock);
+ return;
}
- 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)));
+ /* download rate calculation : note push_stop_ts */
+ g_get_current_time (&stop);
+ push_end_time = (stop.tv_sec * 1000000)+ stop.tv_usec;
- g_print ("caps : %s\n", gst_caps_to_string(GST_BUFFER_CAPS(inbuf)));
- }
+ stream->push_block_time += push_end_time - push_start_time;
- /* 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;
- }
+ g_cond_signal (stream->queue_empty);
-error:
+ g_mutex_unlock (stream->queue_lock);
return;
}
stream->cond = g_cond_new ();
stream->lock = g_mutex_new ();
stream->queue = g_queue_new ();
+ stream->queue_full = g_cond_new ();
+ stream->queue_empty = g_cond_new ();
+ stream->queue_lock = g_mutex_new ();
stream->parent = demux;
stream->pipe = NULL;
stream->urisrc = NULL;
stream->sent_ns = FALSE;
stream->switch_ts = GST_CLOCK_TIME_NONE;
stream->avg_dur = GST_CLOCK_TIME_NONE;
+ stream->percent = 100;
+ stream->rcvd_percent = 0;
+ stream->push_block_time = 0;
+ stream->cached_duration = 0;
+ stream->download_start_ts = 0;
+ stream->download_stop_ts = 0;
+ stream->download_size = 0;
if (stream->type == SS_STREAM_VIDEO) {
stream->pad = gst_pad_new_from_static_template (&ssdemux_videosrc_template, "video");
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));
+ gchar *caps_name = gst_caps_to_string(stream->caps);
+ g_print ("prepare video caps = %s", caps_name);
+ g_free(caps_name);
GST_DEBUG_OBJECT (demux, "setting caps %" GST_PTR_FORMAT, stream->caps);
gst_pad_set_caps (stream->pad, stream->caps);
g_mutex_free (stream->lock);
stream->lock = NULL;
}
-
+ if (stream->queue_lock) {
+ g_mutex_free (stream->queue_lock);
+ stream->queue_lock = NULL;
+ }
+ if (stream->queue_full) {
+ g_cond_free (stream->queue_full);
+ stream->queue_full = NULL;
+ }
+ if (stream->queue_empty) {
+ g_cond_free (stream->queue_empty);
+ stream->queue_empty= NULL;
+ }
g_free (stream);
}
static gboolean
/* 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
+ guint64 max_cache_time;\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
+ gint percent;\r
+ gint low_percent;\r
+ gint high_percent;\r
+ gboolean eos;\r
GstSSDemuxStream *streams[SS_STREAM_NUM];\r
SS_BW_MODE ss_mode;\r
gboolean switch_eos;\r
+ GstBuffer *protection_header;\r
};\r
\r
struct _GstSSDemuxClass\r
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));
+ g_print ("Error in strtol : %s\n", g_strerror(errno));
return FALSE;
}
}
return prop_uint64;
-}
+}
static gint
ssm_parser_sort_qualitylevels_by_bitrate (gconstpointer a, gconstpointer b)
static gboolean
+gst_ssm_parse_confirm_audiotag(gchar ** fourcc, gint audio_tag)
+{
+ switch(audio_tag){
+ case 255:
+ *fourcc = (gchar *)strdup("AACL");
+ break;
+ case 353:
+ *fourcc = (gchar *)strdup("WMAP");
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
ssm_parse_quality_node (GstSSMParse *parser, GstSSMStreamNode *stream, xmlNodePtr quality_node)
{
GstSSMQualityNode *quality_level = NULL;
/* 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){
+ if(!gst_ssm_parse_confirm_audiotag(&quality_level->fourcc, quality_level->audio_tag)){
+ GST_ERROR ("failed to set fourcc from audio tag %d",quality_level->audio_tag);
+ return FALSE;
+ }
+ }
+
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) ||
+ if (quality_level->fourcc && !((!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;
- }
+ free (quality_level->fourcc);
+
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) ||
NULL);
}
- GST_INFO ( "prepared video caps : %s\n", gst_caps_to_string(caps));
+ gchar *caps_name = gst_caps_to_string(caps);
+ GST_INFO ( "prepared video caps : %s\n", caps_name);
+ g_free(caps_name);
- return caps;
+ return caps;
}
GstCaps *
GstBuffer *codec_data = NULL;
GstCaps *caps = NULL;
- if ((!strncmp ((char *)cur_quality_node->fourcc, "AACL", 4)) || !strncmp ((char *)cur_quality_node->fourcc, "WMAP", 4)) {
+ if (cur_quality_node->codec_data &&
+ ((!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 *dci = cur_quality_node->codec_data;
gchar tmp[3] = {0, };
gint val = 0;
gint codec_data_len = (DCI_len >>1);
/* copy codec data */
while (DCI_len) {
memset (tmp, 0x00, 3);
- strncpy ((char*)tmp, (char*)cur_quality_node->codec_data, 2);
+ strncpy ((char*)tmp, (char*)dci, 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 += 2;
DCI_len = DCI_len - 2;
//g_print ("val = 0x%02x, codec_data_length = %d, idx = %d\n", val, DCI_len, idx);
idx++;
"stream-format", G_TYPE_STRING, "raw",
"rate", G_TYPE_INT, (int) cur_quality_node->samplingrate,
"channels", G_TYPE_INT, cur_quality_node->channels,
+ "codec_data", GST_TYPE_BUFFER, codec_data,
NULL);
} else if (!strncmp ((char *)cur_quality_node->fourcc, "WMAP", 4)) {
caps = gst_caps_new_simple ("audio/x-wma",
"channels", G_TYPE_INT, cur_quality_node->channels,
NULL);
}
- GST_INFO ( "prepared video caps : %s\n", gst_caps_to_string(caps));
+ gchar *caps_name = gst_caps_to_string(caps);
+ GST_INFO ( "prepared video caps : %s\n", caps_name);
+ g_free(caps_name);
return caps;
}
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;
+ bitrate = ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate;
} else {
g_print ("Already at MAX Bitrate possible...\n");
ret = SS_MODE_NO_SWITCH;
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);
+ bitrate = ((GstSSMQualityNode *)stream->quality_lists->data)->bitrate;
} else {
g_print ("Reached MIN video bitrate possible...\n");
if (GST_SSM_PARSE_IS_LIVE_PRESENTATION(parser)) {
/* 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,
+ if(stream->fragment_lists && stream->fragment_lists->data) {
+ 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_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;
+ if(stream->fragment_lists && stream->fragment_lists->data)
+ 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,
+ if(stream->fragment_lists && stream->fragment_lists->data) {
+ 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));
+ 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;
+ }
}
- start_ts = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
} else {
/* rare case */
start_ts = ((GstSSMFragmentNode *)stream->fragment_lists->data)->time;
return TRUE;
}
+gboolean
+gst_ssm_parse_get_protection_header (GstSSMParse *parser, unsigned char **protection_header, unsigned int *protection_header_len)
+{
+ if (!parser->RootNode->ProtectNode) {
+ *protection_header = NULL;
+ *protection_header_len = 0;
+ return TRUE;
+ }
+
+ if (parser->RootNode->ProtectNode->ContentSize && parser->RootNode->ProtectNode->Content) {
+ *protection_header = g_malloc0 (parser->RootNode->ProtectNode->ContentSize);
+ if (*protection_header == NULL) {
+ GST_ERROR ("failed to allocate memory...");
+ return FALSE;
+ }
+
+ memcpy (*protection_header, parser->RootNode->ProtectNode->Content, parser->RootNode->ProtectNode->ContentSize);
+ *protection_header_len = parser->RootNode->ProtectNode->ContentSize;
+ }
+
+ return TRUE;
+}
+
static gboolean
convert_NALUnitDCI_to_PacktizedDCI (unsigned char *nalu_dci, unsigned char **packetized_dci, unsigned int *packetized_dci_len)
{
#endif
}
+
+
+
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);
+SS_BW_MODE gst_ssm_parse_switch_qualitylevel (GstSSMParse *parser, guint drate);
gboolean gst_ssm_parse_seek_manifest (GstSSMParse *parser, guint64 seek_time);
+gboolean gst_ssm_parse_get_protection_header (GstSSMParse *parser, unsigned char **protection_header, unsigned int *protection_header_len);
G_END_DECLS
#endif /* __SS_MANIFEST_PARSE_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 = libgstsubmux.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
+libgstsubmux_la_SOURCES = gstsubmux.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstsubmux_la_CFLAGS = $(GST_CFLAGS) $(DRM_CLIENT_CFLAGS) $(DRM_TRUSTED_CFLAGS) $(MMTA_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+libgstsubmux_la_LIBADD = $(GST_LIBS) $(DRM_CLIENT_LIBS)$(DRM_TRUSTED_LIBS) $(GST_BASE_LIBS) $(MMTA_LIBS)$(GST_APP_LIBS) -lgstapp-0.10 $(CRYPTO_LIBS)
+libgstsubmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gstsubmux.h
+
--- /dev/null
+/*
+ * SLP2.0
+ * Copyright (c) 2011 Samsung Electronics, Inc.
+ * All rights reserved.
+ *
+ * This software is a confidential and proprietary information
+ * of Samsung Electronics, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Samsung Electronics.
+ */
+
+/*
+ * @file gstsubmux.c
+ * @author Deepak Singh (deep.singh@samsung.com)
+ * @version 1.0
+ * @brief This source code implements the gstreamer plugin for subtitle muxing requirement in media player.
+ *
+ */
+
+/*! Revision History:
+ *! ---------------------------------------------------------------------------
+ *! DATE | AUTHOR | COMMENTS
+ *! ---------------------------------------------------------------------------
+ *! 1-4-2014 deep.singh@samsung.com created.
+ */
+
+#include "config.h"
+#include "gstsubmux.h"
+#include <gst/base/gstadapter.h>
+#include <glib/gstdio.h>
+#include <gst/gst.h>
+#include <gst/app/gstappsink.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <gst/tag/tag.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <gst/gst.h>
+static const GstElementDetails gst_submux_plugin_details = GST_ELEMENT_DETAILS(
+ "submux",
+ "Codec/Parser/Subtitle",
+ "muxing of different subtitle stream",
+ "Samsung Electronics <www.samsung.com>"
+);
+static GstStaticPadTemplate gst_submux_sink_template = GST_STATIC_PAD_TEMPLATE(
+ "sink%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS("video/x-dvd-subpicture; text/plain; application/x-subtitle; "
+ "text/x-pango-markup;"
+ "application/x-usf; subpicture/x-pgs; subtitle/x-kate; application/x-subtitle; "
+ "application/x-subtitle-sami; application/x-subtitle-tmplayer; "
+ "application/x-subtitle-mpl2; application/x-subtitle-dks; "
+ "application/x-subtitle-qttext")
+);
+static GstStaticPadTemplate gst_submux_src_template = GST_STATIC_PAD_TEMPLATE(
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("text/plain; text/x-pango-markup")
+);
+enum
+{
+ PROP_0,
+ PROP_ENCODING,
+ PROP_VIDEOFPS,
+ PROP_EXTSUB_CURRENT_LANGUAGE,
+ PROP_IS_INTERNAL,
+ PROP_LANG_LIST
+
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_submux_debug);
+#define GST_CAT_DEFAULT gst_submux_debug
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_submux_debug, "submux", 0, "submux");
+////////////////////////////////////////////////////////
+// Gstreamer Base Prototype //
+////////////////////////////////////////////////////////
+
+GST_BOILERPLATE_FULL(Gstsubmux, gst_submux, GstElement, GST_TYPE_ELEMENT, _do_init);
+
+#define GST_SUBMUX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SUBMUX, GstsubmuxPrivate))
+#define MAX_LANGUAGE 10
+static gint gst_submux_buffer_list_sorting (gconstpointer a, gconstpointer b);
+static gboolean gst_submux_create_pipelines(Gstsubmux *self,GstPad * pad);
+static GstPad* gst_submux_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * req_name);
+static void gst_submux_release_pad (GstElement * element, GstPad * pad);
+static gchar* gst_submux_extract_data (Gstsubmux *submux);
+static gboolean gst_create_own_language_list (Gstsubmux *submux) ;
+static GstSubMuxFormat gst_submux_data_format_autodetect (gchar * match_str);
+static gpointer gst_submux_data_format_autodetect_regex_once (GstSubMuxRegex regtype);
+static gboolean gst_submux_format_autodetect (Gstsubmux * self);
+static void gst_submux_base_init(gpointer klass);
+static void gst_submux_class_init(GstsubmuxClass *klass);
+static GstStateChangeReturn gst_submux_change_state (GstElement * element, GstStateChange transition);
+static void gst_submux_init(Gstsubmux *submux, GstsubmuxClass *klass);
+static gboolean gst_submux_setcaps(GstPad *pad, GstCaps *caps);
+static GstFlowReturn gst_submux_chain (GstPad *pad, GstBuffer *buffer);
+static void gst_submux_dispose(GObject *object);
+static void gst_submux_loop (Gstsubmux * submux);
+static gboolean gst_submux_stream_init(GstSubmuxStream * stream);
+static void gst_submux_stream_deinit(GstSubmuxStream * stream,Gstsubmux * submux);
+static void gst_submux_on_new_buffer (GstElement *appsink, void *data);
+static gboolean gst_submux_handle_src_event (GstPad * pad, GstEvent * event);
+
+static gboolean gst_submux_handle_sink_event (GstPad * pad, GstEvent * event);
+////////////////////////////////////////////////////////
+// Plugin Utility Prototype //
+////////////////////////////////////////////////////////
+static void gst_submux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_submux_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static gboolean gst_submux_deinit_private_values(Gstsubmux *submux);
+static gchar *convert_to_utf8 (const gchar * str, gsize len, const gchar * encoding,
+ gsize * consumed, GError ** err, Gstsubmux * self);
+#define DEFAULT_ENCODING NULL
+#define DEFAULT_CURRENT_LANGUAGE NULL
+
+////////////////////////////////////////////////////////
+// Gstreamer Base Functions //
+////////////////////////////////////////////////////////
+
+/*
+**
+** Description : base init
+** Params : (1) instance of gclass
+** return : none
+** Comments : The following code registers templates for src and sink pad.
+**
+*/
+static void
+gst_submux_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_submux_sink_template));
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_submux_src_template));
+ gst_element_class_set_details(element_class, &gst_submux_plugin_details);
+}
+
+/*
+**
+** Description : Initilizes the Gstsubmux's class
+** Params : @ klass instance of submux plugin's class
+** return : None
+** Comments : Declaring properties and over-writing function pointers
+**
+*/
+static void
+gst_submux_class_init(GstsubmuxClass *klass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+ g_type_class_add_private (klass, sizeof (GstsubmuxPrivate));
+ gobject_class->set_property = gst_submux_set_property;
+ gobject_class->get_property = gst_submux_get_property;
+ g_object_class_install_property (gobject_class, PROP_ENCODING,
+ g_param_spec_string ("subtitle-encoding", "subtitle charset encoding",
+ "Encoding to assume if input subtitles are not in UTF-8 or any other "
+ "Unicode encoding. If not set, the GST_SUBTITLE_ENCODING environment "
+ "variable will be checked for an encoding to use. If that is not set "
+ "either, ISO-8859-15 will be assumed.", DEFAULT_ENCODING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_VIDEOFPS,
+ gst_param_spec_fraction ("video-fps", "Video framerate",
+ "Framerate of the video stream. This is needed by some subtitle "
+ "formats to synchronize subtitles and video properly. If not set "
+ "and the subtitle format requires it subtitles may be out of sync.",
+ 0, 1, G_MAXINT, 1, 24000, 1001,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_EXTSUB_CURRENT_LANGUAGE,
+ g_param_spec_string ("current-language", "Current language",
+ "Current language of the subtitle in external subtitle case.",
+ DEFAULT_CURRENT_LANGUAGE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_IS_INTERNAL,
+ g_param_spec_boolean ("is-internal", "is internal",
+ "TRUE for internal subtitle case",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_LANG_LIST,
+ g_param_spec_pointer ("lang-list", "language list", "List of languages selected/not selected",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ parent_class = g_type_class_peek_parent (klass);
+ gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_submux_request_new_pad);
+ gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_submux_dispose);
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_submux_change_state);
+ gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_submux_release_pad);
+}
+
+/*
+**
+** Description : Initilizes the submux element
+** Params : (1)instance of submux (2) instance of submux class
+** return : None
+** Comments : instantiate pads and add them to element, set pad calback functions
+**
+*/
+static void
+gst_submux_init(Gstsubmux *submux, GstsubmuxClass *klass)
+{
+ GST_DEBUG_OBJECT (submux, "Entering in init");
+ submux->priv = GST_SUBMUX_GET_PRIVATE(submux);
+ submux->srcpad = gst_pad_new_from_static_template(&gst_submux_src_template, "src");
+ gst_pad_set_event_function (submux->srcpad,
+ GST_DEBUG_FUNCPTR (gst_submux_handle_src_event));
+ submux->priv->first_buffer = FALSE;
+ gst_segment_init (&submux->segment, GST_FORMAT_TIME);
+ submux->flushing = FALSE;
+ submux->msl_streams = NULL;
+ submux->stop_loop = FALSE;
+ submux->need_segment = TRUE;
+ submux->pipeline_made = FALSE;
+ submux->external_sinkpad = FALSE;
+ submux->detected_encoding = NULL;
+ submux->encoding = NULL;
+ submux->seek_came = FALSE;
+ submux->sinkpads_count = 0;
+ submux->langlist_msg_posted = FALSE;
+ submux->cur_buf_array = NULL;
+ GST_DEBUG_OBJECT (submux, "Making flushing FALSE");
+ submux->priv->is_internal = FALSE;
+ submux->external_filepath = NULL;
+ gst_element_add_pad (GST_ELEMENT (submux), submux->srcpad);
+ GST_DEBUG_OBJECT (submux, "Exiting in init");
+}
+
+/*
+**
+** Description : for setting the property of submux
+** return : None
+** Comments : To set the various properties of submux
+**
+*/
+static void
+gst_submux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ Gstsubmux *submux = GST_SUBMUX (object);
+ guint length = 0;
+ gint i = 0;
+ GstLangStruct *cur_language=NULL;
+ GstSubmuxStream *cur_stream = NULL;
+ GST_OBJECT_LOCK (submux);
+ length = g_list_length(submux->priv->lang_list);
+ switch (prop_id) {
+ case PROP_ENCODING:
+ g_free (submux->encoding);
+ submux->encoding = g_value_dup_string (value);
+ GST_DEBUG_OBJECT (submux, "subtitle encoding set to %s",
+ GST_STR_NULL (submux->encoding));
+ for(i = 0;i < length;i++) {
+ cur_stream = g_list_nth_data(submux->streams,i);
+ GST_DEBUG_OBJECT (submux, "setting the subtitle-encoding to %s", submux->encoding);
+ g_object_set (G_OBJECT (cur_stream->pipe_struc.parser), "subtitle-encoding", submux->encoding, NULL);
+ }
+ break;
+ case PROP_VIDEOFPS:
+ {
+ submux->fps_n = gst_value_get_fraction_numerator (value);
+ submux->fps_d = gst_value_get_fraction_denominator (value);
+ GST_DEBUG_OBJECT (submux, "video framerate set to %d/%d", submux->fps_n, submux->fps_d);
+ break;
+ }
+ case PROP_EXTSUB_CURRENT_LANGUAGE: {
+ for (i = 0; i < length; i++) {
+ cur_stream = g_list_nth_data(submux->streams, i);
+ cur_language = g_list_nth_data(submux->priv->lang_list, i);
+ GST_DEBUG_OBJECT (submux, "value of current-language key is %s", cur_language->language_key);
+ g_object_set (G_OBJECT (cur_stream->pipe_struc.parser), "current-language",
+ cur_language->language_key, NULL);
+ }
+ gchar *dup = g_value_dup_string (value);
+ GST_DEBUG_OBJECT (submux, "Setting property to %s", dup);
+ g_free(dup);
+ }
+ break;
+ case PROP_IS_INTERNAL: {
+ submux->priv->is_internal = g_value_get_boolean (value);
+ GST_DEBUG_OBJECT (submux, "Setting the is_internal prop to %d", submux->priv->is_internal);
+ break;
+ }
+ case PROP_LANG_LIST: {
+ submux->priv->lang_list = (GList*) g_value_get_pointer (value);
+ GST_DEBUG_OBJECT (submux, "updating the languages list and length is %d", g_list_length (submux->priv->lang_list));
+ submux->msl_streams = g_list_copy (submux->priv->lang_list);
+
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (submux);
+}
+
+/*
+**
+** Description : for getting the property of submux
+** return : None
+** Comments : To get the various properties of submux in case called by MSL
+**
+*/
+static void
+gst_submux_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ Gstsubmux *submux = GST_SUBMUX (object);
+
+ GST_OBJECT_LOCK (submux);
+ switch (prop_id) {
+ case PROP_ENCODING:
+ g_value_set_string (value, submux->encoding);
+ break;
+ case PROP_VIDEOFPS:
+ gst_value_set_fraction (value, submux->fps_n, submux->fps_d);
+ break;
+ case PROP_EXTSUB_CURRENT_LANGUAGE:
+ GST_DEBUG_OBJECT (submux, "Getting the current language");
+ break;
+ case PROP_IS_INTERNAL: {
+ g_value_set_boolean(value,submux->priv->is_internal);
+ break;
+ }
+ case PROP_LANG_LIST: {
+ g_value_set_pointer(value,(gpointer)(submux->priv->lang_list));
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (submux);
+}
+
+static void
+gst_submux_dispose (GObject *object)
+{
+ Gstsubmux *submux = GST_SUBMUX(object);
+ int i = 0;
+ gchar *pad_name = gst_pad_get_name (submux->srcpad);
+ if (submux && GST_PAD_TASK(submux->srcpad)) {
+ GST_INFO_OBJECT (submux, "Stopping pad task : %s", pad_name);
+ GST_DEBUG_OBJECT (submux, "Stopping pad task : on src pad %p", submux->srcpad);
+ gst_pad_stop_task (submux->srcpad);
+ GST_INFO_OBJECT (submux, "stopped pad task : %s", pad_name);
+ }
+ g_free(pad_name);
+ if (submux->srcpad) {
+ gst_element_remove_pad (GST_ELEMENT_CAST (submux), submux->srcpad);
+ submux->srcpad = NULL;
+ }
+ if (submux->priv->is_internal) {
+ for (i = 0; i < (submux->sinkpads_count); i++){
+ gst_submux_stream_deinit (g_list_nth_data (submux->streams, i), submux);
+ }
+ } else {
+ for (i = 0; i < g_list_length (submux->priv->lang_list); i++) {
+ gst_submux_stream_deinit (g_list_nth_data (submux->streams, i), submux);
+ }
+ }
+ gst_submux_deinit_private_values (submux);
+
+ GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
+ GST_DEBUG_OBJECT (submux, "Returning from finalize");
+}
+
+static void
+gst_submux_stop (Gstsubmux* submux)
+{
+ GstSubmuxStream *new_stream = NULL;
+ guint i = 0;
+ submux->stop_loop = TRUE;
+ GST_INFO_OBJECT (submux, "stopping the loop");
+ if (submux->priv->is_internal) {
+ for (i = 0; i < (submux->sinkpads_count); i++) {
+ new_stream = g_list_nth_data (submux->streams, i);
+ if (new_stream) {
+ g_mutex_lock (new_stream->queue_lock);
+ g_cond_signal (new_stream->queue_empty);
+ g_mutex_unlock (new_stream->queue_lock);
+ }
+ }
+ } else {
+ for (i = 0; i < g_list_length (submux->priv->lang_list); i++) {
+ new_stream = g_list_nth_data (submux->streams, i);
+ if (new_stream) {
+ g_mutex_lock (new_stream->queue_lock);
+ g_cond_signal (new_stream->queue_empty);
+ g_mutex_unlock (new_stream->queue_lock);
+ }
+ }
+ }
+}
+
+static GstStateChangeReturn
+gst_submux_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ Gstsubmux *submux = GST_SUBMUX (element);
+ gboolean bret = FALSE;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ GST_INFO_OBJECT (submux,"PAUSED->PLAYING");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_INFO_OBJECT (submux,"PAUSED->READY");
+ gst_submux_stop (submux);
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_INFO_OBJECT (submux,"PLAYING->PAUSED");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_INFO_OBJECT (submux,"PAUSED->READY");
+ if(submux->msl_streams) {
+ g_list_free(submux->msl_streams);
+ submux->msl_streams = NULL;
+ }
+ if (submux->priv->lang_list && !submux->priv->is_internal) {
+ g_list_free (submux->priv->lang_list);
+ submux->priv->lang_list = NULL;
+ }
+ if (submux->external_filepath) {
+ g_free (submux->external_filepath);
+ submux->external_filepath = NULL;
+ }
+ GST_WARNING_OBJECT(submux,"stopping has been called ...Moved after change_state");
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_INFO_OBJECT (submux,"READY->NULL");
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/*
+**
+** Description : Setting the caps on sink pad based on upstream element's src pad
+** Params : (1) GstPad to set the capabilities of
+** (2) caps to be set
+** return : TRUE on success
+** Comments : this function handles the link with other elements
+**
+*/
+static gboolean
+gst_submux_setcaps (GstPad *pad, GstCaps *caps)
+{
+ return TRUE;
+}
+
+/*extracting data for file format detection*/
+static gchar* gst_submux_extract_data (Gstsubmux *submux){
+ gchar * file_path_type = NULL;
+ gchar * file_path = NULL;
+ gchar * temp_path = NULL;
+ gchar *line = NULL;
+ gboolean is_converted = FALSE;
+ gchar *converted = NULL;
+ FILE * fp = NULL;
+ guint charCount = 0;
+ GError *err = NULL;
+ gsize * consumed = NULL;
+
+ GstQuery *cquery;
+ GstStructure *structure;
+ const GValue *value;
+ GstPad *sinkpad = (GstPad *)g_list_nth_data (submux->sinkpad, 0);
+ structure = gst_structure_new ("FileSrcURI",
+ "file-uri", G_TYPE_STRING, NULL, NULL);
+
+ cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
+
+ if (!gst_pad_peer_query (sinkpad, cquery))
+ {
+ GST_ERROR_OBJECT (submux, "Failed to query SMI file path");
+ gst_query_unref (cquery);
+ return NULL;
+ }
+ structure = gst_query_get_structure (cquery);
+ value = gst_structure_get_value (structure, "file-uri");
+ file_path = g_strdup (g_value_get_string (value));
+
+ if (file_path == NULL){
+ GST_ERROR_OBJECT (submux, "Could not parse the SMI file path");
+ gst_query_unref (cquery);
+ return NULL;
+ }
+
+ gst_query_unref (cquery);
+ temp_path = file_path;
+ GST_INFO_OBJECT (submux, "File path comes as %s", file_path);
+
+ file_path_type = g_strndup ((gchar *) file_path, 4);
+ GST_INFO_OBJECT (submux, "Received file path by query = %s, %s", file_path, file_path_type);
+ if (!g_strcmp0(file_path_type, "file")){
+ file_path += 7;
+ GST_INFO_OBJECT (submux, "File path comes as %s", file_path);
+
+ fp = fopen (file_path, "r");
+ if (!fp){
+ GST_ERROR_OBJECT (submux, "Failed to open file");
+ g_free(file_path_type);
+ g_free(temp_path);
+ return NULL;
+ }
+ } else {
+ GST_ERROR_OBJECT (submux, "File is not local");
+ g_free(file_path_type);
+ g_free(temp_path);
+ return NULL;
+ }
+ line = (gchar*)g_malloc (2049);
+ charCount = fread (line, sizeof(char), 2048, fp);
+ line[charCount] = '\0';
+ if (submux->encoding && strcmp (submux->encoding, "UTF-8"))
+ converted = convert_to_utf8 (line, charCount, submux->encoding, consumed, &err, submux);
+
+ if (converted)
+ {
+ GST_INFO("returned from conversion and length of converted string is[%d]", strlen(converted));
+ is_converted = TRUE;
+ }
+ if (!charCount) {
+ GST_WARNING_OBJECT (submux, "fread returned zero bytes");
+ fclose (fp);
+ g_free(file_path_type);
+ g_free(temp_path);
+ if(is_converted) {
+ g_free(converted);
+ }
+ g_free(line);
+ return NULL;
+ }
+ g_free(file_path_type);
+ g_free(temp_path);
+ fclose (fp);
+ if(is_converted) {
+ return converted;
+ }
+ return line;
+}
+
+static gpointer
+gst_submux_data_format_autodetect_regex_once (GstSubMuxRegex regtype)
+{
+ gpointer result = NULL;
+ GError *gerr = NULL;
+ switch (regtype) {
+ case GST_SUB_PARSE_REGEX_MDVDSUB:
+ result =
+ (gpointer) g_regex_new ("^\\{[0-9]+\\}\\{[0-9]+\\}",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
+ if (result == NULL) {
+ g_warning ("Compilation of mdvd regex failed: %s", gerr->message);
+ g_error_free (gerr);
+ }
+ break;
+ case GST_SUB_PARSE_REGEX_SUBRIP:
+ result = (gpointer) g_regex_new ("^ {0,3}[ 0-9]{1,4}\\s*(\x0d)?\x0a"
+ " ?[0-9]{1,2}: ?[0-9]{1,2}: ?[0-9]{1,2}[,.] {0,2}[0-9]{1,3}"
+ " +--> +[0-9]{1,2}: ?[0-9]{1,2}: ?[0-9]{1,2}[,.] {0,2}[0-9]{1,2}",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
+ if (result == NULL) {
+ g_warning ("Compilation of subrip regex failed: %s", gerr->message);
+ g_error_free (gerr);
+ }
+ break;
+ case GST_SUB_PARSE_REGEX_DKS:
+ result = (gpointer) g_regex_new ("^\\[[0-9]+:[0-9]+:[0-9]+\\].*",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
+ if (result == NULL) {
+ g_warning ("Compilation of dks regex failed: %s", gerr->message);
+ g_error_free (gerr);
+ }
+ break;
+ default:
+ GST_WARNING ("Trying to allocate regex of unknown type %u", regtype);
+ }
+ return result;
+}
+
+static GstSubMuxFormat
+gst_submux_data_format_autodetect (gchar * match_str)
+{
+ guint n1, n2, n3;
+
+ static GOnce mdvd_rx_once = G_ONCE_INIT;
+ static GOnce subrip_rx_once = G_ONCE_INIT;
+ static GOnce dks_rx_once = G_ONCE_INIT;
+
+ GRegex *mdvd_grx;
+ GRegex *subrip_grx;
+ GRegex *dks_grx;
+
+ g_once (&mdvd_rx_once,
+ (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
+ (gpointer) GST_SUB_PARSE_REGEX_MDVDSUB);
+ g_once (&subrip_rx_once,
+ (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
+ (gpointer) GST_SUB_PARSE_REGEX_SUBRIP);
+ g_once (&dks_rx_once,
+ (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
+ (gpointer) GST_SUB_PARSE_REGEX_DKS);
+
+ mdvd_grx = (GRegex *) mdvd_rx_once.retval;
+ subrip_grx = (GRegex *) subrip_rx_once.retval;
+ dks_grx = (GRegex *) dks_rx_once.retval;
+
+ if (g_regex_match (mdvd_grx, match_str, 0, NULL) == TRUE) {
+ GST_LOG ("MicroDVD (frame based) format detected");
+ return GST_SUB_PARSE_FORMAT_MDVDSUB;
+ }
+ if (g_regex_match (subrip_grx, match_str, 0, NULL) == TRUE) {
+ GST_LOG ("SubRip (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_SUBRIP;
+ }
+ if (g_regex_match (dks_grx, match_str, 0, NULL) == TRUE) {
+ GST_LOG ("DKS (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_DKS;
+ }
+
+ if (!strncmp (match_str, "FORMAT=TIME", 11)) {
+ GST_LOG ("MPSub (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_MPSUB;
+ }
+ if (strstr (match_str, "<SAMI>") != NULL ||
+ strstr (match_str, "<sami>") != NULL) {
+ GST_LOG ("SAMI (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_SAMI;
+ }
+ /* we're boldly assuming the first subtitle appears within the first hour */
+ if (sscanf (match_str, "0:%02u:%02u:", &n1, &n2) == 2 ||
+ sscanf (match_str, "0:%02u:%02u=", &n1, &n2) == 2 ||
+ sscanf (match_str, "00:%02u:%02u:", &n1, &n2) == 2 ||
+ sscanf (match_str, "00:%02u:%02u=", &n1, &n2) == 2 ||
+ sscanf (match_str, "00:%02u:%02u,%u=", &n1, &n2, &n3) == 3) {
+ GST_LOG ("TMPlayer (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_TMPLAYER;
+ }
+ if (sscanf (match_str, "[%u][%u]", &n1, &n2) == 2) {
+ GST_LOG ("MPL2 (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_MPL2;
+ }
+ if (strstr (match_str, "[INFORMATION]") != NULL) {
+ GST_LOG ("SubViewer (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_SUBVIEWER;
+ }
+ if (strstr (match_str, "{QTtext}") != NULL) {
+ GST_LOG ("QTtext (time based) format detected");
+ return GST_SUB_PARSE_FORMAT_QTTEXT;
+ }
+
+ GST_WARNING ("no subtitle format detected");
+ return GST_SUB_PARSE_FORMAT_UNKNOWN;
+}
+
+/*checking the type of subtitle*/
+static gboolean
+gst_submux_format_autodetect (Gstsubmux *self)
+{
+ gchar *data;
+ GstSubMuxFormat format;
+ gchar * line = NULL;
+ if (self->priv->is_internal) {
+ GST_DEBUG_OBJECT (self, "File is of internal type");
+ return TRUE;
+ }
+ line = gst_submux_extract_data (self);
+ if (!line)
+ return FALSE;
+ if (strlen (line) < 30) {
+ GST_WARNING_OBJECT (self, "File too small to be a subtitles file");
+ g_free(line);
+ return FALSE;
+ }
+
+ data = g_strndup (line, 35);
+ format = gst_submux_data_format_autodetect (data);
+ g_free (data);
+
+ self->priv->parser_type = format;
+ g_free(line);
+
+ return TRUE;
+}
+
+/*to validate the number of languages in case of sami files*/
+static gboolean
+gst_calculate_number_languages(Gstsubmux *self) {
+ gchar* text=NULL;
+ gchar *start = NULL;
+ gchar *end = NULL;
+ gint count = 0;
+ gchar* found = NULL;
+ gchar * name_temp = NULL;
+ int i = 0, j = 0;
+
+ GST_DEBUG_OBJECT (self, "Entering in language number");
+
+ if ((self->priv->parser_type != GST_SUB_PARSE_FORMAT_SAMI) || self->priv->is_internal)
+ return TRUE;
+
+ text = gst_submux_extract_data (self);
+ start = g_strstr_len (text, strlen (text), "!--");
+ if (!start) {
+ GST_ERROR_OBJECT (self, "Could not found the language start code in smi file");
+ return gst_create_own_language_list(self);
+ }
+ end = g_strstr_len (start, strlen (start), "-->");
+ if (!end){
+ GST_ERROR_OBJECT (self, "Could not found the language end code in smi file");
+ goto error;
+ }
+
+ found = start + 1;
+
+ while (TRUE) {
+ found = (gchar*)strcasestr (found, "lang");
+ if (!found)
+ break;
+ found++;
+ count++;
+ }
+
+ if (!count)
+ {
+ return gst_create_own_language_list(self);
+ }
+
+ for (i = 0; i < count; i++) {
+ gchar *attr_name = NULL, *attr_value = NULL;
+ GstLangStruct *new = NULL;
+
+ start = (gchar*)strcasestr (start, "lang:");
+ attr_value = (gchar*)malloc (3);
+ if (!attr_value) {
+ GST_ERROR_OBJECT (self, "memory could not be allocated through malloc call");
+ goto error;
+ }
+ start = start + 5;
+ strncpy (attr_value, start, 2);
+ attr_value[2] = '\0';
+ GST_DEBUG_OBJECT (self, "Language value comes as %s", attr_value);
+ name_temp = start;
+ while (TRUE) {
+ if (*name_temp == '{') {
+ int character_count = 0;
+ while (TRUE) {
+ name_temp--;
+
+ if (*name_temp == '.') {
+ attr_name = (gchar*) malloc (character_count + 1);
+ break;
+ } else if (*name_temp != ' ')
+ character_count++;
+ }
+ break;
+ }
+ name_temp--;
+ }
+ if (!attr_name) {
+ GST_ERROR_OBJECT (self, "Could not find the languages field in the file");
+ free(attr_value);
+ goto error;
+ }
+ name_temp++;
+ for (j = 0; *(name_temp + j) != ' '; j++) {
+ attr_name[j] = *(name_temp + j);
+ }
+ attr_name[j] = '\0';
+ new = g_new0 (GstLangStruct, 1);
+ if (attr_value) {
+ new->language_code = g_strdup(attr_value);
+ }
+ if (attr_name) {
+ new->language_key = g_strdup(attr_name);
+ }
+ free (attr_name);
+ free (attr_value);
+ self->priv->lang_list = g_list_append (self->priv->lang_list, new);
+ }
+ g_free(text);
+ return TRUE;
+error:
+ g_free(text);
+ return FALSE;
+}
+
+/*to initialize stream*/
+static gboolean gst_submux_stream_init(GstSubmuxStream * stream)
+{
+ stream->duration = 0;
+ stream->need_segment = TRUE;
+ stream->flushing = FALSE;
+ stream->eos_sent = FALSE;
+ stream->eos_came = FALSE;
+ stream->discont_came = FALSE;
+ stream->eos_ts = -1;
+ stream->last_ts = -1;
+ stream->queue = g_queue_new ();
+ stream->queue_empty = g_cond_new ();
+ stream->queue_lock = g_mutex_new ();
+ stream->flush_done = FALSE;
+ return TRUE;
+}
+
+/*to create pipelines according to internal and external subtitle*/
+gboolean gst_submux_create_pipelines(Gstsubmux *self, GstPad * pad)
+{
+ int i = 0;
+ GstStateChangeReturn ret;
+ GstSubmuxStream *new_stream;
+ guint length = 0;
+
+ if (!self->priv->is_internal) {
+ GstLangStruct *cur_language=NULL;
+
+ GST_DEBUG_OBJECT (self, "creating the pipeline for external pipeline");
+ if (self->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
+ if (!self->priv->lang_list) {
+ GST_ERROR_OBJECT(self, "failed to get the lang list");
+ return FALSE;
+ }
+ length = g_list_length (self->priv->lang_list);
+ } else {
+ length = 1;
+ }
+
+ GST_DEBUG_OBJECT (self, "number of tentative languages present are %d", length);
+
+ for (i = 0; i < length; i++) {
+ new_stream = g_new0 (GstSubmuxStream, 1);
+ if (!gst_submux_stream_init(new_stream)) {
+ GST_ERROR_OBJECT (self, "stream init is failed");
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT (self, "stream init has been done for stream[%d]", i);
+
+ new_stream->pipe_struc.pipe = gst_pipeline_new ("subtitle-pipeline");
+ if (!new_stream->pipe_struc.pipe) {
+ GST_ERROR_OBJECT (self, "failed to create pipeline");
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT (self, "creating appsrc");
+
+ /* creating source element */
+ new_stream->pipe_struc.appsrc = gst_element_factory_make ("appsrc", "pipe_appsrc");
+ if (!new_stream->pipe_struc.appsrc) {
+ GST_ERROR_OBJECT (self, "failed to create appsrc");
+ return FALSE;
+ }
+
+ g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "block", 1, NULL);
+ g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "max-bytes", (guint64)1, NULL);
+ /* create sink element */
+ new_stream->pipe_struc.appsink = gst_element_factory_make ("appsink", "pipe_appsink");
+ if (!new_stream->pipe_struc.appsink) {
+ GST_ERROR_OBJECT (self, "failed to create appsink");
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (new_stream->pipe_struc.appsink), "sync", FALSE, "emit-signals", TRUE, NULL);
+ g_object_set(G_OBJECT (new_stream->pipe_struc.appsrc),"emit-signals", TRUE, NULL);
+
+
+ /* create parsing element */
+ new_stream->pipe_struc.parser = gst_element_factory_make("subparse","pipe_parser");
+ if (!new_stream->pipe_struc.parser) {
+ GST_ERROR_OBJECT (self, "failed to create parser");
+ return FALSE;
+ }
+ if (self->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
+ cur_language = g_list_nth_data(self->priv->lang_list, i);
+ g_object_set (G_OBJECT (new_stream->pipe_struc.parser), "current-language",
+ cur_language->language_key, NULL);
+ }
+ GST_DEBUG_OBJECT (self, "value of subtitle-encoding is %s", self->encoding);
+ g_object_set (G_OBJECT (new_stream->pipe_struc.parser), "subtitle-encoding", self->encoding, NULL);
+ g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "stream-type",0,"format",GST_FORMAT_TIME, NULL);
+ gst_bin_add_many (GST_BIN ( new_stream->pipe_struc.pipe), new_stream->pipe_struc.appsink, new_stream->pipe_struc.appsrc,new_stream->pipe_struc.parser, NULL);
+ if (!gst_element_link_many (new_stream->pipe_struc.appsrc, new_stream->pipe_struc.parser,new_stream->pipe_struc.appsink, NULL)) {
+ GST_ERROR_OBJECT (self, "failed to link elements");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (self, "reached here and linking successful");
+
+ ret = gst_element_set_state (new_stream->pipe_struc.pipe, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT (self, "set_state failed...");
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT (self, "state has been changed succesfully");
+ self->streams = g_list_append(self->streams, new_stream);
+ self->priv->stream_count++;
+ g_signal_connect (new_stream->pipe_struc.appsink, "new-buffer", G_CALLBACK (gst_submux_on_new_buffer), g_list_nth_data(self->streams,i) );
+ }
+ } else {
+ length = self->sinkpads_count;
+ for (i = 0; i < length; i++) {
+ new_stream = g_new0 (GstSubmuxStream, 1);
+ if (!gst_submux_stream_init (new_stream)) {
+ GST_ERROR_OBJECT (self, "stream init is failed");
+ return FALSE;
+ }
+
+ self->streams=g_list_append(self->streams,new_stream);
+ self->priv->stream_count++;
+ }
+ self->pipeline_made = TRUE;
+ }
+
+ self->cur_buf_array = g_malloc0 (self->priv->stream_count * (sizeof (GstBuffer *)));
+ if (!self->cur_buf_array) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* call back on recieving the new buffer in appsink pad */
+static void
+gst_submux_on_new_buffer (GstElement *appsink, void *data)
+{
+ GstSubmuxStream *stream = (GstSubmuxStream *)data;
+ GstBuffer *inbuf = NULL;
+
+ if (!stream) {
+ GST_WARNING("Stream not available...");
+ return;
+ }
+ g_mutex_lock (stream->queue_lock);
+ inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
+ if (!inbuf) {
+ GST_WARNING_OBJECT (stream, "Input buffer not available...");
+ g_mutex_unlock (stream->queue_lock);
+ return;
+ }
+ if(stream->eos_ts == -1) {
+ if (!strcmp ((const char*)GST_BUFFER_DATA (inbuf), "eos") && GST_BUFFER_FLAG_IS_SET(inbuf,GST_BUFFER_FLAG_GAP)){
+ stream->eos_ts = stream->last_ts;
+ if (stream->eos_ts <= stream->seek_ts) {
+ g_queue_push_tail (stream->queue, inbuf);
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ GST_INFO_OBJECT (stream, "signaling queue empty signal as we are seeking beyond last subtitle");
+ return;
+ }
+ gst_buffer_unref(inbuf);
+ } else {
+ stream->last_ts = GST_BUFFER_DURATION(inbuf) + GST_BUFFER_TIMESTAMP(inbuf);
+ }
+ } else if (stream->eos_ts <= stream->seek_ts) {
+ gst_buffer_unref(inbuf);
+ GstBuffer *buf = gst_buffer_new_and_alloc (3 + 1);
+ GST_DEBUG_OBJECT(stream, "sending EOS buffer to chain\n");
+ GST_DEBUG_OBJECT (stream, "EOS. Pushing remaining text (if any)");
+ GST_BUFFER_DATA (buf)[0] = 'e';
+ GST_BUFFER_DATA (buf)[1] = 'o';
+ GST_BUFFER_DATA (buf)[2] = 's';
+ GST_BUFFER_DATA (buf)[3] = '\0';
+ /* play it safe */
+ GST_BUFFER_SIZE (buf) = 3;
+ GST_BUFFER_FLAG_SET(buf,GST_BUFFER_FLAG_GAP);
+ g_queue_push_tail (stream->queue, buf);
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ GST_INFO_OBJECT (stream,"signaling queue empty signal as we are seeking beyond last subtitle");
+ return;
+ }
+ if (!stream->discont_came) {
+ stream->discont_came = GST_BUFFER_IS_DISCONT (inbuf);
+ if (stream->discont_came) {
+ GST_DEBUG_OBJECT (stream, "first buffer with discont on new_buffer for stream with ts = %"
+ GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)),
+ GST_TIME_ARGS(GST_BUFFER_DURATION(inbuf)));
+ }
+ }
+
+ if (!stream->discont_came) {
+ GST_DEBUG_OBJECT (stream, "rejecting the buffer in appsink on new_buffer for stream with ts = %"
+ GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)),
+ GST_TIME_ARGS(GST_BUFFER_DURATION(inbuf)));
+ gst_buffer_unref(inbuf);
+ g_mutex_unlock (stream->queue_lock);
+ return;
+ }
+ g_queue_push_tail (stream->queue, inbuf);
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ GST_DEBUG_OBJECT (stream, "signaling queue empty signal");
+ return;
+}
+
+gchar *
+convert_to_utf8 (const gchar * str, gsize len, const gchar * encoding,
+ gsize * consumed, GError ** err, Gstsubmux * self)
+{
+ gchar *ret = NULL;
+
+ /* The char cast is necessary in glib < 2.24 */
+ ret =
+ g_convert_with_fallback (str, len, "UTF-8", encoding, (char *) "*",
+ consumed, NULL, err);
+
+ if (ret == NULL)
+ {
+ GST_DEBUG_OBJECT (self, "g_convert_with_fallback returns NULL");
+ return ret;
+ }
+
+ /* + 3 to skip UTF-8 BOM if it was added */
+ len = strlen (ret);
+ if (len >= 3 && (guint8) ret[0] == 0xEF && (guint8) ret[1] == 0xBB
+ && (guint8) ret[2] == 0xBF)
+ g_memmove (ret, ret + 3, len + 1 - 3);
+
+ return ret;
+}
+
+static gchar *
+detect_encoding (const gchar * str, gsize len)
+{
+ if (len >= 3 && (guint8) str[0] == 0xEF && (guint8) str[1] == 0xBB
+ && (guint8) str[2] == 0xBF)
+ return g_strdup ("UTF-8");
+
+ if (len >= 2 && (guint8) str[0] == 0xFE && (guint8) str[1] == 0xFF)
+ return g_strdup ("UTF-16BE");
+
+ if (len >= 2 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE)
+ return g_strdup ("UTF-16LE");
+
+ if (len >= 4 && (guint8) str[0] == 0x00 && (guint8) str[1] == 0x00
+ && (guint8) str[2] == 0xFE && (guint8) str[3] == 0xFF)
+ return g_strdup ("UTF-32BE");
+
+ if (len >= 4 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE
+ && (guint8) str[2] == 0x00 && (guint8) str[3] == 0x00)
+ return g_strdup ("UTF-32LE");
+
+ return NULL;
+}
+
+/* If language list is not present in smi file, check the body and create our own list */
+static gboolean
+gst_create_own_language_list (Gstsubmux *self)
+{
+ gchar * file_path_type = NULL;
+ gchar * temp_path = NULL;
+ gchar * file_path = NULL;
+ gsize * consumed = NULL;
+ guint keyCount = 0;
+ GError *err = NULL;
+ GstPad *sinkpad = (GstPad *) g_list_nth_data(self->sinkpad, 0);
+ GstQuery *cquery;
+ GstStructure *structure;
+ const GValue *value;
+ gchar* langkey[MAX_LANG];
+ gint langKey_length[MAX_LANG];
+ FILE *fp=NULL;
+ gint i=0;
+ structure = gst_structure_new ("FileSrcURI", "file-uri", G_TYPE_STRING, NULL, NULL);
+
+ cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
+
+ if (!gst_pad_peer_query (sinkpad, cquery)) {
+ GST_ERROR_OBJECT (self, "Failed to query SMI file path");
+ gst_query_unref (cquery);
+ return FALSE;
+ }
+ structure = gst_query_get_structure (cquery);
+ value = gst_structure_get_value (structure, "file-uri");
+ file_path = g_strdup (g_value_get_string (value));
+
+ if (file_path == NULL){
+ GST_ERROR_OBJECT (self, "Could not parse the SMI file path");
+ gst_query_unref (cquery);
+ return FALSE;
+ }
+ gst_query_unref (cquery);
+ temp_path = file_path;
+ GST_INFO_OBJECT (self, "File path comes as %s", file_path);
+
+ file_path_type = g_strndup ((gchar *) file_path, 4);
+ GST_INFO_OBJECT (self, "Received file path by query = %s, %s", file_path, file_path_type);
+ if (!g_strcmp0(file_path_type, "file")){
+ file_path += 7;
+ GST_INFO_OBJECT (self, "File path comes as %s", file_path);
+
+ fp = fopen (file_path, "r");
+ if (!fp){
+ GST_ERROR_OBJECT (self, "Failed to open file");
+ g_free(temp_path);
+ g_free(file_path_type);
+ return FALSE;
+ }
+ }
+ for( i=0;i<MAX_LANG;i++){
+ langkey[i]=NULL;
+ langKey_length[i]=0;
+ }
+ gboolean lang_found= FALSE;
+ while (!feof (fp) ){
+ gchar line[1025];
+ guint charCount = 0;
+ gboolean conversion = TRUE;
+ gchar *result = NULL;
+ gchar *con_temp = NULL;
+ gchar *delimiter = NULL;
+ gchar *temp = NULL;
+ guint keyLength = 0;
+
+ charCount = fread (line, sizeof(char), 1024, fp);
+ line[charCount] = '\0';
+ if (!charCount) {
+ GST_WARNING_OBJECT (self, "fread returned zero bytes");
+ continue;
+ }
+ GST_DEBUG_OBJECT (self, " Read charCount %d bytes Successfully",charCount);
+ GST_DEBUG_OBJECT (self, "value of detected encoding is %s and self encoding is %s",
+ self->detected_encoding,self->encoding);
+ if (self->detected_encoding && strcmp (self->detected_encoding, "UTF-8") && conversion){
+ result = convert_to_utf8 (line, charCount, self->detected_encoding, consumed, &err, self);
+ GST_DEBUG_OBJECT (self, " Converted convert_to_utf8 result %d ",result);
+ }
+ if (result == NULL) {
+ result = line;
+ conversion = FALSE;
+ }
+ con_temp = result;
+ temp = con_temp;
+
+ while (con_temp){
+ gchar* tempKey = NULL;
+ guint i = 0;
+
+ con_temp = strcasestr(con_temp,"class=");
+ if(con_temp)
+ delimiter = strcasestr(con_temp, ">");
+ GST_DEBUG_OBJECT (self, " Delimiter ...Entering if %s",con_temp);
+ if (con_temp && (delimiter!=NULL)){
+ gchar* tempChar = con_temp + 6;
+ GST_DEBUG_OBJECT (self, "Has class= reading string %s",tempChar);
+ GST_DEBUG_OBJECT (self, "Has class= ");
+ while (*tempChar != '>'){
+ keyLength++;
+ tempChar++;
+ GST_DEBUG_OBJECT (self, " keyLength %d tempChar %c",keyLength,*tempChar);
+ }
+ GST_DEBUG_OBJECT (self, " keyLength %d",keyLength);
+ tempChar -= keyLength;
+ tempKey = (gchar*) g_malloc (keyLength + 1);
+ if(!tempKey){
+ GST_DEBUG_OBJECT (self, "Error 1");
+ goto error;
+ }
+ gchar* temp1 =tempKey;
+ GST_DEBUG_OBJECT (self, "tempChar %s keyLength %d",tempChar,keyLength);
+ while (*tempChar != '>'){
+ *tempKey = *tempChar;
+ tempKey++;
+ tempChar++;
+ }
+ tempKey =temp1;
+ tempKey[keyLength]='\0';
+ GST_DEBUG_OBJECT (self, "tempKey %s keyLength %d keyCount %d",tempKey,keyLength,keyCount);
+ int k =0;
+ for (k = 0; k < keyCount; k++){
+ if(langkey[k]){
+ if (!strcasecmp (tempKey,langkey[k]))
+ {
+ GST_DEBUG_OBJECT (self, "Has the key already so breaking..Entry %d tempKey %s langkey[i] %s ",k,tempKey,langkey[k]);
+ lang_found = TRUE;
+ break;
+ }
+ }
+ }
+ if(lang_found == FALSE){
+ langkey[keyCount] = g_strdup (tempKey);
+ langKey_length[keyCount]=keyLength;
+ keyCount++;
+ }
+ lang_found =FALSE;
+ keyLength =0;
+ if(tempKey){
+ g_free(tempKey);
+ tempKey=NULL;
+ }
+ } else {
+ keyLength =0;
+ lang_found =FALSE;
+ break;
+ }
+ con_temp+=6;
+ GST_DEBUG_OBJECT (self, " ..increment con_temp %s",con_temp);
+ }
+ }
+ GST_DEBUG_OBJECT (self, " At end keyCount no of langs is %d ",keyCount);
+ for(i=0;i<keyCount;i++) {
+ if(langkey[i]) {
+ GstLangStruct *new = g_new0 (GstLangStruct, 1);
+ GST_DEBUG_OBJECT (self, "Adding ign case to the langKey keyCount %d and lang %s ",i, langkey[i]);
+ new->language_code = (gchar*)malloc (3);
+ if(!(new->language_code)){
+ GST_DEBUG_OBJECT (self, " .Error 2");
+ goto error;
+ }
+ gchar *attr_val=new->language_code ;
+ strncpy (attr_val, "un", 2);
+ attr_val[2]='\0';
+
+ new->language_key = g_strdup(langKey_length[i]);
+ self->priv->lang_list = g_list_append (self->priv->lang_list, new);
+ GST_DEBUG_OBJECT (self, " new...Successfull");
+ g_free(langkey[i]);
+ }
+ }
+ if (fp) {
+ g_free(temp_path);
+ g_free(file_path_type);
+ fclose(fp);
+ fp = NULL;
+ }
+ return TRUE;
+error:
+ GST_DEBUG_OBJECT (self, " In Error");
+ if (fp) {
+ g_free(temp_path);
+ g_free(file_path_type);
+ fclose(fp);
+ fp = NULL;
+ }
+ return FALSE;
+}
+
+gboolean
+validate_langlist_body(GList * lang_list, Gstsubmux * self){
+ gchar * file_path_type = NULL;
+ gchar * file_path = NULL;
+ gchar line[1025];
+ FILE * fp = NULL;
+ guint i = 0, found_count = 0,k = 0;
+ const guint list_len = g_list_length(lang_list);
+ gboolean counter[MAX_LANGUAGE];
+ GstPad *sinkpad = NULL;
+ struct LangStruct
+ {
+ gchar *language_code;
+ gchar *language_key;
+ } * lang;
+ sinkpad = (GstPad *) g_list_nth_data(self->sinkpad, 0);
+ GstQuery *cquery;
+ GstStructure *structure;
+ const GValue *value;
+ structure = gst_structure_new ("FileSrcURI", "file-uri", G_TYPE_STRING, NULL, NULL);
+
+ cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
+
+ if (!gst_pad_peer_query (sinkpad, cquery)) {
+ GST_ERROR_OBJECT (self, "Failed to query SMI file path");
+ gst_query_unref (cquery);
+ return FALSE;
+ }
+ structure = gst_query_get_structure (cquery);
+ value = gst_structure_get_value (structure, "file-uri");
+ file_path = g_strdup (g_value_get_string (value));
+
+ if (file_path == NULL){
+ GST_ERROR_OBJECT (self, "Could not parse the SMI file path");
+ gst_query_unref (cquery);
+ return FALSE;
+ }
+
+ if (self->external_filepath == NULL) {
+ self->external_filepath = file_path;
+ }
+ else {
+ if (!g_strcmp0 (file_path, self->external_filepath)) {
+ GST_INFO_OBJECT (self, "Same external file URI, no need to parse again");
+ gst_query_unref (cquery);
+ g_free(file_path);
+ return TRUE;
+ }
+ else {
+ g_free (self->external_filepath);
+ self->external_filepath = NULL;
+ self->external_filepath = file_path;
+ }
+ }
+
+ gst_query_unref (cquery);
+ GST_INFO_OBJECT (self, "File path comes as %s", file_path);
+
+ file_path_type = g_strndup ((gchar *) file_path, 4);
+ GST_INFO_OBJECT (self, "Received file path by query = %s, %s", file_path, file_path_type);
+ if (!g_strcmp0(file_path_type, "file")){
+ file_path += 7;
+ GST_INFO_OBJECT (self, "File path comes as %s", file_path);
+
+ fp = fopen (file_path, "r");
+ if (!fp){
+ GST_ERROR_OBJECT (self, "Failed to open file");
+ g_free(file_path_type);
+ return FALSE;
+ }
+
+ for (i = 0; i < list_len; i++){
+ counter[i] = FALSE;
+ }
+
+ while (!feof (fp) && found_count < list_len){
+ GError *err = NULL;
+ gsize * consumed = NULL;
+ gint gap = 0;
+ guint charCount = 0;
+ gchar* result = NULL;
+ gchar* temp = NULL;
+ gchar* temp_lang = NULL;
+ gchar* con_temp_end = NULL;
+ gchar* con_temp_start = NULL;
+ gchar* new_key = NULL;
+ gint new_key_length = 0;
+ gboolean new_key_found = FALSE;
+ gchar * temp1 = NULL;
+ gchar *con_temp_lang = NULL;
+ gchar *con_temp = NULL;
+ gboolean conversion = TRUE;
+ charCount = fread (line, sizeof(char), 1024, fp);
+ line[charCount] = '\0';
+ if (!charCount) {
+ GST_WARNING_OBJECT (self, "fread returned zero bytes");
+ continue;
+ }
+
+ GST_DEBUG_OBJECT (self, "value of detected encoding is %s and self encoding is %s",
+ self->detected_encoding,self->encoding);
+ if (self->detected_encoding && strcmp (self->detected_encoding, "UTF-8") && conversion){
+ result = convert_to_utf8 (line, charCount, self->detected_encoding, consumed, &err, self);
+ }
+ if (result == NULL) {
+ result = line;
+ conversion = FALSE;
+ }
+ con_temp = g_utf8_strdown (result, strlen (result));
+ temp = con_temp;
+ while (con_temp) {
+ con_temp = g_strstr_len(con_temp, strlen (con_temp), "class=");
+ if (con_temp) {
+ temp1 = g_strstr_len(con_temp+1, strlen (con_temp), "class=");
+ }
+ if (temp1 && con_temp){
+ gap = strlen (con_temp) - strlen (temp1);
+ } else if (con_temp) {
+ gap = strlen (con_temp);
+ } else {
+ continue;
+ }
+ if (con_temp){
+ for (i = 0; i < list_len; i++){
+ if (counter[i] == TRUE) {
+ con_temp = con_temp + 1;
+ continue;
+ }
+ lang = (struct LangStruct *) g_list_nth_data (lang_list, i);
+ if (lang) {
+ temp_lang = g_strdup(lang->language_key);
+ con_temp_lang = g_utf8_strdown (temp_lang, strlen (temp_lang));
+ if (g_strstr_len (con_temp, gap, con_temp_lang)) {
+ found_count++;
+ counter[i] = TRUE;
+ GST_INFO_OBJECT (self, "Valid Language in list : [%s]", lang->language_key);
+ con_temp = con_temp + 1;
+ }
+/* Fix Me: Cases where there is no body for a specific language
+ * inside a single language .smi file */
+#if 0
+ else {
+ con_temp_start = con_temp;
+ con_temp_end = con_temp;
+ while(con_temp_end) {
+ if(*con_temp_end == '=') {
+ con_temp_start = con_temp_end+1;
+ con_temp_end++;
+ }else if(*con_temp_end == '>') {
+ con_temp_end = con_temp_end;
+ new_key_found = TRUE;
+ break;
+ }else {
+ con_temp_end++;
+ new_key_found = FALSE;
+ }
+ }
+ if(new_key_found) {
+ new_key_length = strlen(con_temp_start)-strlen(con_temp_end);
+ new_key = g_malloc(new_key_length +1);
+ for(k=0;k<new_key_length;k++){
+ *(new_key+k)=*(con_temp_start+k);
+ }
+ *(new_key+new_key_length)='\0';
+ GST_INFO("new lang key is %s",lang->language_key);
+ g_free(new_key);
+ found_count++;
+ counter[i] = TRUE;
+ con_temp = con_temp + 1;
+ }
+ }
+#endif
+ g_free (temp_lang);
+ g_free (con_temp_lang);
+ }
+ }
+ }
+ }
+ if (conversion)
+ g_free (result);
+ if (temp)
+ g_free (temp);
+ }
+
+ if (found_count < list_len) {
+ for (i = 0; i < list_len; i++) {
+ if (counter[i] == FALSE)
+ lang_list = g_list_delete_link (lang_list, g_list_nth (lang_list, i));
+ }
+ }
+ } else {
+ GST_ERROR_OBJECT (self, "File is not a local file");
+ g_free(file_path_type);
+ return FALSE;
+ }
+ fclose (fp);
+ g_free(file_path_type);
+ return TRUE;
+}
+
+/*
+**
+** Description : Chain function used to push the subtitle buffer to internal pipelines of submux element
+** Params : (1) sink pad on which buffer is arriving (2) the buffer itself
+** return : GST_FLOW_OK on successfully pushing subtitle buffer to next element
+**
+*/
+static GstFlowReturn
+gst_submux_chain(GstPad *pad, GstBuffer *buffer)
+{
+ guint length = 0;
+ guint i=0;
+ GstPad *checkpad = NULL;
+ Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
+ gboolean ret = FALSE;
+ GstFlowReturn fret = GST_FLOW_ERROR;
+ GstSubmuxStream *stream = NULL;
+ GstMessage *m = NULL;
+
+ if (GST_BUFFER_IS_DISCONT (buffer))
+ GST_DEBUG_OBJECT(submux, "Discont buffer came in chain function");
+ GST_DEBUG_OBJECT (submux, "^^^^^entering in chain^^^^^^");
+ if (!submux->priv->is_internal) {
+ if (!submux->priv->first_buffer) {
+ submux->detected_encoding = detect_encoding ((gchar*)GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
+ }
+ if (!submux->langlist_msg_posted && submux->priv->lang_list) {
+ if (!validate_langlist_body (submux->priv->lang_list, submux)){
+ GST_WARNING_OBJECT(submux, "Error occured while validating language list. Posting without validation");
+ }
+ if (submux->priv->lang_list) {
+ GList* temp_list_to_post = NULL;
+ temp_list_to_post = g_list_copy (submux->priv->lang_list);
+ m = gst_message_new_element (GST_OBJECT_CAST (submux), gst_structure_new("Ext_Sub_Language_List",
+ "lang_list", G_TYPE_POINTER, temp_list_to_post, NULL));
+
+ gst_element_post_message (GST_ELEMENT_CAST (submux), m);
+ submux->langlist_msg_posted = TRUE;
+ }
+ GST_DEBUG_OBJECT (submux, "LANGLIST POSTED");
+ }
+ if (submux->need_segment) {
+ ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
+ submux->segment.format, submux->segment.start, submux->segment.stop,
+ submux->segment.time));
+ GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
+ if (!ret) {
+ GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG_OBJECT (submux, "Starting the loop again");
+ if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
+ GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
+ GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
+ return GST_FLOW_ERROR;
+ }
+ submux->need_segment = FALSE;
+ }
+ GST_DEBUG_OBJECT (submux, "before pushing buffer to each apprsrc");
+ if (!submux->priv->lang_list && submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
+ GST_ERROR_OBJECT (submux, "lang list is not there");
+ return GST_FLOW_ERROR;
+ }
+ if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI)
+ length = g_list_length (submux->priv->lang_list);
+ else
+ length = 1;
+
+ for (i = 0; i < length; i++) {
+ stream = g_list_nth_data(submux->streams, i);
+ if ((submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) && !submux->priv->first_buffer){
+ GstLangStruct *lang = g_list_nth_data(submux->priv->lang_list, i);
+ if(submux->msl_streams){
+ GstLangStruct *lang1 = g_list_nth_data(submux->msl_streams, i);
+ lang->active = lang1->active;
+ } else {
+ if (i == 0)
+ lang->active = TRUE;
+ else
+ lang->active = FALSE;
+ }
+ }
+ GST_DEBUG_OBJECT (submux, "making stream need segment false");
+ stream->need_segment = FALSE;
+ }
+
+ for (i = 0; i < length; i++) {
+ stream = g_list_nth_data(submux->streams, i);
+ if (!stream){
+ GST_ERROR_OBJECT (submux, "stream not found");
+ return GST_FLOW_ERROR;
+ }
+
+ if (!stream->pipe_struc.appsrc) {
+ GST_ERROR_OBJECT (submux, "appsrc not found");
+ return GST_FLOW_ERROR;
+ }
+ if (i < (length - 1))
+ gst_buffer_ref (buffer);
+
+ fret = gst_app_src_push_buffer ((GstAppSrc*)stream->pipe_struc.appsrc, buffer);
+
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (submux, "push buffer failed with fret is %d", fret);
+ return fret;
+ }
+ GST_DEBUG_OBJECT (submux, "pad_push successfull to appsrc %p buffer", buffer);
+ }
+ } else {
+ length = submux->sinkpads_count;
+ checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
+ if (checkpad == pad) {
+ if (submux->need_segment) {
+ ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
+ submux->segment.format, submux->segment.start, submux->segment.stop,
+ submux->segment.time));
+ GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
+ if (!ret) {
+ GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG_OBJECT (submux, "Starting the loop again");
+ if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
+ GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
+ GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
+ return GST_FLOW_ERROR;
+ }
+ submux->need_segment = FALSE;
+ }
+ }
+ for (i = 0; i < length; i++) {
+ checkpad = (GstPad *) g_list_nth_data(submux->sinkpad, i);
+ if (checkpad == pad) {
+ stream = g_list_nth_data (submux->streams, i);
+ if (!stream) {
+ GST_ERROR_OBJECT (submux, "Stream not available...");
+ return GST_FLOW_ERROR;
+ }
+ if (stream->flushing){
+ GST_DEBUG_OBJECT (submux, "flushing going on in appsink");
+ return GST_FLOW_OK ;
+ }
+
+ g_mutex_lock (stream->queue_lock);
+ g_queue_push_tail (stream->queue, buffer);
+ g_cond_signal (stream->queue_empty);
+ g_mutex_unlock (stream->queue_lock);
+ fret = GST_FLOW_OK;
+ GST_DEBUG_OBJECT (submux, "push buffer success to appsrc with fret is %d for stream[%d]", fret, i);
+ break;
+ }
+ }
+ }
+
+ if (!submux->priv->first_buffer) {
+ GST_DEBUG_OBJECT (submux, "got the first buffer");
+ submux->priv->first_buffer = TRUE;
+ if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
+
+ }
+ }
+ GST_DEBUG_OBJECT (submux, "^^^^^exiting in chain^^^^^^");
+ return fret;
+}
+
+/* stream_denit */
+static void
+gst_submux_stream_deinit (GstSubmuxStream *stream,Gstsubmux *submux)
+{
+ GstBuffer *buf = NULL;
+
+ if (stream) {
+ if (stream->queue) {
+ while (!g_queue_is_empty (stream->queue)) {
+ buf = g_queue_pop_head (stream->queue);
+ gst_buffer_unref (buf);
+ buf = NULL;
+ }
+ g_queue_free (stream->queue);
+ stream->queue = NULL;
+ }
+
+ if (stream->pipe_struc.pipe) {
+ gst_element_set_state (stream->pipe_struc.pipe, GST_STATE_NULL);
+ gst_element_get_state (stream->pipe_struc.pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+ gst_object_unref(GST_OBJECT(stream->pipe_struc.appsrc));
+ gst_object_unref(GST_OBJECT(stream->pipe_struc.appsink));
+ gst_object_unref (stream->pipe_struc.pipe);
+ }
+
+ if (stream->queue_lock) {
+ g_cond_broadcast(stream->queue_empty);
+ g_mutex_free (stream->queue_lock);
+ stream->queue_lock = NULL;
+ }
+
+ if (stream->queue_empty) {
+ g_cond_free (stream->queue_empty);
+ stream->queue_empty= NULL;
+ }
+
+ g_free (stream);
+ GST_DEBUG_OBJECT (submux, "stream deinit completed");
+ }
+}
+
+/* releasing the requested pad */
+static void
+gst_submux_release_pad (GstElement * element, GstPad * pad)
+{
+ Gstsubmux *submux = GST_SUBMUX_CAST (element);
+ GstPad *check_pad;
+ int i=0;
+ guint length;
+ GST_INFO_OBJECT(element,"entering in the release pad");
+ length = g_list_length(submux->sinkpad);
+ GST_DEBUG_OBJECT (element, "Releasing %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ for (i=1;i<=length;i++) {
+ check_pad = (struct GstPad *) g_list_nth_data(submux->sinkpad,i);
+ if (check_pad == pad) {
+ /* this is it, remove */
+ submux->sinkpad = g_list_remove_link (submux->sinkpad, pad);
+ gst_element_remove_pad (element, pad);
+ break;
+ }
+ }
+}
+
+/* request new pad */
+static GstPad *
+gst_submux_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * req_name)
+{
+ GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+ Gstsubmux *submux = GST_SUBMUX_CAST (element);
+ GstPad *newpad = NULL;
+ gchar *name = NULL;
+
+ if (templ->direction != GST_PAD_SINK) {
+ GST_ERROR_OBJECT (submux, "templ direction is not sinkpad, returning from here");
+ goto wrong_direction;
+ }
+
+ if (templ == gst_element_class_get_pad_template (klass, "sink%d")) {
+ name = g_strdup_printf ("sink%d", submux->sinkpads_count++);
+ }
+
+ GST_DEBUG_OBJECT (submux, "Requested pad: %s", name);
+ newpad = (GstPad*)g_new0 (GstPad*, 1);
+ /* create pad and add to collections */
+ newpad = gst_pad_new_from_template (templ, name);
+ g_free (name);
+ if(!submux->priv->is_internal) {
+ submux->external_sinkpad = TRUE;
+ }
+ submux->sinkpad = g_list_append (submux->sinkpad, newpad);
+ /* set up pad */
+
+ /* set up pad functions */
+ gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_setcaps));
+ gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_handle_sink_event));
+ gst_pad_set_chain_function(newpad, GST_DEBUG_FUNCPTR (gst_submux_chain));
+ gst_pad_set_active (newpad, TRUE);
+ gst_element_add_pad (element, newpad);
+
+ return newpad;
+
+/* ERRORS */
+wrong_direction:
+ GST_WARNING_OBJECT (submux, "Request pad that is not a SINK pad.");
+ return NULL;
+}
+
+static gboolean
+gst_submux_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
+ gboolean ret = FALSE;
+ guint length = 0;
+ gint i = 0;
+ gboolean update;
+ GstSubmuxStream *cur_stream = NULL;
+
+ GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
+ length = g_list_length(submux->streams);
+
+ switch (GST_EVENT_TYPE (event)) {
+ /* this event indicates speed change or seek */
+ case GST_EVENT_SEEK: {
+ GstFormat format;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ gdouble rate;
+ GstPad *sinkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
+ gst_event_parse_seek (event, &rate, &format, &submux->segment_flags,
+ &start_type, &start, &stop_type, &stop);
+ gst_segment_set_seek (&submux->segment, rate, format, submux->segment_flags,
+ start_type, start, stop_type, stop, &update);
+ if (submux->priv->is_internal || submux->priv->parser_type != GST_SUB_PARSE_FORMAT_SAMI) {
+ length = g_list_length (submux->sinkpad);
+ } else {
+ length = g_list_length(submux->streams);
+ }
+ if (!submux->priv->is_internal) {
+ ret = gst_pad_push_event (sinkpad, gst_event_new_seek (rate, GST_FORMAT_BYTES, submux->segment_flags,
+ GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, 0));
+ gst_event_unref (event);
+ } else {
+ GST_DEBUG_OBJECT (submux, "handling seek in case of internal");
+ ret = gst_pad_event_default (pad, event);
+ }
+
+ if (!ret) {
+ GST_ERROR_OBJECT (submux, "sending seek event to sink pad failed");
+ break;
+ }
+ GST_DEBUG_OBJECT (submux, "sending seek event to sink pad passed");
+
+ break;
+ }
+
+ default: {
+ ret = gst_pad_event_default (pad, event);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_submux_handle_sink_event (GstPad * pad, GstEvent * event)
+{
+ Gstsubmux *submux = GST_SUBMUX (GST_PAD_PARENT (pad));
+ gboolean ret = FALSE;
+ guint length = 0;
+ GstBuffer *buf = NULL;
+ GstPad *checkpad = NULL;
+ gint i = 0;
+ GstSubmuxStream *cur_stream = NULL;
+
+ GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:{
+ length = g_list_length (submux->sinkpad);
+ GST_OBJECT_LOCK (submux);
+ for (i = 0; i < length; i++) {
+ GST_DEBUG_OBJECT(submux, "inside the handling of EOS event");
+ cur_stream = g_list_nth_data(submux->streams,i);
+ if (!cur_stream->eos_sent) {
+ GstBuffer *buf = gst_buffer_new_and_alloc (3 + 1);
+ GST_DEBUG_OBJECT(submux, "sending EOS buffer to chain\n");
+ GST_DEBUG_OBJECT (submux, "EOS. Pushing remaining text (if any)");
+ GST_BUFFER_DATA (buf)[0] = 'e';
+ GST_BUFFER_DATA (buf)[1] = 'o';
+ GST_BUFFER_DATA (buf)[2] = 's';
+ GST_BUFFER_DATA (buf)[3] = '\0';
+ /* play it safe */
+ GST_BUFFER_SIZE (buf) = 3;
+ GST_BUFFER_FLAG_SET(buf,GST_BUFFER_FLAG_GAP);
+ gst_submux_chain (g_list_nth_data(submux->sinkpad,i), buf);//
+ cur_stream->eos_sent = TRUE;
+ }
+ }
+ GST_OBJECT_UNLOCK (submux);
+ gst_event_unref(event);
+ ret = TRUE;
+ break;
+ }
+ case GST_EVENT_NEWSEGMENT: {
+ GstFormat format;
+ gdouble rate,arate;
+ gint64 start, stop, time;
+ gboolean update;
+ GST_OBJECT_LOCK (submux);
+ if (!submux->pipeline_made) {
+ if (!gst_submux_format_autodetect (submux)) {
+ GST_ERROR_OBJECT (submux, "auto detect function failed");
+ return FALSE;
+ }
+ if (!gst_calculate_number_languages(submux)) {
+ GST_ERROR_OBJECT (submux, "failed to calculate number of languages");
+ return FALSE;
+ }
+ if (!gst_submux_create_pipelines (submux, pad)) {
+ GST_ERROR_OBJECT (submux, "failed to create pipelines");
+ return FALSE;
+ }
+ }
+
+ if (!submux->priv->is_internal) {
+ gst_event_unref(event);
+ length = g_list_length(submux->streams);
+ for (i = 0; i < length; i++) {
+ GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
+ cur_stream = g_list_nth_data(submux->streams,i);
+ GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
+ if (!cur_stream->pipe_struc.pipe) {
+ GST_ERROR_OBJECT (submux, "pipeline is null");
+ return FALSE;
+ }
+ cur_stream = g_list_nth_data(submux->streams,i);
+ cur_stream->seek_ts = submux->segment.start;
+ ret = gst_element_send_event (cur_stream->pipe_struc.pipe, gst_event_new_new_segment (FALSE,
+ submux->segment.rate, submux->segment.format,
+ submux->segment.start, submux->segment.stop, submux->segment.time));
+ if (!ret){
+ GST_ERROR_OBJECT(submux, "sending newsegment event to stream[%d] failed", i);
+ break;
+ }
+ }
+ submux->need_segment = TRUE;
+ } else {
+ length = g_list_length (submux->sinkpad);
+ if (length == g_list_length (submux->streams) && submux->need_segment) {
+ for (i = 0; i < length; i++) {
+ GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
+ cur_stream = g_list_nth_data(submux->streams, i);
+ if (cur_stream->need_segment) {
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time);
+ gst_segment_set_newsegment_full (&submux->segment, update, rate, arate, format, start, stop, time);
+ GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
+ ret = TRUE;
+ cur_stream->need_segment = FALSE;
+ }
+ }
+ submux->need_segment = TRUE;
+ gst_event_unref(event);
+ }
+ }
+ GST_OBJECT_UNLOCK (submux);
+ break;
+ }
+ case GST_EVENT_FLUSH_START: {
+ length = g_list_length(submux->streams);
+ if (!submux->priv->is_internal) {
+ gst_event_unref(event);
+ ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
+ for (i = 0;i < length;i++) {
+ cur_stream = g_list_nth_data(submux->streams,i);
+ cur_stream->flushing = TRUE;
+ cur_stream->discont_came = FALSE;
+ GST_DEBUG_OBJECT (submux, "making discont false");
+ GST_DEBUG_OBJECT (submux, "making flushing TRUE");
+ cur_stream->eos_came = FALSE;
+ cur_stream->eos_sent = FALSE;
+ GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
+ }
+ for (i = 0; i < length; i++) {
+ cur_stream = g_list_nth_data(submux->streams,i);
+ ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_start ());
+ ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_stop ());
+ ret = gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_eos ());
+ GST_INFO_OBJECT(cur_stream,"flush stop and start and eos is done with ret %d",ret);
+ submux->flushing =TRUE;
+ g_mutex_lock (cur_stream->queue_lock);
+ g_cond_signal (cur_stream->queue_empty);
+ g_mutex_unlock(cur_stream->queue_lock);
+ cur_stream->flush_done = TRUE;
+ GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
+ ret = TRUE;
+ GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
+ }
+
+ if (!ret){
+ GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
+ break;
+ }
+
+ if (submux && GST_PAD_TASK(submux->srcpad)) {
+ GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
+ GST_PAD_STREAM_LOCK (submux->srcpad);
+ GST_INFO_OBJECT (submux, "acquired stream lock");
+ GST_PAD_STREAM_UNLOCK (submux->srcpad);
+ }
+ /*changes for new design*/
+ for (i = 0;i < length;i++) {
+ cur_stream = g_list_nth_data(submux->streams,i);
+ gst_submux_stream_deinit(cur_stream,submux);
+ }
+ g_list_free(submux->streams);
+ submux->streams = NULL;
+ gst_submux_deinit_private_values (submux);
+
+ submux->stop_loop = FALSE;
+ submux->need_segment = TRUE;
+ submux->langlist_msg_posted = FALSE;
+ GST_DEBUG_OBJECT (submux, "flush start successfully send to next element");
+ } else {
+ GST_DEBUG_OBJECT(submux, "flusht start in case of internal subtitle");
+ gst_event_unref (event);
+ submux->flushing = TRUE;
+ checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
+ if (checkpad == pad) {
+ ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
+ for (i = 0; i < length; i++) {
+ cur_stream = g_list_nth_data(submux->streams, i);
+ cur_stream->flushing = TRUE;
+ GST_DEBUG_OBJECT (submux, "in case of internal making discont unchanged");
+ GST_DEBUG_OBJECT (submux, "making flushing TRUE");
+ }
+ for (i = 0; i < length; i++) {
+ cur_stream = g_list_nth_data(submux->streams, i);
+ submux->flushing = TRUE;
+ g_mutex_lock (cur_stream->queue_lock);
+ while (!g_queue_is_empty (cur_stream->queue)) {
+ buf = g_queue_pop_head (cur_stream->queue);
+ gst_buffer_unref (buf);
+ }
+ GST_DEBUG_OBJECT (submux, "cleared stream cur_stream->queue");
+ g_queue_clear (cur_stream->queue);
+ g_cond_signal (cur_stream->queue_empty);
+ g_mutex_unlock(cur_stream->queue_lock);
+ GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
+ cur_stream->eos_came = FALSE;
+ cur_stream->eos_sent = FALSE;
+ GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
+ ret = TRUE;
+ GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
+ }
+ if (!ret){
+ GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
+ break;
+ }
+
+ if (submux && GST_PAD_TASK (submux->srcpad)) {
+ GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
+ GST_PAD_STREAM_LOCK (submux->srcpad);
+ GST_INFO_OBJECT (submux, "acquired srcpad lock");
+ GST_PAD_STREAM_UNLOCK (submux->srcpad);
+ }
+ GST_DEBUG_OBJECT(submux, "flush start successfully send to next element");
+ }
+ }
+ break;
+ }
+ case GST_EVENT_FLUSH_STOP: {
+ gst_event_unref(event);
+ if (!submux->priv->is_internal) {
+ guint idx = 0;
+ submux->flushing = FALSE;
+ ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
+ if (!ret){
+ GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
+ break;
+ }
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ submux->cur_buf_array[idx] = NULL;
+ }
+ GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
+ } else {
+ length = g_list_length(submux->streams);
+ GST_DEBUG_OBJECT (submux, "flusht stop in case of internal subtitle");
+ checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
+ if (checkpad == pad) {
+ for (i = 0; i < length; i++) {
+ cur_stream = g_list_nth_data(submux->streams, i);
+ cur_stream->need_segment = TRUE;
+ submux->cur_buf_array[i] = NULL;
+ submux->need_segment = TRUE;
+ GST_DEBUG_OBJECT (submux, "making need_segment true");
+ submux->flushing = FALSE;
+ cur_stream->flushing = FALSE;
+ GST_DEBUG_OBJECT (submux, "making flushing FALSE");
+ ret = TRUE;
+ GST_DEBUG_OBJECT (submux, "sending %s event to stream[%d] success", GST_EVENT_TYPE_NAME (event), i);
+ }
+ ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
+ if (!ret){
+ GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
+ break;
+ }
+ GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
+ }
+ }
+ break;
+ }
+ default:{
+ if (!submux->priv->is_internal) {
+ ret = gst_pad_event_default (pad, event);
+ } else {
+ checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
+ if (checkpad == pad) {
+ ret = gst_pad_event_default (pad, event);
+ } else {
+ ret = TRUE;
+ }
+ }
+ if (!ret){
+ GST_ERROR_OBJECT (submux, "sending %s event to srcpad pad failed", GST_EVENT_TYPE_NAME (event));
+ break;
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+static gint gst_submux_buffer_list_sorting (gconstpointer a, gconstpointer b)
+{
+ GstBuffer *buf_a = (GstBuffer *) a;
+ GstBuffer *buf_b = (GstBuffer *) b;
+ if (GST_BUFFER_TIMESTAMP(buf_a)>GST_BUFFER_TIMESTAMP(buf_b))
+ return -1;
+ else if(GST_BUFFER_TIMESTAMP(buf_a)<GST_BUFFER_TIMESTAMP(buf_b))
+ return 1;
+ else
+ return 0;
+}
+
+static gboolean
+gst_submux_is_muxing_needed (GstBuffer *ref_buffer, GstBuffer *cur_buf)
+{
+ GstClockTime ref_start = GST_BUFFER_TIMESTAMP(ref_buffer);
+ GstClockTime ref_stop = GST_BUFFER_TIMESTAMP(ref_buffer) + GST_BUFFER_DURATION(ref_buffer);
+ GstClockTime start = GST_BUFFER_TIMESTAMP(cur_buf);
+ GstClockTime stop = GST_BUFFER_TIMESTAMP(cur_buf) + GST_BUFFER_DURATION(cur_buf);
+
+ /* if we have a stop position and a valid start and start is bigger,
+ * we're outside of the segment */
+ if (G_UNLIKELY (ref_stop != -1 && start != -1 && start >= ref_stop))
+ return FALSE;
+
+ /* if a stop position is given and is before the segment start,
+ * we're outside of the segment. Special case is were start
+ * and stop are equal to the segment start. In that case we
+ * are inside the segment. */
+ if (G_UNLIKELY (stop != -1 && (stop < ref_start || (start != stop && stop == ref_start))))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* This function do the actual muxing of buffer on the basis of timestamps */
+static GList*
+gst_submux_muxing (Gstsubmux *submux)
+{
+ GstClockTime min_timestamp = 0;
+ int min_stream = 0;
+ int overlap = 0;
+ GstClockTime next_min_time = 0;
+ int idx = 0;
+ GList *push_list = NULL;
+
+ /* Finding least timestamp of all streams and their stream ID */
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ if(submux->cur_buf_array[idx] && !min_timestamp) {
+ min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
+ min_stream = idx;
+ }
+ if(submux->cur_buf_array[idx] && (GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < min_timestamp)) {
+ min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
+ min_stream = idx;
+ }
+ }
+
+ GST_DEBUG_OBJECT (submux, "Identified least timestamp: %"GST_TIME_FORMAT" for stream: %d",
+ GST_TIME_ARGS(min_timestamp), min_stream);
+
+ /* Finding overlap buffers and next least timestamp */
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ if(submux->cur_buf_array[idx] && (idx != min_stream) && (!next_min_time)) {
+ next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
+ }
+ if(submux->cur_buf_array[idx] && (idx != min_stream)) {
+ if(gst_submux_is_muxing_needed (submux->cur_buf_array[min_stream], submux->cur_buf_array[idx])) {
+ overlap = overlap | (1<<idx); // bit setting of overlap variable with stream ID
+ GST_DEBUG_OBJECT (submux, "overlapped with stream = %d", idx);
+ if(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < next_min_time)
+ next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
+ }
+ }
+ }
+
+ GST_DEBUG_OBJECT (submux, "Identified overlap: %d next least timestamp: %"GST_TIME_FORMAT" ", overlap, GST_TIME_ARGS(next_min_time));
+
+ /* If no overlap send buffer as it is */
+ if(!overlap) {
+ GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[min_stream]));
+ push_list = g_list_append(push_list, submux->cur_buf_array[min_stream]);
+ GST_DEBUG_OBJECT (submux, "No overlap found pushing buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
+ submux->cur_buf_array[min_stream] = NULL;
+ } else {
+ GstBuffer *push_buf = NULL;
+ GstClockTime stop_time = 0;
+ int stop_idx = 0;
+ GstBuffer *overlap_buf = NULL;
+ guint overlap_buf_length = 0;
+ GString *overlap_text = NULL;
+ gchar *text = NULL;
+
+ if(next_min_time > GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])) {
+ GST_DEBUG_OBJECT (submux, "Before duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
+ push_buf = gst_buffer_copy (submux->cur_buf_array[min_stream]);
+ push_buf->duration = next_min_time - GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]);
+ GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]) = next_min_time;
+ GST_BUFFER_DURATION(submux->cur_buf_array[min_stream]) -= push_buf->duration;
+ GST_DEBUG_OBJECT (submux, "After duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
+
+ min_timestamp = next_min_time;
+ GST_INFO_OBJECT (submux, "pushing string: %s...", (gchar*)GST_BUFFER_DATA(push_buf));
+ push_list = g_list_append(push_list, push_buf);
+ GST_DEBUG_OBJECT (submux, "Overlap found pushing initial partial buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(push_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(push_buf)));
+ }
+
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ if(submux->cur_buf_array[idx] && !stop_time) {
+ stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
+ stop_idx = idx;
+ }
+ if(submux->cur_buf_array[idx] &&
+ ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) < stop_time)) {
+ stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
+ stop_idx = idx;
+ }
+ }
+ GST_DEBUG_OBJECT (submux, "Identified least stop timestamp: %"GST_TIME_FORMAT" for stream: %d",
+ GST_TIME_ARGS(stop_time), stop_idx);
+
+ overlap_text = g_string_new ("");
+ overlap = overlap | (1<<min_stream);
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ int finder = 1<<idx;
+ if(overlap & finder) {
+ GST_DEBUG_OBJECT (submux, "append string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[idx]));
+ g_string_append (overlap_text, (gchar*)GST_BUFFER_DATA (submux->cur_buf_array[idx]));
+ GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+= (stop_time - min_timestamp);
+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])-= (stop_time - min_timestamp);
+ if(overlap > (1<<(idx+1))) g_string_append_c (overlap_text, '\n');
+ }
+ }
+ text = g_string_free (overlap_text, FALSE);
+ overlap_buf_length = strlen(text);
+ overlap_buf = gst_buffer_new_and_alloc (overlap_buf_length + 1);
+ memcpy (GST_BUFFER_DATA (overlap_buf), text, overlap_buf_length + 1);
+ overlap_buf->timestamp = min_timestamp;
+ overlap_buf->duration = stop_time - min_timestamp;
+ g_free (text);
+ text = NULL;
+ submux->cur_buf_array[stop_idx] = NULL;
+ GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(overlap_buf));
+ push_list = g_list_append(push_list, overlap_buf);
+ GST_DEBUG_OBJECT (submux, "Overlap found pushing merged buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(overlap_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(overlap_buf)));
+ GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", stop_idx);
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ if(submux->cur_buf_array[idx] &&
+ ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) <= stop_time)) {
+ submux->cur_buf_array[idx] = NULL;
+ GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", idx);
+ }
+ }
+ }
+ return push_list;
+}
+
+static void gst_submux_loop (Gstsubmux *submux)
+{
+ guint length = 0;
+ GstBuffer *src_buffer = NULL;
+ GstBuffer *temp_buffer = NULL;
+ GstBuffer *check_buffer = NULL;
+ GstSubmuxStream *cur_stream = NULL;
+ GstSubmuxStream *check_stream = NULL;
+ gboolean match = FALSE;
+ GstFlowReturn fret = GST_FLOW_OK;
+ GstClockTime cur_duration = 0 ;
+ GstClockTime cur_ts = 0;
+ gboolean eos = TRUE;
+ guint i= 0,k = 0;
+ GList *push_list = NULL;
+ GstBuffer *push_buf = NULL;
+
+ if (!submux->priv->first_buffer) {
+ GST_INFO_OBJECT (submux, "exiting from lopp");
+ return;
+ }
+
+ if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
+ length = g_list_length (submux->priv->lang_list);
+ } else {
+ length = submux->sinkpads_count;
+ }
+
+ gboolean made = FALSE;
+ // length = 2;
+
+ for (i = 0; i < length; i++) {
+
+re_pop:
+ cur_stream = g_list_nth_data (submux->streams, i);
+ GST_DEBUG_OBJECT (submux, "Before lock acquired in loop stream[%d]", i);
+ g_mutex_lock (cur_stream->queue_lock);
+ GST_DEBUG_OBJECT (submux, "Lock acquired in loop stream[%d]", i);
+
+ if (g_queue_is_empty (cur_stream->queue) && !submux->flushing) {
+ GST_DEBUG_OBJECT (submux, "Queue is empty, waiting for the condition signal stream[%d]", i);
+ g_cond_wait (cur_stream->queue_empty, cur_stream->queue_lock);
+ }
+ GST_DEBUG_OBJECT (submux, "Got the queue condition signal stream[%d]", i);
+
+ if (submux->flushing || submux->stop_loop) {
+ GST_DEBUG_OBJECT (submux, "Flushing going on in loop");
+ GST_DEBUG_OBJECT (submux, "Got the condition signal");
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto error;
+ }
+
+ check_buffer = g_queue_peek_head (cur_stream->queue);
+ if (!strcmp ((const char*)GST_BUFFER_DATA (check_buffer), "eos")){
+ cur_stream->eos_came = TRUE;
+ GST_DEBUG_OBJECT (submux, "Eos recieved for stream");
+ }
+ for (k = 0; k < length; k++) {
+ check_stream = g_list_nth_data(submux->streams, k);
+ if (!check_stream->eos_came) {
+ eos = FALSE;
+ break;
+ } else {
+ eos = TRUE;
+ }
+ }
+ if (eos) {
+ GST_DEBUG_OBJECT (submux, "Sending EOS to submux srcpad");
+ gst_pad_push_event(submux->srcpad, gst_event_new_eos ());
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto eos_sent;
+ }
+
+ if (!cur_stream->eos_came && (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI ||
+ submux->priv->is_internal)) {
+ GstLangStruct *lang = NULL;
+ if (submux->priv->lang_list) {
+ if (submux->cur_buf_array[i] == NULL) {
+ check_buffer = g_queue_pop_head (cur_stream->queue);
+ lang = g_list_nth_data(submux->priv->lang_list, i);
+ if (!lang->active) {
+ if(check_buffer) {
+ gst_buffer_unref(check_buffer);
+ check_buffer = NULL;
+ GST_DEBUG_OBJECT (submux, "unreffing the non-active stream[%d] buffer", i);
+ }
+ submux->cur_buf_array[i] = NULL;
+ g_mutex_unlock (cur_stream->queue_lock);
+ GST_DEBUG_OBJECT(submux,"rejecting not selected language");
+ continue;
+ } else {
+ if (!check_buffer) {
+ GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto re_pop;
+ }
+ if (!GST_BUFFER_DURATION(check_buffer)) {
+ GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
+ gst_buffer_unref (check_buffer);
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto re_pop;
+ }
+ submux->cur_buf_array[i] = check_buffer;
+ GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
+ i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
+ }
+ }
+ } else {
+ g_mutex_unlock (cur_stream->queue_lock);
+ GST_DEBUG_OBJECT(submux,"Coming to Else case lang submux->priv->lang_list %x ",submux->priv->lang_list);
+ continue;
+ }
+ } else if (!cur_stream->eos_came) {
+ /* External subtitle format other than smi */
+ if (submux->sinkpad) {
+ if (submux->cur_buf_array[i] == NULL) {
+ check_buffer = g_queue_pop_head (cur_stream->queue);
+ if (!check_buffer) {
+ GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto re_pop;
+ }
+ if (!GST_BUFFER_DURATION (check_buffer)) {
+ GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
+ gst_buffer_unref (check_buffer);
+ g_mutex_unlock (cur_stream->queue_lock);
+ goto re_pop;
+ }
+ submux->cur_buf_array[i] = check_buffer;
+ GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
+ i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
+ }
+ } else {
+ g_mutex_unlock (cur_stream->queue_lock);
+ GST_DEBUG_OBJECT(submux,"Coming to Else case submux->sinkpad %x ",submux->sinkpad);
+ continue;
+ }
+ } else {
+ GST_DEBUG_OBJECT (submux, "already received EOS on this stream[%d] and cur_buf_array[idx] = NULL", i);
+ submux->cur_buf_array[i] = NULL;
+ }
+
+ g_mutex_unlock (cur_stream->queue_lock);
+ GST_DEBUG_OBJECT (submux, "After unlocking in loop and signal queue full");
+ }
+
+ push_list = gst_submux_muxing (submux);
+ if (push_list) {
+ guint idx = 0;
+ GST_LOG_OBJECT (submux, "length of push list = %d", g_list_length (push_list));
+
+ for (idx = 0; idx < g_list_length (push_list); idx++) {
+ push_buf = g_list_nth_data (push_list, idx);
+
+ if (push_buf) {
+ GST_DEBUG_OBJECT (submux, "pushing buffer : ts = %"GST_TIME_FORMAT", "
+ "dur = %"GST_TIME_FORMAT" and data %s ...",
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (push_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (push_buf)), (gchar*)GST_BUFFER_DATA (push_buf));
+
+ fret = gst_pad_push (submux->srcpad, push_buf);
+ if (fret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (submux, "failed to push buffer. reason : %s", gst_flow_get_name (fret));
+ /* clean any left buffers in push_list */
+ idx++;
+ for (; idx < g_list_length (push_list); idx++) {
+ push_buf = g_list_nth_data (push_list, idx);
+ gst_buffer_unref (push_buf);
+ }
+ g_list_free (push_list);
+ goto error;
+ }
+ }
+ }
+
+ g_list_free (push_list);
+ }
+
+ GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
+
+ return;
+
+eos_sent:
+error:
+ GST_WARNING_OBJECT (submux->srcpad, "Pausing the push task...");
+ if (fret < GST_FLOW_UNEXPECTED) {
+ GST_ERROR_OBJECT (submux, "Crtical error in push loop....");
+ GST_ELEMENT_ERROR (submux, CORE, PAD, ("failed to push. reason - %s", gst_flow_get_name (fret)), (NULL));
+ }
+ gst_pad_pause_task (submux->srcpad);
+ GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
+ return;
+}
+
+////////////////////////////////////////////////////////
+// Plugin Utility Functions //
+////////////////////////////////////////////////////////
+/*
+**
+** Description : De-Initializing the submux private structure
+** Params : (1) submux instance
+** return : TRUE
+** Comments :
+**
+*/
+static gboolean
+gst_submux_deinit_private_values(Gstsubmux *submux)
+{
+ guint idx = 0;
+ GST_DEBUG_OBJECT (submux, "deinit priv values");
+
+ submux->priv->first_buffer = FALSE;
+ submux->priv->parser_type = 0;
+ if (submux->priv->lang_list && !submux->priv->is_internal) {
+ g_list_free (submux->priv->lang_list);
+ submux->priv->lang_list = NULL;
+ }
+ for (idx = 0; idx < submux->priv->stream_count; idx++) {
+ submux->cur_buf_array[idx] = NULL;
+ }
+ if (submux->cur_buf_array) {
+ g_free (submux->cur_buf_array);
+ submux->cur_buf_array = NULL;
+ }
+
+ submux->priv->is_internal = FALSE;
+ submux->priv->stream_count = 0;
+
+ return TRUE;
+}
+
+static gboolean
+gst_submux_plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "submux", GST_RANK_PRIMARY, GST_TYPE_SUBMUX);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,GST_VERSION_MINOR,"submux","submux",gst_submux_plugin_init,"0.10.36","Proprietary","Samsung Electronics Co","http://www.samsung.com")
--- /dev/null
+/*
+ * N-Project
+ * Copyright (c) 2008 Samsung Electronics, Inc.
+ * All rights reserved.
+ *
+ * This software is a confidential and proprietary information
+ * of Samsung Electronics, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Samsung Electronics.
+ */
+
+/*
+ * This file defines the functions of Gstreamer plug-in for submux
+ *
+ * @file
+ * @author Deepak Singh (deep.singh@samsung.com)
+ * @version 1.0
+ * @usage
+ * @brief This plug-in is targeted to be used for ARM MP4ASP decoding.
+ */
+
+/*! Revision History:
+ *! ---------------------------------------------------------------------------
+ *! DATE | AUTHOR | COMMENTS
+ *! ---------------------------------------------------------------------------
+ *! 17-Jan-2014 deep.singh@samsung.com created
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/gsterror.h>
+#include <glib.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/app/gstappsink.h>
+#include <gst/base/gstadapter.h>
+G_BEGIN_DECLS
+
+#define GST_TYPE_SUBMUX (gst_submux_get_type())
+#define GST_SUBMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SUBMUX,Gstsubmux))
+#define GST_SUBMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SUBMUX,GstsubmuxClass))
+#define GST_SUBMUX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_SUBMUX,GstsubmuxClass))
+#define GST_IS_SUBMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SUBMUX))
+#define GST_IS_SUBMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SUBMUX))
+#define GST_SUBMUX_CAST(obj) ((Gstsubmux *)(obj))
+#define MAX_LANG 10
+
+typedef struct _Gstsubmux Gstsubmux;
+typedef struct _GstsubmuxClass GstsubmuxClass;
+typedef struct _GstsubmuxPrivate GstsubmuxPrivate;
+typedef struct _LanguageStruct GstLangStruct;
+typedef struct _GstSubMuxStream GstSubmuxStream;
+typedef struct _GstSubMuxPipe SubMuxPipe;
+
+typedef enum
+{
+ GST_SUB_PARSE_FORMAT_UNKNOWN = 0,
+ GST_SUB_PARSE_FORMAT_MDVDSUB = 1,
+ GST_SUB_PARSE_FORMAT_SUBRIP = 2,
+ GST_SUB_PARSE_FORMAT_MPSUB = 3,
+ GST_SUB_PARSE_FORMAT_SAMI = 4,
+ GST_SUB_PARSE_FORMAT_TMPLAYER = 5,
+ GST_SUB_PARSE_FORMAT_MPL2 = 6,
+ GST_SUB_PARSE_FORMAT_SUBVIEWER = 7,
+ GST_SUB_PARSE_FORMAT_DKS = 8,
+ GST_SUB_PARSE_FORMAT_QTTEXT = 9
+} GstSubMuxFormat;
+typedef enum
+{
+ GST_SUB_PARSE_REGEX_UNKNOWN = 0,
+ GST_SUB_PARSE_REGEX_MDVDSUB = 1,
+ GST_SUB_PARSE_REGEX_SUBRIP = 2,
+ GST_SUB_PARSE_REGEX_DKS = 3,
+} GstSubMuxRegex;
+struct _GstSubMuxPipe
+{
+ GstElement *pipe;
+ GstElement *appsrc;
+ GstElement *appsink;
+ GstElement *parser;
+ GstPad *app_sinkpad;
+};
+struct _GstSubMuxStream
+{
+ void *parent;
+ gboolean need_segment;
+ gboolean discont_came;
+ gboolean flushing;
+ SubMuxPipe pipe_struc;
+ gboolean eos_sent;
+ gboolean eos_came;
+ GstClockTime duration;
+ GstClockTime last_ts; /* last timestamp of subtitle present in subtitle file*/
+ GstClockTime eos_ts;
+ GstClockTime seek_ts;
+ GstBuffer *buffer;
+ GQueue *queue;
+ GMutex *queue_lock;
+ GCond *queue_empty;
+ gboolean flush_done;
+};
+struct _Gstsubmux
+{
+ GstElement element;
+
+ /*< private >*/
+ GstsubmuxPrivate *priv;
+
+ /* pads */
+ guint sinkpads_count;
+ gboolean external_sinkpad;
+ gboolean stop_loop;
+ GstPad *srcpad;
+ GList *sinkpad;
+ gboolean pipeline_made;
+ //GstPad *sinkpad;
+ GList *buffer_list;
+ gboolean init_done;
+ GList *msl_streams;
+ GList *streams;
+ gboolean flushing;
+ gboolean need_segment;
+ GstSubmuxStream muxed_stream;
+ gchar *encoding;
+ gchar *detected_encoding;
+ gint fps_n, fps_d;
+ GstSegment segment;
+ GstTask *loop_task;
+ GstSeekFlags segment_flags;
+ gboolean seek_came ;
+ gboolean finalize;
+ gboolean langlist_msg_posted;
+ GstBuffer **cur_buf_array;
+ gchar* external_filepath;
+};
+
+struct _GstsubmuxPrivate
+{
+ gboolean first_buffer;
+ GstSubMuxFormat parser_type;
+ GList *lang_list;
+ gboolean is_internal;
+ guint stream_count;
+};
+
+
+struct _LanguageStruct
+{
+ gchar *language_code;
+ gchar *language_key;
+ gboolean active;
+};
+
+struct _GstsubmuxClass
+{
+ GstElementClass parent_class;
+};
+
+GType gst_submux_get_type (void);
+
+G_END_DECLS
+
+
gst_mytoggle_class_init (GstMytoggleClass * klass)
{
GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
GstBaseTransformClass *gstbasetrans_class;
gobject_class = G_OBJECT_CLASS (klass);
- gstelement_class = GST_ELEMENT_CLASS (klass);
gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mytoggle_set_property);
--- /dev/null
+# plugindir is set in configure
+
+##############################################################################
+# change libgstplugin.la to something more suitable, e.g. libmysomething.la #
+##############################################################################
+plugin_LTLIBRARIES = libgstxvimagesrc.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
+libgstxvimagesrc_la_SOURCES = gstxvimagesrc.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstxvimagesrc_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(TBM_CFLAGS) $(DRI2_CFLAGS) $(X11_CFLAGS) $(XEXT_CFLAGS) $(XV_CFLAGS) $(XDAMAGE_CFLAGS) $(DRM_DEVEL_CFLAGS) $(DRM_CFLAGS)
+libgstxvimagesrc_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(TBM_LIBS) $(DRI2_LIBS) $(X11_LIBS) $(XEXT_LIBS) $(XV_LIBS) $(XDAMAGE_LIBS) $(DRM_LIBS) $(DRM_DEVEL_LIBS) $(DRM_SLP_MSM)
+libgstxvimagesrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gstxvimagesrc.h
--- /dev/null
+/*
+ * xvimagesrc
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Hyunil Park <hyunil46.park@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
+ *
+ */
+/**
+* SECTION:element-xvimagesrc
+*
+* xvimagesrc captures frame buffer which includes the application data along with video layer data
+* from the XServer and pushes the data to the downstream element.
+*
+*
+* <refsect2>
+* <title>Example launch line</title>
+* |[
+* gst-launch xvimagesrc ! "video/x-raw-yuv, width=720, height=1280, framerate=(fraction)30/1, format=(fourcc)ST12" ! fakesink
+* ]| captures the frame buffer from the XServer and send the buffers to a fakesink.
+* </refsect2>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstxvimagesrc.h"
+
+/* headers for drm */
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <X11/Xmd.h>
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <dri2.h>
+#include <drm.h>
+#include <exynos_drm.h>
+
+GST_DEBUG_CATEGORY_STATIC (xvimagesrc_debug);
+#define GST_CAT_DEFAULT xvimagesrc_debug
+
+
+#define GST_XV_IMAGE_SRC_CAPS \
+ "video/x-raw-rgb, " \
+ "bpp = (int) 32, " \
+ "depth = (int) 24, " \
+ "endianness = (int) BIG_ENDIAN, " \
+ "red_mask = (int) 0xFF000000, " \
+ "green_mask = (int) 0x00FF0000, " \
+ "blue_mask = (int) 0x0000FF00, " \
+ "width = (int) [ 16, 4096 ], " \
+ "height = (int) [ 16, 4096 ], " \
+ "framerate = (fraction) [0/1, 2147483647/1];" \
+ "video/x-raw-yuv," \
+ "format = (fourcc) { SN12, ST12, NV12 }, " \
+ "width = (int) [ 1, 4096 ], " \
+ "height = (int) [ 1, 4096 ], " \
+ "framerate = (fraction) [0/1, 2147483647/1];" \
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS(GST_XV_IMAGE_SRC_CAPS)
+);
+
+enum
+{
+ PROP_0,
+ PROP_AUDIO_LATENCY,
+ PROP_SECURE_MODE,
+ PROP_DISPLAY_ROTATE,
+};
+
+enum
+{
+ VIDEO_TYPE_UI,
+ VIDEO_TYPE_VIDEO_WITH_UI,
+ VIDEO_TYPE_VIDEO_ONLY,
+};
+
+enum
+{
+ SIGNAL_UI_ONLY = 0,
+ SIGNAL_VIDEO_WITH_UI,
+ SIGNAL_VIDEO_ONLY,
+ SIGNAL_SELECTION_NOTIFY,
+ SIGNAL_LAST
+};
+
+#define GEM_NAME_MAX 10
+
+#define SCMN_CS_YUV420 1 /* Y:U:V 4:2:0 */
+#define SCMN_CS_I420 SCMN_CS_YUV420 /* Y:U:V */
+#define SCMN_CS_NV12 6
+#define SCMN_CS_NV12_T64X32 11 /* 64x32 Tiled NV12 type */
+#define SCMN_CS_UYVY 100
+#define SCMN_CS_YUYV 101
+#define SCMN_CS_YUY2 SCMN_CS_YUYV
+
+/* max channel count *********************************************************/
+#define SCMN_IMGB_MAX_PLANE (4)
+
+/* image buffer definition ***************************************************
+
+ +------------------------------------------+ ---
+ | | ^
+ | a[], p[] | |
+ | +---------------------------+ --- | |
+ | | | ^ | |
+ | |<---------- w[] ---------->| | | |
+ | | | | | |
+ | | | |
+ | | | h[] | e[]
+ | | | |
+ | | | | | |
+ | | | | | |
+ | | | v | |
+ | +---------------------------+ --- | |
+ | | v
+ +------------------------------------------+ ---
+
+ |<----------------- s[] ------------------>|
+*/
+
+typedef struct
+{
+ /* width of each image plane */
+ int w[SCMN_IMGB_MAX_PLANE];
+ /* height of each image plane */
+ int h[SCMN_IMGB_MAX_PLANE];
+ /* stride of each image plane */
+ int s[SCMN_IMGB_MAX_PLANE];
+ /* elevation of each image plane */
+ int e[SCMN_IMGB_MAX_PLANE];
+ /* user space address of each image plane */
+ void *a[SCMN_IMGB_MAX_PLANE];
+ /* physical address of each image plane, if needs */
+ void *p[SCMN_IMGB_MAX_PLANE];
+ /* color space type of image */
+ int cs;
+ /* left postion, if needs */
+ int x;
+ /* top position, if needs */
+ int y;
+ /* to align memory */
+ int __dummy2;
+ /* arbitrary data */
+ int data[16];
+ /* dmabuf fd */
+ gint32 fd[SCMN_IMGB_MAX_PLANE];
+ /* flag for buffer share */
+ int buf_share_method;
+ /* Y plane size in case of ST12 */
+ int y_size;
+ /* UV plane size in case of ST12 */
+ int uv_size;
+
+ /* Tizen buffer object of each image plane */
+ void *bo[SCMN_IMGB_MAX_PLANE];
+
+ /* JPEG data */
+ void *jpeg_data;
+ /* JPEG size */
+ int jpeg_size;
+
+ /* tzmem buffer */
+ int tz_enable;
+} SCMN_IMGB;
+
+typedef struct
+{
+ void *address[GEM_NAME_MAX];
+ int buffer_size[GEM_NAME_MAX];
+ int name[GEM_NAME_MAX];
+ gint32 fd[GEM_NAME_MAX];
+ gint32 handle[GEM_NAME_MAX];
+} GEM_MMAP;
+
+typedef enum {
+ BUF_SHARE_METHOD_PADDR = 0,
+ BUF_SHARE_METHOD_FD
+} buf_share_method_t;
+
+typedef struct GstXvImageOutBuffer GstXvImageOutBuffer;
+
+struct GstXvImageOutBuffer {
+ GstBuffer buffer;
+ int fd_name;
+ int YBuf;
+ GstXVImageSrc * xvimagesrc;
+};
+
+
+
+#define DEFAULT_USER_AGENT "GStreamer xvimagesrc "
+
+//#define _MAKE_DUMP
+#ifdef _MAKE_DUMP
+#define YUV_FRAME_SIZE 3110400
+#define YUV_720_FRAME_SIZE 1382400
+#define YUV_VGA_FRAME_SIZE 460800
+
+static int g_prev_frame[YUV_FRAME_SIZE] = {0,};
+static int g_dump_frame[100][YUV_720_FRAME_SIZE];
+//static int g_dump_frame[200][YUV_VGA_FRAME_SIZE];
+static int f_idx = 0;
+static int f_done = 0;
+#endif
+
+static guint gst_xv_image_src_signals[SIGNAL_LAST] = { 0 };
+
+//#define COUNT_FRAMES
+#ifdef COUNT_FRAMES
+gchar old_time[10] = {0, };
+#endif
+static gboolean error_caught = FALSE;
+
+#define HANDLE_OUTBUF_UNREF
+
+#ifdef HANDLE_OUTBUF_UNREF
+#define BUFFER_COND_WAIT_TIMEOUT 1000000
+#define GST_TYPE_GST_XV_IMAGE_OUT_BUFFER (gst_xv_image_out_buffer_get_type())
+#define GST_IS_GST_XV_IMAGE_OUT_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GST_XV_IMAGE_OUT_BUFFER))
+static GstBufferClass *gst_xv_image_out_buffer_parent_class = NULL;
+static void gst_xv_image_out_buffer_class_init(gpointer g_class, gpointer class_data);
+static void gst_xv_image_out_buffer_finalize(GstXvImageOutBuffer *buffer);
+static GstXvImageOutBuffer *gst_xv_image_out_buffer_new(GstXVImageSrc *src);
+#endif
+static GstStateChangeReturn gst_xv_image_src_change_state (GstElement * element, GstStateChange transition);
+static void gst_xv_image_src_finalize (GObject * gobject);
+
+static void gst_xv_image_src_set_property (GObject *object, guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_xv_image_src_get_property (GObject *object, guint prop_id, GValue * value, GParamSpec * pspec);
+static GstFlowReturn gst_xv_image_src_create (GstPushSrc * psrc, GstBuffer ** outbuf);
+static gboolean gst_xv_image_src_start (GstBaseSrc * bsrc);
+static gboolean gst_xv_image_src_stop (GstBaseSrc * bsrc);
+static gboolean gst_xv_image_src_get_size (GstBaseSrc * bsrc, guint64 * size);
+static gboolean gst_xv_image_src_is_seekable (GstBaseSrc * bsrc);
+static gboolean gst_xv_image_src_query (GstBaseSrc * bsrc, GstQuery * query);
+static gboolean gst_xv_image_src_unlock (GstBaseSrc * bsrc);
+static gboolean gst_xv_image_src_unlock_stop (GstBaseSrc * bsrc);
+static gboolean gst_xv_image_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
+
+static tbm_bufmgr bufmgr_get (Display *dpy, Pixmap pixmap);
+static int port_get (GstXVImageSrc * src, unsigned int id);
+static void pixmap_update (GstXVImageSrc * src,Display *dpy, tbm_bufmgr bufmgr, Pixmap pixmap,
+ int x, int y, int width, int height);
+static Pixmap pixmap_create (GstXVImageSrc * src, Display *dpy, int width, int height);
+
+static void* gst_xv_image_src_update_thread (void * asrc);
+static gboolean xvimagesrc_thread_start(GstXVImageSrc *src);
+static void drm_init(GstXVImageSrc *src);
+static void drm_finalize(GstXVImageSrc *src);
+static gint32 drm_convert_gem_to_fd(int * gemname_cnt, int drm_fd, unsigned int name, void * data, void **virtual_address);
+static void gst_xv_get_image_sleep(void *asrc, long duration);
+
+static void
+_do_init (GType type)
+{
+ GST_DEBUG_CATEGORY_INIT (xvimagesrc_debug, "xvimagesrc", 0, "Xv image src");
+}
+
+GST_BOILERPLATE_FULL (GstXVImageSrc, gst_xv_image_src, GstPushSrc, GST_TYPE_PUSH_SRC, _do_init);
+
+#ifdef HANDLE_OUTBUF_UNREF
+static void gst_xv_image_out_buffer_class_init(gpointer g_class, gpointer class_data)
+{
+ GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS(g_class);
+ gst_xv_image_out_buffer_parent_class = g_type_class_peek_parent(g_class);
+ mini_object_class->finalize = (GstMiniObjectFinalizeFunction)gst_xv_image_out_buffer_finalize;
+}
+
+static GType gst_xv_image_out_buffer_get_type(void)
+{
+ static GType _gst_gst_xv_image_out_buffer_type;
+
+ if (G_UNLIKELY(_gst_gst_xv_image_out_buffer_type == 0)) {
+ static const GTypeInfo gst_xv_image_out_buffer_info = {
+ sizeof (GstBufferClass),
+ NULL,
+ NULL,
+ gst_xv_image_out_buffer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstXvImageOutBuffer),
+ 0,
+ NULL,
+ NULL
+ };
+ _gst_gst_xv_image_out_buffer_type = g_type_register_static(GST_TYPE_BUFFER,
+ "GstOmxOutBuffer",
+ &gst_xv_image_out_buffer_info, 0);
+ }
+ return _gst_gst_xv_image_out_buffer_type;
+}
+#ifdef DEBUG_BUFFER
+ int value[5] ={0};
+ static int value_count =0;
+#endif
+
+static GstStateChangeReturn
+gst_xv_image_src_change_state (GstElement * element, GstStateChange transition)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (element);
+
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+ 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:
+ src->pause_cond_var = FALSE;
+ g_cond_signal(src->pause_cond);
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_WARNING("GST_STATE_CHANGE_PLAYING_TO_PAUSED: START");
+ src->pause_cond_var = TRUE;
+ g_cond_wait(src->pause_resp, src->pause_resp_lock);
+ GST_WARNING("GST_STATE_CHANGE_PLAYING_TO_PAUSED: End");
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_WARNING("GST_STATE_CHANGE_PAUSED_TO_READY");
+ src->thread_return = TRUE;
+ g_cond_signal(src->pause_cond);
+ g_cond_signal(src->queue_cond);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default :
+ break;
+ }
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ return result;
+}
+
+
+static void gst_xv_image_out_buffer_finalize(GstXvImageOutBuffer *buffer)
+{
+ Atom atom_retbuf = 0;
+ if (buffer->xvimagesrc->thread_return) {
+ GST_INFO("xvimagesrc is being shutdown");
+ return;
+ }
+
+ g_mutex_lock (buffer->xvimagesrc->dpy_lock);
+ atom_retbuf = XInternAtom (buffer->xvimagesrc->dpy, "_USER_WM_PORT_ATTRIBUTE_RETURN_BUFFER", False);
+ XvSetPortAttribute (buffer->xvimagesrc->dpy, buffer->xvimagesrc->p, atom_retbuf, buffer->YBuf); //data->YBuf is gemname, refer to drm_convert_gem_to_fd
+ g_mutex_unlock (buffer->xvimagesrc->dpy_lock);
+ g_cond_signal(buffer->xvimagesrc->buffer_cond);
+ GST_INFO(" xvimagesrc = %p, gem_name =%d, fd_name =%d", buffer->xvimagesrc, buffer->YBuf, buffer->fd_name);
+#ifdef DEBUG_BUFFER
+ int i = 0;
+ for(i=0 ; i<5; i++){
+ if (value[i] == buffer->YBuf) {
+ value[i]=0;
+ GST_ERROR("value[%d]=%d", i, value[i]);
+ }
+ }
+#endif
+ return;
+}
+
+
+static GstXvImageOutBuffer *gst_xv_image_out_buffer_new(GstXVImageSrc *src)
+{
+ GstXvImageOutBuffer *newbuf = NULL;
+ GST_LOG("gst_omx_out_buffer_new");
+
+ newbuf = (GstXvImageOutBuffer *)gst_mini_object_new(GST_TYPE_GST_XV_IMAGE_OUT_BUFFER);
+ if(!newbuf)
+ {
+ GST_ERROR("gst_omx_out_buffer_new out of memory");
+ return NULL;
+ }
+ GST_LOG("creating buffer : %p", newbuf);
+ newbuf->xvimagesrc = gst_object_ref(GST_OBJECT(src));
+ newbuf->fd_name = 0;
+ newbuf->YBuf =0;
+ return newbuf;
+}
+#endif
+
+static void
+gst_xv_image_src_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 (&srctemplate));
+ gst_element_class_set_details_simple (element_class, "XServer Display FB video source",
+ "Source/video",
+ "Receive frame buffer data from XServer and passes to next element",
+ "");
+}
+
+static void
+gst_xv_image_src_class_init (GstXVImageSrcClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstBaseSrcClass *gstbasesrc_class;
+ GstPushSrcClass *gstpushsrc_class;
+ GstElementClass *gstelement_class;
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesrc_class = (GstBaseSrcClass *) klass;
+ gstpushsrc_class = (GstPushSrcClass *) klass;
+ gobject_class->set_property = gst_xv_image_src_set_property;
+ gobject_class->get_property = gst_xv_image_src_get_property;
+ gobject_class->finalize = gst_xv_image_src_finalize;
+
+ gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_xv_image_src_start);
+ gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_xv_image_src_stop);
+ gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_xv_image_src_unlock);
+ gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_xv_image_src_unlock_stop);
+ gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_xv_image_src_get_size);
+ gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_xv_image_src_is_seekable);
+ gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_xv_image_src_query);
+ gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_src_setcaps);
+ gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_xv_image_src_create);
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_xv_image_src_change_state);
+ g_object_class_install_property (gobject_class, PROP_DISPLAY_ROTATE,
+ g_param_spec_uint64 ("display-rotate",
+ "display-rotate",
+ "Display rotate info",
+ 0, G_MAXUINT64, 0, G_PARAM_READWRITE));
+
+ gst_xv_image_src_signals[SIGNAL_UI_ONLY] =
+ g_signal_new ("ui-only", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstXVImageSrcClass, ui_only), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ gst_xv_image_src_signals[SIGNAL_VIDEO_WITH_UI] =
+ g_signal_new ("video-with-ui", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstXVImageSrcClass, video_with_ui), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ gst_xv_image_src_signals[SIGNAL_VIDEO_ONLY] =
+ g_signal_new ("video-only", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstXVImageSrcClass, video_only), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ gst_xv_image_src_signals[SIGNAL_SELECTION_NOTIFY] =
+ g_signal_new ("selection-notify", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstXVImageSrcClass, selection_notify), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+}
+
+static gboolean gst_xv_image_src_get_frame_size(int fourcc, int width, int height, unsigned int *outsize)
+{
+ switch (fourcc) {
+/* case GST_MAKE_FOURCC('I','4','2','0'): // V4L2_PIX_FMT_YUV420
+ *outsize = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height);
+ *outsize += 2 * ((GST_ROUND_UP_8 (width) / 2) * (GST_ROUND_UP_2 (height) / 2));
+ break;*/
+ case GST_MAKE_FOURCC('N','V','1','2'): // V4L2_PIX_FMT_NV12 non-linear
+ GST_INFO("NV12");
+ *outsize = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height);
+ *outsize += (GST_ROUND_UP_4 (width) * height) / 2;
+ break;
+ case GST_MAKE_FOURCC('S','N','1','2'): // V4L2_PIX_FMT_NV12 non-linear
+ GST_INFO("SN12");
+ *outsize = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height);
+ *outsize += (GST_ROUND_UP_4 (width) * height) / 2;
+ break;
+ case GST_MAKE_FOURCC('S','T','1','2'): // V4L2_PIX_FMT_NV12 tiled non-linear
+ GST_INFO("ST12");
+ *outsize = GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height);
+ *outsize += (GST_ROUND_UP_4 (width) * height) / 2;
+ break;
+ case GST_MAKE_FOURCC('R','G','B','4'):
+ /* jpeg size can't be calculated here. */
+ *outsize = width * height * 4;
+ break;
+ default:
+ /* unkown format!! */
+ *outsize = 0;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_xv_image_src_parse_caps (const GstCaps * caps, guint32 *fourcc,
+ gint * width, gint * height, gint * rate_numerator, gint * rate_denominator, unsigned int *framesize)
+{
+ const GstStructure *structure;
+ GstPadLinkReturn ret = TRUE;
+ const GValue *framerate;
+ const char *media_type = NULL;
+ GST_DEBUG ("parsing caps");
+ if (gst_caps_get_size (caps) < 1) return FALSE;
+ GST_INFO("xvimagesrc src caps:%"GST_PTR_FORMAT, caps);
+ structure = gst_caps_get_structure (caps, 0);
+ ret = gst_structure_get_int (structure, "width", width);
+ if(!ret) {
+ GST_ERROR ("xvimagesrc width not specified in caps");
+ goto error;
+ }
+ ret = gst_structure_get_int (structure, "height", height);
+ if(!ret) {
+ GST_ERROR ("xvimagesrc height not specified in caps");
+ goto error;
+ }
+ media_type = gst_structure_get_name (structure);
+ if(media_type == NULL) {
+ GST_ERROR ("xvimagesrc media type not specified in caps");
+ goto error;
+ }
+ framerate = gst_structure_get_value (structure, "framerate");
+ if (framerate) {
+ *rate_numerator = gst_value_get_fraction_numerator (framerate);
+ *rate_denominator = gst_value_get_fraction_denominator (framerate);
+ } else {
+ GST_ERROR ("xvimagesrc frametype not specified in caps");
+ goto error;
+ }
+ if (g_strcmp0 (media_type, "video/x-raw-rgb") == 0) {
+ gst_xv_image_src_get_frame_size(FOURCC_RGB32, *width, *height, framesize);
+ *fourcc = FOURCC_RGB32;
+ GST_DEBUG ("Caps set to RGB32");
+ } else if(g_strcmp0 (media_type, "video/x-raw-yuv") == 0) {
+
+ GST_INFO ("media_type is video/x-raw-yuv");
+ guint32 format = FOURCC_SN12;
+ ret = gst_structure_get_fourcc (structure, "format", &format);
+ if (!ret) GST_DEBUG ("xvimagesrc format not specified in caps, SN12 selected as default");
+ ret = gst_xv_image_src_get_frame_size(format, *width, *height, framesize);
+ if(!ret) {
+ GST_ERROR ("xvimagesrc unsupported format type specified in caps");
+ goto error;
+ }
+ *fourcc = format;
+ }
+ return TRUE;
+/* ERRORS */
+error:
+ return FALSE;
+}
+
+static gboolean gst_xv_image_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
+{
+ gboolean res = TRUE;
+ gint width, height, rate_denominator, rate_numerator;
+ GstXVImageSrc *src;
+ src = GST_XV_IMAGE_SRC (bsrc);
+ res = gst_xv_image_src_parse_caps (caps, &src->format_id, &width, &height,
+ &rate_numerator, &rate_denominator, &src->framesize);
+ if (res) {
+ /* looks ok here */
+ src->width = width;
+ src->height = height;
+ src->rate_numerator = rate_numerator;
+ src->rate_denominator = rate_denominator;
+ GST_DEBUG_OBJECT (src, "size %dx%d, %d/%d fps",
+ src->width, src->height,
+ src->rate_numerator, src->rate_denominator);
+ if(src->rate_numerator)
+ {
+ src->sleep_base_time = (long) (((int)(1000/src->rate_numerator))*1000);
+ src->sleep_limit_time = (long) (-1*src->sleep_base_time);
+ }
+ }
+ xvimagesrc_thread_start(src);
+ return res;
+}
+static void
+gst_xv_image_src_reset (GstXVImageSrc * src)
+{
+}
+
+static void
+gst_xv_image_src_init (GstXVImageSrc * src, GstXVImageSrcClass * g_class)
+{
+ src->format_id = 0;
+ src->running_time = GST_CLOCK_TIME_NONE;
+ src->frame_duration = GST_CLOCK_TIME_NONE;
+ src->virtual = NULL;
+ src->bo = NULL;
+ src->dri2_buffers = NULL;
+ src->queue_lock = g_mutex_new ();
+ src->queue = g_queue_new ();
+ src->queue_cond = g_cond_new ();
+ src->cond_lock = g_mutex_new ();
+ src->buffer_cond = g_cond_new ();
+ src->buffer_cond_lock = g_mutex_new ();
+ src->pause_cond = g_cond_new ();
+ src->pause_cond_lock = g_mutex_new ();
+ src->pause_resp = g_cond_new ();
+ src->pause_resp_lock = g_mutex_new ();
+ src->dpy_lock = g_mutex_new ();
+ src->pause_cond_var = FALSE;
+ src->drm_fd = -1;
+ src->current_data_type = VIDEO_TYPE_VIDEO_WITH_UI;
+ src->new_data_type = VIDEO_TYPE_VIDEO_WITH_UI;
+ src->get_image_overtime = 0;
+ src->get_image_overtime_cnt = 0;
+ src->gemname_cnt = 0;
+ src->tz_enable = 0;
+ src->sleep_base_time = 0;
+ src->sleep_limit_time = 0;
+ src->switching_to_udp = FALSE;
+ drm_init(src);
+ gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
+}
+
+static void
+gst_xv_image_src_finalize (GObject * gobject)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (gobject);
+ GST_DEBUG_OBJECT (src, "finalize");
+ g_mutex_free (src->queue_lock);
+ drm_finalize(src);
+ G_OBJECT_CLASS (parent_class)->finalize (gobject);
+}
+static void drm_init(GstXVImageSrc *src)
+{
+ Display *dpy;
+ int eventBase, errorBase;
+ int dri2Major, dri2Minor;
+ char *driverName, *deviceName;
+ struct drm_auth auth_arg = {0};
+
+ src->drm_fd = -1;
+ dpy = XOpenDisplay(0);
+
+ /* DRI2 */
+ if (!DRI2QueryExtension(dpy, &eventBase, &errorBase)) {
+ GST_ERROR("DRI2QueryExtension !!");
+ return;
+ }
+ if (!DRI2QueryVersion(dpy, &dri2Major, &dri2Minor)) {
+ GST_ERROR("DRI2QueryVersion !!");
+ return;
+ }
+ if (!DRI2Connect(dpy, RootWindow(dpy, DefaultScreen(dpy)), &driverName, &deviceName)) {
+ GST_ERROR("DRI2Connect !!");
+ return;
+ }
+ GST_INFO("Open drm device : %s", deviceName);
+
+ /* get the drm_fd though opening the deviceName */
+ src->drm_fd = open(deviceName, O_RDWR);
+ if (src->drm_fd < 0) {
+ GST_ERROR("cannot open drm device (%s)", deviceName);
+ return;
+ }
+
+ /* get magic from drm to authentication */
+ if (ioctl(src->drm_fd, DRM_IOCTL_GET_MAGIC, &auth_arg)) {
+ GST_ERROR("cannot get drm auth magic");
+ close(src->drm_fd);
+ src->drm_fd = -1;
+ return;
+ }
+ if (!DRI2Authenticate(dpy, RootWindow(dpy, DefaultScreen(dpy)), auth_arg.magic)) {
+ GST_ERROR("cannot get drm authentication from X");
+ close(src->drm_fd);
+ src->drm_fd = -1;
+ return;
+ }
+}
+static void drm_finalize(GstXVImageSrc *src)
+{
+ if (src->drm_fd >= 0) {
+ close(src->drm_fd);
+ src->drm_fd = -1;
+ }
+}
+
+static gint32 drm_convert_gem_to_fd(int *gemname_cnt, int drm_fd, unsigned int name, void *data, void **virtual_address)
+{
+ g_return_val_if_fail((data != NULL),0);
+ int count=0;
+ gint32 fd = 0;
+ count = *gemname_cnt;
+ GST_DEBUG("gamname_cnt = %d", count);
+ GST_DEBUG("name = %u", name);
+
+
+ GEM_MMAP *xv_gem_mmap = NULL;
+ xv_gem_mmap = (GEM_MMAP *) data;
+ if(count >=GEM_NAME_MAX)
+ goto PASS;
+
+ if (count < GEM_NAME_MAX ) {
+ int i =0;
+ for ( i =0 ; i < GEM_NAME_MAX ; i++) {
+ if (name == xv_gem_mmap->name[i])
+ goto PASS;
+ }
+
+ struct drm_prime_handle prime;
+ struct drm_gem_open gem_open;
+ struct drm_exynos_gem_mmap gem_mmap; //for virtual address
+
+ memset (&gem_open, 0, sizeof (struct drm_gem_open));
+ gem_open.name = name;
+ if (ioctl(drm_fd, DRM_IOCTL_GEM_OPEN, &gem_open)) {
+ GST_ERROR("Gem Open failed");
+ return 0;
+ }
+ memset (&prime, 0, sizeof (struct drm_prime_handle));
+ prime.handle = gem_open.handle;
+ prime.flags = DRM_CLOEXEC;
+ /*get gem_open handle*/
+ xv_gem_mmap->handle[count] = gem_open.handle;
+ GST_DEBUG("gem_open.handle =%d, xv_gem_mmap->handle[count]=%d", gem_open.handle, xv_gem_mmap->handle[count]);
+ /*get virtual address */
+ /*set name*/
+ xv_gem_mmap->name[count] = name;
+ memset (&gem_mmap, 0, sizeof (struct drm_exynos_gem_mmap));
+ gem_mmap.handle = prime.handle;
+ gem_mmap.size = gem_open.size;
+ /*set size*/
+ xv_gem_mmap->buffer_size[count] = gem_mmap.size;
+ if (drmIoctl(drm_fd, DRM_IOCTL_EXYNOS_GEM_MMAP, &gem_mmap) !=0) {
+ GST_ERROR("Gem mmap failed [handle %d, size %d]", gem_mmap.handle, gem_mmap.size);
+ return 0;
+ }
+ /*set virtual address*/
+ xv_gem_mmap->address[count] = (void *)(gem_mmap.mapped);
+ GST_DEBUG ("%d - Virtual address[%d] = %p size=%d ", name, count, xv_gem_mmap->address[count], xv_gem_mmap->buffer_size[count] );
+
+ /*get fd*/
+ if (ioctl(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime) < 0) {
+ GST_ERROR("Gem Handle to Fd failed");
+ return 0;
+ }
+ xv_gem_mmap->fd[count] = prime.fd;
+ GST_DEBUG("fd = %d", xv_gem_mmap->fd[count]);
+ }
+
+ if (count < GEM_NAME_MAX) {
+ count ++;
+ *gemname_cnt = count;
+ }
+
+PASS:
+
+ if (name == xv_gem_mmap->name[0]) {
+ *virtual_address = xv_gem_mmap->address[0];
+ fd = xv_gem_mmap->fd[0];
+ } else if (name == xv_gem_mmap->name[1]) {
+ *virtual_address = xv_gem_mmap->address[1];
+ fd = xv_gem_mmap->fd[1];
+ } else if (name == xv_gem_mmap->name[2]) {
+ *virtual_address = xv_gem_mmap->address[2];
+ fd = xv_gem_mmap->fd[2];
+ } else if ( name == xv_gem_mmap->name[3]) {
+ *virtual_address = xv_gem_mmap->address[3];
+ fd = xv_gem_mmap->fd[3];
+ } else if (name == xv_gem_mmap->name[4]) {
+ *virtual_address = xv_gem_mmap->address[4];
+ fd = xv_gem_mmap->fd[4];
+ } else if ( name == xv_gem_mmap->name[5]) {
+ *virtual_address = xv_gem_mmap->address[5];
+ fd = xv_gem_mmap->fd[5];
+ } else if (name == xv_gem_mmap->name[6]) {
+ *virtual_address = xv_gem_mmap->address[6];
+ fd = xv_gem_mmap->fd[6];
+ } else if (name == xv_gem_mmap->name[7]) {
+ *virtual_address = xv_gem_mmap->address[7];
+ fd = xv_gem_mmap->fd[7];
+ } else if ( name == xv_gem_mmap->name[8]) {
+ *virtual_address = xv_gem_mmap->address[8];
+ fd = xv_gem_mmap->fd[8];
+ } else if (name == xv_gem_mmap->name[9]) {
+ *virtual_address = xv_gem_mmap->address[9];
+ fd = xv_gem_mmap->fd[9];
+ }
+
+ GST_DEBUG("virtual_address = %p fd = %d", *virtual_address, fd);
+ return fd;
+}
+
+static void
+gst_xv_image_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (object);
+ GST_INFO ("set property function %x", src);
+ switch (prop_id) {
+ case PROP_DISPLAY_ROTATE:
+ src->display_rotate = g_value_get_uint64 (value);
+ GST_DEBUG("display_rotate [%d]", src->display_rotate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ return;
+}
+
+static void
+gst_xv_image_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (object);
+ GST_INFO ("get property function %x", src);
+ switch (prop_id) {
+ case PROP_DISPLAY_ROTATE:
+ g_value_set_uint64 (value, src->display_rotate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static inline GstClockTime xvimagesrc_clock (GstXVImageSrc *src)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ if(!src->base_time) {
+ src->base_time = GST_TIMEVAL_TO_TIME(tv);
+ }
+ return (GstClockTime)(GST_TIMEVAL_TO_TIME(tv) - src->base_time);
+}
+
+static gboolean gst_xv_image_src_get_timeinfo2(GstXVImageSrc *src, GstClockTime *time, GstClockTime *dur)
+{
+ int fps_nu = 0;
+ int fps_de = 0;
+ GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+ static GstClockTime prev_timestamp = 0;
+
+ GstClock *clock;
+ if (!src) {
+ GST_WARNING("Invalid pointer [handle:%p]", src);
+ return FALSE;
+ }
+ clock = gst_element_get_clock (GST_ELEMENT (src));
+ if (clock) {
+ timestamp = gst_clock_get_time (clock);
+ if(!gst_element_get_base_time (GST_ELEMENT(src)))
+ timestamp = GST_CLOCK_TIME_NONE;
+ else timestamp -= gst_element_get_base_time (GST_ELEMENT (src));
+ gst_object_unref (clock);
+ } else {
+ /* not an error not to have a clock */
+ timestamp = GST_CLOCK_TIME_NONE;
+ }
+
+ if ((timestamp - prev_timestamp <= 30000000) || (timestamp-prev_timestamp >=36000000)) {
+ //GST_ERROR("Gap is below 30ms or over 36ms!!");
+ }
+
+ /* if we have a framerate adjust timestamp for frame latency */
+ if ((int)((float)src->rate_numerator / (float)src->rate_denominator) <= 0) {
+ /*if fps is zero, auto fps mode*/
+ fps_nu = 0;
+ fps_de = 1;
+ } else {
+ fps_nu = 1;
+ fps_de = (int)((float)src->rate_numerator / (float)src->rate_denominator);
+ }
+ if (fps_nu > 0 && fps_de > 0) {
+ GstClockTime latency;
+ latency = gst_util_uint64_scale_int(GST_SECOND, fps_nu, fps_de);
+ duration = latency;
+ }
+ timestamp+=src->initial_audio_latency;
+ *time = timestamp;
+ *dur = duration;
+ prev_timestamp = timestamp;
+ return TRUE;
+}
+
+static gboolean gst_xv_image_src_get_timeinfo(GstXVImageSrc *src, GstBuffer *buffer)
+{
+ int fps_nu = 0;
+ int fps_de = 0;
+ GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+ GstClock *clock;
+ if (!src || !buffer) {
+ GST_WARNING("Invalid pointer [handle:%p, buffer:%p]", src, buffer);
+ return FALSE;
+ }
+ clock = gst_element_get_clock (GST_ELEMENT (src));
+ if (clock) {
+ timestamp = gst_clock_get_time (clock);
+ timestamp -= gst_element_get_base_time (GST_ELEMENT (src));
+ gst_object_unref (clock);
+ } else {
+ /* not an error not to have a clock */
+ timestamp = GST_CLOCK_TIME_NONE;
+ }
+
+ //timestamp = xvimagesrc_clock(src);
+ /* if we have a framerate adjust timestamp for frame latency */
+ if ((int)((float)src->rate_numerator / (float)src->rate_denominator) <= 0) {
+ /*if fps is zero, auto fps mode*/
+ fps_nu = 0;
+ fps_de = 1;
+ } else {
+ fps_nu = 1;
+ fps_de = (int)((float)src->rate_numerator / (float)src->rate_denominator);
+ }
+ if (fps_nu > 0 && fps_de > 0) {
+ GstClockTime latency;
+ latency = gst_util_uint64_scale_int(GST_SECOND, fps_nu, fps_de);
+ duration = latency;
+ }
+ GST_BUFFER_TIMESTAMP(buffer) = timestamp;
+ GST_BUFFER_DURATION(buffer) = duration;
+ return TRUE;
+}
+#ifdef COUNT_FRAMES
+static int fps = 0;
+
+static gchar *get_current_system_time()
+{
+ gchar target[10]={0,};
+ time_t t;
+ struct tm tm;
+
+ t = time(NULL);
+ tzset();
+ /*localtimer_r : available since libc 5.2.5*/
+ if(localtime_r(&t, &tm) == NULL) {
+ return NULL;
+ }
+ snprintf(target, sizeof(target), "%02i:%02i:%02i", tm.tm_hour, tm.tm_min, tm.tm_sec);
+ return g_strdup(target);
+}
+#endif
+static void gst_xv_get_image_sleep(void *asrc, long duration)
+{
+ GST_INFO("end_time duration=%d", duration);
+ if (duration < 0) return;
+ GstXVImageSrc *src = (GstXVImageSrc *)asrc;
+ g_return_if_fail(src != NULL);
+ long sleep_time = 0;
+ sleep_time = src->sleep_base_time - duration;
+
+ if (sleep_time < 0) {
+ src->get_image_overtime_cnt ++;
+ src->get_image_overtime += sleep_time;
+ if (src->get_image_overtime_cnt > 2)
+ src->get_image_overtime = 0;
+ if (src->get_image_overtime <= src->sleep_limit_time)
+ src->get_image_overtime = 0;
+ //GST_WARNING("Over Time[%d] : %d", src->get_image_overtime_cnt, src->get_image_overtime);
+ } else if (sleep_time > 0) {
+ src->get_image_overtime_cnt = 0;
+ sleep_time = sleep_time + src->get_image_overtime;
+
+ if (src->get_image_overtime < 0) {
+ //GST_WARNING("Over Time : %d, So sleep time : %d", src->get_image_overtime, sleep_time);
+ }
+
+ src->get_image_overtime = (sleep_time < 0) ? sleep_time : 0;
+
+ if (sleep_time >0) {
+ GST_INFO("end_time : sleep_time = %d", sleep_time);
+ usleep(sleep_time);
+ }
+ }
+}
+static GstFlowReturn
+gst_xv_image_src_create (GstPushSrc * psrc, GstBuffer ** buffer)
+{
+ GST_INFO("gst_xv_image_src_create");
+ GstXVImageSrc *src;
+ GstXvImageOutBuffer *outbuf = NULL;
+ src = GST_XV_IMAGE_SRC (psrc);
+ g_mutex_lock (src->queue_lock);
+ if(g_queue_is_empty (src->queue)) {
+ GST_INFO("g_queue_is_empty");
+ g_mutex_unlock (src->queue_lock);
+ GST_INFO("g_cond_wait");
+ g_cond_wait(src->queue_cond, src->cond_lock);
+ if(src->pause_cond_var) return GST_FLOW_WRONG_STATE;
+ g_mutex_lock (src->queue_lock);
+
+ if(src->switching_to_udp == TRUE) {
+ GstStructure *structure;
+ structure = gst_structure_new("Switch_udp", "switch_to_udp", G_TYPE_BOOLEAN, NULL, NULL);
+ GstEvent *event = gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM, structure);
+ GstPad *srcpad = gst_element_get_static_pad(src, "src");
+ gst_pad_push_event(srcpad, event);
+ GST_INFO("sending the event");
+ src->switching_to_udp = FALSE;
+ }
+
+ outbuf = (GstXvImageOutBuffer *)g_queue_pop_head(src->queue);
+ GST_INFO("g_queue_pop_head");
+ g_mutex_unlock (src->queue_lock);
+ }
+ else {
+ GstXvImageOutBuffer *tempbuf = NULL;
+ if((tempbuf = (GstXvImageOutBuffer*)g_queue_pop_head(src->queue)) != NULL)
+ {
+ outbuf = tempbuf; // To reduce latency, skipping the old frames and submitting only latest frames
+ g_mutex_unlock (src->queue_lock);
+ }
+ GST_INFO("g_queue_pop_head end");
+ }
+ if(outbuf == NULL) return GST_FLOW_ERROR;
+ GST_INFO("gem_name=%d, fd_name=%d, Time stamp of the buffer is %"GST_TIME_FORMAT, outbuf->YBuf, outbuf->fd_name, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(outbuf)));
+ *buffer = GST_BUFFER_CAST(outbuf);
+#ifdef COUNT_FRAMES
+ gchar *current_time = NULL;
+ current_time = get_current_system_time();
+ if (strncmp(current_time, old_time, 10) == 0) {
+ fps ++;
+ strncpy(old_time, current_time, 10);
+ } else {
+ g_printf("xvimagesrc %s - fps : %d\n", old_time, fps);
+ fps = 1;
+ strncpy(old_time, current_time, 10);
+ }
+#endif
+ GST_INFO("return ok");
+ return GST_FLOW_OK;
+}
+
+static gboolean xvimagesrc_thread_start(GstXVImageSrc *src)
+{
+ GError *error;
+ if(!src->updates_thread) src->updates_thread = g_thread_create ((GThreadFunc) gst_xv_image_src_update_thread, src, TRUE, &error);
+ else GST_LOG_OBJECT (src, "The thread function already running");
+ return TRUE;
+}
+
+static gboolean
+gst_xv_image_src_start (GstBaseSrc * bsrc)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ if(src->format_id) xvimagesrc_thread_start(src);
+ return TRUE;
+}
+
+gboolean signal_emit_func(void *asrc)
+{
+ GstXVImageSrc *src = (GstXVImageSrc *)asrc;
+
+ if (src->current_data_type == VIDEO_TYPE_UI) {
+ src->switching_to_udp = TRUE;
+ g_signal_emit (src, gst_xv_image_src_signals[SIGNAL_UI_ONLY] , 0, NULL);
+ } else if (src->current_data_type == VIDEO_TYPE_VIDEO_ONLY) {
+ g_signal_emit (src, gst_xv_image_src_signals[SIGNAL_VIDEO_ONLY] , 0, NULL);
+ } else if (src->current_data_type == VIDEO_TYPE_VIDEO_WITH_UI) {
+ g_signal_emit (src, gst_xv_image_src_signals[SIGNAL_VIDEO_WITH_UI] , 0, NULL);
+ }
+
+ return FALSE;
+}
+
+gboolean signal_selection_emit_func(void *asrc)
+{
+ GstXVImageSrc *src = (GstXVImageSrc *)asrc;
+
+ g_signal_emit (src, gst_xv_image_src_signals[SIGNAL_SELECTION_NOTIFY] , 0, NULL);
+
+ return FALSE;
+}
+
+static int
+gst_xvimagesrc_handle_xerror (Display * display, XErrorEvent * xevent)
+{
+ char error_msg[1024];
+
+ XGetErrorText (display, xevent->error_code, error_msg, 1024);
+ GST_DEBUG ("XError. error: %s", error_msg);
+ error_caught = TRUE;
+ return 0;
+}
+
+//static gboolean first_frame=TRUE;
+static int framecount=0;
+static void* gst_xv_image_src_update_thread (void * asrc)
+{
+ GstXVImageSrc *src = (GstXVImageSrc *)asrc;
+ Atom atom_data_type = 0;
+ Atom atom_secure = 0, atom_capture=0, atom_display=0;
+ Atom atom_fps = 0;
+ g_return_val_if_fail((src != NULL),NULL);
+ int i=0;
+ struct drm_gem_close gem_close;
+ GEM_MMAP *xv_gem_mmap = NULL;
+ xv_gem_mmap = (GEM_MMAP *)malloc(sizeof(GEM_MMAP));
+ g_return_val_if_fail((xv_gem_mmap != NULL),NULL);
+
+ memset(xv_gem_mmap, 0, sizeof(GEM_MMAP));
+
+ GST_LOG_OBJECT (src, "The thread function start");
+ {
+ int damage_err_base = 0;
+ Atom atom_format = 0;
+ src->dpy = XOpenDisplay (NULL);
+ src->p = port_get (src, src->format_id);
+ if (src->p < 0) goto finish;
+ /*src->width and src->height is set by caps info */
+ GST_DEBUG (" width and height of caps : %dx%d ", src->width, src->height);
+ if (src->width == 0 || src->height == 0) goto finish;
+ GST_DEBUG ("gst_xv_image_src_update_thread pixmap_create !!");
+ src->pixmap = pixmap_create (src, src->dpy, src->width, src->height);
+ GST_DEBUG ("gst_xv_image_src_update_thread pixmap_create !!");
+ src->gc = XCreateGC (src->dpy, src->pixmap, 0, 0);
+ GST_DEBUG ("gst_xv_image_src_update_thread XCreateGC !!");
+ src->bufmgr = bufmgr_get (src->dpy, src->pixmap);
+ if (!src->bufmgr) goto finish;
+ GST_DEBUG ("gst_xv_image_src_update_thread bufmgr_get !!");
+ if (!XDamageQueryExtension(src->dpy, &src->damage_base, &damage_err_base)) goto finish;
+ GST_DEBUG ("gst_xv_image_src_update_thread XDamageQueryExtension !!");
+ src->damage = XDamageCreate (src->dpy, src->pixmap, XDamageReportNonEmpty);
+ atom_format = XInternAtom (src->dpy, "_USER_WM_PORT_ATTRIBUTE_FORMAT", False);
+ atom_capture = XInternAtom (src->dpy, "_USER_WM_PORT_ATTRIBUTE_CAPTURE", False);
+ atom_display = XInternAtom (src->dpy, "_USER_WM_PORT_ATTRIBUTE_DISPLAY", False);
+
+ /*get data type*/
+ atom_data_type = XInternAtom (src->dpy, "_USER_WM_PORT_ATTRIBUTE_DATA_TYPE", False);
+ /* _USER_WM_PORT_ATTRIBUTE_STREAM_OFF is removed */
+ //src->atom_stream_off = XInternAtom (src->dpy, "_USER_WM_PORT_ATTRIBUTE_STREAM_OFF", False);
+
+ XvSetPortAttribute (src->dpy, src->p, atom_format, src->format_id);
+ XvSetPortAttribute (src->dpy, src->p, atom_capture, 2);
+ XvSetPortAttribute (src->dpy, src->p, atom_display, 1);
+
+ XvSelectPortNotify (src->dpy, src->p, 1);
+ XvGetPortAttribute (src->dpy, src->p, atom_data_type, &(src->new_data_type));
+
+
+ /* Set display type */
+ Atom atom_select_display=0;
+ Atom atom_select_display_type=0;
+ Atom atom_encoding=0;
+ XTextProperty xtp;
+ const char *display_type = "WFD";
+
+ atom_select_display = XInternAtom(src->dpy, "SEL_EXT_DISPLAY", False);
+ atom_select_display_type = XInternAtom(src->dpy, "SEL_EXT_DISPLAY_TYPE", False);
+ atom_encoding = XInternAtom(src->dpy, "UTF8_STRING", False);
+ src->win = DefaultRootWindow(src->dpy);
+
+ XSetSelectionOwner(src->dpy, atom_select_display, src->win, 0);
+
+ xtp.value = (unsigned char *)display_type;
+ xtp.format = 8;
+ xtp.encoding = atom_encoding;
+ xtp.nitems = strlen(display_type);
+ XSetTextProperty(src->dpy, src->win, &xtp, atom_select_display_type);
+ }
+
+ struct timeval start_time, end_time;
+ long duration;
+ long starttime, endtime;
+ GTimeVal timeout;
+ XEvent ev;
+ int eventcount = 0;
+ int refresh = 1;
+ void *virtual_address = NULL;
+ int (*handler) (Display *, XErrorEvent *);
+ GstXvImageOutBuffer *outbuf = NULL;
+
+ GstClockTime ts_putstill = 0;
+ GstClockTime dur_putstill = 0;
+
+ gboolean got_display_select_req = FALSE;
+
+ while(!src->thread_return) {
+ if(src->pause_cond_var == TRUE) {
+ GST_WARNING("PAUSED in thread");
+ g_cond_signal(src->pause_resp);
+ g_cond_wait(src->pause_cond,src->pause_cond_lock);
+ }
+
+ if (src->thread_return) {
+ GST_WARNING("Thread return");
+ break;
+ }
+
+ if (refresh == 1) {
+ duration = 0;
+ starttime =0;
+ endtime = 0;
+ start_time.tv_sec = 0;
+ start_time.tv_usec = 0;
+ end_time.tv_sec = 0;
+ end_time.tv_usec = 0;
+ gettimeofday(&start_time, NULL);
+ refresh = 0;
+ }
+
+ eventcount = 0;
+ virtual_address = NULL;
+ duration = 0;
+ outbuf = NULL;
+ g_mutex_lock (src->dpy_lock);
+ GST_DEBUG("[XCALL] call XSync");
+ XSync(src->dpy, 0);
+ GST_INFO ("gst_xv_image_src_update_thread XSync@@ !!");
+ g_mutex_unlock (src->dpy_lock);
+ error_caught = FALSE;
+ //GST_DEBUG ("gst_xv_image_src_update_thread XSetErrorHandler in !!");
+ handler = XSetErrorHandler (gst_xvimagesrc_handle_xerror);
+ //GST_INFO ("gst_xv_image_src_update_thread XSetErrorHandler !!");
+ //GST_INFO ("gst_xv_image_src_update_thread XvPutStill in !!");
+ g_mutex_lock (src->dpy_lock);
+ GST_DEBUG("[XCALL] call XvPutStill");
+ XvPutStill (src->dpy, src->p, src->pixmap, src->gc, 0, 0, src->width, src->height, 0, 0, src->width, src->height);
+
+ gst_xv_image_src_get_timeinfo2(src, &ts_putstill, &dur_putstill);
+
+ //GST_INFO ("gst_xv_image_src_update_thread XvPutStill !!");
+ GST_DEBUG("[XCALL] call 2nd XSync");
+ XSync (src->dpy, 0);
+ GST_DEBUG("[XCALL] call 2nd XSync done");
+ g_mutex_unlock (src->dpy_lock);
+ if (error_caught) {
+ GST_ERROR("gst_xv_image_src_update_thread error_caught is TRUE, X is out of buffers");
+ error_caught = FALSE;
+ XSetErrorHandler(handler);
+ g_get_current_time(&timeout);
+ g_time_val_add(&timeout, BUFFER_COND_WAIT_TIMEOUT);
+ if(!g_cond_timed_wait(src->buffer_cond, src->buffer_cond_lock, &timeout)) {
+ GST_ERROR("skip wating");
+ } else {
+ GST_ERROR("Signal received");
+ }
+ continue;
+ }
+ /*reset error handler*/
+ error_caught = FALSE;
+ XSetErrorHandler(handler);
+
+ //GST_INFO ("gst_xv_image_src_update_thread XSync !!");
+next_event:
+ g_mutex_lock (src->dpy_lock);
+ //GST_INFO("XNextEvent in");
+ XNextEvent (src->dpy, &ev); /* wating for x event */
+ //GST_INFO("XNextEvent out");
+ g_mutex_unlock (src->dpy_lock);
+ //GST_INFO ("gst_xv_image_src_update_thread XNextEvent !!");
+ if (ev.type == (src->damage_base + XDamageNotify)) {
+ XDamageNotifyEvent *damage_ev = (XDamageNotifyEvent *)&ev;
+ GST_INFO ("gst_xv_image_src_update_thread XDamageNotifyEvent");
+ g_mutex_lock (src->dpy_lock);
+ if (damage_ev->drawable == src->pixmap) {
+ pixmap_update (src, src->dpy, src->bufmgr, src->pixmap,
+ damage_ev->area.x,
+ damage_ev->area.y,
+ damage_ev->area.width,
+ damage_ev->area.height);
+ GST_INFO("gst_xv_image_src_update_thread pixmap_update");
+ }
+ XDamageSubtract (src->dpy, src->damage, None, None );
+ g_mutex_unlock (src->dpy_lock);
+ GST_INFO ("gst_xv_image_src_update_thread XDamageSubtract");
+ }
+ /* Added to handle display selection notify */
+ else if (ev.type == SelectionClear) {
+ XSelectionEvent *selection_ev = (XSelectionEvent*)&ev;
+
+ GST_ERROR("ev.type : %d SelectionClear", ev.type);
+ //g_timeout_add(1, signal_selection_emit_func, src);
+ }
+ else if (ev.type == SelectionRequest) {
+ XSelectionRequestEvent *selection_ev = (XSelectionRequestEvent*)&ev;
+
+ GST_ERROR("ev.type : %d SelectionRequest", ev.type);
+ src->requestor = selection_ev->requestor;
+ src->selection = selection_ev->selection;
+ src->target = selection_ev->target;
+ src->property = selection_ev->property;
+
+ got_display_select_req = TRUE;
+ g_timeout_add(1, signal_selection_emit_func, src);
+ }
+ else if (ev.type == SelectionNotify) {
+ XSelectionEvent *selection_ev = (XSelectionEvent*)&ev;
+
+ GST_ERROR("ev.type : %d SelectionNotify", ev.type);
+ //g_timeout_add(1, signal_selection_emit_func, src);
+ }
+ else if (ev.type == (src->evt_base + XvPortNotify)) {
+ XvPortNotifyEvent *notify_ev = (XvPortNotifyEvent*)&ev;
+ if (notify_ev->attribute == atom_secure) {
+ GST_WARNING ("secure attr changed : %s \n", ((int)notify_ev->value)?"Secure":"Normal");
+ src->tz_enable = (int)notify_ev->value;
+ GST_ERROR("src->tz_enable = %d", src->tz_enable);
+ }
+ else
+ if (notify_ev->attribute == atom_data_type) {
+ /* got a port notify, data_type */
+ src->new_data_type = (int)notify_ev->value;
+ if (src->current_data_type != src->new_data_type) {
+ src->current_data_type = src->new_data_type;
+ //GST_WARNING("current_data_type : %s \n", (src->current_data_type)?"Video":"UI+Video");
+ if (src->current_data_type == VIDEO_TYPE_UI) {
+ GST_WARNING("current_data_type : UI\n");
+ } else if (src->current_data_type == VIDEO_TYPE_VIDEO_WITH_UI) {
+ GST_WARNING("current_data_type : Video+UI\n");
+ } else if (src->current_data_type == VIDEO_TYPE_VIDEO_ONLY) {
+ GST_WARNING("current_data_type : VideoOnly\n");
+ }
+
+ g_timeout_add(1, signal_emit_func, src);
+ }
+ }
+ goto next_event;
+ }
+
+ if(!src->virtual) continue;
+ if(src->format_id == FOURCC_RGB32) {
+ outbuf = (GstXvImageOutBuffer*)gst_buffer_new_and_alloc (src->framesize);
+ gst_buffer_set_caps (GST_BUFFER_CAST(outbuf), GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
+ memcpy(GST_BUFFER_DATA (outbuf), src->virtual, src->framesize);
+ } else if ((src->format_id == FOURCC_SN12) || (src->format_id == FOURCC_NV12)) {
+ XV_DATA_PTR data = (XV_DATA_PTR)src->virtual;
+ int error = XV_VALIDATE_DATA (data);
+ outbuf = gst_xv_image_out_buffer_new(src);
+ if(!outbuf)
+ {
+ GST_ERROR("Out of memory");
+ continue;
+ }
+ gst_buffer_set_caps (GST_BUFFER_CAST(outbuf), GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
+ if (error == XV_HEADER_ERROR)
+ GST_ERROR ("XV_HEADER_ERROR\n");
+ else if (error == XV_VERSION_MISMATCH)
+ GST_ERROR ("XV_VERSION_MISMATCH\n");
+ else
+ {
+ SCMN_IMGB *psimgb = NULL;
+ psimgb = (SCMN_IMGB *)malloc(sizeof(SCMN_IMGB));
+ if (psimgb == NULL) {
+ GST_ERROR_OBJECT(src, "failed to alloc SCMN_IMGB");
+ return NULL;
+ }
+ memset(psimgb, 0x00, sizeof(SCMN_IMGB));
+ if (data->BufType == XV_BUF_TYPE_LEGACY) {
+ psimgb->p[0] = (void *)data->YBuf;
+ psimgb->p[1] = (void *)data->CbBuf;
+ psimgb->buf_share_method = BUF_SHARE_METHOD_PADDR;
+ psimgb->a[0] = NULL;
+ psimgb->a[1] = NULL;
+ } else if (data->BufType == XV_BUF_TYPE_DMABUF) {
+ psimgb->fd[0] = drm_convert_gem_to_fd(&src->gemname_cnt, src->drm_fd, (void *)data->YBuf, xv_gem_mmap, &virtual_address);
+ if(!virtual_address) {
+ free(psimgb);
+ psimgb = NULL;
+ continue;
+ }
+ psimgb->a[0] = virtual_address;
+ GST_DEBUG("YBuf gem to fd[0]=%d virtual_address = %p", psimgb->fd[0], psimgb->a[0]);
+ psimgb->fd[1] = drm_convert_gem_to_fd(&src->gemname_cnt, src->drm_fd, (void *)data->CbBuf, xv_gem_mmap, &virtual_address);
+ if(!virtual_address) {
+ free(psimgb);
+ psimgb = NULL;
+ continue;
+ }
+ psimgb->a[1] = virtual_address;
+ GST_DEBUG("CbBuf gem to fd[1]=%d virtual_address = %p", psimgb->fd[1], psimgb->a[1]);
+ psimgb->buf_share_method = BUF_SHARE_METHOD_FD;
+ psimgb->w[1] = src->width;
+ psimgb->h[1] = src->height >> 1;
+ psimgb->s[1] = GST_ROUND_UP_16(psimgb->w[1]);
+ psimgb->e[1] = GST_ROUND_UP_16(psimgb->h[1]);
+ psimgb->cs = SCMN_CS_NV12;
+ psimgb->tz_enable = 0;
+ }
+ psimgb->w[0] = src->width;
+ psimgb->h[0] = src->height;
+ psimgb->s[0] = GST_ROUND_UP_16(psimgb->w[0]);
+ psimgb->e[0] = GST_ROUND_UP_16(psimgb->h[0]);
+ outbuf->fd_name = psimgb->fd[0];
+ GST_BUFFER_MALLOCDATA(outbuf) = (unsigned char*)psimgb;
+ GST_BUFFER_DATA(outbuf) = src->virtual;
+ outbuf->YBuf = data->YBuf;
+ outbuf->fd_name = psimgb->fd[0];
+#ifdef DEBUG_BUFFER
+ for ( i=0; i<5 ; i++) {
+ if(value[value_count] == outbuf->YBuf ) {
+ GST_ERROR("ERROR: value[%d](%d)==YBUf(%d)", value_count, value[value_count], outbuf->YBuf);
+ }
+ }
+ value[value_count] = outbuf->YBuf ;
+ GST_ERROR("value[%d]=%d", value_count, value[value_count]);
+ if( value_count < 4){
+ value_count ++;
+ } else {
+ value_count = 0;
+ }
+#endif
+
+#ifdef _MAKE_DUMP
+ if (f_idx < 100) {
+ if (src->running_time > 14000000000 && src->running_time < 20000000000) {
+ GST_ERROR("Mem copy");
+ if (virtual_address == NULL) {
+ GST_ERROR("Mem virtual is NULL[%d]", f_idx);
+ } else {
+ memcpy(g_dump_frame[f_idx++], virtual_address, YUV_720_FRAME_SIZE);
+ //memcpy(g_dump_frame[f_idx++], virtual_address, YUV_VGA_FRAME_SIZE);
+ GST_ERROR("Mem copy done[%d]", f_idx);
+ }
+ }
+ } else {
+ if (f_done == 0) {
+ GST_ERROR("File DUMP!!");
+ FILE *fp = NULL;
+ fp = fopen("/opt/usr/media/Videos/frame.yuv", "a");
+ int i = 0;
+ for (i = 0; i < 100; i++) {
+ fwrite(g_dump_frame[i], YUV_720_FRAME_SIZE, 1, fp);
+ //fwrite(g_dump_frame[i], YUV_VGA_FRAME_SIZE, 1, fp);
+ }
+ fclose(fp);
+ f_done = 1;
+ GST_ERROR("File DUMP done!!");
+ }
+ }
+#endif
+ }
+ }
+ if(!outbuf) continue;
+ GST_BUFFER_SIZE (outbuf) = src->framesize;
+ //gst_xv_image_src_get_timeinfo(src, GST_BUFFER_CAST(outbuf));
+ GST_BUFFER_TIMESTAMP(outbuf) = ts_putstill;
+ GST_BUFFER_DURATION(outbuf) = dur_putstill;
+ src->running_time = GST_BUFFER_TIMESTAMP(outbuf);
+ src->frame_duration = GST_BUFFER_DURATION(outbuf);
+ //first_frame = FALSE;
+ g_mutex_lock (src->queue_lock);
+ g_queue_push_tail(src->queue, outbuf);
+ //GST_INFO("g_queue_push_tail");
+ g_mutex_unlock (src->queue_lock);
+ g_cond_signal(src->queue_cond);
+ //GST_INFO("g_cond_signal");
+
+ update_done:
+ if (src->virtual) tbm_bo_unmap(src->bo);
+ src->virtual = NULL;
+ GST_INFO("g_cond_signal");
+ if (src->bo) tbm_bo_unref(src->bo);
+ src->bo = NULL;
+ if (src->dri2_buffers) free(src->dri2_buffers);
+ src->dri2_buffers = NULL;
+ gettimeofday(&end_time, NULL);
+ starttime = start_time.tv_usec;
+ endtime = end_time.tv_usec;
+ //GST_INFO("star_time: %d, end_time:%d", starttime, endtime);
+ if (endtime > starttime) {
+ //GST_INFO("end_time > start_time");
+ duration = endtime - starttime;
+ } else {
+ //GST_INFO("end_time.tv_usec < start_time.tv_usec");
+ endtime=endtime+1000000;
+ //GST_INFO("end_time =%d", endtime);
+ duration = endtime -starttime;
+ }
+
+ GST_INFO("end_time duration = %d", duration);
+ //if (src->sleep_base_time > duration)
+ gst_xv_get_image_sleep (src, duration);
+
+ refresh = 1;
+ GST_INFO("gst_xv_image_src_update_thread cleanup !!");
+ }
+ GST_WARNING("gst_xv_image_src_update_thread loop ended");
+
+ for ( i=0 ; i < GEM_NAME_MAX ; i++) {
+ /*gem munmap*/
+ if (xv_gem_mmap->address[i]) {
+ if (-1 == munmap(xv_gem_mmap->address[i],xv_gem_mmap->buffer_size[i])) {
+ GST_ERROR ("munmap failed");
+ return NULL;
+ }
+ }
+ if (xv_gem_mmap->handle[i]) {
+ gem_close.handle = xv_gem_mmap->handle[i];
+ if (ioctl(src->drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close)) {
+ GST_ERROR("Gem Close failed");
+ }
+ }
+ if (xv_gem_mmap->fd[i]) {
+ close(xv_gem_mmap->fd[i]);
+ }
+ }
+ if (xv_gem_mmap) {
+ free(xv_gem_mmap);
+ xv_gem_mmap = NULL;
+ }
+ GST_LOG_OBJECT (src, "The thread function cleanup");
+
+ XvStopVideo(src->dpy, src->p, src->pixmap);
+
+ if (src->bufmgr) {
+ tbm_bufmgr_deinit (src->bufmgr);
+ src->bufmgr = NULL;
+ }
+ if (src->p > 0) {
+ XvUngrabPort (src->dpy, src->p, 0);
+ src->p = 0;
+ }
+
+ if (got_display_select_req) {
+ /* Notify miracast-destroyed to X */
+ GST_WARNING_OBJECT (src, "There is display selection request");
+ XEvent xev;
+ XSelectionEvent xnotify;
+
+ xnotify.type = SelectionNotify;
+ xnotify.display = src->dpy;
+ xnotify.requestor = src->requestor;
+ xnotify.selection = src->selection;
+ xnotify.target = src->target;
+ xnotify.property = src->property;
+ xnotify.time = 0;
+ xnotify.send_event = True;
+ xnotify.serial = 0;
+ xev.xselection = xnotify;
+ if (XSendEvent(src->dpy, src->requestor, False, 0, &xev) < 0) {
+ GST_ERROR("XSendEvent failed!");
+ } else {
+ GST_INFO("XSendEvent success!");
+ }
+ }
+
+ if (src->gc) {
+ XFreeGC (src->dpy, src->gc);
+ src->gc = NULL;
+ }
+ if (src->pixmap > 0) {
+ XFreePixmap (src->dpy, src->pixmap);
+ src->pixmap = 0;
+ }
+ if (src->dpy) {
+ XCloseDisplay (src->dpy);
+ src->dpy = NULL;
+ }
+
+ GST_LOG_OBJECT (src, "The thread function stop");
+ return NULL;
+finish:
+ GST_LOG_OBJECT (src, "The thread function Error cleanup");
+
+ XvStopVideo(src->dpy, src->p, src->pixmap);
+
+ if (src->bufmgr) tbm_bufmgr_deinit (src->bufmgr);
+ src->bufmgr = NULL;
+ if (src->p > 0) XvUngrabPort (src->dpy, src->p, 0);
+ src->p = 0;
+ if (src->gc) XFreeGC (src->dpy, src->gc);
+ src->gc = NULL;
+ if (src->pixmap > 0) XFreePixmap (src->dpy, src->pixmap);
+ src->pixmap = 0;
+ if (src->dpy) XCloseDisplay (src->dpy);
+ src->dpy = NULL;
+ GST_LOG_OBJECT (src, "The thread function Error stop");
+ return NULL;
+}
+
+static tbm_bufmgr bufmgr_get (Display *dpy, Pixmap pixmap)
+{
+ int screen;
+ int drm_fd;
+ tbm_bufmgr bufmgr;
+ int eventBase, errorBase;
+ int dri2Major, dri2Minor;
+ char *driverName = NULL, *deviceName = NULL;
+ drm_magic_t magic;
+
+ screen = DefaultScreen(dpy);
+ if (!DRI2QueryExtension (dpy, &eventBase, &errorBase)) {
+ GST_ERROR ("!!Error : DRI2QueryExtension !!");
+ return NULL;
+ }
+ if (!DRI2QueryVersion (dpy, &dri2Major, &dri2Minor)) {
+ GST_ERROR ("!!Error : DRI2QueryVersion !!");
+ return NULL;
+ }
+ if (!DRI2Connect (dpy, RootWindow(dpy, screen), &driverName, &deviceName)) {
+ GST_ERROR ( "!!Error : DRI2Connect !!");
+ if(driverName) Xfree(driverName);
+ if(deviceName) Xfree(deviceName);
+ return NULL;
+ }
+
+ if(driverName) Xfree(driverName);
+ if(!deviceName) return NULL;
+
+ GST_DEBUG("Open drm device : %s", deviceName);
+ // get the drm_fd though opening the deviceName
+ drm_fd = open (deviceName, O_RDWR);
+ if (drm_fd < 0) {
+ GST_ERROR ("!!Error : cannot open drm device (%s)", deviceName);
+ if(deviceName) Xfree(deviceName);
+ return NULL;
+ }
+ if(deviceName) Xfree(deviceName);
+ /* get the drm magic */
+ drmGetMagic(drm_fd, &magic);
+ fprintf(stderr, ">>> drm magic=%d \n", magic);
+ if (!DRI2Authenticate(dpy, RootWindow(dpy, screen), magic))
+ {
+ fprintf(stderr, "!!Error : DRI2Authenticate !!\n");
+ close (drm_fd);
+ return NULL;
+ }
+ // drm slp buffer manager init
+ bufmgr = tbm_bufmgr_init (drm_fd);
+ if (!bufmgr) {
+ GST_ERROR ("!!Error : fail to init buffer manager ");
+ close (drm_fd);
+ return NULL;
+ }
+ DRI2CreateDrawable (dpy, pixmap);
+ close (drm_fd);
+ return bufmgr;
+}
+
+static int port_get (GstXVImageSrc * src, unsigned int id)
+{
+ unsigned int ver, rev, req_base, err_base;
+ unsigned int adaptors;
+ XvAdaptorInfo *ai = NULL;
+ XvAttribute *at = NULL;
+ XvImageFormatValues *fo = NULL;
+ int attributes, formats;
+ int i, j, p;
+
+ if (XvQueryExtension (src->dpy, &ver, &rev, &req_base, &src->evt_base, &err_base) != Success) return -1;
+ if (XvQueryAdaptors (src->dpy, DefaultRootWindow (src->dpy), &adaptors, &ai) != Success) return -1;
+ if (!ai) return -1;
+ for (i = 0; i < adaptors; i++) {
+ int support_format = False;
+ if (!(ai[i].type & XvInputMask) || !(ai[i].type & XvStillMask)) continue;
+ GST_LOG ("===========================================");
+ GST_LOG (" name: %s"
+ " first port: %ld"
+ " ports: %ld",
+ ai[i].name,
+ ai[i].base_id,
+ ai[i].num_ports);
+ p = ai[i].base_id;
+ GST_LOG (" attribute list:");
+ at = XvQueryPortAttributes (src->dpy, p, &attributes);
+ for (j = 0; j < attributes; j++) GST_LOG ("\t- name: %s\n"
+ "\t\t flags: %s%s\n"
+ "\t\t min_value: %i\n"
+ "\t\t max_value: %i\n",
+ at[j].name,
+ (at[j].flags & XvGettable) ? " get" : "",
+ (at[j].flags & XvSettable) ? " set" : "",
+ at[j].min_value, at[j].max_value);
+ if (at) XFree (at);
+ GST_LOG (" image format list:");
+ fo = XvListImageFormats (src->dpy, p, &formats);
+ for (j = 0; j < formats; j++) {
+ GST_LOG ("\t- 0x%x (%4.4s) %s", fo[j].id, (char *)&fo[j].id, (fo[j].format == XvPacked) ? "packed" : "planar");
+ if (fo[j].id == (int)id) support_format = True;
+ }
+ if (fo) XFree (fo);
+ if (!support_format) continue;
+ for (; p < ai[i].base_id + ai[i].num_ports; p++) {
+ if (XvGrabPort (src->dpy, p, 0) == Success) {
+ GST_LOG ("========================================");
+ GST_DEBUG ("XvGrabPort success : %d", p);
+ GST_LOG ("========================================");
+ XvFreeAdaptorInfo (ai);
+ return p;
+ }
+ }
+ }
+ XvFreeAdaptorInfo (ai);
+ return -1;
+}
+
+static void pixmap_update (GstXVImageSrc * src, Display *dpy, tbm_bufmgr bufmgr, Pixmap pixmap,
+ int x, int y, int width, int height)
+{
+ unsigned int attachments[1];
+ int dri2_count, dri2_out_count;
+ int dri2_width, dri2_height, dri2_stride;
+ int opt;
+ tbm_bo_handle temp_virtual;
+ attachments[0] = DRI2BufferFrontLeft;
+ dri2_count = 1;
+ GST_DEBUG ("DRI2GetBuffers");
+ src->dri2_buffers = DRI2GetBuffers (dpy, pixmap, &dri2_width, &dri2_height, attachments, dri2_count, &dri2_out_count);
+ if (!src->dri2_buffers) {
+ GST_ERROR ("[Error] : fail to get buffers");
+ goto update_done;
+ }
+ if (!src->dri2_buffers[0].name) {
+ GST_ERROR ("[Error] : a handle of the dri2 buffer is null ");
+ goto update_done;
+ }
+ GST_DEBUG ("tbm_bo_import");
+ src->bo = tbm_bo_import(bufmgr, src->dri2_buffers[0].name);
+ if (!src->bo) {
+ GST_ERROR ("[Error] : cannot import bo (key:%d)", src->dri2_buffers[0].name);
+ goto update_done;
+ }
+ dri2_stride = src->dri2_buffers[0].pitch;
+ opt = TBM_OPTION_READ|TBM_OPTION_WRITE;
+ GST_DEBUG ("tbm_bo_map");
+ temp_virtual = tbm_bo_map (src->bo, TBM_DEVICE_CPU, opt);
+ src->virtual = temp_virtual.ptr;
+ if (!src->virtual) {
+ GST_ERROR ("[Error] : fail to map ");
+ goto update_done;
+ }
+ return;
+update_done:
+ if (src->virtual) tbm_bo_unmap(src->bo);
+ src->virtual = NULL;
+ if (src->bo) tbm_bo_unref(src->bo);
+ src->bo = NULL;
+ if (src->dri2_buffers) free(src->dri2_buffers);
+ src->dri2_buffers = NULL;
+ return;
+}
+
+static Pixmap pixmap_create (GstXVImageSrc * src, Display *dpy, int width, int height)
+{
+ src->pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), width, height, DefaultDepth(dpy, DefaultScreen(dpy)));
+ src->gc = XCreateGC (dpy, src->pixmap, 0, 0);
+ XSetForeground (dpy, src->gc, 0xFFFF0000);
+ XFillRectangle (dpy, src->pixmap, src->gc, 0, 0, width, height);
+ XSync(dpy, 0);
+ XFreeGC (dpy, src->gc);
+ return src->pixmap;
+}
+
+static gboolean
+gst_xv_image_src_stop (GstBaseSrc * bsrc)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ GST_WARNING ("stop()");
+
+ if(src->updates_thread) {
+ src->thread_return = TRUE;
+ g_cond_signal(src->pause_cond);
+ g_thread_join ( src->updates_thread);
+ src->updates_thread = NULL;
+ }
+ GST_WARNING ("stop end ");
+ return TRUE;
+}
+
+/* Interrupt a blocking request. */
+static gboolean
+gst_xv_image_src_unlock (GstBaseSrc * bsrc)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ GST_DEBUG_OBJECT (src, "unlock()");
+ g_cond_signal(src->queue_cond);
+ return TRUE;
+}
+
+/* Interrupt interrupt. */
+static gboolean
+gst_xv_image_src_unlock_stop (GstBaseSrc * bsrc)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ GST_DEBUG_OBJECT (src, "unlock_stop()");
+ return TRUE;
+}
+
+static gboolean
+gst_xv_image_src_get_size (GstBaseSrc * bsrc, guint64 * size)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ GST_INFO ("Get size %x", src);
+ return FALSE;
+}
+
+static gboolean
+gst_xv_image_src_is_seekable (GstBaseSrc * bsrc)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ GST_INFO ("Is seekable %x", src);
+ return FALSE;
+}
+
+static gboolean
+gst_xv_image_src_query (GstBaseSrc * bsrc, GstQuery * query)
+{
+ GstXVImageSrc *src = GST_XV_IMAGE_SRC (bsrc);
+ gboolean ret;
+ GST_INFO ("src query %x", src);
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_URI:
+ ret = TRUE;
+ break;
+ default:
+ ret = FALSE;
+ break;
+ }
+
+ if (!ret)
+ ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
+
+ return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (xvimagesrc_debug, "xvimagesrc", 0, "XServer display FB video capture Source");
+ return gst_element_register (plugin, "xvimagesrc", GST_RANK_PRIMARY, GST_TYPE_XV_IMAGE_SRC);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "xvimage",
+ "XServer display video src",
+ plugin_init, VERSION, "LGPL", "Samsung Electronics Co", "http://www.samsung.com")
--- /dev/null
+/*
+ * xvimagesrc
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Hyunil Park <hyunil46.park@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_XV_IMAGE_SRC_H__
+#define __GST_XV_IMAGE_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+#include <glib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/shm.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xvlib.h>
+#include <X11/extensions/Xvproto.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xdamage.h>
+
+#include <dri2.h> //libdri2-dev, libdrm-dev
+#include <tbm_bufmgr.h>
+
+#include "xv_types.h"
+
+#define C(b,m) (((b) >> (m)) & 0xFF)
+#define B(c,s) ((((unsigned int)(c)) & 0xff) << (s))
+#define FOURCC(a,b,c,d) (B(d,24) | B(c,16) | B(b,8) | B(a,0))
+#define FOURCC_RGB32 FOURCC('R','G','B','4')
+#define FOURCC_I420 FOURCC('I','4','2','0')
+#define FOURCC_SN12 FOURCC('S','N','1','2')
+#define FOURCC_NV12 FOURCC('N','V','1','2')
+#define FOURCC_ST12 FOURCC('S','T','1','2')
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_XV_IMAGE_SRC \
+ (gst_xv_image_src_get_type())
+#define GST_XV_IMAGE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_XV_IMAGE_SRC,GstXVImageSrc))
+#define GST_XV_IMAGE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), \
+ GST_TYPE_XV_IMAGE_SRC,GstXVImageSrcClass))
+#define GST_IS_XV_IMAGE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_XV_IMAGE_SRC))
+#define GST_IS_XV_IMAGE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_XV_IMAGE_SRC))
+
+typedef struct _GstXVImageSrc GstXVImageSrc;
+typedef struct _GstXVImageSrcClass GstXVImageSrcClass;
+
+struct _GstXVImageSrc {
+ GstPushSrc element;
+ Display *dpy;
+ int p;
+ Pixmap pixmap;
+ GC gc;
+ unsigned int width;
+ unsigned int height;
+ unsigned int framesize;
+ guint32 format_id;
+ Damage damage;
+ int damage_base;
+ unsigned int evt_base;
+ tbm_bufmgr bufmgr;
+ void *virtual;
+ tbm_bo bo;
+ DRI2Buffer* dri2_buffers;
+ guint64 running_time;
+ guint64 base_time;
+ guint64 frame_duration;
+ Atom atom_stream_off;
+ gint rate_numerator;
+ gint rate_denominator;
+ GThread *updates_thread;
+ gboolean thread_return;
+ GQueue *queue;
+ GMutex *queue_lock;
+ GCond *queue_cond;
+ GMutex *cond_lock;
+ GCond *buffer_cond;
+ gboolean pause_cond_var;
+ GCond *pause_cond;
+ GMutex *pause_cond_lock;
+ GCond *pause_resp;
+ GMutex *pause_resp_lock;
+ GMutex *buffer_cond_lock;
+ GMutex *dpy_lock;
+ gint drm_fd;
+ gboolean is_secured_mode;
+ int current_data_type;
+ int new_data_type;
+ long get_image_overtime;
+ int get_image_overtime_cnt;
+ int gemname_cnt;
+ int tz_enable;
+ long sleep_base_time;
+ long sleep_limit_time;
+ gboolean switching_to_udp;
+ guint64 initial_audio_latency;
+ guint64 display_rotate;
+
+ /* For display selection */
+ Window win;
+ Atom requestor;
+ Atom selection;
+ Atom target;
+ Atom property;
+};
+
+struct _GstXVImageSrcClass {
+ GstPushSrcClass parent_class;
+
+ /* signals */
+ void (*ui_only) (void *data);
+ void (*video_with_ui) (void *data);
+ void (*video_only) (void *data);
+ void (*selection_notify) (void *data);
+};
+
+GType gst_xv_image_src_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_XV_IMAGE_SRC_H__ */
+
--- /dev/null
+/**************************************************************************
+
+xserver-xorg-video-exynos
+
+Copyright 2010 - 2011 Samsung Electronics co., Ltd. All Rights Reserved.
+
+Contact: Boram Park <boram1288.park@samsung.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+/* */
+/* File name : xv_types.h */
+/* Author : Boram Park (boram1288.park@samsung.com) */
+/* Protocol Version : 1.0.1 (Dec 16th 2009) */
+/* This file is for describing Xv APIs' buffer encoding method. */
+/* */
+
+#ifndef __XV_TYPE_H__
+#define __XV_TYPE_H__
+
+#define XV_DATA_HEADER 0xDEADCD01
+#define XV_DATA_VERSION 0x00010001
+
+/* Return Values */
+#define XV_OK 0
+#define XV_HEADER_ERROR -1
+#define XV_VERSION_MISMATCH -2
+
+#define XV_BUF_TYPE_DMABUF 0
+#define XV_BUF_TYPE_LEGACY 1
+
+/* Data structure for XvPutImage / XvShmPutImage */
+typedef struct
+{
+ unsigned int _header; /* for internal use only */
+ unsigned int _version; /* for internal use only */
+
+ unsigned int YBuf;
+ unsigned int CbBuf;
+ unsigned int CrBuf;
+
+ unsigned int BufType;
+} XV_DATA, * XV_DATA_PTR;
+
+static void
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+XV_INIT_DATA (XV_DATA_PTR data)
+{
+ data->_header = XV_DATA_HEADER;
+ data->_version = XV_DATA_VERSION;
+}
+
+static int
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+XV_VALIDATE_DATA (XV_DATA_PTR data)
+{
+ if (data->_header != XV_DATA_HEADER)
+ return XV_HEADER_ERROR;
+ if (data->_version != XV_DATA_VERSION)
+ return XV_VERSION_MISMATCH;
+ return XV_OK;
+}
+
+#endif
+