tee: Check for the removed pad flag also in the slow pushing path
[platform/upstream/gstreamer.git] / gst / gstbin.c
index 665259e..17190fd 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  *
  * MT safe.
  */
 
 /**
  * SECTION:gstbin
+ * @title: GstBin
  * @short_description: Base class and element that can contain other elements
  *
  * #GstBin is an element that can contain other #GstElement, allowing them to be
  * the bin. Likewise the #GstBin::element-removed signal is fired whenever an
  * element is removed from the bin.
  *
- * <refsect2><title>Notes</title>
- * <para>
+ * ## Notes
+ *
  * A #GstBin internally intercepts every #GstMessage posted by its children and
  * implements the following default behaviour for each of them:
- * <variablelist>
- *   <varlistentry>
- *     <term>GST_MESSAGE_EOS</term>
- *     <listitem><para>This message is only posted by sinks in the PLAYING
- *     state. If all sinks posted the EOS message, this bin will post and EOS
- *     message upwards.</para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_MESSAGE_SEGMENT_START</term>
- *     <listitem><para>just collected and never forwarded upwards.
- *     The messages are used to decide when all elements have completed playback
- *     of their segment.</para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_MESSAGE_SEGMENT_DONE</term>
- *     <listitem><para> Is posted by #GstBin when all elements that posted
- *     a SEGMENT_START have posted a SEGMENT_DONE.</para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_MESSAGE_DURATION</term>
- *     <listitem><para> Is posted by an element that detected a change
- *     in the stream duration. The default bin behaviour is to clear any
- *     cached duration values so that the next duration query will perform
- *     a full duration recalculation. The duration change is posted to the
- *     application so that it can refetch the new duration with a duration
- *     query. Note that these messages can be posted before the bin is
- *     prerolled, in which case the duration query might fail.
- *     </para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_MESSAGE_CLOCK_LOST</term>
- *     <listitem><para> This message is posted by an element when it
- *     can no longer provide a clock. The default bin behaviour is to
- *     check if the lost clock was the one provided by the bin. If so and
- *     the bin is currently in the PLAYING state, the message is forwarded to
- *     the bin parent.
- *     This message is also generated when a clock provider is removed from
- *     the bin. If this message is received by the application, it should
- *     PAUSE the pipeline and set it back to PLAYING to force a new clock
- *     distribution.
- *     </para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_MESSAGE_CLOCK_PROVIDE</term>
- *     <listitem><para> This message is generated when an element
- *     can provide a clock. This mostly happens when a new clock
- *     provider is added to the bin. The default behaviour of the bin is to
- *     mark the currently selected clock as dirty, which will perform a clock
- *     recalculation the next time the bin is asked to provide a clock.
- *     This message is never sent tot the application but is forwarded to
- *     the parent of the bin.
- *     </para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>OTHERS</term>
- *     <listitem><para> posted upwards.</para></listitem>
- *   </varlistentry>
- * </variablelist>
  *
+ * * GST_MESSAGE_EOS: This message is only posted by sinks in the PLAYING
+ * state. If all sinks posted the EOS message, this bin will post and EOS
+ * message upwards.
+ *
+ * * GST_MESSAGE_SEGMENT_START: Just collected and never forwarded upwards.
+ * The messages are used to decide when all elements have completed playback
+ * of their segment.
+ *
+ * * GST_MESSAGE_SEGMENT_DONE: Is posted by #GstBin when all elements that posted
+ * a SEGMENT_START have posted a SEGMENT_DONE.
+ *
+ * * GST_MESSAGE_DURATION_CHANGED: Is posted by an element that detected a change
+ * in the stream duration. The default bin behaviour is to clear any
+ * cached duration values so that the next duration query will perform
+ * a full duration recalculation. The duration change is posted to the
+ * application so that it can refetch the new duration with a duration
+ * query. Note that these messages can be posted before the bin is
+ * prerolled, in which case the duration query might fail.
+ *
+ * * GST_MESSAGE_CLOCK_LOST: This message is posted by an element when it
+ * can no longer provide a clock. The default bin behaviour is to
+ * check if the lost clock was the one provided by the bin. If so and
+ * the bin is currently in the PLAYING state, the message is forwarded to
+ * the bin parent.
+ * This message is also generated when a clock provider is removed from
+ * the bin. If this message is received by the application, it should
+ * PAUSE the pipeline and set it back to PLAYING to force a new clock
+ * distribution.
+ *
+ * * GST_MESSAGE_CLOCK_PROVIDE: This message is generated when an element
+ * can provide a clock. This mostly happens when a new clock
+ * provider is added to the bin. The default behaviour of the bin is to
+ * mark the currently selected clock as dirty, which will perform a clock
+ * recalculation the next time the bin is asked to provide a clock.
+ * This message is never sent tot the application but is forwarded to
+ * the parent of the bin.
+ *
+ * * OTHERS: posted upwards.
  *
  * A #GstBin implements the following default behaviour for answering to a
  * #GstQuery:
- * <variablelist>
- *   <varlistentry>
- *     <term>GST_QUERY_DURATION</term>
- *     <listitem><para>If the query has been asked before with the same format
- *     and the bin is a toplevel bin (ie. has no parent),
- *     use the cached previous value. If no previous value was cached, the
- *     query is sent to all sink elements in the bin and the MAXIMUM of all
- *     values is returned. If the bin is a toplevel bin the value is cached.
- *     If no sinks are available in the bin, the query fails.
- *     </para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>GST_QUERY_POSITION</term>
- *     <listitem><para>The query is sent to all sink elements in the bin and the
- *     MAXIMUM of all values is returned. If no sinks are available in the bin,
- *     the query fails.
- *     </para></listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>OTHERS</term>
- *     <listitem><para>the query is forwarded to all sink elements, the result
- *     of the first sink that answers the query successfully is returned. If no
- *     sink is in the bin, the query fails.</para></listitem>
- *   </varlistentry>
- * </variablelist>
- *
- * A #GstBin will by default forward any event sent to it to all sink elements.
- * If all the sinks return TRUE, the bin will also return TRUE, else FALSE is
- * returned. If no sinks are in the bin, the event handler will return TRUE.
- *
- * </para>
- * </refsect2>
- *
- * Last reviewed on 2012-03-28 (0.11.3)
+ *
+ * * GST_QUERY_DURATION:If the query has been asked before with the same format
+ * and the bin is a toplevel bin (ie. has no parent),
+ * use the cached previous value. If no previous value was cached, the
+ * query is sent to all sink elements in the bin and the MAXIMUM of all
+ * values is returned. If the bin is a toplevel bin the value is cached.
+ * If no sinks are available in the bin, the query fails.
+ *
+ * * GST_QUERY_POSITION:The query is sent to all sink elements in the bin and the
+ * MAXIMUM of all values is returned. If no sinks are available in the bin,
+ * the query fails.
+ *
+ * * OTHERS:the query is forwarded to all sink elements, the result
+ * of the first sink that answers the query successfully is returned. If no
+ * sink is in the bin, the query fails.
+ *
+ * A #GstBin will by default forward any event sent to it to all sink
+ * (#GST_EVENT_TYPE_DOWNSTREAM) or source (#GST_EVENT_TYPE_UPSTREAM) elements
+ * depending on the event type.
+ * If all the elements return %TRUE, the bin will also return %TRUE, else %FALSE
+ * is returned. If no elements of the required type are in the bin, the event
+ * handler will return %TRUE.
+ *
  */
 
 #include "gst_private.h"
@@ -174,9 +144,6 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug);
  * a toplevel bin */
 #define BIN_IS_TOPLEVEL(bin) ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling)
 
-#define GST_BIN_GET_PRIVATE(obj)  \
-   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))
-
 struct _GstBinPrivate
 {
   gboolean asynchandling;
@@ -198,11 +165,12 @@ struct _GstBinPrivate
   gboolean message_forward;
 
   gboolean posted_eos;
+  gboolean posted_playing;
+  GstElementFlags suppressed_flags;
 };
 
 typedef struct
 {
-  GstBin *bin;
   guint32 cookie;
   GstState pending;
 } BinContinueData;
@@ -216,18 +184,24 @@ static void gst_bin_get_property (GObject * object, guint prop_id,
 
 static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
     GstStateChange transition);
-static void gst_bin_state_changed (GstElement * element, GstState oldstate,
-    GstState newstate, GstState pending);
+static gboolean gst_bin_post_message (GstElement * element, GstMessage * msg);
 static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
     GstState * state, GstState * pending, GstClockTime timeout);
 static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
-    gboolean flag_pending, gboolean reset_time);
+    gboolean flag_pending, GstClockTime running_time);
 static void bin_handle_async_start (GstBin * bin);
-static void bin_push_state_continue (BinContinueData * data);
+static void bin_push_state_continue (GstBin * bin, BinContinueData * data);
 static void bin_do_eos (GstBin * bin);
 
 static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
 static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
+static void gst_bin_deep_element_added_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * element);
+static void gst_bin_deep_element_removed_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * element);
+static void gst_bin_update_context (GstBin * bin, GstContext * context);
+static void gst_bin_update_context_unlocked (GstBin * bin,
+    GstContext * context);
 
 #if 0
 static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
@@ -242,12 +216,13 @@ static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
 static GstBusSyncReply bin_bus_handler (GstBus * bus,
     GstMessage * message, GstBin * bin);
 static gboolean gst_bin_query (GstElement * element, GstQuery * query);
+static void gst_bin_set_context (GstElement * element, GstContext * context);
 
 static gboolean gst_bin_do_latency_func (GstBin * bin);
 
 static void bin_remove_messages (GstBin * bin, GstObject * src,
     GstMessageType types);
-static void gst_bin_continue_func (BinContinueData * data);
+static void gst_bin_continue_func (GstBin * bin, BinContinueData * data);
 static gint bin_element_is_sink (GstElement * child, GstBin * bin);
 static gint bin_element_is_src (GstElement * child, GstBin * bin);
 
@@ -259,6 +234,8 @@ enum
   ELEMENT_ADDED,
   ELEMENT_REMOVED,
   DO_LATENCY,
+  DEEP_ELEMENT_ADDED,
+  DEEP_ELEMENT_REMOVED,
   LAST_SIGNAL
 };
 
@@ -292,7 +269,9 @@ static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
 }
 
 #define gst_bin_parent_class parent_class
-G_DEFINE_TYPE_WITH_CODE (GstBin, gst_bin, GST_TYPE_ELEMENT, _do_init);
+G_DEFINE_TYPE_WITH_CODE (GstBin, gst_bin, GST_TYPE_ELEMENT,
+    G_ADD_PRIVATE (GstBin)
+    _do_init);
 
 static GObject *
 gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
@@ -356,24 +335,19 @@ gst_bin_class_init (GstBinClass * klass)
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
-  GError *err;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
 
