gstfunnel: avoid access of freed pad
[platform/upstream/gstreamer.git] / gst / gstbin.c
index 665259e..8e31a48 100644 (file)
@@ -221,7 +221,7 @@ static void gst_bin_state_changed (GstElement * element, GstState oldstate,
 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_do_eos (GstBin * bin);
@@ -1134,7 +1134,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;
@@ -1162,7 +1162,8 @@ no_state_recalc:
       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);
 
   g_free (elem_name);
 
@@ -1268,16 +1269,18 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
 
   GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
 
+  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 +1289,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;
@@ -1454,7 +1451,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,30 +1471,25 @@ 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);
+  gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads, NULL);
+  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);
 
   g_free (elem_name);
@@ -1511,16 +1503,11 @@ not_in_bin:
   {
     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;
   }
-already_removing:
-  {
-    GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "already removing child");
-    GST_OBJECT_UNLOCK (element);
-    return FALSE;
-  }
 }
 
 /**
@@ -2152,7 +2139,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,17 +2169,81 @@ 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. */
   if ((found =
@@ -2234,25 +2285,22 @@ 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
@@ -2648,7 +2696,8 @@ done:
     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);
+    bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE,
+        GST_CLOCK_TIME_NONE);
   }
 
 state_end:
@@ -2907,7 +2956,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 +2984,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 */
@@ -3365,13 +3414,13 @@ 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);
@@ -3393,7 +3442,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
          * 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");
       }