Documentation updates.
[platform/upstream/gstreamer.git] / gst / gstelement.c
index 326c1ce..355af89 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+/**
+ * SECTION:gstelement
+ * @short_description: Abstract base class for all pipeline elements
+ * @see_also: #GstElementFactory, #GstPad
+ *
+ * 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
+ * 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.  #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()
+ * 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
+ * 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().
+ * 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
+ * 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.
+ *
+ * Last reviewed on 2006-03-12 (0.10.5)
+ */
+
 #include "gst_private.h"
 #include <glib.h>
 #include <stdarg.h>
@@ -29,7 +85,6 @@
 #include "gstbus.h"
 #include "gstmarshal.h"
 #include "gsterror.h"
-#include "gstscheduler.h"
 #include "gstevent.h"
 #include "gstutils.h"
 #include "gstinfo.h"
@@ -38,7 +93,6 @@
 /* Element signals and args */
 enum
 {
-  STATE_CHANGE,
   NEW_PAD,
   PAD_REMOVED,
   NO_MORE_PADS,
@@ -64,14 +118,15 @@ static void gst_element_base_class_finalize (gpointer g_class);
 static void gst_element_dispose (GObject * object);
 static void gst_element_finalize (GObject * object);
 
-static GstElementStateReturn gst_element_change_state (GstElement * element);
-static GstElementStateReturn gst_element_get_state_func (GstElement * element,
-    GstElementState * state, GstElementState * pending, GTimeVal * timeout);
-static void gst_element_set_manager_func (GstElement * element,
-    GstPipeline * manager);
+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, GstClockTime timeout);
+static GstStateChangeReturn gst_element_set_state_func (GstElement * element,
+    GstState state);
 static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
-static void gst_element_set_scheduler_func (GstElement * element,
-    GstScheduler * scheduler);
 
 #ifndef GST_DISABLE_LOADSAVE
 static xmlNodePtr gst_element_save_thyself (GstObject * object,
@@ -79,15 +134,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 (G_UNLIKELY (gst_element_type == 0)) {
     static const GTypeInfo element_info = {
       sizeof (GstElementClass),
       gst_element_base_class_init,
@@ -101,10 +156,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
@@ -119,32 +174,20 @@ gst_element_class_init (GstElementClass * klass)
   parent_class = g_type_class_ref (GST_TYPE_OBJECT);
 
   /**
-   * GstElement::state-change:
+   * GstElement::pad-added:
    * @gstelement: the object which received the signal
-   * @int:
-   * @int:
+   * @new_pad: the pad that has been added
    *
-   * the #GstElementState of the element has been changed
-   */
-  gst_element_signals[STATE_CHANGE] =
-      g_signal_new ("state-change", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, state_change), NULL,
-      NULL, gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-  /**
-   * GstElement::new-pad:
-   * @gstelement: the object which received the signal
-   * @object:
-   *
-   * a new #GstPad has been added to the element
+   * a new #GstPad has been added to the element.
    */
   gst_element_signals[NEW_PAD] =
-      g_signal_new ("new-pad", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
-      G_STRUCT_OFFSET (GstElementClass, new_pad), NULL, NULL,
+      g_signal_new ("pad-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstElementClass, pad_added), NULL, NULL,
       gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
   /**
    * GstElement::pad-removed:
    * @gstelement: the object which received the signal
-   * @object:
+   * @old_pad: the pad that has been removed
    *
    * a #GstPad has been removed from the element
    */
@@ -156,7 +199,7 @@ gst_element_class_init (GstElementClass * klass)
    * GstElement::no-more-pads:
    * @gstelement: the object which received the signal
    *
-   * ?
+   * This signals that the element will not generate more dynamic pads.
    */
   gst_element_signals[NO_MORE_PADS] =
       g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass),
@@ -172,11 +215,10 @@ 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_manager = GST_DEBUG_FUNCPTR (gst_element_set_manager_func);
   klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
-  klass->set_scheduler = GST_DEBUG_FUNCPTR (gst_element_set_scheduler_func);
   klass->numpadtemplates = 0;
 
   klass->elementfactory = NULL;
@@ -204,19 +246,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->numpads = 0;
-  element->numsrcpads = 0;
-  element->numsinkpads = 0;
-  element->pads_cookie = 0;
-  element->pads = NULL;
-  element->srcpads = NULL;
-  element->sinkpads = NULL;
-  element->manager = NULL;
-  element->clock = NULL;
-  element->sched_private = NULL;
-  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 ();
 }
 
@@ -286,9 +322,9 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad)
 gboolean
 gst_element_requires_clock (GstElement * element)
 {
-  gboolean result = FALSE;
+  gboolean result;
 
-  g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL);
 
@@ -299,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
  *
@@ -308,11 +345,38 @@ gst_element_requires_clock (GstElement * element)
 gboolean
 gst_element_provides_clock (GstElement * element)
 {
-  gboolean result = FALSE;
+  gboolean result;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  result = (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL);
+  result = (GST_ELEMENT_GET_CLASS (element)->provide_clock != NULL);
+
+  return result;
+}
+
+/**
+ * gst_element_provide_clock:
+ * @element: a #GstElement to query
+ *
+ * Get the clock provided by the given element.
+ *
+ * Returns: the GstClock provided by the element or NULL
+ * if no clock could be provided. Unref after usage.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_element_provide_clock (GstElement * element)
+{
+  GstClock *result = NULL;
+  GstElementClass *oclass;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
+
+  if (oclass->provide_clock)
+    result = oclass->provide_clock (element);
 
   return result;
 }
@@ -326,32 +390,38 @@ gst_element_provides_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;
 }
 
 /**
  * gst_element_get_clock:
  * @element: a #GstElement to get the clock of.
  *
- * Gets the clock of the element. If the element provides a clock,
- * this function will return this clock. For elements that do not
- * provide a clock, this function returns NULL.
+ * Gets the currently configured clock of the element.
  *
  * Returns: the #GstClock of the element. unref after usage.
  *
@@ -360,16 +430,65 @@ gst_element_set_clock (GstElement * element, GstClock * clock)
 GstClock *
 gst_element_get_clock (GstElement * element)
 {
-  GstElementClass *oclass;
+  GstClock *result;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
 
-  oclass = GST_ELEMENT_GET_CLASS (element);
+  GST_OBJECT_LOCK (element);
+  if ((result = element->clock))
+    gst_object_ref (result);
+  GST_OBJECT_UNLOCK (element);
 
-  if (oclass->get_clock)
-    return oclass->get_clock (element);
+  return result;
+}
 
-  return NULL;
+/**
+ * gst_element_set_base_time:
+ * @element: a #GstElement.
+ * @time: the base time to set.
+ *
+ * Set the base time of an element. See gst_element_get_base_time().
+ *
+ * MT safe.
+ */
+void
+gst_element_set_base_time (GstElement * element, GstClockTime time)
+{
+  g_return_if_fail (GST_IS_ELEMENT (element));
+
+  GST_OBJECT_LOCK (element);
+  element->base_time = time;
+  GST_OBJECT_UNLOCK (element);
+
+  GST_DEBUG_OBJECT (element, "set base_time=%" GST_TIME_FORMAT,
+      GST_TIME_ARGS (time));
+}
+
+/**
+ * gst_element_get_base_time:
+ * @element: a #GstElement.
+ *
+ * Returns the base time of the element. The base time is the
+ * absolute time of the clock when this element was last put to
+ * PLAYING. Substracting the base time from the clock time gives
+ * the stream time of the element.
+ *
+ * Returns: the base time of the element.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_element_get_base_time (GstElement * element)
+{
+  GstClockTime result;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
+
+  GST_OBJECT_LOCK (element);
+  result = element->base_time;
+  GST_OBJECT_UNLOCK (element);
+
+  return result;
 }
 
 #ifndef GST_DISABLE_INDEX
@@ -386,9 +505,9 @@ gst_element_get_clock (GstElement * element)
 gboolean
 gst_element_is_indexable (GstElement * element)
 {
-  gboolean result = FALSE;
+  gboolean result;
 
-  g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL);
 
@@ -400,7 +519,8 @@ gst_element_is_indexable (GstElement * element)
  * @element: a #GstElement.
  * @index: a #GstIndex.
  *
- * Set the specified GstIndex on the element.
+ * Set @index on the element. The refcount of the index
+ * will be increased, any previously set index is unreffed.
  *
  * MT safe.
  */
@@ -454,12 +574,14 @@ gst_element_get_index (GstElement * element)
  * see gst_object_set_parent() for refcounting information.
  *
  * Pads are not automatically activated so elements should perform the needed
- * steps to activate the pad. 
+ * steps to activate the pad in case this pad is added in the PAUSED or PLAYING
+ * 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.
  */
@@ -472,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;
 
@@ -501,15 +623,12 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
       element->numsinkpads++;
       break;
     default:
-      /* can happen for ghost pads */
-      g_warning ("adding pad %s:%s wothout direction",
-          GST_DEBUG_PAD_NAME (pad));
-      break;
+      goto no_direction;
   }
   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);