-  g_type_class_add_private (klass, sizeof (GstBinPrivate));
-
   gobject_class->set_property = gst_bin_set_property;
   gobject_class->get_property = gst_bin_get_property;
 
   /**
-   * GstBin:async-handling
+   * GstBin:async-handling:
    *
-   * If set to #TRUE, the bin will handle asynchronous state changes.
+   * If set to %TRUE, the bin will handle asynchronous state changes.
    * This should be used only if the bin subclass is modifying the state
    * of its children on its own.
-   *
-   * Since: 0.10.13
    */
   g_object_class_install_property (gobject_class, PROP_ASYNC_HANDLING,
       g_param_spec_boolean ("async-handling", "Async Handling",
@@ -403,11 +377,41 @@ gst_bin_class_init (GstBinClass * klass)
       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
   /**
+   * GstBin::deep-element-added:
+   * @bin: the #GstBin
+   * @sub_bin: the #GstBin the element was added to
+   * @element: the #GstElement that was added to @sub_bin
+   *
+   * Will be emitted after the element was added to sub_bin.
+   *
+   * Since: 1.10
+   */
+  gst_bin_signals[DEEP_ELEMENT_ADDED] =
+      g_signal_new ("deep-element-added", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, deep_element_added),
+      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_BIN,
+      GST_TYPE_ELEMENT);
+  /**
+   * GstBin::deep-element-removed:
+   * @bin: the #GstBin
+   * @sub_bin: the #GstBin the element was removed from
+   * @element: the #GstElement that was removed from @sub_bin
+   *
+   * Will be emitted after the element was removed from sub_bin.
+   *
+   * Since: 1.10
+   */
+  gst_bin_signals[DEEP_ELEMENT_REMOVED] =
+      g_signal_new ("deep-element-removed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, deep_element_removed),
+      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_BIN,
+      GST_TYPE_ELEMENT);
+  /**
    * GstBin::do-latency:
    * @bin: the #GstBin
    *
    * Will be emitted when the bin needs to perform latency calculations. This
-   * signal is only emited for toplevel bins or when async-handling is
+   * signal is only emitted for toplevel bins or when async-handling is
    * enabled.
    *
    * Only one signal handler is invoked. If no signals are connected, the
@@ -417,8 +421,6 @@ gst_bin_class_init (GstBinClass * klass)
    * Connect to this signal if the default latency calculations are not
    * sufficient, like when you need different latencies for different sinks in
    * the same pipeline.
-   *
-   * Since: 0.10.22
    */
   gst_bin_signals[DO_LATENCY] =
       g_signal_new ("do-latency", G_TYPE_FROM_CLASS (klass),
@@ -427,7 +429,7 @@ gst_bin_class_init (GstBinClass * klass)
       G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
 
   /**
-   * GstBin:message-forward
+   * GstBin:message-forward:
    *
    * Forward all children messages, even those that would normally be filtered by
    * the bin. This can be interesting when one wants to be notified of the EOS
@@ -437,8 +439,6 @@ gst_bin_class_init (GstBinClass * klass)
    * source. The structure of the message is named 'GstBinForwarded' and contains
    * a field named 'message' of type GST_TYPE_MESSAGE that contains the original
    * forwarded message.
-   *
-   * Since: 0.10.31
    */
   g_object_class_install_property (gobject_class, PROP_MESSAGE_FORWARD,
       g_param_spec_boolean ("message-forward", "Message Forward",
@@ -447,7 +447,7 @@ gst_bin_class_init (GstBinClass * klass)
 
   gobject_class->dispose = gst_bin_dispose;
 
-  gst_element_class_set_metadata (gstelement_class, "Generic bin",
+  gst_element_class_set_static_metadata (gstelement_class, "Generic bin",
       "Generic/Bin",
       "Simple container object",
       "Erik Walthinsen <omega@cse.ogi.edu>,"
@@ -455,7 +455,7 @@ gst_bin_class_init (GstBinClass * klass)
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
-  gstelement_class->state_changed = GST_DEBUG_FUNCPTR (gst_bin_state_changed);
+  gstelement_class->post_message = GST_DEBUG_FUNCPTR (gst_bin_post_message);
   gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
 #if 0
   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_bin_get_index_func);
@@ -467,20 +467,16 @@ gst_bin_class_init (GstBinClass * klass)
 
   gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
   gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
+  gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_bin_set_context);
 
   klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
   klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
   klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
 
-  klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);
+  klass->deep_element_added = gst_bin_deep_element_added_func;
+  klass->deep_element_removed = gst_bin_deep_element_removed_func;
 
-  GST_DEBUG ("creating bin thread pool");
-  err = NULL;
-  klass->pool =
-      g_thread_pool_new ((GFunc) gst_bin_continue_func, NULL, -1, FALSE, &err);
-  if (err != NULL) {
-    g_critical ("could alloc threadpool %s", err->message);
-  }
+  klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);
 }
 
 static void
@@ -497,12 +493,14 @@ gst_bin_init (GstBin * bin)
 
   /* Set up a bus for listening to child elements */
   bus = g_object_new (GST_TYPE_BUS, "enable-async", FALSE, NULL);
+  gst_object_ref_sink (bus);
   bin->child_bus = bus;
   GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children",
       bus);
-  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
+  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin,
+      NULL);
 
-  bin->priv = GST_BIN_GET_PRIVATE (bin);
+  bin->priv = gst_bin_get_instance_private (bin);
   bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
   bin->priv->structure_cookie = 0;
   bin->priv->message_forward = DEFAULT_MESSAGE_FORWARD;
@@ -516,7 +514,7 @@ gst_bin_dispose (GObject * object)
   GstClock **provided_clock_p = &bin->provided_clock;
   GstElement **clock_provider_p = &bin->clock_provider;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "%p dispose", object);
 
   GST_OBJECT_LOCK (object);
   gst_object_replace ((GstObject **) child_bus_p, NULL);
@@ -538,7 +536,7 @@ gst_bin_dispose (GObject * object)
 
 /**
  * gst_bin_new:
- * @name: the name of the new bin
+ * @name: (allow-none): the name of the new bin
  *
  * Creates a new bin with the given name.
  *
@@ -887,8 +885,8 @@ find_message (GstBin * bin, GstObject * src, GstMessageType types)
       guint i;
 
       for (i = 0; i < 32; i++)
-        if (types & (1 << i))
-          GST_DEBUG_OBJECT (bin, "  %s", gst_message_type_get_name (1 << i));
+        if (types & (1U << i))
+          GST_DEBUG_OBJECT (bin, "  %s", gst_message_type_get_name (1U << i));
     }
 #endif
   }
@@ -956,7 +954,8 @@ bin_remove_messages (GstBin * bin, GstObject * src, GstMessageType types)
 
     if (message_check (message, &find) == 0) {
       GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
-          "deleting message %p of types 0x%08x", message, types);
+          "deleting message %p of type %s (types 0x%08x)", message,
+          GST_MESSAGE_TYPE_NAME (message), types);
       bin->messages = g_list_delete_link (bin->messages, walk);
       gst_message_unref (message);
     } else {
@@ -1008,6 +1007,66 @@ is_eos (GstBin * bin, guint32 * seqnum)
   return result && n_eos > 0;
 }
 
+
+/* Check if the bin is STREAM_START. We do this by scanning all sinks and
+ * checking if they posted an STREAM_START message.
+ *
+ * call with bin LOCK */
+static gboolean
+is_stream_start (GstBin * bin, guint32 * seqnum, gboolean * have_group_id,
+    guint * group_id)
+{
+  gboolean result;
+  GList *walk, *msgs;
+  guint tmp_group_id;
+  gboolean first = TRUE, same_group_id = TRUE;
+
+  *have_group_id = TRUE;
+  *group_id = 0;
+  result = TRUE;
+  for (walk = bin->children; walk; walk = g_list_next (walk)) {
+    GstElement *element;
+
+    element = GST_ELEMENT_CAST (walk->data);
+    if (bin_element_is_sink (element, bin) == 0) {
+      /* check if element posted STREAM_START */
+      if ((msgs =
+              find_message (bin, GST_OBJECT_CAST (element),
+                  GST_MESSAGE_STREAM_START))) {
+        GST_DEBUG ("sink '%s' posted STREAM_START", GST_ELEMENT_NAME (element));
+        *seqnum = gst_message_get_seqnum (GST_MESSAGE_CAST (msgs->data));
+        if (gst_message_parse_group_id (GST_MESSAGE_CAST (msgs->data),
+                &tmp_group_id)) {
+          if (first) {
+            first = FALSE;
+            *group_id = tmp_group_id;
+          } else {
+            if (tmp_group_id != *group_id)
+              same_group_id = FALSE;
+          }
+        } else {
+          *have_group_id = FALSE;
+        }
+      } else {
+        GST_DEBUG ("sink '%s' did not post STREAM_START yet",
+            GST_ELEMENT_NAME (element));
+        result = FALSE;
+        break;
+      }
+    }
+  }
+
+  /* If all have a group_id we only consider this stream started
+   * if all group ids were the same and all sinks posted a stream-start
+   * message */
+  if (*have_group_id)
+    return same_group_id && result;
+  /* otherwise consider this stream started after all sinks
+   * have reported stream-start for backward compatibility.
+   * FIXME 2.0: This should go away! */
+  return result;
+}
+
 static void
 unlink_pads (const GValue * item, gpointer user_data)
 {
@@ -1025,6 +1084,59 @@ unlink_pads (const GValue * item, gpointer user_data)
   }
 }
 
+static void
+bin_deep_iterator_foreach (const GValue * item, gpointer user_data)
+{
+  GQueue *queue = user_data;
+
+  g_queue_push_tail (queue, g_value_dup_object (item));
+}
+
+static void
+gst_bin_do_deep_add_remove (GstBin * bin, gint sig_id, const gchar * sig_name,
+    GstElement * element)
+{
+  g_signal_emit (bin, sig_id, 0, bin, element);
+
+  /* When removing a bin, emit deep-element-* for everything in the bin too */
+  if (GST_IS_BIN (element)) {
+    GstIterator *it;
+    GstIteratorResult ires;
+    GQueue elements = G_QUEUE_INIT;
+
+    GST_LOG_OBJECT (bin, "Recursing into bin %" GST_PTR_FORMAT " for %s",
+        element, sig_name);
+    it = gst_bin_iterate_recurse (GST_BIN_CAST (element));
+    do {
+      ires = gst_iterator_foreach (it, bin_deep_iterator_foreach, &elements);
+      if (ires != GST_ITERATOR_DONE) {
+        g_queue_foreach (&elements, (GFunc) g_object_unref, NULL);
+        g_queue_clear (&elements);
+      }
+      if (ires == GST_ITERATOR_RESYNC)
+        gst_iterator_resync (it);
+    } while (ires == GST_ITERATOR_RESYNC);
+    if (ires != GST_ITERATOR_ERROR) {
+      GstElement *e;
+
+      while ((e = g_queue_pop_head (&elements))) {
+        GstObject *parent = gst_object_get_parent (GST_OBJECT_CAST (e));
+
+        /* an element could have removed some of its internal elements
+         * meanwhile, so protect against that */
+        if (parent) {
+          GST_LOG_OBJECT (bin, "calling %s for element %" GST_PTR_FORMAT
+              " in bin %" GST_PTR_FORMAT, sig_name, e, parent);
+          g_signal_emit (bin, sig_id, 0, parent, e);
+          gst_object_unref (parent);
+          g_object_unref (e);
+        }
+      }
+    }
+    gst_iterator_free (it);
+  }
+}
+
 /* vmethod that adds an element to a bin
  *
  * MT safe
@@ -1037,11 +1149,12 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
   gboolean is_sink, is_source, provides_clock, requires_clock;
   GstMessage *clock_message = NULL, *async_message = NULL;
   GstStateChangeReturn ret;
+  GList *l, *elem_contexts, *need_context_messages;
 
   GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
 
   /* we obviously can't add ourself to ourself */
