docs/manual/: Some more minor docs additions and updates.
[platform/upstream/gstreamer.git] / gst / gstelement.c
index a19bf58..c2ca06e 100644 (file)
@@ -19,6 +19,7 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
 /**
  * SECTION:gstelement
  * @short_description: Abstract base class for all pipeline elements
  * 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.
- * 
- * 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 
+ *
+ * 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)
- * and output (or source) pads.  
+ * 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().
  *
  * A pad of an element can be retrieved by name with gst_element_get_pad().
  * An iterator of all pads can be retrieved with gst_element_iterate_pads().
- * 
+ *
  * Elements can be linked through their pads.
- * If the link is straightforward, use the gst_element_link() 
- * convenience function to link two elements, or gst_element_link_many() 
+ * If the link is straightforward, use the gst_element_link()
+ * convenience function to link two elements, or gst_element_link_many()
  * for more elements in a row.
  * Use gst_element_link_filtered() to link two elements constrained by
  * a specified set of #GstCaps.
- * For finer control, use gst_element_link_pads() and 
- * gst_element_link_pads_filtered() to specify the pads to link on 
+ * For finer control, use gst_element_link_pads() and
+ * gst_element_link_pads_filtered() to specify the pads to link on
  * each element by name.
- * 
+ *
  * Each element has a state (see #GstState).  You can get and set the state
- * of an element with gst_element_get_state() and gst_element_set_state().  
- * You can wait for an element to change it's state with gst_element_wait_state_change().
- * To get a string representation of a #GstState, use 
+ * of an element with gst_element_get_state() and gst_element_set_state().
+ * To get a string representation of a #GstState, use
  * gst_element_state_get_name().
- * 
+ *
  * 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  
+ * 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.
- * Not all elements require a clock to operate correctly. If 
- * 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.
+ * Not all elements require a clock to operate correctly. If
+ * 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 2005-11-23 (0.9.5)
  */
+
 #include "gst_private.h"
 #include <glib.h>
 #include <stdarg.h>
@@ -87,7 +92,6 @@
 /* Element signals and args */
 enum
 {
-  STATE_CHANGE,
   NEW_PAD,
   PAD_REMOVED,
   NO_MORE_PADS,
@@ -115,8 +119,12 @@ static void gst_element_finalize (GObject * object);
 
 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_get_state_func (GstElement * element,
-    GstState * state, GstState * pending, GTimeVal * timeout);
+    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
@@ -125,15 +133,15 @@ static xmlNodePtr gst_element_save_thyself (GstObject * object,
 static void gst_element_restore_thyself (GstObject * parent, xmlNodePtr self);
 #endif
 
-GType _gst_element_type = 0;
-
 static GstObjectClass *parent_class = NULL;
 static guint gst_element_signals[LAST_SIGNAL] = { 0 };
 
 GType
 gst_element_get_type (void)
 {
-  if (!_gst_element_type) {
+  static GType gst_element_type = 0;
+
+  if (!gst_element_type) {
     static const GTypeInfo element_info = {
       sizeof (GstElementClass),
       gst_element_base_class_init,
@@ -147,10 +155,10 @@ gst_element_get_type (void)
       NULL
     };
 
-    _gst_element_type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
+    gst_element_type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
         &element_info, G_TYPE_FLAG_ABSTRACT);
   }
-  return _gst_element_type;
+  return gst_element_type;
 }
 
 static void
@@ -206,7 +214,8 @@ gst_element_class_init (GstElementClass * klass)
       GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
 #endif
 
-  klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state);
+  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;
@@ -236,9 +245,13 @@ gst_element_base_class_finalize (gpointer g_class)
 static void
 gst_element_init (GstElement * element)
 {
-  element->current_state = GST_STATE_NULL;
-  element->pending_state = GST_STATE_VOID_PENDING;
-  element->state_lock = g_mutex_new ();
+  GST_STATE (element) = GST_STATE_NULL;
+  GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+  GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+  GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
+
+  element->state_lock = g_new0 (GStaticRecMutex, 1);
+  g_static_rec_mutex_init (element->state_lock);
   element->state_cond = g_cond_new ();
 }
 
@@ -321,7 +334,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
  *
@@ -375,23 +389,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;
 }
 
 /**
@@ -411,10 +433,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;
 }
@@ -433,9 +455,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));
 }
 
 /**
@@ -458,9 +483,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;
 }
@@ -493,7 +518,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.
@@ -549,12 +574,13 @@ gst_element_get_index (GstElement * element)
  *
  * Pads are not automatically activated so elements should perform the needed
  * steps to activate the pad in case this pad is added in the PAUSED or PLAYING
- * state. 
+ * state.
  *
  * 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.
  */