@@ -521,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;
   }
@@ -530,43 +649,20 @@ 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;
   }
-}
-
-/**
- * gst_element_add_ghost_pad:
- * @element: a #GstElement to add the ghost pad to.
- * @pad: the #GstPad from which the new ghost pad will be created.
- * @name: the name of the new ghost pad, or NULL to assign a unique name
- * automatically.
- *
- * Creates a ghost pad from @pad, and adds it to @element via
- * gst_element_add_pad().
- *
- * Returns: the added ghost #GstPad, or NULL on error.
- *
- * MT safe.
- */
-GstPad *
-gst_element_add_ghost_pad (GstElement * element, GstPad * pad,
-    const gchar * name)
-{
-  GstPad *ghostpad;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  ghostpad = gst_ghost_pad_new (name, pad);
-
-  if (!gst_element_add_pad (element, ghostpad)) {
-    gst_object_unref (GST_OBJECT (ghostpad));
-    ghostpad = NULL;
+no_direction:
+  {
+    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_OBJECT_UNLOCK (pad);
+    GST_OBJECT_UNLOCK (element);
+    return FALSE;
   }
-
-  return ghostpad;
 }
 
 /**
@@ -578,53 +674,41 @@ gst_element_add_ghost_pad (GstElement * element, GstPad * pad,
  * 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.
  */
 gboolean
 gst_element_remove_pad (GstElement * element, GstPad * pad)
 {
-  gchar *pad_name;
+  GstPad *peer;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
   /* locking pad to look at the name and parent */
-  GST_LOCK (pad);
-  pad_name = g_strdup (GST_PAD_NAME (pad));
-
+  GST_OBJECT_LOCK (pad);
   GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'",
-      GST_STR_NULL (pad_name));
+      GST_STR_NULL (GST_PAD_NAME (pad)));
 
   if (G_UNLIKELY (GST_PAD_PARENT (pad) != element))
     goto not_our_pad;
