]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blobdiff - gst/gstelement.c
gstelement: add gst_element_class_add_pad_template_from_static
[glsdk/gstreamer0-10.git] / gst / gstelement.c
index 20eeffae50e303f0a45d96fd2614a005d33c87c2..afa25b9d3717cbe247c5606cf9bbd57e73886cc7 100644 (file)
@@ -25,7 +25,7 @@
  * @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 
+ * 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.
  *
  * 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 existing pad of an element can be retrieved by name with
+ * gst_element_get_static_pad(). A new dynamic pad can be created using
+ * gst_element_request_pad() with a #GstPadTemplate or 
+ * gst_element_get_request_pad() with the template name such as "src_\%d".
  * An iterator of all pads can be retrieved with gst_element_iterate_pads().
  *
  * Elements can be linked through their pads.
@@ -56,8 +59,8 @@
  *
  * 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().
+ * Setting a state triggers a #GstStateChange. 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().
@@ -73,7 +76,7 @@
  * toplevel #GstPipeline so the clock functions are only to be used in very
  * specific situations.
  *
- * Last reviewed on 2006-03-12 (0.10.5)
+ * Last reviewed on 2009-05-29 (0.10.24)
  */
 
 #include "gst_private.h"
 #include <gobject/gvaluecollector.h>
 
 #include "gstelement.h"
+#include "gstelementdetails.h"
+#include "gstenumtypes.h"
 #include "gstbus.h"
 #include "gstmarshal.h"
 #include "gsterror.h"
 #include "gstevent.h"
 #include "gstutils.h"
 #include "gstinfo.h"
+#include "gstvalue.h"
 #include "gst-i18n-lib.h"
 
 /* Element signals and args */
@@ -106,9 +112,15 @@ enum
       /* FILL ME */
 };
 
-extern void __gst_element_details_clear (GstElementDetails * dp);
-extern void __gst_element_details_copy (GstElementDetails * dest,
-    const GstElementDetails * src);
+#ifdef GST_DISABLE_DEPRECATED
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
+#include <libxml/parser.h>
+xmlNodePtr gst_object_save_thyself (const GstObject * object,
+    xmlNodePtr parent);
+GstObject *gst_object_load_thyself (xmlNodePtr parent);
+void gst_pad_load_and_link (xmlNodePtr self, GstObject * parent);
+#endif
+#endif
 
 static void gst_element_class_init (GstElementClass * klass);
 static void gst_element_init (GstElement * element);
@@ -118,8 +130,6 @@ 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 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,
@@ -128,7 +138,16 @@ 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
+static gboolean gst_element_default_send_event (GstElement * element,
+    GstEvent * event);
+static gboolean gst_element_default_query (GstElement * element,
+    GstQuery * query);
+
+static GstPadTemplate
+    * gst_element_class_get_request_pad_template (GstElementClass *
+    element_class, const gchar * name);
+
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
 static xmlNodePtr gst_element_save_thyself (GstObject * object,
     xmlNodePtr parent);
 static void gst_element_restore_thyself (GstObject * parent, xmlNodePtr self);
@@ -137,12 +156,16 @@ static void gst_element_restore_thyself (GstObject * parent, xmlNodePtr self);
 static GstObjectClass *parent_class = NULL;
 static guint gst_element_signals[LAST_SIGNAL] = { 0 };
 
+/* this is used in gstelementfactory.c:gst_element_register() */
+GQuark _gst_elementclass_factory = 0;
+
 GType
 gst_element_get_type (void)
 {
-  static GType gst_element_type = 0;
+  static volatile gsize gst_element_type = 0;
 
-  if (G_UNLIKELY (gst_element_type == 0)) {
+  if (g_once_init_enter (&gst_element_type)) {
+    GType _type;
     static const GTypeInfo element_info = {
       sizeof (GstElementClass),
       gst_element_base_class_init,
@@ -156,8 +179,12 @@ gst_element_get_type (void)
       NULL
     };
 
-    gst_element_type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
+    _type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
         &element_info, G_TYPE_FLAG_ABSTRACT);
+
+    _gst_elementclass_factory =
+        g_quark_from_static_string ("GST_ELEMENTCLASS_FACTORY");
+    g_once_init_leave (&gst_element_type, _type);
   }
   return gst_element_type;
 }
@@ -165,11 +192,10 @@ gst_element_get_type (void)
 static void
 gst_element_class_init (GstElementClass * klass)
 {
-  GObjectClass *gobject_class;
-  GstObjectClass *gstobject_class;
-
-  gobject_class = (GObjectClass *) klass;
-  gstobject_class = (GstObjectClass *) klass;
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
+  GstObjectClass *gstobject_class = (GstObjectClass *) klass;
+#endif
 
   parent_class = g_type_class_peek_parent (klass);
 
@@ -178,12 +204,16 @@ gst_element_class_init (GstElementClass * klass)
    * @gstelement: the object which received the signal
    * @new_pad: the pad that has been added
    *
-   * a new #GstPad has been added to the element.
+   * a new #GstPad has been added to the element. Note that this signal will
+   * usually be emitted from the context of the streaming thread. Also keep in
+   * mind that if you add new elements to the pipeline in the signal handler
+   * you will need to set them to the desired target state with
+   * gst_element_set_state() or gst_element_sync_state_with_parent().
    */
   gst_element_signals[PAD_ADDED] =
       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);
+      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
   /**
    * GstElement::pad-removed:
    * @gstelement: the object which received the signal
@@ -194,31 +224,38 @@ gst_element_class_init (GstElementClass * klass)
   gst_element_signals[PAD_REMOVED] =
       g_signal_new ("pad-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
       G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL,
-      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
+      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
   /**
    * GstElement::no-more-pads:
    * @gstelement: the object which received the signal
    *
    * This signals that the element will not generate more dynamic pads.
+   * Note that this signal will usually be emitted from the context of
+   * the streaming thread.
    */
   gst_element_signals[NO_MORE_PADS] =
       g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, no_more_pads), NULL,
       NULL, gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
 
-  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_element_dispose);
-  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_element_finalize);
+  gobject_class->dispose = gst_element_dispose;
+  gobject_class->finalize = gst_element_finalize;
 
-#ifndef GST_DISABLE_LOADSAVE
-  gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_element_save_thyself);
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
+  gstobject_class->save_thyself =
+      ((gpointer (*)(GstObject * object,
+              gpointer self)) * GST_DEBUG_FUNCPTR (gst_element_save_thyself));
   gstobject_class->restore_thyself =
-      GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
+      ((void (*)(GstObject * object,
+              gpointer self)) *GST_DEBUG_FUNCPTR (gst_element_restore_thyself));
 #endif
 
   klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func);
   klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func);
   klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func);
   klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
+  klass->query = GST_DEBUG_FUNCPTR (gst_element_default_query);
+  klass->send_event = GST_DEBUG_FUNCPTR (gst_element_default_send_event);
   klass->numpadtemplates = 0;
 
   klass->elementfactory = NULL;
@@ -229,8 +266,25 @@ gst_element_base_class_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
+  /* FIXME 0.11: Copy the element details and instead of clearing the
+   * pad template list copy the list and increase the refcount of
+   * the pad templates by one.
+   *
+   * This will make it possible to add pad templates and set element
+   * details in the class_init functions and is the real GObject way
+   * of doing things.
+   * See http://bugzilla.gnome.org/show_bug.cgi?id=491501
+   */
   memset (&element_class->details, 0, sizeof (GstElementDetails));
+  element_class->meta_data = NULL;
   element_class->padtemplates = NULL;
+
+  /* set the factory, see gst_element_register() */
+  element_class->elementfactory =
+      g_type_get_qdata (G_TYPE_FROM_CLASS (element_class),
+      _gst_elementclass_factory);
+  GST_DEBUG ("type %s : factory %p", G_OBJECT_CLASS_NAME (element_class),
+      element_class->elementfactory);
 }
 
 static void
@@ -241,48 +295,27 @@ gst_element_base_class_finalize (gpointer g_class)
   g_list_foreach (klass->padtemplates, (GFunc) gst_object_unref, NULL);
   g_list_free (klass->padtemplates);
   __gst_element_details_clear (&klass->details);
+  if (klass->meta_data) {
+    gst_structure_free (klass->meta_data);
+    klass->meta_data = NULL;
+  }
 }
 
 static void
 gst_element_init (GstElement * element)
 {
   GST_STATE (element) = GST_STATE_NULL;
+  GST_STATE_TARGET (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);
+  /* FIXME 0.11: Store this directly in the instance struct */
+  element->state_lock = g_slice_new (GStaticRecMutex);
   g_static_rec_mutex_init (element->state_lock);
   element->state_cond = g_cond_new ();
 }
 
