? gstreamer-structure-change-handling-new-message.patch
? gstreamer-structure-change-handling-parent-ok.patch
? gstreamer-structure-change-handling-r2-ported-pad-parent-save-pads.patch
? gstreamer-structure-change-handling-r2-ported-pad-parent.patch
? gstreamer-structure-change-handling-r2-ported.patch
? gstreamer-structure-change-handling-r3.patch
? log
? patched-tee.diff
? tee-events-04.patch
? tee-events-05.patch
? gst/one-bus-watcher.diff
? plugins/elements/gsttee.c.2
? plugins/elements/queue-min-latency.diff
? plugins/elements/tee-events-04-1.patch
? plugins/elements/tee-events-04.patch
? plugins/elements/tee-prefer-higher.patch
? plugins/elements/tee-prefer-ok.patch
Index: docs/gst/gstreamer-sections.txt
===================================================================
RCS file: /cvs/gstreamer/gstreamer/docs/gst/gstreamer-sections.txt,v
retrieving revision 1.342
diff -u -p -u -r1.342 gstreamer-sections.txt
--- docs/gst/gstreamer-sections.txt	20 Jun 2008 08:39:54 -0000	1.342
+++ docs/gst/gstreamer-sections.txt	25 Jul 2008 00:32:26 -0000
@@ -1024,6 +1024,7 @@ gst_iterator_result_get_type
 <TITLE>GstMessage</TITLE>
 GstMessage
 GstMessageType
+GstStructureChangeType
 GST_MESSAGE_SRC
 GST_MESSAGE_TIMESTAMP
 GST_MESSAGE_TYPE
@@ -1072,7 +1073,8 @@ gst_message_new_latency
 gst_message_new_async_start
 gst_message_parse_async_start
 gst_message_new_async_done
-
+gst_message_new_structure_change
+gst_message_parse_structure_change
 <SUBSECTION Standard>
 GstMessageClass
 GST_MESSAGE
@@ -1083,9 +1085,11 @@ GST_MESSAGE_CLASS
 GST_IS_MESSAGE_CLASS
 GST_MESSAGE_GET_CLASS
 GST_TYPE_MESSAGE_TYPE
+GST_TYPE_STRUCTURE_CHANGE_TYPE
 <SUBSECTION Private>
 gst_message_get_type
 gst_message_type_get_type
+gst_structure_change_type_get_type
 GST_MESSAGE_COND
 GST_MESSAGE_GET_LOCK
 GST_MESSAGE_LOCK
Index: gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.c,v
retrieving revision 1.380
diff -u -p -u -r1.380 gstbin.c
--- gst/gstbin.c	25 Jun 2008 11:50:06 -0000	1.380
+++ gst/gstbin.c	25 Jul 2008 00:32:27 -0000
@@ -195,6 +195,9 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug);
 struct _GstBinPrivate
 {
   gboolean asynchandling;
+  guint32 structure_cookie;
+
+  GPtrArray *structure_changes;
 };
 
 typedef struct
@@ -482,6 +485,7 @@ gst_bin_init (GstBin * bin)
 
   bin->priv = GST_BIN_GET_PRIVATE (bin);
   bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
+  bin->priv->structure_changes = g_ptr_array_new ();
 }
 
 static void
@@ -509,6 +513,14 @@ gst_bin_dispose (GObject * object)
         GST_STR_NULL (GST_OBJECT_NAME (object)));
   }
 