@@ -567,14 +593,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;
 
@@ -601,7 +627,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);
@@ -613,7 +639,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;
   }
@@ -622,18 +648,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;
   }
 }
@@ -647,8 +673,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.
  */
@@ -661,13 +686,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))) {
@@ -682,7 +707,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:
@@ -700,7 +725,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);
 
@@ -711,12 +736,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;
   }
 }
@@ -727,7 +752,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.
  *
@@ -746,9 +771,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;
 }
@@ -775,7 +800,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) {
@@ -790,7 +815,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;
 }
@@ -921,6 +946,25 @@ iterate_pad (GstIterator * it, GstPad * pad)
   return GST_ITERATOR_ITEM_PASS;
 }
 
+static GstIterator *
+gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
+{
+  GstIterator *result;
+
+  GST_OBJECT_LOCK (element);
+  gst_object_ref (element);
+  result = gst_iterator_new_list (GST_TYPE_PAD,
+      GST_OBJECT_GET_LOCK (element),
+      &element->pads_cookie,
+      padlist,
+      element,
+      (GstIteratorItemFunction) iterate_pad,
+      (GstIteratorDisposeFunction) gst_object_unref);
+  GST_OBJECT_UNLOCK (element);
+
+  return result;
+}
+
 /**
  * gst_element_iterate_pads:
  * @element: a #GstElement to iterate pads of.
@@ -935,43 +979,16 @@ iterate_pad (GstIterator * it, GstPad * pad)
 GstIterator *
 gst_element_iterate_pads (GstElement * element)
 {
-  GstIterator *result;
-
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
 
-  GST_LOCK (element);
-  gst_object_ref (element);
-  result = gst_iterator_new_list (GST_TYPE_PAD,
-      GST_GET_LOCK (element),
-      &element->pads_cookie,
-      &element->pads,
-      element,
-      (GstIteratorItemFunction) iterate_pad,
-      (GstIteratorDisposeFunction) gst_object_unref);
-  GST_UNLOCK (element);
-
-  return result;
-}
-
-static gint
-direction_filter (gconstpointer pad, gconstpointer direction)
-{
-  if (GST_PAD_DIRECTION (pad) == GPOINTER_TO_INT (direction)) {
-    /* pass the ref through */
-    return 0;
-  } else {
-    /* unref */
-    /* FIXME: this is very stupid */
-    gst_object_unref (GST_OBJECT_CAST (pad));
-    return 1;
-  }
+  return gst_element_iterate_pad_list (element, &element->pads);
 }
 
 /**
  * gst_element_iterate_src_pads:
  * @element: a #GstElement.
  *
- * Retrieves an iterator of @element's source pads. 
+ * Retrieves an iterator of @element's source pads.
  *
  * Returns: the #GstIterator of #GstPad. Unref each pad after use.
  *
@@ -982,15 +999,14 @@ gst_element_iterate_src_pads (GstElement * element)
 {
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
 
-  return gst_iterator_filter (gst_element_iterate_pads (element),
-      direction_filter, GINT_TO_POINTER (GST_PAD_SRC));
+  return gst_element_iterate_pad_list (element, &element->srcpads);
 }
 
 /**
  * gst_element_iterate_sink_pads:
  * @element: a #GstElement.
  *
- * Retrieves an iterator of @element's sink pads. 
+ * Retrieves an iterator of @element's sink pads.
  *
  * Returns: the #GstIterator of #GstPad. Unref each pad after use.
  *
@@ -1001,8 +1017,7 @@ gst_element_iterate_sink_pads (GstElement * element)
 {
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
 
-  return gst_iterator_filter (gst_element_iterate_pads (element),
-      direction_filter, GINT_TO_POINTER (GST_PAD_SINK));
+  return gst_element_iterate_pad_list (element, &element->sinkpads);
 }
 
 /**
@@ -1113,11 +1128,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 +1141,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 +1178,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,6 +1197,8 @@ 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 event on element %s",
+        GST_ELEMENT_NAME (element));
     result = oclass->send_event (element, event);
   } else {
     GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK);
@@ -1193,7 +1210,7 @@ gst_element_send_event (GstElement * element, GstEvent * event)
       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_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
           GST_ELEMENT_NAME (element));
     }
   }
@@ -1282,7 +1299,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 +1320,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);
@@ -1333,7 +1352,9 @@ gst_element_query (GstElement * element, GstQuery * query)
  * @element: a #GstElement posting the message
  * @message: a #GstMessage to post
  *
- * Post a message on the element's #GstBus.
+ * Post a message on the element's #GstBus. This function takes ownership of the
+ * message; if you want to access the message after this call, you should add an
+ * additional reference before calling.
  *
  * Returns: TRUE if the message was successfully posted.
  *
@@ -1345,28 +1366,30 @@ gst_element_post_message (GstElement * element, GstMessage * message)
   GstBus *bus;
   gboolean result = FALSE;
 
-  GST_DEBUG ("posting message %p ...", 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)) {
-    GST_DEBUG ("... but I won't because I have no bus");
-    GST_UNLOCK (element);
-    gst_message_unref (message);
-    return FALSE;
-  }
+  if (G_UNLIKELY (bus == NULL))
+    goto no_bus;
+
   gst_object_ref (bus);
-  GST_DEBUG ("... on bus %" GST_PTR_FORMAT, bus);
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   result = gst_bus_post (bus, message);
   gst_object_unref (bus);
 
   return result;
+
+no_bus:
+  {
+    GST_DEBUG ("not posting message %p: no bus", message);
+    GST_OBJECT_UNLOCK (element);
+    gst_message_unref (message);
+    return FALSE;
+  }
 }
 
 /**
@@ -1485,9 +1508,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)
@@ -1496,9 +1519,9 @@ gst_element_is_locked_state (GstElement * element)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  GST_LOCK (element);
-  result = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
-  GST_UNLOCK (element);
+  GST_OBJECT_LOCK (element);
+  result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 }
@@ -1511,10 +1534,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)
@@ -1523,8 +1546,8 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  GST_LOCK (element);
-  old = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+  GST_OBJECT_LOCK (element);
+  old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
 
   if (G_UNLIKELY (old == locked_state))
     goto was_ok;
@@ -1532,18 +1555,18 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
   if (locked_state) {
     GST_CAT_DEBUG (GST_CAT_STATES, "locking state of element %s",
         GST_ELEMENT_NAME (element));
-    GST_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
+    GST_OBJECT_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
   } else {
     GST_CAT_DEBUG (GST_CAT_STATES, "unlocking state of element %s",
         GST_ELEMENT_NAME (element));
-    GST_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
+    GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
   }
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return TRUE;
 
 was_ok:
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return FALSE;
 }
@@ -1556,6 +1579,8 @@ was_ok:
  * If this function returns FALSE, the state of element is undefined.
  *
  * Returns: TRUE, if the element's state could be synced to the parent's state.
+ *
+ * MT safe.
  */
 gboolean
 gst_element_sync_state_with_parent (GstElement * element)