-/**
- * gst_element_default_error:
- * @object: a #GObject that signalled the error.
- * @orig: the #GstObject that initiated the error.
- * @error: the GError.
- * @debug: an additional debug information string, or %NULL.
- *
- * A default error signal callback to attach to an element.
- * The user data passed to the g_signal_connect is ignored.
- *
- * The default handler will simply print the error string using g_print.
- *
- * MT safe.
- */
-void
-gst_element_default_error (GObject * object, GstObject * source, GError * error,
-    gchar * debug)
-{
-  gchar *name = gst_object_get_path_string (source);
-
-  g_print (_("ERROR: from element %s: %s\n"), name, error->message);
-  if (debug)
-    g_print (_("Additional debug info:\n%s\n"), debug);
-
-  g_free (name);
-}
-
 /**
  * gst_element_release_request_pad:
  * @element: a #GstElement to release the request pad of.
@@ -291,6 +324,10 @@ gst_element_default_error (GObject * object, GstObject * source, GError * error,
  * Makes the element free the previously requested pad as obtained
  * with gst_element_get_request_pad().
  *
+ * This does not unref the pad. If the pad was created by using
+ * gst_element_get_request_pad(), gst_element_release_request_pad() needs to be
+ * followed by gst_object_unref() to free the @pad.
+ *
  * MT safe.
  */
 void
@@ -338,7 +375,7 @@ gst_element_requires_clock (GstElement * element)
  * @element: a #GstElement to query
  *
  * Query if the element provides a clock. A #GstClock provided by an
- * element can be used as the global #GstClock for the pipeline. 
+ * element can be used as the global #GstClock for the pipeline.
  * An element that can provide a clock is only required to do so in the PAUSED
  * state, this means when it is fully negotiated and has allocated the resources
  * to operate the clock.
@@ -363,11 +400,11 @@ gst_element_provides_clock (GstElement * element)
  * gst_element_provide_clock:
  * @element: a #GstElement to query
  *
- * Get the clock provided by the given element. 
+ * Get the clock provided by the given element.
  * <note>An element is only required to provide a clock in the PAUSED
  * state. Some elements can provide a clock in other states.</note>
  *
- * Returns: the GstClock provided by the element or %NULL
+ * Returns: (transfer full): the GstClock provided by the element or %NULL
  * if no clock could be provided.  Unref after usage.
  *
  * MT safe.
@@ -411,10 +448,11 @@ gst_element_set_clock (GstElement * element, GstClock * clock)
   GstClock **clock_p;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (clock == NULL || GST_IS_CLOCK (clock), FALSE);
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
-  GST_DEBUG_OBJECT (element, "setting clock %p", clock);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element, "setting clock %p", clock);
 
   if (oclass->set_clock)
     res = oclass->set_clock (element, clock);
@@ -436,7 +474,7 @@ gst_element_set_clock (GstElement * element, GstClock * clock)
  * Gets the currently configured clock of the element. This is the clock as was
  * last set with gst_element_set_clock().
  *
- * Returns: the #GstClock of the element. unref after usage.
+ * Returns: (transfer full): the #GstClock of the element. unref after usage.
  *
  * MT safe.
  */
@@ -467,14 +505,18 @@ gst_element_get_clock (GstElement * element)
 void
 gst_element_set_base_time (GstElement * element, GstClockTime time)
 {
+  GstClockTime old;
+
   g_return_if_fail (GST_IS_ELEMENT (element));
 
   GST_OBJECT_LOCK (element);
+  old = element->base_time;
   element->base_time = time;
   GST_OBJECT_UNLOCK (element);
 
-  GST_DEBUG_OBJECT (element, "set base_time=%" GST_TIME_FORMAT,
-      GST_TIME_ARGS (time));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element,
+      "set base_time=%" GST_TIME_FORMAT ", old %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (time), GST_TIME_ARGS (old));
 }
 
 /**
@@ -484,7 +526,7 @@ gst_element_set_base_time (GstElement * element, GstClockTime time)
  * 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. Subtracting the base time from the clock time gives
- * the stream time of the element.
+ * the running time of the element.
  *
  * Returns: the base time of the element.
  *
@@ -504,7 +546,73 @@ gst_element_get_base_time (GstElement * element)
   return result;
 }
 
-#ifndef GST_DISABLE_INDEX
+/**
+ * gst_element_set_start_time:
+ * @element: a #GstElement.
+ * @time: the base time to set.
+ *
+ * Set the start time of an element. The start time of the element is the
+ * running time of the element when it last went to the PAUSED state. In READY
+ * or after a flushing seek, it is set to 0.
+ *
+ * Toplevel elements like #GstPipeline will manage the start_time and
+ * base_time on its children. Setting the start_time to #GST_CLOCK_TIME_NONE
+ * on such a toplevel element will disable the distribution of the base_time to
+ * the children and can be useful if the application manages the base_time
+ * itself, for example if you want to synchronize capture from multiple
+ * pipelines, and you can also ensure that the pipelines have the same clock.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_element_set_start_time (GstElement * element, GstClockTime time)
+{
+  GstClockTime old;
+
+  g_return_if_fail (GST_IS_ELEMENT (element));
+
+  GST_OBJECT_LOCK (element);
+  old = GST_ELEMENT_START_TIME (element);
+  GST_ELEMENT_START_TIME (element) = time;
+  GST_OBJECT_UNLOCK (element);
+
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element,
+      "set start_time=%" GST_TIME_FORMAT ", old %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (time), GST_TIME_ARGS (old));
+}
+
+/**
+ * gst_element_get_start_time:
+ * @element: a #GstElement.
+ *
+ * Returns the start time of the element. The start time is the
+ * running time of the clock when this element was last put to PAUSED.
+ *
+ * Usually the start_time is managed by a toplevel element such as
+ * #GstPipeline.
+ *
+ * MT safe.
+ *
+ * Returns: the start time of the element.
+ *
+ * Since: 0.10.24
+ */
+GstClockTime
+gst_element_get_start_time (GstElement * element)
+{
+  GstClockTime result;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
+
+  GST_OBJECT_LOCK (element);
+  result = GST_ELEMENT_START_TIME (element);
+  GST_OBJECT_UNLOCK (element);
+
+  return result;
+}
+
 /**
  * gst_element_is_indexable:
  * @element: a #GstElement.
@@ -530,7 +638,7 @@ gst_element_is_indexable (GstElement * element)
 /**
  * gst_element_set_index:
  * @element: a #GstElement.
- * @index: a #GstIndex.
+ * @index: (transfer none): a #GstIndex.
  *
  * Set @index on the element. The refcount of the index
  * will be increased, any previously set index is unreffed.
@@ -543,7 +651,7 @@ gst_element_set_index (GstElement * element, GstIndex * index)
   GstElementClass *oclass;
 
   g_return_if_fail (GST_IS_ELEMENT (element));
-  g_return_if_fail (GST_IS_INDEX (index));
+  g_return_if_fail (index == NULL || GST_IS_INDEX (index));
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
@@ -557,7 +665,7 @@ gst_element_set_index (GstElement * element, GstIndex * index)
  *
  * Gets the index from the element.
  *
- * Returns: a #GstIndex or %NULL when no index was set on the
+ * Returns: (transfer full): a #GstIndex or %NULL when no index was set on the
  * element. unref after usage.
  *
  * MT safe.
@@ -577,12 +685,11 @@ gst_element_get_index (GstElement * element)
 
   return result;
 }
-#endif
 
 /**
  * gst_element_add_pad:
  * @element: a #GstElement to add the pad to.
- * @pad: the #GstPad to add to the element.
+ * @pad: (transfer full): the #GstPad to add to the element.
  *
  * Adds a pad (link point) to @element. @pad's parent will be set to @element;
  * see gst_object_set_parent() for refcounting information.
@@ -605,6 +712,7 @@ gboolean
 gst_element_add_pad (GstElement * element, GstPad * pad)
 {
   gchar *pad_name;
+  gboolean flushing;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
@@ -614,6 +722,7 @@ gst_element_add_pad (GstElement * element, GstPad * 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));
+  flushing = GST_PAD_IS_FLUSHING (pad);
   GST_OBJECT_UNLOCK (pad);
 
   /* then check to see if there's already a pad by that name here */
@@ -626,6 +735,18 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
               GST_OBJECT_CAST (element))))
     goto had_parent;
 