+  GST_OBJECT_LOCK (object);
+  if (G_UNLIKELY (bin->priv->structure_changes &&
+          bin->priv->structure_changes->len > 0))
+    GST_ERROR_OBJECT (bin, "Disposing of GstBin, but it still has ongoing"
+        " structure changes");
+  g_ptr_array_free (bin->priv->structure_changes, TRUE);
+  GST_OBJECT_UNLOCK (object);
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -915,6 +927,7 @@ gst_bin_add_func (GstBin * bin, GstEleme
   bin->children = g_list_prepend (bin->children, element);
   bin->numchildren++;
   bin->children_cookie++;
+  bin->priv->structure_cookie++;
 
   /* distribute the bus */
   gst_element_set_bus (element, bin->child_bus);
@@ -1124,6 +1137,7 @@ gst_bin_remove_func (GstBin * bin, GstEl
    * so that others can detect a change in the children list. */
   bin->numchildren--;
   bin->children_cookie++;
+  bin->priv->structure_cookie++;
 
   if (is_sink && !othersink) {
     /* we're not a sink anymore */
@@ -1638,6 +1652,31 @@ reset_degree (GstElement * element, GstB
   }
 }
 
+/*
+ * Returns TRUE if the pad is currently being linked or unlinked
+ *
+ * This should be called with the Gstbin Object lock held
+ */
+
+static gboolean
+is_being_changed (GstBinSortIterator * bit, GstPad *pad)
+{
+  gint i;
+  gboolean res = FALSE;
+
+  if (bit->bin->priv->structure_changes) {
+    for (i = 0; i < bit->bin->priv->structure_changes->len; i++) {
+      if (G_UNLIKELY (g_ptr_array_index (bit->bin->priv->structure_changes, i)
+              ==  pad)) {
+        res = TRUE;
+        break;
+      }
+    }
+  }
+
+  return res;
+}
+
 /* adjust the degree of all elements connected to the given
  * element. If a degree of an element drops to 0, it is
  * added to the queue of elements to schedule next.
@@ -1660,9 +1699,17 @@ update_degree (GstElement * element, Gst
     for (pads = element->sinkpads; pads; pads = g_list_next (pads)) {
       GstPad *peer;
 
+      if (G_UNLIKELY (is_being_changed (bit, GST_PAD_CAST (pads->data))))
+        continue;
+
       if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) {
         GstElement *peer_element;
 
+        if (G_UNLIKELY (is_being_changed (bit, peer))) {
+          gst_object_unref (peer);
+          continue;
+        }
+
         if ((peer_element = gst_pad_get_parent_element (peer))) {
           GST_OBJECT_LOCK (peer_element);
           /* check that we don't go outside of this bin */
@@ -1813,7 +1860,7 @@ gst_bin_sort_iterator_new (GstBin * bin)
       gst_iterator_new (sizeof (GstBinSortIterator),
       GST_TYPE_ELEMENT,
       GST_OBJECT_GET_LOCK (bin),
-      &bin->children_cookie,
+      &bin->priv->structure_cookie,
       (GstIteratorNextFunction) gst_bin_sort_iterator_next,
       (GstIteratorItemFunction) NULL,
       (GstIteratorResyncFunction) gst_bin_sort_iterator_resync,
@@ -2908,6 +2955,35 @@ gst_bin_handle_message_func (GstBin * bi
         break;
       }
     }
+    case GST_MESSAGE_STRUCTURE_CHANGE:
+    {
+      GstStructureChangeType change_type;
+      gboolean done;
+
+      gst_message_parse_structure_change (message, &change_type, &done);
+
+      GST_OBJECT_LOCK (bin);
+      if (!done)
+      {
+        if (G_LIKELY (bin->priv->structure_changes))
+          g_ptr_array_add (bin->priv->structure_changes,
+              GST_MESSAGE_SRC (message));
+      }
+      else
+      {
+        if (G_LIKELY (bin->priv->structure_changes))
+          if (G_UNLIKELY (!g_ptr_array_remove (bin->priv->structure_changes,
+                      GST_MESSAGE_SRC (message))))
+              GST_ERROR_OBJECT (bin, "Got end of structure change message"
+                  " but could not find local record");
+
+        bin->priv->structure_cookie++;
+      }
+      GST_OBJECT_UNLOCK (bin);
+
+      gst_message_unref (message);
+      break;
+    }
     default:
       goto forward;
   }
Index: gst/gstmessage.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.c,v
retrieving revision 1.68
diff -u -p -u -r1.68 gstmessage.c
--- gst/gstmessage.c	24 Jul 2008 17:38:43 -0000	1.68
+++ gst/gstmessage.c	25 Jul 2008 00:32:27 -0000
@@ -623,6 +623,37 @@ gst_message_new_new_clock (GstObject * s
 }
 
 /**
+ * gst_message_new_structure_change:
+ * @src: The object originating the message.
+ * @type: The change type.
+ * @done: Whether the structure change is done.
+ *
+ * Create a new structure change message. This message is posted when the
+ * structure of a pipeline is in the process of being changed, for example
+ * when pads are linked or unlinked.
+ *
+ * Returns: The new structure change message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.17
+ */
+GstMessage *
+gst_message_new_structure_change (GstObject * src, GstStructureChangeType type,
+    gboolean done)
+{
+  GstMessage * message;
+
+  message = gst_message_new_custom (GST_MESSAGE_STRUCTURE_CHANGE, src,
+      gst_structure_new ("GstMessageStructureChange",
+          "type", GST_TYPE_STRUCTURE_CHANGE_TYPE, type,
+          "done", G_TYPE_BOOLEAN, done,
+          NULL));
+
+  return message;
+}
+
+/**
  * gst_message_new_segment_start:
  * @src: The object originating the message.
  * @format: The format of the position being played
@@ -1072,6 +1103,31 @@ gst_message_parse_new_clock (GstMessage 
 }
 
 /**
+ * gst_message_parse_structure_change:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STRUCTURE_CHANGE.
+ * @type: A pointer to hold the change type
+ * @done: A pointer to hold whether the change has been completed
+ *
+ * Extracts the change type and completion status from the GstMessage.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.17
+ */
+void
+gst_message_parse_structure_change (GstMessage * message, GstStructureChangeType * type, gboolean * done)
+{
+  g_return_if_fail (GST_IS_MESSAGE (message));
+  g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STRUCTURE_CHANGE);
+  g_return_if_fail (type != NULL);
+
+  g_return_if_fail (gst_structure_get_enum (message->structure,
+      "type", GST_TYPE_STRUCTURE_CHANGE_TYPE, (gint *) type));
+  g_return_if_fail (gst_structure_get_boolean (message->structure,
+      "done", done));
+}
+
+/**
  * gst_message_parse_error:
  * @message: A valid #GstMessage of type GST_MESSAGE_ERROR.
  * @gerror: Location for the GError
Index: gst/gstmessage.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.h,v
retrieving revision 1.52
diff -u -p -u -r1.52 gstmessage.h
--- gst/gstmessage.h	17 May 2008 13:54:52 -0000	1.52
+++ gst/gstmessage.h	25 Jul 2008 00:32:27 -0000
@@ -58,8 +58,8 @@ typedef struct _GstMessageClass GstMessa
  *                          unusable. The pipeline will select a new clock on
  *                          the next PLAYING state change.
  * @GST_MESSAGE_NEW_CLOCK: a new clock was selected in the pipeline.
- * @GST_MESSAGE_STRUCTURE_CHANGE: the structure of the pipeline changed. Not
- * implemented yet.
+ * @GST_MESSAGE_STRUCTURE_CHANGE: the structure of the pipeline changed. This
+ * message is used internally and never forwarded to the application.
  * @GST_MESSAGE_STREAM_STATUS: status about a stream, emitted when it starts,
  *                             stops, errors, etc.. Not implemented yet.
  * @GST_MESSAGE_APPLICATION: message posted by the application, possibly
@@ -180,6 +180,20 @@ typedef enum
 #define GST_MESSAGE_SRC(message)	(GST_MESSAGE(message)->src)
 
 /**
+ * GstStructureChangeType:
+ * @GST_STRUCTURE_CHANGE_TYPE_PAD_LINK: Pad linking is starting or done.
+ * @GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK: Pad unlinking is starting or done.
+ *
+ * The type of a #GstMessageStructureChange.
+ *
+ * Since: 0.10.17
+ */
+typedef enum {
+  GST_STRUCTURE_CHANGE_TYPE_PAD_LINK   = 0,
+  GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK = 1
+} GstStructureChangeType;
+
+/**
  * GstMessage:
  * @mini_object: the parent structure
  * @type: the #GstMessageType of the message
@@ -353,6 +367,10 @@ void		gst_message_parse_async_start	(Gst
 /* ASYNC_DONE */
 GstMessage *	gst_message_new_async_done	(GstObject * src);
 