@@ -1563,78 +1588,111 @@ gst_element_sync_state_with_parent (GstElement * element)
   GstElement *parent;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
-  parent = GST_ELEMENT (GST_ELEMENT_PARENT (element));
-  g_return_val_if_fail (GST_IS_BIN (parent), FALSE);
 
-  GST_CAT_DEBUG (GST_CAT_STATES, "syncing state of element %s (%s) to %s (%s)",
-      GST_ELEMENT_NAME (element),
-      gst_element_state_get_name (GST_STATE (element)),
-      GST_ELEMENT_NAME (parent),
-      gst_element_state_get_name (GST_STATE (parent)));
+  if ((parent = GST_ELEMENT_CAST (gst_element_get_parent (element)))) {
+    GstState parent_current, parent_pending;
+    GstStateChangeReturn ret;
+
+    GST_OBJECT_LOCK (parent);
+    parent_current = GST_STATE (parent);
+    parent_pending = GST_STATE_PENDING (parent);
+    GST_OBJECT_UNLOCK (parent);
 
-  if (gst_element_set_state (element,
-          GST_STATE (parent)) == GST_STATE_CHANGE_FAILURE) {
+    GST_CAT_DEBUG (GST_CAT_STATES,
+        "syncing state of element %s (%s) to %s (%s, %s)",
+        GST_ELEMENT_NAME (element),
+        gst_element_state_get_name (GST_STATE (element)),
+        GST_ELEMENT_NAME (parent), gst_element_state_get_name (parent_current),
+        gst_element_state_get_name (parent_pending));
+
+    ret = gst_element_set_state (element, parent_current);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+      goto failed;
+
+    gst_object_unref (parent);
+
+    return TRUE;
+  }
+  return FALSE;
+
+  /* ERROR */
+failed:
+  {
     return FALSE;
   }
-  return TRUE;
 }
 
 /* MT safe */
 static GstStateChangeReturn
 gst_element_get_state_func (GstElement * element,
-    GstState * state, GstState * pending, GTimeVal * timeout)
+    GstState * state, GstState * pending, GstClockTime timeout)
 {
   GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
   GstState old_pending;
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
+
+  GST_OBJECT_LOCK (element);
+  ret = GST_STATE_RETURN (element);
 
-  GST_STATE_LOCK (element);
   /* we got an error, report immediatly */
-  if (GST_STATE_NO_PREROLL (element)) {
-    ret = GST_STATE_CHANGE_NO_PREROLL;
+  if (ret == GST_STATE_CHANGE_FAILURE)
     goto done;
-  }
 
-  /* we got an error, report immediatly */
-  if (GST_STATE_ERROR (element)) {
-    ret = GST_STATE_CHANGE_FAILURE;
+  /* we got no_preroll, report immediatly */
+  if (ret == GST_STATE_CHANGE_NO_PREROLL)
+    goto done;
+
+  /* no need to wait async if we are not async */
+  if (ret != GST_STATE_CHANGE_ASYNC)
     goto done;
-  }
 
   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;
+
+      if (add == 0)
+        goto done;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending");
-    if (timeout) {
       /* make timeout absolute */
       g_get_current_time (&abstimeout);
-      g_time_val_add (&abstimeout,
-          timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec);
+      g_time_val_add (&abstimeout, add);
       timeval = &abstimeout;
     } 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, "timeout");