+  /* check for flushing pads */
+  if (flushing && (GST_STATE (element) > GST_STATE_READY ||
+          GST_STATE_NEXT (element) == GST_STATE_PAUSED)) {
+    g_warning ("adding flushing pad '%s' to running element '%s', you need to "
+        "use gst_pad_set_active(pad,TRUE) before adding it.",
+        GST_STR_NULL (pad_name), GST_ELEMENT_NAME (element));
+    /* unset flushing */
+    GST_OBJECT_LOCK (pad);
+    GST_PAD_UNSET_FLUSHING (pad);
+    GST_OBJECT_UNLOCK (pad);
+  }
+
   g_free (pad_name);
 
   /* add it to the list */
@@ -684,7 +805,7 @@ no_direction:
 /**
  * gst_element_remove_pad:
  * @element: a #GstElement to remove pad from.
- * @pad: the #GstPad to remove from the element.
+ * @pad: (transfer none): the #GstPad to remove from the element.
  *
  * Removes @pad from @element. @pad will be destroyed if it has not been
  * referenced elsewhere using gst_object_unparent().
@@ -695,8 +816,9 @@ no_direction:
  * gst_element_release_request_pad() function instead.
  *
  * Pads are not automatically deactivated so elements should perform the needed
- * steps to deactivate the pad in case this pad is removed in the PAUSED or PLAYING
- * state. See gst_pad_set_active() for more information about deactivating pads.
+ * steps to deactivate the pad in case this pad is removed in the PAUSED or
+ * PLAYING state. See gst_pad_set_active() for more information about
+ * deactivating pads.
  *
  * The pad and the element should be unlocked when calling this function.
  *
@@ -819,8 +941,8 @@ pad_compare_name (GstPad * pad1, const gchar * name)
  * Retrieves a pad from @element by name. This version only retrieves
  * already-existing (i.e. 'static') pads.
  *
- * Returns: the requested #GstPad if found, otherwise %NULL. unref after
- * usage.
+ * Returns: (transfer full): the requested #GstPad if found, otherwise %NULL.
+ *     unref after usage.
  *
  * MT safe.
  */
@@ -854,15 +976,66 @@ gst_element_get_static_pad (GstElement * element, const gchar * name)
 }
 
 static GstPad *
-gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
-    const gchar * name)
+_gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
+    const gchar * name, const GstCaps * caps)
 {
   GstPad *newpad = NULL;
   GstElementClass *oclass;
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
-  if (oclass->request_new_pad)
+#ifndef G_DISABLE_CHECKS
+  /* Some sanity checking here */
+  if (name) {
+    GstPad *pad;
+
+    /* Is this the template name? */
+    if (strstr (name, "%") || !strchr (templ->name_template, '%')) {
+      g_return_val_if_fail (strcmp (name, templ->name_template) == 0, NULL);
+    } else {
+      const gchar *str, *data;
+      gchar *endptr;
+
+      /* Otherwise check if it's a valid name for the name template */
+      str = strchr (templ->name_template, '%');
+      g_return_val_if_fail (str != NULL, NULL);
+      g_return_val_if_fail (strncmp (templ->name_template, name,
+              str - templ->name_template) == 0, NULL);
+      g_return_val_if_fail (strlen (name) > str - templ->name_template, NULL);
+
+      data = name + (str - templ->name_template);
+
+      /* Can either be %s or %d or %u, do sanity checking for %d */
+      if (*(str + 1) == 'd') {
+        gint64 tmp;
+
+        /* it's an int */
+        tmp = g_ascii_strtoll (data, &endptr, 10);
+        g_return_val_if_fail (tmp >= G_MININT && tmp <= G_MAXINT
+            && *endptr == '\0', NULL);
+      } else if (*(str + 1) == 'u') {
+        guint64 tmp;
+
+        /* it's an int */
+        tmp = g_ascii_strtoull (data, &endptr, 10);
+        g_return_val_if_fail (tmp <= G_MAXUINT && *endptr == '\0', NULL);
+      }
+    }
+
+    pad = gst_element_get_static_pad (element, name);
+    if (pad) {
+      gst_object_unref (pad);
+      /* FIXME 0.11: Change this to g_return_val_if_fail() */
+      g_critical ("Element %s already has a pad named %s, the behaviour of "
+          " gst_element_get_request_pad() for existing pads is undefined!",
+          GST_ELEMENT_NAME (element), name);
+    }
+  }
+#endif
+
+  if (oclass->request_new_pad_full)
+    newpad = (oclass->request_new_pad_full) (element, templ, name, caps);
+  else if (oclass->request_new_pad)
     newpad = (oclass->request_new_pad) (element, templ, name);
 
   if (newpad)
@@ -876,11 +1049,15 @@ gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
  * @element: a #GstElement to find a request pad of.
  * @name: the name of the request #GstPad to retrieve.
  *
- * Retrieves a pad from the element by name. This version only retrieves
- * request pads. The pad should be released with 
+ * Retrieves a pad from the element by name (e.g. "src_\%d"). This version only
+ * retrieves request pads. The pad should be released with
  * gst_element_release_request_pad().
  *
- * Returns: requested #GstPad if found, otherwise %NULL. Release after usage.
+ * This method is slow and will be deprecated in the future. New code should
+ * use gst_element_request_pad() with the requested template.
+ *
+ * Returns: (transfer full): requested #GstPad if found, otherwise %NULL.
+ *     Release after usage.
  */
 GstPad *
 gst_element_get_request_pad (GstElement * element, const gchar * name)
@@ -890,7 +1067,6 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
   const gchar *req_name = NULL;
   gboolean templ_found = FALSE;
   GList *list;
-  gint n;
   const gchar *data;
   gchar *str, *endptr = NULL;
   GstElementClass *class;
@@ -900,30 +1076,52 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
 
   class = GST_ELEMENT_GET_CLASS (element);
 
+  /* if the name contains a %, we assume it's the complete template name. Get
+   * the template and try to get a pad */
   if (strstr (name, "%")) {
-    templ = gst_element_class_get_pad_template (class, name);
+    templ = gst_element_class_get_request_pad_template (class, name);
     req_name = NULL;
     if (templ)
       templ_found = TRUE;
   } else {
-    list = gst_element_class_get_pad_template_list (class);
+    /* there is no % in the name, try to find a matching template */
+    list = class->padtemplates;
     while (!templ_found && list) {
       templ = (GstPadTemplate *) list->data;
       if (templ->presence == GST_PAD_REQUEST) {
-        /* Because of sanity checks in gst_pad_template_new(), we know that %s
-           and %d, occurring at the end of the name_template, are the only
-           possibilities. */
         GST_CAT_DEBUG (GST_CAT_PADS, "comparing %s to %s", name,
             templ->name_template);
-        if ((str = strchr (templ->name_template, '%'))
+        /* see if we find an exact match */
+        if (strcmp (name, templ->name_template) == 0) {
+          templ_found = TRUE;
+          req_name = name;
+          break;
+        }
+        /* Because of sanity checks in gst_pad_template_new(), we know that %s
+           and %d and %u, occurring at the end of the name_template, are the only
+           possibilities. */
+        else if ((str = strchr (templ->name_template, '%'))
             && strncmp (templ->name_template, name,
                 str - templ->name_template) == 0
             && strlen (name) > str - templ->name_template) {
           data = name + (str - templ->name_template);
           if (*(str + 1) == 'd') {
+            glong tmp;
+
+            /* it's an int */
+            tmp = strtol (data, &endptr, 10);
+            if (tmp != G_MINLONG && tmp != G_MAXLONG && endptr &&
+                *endptr == '\0') {
+              templ_found = TRUE;
+              req_name = name;
+              break;
+            }
+          } else if (*(str + 1) == 'u') {
+            gulong tmp;
+
             /* it's an int */
-            n = (gint) strtol (data, &endptr, 10);
-            if (endptr && *endptr == '\0') {
+            tmp = strtoul (data, &endptr, 10);
+            if (tmp != G_MAXULONG && endptr && *endptr == '\0') {
               templ_found = TRUE;
               req_name = name;
               break;
@@ -943,11 +1141,45 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
   if (!templ_found)
     return NULL;
 
-  pad = gst_element_request_pad (element, templ, req_name);
+  pad = _gst_element_request_pad (element, templ, req_name, NULL);
 
   return pad;
 }
 
+/**
+ * gst_element_request_pad:
+ * @element: a #GstElement to find a request pad of.
+ * @templ: a #GstPadTemplate of which we want a pad of.
+ * @name: (transfer none) (allow-none): the name of the request #GstPad
+ * to retrieve. Can be %NULL.
+ * @caps: (transfer none) (allow-none): the caps of the pad we want to
+ * request. Can be %NULL.
+ *
+ * Retrieves a request pad from the element according to the provided template.
+ * Pad templates can be looked up using
+ * gst_element_factory_get_static_pad_templates().
+ *
+ * If the @caps are specified and the element implements thew new
+ * request_new_pad_full virtual method, the element will use them to select
+ * which pad to create.
+ *
+ * The pad should be released with gst_element_release_request_pad().
+ *
+ * Returns: (transfer full): requested #GstPad if found, otherwise %NULL.
+ *     Release after usage.
+ *
+ * Since: 0.10.32
+ */
+GstPad *
+gst_element_request_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+  g_return_val_if_fail (templ != NULL, NULL);
+
+  return _gst_element_request_pad (element, templ, name, caps);
+}
+
 /**
  * gst_element_get_pad:
  * @element: a #GstElement.
@@ -956,13 +1188,18 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
  * Retrieves a pad from @element by name. Tries gst_element_get_static_pad()
  * first, then gst_element_get_request_pad().
  *
- * <note>Usage of this function is not recommended as it is unclear if the reference
+ * Deprecated: This function is deprecated as it's unclear if the reference
  * to the result pad should be released with gst_object_unref() in case of a static pad
- * or gst_element_release_request_pad() in case of a request pad.</note>
+ * or gst_element_release_request_pad() in case of a request pad.
+ * Use gst_element_get_static_pad() or gst_element_get_request_pad() instead.
  *
- * Returns: the #GstPad if found, otherwise %NULL. Unref or Release after usage,
+ * Returns: (transfer full): the #GstPad if found, otherwise %NULL. Unref or Release after usage,
  * depending on the type of the pad.
  */
+#ifndef GST_REMOVE_DEPRECATED
+#ifdef GST_DISABLE_DEPRECATED
+GstPad *gst_element_get_pad (GstElement * element, const gchar * name);
+#endif
 GstPad *
 gst_element_get_pad (GstElement * element, const gchar * name)
 {
@@ -977,6 +1214,7 @@ gst_element_get_pad (GstElement * element, const gchar * name)
 
   return pad;
 }
+#endif /* GST_REMOVE_DEPRECATED */
 
 static GstIteratorItem
 iterate_pad (GstIterator * it, GstPad * pad)
@@ -1008,10 +1246,12 @@ gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
  * gst_element_iterate_pads:
  * @element: a #GstElement to iterate pads of.
  *
- * Retrieves an iterattor of @element's pads. The iterator should
- * be freed after usage.
+ * Retrieves an iterator of @element's pads. The iterator should
+ * be freed after usage. Also more specialized iterators exists such as
+ * gst_element_iterate_src_pads() or gst_element_iterate_sink_pads().
  *
- * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ *     after use.
  *
  * MT safe.
  */
@@ -1029,7 +1269,8 @@ gst_element_iterate_pads (GstElement * element)
  *
  * Retrieves an iterator of @element's source pads.
  *
- * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ *     after use.
  *
  * MT safe.
  */
@@ -1047,7 +1288,8 @@ gst_element_iterate_src_pads (GstElement * element)
  *
  * Retrieves an iterator of @element's sink pads.
  *
- * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ *     after use.
  *
  * MT safe.
  */
@@ -1062,7 +1304,7 @@ gst_element_iterate_sink_pads (GstElement * element)
 /**
  * gst_element_class_add_pad_template:
  * @klass: the #GstElementClass to add the pad template to.
- * @templ: a #GstPadTemplate to add to the element class.
+ * @templ: (transfer none): a #GstPadTemplate to add to the element class.
  *
  * Adds a padtemplate to an element class. This is mainly used in the _base_init
  * functions of classes.
@@ -1074,6 +1316,12 @@ gst_element_class_add_pad_template (GstElementClass * klass,
   g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
   g_return_if_fail (GST_IS_PAD_TEMPLATE (templ));
 
+  /* FIXME 0.11: allow replacing the pad templates by
+   * calling this with the same name as an already existing pad
+   * template. For this we _must_ _not_ ref the added pad template
+   * a second time and _must_ document that this function takes
+   * ownership of the pad template. Otherwise we will leak pad templates
+   * or the caller unref's the pad template and it disappears */
   /* avoid registering pad templates with the same name */
   g_return_if_fail (gst_element_class_get_pad_template (klass,
           templ->name_template) == NULL);
@@ -1083,6 +1331,82 @@ gst_element_class_add_pad_template (GstElementClass * klass,
   klass->numpadtemplates++;
 }
 
+/**
+ * gst_element_class_add_static_pad_template:
+ * @klass: the #GstElementClass to add the pad template to.
+ * @templ: (transfer none): a #GstStaticPadTemplate describing the pad
+ * to add to the element class.
+ *
+ * Adds a padtemplate to an element class. This is mainly used in the _base_init
+ * functions of classes.
+ *
+ * Since: 0.10.36
+ */
+void
+gst_element_class_add_static_pad_template (GstElementClass * klass,
+    GstStaticPadTemplate * templ)
+{
+  GstPadTemplate *pt;
+
+  g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+  pt = gst_static_pad_template_get (templ);
+  gst_element_class_add_pad_template (klass, pt);
+  gst_object_unref (pt);
+}
+
+static void
+gst_element_class_add_meta_data (GstElementClass * klass,
+    const gchar * key, const gchar * value)
+{
+  if (!klass->meta_data) {
+    /* FIXME: use a quark for "metadata" */
+    klass->meta_data = gst_structure_empty_new ("metadata");
+  }
+
+  gst_structure_set ((GstStructure *) klass->meta_data,
+      key, G_TYPE_STRING, value, NULL);
+}
+
+/**
+ * gst_element_class_set_documentation_uri:
+ * @klass: class to set details for
+ * @uri: uri of element documentation
+ *
+ * Set uri pointing to user documentation. Applications can use this to show
+ * help for e.g. effects to users.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_element_class_set_documentation_uri (GstElementClass * klass,
+    const gchar * uri)
+{
+  g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+  gst_element_class_add_meta_data (klass, "doc-uri", uri);
+}
+
+/**
+ * gst_element_class_set_icon_name:
+ * @klass: class to set details for
+ * @name: name of an icon
+ *
+ * Elements that bridge to certain other products can include an icon of that
+ * used product. Application can show the icon in menus/selectors to help
+ * identifying specific elements.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_element_class_set_icon_name (GstElementClass * klass, const gchar * name)
+{
+  g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+  gst_element_class_add_meta_data (klass, "icon-name", name);
+}
+
+/* FIXME-0.11: deprecate and remove gst_element_class_set_details*() */
 /**
  * gst_element_class_set_details:
  * @klass: class to set details for
@@ -1092,7 +1416,14 @@ gst_element_class_add_pad_template (GstElementClass * klass,
  * <note>This function is for use in _base_init functions only.</note>
  *
  * The @details are copied.
+ *
+ * Deprecated: Use gst_element_class_set_details_simple() instead.
  */
+#ifndef GST_REMOVE_DEPRECATED
+#ifdef GST_DISABLE_DEPRECATED
+void gst_element_class_set_details (GstElementClass * klass,
+    const GstElementDetails * details);
+#endif
 void
 gst_element_class_set_details (GstElementClass * klass,
     const GstElementDetails * details)
@@ -1102,6 +1433,42 @@ gst_element_class_set_details (GstElementClass * klass,
 
   __gst_element_details_copy (&klass->details, details);
 }
+#endif
+
+/**
+ * gst_element_class_set_details_simple:
+ * @klass: class to set details for
+ * @longname: The long English name of the element. E.g. "File Sink"
+ * @classification: String describing the type of element, as an unordered list
+ * separated with slashes ('/'). See draft-klass.txt of the design docs
+ * for more details and common types. E.g: "Sink/File"
+ * @description: Sentence describing the purpose of the element.
+ * E.g: "Write stream to a file"
+ * @author: Name and contact details of the author(s). Use \n to separate
+ * multiple author details. E.g: "Joe Bloggs &lt;joe.blogs at foo.com&gt;"
+ *
+ * Sets the detailed information for a #GstElementClass. Simpler version of
+ * gst_element_class_set_details() that generates less linker overhead.
+ * <note>This function is for use in _base_init functions only.</note>
+ *
+ * The detail parameter strings are copied into the #GstElementDetails for
+ * the element class.
+ *
+ * Since: 0.10.14
+ */
+void
+gst_element_class_set_details_simple (GstElementClass * klass,
+    const gchar * longname, const gchar * classification,
+    const gchar * description, const gchar * author)
+{
+  const GstElementDetails details =
+      GST_ELEMENT_DETAILS ((gchar *) longname, (gchar *) classification,
+      (gchar *) description, (gchar *) author);
+
+  g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+  __gst_element_details_copy (&klass->details, &details);
+}
 
 /**
  * gst_element_class_get_pad_template_list:
@@ -1113,7 +1480,8 @@ gst_element_class_set_details (GstElementClass * klass,
  * that has subclasses, make sure to pass the g_class parameter of the
  * #GInstanceInitFunc here.</note>
  *
- * Returns: the #GList of padtemplates.
+ * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of
+ *     pad templates.
  */
 GList *
 gst_element_class_get_pad_template_list (GstElementClass * element_class)
@@ -1133,19 +1501,19 @@ gst_element_class_get_pad_template_list (GstElementClass * element_class)
  * that has subclasses, make sure to pass the g_class parameter of the
  * #GInstanceInitFunc here.</note>
  *
- * Returns: the #GstPadTemplate with the given name, or %NULL if none was found.
- * No unreferencing is necessary.
+ * Returns: (transfer none): the #GstPadTemplate with the given name, or %NULL
+ *     if none was found. No unreferencing is necessary.
  */
 GstPadTemplate *
-gst_element_class_get_pad_template (GstElementClass * element_class,
-    const gchar * name)
+gst_element_class_get_pad_template (GstElementClass *
+    element_class, const gchar * name)
 {
   GList *padlist;
 
   g_return_val_if_fail (GST_IS_ELEMENT_CLASS (element_class), NULL);
   g_return_val_if_fail (name != NULL, NULL);
 
-  padlist = gst_element_class_get_pad_template_list (element_class);
+  padlist = element_class->padtemplates;
 
   while (padlist) {
     GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data;
@@ -1159,12 +1527,25 @@ gst_element_class_get_pad_template (GstElementClass * element_class,
   return NULL;
 }
 
+static GstPadTemplate *
+gst_element_class_get_request_pad_template (GstElementClass *
+    element_class, const gchar * name)
+{
+  GstPadTemplate *tmpl;
+
+  tmpl = gst_element_class_get_pad_template (element_class, name);
+  if (tmpl != NULL && tmpl->presence == GST_PAD_REQUEST)
+    return tmpl;
+
+  return NULL;
+}
+
 /* get a random pad on element of the given direction.
  * The pad is random in a sense that it is the first pad that is (optionaly) linked.
  */
 static GstPad *
-gst_element_get_random_pad (GstElement * element, gboolean need_linked,
-    GstPadDirection dir)
+gst_element_get_random_pad (GstElement * element,
+    gboolean need_linked, GstPadDirection dir)
 {
   GstPad *result = NULL;
   GList *pads;
@@ -1221,14 +1602,41 @@ wrong_direction:
   }
 }
 
+static gboolean
+gst_element_default_send_event (GstElement * element, GstEvent * event)
+{
+  gboolean result = FALSE;
+  GstPad *pad;
+
+  pad = GST_EVENT_IS_DOWNSTREAM (event) ?
+      gst_element_get_random_pad (element, TRUE, GST_PAD_SRC) :
+      gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
+
+  if (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_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_event_unref (event);
+  }
+  return result;
+}
+
 /**
  * gst_element_send_event:
  * @element: a #GstElement to send the event to.
- * @event: the #GstEvent to send to the element.
+ * @event: (transfer full): the #GstEvent to send to the element.
  *
  * Sends an event to an element. If the element doesn't implement an
  * event handler, the event will be pushed on a random linked sink pad for
- * upstream events or a random linked source pad for downstream events. 
+ * upstream events or a random linked source pad for downstream events.
  *
  * This function takes owership of the provided event so you should
  * gst_event_ref() it if you want to reuse the event after this call.
@@ -1248,29 +1656,16 @@ gst_element_send_event (GstElement * element, GstEvent * event)
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
+  GST_STATE_LOCK (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_EVENT_IS_DOWNSTREAM (event) ?
-        gst_element_get_random_pad (element, TRUE, GST_PAD_SRC) :
-        gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
-
-    if (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_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));
-    }
+    result = gst_element_default_send_event (element, event);
   }
+  GST_STATE_UNLOCK (element);
+
   return result;
 }
 
@@ -1352,10 +1747,37 @@ gst_element_get_query_types (GstElement * element)
   return result;
 }
 
+static gboolean
+gst_element_default_query (GstElement * element, GstQuery * query)
+{
+  gboolean result = FALSE;
+  GstPad *pad;
+
+  pad = gst_element_get_random_pad (element, FALSE, GST_PAD_SRC);
+  if (pad) {
+    result = gst_pad_query (pad, query);
+
+    gst_object_unref (pad);
+  } else {
+    pad = gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
+    if (pad) {
+      GstPad *peer = gst_pad_get_peer (pad);
+
+      if (peer) {
+        result = gst_pad_query (peer, query);
+
+        gst_object_unref (peer);
+      }
+      gst_object_unref (pad);
+    }
+  }
+  return result;
+}
+
 /**
  * gst_element_query:
  * @element: a #GstElement to perform the query on.
- * @query: the #GstQuery.
+ * @query: (transfer none): the #GstQuery.
  *
  * Performs a query on the given element.
  *
@@ -1363,6 +1785,8 @@ gst_element_get_query_types (GstElement * element)
  * forwards the query to a random srcpad or to the peer of a
  * random linked sinkpad of this element.
  *
+ * Please note that some queries might need a running pipeline to work.
+ *
  * Returns: TRUE if the query could be performed.
  *
  * MT safe.
@@ -1383,25 +1807,7 @@ gst_element_query (GstElement * element, GstQuery * query)
         GST_ELEMENT_NAME (element));
     result = oclass->query (element, query);
   } else {
-    GstPad *pad = gst_element_get_random_pad (element, FALSE, GST_PAD_SRC);
-
-    if (pad) {
-      result = gst_pad_query (pad, query);
-
-      gst_object_unref (pad);
-    } else {
-      pad = gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
-      if (pad) {
-        GstPad *peer = gst_pad_get_peer (pad);
-
-        if (peer) {
-          result = gst_pad_query (peer, query);
-
-          gst_object_unref (peer);
-        }
-        gst_object_unref (pad);
-      }
-    }
+    result = gst_element_default_query (element, query);
   }
   return result;
 }
@@ -1409,7 +1815,7 @@ gst_element_query (GstElement * element, GstQuery * query)
 /**
  * gst_element_post_message:
  * @element: a #GstElement posting the message
- * @message: a #GstMessage to post
+ * @message: (transfer full): a #GstMessage to post
  *
  * 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
@@ -1448,7 +1854,8 @@ gst_element_post_message (GstElement * element, GstMessage * message)
   /* ERRORS */
 no_bus:
   {
-    GST_DEBUG_OBJECT (element, "not posting message %p: no bus", message);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_MESSAGE, element,
+        "not posting message %p: no bus", message);
     GST_OBJECT_UNLOCK (element);
     gst_message_unref (message);
     return FALSE;
@@ -1461,7 +1868,8 @@ no_bus:
  *
  * 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 ""
+ * Returns: (transfer full): a newly allocated string, or %NULL if the format
+ *     was %NULL or ""
  *
  * MT safe.
  */
@@ -1488,15 +1896,20 @@ _gst_element_error_printf (const gchar * format, ...)
  * @type:     the #GstMessageType
  * @domain:   the GStreamer GError domain this message belongs to
  * @code:     the GError code belonging to the domain
- * @text:     an allocated text string to be used as a replacement for the
- *            default message connected to code, or %NULL
- * @debug:    an allocated debug message to be used as a replacement for the
- *            default debugging information, or %NULL
+ * @text:     (allow-none) (transfer full): an allocated text string to be used
+ *            as a replacement for the default message connected to code,
+ *            or %NULL
+ * @debug:    (allow-none) (transfer full): an allocated debug message to be
+ *            used as a replacement for the default debugging information,
+ *            or %NULL
  * @file:     the source code file where the error was generated
  * @function: the source code function where the error was generated
  * @line:     the source code line where the error was generated
  *
- * Post an error or warning message on the bus from inside an element.
+ * Post an error, warning or info message on the bus from inside an element.
+ *
+ * @type must be of #GST_MESSAGE_ERROR, #GST_MESSAGE_WARNING or
+ * #GST_MESSAGE_INFO.
  *
  * MT safe.
  */
@@ -1513,10 +1926,10 @@ void gst_element_message_full
   GstMessage *message = NULL;
 
   /* checks */
-  GST_DEBUG_OBJECT (element, "start");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_MESSAGE, element, "start");
   g_return_if_fail (GST_IS_ELEMENT (element));
   g_return_if_fail ((type == GST_MESSAGE_ERROR) ||
-      (type == GST_MESSAGE_WARNING));
+      (type == GST_MESSAGE_WARNING) || (type == GST_MESSAGE_INFO));
 
   /* check if we send the given text or the default error text */
   if ((text == NULL) || (text[0] == 0)) {
@@ -1547,14 +1960,22 @@ void gst_element_message_full
       sent_text);
   gerror = g_error_new_literal (domain, code, sent_text);
 
-  if (type == GST_MESSAGE_ERROR) {
-    message =
-        gst_message_new_error (GST_OBJECT_CAST (element), gerror, sent_debug);
-  } else if (type == GST_MESSAGE_WARNING) {
-    message = gst_message_new_warning (GST_OBJECT_CAST (element), gerror,
-        sent_debug);
-  } else {
-    g_assert_not_reached ();
+  switch (type) {
+    case GST_MESSAGE_ERROR:
+      message =
+          gst_message_new_error (GST_OBJECT_CAST (element), gerror, sent_debug);
+      break;
+    case GST_MESSAGE_WARNING:
+      message = gst_message_new_warning (GST_OBJECT_CAST (element), gerror,
+          sent_debug);
+      break;
+    case GST_MESSAGE_INFO:
+      message = gst_message_new_info (GST_OBJECT_CAST (element), gerror,
+          sent_debug);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
   }
   gst_element_post_message (element, message);
 
@@ -1605,7 +2026,7 @@ gst_element_is_locked_state (GstElement * element)
  *
  * MT safe.
  *
- * Returns: TRUE if the state was changed, FALSE if bad parameterss were given
+ * Returns: TRUE if the state was changed, FALSE if bad parameters were given
  * or the elements state-locking needed no change.
  */
 gboolean
@@ -1635,11 +2056,14 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
   return TRUE;
 
 was_ok:
-  GST_CAT_DEBUG (GST_CAT_STATES, "elements %s was in locked state %d",
-      GST_ELEMENT_NAME (element), old);
-  GST_OBJECT_UNLOCK (element);
+  {
+    GST_CAT_DEBUG (GST_CAT_STATES,
+        "elements %s was already in locked state %d",
+        GST_ELEMENT_NAME (element), old);
+    GST_OBJECT_UNLOCK (element);
 
-  return FALSE;
+    return FALSE;
+  }
 }
 
 /**
@@ -1657,38 +2081,52 @@ gboolean
 gst_element_sync_state_with_parent (GstElement * element)
 {
   GstElement *parent;
+  GstState target;
+  GstStateChangeReturn ret;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   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),
+    /* set to pending if there is one, else we set it to the current state of
+     * the parent */
+    if (parent_pending != GST_STATE_VOID_PENDING)
+      target = parent_pending;
+    else
+      target = parent_current;
+
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+        "syncing state (%s) to parent %s %s (%s, %s)",
         gst_element_state_get_name (GST_STATE (element)),
-        GST_ELEMENT_NAME (parent), gst_element_state_get_name (parent_current),
+        GST_ELEMENT_NAME (parent), gst_element_state_get_name (target),
+        gst_element_state_get_name (parent_current),
         gst_element_state_get_name (parent_pending));
 
-    ret = gst_element_set_state (element, parent_current);
+    ret = gst_element_set_state (element, target);
     if (ret == GST_STATE_CHANGE_FAILURE)
       goto failed;
 
     gst_object_unref (parent);
 
     return TRUE;
+  } else {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "element has no parent");
   }
   return FALSE;
 
   /* ERROR */
 failed:
   {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+        "syncing state failed (%s)",
+        gst_element_state_change_return_get_name (ret));
+    gst_object_unref (parent);
     return FALSE;
   }
 }
@@ -1701,16 +2139,19 @@ gst_element_get_state_func (GstElement * element,
   GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
   GstState old_pending;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state, timeout %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (timeout));
 
   GST_OBJECT_LOCK (element);
   ret = GST_STATE_RETURN (element);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "RETURN is %s",
+      gst_element_state_change_return_get_name (ret));
 