-  GST_UNLOCK (pad);
-
-  g_free (pad_name);
-
-  /* FIXME, is this redundant with pad disposal? */
-  if (GST_IS_REAL_PAD (pad)) {
-    GstPad *peer = gst_pad_get_peer (pad);
-
-    /* unlink */
-    if (peer != NULL) {
-      /* window for MT unsafeness, someone else could unlink here
-       * and then we call unlink with wrong pads. The unlink
-       * function would catch this and safely return failed. */
-      if (GST_PAD_IS_SRC (pad))
-        gst_pad_unlink (pad, GST_PAD_CAST (peer));
-      else
-        gst_pad_unlink (GST_PAD_CAST (peer), pad);
+  GST_OBJECT_UNLOCK (pad);
+
+  /* unlink */
+  if ((peer = gst_pad_get_peer (pad))) {
+    /* window for MT unsafeness, someone else could unlink here
+     * and then we call unlink with wrong pads. The unlink
+     * function would catch this and safely return failed. */
+    if (GST_PAD_IS_SRC (pad))
+      gst_pad_unlink (pad, peer);
+    else
+      gst_pad_unlink (peer, pad);
 
-      gst_object_unref (GST_OBJECT (peer));
-    }
-  } else if (GST_IS_GHOST_PAD (pad)) {
-    g_object_set (pad, "real-pad", NULL, NULL);
+    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:
@@ -636,13 +720,13 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
       element->numsinkpads--;
       break;
     default:
-      /* can happen for ghost pads */
+      g_critical ("Removing pad without direction???");
       break;
   }
   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);
 
@@ -653,13 +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);
-    g_free (pad_name);
+    GST_OBJECT_UNLOCK (element);
+    GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
 }
@@ -670,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.
  *
@@ -689,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;
 }
@@ -718,12 +801,12 @@ 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) {
     result = GST_PAD_CAST (find->data);
-    gst_object_ref (GST_OBJECT_CAST (result));
+    gst_object_ref (result);
   }
 
   if (result == NULL) {
@@ -733,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;
 }
@@ -751,7 +834,7 @@ gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
     newpad = (oclass->request_new_pad) (element, templ, name);
 
   if (newpad)
-    gst_object_ref (GST_OBJECT (newpad));
+    gst_object_ref (newpad);
 
   return newpad;
 }
@@ -857,43 +940,86 @@ gst_element_get_pad (GstElement * element, const gchar * name)
   return pad;
 }
 
-GstIteratorItem
+static GstIteratorItem
 iterate_pad (GstIterator * it, GstPad * pad)
 {
-  gst_object_ref (GST_OBJECT_CAST (pad));
+  gst_object_ref (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.
  *
- * Retrieves an iterattor of @element's pads. 
+ * Retrieves an iterattor of @element's pads. The iterator should
+ * be freed after usage.
  *
- * Returns: the #GstIterator of #GstPad. unref each pad after usage.
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
  *
  * MT safe.
  */
 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 (GST_OBJECT (element));
-  result = gst_iterator_new_list (GST_GET_LOCK (element),
-      &element->pads_cookie,
-      &element->pads,
-      element,
-      (GstIteratorItemFunction) iterate_pad,
-      (GstIteratorDisposeFunction) gst_object_unref);
-  GST_UNLOCK (element);
+  return gst_element_iterate_pad_list (element, &element->pads);
+}
 
-  return result;
+/**
+ * gst_element_iterate_src_pads:
+ * @element: a #GstElement.
+ *
+ * Retrieves an iterator of @element's source pads.
+ *
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_src_pads (GstElement * element)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  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.
+ *
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_sink_pads (GstElement * element)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  return gst_element_iterate_pad_list (element, &element->sinkpads);
+}
 
 /**
  * gst_element_class_add_pad_template:
@@ -915,7 +1041,7 @@ gst_element_class_add_pad_template (GstElementClass * klass,
           templ->name_template) == NULL);
 
   klass->padtemplates = g_list_append (klass->padtemplates,
-      gst_object_ref (GST_OBJECT (templ)));
+      gst_object_ref (templ));
   klass->numpadtemplates++;
 }
 
@@ -1003,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:
@@ -1016,31 +1142,31 @@ 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 (GST_OBJECT (result));
+    gst_object_ref (result);
 
-  GST_UNLOCK (element);
+  GST_OBJECT_UNLOCK (element);
 
   return result;
 
   /* ERROR handling */
 wrong_direction:
   {
-    g_warning ("unknown pad direction");
+    g_warning ("unknown pad direction %d", dir);
     return NULL;
   }
 }
@@ -1053,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.
@@ -1072,50 +1198,60 @@ 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) {
-      GstPad *peer = gst_pad_get_peer (pad);
-
-      if (peer) {
-        GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
-            "sending event to random pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+      GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+          "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_send_event (peer, event);
-        gst_object_unref (GST_OBJECT (peer));
-      }
-      gst_object_unref (GST_OBJECT (pad));
+      result = gst_pad_push_event (pad, event);
+      gst_object_unref (pad);
+    } else {
+      GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
+          GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
     }
   }