+      GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timeout");
       /* 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_INFO_OBJECT (GST_CAT_STATES, element, "got success");
+        GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got success");
         ret = GST_STATE_CHANGE_SUCCESS;
       } else {
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got failure");
+        GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got failure");
         ret = GST_STATE_CHANGE_FAILURE;
       }
     }
   }
   /* if nothing is pending anymore we can return SUCCESS */
   if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
+    GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
     ret = GST_STATE_CHANGE_SUCCESS;
   }
 
@@ -1644,15 +1702,27 @@ done:
   if (pending)
     *pending = GST_STATE_PENDING (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-      "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d",
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+      "state current: %s, pending: %s, result: %d",
       gst_element_state_get_name (GST_STATE (element)),
-      gst_element_state_get_name (GST_STATE_PENDING (element)),
-      GST_STATE_ERROR (element), GST_STATE_NO_PREROLL (element), ret);
-
-  GST_STATE_UNLOCK (element);
+      gst_element_state_get_name (GST_STATE_PENDING (element)), ret);
+  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;
+  }
 }
 
 /**
@@ -1661,28 +1731,41 @@ done:
  * @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.
- * @timeout: a #GTimeVal to specify the timeout for an async
- *           state change or NULL for infinite timeout.
+ * @timeout: a #GstClockTime to specify the timeout for an async
+ *           state change or GST_CLOCK_TIME_NONE for infinite timeout.
  *
- * Gets the state of the element. 
+ * 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 
- * specified timeout value for the state change to complete. 
+ * For elements that performed an ASYNC state change, as reported by
+ * #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. 
- *
- * 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.
  */
 GstStateChangeReturn
 gst_element_get_state (GstElement * element,
-    GstState * state, GstState * pending, GTimeVal * timeout)
+    GstState * state, GstState * pending, GstClockTime timeout)
 {
   GstElementClass *oclass;
   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
@@ -1714,63 +1797,145 @@ 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_ERROR (element)) {
+  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_ERROR (element) = TRUE;
+  GST_STATE_BROADCAST (element);
+  GST_OBJECT_UNLOCK (element);
 
-    GST_STATE_BROADCAST (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.
- *
- * This function can only be called with the STATE_LOCK held.
- *
- * 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.      
  */
-void
-gst_element_commit_state (GstElement * element)
+GstStateChangeReturn
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
 {
   GstState pending;
+  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);
 
-  g_return_if_fail (GST_IS_ELEMENT (element));
+  /* check if there is something to commit */
+  if (pending == GST_STATE_VOID_PENDING)
+    goto nothing_pending;
 
-  pending = GST_STATE_PENDING (element);
+  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;
 
-  if (pending != GST_STATE_VOID_PENDING) {
-    GstState old_state = GST_STATE (element);
-    GstMessage *message;
+  /* see if we reached the final state */
+  if (pending == current)
+    goto complete;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "committing state from %s to %s",
-        gst_element_state_get_name (old_state),
-        gst_element_state_get_name (pending));
+  next = GST_STATE_GET_NEXT (current, pending);
+  transition = GST_STATE_TRANSITION (current, next);
+
+  GST_STATE_NEXT (element) = next;
+  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);
+
+  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));
+
+  ret = gst_element_change_state (element, transition);
+
+  return ret;
 