-  /* we got an error, report immediatly */
+  /* we got an error, report immediately */
   if (ret == GST_STATE_CHANGE_FAILURE)
     goto done;
 
-  /* we got no_preroll, report immediatly */
+  /* we got no_preroll, report immediately */
   if (ret == GST_STATE_CHANGE_NO_PREROLL)
     goto done;
 
@@ -1736,7 +2177,7 @@ gst_element_get_state_func (GstElement * element,
     } else {
       timeval = NULL;
     }
-    /* get cookie to dected state change during waiting */
+    /* get cookie to detect state changes during waiting */
     cookie = element->state_cookie;
 
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
@@ -1760,11 +2201,11 @@ gst_element_get_state_func (GstElement * element,
         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_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
-    ret = GST_STATE_CHANGE_SUCCESS;
+    /* if nothing is pending anymore we can return SUCCESS */
+    if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
+      GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
+      ret = GST_STATE_CHANGE_SUCCESS;
+    }
   }
 
 done:
@@ -1789,7 +2230,7 @@ interrupted:
     if (pending)
       *pending = GST_STATE_VOID_PENDING;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "get_state() interruped");
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interruped");
 
     GST_OBJECT_UNLOCK (element);
 
@@ -1800,9 +2241,10 @@ interrupted:
 /**
  * gst_element_get_state:
  * @element: a #GstElement to get the state of.
- * @state: a pointer to #GstState to hold the state. Can be %NULL.
- * @pending: a pointer to #GstState to hold the pending state.
- *           Can be %NULL.
+ * @state: (out) (allow-none): a pointer to #GstState to hold the state.
+ *     Can be %NULL.
+ * @pending: (out) (allow-none): 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.
  *
@@ -1820,13 +2262,12 @@ interrupted:
  *
  * 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.
+ * This mostly happens for live sources that only produce data in
+ * %GST_STATE_PLAYING. 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
@@ -1905,38 +2346,59 @@ nothing_aborted:
   }
 }
 
+/* Not static because GstBin has manual state handling too */
+void
+_priv_gst_element_state_changed (GstElement * element, GstState oldstate,
+    GstState newstate, GstState pending)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+  GstMessage *message;
+
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+      "notifying about state-changed %s to %s (%s pending)",
+      gst_element_state_get_name (oldstate),
+      gst_element_state_get_name (newstate),
+      gst_element_state_get_name (pending));
+
+  if (klass->state_changed)
+    klass->state_changed (element, oldstate, newstate, pending);
+
+  message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
+      oldstate, newstate, pending);
+  gst_element_post_message (element, message);
+}
+
 /**
- * gst_element_continue_state:   
- * @element: a #GstElement to continue the state change of.      
+ * 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 %GST_STATE_CHANGE_SUCCESS from the state change function.      
- * Elements that return %GST_STATE_CHANGE_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.      
+ *
+ * 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 %GST_STATE_CHANGE_SUCCESS from the state change function.
+ *
+ * If after calling this method the element still has not reached
+ * the pending state, the next state change is performed.
+ *
+ * This method is used internally and should normally not be called by plugins
+ * or applications.
+ *
+ * Returns: The result of the commit state change.
+ *
+ * MT safe.
  */
 GstStateChangeReturn
 gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
 {
-  GstState pending;
-  GstState old_ret, old_state, old_next;
-  GstState current, next;
-  GstMessage *message;
+  GstStateChangeReturn old_ret;
+  GstState old_state, old_next;
+  GstState current, next, pending;
   GstStateChange transition;
 
   GST_OBJECT_LOCK (element);
-  old_ret = (GstState) GST_STATE_RETURN (element);
+  old_ret = GST_STATE_RETURN (element);
   GST_STATE_RETURN (element) = ret;
   pending = GST_STATE_PENDING (element);
 
@@ -1963,14 +2425,12 @@ gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
   GST_OBJECT_UNLOCK (element);
 
   GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-      "committing state from %s to %s, pending %s",
+      "committing state from %s to %s, pending %s, next %s",
       gst_element_state_get_name (old_state),
       gst_element_state_get_name (old_next),
-      gst_element_state_get_name (pending));
+      gst_element_state_get_name (pending), gst_element_state_get_name (next));
 
