Documentation updates.
[platform/upstream/gstreamer.git] / gst / gstelement.c
index 329bb13..355af89 100644 (file)
  * @short_description: Abstract base class for all pipeline elements
  * @see_also: #GstElementFactory, #GstPad
  *
- * GstElement is the base class needed to construct an element that can be
- * used in a GStreamer pipeline.  As such, it is not a functional entity, and
- * cannot do anything when placed in a pipeline.
+ * GstElement is the abstract base class needed to construct an element that 
+ * can be used in a GStreamer pipeline. Please refer to the plugin writers
+ * guide for more information on creating #GstElement subclasses.
  *
- * The name of a GstElement can be get with gst_element_get_name() and set with
+ * The name of a #GstElement can be get with gst_element_get_name() and set with
  * gst_element_set_name().  For speed, GST_ELEMENT_NAME() can be used in the
  * core when using the appropriate locking. Do not use this in plug-ins or
  * applications in order to retain ABI compatibility.
  *
  * All elements have pads (of the type #GstPad).  These pads link to pads on
- * other elements.  Buffers flow between these linked pads.
- * A GstElement has a GList of #GstPad structures for all their input (or sink)
+ * other elements.  #GstBuffer flow between these linked pads.
+ * A #GstElement has a #GList of #GstPad structures for all their input (or sink)
  * and output (or source) pads.
  * Core and plug-in writers can add and remove pads with gst_element_add_pad()
  * and gst_element_remove_pad().
  * You can get and set a #GstClock on an element using gst_element_get_clock()
  * and gst_element_set_clock().
  * Some elements can provide a clock for the pipeline if
- * gst_element_provides_clock() returns TRUE. With the gst_element_provide_clock()
- * method one can retrieve the clock provided by such an element.
+ * gst_element_provides_clock() returns %TRUE. With the
+ * gst_element_provide_clock() method one can retrieve the clock provided by
+ * such an element.
  * Not all elements require a clock to operate correctly. If
- * gst_element_requires_clock() returns TRUE, a clock should be set on the
+ * gst_element_requires_clock() returns %TRUE, a clock should be set on the
  * element with gst_element_set_clock().
  *
  * Note that clock slection and distribution is normally handled by the
  * toplevel #GstPipeline so the clock functions are only to be used in very
  * specific situations.
+ *
+ * Last reviewed on 2006-03-12 (0.10.5)
  */
 
 #include "gst_private.h"
@@ -119,10 +122,10 @@ static GstStateChangeReturn gst_element_change_state (GstElement * element,
     GstStateChange transition);
 static GstStateChangeReturn gst_element_change_state_func (GstElement * element,
     GstStateChange transition);
-static GstStateChangeReturn gst_element_change_state_func (GstElement * element,
-    GstStateChange transition);
 static GstStateChangeReturn gst_element_get_state_func (GstElement * element,
     GstState * state, GstState * pending, GstClockTime timeout);
+static GstStateChangeReturn gst_element_set_state_func (GstElement * element,
+    GstState state);
 static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
 
 #ifndef GST_DISABLE_LOADSAVE
@@ -139,7 +142,7 @@ gst_element_get_type (void)
 {
   static GType gst_element_type = 0;
 
-  if (!gst_element_type) {
+  if (G_UNLIKELY (gst_element_type == 0)) {
     static const GTypeInfo element_info = {
       sizeof (GstElementClass),
       gst_element_base_class_init,
@@ -213,6 +216,7 @@ gst_element_class_init (GstElementClass * klass)
 #endif
 
   klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func);
+  klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func);
   klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func);
   klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
   klass->numpadtemplates = 0;
@@ -331,7 +335,8 @@ gst_element_requires_clock (GstElement * element)
  * gst_element_provides_clock:
  * @element: a #GstElement to query
  *
- * Query if the element provides a clock.
+ * Query if the element provides a clock. A #GstClock provided by an
+ * element can be used as the global #GstClock for the pipeline. 
  *
  * Returns: TRUE if the element provides a clock
  *
@@ -385,23 +390,31 @@ gst_element_provide_clock (GstElement * element)
  * refcount on the clock. Any previously set clock on the object
  * is unreffed.
  *
+ * Returns: TRUE if the element accepted the clock.
+ *
  * MT safe.
  */
-void
+gboolean
 gst_element_set_clock (GstElement * element, GstClock * clock)
 {
   GstElementClass *oclass;
+  gboolean res = TRUE;
 
-  g_return_if_fail (GST_IS_ELEMENT (element));
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
+  GST_DEBUG_OBJECT (element, "setting clock %p", clock);
+
   if (oclass->set_clock)
-    oclass->set_clock (element, clock);
+    res = oclass->set_clock (element, clock);
 
-  GST_LOCK (element);
-  gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
-  GST_UNLOCK (element);
+  if (res) {
+    GST_OBJECT_LOCK (element);
+    gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
+    GST_OBJECT_UNLOCK (element);
+  }
+  return res;
 }
 
 /**
@@ -421,10 +434,10 @@ gst_element_get_clock (GstElement * element)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   if ((result = element->clock))
     gst_object_ref (result);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -434,7 +447,7 @@ gst_element_get_clock (GstElement * element)
  * @element: a #GstElement.
  * @time: the base time to set.
  *
- * Set the base time of an element. See @gst_element_get_base_time().
+ * Set the base time of an element. See gst_element_get_base_time().
  *
  * MT safe.
  */
@@ -443,9 +456,12 @@ gst_element_set_base_time (GstElement * element, GstClockTime time)
 {
   g_return_if_fail (GST_IS_ELEMENT (element));
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   element->base_time = time;
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
+
+  GST_DEBUG_OBJECT (element, "set base_time=%" GST_TIME_FORMAT,
+      GST_TIME_ARGS (time));
 }
 
 /**
@@ -468,9 +484,9 @@ gst_element_get_base_time (GstElement * element)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   result = element->base_time;
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -503,7 +519,7 @@ gst_element_is_indexable (GstElement * element)
  * @element: a #GstElement.
  * @index: a #GstIndex.
  *
- * Set the specified GstIndex on the element. The refcount of the index
+ * Set @index on the element. The refcount of the index
  * will be increased, any previously set index is unreffed.
  *
  * MT safe.
@@ -564,7 +580,8 @@ gst_element_get_index (GstElement * element)
  * The pad and the element should be unlocked when calling this function.
  *
  * Returns: TRUE if the pad could be added. This function can fail when
- * passing bad arguments or when a pad with the same name already existed.
+ * a pad with the same name already existed or the pad already had another
+ * parent.
  *
  * MT safe.
  */
@@ -577,14 +594,14 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
   /* locking pad to look at the name */
-  GST_LOCK (pad);
+  GST_OBJECT_LOCK (pad);
   pad_name = g_strdup (GST_PAD_NAME (pad));
   GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'",
       GST_STR_NULL (pad_name));
-  GST_UNLOCK (pad);
+  GST_OBJECT_UNLOCK (pad);
 
   /* then check to see if there's already a pad by that name here */
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads, pad_name)))
     goto name_exists;
 