-  if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
+  if (G_UNLIKELY (element == GST_ELEMENT_CAST (bin)))
     goto adding_itself;
 
   /* get the element name to make sure it is unique in this bin. */
@@ -1070,23 +1183,25 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
     goto had_parent;
 
   /* if we add a sink we become a sink */
-  if (is_sink) {
+  if (is_sink && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
     GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
         elem_name);
     GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_SINK);
   }
-  if (is_source) {
+  if (is_source && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
     GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was source",
         elem_name);
     GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_SOURCE);
   }
-  if (provides_clock) {
+  if (provides_clock
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
     GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
     clock_message =
         gst_message_new_clock_provide (GST_OBJECT_CAST (element), NULL, TRUE);
     GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
   }
-  if (requires_clock) {
+  if (requires_clock
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
     GST_DEBUG_OBJECT (bin, "element \"%s\" requires a clock", elem_name);
     GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
   }
@@ -1094,7 +1209,8 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
   bin->children = g_list_prepend (bin->children, element);
   bin->numchildren++;
   bin->children_cookie++;
-  bin->priv->structure_cookie++;
+  if (!GST_BIN_IS_NO_RESYNC (bin))
+    bin->priv->structure_cookie++;
 
   /* distribute the bus */
   gst_element_set_bus (element, bin->child_bus);
@@ -1107,6 +1223,36 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
    * a new clock will be selected */
   gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
 
+  /* get the element's list of contexts before propagating our own */
+  elem_contexts = gst_element_get_contexts (element);
+  for (l = GST_ELEMENT_CAST (bin)->contexts; l; l = l->next)
+    gst_element_set_context (element, l->data);
+
+  need_context_messages = NULL;
+  for (l = elem_contexts; l; l = l->next) {
+    GstContext *replacement, *context = l->data;
+    const gchar *context_type;
+
+    context_type = gst_context_get_context_type (context);
+
+    /* we already set this context above? */
+    replacement =
+        gst_element_get_context_unlocked (GST_ELEMENT (bin), context_type);
+    if (replacement) {
+      gst_context_unref (replacement);
+    } else {
+      GstMessage *msg;
+      GstStructure *s;
+
+      /* ask our parent for the context */
+      msg = gst_message_new_need_context (GST_OBJECT_CAST (bin), context_type);
+      s = (GstStructure *) gst_message_get_structure (msg);
+      gst_structure_set (s, "bin.old.context", GST_TYPE_CONTEXT, context, NULL);
+
+      need_context_messages = g_list_prepend (need_context_messages, msg);
+    }
+  }
+
 #if 0
   /* set the cached index on the children */
   if (bin->priv->index)
@@ -1134,7 +1280,7 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
     }
     case GST_STATE_CHANGE_NO_PREROLL:
       /* ignore all async elements we might have and commit our state */
-      bin_handle_async_done (bin, ret, FALSE, FALSE);
+      bin_handle_async_done (bin, ret, FALSE, GST_CLOCK_TIME_NONE);
       break;
     case GST_STATE_CHANGE_FAILURE:
       break;
@@ -1145,24 +1291,75 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
 no_state_recalc:
   GST_OBJECT_UNLOCK (bin);
 
+  for (l = need_context_messages; l; l = l->next) {
+    GstMessage *msg = l->data;
+    GstStructure *s;
+    const gchar *context_type;
+    GstContext *replacement, *context;
+
+    gst_message_parse_context_type (msg, &context_type);
+
+    GST_LOG_OBJECT (bin, "asking parent for context type: %s "
+        "from %" GST_PTR_FORMAT, context_type, element);
+
+    s = (GstStructure *) gst_message_get_structure (msg);
+    gst_structure_get (s, "bin.old.context", GST_TYPE_CONTEXT, &context, NULL);
+    gst_structure_remove_field (s, "bin.old.context");
+    /* Keep the msg around while we still need access to the context_type */
+    gst_element_post_message (GST_ELEMENT_CAST (bin), gst_message_ref (msg));
+
+    /* lock to avoid losing a potential write */
+    GST_OBJECT_LOCK (bin);
+    replacement =
+        gst_element_get_context_unlocked (GST_ELEMENT_CAST (bin), context_type);
+    gst_message_unref (msg);
+
+    if (replacement) {
+      /* we got the context set from GstElement::set_context */
+      gst_context_unref (replacement);
+      GST_OBJECT_UNLOCK (bin);
+    } else {
+      /* Propagate the element's context upwards */
+      GST_LOG_OBJECT (bin, "propagating existing context type: %s %p "
+          "from %" GST_PTR_FORMAT, context_type, context, element);
+
+      gst_bin_update_context_unlocked (bin, context);
+
+      msg =
+          gst_message_new_have_context (GST_OBJECT_CAST (bin),
+          gst_context_ref (context));
+      GST_OBJECT_UNLOCK (bin);
+      gst_element_post_message (GST_ELEMENT_CAST (bin), msg);
+    }
+    gst_context_unref (context);
+  }
+  g_list_free_full (elem_contexts, (GDestroyNotify) gst_context_unref);
+  g_list_free (need_context_messages);
+
   /* post the messages on the bus of the element so that the bin can handle
    * them */
   if (clock_message)
-    gst_element_post_message (GST_ELEMENT_CAST (element), clock_message);
+    gst_element_post_message (element, clock_message);
 
   if (async_message)
-    gst_element_post_message (GST_ELEMENT_CAST (element), async_message);
+    gst_element_post_message (element, async_message);
 
   /* unlink all linked pads */
   it = gst_element_iterate_pads (element);
-  gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads, NULL);
+  while (gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads,
+          NULL) == GST_ITERATOR_RESYNC)
+    gst_iterator_resync (it);
   gst_iterator_free (it);
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
       elem_name);
 
   g_signal_emit (bin, gst_bin_signals[ELEMENT_ADDED], 0, element);
-  gst_child_proxy_child_added ((GObject *) bin, (GObject *) element, elem_name);
+  gst_child_proxy_child_added ((GstChildProxy *) bin, (GObject *) element,
+      elem_name);
+
+  gst_bin_do_deep_add_remove (bin, gst_bin_signals[DEEP_ELEMENT_ADDED],
+      "deep-element-added", element);
 
   g_free (elem_name);
 
@@ -1174,6 +1371,8 @@ adding_itself:
     GST_OBJECT_LOCK (bin);
     g_warning ("Cannot add bin '%s' to itself", GST_ELEMENT_NAME (bin));
     GST_OBJECT_UNLOCK (bin);
+    gst_object_ref_sink (element);
+    gst_object_unref (element);
     return FALSE;
   }
 duplicate_name:
@@ -1182,6 +1381,8 @@ duplicate_name:
         elem_name, GST_ELEMENT_NAME (bin));
     GST_OBJECT_UNLOCK (bin);
     g_free (elem_name);
+    gst_object_ref_sink (element);
+    gst_object_unref (element);
     return FALSE;
   }
 had_parent:
@@ -1194,9 +1395,108 @@ had_parent:
 }
 
 /**
+ * gst_bin_set_suppressed_flags:
+ * @bin: a #GstBin
+ * @flags: the #GstElementFlags to suppress
+ *
+ * Suppress the given flags on the bin. #GstElementFlags of a
+ * child element are propagated when it is added to the bin.
+ * When suppressed flags are set, those specified flags will
+ * not be propagated to the bin.
+ *
+ * MT safe.
+ *
+ * Since: 1.10
+ */
+void
+gst_bin_set_suppressed_flags (GstBin * bin, GstElementFlags flags)
+{
+  g_return_if_fail (GST_IS_BIN (bin));
+
+  GST_OBJECT_LOCK (bin);
+  bin->priv->suppressed_flags = bin->priv->suppressed_flags | flags;
+  GST_OBJECT_UNLOCK (bin);
+
+  GST_DEBUG_OBJECT (bin, "Set suppressed flags(0x%x) to bin '%s'", flags,
+      GST_ELEMENT_NAME (bin));
+}
+
+/**
+ * gst_bin_get_suppressed_flags:
+ * @bin: a #GstBin
+ *
+ * Return the suppressed flags of the bin.
+ *
+ * MT safe.
+ *
+ * Returns: the bin's suppressed #GstElementFlags.
+ *
+ * Since: 1.10
+ */
+GstElementFlags
+gst_bin_get_suppressed_flags (GstBin * bin)
+{
+  GstElementFlags res;
+
+  g_return_val_if_fail (GST_IS_BIN (bin), 0);
+
+  GST_OBJECT_LOCK (bin);
+  res = bin->priv->suppressed_flags;
+  GST_OBJECT_UNLOCK (bin);
+
+  return res;
+}
+
+/* signal vfunc, will be called when a new element was added */
+static void
+gst_bin_deep_element_added_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * child)
+{
+  GstBin *parent_bin;
+
+  parent_bin = (GstBin *) gst_object_get_parent (GST_OBJECT_CAST (bin));
+  if (parent_bin == NULL) {
+    GST_LOG_OBJECT (bin, "no parent, reached top-level");
+    return;
+  }
+
+  GST_LOG_OBJECT (parent_bin, "emitting deep-element-added for element "
+      "%" GST_PTR_FORMAT " which has just been added to %" GST_PTR_FORMAT,
+      child, sub_bin);
+
+  g_signal_emit (parent_bin, gst_bin_signals[DEEP_ELEMENT_ADDED], 0, sub_bin,
+      child);
+
+  gst_object_unref (parent_bin);
+}
+
+/* signal vfunc, will be called when an element was removed */
+static void
+gst_bin_deep_element_removed_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * child)
+{
+  GstBin *parent_bin;
+
+  parent_bin = (GstBin *) gst_object_get_parent (GST_OBJECT_CAST (bin));
+  if (parent_bin == NULL) {
+    GST_LOG_OBJECT (bin, "no parent, reached top-level");
+    return;
+  }
+
+  GST_LOG_OBJECT (parent_bin, "emitting deep-element-removed for element "
+      "%" GST_PTR_FORMAT " which has just been removed from %" GST_PTR_FORMAT,
+      sub_bin, child);
+
+  g_signal_emit (parent_bin, gst_bin_signals[DEEP_ELEMENT_REMOVED], 0, sub_bin,
+      child);
+
+  gst_object_unref (parent_bin);
+}
+
+/**
  * gst_bin_add:
  * @bin: a #GstBin
- * @element: (transfer full): the #GstElement to add
+ * @element: (transfer floating): the #GstElement to add
  *
  * Adds the given element to the bin.  Sets the element's parent, and thus
  * takes ownership of the element. An element can only be added to one bin.
@@ -1204,17 +1504,15 @@ had_parent:
  * If the element's pads are linked to other pads, the pads will be unlinked
  * before the element is added to the bin.
  *
- * <note>
- * When you add an element to an already-running pipeline, you will have to
- * take care to set the state of the newly-added element to the desired
- * state (usually PLAYING or PAUSED, same you set the pipeline to originally)
- * with gst_element_set_state(), or use gst_element_sync_state_with_parent().
- * The bin or pipeline will not take care of this for you.
- * </note>
+ * > When you add an element to an already-running pipeline, you will have to
+ * > take care to set the state of the newly-added element to the desired
+ * > state (usually PLAYING or PAUSED, same you set the pipeline to originally)
+ * > with gst_element_set_state(), or use gst_element_sync_state_with_parent().
+ * > The bin or pipeline will not take care of this for you.
  *
  * MT safe.
  *
- * Returns: TRUE if the element could be added, FALSE if
+ * Returns: %TRUE if the element could be added, %FALSE if
  * the bin does not want to accept the element.
  */
 gboolean