-  message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
-      old_state, old_next, pending);
-  gst_element_post_message (element, message);
+  _priv_gst_element_state_changed (element, old_state, old_next, pending);
 
   GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
       "continue state change %s to %s, final %s",
@@ -1992,20 +2452,19 @@ complete:
     GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
     GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "completed state change");
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+        "completed state change to %s", gst_element_state_get_name (pending));
     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 
+     * 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_CAST (element),
-          old_state, old_next, GST_STATE_VOID_PENDING);
-      gst_element_post_message (element, message);
-    }
+    if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC)
+      _priv_gst_element_state_changed (element, old_state, old_next,
+          GST_STATE_VOID_PENDING);
 
     GST_STATE_BROADCAST (element);
 
@@ -2014,52 +2473,74 @@ complete:
 }
 
 /**
- * gst_element_lost_state:
+ * gst_element_lost_state_full:
  * @element: a #GstElement the state is lost of
+ * @new_base_time: if a new base time should be distributed
  *
  * 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 %GST_STATE_CHANGE_ASYNC.
  *
+ * An ASYNC_START message is posted with indication to distribute a new
+ * base_time to the element when @new_base_time is %TRUE.
+ * If the element was PLAYING, it will go to PAUSED. The element
+ * will be restored to its PLAYING state by the parent pipeline when it
+ * prerolls again.
+ *
  * This is mostly used for elements that lost their preroll buffer
- * in the %GST_STATE_PAUSED state after a flush, they become %GST_STATE_PAUSED
- * again if a new preroll buffer is queued.
- * This function can only be called when the element is currently
+ * in the %GST_STATE_PAUSED or %GST_STATE_PLAYING state after a flush,
+ * they will go to their pending state again when a new preroll buffer is
+ * queued. This function can only be called when the element is currently
  * not in error or an async state change.
  *
- * This function can only be called with the STATE_LOCK held.
+ * This function is used internally and should normally not be called from
+ * plugins or applications.
  *
  * MT safe.
+ *
+ * Since: 0.10.24
  */
 void