@@ -611,7 +628,7 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
   element->pads = g_list_prepend (element->pads, pad);
   element->numpads++;
   element->pads_cookie++;
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   /* emit the NEW_PAD signal */
   g_signal_emit (G_OBJECT (element), gst_element_signals[NEW_PAD], 0, pad);
@@ -623,7 +640,7 @@ name_exists:
   {
     g_critical ("Padname %s is not unique in element %s, not adding",
         pad_name, GST_ELEMENT_NAME (element));
-    GST_UNLOCK (element);
+    GST_OBJECT_UNLOCK (element);
     g_free (pad_name);
     return FALSE;
   }
@@ -632,18 +649,18 @@ had_parent:
     g_critical
         ("Pad %s already has parent when trying to add to element %s",
         pad_name, GST_ELEMENT_NAME (element));
-    GST_UNLOCK (element);
+    GST_OBJECT_UNLOCK (element);
     g_free (pad_name);
     return FALSE;
   }
 no_direction:
   {
-    GST_LOCK (pad);
+    GST_OBJECT_LOCK (pad);
     g_critical
         ("Trying to add pad %s to element %s, but it has no direction",
         GST_OBJECT_NAME (pad), GST_ELEMENT_NAME (element));
-    GST_UNLOCK (pad);
-    GST_UNLOCK (element);
+    GST_OBJECT_UNLOCK (pad);
+    GST_OBJECT_UNLOCK (element);
     return FALSE;
   }
 }
@@ -657,8 +674,7 @@ no_direction:
  * referenced elsewhere.
  *
  * Returns: TRUE if the pad could be removed. Can return FALSE if the
- * pad is not belonging to the provided element or when wrong parameters
- * are passed to this function.
+ * pad is not belonging to the provided element.
  *
  * MT safe.
  */
@@ -671,13 +687,13 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
   /* locking pad to look at the name and parent */
-  GST_LOCK (pad);
+  GST_OBJECT_LOCK (pad);
   GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'",
       GST_STR_NULL (GST_PAD_NAME (pad)));
 
   if (G_UNLIKELY (GST_PAD_PARENT (pad) != element))
     goto not_our_pad;