+/* STRUCTURE CHANGE */
+GstMessage *	gst_message_new_structure_change (GstObject * src, GstStructureChangeType type, gboolean done);
+void		gst_message_parse_structure_change	(GstMessage *message, GstStructureChangeType *type, gboolean *done);
+
 /* custom messages */
 GstMessage *	gst_message_new_custom		(GstMessageType type,
 						 GstObject    * src,
Index: gst/gstpad.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstpad.c,v
retrieving revision 1.574
diff -u -p -u -r1.574 gstpad.c
--- gst/gstpad.c	27 May 2008 10:59:38 -0000	1.574
+++ gst/gstpad.c	25 Jul 2008 00:32:27 -0000
@@ -1540,6 +1540,9 @@ gst_pad_set_bufferalloc_function (GstPad
 gboolean
 gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
 {
+  gboolean result = FALSE;
+  GstElement * parent = NULL;
+
   g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
   g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
 
@@ -1547,6 +1550,21 @@ gst_pad_unlink (GstPad * srcpad, GstPad 
       GST_DEBUG_PAD_NAME (srcpad), srcpad,
       GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
 
+  /* We need to notify the parent before taking any pad locks as the bin in
+   * question might be waiting for a lock on the pad while holding its lock
+   * that our message will try to take. */
+  parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad));
+  if (parent != NULL && GST_IS_ELEMENT (parent))
+  {
+    gst_element_post_message (parent,
+        gst_message_new_structure_change (GST_OBJECT (srcpad),
+            GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK, FALSE));
+  }
+  else {
+    gst_object_unref (parent);
+    parent = NULL;
+  }
+
   GST_OBJECT_LOCK (srcpad);
 
   if (G_UNLIKELY (GST_PAD_DIRECTION (srcpad) != GST_PAD_SRC))
@@ -1582,20 +1600,21 @@ gst_pad_unlink (GstPad * srcpad, GstPad 
   GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinked %s:%s and %s:%s",
       GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
 
-  return TRUE;
+  result = TRUE;
+  goto beach;
 
 not_srcpad:
   {
     g_critical ("pad %s is not a source pad", GST_PAD_NAME (srcpad));
     GST_OBJECT_UNLOCK (srcpad);
-    return FALSE;
+    goto beach;
   }
 not_sinkpad:
   {
     g_critical ("pad %s is not a sink pad", GST_PAD_NAME (sinkpad));
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
-    return FALSE;
+    goto beach;
   }
 not_linked_together:
   {
@@ -1603,8 +1622,17 @@ not_linked_together:
      * be made MT safe.*/
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
-    return FALSE;
+    goto beach;
+  }
+beach:
+  if (parent != NULL) {
+    gst_element_post_message (parent,
+        gst_message_new_structure_change (GST_OBJECT (srcpad),
+            GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK, TRUE));
+    gst_object_unref (parent);
   }
+
+  return result;
 }
 
 /**
@@ -1863,12 +1891,26 @@ GstPadLinkReturn
 gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
 {
   GstPadLinkReturn result;
+  GstElement * parent;
+
+  /* Notify the parent early. See gst_pad_unlink for details. */
+  parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad));
+  if (parent && GST_IS_ELEMENT (parent))
+  {
+    gst_element_post_message (parent,
+        gst_message_new_structure_change (GST_OBJECT (srcpad),
+            GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, FALSE));
+  }
+  else {
+    gst_object_unref (parent);
+    parent = NULL;
+  }
 
   /* prepare will also lock the two pads */
   result = gst_pad_link_prepare (srcpad, sinkpad);
 
   if (result != GST_PAD_LINK_OK)