-    GST_STATE (element) = pending;
+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_ERROR (element) = FALSE;
+    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);
+    }
 
-    message = gst_message_new_state_changed (GST_OBJECT (element),
-        old_state, pending);
-    gst_element_post_message (element, message);
     GST_STATE_BROADCAST (element);
+
+    return ret;
   }
 }
 
@@ -1781,6 +1946,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.
+ *
  * This is mostly used for elements that lost their preroll buffer
  * in the PAUSED state after a flush, they become PAUSED again
  * if a new preroll buffer is queued.
@@ -1794,22 +1960,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_ERROR (element)) {
-    GstState current_state = GST_STATE (element);
-    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;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "lost state of %s", gst_element_state_get_name (current_state));
+  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_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);
 
-    GST_STATE_PENDING (element) = current_state;
-    GST_STATE_ERROR (element) = FALSE;
+  return;
 
-    message = gst_message_new_state_changed (GST_OBJECT (element),
-        current_state, current_state);
-    gst_element_post_message (element, message);
+nothing_lost:
+  {
+    GST_OBJECT_UNLOCK (element);
+    return;
   }
 }
 
@@ -1822,6 +2006,12 @@ 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 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.
+ *
  * Returns: Result of the state change using #GstStateChangeReturn.
  *
  * MT safe.