-  GST_UNLOCK (pad);
+  GST_OBJECT_UNLOCK (pad);
 
   /* unlink */
   if ((peer = gst_pad_get_peer (pad))) {
@@ -692,7 +708,7 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
     gst_object_unref (peer);
   }
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   /* remove it from the list */
   switch (gst_pad_get_direction (pad)) {
     case GST_PAD_SRC:
@@ -710,7 +726,7 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
   element->pads = g_list_remove (element->pads, pad);
   element->numpads--;
   element->pads_cookie++;
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   g_signal_emit (G_OBJECT (element), gst_element_signals[PAD_REMOVED], 0, pad);
 
@@ -721,12 +737,12 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
 not_our_pad:
   {
     /* FIXME, locking order? */
-    GST_LOCK (element);
+    GST_OBJECT_LOCK (element);
     g_critical ("Padname %s:%s does not belong to element %s when removing",
         GST_ELEMENT_NAME (GST_PAD_PARENT (pad)), GST_PAD_NAME (pad),
         GST_ELEMENT_NAME (element));
-    GST_UNLOCK (element);
-    GST_UNLOCK (pad);
+    GST_OBJECT_UNLOCK (element);
+    GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
 }
@@ -737,7 +753,7 @@ not_our_pad:
  *
  * Use this function to signal that the element does not expect any more pads
  * to show up in the current pipeline. This function should be called whenever
- * pads have been added by the element itself. Elements with GST_PAD_SOMETIMES
+ * pads have been added by the element itself. Elements with #GST_PAD_SOMETIMES
  * pad templates use this in combination with autopluggers to figure out that
  * the element is done initializing its pads.
  *
@@ -756,9 +772,9 @@ pad_compare_name (GstPad * pad1, const gchar * name)
 {
   gint result;
 
-  GST_LOCK (pad1);
+  GST_OBJECT_LOCK (pad1);
   result = strcmp (GST_PAD_NAME (pad1), name);
-  GST_UNLOCK (pad1);
+  GST_OBJECT_UNLOCK (pad1);
 
   return result;
 }
@@ -785,7 +801,7 @@ gst_element_get_static_pad (GstElement * element, const gchar * name)
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
   g_return_val_if_fail (name != NULL, NULL);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   find =
       g_list_find_custom (element->pads, name, (GCompareFunc) pad_compare_name);
   if (find) {
@@ -800,7 +816,7 @@ gst_element_get_static_pad (GstElement * element, const gchar * name)
     GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s",
         GST_ELEMENT_NAME (element), name);
   }
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -936,16 +952,16 @@ gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
 {
   GstIterator *result;
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   gst_object_ref (element);
   result = gst_iterator_new_list (GST_TYPE_PAD,
-      GST_GET_LOCK (element),
+      GST_OBJECT_GET_LOCK (element),
       &element->pads_cookie,
       padlist,
       element,
       (GstIteratorItemFunction) iterate_pad,
       (GstIteratorDisposeFunction) gst_object_unref);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -1113,11 +1129,11 @@ gst_element_get_random_pad (GstElement * element, GstPadDirection dir)
 
   switch (dir) {
     case GST_PAD_SRC:
-      GST_LOCK (element);
+      GST_OBJECT_LOCK (element);
       pads = element->srcpads;
       break;
     case GST_PAD_SINK:
-      GST_LOCK (element);
+      GST_OBJECT_LOCK (element);
       pads = element->sinkpads;
       break;
     default:
@@ -1126,24 +1142,24 @@ gst_element_get_random_pad (GstElement * element, GstPadDirection dir)
   for (; pads; pads = g_list_next (pads)) {
     GstPad *pad = GST_PAD (pads->data);
 
-    GST_LOCK (pad);
+    GST_OBJECT_LOCK (pad);
     GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "checking pad %s:%s",
         GST_DEBUG_PAD_NAME (pad));
 
     if (GST_PAD_IS_LINKED (pad)) {
-      GST_UNLOCK (pad);
+      GST_OBJECT_UNLOCK (pad);
       result = pad;
       break;
     } else {
       GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked",
           GST_DEBUG_PAD_NAME (pad));
     }
-    GST_UNLOCK (pad);
+    GST_OBJECT_UNLOCK (pad);
   }
   if (result)
     gst_object_ref (result);
 
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 
@@ -1163,7 +1179,7 @@ wrong_direction:
  * Sends an event to an element. If the element doesn't
  * implement an event handler, the event will be forwarded
  * to a random sink pad. This function takes owership of the
- * provided event so you should _ref it if you want to reuse
+ * provided event so you should gst_event_ref() it if you want to reuse
  * the event after this call.
  *
  * Returns: TRUE if the event was handled.
@@ -1182,19 +1198,26 @@ gst_element_send_event (GstElement * element, GstEvent * event)
   oclass = GST_ELEMENT_GET_CLASS (element);
 
   if (oclass->send_event) {
+    GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send %s event on element %s",
+        GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
     result = oclass->send_event (element, event);
   } else {
-    GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK);
+    GstPad *pad = GST_EVENT_IS_DOWNSTREAM (event) ?
+        gst_element_get_random_pad (element, GST_PAD_SRC) :
+        gst_element_get_random_pad (element, GST_PAD_SINK);
 
     if (pad) {
       GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
-          "pushing event to random pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+          "pushing %s event to random %s pad %s:%s",
+          GST_EVENT_TYPE_NAME (event),
+          (GST_PAD_DIRECTION (pad) == GST_PAD_SRC ? "src" : "sink"),
+          GST_DEBUG_PAD_NAME (pad));
 
       result = gst_pad_push_event (pad, event);
       gst_object_unref (pad);
     } else {
-      GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
-          GST_ELEMENT_NAME (element));
+      GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
+          GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
     }
   }
   return result;
@@ -1282,7 +1305,7 @@ gst_element_get_query_types (GstElement * element)
  * @query: the #GstQuery.
  *
  * Performs a query on the given element. If the format is set
- * to GST_FORMAT_DEFAULT and this function returns TRUE, the
+ * to #GST_FORMAT_DEFAULT and this function returns TRUE, the
  * format pointer will hold the default format.
  * For element that don't implement a query handler, this function
  * forwards the query to a random usable sinkpad of this element.
@@ -1303,6 +1326,8 @@ gst_element_query (GstElement * element, GstQuery * query)
   oclass = GST_ELEMENT_GET_CLASS (element);
 
   if (oclass->query) {
+    GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s",
+        GST_ELEMENT_NAME (element));
     result = oclass->query (element, query);
   } else {
     GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SRC);
@@ -1350,14 +1375,14 @@ gst_element_post_message (GstElement * element, GstMessage * message)
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (message != NULL, FALSE);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   bus = element->bus;
 
   if (G_UNLIKELY (bus == NULL))
     goto no_bus;
 
   gst_object_ref (bus);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   result = gst_bus_post (bus, message);
   gst_object_unref (bus);