-gst_element_lost_state (GstElement * element)
+gst_element_lost_state_full (GstElement * element, gboolean new_base_time)
 {
-  GstState current_state;
+  GstState old_state, new_state;
   GstMessage *message;
 
   g_return_if_fail (GST_IS_ELEMENT (element));
 
   GST_OBJECT_LOCK (element);
-  if (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING ||
-      GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+  if (GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
     goto nothing_lost;
 
-  current_state = GST_STATE (element);
+  if (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING)
+    goto only_async_start;
+
+  old_state = GST_STATE (element);
+
+  /* when we were PLAYING, the new state is PAUSED. We will also not
+   * automatically go to PLAYING but let the parent bin(s) set us to PLAYING
+   * when we preroll. */
+  if (old_state > GST_STATE_PAUSED)
+    new_state = GST_STATE_PAUSED;
+  else
+    new_state = old_state;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-      "lost state of %s", gst_element_state_get_name (current_state));
+      "lost state of %s to %s", gst_element_state_get_name (old_state),
+      gst_element_state_get_name (new_state));
 
-  GST_STATE_NEXT (element) = current_state;
-  GST_STATE_PENDING (element) = current_state;
+  GST_STATE (element) = new_state;
+  GST_STATE_NEXT (element) = new_state;
+  GST_STATE_PENDING (element) = new_state;
   GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+  if (new_base_time)
+    GST_ELEMENT_START_TIME (element) = 0;
   GST_OBJECT_UNLOCK (element);
 
-  message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
-      current_state, current_state, current_state);
-  gst_element_post_message (element, message);
+  _priv_gst_element_state_changed (element, new_state, new_state, new_state);
 
-  /* and mark us dirty */
-  message = gst_message_new_state_dirty (GST_OBJECT_CAST (element));
+  message =
+      gst_message_new_async_start (GST_OBJECT_CAST (element), new_base_time);
   gst_element_post_message (element, message);
 
   return;
@@ -2069,6 +2550,32 @@ nothing_lost:
     GST_OBJECT_UNLOCK (element);
     return;
   }
+only_async_start:
+  {
+    GST_OBJECT_UNLOCK (element);
+
+    message = gst_message_new_async_start (GST_OBJECT_CAST (element), TRUE);
+    gst_element_post_message (element, message);
+    return;
+  }
+}
+
+/**
+ * gst_element_lost_state:
+ * @element: a #GstElement the state is lost of
+ *
+ * Brings the element to the lost state. This function calls
+ * gst_element_lost_state_full() with the new_base_time set to %TRUE.
+ *
+ * This function is used internally and should normally not be called from
+ * plugins or applications.
+ *
+ * MT safe.
+ */
+void
+gst_element_lost_state (GstElement * element)
+{
+  gst_element_lost_state_full (element, TRUE);
 }
 
 /**
@@ -2086,6 +2593,9 @@ nothing_lost:
  * 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.
  *
+ * State changes to %GST_STATE_READY or %GST_STATE_NULL never return
+ * #GST_STATE_CHANGE_ASYNC.
+ *
  * Returns: Result of the state change using #GstStateChangeReturn.
  *
  * MT safe.
@@ -2108,7 +2618,7 @@ gst_element_set_state (GstElement * element, GstState state)
 
 /*
  * default set state function, calculates the next state based
- * on current state and calls the change_state function 
+ * on current state and calls the change_state function
  */
 static GstStateChangeReturn
 gst_element_set_state_func (GstElement * element, GstState state)
@@ -2123,7 +2633,7 @@ gst_element_set_state_func (GstElement * element, GstState state)
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
       gst_element_state_get_name (state));
 
-  /* state lock is taken to protect the set_state() and get_state() 
+  /* state lock is taken to protect the set_state() and get_state()
    * procedures, it does not lock any variables. */
   GST_STATE_LOCK (element);
 
@@ -2141,10 +2651,17 @@ gst_element_set_state_func (GstElement * element, GstState state)
   current = GST_STATE (element);
   next = GST_STATE_NEXT (element);
   old_pending = GST_STATE_PENDING (element);
-  /* increment state cookie so that we can track each state change */
-  element->state_cookie++;
 
-  /* this is the (new) state we should go to */
+  /* this is the (new) state we should go to. TARGET is the last state we set on
+   * the element. */
+  if (state != GST_STATE_TARGET (element)) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+        "setting target state to %s", gst_element_state_get_name (state));
+    GST_STATE_TARGET (element) = state;
+    /* increment state cookie so that we can track each state change. We only do
+     * this if this is actually a new state change. */
+    element->state_cookie++;
+  }
   GST_STATE_PENDING (element) = state;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
@@ -2188,7 +2705,7 @@ gst_element_set_state_func (GstElement * element, GstState state)
       (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 */
+  /* now signal any waiters, they will error since the cookie was incremented */
   GST_STATE_BROADCAST (element);
 
   GST_OBJECT_UNLOCK (element);
@@ -2215,21 +2732,26 @@ was_busy:
   }
 }
 
-/* with STATE_LOCK */
-static GstStateChangeReturn
+/**
+ * gst_element_change_state:
+ * @element: a #GstElement
+ * @transition: the requested transition
+ *
+ * Perform @transition on @element.
+ *
+ * This function must be called with STATE_LOCK held and is mainly used
+ * internally.
+ *
+ * Returns: the #GstStateChangeReturn of the state transition.
+ */
+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 = (GstState) 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);
@@ -2244,22 +2766,26 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
       gst_element_abort_state (element);
       break;
     case GST_STATE_CHANGE_ASYNC:
+    {
+      GstState target;
+
       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)
+      target = GST_STATE_TARGET (element);
+
+      if (target > GST_STATE_READY)
         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));
+          "forcing commit state %s <= %s",
+          gst_element_state_get_name (target),
+          gst_element_state_get_name (GST_STATE_READY));
 
       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");
@@ -2296,6 +2822,7 @@ invalid_return:
     g_critical ("%s: unknown return value %d from a state change function",
         GST_ELEMENT_NAME (element), ret);
 
+    /* we are in error now */
     ret = GST_STATE_CHANGE_FAILURE;
     GST_STATE_RETURN (element) = ret;
     GST_OBJECT_UNLOCK (element);
@@ -2305,18 +2832,18 @@ invalid_return:
 }
 
 /* gst_iterator_fold functions for pads_activate
- * Note how we don't stop the iterator when we fail an activation. This is
- * probably a FIXME since when one pad activation fails, we don't want to
- * continue our state change. */
+ * Stop the iterator if activating one pad failed. */
 static gboolean
 activate_pads (GstPad * pad, GValue * ret, gboolean * active)
 {
-  if (!gst_pad_set_active (pad, *active))
+  gboolean cont = TRUE;
+
+  if (!(cont = gst_pad_set_active (pad, *active)))
     g_value_set_boolean (ret, FALSE);
 
   /* unref the object that was reffed for us by _fold */
   gst_object_unref (pad);
-  return TRUE;
+  return cont;
 }
 
 /* set the caps on the pad to NULL */
@@ -2328,8 +2855,7 @@ clear_caps (GstPad * pad, GValue * ret, gboolean * active)
   return TRUE;
 }
 
