summaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorEdward Hervey <edward@centricular.com>2017-11-09 10:45:37 +0100
committerEdward Hervey <bilboed@bilboed.com>2018-02-10 12:24:09 +0100
commit08044ab7e645f72f0233ec8607f63937684d3fdd (patch)
tree8c6b9fb464734298bf68c8dd328ff3d89715cf19 /gst
parent2858400608919c0875014315e14746a87bde3703 (diff)
playback: New uridecodebin3 element
In the same vein as old uridecodebin except that it also accepts a suburi and uses urisourcebin and decodebin3 internally
Diffstat (limited to 'gst')
-rw-r--r--gst/playback/Makefile.am1
-rw-r--r--gst/playback/gstplayback.c1
-rw-r--r--gst/playback/gstplayback.h1
-rw-r--r--gst/playback/gsturidecodebin3.c1098
-rw-r--r--gst/playback/meson.build1
5 files changed, 1102 insertions, 0 deletions
diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am
index ec6d622fd..ff5b8b489 100644
--- a/gst/playback/Makefile.am
+++ b/gst/playback/Makefile.am
@@ -6,6 +6,7 @@ libgstplayback_la_SOURCES = \
gstdecodebin2.c \
gstdecodebin3.c \
gsturidecodebin.c \
+ gsturidecodebin3.c \
gsturisourcebin.c \
gstparsebin.c \
gstplayback.c \
diff --git a/gst/playback/gstplayback.c b/gst/playback/gstplayback.c
index 4789db6df..3c3efa52e 100644
--- a/gst/playback/gstplayback.c
+++ b/gst/playback/gstplayback.c
@@ -63,6 +63,7 @@ plugin_init (GstPlugin * plugin)
res &= gst_decode_bin_plugin_init (plugin);
res &= gst_decodebin3_plugin_init (plugin);
res &= gst_uri_decode_bin_plugin_init (plugin);
+ res &= gst_uri_decode_bin3_plugin_init (plugin);
res &= gst_uri_source_bin_plugin_init (plugin);
res &= gst_parse_bin_plugin_init (plugin);
diff --git a/gst/playback/gstplayback.h b/gst/playback/gstplayback.h
index eaa0564ba..26e3683cf 100644
--- a/gst/playback/gstplayback.h
+++ b/gst/playback/gstplayback.h
@@ -26,6 +26,7 @@
gboolean gst_decode_bin_plugin_init (GstPlugin * plugin);
gboolean gst_decodebin3_plugin_init (GstPlugin * plugin);
gboolean gst_uri_decode_bin_plugin_init (GstPlugin * plugin);
+gboolean gst_uri_decode_bin3_plugin_init (GstPlugin * plugin);
gboolean gst_uri_source_bin_plugin_init (GstPlugin * plugin);
gboolean gst_parse_bin_plugin_init (GstPlugin * plugin);
diff --git a/gst/playback/gsturidecodebin3.c b/gst/playback/gsturidecodebin3.c
new file mode 100644
index 000000000..f48d8a4ee
--- /dev/null
+++ b/gst/playback/gsturidecodebin3.c
@@ -0,0 +1,1098 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@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 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO/FIXME:
+ *
+ * * BUFFERING MESSAGES
+ * ** How/Where do we deal with buffering messages from a new/prerolling
+ * source ? Ideally we want to re-use the same sourcebin ?
+ * ** Remember last buffering messages per source handler, if the SourceEntry
+ * group_id is the one being currently outputted on the source ghostpads,
+ * post the (last) buffering messages.
+ * If no group_id is being outputted (still prerolling), then output
+ * the messages directly
+ *
+ * * ASYNC HANDLING
+ * ** URIDECODEBIN3 is not async-aware.
+ *
+ * * GAPLESS HANDLING
+ * ** Correlate group_id and URI to know when/which stream is being outputted/started
+ */
+
+/**
+ * SECTION:element-uridecodebin3
+ * @title: uridecodebin3
+ *
+ * Decodes data from a URI into raw media. It selects a source element that can
+ * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/gst-i18n-plugin.h>
+#include <gst/pbutils/missing-plugins.h>
+
+#include "gstplay-enum.h"
+#include "gstrawcaps.h"
+#include "gstplayback.h"
+#include "gstplaybackutils.h"
+
+#define GST_TYPE_URI_DECODE_BIN3 \
+ (gst_uri_decode_bin3_get_type())
+#define GST_URI_DECODE_BIN3(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
+#define GST_URI_DECODE_BIN3_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
+#define GST_IS_URI_DECODE_BIN3(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
+#define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
+#define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
+
+typedef struct _GstSourceGroup GstSourceGroup;
+typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
+typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
+
+#define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
+#define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
+
+typedef struct _GstPlayItem GstPlayItem;
+typedef struct _GstSourceItem GstSourceItem;
+typedef struct _GstSourceHandler GstSourceHandler;
+typedef struct _OutputPad OutputPad;
+
+/* A structure describing a play item, which travels through the elements
+ * over time. */
+struct _GstPlayItem
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ /* Main URI */
+ GstSourceItem *main_item;
+
+ /* Auxiliary URI */
+ /* FIXME : Replace by a list later */
+ GstSourceItem *sub_item;
+
+ /* The group_id used to identify this play item via STREAM_START events
+ * This is the group_id which will be used externally (i.e. rewritten
+ * to outgoing STREAM_START events and in emitted signals).
+ * The urisourcebin-specific group_id is located in GstSourceItem */
+ guint group_id;
+
+ /* Is this play item the one being currently outputted by decodebin3
+ * and on our source ghostpads */
+ gboolean currently_outputted;
+};
+
+struct _GstSourceItem
+{
+ /* The GstPlayItem to which this GstSourceItem belongs to */
+ GstPlayItem *play_item;
+
+ gchar *uri;
+
+ /* The urisourcebin controlling this uri
+ * Can be NULL */
+ GstSourceHandler *handler;
+
+ /* Last buffering information */
+ gint last_perc;
+ GstMessage *last_buffering_message;
+
+ /* The groupid created by urisourcebin for this uri */
+ guint internal_groupid;
+
+ /* FIXME : Add tag lists and other uri-specific items here ? */
+};
+
+/* Structure wrapping everything related to a urisourcebin */
+struct _GstSourceHandler
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ GstElement *urisourcebin;
+
+ /* Signal handlers */
+ gulong pad_added_id;
+ gulong pad_removed_id;
+ gulong source_setup_id;
+ gulong about_to_finish_id;
+
+ /* TRUE if the controlled urisourcebin was added to uridecodebin */
+ gboolean active;
+
+ /* whether urisourcebin is drained or not.
+ * Reset if/when setting a new URI */
+ gboolean drained;
+
+ /* Whether urisourcebin posted EOS on all pads and
+ * there is no pending entry */
+ gboolean is_eos;
+
+ /* TRUE if the urisourcebin handles main item */
+ gboolean is_main_source;
+
+ /* buffering message stored for after switching */
+ GstMessage *pending_buffering_msg;
+};
+
+/* Controls an output source pad */
+struct _OutputPad
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ GstPad *target_pad;
+ GstPad *ghost_pad;
+
+ /* Downstream event probe id */
+ gulong probe_id;
+
+ /* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */
+ gboolean is_eos;
+
+ /* The last seen (i.e. current) group_id
+ * Can be (guint)-1 if no group_id was seen yet */
+ guint current_group_id;
+};
+
+/**
+ * GstURIDecodeBin3
+ *
+ * uridecodebin3 element struct
+ */
+struct _GstURIDecodeBin3
+{
+ GstBin parent_instance;
+
+ GMutex lock; /* lock for constructing */
+
+ /* Properties */
+ GstElement *source;
+ guint64 connection_speed; /* In bits/sec (0 = unknown) */
+ GstCaps *caps;
+ guint64 buffer_duration; /* When buffering, buffer duration (ns) */
+ guint buffer_size; /* When buffering, buffer size (bytes) */
+ gboolean download;
+ gboolean use_buffering;
+ guint64 ring_buffer_max_size;
+
+ GList *play_items; /* List of GstPlayItem ordered by time of
+ * creation. Head of list is therefore the
+ * current (or pending if initial) one being
+ * outputted */
+ GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL
+ * if no entry is active yet (i.e. no source
+ * pads) */
+
+ /* sources.
+ * FIXME : Replace by a more modular system later on */
+ GstSourceHandler *main_handler;
+ GstSourceHandler *sub_handler;
+
+ /* URI handling
+ * FIXME : Switch to a playlist-based API */
+ gchar *uri;
+ gboolean uri_changed; /* TRUE if uri changed */
+ gchar *suburi;
+ gboolean suburi_changed; /* TRUE if suburi changed */
+
+ /* A global decodebin3 that's used to actually do decoding */
+ GstElement *decodebin;
+
+ /* db3 signals */
+ gulong db_pad_added_id;
+ gulong db_pad_removed_id;
+ gulong db_select_stream_id;
+ gulong db_about_to_finish_id;
+
+ GList *output_pads; /* List of OutputPad */
+
+ GList *source_handlers; /* List of SourceHandler */
+
+ /* Whether we already signalled about-to-finish or not
+ * FIXME: Track this by group-id ! */
+ gboolean posted_about_to_finish;
+};
+
+struct _GstURIDecodeBin3Class
+{
+ GstBinClass parent_class;
+
+ gint (*select_stream) (GstURIDecodeBin3 * dbin,
+ GstStreamCollection * collection, GstStream * stream);
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
+#define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
+
+/* signals */
+enum
+{
+ SIGNAL_SELECT_STREAM,
+ SIGNAL_SOURCE_SETUP,
+ SIGNAL_ABOUT_TO_FINISH,
+ LAST_SIGNAL
+};
+
+#if 0
+static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
+static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
+#endif
+
+/* properties */
+#define DEFAULT_PROP_URI NULL
+#define DEFAULT_PROP_SUBURI NULL
+#define DEFAULT_PROP_SOURCE NULL
+#define DEFAULT_CONNECTION_SPEED 0
+#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
+#define DEFAULT_SUBTITLE_ENCODING NULL
+#define DEFAULT_BUFFER_DURATION -1
+#define DEFAULT_BUFFER_SIZE -1
+#define DEFAULT_DOWNLOAD FALSE
+#define DEFAULT_USE_BUFFERING FALSE
+#define DEFAULT_RING_BUFFER_MAX_SIZE 0
+
+enum
+{
+ PROP_0,
+ PROP_URI,
+ PROP_CURRENT_URI,
+ PROP_SUBURI,
+ PROP_CURRENT_SUBURI,
+ PROP_SOURCE,
+ PROP_SUBTITLE_ENCODING,
+ PROP_CONNECTION_SPEED,
+ PROP_BUFFER_SIZE,
+ PROP_BUFFER_DURATION,
+ PROP_DOWNLOAD,
+ PROP_USE_BUFFERING,
+ PROP_RING_BUFFER_MAX_SIZE,
+ PROP_CAPS
+};
+
+static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
+
+static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate audio_src_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate text_src_template =
+GST_STATIC_PAD_TEMPLATE ("text_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+GType gst_uri_decode_bin3_get_type (void);
+#define gst_uri_decode_bin3_parent_class parent_class
+G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
+
+#define REMOVE_SIGNAL(obj,id) \
+if (id) { \
+ g_signal_handler_disconnect (obj, id); \
+ id = 0; \
+}
+
+static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_finalize (GObject * obj);
+
+static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
+ element, GstStateChange transition);
+
+static gboolean
+_gst_int_accumulator (GSignalInvocationHint * ihint,
+ GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+ gint res = g_value_get_int (handler_return);
+
+ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+ g_value_set_int (return_accu, res);
+
+ if (res == -1)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static void
+gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gobject_class->set_property = gst_uri_decode_bin3_set_property;
+ gobject_class->get_property = gst_uri_decode_bin3_get_property;
+ gobject_class->finalize = gst_uri_decode_bin3_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "URI to decode",
+ DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
+ g_param_spec_string ("current-uri", "Current URI",
+ "The currently playing URI", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SUBURI,
+ g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
+ g_param_spec_string ("current-suburi", "Current .sub-URI",
+ "The currently playing URI of a subtitle",
+ NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SOURCE,
+ g_param_spec_object ("source", "Source", "Source object used",
+ GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SUBTITLE_ENCODING,
+ g_param_spec_string ("subtitle-encoding", "subtitle encoding",
+ "Encoding to assume if input subtitles are not in UTF-8 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.", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
+ g_param_spec_uint64 ("connection-speed", "Connection Speed",
+ "Network connection speed in kbps (0 = unknown)",
+ 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
+ g_param_spec_int ("buffer-size", "Buffer size (bytes)",
+ "Buffer size when buffering streams (-1 default value)",
+ -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
+ g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
+ "Buffer duration when buffering streams (-1 default value)",
+ -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodeBin3::download:
+ *
+ * For certain media type, enable download buffering.
+ */
+ g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
+ g_param_spec_boolean ("download", "Download",
+ "Attempt download buffering when buffering network streams",
+ DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstURIDecodeBin3::use-buffering:
+ *
+ * Emit BUFFERING messages based on low-/high-percent thresholds of the
+ * demuxed or parsed data.
+ * When download buffering is activated and used for the current media
+ * type, this property does nothing. Otherwise perform buffering on the
+ * demuxed or parsed media.
+ */
+ g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
+ g_param_spec_boolean ("use-buffering", "Use Buffering",
+ "Perform buffering on demuxed/parsed media",
+ DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodeBin3::ring-buffer-max-size
+ *
+ * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
+ * buffer is disabled. Default is 0.
+ */
+ g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
+ g_param_spec_uint64 ("ring-buffer-max-size",
+ "Max. ring buffer size (bytes)",
+ "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
+ 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CAPS,
+ g_param_spec_boxed ("caps", "Caps",
+ "The caps on which to stop decoding. (NULL = default)",
+ GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodebin3::select-stream
+ * @decodebin: a #GstURIDecodebin3
+ * @collection: a #GstStreamCollection
+ * @stream: a #GstStream
+ *
+ * This signal is emitted whenever @decodebin needs to decide whether
+ * to expose a @stream of a given @collection.
+ *
+ * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
+ * A value of -1 (default) lets @decodebin decide what to do with the stream.
+ * */
+ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
+ g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
+ _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
+ G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
+
+ /**
+ * GstURIDecodeBin3::source-setup:
+ * @bin: the uridecodebin.
+ * @source: source element
+ *
+ * This signal is emitted after a source element has been created, so
+ * it can be configured by setting additional properties (e.g. set a
+ * proxy server for an http source, or set the device and read speed for
+ * an audio cd source).
+ */
+ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
+ g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ /**
+ * GstURIDecodeBin3::about-to-finish:
+ *
+ * This signal is emitted when the data for the selected URI is
+ * entirely buffered and it is safe to specify anothe URI.
+ */
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
+ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+ 0, G_TYPE_NONE);
+
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &video_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &audio_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &text_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class, &src_template);
+ gst_element_class_set_static_metadata (gstelement_class,
+ "URI Decoder", "Generic/Bin/Decoder",
+ "Autoplug and decode an URI to raw media",
+ "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
+
+ gstelement_class->change_state = gst_uri_decode_bin3_change_state;
+
+}
+
+static GstPadProbeReturn
+db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
+{
+ /* FIXME : IMPLEMENT */
+
+ /* EOS : Mark pad as EOS */
+
+ /* STREAM_START : Store group_id and check if currently active
+ * PlayEntry changed */
+
+ return GST_PAD_PROBE_OK;
+}
+
+static OutputPad *
+add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
+{
+ OutputPad *output;
+ gchar *pad_name;
+
+ output = g_slice_new0 (OutputPad);
+
+ GST_LOG_OBJECT (dec, "Created output %p", output);
+
+ output->uridecodebin = dec;
+ output->target_pad = target_pad;
+ output->current_group_id = (guint) - 1;
+ pad_name = gst_pad_get_name (target_pad);
+ output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
+ g_free (pad_name);
+
+ gst_pad_set_active (output->ghost_pad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
+
+ output->probe_id =
+ gst_pad_add_probe (output->target_pad,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
+ output, NULL);
+
+ /* FIXME: LOCK TO PROTECT PAD LIST */
+ dec->output_pads = g_list_append (dec->output_pads, output);
+
+ return output;
+}
+
+static void
+db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+ GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ if (GST_PAD_IS_SRC (pad))
+ add_output_pad (dec, pad);
+}
+
+static void
+db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+ GList *tmp;
+ OutputPad *output = NULL;
+
+ GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ /* FIXME: LOCK for list access */
+
+ for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
+ OutputPad *cand = (OutputPad *) tmp->data;
+
+ if (cand->target_pad == pad) {
+ output = cand;
+ dec->output_pads = g_list_remove_link (dec->output_pads, tmp);
+ break;
+ }
+ }
+
+ if (output) {
+ GST_LOG_OBJECT (element, "Removing output %p", output);
+ /* Remove source ghost pad */
+ gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
+ gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
+
+ /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
+
+ /* Remove event probe */
+ gst_pad_remove_probe (output->target_pad, output->probe_id);
+
+ g_slice_free (OutputPad, output);
+ }
+}
+
+static gint
+db_select_stream_cb (GstElement * decodebin,
+ GstStreamCollection * collection, GstStream * stream,
+ GstURIDecodeBin3 * uridecodebin)
+{
+ gint response = -1;
+
+ g_signal_emit (uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
+ &response);
+ return response;
+}
+
+static void
+db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
+{
+ if (!uridecodebin->posted_about_to_finish) {
+ uridecodebin->posted_about_to_finish = TRUE;
+ g_signal_emit (uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
+}
+
+static void
+gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
+{
+ g_mutex_init (&dec->lock);
+
+ dec->caps = gst_static_caps_get (&default_raw_caps);
+
+ dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
+ gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
+ dec->db_pad_added_id =
+ g_signal_connect (dec->decodebin, "pad-added",
+ G_CALLBACK (db_pad_added_cb), dec);
+ dec->db_pad_removed_id =
+ g_signal_connect (dec->decodebin, "pad-removed",
+ G_CALLBACK (db_pad_removed_cb), dec);
+ dec->db_select_stream_id =
+ g_signal_connect (dec->decodebin, "select-stream",
+ G_CALLBACK (db_select_stream_cb), dec);
+ dec->db_about_to_finish_id =
+ g_signal_connect (dec->decodebin, "about-to-finish",
+ G_CALLBACK (db_about_to_finish_cb), dec);
+
+ GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE);
+ gst_bin_set_suppressed_flags (GST_BIN (dec),
+ GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
+}
+
+static void
+gst_uri_decode_bin3_finalize (GObject * obj)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
+
+ g_mutex_clear (&dec->lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static GstStateChangeReturn
+activate_source_item (GstSourceItem * item)
+{
+ GstSourceHandler *handler = item->handler;
+
+ if (handler == NULL) {
+ GST_WARNING ("Can't activate item without a handler");
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ g_object_set (handler->urisourcebin, "uri", item->uri, NULL);
+ if (!handler->active) {
+ gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin);
+ /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */
+ /* return GST_STATE_CHANGE_FAILURE; */
+ handler->active = TRUE;
+ }
+
+ return GST_STATE_CHANGE_SUCCESS;
+}
+
+static void
+src_pad_added_cb (GstElement * element, GstPad * pad,
+ GstSourceHandler * handler)
+{
+ GstURIDecodeBin3 *uridecodebin;
+ GstPad *sinkpad = NULL;
+ GstPadLinkReturn res;
+
+ uridecodebin = handler->uridecodebin;
+
+ GST_DEBUG_OBJECT (uridecodebin,
+ "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
+
+ /* FIXME: Add probe to unify group_id and detect EOS */
+
+ /* Try to link to main sink pad only if it's from a main handler */
+ if (handler->is_main_source) {
+ sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink");
+ if (gst_pad_is_linked (sinkpad)) {
+ gst_object_unref (sinkpad);
+ sinkpad = NULL;
+ }
+ }
+
+ if (sinkpad == NULL)
+ sinkpad = gst_element_get_request_pad (uridecodebin->decodebin, "sink_%u");
+
+ if (sinkpad) {
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad);
+ res = gst_pad_link (pad, sinkpad);
+ gst_object_unref (sinkpad);
+ if (GST_PAD_LINK_FAILED (res))
+ goto link_failed;
+ }
+ return;
+
+link_failed:
+ {
+ GST_ERROR_OBJECT (uridecodebin,
+ "failed to link pad %s:%s to decodebin, reason %s (%d)",
+ GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
+ return;
+ }
+}
+
+static void
+src_pad_removed_cb (GstElement * element, GstPad * pad,
+ GstSourceHandler * handler)
+{
+ /* FIXME : IMPLEMENT */
+}
+
+static void
+src_source_setup_cb (GstElement * element, GstElement * source,
+ GstSourceHandler * handler)
+{
+ g_signal_emit (handler->uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL);
+}
+
+static void
+src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler)
+{
+ /* FIXME : check if all sources are done */
+ if (!handler->uridecodebin->posted_about_to_finish) {
+ handler->uridecodebin->posted_about_to_finish = TRUE;
+ g_signal_emit (handler->uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
+}
+
+static GstSourceHandler *
+new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
+{
+ GstSourceHandler *handler;
+
+ handler = g_slice_new0 (GstSourceHandler);
+
+ handler->uridecodebin = uridecodebin;
+ handler->is_main_source = is_main;
+ handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
+ /* Set pending properties */
+ g_object_set (handler->urisourcebin,
+ "connection-speed", uridecodebin->connection_speed / 1000,
+ "download", uridecodebin->download,
+ "use-buffering", uridecodebin->use_buffering,
+ "buffer-duration", uridecodebin->buffer_duration,
+ "buffer-size", uridecodebin->buffer_size,
+ "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL);
+
+ handler->pad_added_id =
+ g_signal_connect (handler->urisourcebin, "pad-added",
+ (GCallback) src_pad_added_cb, handler);
+ handler->pad_removed_id =
+ g_signal_connect (handler->urisourcebin, "pad-removed",
+ (GCallback) src_pad_removed_cb, handler);
+ handler->source_setup_id =
+ g_signal_connect (handler->urisourcebin, "source-setup",
+ (GCallback) src_source_setup_cb, handler);
+ handler->about_to_finish_id =
+ g_signal_connect (handler->urisourcebin, "about-to-finish",
+ (GCallback) src_about_to_finish_cb, handler);
+
+ uridecodebin->source_handlers =
+ g_list_append (uridecodebin->source_handlers, handler);
+
+ return handler;
+}
+
+static void
+gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ if (dec->uri)
+ g_free (dec->uri);
+ dec->uri = g_value_dup_string (value);
+ break;
+ case PROP_SUBURI:
+ if (dec->suburi)
+ g_free (dec->suburi);
+ dec->suburi = g_value_dup_string (value);
+ break;
+ case PROP_SUBTITLE_ENCODING:
+ g_object_set_property (G_OBJECT (dec->decodebin), "subtitle-encoding",
+ value);
+ break;
+ case PROP_CONNECTION_SPEED:
+ GST_URI_DECODE_BIN3_LOCK (dec);
+ dec->connection_speed = g_value_get_uint64 (value) * 1000;
+ GST_URI_DECODE_BIN3_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_SIZE:
+ dec->buffer_size = g_value_get_int (value);
+ break;
+ case PROP_BUFFER_DURATION:
+ dec->buffer_duration = g_value_get_int64 (value);
+ break;
+ case PROP_DOWNLOAD:
+ dec->download = g_value_get_boolean (value);
+ break;
+ case PROP_USE_BUFFERING:
+ dec->use_buffering = g_value_get_boolean (value);
+ break;
+ case PROP_RING_BUFFER_MAX_SIZE:
+ dec->ring_buffer_max_size = g_value_get_uint64 (value);
+ break;
+ case PROP_CAPS:
+ GST_OBJECT_LOCK (dec);
+ if (dec->caps)
+ gst_caps_unref (dec->caps);
+ dec->caps = g_value_dup_boxed (value);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ {
+ g_value_set_string (value, dec->uri);
+ break;
+ }
+ case PROP_CURRENT_URI:
+ {
+ g_value_set_string (value, dec->suburi);
+ break;
+ }
+ case PROP_SUBURI:
+ {
+ /* FIXME : Return current uri */
+ g_value_set_string (value, dec->uri);
+ break;
+ }
+ case PROP_CURRENT_SUBURI:
+ {
+ /* FIXME : Return current suburi */
+ g_value_set_string (value, dec->suburi);
+ break;
+ }
+ case PROP_SOURCE:
+ {
+ GST_OBJECT_LOCK (dec);
+ g_value_set_object (value, dec->source);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ }
+ case PROP_SUBTITLE_ENCODING:
+ g_object_get_property (G_OBJECT (dec->decodebin), "subtitle-encoding",
+ value);
+ break;
+ case PROP_CONNECTION_SPEED:
+ GST_URI_DECODE_BIN3_LOCK (dec);
+ g_value_set_uint64 (value, dec->connection_speed / 1000);
+ GST_URI_DECODE_BIN3_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_SIZE:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_int (value, dec->buffer_size);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_DURATION:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_int64 (value, dec->buffer_duration);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ case PROP_DOWNLOAD:
+ g_value_set_boolean (value, dec->download);
+ break;
+ case PROP_USE_BUFFERING:
+ g_value_set_boolean (value, dec->use_buffering);
+ break;
+ case PROP_RING_BUFFER_MAX_SIZE:
+ g_value_set_uint64 (value, dec->ring_buffer_max_size);
+ break;
+ case PROP_CAPS:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_boxed (value, dec->caps);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+free_source_handler (GstURIDecodeBin3 * uridecodebin,
+ GstSourceHandler * handler)
+{
+ GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
+ if (handler->active) {
+ GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
+ handler->urisourcebin);
+ gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
+ gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
+ }
+ uridecodebin->source_handlers =
+ g_list_remove (uridecodebin->source_handlers, handler);
+ g_slice_free (GstSourceHandler, handler);
+}
+
+static GstSourceItem *
+new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri)
+{
+ GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem);
+
+ sourceitem->play_item = item;
+ sourceitem->uri = uri;
+
+ return sourceitem;
+}
+
+static void
+free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item)
+{
+ GST_LOG_OBJECT (uridecodebin, "source item %p", item);
+ if (item->handler)
+ free_source_handler (uridecodebin, item->handler);
+ g_slice_free (GstSourceItem, item);
+}
+
+static GstPlayItem *
+new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
+{
+ GstPlayItem *item = g_slice_new0 (GstPlayItem);
+
+ item->uridecodebin = dec;
+ item->main_item = new_source_item (dec, item, uri);
+ if (suburi)
+ item->sub_item = new_source_item (dec, item, suburi);
+
+ return item;
+}
+
+static void
+free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+ GST_LOG_OBJECT (dec, "play item %p", item);
+ if (item->main_item)
+ free_source_item (dec, item->main_item);
+ if (item->sub_item)
+ free_source_item (dec, item->sub_item);
+
+ g_slice_free (GstPlayItem, item);
+}
+
+/* Sync source handlers for the given play item. Might require creating/removing some
+ * and/or configure the handlers accordingly */
+static GstStateChangeReturn
+assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ /* FIXME : Go over existing handlers to see if we can assign some to the
+ * given item */
+
+ /* Create missing handlers */
+ if (item->main_item->handler == NULL) {
+ item->main_item->handler = new_source_handler (dec, TRUE);
+ ret = activate_source_item (item->main_item);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+ }
+
+ if (item->sub_item && item->sub_item->handler) {
+ item->sub_item->handler = new_source_handler (dec, FALSE);
+ ret = activate_source_item (item->sub_item);
+ }
+
+ return ret;
+}
+
+/* Called to activate the next play item */
+static GstStateChangeReturn
+activate_next_play_item (GstURIDecodeBin3 * dec)
+{
+ GstPlayItem *item;
+ GstStateChangeReturn ret;
+
+ /* If there is no current play entry, create one from the uri/suburi
+ * FIXME : Use a playlist API in the future */
+ item = new_play_item (dec, dec->uri, dec->suburi);
+
+ ret = assign_handlers_to_item (dec, item);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ free_play_item (dec, item);
+ return ret;
+ }
+
+ dec->play_items = g_list_append (dec->play_items, item);
+
+ return ret;
+}
+
+static void
+free_play_items (GstURIDecodeBin3 * dec)
+{
+ GList *tmp;
+
+ for (tmp = dec->play_items; tmp; tmp = tmp->next) {
+ GstPlayItem *item = (GstPlayItem *) tmp->data;
+ free_play_item (dec, item);
+ }
+
+ g_list_free (dec->play_items);
+ dec->play_items = NULL;
+}
+
+static GstStateChangeReturn
+gst_uri_decode_bin3_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ ret = activate_next_play_item (uridecodebin);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failure;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failure;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* FIXME: Cleanup everything */
+ free_play_items (uridecodebin);
+ /* Free play item */
+ uridecodebin->posted_about_to_finish = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+
+ /* ERRORS */
+failure:
+ {
+ return ret;
+ }
+}
+
+
+gboolean
+gst_uri_decode_bin3_plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0,
+ "URI decoder element 3");
+
+ return gst_element_register (plugin, "uridecodebin3", GST_RANK_NONE,
+ GST_TYPE_URI_DECODE_BIN3);
+}
diff --git a/gst/playback/meson.build b/gst/playback/meson.build
index d8411a284..ef765a3e0 100644
--- a/gst/playback/meson.build
+++ b/gst/playback/meson.build
@@ -2,6 +2,7 @@ playback_sources = [
'gstdecodebin2.c',
'gstdecodebin3.c',
'gsturidecodebin.c',
+ 'gsturidecodebin3.c',
'gsturisourcebin.c',
'gstparsebin.c',
'gstplayback.c',