@@ -1367,7 +1392,7 @@ gst_element_post_message (GstElement * element, GstMessage * message)
 no_bus:
   {
     GST_DEBUG ("not posting message %p: no bus", message);
-    GST_UNLOCK (element);
+    GST_OBJECT_UNLOCK (element);
     gst_message_unref (message);
     return FALSE;
   }
@@ -1377,7 +1402,7 @@ no_bus:
  * _gst_element_error_printf:
  * @format: the printf-like format to use, or NULL
  *
- * This function is only used internally by the #gst_element_error macro.
+ * This function is only used internally by the gst_element_error() macro.
  *
  * Returns: a newly allocated string, or NULL if the format was NULL or ""
  *
@@ -1427,10 +1452,11 @@ void gst_element_message_full
   gchar *name;
   gchar *sent_text;
   gchar *sent_debug;
+  gboolean has_debug = TRUE;
   GstMessage *message = NULL;
 
   /* checks */
-  GST_DEBUG ("start");
+  GST_DEBUG_OBJECT (element, "start");
   g_return_if_fail (GST_IS_ELEMENT (element));
   g_return_if_fail ((type == GST_MESSAGE_ERROR) ||
       (type == GST_MESSAGE_WARNING));
@@ -1446,13 +1472,17 @@ void gst_element_message_full
   /* construct a sent_debug with extra information from source */
   if ((debug == NULL) || (debug[0] == 0)) {
     /* debug could have come from g_strdup_printf (""); */
-    sent_debug = NULL;
-  } else {
-    name = gst_object_get_path_string (GST_OBJECT (element));
-    sent_debug = g_strdup_printf ("%s(%d): %s: %s:\n%s",
-        file, line, function, name, debug ? debug : "");
-    g_free (name);
+    has_debug = FALSE;
   }
+
+  name = gst_object_get_path_string (GST_OBJECT (element));
+  if (has_debug)
+    sent_debug = g_strdup_printf ("%s(%d): %s (): %s:\n%s",
+        file, line, function, name, debug);
+  else
+    sent_debug = g_strdup_printf ("%s(%d): %s (): %s",
+        file, line, function, name);
+  g_free (name);
   g_free (debug);
 
   /* create gerror and post message */
@@ -1489,9 +1519,9 @@ void gst_element_message_full
  * This way you can leave currently unused elements inside bins. Just lock their
  * state before changing the state from #GST_STATE_NULL.
  *
- * Returns: TRUE, if the element's state is locked.
- *
  * MT safe.
+ *
+ * Returns: TRUE, if the element's state is locked.
  */
 gboolean
 gst_element_is_locked_state (GstElement * element)
@@ -1500,9 +1530,9 @@ gst_element_is_locked_state (GstElement * element)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -1515,10 +1545,10 @@ gst_element_is_locked_state (GstElement * element)
  * Locks the state of an element, so state changes of the parent don't affect
  * this element anymore.
  *
- * Returns: TRUE if the state was changed, FALSE if bad params were given or
- * the element was already in the correct state.
- *
  * MT safe.
+ *
+ * Returns: TRUE if the state was changed, FALSE if bad parameterss were given
+ * or the elements state-locking needed no change.
  */
 gboolean
 gst_element_set_locked_state (GstElement * element, gboolean locked_state)
@@ -1527,7 +1557,7 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
 
   if (G_UNLIKELY (old == locked_state))
@@ -1542,12 +1572,14 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
         GST_ELEMENT_NAME (element));
     GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
   }
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return TRUE;
 
 was_ok:
-  GST_UNLOCK (element);
+  GST_CAT_DEBUG (GST_CAT_STATES, "elements %s was in locked state %d",
+      GST_ELEMENT_NAME (element), old);
+  GST_OBJECT_UNLOCK (element);
 
   return FALSE;
 }
@@ -1574,10 +1606,10 @@ gst_element_sync_state_with_parent (GstElement * element)
     GstState parent_current, parent_pending;
     GstStateChangeReturn ret;
 
-    GST_STATE_LOCK (parent);
+    GST_OBJECT_LOCK (parent);
     parent_current = GST_STATE (parent);
     parent_pending = GST_STATE_PENDING (parent);