-  GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
-      GST_ELEMENT_NAME (element));
-
   return result;
 }
 
 /**
  * gst_element_seek:
  * @element: a #GstElement to send the event to.
- * @seek_type: the method to use for seeking.
- * @offset: the offset to seek to.
+ * @rate: The new playback rate
+ * @format: The format of the seek values
+ * @flags: The optional seek flags.
+ * @cur_type: The type and flags for the new current position
+ * @cur: The value of the new current position
+ * @stop_type: The type and flags for the new stop position
+ * @stop: The value of the new stop position
  *
  * Sends a seek event to an element.
  *
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
  *
  * MT safe.
  */
 gboolean
-gst_element_seek (GstElement * element, GstSeekType seek_type, guint64 offset)
+gst_element_seek (GstElement * element, gdouble rate, GstFormat format,
+    GstSeekFlags flags, GstSeekType cur_type, gint64 cur,
+    GstSeekType stop_type, gint64 stop)
 {
   GstEvent *event;
   gboolean result;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
-  event = gst_event_new_seek (seek_type, offset);
+  event =
+      gst_event_new_seek (rate, format, flags, cur_type, cur, stop_type, stop);
   result = gst_element_send_event (element, event);
 
   return result;
@@ -1155,9 +1291,9 @@ gst_element_get_query_types (GstElement * element)
       if (peer) {
         result = gst_pad_get_query_types (peer);
 
-        gst_object_unref (GST_OBJECT (peer));
+        gst_object_unref (peer);
       }
-      gst_object_unref (GST_OBJECT (pad));
+      gst_object_unref (pad);
     }
   }
   return result;
@@ -1166,12 +1302,10 @@ gst_element_get_query_types (GstElement * element)
 /**
  * gst_element_query:
  * @element: a #GstElement to perform the query on.
- * @type: the #GstQueryType.
- * @format: the #GstFormat pointer to hold the format of the result.
- * @value: the pointer to the value of the result.
+ * @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.
@@ -1192,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);
@@ -1199,7 +1335,7 @@ gst_element_query (GstElement * element, GstQuery * query)
     if (pad) {
       result = gst_pad_query (pad, query);
 
-      gst_object_unref (GST_OBJECT (pad));
+      gst_object_unref (pad);
     } else {
       pad = gst_element_get_random_pad (element, GST_PAD_SINK);
       if (pad) {
@@ -1208,9 +1344,9 @@ gst_element_query (GstElement * element, GstQuery * query)
         if (peer) {
           result = gst_pad_query (peer, query);
 
-          gst_object_unref (GST_OBJECT (peer));
+          gst_object_unref (peer);
         }
-        gst_object_unref (GST_OBJECT (pad));
+        gst_object_unref (pad);
       }
     }
   }
@@ -1222,9 +1358,11 @@ gst_element_query (GstElement * element, GstQuery * query)
  * @element: a #GstElement posting the message
  * @message: a #GstMessage to post
  *
- * Post a message on the elements #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 successfuly posted.
+ * Returns: TRUE if the message was successfully posted.
  *
  * MT safe.
  */
@@ -1234,35 +1372,37 @@ 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_data_unref (GST_DATA (message));
-    return FALSE;
-  }
-  gst_object_ref (GST_OBJECT (bus));
-  GST_DEBUG ("... on bus %p", bus);
-  GST_UNLOCK (element);
+  if (G_UNLIKELY (bus == NULL))
+    goto no_bus;
+
+  gst_object_ref (bus);
+  GST_OBJECT_UNLOCK (element);
 
   result = gst_bus_post (bus, message);
-  gst_object_unref (GST_OBJECT (bus));
+  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;
+  }
 }
 
 /**
  * _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 ""
  *
@@ -1312,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));
@@ -1331,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 */
@@ -1347,9 +1492,11 @@ void gst_element_message_full
 
   if (type == GST_MESSAGE_ERROR) {
     message = gst_message_new_error (GST_OBJECT (element), gerror, sent_debug);
-  } else {
+  } else if (type == GST_MESSAGE_WARNING) {
     message = gst_message_new_warning (GST_OBJECT (element), gerror,
         sent_debug);
+  } else {
+    g_assert_not_reached ();
   }
   gst_element_post_message (element, message);
 
@@ -1357,8 +1504,9 @@ void gst_element_message_full
       sent_text);
 
   /* cleanup */
+  g_error_free (gerror);
+  g_free (sent_debug);
   g_free (sent_text);
-  /* sent_debug is not part of the gerror, so don't free it here */
 }
 
 /**
@@ -1371,20 +1519,20 @@ 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)
 {
-  gboolean result = FALSE;
+  gboolean result;
 
   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;
 }
@@ -1397,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)
@@ -1409,8 +1557,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;
@@ -1418,18 +1566,20 @@ 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_CAT_DEBUG (GST_CAT_STATES, "elements %s was in locked state %d",
+      GST_ELEMENT_NAME (element), old);
+  GST_OBJECT_UNLOCK (element);
 
   return FALSE;
 }
@@ -1442,6 +1592,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)
@@ -1449,72 +1601,112 @@ 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);
+
+    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);
 
-  if (gst_element_set_state (element, GST_STATE (parent)) == GST_STATE_FAILURE) {
+    return TRUE;
+  }
+  return FALSE;
+
+  /* ERROR */
+failed:
+  {
     return FALSE;
   }
-  return TRUE;
 }
 
 /* MT safe */