@@ -1225,6 +1523,7 @@ gst_bin_add (GstBin * bin, GstElement * element)
 
   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (GST_ELEMENT_CAST (bin) != element, FALSE);
 
   bclass = GST_BIN_GET_CLASS (bin);
 
@@ -1235,7 +1534,9 @@ gst_bin_add (GstBin * bin, GstElement * element)
       GST_STR_NULL (GST_ELEMENT_NAME (element)),
       GST_STR_NULL (GST_ELEMENT_NAME (bin)));
 
+  GST_TRACER_BIN_ADD_PRE (bin, element);
   result = bclass->add_element (bin, element);
+  GST_TRACER_BIN_ADD_POST (bin, element, result);
 
   return result;
 
@@ -1244,6 +1545,8 @@ no_function:
   {
     g_warning ("adding elements to bin '%s' is not supported",
         GST_ELEMENT_NAME (bin));
+    gst_object_ref_sink (element);
+    gst_object_unref (element);
     return FALSE;
   }
 }
@@ -1268,16 +1571,22 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
 
   GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
 
+  /* we obviously can't remove ourself from ourself */
+  if (G_UNLIKELY (element == GST_ELEMENT_CAST (bin)))
+    goto removing_itself;
+
+  GST_OBJECT_LOCK (bin);
+
   GST_OBJECT_LOCK (element);
-  /* Check if the element is already being removed and immediately
-   * return */
-  if (G_UNLIKELY (GST_OBJECT_FLAG_IS_SET (element,
-              GST_ELEMENT_FLAG_UNPARENTING)))
-    goto already_removing;
+  elem_name = g_strdup (GST_ELEMENT_NAME (element));
+
+  if (GST_OBJECT_PARENT (element) != GST_OBJECT_CAST (bin))
+    goto not_in_bin;
+
+  /* remove the parent ref */
+  GST_OBJECT_PARENT (element) = NULL;
 
-  GST_OBJECT_FLAG_SET (element, GST_ELEMENT_FLAG_UNPARENTING);
   /* grab element name so we can print it */
-  elem_name = g_strdup (GST_ELEMENT_NAME (element));
   is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
   is_source = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SOURCE);
   provides_clock =
@@ -1286,12 +1595,6 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
       GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
   GST_OBJECT_UNLOCK (element);
 
-  /* unlink all linked pads */
-  it = gst_element_iterate_pads (element);
-  gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads, NULL);
-  gst_iterator_free (it);
-
-  GST_OBJECT_LOCK (bin);
   found = FALSE;
   othersink = FALSE;
   othersource = FALSE;
@@ -1343,24 +1646,29 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
    * so that others can detect a change in the children list. */
   bin->numchildren--;
   bin->children_cookie++;
-  bin->priv->structure_cookie++;
+  if (!GST_BIN_IS_NO_RESYNC (bin))
+    bin->priv->structure_cookie++;
 
-  if (is_sink && !othersink) {
+  if (is_sink && !othersink
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
     /* we're not a sink anymore */
     GST_DEBUG_OBJECT (bin, "we removed the last sink");
     GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_SINK);
   }
-  if (is_source && !othersource) {
+  if (is_source && !othersource
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
     /* we're not a source anymore */
     GST_DEBUG_OBJECT (bin, "we removed the last source");
     GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_SOURCE);
   }
-  if (provides_clock && !otherprovider) {
+  if (provides_clock && !otherprovider
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
     /* we're not a clock provider anymore */
     GST_DEBUG_OBJECT (bin, "we removed the last clock provider");
     GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
   }
-  if (requires_clock && !otherrequirer) {
+  if (requires_clock && !otherrequirer
+      && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
     /* we're not a clock requirer anymore */
     GST_DEBUG_OBJECT (bin, "we removed the last clock requirer");
     GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
@@ -1454,7 +1762,7 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
     else
       ret = GST_STATE_CHANGE_SUCCESS;
 
-    bin_handle_async_done (bin, ret, FALSE, FALSE);
+    bin_handle_async_done (bin, ret, FALSE, GST_CLOCK_TIME_NONE);
   } else {
     GST_DEBUG_OBJECT (bin,
         "recalc state preroll: %d, other async: %d, this async %d",
@@ -1474,32 +1782,32 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
     GST_STATE_RETURN (bin) = ret;
   }
 no_state_recalc:
+  /* clear bus */
+  gst_element_set_bus (element, NULL);
+  /* Clear the clock we provided to the element */
+  gst_element_set_clock (element, NULL);
   GST_OBJECT_UNLOCK (bin);
 
   if (clock_message)
     gst_element_post_message (GST_ELEMENT_CAST (bin), clock_message);
 
+  /* unlink all linked pads */
+  it = gst_element_iterate_pads (element);
+  while (gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads,
+          NULL) == GST_ITERATOR_RESYNC)
+    gst_iterator_resync (it);
+  gst_iterator_free (it);
+
   GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
       elem_name);
 
-  gst_element_set_bus (element, NULL);
-
-  /* Clear the clock we provided to the element */
-  gst_element_set_clock (element, NULL);
-
-  /* we ref here because after the _unparent() the element can be disposed
-   * and we still need it to reset the UNPARENTING flag and fire a signal. */
-  gst_object_ref (element);
-  gst_object_unparent (GST_OBJECT_CAST (element));
-
-  GST_OBJECT_LOCK (element);
-  GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_FLAG_UNPARENTING);
-  GST_OBJECT_UNLOCK (element);
-
   g_signal_emit (bin, gst_bin_signals[ELEMENT_REMOVED], 0, element);
-  gst_child_proxy_child_removed ((GObject *) bin, (GObject *) element,
+  gst_child_proxy_child_removed ((GstChildProxy *) bin, (GObject *) element,
       elem_name);
 
+  gst_bin_do_deep_add_remove (bin, gst_bin_signals[DEEP_ELEMENT_REMOVED],
+      "deep-element-removed", element);
+
   g_free (elem_name);
   /* element is really out of our control now */
   gst_object_unref (element);
@@ -1507,18 +1815,20 @@ no_state_recalc:
   return TRUE;
 
   /* ERROR handling */
-not_in_bin:
+removing_itself:
   {
-    g_warning ("Element '%s' is not in bin '%s'", elem_name,
-        GST_ELEMENT_NAME (bin));
+    GST_OBJECT_LOCK (bin);
+    g_warning ("Cannot remove bin '%s' from itself", GST_ELEMENT_NAME (bin));
     GST_OBJECT_UNLOCK (bin);
-    g_free (elem_name);
     return FALSE;
   }
-already_removing:
+not_in_bin:
   {
-    GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "already removing child");
+    g_warning ("Element '%s' is not in bin '%s'", elem_name,
+        GST_ELEMENT_NAME (bin));
     GST_OBJECT_UNLOCK (element);
+    GST_OBJECT_UNLOCK (bin);
+    g_free (elem_name);
     return FALSE;
   }
 }
@@ -1540,7 +1850,7 @@ already_removing:
  *
  * MT safe.
  *
- * Returns: TRUE if the element could be removed, FALSE if
+ * Returns: %TRUE if the element could be removed, %FALSE if
  * the bin does not want to remove the element.
  */
 gboolean
@@ -1551,6 +1861,7 @@ gst_bin_remove (GstBin * bin, GstElement * element)
 
   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (GST_ELEMENT_CAST (bin) != element, FALSE);
 
   bclass = GST_BIN_GET_CLASS (bin);
 
@@ -1560,7 +1871,9 @@ gst_bin_remove (GstBin * bin, GstElement * element)
   GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s",
       GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
 
+  GST_TRACER_BIN_REMOVE_PRE (bin, element);
   result = bclass->remove_element (bin, element);
+  GST_TRACER_BIN_REMOVE_POST (bin, result);
 
   return result;
 
@@ -1579,12 +1892,10 @@ no_function:
  *
  * Gets an iterator for the elements in this bin.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement,
+ * or %NULL
  */
 GstIterator *
 gst_bin_iterate_elements (GstBin * bin)
@@ -1622,12 +1933,10 @@ iterate_child_recurse (GstIterator * it, const GValue * item)
  * Gets an iterator for the elements in this bin.
  * This iterator recurses into GstBin children.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement,
+ * or %NULL
  */
 GstIterator *
 gst_bin_iterate_recurse (GstBin * bin)
@@ -1682,12 +1991,10 @@ sink_iterator_filter (const GValue * vchild, GValue * vbin)
  * Gets an iterator for all elements in the bin that have the
  * #GST_ELEMENT_FLAG_SINK flag set.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement,
+ * or %NULL
  */
 GstIterator *
 gst_bin_iterate_sinks (GstBin * bin)
@@ -1745,12 +2052,10 @@ src_iterator_filter (const GValue * vchild, GValue * vbin)
  * Gets an iterator for all elements in the bin that have the
  * #GST_ELEMENT_FLAG_SOURCE flag set.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement,
+ * or %NULL
  */
 GstIterator *
 gst_bin_iterate_sources (GstBin * bin)
@@ -1816,14 +2121,24 @@ typedef struct _GstBinSortIterator
 } GstBinSortIterator;
 
 static void
+copy_to_queue (gpointer data, gpointer user_data)
+{
+  GstElement *element = data;
+  GQueue *queue = user_data;
+
+  gst_object_ref (element);
+  g_queue_push_tail (queue, element);
+}
+
+static void
 gst_bin_sort_iterator_copy (const GstBinSortIterator * it,
     GstBinSortIterator * copy)
 {
   GHashTableIter iter;
   gpointer key, value;
 
-  copy->queue = it->queue;
-  g_queue_foreach (&copy->queue, (GFunc) gst_object_ref, NULL);
+  g_queue_init (&copy->queue);
+  g_queue_foreach ((GQueue *) & it->queue, copy_to_queue, &copy->queue);
 
   copy->bin = gst_object_ref (it->bin);
   if (it->best)
@@ -2008,11 +2323,16 @@ find_element (GstElement * element, GstBinSortIterator * bit)
   if (bit->best == NULL || bit->best_deg > degree) {
     bit->best = element;
     bit->best_deg = degree;
+  } else if (bit->best_deg == degree
+      && GST_OBJECT_FLAG_IS_SET (bit->best, GST_ELEMENT_FLAG_SOURCE)
+      && !GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SOURCE)) {
+    /* If two elements have the same degree, we want to ensure we
+     * return non-source elements first. */
+    bit->best = element;
   }
 }
 