@@ -1830,133 +2020,206 @@ GstStateChangeReturn
 gst_element_set_state (GstElement * element, GstState state)
 {
   GstElementClass *oclass;
-  GstState current;
-  GstStateChangeReturn return_val = GST_STATE_CHANGE_SUCCESS;
+  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;
-  GstState pending;
-  GTimeVal tv;
+  GstStateChange transition;
+  GstStateChangeReturn old_ret;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
       gst_element_state_get_name (state));
-  /* get current element state,  need to call the method so that
-   * we call the virtual method and subclasses can implement their
-   * own algorithms */
-  GST_TIME_TO_TIMEVAL (0, tv);
-  ret = gst_element_get_state (element, &current, &pending, &tv);
 
+  /* state lock is taken to protect the set_state() and get_state() 
+   * procedures, it does not lock any variables. */
   GST_STATE_LOCK (element);
-  /* this is the state we should go to */
-  GST_STATE_FINAL (element) = state;
-  if (ret == GST_STATE_CHANGE_ASYNC) {
-    /* force next state keeping ASYNC, this is atomic as we hold
-     * the STATE_LOCK */
-    gst_element_commit_state (element);
-    gst_element_lost_state (element);
-    if (state == GST_STATE_PENDING (element)) {
-      GST_STATE_UNLOCK (element);
-      return GST_STATE_CHANGE_ASYNC;
-    }
+
+  /* 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 */
+  if (old_ret == GST_STATE_CHANGE_FAILURE) {
+    GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+    GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+    GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
   }
 
-  /* start with the current state */
   current = GST_STATE (element);
+  next = GST_STATE_NEXT (element);
+  old_pending = GST_STATE_PENDING (element);
+  element->state_cookie++;
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "setting state from %s to %s",
-      gst_element_state_get_name (current), gst_element_state_get_name (state));
+  /* this is the (new) state we should go to */
+  GST_STATE_PENDING (element) = state;
 
-  oclass = GST_ELEMENT_GET_CLASS (element);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+      "current %s, old_pending %s, next %s, old return %d",
+      gst_element_state_get_name (current),
+      gst_element_state_get_name (old_pending),
+      gst_element_state_get_name (next), old_ret);
 
-  /* We always perform at least one state change, even if the
-   * current state is equal to the required state. This is needed
-   * for bins that sync their children. */
-  do {
-    GstState pending;
-
-    /* calculate the pending state */
-    if (current < state)
-      pending = current + 1;
-    else if (current > state)
-      pending = current - 1;
-    else
-      pending = current;
+  /* if the element was busy doing a state change, we just update the
+   * target state, it'll get to it async then. */
+  if (old_pending != GST_STATE_VOID_PENDING) {
+    /* upwards state change will happen ASYNC */
+    if (old_pending <= state)
+      goto was_busy;
+    /* element is going to this state already */
+    else if (next == state)
+      goto was_busy;
+    /* element was performing an ASYNC upward state change and
+     * we request to go downward again. Start from the next pending
+     * state then. */
+    else if (next > state
+        && GST_STATE_RETURN (element) == GST_STATE_CHANGE_ASYNC) {
+      current = next;
+    }
+  }
+  next = GST_STATE_GET_NEXT (current, state);
+  /* now we store the next state */
+  GST_STATE_NEXT (element) = next;
+  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));
+
+  /* now signal any waiters, they will error since the cookie was increased */
+  GST_STATE_BROADCAST (element);
+
+  GST_OBJECT_UNLOCK (element);
 
-    /* set the pending state variable */
-    GST_STATE_PENDING (element) = pending;
+  ret = gst_element_change_state (element, transition);
 