-static GstElementStateReturn
+static GstStateChangeReturn
 gst_element_get_state_func (GstElement * element,
-    GstElementState * state, GstElementState * pending, GTimeVal * timeout)
+    GstState * state, GstState * pending, GstClockTime timeout)
 {
-  GstElementStateReturn ret = GST_STATE_FAILURE;
-  GstElementState old_pending;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
+  GstState old_pending;
 
-  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
 
-  GST_CAT_INFO_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_ERROR (element))
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    goto done;
+
+  /* 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_ASYNC;
+      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");
-        ret = GST_STATE_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");
-        ret = GST_STATE_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");
-    ret = GST_STATE_SUCCESS;
+    GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
+    ret = GST_STATE_CHANGE_SUCCESS;
   }
 
 done:
@@ -1523,49 +1715,75 @@ done:
   if (pending)
     *pending = GST_STATE_PENDING (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-      "state current: %s, pending: %s",
+  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_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;
+  }
 }
 
 /**
  * gst_element_get_state:
  * @element: a #GstElement to get the state of.
- * @state: a pointer to #GstElementState to hold the state. Can be NULL.
- * @pending: a pointer to #GstElementState 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.
+ * @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 #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_SUCCESS or GST_STATE_FAILURE respectively. 
- *
- * Returns: GST_STATE_SUCCESS if the element has no more pending state and
- *          the last state change succeeded, GST_STATE_ASYNC
- *          if the element is still performing a state change or 
- *          GST_STATE_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.
  */
-GstElementStateReturn
+GstStateChangeReturn
 gst_element_get_state (GstElement * element,
-    GstElementState * state, GstElementState * pending, GTimeVal * timeout)
+    GstState * state, GstState * pending, GstClockTime timeout)
 {
   GstElementClass *oclass;
-  GstElementStateReturn result = GST_STATE_FAILURE;
+  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
 
-  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
@@ -1580,7 +1798,7 @@ gst_element_get_state (GstElement * element,
  * @element: a #GstElement to abort the state of.
  *
  * Abort the state change of the element. This function is used
- * by elements that do asynchronous state changes and find out 
+ * by elements that do asynchronous state changes and find out
  * something is wrong.
  *
  * This function should be called with the STATE_LOCK held.
@@ -1590,66 +1808,149 @@ gst_element_get_state (GstElement * element,
 void
 gst_element_abort_state (GstElement * element)
 {
-  GstElementState pending;
+  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
-    GstElementState 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)
 {
-  GstElementState pending;
+  GstState pending;
+  GstState old_ret, old_state, old_next;
+  GstState current, next;
   GstMessage *message;
+  GstStateChange transition;
 
-  g_return_if_fail (GST_IS_ELEMENT (element));
-
+  GST_OBJECT_LOCK (element);
+  old_ret = GST_STATE_RETURN (element);
+  GST_STATE_RETURN (element) = ret;
   pending = GST_STATE_PENDING (element);
 
-  if (pending != GST_STATE_VOID_PENDING) {
-    GstElementState old_state = GST_STATE (element);
+  /* check if there is something to commit */
+  if (pending == GST_STATE_VOID_PENDING)
+    goto nothing_pending;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "commiting state from %s to %s", gst_element_state_get_name (old_state),
-        gst_element_state_get_name (pending));
+  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;
+
+  /* see if we reached the final state */
+  if (pending == current)
+    goto complete;
+
+  next = GST_STATE_GET_NEXT (current, pending);
+  transition = GST_STATE_TRANSITION (current, next);
+
+  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);
 
-    GST_STATE (element) = 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));
+
+  ret = gst_element_change_state (element, transition);
+
+  return ret;
+
+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);
+    }
 
-    g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE],
-        0, old_state, pending);
-    message = gst_message_new_state_changed (GST_OBJECT (element),
-        old_state, pending);
-    gst_element_post_message (element, message);
     GST_STATE_BROADCAST (element);
+
+    return ret;
   }
 }
 
@@ -1659,7 +1960,8 @@ 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
  * if a new preroll buffer is queued.
@@ -1673,297 +1975,400 @@ 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)) {
-    GstElementState current_state = GST_STATE (element);
+  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);
+
+  return;
 
-    GST_STATE_PENDING (element) = current_state;
-    GST_STATE_ERROR (element) = FALSE;
+nothing_lost:
+  {
+    GST_OBJECT_UNLOCK (element);
+    return;
   }
 }
 
 /**
  * gst_element_set_state:
  * @element: a #GstElement to change state of.
- * @state: the element's new #GstElementState.
+ * @state: the element's new #GstState.
  *
  * Sets the state of the element. This function will try to set the
  * requested state by going through all the intermediary states and calling
  * the class's state change function for each.
  *
- * Returns: Result of the state change using #GstElementStateReturn.
+ * 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.
  */
-GstElementStateReturn
-gst_element_set_state (GstElement * element, GstElementState state)
+GstStateChangeReturn
+gst_element_set_state (GstElement * element, GstState state)
 {
   GstElementClass *oclass;
-  GstElementState current;
-  GstElementStateReturn return_val = GST_STATE_SUCCESS;
+  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
 
-  /* get the element state lock */
-  GST_STATE_LOCK (element);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
-#if 0
-  /* a state change is pending and we are not in error, the element is busy
-   * with a state change and we cannot proceed. 
-   * FIXME, does not work for a bin.*/
-  if (G_UNLIKELY (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING &&
-          !GST_STATE_ERROR (element)))
-    goto was_busy;
-#endif
+  oclass = GST_ELEMENT_GET_CLASS (element);
 
-  /* clear the error flag */
-  GST_STATE_ERROR (element) = FALSE;
+  if (oclass->set_state)
+    result = (oclass->set_state) (element, state);
 
-  /* start with the current state */
-  current = GST_STATE (element);
+  return result;
+}
 
