--- /dev/null
+From 7186918c4d8ecb95f7b669205f7b06c1f462b6b2 Mon Sep 17 00:00:00 2001
+From: Maemo Multimedia <multimedia@maemo.org>
+Date: Thu, 22 Jan 2009 09:02:18 +0200
+Subject: [PATCH] playbasebin: add network queue support, adding nw-queue
+ property
+
+---
+ gst/playback/gstplaybasebin.c | 256 ++++++++++++++++++++++++++++++++---------
+ gst/playback/gstplaybasebin.h | 1 +
+ 2 files changed, 205 insertions(+), 52 deletions(-)
+
+diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c
+index 847e12f..7f81da6 100644
+--- a/gst/playback/gstplaybasebin.c
++++ b/gst/playback/gstplaybasebin.c
+@@ -29,6 +29,11 @@
+
+ #include <gst/pbutils/pbutils.h>
+
++#define SCAN_SINKS_FROM_REGISTRY
++#define PREROLL_QUEUE_MAX_BUFFERS 5
++#define PREROLL_QUEUE_MAX_BYTES (1 * 1024 * 1024)
++#define NETWORK_QUEUE_MAX_BUFFERS 100
++
+ GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug);
+ #define GST_CAT_DEFAULT gst_play_base_bin_debug
+
+@@ -59,7 +64,8 @@ enum
+ ARG_AUDIO,
+ ARG_TEXT,
+ ARG_SUBTITLE_ENCODING,
+- ARG_CONNECTION_SPEED
++ ARG_CONNECTION_SPEED,
++ ARG_NW_QUEUE
+ };
+
+ static void gst_play_base_bin_class_init (GstPlayBaseBinClass * klass);
+@@ -94,6 +100,8 @@ static void set_active_source (GstPlayBaseBin * play_base_bin,
+ static gboolean probe_triggered (GstPad * pad, GstEvent * event,
+ gpointer user_data);
+ static void setup_substreams (GstPlayBaseBin * play_base_bin);
++static void queue_threshold_reached (GstElement * queue, GstPlayBaseBin
++ * play_base_bin);
+
+ static GstPipelineClass *parent_class;
+
+@@ -215,6 +223,10 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
+ "Network connection speed in kbps (0 = unknown)",
+ 0, G_MAXUINT, DEFAULT_CONNECTION_SPEED,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
++ g_object_class_install_property (gobject_klass, ARG_NW_QUEUE,
++ g_param_spec_boolean ("nw-queue", "Use network queue",
++ "Use separate queue for network buffering instead of using "
++ "internal audio/video queues.", FALSE, G_PARAM_READWRITE));
+
+ GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0,
+ "playbasebin");
+@@ -236,6 +248,7 @@ gst_play_base_bin_init (GstPlayBaseBin * play_base_bin)
+ play_base_bin->suburi = NULL;
+ play_base_bin->need_rebuild = TRUE;
+ play_base_bin->is_stream = FALSE;
++ play_base_bin->use_nw_queue = FALSE;
+ play_base_bin->source = NULL;
+ play_base_bin->decoders = NULL;
+ play_base_bin->subtitle = NULL;
+@@ -518,7 +531,6 @@ group_is_muted (GstPlayBaseGroup * group)
+ /*
+ * Buffer/cache checking.
+ */
+-
+ static inline void
+ fill_buffer (GstPlayBaseBin * play_base_bin, gint percent)
+ {
+@@ -716,12 +728,18 @@ queue_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin)
+
+ preroll_remove_overrun (queue, play_base_bin);
+
+- group_commit (play_base_bin, FALSE,
+- GST_OBJECT_PARENT (GST_OBJECT_CAST (queue)) ==
+- GST_OBJECT_CAST (play_base_bin->subtitle));
++ // Commit the group only when we are using "stupid" buffering, because when
++ // using separate network queue element this preroll queue size is much
++ // smaller, causing premature overrun (decodebin2 is still in the
++ // pad expose-unlocking loop when that happens)
++ if (play_base_bin->use_nw_queue == FALSE) {
++ group_commit (play_base_bin, FALSE,
++ GST_OBJECT_PARENT (GST_OBJECT_CAST (queue)) ==
++ GST_OBJECT_CAST (play_base_bin->subtitle));
+
+- /* notify end of buffering */
+- queue_threshold_reached (queue, play_base_bin);
++ /* notify end of buffering */
++ queue_threshold_reached (queue, play_base_bin);
++ }
+ }
+
+ /* this signal is only added when in streaming mode to catch underruns
+@@ -832,6 +850,11 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
+ "max-size-buffers", 0, "max-size-bytes",
+ 2 * 1024 * 1024, "max-size-time", play_base_bin->queue_size, NULL);
+ }
++ } else if (play_base_bin->use_nw_queue) {
++ g_object_set (G_OBJECT (preroll),
++ "max-size-buffers", PREROLL_QUEUE_MAX_BUFFERS,
++ "max-size-bytes", PREROLL_QUEUE_MAX_BYTES,
++ "max-size-time", play_base_bin->queue_size, NULL);
+ } else {
+ g_object_set (G_OBJECT (preroll),
+ "max-size-buffers", 0, "max-size-bytes",
+@@ -860,42 +883,45 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
+ g_object_set_data (G_OBJECT (preroll), "overrun_signal_id",
+ GINT_TO_POINTER (overrun_sig));
+
+- if (play_base_bin->is_stream &&
+- ((type == GST_STREAM_TYPE_VIDEO &&
+- group->type[GST_STREAM_TYPE_AUDIO - 1].npads == 0) ||
+- (type == GST_STREAM_TYPE_AUDIO &&
+- group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0))) {
+- GstPad *sinkpad;
+- guint id;
+-
+- /* catch deadlocks when we are network buffering in time but the max-limit
+- * in bytes is hit. */
+- g_signal_connect (G_OBJECT (preroll), "overrun",
+- G_CALLBACK (queue_deadlock_check), play_base_bin);
+-
+- /* attach pointer to playbasebin */
+- g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin);
+-
+- /* give updates on queue size */
+- sinkpad = gst_element_get_static_pad (preroll, "sink");
+- id = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (check_queue), preroll);
+- GST_DEBUG_OBJECT (play_base_bin, "Attaching probe to pad %s:%s (%p)",
+- GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
+- g_object_set_data (G_OBJECT (preroll), "probe", GINT_TO_POINTER (id));
+-
+- /* catch eos and flush events so that we can ignore underruns */
+- id = gst_pad_add_event_probe (sinkpad, G_CALLBACK (check_queue_event),
+- preroll);
+- g_object_set_data (G_OBJECT (preroll), "eos_probe", GINT_TO_POINTER (id));
+-
+- gst_object_unref (sinkpad);
++ if (play_base_bin->use_nw_queue == FALSE) {
++ if (play_base_bin->is_stream &&
++ ((type == GST_STREAM_TYPE_VIDEO &&
++ group->type[GST_STREAM_TYPE_AUDIO - 1].npads == 0) ||
++ (type == GST_STREAM_TYPE_AUDIO &&
++ group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0))) {
++ GstPad *sinkpad;
++ guint id;
+
+- /* When we connect this queue, it will start running and immediatly
+- * fire an underrun. */
+- g_signal_connect (G_OBJECT (preroll), "underrun",
+- G_CALLBACK (queue_out_of_data), play_base_bin);
+- /* configure threshold and callbacks */
+- queue_out_of_data (preroll, play_base_bin);
++ /* catch deadlocks when we are network buffering in time but the max-limit
++ * in bytes is hit. */
++ g_signal_connect (G_OBJECT (preroll), "overrun",
++ G_CALLBACK (queue_deadlock_check), play_base_bin);
++
++ /* attach pointer to playbasebin */
++ g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin);
++
++ /* give updates on queue size */
++ sinkpad = gst_element_get_static_pad (preroll, "sink");
++ id = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (check_queue),
++ preroll);
++ GST_DEBUG_OBJECT (play_base_bin, "Attaching probe to pad %s:%s (%p)",
++ GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
++ g_object_set_data (G_OBJECT (preroll), "probe", GINT_TO_POINTER (id));
++
++ /* catch eos and flush events so that we can ignore underruns */
++ id = gst_pad_add_event_probe (sinkpad, G_CALLBACK (check_queue_event),
++ preroll);
++ g_object_set_data (G_OBJECT (preroll), "eos_probe", GINT_TO_POINTER (id));
++
++ gst_object_unref (sinkpad);
++
++ /* When we connect this queue, it will start running and immediatly
++ * fire an underrun. */
++ g_signal_connect (G_OBJECT (preroll), "underrun",
++ G_CALLBACK (queue_out_of_data), play_base_bin);
++ /* configure threshold and callbacks */
++ queue_out_of_data (preroll, play_base_bin);
++ }
+ }
+
+ /* listen for EOS so we can switch groups when one ended. */
+@@ -1508,10 +1534,7 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri)
+ if (!source)
+ goto unknown_uri;
+
+- if (g_getenv ("USE_DECODEBIN2"))
+- subdecodebin = gst_element_factory_make ("decodebin2", "subtitle-decoder");
+- else
+- subdecodebin = gst_element_factory_make ("decodebin", "subtitle-decoder");
++ subdecodebin = gst_element_factory_make ("decodebin2", "subtitle-decoder");
+ g_signal_connect (subdecodebin, "element-added",
+ G_CALLBACK (decodebin_element_added_cb), play_base_bin);
+ g_signal_connect (subdecodebin, "element-removed",
+@@ -1572,7 +1595,7 @@ array_has_value (const gchar * values[], const gchar * value)
+ /* list of URIs that we consider to be streams and that need buffering.
+ * We have no mechanism yet to figure this out with a query. */
+ static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://",
+- "mmsu://", "mmst://", "myth://", NULL
++ "mmsu://", "mmst://", "myth://", "upnpav://", "obex://", "smb://", NULL
+ };
+
+ /* blacklisted URIs, we know they will always fail. */
+@@ -1974,16 +1997,80 @@ remove_decoders (GstPlayBaseBin * bin)
+ bin->decoders = NULL;
+ }
+
++#ifdef SCAN_SINKS_FROM_REGISTRY
++
++/* filter function for sink elements */
++static gboolean
++gst_play_base_bin_sink_factory_filter (GstPluginFeature * feature,
++ GstPlayBaseBin * bin)
++{
++ guint rank;
++ const gchar *klass;
++
++ /* we only care about element factories */
++ if (!GST_IS_ELEMENT_FACTORY (feature))
++ return FALSE;
++
++ klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
++ /* only sinks */
++ if (strstr (klass, "Sink") == NULL) {
++ return FALSE;
++ }
++
++ /* only select elements with autoplugging rank */
++ rank = gst_plugin_feature_get_rank (feature);
++ if (rank < GST_RANK_MARGINAL)
++ return FALSE;
++
++ return TRUE;
++}
++
++static GstCaps *
++get_supported_sink_caps (GstPlayBaseBin * bin)
++{
++ GList *sink_factories;
++ GstCaps *sink_caps = gst_caps_new_empty ();
++ GstCaps *caps = NULL;
++ const GList *item, *pad_templates;
++ GstStaticPadTemplate *templ;
++
++ /* filter out the sink factories;
++ stop decodebin construction when compatible sink is found */
++ sink_factories = gst_default_registry_feature_filter (
++ (GstPluginFeatureFilter) gst_play_base_bin_sink_factory_filter,
++ FALSE, bin);
++
++ /* scan sink caps from registry, assumes only 1 pad template per sink */
++ for (item = sink_factories; item != NULL; item = item->next) {
++ pad_templates =
++ gst_element_factory_get_static_pad_templates (GST_ELEMENT_FACTORY
++ (item->data));
++ templ = pad_templates->data;
++ /* Get caps from template */
++ caps = gst_caps_make_writable (gst_static_pad_template_get_caps (templ));
++ if (!gst_caps_is_any (caps)) {
++ gst_caps_append (sink_caps, caps);
++ } else {
++ gst_caps_unref (caps);
++ }
++ }
++
++ gst_plugin_feature_list_free (sink_factories);
++ GST_DEBUG_OBJECT (bin, "Sink caps from registry: %" GST_PTR_FORMAT,
++ sink_caps);
++
++ return sink_caps;
++
++}
++#endif
++
+ static GstElement *
+ make_decoder (GstPlayBaseBin * play_base_bin)
+ {
+ GstElement *decoder;
+
+ /* now create the decoder element */
+- if (g_getenv ("USE_DECODEBIN2"))
+- decoder = gst_element_factory_make ("decodebin2", NULL);
+- else
+- decoder = gst_element_factory_make ("decodebin", NULL);
++ decoder = gst_element_factory_make ("decodebin2", NULL);
+ if (!decoder)
+ goto no_decodebin;
+
+@@ -2010,6 +2097,14 @@ make_decoder (GstPlayBaseBin * play_base_bin)
+
+ play_base_bin->decoders = g_slist_prepend (play_base_bin->decoders, decoder);
+
++#ifdef SCAN_SINKS_FROM_REGISTRY
++ {
++ GstCaps *sink_caps = get_supported_sink_caps (play_base_bin);
++ g_object_set (decoder, "caps", sink_caps, NULL);
++ gst_caps_unref (sink_caps);
++ }
++#endif
++
+ return decoder;
+
+ /* ERRORS */
+@@ -2021,6 +2116,32 @@ no_decodebin:
+ }
+ }
+
++/* SOURCE ! NETWORK QUEUE ! DECODEBIN ! SINKS */
++static gboolean
++add_network_queue (GstPlayBaseBin * play_base_bin, GstElement * decoder)
++{
++ gboolean ret = FALSE;
++ GstElement *nw_queue = gst_element_factory_make ("queue2", "nw_queue");
++
++ /* Add network queue to bin */
++ if (nw_queue && gst_bin_add (GST_BIN_CAST (play_base_bin), nw_queue)) {
++ /* Link network queue */
++ if (gst_element_link (play_base_bin->source, nw_queue) &&
++ gst_element_link (nw_queue, decoder)) {
++ /* Set queue2 properties */
++ g_object_set (G_OBJECT (nw_queue),
++ "max-size-buffers", NETWORK_QUEUE_MAX_BUFFERS,
++ "max-size-bytes", 0,
++ "max-size-time", (guint64) 0,
++ "use-buffering", TRUE, "use-rate-estimate", FALSE, NULL);
++ ret = TRUE;
++ } else {
++ gst_bin_remove (GST_BIN_CAST (play_base_bin), nw_queue);
++ }
++ }
++ return ret;
++}
++
+ static void
+ remove_source (GstPlayBaseBin * bin)
+ {
+@@ -2040,6 +2161,17 @@ remove_source (GstPlayBaseBin * bin)
+ }
+ gst_bin_remove (GST_BIN_CAST (bin), source);
+ bin->source = NULL;
++
++ if (bin->use_nw_queue) {
++ if (bin->is_stream) {
++ GstElement *nw_queue =
++ gst_bin_get_by_name (GST_BIN_CAST (bin), "nw_queue");
++ GST_DEBUG_OBJECT (bin, "removing network queue element");
++ gst_element_set_state (nw_queue, GST_STATE_NULL);
++ gst_bin_remove (GST_BIN_CAST (bin), nw_queue);
++ gst_object_unref (nw_queue);
++ }
++ }
+ }
+ }
+
+@@ -2210,13 +2342,27 @@ setup_source (GstPlayBaseBin * play_base_bin)
+ "Source has dynamic output pads, %d pending", play_base_bin->pending);
+ } else {
+ GstElement *decoder;
++ gboolean link_ok = FALSE;
+
+ /* no dynamic source, we can link now */
+ decoder = make_decoder (play_base_bin);
+ if (!decoder)
+ goto no_decodebin;
+
+- if (!gst_element_link (play_base_bin->source, decoder))
++ if (play_base_bin->use_nw_queue) {
++ /* FIXME: network queue isn't added with source having dynamic pads */
++ if (play_base_bin->is_stream) {
++ GST_DEBUG_OBJECT (play_base_bin,
++ "linking source to decoder using network queue");
++ link_ok = add_network_queue (play_base_bin, decoder);
++ } else {
++ GST_DEBUG_OBJECT (play_base_bin, "linking source to decoder");
++ link_ok = gst_element_link (play_base_bin->source, decoder);
++ }
++ } else {
++ link_ok = gst_element_link (play_base_bin->source, decoder);
++ }
++ if (!link_ok)
+ goto could_not_link;
+ }
+
+@@ -2631,6 +2777,9 @@ gst_play_base_bin_set_property (GObject * object, guint prop_id,
+ g_slist_free (list);
+ break;
+ }
++ case ARG_NW_QUEUE:
++ play_base_bin->use_nw_queue = g_value_get_boolean (value);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -2718,6 +2867,9 @@ gst_play_base_bin_get_property (GObject * object, guint prop_id, GValue * value,
+ g_value_set_string (value, play_base_bin->subencoding);
+ GST_OBJECT_UNLOCK (play_base_bin);
+ break;
++ case ARG_NW_QUEUE:
++ g_value_set_boolean (value, play_base_bin->use_nw_queue);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+diff --git a/gst/playback/gstplaybasebin.h b/gst/playback/gstplaybasebin.h
+index c8c8649..26c9fcd 100644
+--- a/gst/playback/gstplaybasebin.h
++++ b/gst/playback/gstplaybasebin.h
+@@ -88,6 +88,7 @@ struct _GstPlayBaseBin {
+ gboolean subtitle_done;
+ gboolean need_rebuild;
+ gboolean raw_decoding_mode; /* Use smaller queues when source outputs raw data */
++ gboolean use_nw_queue;
+
+ GSList *subtitle_elements; /* subtitle elements that have 'subtitle-encoding' property */
+ gchar *subencoding; /* encoding to propagate to the above subtitle elements */