+  GST_STATE_UNLOCK (element);
+
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
+
+  return ret;
+
+was_busy:
+  {
+    GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
     GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-        "%s: setting state from %s to %s",
-        (pending != state ? "intermediate" : "final"),
-        gst_element_state_get_name (current),
-        gst_element_state_get_name (pending));
-
-    /* call the state change function so it can set the state */
-    if (oclass->change_state)
-      return_val = (oclass->change_state) (element, GST_STATE_CHANGE (element));
-    else
-      return_val = GST_STATE_CHANGE_FAILURE;
-
-    /* clear the error and preroll flag, we need to do that after
-     * calling the virtual change_state function so that it can use the
-     * old previous value. */
-    GST_STATE_ERROR (element) = FALSE;
-    GST_STATE_NO_PREROLL (element) = FALSE;
-
-    switch (return_val) {
-      case GST_STATE_CHANGE_FAILURE:
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "have failed change_state return");
-        /* state change failure exits the loop */
-        gst_element_abort_state (element);
-        goto exit;
-      case GST_STATE_CHANGE_ASYNC:
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "element will change state async");
-        /* an async state change exits the loop, we can only
-         * go to the next state change when this one completes. */
-        goto exit;
-      case GST_STATE_CHANGE_SUCCESS:
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "element changed state successfully");
-        /* we can commit the state now and proceed to the next state */
-        gst_element_commit_state (element);
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state");
-        break;
-      case GST_STATE_CHANGE_NO_PREROLL:
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "element changed state successfully and can't preroll");
-        /* we can commit the state now and proceed to the next state */
-        gst_element_commit_state (element);
-        GST_STATE_NO_PREROLL (element) = TRUE;
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state");
-        break;
-      default:
-        goto invalid_return;
-    }
-    /* get the current state of the element and see if we need to do more
-     * state changes */
-    current = GST_STATE (element);
+        "element was busy with async state change");
+    GST_OBJECT_UNLOCK (element);
+
+    GST_STATE_UNLOCK (element);
+
+    return GST_STATE_CHANGE_ASYNC;
   }
-  while (current != state);
+}
 
-exit:
-  GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
-  GST_STATE_UNLOCK (element);
+/* with STATE_LOCK */
+static GstStateChangeReturn
+gst_element_change_state (GstElement * element, GstStateChange transition)
+{
+  GstElementClass *oclass;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstState current;
+  GstState next;
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change");
+  oclass = GST_ELEMENT_GET_CLASS (element);
 
-  return return_val;
+  /* start with the current state. */
+  current = GST_STATE_TRANSITION_CURRENT (transition);
+  next = GST_STATE_TRANSITION_NEXT (transition);
+
+  /* call the state change function so it can set the state */
+  if (oclass->change_state)
+    ret = (oclass->change_state) (element, transition);
+  else
+    ret = GST_STATE_CHANGE_FAILURE;
+
+  switch (ret) {
+    case GST_STATE_CHANGE_FAILURE:
+      GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+          "have FAILURE change_state return");
+      /* state change failure */
+      gst_element_abort_state (element);
+      break;
+    case GST_STATE_CHANGE_ASYNC:
+      GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+          "element will change state ASYNC");
+
+      /* if we go upwards, we give the app a change to wait for
+       * completion */
+      if (current < next)
+        goto async;
+
+      /* else we just continue the state change downwards */
+      GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+          "forcing commit state %s < %s",
+          gst_element_state_get_name (current),
+          gst_element_state_get_name (next));
+
+      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_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 */
+      ret = gst_element_continue_state (element, ret);
+      break;
+    default:
+      goto invalid_return;
+  }
+
+  GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
+
+  return ret;
+
+async:
+  GST_OBJECT_LOCK (element);
+  GST_STATE_RETURN (element) = ret;
+  GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+      ret);
+  GST_OBJECT_UNLOCK (element);
+
+  return ret;
 
   /* ERROR */
 invalid_return:
   {
-    GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
-    GST_STATE_UNLOCK (element);
+    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",
-        return_val);
-    return GST_STATE_CHANGE_FAILURE;
+    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;
   }
 }
 
@@ -2011,6 +2274,7 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   GstIterator *iter;
   gboolean fold_ok;
 
+  GST_DEBUG_OBJECT (element, "pads_activate with active %d", active);
   /* no need to unset this later, it's just a boolean */
   g_value_init (&ret, G_TYPE_BOOLEAN);
   g_value_set_boolean (&ret, TRUE);