-/* get next element in iterator. the returned element has the
- * refcount increased */
+/* get next element in iterator. */
 static GstIteratorResult
 gst_bin_sort_iterator_next (GstBinSortIterator * bit, GValue * result)
 {
@@ -2125,12 +2445,10 @@ gst_bin_sort_iterator_new (GstBin * bin)
  * This function is used internally to perform the state changes
  * of the bin elements and for clock selection.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement,
+ * or %NULL
  */
 GstIterator *
 gst_bin_iterate_sorted (GstBin * bin)
@@ -2152,7 +2470,7 @@ gst_bin_element_set_state (GstBin * bin, GstElement * element,
     GstState next)
 {
   GstStateChangeReturn ret;
-  GstState pending, child_current, child_pending;
+  GstState child_current, child_pending;
   gboolean locked;
   GList *found;
 
@@ -2182,19 +2500,83 @@ gst_bin_element_set_state (GstBin * bin, GstElement * element,
     goto no_preroll;
   }
 
-  GST_OBJECT_LOCK (bin);
-  pending = GST_STATE_PENDING (bin);
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+      "current %s pending %s, desired next %s",
+      gst_element_state_get_name (child_current),
+      gst_element_state_get_name (child_pending),
+      gst_element_state_get_name (next));
+
+  /* always recurse into bins so that we can set the base time */
+  if (GST_IS_BIN (element))
+    goto do_state;
 
   /* Try not to change the state of elements that are already in the state we're
    * going to */
-  if (!(next == GST_STATE_PLAYING || child_pending != GST_STATE_VOID_PENDING ||
-          (child_pending == GST_STATE_VOID_PENDING &&
-              ((pending > child_current && next > child_current) ||
-                  (pending < child_current && next < child_current)))))
+  if (child_current == next && child_pending == GST_STATE_VOID_PENDING) {
+    /* child is already at the requested state, return previous return. Note that
+     * if the child has a pending state to next, we will still call the
+     * set_state function */
     goto unneeded;
+  } else if (next > current) {
+    /* upward state change */
+    if (child_pending == GST_STATE_VOID_PENDING) {
+      /* .. and the child is not busy doing anything */
+      if (child_current > next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    } else if (child_pending > child_current) {
+      /* .. and the child is busy going upwards */
+      if (child_current >= next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    } else {
+      /* .. and the child is busy going downwards */
+      if (child_current > next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    }
+  } else if (next < current) {
+    /* downward state change */
+    if (child_pending == GST_STATE_VOID_PENDING) {
+      /* .. and the child is not busy doing anything */
+      if (child_current < next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    } else if (child_pending < child_current) {
+      /* .. and the child is busy going downwards */
+      if (child_current <= next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    } else {
+      /* .. and the child is busy going upwards */
+      if (child_current < next) {
+        /* .. and is already past the requested state, assume it got there
+         * without error */
+        ret = GST_STATE_CHANGE_SUCCESS;
+        goto unneeded;
+      }
+    }
+  }
 
+do_state:
+  GST_OBJECT_LOCK (bin);
   /* the element was busy with an upwards async state change, we must wait for
-   * an ASYNC_DONE message before we attemp to change the state. */
+   * an ASYNC_DONE message before we attempt to change the state. */
   if ((found =
           find_message (bin, GST_OBJECT_CAST (element),
               GST_MESSAGE_ASYNC_START))) {
@@ -2234,37 +2616,39 @@ locked:
     GST_STATE_UNLOCK (element);
     return ret;
   }
-was_busy:
-  {
-    GST_DEBUG_OBJECT (element, "element was busy, delaying state change");
-    GST_OBJECT_UNLOCK (bin);
-    GST_STATE_UNLOCK (element);
-    return GST_STATE_CHANGE_ASYNC;
-  }
 unneeded:
   {
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "skipping transition from %s to  %s, since bin pending"
-        " is %s : last change state return follows",
+        "skipping transition from %s to  %s",
         gst_element_state_get_name (child_current),
-        gst_element_state_get_name (next),
-        gst_element_state_get_name (pending));
-    GST_OBJECT_UNLOCK (bin);
+        gst_element_state_get_name (next));
     GST_STATE_UNLOCK (element);
     return ret;
   }
+was_busy:
+  {
+    GST_DEBUG_OBJECT (element, "element was busy, delaying state change");
+    GST_OBJECT_UNLOCK (bin);
+    GST_STATE_UNLOCK (element);
+    return GST_STATE_CHANGE_ASYNC;
+  }
 }
 
 /* gst_iterator_fold functions for pads_activate
- * Stop the iterator if activating one pad failed. */
+ * Stop the iterator if activating one pad failed, but only if that pad
+ * has not been removed from the element. */
 static gboolean
 activate_pads (const GValue * vpad, GValue * ret, gboolean * active)
 {
   GstPad *pad = g_value_get_object (vpad);
   gboolean cont = TRUE;
 
-  if (!(cont = gst_pad_set_active (pad, *active)))
-    g_value_set_boolean (ret, FALSE);
+  if (!gst_pad_set_active (pad, *active)) {
+    if (GST_PAD_PARENT (pad) != NULL) {
+      cont = FALSE;
+      g_value_set_boolean (ret, FALSE);
+    }
+  }
 
   return cont;
 }
@@ -2313,7 +2697,7 @@ gst_bin_src_pads_activate (GstBin * bin, gboolean active)
   GstIterator *iter;
   gboolean fold_ok;
 
-  GST_DEBUG_OBJECT (bin, "src_pads_activate with active %d", active);
+  GST_DEBUG_OBJECT (bin, "%s pads", active ? "activate" : "deactivate");
 
   iter = gst_element_iterate_src_pads ((GstElement *) bin);
   fold_ok = iterator_activate_fold_with_resync (iter, &active);
@@ -2321,14 +2705,14 @@ gst_bin_src_pads_activate (GstBin * bin, gboolean active)
   if (G_UNLIKELY (!fold_ok))
     goto failed;
 
-  GST_DEBUG_OBJECT (bin, "pads_activate successful");
+  GST_DEBUG_OBJECT (bin, "pad %sactivation successful", active ? "" : "de");
 
   return TRUE;
 
   /* ERRORS */
 failed:
   {
-    GST_DEBUG_OBJECT (bin, "source pads_activate failed");
+    GST_DEBUG_OBJECT (bin, "pad %sactivation failed", active ? "" : "de");
     return FALSE;
   }
 }
@@ -2347,8 +2731,6 @@ failed:
  * calculations will be performed.
  *
  * Returns: %TRUE if the latency could be queried and reconfigured.
- *
- * Since: 0.10.22.
  */
 gboolean
 gst_bin_recalculate_latency (GstBin * bin)
@@ -2414,17 +2796,41 @@ gst_bin_do_latency_func (GstBin * bin)
   return res;
 }
 
-static void
-gst_bin_state_changed (GstElement * element, GstState oldstate,
-    GstState newstate, GstState pending)
+static gboolean
+gst_bin_post_message (GstElement * element, GstMessage * msg)
 {
   GstElementClass *pklass = (GstElementClass *) parent_class;
+  gboolean ret;
+
+  ret = pklass->post_message (element, gst_message_ref (msg));
+
+  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STATE_CHANGED &&
+      GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (element)) {
+    GstState newstate, pending;
+
+    gst_message_parse_state_changed (msg, NULL, &newstate, &pending);
+    if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING) {
+      GST_BIN_CAST (element)->priv->posted_playing = TRUE;
+      bin_do_eos (GST_BIN_CAST (element));
+    } else {
+      GST_BIN_CAST (element)->priv->posted_playing = FALSE;
+    }
+  }
+
+  gst_message_unref (msg);
+
+  return ret;
+}
 
-  if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING)
-    bin_do_eos (GST_BIN_CAST (element));
+static void
+reset_state (const GValue * data, gpointer user_data)
+{
+  GstElement *e = g_value_get_object (data);
+  GstState state = GPOINTER_TO_INT (user_data);
 
-  if (pklass->state_changed)
-    pklass->state_changed (element, oldstate, newstate, pending);
+  if (gst_element_set_state (e, state) == GST_STATE_CHANGE_FAILURE)
+    GST_WARNING_OBJECT (e, "Failed to switch back down to %s",
+        gst_element_state_get_name (state));
 }
 
 static GstStateChangeReturn
@@ -2453,14 +2859,18 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition)
   switch (next) {
     case GST_STATE_PLAYING:
     {
-      gboolean toplevel;
+      gboolean toplevel, asynchandling;
 
       GST_OBJECT_LOCK (bin);
       toplevel = BIN_IS_TOPLEVEL (bin);
+      asynchandling = bin->priv->asynchandling;
       GST_OBJECT_UNLOCK (bin);
 
       if (toplevel)
         gst_bin_recalculate_latency (bin);
+      if (asynchandling)
+        gst_element_post_message (element,
+            gst_message_new_latency (GST_OBJECT_CAST (element)));
       break;
     }
     case GST_STATE_PAUSED:
@@ -2469,6 +2879,8 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition)
       GST_DEBUG_OBJECT (element, "clearing EOS elements");
       bin_remove_messages (bin, NULL, GST_MESSAGE_EOS);
       bin->priv->posted_eos = FALSE;
+      if (current == GST_STATE_READY)
+        bin_remove_messages (bin, NULL, GST_MESSAGE_STREAM_START);
       GST_OBJECT_UNLOCK (bin);
       if (current == GST_STATE_READY)
         if (!(gst_bin_src_pads_activate (bin, TRUE)))
@@ -2480,14 +2892,21 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition)
       GST_DEBUG_OBJECT (element, "clearing all cached messages");
       bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
       GST_OBJECT_UNLOCK (bin);
-      if (current == GST_STATE_PAUSED)
-        if (!(gst_bin_src_pads_activate (bin, FALSE)))
-          goto activate_failure;
+      /* We might not have reached PAUSED yet due to async errors,
+       * make sure to always deactivate the pads nonetheless */
+      if (!(gst_bin_src_pads_activate (bin, FALSE)))
+        goto activate_failure;
       break;
     case GST_STATE_NULL:
-      if (current == GST_STATE_READY)
+      /* Clear message list on next NULL */
+      GST_OBJECT_LOCK (bin);
+      GST_DEBUG_OBJECT (element, "clearing all cached messages");
+      bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
+      GST_OBJECT_UNLOCK (bin);
+      if (current == GST_STATE_READY) {
         if (!(gst_bin_src_pads_activate (bin, FALSE)))
           goto activate_failure;
+      }
       break;
     default:
       break;
@@ -2559,7 +2978,7 @@ restart:
             if (parent == GST_OBJECT_CAST (element)) {
               /* element is still in bin, really error now */
               gst_object_unref (parent);
-              goto done;
+              goto undo;
             }
             /* child removed from bin, let the resync code redo the state
              * change */