-  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));
+/*
+ * 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;
+  GstStateChangeReturn old_ret;
 
-  oclass = GST_ELEMENT_GET_CLASS (element);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
-  /* 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 {
-    GstElementState pending;
-
-    /* calculate the pending state */
-    if (current < state)
-      pending = current << 1;
-    else if (current > state)
-      pending = current >> 1;
-    else
-      pending = current;
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
+      gst_element_state_get_name (state));
 
-    /* set the pending state variable */
-    GST_STATE_PENDING (element) = pending;
+  /* state lock is taken to protect the set_state() and get_state() 
+   * procedures, it does not lock any variables. */
+  GST_STATE_LOCK (element);
 
-    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);
-    else
-      return_val = GST_STATE_FAILURE;
-
-    switch (return_val) {
-      case GST_STATE_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_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_SUCCESS:
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "element changed state successfuly");
-        /* 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, "commited state");
-        break;
-      default:
-        goto invalid_return;
+  /* 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;
+  }
+
+  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;
+
+  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);
+
+  /* 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;
     }
-    /* get the current state of the element and see if we need to do more
-     * state changes */
-    current = GST_STATE (element);
   }
-  while (current != 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));
+
+  /* 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);
 
-exit:
   GST_STATE_UNLOCK (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
 
-  return return_val;
+  return ret;
 
-  /* ERROR */
-#if 0
 was_busy:
   {
+    GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+        "element was busy with async state change");
+    GST_OBJECT_UNLOCK (element);
+
     GST_STATE_UNLOCK (element);
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "was busy with a state change");
 
-    return GST_STATE_BUSY;
+    return GST_STATE_CHANGE_ASYNC;
   }
-#endif
+}
+
+/* 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;
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
+
+  /* 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_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+      ret);
+
+  return ret;
+
+  /* ERROR */
 invalid_return:
   {
-    GST_STATE_UNLOCK (element);
+    GST_OBJECT_LOCK (element);
     /* somebody added a GST_STATE_ and forgot to do stuff here ! */
-    g_critical ("unkown return value from a state change function");
-    return GST_STATE_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;
   }
 }
 
-/* is called with STATE_LOCK
- *
- * This function activates the pads of a given element. 
- *
- * TODO: activate pads from src to sinks?
- *       move pad activate logic to GstPad because we also need this
- *       when pads are added to elements?
- */
+/* gst_iterator_fold functions for pads_activate */
+
 static gboolean
-gst_element_pads_activate (GstElement * element, gboolean active)
+activate_pads (GstPad * pad, GValue * ret, gboolean * active)
 {
-  GList *pads;
-  gboolean result;
-  guint32 cookie;
-
-  GST_LOCK (element);
-restart:
-  result = TRUE;
-  pads = element->pads;
-  cookie = element->pads_cookie;
-  for (; pads && result; pads = g_list_next (pads)) {
-    GstPad *pad = GST_PAD (pads->data);
+  if (!gst_pad_set_active (pad, *active))
+    g_value_set_boolean (ret, FALSE);
+  else if (!*active)
+    gst_pad_set_caps (pad, NULL);
 
-    gst_object_ref (GST_OBJECT (pad));
-    GST_UNLOCK (element);
+  gst_object_unref (pad);
+  return TRUE;
+}
 
-    /* we only care about real pads */
-    if (GST_IS_REAL_PAD (pad)) {
-      GstRealPad *peer;
-      gboolean pad_loop, pad_get;
-      gboolean done = FALSE;
+/* returns false on error or early cutout of the fold, true otherwise */
+static gboolean
+iterator_fold_with_resync (GstIterator * iter, GstIteratorFoldFunction func,
+    GValue * ret, gpointer user_data)
+{
+  GstIteratorResult ires;
+  gboolean res = TRUE;
 
-      /* see if the pad has a loop function and grab
-       * the peer */
-      pad_get = gst_pad_check_pull_range (pad);
-      GST_LOCK (pad);
-      pad_loop = GST_RPAD_LOOPFUNC (pad) != NULL;
-      peer = GST_RPAD_PEER (pad);
-      if (peer)
-        gst_object_ref (GST_OBJECT (peer));
-      GST_UNLOCK (pad);
+  while (1) {
+    ires = gst_iterator_fold (iter, func, ret, user_data);
 
-      GST_DEBUG ("pad %s:%s: get: %d, loop: %d",
-          GST_DEBUG_PAD_NAME (pad), pad_get, pad_loop);
+    switch (ires) {
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        break;
+      case GST_ITERATOR_DONE:
+        res = TRUE;
+        goto done;
+      default:
+        res = FALSE;
+        goto done;
+    }
+  }
 
-      if (peer) {
-        gboolean peer_loop, peer_get;
-        GstActivateMode mode;
-
-        /* see if the peer has a getrange function */
-        peer_get = gst_pad_check_pull_range (GST_PAD_CAST (peer));
-        /* see if the peer has a loop function */
-        peer_loop = GST_RPAD_LOOPFUNC (peer) != NULL;
-
-        GST_DEBUG ("peer %s:%s: get: %d, loop: %d",
-            GST_DEBUG_PAD_NAME (peer), peer_get, peer_loop);
-
-        /* If the pad is a sink with loop and the peer has a get function,
-         * we can activate the sinkpad,  FIXME, logic is reversed as
-         * check_pull_range() checks the peer of the given pad. */
-        if ((GST_PAD_IS_SINK (pad) && pad_get && pad_loop) ||
-            (GST_PAD_IS_SRC (pad) && peer_get && peer_loop)) {
-          GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-              "%sactivating pad %s pull mode", (active ? "" : "(de)"),
-              GST_OBJECT_NAME (pad));
-          /* only one of pad_random and peer_random can be true */
-          mode = GST_ACTIVATE_PULL;
-          result &= gst_pad_set_active (pad, active ? mode : GST_ACTIVATE_NONE);
-          done = TRUE;
-        }
-        gst_object_unref (GST_OBJECT (peer));
-      }
+done:
+  return res;
+}
 
-      if (!done) {
-        /* all other conditions are just push based pads */
-        GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-            "%sactivating pad %s push mode", (active ? "" : "(de)"),
-            GST_OBJECT_NAME (pad));
+/* is called with STATE_LOCK
+ */
+static gboolean
+gst_element_pads_activate (GstElement * element, gboolean active)
+{
+  GValue ret = { 0, };
+  GstIterator *iter;
+  gboolean fold_ok;
 
-        result &= gst_pad_set_active (pad,
-            (active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE));
-      }
-    }
-    gst_object_unref (GST_OBJECT (pad));
+  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);
 