-    GST_STATE_UNLOCK (parent);
+    GST_OBJECT_UNLOCK (parent);
 
     GST_CAT_DEBUG (GST_CAT_STATES,
         "syncing state of element %s (%s) to %s (%s, %s)",
@@ -1613,7 +1645,7 @@ gst_element_get_state_func (GstElement * element,
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
 
-  GST_STATE_LOCK (element);
+  GST_OBJECT_LOCK (element);
   ret = GST_STATE_RETURN (element);
 
   /* we got an error, report immediatly */
@@ -1631,6 +1663,7 @@ gst_element_get_state_func (GstElement * element,
   old_pending = GST_STATE_PENDING (element);
   if (old_pending != GST_STATE_VOID_PENDING) {
     GTimeVal *timeval, abstimeout;
+    guint32 cookie;
 
     if (timeout != GST_CLOCK_TIME_NONE) {
       glong add = timeout / 1000;
@@ -1645,14 +1678,21 @@ gst_element_get_state_func (GstElement * element,
     } else {
       timeval = NULL;
     }
+    /* get cookie to dected state change during waiting */
+    cookie = element->state_cookie;
+
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
         "waiting for element to commit state");
+
     /* we have a pending state change, wait for it to complete */
     if (!GST_STATE_TIMED_WAIT (element, timeval)) {
       GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timed out");
       /* timeout triggered */
       ret = GST_STATE_CHANGE_ASYNC;
     } else {
+      if (cookie != element->state_cookie)
+        goto interrupted;
+
       /* could be success or failure */
       if (old_pending == GST_STATE (element)) {
         GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got success");
@@ -1679,10 +1719,23 @@ done:
       "state current: %s, pending: %s, result: %d",
       gst_element_state_get_name (GST_STATE (element)),
       gst_element_state_get_name (GST_STATE_PENDING (element)), ret);
-
-  GST_STATE_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return ret;
+
+interrupted:
+  {
+    if (state)
+      *state = GST_STATE_VOID_PENDING;
+    if (pending)
+      *pending = GST_STATE_VOID_PENDING;
+
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "get_state() interruped");
+
+    GST_OBJECT_UNLOCK (element);
+
+    return GST_STATE_CHANGE_FAILURE;
+  }
 }
 
 /**
@@ -1690,26 +1743,36 @@ done:
  * @element: a #GstElement to get the state of.
  * @state: a pointer to #GstState to hold the state. Can be NULL.
  * @pending: a pointer to #GstState to hold the pending state.
- *           Can be NULL.
+ *           Can be %NULL.
  * @timeout: a #GstClockTime to specify the timeout for an async
- *           state change or GST_CLOCK_TIME_NONE for infinite timeout.
+ *           state change or %GST_CLOCK_TIME_NONE for infinite timeout.
  *
  * Gets the state of the element.
  *
  * For elements that performed an ASYNC state change, as reported by
- * #gst_element_set_state(), this function will block up to the
+ * gst_element_set_state(), this function will block up to the
  * specified timeout value for the state change to complete.
  * If the element completes the state change or goes into
- * an error, this function returns immediatly with a return value of
- * GST_STATE_CHANGE_SUCCESS or GST_STATE_CHANGE_FAILURE respectively.
- *
- * For elements that did not return ASYNC, this function returns the
- * current and pending state immediatly.
- *
- * Returns: GST_STATE_CHANGE_SUCCESS if the element has no more pending state and
- *          the last state change succeeded, GST_STATE_CHANGE_ASYNC
- *          if the element is still performing a state change or
- *          GST_STATE_CHANGE_FAILURE if the last state change failed.
+ * an error, this function returns immediately with a return value of
+ * %GST_STATE_CHANGE_SUCCESS or %GST_STATE_CHANGE_FAILURE respectively.
+ *
+ * For elements that did not return %GST_STATE_CHANGE_ASYNC, this function
+ * returns the current and pending state immediately.
+ *
+ * This function returns %GST_STATE_CHANGE_NO_PREROLL if the element
+ * successfully changed its state but is not able to provide data yet.
+ * This mostly
+ * happens for live sources that only produce data in the PLAYING state.
+ * While the state change return is equivalent to %GST_STATE_CHANGE_SUCCESS, it
+ * is returned to the application to signal that some sink elements might not
+ * be able to complete their state change because an element is not producing
+ * data to complete the preroll. When setting the element to playing,
+ * the preroll will complete and playback will start.
+ *
+ * Returns: %GST_STATE_CHANGE_SUCCESS if the element has no more pending state
+ *          and the last state change succeeded, %GST_STATE_CHANGE_ASYNC if the
+ *          element is still performing a state change or
+ *          %GST_STATE_CHANGE_FAILURE if the last state change failed.
  *
  * MT safe.
  */
@@ -1747,123 +1810,148 @@ gst_element_abort_state (GstElement * element)
 {
   GstState pending;
 
+#ifndef GST_DISABLE_GST_DEBUG
+  GstState old_state;
+#endif
+
   g_return_if_fail (GST_IS_ELEMENT (element));
 
+  GST_OBJECT_LOCK (element);
   pending = GST_STATE_PENDING (element);
 
-  if (pending != GST_STATE_VOID_PENDING &&
-      GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
+  if (pending == GST_STATE_VOID_PENDING ||
+      GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+    goto nothing_aborted;
+
 #ifndef GST_DISABLE_GST_DEBUG
-    GstState old_state = GST_STATE (element);
+  old_state = GST_STATE (element);
+
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+      "aborting state from %s to %s", gst_element_state_get_name (old_state),
+      gst_element_state_get_name (pending));
 #endif
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "aborting state from %s to %s", gst_element_state_get_name (old_state),
-        gst_element_state_get_name (pending));
 
-    /* flag error */
-    GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE;
+  /* flag error */
+  GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE;
 
-    GST_STATE_BROADCAST (element);
+  GST_STATE_BROADCAST (element);
+  GST_OBJECT_UNLOCK (element);
+
+  return;
+
+nothing_aborted:
+  {
+    GST_OBJECT_UNLOCK (element);
+    return;
   }
 }
 
 /**
- * gst_element_commit_state:
- * @element: a #GstElement to commit the state of.
- *
- * Commit the state change of the element. This function is used
- * by elements that do asynchronous state changes.
- * The core will normally call this method automatically when an
- * element returned SUCCESS from the state change function.
- * Elements that return ASYNC from the change_state function should
- * eventually call this method from the streaming thread to signal
- * successfull state change completion.
- *
- * If after calling this method the element still has not reached
- * the pending state, the next state change is performed.
- *
- * This function can only be called with the STATE_LOCK held.
- *
- * Returns: The result of the commit state change.
- *
- * MT safe.
+ * gst_element_continue_state:   
+ * @element: a #GstElement to continue the state change of.      
+ * @ret: The previous state return value
+ *       
+ * Commit the state change of the element and proceed to the next 
+ * pending state if any. This function is used   
+ * by elements that do asynchronous state changes.       
+ * The core will normally call this method automatically when an         
+ * element returned SUCCESS from the state change function.      
+ * Elements that return ASYNC from the change_state function should      
+ * eventually call this method from the streaming thread to signal       
+ * successfull state change completion.          
+ *       
+ * If after calling this method the element still has not reached        
+ * the pending state, the next state change is performed.        
+ *       
+ * Returns: The result of the commit state change.       
+ *       
+ * MT safe.      
  */
 GstStateChangeReturn
-gst_element_commit_state (GstElement * element)
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
 {
   GstState pending;
-  GstStateChangeReturn ret;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
-
-  ret = GST_STATE_CHANGE_SUCCESS;
+  GstState old_ret, old_state, old_next;
+  GstState current, next;
+  GstMessage *message;
+  GstStateChange transition;
 
+  GST_OBJECT_LOCK (element);
+  old_ret = GST_STATE_RETURN (element);
+  GST_STATE_RETURN (element) = ret;
   pending = GST_STATE_PENDING (element);
 
   /* check if there is something to commit */
-  if (pending != GST_STATE_VOID_PENDING) {
-    GstState old_state;
-    GstState current, next;
-    GstMessage *message;
-
-    GST_LOCK (element);
-    GST_OBJECT_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
-    GST_UNLOCK (element);
-
-    old_state = GST_STATE (element);
-    /* this is the state we should go to next */
-    next = GST_STATE_NEXT (element);
-    /* update current state */
-    current = GST_STATE (element) = next;
-
-    /* see if we reached the final state */
-    if (pending == next) {
-      pending = GST_STATE_VOID_PENDING;
-      GST_STATE_PENDING (element) = pending;
-      GST_STATE_NEXT (element) = pending;
-      ret = GST_STATE_CHANGE_SUCCESS;
-    } else {
-      /* not there yet, will get there ASYNC */
-      ret = GST_STATE_CHANGE_ASYNC;
-    }
+  if (pending == GST_STATE_VOID_PENDING)
+    goto nothing_pending;
 
-    GST_STATE_RETURN (element) = ret;
+  old_state = GST_STATE (element);
+  /* this is the state we should go to next */
+  old_next = GST_STATE_NEXT (element);
+  /* update current state */
+  current = GST_STATE (element) = old_next;
 
-    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-        "committing state from %s to %s, pending %s",
-        gst_element_state_get_name (old_state),
-        gst_element_state_get_name (next),
-        gst_element_state_get_name (pending));
+  /* see if we reached the final state */
+  if (pending == current)
+    goto complete;
 
-    message = gst_message_new_state_changed (GST_OBJECT (element),
-        FALSE, old_state, next, pending);
-    gst_element_post_message (element, message);
+  next = GST_STATE_GET_NEXT (current, pending);
+  transition = GST_STATE_TRANSITION (current, next);
 
-    if (pending != GST_STATE_VOID_PENDING) {
-      GstStateChange transition;
+  GST_STATE_NEXT (element) = next;
+  /* mark busy */
+  GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+  GST_OBJECT_UNLOCK (element);
+
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+      "committing state from %s to %s, pending %s",
+      gst_element_state_get_name (old_state),
+      gst_element_state_get_name (old_next),
+      gst_element_state_get_name (pending));
+
+  message = gst_message_new_state_changed (GST_OBJECT (element),
+      old_state, old_next, pending);
+  gst_element_post_message (element, message);
 
-      /* calc new next state */
-      next = GST_STATE_GET_NEXT (current, pending);
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+      "continue state change %s to %s, final %s",
+      gst_element_state_get_name (current),
+      gst_element_state_get_name (next), gst_element_state_get_name (pending));
 
-      GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-          "continue state change %s to %s, final %s",
-          gst_element_state_get_name (current),
-          gst_element_state_get_name (next),
-          gst_element_state_get_name (pending));
+  ret = gst_element_change_state (element, transition);
 
-      /* create transition */
-      transition = GST_STATE_TRANSITION (current, next);
+  return ret;
 
-      /* perform next transition */
-      ret = gst_element_change_state (element, transition);
-    } else {
-      GST_STATE_BROADCAST (element);
+nothing_pending:
+  {
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
+    GST_OBJECT_UNLOCK (element);
+    return ret;
+  }
+complete:
+  {
+    GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+    GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "completed state change");
+    GST_OBJECT_UNLOCK (element);
+
+    /* don't post silly messages with the same state. This can happen
+     * when an element state is changed to what it already was. For bins
+     * this can be the result of a lost state, which we check with the
+     * previous return value. 
+     * We do signal the cond though as a _get_state() might be blocking 
+     * on it. */
+    if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
+      message = gst_message_new_state_changed (GST_OBJECT (element),
+          old_state, old_next, GST_STATE_VOID_PENDING);
+      gst_element_post_message (element, message);
     }
-    GST_LOCK (element);
-    GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
-    GST_UNLOCK (element);
+
+    GST_STATE_BROADCAST (element);
+
+    return ret;
   }