@@ -2642,13 +3061,14 @@ done:
   /* check if all elements managed to commit their state already */
   if (!find_message (bin, NULL, GST_MESSAGE_ASYNC_START)) {
     /* nothing found, remove all old ASYNC_DONE messages. This can happen when
-     * all the elements commited their state while we were doing the state
+     * all the elements committed their state while we were doing the state
      * change. We will still return ASYNC for consistency but we commit the
      * state already so that a _get_state() will return immediately. */
     bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
 
-    GST_DEBUG_OBJECT (bin, "async elements commited");
-    bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE, FALSE);
+    GST_DEBUG_OBJECT (bin, "async elements committed");
+    bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE,
+        GST_CLOCK_TIME_NONE);
   }
 
 state_end:
@@ -2671,16 +3091,35 @@ activate_failure:
         "failure (de)activating src pads");
     return GST_STATE_CHANGE_FAILURE;
   }
+
+undo:
+  {
+    if (current < next) {
+      GstIterator *it = gst_bin_iterate_sorted (GST_BIN (element));
+      GstIteratorResult ret;
+
+      GST_DEBUG_OBJECT (element,
+          "Bin failed to change state, switching children back to %s",
+          gst_element_state_get_name (current));
+      while (TRUE) {
+        ret =
+            gst_iterator_foreach (it, &reset_state, GINT_TO_POINTER (current));
+        if (ret != GST_ITERATOR_RESYNC)
+          break;
+        gst_iterator_resync (it);
+      }
+      gst_iterator_free (it);
+    }
+    goto done;
+  }
 }
 
 /*
- * This function is a utility event handler for seek events.
- * It will send the event to all sinks or sources depending on the
- * event-direction.
+ * This function is a utility event handler. It will send the event to all sinks
+ * or sources and appropriate ghost pads depending on the event-direction.
  *
- * Applications are free to override this behaviour and
- * implement their own seek handler, but this will work for
- * pretty much all cases in practice.
+ * Applications are free to override this behaviour and implement their own
+ * handler, but this will work for pretty much all cases in practice.
  */
 static gboolean
 gst_bin_send_event (GstElement * element, GstEvent * event)
@@ -2705,12 +3144,53 @@ gst_bin_send_event (GstElement * element, GstEvent * event)
     switch (gst_iterator_next (iter, &data)) {
       case GST_ITERATOR_OK:
       {
-        GstElement *child = g_value_get_object (&data);;
+        GstElement *child = g_value_get_object (&data);
 
         gst_event_ref (event);
         res &= gst_element_send_event (child, event);
+
         GST_LOG_OBJECT (child, "After handling %s event: %d",
             GST_EVENT_TYPE_NAME (event), res);
+
+        g_value_reset (&data);
+        break;
+      }
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        res = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+      case GST_ITERATOR_ERROR:
+        g_assert_not_reached ();
+        break;
+    }
+  }
+  g_value_unset (&data);
+  gst_iterator_free (iter);
+
+  if (GST_EVENT_IS_DOWNSTREAM (event)) {
+    iter = gst_element_iterate_sink_pads (GST_ELEMENT (bin));
+    GST_DEBUG_OBJECT (bin, "Sending %s event to sink pads",
+        GST_EVENT_TYPE_NAME (event));
+  } else {
+    iter = gst_element_iterate_src_pads (GST_ELEMENT (bin));
+    GST_DEBUG_OBJECT (bin, "Sending %s event to src pads",
+        GST_EVENT_TYPE_NAME (event));
+  }
+
+  done = FALSE;
+  while (!done) {
+    switch (gst_iterator_next (iter, &data)) {
+      case GST_ITERATOR_OK:
+      {
+        GstPad *pad = g_value_get_object (&data);
+
+        gst_event_ref (event);
+        res &= gst_pad_send_event (pad, event);
+        GST_LOG_OBJECT (pad, "After handling %s event: %d",
+            GST_EVENT_TYPE_NAME (event), res);
         break;
       }
       case GST_ITERATOR_RESYNC:
@@ -2725,6 +3205,7 @@ gst_bin_send_event (GstElement * element, GstEvent * event)
         break;
     }
   }
+
   g_value_unset (&data);
   gst_iterator_free (iter);
   gst_event_unref (event);
@@ -2736,13 +3217,11 @@ gst_bin_send_event (GstElement * element, GstEvent * event)
  * their state, this function will attempt to bring the bin to the next state.
  */
 static void
-gst_bin_continue_func (BinContinueData * data)
+gst_bin_continue_func (GstBin * bin, BinContinueData * data)
 {
-  GstBin *bin;
   GstState current, next, pending;
   GstStateChange transition;
 
-  bin = data->bin;
   pending = data->pending;
 
   GST_DEBUG_OBJECT (bin, "waiting for state lock");
@@ -2776,8 +3255,6 @@ gst_bin_continue_func (BinContinueData * data)
   GST_STATE_UNLOCK (bin);
   GST_DEBUG_OBJECT (bin, "state continue done");
 
-  gst_object_unref (bin);
-  g_slice_free (BinContinueData, data);
   return;
 
 interrupted:
@@ -2785,8 +3262,6 @@ interrupted:
     GST_OBJECT_UNLOCK (bin);
     GST_STATE_UNLOCK (bin);
     GST_DEBUG_OBJECT (bin, "state continue aborted due to intervening change");
-    gst_object_unref (bin);
-    g_slice_free (BinContinueData, data);
     return;
   }
 }
@@ -2806,17 +3281,18 @@ bin_bus_handler (GstBus * bus, GstMessage * message, GstBin * bin)
 }
 
 static void
-bin_push_state_continue (BinContinueData * data)
+free_bin_continue_data (BinContinueData * data)
 {
-  GstBinClass *klass;
-  GstBin *bin;
-
-  /* ref was taken */
-  bin = data->bin;
-  klass = GST_BIN_GET_CLASS (bin);
+  g_slice_free (BinContinueData, data);
+}
 
+static void
+bin_push_state_continue (GstBin * bin, BinContinueData * data)
+{
   GST_DEBUG_OBJECT (bin, "pushing continue on thread pool");
-  g_thread_pool_push (klass->pool, data, NULL);
+  gst_element_call_async (GST_ELEMENT_CAST (bin),
+      (GstElementCallAsyncFunc) gst_bin_continue_func, data,
+      (GDestroyNotify) free_bin_continue_data);
 }
 
 /* an element started an async state change, if we were not busy with a state
@@ -2907,7 +3383,7 @@ was_no_preroll:
  */
 static void
 bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
-    gboolean flag_pending, gboolean reset_time)
+    gboolean flag_pending, GstClockTime running_time)
 {
   GstState current, pending, target;
   GstStateChangeReturn old_ret;
@@ -2935,7 +3411,7 @@ bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
   target = GST_STATE_TARGET (bin);
   pending = GST_STATE_PENDING (bin) = target;
 
-  amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin), reset_time);
+  amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin), running_time);
 
   old_state = GST_STATE (bin);
   /* this is the state we should go to next */
@@ -2979,8 +3455,6 @@ bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
 
     cont = g_slice_new (BinContinueData);
 
-    /* ref to the bin */
-    cont->bin = gst_object_ref (bin);
     /* cookie to detect concurrent state change */
     cont->cookie = GST_ELEMENT_CAST (bin)->state_cookie;
     /* pending target state */
@@ -3011,7 +3485,7 @@ bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
   if (cont) {
     /* toplevel, start continue state */
     GST_DEBUG_OBJECT (bin, "all async-done, starting state continue");
-    bin_push_state_continue (cont);
+    bin_push_state_continue (bin, cont);
   } else {
     GST_DEBUG_OBJECT (bin, "state change complete");
     GST_STATE_BROADCAST (bin);
@@ -3042,16 +3516,18 @@ nothing_pending:
 static void
 bin_do_eos (GstBin * bin)
 {
-  guint32 seqnum = 0;
+  guint32 seqnum = GST_SEQNUM_INVALID;
   gboolean eos;
 
   GST_OBJECT_LOCK (bin);
   /* If all sinks are EOS, we're in PLAYING and no state change is pending
-   * we forward the EOS message to the parent bin or application
+   * (or we're doing playing to playing and no one else will trigger posting
+   * EOS for us) we forward the EOS message to the parent bin or application
    */
   eos = GST_STATE (bin) == GST_STATE_PLAYING
-      && GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING
-      && is_eos (bin, &seqnum);
+      && (GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING ||
+      GST_STATE_PENDING (bin) == GST_STATE_PLAYING)
+      && bin->priv->posted_playing && is_eos (bin, &seqnum);
   GST_OBJECT_UNLOCK (bin);
 
   if (eos
@@ -3069,15 +3545,53 @@ bin_do_eos (GstBin * bin)
     GST_OBJECT_UNLOCK (bin);
 
     tmessage = gst_message_new_eos (GST_OBJECT_CAST (bin));
-    gst_message_set_seqnum (tmessage, seqnum);
+    if (seqnum != GST_SEQNUM_INVALID)
+      gst_message_set_seqnum (tmessage, seqnum);
     GST_DEBUG_OBJECT (bin,
         "all sinks posted EOS, posting seqnum #%" G_GUINT32_FORMAT, seqnum);
     gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
+  } else {
+    GST_LOG_OBJECT (bin, "Not forwarding EOS due to in progress state change, "
+        " or already posted, or waiting for more EOS");
+  }
+}
+
+static void
+bin_do_stream_start (GstBin * bin)
+{
+  guint32 seqnum = GST_SEQNUM_INVALID;
+  gboolean stream_start;
+  gboolean have_group_id = FALSE;
+  guint group_id = 0;
+
+  GST_OBJECT_LOCK (bin);
+  /* If all sinks are STREAM_START we forward the STREAM_START message
+   * to the parent bin or application
+   */
+  stream_start = is_stream_start (bin, &seqnum, &have_group_id, &group_id);
+  GST_OBJECT_UNLOCK (bin);
+
+  if (stream_start) {
+    GstMessage *tmessage;
+
+    GST_OBJECT_LOCK (bin);
+    bin_remove_messages (bin, NULL, GST_MESSAGE_STREAM_START);
+    GST_OBJECT_UNLOCK (bin);
+
+    tmessage = gst_message_new_stream_start (GST_OBJECT_CAST (bin));
+    if (seqnum != GST_SEQNUM_INVALID)
+      gst_message_set_seqnum (tmessage, seqnum);
+    if (have_group_id)
+      gst_message_set_group_id (tmessage, group_id);
+
+    GST_DEBUG_OBJECT (bin,
+        "all sinks posted STREAM_START, posting seqnum #%" G_GUINT32_FORMAT,
+        seqnum);
+    gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
   }
 }
 
-/* must be called with the object lock. This function releases the lock to post
- * the message. */
+/* must be called without the object lock as it posts messages */
 static void
 bin_do_message_forward (GstBin * bin, GstMessage * message)
 {
@@ -3086,7 +3600,6 @@ bin_do_message_forward (GstBin * bin, GstMessage * message)
 
     GST_DEBUG_OBJECT (bin, "pass %s message upward",
         GST_MESSAGE_TYPE_NAME (message));
-    GST_OBJECT_UNLOCK (bin);
 
     /* we need to convert these messages to element messages so that our parent
      * bin can easily ignore them and so that the application can easily
@@ -3096,8 +3609,44 @@ bin_do_message_forward (GstBin * bin, GstMessage * message)
             "message", GST_TYPE_MESSAGE, message, NULL));
 
     gst_element_post_message (GST_ELEMENT_CAST (bin), forwarded);
+  }
+}
 
-    GST_OBJECT_LOCK (bin);
+static void
+gst_bin_update_context (GstBin * bin, GstContext * context)
+{
+  GST_OBJECT_LOCK (bin);
+  gst_bin_update_context_unlocked (bin, context);
+  GST_OBJECT_UNLOCK (bin);
+}
+
+static void
+gst_bin_update_context_unlocked (GstBin * bin, GstContext * context)
+{
+  const gchar *context_type;
+  GList *l, **contexts;
+
+  contexts = &GST_ELEMENT_CAST (bin)->contexts;
+  context_type = gst_context_get_context_type (context);
+
+  GST_DEBUG_OBJECT (bin, "set context %p %" GST_PTR_FORMAT, context,
+      gst_context_get_structure (context));
+  for (l = *contexts; l; l = l->next) {
+    GstContext *tmp = l->data;
+    const gchar *tmp_type = gst_context_get_context_type (tmp);
+
+    /* Always store newest context but never replace
+     * a persistent one by a non-persistent one */
+    if (strcmp (context_type, tmp_type) == 0 &&
+        (gst_context_is_persistent (context) ||
+            !gst_context_is_persistent (tmp))) {
+      gst_context_replace ((GstContext **) & l->data, context);
+      break;
+    }
+  }
+  /* Not found? Add */
+  if (l == NULL) {
+    *contexts = g_list_prepend (*contexts, gst_context_ref (context));
   }
 }
 
