[gstreamer-plugins-base: 98/216] - Fix audio disappearing with newer pulsesink
by Dominik Mierzejewski
commit c277d043ffaa3404f76707a7184fe1dd3bfd0ffd
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Tue Oct 27 10:31:43 2009 +0000
- Fix audio disappearing with newer pulsesink
gstreamer-plugins-base.spec | 11 +++-
pulsesink-disable-old-version-hack.patch | 94 ++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 1 deletion(-)
---
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index c18efde..3f137f3 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -3,7 +3,7 @@
Name: %{gstreamer}-plugins-base
Version: 0.10.25
-Release: 2%{?dist}
+Release: 3%{?dist}
Summary: GStreamer streaming media framework base plug-ins
Group: Applications/Multimedia
@@ -37,6 +37,11 @@ Obsoletes: gstreamer-plugins
Patch0: gstpb-no-subtitle-errors.patch
# https://bugzilla.gnome.org/show_bug.cgi?id=591677
Patch1: gstpb-fix-missing-plugins.patch
+# Revert http://cgit.freedesktop.org/gstreamer/gst-plugins-base/commit/?id=35cddfb...
+# We have a new enough pulsesink
+# https://bugzilla.gnome.org/show_bug.cgi?id=599105
+Patch2: pulsesink-disable-old-version-hack.patch
+Conflicts: gstreamer-plugins-good < 0.10.16-3
# documentation
BuildRequires: gtk-doc >= 1.3
@@ -56,6 +61,7 @@ This package contains a set of well-maintained base plug-ins.
%setup -q -n gst-plugins-base-%{version}
%patch0 -p1 -b .subtitle-errors
%patch1 -p1 -b .missing-plugins
+%patch2 -p1 -R -b .old-pulsesink
%build
%configure \
@@ -251,6 +257,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Tue Oct 27 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.25-3
+- Fix audio disappearing with newer pulsesink
+
* Tue Oct 13 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.25-2
- Add patches to fix some playbin2 bugs (#518880)
diff --git a/pulsesink-disable-old-version-hack.patch b/pulsesink-disable-old-version-hack.patch
new file mode 100644
index 0000000..b4d3a7f
--- /dev/null
+++ b/pulsesink-disable-old-version-hack.patch
@@ -0,0 +1,94 @@
+From 35cddfb1e3ddc6513c7daca093d72151a13e9342 Mon Sep 17 00:00:00 2001
+From: Wim Taymans <wim.taymans(a)collabora.co.uk>
+Date: Thu, 10 Sep 2009 10:38:16 +0000
+Subject: baseaudiosink: add ugly backward compat hack
+
+Check for pulsesink < 0.10.17 because it includes code that is now included in
+baseaudiosink. Disable that code in baseaudiosink to be compatible with the
+older version.
+---
+diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c
+index afa62c3..23fde25 100644
+--- a/gst-libs/gst/audio/gstbaseaudiosink.c
++++ b/gst-libs/gst/audio/gstbaseaudiosink.c
+@@ -56,6 +56,8 @@ struct _GstBaseAudioSinkPrivate
+ gboolean sync_latency;
+
+ GstClockTime eos_time;
++
++ gboolean do_time_offset;
+ };
+
+ /* BaseAudioSink signals and args */
+@@ -231,12 +233,15 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass)
+ * thread-safety in GObject */
+ g_type_class_ref (GST_TYPE_AUDIO_CLOCK);
+ g_type_class_ref (GST_TYPE_RING_BUFFER);
++
+ }
+
+ static void
+ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,
+ GstBaseAudioSinkClass * g_class)
+ {
++ GstPluginFeature *feature;
++
+ baseaudiosink->priv = GST_BASE_AUDIO_SINK_GET_PRIVATE (baseaudiosink);
+
+ baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME;
+@@ -253,6 +258,24 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,
+ /* install some custom pad_query functions */
+ gst_pad_set_query_function (GST_BASE_SINK_PAD (baseaudiosink),
+ GST_DEBUG_FUNCPTR (gst_base_audio_sink_query_pad));
++
++ baseaudiosink->priv->do_time_offset = TRUE;
++
++ /* check the factory, pulsesink < 0.10.17 does the timestamp offset itself so
++ * we should not do ourselves */
++ feature =
++ GST_PLUGIN_FEATURE_CAST (GST_ELEMENT_CLASS (g_class)->elementfactory);
++ GST_DEBUG ("created from factory %p", feature);
++
++ /* HACK for old pulsesink that did the time_offset themselves */
++ if (feature) {
++ if (strcmp (gst_plugin_feature_get_name (feature), "pulsesink") == 0) {
++ if (!gst_plugin_feature_check_version (feature, 0, 10, 17)) {
++ /* we're dealing with an old pulsesink, we need to disable time corection */
++ baseaudiosink->priv->do_time_offset = FALSE;
++ }
++ }
++ }
+ }
+
+ static void
+@@ -1409,16 +1432,18 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
+ }
+
+ /* bring to position in the ringbuffer */
+- time_offset =
+- GST_AUDIO_CLOCK_CAST (sink->provided_clock)->abidata.ABI.time_offset;
+- if (render_start > time_offset)
+- render_start -= time_offset;
+- else
+- render_start = 0;
+- if (render_stop > time_offset)
+- render_stop -= time_offset;
+- else
+- render_stop = 0;
++ if (sink->priv->do_time_offset) {
++ time_offset =
++ GST_AUDIO_CLOCK_CAST (sink->provided_clock)->abidata.ABI.time_offset;
++ if (render_start > time_offset)
++ render_start -= time_offset;
++ else
++ render_start = 0;
++ if (render_stop > time_offset)
++ render_stop -= time_offset;
++ else
++ render_stop = 0;
++ }
+
+ /* and bring the time to the rate corrected offset in the buffer */
+ render_start = gst_util_uint64_scale_int (render_start,
+--
+cgit v0.8.2
4 years, 4 months
[gstreamer-plugins-base: 97/216] - Add patches to fix some playbin2 bugs (#518880)
by Dominik Mierzejewski
commit 2a5c1905e8be577c5aa28f81a634a3b227f2d0be
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Tue Oct 13 15:32:49 2009 +0000
- Add patches to fix some playbin2 bugs (#518880)
gstpb-fix-missing-plugins.patch | 2853 +++++++++++++++++++++++++++++++++++++++
gstpb-no-subtitle-errors.patch | 164 +++
gstreamer-plugins-base.spec | 12 +-
3 files changed, 3028 insertions(+), 1 deletion(-)
---
diff --git a/gstpb-fix-missing-plugins.patch b/gstpb-fix-missing-plugins.patch
new file mode 100644
index 0000000..dacfa8e
--- /dev/null
+++ b/gstpb-fix-missing-plugins.patch
@@ -0,0 +1,2853 @@
+From 881e4aa606ac184e4da485a88066ff30e88fc5cd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Sat, 26 Sep 2009 12:04:55 +0200
+Subject: [PATCH 1/7] decodebin2: Only use the object lock for protecting the subtitle elements
+
+Using the decodebin lock will result in deadlocks if the subtitle encoding
+is accessed from a pad-added handler.
+---
+ gst/playback/gstdecodebin2.c | 12 +++++-------
+ 1 files changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c
+index 3b60ae9..1ad7968 100644
+--- a/gst/playback/gstdecodebin2.c
++++ b/gst/playback/gstdecodebin2.c
+@@ -857,18 +857,16 @@ gst_decode_bin_set_subs_encoding (GstDecodeBin * dbin, const gchar * encoding)
+
+ GST_DEBUG_OBJECT (dbin, "Setting new encoding: %s", GST_STR_NULL (encoding));
+
+- DECODE_BIN_LOCK (dbin);
+ GST_OBJECT_LOCK (dbin);
+ g_free (dbin->encoding);
+ dbin->encoding = g_strdup (encoding);
+- GST_OBJECT_UNLOCK (dbin);
+
+ /* set the subtitle encoding on all added elements */
+ for (walk = dbin->subtitles; walk; walk = g_list_next (walk)) {
+ g_object_set (G_OBJECT (walk->data), "subtitle-encoding", dbin->encoding,
+ NULL);
+ }
+- DECODE_BIN_UNLOCK (dbin);
++ GST_OBJECT_UNLOCK (dbin);
+ }
+
+ static gchar *
+@@ -1319,11 +1317,11 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ continue;
+ }
+ if (subtitle) {
+- DECODE_BIN_LOCK (dbin);
++ GST_OBJECT_LOCK (dbin);
+ /* we added the element now, add it to the list of subtitle-encoding
+ * elements when we can set the property */
+ dbin->subtitles = g_list_prepend (dbin->subtitles, element);
+- DECODE_BIN_UNLOCK (dbin);
++ GST_OBJECT_UNLOCK (dbin);
+ }
+
+ res = TRUE;
+@@ -2364,10 +2362,10 @@ restart:
+
+ done:
+ gst_element_set_state (element, GST_STATE_NULL);
+- DECODE_BIN_LOCK (dbin);
++ GST_OBJECT_LOCK (dbin);
+ /* remove possible subtitle element */
+ dbin->subtitles = g_list_remove (dbin->subtitles, element);
+- DECODE_BIN_UNLOCK (dbin);
++ GST_OBJECT_UNLOCK (dbin);
+ gst_bin_remove (GST_BIN (dbin), element);
+
+ beach:
+--
+1.6.4.3
+
+From a7fe6e683ae91caa9445d899964553a2610fba17 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Sat, 26 Sep 2009 12:10:21 +0200
+Subject: [PATCH 2/7] decodebin2: Don't set the external ghostpads blocked but only their targets
+
+Pad blocks should never be done on external pads as outside elements
+might want to use their own pad blocks on them and this will lead to
+conflicts and deadlocks.
+---
+ gst/playback/gstdecodebin2.c | 23 ++++++++++++++++++-----
+ 1 files changed, 18 insertions(+), 5 deletions(-)
+
+diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c
+index 1ad7968..24e251b 100644
+--- a/gst/playback/gstdecodebin2.c
++++ b/gst/playback/gstdecodebin2.c
+@@ -2484,7 +2484,7 @@ gst_decode_pad_init (GstDecodePad * pad)
+ }
+
+ static void
+-source_pad_blocked_cb (GstDecodePad * dpad, gboolean blocked, gpointer unused)
++source_pad_blocked_cb (GstPad * opad, gboolean blocked, GstDecodePad * dpad)
+ {
+ GstDecodeGroup *group;
+ GstDecodeBin *dbin;
+@@ -2531,10 +2531,16 @@ static void
+ gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked)
+ {
+ GstDecodeBin *dbin = dpad->dbin;
++ GstPad *opad;
+
+ DECODE_BIN_DYN_LOCK (dbin);
+- gst_pad_set_blocked_async (GST_PAD (dpad), blocked,
+- (GstPadBlockCallback) source_pad_blocked_cb, NULL);
++ opad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (dpad));
++ if (!opad)
++ goto out;
++
++ gst_pad_set_blocked_async_full (opad, blocked,
++ (GstPadBlockCallback) source_pad_blocked_cb, gst_object_ref (dpad),
++ (GDestroyNotify) gst_object_unref);
+ if (blocked) {
+ if (dbin->shutdown) {
+ /* deactivate to force flushing state to prevent NOT_LINKED errors */
+@@ -2548,6 +2554,8 @@ gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked)
+ gst_object_unref (dpad);
+ dbin->blocked_pads = g_list_remove (dbin->blocked_pads, dpad);
+ }
++ gst_object_unref (opad);
++out:
+ DECODE_BIN_DYN_UNLOCK (dbin);
+ }
+
+@@ -2659,15 +2667,20 @@ unblock_pads (GstDecodeBin * dbin)
+
+ for (tmp = dbin->blocked_pads; tmp; tmp = next) {
+ GstDecodePad *dpad = (GstDecodePad *) tmp->data;
++ GstPad *opad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (dpad));
+
+ next = g_list_next (tmp);
++ if (!opad)
++ continue;
+
+ GST_DEBUG_OBJECT (dpad, "unblocking");
+- gst_pad_set_blocked_async (GST_PAD (dpad), FALSE,
+- (GstPadBlockCallback) source_pad_blocked_cb, NULL);
++ gst_pad_set_blocked_async_full (opad, FALSE,
++ (GstPadBlockCallback) source_pad_blocked_cb, gst_object_ref (dpad),
++ (GDestroyNotify) gst_object_unref);
+ /* make flushing, prevent NOT_LINKED */
+ GST_PAD_SET_FLUSHING (GST_PAD (dpad));
+ gst_object_unref (dpad);
++ gst_object_unref (opad);
+ GST_DEBUG_OBJECT (dpad, "unblocked");
+ }
+
+--
+1.6.4.3
+
+From 10cb19bcda3082435d57518666076a69e51a1bba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Thu, 24 Sep 2009 16:05:58 +0200
+Subject: [PATCH 3/7] factorylist: Use gst_caps_can_intersect() instead of _intersect()
+
+This is faster and results in less allocations.
+---
+ gst/playback/gstfactorylists.c | 9 +++------
+ 1 files changed, 3 insertions(+), 6 deletions(-)
+
+diff --git a/gst/playback/gstfactorylists.c b/gst/playback/gstfactorylists.c
+index 7099927..df1916f 100644
+--- a/gst/playback/gstfactorylists.c
++++ b/gst/playback/gstfactorylists.c
+@@ -240,7 +240,6 @@ gst_factory_list_filter (GValueArray * array, const GstCaps * caps)
+
+ /* we only care about the sink templates */
+ if (templ->direction == GST_PAD_SINK) {
+- GstCaps *intersect;
+ GstCaps *tmpl_caps;
+
+ /* try to intersect the caps with the caps of the template */
+@@ -248,21 +247,19 @@ gst_factory_list_filter (GValueArray * array, const GstCaps * caps)
+
+ /* FIXME, intersect is not the right method, we ideally want to check
+ * for a subset here */
+- intersect = gst_caps_intersect (caps, tmpl_caps);
+- gst_caps_unref (tmpl_caps);
+
+ /* check if the intersection is empty */
+- if (!gst_caps_is_empty (intersect)) {
++ if (gst_caps_can_intersect (caps, tmpl_caps)) {
+ /* non empty intersection, we can use this element */
+ GValue resval = { 0, };
+ g_value_init (&resval, G_TYPE_OBJECT);
+ g_value_set_object (&resval, factory);
+ g_value_array_append (result, &resval);
+ g_value_unset (&resval);
+- gst_caps_unref (intersect);
++ gst_caps_unref (tmpl_caps);
+ break;
+ }
+- gst_caps_unref (intersect);
++ gst_caps_unref (tmpl_caps);
+ }
+ }
+ }
+--
+1.6.4.3
+
+From 39bc12ee20437b352a13361ba48dcc886fce35a2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Sat, 26 Sep 2009 12:17:49 +0200
+Subject: [PATCH 4/7] decodebin2: Rewrite autoplugging and how groups of pads are exposed
+
+This now keeps track of everything that is going on, creates
+a tree of chains and groups to allow "demuxer after demuxer" scenarios
+and allows chained Oggs with multiple streams (needs oggdemux or playbin2 fixes).
+
+Also document everything in detail and give a general overview of what
+decodebin2 is doing at the top of the sources.
+
+Fixes bug #596183, #563828 and #591677.
+---
+ gst/playback/gstdecodebin2.c | 1782 ++++++++++++++++++++++--------------------
+ 1 files changed, 946 insertions(+), 836 deletions(-)
+
+diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c
+index 24e251b..1d6c9d2 100644
+--- a/gst/playback/gstdecodebin2.c
++++ b/gst/playback/gstdecodebin2.c
+@@ -1,5 +1,6 @@
+ /* GStreamer
+ * Copyright (C) <2006> Edward Hervey <edward(a)fluendo.com>
++ * Copyright (C) <2009> Sebastian Dröge <sebastian.droege(a)collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+@@ -31,6 +32,51 @@
+ * decodebin2 instead of the older #GstDecodeBin for its internal auto-plugging.
+ */
+
++/* Implementation notes:
++ *
++ * The following section describes how decodebin2 works internally.
++ *
++ * The first part of decodebin2 is it's typefind element, which tries
++ * to get the type of the input stream. If the type is found autoplugging starts.
++ *
++ * decodebin2 internally organizes the elements it autoplugged into GstDecodeChains
++ * and GstDecodeGroups. A decode chain is a single chain of decoding, this
++ * means that if decodebin2 every autoplugs an element with two+ srcpads
++ * (e.g. a demuxer) this will end the chain and everything following this
++ * demuxer will be put into decode groups below the chain. Otherwise,
++ * if an element has a single srcpad that outputs raw data the decode chain
++ * is ended too and a GstDecodePad is stored and blocked.
++ *
++ * A decode group combines a number of chains that are created by a
++ * demuxer element. All those chains are connected through a multiqueue to
++ * the demuxer. A new group for the same demuxer is only created if the
++ * demuxer has signaled no-more pads, in which case all following pads
++ * create a new chain in the new group.
++ *
++ * This continues until the top-level decode chain is complete. A decode
++ * chain is complete if it either ends with a blocked endpad, if autoplugging
++ * stopped because no suitable plugins could be found or if the active group
++ * is complete. A decode group OTOH is complete if all child chains are complete.
++ *
++ * If this happens at some point, all endpads of all active groups are exposed.
++ * For this decodebin2 adds the endpads, signals no-more-pads and then unblocks
++ * them. Now playback starts.
++ *
++ * If one of the chains that end on a endpad receives EOS decodebin2 checks upwards
++ * via the parent pointers if all chains and groups are drained. In that case
++ * everything goes into EOS.
++ * If there is a chain where the active group is drained but there exist next groups
++ * the active group is hidden (endpads are removed) and the next group is exposed.
++ *
++ * Note 1: If we're talking about blocked endpads this really means that the
++ * *target* pads of the endpads are blocked. Pads that are exposed to the outside
++ * should never ever be blocked!
++ *
++ * Note 2: If a group is complete and the parent's chain demuxer adds new pads
++ * but never signaled no-more-pads this additional pads will be ignored!
++ *
++ */
++
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+@@ -61,6 +107,7 @@ GST_STATIC_PAD_TEMPLATE ("src%d",
+ GST_DEBUG_CATEGORY_STATIC (gst_decode_bin_debug);
+ #define GST_CAT_DEFAULT gst_decode_bin_debug
+
++typedef struct _GstDecodeChain GstDecodeChain;
+ typedef struct _GstDecodeGroup GstDecodeGroup;
+ typedef struct _GstDecodePad GstDecodePad;
+ typedef GstGhostPadClass GstDecodePadClass;
+@@ -90,16 +137,14 @@ struct _GstDecodeBin
+
+ GstElement *typefind; /* this holds the typefind object */
+
+- GMutex *lock; /* Protects activegroup and groups */
+- GstDecodeGroup *activegroup; /* group currently active */
+- GList *groups; /* List of non-active GstDecodeGroups, sorted in
+- * order of creation. */
+- GList *oldgroups; /* List of no-longer-used GstDecodeGroups.
+- * Should be freed in dispose */
++ GMutex *expose_lock; /* Protects exposal and removal of groups */
++ GstDecodeChain *decode_chain; /* Top level decode chain */
+ gint nbpads; /* unique identifier for source pads */
+
+ GValueArray *factories; /* factories we can use for selecting elements */
+- GList *subtitles; /* List of elements with subtitle-encoding */
++
++ GList *subtitles; /* List of elements with subtitle-encoding,
++ * protected by object lock! */
+
+ gboolean have_type; /* if we received the have_type signal */
+ guint have_type_id; /* signal id for have-type from typefind */
+@@ -170,8 +215,8 @@ static const GstElementDetails gst_decode_bin_details =
+ GST_ELEMENT_DETAILS ("Decoder Bin",
+ "Generic/Bin/Decoder",
+ "Autoplug and decode to raw media",
+- "Edward Hervey <edward(a)fluendo.com>");
+-
++ "Edward Hervey <edward.hervey(a)collabora.co.uk>, "
++ "Sebastian Dröge <sebastian.droege(a)collabora.co.uk>");
+
+ static void do_async_start (GstDecodeBin * dbin);
+ static void do_async_done (GstDecodeBin * dbin);
+@@ -194,33 +239,31 @@ static void gst_decode_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+ static void gst_decode_bin_set_caps (GstDecodeBin * dbin, GstCaps * caps);
+ static GstCaps *gst_decode_bin_get_caps (GstDecodeBin * dbin);
+-static void caps_notify_group_cb (GstPad * pad, GParamSpec * unused,
+- GstDecodeGroup * group);
+ static void caps_notify_cb (GstPad * pad, GParamSpec * unused,
+- GstDecodeBin * dbin);
++ GstDecodeChain * chain);
+
+ static GstPad *find_sink_pad (GstElement * element);
+ static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element,
+ GstStateChange transition);
+
+-#define DECODE_BIN_LOCK(dbin) G_STMT_START { \
++#define EXPOSE_LOCK(dbin) G_STMT_START { \
+ GST_LOG_OBJECT (dbin, \
+- "locking from thread %p", \
++ "expose locking from thread %p", \
+ g_thread_self ()); \
+- g_mutex_lock (GST_DECODE_BIN_CAST(dbin)->lock); \
++ g_mutex_lock (GST_DECODE_BIN_CAST(dbin)->expose_lock); \
+ GST_LOG_OBJECT (dbin, \
+- "locked from thread %p", \
++ "expose locked from thread %p", \
+ g_thread_self ()); \
+ } G_STMT_END
+
+-#define DECODE_BIN_UNLOCK(dbin) G_STMT_START { \
++#define EXPOSE_UNLOCK(dbin) G_STMT_START { \
+ GST_LOG_OBJECT (dbin, \
+- "unlocking from thread %p", \
++ "expose unlocking from thread %p", \
+ g_thread_self ()); \
+- g_mutex_unlock (GST_DECODE_BIN_CAST(dbin)->lock); \
++ g_mutex_unlock (GST_DECODE_BIN_CAST(dbin)->expose_lock); \
+ } G_STMT_END
+
+-#define DECODE_BIN_DYN_LOCK(dbin) G_STMT_START { \
++#define DYN_LOCK(dbin) G_STMT_START { \
+ GST_LOG_OBJECT (dbin, \
+ "dynlocking from thread %p", \
+ g_thread_self ()); \
+@@ -230,7 +273,7 @@ static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element,
+ g_thread_self ()); \
+ } G_STMT_END
+
+-#define DECODE_BIN_DYN_UNLOCK(dbin) G_STMT_START { \
++#define DYN_UNLOCK(dbin) G_STMT_START { \
+ GST_LOG_OBJECT (dbin, \
+ "dynunlocking from thread %p", \
+ g_thread_self ()); \
+@@ -241,68 +284,109 @@ static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element,
+ *
+ * Streams belonging to the same group/chain of a media file
+ *
++ * When changing something here lock the parent chain!
+ */
+ struct _GstDecodeGroup
+ {
+ GstDecodeBin *dbin;
+- GMutex *lock;
+- GstElement *multiqueue;
+-
+- gboolean exposed; /* TRUE if this group is exposed */
+- gboolean drained; /* TRUE if EOS went through all endpads */
+- gboolean blocked; /* TRUE if all endpads are blocked */
+- gboolean complete; /* TRUE if we are not expecting anymore streams
+- * on this group */
++ GstDecodeChain *parent;
++
++ GstElement *multiqueue; /* Used for linking all child chains */
+ gulong overrunsig; /* the overrun signal for multiqueue */
+- guint nbdynamic; /* number of dynamic pads in the group. */
+
+- GList *endpads; /* List of GstDecodePad of source pads to be exposed */
+- GList *reqpads; /* List of RequestPads for multiqueue. */
++ gboolean overrun; /* TRUE if the multiqueue signaled overrun. This
++ * means that we should really expose the group */
++
++ gboolean no_more_pads; /* TRUE if the demuxer signaled no-more-pads */
++ gboolean drained; /* TRUE if the all children are drained */
++
++ GList *children; /* List of GstDecodeChains in this group */
++
++ GList *reqpads; /* List of RequestPads for multiqueue, there is
++ * exactly one RequestPad per child chain */
+ };
+
+-#define GROUP_MUTEX_LOCK(group) G_STMT_START { \
+- GST_LOG_OBJECT (group->dbin, \
+- "locking group %p from thread %p", \
+- group, g_thread_self ()); \
+- g_mutex_lock (group->lock); \
+- GST_LOG_OBJECT (group->dbin, \
+- "locked group %p from thread %p", \
+- group, g_thread_self ()); \
+-} G_STMT_END
++struct _GstDecodeChain
++{
++ GstDecodeGroup *parent;
++ GstDecodeBin *dbin;
+
+-#define GROUP_MUTEX_UNLOCK(group) G_STMT_START { \
+- GST_LOG_OBJECT (group->dbin, \
+- "unlocking group %p from thread %p", \
+- group, g_thread_self ()); \
+- g_mutex_unlock (group->lock); \
+-} G_STMT_END
++ GMutex *lock; /* Protects this chain and its groups */
+
++ gboolean demuxer; /* TRUE if elements->data is a demuxer */
++ GList *elements; /* All elements in this group, first
++ is the latest and most downstream element */
+
+-static GstDecodeGroup *gst_decode_group_new (GstDecodeBin * decode_bin,
+- gboolean use_queue);
+-static GstPad *gst_decode_group_control_demuxer_pad (GstDecodeGroup * group,
+- GstPad * pad);
+-static gboolean gst_decode_group_control_source_pad (GstDecodeGroup * group,
+- GstDecodePad * pad);
+-static gboolean gst_decode_group_expose (GstDecodeGroup * group);
+-static gboolean gst_decode_group_check_if_blocked (GstDecodeGroup * group);
+-static void gst_decode_group_set_complete (GstDecodeGroup * group);
++ /* Note: there are only groups if the last element of this chain
++ * is a demuxer, otherwise the chain will end with an endpad.
++ * The other way around this means, that endpad only exists if this
++ * chain doesn't end with a demuxer! */
++
++ GstDecodeGroup *active_group; /* Currently active group */
++ GList *next_groups; /* head is newest group, tail is next group.
++ a new group will be created only if the head
++ group had no-more-pads. If it's only exposed
++ all new pads will be ignored! */
++ GList *pending_pads; /* Pads that have no fixed caps yet */
++
++ GstDecodePad *endpad; /* Pad of this chain that could be exposed */
++ gboolean deadend; /* This chain is incomplete and can't be completed,
++ e.g. no suitable decoder could be found
++ */
++
++ /* FIXME: This should be done directly via a thread! */
++ GList *old_groups; /* Groups that should be freed later */
++};
++
++static void gst_decode_chain_free (GstDecodeChain * chain);
++static GstDecodeChain *gst_decode_chain_new (GstDecodeBin * dbin,
++ GstDecodeGroup * group);
+ static void gst_decode_group_hide (GstDecodeGroup * group);
+ static void gst_decode_group_free (GstDecodeGroup * group);
++static GstDecodeGroup *gst_decode_group_new (GstDecodeBin * dbin,
++ GstDecodeChain * chain);
++static gboolean gst_decode_chain_is_complete (GstDecodeChain * chain);
++static void gst_decode_chain_handle_eos (GstDecodeChain * chain);
++static gboolean gst_decode_chain_expose (GstDecodeChain * chain,
++ GList ** endpads);
++static gboolean gst_decode_chain_is_drained (GstDecodeChain * chain);
++static gboolean gst_decode_group_is_complete (GstDecodeGroup * group);
++static GstPad *gst_decode_group_control_demuxer_pad (GstDecodeGroup * group,
++ GstPad * pad);
++static gboolean gst_decode_group_is_drained (GstDecodeGroup * group);
++
++static gboolean gst_decode_bin_expose (GstDecodeBin * dbin);
++
++#define CHAIN_MUTEX_LOCK(chain) G_STMT_START { \
++ GST_LOG_OBJECT (chain->dbin, \
++ "locking chain %p from thread %p", \
++ chain, g_thread_self ()); \
++ g_mutex_lock (chain->lock); \
++ GST_LOG_OBJECT (chain->dbin, \
++ "locked chain %p from thread %p", \
++ chain, g_thread_self ()); \
++} G_STMT_END
++
++#define CHAIN_MUTEX_UNLOCK(chain) G_STMT_START { \
++ GST_LOG_OBJECT (chain->dbin, \
++ "unlocking chain %p from thread %p", \
++ chain, g_thread_self ()); \
++ g_mutex_unlock (chain->lock); \
++} G_STMT_END
+
+ /* GstDecodePad
+ *
+- * GstPad private used for source pads of groups
++ * GstPad private used for source pads of chains
+ */
+ struct _GstDecodePad
+ {
+ GstGhostPad parent;
+ GstDecodeBin *dbin;
+- GstDecodeGroup *group;
++ GstDecodeChain *chain;
+
+- gboolean blocked; /* the pad is blocked */
++ gboolean blocked; /* the *target* pad is blocked */
++ gboolean exposed; /* the pad is exposed */
+ gboolean drained; /* an EOS has been seen on the pad */
+- gboolean added; /* the pad is added to decodebin */
+ };
+
+ G_DEFINE_TYPE (GstDecodePad, gst_decode_pad, GST_TYPE_GHOST_PAD);
+@@ -310,10 +394,11 @@ G_DEFINE_TYPE (GstDecodePad, gst_decode_pad, GST_TYPE_GHOST_PAD);
+ #define GST_DECODE_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_PAD,GstDecodePad))
+
+ static GstDecodePad *gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad,
+- GstDecodeGroup * group);
++ GstDecodeChain * chain);
+ static void gst_decode_pad_activate (GstDecodePad * dpad,
+- GstDecodeGroup * group);
++ GstDecodeChain * chain);
+ static void gst_decode_pad_unblock (GstDecodePad * dpad);
++static void gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked);
+
+ /********************************
+ * Standard GObject boilerplate *
+@@ -643,9 +728,8 @@ gst_decode_bin_init (GstDecodeBin * decode_bin)
+ G_CALLBACK (type_found), decode_bin);
+ }
+
+- decode_bin->lock = g_mutex_new ();
+- decode_bin->activegroup = NULL;
+- decode_bin->groups = NULL;
++ decode_bin->expose_lock = g_mutex_new ();
++ decode_bin->decode_chain = NULL;
+
+ decode_bin->dyn_lock = g_mutex_new ();
+ decode_bin->shutdown = FALSE;
+@@ -658,82 +742,6 @@ gst_decode_bin_init (GstDecodeBin * decode_bin)
+ }
+
+ static void
+-gst_decode_bin_remove_groups (GstDecodeBin * dbin)
+-{
+- GList *tmp;
+- GstIterator *it;
+- gpointer point;
+- gboolean done;
+- GstIteratorResult res;
+-
+- GST_DEBUG_OBJECT (dbin, "cleaning up");
+-
+- if (dbin->activegroup) {
+- GST_DEBUG_OBJECT (dbin, "free active group %p", dbin->activegroup);
+- gst_decode_group_free (dbin->activegroup);
+- dbin->activegroup = NULL;
+- }
+-
+- /* remove groups */
+- for (tmp = dbin->groups; tmp; tmp = g_list_next (tmp)) {
+- GstDecodeGroup *group = (GstDecodeGroup *) tmp->data;
+-
+- GST_DEBUG_OBJECT (dbin, "free group %p", group);
+- gst_decode_group_free (group);
+- }
+- g_list_free (dbin->groups);
+- dbin->groups = NULL;
+-
+- for (tmp = dbin->oldgroups; tmp; tmp = g_list_next (tmp)) {
+- GstDecodeGroup *group = (GstDecodeGroup *) tmp->data;
+-
+- GST_DEBUG_OBJECT (dbin, "free old group %p", group);
+- gst_decode_group_free (group);
+- }
+- g_list_free (dbin->oldgroups);
+- dbin->oldgroups = NULL;
+-
+- GST_DEBUG_OBJECT (dbin, "removing last elements");
+-
+- /* remove all remaining elements */
+- it = gst_bin_iterate_elements (GST_BIN_CAST (dbin));
+-restart:
+- done = FALSE;
+- while (!done) {
+- res = gst_iterator_next (it, &point);
+- switch (res) {
+- case GST_ITERATOR_DONE:
+- done = TRUE;
+- break;
+- case GST_ITERATOR_RESYNC:
+- gst_iterator_resync (it);
+- goto restart;
+- case GST_ITERATOR_ERROR:
+- GST_WARNING_OBJECT (dbin,
+- "Had an error while iterating bin %s", GST_ELEMENT_NAME (dbin));
+- done = TRUE;
+- break;
+- case GST_ITERATOR_OK:
+- {
+- GstElement *elem = GST_ELEMENT_CAST (point);
+-
+- /* don't remove the typefind element */
+- if (elem != dbin->typefind) {
+- GST_DEBUG_OBJECT (dbin, "remove element %s", GST_ELEMENT_NAME (elem));
+- gst_bin_remove (GST_BIN_CAST (dbin), elem);
+- gst_element_set_state (elem, GST_STATE_NULL);
+- }
+- gst_object_unref (elem);
+- break;
+- }
+- default:
+- break;
+- }
+- }
+- gst_iterator_free (it);
+-}
+-
+-static void
+ gst_decode_bin_dispose (GObject * object)
+ {
+ GstDecodeBin *decode_bin;
+@@ -744,7 +752,9 @@ gst_decode_bin_dispose (GObject * object)
+ g_value_array_free (decode_bin->factories);
+ decode_bin->factories = NULL;
+
+- gst_decode_bin_remove_groups (decode_bin);
++ if (decode_bin->decode_chain)
++ gst_decode_chain_free (decode_bin->decode_chain);
++ decode_bin->decode_chain = NULL;
+
+ if (decode_bin->caps)
+ gst_caps_unref (decode_bin->caps);
+@@ -766,9 +776,9 @@ gst_decode_bin_finalize (GObject * object)
+
+ decode_bin = GST_DECODE_BIN (object);
+
+- if (decode_bin->lock) {
+- g_mutex_free (decode_bin->lock);
+- decode_bin->lock = NULL;
++ if (decode_bin->expose_lock) {
++ g_mutex_free (decode_bin->expose_lock);
++ decode_bin->expose_lock = NULL;
+ }
+
+ if (decode_bin->dyn_lock) {
+@@ -790,21 +800,10 @@ gst_decode_bin_finalize (GObject * object)
+ static void
+ gst_decode_bin_set_caps (GstDecodeBin * dbin, GstCaps * caps)
+ {
+- GstCaps *old;
+-
+ GST_DEBUG_OBJECT (dbin, "Setting new caps: %" GST_PTR_FORMAT, caps);
+
+ GST_OBJECT_LOCK (dbin);
+- old = dbin->caps;
+- if (old != caps) {
+- if (caps)
+- gst_caps_ref (caps);
+-
+- dbin->caps = caps;
+-
+- if (old)
+- gst_caps_unref (old);
+- }
++ gst_caps_replace (&dbin->caps, caps);
+ GST_OBJECT_UNLOCK (dbin);
+ }
+
+@@ -994,26 +993,20 @@ static gboolean is_demuxer_element (GstElement * srcelement);
+
+ static gboolean connect_pad (GstDecodeBin * dbin, GstElement * src,
+ GstDecodePad * dpad, GstPad * pad, GstCaps * caps, GValueArray * factories,
+- GstDecodeGroup * group);
++ GstDecodeChain * chain);
+ static gboolean connect_element (GstDecodeBin * dbin, GstElement * element,
+- GstDecodeGroup * group);
++ GstDecodeChain * chain);
+ static void expose_pad (GstDecodeBin * dbin, GstElement * src,
+- GstDecodePad * dpad, GstPad * pad, GstDecodeGroup * group);
++ GstDecodePad * dpad, GstPad * pad, GstDecodeChain * chain);
+
+-static void pad_added_group_cb (GstElement * element, GstPad * pad,
+- GstDecodeGroup * group);
+-static void pad_removed_group_cb (GstElement * element, GstPad * pad,
+- GstDecodeGroup * group);
+-static void no_more_pads_group_cb (GstElement * element,
+- GstDecodeGroup * group);
+ static void pad_added_cb (GstElement * element, GstPad * pad,
+- GstDecodeBin * dbin);
++ GstDecodeChain * chain);
+ static void pad_removed_cb (GstElement * element, GstPad * pad,
+- GstDecodeBin * dbin);
+-static void no_more_pads_cb (GstElement * element, GstDecodeBin * dbin);
++ GstDecodeChain * chain);
++static void no_more_pads_cb (GstElement * element, GstDecodeChain * chain);
+
+-static GstDecodeGroup *get_current_group (GstDecodeBin * dbin,
+- gboolean create, gboolean demux, gboolean * created);
++static GstDecodeGroup *gst_decode_chain_get_current_group (GstDecodeChain *
++ chain);
+
+ /* called when a new pad is discovered. It will perform some basic actions
+ * before trying to link something to it.
+@@ -1029,7 +1022,7 @@ static GstDecodeGroup *get_current_group (GstDecodeBin * dbin,
+ */
+ static void
+ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
+- GstCaps * caps, GstDecodeGroup * group)
++ GstCaps * caps, GstDecodeChain * chain)
+ {
+ gboolean apcontinue = TRUE;
+ GValueArray *factories = NULL, *result = NULL;
+@@ -1038,13 +1031,40 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
+ GST_DEBUG_OBJECT (dbin, "Pad %s:%s caps:%" GST_PTR_FORMAT,
+ GST_DEBUG_PAD_NAME (pad), caps);
+
++ if (chain->elements && src != chain->elements->data) {
++ GST_ERROR_OBJECT (dbin, "New pad from not the last element in this chain");
++ return;
++ }
++
++ if (chain->endpad) {
++ GST_ERROR_OBJECT (dbin, "New pad in a chain that is already complete");
++ return;
++ }
++
++ if (chain->demuxer) {
++ GstDecodeGroup *group;
++ GstDecodeChain *oldchain = chain;
++
++ CHAIN_MUTEX_LOCK (oldchain);
++ group = gst_decode_chain_get_current_group (chain);
++ if (group) {
++ chain = gst_decode_chain_new (dbin, group);
++ group->children = g_list_prepend (group->children, chain);
++ }
++ CHAIN_MUTEX_UNLOCK (oldchain);
++ if (!group) {
++ GST_WARNING_OBJECT (dbin, "No current group");
++ return;
++ }
++ }
++
+ if ((caps == NULL) || gst_caps_is_empty (caps))
+ goto unknown_type;
+
+ if (gst_caps_is_any (caps))
+ goto any_caps;
+
+- dpad = gst_decode_pad_new (dbin, pad, group);
++ dpad = gst_decode_pad_new (dbin, pad, chain);
+
+ /* 1. Emit 'autoplug-continue' the result will tell us if this pads needs
+ * further autoplugging. */
+@@ -1088,7 +1108,7 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
+
+ /* 1.e else continue autoplugging something from the list. */
+ GST_LOG_OBJECT (pad, "Let's continue discovery on this pad");
+- connect_pad (dbin, src, dpad, pad, caps, factories, group);
++ connect_pad (dbin, src, dpad, pad, caps, factories, chain);
+
+ gst_object_unref (dpad);
+ g_value_array_free (factories);
+@@ -1098,7 +1118,7 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
+ expose_pad:
+ {
+ GST_LOG_OBJECT (dbin, "Pad is final. autoplug-continue:%d", apcontinue);
+- expose_pad (dbin, src, dpad, pad, group);
++ expose_pad (dbin, src, dpad, pad, chain);
+ gst_object_unref (dpad);
+ return;
+ }
+@@ -1108,14 +1128,19 @@ unknown_type:
+ g_signal_emit (G_OBJECT (dbin),
+ gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
+
+- /* Check if there are no pending groups, if so, commit our state */
+- if (dbin->groups == NULL) {
+- do_async_done (dbin);
+- }
++ chain->deadend = TRUE;
+
+ gst_element_post_message (GST_ELEMENT_CAST (dbin),
+ gst_missing_decoder_message_new (GST_ELEMENT_CAST (dbin), caps));
+
++ /* Try to expose anything */
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain)) {
++ gst_decode_bin_expose (dbin);
++ }
++ EXPOSE_UNLOCK (dbin);
++ do_async_done (dbin);
++
+ if (src == dbin->typefind) {
+ gchar *desc;
+
+@@ -1150,17 +1175,14 @@ any_caps:
+ setup_caps_delay:
+ {
+ /* connect to caps notification */
+- if (group) {
+- GROUP_MUTEX_LOCK (group);
+- group->nbdynamic++;
+- GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic elements", group,
+- group->nbdynamic);
+- GROUP_MUTEX_UNLOCK (group);
+- g_signal_connect (G_OBJECT (pad), "notify::caps",
+- G_CALLBACK (caps_notify_group_cb), group);
+- } else
+- g_signal_connect (G_OBJECT (pad), "notify::caps",
+- G_CALLBACK (caps_notify_cb), dbin);
++ CHAIN_MUTEX_LOCK (chain);
++ GST_LOG_OBJECT (dbin, "Chain %p has now %d dynamic pads", chain,
++ g_list_length (chain->pending_pads));
++ chain->pending_pads =
++ g_list_prepend (chain->pending_pads, gst_object_ref (pad));
++ CHAIN_MUTEX_UNLOCK (chain);
++ g_signal_connect (G_OBJECT (pad), "notify::caps",
++ G_CALLBACK (caps_notify_cb), chain);
+ return;
+ }
+ }
+@@ -1179,28 +1201,28 @@ setup_caps_delay:
+ static gboolean
+ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ GstPad * pad, GstCaps * caps, GValueArray * factories,
+- GstDecodeGroup * group)
++ GstDecodeChain * chain)
+ {
+ gboolean res = FALSE;
+ GstPad *mqpad = NULL;
++ gboolean is_demuxer = chain->parent && !chain->elements; /* First pad after the demuxer */
+
+ g_return_val_if_fail (factories != NULL, FALSE);
+ g_return_val_if_fail (factories->n_values > 0, FALSE);
+
+- GST_DEBUG_OBJECT (dbin, "pad %s:%s , group:%p",
+- GST_DEBUG_PAD_NAME (pad), group);
++ GST_DEBUG_OBJECT (dbin, "pad %s:%s , chain:%p",
++ GST_DEBUG_PAD_NAME (pad), chain);
+
+ /* 1. is element demuxer or parser */
+- if (is_demuxer_element (src)) {
+- GST_LOG_OBJECT (src, "is a demuxer, connecting the pad through multiqueue");
+-
+- if (!group)
+- group = get_current_group (dbin, TRUE, TRUE, NULL);
++ if (is_demuxer) {
++ GST_LOG_OBJECT (src,
++ "is a demuxer, connecting the pad through multiqueue '%s'",
++ GST_OBJECT_NAME (chain->parent->multiqueue));
+
+ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), NULL);
+- if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad)))
++ if (!(mqpad = gst_decode_group_control_demuxer_pad (chain->parent, pad)))
+ goto beach;
+- src = group->multiqueue;
++ src = chain->parent->multiqueue;
+ pad = mqpad;
+ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad);
+ }
+@@ -1213,6 +1235,11 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ GstPad *sinkpad;
+ gboolean subtitle;
+
++ /* Set dpad target to pad again, it might've been unset
++ * below but we came back here because something failed
++ */
++ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad);
++
+ /* take first factory */
+ factory = g_value_get_object (g_value_array_get_nth (factories, 0));
+ /* Remove selected factory from the list. */
+@@ -1230,7 +1257,7 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ case GST_AUTOPLUG_SELECT_EXPOSE:
+ GST_DEBUG_OBJECT (dbin, "autoplug select requested expose");
+ /* expose the pad, we don't have the source element */
+- expose_pad (dbin, src, dpad, pad, group);
++ expose_pad (dbin, src, dpad, pad, chain);
+ res = TRUE;
+ goto beach;
+ case GST_AUTOPLUG_SELECT_SKIP:
+@@ -1293,8 +1320,14 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ gst_object_unref (sinkpad);
+ GST_LOG_OBJECT (dbin, "linked on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
++ CHAIN_MUTEX_LOCK (chain);
++ chain->elements =
++ g_list_prepend (chain->elements, gst_object_ref (element));
++ chain->demuxer = is_demuxer_element (element);
++ CHAIN_MUTEX_UNLOCK (chain);
++
+ /* link this element further */
+- connect_element (dbin, element, group);
++ connect_element (dbin, element, chain);
+
+ /* try to configure the subtitle encoding property when we can */
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
+@@ -1310,10 +1343,22 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+ /* Bring the element to the state of the parent */
+ if ((gst_element_set_state (element,
+ GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) {
++ GstElement *tmp = NULL;
++
+ GST_WARNING_OBJECT (dbin, "Couldn't set %s to PAUSED",
+ GST_ELEMENT_NAME (element));
+- gst_element_set_state (element, GST_STATE_NULL);
+- gst_bin_remove (GST_BIN (dbin), element);
++
++ /* Remove all elements in this chain that were just added. No
++ * other thread could've added elements in the meantime */
++ CHAIN_MUTEX_LOCK (chain);
++ do {
++ tmp = chain->elements->data;
++ gst_element_set_state (tmp, GST_STATE_NULL);
++ gst_bin_remove (GST_BIN (dbin), tmp);
++ chain->elements = g_list_delete_link (chain->elements, chain->elements);
++ } while (tmp != element);
++ CHAIN_MUTEX_UNLOCK (chain);
++
+ continue;
+ }
+ if (subtitle) {
+@@ -1337,15 +1382,15 @@ beach:
+
+ static gboolean
+ connect_element (GstDecodeBin * dbin, GstElement * element,
+- GstDecodeGroup * group)
++ GstDecodeChain * chain)
+ {
+ GList *pads;
+ gboolean res = TRUE;
+ gboolean dynamic = FALSE;
+ GList *to_connect = NULL;
+
+- GST_DEBUG_OBJECT (dbin, "Attempting to connect element %s [group:%p] further",
+- GST_ELEMENT_NAME (element), group);
++ GST_DEBUG_OBJECT (dbin, "Attempting to connect element %s [chain:%p] further",
++ GST_ELEMENT_NAME (element), chain);
+
+ /* 1. Loop over pad templates, grabbing existing pads along the way */
+ for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads;
+@@ -1406,31 +1451,16 @@ connect_element (GstDecodeBin * dbin, GstElement * element,
+ }
+ }
+
+- /* 2. if there are more potential pads, connect to relevent signals */
++ /* 2. if there are more potential pads, connect to relevant signals */
+ if (dynamic) {
+- if (group) {
+- GST_LOG_OBJECT (dbin, "Adding signals to element %s in group %p",
+- GST_ELEMENT_NAME (element), group);
+- GROUP_MUTEX_LOCK (group);
+- group->nbdynamic++;
+- GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic elements", group,
+- group->nbdynamic);
+- GROUP_MUTEX_UNLOCK (group);
+- g_signal_connect (G_OBJECT (element), "pad-added",
+- G_CALLBACK (pad_added_group_cb), group);
+- g_signal_connect (G_OBJECT (element), "pad-removed",
+- G_CALLBACK (pad_removed_group_cb), group);
+- g_signal_connect (G_OBJECT (element), "no-more-pads",
+- G_CALLBACK (no_more_pads_group_cb), group);
+- } else {
+- /* This is a non-grouped element, the handlers are different */
+- g_signal_connect (G_OBJECT (element), "pad-added",
+- G_CALLBACK (pad_added_cb), dbin);
+- g_signal_connect (G_OBJECT (element), "pad-removed",
+- G_CALLBACK (pad_removed_cb), dbin);
+- g_signal_connect (G_OBJECT (element), "no-more-pads",
+- G_CALLBACK (no_more_pads_cb), dbin);
+- }
++ GST_LOG_OBJECT (dbin, "Adding signals to element %s in chain %p",
++ GST_ELEMENT_NAME (element), chain);
++ g_signal_connect (G_OBJECT (element), "pad-added",
++ G_CALLBACK (pad_added_cb), chain);
++ g_signal_connect (G_OBJECT (element), "pad-removed",
++ G_CALLBACK (pad_removed_cb), chain);
++ g_signal_connect (G_OBJECT (element), "no-more-pads",
++ G_CALLBACK (no_more_pads_cb), chain);
+ }
+
+ /* 3. for every available pad, connect it */
+@@ -1439,7 +1469,7 @@ connect_element (GstDecodeBin * dbin, GstElement * element,
+ GstCaps *caps;
+
+ caps = gst_pad_get_caps (pad);
+- analyze_new_pad (dbin, element, pad, caps, group);
++ analyze_new_pad (dbin, element, pad, caps, chain);
+ if (caps)
+ gst_caps_unref (caps);
+
+@@ -1452,43 +1482,42 @@ connect_element (GstDecodeBin * dbin, GstElement * element,
+
+ /* expose_pad:
+ *
+- * Expose the given pad on the group as a decoded pad.
+- * If group is NULL, a GstDecodeGroup will be created and setup properly.
++ * Expose the given pad on the chain as a decoded pad.
+ */
+ static void
+ expose_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad,
+- GstPad * pad, GstDecodeGroup * group)
++ GstPad * pad, GstDecodeChain * chain)
+ {
+- gboolean newgroup = FALSE;
+- gboolean isdemux;
+ GstPad *mqpad = NULL;
+
+- GST_DEBUG_OBJECT (dbin, "pad %s:%s, group:%p",
+- GST_DEBUG_PAD_NAME (pad), group);
+-
+- isdemux = is_demuxer_element (src);
+-
+- if (!group)
+- group = get_current_group (dbin, TRUE, isdemux, &newgroup);
++ GST_DEBUG_OBJECT (dbin, "pad %s:%s, chain:%p",
++ GST_DEBUG_PAD_NAME (pad), chain);
+
+- if (isdemux) {
++ /* If this is the first pad for this chain, there are no other elements
++ * and the source element is not the multiqueue we must link through the
++ * multiqueue.
++ *
++ * This is the case if a demuxer directly exposed a raw pad.
++ */
++ if (chain->parent && !chain->elements && src != chain->parent->multiqueue) {
+ GST_LOG_OBJECT (src, "connecting the pad through multiqueue");
+
+ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), NULL);
+- if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad)))
++ if (!(mqpad = gst_decode_group_control_demuxer_pad (chain->parent, pad)))
+ goto beach;
+ pad = mqpad;
+ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad);
+ }
+
+- gst_decode_group_control_source_pad (group, dpad);
++ gst_decode_pad_activate (dpad, chain);
++ chain->endpad = gst_object_ref (dpad);
+
+- if (newgroup && !isdemux) {
+- /* If we have discovered a raw pad and it doesn't belong to any group,
+- * that means there wasn't any demuxer. In that case, we consider the
+- * group as being complete. */
+- gst_decode_group_set_complete (group);
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain)) {
++ gst_decode_bin_expose (dbin);
+ }
++ EXPOSE_UNLOCK (dbin);
++
+ if (mqpad)
+ gst_object_unref (mqpad);
+
+@@ -1514,16 +1543,17 @@ type_found (GstElement * typefind, guint probability,
+ goto exit;
+ }
+
+- /* we can only deal with one type, we don't yet support dynamically changing
++ /* FIXME: we can only deal with one type, we don't yet support dynamically changing
+ * caps from the typefind element */
+- if (decode_bin->have_type)
++ if (decode_bin->have_type || decode_bin->decode_chain)
+ goto exit;
+
+ decode_bin->have_type = TRUE;
+
+ pad = gst_element_get_static_pad (typefind, "src");
+
+- analyze_new_pad (decode_bin, typefind, pad, caps, NULL);
++ decode_bin->decode_chain = gst_decode_chain_new (decode_bin, NULL);
++ analyze_new_pad (decode_bin, typefind, pad, caps, decode_bin->decode_chain);
+
+ gst_object_unref (pad);
+
+@@ -1532,133 +1562,116 @@ exit:
+ }
+
+ static void
+-pad_added_group_cb (GstElement * element, GstPad * pad, GstDecodeGroup * group)
++pad_added_cb (GstElement * element, GstPad * pad, GstDecodeChain * chain)
+ {
+ GstCaps *caps;
+- gboolean expose = FALSE;
+ GstDecodeBin *dbin;
+
+- dbin = group->dbin;
++ dbin = chain->dbin;
+
+- GST_DEBUG_OBJECT (pad, "pad added, group:%p", group);
++ GST_DEBUG_OBJECT (pad, "pad added, chain:%p", chain);
+
+ caps = gst_pad_get_caps (pad);
+- analyze_new_pad (dbin, element, pad, caps, group);
++ analyze_new_pad (dbin, element, pad, caps, chain);
+ if (caps)
+ gst_caps_unref (caps);
+
+- GROUP_MUTEX_LOCK (group);
+- if (group->nbdynamic > 0)
+- group->nbdynamic--;
+- GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic objects", group,
+- group->nbdynamic);
+- if (group->nbdynamic == 0)
+- expose = TRUE;
+- GROUP_MUTEX_UNLOCK (group);
+-
+- if (expose) {
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain)) {
+ GST_LOG_OBJECT (dbin,
+ "That was the last dynamic object, now attempting to expose the group");
+- DECODE_BIN_LOCK (dbin);
+- if (!gst_decode_group_expose (group))
++ if (!gst_decode_bin_expose (dbin))
+ GST_WARNING_OBJECT (dbin, "Couldn't expose group");
+- DECODE_BIN_UNLOCK (dbin);
+ }
++ EXPOSE_UNLOCK (dbin);
+ }
+
+ static void
+-pad_removed_group_cb (GstElement * element, GstPad * pad,
+- GstDecodeGroup * group)
++pad_removed_cb (GstElement * element, GstPad * pad, GstDecodeChain * chain)
+ {
+- GST_LOG_OBJECT (pad, "pad removed, group:%p", group);
++ GList *l;
++
++ GST_LOG_OBJECT (pad, "pad removed, chain:%p", chain);
+
+ /* In fact, we don't have to do anything here, the active group will be
+ * removed when the group's multiqueue is drained */
++ CHAIN_MUTEX_LOCK (chain);
++ for (l = chain->pending_pads; l; l = l->next) {
++ GstPad *opad = l->data;
++
++ if (pad == opad) {
++ g_signal_handlers_disconnect_by_func (pad, caps_notify_cb, chain);
++ gst_object_unref (pad);
++ chain->pending_pads = g_list_delete_link (chain->pending_pads, l);
++ break;
++ }
++ }
++ CHAIN_MUTEX_UNLOCK (chain);
+ }
+
+ static void
+-no_more_pads_group_cb (GstElement * element, GstDecodeGroup * group)
+-{
+- GST_LOG_OBJECT (element, "no more pads, setting group %p to complete", group);
+-
+- /* when we received no_more_pads, we can complete the pads of the group */
+- gst_decode_group_set_complete (group);
+-}
+-
+-static void
+-pad_added_cb (GstElement * element, GstPad * pad, GstDecodeBin * dbin)
+-{
+- GstCaps *caps;
+-
+- GST_LOG_OBJECT (pad, "Pad added to non-grouped element");
+-
+- caps = gst_pad_get_caps (pad);
+- analyze_new_pad (dbin, element, pad, caps, NULL);
+- if (caps)
+- gst_caps_unref (caps);
+-}
+-
+-static void
+-pad_removed_cb (GstElement * element, GstPad * pad, GstDecodeBin * dbin)
+-{
+- GST_LOG_OBJECT (pad, "Pad removed from non-grouped element");
+-}
+-
+-static void
+-no_more_pads_cb (GstElement * element, GstDecodeBin * dbin)
++no_more_pads_cb (GstElement * element, GstDecodeChain * chain)
+ {
+- GstDecodeGroup *group;
+-
+- GST_LOG_OBJECT (element, "No more pads, setting current group to complete");
+-
+- /* Find the non-complete group, there should only be one */
+- if (!(group = get_current_group (dbin, FALSE, FALSE, NULL)))
+- goto no_group;
+-
+- gst_decode_group_set_complete (group);
++ GstDecodeGroup *group = NULL;
+
+- return;
++ GST_LOG_OBJECT (element, "got no more pads");
+
+-no_group:
+- {
+- GST_DEBUG_OBJECT (dbin, "We couldn't find a non-completed group");
++ if (!chain->elements || (GstElement *) chain->elements->data != element) {
++ GST_LOG_OBJECT (chain->dbin, "no-more-pads from old chain element '%s'",
++ GST_OBJECT_NAME (element));
+ return;
+ }
+-}
+
+-static void
+-caps_notify_cb (GstPad * pad, GParamSpec * unused, GstDecodeBin * dbin)
+-{
+- GstElement *element;
+-
+- GST_LOG_OBJECT (dbin, "Notified caps for pad %s:%s",
+- GST_DEBUG_PAD_NAME (pad));
+-
+- /* Disconnect this; if we still need it, we'll reconnect to this in
+- * analyze_new_pad */
+- g_signal_handlers_disconnect_by_func (pad, caps_notify_cb, dbin);
++ CHAIN_MUTEX_LOCK (chain);
++ /* when we received no_more_pads, we can complete the pads of the chain */
++ if (!chain->next_groups && chain->active_group) {
++ group = chain->active_group;
++ } else if (chain->next_groups) {
++ group = chain->next_groups->data;
++ }
++ if (!group) {
++ GST_ERROR_OBJECT (chain->dbin, "can't find group for element");
++ CHAIN_MUTEX_UNLOCK (chain);
++ return;
++ }
+
+- element = GST_ELEMENT_CAST (gst_pad_get_parent (pad));
++ GST_DEBUG_OBJECT (element, "Setting group %p to complete", group);
+
+- pad_added_cb (element, pad, dbin);
++ group->no_more_pads = TRUE;
++ CHAIN_MUTEX_UNLOCK (chain);
+
+- gst_object_unref (element);
++ EXPOSE_LOCK (chain->dbin);
++ if (gst_decode_chain_is_complete (chain->dbin->decode_chain)) {
++ gst_decode_bin_expose (chain->dbin);
++ }
++ EXPOSE_UNLOCK (chain->dbin);
+ }
+
+ static void
+-caps_notify_group_cb (GstPad * pad, GParamSpec * unused, GstDecodeGroup * group)
++caps_notify_cb (GstPad * pad, GParamSpec * unused, GstDecodeChain * chain)
+ {
+ GstElement *element;
++ GList *l;
+
+ GST_LOG_OBJECT (pad, "Notified caps for pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ /* Disconnect this; if we still need it, we'll reconnect to this in
+ * analyze_new_pad */
+- g_signal_handlers_disconnect_by_func (pad, caps_notify_group_cb, group);
++ g_signal_handlers_disconnect_by_func (pad, caps_notify_cb, chain);
+
+ element = GST_ELEMENT_CAST (gst_pad_get_parent (pad));
+
+- pad_added_group_cb (element, pad, group);
++ CHAIN_MUTEX_LOCK (chain);
++ for (l = chain->pending_pads; l; l = l->next) {
++ if (l->data == pad) {
++ gst_object_unref (GST_OBJECT_CAST (l->data));
++ chain->pending_pads = g_list_delete_link (chain->pending_pads, l);
++ break;
++ }
++ }
++ CHAIN_MUTEX_UNLOCK (chain);
++
++ pad_added_cb (element, pad, chain);
+
+ gst_object_unref (element);
+ }
+@@ -1721,25 +1734,204 @@ is_demuxer_element (GstElement * srcelement)
+ static gboolean
+ are_raw_caps (GstDecodeBin * dbin, GstCaps * caps)
+ {
+- GstCaps *intersection;
+ gboolean res;
+
+ GST_LOG_OBJECT (dbin, "Checking with caps %" GST_PTR_FORMAT, caps);
+
+ /* lock for getting the caps */
+ GST_OBJECT_LOCK (dbin);
+- intersection = gst_caps_intersect (dbin->caps, caps);
++ res = gst_caps_can_intersect (dbin->caps, caps);
+ GST_OBJECT_UNLOCK (dbin);
+
+- res = (!(gst_caps_is_empty (intersection)));
+-
+- gst_caps_unref (intersection);
+-
+ GST_LOG_OBJECT (dbin, "Caps are %sfinal caps", res ? "" : "not ");
+
+ return res;
+ }
+
++/****
++ * GstDecodeChain functions
++ ****/
++
++/* gst_decode_chain_get_current_group:
++ *
++ * Returns the current group of this chain, to which
++ * new chains should be attached or NULL if the last
++ * group didn't have no-more-pads.
++ *
++ * Not MT-safe: Call with parent chain lock!
++ */
++static GstDecodeGroup *
++gst_decode_chain_get_current_group (GstDecodeChain * chain)
++{
++ GstDecodeGroup *group;
++
++ if (!chain->next_groups && chain->active_group
++ && chain->active_group->overrun && !chain->active_group->no_more_pads) {
++ GST_WARNING_OBJECT (chain->dbin,
++ "Currently active group %p is exposed"
++ " and wants to add a new pad without having signaled no-more-pads",
++ chain->active_group);
++ return NULL;
++ }
++
++ if (chain->next_groups && (group = chain->next_groups->data) && group->overrun
++ && !group->no_more_pads) {
++ GST_WARNING_OBJECT (chain->dbin,
++ "Currently newest pending group %p "
++ "had overflow but didn't signal no-more-pads", group);
++ return NULL;
++ }
++
++ /* Now we know that we can really return something useful */
++ if (!chain->active_group) {
++ chain->active_group = group = gst_decode_group_new (chain->dbin, chain);
++ } else if (!chain->active_group->overrun
++ && !chain->active_group->no_more_pads) {
++ group = chain->active_group;
++ } else if (chain->next_groups && (group = chain->next_groups->data)
++ && !group->overrun && !group->no_more_pads) {
++ /* group = chain->next_groups->data */
++ } else {
++ group = gst_decode_group_new (chain->dbin, chain);
++ chain->next_groups = g_list_prepend (chain->next_groups, group);
++ }
++
++ return group;
++}
++
++static void gst_decode_group_free_internal (GstDecodeGroup * group,
++ gboolean hide);
++
++static void
++gst_decode_chain_free_internal (GstDecodeChain * chain, gboolean hide)
++{
++ GList *l;
++
++ CHAIN_MUTEX_LOCK (chain);
++
++ GST_DEBUG_OBJECT (chain->dbin, "%s chain %p", (hide ? "Hiding" : "Freeing"),
++ chain);
++
++ if (chain->active_group) {
++ gst_decode_group_free_internal (chain->active_group, hide);
++ if (!hide)
++ chain->active_group = NULL;
++ }
++
++ for (l = chain->next_groups; l; l = l->next) {
++ gst_decode_group_free_internal ((GstDecodeGroup *) l->data, hide);
++ if (!hide)
++ l->data = NULL;
++ }
++ if (!hide) {
++ g_list_free (chain->next_groups);
++ chain->next_groups = NULL;
++ }
++
++ if (!hide) {
++ for (l = chain->old_groups; l; l = l->next) {
++ GstDecodeGroup *group = l->data;
++
++ gst_decode_group_free (group);
++ }
++ g_list_free (chain->old_groups);
++ chain->old_groups = NULL;
++ }
++
++ for (l = chain->pending_pads; l; l = l->next) {
++ GstPad *pad = GST_PAD (l->data);
++
++ g_signal_handlers_disconnect_by_func (pad, caps_notify_cb, chain);
++ gst_object_unref (pad);
++ l->data = NULL;
++ }
++ g_list_free (chain->pending_pads);
++ chain->pending_pads = NULL;
++
++ for (l = chain->elements; l; l = l->next) {
++ GstElement *element = GST_ELEMENT (l->data);
++
++ g_signal_handlers_disconnect_by_func (element, pad_added_cb, chain);
++ g_signal_handlers_disconnect_by_func (element, pad_removed_cb, chain);
++ g_signal_handlers_disconnect_by_func (element, no_more_pads_cb, chain);
++
++ if (GST_OBJECT_PARENT (element) == GST_OBJECT_CAST (chain->dbin))
++ gst_bin_remove (GST_BIN_CAST (chain->dbin), element);
++ if (!hide) {
++ gst_element_set_state (element, GST_STATE_NULL);
++ }
++
++ GST_OBJECT_LOCK (chain->dbin);
++ /* remove possible subtitle element */
++ chain->dbin->subtitles = g_list_remove (chain->dbin->subtitles, element);
++ GST_OBJECT_UNLOCK (chain->dbin);
++
++ if (!hide) {
++ gst_object_unref (element);
++ l->data = NULL;
++ }
++ }
++ if (!hide) {
++ g_list_free (chain->elements);
++ chain->elements = NULL;
++ }
++
++ if (chain->endpad) {
++ if (chain->endpad->exposed)
++ gst_element_remove_pad (GST_ELEMENT_CAST (chain->dbin),
++ GST_PAD_CAST (chain->endpad));
++
++ chain->endpad->exposed = FALSE;
++ if (!hide) {
++ gst_object_unref (chain->endpad);
++ chain->endpad = NULL;
++ }
++ }
++
++ GST_DEBUG_OBJECT (chain->dbin, "%s chain %p", (hide ? "Hided" : "Freed"),
++ chain);
++ CHAIN_MUTEX_UNLOCK (chain);
++ if (!hide) {
++ g_mutex_free (chain->lock);
++ g_slice_free (GstDecodeChain, chain);
++ }
++}
++
++/* gst_decode_chain_free:
++ *
++ * Completely frees and removes the chain and all
++ * child groups from decodebin2.
++ *
++ * MT-safe, don't hold the chain lock or any child chain's lock
++ * when calling this!
++ */
++static void
++gst_decode_chain_free (GstDecodeChain * chain)
++{
++ gst_decode_chain_free_internal (chain, FALSE);
++}
++
++/* gst_decode_chain_new:
++ *
++ * Creates a new decode chain and initializes it.
++ *
++ * It's up to the caller to add it to the list of child chains of
++ * a group!
++ */
++static GstDecodeChain *
++gst_decode_chain_new (GstDecodeBin * dbin, GstDecodeGroup * parent)
++{
++ GstDecodeChain *chain = g_slice_new0 (GstDecodeChain);
++
++ GST_DEBUG_OBJECT (dbin, "Creating new chain %p with parent group %p", chain,
++ parent);
++
++ chain->dbin = dbin;
++ chain->parent = parent;
++ chain->lock = g_mutex_new ();
++
++ return chain;
++}
+
+ /****
+ * GstDecodeGroup functions
+@@ -1754,135 +1946,152 @@ static void
+ multi_queue_overrun_cb (GstElement * queue, GstDecodeGroup * group)
+ {
+ GstDecodeBin *dbin;
+- gboolean expose;
+
+ dbin = group->dbin;
+
+- GST_LOG_OBJECT (dbin, "multiqueue %p is full", queue);
++ GST_LOG_OBJECT (dbin, "multiqueue '%s' (%p) is full", GST_OBJECT_NAME (queue),
++ queue);
+
+- GROUP_MUTEX_LOCK (group);
+- if (group->complete) {
+- /* the group was already complete (had the no_more_pads called), we
+- * can ignore the overrun signal, the last remaining dynamic element
+- * will expose the group eventually. */
+- GST_LOG_OBJECT (dbin, "group %p was already complete", group);
+- expose = FALSE;
+- } else {
+- /* set number of dynamic element to 0, we don't expect anything anymore
+- * and we need the groups to be 0 for the expose to work */
+- group->nbdynamic = 0;
+- expose = TRUE;
+- }
+- GROUP_MUTEX_UNLOCK (group);
++ group->overrun = TRUE;
++
++ /* FIXME: We should make sure that everything gets exposed now
++ * even if child chains are not complete because the will never
++ * be complete! Ignore any non-complete chains when exposing
++ * and never expose them later
++ */
+
+- if (expose) {
+- DECODE_BIN_LOCK (dbin);
+- if (!gst_decode_group_expose (group))
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain)) {
++ if (!gst_decode_bin_expose (dbin))
+ GST_WARNING_OBJECT (dbin, "Couldn't expose group");
+- DECODE_BIN_UNLOCK (group->dbin);
+ }
++ EXPOSE_UNLOCK (group->dbin);
+ }
+
+-/* gst_decode_group_new:
+- *
+- * Creates a new GstDecodeGroup. It is up to the caller to add it to the list
+- * of groups.
+- */
+-static GstDecodeGroup *
+-gst_decode_group_new (GstDecodeBin * dbin, gboolean use_queue)
++static void
++gst_decode_group_free_internal (GstDecodeGroup * group, gboolean hide)
+ {
+- GstDecodeGroup *group;
+- GstElement *mq;
++ GList *l;
+
+- GST_LOG_OBJECT (dbin, "Creating new group");
++ GST_DEBUG_OBJECT (group->dbin, "%s group %p", (hide ? "Hiding" : "Freeing"),
++ group);
++ for (l = group->children; l; l = l->next) {
++ GstDecodeChain *chain = (GstDecodeChain *) l->data;
+
+- if (use_queue) {
+- if (!(mq = gst_element_factory_make ("multiqueue", NULL))) {
+- GST_ERROR_OBJECT (dbin, "Couldn't create multiqueue element");
+- return NULL;
++ gst_decode_chain_free_internal (chain, hide);
++ if (!hide)
++ l->data = NULL;
++ }
++ if (!hide) {
++ g_list_free (group->children);
++ group->children = NULL;
++ }
++
++ if (!hide) {
++ for (l = group->reqpads; l; l = l->next) {
++ GstPad *pad = l->data;
++
++ gst_element_release_request_pad (group->multiqueue, pad);
++ gst_object_unref (pad);
++ l->data = NULL;
+ }
+- } else {
+- mq = NULL;
++ g_list_free (group->reqpads);
++ group->reqpads = NULL;
+ }
+
+- group = g_new0 (GstDecodeGroup, 1);
+- group->lock = g_mutex_new ();
+- group->dbin = dbin;
+- group->multiqueue = mq;
+- group->exposed = FALSE;
+- group->drained = FALSE;
+- group->blocked = FALSE;
+- group->complete = FALSE;
+- group->endpads = NULL;
+- group->reqpads = NULL;
+-
+- if (mq) {
+- /* we first configure the multiqueue to buffer an unlimited number of
+- * buffers up to 5 seconds or, when no timestamps are present, up to 2 MB of
+- * memory. When this queue overruns, we assume the group is complete and can
+- * be exposed. */
+- g_object_set (G_OBJECT (mq),
+- "max-size-bytes", (guint) 2 * 1024 * 1024,
+- "max-size-time", (guint64) 0, "max-size-buffers", (guint) 0, NULL);
+- /* will expose the group */
+- group->overrunsig = g_signal_connect (G_OBJECT (mq), "overrun",
+- G_CALLBACK (multi_queue_overrun_cb), group);
+-
+- gst_bin_add (GST_BIN (dbin), mq);
+- gst_element_set_state (mq, GST_STATE_PAUSED);
+- }
+-
+- GST_LOG_OBJECT (dbin, "Returning new group %p", group);
++ if (group->multiqueue) {
++ if (group->overrunsig) {
++ g_signal_handler_disconnect (group->multiqueue, group->overrunsig);
++ group->overrunsig = 0;
++ }
+
+- return group;
++ if (GST_OBJECT_PARENT (group->multiqueue) == GST_OBJECT_CAST (group->dbin))
++ gst_bin_remove (GST_BIN_CAST (group->dbin), group->multiqueue);
++ if (!hide) {
++ gst_element_set_state (group->multiqueue, GST_STATE_NULL);
++ gst_object_unref (group->multiqueue);
++ group->multiqueue = NULL;
++ }
++ }
++
++ GST_DEBUG_OBJECT (group->dbin, "%s group %p", (hide ? "Hided" : "Freed"),
++ group);
++ if (!hide)
++ g_slice_free (GstDecodeGroup, group);
+ }
+
+-/* get_current_group:
+- * @dbin: the decodebin
+- * @create: create the group when not present
+- * @as_demux: create the group as a demuxer
+- * @created: result when the group was created
++/* gst_decode_group_free:
+ *
+- * Returns the current non-completed group. The dynamic refcount of the group is
+- * increased when dealing with a demuxer.
++ * Completely frees and removes the decode group and all
++ * it's children.
+ *
+- * Returns: %NULL if no groups are available, or all groups are completed.
++ * Never call this from any streaming thread!
++ *
++ * Not MT-safe, call with parent's chain lock!
++ */
++static void
++gst_decode_group_free (GstDecodeGroup * group)
++{
++ gst_decode_group_free_internal (group, FALSE);
++}
++
++/* gst_decode_group_hide:
++ *
++ * Hide the decode group only, this means that
++ * all child endpads are removed from decodebin2
++ * and all signals are unconnected.
++ *
++ * No element is set to NULL state and completely
++ * unreffed here.
++ *
++ * Can be called from streaming threads.
++ *
++ * Not MT-safe, call with parent's chain lock!
++ */
++static void
++gst_decode_group_hide (GstDecodeGroup * group)
++{
++ gst_decode_group_free_internal (group, TRUE);
++}
++
++/* gst_decode_group_new:
++ * @dbin: Parent decodebin
++ * @parent: Parent chain or %NULL
++ *
++ * Creates a new GstDecodeGroup. It is up to the caller to add it to the list
++ * of groups.
+ */
+ static GstDecodeGroup *
+-get_current_group (GstDecodeBin * dbin, gboolean create, gboolean as_demux,
+- gboolean * created)
++gst_decode_group_new (GstDecodeBin * dbin, GstDecodeChain * parent)
+ {
+- GList *tmp;
+- GstDecodeGroup *group = NULL;
++ GstDecodeGroup *group = g_slice_new0 (GstDecodeGroup);
++ GstElement *mq;
+
+- DECODE_BIN_LOCK (dbin);
+- for (tmp = dbin->groups; tmp; tmp = g_list_next (tmp)) {
+- GstDecodeGroup *this = (GstDecodeGroup *) tmp->data;
++ GST_DEBUG_OBJECT (dbin, "Creating new group %p with parent chain %p", group,
++ parent);
+
+- GROUP_MUTEX_LOCK (this);
+- GST_LOG_OBJECT (dbin, "group %p, complete:%d", this, this->complete);
++ group->dbin = dbin;
++ group->parent = parent;
+
+- if (!this->complete) {
+- group = this;
+- GROUP_MUTEX_UNLOCK (this);
+- break;
+- } else {
+- GROUP_MUTEX_UNLOCK (this);
+- }
+- }
+- if (group == NULL && create) {
+- group = gst_decode_group_new (dbin, as_demux);
+- GST_LOG_OBJECT (dbin, "added group %p, demux %d", group, as_demux);
+- dbin->groups = g_list_prepend (dbin->groups, group);
+- if (created)
+- *created = TRUE;
+- /* demuxers are dynamic, we need no-more-pads or overrun now */
+- if (as_demux)
+- group->nbdynamic++;
++ mq = group->multiqueue = gst_element_factory_make ("multiqueue", NULL);
++ if (G_UNLIKELY (!group->multiqueue)) {
++ gst_element_post_message (GST_ELEMENT_CAST (dbin),
++ gst_missing_element_message_new (GST_ELEMENT_CAST (dbin),
++ "multiqueue"));
++ GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL), ("no multiqueue!"));
++ g_slice_free (GstDecodeGroup, group);
++ return NULL;
+ }
+- DECODE_BIN_UNLOCK (dbin);
+
+- GST_LOG_OBJECT (dbin, "Returning group %p", group);
++ g_object_set (G_OBJECT (mq),
++ "max-size-bytes", (guint) 2 * 1024 * 1024,
++ "max-size-time", (guint64) 0, "max-size-buffers", (guint) 0, NULL);
++
++ group->overrunsig = g_signal_connect (G_OBJECT (mq), "overrun",
++ G_CALLBACK (multi_queue_overrun_cb), group);
++
++ gst_bin_add (GST_BIN (dbin), gst_object_ref (mq));
++ gst_element_set_state (mq, GST_STATE_PAUSED);
+
+ return group;
+ }
+@@ -1907,6 +2116,9 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
+
+ srcpad = NULL;
+
++ if (G_UNLIKELY (!group->multiqueue))
++ return NULL;
++
+ if (!(sinkpad = gst_element_get_request_pad (group->multiqueue, "sink%d"))) {
+ GST_ERROR_OBJECT (dbin, "Couldn't get sinkpad from multiqueue");
+ return NULL;
+@@ -1917,15 +2129,14 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
+ goto beach;
+ }
+
+- group->reqpads = g_list_prepend (group->reqpads, sinkpad);
++ CHAIN_MUTEX_LOCK (group->parent);
++ group->reqpads = g_list_prepend (group->reqpads, gst_object_ref (sinkpad));
+
+ sinkname = gst_pad_get_name (sinkpad);
+ nb = sinkname + 4;
+ srcname = g_strdup_printf ("src%s", nb);
+ g_free (sinkname);
+
+- GROUP_MUTEX_LOCK (group);
+-
+ if (!(srcpad = gst_element_get_static_pad (group->multiqueue, srcname))) {
+ GST_ERROR_OBJECT (dbin, "Couldn't get srcpad %s from multiqueue", srcname);
+ goto chiringuito;
+@@ -1933,163 +2144,224 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
+
+ chiringuito:
+ g_free (srcname);
+- GROUP_MUTEX_UNLOCK (group);
++ CHAIN_MUTEX_UNLOCK (group->parent);
+
+ beach:
+ gst_object_unref (sinkpad);
+ return srcpad;
+ }
+
++/* gst_decode_group_is_complete:
++ *
++ * Checks if the group is complete, this means that
++ * a) overrun of the multiqueue or no-more-pads happened
++ * b) all child chains are complete
++ *
++ * Not MT-safe, always call with decodebin expose lock
++ */
+ static gboolean
+-gst_decode_group_control_source_pad (GstDecodeGroup * group,
+- GstDecodePad * dpad)
++gst_decode_group_is_complete (GstDecodeGroup * group)
+ {
+- g_return_val_if_fail (group != NULL, FALSE);
++ GList *l;
++ gboolean complete = TRUE;
+
+- GST_DEBUG_OBJECT (dpad, "adding decode pad to group %p", group);
++ if (!group->overrun && !group->no_more_pads) {
++ complete = FALSE;
++ goto out;
++ }
+
+- /* FIXME : check if pad is already controlled */
+- gst_decode_pad_activate (dpad, group);
++ for (l = group->children; l; l = l->next) {
++ GstDecodeChain *chain = l->data;
+
+- GROUP_MUTEX_LOCK (group);
+- group->endpads = g_list_prepend (group->endpads, gst_object_ref (dpad));
+- GROUP_MUTEX_UNLOCK (group);
++ if (!gst_decode_chain_is_complete (chain)) {
++ complete = FALSE;
++ goto out;
++ }
++ }
+
+- return TRUE;
++out:
++ GST_DEBUG_OBJECT (group->dbin, "Group %p is complete: %d", group, complete);
++ return complete;
+ }
+
+-/* gst_decode_group_check_if_blocked:
++/* gst_decode_chain_is_complete:
+ *
+- * Call this when one of the pads blocked status has changed.
+- * If the group is complete and blocked, the group will be marked as blocked
+- * and will ghost/expose all pads on decodebin if the group is the current one.
++ * Returns TRUE if the chain is complete, this means either
++ * a) This chain is a dead end, i.e. we have no suitable plugins
++ * b) This chain ends in an endpad and this is blocked
+ *
+- * Call with the group lock taken ! MT safe
+- *
+- * Returns: TRUE when the group is completely blocked and ready to be exposed.
++ * Not MT-safe, always call with decodebin expose lock
+ */
+ static gboolean
+-gst_decode_group_check_if_blocked (GstDecodeGroup * group)
++gst_decode_chain_is_complete (GstDecodeChain * chain)
+ {
+- GstDecodeBin *dbin;
+- GList *tmp;
+- gboolean blocked = TRUE;
+-
+- dbin = group->dbin;
+-
+- GST_LOG_OBJECT (dbin, "group : %p , ->complete:%d , ->nbdynamic:%d",
+- group, group->complete, group->nbdynamic);
++ gboolean complete = FALSE;
+
+- /* don't do anything if group is not complete */
+- if (!group->complete || group->nbdynamic) {
+- GST_DEBUG_OBJECT (group->dbin, "Group isn't complete yet");
+- return FALSE;
++ if (chain->deadend || (chain->endpad && chain->endpad->blocked)) {
++ complete = TRUE;
++ goto out;
+ }
+
+- for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) {
+- GstDecodePad *dpad = (GstDecodePad *) tmp->data;
+-
+- if (!dpad->blocked) {
+- blocked = FALSE;
+- break;
++ if (chain->demuxer) {
++ if (chain->active_group
++ && gst_decode_group_is_complete (chain->active_group)) {
++ complete = TRUE;
++ goto out;
+ }
+ }
+
+- /* Update status of group */
+- group->blocked = blocked;
+- GST_LOG_OBJECT (dbin, "group is blocked:%d", blocked);
++out:
++ GST_DEBUG_OBJECT (chain->dbin, "Chain %p is complete: %d", chain, complete);
++ return complete;
++}
+
+- return blocked;
++/* check if the group is drained, meaning all pads have seen an EOS
++ * event. */
++static void
++gst_decode_pad_handle_eos (GstDecodePad * pad)
++{
++ GstDecodeChain *chain = pad->chain;
++
++ GST_LOG_OBJECT (pad->dbin, "chain : %p, pad %p", chain, pad);
++ pad->drained = TRUE;
++ gst_decode_chain_handle_eos (chain);
+ }
+
+-/* activate the next group when there is one
++/* gst_decode_chain_handle_eos:
++ *
++ * Checks if there are next groups in any parent chain
++ * to which we can switch or if everything is drained.
+ *
+- * Returns: TRUE when group was the active group and there was a
+- * next group to activate.
++ * If there are groups to switch to, hide the current active
++ * one and expose the new one.
++ *
++ * MT-safe, don't call with chain lock!
+ */
+-static gboolean
+-gst_decode_bin_activate_next_group (GstDecodeBin * dbin, GstDecodeGroup * group)
++static void
++gst_decode_chain_handle_eos (GstDecodeChain * eos_chain)
+ {
+- gboolean have_next = FALSE;
+-
+- DECODE_BIN_LOCK (dbin);
+- /* Check if there is a next group to activate */
+- if ((group == dbin->activegroup) && dbin->groups) {
+- GstDecodeGroup *newgroup;
++ GstDecodeBin *dbin = eos_chain->dbin;
++ GstDecodeGroup *group = eos_chain->parent;
++ GstDecodeChain *chain = eos_chain;
++ gboolean drained;
+
+- /* get the next group */
+- newgroup = (GstDecodeGroup *) dbin->groups->data;
++ g_return_if_fail (eos_chain->endpad);
+
+- GST_DEBUG_OBJECT (dbin, "Switching to new group");
++ CHAIN_MUTEX_LOCK (chain);
++ while ((group = chain->parent)) {
++ CHAIN_MUTEX_UNLOCK (chain);
++ chain = group->parent;
++ CHAIN_MUTEX_LOCK (chain);
+
+- /* hide current group */
+- gst_decode_group_hide (group);
+- /* expose next group */
+- gst_decode_group_expose (newgroup);
+-
+- /* we have a next group */
+- have_next = TRUE;
++ if (gst_decode_group_is_drained (group)) {
++ continue;
++ }
++ break;
+ }
+- DECODE_BIN_UNLOCK (dbin);
+
+- return have_next;
++ drained =
++ chain->active_group ? gst_decode_group_is_drained (chain->
++ active_group) : TRUE;
++
++ /* Now either group == NULL and chain == dbin->decode_chain
++ * or chain is the lowest chain that has a non-drained group */
++ if (chain->active_group && drained && chain->next_groups) {
++ GST_DEBUG_OBJECT (dbin, "Hiding current group %p", chain->active_group);
++ gst_decode_group_hide (chain->active_group);
++ chain->old_groups = g_list_prepend (chain->old_groups, chain->active_group);
++ GST_DEBUG_OBJECT (dbin, "Switching to next group %p",
++ chain->next_groups->data);
++ chain->active_group = chain->next_groups->data;
++ chain->next_groups =
++ g_list_delete_link (chain->next_groups, chain->next_groups);
++ CHAIN_MUTEX_UNLOCK (chain);
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain))
++ gst_decode_bin_expose (dbin);
++ EXPOSE_UNLOCK (dbin);
++ } else if (!chain->active_group || drained) {
++ g_assert (chain == dbin->decode_chain);
++ CHAIN_MUTEX_UNLOCK (chain);
++
++ GST_LOG_OBJECT (dbin, "all groups drained, fire signal");
++ g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_DRAINED], 0,
++ NULL);
++ } else {
++ CHAIN_MUTEX_UNLOCK (chain);
++ GST_DEBUG_OBJECT (dbin,
++ "Current active group in chain %p is not drained yet", chain);
++ }
+ }
+
+-/* check if the group is drained, meaning all pads have seen an EOS
+- * event. */
+-static void
+-gst_decode_pad_handle_eos (GstDecodePad * pad)
++/* gst_decode_group_is_drained:
++ *
++ * Check is this group is drained and cache this result.
++ * The group is drained if all child chains are drained.
++ *
++ * Not MT-safe, call with group->parent's lock */
++static gboolean
++gst_decode_group_is_drained (GstDecodeGroup * group)
+ {
+- GList *tmp;
+- GstDecodeBin *dbin;
+- GstDecodeGroup *group;
++ GList *l;
+ gboolean drained = TRUE;
+
+- group = pad->group;
+- dbin = group->dbin;
++ if (group->drained) {
++ drained = TRUE;
++ goto out;
++ }
+
+- GST_LOG_OBJECT (dbin, "group : %p, pad %p", group, pad);
++ for (l = group->children; l; l = l->next) {
++ GstDecodeChain *chain = l->data;
+
+- GROUP_MUTEX_LOCK (group);
+- /* mark pad as drained */
+- pad->drained = TRUE;
+-
+- /* Ensure we only emit the drained signal once, for this group */
+- if (group->drained)
+- goto was_drained;
++ CHAIN_MUTEX_LOCK (chain);
++ if (!gst_decode_chain_is_drained (chain))
++ drained = FALSE;
++ CHAIN_MUTEX_UNLOCK (chain);
++ if (!drained)
++ goto out;
++ }
++ group->drained = drained;
+
+- for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) {
+- GstDecodePad *dpad = (GstDecodePad *) tmp->data;
++out:
++ GST_DEBUG_OBJECT (group->dbin, "Group %p is drained: %d", group, drained);
++ return drained;
++}
+
+- GST_LOG_OBJECT (dbin, "testing dpad %p %d", dpad, dpad->drained);
++/* gst_decode_chain_is_drained:
++ *
++ * Check is the chain is drained, which means that
++ * either
++ *
++ * a) it's endpad is drained
++ * b) there are no pending pads, the active group is drained
++ * and there are no next groups
++ *
++ * Not MT-safe, call with chain lock
++ */
++static gboolean
++gst_decode_chain_is_drained (GstDecodeChain * chain)
++{
++ gboolean drained = FALSE;
+
+- if (!dpad->drained) {
+- drained = FALSE;
+- break;
+- }
++ if (chain->endpad) {
++ drained = chain->endpad->drained;
++ goto out;
+ }
+- group->drained = drained;
+- GROUP_MUTEX_UNLOCK (group);
+-
+- if (drained) {
+- /* the current group is completely drained, try to activate the next
+- * group. this function returns FALSE if there was no next group activated
+- * and so we are really drained. */
+- if (!gst_decode_bin_activate_next_group (dbin, group)) {
+- /* no more groups to activate, we're completely drained now */
+- GST_LOG_OBJECT (dbin, "all groups drained, fire signal");
+- g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_DRAINED], 0,
+- NULL);
+- }
++
++ if (chain->pending_pads) {
++ drained = FALSE;
++ goto out;
+ }
+- return;
+
+-was_drained:
+- {
+- GST_LOG_OBJECT (dbin, "group was already drained");
+- GROUP_MUTEX_UNLOCK (group);
+- return;
++ if (chain->active_group && gst_decode_group_is_drained (chain->active_group)
++ && !chain->next_groups) {
++ drained = TRUE;
++ goto out;
+ }
++
++out:
++ GST_DEBUG_OBJECT (chain->dbin, "Chain %p is drained: %d", chain, drained);
++ return drained;
+ }
+
+ /* sort_end_pads:
+@@ -2100,7 +2372,6 @@ was_drained:
+ *
+ * Return: negative if a<b, 0 if a==b, positive if a>b
+ */
+-
+ static gint
+ sort_end_pads (GstDecodePad * da, GstDecodePad * db)
+ {
+@@ -2146,74 +2417,62 @@ sort_end_pads (GstDecodePad * da, GstDecodePad * db)
+ return va - vb;
+ }
+
+-/* gst_decode_group_expose:
+- *
+- * Expose this group's pads.
+- *
+- * Not MT safe, please take the decodebin lock
+- */
++/* Must only be called if the toplevel chain is complete and blocked! */
++/* Not MT-safe, call with decodebin expose lock! */
+ static gboolean
+-gst_decode_group_expose (GstDecodeGroup * group)
++gst_decode_bin_expose (GstDecodeBin * dbin)
+ {
+- GList *tmp;
+- GList *next = NULL;
+- GstDecodeBin *dbin;
++ GList *tmp, *endpads = NULL;
++ gboolean already_exposed = TRUE;
+
+- dbin = group->dbin;
++ GST_DEBUG_OBJECT (dbin, "Exposing currently active chains/groups");
+
+- GST_DEBUG_OBJECT (dbin, "going to expose group %p", group);
+-
+- if (group->nbdynamic) {
+- GST_DEBUG_OBJECT (dbin,
+- "Group %p still has %d dynamic objects, not exposing yet", group,
+- group->nbdynamic);
++ /* Get the pads that we're going to expose and mark things as exposed */
++ if (!gst_decode_chain_expose (dbin->decode_chain, &endpads)) {
++ g_list_foreach (endpads, (GFunc) gst_object_unref, NULL);
++ g_list_free (endpads);
++ GST_ERROR_OBJECT (dbin, "Broken chain/group tree");
++ g_return_val_if_reached (FALSE);
+ return FALSE;
+ }
+-
+- if (dbin->activegroup == group) {
+- GST_DEBUG_OBJECT (dbin, "Group %p is already exposed, all is fine", group);
+- return TRUE;
++ if (endpads == NULL) {
++ GST_WARNING_OBJECT (dbin, "No suitable plugins found");
++ GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL),
++ ("no suitable plugins found"));
++ return FALSE;
+ }
+
+- if (group->multiqueue) {
+- /* update runtime limits. At runtime, we try to keep the amount of buffers
+- * in the queues as low as possible (but at least 5 buffers). */
+- g_object_set (G_OBJECT (group->multiqueue),
+- "max-size-bytes", 2 * 1024 * 1024, "max-size-buffers", 5, NULL);
+- /* we can now disconnect any overrun signal, which is used to expose the
+- * group. */
+- if (group->overrunsig) {
+- GST_LOG_OBJECT (dbin, "Disconnecting overrun");
+- g_signal_handler_disconnect (group->multiqueue, group->overrunsig);
+- group->overrunsig = 0;
+- }
+- }
++ /* Check if this was called when everything was exposed already */
++ for (tmp = endpads; tmp && already_exposed; tmp = tmp->next) {
++ GstDecodePad *dpad = tmp->data;
+
+- if (dbin->activegroup) {
+- GST_DEBUG_OBJECT (dbin,
+- "another group %p is already exposed, waiting for EOS",
+- dbin->activegroup);
++ already_exposed &= dpad->exposed;
++ if (!already_exposed)
++ break;
++ }
++ if (already_exposed) {
++ GST_DEBUG_OBJECT (dbin, "Everything was exposed already!");
++ g_list_foreach (endpads, (GFunc) gst_object_unref, NULL);
++ g_list_free (endpads);
+ return TRUE;
+ }
+
+- if (!dbin->groups || (group != (GstDecodeGroup *) dbin->groups->data)) {
+- GST_WARNING_OBJECT (dbin, "Group %p is not the first group to expose",
+- group);
+- return FALSE;
+- }
++ /* Set all already exposed pads to blocked */
++ for (tmp = endpads; tmp; tmp = tmp->next) {
++ GstDecodePad *dpad = tmp->data;
+
+- GST_LOG_OBJECT (dbin, "Exposing group %p", group);
++ if (dpad->exposed)
++ gst_decode_pad_set_blocked (dpad, TRUE);
++ }
+
+ /* re-order pads : video, then audio, then others */
+- group->endpads = g_list_sort (group->endpads, (GCompareFunc) sort_end_pads);
++ endpads = g_list_sort (endpads, (GCompareFunc) sort_end_pads);
+
+ /* Expose pads */
+- for (tmp = group->endpads; tmp; tmp = next) {
++ for (tmp = endpads; tmp; tmp = tmp->next) {
+ GstDecodePad *dpad = (GstDecodePad *) tmp->data;
+ gchar *padname;
+
+- next = g_list_next (tmp);
+-
+ /* 1. rewrite name */
+ padname = g_strdup_printf ("src%d", dbin->nbpads);
+ dbin->nbpads++;
+@@ -2223,18 +2482,19 @@ gst_decode_group_expose (GstDecodeGroup * group)
+ g_free (padname);
+
+ /* 2. activate and add */
+- if (!gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD (dpad))) {
++ if (!dpad->exposed
++ && !gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD (dpad))) {
+ /* not really fatal, we can try to add the other pads */
+ g_warning ("error adding pad to decodebin2");
+ continue;
+ }
+- dpad->added = TRUE;
++ dpad->exposed = TRUE;
+
+ /* 3. emit signal */
+ GST_DEBUG_OBJECT (dbin, "emitting new-decoded-pad");
+ g_signal_emit (G_OBJECT (dbin),
+ gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, dpad,
+- (next == NULL));
++ (tmp->next == NULL));
+ GST_DEBUG_OBJECT (dbin, "emitted new-decoded-pad");
+ }
+
+@@ -2245,223 +2505,73 @@ gst_decode_group_expose (GstDecodeGroup * group)
+
+ /* 4. Unblock internal pads. The application should have connected stuff now
+ * so that streaming can continue. */
+- for (tmp = group->endpads; tmp; tmp = next) {
++ for (tmp = endpads; tmp; tmp = tmp->next) {
+ GstDecodePad *dpad = (GstDecodePad *) tmp->data;
+
+- next = g_list_next (tmp);
+-
+ GST_DEBUG_OBJECT (dpad, "unblocking");
+ gst_decode_pad_unblock (dpad);
+ GST_DEBUG_OBJECT (dpad, "unblocked");
++ gst_object_unref (dpad);
+ }
+-
+- dbin->activegroup = group;
+-
+- /* pop off the first group */
+- if (dbin->groups && dbin->groups->data) {
+- GST_LOG_OBJECT (dbin, "removed group %p", dbin->groups->data);
+- dbin->groups = g_list_delete_link (dbin->groups, dbin->groups);
+- } else {
+- GST_LOG_OBJECT (dbin, "no more groups");
+- }
++ g_list_free (endpads);
+
+ do_async_done (dbin);
+-
+- group->exposed = TRUE;
+-
+- GST_LOG_OBJECT (dbin, "Group %p exposed", group);
++ GST_DEBUG_OBJECT (dbin, "Exposed everything");
+ return TRUE;
+ }
+
+-/* must be called with the decodebin lock */
+-static void
+-gst_decode_group_hide (GstDecodeGroup * group)
+-{
+- GList *tmp;
+- GstDecodeBin *dbin;
+-
+- dbin = group->dbin;
+-
+- GST_LOG_OBJECT (dbin, "Hiding group %p", group);
+-
+- if (group != dbin->activegroup) {
+- GST_WARNING_OBJECT (dbin, "This group is not the active one, ignoring");
+- return;
+- }
+-
+- GROUP_MUTEX_LOCK (group);
+- /* Remove ghost pads */
+- for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) {
+- GstDecodePad *dpad = (GstDecodePad *) tmp->data;
+-
+- if (dpad->added)
+- gst_element_remove_pad (GST_ELEMENT (group->dbin), GST_PAD (dpad));
+- dpad->added = FALSE;
+- }
+- group->exposed = FALSE;
+- GROUP_MUTEX_UNLOCK (group);
+-
+- group->dbin->activegroup = NULL;
+- group->dbin->oldgroups = g_list_prepend (group->dbin->oldgroups, group);
+-}
+-
+-static void
+-deactivate_free_recursive (GstDecodeGroup * group, GstElement * element)
++/* gst_decode_chain_expose:
++ *
++ * Check if the chain can be exposed and add all endpads
++ * to the endpads list.
++ *
++ * Also update the active group's multiqueue to the
++ * runtime limits.
++ *
++ * Not MT-safe, call with decodebin expose lock! *
++ */
++static gboolean
++gst_decode_chain_expose (GstDecodeChain * chain, GList ** endpads)
+ {
+- GstIterator *it;
+- GstIteratorResult res;
+- gpointer point;
+- GstDecodeBin *dbin;
+-
+- dbin = group->dbin;
+-
+- GST_LOG_OBJECT (dbin, "element:%s", GST_ELEMENT_NAME (element));
+-
+- /* call on downstream elements */
+- it = gst_element_iterate_src_pads (element);
+-
+-restart:
+-
+- while (1) {
+- res = gst_iterator_next (it, &point);
+- switch (res) {
+- case GST_ITERATOR_DONE:
+- goto done;
+- case GST_ITERATOR_RESYNC:
+- gst_iterator_resync (it);
+- goto restart;
+- case GST_ITERATOR_ERROR:
+- {
+- GST_WARNING_OBJECT (dbin,
+- "Had an error while iterating source pads of element: %s",
+- GST_ELEMENT_NAME (element));
+- goto beach;
+- }
+- case GST_ITERATOR_OK:
+- {
+- GstPad *pad = GST_PAD (point);
+- GstPad *peerpad = NULL;
+-
+- if ((peerpad = gst_pad_get_peer (pad))) {
+- GstObject *parent;
++ GstDecodeGroup *group;
++ GList *l;
+
+- parent = gst_pad_get_parent (peerpad);
+- gst_object_unref (peerpad);
++ if (chain->deadend)
++ return TRUE;
+
+- if (parent && GST_IS_ELEMENT (parent))
+- deactivate_free_recursive (group, GST_ELEMENT (parent));
+- if (parent)
+- gst_object_unref (parent);
+- }
+- }
+- break;
+- default:
+- break;
+- }
++ if (chain->endpad) {
++ if (!chain->endpad->blocked)
++ return FALSE;
++ *endpads = g_list_prepend (*endpads, gst_object_ref (chain->endpad));
++ return TRUE;
+ }
+
+-done:
+- gst_element_set_state (element, GST_STATE_NULL);
+- GST_OBJECT_LOCK (dbin);
+- /* remove possible subtitle element */
+- dbin->subtitles = g_list_remove (dbin->subtitles, element);
+- GST_OBJECT_UNLOCK (dbin);
+- gst_bin_remove (GST_BIN (dbin), element);
+-
+-beach:
+- gst_iterator_free (it);
+-
+- return;
+-}
+-
+-static void
+-gst_decode_group_free (GstDecodeGroup * group)
+-{
+- GstDecodeBin *dbin;
+- GList *tmp;
+-
+- dbin = group->dbin;
+-
+- GST_LOG_OBJECT (dbin, "group %p", group);
+-
+- GROUP_MUTEX_LOCK (group);
+-
+- /* remove exposed pads */
+- if (group == dbin->activegroup) {
+- for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) {
+- GstDecodePad *dpad = (GstDecodePad *) tmp->data;
++ group = chain->active_group;
++ if (!group)
++ return FALSE;
++ if (!group->no_more_pads && !group->overrun)
++ return FALSE;
+
+- if (dpad->added)
+- gst_element_remove_pad (GST_ELEMENT (dbin), GST_PAD (dpad));
+- dpad->added = FALSE;
+- }
++ /* update runtime limits. At runtime, we try to keep the amount of buffers
++ * in the queues as low as possible (but at least 5 buffers). */
++ g_object_set (G_OBJECT (group->multiqueue),
++ "max-size-bytes", 2 * 1024 * 1024, "max-size-buffers", 5, NULL);
++ /* we can now disconnect any overrun signal, which is used to expose the
++ * group. */
++ if (group->overrunsig) {
++ GST_LOG_OBJECT (group->dbin, "Disconnecting overrun");
++ g_signal_handler_disconnect (group->multiqueue, group->overrunsig);
++ group->overrunsig = 0;
+ }
+
+- /* Clear all GstDecodePad */
+- for (tmp = group->endpads; tmp; tmp = g_list_next (tmp))
+- gst_object_unref (tmp->data);
+- g_list_free (group->endpads);
+- group->endpads = NULL;
++ for (l = group->children; l; l = l->next) {
++ GstDecodeChain *childchain = l->data;
+
+- /* release request pads */
+- for (tmp = group->reqpads; tmp; tmp = g_list_next (tmp)) {
+- gst_element_release_request_pad (group->multiqueue, GST_PAD (tmp->data));
++ if (!gst_decode_chain_expose (childchain, endpads))
++ return FALSE;
+ }
+- g_list_free (group->reqpads);
+- group->reqpads = NULL;
+
+- /* disconnect signal handlers on multiqueue */
+- if (group->multiqueue) {
+- if (group->overrunsig)
+- g_signal_handler_disconnect (group->multiqueue, group->overrunsig);
+- deactivate_free_recursive (group, group->multiqueue);
+- }
+-
+- /* remove all elements */
+-
+- GROUP_MUTEX_UNLOCK (group);
+-
+- g_mutex_free (group->lock);
+- g_free (group);
+-}
+-
+-/* gst_decode_group_set_complete:
+- *
+- * Mark the group as complete. This means no more streams will be controlled
+- * through this group. This method is usually called when we got no_more_pads or
+- * when we added the last pad not from a demuxer.
+- *
+- * When this method is called, it is possible that some dynamic plugging is
+- * going on in streaming threads. We decrement the dynamic counter and when it
+- * reaches zero, we check if all of our pads are blocked before we finally
+- * expose the group.
+- *
+- * MT safe
+- */
+-static void
+-gst_decode_group_set_complete (GstDecodeGroup * group)
+-{
+- gboolean expose = FALSE;
+- GstDecodeBin *dbin;
+-
+- dbin = group->dbin;
+-
+- GST_LOG_OBJECT (dbin, "Setting group %p to COMPLETE", group);
+-
+- GROUP_MUTEX_LOCK (group);
+- group->complete = TRUE;
+- if (group->nbdynamic > 0)
+- group->nbdynamic--;
+- expose = gst_decode_group_check_if_blocked (group);
+- GROUP_MUTEX_UNLOCK (group);
+-
+- /* don't do anything if not blocked completely */
+- if (expose) {
+- DECODE_BIN_LOCK (dbin);
+- if (!gst_decode_group_expose (group))
+- GST_WARNING_OBJECT (dbin, "Couldn't expose group");
+- DECODE_BIN_UNLOCK (dbin);
+- }
++ return TRUE;
+ }
+
+ /*************************
+@@ -2476,37 +2586,34 @@ gst_decode_pad_class_init (GstDecodePadClass * klass)
+ static void
+ gst_decode_pad_init (GstDecodePad * pad)
+ {
+- pad->group = NULL;
++ pad->chain = NULL;
+ pad->blocked = FALSE;
++ pad->exposed = FALSE;
+ pad->drained = FALSE;
+ gst_object_ref (pad);
+ gst_object_sink (pad);
+ }
+
+ static void
+-source_pad_blocked_cb (GstPad * opad, gboolean blocked, GstDecodePad * dpad)
++source_pad_blocked_cb (GstPad * pad, gboolean blocked, GstDecodePad * dpad)
+ {
+- GstDecodeGroup *group;
++ GstDecodeChain *chain;
+ GstDecodeBin *dbin;
+- gboolean expose = FALSE;
+
+- group = dpad->group;
+- dbin = group->dbin;
++ chain = dpad->chain;
++ dbin = chain->dbin;
+
+- GST_LOG_OBJECT (dpad, "blocked:%d, dpad->group:%p", blocked, group);
++ GST_LOG_OBJECT (dpad, "blocked:%d, dpad->chain:%p", blocked, chain);
+
+- GROUP_MUTEX_LOCK (group);
+- /* Update this GstDecodePad status */
+ dpad->blocked = blocked;
+- if (blocked)
+- expose = gst_decode_group_check_if_blocked (group);
+- GROUP_MUTEX_UNLOCK (group);
+
+- if (expose) {
+- DECODE_BIN_LOCK (dbin);
+- if (!gst_decode_group_expose (group))
+- GST_WARNING_OBJECT (dbin, "Couldn't expose group");
+- DECODE_BIN_UNLOCK (dbin);
++ if (dpad->blocked) {
++ EXPOSE_LOCK (dbin);
++ if (gst_decode_chain_is_complete (dbin->decode_chain)) {
++ if (!gst_decode_bin_expose (dbin))
++ GST_WARNING_OBJECT (dbin, "Couldn't expose group");
++ }
++ EXPOSE_UNLOCK (dbin);
+ }
+ }
+
+@@ -2533,7 +2640,7 @@ gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked)
+ GstDecodeBin *dbin = dpad->dbin;
+ GstPad *opad;
+
+- DECODE_BIN_DYN_LOCK (dbin);
++ DYN_LOCK (dbin);
+ opad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (dpad));
+ if (!opad)
+ goto out;
+@@ -2550,13 +2657,16 @@ gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked)
+ dbin->blocked_pads = g_list_prepend (dbin->blocked_pads, dpad);
+ }
+ } else {
+- if (g_list_find (dbin->blocked_pads, dpad))
++ GList *l;
++
++ if ((l = g_list_find (dbin->blocked_pads, dpad))) {
+ gst_object_unref (dpad);
+- dbin->blocked_pads = g_list_remove (dbin->blocked_pads, dpad);
++ dbin->blocked_pads = g_list_delete_link (dbin->blocked_pads, l);
++ }
+ }
+ gst_object_unref (opad);
+ out:
+- DECODE_BIN_DYN_UNLOCK (dbin);
++ DYN_UNLOCK (dbin);
+ }
+
+ static void
+@@ -2567,11 +2677,11 @@ gst_decode_pad_add_drained_check (GstDecodePad * dpad)
+ }
+
+ static void
+-gst_decode_pad_activate (GstDecodePad * dpad, GstDecodeGroup * group)
++gst_decode_pad_activate (GstDecodePad * dpad, GstDecodeChain * chain)
+ {
+- g_return_if_fail (group != NULL);
++ g_return_if_fail (chain != NULL);
+
+- dpad->group = group;
++ dpad->chain = chain;
+ gst_pad_set_active (GST_PAD (dpad), TRUE);
+ gst_decode_pad_set_blocked (dpad, TRUE);
+ gst_decode_pad_add_drained_check (dpad);
+@@ -2588,7 +2698,7 @@ gst_decode_pad_unblock (GstDecodePad * dpad)
+ * Creates a new GstDecodePad for the given pad.
+ */
+ static GstDecodePad *
+-gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeGroup * group)
++gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeChain * chain)
+ {
+ GstDecodePad *dpad;
+
+@@ -2597,7 +2707,7 @@ gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeGroup * group)
+ NULL);
+ gst_ghost_pad_construct (GST_GHOST_PAD (dpad));
+ gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad);
+- dpad->group = group;
++ dpad->chain = chain;
+ dpad->dbin = dbin;
+
+ return dpad;
+@@ -2663,13 +2773,12 @@ find_sink_pad (GstElement * element)
+ static void
+ unblock_pads (GstDecodeBin * dbin)
+ {
+- GList *tmp, *next;
++ GList *tmp;
+
+- for (tmp = dbin->blocked_pads; tmp; tmp = next) {
++ for (tmp = dbin->blocked_pads; tmp; tmp = tmp->next) {
+ GstDecodePad *dpad = (GstDecodePad *) tmp->data;
+ GstPad *opad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (dpad));
+
+- next = g_list_next (tmp);
+ if (!opad)
+ continue;
+
+@@ -2701,20 +2810,20 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
+ goto missing_typefind;
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+- DECODE_BIN_DYN_LOCK (dbin);
++ DYN_LOCK (dbin);
+ GST_LOG_OBJECT (dbin, "clearing shutdown flag");
+ dbin->shutdown = FALSE;
+- DECODE_BIN_DYN_UNLOCK (dbin);
++ DYN_UNLOCK (dbin);
+ dbin->have_type = FALSE;
+ ret = GST_STATE_CHANGE_ASYNC;
+ do_async_start (dbin);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+- DECODE_BIN_DYN_LOCK (dbin);
++ DYN_LOCK (dbin);
+ GST_LOG_OBJECT (dbin, "setting shutdown flag");
+ dbin->shutdown = TRUE;
+ unblock_pads (dbin);
+- DECODE_BIN_DYN_UNLOCK (dbin);
++ DYN_UNLOCK (dbin);
+ default:
+ break;
+ }
+@@ -2733,11 +2842,12 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ do_async_done (dbin);
+- gst_decode_bin_remove_groups (dbin);
++ if (dbin->decode_chain) {
++ gst_decode_chain_free (dbin->decode_chain);
++ dbin->decode_chain = NULL;
++ }
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+- gst_decode_bin_remove_groups (dbin);
+- break;
+ default:
+ break;
+ }
+--
+1.6.4.3
+
+From 5ee4ee1682cff7ca44897fec0dcee6374cd6636d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Thu, 24 Sep 2009 14:56:48 +0200
+Subject: [PATCH 5/7] uridecodebin: Don't post missing plugin messages twice
+
+decodebin2 already posts them after emitting the unknown-type signal,
+there's no need to post another one.
+---
+ gst/playback/gsturidecodebin.c | 6 ------
+ 1 files changed, 0 insertions(+), 6 deletions(-)
+
+diff --git a/gst/playback/gsturidecodebin.c b/gst/playback/gsturidecodebin.c
+index 0e0bb74..6420414 100644
+--- a/gst/playback/gsturidecodebin.c
++++ b/gst/playback/gsturidecodebin.c
+@@ -33,8 +33,6 @@
+ #include <gst/gst.h>
+ #include <gst/gst-i18n-plugin.h>
+
+-#include <gst/pbutils/missing-plugins.h>
+-
+ #include "gstfactorylists.h"
+ #include "gstplay-marshal.h"
+ #include "gstplay-enum.h"
+@@ -622,12 +620,8 @@ static void
+ unknown_type_cb (GstElement * element, GstPad * pad, GstCaps * caps,
+ GstURIDecodeBin * decoder)
+ {
+- GstMessage *msg;
+ gchar *capsstr;
+
+- msg = gst_missing_decoder_message_new (GST_ELEMENT_CAST (decoder), caps);
+- gst_element_post_message (GST_ELEMENT_CAST (decoder), msg);
+-
+ capsstr = gst_caps_to_string (caps);
+ GST_ELEMENT_WARNING (decoder, CORE, MISSING_PLUGIN,
+ (_("No decoder available for type \'%s\'."), capsstr), (NULL));
+--
+1.6.4.3
+
+From 4a7f2e6c102eeefa7da8e7c9c2eec3c0019bba01 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Sat, 26 Sep 2009 12:47:53 +0200
+Subject: [PATCH 6/7] decodebin2: Use the iterate internal links function instead of string magic to get multiqueue srcpads
+
+---
+ gst/playback/gstdecodebin2.c | 31 +++++++++++++++++--------------
+ 1 files changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c
+index 1d6c9d2..4fe62b4 100644
+--- a/gst/playback/gstdecodebin2.c
++++ b/gst/playback/gstdecodebin2.c
+@@ -2108,7 +2108,7 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
+ {
+ GstDecodeBin *dbin;
+ GstPad *srcpad, *sinkpad;
+- gchar *nb, *sinkname, *srcname;
++ GstIterator *it = NULL;
+
+ dbin = group->dbin;
+
+@@ -2126,29 +2126,32 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
+
+ if ((gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) {
+ GST_ERROR_OBJECT (dbin, "Couldn't link demuxer and multiqueue");
+- goto beach;
++ goto error;
+ }
+
+- CHAIN_MUTEX_LOCK (group->parent);
+- group->reqpads = g_list_prepend (group->reqpads, gst_object_ref (sinkpad));
+-
+- sinkname = gst_pad_get_name (sinkpad);
+- nb = sinkname + 4;
+- srcname = g_strdup_printf ("src%s", nb);
+- g_free (sinkname);
++ it = gst_pad_iterate_internal_links (sinkpad);
+
+- if (!(srcpad = gst_element_get_static_pad (group->multiqueue, srcname))) {
+- GST_ERROR_OBJECT (dbin, "Couldn't get srcpad %s from multiqueue", srcname);
+- goto chiringuito;
++ if (!it || (gst_iterator_next (it, (gpointer *) & srcpad)) != GST_ITERATOR_OK
++ || srcpad == NULL) {
++ GST_ERROR_OBJECT (dbin,
++ "Couldn't get srcpad from multiqueue for sinkpad %" GST_PTR_FORMAT,
++ sinkpad);
++ goto error;
+ }
+
+-chiringuito:
+- g_free (srcname);
++ CHAIN_MUTEX_LOCK (group->parent);
++ group->reqpads = g_list_prepend (group->reqpads, gst_object_ref (sinkpad));
+ CHAIN_MUTEX_UNLOCK (group->parent);
+
+ beach:
++ if (it)
++ gst_iterator_free (it);
+ gst_object_unref (sinkpad);
+ return srcpad;
++
++error:
++ gst_element_release_request_pad (group->multiqueue, sinkpad);
++ goto beach;
+ }
+
+ /* gst_decode_group_is_complete:
+--
+1.6.4.3
+
+From 68cef57e9260265229d194f1309cdffd654c95b6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege(a)collabora.co.uk>
+Date: Sat, 26 Sep 2009 12:56:36 +0200
+Subject: [PATCH 7/7] decodebin2: Chains with an exposed endpad are complete too
+
+This allows partial group changes, i.e. demuxer2 in the example below
+goes EOS but has a next group and audio2 stays the same.
+
+ /-- >demuxer2---->video
+demuxer--- \--->audio1
+ \--->audio2
+---
+ gst/playback/gstdecodebin2.c | 11 ++++++++---
+ 1 files changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c
+index 4fe62b4..60dbf57 100644
+--- a/gst/playback/gstdecodebin2.c
++++ b/gst/playback/gstdecodebin2.c
+@@ -2191,7 +2191,7 @@ out:
+ *
+ * Returns TRUE if the chain is complete, this means either
+ * a) This chain is a dead end, i.e. we have no suitable plugins
+- * b) This chain ends in an endpad and this is blocked
++ * b) This chain ends in an endpad and this is blocked or exposed
+ *
+ * Not MT-safe, always call with decodebin expose lock
+ */
+@@ -2200,7 +2200,12 @@ gst_decode_chain_is_complete (GstDecodeChain * chain)
+ {
+ gboolean complete = FALSE;
+
+- if (chain->deadend || (chain->endpad && chain->endpad->blocked)) {
++ if (chain->deadend) {
++ complete = TRUE;
++ goto out;
++ }
++
++ if (chain->endpad && (chain->endpad->blocked || chain->endpad->exposed)) {
+ complete = TRUE;
+ goto out;
+ }
+@@ -2543,7 +2548,7 @@ gst_decode_chain_expose (GstDecodeChain * chain, GList ** endpads)
+ return TRUE;
+
+ if (chain->endpad) {
+- if (!chain->endpad->blocked)
++ if (!chain->endpad->blocked && !chain->endpad->exposed)
+ return FALSE;
+ *endpads = g_list_prepend (*endpads, gst_object_ref (chain->endpad));
+ return TRUE;
+--
+1.6.4.3
+
diff --git a/gstpb-no-subtitle-errors.patch b/gstpb-no-subtitle-errors.patch
new file mode 100644
index 0000000..e6b4dd1
--- /dev/null
+++ b/gstpb-no-subtitle-errors.patch
@@ -0,0 +1,164 @@
+commit a4bc39ef4836abcf16bd9d451ed89979ff090c0d
+Author: Sebastian Dröge <sebastian.droege(a)collabora.co.uk>
+Date: Thu Oct 8 19:55:42 2009 +0200
+
+ playbin2: Don't stop completely on initialization errors from subtitle elements
+
+ Instead disable the subtitles and play the other parts of the stream.
+
+ Fixes bug #587704.
+
+diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c
+index b2f41a6..c134036 100644
+--- a/gst/playback/gstplaybin2.c
++++ b/gst/playback/gstplaybin2.c
+@@ -522,6 +522,10 @@ static GstPad *gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream);
+
+ static gboolean setup_next_source (GstPlayBin * playbin, GstState target);
+
++static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
++static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
++ GstSourceGroup * group);
++
+ static GstElementClass *parent_class;
+
+ static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
+@@ -532,6 +536,12 @@ GST_ELEMENT_DETAILS ("Player Bin 2",
+ "Autoplug and play media from an uri",
+ "Wim Taymans <wim.taymans(a)gmail.com>");
+
++#define REMOVE_SIGNAL(obj,id) \
++if (id) { \
++ g_signal_handler_disconnect (obj, id); \
++ id = 0; \
++}
++
+ static void
+ gst_play_marshal_BUFFER__BOXED (GClosure * closure,
+ GValue * return_value G_GNUC_UNUSED,
+@@ -1800,9 +1810,38 @@ static const gchar *blacklisted_mimes[] = {
+ "video/x-dvd-subpicture", "subpicture/x-pgs", NULL
+ };
+
++
++/* Returns TRUE if child is object or any parent (transitive)
++ * of child is object */
++static gboolean
++_gst_object_contains_object (GstObject * object, GstObject * child)
++{
++ GstObject *parent, *tmp;
++
++ if (!object || !child)
++ return FALSE;
++
++ parent = gst_object_ref (child);
++ do {
++ if (parent == object) {
++ gst_object_unref (parent);
++ return TRUE;
++ }
++
++ tmp = gst_object_get_parent (parent);
++ gst_object_unref (parent);
++ parent = tmp;
++ } while (parent);
++
++ return FALSE;
++}
++
+ static void
+ gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
+ {
++ GstPlayBin *playbin = GST_PLAY_BIN (bin);
++ GstSourceGroup *group;
++
+ if (gst_is_missing_plugin_message (msg)) {
+ gchar *detail;
+ guint i;
+@@ -1818,6 +1857,73 @@ gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
+ }
+ g_free (detail);
+ }
++
++ group = playbin->curr_group;
++ /* If we get an error of the subtitle uridecodebin transform
++ * them into warnings and disable the subtitles */
++ if (group && group->pending && group->suburidecodebin) {
++ GstObject *srcparent = gst_object_get_parent (GST_OBJECT_CAST (msg->src));
++
++ if (G_UNLIKELY (_gst_object_contains_object (GST_OBJECT_CAST
++ (group->suburidecodebin), msg->src)
++ && GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)) {
++ GError *err;
++ gchar *debug = NULL;
++ GstMessage *new_msg;
++ GstIterator *it;
++ gboolean done = FALSE;
++
++ gst_message_parse_error (msg, &err, &debug);
++ new_msg = gst_message_new_warning (msg->src, err, debug);
++
++ gst_message_unref (msg);
++ msg = new_msg;
++
++ REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
++ REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
++ REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
++
++ it = gst_element_iterate_src_pads (group->suburidecodebin);
++ while (it && !done) {
++ GstPad *p = NULL;
++ GstIteratorResult res;
++
++ res = gst_iterator_next (it, (gpointer) & p);
++
++ switch (res) {
++ case GST_ITERATOR_DONE:
++ done = TRUE;
++ break;
++ case GST_ITERATOR_OK:
++ pad_removed_cb (NULL, p, group);
++ gst_object_unref (p);
++ break;
++
++ case GST_ITERATOR_RESYNC:
++ gst_iterator_resync (it);
++ break;
++ case GST_ITERATOR_ERROR:
++ done = TRUE;
++ break;
++ }
++ }
++ if (it)
++ gst_iterator_free (it);
++
++ gst_object_ref (group->suburidecodebin);
++ gst_bin_remove (bin, group->suburidecodebin);
++ gst_element_set_locked_state (group->suburidecodebin, FALSE);
++ gst_element_set_state (group->suburidecodebin, GST_STATE_NULL);
++ gst_object_unref (group->suburidecodebin);
++ group->suburidecodebin = NULL;
++
++ no_more_pads_cb (NULL, group);
++ }
++
++ if (srcparent)
++ gst_object_unref (srcparent);
++ }
++
+ GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
+ }
+
+@@ -2516,12 +2622,6 @@ group_set_locked_state_unlocked (GstPlayBin * playbin, GstSourceGroup * group,
+ return TRUE;
+ }
+
+-#define REMOVE_SIGNAL(obj,id) \
+-if (id) { \
+- g_signal_handler_disconnect (obj, id); \
+- id = 0; \
+-}
+-
+ /* must be called with PLAY_BIN_LOCK */
+ static gboolean
+ activate_group (GstPlayBin * playbin, GstSourceGroup * group, GstState target)
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 3d36449..c18efde 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -3,7 +3,7 @@
Name: %{gstreamer}-plugins-base
Version: 0.10.25
-Release: 1%{?dist}
+Release: 2%{?dist}
Summary: GStreamer streaming media framework base plug-ins
Group: Applications/Multimedia
@@ -33,6 +33,11 @@ BuildRequires: gtk2-devel
BuildRequires: pkgconfig
Obsoletes: gstreamer-plugins
+# https://bugzilla.gnome.org/show_bug.cgi?id=587704
+Patch0: gstpb-no-subtitle-errors.patch
+# https://bugzilla.gnome.org/show_bug.cgi?id=591677
+Patch1: gstpb-fix-missing-plugins.patch
+
# documentation
BuildRequires: gtk-doc >= 1.3
BuildRequires: PyXML
@@ -49,6 +54,8 @@ This package contains a set of well-maintained base plug-ins.
%prep
%setup -q -n gst-plugins-base-%{version}
+%patch0 -p1 -b .subtitle-errors
+%patch1 -p1 -b .missing-plugins
%build
%configure \
@@ -244,6 +251,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Tue Oct 13 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.25-2
+- Add patches to fix some playbin2 bugs (#518880)
+
* Mon Oct 05 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.25-1
- Update to 0.10.25
- Require a gstreamer of the same version as us (#503707)
4 years, 4 months
[gstreamer-plugins-base: 96/216] - Update to 0.10.25 - Require a gstreamer of the same version as us (#503707)
by Dominik Mierzejewski
commit 82628ac200c094cf0b3e7970a616aff76ecc3e0b
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Mon Oct 5 16:24:58 2009 +0000
- Update to 0.10.25
- Require a gstreamer of the same version as us (#503707)
.cvsignore | 2 +-
gstreamer-plugins-base.spec | 12 +++++++-----
sources | 2 +-
3 files changed, 9 insertions(+), 7 deletions(-)
---
diff --git a/.cvsignore b/.cvsignore
index 3095cae..916451f 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-gst-plugins-base-0.10.24.4.tar.bz2
+gst-plugins-base-0.10.25.tar.bz2
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index e952b6f..3d36449 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -1,10 +1,8 @@
%define gstreamer gstreamer
%define majorminor 0.10
-%define _gst 0.10.24.4
-
Name: %{gstreamer}-plugins-base
-Version: 0.10.24.4
+Version: 0.10.25
Release: 1%{?dist}
Summary: GStreamer streaming media framework base plug-ins
@@ -14,9 +12,9 @@ URL: http://gstreamer.freedesktop.org/
Source: http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-%{...
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: %{gstreamer} >= %{_gst}
+Requires: %{gstreamer} >= %{version}
Requires: liboil >= 0.3.12-9
-BuildRequires: %{gstreamer}-devel >= %{_gst}
+BuildRequires: %{gstreamer}-devel >= %{version}
BuildRequires: gettext
BuildRequires: gcc-c++
@@ -246,6 +244,10 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Mon Oct 05 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.25-1
+- Update to 0.10.25
+- Require a gstreamer of the same version as us (#503707)
+
* Thu Oct 01 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.24.4-1
- Update to pre-release
diff --git a/sources b/sources
index 27ec874..b920614 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-72391698d726933b2cf089eeb5ced66d gst-plugins-base-0.10.24.4.tar.bz2
+d29669dd79276c5cd94e1613c03cd9ab gst-plugins-base-0.10.25.tar.bz2
4 years, 4 months
[gstreamer-plugins-base: 95/216] Bastien is tired
by Dominik Mierzejewski
commit a3d7b8087a51f88bf566140dcc7547e6a2cf4ae9
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Thu Oct 1 21:01:05 2009 +0000
Bastien is tired
gstreamer-plugins-base.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
---
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 341d830..e952b6f 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -181,7 +181,7 @@ GStreamer Base Plugins library development and header files.
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/tunerchannel.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/tunernorm.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/videoorientation.h
-%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/interfaces/streamvolume.h
+%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/streamvolume.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/xoverlay.h
%dir %{_includedir}/gstreamer-%{majorminor}/gst/netbuffer
%{_includedir}/gstreamer-%{majorminor}/gst/netbuffer/gstnetbuffer.h
4 years, 4 months
[gstreamer-plugins-base: 94/216] Fix filelist
by Dominik Mierzejewski
commit 15a4824c73b1e8485cb2354dc5cbfcbfc80c6a19
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Thu Oct 1 20:38:13 2009 +0000
Fix filelist
gstreamer-plugins-base.spec | 1 +
1 file changed, 1 insertion(+)
---
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index f8c07bb..341d830 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -181,6 +181,7 @@ GStreamer Base Plugins library development and header files.
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/tunerchannel.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/tunernorm.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/videoorientation.h
+%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/interfaces/streamvolume.h
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/xoverlay.h
%dir %{_includedir}/gstreamer-%{majorminor}/gst/netbuffer
%{_includedir}/gstreamer-%{majorminor}/gst/netbuffer/gstnetbuffer.h
4 years, 4 months
[gstreamer-plugins-base: 93/216] Remove obsolete patch
by Dominik Mierzejewski
commit 79b273befcfe0835db32c9666e9a3959820e5564
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Thu Oct 1 17:21:25 2009 +0000
Remove obsolete patch
avf-support.patch | 37 -------------------------------------
gstreamer-plugins-base.spec | 3 ---
2 files changed, 40 deletions(-)
---
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 4e90bc4..f8c07bb 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -14,8 +14,6 @@ URL: http://gstreamer.freedesktop.org/
Source: http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-%{...
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Patch0: avf-support.patch
-
Requires: %{gstreamer} >= %{_gst}
Requires: liboil >= 0.3.12-9
BuildRequires: %{gstreamer}-devel >= %{_gst}
@@ -53,7 +51,6 @@ This package contains a set of well-maintained base plug-ins.
%prep
%setup -q -n gst-plugins-base-%{version}
-%patch0 -p1 -b .avf
%build
%configure \
4 years, 4 months
[gstreamer-plugins-base: 92/216] - Update to pre-release
by Dominik Mierzejewski
commit fbfbb43965524fb33d583e311085552995a4d57e
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Thu Oct 1 17:10:58 2009 +0000
- Update to pre-release
.cvsignore | 2 +-
gstreamer-plugins-base.spec | 9 ++++++---
sources | 2 +-
3 files changed, 8 insertions(+), 5 deletions(-)
---
diff --git a/.cvsignore b/.cvsignore
index 6a32246..3095cae 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-gst-plugins-base-0.10.24.tar.bz2
+gst-plugins-base-0.10.24.4.tar.bz2
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 879ddf5..4e90bc4 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -1,11 +1,11 @@
%define gstreamer gstreamer
%define majorminor 0.10
-%define _gst 0.10.24
+%define _gst 0.10.24.4
Name: %{gstreamer}-plugins-base
-Version: 0.10.24
-Release: 2%{?dist}
+Version: 0.10.24.4
+Release: 1%{?dist}
Summary: GStreamer streaming media framework base plug-ins
Group: Applications/Multimedia
@@ -248,6 +248,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Thu Oct 01 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.24.4-1
+- Update to pre-release
+
* Wed Aug 26 2009 Adam Jackson <ajax(a)redhat.com> 0.10.24-2
- avf-support.patch: Add AVF file recognition (gnome #593117)
diff --git a/sources b/sources
index 665ac49..27ec874 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-a3ec977c9c6602caacc2d3a7c12a17bf gst-plugins-base-0.10.24.tar.bz2
+72391698d726933b2cf089eeb5ced66d gst-plugins-base-0.10.24.4.tar.bz2
4 years, 4 months
[gstreamer-plugins-base: 91/216] - avf-support.patch: Add AVF file recognition (gnome #593117)
by Dominik Mierzejewski
commit bf1205e329c33030f4554844e9689433779f437b
Author: Adam Jackson <ajax(a)fedoraproject.org>
Date: Wed Aug 26 15:21:47 2009 +0000
- avf-support.patch: Add AVF file recognition (gnome #593117)
avf-support.patch | 37 +++++++++++++++++++++++++++++++++++++
gstreamer-plugins-base.spec | 8 +++++++-
2 files changed, 44 insertions(+), 1 deletion(-)
---
diff --git a/avf-support.patch b/avf-support.patch
new file mode 100644
index 0000000..88b9bd8
--- /dev/null
+++ b/avf-support.patch
@@ -0,0 +1,37 @@
+Ref: http://bugzilla.gnome.org/show_bug.cgi?id=593117
+
+diff -up gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-ids.h.jx gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-ids.h
+--- gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-ids.h.jx 2009-07-14 06:03:45.000000000 -0400
++++ gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-ids.h 2009-08-26 11:16:20.000000000 -0400
+@@ -33,6 +33,7 @@ G_BEGIN_DECLS
+
+ /* tags */
+ #define GST_RIFF_TAG_RIFF GST_MAKE_FOURCC ('R','I','F','F')
++#define GST_RIFF_TAG_AVF0 GST_MAKE_FOURCC ('A','V','F','0')
+ #define GST_RIFF_TAG_RIFX GST_MAKE_FOURCC ('R','I','F','X')
+ #define GST_RIFF_TAG_LIST GST_MAKE_FOURCC ('L','I','S','T')
+ #define GST_RIFF_TAG_avih GST_MAKE_FOURCC ('a','v','i','h')
+diff -up gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-read.c.jx gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-read.c
+--- gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-read.c.jx 2009-07-14 06:03:45.000000000 -0400
++++ gst-plugins-base-0.10.24/gst-libs/gst/riff/riff-read.c 2009-08-26 11:16:54.000000000 -0400
+@@ -226,7 +226,7 @@ gst_riff_parse_file_header (GstElement *
+
+ data = GST_BUFFER_DATA (buf);
+ tag = GST_READ_UINT32_LE (data);
+- if (tag != GST_RIFF_TAG_RIFF)
++ if (tag != GST_RIFF_TAG_RIFF && tag != GST_RIFF_TAG_AVF0)
+ goto not_riff;
+
+ *doctype = GST_READ_UINT32_LE (data + 8);
+diff -up gst-plugins-base-0.10.24/gst/typefind/gsttypefindfunctions.c.jx gst-plugins-base-0.10.24/gst/typefind/gsttypefindfunctions.c
+--- gst-plugins-base-0.10.24/gst/typefind/gsttypefindfunctions.c.jx 2009-08-04 18:26:58.000000000 -0400
++++ gst-plugins-base-0.10.24/gst/typefind/gsttypefindfunctions.c 2009-08-26 11:17:56.000000000 -0400
+@@ -3147,7 +3147,7 @@ riff_type_find (GstTypeFind * tf, gpoint
+ GstTypeFindData *riff_data = (GstTypeFindData *) private;
+ guint8 *data = gst_type_find_peek (tf, 0, 12);
+
+- if (data && memcmp (data, "RIFF", 4) == 0) {
++ if (data && (memcmp (data, "RIFF", 4) == 0 || memcmp (data, "AVF0", 4) == 0)) {
+ data += 8;
+ if (memcmp (data, riff_data->data, 4) == 0)
+ gst_type_find_suggest (tf, riff_data->probability, riff_data->caps);
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 62561a1..879ddf5 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -5,7 +5,7 @@
Name: %{gstreamer}-plugins-base
Version: 0.10.24
-Release: 1%{?dist}
+Release: 2%{?dist}
Summary: GStreamer streaming media framework base plug-ins
Group: Applications/Multimedia
@@ -14,6 +14,8 @@ URL: http://gstreamer.freedesktop.org/
Source: http://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-%{...
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Patch0: avf-support.patch
+
Requires: %{gstreamer} >= %{_gst}
Requires: liboil >= 0.3.12-9
BuildRequires: %{gstreamer}-devel >= %{_gst}
@@ -51,6 +53,7 @@ This package contains a set of well-maintained base plug-ins.
%prep
%setup -q -n gst-plugins-base-%{version}
+%patch0 -p1 -b .avf
%build
%configure \
@@ -245,6 +248,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Wed Aug 26 2009 Adam Jackson <ajax(a)redhat.com> 0.10.24-2
+- avf-support.patch: Add AVF file recognition (gnome #593117)
+
* Wed Aug 05 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.24-1
- Update to 0.10.24
4 years, 4 months
[gstreamer-plugins-base: 90/216] - Update to 0.10.24
by Dominik Mierzejewski
commit 07c70f49de217e0bbec03e58a6a3e2ae458e88f3
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Wed Aug 5 12:46:15 2009 +0000
- Update to 0.10.24
.cvsignore | 2 +-
gstreamer-plugins-base.spec | 7 +++++--
sources | 2 +-
3 files changed, 7 insertions(+), 4 deletions(-)
---
diff --git a/.cvsignore b/.cvsignore
index 613a564..6a32246 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-gst-plugins-base-0.10.23.4.tar.bz2
+gst-plugins-base-0.10.24.tar.bz2
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index d8c3833..62561a1 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -1,10 +1,10 @@
%define gstreamer gstreamer
%define majorminor 0.10
-%define _gst 0.10.22
+%define _gst 0.10.24
Name: %{gstreamer}-plugins-base
-Version: 0.10.23.4
+Version: 0.10.24
Release: 1%{?dist}
Summary: GStreamer streaming media framework base plug-ins
@@ -245,6 +245,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Wed Aug 05 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.24-1
+- Update to 0.10.24
+
* Tue Jul 28 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.23.4-1
- Update to 0.10.23.4
diff --git a/sources b/sources
index 43c8844..665ac49 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-78594c5672b3bf41b37a3a9f7c16bb3b gst-plugins-base-0.10.23.4.tar.bz2
+a3ec977c9c6602caacc2d3a7c12a17bf gst-plugins-base-0.10.24.tar.bz2
4 years, 4 months
[gstreamer-plugins-base: 89/216] - Update to 0.10.23.4
by Dominik Mierzejewski
commit d464b8784ca5f574d8554a0f298741c7c96c402f
Author: Bastien Nocera <hadess(a)fedoraproject.org>
Date: Tue Jul 28 12:01:58 2009 +0000
- Update to 0.10.23.4
.cvsignore | 2 +-
gstreamer-plugins-base.spec | 7 +++++--
sources | 2 +-
3 files changed, 7 insertions(+), 4 deletions(-)
---
diff --git a/.cvsignore b/.cvsignore
index 5692498..613a564 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-gst-plugins-base-0.10.23.3.tar.bz2
+gst-plugins-base-0.10.23.4.tar.bz2
diff --git a/gstreamer-plugins-base.spec b/gstreamer-plugins-base.spec
index 24d1cb1..d8c3833 100644
--- a/gstreamer-plugins-base.spec
+++ b/gstreamer-plugins-base.spec
@@ -4,8 +4,8 @@
%define _gst 0.10.22
Name: %{gstreamer}-plugins-base
-Version: 0.10.23.3
-Release: 3%{?dist}
+Version: 0.10.23.4
+Release: 1%{?dist}
Summary: GStreamer streaming media framework base plug-ins
Group: Applications/Multimedia
@@ -245,6 +245,9 @@ GStreamer Base Plugins library development and header files.
%doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor}
%changelog
+* Tue Jul 28 2009 Bastien Nocera <bnocera(a)redhat.com> 0.10.23.4-1
+- Update to 0.10.23.4
+
* Fri Jul 24 2009 Fedora Release Engineering <rel-eng(a)lists.fedoraproject.org> - 0.10.23.3-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
diff --git a/sources b/sources
index a74221c..43c8844 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-ae26e7cfd13cbbcf78658f9ada7866a0 gst-plugins-base-0.10.23.3.tar.bz2
+78594c5672b3bf41b37a3a9f7c16bb3b gst-plugins-base-0.10.23.4.tar.bz2
4 years, 4 months