-    GST_LOCK (element);
-    if (cookie != element->pads_cookie)
-      goto restart;
+  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);
+  if (!fold_ok || !g_value_get_boolean (&ret)) {
+    GST_DEBUG_OBJECT (element, "pads_activate failed");
+    return FALSE;
   }
-  GST_UNLOCK (element);
 
-  return result;
+  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);
+  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 GstElementStateReturn
-gst_element_change_state (GstElement * element)
+static GstStateChangeReturn
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
 {
-  GstElementState old_state;
-  gint old_pending, old_transition;
-  GstElementStateReturn result = GST_STATE_SUCCESS;
+  GstState state, next;
+  GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
 
-  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
-  old_state = GST_STATE (element);
-  old_pending = GST_STATE_PENDING (element);
-  old_transition = GST_STATE_TRANSITION (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 (old_pending == GST_STATE_VOID_PENDING ||
-      old_state == GST_STATE_PENDING (element)) {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-        "element is already in the %s state",
-        gst_element_state_get_name (old_state));
-    return GST_STATE_SUCCESS;
-  }
+  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 (old_state),
-      gst_element_state_get_name (old_pending), old_transition);
+      gst_element_state_get_name (state),
+      gst_element_state_get_name (next), transition);
 
-  switch (old_transition) {
-    case GST_STATE_NULL_TO_READY:
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
       break;
-    case GST_STATE_READY_TO_PAUSED:
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
       if (!gst_element_pads_activate (element, TRUE)) {
-        result = GST_STATE_FAILURE;
+        result = GST_STATE_CHANGE_FAILURE;
       }
       break;
-    case GST_STATE_PAUSED_TO_PLAYING:
-      GST_LOCK (element);
-      if (GST_ELEMENT_MANAGER (element)) {
-        element->base_time =
-            GST_ELEMENT_CAST (GST_ELEMENT_MANAGER (element))->base_time;
-      }
-      GST_UNLOCK (element);
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       break;
-    case GST_STATE_PLAYING_TO_PAUSED:
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       break;
-    case GST_STATE_PAUSED_TO_READY:
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      /* deactivate pads in both cases, since they are activated on
+         ready->paused but the element might not have made it to paused */
       if (!gst_element_pads_activate (element, FALSE)) {
-        result = GST_STATE_FAILURE;
+        result = GST_STATE_CHANGE_FAILURE;
       } else {
-        element->base_time = 0;
+        gst_element_set_base_time (element, 0);
       }
       break;
-    case GST_STATE_READY_TO_NULL:
-      break;
     default:
       /* this will catch real but unhandled state changes;
        * can only be caused by:
@@ -1971,12 +2376,23 @@ gst_element_change_state (GstElement * element)
        * - somehow the element was asked to jump across an intermediate state
        */
       g_warning ("Unhandled state change from %s to %s",
-          gst_element_state_get_name (old_state),
-          gst_element_state_get_name (old_pending));
+          gst_element_state_get_name (state),
+          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));
+    GST_OBJECT_UNLOCK (element);
+
+    return result;
+  }
 }
 
 /**
@@ -1986,6 +2402,7 @@ gst_element_change_state (GstElement * element)
  * Retrieves the factory that was used to create this element.
  *
  * Returns: the #GstElementFactory used for creating this element.
+ * no refcounting is needed.
  */
 GstElementFactory *
 gst_element_get_factory (GstElement * element)
@@ -2002,24 +2419,33 @@ gst_element_dispose (GObject * object)
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
 
-  /* ref so we don't hit 0 again */
-  gst_object_ref (GST_OBJECT (object));
+  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);
 
-  /* first we break all our links with the ouside */
-  while (element->pads) {
-    gst_element_remove_pad (element, GST_PAD (element->pads->data));
+  GST_DEBUG ("removing %d pads", g_list_length (element->pads));
+  /* first we break all our links with the outside */
+  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)) {
     g_critical ("could not remove pads from element %s",
         GST_STR_NULL (GST_OBJECT_NAME (object)));
   }
 