-/* returns false on error or early cutout (will never happen because the fold
- * function always returns TRUE, see FIXME above) of the fold, true if all
+/* returns false on error or early cutout of the fold, true if all
  * pads in @iter were (de)activated successfully. */
 static gboolean
 iterator_activate_fold_with_resync (GstIterator * iter,
@@ -2354,7 +2880,7 @@ iterator_activate_fold_with_resync (GstIterator * iter,
         /* all pads iterated, return collected value */
         goto done;
       default:
-        /* iterator returned _ERROR or premature end with _OK, 
+        /* iterator returned _ERROR or premature end with _OK,
          * mark an error and exit */
         g_value_set_boolean (&ret, FALSE);
         goto done;
@@ -2367,7 +2893,7 @@ done:
 
 /* is called with STATE_LOCK
  *
- * Pads are activated from source pads to sinkpads. 
+ * Pads are activated from source pads to sinkpads.
  */
 static gboolean
 gst_element_pads_activate (GstElement * element, gboolean active)
@@ -2375,7 +2901,8 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   GstIterator *iter;
   gboolean res;
 
-  GST_DEBUG_OBJECT (element, "pads_activate with active %d", active);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+      "pads_activate with active %d", active);
 
   iter = gst_element_iterate_src_pads (element);
   res =
@@ -2404,24 +2931,28 @@ gst_element_pads_activate (GstElement * element, gboolean active)
       goto caps_failed;
   }
 
-  GST_DEBUG_OBJECT (element, "pads_activate successful");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+      "pads_activate successful");
 
   return TRUE;
 
   /* ERRORS */
 src_failed:
   {
-    GST_DEBUG_OBJECT (element, "source pads_activate failed");
+    GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+        "source pads_activate failed");
     return FALSE;
   }
 sink_failed:
   {
-    GST_DEBUG_OBJECT (element, "sink pads_activate failed");
+    GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+        "sink pads_activate failed");
     return FALSE;
   }
 caps_failed:
   {
-    GST_DEBUG_OBJECT (element, "failed to clear caps on pads");
+    GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+        "failed to clear caps on pads");
     return FALSE;
   }
 }
@@ -2432,6 +2963,7 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition)
 {
   GstState state, next;
   GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
+  GstClock **clock_p;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
@@ -2468,6 +3000,12 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition)
       } else {
         gst_element_set_base_time (element, 0);
       }
+
+      /* In null state release the reference to the clock */
+      GST_OBJECT_LOCK (element);
+      clock_p = &element->clock;
+      gst_object_replace ((GstObject **) clock_p, NULL);
+      GST_OBJECT_UNLOCK (element);
       break;
     default:
       /* this will catch real but unhandled state changes;
@@ -2501,8 +3039,8 @@ was_ok:
  *
  * Retrieves the factory that was used to create this element.
  *
- * Returns: the #GstElementFactory used for creating this element.
- * no refcounting is needed.
+ * Returns: (transfer none): the #GstElementFactory used for creating this
+ *     element. no refcounting is needed.
  */
 GstElementFactory *
 gst_element_get_factory (GstElement * element)
@@ -2515,7 +3053,7 @@ gst_element_get_factory (GstElement * element)
 static void
 gst_element_dispose (GObject * object)
 {
-  GstElement *element = GST_ELEMENT (object);
+  GstElement *element = GST_ELEMENT_CAST (object);
   GstClock **clock_p;
   GstBus **bus_p;
 
@@ -2524,15 +3062,14 @@ gst_element_dispose (GObject * object)
   if (GST_STATE (element) != GST_STATE_NULL)
     goto not_null;
 
-  g_return_if_fail (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING);
-
-  GST_DEBUG_OBJECT (element, "removing %d pads", g_list_length (element->pads));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+      "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)) {
+  if (G_UNLIKELY (element->pads != NULL)) {
     g_critical ("could not remove pads from element %s",
         GST_STR_NULL (GST_OBJECT_NAME (object)));
   }
@@ -2553,11 +3090,19 @@ gst_element_dispose (GObject * object)
   /* ERRORS */
 not_null:
   {
+    gboolean is_locked;
+
+    is_locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
     g_critical
-        ("\nTrying to dispose element %s, but it is not in the NULL state.\n"
+        ("\nTrying to dispose element %s, but it is in %s%s instead of 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));
+        "dropping the final reference, to allow them to clean up.\n"
+        "This problem may also be caused by a refcounting bug in the\n"
+        "application or some element.\n",
+        GST_OBJECT_NAME (element),
+        gst_element_state_get_name (GST_STATE (element)),
+        is_locked ? " (locked)" : "");
     return;
   }
 }
@@ -2565,7 +3110,7 @@ not_null:
 static void
 gst_element_finalize (GObject * object)
 {
-  GstElement *element = GST_ELEMENT (object);
+  GstElement *element = GST_ELEMENT_CAST (object);
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize");
 
@@ -2575,7 +3120,7 @@ gst_element_finalize (GObject * object)
   element->state_cond = NULL;
   GST_STATE_UNLOCK (element);
   g_static_rec_mutex_free (element->state_lock);
-  g_free (element->state_lock);
+  g_slice_free (GStaticRecMutex, element->state_lock);
   element->state_lock = NULL;
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
@@ -2583,7 +3128,7 @@ gst_element_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-#ifndef GST_DISABLE_LOADSAVE
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
 /**
  * gst_element_save_thyself:
  * @element: a #GstElement to save.
@@ -2606,7 +3151,7 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
 
   g_return_val_if_fail (GST_IS_ELEMENT (object), parent);
 
-  element = GST_ELEMENT (object);
+  element = GST_ELEMENT_CAST (object);
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
@@ -2629,7 +3174,7 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
       xmlNodePtr param;
       char *contents;
 
-      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (spec));
+      g_value_init (&value, spec->value_type);
 
       g_object_get_property (G_OBJECT (element), spec->name, &value);
       param = xmlNewChild (parent, NULL, (xmlChar *) "param", NULL);
@@ -2642,7 +3187,13 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
       else if (G_IS_PARAM_SPEC_INT64 (spec))
         contents = g_strdup_printf ("%" G_GINT64_FORMAT,
             g_value_get_int64 (&value));
-      else
+      else if (GST_VALUE_HOLDS_STRUCTURE (&value)) {
+        if (g_value_get_boxed (&value) != NULL) {
+          contents = g_strdup_value_contents (&value);
+        } else {
+          contents = g_strdup ("NULL");
+        }
+      } else
         contents = g_strdup_value_contents (&value);
 
       xmlNewChild (param, NULL, (xmlChar *) "value", (xmlChar *) contents);
@@ -2654,18 +3205,18 @@ gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
 
   g_free (specs);
 
-  pads = GST_ELEMENT_PADS (element);
+  pads = g_list_last (GST_ELEMENT_PADS (element));
 
   while (pads) {
     GstPad *pad = GST_PAD_CAST (pads->data);
 
     /* figure out if it's a direct pad or a ghostpad */
-    if (GST_ELEMENT (GST_OBJECT_PARENT (pad)) == element) {
+    if (GST_ELEMENT_CAST (GST_OBJECT_PARENT (pad)) == element) {
       xmlNodePtr padtag = xmlNewChild (parent, NULL, (xmlChar *) "pad", NULL);
 
       gst_object_save_thyself (GST_OBJECT_CAST (pad), padtag);
     }
-    pads = g_list_next (pads);
+    pads = g_list_previous (pads);
   }
 
   return parent;
@@ -2679,7 +3230,7 @@ gst_element_restore_thyself (GstObject * object, xmlNodePtr self)
   gchar *name = NULL;
   gchar *value = NULL;
 
-  element = GST_ELEMENT (object);
+  element = GST_ELEMENT_CAST (object);
   g_return_if_fail (element != NULL);
 
   /* parameters */
@@ -2737,7 +3288,7 @@ gst_element_set_bus_func (GstElement * element, GstBus * bus)
 /**
  * gst_element_set_bus:
  * @element: a #GstElement to set the bus of.
- * @bus: the #GstBus to set.
+ * @bus: (transfer none): the #GstBus to set.
  *
  * Sets the bus of the element. Increases the refcount on the bus.
  * For internal use only, unless you're testing elements.
@@ -2761,9 +3312,10 @@ gst_element_set_bus (GstElement * element, GstBus * bus)
  * gst_element_get_bus:
  * @element: a #GstElement to get the bus of.
  *
- * Returns the bus of the element.
+ * Returns the bus of the element. Note that only a #GstPipeline will provide a
+ * bus for the application.
  *
- * Returns: the element's #GstBus. unref after usage.
+ * Returns: (transfer full): the element's #GstBus. unref after usage.
  *
  * MT safe.
  */
@@ -2779,7 +3331,8 @@ gst_element_get_bus (GstElement * element)
     gst_object_ref (result);
   GST_OBJECT_UNLOCK (element);
 
-  GST_DEBUG_OBJECT (element, "got bus %" GST_PTR_FORMAT, result);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_BUS, element, "got bus %" GST_PTR_FORMAT,
+      result);
 
   return result;
 }