-    goto prepare_failed;
+    goto beach;
 
   /* must set peers before calling the link function */
   GST_PAD_PEER (srcpad) = sinkpad;
@@ -1914,12 +1956,18 @@ gst_pad_link (GstPad * srcpad, GstPad * 
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
   }
-  return result;
 
-prepare_failed:
-  {
-    return result;
+  goto beach;
+
+beach:
+  if (parent) {
+    gst_element_post_message (parent,
+        gst_message_new_structure_change (GST_OBJECT (srcpad),
+            GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, TRUE));
+    gst_object_unref (parent);
   }
+
+  return result;
 }
 
 static void
Index: tests/check/gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/gst/gstbin.c,v
retrieving revision 1.59
diff -u -p -u -r1.59 gstbin.c
--- tests/check/gst/gstbin.c	21 May 2008 15:57:51 -0000	1.59
+++ tests/check/gst/gstbin.c	25 Jul 2008 00:32:28 -0000
@@ -891,6 +891,81 @@ GST_START_TEST (test_iterate_sorted)
 
 GST_END_TEST;
 
+static void
+test_link_structure_change_state_changed_sync_cb (GstBus *bus,
+    GstMessage *message, gpointer data)
+{
+  GstPipeline *pipeline = GST_PIPELINE (data);
+  GstElement *src, *identity, *sink;
+  GstState old, snew, pending;
+
+  sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
+  fail_unless (sink != NULL, "Could not get sink");
+
+  gst_message_parse_state_changed (message, &old, &snew, &pending);
+  if (message->src != GST_OBJECT (sink) || snew != GST_STATE_READY) {
+    gst_object_unref (sink);
+    return;
+  }
+
+  src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
+  fail_unless (src != NULL, "Could not get src");
+
+  identity = gst_bin_get_by_name (GST_BIN (pipeline), "identity");
+  fail_unless (identity != NULL, "Could not get identity");
+
+  /* link src to identity, the pipeline should detect the new link and
+   * resync the state change */
+  fail_unless (gst_element_link (src, identity) == TRUE);
+
+  gst_object_unref (src);
+  gst_object_unref (identity);
+  gst_object_unref (sink);
+}
+
+GST_START_TEST (test_link_structure_change)
+{
+  GstElement *src, *identity, *sink, *pipeline;
+  GstBus *bus;
+  GstState state;
+
+  pipeline = gst_pipeline_new (NULL);
+  fail_unless (pipeline != NULL, "Could not create pipeline");
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+  fail_unless (bus != NULL, "Could not get bus");
+
+  /* use the sync signal handler to link elements while the pipeline is still
+   * doing the state change */
+  gst_bus_set_sync_handler (bus, gst_bus_sync_signal_handler, pipeline);
+  g_object_connect (bus, "signal::sync-message::state-changed",
+      G_CALLBACK (test_link_structure_change_state_changed_sync_cb), pipeline, NULL);
+
+  src = gst_element_factory_make ("fakesrc", "src");
+  fail_if (src == NULL, "Could not create fakesrc");
+
+  identity = gst_element_factory_make ("identity", "identity");
+  fail_if (identity == NULL, "Could not create identity");
+
+  sink = gst_element_factory_make ("fakesink", "sink");
+  fail_if (sink == NULL, "Could not create fakesink1");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, identity, sink, NULL);
+
+  gst_element_set_state (pipeline, GST_STATE_READY);
+  gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+
+  /* the state change will be done on src only if the pipeline correctly resyncs
+   * after that filesrc has been linked to identity */
+  gst_element_get_state (src, &state, NULL, 0);
+  fail_unless_equals_int (state, GST_STATE_READY);
+
+  gst_object_unref (bus);
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
 static Suite *
 gst_bin_suite (void)
 {
@@ -911,6 +986,7 @@ gst_bin_suite (void)
   tcase_add_test (tc_chain, test_add_linked);
   tcase_add_test (tc_chain, test_add_self);
   tcase_add_test (tc_chain, test_iterate_sorted);
+  tcase_add_test (tc_chain, test_link_structure_change);
 
   return s;
 }