@@ -2019,39 +2283,44 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   fold_ok = iterator_fold_with_resync
       (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
   gst_iterator_free (iter);
-  if (!fold_ok || !g_value_get_boolean (&ret))
+  if (!fold_ok || !g_value_get_boolean (&ret)) {
+    GST_DEBUG_OBJECT (element, "pads_activate failed");
     return FALSE;
+  }
 
   iter = gst_element_iterate_sink_pads (element);
   fold_ok = iterator_fold_with_resync
       (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
   gst_iterator_free (iter);
-  if (!fold_ok || !g_value_get_boolean (&ret))
+  if (!fold_ok || !g_value_get_boolean (&ret)) {
+    GST_DEBUG_OBJECT (element, "pads_activate failed");
     return FALSE;
+  }
 
+  GST_DEBUG_OBJECT (element, "pads_activate successful");
   return TRUE;
 }
 
 /* is called with STATE_LOCK */
 static GstStateChangeReturn
-gst_element_change_state (GstElement * element, GstStateChange transition)
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
 {
-  GstState state, pending;
+  GstState state, next;
   GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
-  state = GST_STATE (element);
-  pending = GST_STATE_PENDING (element);
+  state = GST_STATE_TRANSITION_CURRENT (transition);
+  next = GST_STATE_TRANSITION_NEXT (transition);
 
   /* if the element already is in the given state, we just return success */
-  if (pending == GST_STATE_VOID_PENDING || state == GST_STATE_PENDING (element))
+  if (next == GST_STATE_VOID_PENDING || state == next)
     goto was_ok;
 
   GST_CAT_LOG_OBJECT (GST_CAT_STATES, element,
       "default handler tries setting state from %s to %s (%04x)",
       gst_element_state_get_name (state),
-      gst_element_state_get_name (pending), transition);
+      gst_element_state_get_name (next), transition);
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
@@ -2083,20 +2352,21 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
        */
       g_warning ("Unhandled state change from %s to %s",
           gst_element_state_get_name (state),
-          gst_element_state_get_name (pending));
+          gst_element_state_get_name (next));
       break;
   }
   return result;
 
 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));
-    if (GST_STATE_NO_PREROLL (element))
-      return GST_STATE_CHANGE_NO_PREROLL;
-    else
-      return GST_STATE_CHANGE_SUCCESS;
+    GST_OBJECT_UNLOCK (element);
+
+    return result;
   }
 }
 
@@ -2106,7 +2376,7 @@ was_ok:
  *
  * Retrieves the factory that was used to create this element.
  *
- * Returns: the #GstElementFactory used for creating this element. 
+ * Returns: the #GstElementFactory used for creating this element.
  * no refcounting is needed.
  */
 GstElementFactory *
@@ -2124,11 +2394,20 @@ gst_element_dispose (GObject * object)
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
 
-  g_return_if_fail (GST_STATE (element) == GST_STATE_NULL);
+  if (GST_STATE (element) != GST_STATE_NULL) {
+    g_critical
+        ("\nTrying to dispose element %s, but it is not in the NULL state.\n"
+        "You need to explicitly set elements to the NULL state before\n"
+        "dropping the final reference, to allow them to clean up.\n",
+        GST_OBJECT_NAME (element));
+    return;
+  }
   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)) {
@@ -2136,10 +2415,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");
 
@@ -2158,7 +2437,9 @@ gst_element_finalize (GObject * object)
     g_cond_free (element->state_cond);
   element->state_cond = NULL;
   GST_STATE_UNLOCK (element);
-  g_mutex_free (element->state_lock);
+  g_static_rec_mutex_free (element->state_lock);
+  g_free (element->state_lock);
+  element->state_lock = NULL;
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
 
@@ -2182,7 +2463,7 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
   GstElementClass *oclass;
   GParamSpec **specs, *spec;
   guint nspecs;
-  gint i;
+  guint i;
   GValue value = { 0, };
   GstElement *element;
 
@@ -2308,10 +2589,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);
 }
 
 /**
@@ -2354,10 +2635,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);