@@ -3119,13 +3668,13 @@ bin_do_message_forward (GstBin * bin, GstMessage * message)
  *     with the segment_done message. If there are no more segment_start
  *     messages, post segment_done message upwards.
  *
- * GST_MESSAGE_DURATION: remove all previously cached duration messages.
+ * GST_MESSAGE_DURATION_CHANGED: clear any cached durations.
  *     Whenever someone performs a duration query on the bin, we store the
  *     result so we can answer it quicker the next time. Any element that
  *     changes its duration marks our cached values invalid.
  *     This message is also posted upwards. This is currently disabled
- *     because too many elements don't post DURATION messages when the
- *     duration changes.
+ *     because too many elements don't post DURATION_CHANGED messages when
+ *     the duration changes.
  *
  * GST_MESSAGE_CLOCK_LOST: This message is posted by an element when it
  *     can no longer provide a clock. The default bin behaviour is to
@@ -3186,8 +3735,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
     {
 
       /* collect all eos messages from the children */
-      GST_OBJECT_LOCK (bin);
       bin_do_message_forward (bin, message);
+      GST_OBJECT_LOCK (bin);
       /* ref message for future use  */
       bin_replace_message (bin, message, GST_MESSAGE_EOS);
       GST_OBJECT_UNLOCK (bin);
@@ -3195,6 +3744,18 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       bin_do_eos (bin);
       break;
     }
+    case GST_MESSAGE_STREAM_START:
+    {
+
+      /* collect all stream_start messages from the children */
+      GST_OBJECT_LOCK (bin);
+      /* ref message for future use  */
+      bin_replace_message (bin, message, GST_MESSAGE_STREAM_START);
+      GST_OBJECT_UNLOCK (bin);
+
+      bin_do_stream_start (bin);
+      break;
+    }
     case GST_MESSAGE_STATE_DIRTY:
     {
       GST_WARNING_OBJECT (bin, "received deprecated STATE_DIRTY message");
@@ -3211,8 +3772,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       gst_message_parse_segment_start (message, &format, &position);
       seqnum = gst_message_get_seqnum (message);
 
-      GST_OBJECT_LOCK (bin);
       bin_do_message_forward (bin, message);
+
+      GST_OBJECT_LOCK (bin);
       /* if this is the first segment-start, post to parent but not to the
        * application */
       if (!find_message (bin, NULL, GST_MESSAGE_SEGMENT_START) &&
@@ -3244,8 +3806,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       gst_message_parse_segment_done (message, &format, &position);
       seqnum = gst_message_get_seqnum (message);
 
-      GST_OBJECT_LOCK (bin);
       bin_do_message_forward (bin, message);
+
+      GST_OBJECT_LOCK (bin);
       bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START);
       /* if there are no more segment_start messages, everybody posted
        * a segment_done and we can post one on the bus. */
@@ -3270,20 +3833,22 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       }
       break;
     }
-    case GST_MESSAGE_DURATION:
+    case GST_MESSAGE_DURATION_CHANGED:
     {
-      /* remove all cached duration messages, next time somebody asks
+      /* FIXME: remove all cached durations, next time somebody asks
        * for duration, we will recalculate. */
+#if 0
       GST_OBJECT_LOCK (bin);
-      bin_remove_messages (bin, NULL, GST_MESSAGE_DURATION);
+      bin_remove_messages (bin, NULL, GST_MESSAGE_DURATION_CHANGED);
       GST_OBJECT_UNLOCK (bin);
+#endif
       goto forward;
     }
     case GST_MESSAGE_CLOCK_LOST:
     {
       GstClock **provided_clock_p;
       GstElement **clock_provider_p;
-      gboolean playing, provided, forward;
+      gboolean playing, toplevel, provided, forward;
       GstClock *clock;
 
       gst_message_parse_clock_lost (message, &clock);
@@ -3291,10 +3856,14 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       GST_OBJECT_LOCK (bin);
       bin->clock_dirty = TRUE;
       /* if we lost the clock that we provided, post to parent but
-       * only if we are PLAYING. */
+       * only if we are not a top-level bin or PLAYING.
+       * The reason for this is that applications should be able
+       * to PAUSE/PLAY if they receive this message without worrying
+       * about the state of the pipeline. */
       provided = (clock == bin->provided_clock);
       playing = (GST_STATE (bin) == GST_STATE_PLAYING);
-      forward = playing & provided;
+      toplevel = GST_OBJECT_PARENT (bin) == NULL;
+      forward = provided && (playing || !toplevel);
       if (provided) {
         GST_DEBUG_OBJECT (bin,
             "Lost clock %" GST_PTR_FORMAT " provided by %" GST_PTR_FORMAT,
@@ -3340,9 +3909,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
       GST_DEBUG_OBJECT (bin, "ASYNC_START message %p, %s", message,
           src ? GST_OBJECT_NAME (src) : "(NULL)");
 
-      GST_OBJECT_LOCK (bin);
       bin_do_message_forward (bin, message);
 
+      GST_OBJECT_LOCK (bin);
       /* we ignore the message if we are going to <= READY */
       if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY)
         goto ignore_start_message;
@@ -3365,17 +3934,17 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
     }
     case GST_MESSAGE_ASYNC_DONE:
     {
-      gboolean reset_time;
+      GstClockTime running_time;
       GstState target;
 
       GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
           src ? GST_OBJECT_NAME (src) : "(NULL)");
 
-      gst_message_parse_async_done (message, &reset_time);
+      gst_message_parse_async_done (message, &running_time);
 
-      GST_OBJECT_LOCK (bin);
       bin_do_message_forward (bin, message);
 
+      GST_OBJECT_LOCK (bin);
       /* ignore messages if we are shutting down */
       if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY)
         goto ignore_done_message;
@@ -3388,12 +3957,13 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
         /* nothing found, remove all old ASYNC_DONE messages */
         bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
 
-        GST_DEBUG_OBJECT (bin, "async elements commited");
+        GST_DEBUG_OBJECT (bin, "async elements committed");
         /* when we get an async done message when a state change was busy, we
          * need to set the pending_done flag so that at the end of the state
          * change we can see if we need to verify pending async elements, hence
          * the TRUE argument here. */
-        bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, TRUE, reset_time);
+        bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, TRUE,
+            running_time);
       } else {
         GST_DEBUG_OBJECT (bin, "there are more async elements pending");
       }
@@ -3428,7 +3998,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
          * need to resync by updating the structure_cookie. */
         bin_remove_messages (bin, GST_MESSAGE_SRC (message),
             GST_MESSAGE_STRUCTURE_CHANGE);
-        bin->priv->structure_cookie++;
+        if (!GST_BIN_IS_NO_RESYNC (bin))
+          bin->priv->structure_cookie++;
       }
       GST_OBJECT_UNLOCK (bin);
 
@@ -3437,6 +4008,52 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
 
       break;
     }
+    case GST_MESSAGE_NEED_CONTEXT:{
+      const gchar *context_type;
+      GList *l, *contexts;
+
+      gst_message_parse_context_type (message, &context_type);
+
+      if (src) {
+        GST_OBJECT_LOCK (bin);
+        contexts = GST_ELEMENT_CAST (bin)->contexts;
+        GST_LOG_OBJECT (bin, "got need-context message type: %s", context_type);
+        for (l = contexts; l; l = l->next) {
+          GstContext *tmp = l->data;
+          const gchar *tmp_type = gst_context_get_context_type (tmp);
+
+          if (strcmp (context_type, tmp_type) == 0) {
+            gst_element_set_context (GST_ELEMENT (src), l->data);
+            break;
+          }
+        }
+        GST_OBJECT_UNLOCK (bin);
+
+        /* Forward if we couldn't answer the message */
+        if (l == NULL) {
+          goto forward;
+        } else {
+          gst_message_unref (message);
+        }
+      } else {
+        g_warning
+            ("Got need-context message in bin '%s' without source element, dropping",
+            GST_ELEMENT_NAME (bin));
+        gst_message_unref (message);
+      }
+
+      break;
+    }
+    case GST_MESSAGE_HAVE_CONTEXT:{
+      GstContext *context;
+
+      gst_message_parse_have_context (message, &context);
+      gst_bin_update_context (bin, context);
+      gst_context_unref (context);
+
+      goto forward;
+      break;
+    }
     default:
       goto forward;
   }