-  GST_LOCK (element);
-  gst_object_replace ((GstObject **) & element->manager, NULL);
+  GST_OBJECT_LOCK (element);
   gst_object_replace ((GstObject **) & element->clock, NULL);
-  GST_UNLOCK (element);
+  gst_object_replace ((GstObject **) & element->bus, NULL);
+  GST_OBJECT_UNLOCK (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose parent");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -2036,7 +2462,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");
 
@@ -2060,7 +2488,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;
 
@@ -2080,10 +2508,6 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
         (xmlChar *) GST_PLUGIN_FEATURE (factory)->name);
   }
 
-/* FIXME: what is this? */
-/*  if (element->manager) */
-/*    xmlNewChild(parent, NULL, "manager", GST_ELEMENT_NAME(element->manager)); */
-
   /* params */
   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &nspecs);
 
@@ -2116,6 +2540,8 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
     }
   }
 
+  g_free (specs);
+
   pads = GST_ELEMENT_PADS (element);
 
   while (pads) {
@@ -2182,93 +2608,16 @@ gst_element_restore_thyself (GstObject * object, xmlNodePtr self)
 #endif /* GST_DISABLE_LOADSAVE */
 
 static void
-gst_element_set_manager_func (GstElement * element, GstPipeline * manager)
-{
-  g_return_if_fail (GST_IS_ELEMENT (element));
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting manager to %p",
-      manager);
-
-  /* setting the manager cannot increase the refcount */
-  GST_LOCK (element);
-  GST_ELEMENT_MANAGER (element) = manager;
-  GST_UNLOCK (element);
-}
-
-static void
 gst_element_set_bus_func (GstElement * element, GstBus * bus)
 {
   g_return_if_fail (GST_IS_ELEMENT (element));
 
   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 (bus));
-  GST_UNLOCK (element);
-}
-
-static void
-gst_element_set_scheduler_func (GstElement * element, GstScheduler * scheduler)
-{
-  g_return_if_fail (GST_IS_ELEMENT (element));
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting scheduler to %p",
-      scheduler);
-
-  GST_LOCK (element);
-  gst_object_replace ((GstObject **) & GST_ELEMENT_SCHEDULER (element),
-      GST_OBJECT (scheduler));
-  GST_UNLOCK (element);
-}
-
-/**
- * gst_element_set_manager:
- * @element: a #GstElement to set the manager of.
- * @manager: the #GstManager to set.
- *
- * Sets the manager of the element.  For internal use only, unless you're
- * writing a new bin subclass.
- *
- * MT safe.
- */
-void
-gst_element_set_manager (GstElement * element, GstPipeline * manager)
-{
-  GstElementClass *oclass;
-
-  g_return_if_fail (GST_IS_ELEMENT (element));
-
-  oclass = GST_ELEMENT_GET_CLASS (element);
-
-  if (oclass->set_manager)
-    oclass->set_manager (element, manager);
-}
-
-
-/**
- * gst_element_get_manager:
- * @element: a #GstElement to get the manager of.
- *
- * Returns the manager of the element. 
- *
- * Returns: the element's #GstPipeline. unref after usage.
- *
- * MT safe.
- */
-GstPipeline *
-gst_element_get_manager (GstElement * element)
-{
-  GstPipeline *result = NULL;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), result);
-
-  GST_LOCK (element);
-  result = GST_ELEMENT_MANAGER (element);
-  gst_object_ref (GST_OBJECT (result));
-  GST_UNLOCK (element);
-
-  return result;
+      GST_OBJECT_CAST (bus));
+  GST_OBJECT_UNLOCK (element);
 }
 
 /**
@@ -2300,7 +2649,7 @@ gst_element_set_bus (GstElement * element, GstBus * bus)
  *
  * Returns the bus of the element.
  *
- * Returns: the element's #GstBus.
+ * Returns: the element's #GstBus. unref after usage.
  *
  * MT safe.
  */
@@ -2311,56 +2660,12 @@ 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_UNLOCK (element);
-
-  return result;
-}
-
-/**
- * gst_element_set_scheduler:
- * @element: a #GstElement to set the scheduler of.
- * @scheduler: the #GstScheduler to set.
- *
- * Sets the scheduler of the element.  For internal use only, unless you're
- * testing elements.
- *
- * MT safe.
- */
-void
-gst_element_set_scheduler (GstElement * element, GstScheduler * scheduler)
-{
-  GstElementClass *oclass;
-
-  g_return_if_fail (GST_IS_ELEMENT (element));
-
-  oclass = GST_ELEMENT_GET_CLASS (element);
-
-  if (oclass->set_scheduler)
-    oclass->set_scheduler (element, scheduler);
-}
-
-/**
- * gst_element_get_scheduler:
- * @element: a #GstElement to get the scheduler of.
- *
- * Returns the scheduler of the element.
- *
- * Returns: the element's #GstScheduler.
- *
- * MT safe.
- */
-GstScheduler *
-gst_element_get_scheduler (GstElement * element)
-{
-  GstScheduler *result = NULL;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+  gst_object_ref (result);
+  GST_OBJECT_UNLOCK (element);
 
-  GST_LOCK (element);
-  result = GST_ELEMENT_SCHEDULER (element);
-  GST_UNLOCK (element);
+  GST_DEBUG_OBJECT (element, "got bus %" GST_PTR_FORMAT, result);
 
   return result;
 }