-  return ret;
 }
 
 /**
@@ -1872,7 +1960,7 @@ gst_element_commit_state (GstElement * element)
  *
  * Brings the element to the lost state. The current state of the
  * element is copied to the pending state so that any call to
- * #gst_element_get_state() will return ASYNC.
+ * gst_element_get_state() will return ASYNC.
  *
  * This is mostly used for elements that lost their preroll buffer
  * in the PAUSED state after a flush, they become PAUSED again
@@ -1887,25 +1975,40 @@ gst_element_commit_state (GstElement * element)
 void
 gst_element_lost_state (GstElement * element)
 {
+  GstState current_state;
+  GstMessage *message;
+
   g_return_if_fail (GST_IS_ELEMENT (element));
 
-  if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING &&
-      GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
-    GstState current_state;
-    GstMessage *message;
+  GST_OBJECT_LOCK (element);
+  if (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING ||
+      GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+    goto nothing_lost;
 
-    current_state = GST_STATE (element);
+  current_state = GST_STATE (element);
 
-    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-        "lost state of %s", gst_element_state_get_name (current_state));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+      "lost state of %s", gst_element_state_get_name (current_state));
 
-    GST_STATE_NEXT (element) = current_state;
-    GST_STATE_PENDING (element) = current_state;
-    GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+  GST_STATE_NEXT (element) = current_state;
+  GST_STATE_PENDING (element) = current_state;
+  GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+  GST_OBJECT_UNLOCK (element);
+
+  message = gst_message_new_state_changed (GST_OBJECT (element),
+      current_state, current_state, current_state);
+  gst_element_post_message (element, message);
+
+  /* and mark us dirty */
+  message = gst_message_new_state_dirty (GST_OBJECT (element));
+  gst_element_post_message (element, message);
+
+  return;
 
