bin: undo upward state changes on children when a child fails
[platform/upstream/gstreamer.git] / gst / gstbin.c
index d54d35e..cdd585a 100644 (file)
  * </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.
+ * 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>
@@ -370,7 +370,7 @@ gst_bin_class_init (GstBinClass * klass)
   /**
    * 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.
    */
@@ -537,7 +537,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.
  *
@@ -1279,7 +1279,7 @@ had_parent:
  *
  * 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
@@ -1592,7 +1592,7 @@ not_in_bin:
  *
  * 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
@@ -1633,7 +1633,8 @@ no_function:
  *
  * 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)
@@ -1673,7 +1674,8 @@ iterate_child_recurse (GstIterator * it, const GValue * item)
  *
  * 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)
@@ -1730,7 +1732,8 @@ sink_iterator_filter (const GValue * vchild, GValue * vbin)
  *
  * 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)
@@ -1790,7 +1793,8 @@ src_iterator_filter (const GValue * vchild, GValue * vbin)
  *
  * 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)
@@ -2166,7 +2170,8 @@ gst_bin_sort_iterator_new (GstBin * bin)
  *
  * 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)
@@ -2535,6 +2540,17 @@ gst_bin_post_message (GstElement * element, GstMessage * msg)
   return ret;
 }
 
+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 (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
 gst_bin_change_state_func (GstElement * element, GstStateChange transition)
 {
@@ -2691,7 +2707,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 */
@@ -2804,6 +2820,24 @@ 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));
+      do {
+        ret =
+            gst_iterator_foreach (it, &reset_state, GINT_TO_POINTER (current));
+      } while (ret == GST_ITERATOR_RESYNC);
+      gst_iterator_free (it);
+    }
+    goto done;
+  }
 }
 
 /*
@@ -3883,7 +3917,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 {
@@ -3931,7 +3965,7 @@ bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
 static gboolean
 bin_iterate_fold (GstBin * bin, GstIterator * iter, QueryInitFunction fold_init,
     QueryDoneFunction fold_done, GstIteratorFoldFunction fold_func,
-    QueryFold fold_data, gboolean default_return)
+    QueryFold fold_data, gboolean default_return)
 {
   gboolean res = default_return;
   GValue ret = { 0 };
@@ -3942,20 +3976,20 @@ bin_iterate_fold (GstBin * bin, GstIterator * iter, QueryInitFunction fold_init,
   while (TRUE) {
     GstIteratorResult ires;
 
-    ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data);
+    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);
+          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);
+          fold_done (bin, fold_data);
         goto done;
       default:
         res = FALSE;
@@ -4053,7 +4087,7 @@ gst_bin_query (GstElement * element, GstQuery * query)
     fold_init (bin, &fold_data);
 
   res =
-      bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, fold_data,
+      bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, &fold_data,
       default_return);
   gst_iterator_free (iter);
 
@@ -4062,7 +4096,7 @@ gst_bin_query (GstElement * element, GstQuery * query)
     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);
+        &fold_data, default_return);
     gst_iterator_free (iter);
 
     if (src_pads_query_result)
@@ -4122,11 +4156,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)
@@ -4164,12 +4199,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)
@@ -4269,8 +4305,9 @@ gst_bin_get_by_interface (GstBin * bin, GType iface)
  *
  * 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)