@@ -3476,9 +4093,14 @@ bin_query_min_max_init (GstBin * bin, QueryFold * fold)
 static gboolean
 bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 {
-  GstElement *item = g_value_get_object (vitem);
+  gboolean res = FALSE;
+  GstObject *item = g_value_get_object (vitem);
+  if (GST_IS_PAD (item))
+    res = gst_pad_query (GST_PAD (item), fold->query);
+  else
+    res = gst_element_query (GST_ELEMENT (item), fold->query);
 
-  if (gst_element_query (item, fold->query)) {
+  if (res) {
     gint64 duration;
 
     g_value_set_boolean (ret, TRUE);
@@ -3487,6 +4109,12 @@ bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 
     GST_DEBUG_OBJECT (item, "got duration %" G_GINT64_FORMAT, duration);
 
+    if (duration == -1) {
+      /* duration query succeeded, but duration is unknown */
+      fold->max = -1;
+      return FALSE;
+    }
+
     if (duration > fold->max)
       fold->max = duration;
   }
@@ -3505,19 +4133,27 @@ bin_query_duration_done (GstBin * bin, QueryFold * fold)
 
   GST_DEBUG_OBJECT (bin, "max duration %" G_GINT64_FORMAT, fold->max);
 
+  /* FIXME: re-implement duration caching */
+#if 0
   /* and cache now */
   GST_OBJECT_LOCK (bin);
   bin->messages = g_list_prepend (bin->messages,
       gst_message_new_duration (GST_OBJECT_CAST (bin), format, fold->max));
   GST_OBJECT_UNLOCK (bin);
+#endif
 }
 
 static gboolean
 bin_query_position_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 {
-  GstElement *item = g_value_get_object (vitem);
+  gboolean res = FALSE;
+  GstObject *item = g_value_get_object (vitem);
+  if (GST_IS_PAD (item))
+    res = gst_pad_query (GST_PAD (item), fold->query);
+  else
+    res = gst_element_query (GST_ELEMENT (item), fold->query);
 
-  if (gst_element_query (item, fold->query)) {
+  if (res) {
     gint64 position;
 
     g_value_set_boolean (ret, TRUE);
@@ -3548,9 +4184,13 @@ bin_query_position_done (GstBin * bin, QueryFold * fold)
 static gboolean
 bin_query_latency_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 {
-  GstElement *item = g_value_get_object (vitem);
-
-  if (gst_element_query (item, fold->query)) {
+  gboolean res = FALSE;
+  GstObject *item = g_value_get_object (vitem);
+  if (GST_IS_PAD (item))
+    res = gst_pad_query (GST_PAD (item), fold->query);
+  else
+    res = gst_element_query (GST_ELEMENT (item), fold->query);
+  if (res) {
     GstClockTime min, max;
     gboolean live;
 
@@ -3569,7 +4209,7 @@ bin_query_latency_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
         fold->max = max;
       else if (max < fold->max)
         fold->max = max;
-      if (fold->live == FALSE)
+      if (!fold->live)
         fold->live = live;
     }
   } else {
@@ -3596,10 +4236,13 @@ bin_query_latency_done (GstBin * bin, QueryFold * fold)
 static gboolean
 bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 {
-  GstElement *item = g_value_get_object (vitem);
-  gboolean res;
-
-  if ((res = gst_element_query (item, fold->query))) {
+  gboolean res = FALSE;
+  GstObject *item = g_value_get_object (vitem);
+  if (GST_IS_PAD (item))
+    res = gst_pad_query (GST_PAD (item), fold->query);
+  else
+    res = gst_element_query (GST_ELEMENT (item), fold->query);
+  if (res) {
     g_value_set_boolean (ret, TRUE);
     GST_DEBUG_OBJECT (item, "answered query %p", fold->query);
   }
@@ -3608,21 +4251,65 @@ bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
   return !res;
 }
 
+/* Perform a query iteration for the given bin. The query is stored in
+ * QueryFold and iter should be either a GstPad iterator or a
+ * GstElement iterator. */
+static gboolean
+bin_iterate_fold (GstBin * bin, GstIterator * iter, QueryInitFunction fold_init,
+    QueryDoneFunction fold_done, GstIteratorFoldFunction fold_func,
+    QueryFold * fold_data, gboolean default_return)
+{
+  gboolean res = default_return;
+  GValue ret = { 0 };
+  /* set the result of the query to FALSE initially */
+  g_value_init (&ret, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&ret, res);
+
+  while (TRUE) {
+    GstIteratorResult ires;
+
+    ires = gst_iterator_fold (iter, fold_func, &ret, fold_data);
+
+    switch (ires) {
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        if (fold_init)
+          fold_init (bin, fold_data);
+        g_value_set_boolean (&ret, res);
+        break;
+      case GST_ITERATOR_OK:
+      case GST_ITERATOR_DONE:
+        res = g_value_get_boolean (&ret);
+        if (fold_done != NULL && res)
+          fold_done (bin, fold_data);
+        goto done;
+      default:
+        res = FALSE;
+        goto done;
+    }
+  }
+done:
+  return res;
+}
+
 static gboolean
 gst_bin_query (GstElement * element, GstQuery * query)
 {
   GstBin *bin = GST_BIN_CAST (element);
   GstIterator *iter;
+  gboolean default_return = FALSE;
   gboolean res = FALSE;
+  gboolean src_pads_query_result = FALSE;
   GstIteratorFoldFunction fold_func;
   QueryInitFunction fold_init = NULL;
   QueryDoneFunction fold_done = NULL;
   QueryFold fold_data;
-  GValue ret = { 0 };
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_DURATION:
     {
+      /* FIXME: implement duration caching in GstBin again */
+#if 0
       GList *cached;
       GstFormat qformat;
 
@@ -3633,7 +4320,7 @@ gst_bin_query (GstElement * element, GstQuery * query)
       for (cached = bin->messages; cached; cached = g_list_next (cached)) {
         GstMessage *message = (GstMessage *) cached->data;
 
-        if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION &&
+        if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION_CHANGED &&
             GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (bin)) {
           GstFormat format;
           gint64 duration;
@@ -3653,6 +4340,19 @@ gst_bin_query (GstElement * element, GstQuery * query)
         }
       }
       GST_OBJECT_UNLOCK (bin);
+#else
+#ifndef GST_DISABLE_GST_DEBUG
+      G_STMT_START {
+        /* Quieten this particularly annoying FIXME a bit: */
+        static gboolean printed_fixme = FALSE;
+        if (!printed_fixme) {
+          GST_FIXME ("implement duration caching in GstBin again");
+          printed_fixme = TRUE;
+        }
+      }
+      G_STMT_END;
+#endif
+#endif
       /* no cached value found, iterate and collect durations */
       fold_func = (GstIteratorFoldFunction) bin_query_duration_fold;
       fold_init = bin_query_min_max_init;
@@ -3671,7 +4371,7 @@ gst_bin_query (GstElement * element, GstQuery * query)
       fold_func = (GstIteratorFoldFunction) bin_query_latency_fold;
       fold_init = bin_query_min_max_init;
       fold_done = bin_query_latency_done;
-      res = TRUE;
+      default_return = TRUE;
       break;
     }
     default:
@@ -3681,10 +4381,6 @@ gst_bin_query (GstElement * element, GstQuery * query)
 
   fold_data.query = query;
 
-  /* set the result of the query to FALSE initially */
-  g_value_init (&ret, G_TYPE_BOOLEAN);
-  g_value_set_boolean (&ret, res);
-
   iter = gst_bin_iterate_sinks (bin);
   GST_DEBUG_OBJECT (bin, "Sending query %p (type %s) to sink children",
       query, GST_QUERY_TYPE_NAME (query));
@@ -3692,38 +4388,55 @@ gst_bin_query (GstElement * element, GstQuery * query)
   if (fold_init)
     fold_init (bin, &fold_data);
 
-  while (TRUE) {
-    GstIteratorResult ires;
+  res =
+      bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, &fold_data,
+      default_return);
+  gst_iterator_free (iter);
 
-    ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data);
+  if (!res) {
+    /* Query the source pads of the element */
+    iter = gst_element_iterate_src_pads (element);
+    src_pads_query_result =
+        bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func,
+        &fold_data, default_return);
+    gst_iterator_free (iter);
 
-    switch (ires) {
-      case GST_ITERATOR_RESYNC:
-        gst_iterator_resync (iter);
-        if (fold_init)
-          fold_init (bin, &fold_data);
-        g_value_set_boolean (&ret, res);
-        break;
-      case GST_ITERATOR_OK:
-      case GST_ITERATOR_DONE:
-        res = g_value_get_boolean (&ret);
-        if (fold_done != NULL && res)
-          fold_done (bin, &fold_data);
-        goto done;
-      default:
-        res = FALSE;
-        goto done;
-    }
+    if (src_pads_query_result)
+      res = TRUE;
   }
-done:
-  gst_iterator_free (iter);
 
-exit:
   GST_DEBUG_OBJECT (bin, "query %p result %d", query, res);
 
   return res;
 }
 
+static void
+set_context (const GValue * item, gpointer user_data)
+{
+  GstElement *element = g_value_get_object (item);
+
+  gst_element_set_context (element, user_data);
+}
+
+static void
+gst_bin_set_context (GstElement * element, GstContext * context)
+{
+  GstBin *bin;
+  GstIterator *children;
+
+  g_return_if_fail (GST_IS_BIN (element));
+
+  bin = GST_BIN (element);
+
+  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
+
+  children = gst_bin_iterate_elements (bin);
+  while (gst_iterator_foreach (children, set_context,
+          context) == GST_ITERATOR_RESYNC)
+    gst_iterator_resync (children);
+  gst_iterator_free (children);
+}
+
 static gint
 compare_name (const GValue * velement, const gchar * name)
 {
@@ -3745,11 +4458,12 @@ compare_name (const GValue * velement, const gchar * name)
  * Gets the element with the given name from a bin. This
  * function recurses into child bins.
  *
- * Returns NULL if no element with the given name is found in the bin.
+ * Returns %NULL if no element with the given name is found in the bin.
  *
  * MT safe.  Caller owns returned reference.
  *
- * Returns: (transfer full): the #GstElement with the given name, or NULL
+ * Returns: (transfer full) (nullable): the #GstElement with the given
+ * name, or %NULL
  */
 GstElement *
 gst_bin_get_by_name (GstBin * bin, const gchar * name)
@@ -3787,12 +4501,13 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name)
  * Gets the element with the given name from this bin. If the
  * element is not found, a recursion is performed on the parent bin.
  *
- * Returns NULL if:
+ * Returns %NULL if:
  * - no element with the given name is found in the bin
  *
  * MT safe.  Caller owns returned reference.
  *
- * Returns: (transfer full): the #GstElement with the given name, or NULL
+ * Returns: (transfer full) (nullable): the #GstElement with the given
+ * name, or %NULL
  */
 GstElement *
 gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
@@ -3847,7 +4562,8 @@ compare_interface (const GValue * velement, GValue * interface)
  *
  * MT safe.  Caller owns returned reference.
  *
- * Returns: (transfer full): A #GstElement inside the bin implementing the interface
+ * Returns: (transfer full) (nullable): A #GstElement inside the bin
+ * implementing the interface
  */
 GstElement *
 gst_bin_get_by_interface (GstBin * bin, GType iface)
@@ -3890,13 +4606,11 @@ gst_bin_get_by_interface (GstBin * bin, GType iface)
  * The function recurses inside child bins. The iterator will yield a series
  * of #GstElement that should be unreffed after use.
  *
- * Each element yielded by the iterator will have its refcount increased, so
- * unref after use.
- *
  * MT safe.  Caller owns returned value.
  *
- * Returns: (transfer full): a #GstIterator of #GstElement for all elements
- *          in the bin implementing the given interface, or NULL
+ * Returns: (transfer full) (nullable): a #GstIterator of #GstElement
+ *     for all elements in the bin implementing the given interface,
+ *     or %NULL
  */
 GstIterator *
 gst_bin_iterate_all_by_interface (GstBin * bin, GType iface)