-    message = gst_message_new_state_changed (GST_OBJECT (element),
-        FALSE, current_state, current_state, current_state);
-    gst_element_post_message (element, message);
+nothing_lost:
+  {
+    GST_OBJECT_UNLOCK (element);
+    return;
   }
 }
 
@@ -1918,8 +2021,9 @@ gst_element_lost_state (GstElement * element)
  * requested state by going through all the intermediary states and calling
  * the class's state change function for each.
  *
- * This function can return GST_STATE_CHANGE_ASYNC, in which case the
- * element will perform the remainder of the state change asynchronously.
+ * This function can return #GST_STATE_CHANGE_ASYNC, in which case the
+ * element will perform the remainder of the state change asynchronously in
+ * another thread.
  * An application can use gst_element_get_state() to wait for the completion
  * of the state change or it can wait for a state change message on the bus.
  *
@@ -1930,6 +2034,26 @@ gst_element_lost_state (GstElement * element)
 GstStateChangeReturn
 gst_element_set_state (GstElement * element, GstState state)
 {
+  GstElementClass *oclass;
+  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
+
+  if (oclass->set_state)
+    result = (oclass->set_state) (element, state);
+
+  return result;
+}
+
+/*
+ * default set state function, calculates the next state based
+ * on current state and calls the change_state function 
+ */
+static GstStateChangeReturn
+gst_element_set_state_func (GstElement * element, GstState state)
+{
   GstState current, next, old_pending;
   GstStateChangeReturn ret;
   GstStateChange transition;
@@ -1940,12 +2064,12 @@ gst_element_set_state (GstElement * element, GstState state)
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
       gst_element_state_get_name (state));
 
+  /* state lock is taken to protect the set_state() and get_state() 
+   * procedures, it does not lock any variables. */
   GST_STATE_LOCK (element);
 
-  GST_LOCK (element);
-  GST_OBJECT_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
-  GST_UNLOCK (element);
-
+  /* now calculate how to get to the new state */
+  GST_OBJECT_LOCK (element);
   old_ret = GST_STATE_RETURN (element);
   /* previous state change returned an error, remove all pending
    * and next states */
@@ -1958,6 +2082,7 @@ gst_element_set_state (GstElement * element, GstState state)
   current = GST_STATE (element);
   next = GST_STATE_NEXT (element);
   old_pending = GST_STATE_PENDING (element);
+  element->state_cookie++;
 
   /* this is the (new) state we should go to */
   GST_STATE_PENDING (element) = state;
@@ -1986,38 +2111,46 @@ gst_element_set_state (GstElement * element, GstState state)
     }
   }
   next = GST_STATE_GET_NEXT (current, state);
+  /* now we store the next state */
+  GST_STATE_NEXT (element) = next;
+  /* mark busy, we need to check that there is actually a state change
+   * to be done else we could accidentally override SUCCESS/NO_PREROLL and
+   * the default element change_state function has no way to know what the
+   * old value was... could consider this a FIXME...*/
+  if (current != next)
+    GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+
+  transition = GST_STATE_TRANSITION (current, next);
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
       "%s: setting state from %s to %s",
       (next != state ? "intermediate" : "final"),
       gst_element_state_get_name (current), gst_element_state_get_name (next));
 
-  transition = GST_STATE_TRANSITION (current, next);
+  /* now signal any waiters, they will error since the cookie was increased */
+  GST_STATE_BROADCAST (element);
+
+  GST_OBJECT_UNLOCK (element);
 
   ret = gst_element_change_state (element, transition);
 
-  GST_LOCK (element);
-  GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
-  GST_UNLOCK (element);
   GST_STATE_UNLOCK (element);
 
-  GST_DEBUG_OBJECT (element, "returned %d", ret);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
 
   return ret;
 
 was_busy:
   {
     GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
-    GST_LOCK (element);
-    GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
-    GST_UNLOCK (element);
-    GST_STATE_UNLOCK (element);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+        "element was busy with async state change");
+    GST_OBJECT_UNLOCK (element);
 
-    GST_DEBUG_OBJECT (element, "element was busy with async state change");
+    GST_STATE_UNLOCK (element);
 
     return GST_STATE_CHANGE_ASYNC;
   }
-
 }
 
 /* with STATE_LOCK */
@@ -2035,9 +2168,6 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
   current = GST_STATE_TRANSITION_CURRENT (transition);
   next = GST_STATE_TRANSITION_NEXT (transition);
 
-  /* now we store the next state */
-  GST_STATE_NEXT (element) = next;
-
   /* call the state change function so it can set the state */
   if (oclass->change_state)
     ret = (oclass->change_state) (element, transition);
@@ -2058,7 +2188,7 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
       /* if we go upwards, we give the app a change to wait for
        * completion */
       if (current < next)
-        goto exit;
+        goto async;
 
       /* else we just continue the state change downwards */
       GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
@@ -2066,43 +2196,48 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
           gst_element_state_get_name (current),
           gst_element_state_get_name (next));
 
-      GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
-
-      ret = gst_element_commit_state (element);
+      ret = gst_element_continue_state (element, GST_STATE_CHANGE_SUCCESS);
       break;
     case GST_STATE_CHANGE_SUCCESS:
       GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
           "element changed state SUCCESS");
       /* we can commit the state now which will proceeed to
        * the next state */
-      ret = gst_element_commit_state (element);
+      ret = gst_element_continue_state (element, ret);
       break;
     case GST_STATE_CHANGE_NO_PREROLL:
       GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
           "element changed state NO_PREROLL");
       /* we can commit the state now which will proceeed to
        * the next state */
-      gst_element_commit_state (element);
-      ret = GST_STATE_CHANGE_NO_PREROLL;
+      ret = gst_element_continue_state (element, ret);
       break;
     default:
-      ret = GST_STATE_CHANGE_FAILURE;
       goto invalid_return;
   }
 
-exit:
-  GST_STATE_RETURN (element) = ret;
-
   GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
 
   return ret;
 
+async:
+  GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+      ret);
+
+  return ret;
+
   /* ERROR */
 invalid_return:
   {
-    GST_STATE_RETURN (element) = ret;
+    GST_OBJECT_LOCK (element);
     /* somebody added a GST_STATE_ and forgot to do stuff here ! */
-    g_critical ("unknown return value %d from a state change function", ret);
+    g_critical ("%s: unknown return value %d from a state change function",
+        GST_ELEMENT_NAME (element), ret);
+
+    ret = GST_STATE_CHANGE_FAILURE;
+    GST_STATE_RETURN (element) = ret;
+    GST_OBJECT_UNLOCK (element);
+
     return ret;
   }
 }
@@ -2163,7 +2298,10 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   g_value_init (&ret, G_TYPE_BOOLEAN);
   g_value_set_boolean (&ret, TRUE);
 
-  iter = gst_element_iterate_src_pads (element);
+  if (active)
+    iter = gst_element_iterate_src_pads (element);
+  else
+    iter = gst_element_iterate_sink_pads (element);
   fold_ok = iterator_fold_with_resync
       (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
   gst_iterator_free (iter);
@@ -2172,7 +2310,10 @@ gst_element_pads_activate (GstElement * element, gboolean active)
     return FALSE;
   }
 
-  iter = gst_element_iterate_sink_pads (element);
+  if (active)
+    iter = gst_element_iterate_sink_pads (element);
+  else
+    iter = gst_element_iterate_src_pads (element);
   fold_ok = iterator_fold_with_resync
       (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
   gst_iterator_free (iter);
@@ -2243,10 +2384,14 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition)
 
 was_ok:
   {
+    GST_OBJECT_LOCK (element);
+    result = GST_STATE_RETURN (element);
     GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
         "element is already in the %s state",
         gst_element_state_get_name (state));
-    return GST_STATE_RETURN (element);
+    GST_OBJECT_UNLOCK (element);
+
+    return result;
   }
 }
 
@@ -2284,8 +2429,10 @@ gst_element_dispose (GObject * object)
   }
   g_return_if_fail (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING);
 
+  GST_DEBUG ("removing %d pads", g_list_length (element->pads));
   /* first we break all our links with the outside */
-  while (element->pads) {
+  while (element->pads && element->pads->data) {
+    /* don't call _remove_pad with NULL */
     gst_element_remove_pad (element, GST_PAD_CAST (element->pads->data));
   }
   if (G_UNLIKELY (element->pads != 0)) {
@@ -2293,10 +2440,10 @@ gst_element_dispose (GObject * object)
         GST_STR_NULL (GST_OBJECT_NAME (object)));
   }
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   gst_object_replace ((GstObject **) & element->clock, NULL);
   gst_object_replace ((GstObject **) & element->bus, NULL);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
 
@@ -2467,10 +2614,10 @@ gst_element_set_bus_func (GstElement * element, GstBus * bus)
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting bus to %p", bus);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   gst_object_replace ((GstObject **) & GST_ELEMENT_BUS (element),
       GST_OBJECT_CAST (bus));
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 }
 
 /**
@@ -2513,10 +2660,10 @@ gst_element_get_bus (GstElement * element)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), result);
 
-  GST_LOCK (element);
+  GST_OBJECT_LOCK (element);
   result = GST_ELEMENT_BUS (element);
   gst_object_ref (result);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   GST_DEBUG_OBJECT (element, "got bus %" GST_PTR_FORMAT, result);