tracer: latency: Show element id, element name and pad name
[platform/upstream/gstreamer.git] / gst / gstelement.c
index 60734a3..64aa39a 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
  * SECTION:gstelement
+ * @title: GstElement
  * @short_description: Abstract base class for all pipeline elements
  * @see_also: #GstElementFactory, #GstPad
  *
@@ -34,7 +35,7 @@
  * 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
+ * Elements can 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.
@@ -43,8 +44,7 @@
  *
  * 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".
+ * gst_element_request_pad() with a #GstPadTemplate.
  * An iterator of all pads can be retrieved with gst_element_iterate_pads().
  *
  * Elements can be linked through their pads.
  * 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
+ * the #GST_ELEMENT_FLAG_PROVIDE_CLOCK flag is set. 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
+ * Not all elements require a clock to operate correctly. If the
+ * #GST_ELEMENT_FLAG_REQUIRE_CLOCK() flag is set, a clock should be set on the
  * element with gst_element_set_clock().
  *
- * Note that clock slection and distribution is normally handled by the
+ * Note that clock selection 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 2009-05-29 (0.10.24)
  */
 
-/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
- * with newer GLib versions (>= 2.31.0) */
-#define GLIB_DISABLE_DEPRECATION_WARNINGS
 #include "gst_private.h"
 #include <glib.h>
 #include <stdarg.h>
 #include <gobject/gvaluecollector.h>
 
 #include "gstelement.h"
-#include "gstelementdetails.h"
+#include "gstelementmetadata.h"
 #include "gstenumtypes.h"
 #include "gstbus.h"
-#include "gstmarshal.h"
 #include "gsterror.h"
 #include "gstevent.h"
 #include "gstutils.h"
 #include "gstinfo.h"
+#include "gstquark.h"
+#include "gsttracerutils.h"
 #include "gstvalue.h"
 #include "gst-i18n-lib.h"
 #include "glib-compat-private.h"
 
+#ifndef GST_DISABLE_GST_DEBUG
+#include "printf/printf.h"
+#endif
+
 /* Element signals and args */
 enum
 {
@@ -116,21 +116,11 @@ enum
       /* FILL ME */
 };
 
-#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);
 static void gst_element_base_class_init (gpointer g_class);
-static void gst_element_base_class_finalize (gpointer g_class);
 
+static void gst_element_constructed (GObject * object);
 static void gst_element_dispose (GObject * object);
 static void gst_element_finalize (GObject * object);
 
@@ -140,7 +130,13 @@ 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 gboolean gst_element_set_clock_func (GstElement * element,
+    GstClock * clock);
 static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
+static gboolean gst_element_post_message_default (GstElement * element,
+    GstMessage * message);
+static void gst_element_set_context_default (GstElement * element,
+    GstContext * context);
 
 static gboolean gst_element_default_send_event (GstElement * element,
     GstEvent * event);
@@ -151,17 +147,15 @@ 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);
-#endif
+static void gst_element_call_async_func (gpointer data, gpointer user_data);
 
 static GstObjectClass *parent_class = NULL;
 static guint gst_element_signals[LAST_SIGNAL] = { 0 };
 
+static GThreadPool *gst_element_pool = NULL;
+
 /* this is used in gstelementfactory.c:gst_element_register() */
-GQuark _gst_elementclass_factory = 0;
+GQuark __gst_elementclass_factory = 0;
 
 GType
 gst_element_get_type (void)
@@ -173,7 +167,7 @@ gst_element_get_type (void)
     static const GTypeInfo element_info = {
       sizeof (GstElementClass),
       gst_element_base_class_init,
-      gst_element_base_class_finalize,
+      NULL,                     /* base_class_finalize */
       (GClassInitFunc) gst_element_class_init,
       NULL,
       NULL,
@@ -186,7 +180,7 @@ gst_element_get_type (void)
     _type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
         &element_info, G_TYPE_FLAG_ABSTRACT);
 
-    _gst_elementclass_factory =
+    __gst_elementclass_factory =
         g_quark_from_static_string ("GST_ELEMENTCLASS_FACTORY");
     g_once_init_leave (&gst_element_type, _type);
   }
@@ -194,12 +188,26 @@ gst_element_get_type (void)
 }
 
 static void
+gst_element_setup_thread_pool (void)
+{
+  GError *err = NULL;
+
+  GST_DEBUG ("creating element thread pool");
+  gst_element_pool =
+      g_thread_pool_new ((GFunc) gst_element_call_async_func, NULL, -1, FALSE,
+      &err);
+  if (err != NULL) {
+    g_critical ("could not alloc threadpool %s", err->message);
+    g_clear_error (&err);
+  }
+}
+
+static void
 gst_element_class_init (GstElementClass * klass)
 {
-  GObjectClass *gobject_class = (GObjectClass *) klass;
-#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
-  GstObjectClass *gstobject_class = (GstObjectClass *) klass;
-#endif
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
 
   parent_class = g_type_class_peek_parent (klass);
 
@@ -217,7 +225,7 @@ gst_element_class_init (GstElementClass * klass)
   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, GST_TYPE_PAD);
+      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
   /**
    * GstElement::pad-removed:
    * @gstelement: the object which received the signal
@@ -228,7 +236,7 @@ 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, GST_TYPE_PAD);
+      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
   /**
    * GstElement::no-more-pads:
    * @gstelement: the object which received the signal
@@ -240,69 +248,58 @@ gst_element_class_init (GstElementClass * klass)
   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);
+      NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 0);
 
   gobject_class->dispose = gst_element_dispose;
   gobject_class->finalize = gst_element_finalize;
-
-#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 =
-      ((void (*)(GstObject * object,
-              gpointer self)) *GST_DEBUG_FUNCPTR (gst_element_restore_thyself));
-#endif
+  gobject_class->constructed = gst_element_constructed;
 
   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_clock = GST_DEBUG_FUNCPTR (gst_element_set_clock_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->post_message = GST_DEBUG_FUNCPTR (gst_element_post_message_default);
+  klass->set_context = GST_DEBUG_FUNCPTR (gst_element_set_context_default);
 
   klass->elementfactory = NULL;
+
+  gst_element_setup_thread_pool ();
 }
 
 static void
 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
+  GList *node, *padtemplates;
+
+  /* Copy the element details here so elements can inherit the
+   * details from their base class and classes only need to set
+   * the details in class_init instead of base_init */
+  element_class->metadata =
+      element_class->metadata ? gst_structure_copy (element_class->metadata) :
+      gst_structure_new_empty ("metadata");
+
+  /* Copy the pad templates so elements inherit them
+   * from their base class but elements can add pad templates in class_init
+   * instead of base_init.
    */
-  memset (&element_class->details, 0, sizeof (GstElementDetails));
-  element_class->meta_data = NULL;
-  element_class->padtemplates = NULL;
+  padtemplates = g_list_copy (element_class->padtemplates);
+  for (node = padtemplates; node != NULL; node = node->next) {
+    GstPadTemplate *tmpl = (GstPadTemplate *) node->data;
+    gst_object_ref (tmpl);
+  }
+  element_class->padtemplates = padtemplates;
 
   /* 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
-gst_element_base_class_finalize (gpointer g_class)
-{
-  GstElementClass *klass = GST_ELEMENT_CLASS (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;
-  }
+      __gst_elementclass_factory);
+  GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "type %s : factory %p",
+      G_OBJECT_CLASS_NAME (element_class), element_class->elementfactory);
 }
 
 static void
@@ -314,10 +311,15 @@ gst_element_init (GstElement * element)
   GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
   GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
 
-  /* 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 ();
+  g_rec_mutex_init (&element->state_lock);
+  g_cond_init (&element->state_cond);
+}
+
+static void
+gst_element_constructed (GObject * object)
+{
+  GST_TRACER_ELEMENT_NEW (GST_ELEMENT_CAST (object));
+  G_OBJECT_CLASS (parent_class)->constructed (object);
 }
 
 /**
@@ -326,10 +328,10 @@ gst_element_init (GstElement * element)
  * @pad: the #GstPad to release.
  *
  * Makes the element free the previously requested pad as obtained
- * with gst_element_get_request_pad().
+ * with gst_element_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
+ * gst_element_request_pad(), gst_element_release_request_pad() needs to be
  * followed by gst_object_unref() to free the @pad.
  *
  * MT safe.
@@ -341,75 +343,31 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad)
 
   g_return_if_fail (GST_IS_ELEMENT (element));
   g_return_if_fail (GST_IS_PAD (pad));
+  g_return_if_fail (GST_PAD_PAD_TEMPLATE (pad) == NULL ||
+      GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad)) ==
+      GST_PAD_REQUEST);
+  g_return_if_fail (GST_PAD_PARENT (pad) == element);
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
   /* if the element implements a custom release function we call that, else we
    * simply remove the pad from the element */
   if (oclass->release_pad)
-    (oclass->release_pad) (element, pad);
+    oclass->release_pad (element, pad);
   else
     gst_element_remove_pad (element, pad);
 }
 
 /**
- * gst_element_requires_clock:
- * @element: a #GstElement to query
- *
- * Query if the element requires a clock.
- *
- * Returns: %TRUE if the element requires a clock
- *
- * MT safe.
- */
-gboolean
-gst_element_requires_clock (GstElement * element)
-{
-  gboolean result;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
-
-  result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL);
-
-  return result;
-}
-
-/**
- * gst_element_provides_clock:
- * @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.
- * 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.
- *
- * Returns: %TRUE if the element provides a clock
- *
- * MT safe.
- */
-gboolean
-gst_element_provides_clock (GstElement * element)
-{
-  gboolean result;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
-
-  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.
- * <note>An element is only required to provide a clock in the PAUSED
- * state. Some elements can provide a clock in other states.</note>
+ * An element is only required to provide a clock in the PAUSED
+ * > state. Some elements can provide a clock in other states.
  *
- * Returns: (transfer full): the GstClock provided by the element or %NULL
- * if no clock could be provided.  Unref after usage.
+ * Returns: (transfer full) (nullable): the GstClock provided by the
+ * element or %NULL if no clock could be provided.  Unref after usage.
  *
  * MT safe.
  */
@@ -429,10 +387,23 @@ gst_element_provide_clock (GstElement * element)
   return result;
 }
 
+static gboolean
+gst_element_set_clock_func (GstElement * element, GstClock * clock)
+{
+  GstClock **clock_p;
+
+  GST_OBJECT_LOCK (element);
+  clock_p = &element->clock;
+  gst_object_replace ((GstObject **) clock_p, (GstObject *) clock);
+  GST_OBJECT_UNLOCK (element);
+
+  return TRUE;
+}
+
 /**
  * gst_element_set_clock:
  * @element: a #GstElement to set the clock for.
- * @clock: the #GstClock to set for the element.
+ * @clock: (transfer none) (allow-none): the #GstClock to set for the element.
  *
  * Sets the clock for the element. This function increases the
  * refcount on the clock. Any previously set clock on the object
@@ -448,8 +419,7 @@ gboolean
 gst_element_set_clock (GstElement * element, GstClock * clock)
 {
   GstElementClass *oclass;
-  gboolean res = TRUE;
-  GstClock **clock_p;
+  gboolean res = FALSE;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (clock == NULL || GST_IS_CLOCK (clock), FALSE);
@@ -461,13 +431,6 @@ gst_element_set_clock (GstElement * element, GstClock * clock)
   if (oclass->set_clock)
     res = oclass->set_clock (element, clock);
 
-  if (res) {
-    /* only update the clock pointer if the element accepted the clock */
-    GST_OBJECT_LOCK (element);
-    clock_p = &element->clock;
-    gst_object_replace ((GstObject **) clock_p, (GstObject *) clock);
-    GST_OBJECT_UNLOCK (element);
-  }
   return res;
 }
 
@@ -478,7 +441,10 @@ 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: (transfer full): the #GstClock of the element. unref after usage.
+ * Elements in a pipeline will only have their clock set when the
+ * pipeline is in the PLAYING state.
+ *
+ * Returns: (transfer full) (nullable): the #GstClock of the element. unref after usage.
  *
  * MT safe.
  */
@@ -567,8 +533,6 @@ gst_element_get_base_time (GstElement * element)
  * 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)
@@ -600,8 +564,6 @@ gst_element_set_start_time (GstElement * element, GstClockTime time)
  * MT safe.
  *
  * Returns: the start time of the element.
- *
- * Since: 0.10.24
  */
 GstClockTime
 gst_element_get_start_time (GstElement * element)
@@ -617,28 +579,7 @@ gst_element_get_start_time (GstElement * element)
   return result;
 }
 
-/**
- * gst_element_is_indexable:
- * @element: a #GstElement.
- *
- * Queries if the element can be indexed.
- *
- * Returns: TRUE if the element can be indexed.
- *
- * MT safe.
- */
-gboolean
-gst_element_is_indexable (GstElement * element)
-{
-  gboolean result;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
-
-  result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL);
-
-  return result;
-}
-
+#if 0
 /**
  * gst_element_set_index:
  * @element: a #GstElement.
@@ -669,8 +610,8 @@ gst_element_set_index (GstElement * element, GstIndex * index)
  *
  * Gets the index from the element.
  *
- * Returns: (transfer full): a #GstIndex or %NULL when no index was set on the
- * element. unref after usage.
+ * Returns: (transfer full) (nullable): a #GstIndex or %NULL when no
+ * index was set on the element. unref after usage.
  *
  * MT safe.
  */
@@ -689,18 +630,18 @@ gst_element_get_index (GstElement * element)
 
   return result;
 }
+#endif
 
 /**
  * gst_element_add_pad:
  * @element: a #GstElement to add the pad to.
- * @pad: (transfer full): the #GstPad to add to the element.
+ * @pad: (transfer floating): 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.
  *
- * Pads are not automatically activated so elements should perform the needed
- * steps to activate the pad in case this pad is added in the PAUSED or PLAYING
- * state. See gst_pad_set_active() for more information about activating pads.
+ * Pads are automatically activated when added in the PAUSED or PLAYING
+ * state.
  *
  * The pad and the element should be unlocked when calling this function.
  *
@@ -716,7 +657,7 @@ gboolean
 gst_element_add_pad (GstElement * element, GstPad * pad)
 {
   gchar *pad_name;
-  gboolean flushing;
+  gboolean active;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
@@ -726,7 +667,8 @@ 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);
+  active = GST_PAD_IS_ACTIVE (pad);
+  GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_PARENT);
   GST_OBJECT_UNLOCK (pad);
 
   /* then check to see if there's already a pad by that name here */
@@ -739,16 +681,10 @@ 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 ||
+  /* check for active pads */
+  if (!active && (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);
+    gst_pad_set_active (pad, TRUE);
   }
 
   g_free (pad_name);
@@ -756,24 +692,24 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
   /* add it to the list */
   switch (gst_pad_get_direction (pad)) {
     case GST_PAD_SRC:
-      element->srcpads = g_list_prepend (element->srcpads, pad);
+      element->srcpads = g_list_append (element->srcpads, pad);
       element->numsrcpads++;
       break;
     case GST_PAD_SINK:
-      element->sinkpads = g_list_prepend (element->sinkpads, pad);
+      element->sinkpads = g_list_append (element->sinkpads, pad);
       element->numsinkpads++;
       break;
     default:
       goto no_direction;
   }
-  element->pads = g_list_prepend (element->pads, pad);
+  element->pads = g_list_append (element->pads, pad);
   element->numpads++;
   element->pads_cookie++;
   GST_OBJECT_UNLOCK (element);
 
   /* emit the PAD_ADDED signal */
   g_signal_emit (element, gst_element_signals[PAD_ADDED], 0, pad);
-
+  GST_TRACER_ELEMENT_ADD_PAD (element, pad);
   return TRUE;
 
   /* ERROR cases */
@@ -783,6 +719,8 @@ name_exists:
         pad_name, GST_ELEMENT_NAME (element));
     GST_OBJECT_UNLOCK (element);
     g_free (pad_name);
+    gst_object_ref_sink (pad);
+    gst_object_unref (pad);
     return FALSE;
   }
 had_parent:
@@ -816,7 +754,7 @@ no_direction:
  *
  * This function is used by plugin developers and should not be used
  * by applications. Pads that were dynamically requested from elements
- * with gst_element_get_request_pad() should be released with the
+ * with gst_element_request_pad() should be released with the
  * gst_element_release_request_pad() function instead.
  *
  * Pads are not automatically deactivated so elements should perform the needed
@@ -885,7 +823,7 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
 
   /* emit the PAD_REMOVED signal before unparenting and losing the last ref. */
   g_signal_emit (element, gst_element_signals[PAD_REMOVED], 0, pad);
-
+  GST_TRACER_ELEMENT_REMOVE_PAD (element, pad);
   gst_object_unparent (GST_OBJECT_CAST (pad));
 
   return TRUE;
@@ -893,12 +831,15 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
   /* ERRORS */
 not_our_pad:
   {
-    /* FIXME, locking order? */
+    /* locking order is element > pad */
+    GST_OBJECT_UNLOCK (pad);
+
     GST_OBJECT_LOCK (element);
+    GST_OBJECT_LOCK (pad);
     g_critical ("Padname %s:%s does not belong to element %s when removing",
         GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element));
-    GST_OBJECT_UNLOCK (element);
     GST_OBJECT_UNLOCK (pad);
+    GST_OBJECT_UNLOCK (element);
     return FALSE;
   }
 }
@@ -945,8 +886,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: (transfer full): the requested #GstPad if found, otherwise %NULL.
- *     unref after usage.
+ * Returns: (transfer full) (nullable): the requested #GstPad if
+ *     found, otherwise %NULL.  unref after usage.
  *
  * MT safe.
  */
@@ -979,57 +920,143 @@ gst_element_get_static_pad (GstElement * element, const gchar * name)
   return result;
 }
 
-static GstPad *
-_gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
-    const gchar * name, const GstCaps * caps)
+static gboolean
+gst_element_is_valid_request_template_name (const gchar * templ_name,
+    const gchar * name)
 {
-  GstPad *newpad = NULL;
-  GstElementClass *oclass;
+  gchar *endptr;
+  const gchar *templ_name_ptr, *name_ptr;
+  gboolean next_specifier;
+  guint templ_postfix_len = 0, name_postfix_len = 0;
 
-  oclass = GST_ELEMENT_GET_CLASS (element);
+  g_return_val_if_fail (templ_name != NULL, FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
 
-#ifndef G_DISABLE_CHECKS
-  /* Some sanity checking here */
-  if (name) {
-    GstPad *pad;
+  /* Is this the template name? */
+  if (strcmp (templ_name, name) == 0)
+    return TRUE;
 
-    /* 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 all the specifiers */
+  do {
+    /* Because of sanity checks in gst_pad_template_new(), we know that %s
+     * and %d and %u, occurring at the template_name */
+    templ_name_ptr = strchr (templ_name, '%');
+
+    /* check characters ahead of the specifier */
+    if (!templ_name_ptr || strlen (name) <= templ_name_ptr - templ_name
+        || strncmp (templ_name, name, templ_name_ptr - templ_name) != 0) {
+      return FALSE;
+    }
 
-      /* 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);
+    /* %s is not allowed for multiple specifiers, just a single specifier can be
+     * accepted in gst_pad_template_new() and can not be mixed with other
+     * specifier '%u' and '%d' */
+    if (*(templ_name_ptr + 1) == 's' && g_strcmp0 (templ_name, name) == 0) {
+      return TRUE;
+    }
+
+    name_ptr = name + (templ_name_ptr - templ_name);
+
+    /* search next specifier, each of specifier should be separated by '_' */
+    templ_name = strchr (templ_name_ptr, '_');
+    name = strchr (name_ptr, '_');
+
+    /* don't match the number of specifiers */
+    if ((templ_name && !name) || (!templ_name && name))
+      return FALSE;
+
+    if (templ_name && name)
+      next_specifier = TRUE;
+    else
+      next_specifier = FALSE;
+
+    /* check characters followed by the specifier */
+    if (*(templ_name_ptr + 2) != '\0' && *(templ_name_ptr + 2) != '_') {
+      if (next_specifier) {
+        templ_postfix_len = templ_name - (templ_name_ptr + 2);
+        name_postfix_len = name - name_ptr;
+      } else {
+        templ_postfix_len = strlen (templ_name_ptr + 2);
+        name_postfix_len = strlen (name_ptr);
+      }
+
+      if (strncmp (templ_name_ptr + 2,
+              name_ptr + name_postfix_len - templ_postfix_len,
+              templ_postfix_len) != 0) {
+        return FALSE;
+      }
+    }
+
+    /* verify the specifier */
+    if (*(name_ptr) == '%') {
+      guint len;
+
+      len = (next_specifier) ? name - name_ptr : strlen (name_ptr);
+
+      if (strncmp (name_ptr, templ_name_ptr, len) != 0)
+        return FALSE;
+
+    } else {
+      const gchar *specifier;
+      gchar *target = NULL;
 
-      data = name + (str - templ->name_template);
+      /* extract specifier when it has postfix characters */
+      if (name_postfix_len > templ_postfix_len) {
+        target = g_strndup (name_ptr, name_postfix_len - templ_postfix_len);
+      }
+      specifier = target ? target : name_ptr;
 
-      /* Can either be %s or %d or %u, do sanity checking for %d */
-      if (*(str + 1) == 'd') {
+      if (*(templ_name_ptr + 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') {
+        tmp = g_ascii_strtoll (specifier, &endptr, 10);
+        if (tmp < G_MININT || tmp > G_MAXINT || (*endptr != '\0'
+                && *endptr != '_'))
+          return FALSE;
+      } else if (*(templ_name_ptr + 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);
+        tmp = g_ascii_strtoull (specifier, &endptr, 10);
+        if (tmp > G_MAXUINT || (*endptr != '\0' && *endptr != '_'))
+          return FALSE;
       }
+
+      g_free (target);
+    }
+
+    /* otherwise we increment these from NULL to 1 */
+    if (next_specifier) {
+      templ_name++;
+      name++;
     }
+  } while (next_specifier);
+
+  return TRUE;
+}
+
+static GstPad *
+_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);
+
+#ifndef G_DISABLE_CHECKS
+  /* Some sanity checking here */
+  if (name) {
+    GstPad *pad;
+
+    g_return_val_if_fail (gst_element_is_valid_request_template_name
+        (templ->name_template, name), 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() */
+      /* FIXME 2.0: 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);
@@ -1037,10 +1064,8 @@ _gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
   }
 #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 (oclass->request_new_pad)
+    newpad = (oclass->request_new_pad) (element, templ, name, caps);
 
   if (newpad)
     gst_object_ref (newpad);
@@ -1057,11 +1082,12 @@ _gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
  * retrieves request pads. The pad should be released with
  * gst_element_release_request_pad().
  *
- * This method is slow and will be deprecated in the future. New code should
- * use gst_element_request_pad() with the requested template.
+ * This method is slower than manually getting the pad template and calling
+ * gst_element_request_pad() if the pads should have a specific name (e.g.
+ * @name is "src_1" instead of "src_\%u").
  *
- * Returns: (transfer full): requested #GstPad if found, otherwise %NULL.
- *     Release after usage.
+ * Returns: (transfer full) (nullable): requested #GstPad if found,
+ *     otherwise %NULL.  Release after usage.
  */
 GstPad *
 gst_element_get_request_pad (GstElement * element, const gchar * name)
@@ -1071,8 +1097,6 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
   const gchar *req_name = NULL;
   gboolean templ_found = FALSE;
   GList *list;
-  const gchar *data;
-  gchar *str, *endptr = NULL;
   GstElementClass *class;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
@@ -1080,13 +1104,10 @@ 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_request_pad_template (class, name);
-    req_name = NULL;
-    if (templ)
-      templ_found = TRUE;
+  templ = gst_element_class_get_request_pad_template (class, name);
+  if (templ) {
+    req_name = strstr (name, "%") ? NULL : name;
+    templ_found = TRUE;
   } else {
     /* there is no % in the name, try to find a matching template */
     list = class->padtemplates;
@@ -1095,48 +1116,12 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
       if (templ->presence == GST_PAD_REQUEST) {
         GST_CAT_DEBUG (GST_CAT_PADS, "comparing %s to %s", name,
             templ->name_template);
-        /* see if we find an exact match */
-        if (strcmp (name, templ->name_template) == 0) {
+        if (gst_element_is_valid_request_template_name (templ->name_template,
+                name)) {
           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 */
-            tmp = strtoul (data, &endptr, 10);
-            if (tmp != G_MAXULONG && endptr && *endptr == '\0') {
-              templ_found = TRUE;
-              req_name = name;
-              break;
-            }
-          } else {
-            /* it's a string */
-            templ_found = TRUE;
-            req_name = name;
-            break;
-          }
-        }
       }
       list = list->next;
     }
@@ -1151,7 +1136,7 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
 }
 
 /**
- * gst_element_request_pad:
+ * gst_element_request_pad: (virtual request_new_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
@@ -1163,16 +1148,10 @@ gst_element_get_request_pad (GstElement * element, const gchar * name)
  * 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
+ * Returns: (transfer full) (nullable): requested #GstPad if found,
+ *     otherwise %NULL.  Release after usage.
  */
 GstPad *
 gst_element_request_pad (GstElement * element,
@@ -1185,63 +1164,15 @@ gst_element_request_pad (GstElement * element,
   return _gst_element_request_pad (element, templ, name, caps);
 }
 
-/**
- * gst_element_get_pad:
- * @element: a #GstElement.
- * @name: the name of the pad to retrieve.
- *
- * Retrieves a pad from @element by name. Tries gst_element_get_static_pad()
- * first, then gst_element_get_request_pad().
- *
- * 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.
- * Use gst_element_get_static_pad() or gst_element_get_request_pad() instead.
- *
- * 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)
-{
-  GstPad *pad;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
-  g_return_val_if_fail (name != NULL, NULL);
-
-  pad = gst_element_get_static_pad (element, name);
-  if (!pad)
-    pad = gst_element_get_request_pad (element, name);
-
-  return pad;
-}
-#endif /* GST_REMOVE_DEPRECATED */
-
-static GstIteratorItem
-iterate_pad (GstIterator * it, GstPad * 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);
+      &element->pads_cookie, padlist, (GObject *) element, NULL);
   GST_OBJECT_UNLOCK (element);
 
   return result;
@@ -1255,8 +1186,10 @@ gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
  * be freed after usage. Also more specialized iterators exists such as
  * gst_element_iterate_src_pads() or gst_element_iterate_sink_pads().
  *
- * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
- *     after use.
+ * The order of pads returned by the iterator will be the order in which
+ * the pads were added to the element.
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad.
  *
  * MT safe.
  */
@@ -1274,8 +1207,10 @@ gst_element_iterate_pads (GstElement * element)
  *
  * Retrieves an iterator of @element's source pads.
  *
- * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
- *     after use.
+ * The order of pads returned by the iterator will be the order in which
+ * the pads were added to the element.
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad.
  *
  * MT safe.
  */
@@ -1293,8 +1228,10 @@ gst_element_iterate_src_pads (GstElement * element)
  *
  * Retrieves an iterator of @element's sink pads.
  *
- * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
- *     after use.
+ * The order of pads returned by the iterator will be the order in which
+ * the pads were added to the element.
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad.
  *
  * MT safe.
  */
@@ -1306,143 +1243,291 @@ gst_element_iterate_sink_pads (GstElement * element)
   return gst_element_iterate_pad_list (element, &element->sinkpads);
 }
 
+static gboolean
+gst_element_do_foreach_pad (GstElement * element,
+    GstElementForeachPadFunc func, gpointer user_data,
+    GList ** p_pads, guint16 * p_npads)
+{
+  gboolean ret = TRUE;
+  GstPad **pads;
+  guint n_pads, i;
+  GList *l;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (func != NULL, FALSE);
+
+  GST_OBJECT_LOCK (element);
+  n_pads = *p_npads;
+  pads = g_newa (GstPad *, n_pads + 1);
+  for (l = *p_pads, i = 0; l != NULL; l = l->next) {
+    g_assert (i < n_pads);
+    pads[i++] = gst_object_ref (l->data);
+  }
+  GST_OBJECT_UNLOCK (element);
+
+  if (n_pads == 0)
+    return FALSE;
+
+  for (i = 0; i < n_pads; ++i) {
+    ret = func (element, pads[i], user_data);
+    if (!ret)
+      break;
+  }
+
+  for (i = 0; i < n_pads; ++i)
+    gst_object_unref (pads[i]);
+
+  return ret;
+}
+
+/**
+ * gst_element_foreach_sink_pad:
+ * @element: a #GstElement to iterate sink pads of
+ * @func: (scope call): function to call for each sink pad
+ * @user_data: (closure): user data passed to @func
+ *
+ * Call @func with @user_data for each of @element's sink pads. @func will be
+ * called exactly once for each sink pad that exists at the time of this call,
+ * unless one of the calls to @func returns %FALSE in which case we will stop
+ * iterating pads and return early. If new sink pads are added or sink pads
+ * are removed while the sink pads are being iterated, this will not be taken
+ * into account until next time this function is used.
+ *
+ * Returns: %FALSE if @element had no sink pads or if one of the calls to @func
+ *   returned %FALSE.
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_element_foreach_sink_pad (GstElement * element,
+    GstElementForeachPadFunc func, gpointer user_data)
+{
+  return gst_element_do_foreach_pad (element, func, user_data,
+      &element->sinkpads, &element->numsinkpads);
+}
+
+/**
+ * gst_element_foreach_src_pad:
+ * @element: a #GstElement to iterate source pads of
+ * @func: (scope call): function to call for each source pad
+ * @user_data: (closure): user data passed to @func
+ *
+ * Call @func with @user_data for each of @element's source pads. @func will be
+ * called exactly once for each source pad that exists at the time of this call,
+ * unless one of the calls to @func returns %FALSE in which case we will stop
+ * iterating pads and return early. If new source pads are added or source pads
+ * are removed while the source pads are being iterated, this will not be taken
+ * into account until next time this function is used.
+ *
+ * Returns: %FALSE if @element had no source pads or if one of the calls
+ *   to @func returned %FALSE.
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_element_foreach_src_pad (GstElement * element,
+    GstElementForeachPadFunc func, gpointer user_data)
+{
+  return gst_element_do_foreach_pad (element, func, user_data,
+      &element->srcpads, &element->numsrcpads);
+}
+
+/**
+ * gst_element_foreach_pad:
+ * @element: a #GstElement to iterate pads of
+ * @func: (scope call): function to call for each pad
+ * @user_data: (closure): user data passed to @func
+ *
+ * Call @func with @user_data for each of @element's pads. @func will be called
+ * exactly once for each pad that exists at the time of this call, unless
+ * one of the calls to @func returns %FALSE in which case we will stop
+ * iterating pads and return early. If new pads are added or pads are removed
+ * while pads are being iterated, this will not be taken into account until
+ * next time this function is used.
+ *
+ * Returns: %FALSE if @element had no pads or if one of the calls to @func
+ *   returned %FALSE.
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_element_foreach_pad (GstElement * element, GstElementForeachPadFunc func,
+    gpointer user_data)
+{
+  return gst_element_do_foreach_pad (element, func, user_data,
+      &element->pads, &element->numpads);
+}
+
 /**
  * gst_element_class_add_pad_template:
  * @klass: the #GstElementClass to add the pad template to.
- * @templ: (transfer none): a #GstPadTemplate to add to the element class.
+ * @templ: (transfer floating): a #GstPadTemplate to add to the element class.
+ *
+ * Adds a padtemplate to an element class. This is mainly used in the _class_init
+ * functions of classes. If a pad template with the same name as an already
+ * existing one is added the old one is replaced by the new one.
+ *
+ * @templ's reference count will be incremented, and any floating
+ * reference will be removed (see gst_object_ref_sink())
  *
- * Adds a padtemplate to an element class. This is mainly used in the _base_init
- * functions of classes.
  */
 void
 gst_element_class_add_pad_template (GstElementClass * klass,
     GstPadTemplate * templ)
 {
+  GList *template_list = klass->padtemplates;
+
   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);
-
-  klass->padtemplates = g_list_append (klass->padtemplates,
-      gst_object_ref (templ));
+  /* If we already have a pad template with the same name replace the
+   * old one. */
+  while (template_list) {
+    GstPadTemplate *padtempl = (GstPadTemplate *) template_list->data;
+
+    /* Found pad with the same name, replace and return */
+    if (strcmp (templ->name_template, padtempl->name_template) == 0) {
+      gst_object_ref_sink (padtempl);
+      gst_object_unref (padtempl);
+      template_list->data = templ;
+      return;
+    }
+    template_list = g_list_next (template_list);
+  }
+
+  /* Take ownership of the floating ref */
+  gst_object_ref_sink (templ);
+
+  klass->padtemplates = g_list_append (klass->padtemplates, templ);
   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.
+ * @static_templ: #GstStaticPadTemplate to add as pad template to the element class.
  *
- * Adds a padtemplate to an element class. This is mainly used in the _base_init
- * functions of classes.
+ * Adds a pad template to an element class based on the static pad template
+ * @templ. This is mainly used in the _class_init functions of element
+ * implementations. If a pad template with the same name already exists,
+ * the old one is replaced by the new one.
  *
- * Since: 0.10.36
+ * Since: 1.8
  */
 void
 gst_element_class_add_static_pad_template (GstElementClass * klass,
-    GstStaticPadTemplate * templ)
+    GstStaticPadTemplate * static_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);
+  gst_element_class_add_pad_template (klass,
+      gst_static_pad_template_get (static_templ));
 }
 
-static void
-gst_element_class_add_meta_data (GstElementClass * klass,
-    const gchar * key, const gchar * value)
+/**
+ * gst_element_class_add_static_pad_template_with_gtype:
+ * @klass: the #GstElementClass to add the pad template to.
+ * @static_templ: #GstStaticPadTemplate to add as pad template to the element class.
+ * @pad_type: The #GType of the pad to create
+ *
+ * Adds a pad template to an element class based on the static pad template
+ * @templ. This is mainly used in the _class_init functions of element
+ * implementations. If a pad template with the same name already exists,
+ * the old one is replaced by the new one.
+ *
+ * Since: 1.14
+ */
+void
+gst_element_class_add_static_pad_template_with_gtype (GstElementClass * klass,
+    GstStaticPadTemplate * static_templ, GType pad_type)
 {
-  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_add_pad_template (klass,
+      gst_pad_template_new_from_static_pad_template_with_gtype (static_templ,
+          pad_type));
 }
 
 /**
- * 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.
+ * gst_element_class_add_metadata:
+ * @klass: class to set metadata for
+ * @key: the key to set
+ * @value: the value to set
  *
- * Since: 0.10.31
+ * Set @key with @value as metadata in @klass.
  */
 void
-gst_element_class_set_documentation_uri (GstElementClass * klass,
-    const gchar * uri)
+gst_element_class_add_metadata (GstElementClass * klass,
+    const gchar * key, const gchar * value)
 {
   g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+  g_return_if_fail (key != NULL);
+  g_return_if_fail (value != NULL);
 
-  gst_element_class_add_meta_data (klass, "doc-uri", uri);
+  gst_structure_set ((GstStructure *) klass->metadata,
+      key, G_TYPE_STRING, value, NULL);
 }
 
 /**
- * gst_element_class_set_icon_name:
- * @klass: class to set details for
- * @name: name of an icon
+ * gst_element_class_add_static_metadata:
+ * @klass: class to set metadata for
+ * @key: the key to set
+ * @value: the value to set
  *
- * 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.
+ * Set @key with @value as metadata in @klass.
  *
- * Since: 0.10.31
+ * Same as gst_element_class_add_metadata(), but @value must be a static string
+ * or an inlined string, as it will not be copied. (GStreamer plugins will
+ * be made resident once loaded, so this function can be used even from
+ * dynamically loaded plugins.)
  */
 void
-gst_element_class_set_icon_name (GstElementClass * klass, const gchar * name)
+gst_element_class_add_static_metadata (GstElementClass * klass,
+    const gchar * key, const gchar * value)
 {
+  GValue val = G_VALUE_INIT;
+
   g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+  g_return_if_fail (key != NULL);
+  g_return_if_fail (value != NULL);
 
-  gst_element_class_add_meta_data (klass, "icon-name", name);
+  g_value_init (&val, G_TYPE_STRING);
+  g_value_set_static_string (&val, value);
+  gst_structure_take_value ((GstStructure *) klass->metadata, key, &val);
 }
 
-/* FIXME-0.11: deprecate and remove gst_element_class_set_details*() */
 /**
- * gst_element_class_set_details:
- * @klass: class to set details for
- * @details: details to set
+ * gst_element_class_set_metadata:
+ * @klass: class to set metadata 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 metadata. E.g: "Joe Bloggs &lt;joe.blogs at foo.com&gt;"
  *
  * Sets the detailed information for a #GstElementClass.
- * <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.
+ * > This function is for use in _class_init functions only.
  */
-#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)
+gst_element_class_set_metadata (GstElementClass * klass,
+    const gchar * longname, const gchar * classification,
+    const gchar * description, const gchar * author)
 {
   g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
-  g_return_if_fail (GST_IS_ELEMENT_DETAILS (details));
+  g_return_if_fail (longname != NULL && *longname != '\0');
+  g_return_if_fail (classification != NULL && *classification != '\0');
+  g_return_if_fail (description != NULL && *description != '\0');
+  g_return_if_fail (author != NULL && *author != '\0');
 
-  __gst_element_details_copy (&klass->details, details);
+  gst_structure_id_set ((GstStructure *) klass->metadata,
+      GST_QUARK (ELEMENT_METADATA_LONGNAME), G_TYPE_STRING, longname,
+      GST_QUARK (ELEMENT_METADATA_KLASS), G_TYPE_STRING, classification,
+      GST_QUARK (ELEMENT_METADATA_DESCRIPTION), G_TYPE_STRING, description,
+      GST_QUARK (ELEMENT_METADATA_AUTHOR), G_TYPE_STRING, author, NULL);
 }
-#endif
 
 /**
- * gst_element_class_set_details_simple:
- * @klass: class to set details for
+ * gst_element_class_set_static_metadata:
+ * @klass: class to set metadata 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
@@ -1450,29 +1535,83 @@ gst_element_class_set_details (GstElementClass * klass,
  * @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;"
+ * multiple author metadata. 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>
+ * Sets the detailed information for a #GstElementClass.
  *
- * The detail parameter strings are copied into the #GstElementDetails for
- * the element class.
+ * > This function is for use in _class_init functions only.
  *
- * Since: 0.10.14
+ * Same as gst_element_class_set_metadata(), but @longname, @classification,
+ * @description, and @author must be static strings or inlined strings, as
+ * they will not be copied. (GStreamer plugins will be made resident once
+ * loaded, so this function can be used even from dynamically loaded plugins.)
  */
 void
-gst_element_class_set_details_simple (GstElementClass * klass,
+gst_element_class_set_static_metadata (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);
+  GstStructure *s = (GstStructure *) klass->metadata;
+  GValue val = G_VALUE_INIT;
 
   g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+  g_return_if_fail (longname != NULL && *longname != '\0');
+  g_return_if_fail (classification != NULL && *classification != '\0');
+  g_return_if_fail (description != NULL && *description != '\0');
+  g_return_if_fail (author != NULL && *author != '\0');
+
+  g_value_init (&val, G_TYPE_STRING);
 
-  __gst_element_details_copy (&klass->details, &details);
+  g_value_set_static_string (&val, longname);
+  gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_LONGNAME), &val);
+
+  g_value_set_static_string (&val, classification);
+  gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_KLASS), &val);
+
+  g_value_set_static_string (&val, description);
+  gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_DESCRIPTION),
+      &val);
+
+  g_value_set_static_string (&val, author);
+  gst_structure_id_take_value (s, GST_QUARK (ELEMENT_METADATA_AUTHOR), &val);
+}
+
+/**
+ * gst_element_class_get_metadata:
+ * @klass: class to get metadata for
+ * @key: the key to get
+ *
+ * Get metadata with @key in @klass.
+ *
+ * Returns: the metadata for @key.
+ */
+const gchar *
+gst_element_class_get_metadata (GstElementClass * klass, const gchar * key)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT_CLASS (klass), NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  return gst_structure_get_string ((GstStructure *) klass->metadata, key);
+}
+
+/**
+ * gst_element_get_metadata:
+ * @element: class to get metadata for
+ * @key: the key to get
+ *
+ * Get metadata with @key in @klass.
+ *
+ * Returns: the metadata for @key.
+ *
+ * Since: 1.14
+ */
+const gchar *
+gst_element_get_metadata (GstElement * element, const gchar * key)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  return gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element), key);
 }
 
 /**
@@ -1481,9 +1620,9 @@ gst_element_class_set_details_simple (GstElementClass * klass,
  *
  * Retrieves a list of the pad templates associated with @element_class. The
  * list must not be modified by the calling code.
- * <note>If you use this function in the #GInstanceInitFunc of an object class
- * that has subclasses, make sure to pass the g_class parameter of the
- * #GInstanceInitFunc here.</note>
+ * If you use this function in the #GInstanceInitFunc of an object class
+ * that has subclasses, make sure to pass the g_class parameter of the
+ * > #GInstanceInitFunc here.
  *
  * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of
  *     pad templates.
@@ -1497,17 +1636,39 @@ gst_element_class_get_pad_template_list (GstElementClass * element_class)
 }
 
 /**
+ * gst_element_get_pad_template_list:
+ * @element: a #GstElement to get pad templates of.
+ *
+ * Retrieves a list of the pad templates associated with @element. The
+ * list must not be modified by the calling code.
+ *
+ * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of
+ *     pad templates.
+ *
+ * Since: 1.14
+ */
+GList *
+gst_element_get_pad_template_list (GstElement * element)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  return
+      gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (element));
+}
+
+/**
  * gst_element_class_get_pad_template:
  * @element_class: a #GstElementClass to get the pad template of.
  * @name: the name of the #GstPadTemplate to get.
  *
  * Retrieves a padtemplate from @element_class with the given name.
- * <note>If you use this function in the #GInstanceInitFunc of an object class
- * that has subclasses, make sure to pass the g_class parameter of the
- * #GInstanceInitFunc here.</note>
+ * If you use this function in the #GInstanceInitFunc of an object class
+ * that has subclasses, make sure to pass the g_class parameter of the
+ * > #GInstanceInitFunc here.
  *
- * Returns: (transfer none): the #GstPadTemplate with the given name, or %NULL
- *     if none was found. No unreferencing is necessary.
+ * Returns: (transfer none) (nullable): the #GstPadTemplate with the
+ *     given name, or %NULL if none was found. No unreferencing is
+ *     necessary.
  */
 GstPadTemplate *
 gst_element_class_get_pad_template (GstElementClass *
@@ -1532,6 +1693,29 @@ gst_element_class_get_pad_template (GstElementClass *
   return NULL;
 }
 
+/**
+ * gst_element_get_pad_template:
+ * @element: a #GstElement to get the pad template of.
+ * @name: the name of the #GstPadTemplate to get.
+ *
+ * Retrieves a padtemplate from @element with the given name.
+ *
+ * Returns: (transfer none) (nullable): the #GstPadTemplate with the
+ *     given name, or %NULL if none was found. No unreferencing is
+ *     necessary.
+ *
+ * Since: 1.14
+ */
+GstPadTemplate *
+gst_element_get_pad_template (GstElement * element, const gchar * name)
+{
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  return gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (element),
+      name);
+}
+
 static GstPadTemplate *
 gst_element_class_get_request_pad_template (GstElementClass *
     element_class, const gchar * name)
@@ -1546,7 +1730,7 @@ gst_element_class_get_request_pad_template (GstElementClass *
 }
 
 /* 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.
+ * The pad is random in a sense that it is the first pad that is (optionally) linked.
  */
 static GstPad *
 gst_element_get_random_pad (GstElement * element,
@@ -1614,8 +1798,8 @@ gst_element_default_send_event (GstElement * element, GstEvent * event)
   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);
+      gst_element_get_random_pad (element, TRUE, GST_PAD_SINK) :
+      gst_element_get_random_pad (element, TRUE, GST_PAD_SRC);
 
   if (pad) {
     GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
@@ -1624,7 +1808,7 @@ gst_element_default_send_event (GstElement * element, GstEvent * event)
         (GST_PAD_DIRECTION (pad) == GST_PAD_SRC ? "src" : "sink"),
         GST_DEBUG_PAD_NAME (pad));
 
-    result = gst_pad_push_event (pad, event);
+    result = gst_pad_send_event (pad, event);
     gst_object_unref (pad);
   } else {
     GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
@@ -1641,14 +1825,15 @@ gst_element_default_send_event (GstElement * element, GstEvent * event)
  *
  * 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.
+ * downstream events or a random linked source pad for upstream events.
  *
- * This function takes owership of the provided event so you should
+ * This function takes ownership of the 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.
- *
  * MT safe.
+ *
+ * Returns: %TRUE if the event was handled. Events that trigger a preroll (such
+ * as flushing seeks and steps) will emit %GST_MESSAGE_ASYNC_DONE.
  */
 gboolean
 gst_element_send_event (GstElement * element, GstEvent * event)
@@ -1667,7 +1852,7 @@ gst_element_send_event (GstElement * element, GstEvent * event)
         GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
     result = oclass->send_event (element, event);
   } else {
-    result = gst_element_default_send_event (element, event);
+    gst_event_unref (event);
   }
   GST_STATE_UNLOCK (element);
 
@@ -1680,8 +1865,8 @@ gst_element_send_event (GstElement * element, GstEvent * event)
  * @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
+ * @start_type: The type and flags for the new start position
+ * @start: The value of the new start position
  * @stop_type: The type and flags for the new stop position
  * @stop: The value of the new stop position
  *
@@ -1689,13 +1874,14 @@ gst_element_send_event (GstElement * element, GstEvent * event)
  * the parameters. The seek event is sent to the element using
  * gst_element_send_event().
  *
- * Returns: %TRUE if the event was handled.
- *
  * MT safe.
+ *
+ * Returns: %TRUE if the event was handled. Flushing seeks will trigger a
+ * preroll, which will emit %GST_MESSAGE_ASYNC_DONE.
  */
 gboolean
 gst_element_seek (GstElement * element, gdouble rate, GstFormat format,
-    GstSeekFlags flags, GstSeekType cur_type, gint64 cur,
+    GstSeekFlags flags, GstSeekType start_type, gint64 start,
     GstSeekType stop_type, gint64 stop)
 {
   GstEvent *event;
@@ -1704,54 +1890,13 @@ gst_element_seek (GstElement * element, gdouble rate, GstFormat format,
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   event =
-      gst_event_new_seek (rate, format, flags, cur_type, cur, stop_type, stop);
+      gst_event_new_seek (rate, format, flags, start_type, start, stop_type,
+      stop);
   result = gst_element_send_event (element, event);
 
   return result;
 }
 
-/**
- * gst_element_get_query_types:
- * @element: a #GstElement to query
- *
- * Get an array of query types from the element.
- * If the element doesn't implement a query types function,
- * the query will be forwarded to the peer of a random linked sink pad.
- *
- * Returns: An array of #GstQueryType elements that should not
- * be freed or modified.
- *
- * MT safe.
- */
-const GstQueryType *
-gst_element_get_query_types (GstElement * element)
-{
-  GstElementClass *oclass;
-  const GstQueryType *result = NULL;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
-
-  oclass = GST_ELEMENT_GET_CLASS (element);
-
-  if (oclass->get_query_types) {
-    result = oclass->get_query_types (element);
-  } else {
-    GstPad *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_get_query_types (peer);
-
-        gst_object_unref (peer);
-      }
-      gst_object_unref (pad);
-    }
-  }
-  return result;
-}
-
 static gboolean
 gst_element_default_query (GstElement * element, GstQuery * query)
 {
@@ -1792,47 +1937,34 @@ gst_element_default_query (GstElement * element, GstQuery * query)
  *
  * Please note that some queries might need a running pipeline to work.
  *
- * Returns: TRUE if the query could be performed.
+ * Returns: %TRUE if the query could be performed.
  *
  * MT safe.
  */
 gboolean
 gst_element_query (GstElement * element, GstQuery * query)
 {
-  GstElementClass *oclass;
-  gboolean result = FALSE;
+  GstElementClass *klass;
+  gboolean res = FALSE;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
   g_return_val_if_fail (query != NULL, FALSE);
 
-  oclass = GST_ELEMENT_GET_CLASS (element);
+  GST_TRACER_ELEMENT_QUERY_PRE (element, query);
 
-  if (oclass->query) {
+  klass = GST_ELEMENT_GET_CLASS (element);
+  if (klass->query) {
     GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s",
         GST_ELEMENT_NAME (element));
-    result = oclass->query (element, query);
-  } else {
-    result = gst_element_default_query (element, query);
+    res = klass->query (element, query);
   }
-  return result;
+
+  GST_TRACER_ELEMENT_QUERY_POST (element, query, res);
+  return res;
 }
 
-/**
- * gst_element_post_message:
- * @element: a #GstElement posting the message
- * @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
- * additional reference before calling.
- *
- * Returns: %TRUE if the message was successfully posted. The function returns
- * %FALSE if the element did not have a bus.
- *
- * MT safe.
- */
-gboolean
-gst_element_post_message (GstElement * element, GstMessage * message)
+static gboolean
+gst_element_post_message_default (GstElement * element, GstMessage * message)
 {
   GstBus *bus;
   gboolean result = FALSE;
@@ -1868,13 +2000,48 @@ no_bus:
 }
 
 /**
+ * gst_element_post_message:
+ * @element: a #GstElement posting the message
+ * @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
+ * additional reference before calling.
+ *
+ * Returns: %TRUE if the message was successfully posted. The function returns
+ * %FALSE if the element did not have a bus.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_post_message (GstElement * element, GstMessage * message)
+{
+  GstElementClass *klass;
+  gboolean res = FALSE;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+
+  GST_TRACER_ELEMENT_POST_MESSAGE_PRE (element, message);
+
+  klass = GST_ELEMENT_GET_CLASS (element);
+  if (klass->post_message)
+    res = klass->post_message (element, message);
+  else
+    gst_message_unref (message);
+
+  GST_TRACER_ELEMENT_POST_MESSAGE_POST (element, res);
+  return res;
+}
+
+/**
  * _gst_element_error_printf:
- * @format: the printf-like format to use, or %NULL
+ * @format: (allow-none): the printf-like format to use, or %NULL
  *
  * This function is only used internally by the gst_element_error() macro.
  *
- * Returns: (transfer full): a newly allocated string, or %NULL if the format
- *     was %NULL or ""
+ * Returns: (transfer full) (nullable): a newly allocated string, or
+ *     %NULL if the format was %NULL or ""
  *
  * MT safe.
  */
@@ -1883,6 +2050,7 @@ _gst_element_error_printf (const gchar * format, ...)
 {
   va_list args;
   gchar *buffer;
+  int len;
 
   if (format == NULL)
     return NULL;
@@ -1890,13 +2058,19 @@ _gst_element_error_printf (const gchar * format, ...)
     return NULL;
 
   va_start (args, format);
-  buffer = g_strdup_vprintf (format, args);
+
+  len = __gst_vasprintf (&buffer, format, args);
+
   va_end (args);
+
+  if (len < 0)
+    buffer = NULL;
+
   return buffer;
 }
 
 /**
- * gst_element_message_full:
+ * gst_element_message_full_with_details:
  * @element:  a #GstElement to send message from
  * @type:     the #GstMessageType
  * @domain:   the GStreamer GError domain this message belongs to
@@ -1910,18 +2084,20 @@ _gst_element_error_printf (const gchar * format, ...)
  * @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
+ * @structure:(transfer full): optional details structure
  *
  * 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.
+ * Since: 1.10
  */
-void gst_element_message_full
+void gst_element_message_full_with_details
     (GstElement * element, GstMessageType type,
     GQuark domain, gint code, gchar * text,
-    gchar * debug, const gchar * file, const gchar * function, gint line)
+    gchar * debug, const gchar * file, const gchar * function, gint line,
+    GstStructure * structure)
 {
   GError *gerror = NULL;
   gchar *name;
@@ -1968,20 +2144,24 @@ void gst_element_message_full
   switch (type) {
     case GST_MESSAGE_ERROR:
       message =
-          gst_message_new_error (GST_OBJECT_CAST (element), gerror, sent_debug);
+          gst_message_new_error_with_details (GST_OBJECT_CAST (element), gerror,
+          sent_debug, structure);
       break;
     case GST_MESSAGE_WARNING:
-      message = gst_message_new_warning (GST_OBJECT_CAST (element), gerror,
-          sent_debug);
+      message =
+          gst_message_new_warning_with_details (GST_OBJECT_CAST (element),
+          gerror, sent_debug, structure);
       break;
     case GST_MESSAGE_INFO:
-      message = gst_message_new_info (GST_OBJECT_CAST (element), gerror,
-          sent_debug);
+      message =
+          gst_message_new_info_with_details (GST_OBJECT_CAST (element), gerror,
+          sent_debug, structure);
       break;
     default:
       g_assert_not_reached ();
       break;
   }
+
   gst_element_post_message (element, message);
 
   GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posted %s message: %s",
@@ -1994,6 +2174,38 @@ void gst_element_message_full
 }
 
 /**
+ * gst_element_message_full:
+ * @element:  a #GstElement to send message from
+ * @type:     the #GstMessageType
+ * @domain:   the GStreamer GError domain this message belongs to
+ * @code:     the GError code belonging to the domain
+ * @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, 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.
+ */
+void gst_element_message_full
+    (GstElement * element, GstMessageType type,
+    GQuark domain, gint code, gchar * text,
+    gchar * debug, const gchar * file, const gchar * function, gint line)
+{
+  gst_element_message_full_with_details (element, type, domain, code, text,
+      debug, file, function, line, NULL);
+}
+
+/**
  * gst_element_is_locked_state:
  * @element: a #GstElement.
  *
@@ -2005,7 +2217,7 @@ void gst_element_message_full
  *
  * MT safe.
  *
- * Returns: TRUE, if the element's state is locked.
+ * Returns: %TRUE, if the element's state is locked.
  */
 gboolean
 gst_element_is_locked_state (GstElement * element)
@@ -2015,7 +2227,7 @@ gst_element_is_locked_state (GstElement * element)
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   GST_OBJECT_LOCK (element);
-  result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+  result = GST_ELEMENT_IS_LOCKED_STATE (element);
   GST_OBJECT_UNLOCK (element);
 
   return result;
@@ -2024,14 +2236,18 @@ gst_element_is_locked_state (GstElement * element)
 /**
  * gst_element_set_locked_state:
  * @element: a #GstElement
- * @locked_state: TRUE to lock the element's state
+ * @locked_state: %TRUE to lock the element's state
  *
  * Locks the state of an element, so state changes of the parent don't affect
  * this element anymore.
  *
+ * Note that this is racy if the state lock of the parent bin is not taken.
+ * The parent bin might've just checked the flag in another thread and as the
+ * next step proceed to change the child element's state.
+ *
  * MT safe.
  *
- * Returns: TRUE if the state was changed, FALSE if bad parameters were given
+ * Returns: %TRUE if the state was changed, %FALSE if bad parameters were given
  * or the elements state-locking needed no change.
  */
 gboolean
@@ -2042,7 +2258,7 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
 
   GST_OBJECT_LOCK (element);
-  old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+  old = GST_ELEMENT_IS_LOCKED_STATE (element);
 
   if (G_UNLIKELY (old == locked_state))
     goto was_ok;
@@ -2050,11 +2266,11 @@ 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_OBJECT_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
+    GST_OBJECT_FLAG_SET (element, GST_ELEMENT_FLAG_LOCKED_STATE);
   } else {
     GST_CAT_DEBUG (GST_CAT_STATES, "unlocking state of element %s",
         GST_ELEMENT_NAME (element));
-    GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
+    GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_FLAG_LOCKED_STATE);
   }
   GST_OBJECT_UNLOCK (element);
 
@@ -2076,9 +2292,9 @@ was_ok:
  * @element: a #GstElement.
  *
  * Tries to change the state of the element to the same as its parent.
- * If this function returns FALSE, the state of element is undefined.
+ * 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.
+ * Returns: %TRUE, if the element's state could be synced to the parent's state.
  *
  * MT safe.
  */
@@ -2166,22 +2382,9 @@ gst_element_get_state_func (GstElement * element,
 
   old_pending = GST_STATE_PENDING (element);
   if (old_pending != GST_STATE_VOID_PENDING) {
-    GTimeVal *timeval, abstimeout;
+    gboolean signaled;
     guint32 cookie;
 
-    if (timeout != GST_CLOCK_TIME_NONE) {
-      glong add = timeout / 1000;
-
-      if (add == 0)
-        goto done;
-
-      /* make timeout absolute */
-      g_get_current_time (&abstimeout);
-      g_time_val_add (&abstimeout, add);
-      timeval = &abstimeout;
-    } else {
-      timeval = NULL;
-    }
     /* get cookie to detect state changes during waiting */
     cookie = element->state_cookie;
 
@@ -2189,7 +2392,17 @@ gst_element_get_state_func (GstElement * 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)) {
+    if (timeout != GST_CLOCK_TIME_NONE) {
+      gint64 end_time;
+      /* make timeout absolute */
+      end_time = g_get_monotonic_time () + (timeout / 1000);
+      signaled = GST_STATE_WAIT_UNTIL (element, end_time);
+    } else {
+      GST_STATE_WAIT (element);
+      signaled = TRUE;
+    }
+
+    if (!signaled) {
       GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timed out");
       /* timeout triggered */
       ret = GST_STATE_CHANGE_ASYNC;
@@ -2235,7 +2448,7 @@ interrupted:
     if (pending)
       *pending = GST_STATE_VOID_PENDING;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interruped");
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interrupted");
 
     GST_OBJECT_UNLOCK (element);
 
@@ -2390,6 +2603,8 @@ _priv_gst_element_state_changed (GstElement * element, GstState oldstate,
  * This method is used internally and should normally not be called by plugins
  * or applications.
  *
+ * This function must be called with STATE_LOCK held.
+ *
  * Returns: The result of the commit state change.
  *
  * MT safe.
@@ -2478,19 +2693,16 @@ complete:
 }
 
 /**
- * gst_element_lost_state_full:
+ * gst_element_lost_state:
  * @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.
+ * An ASYNC_START message is posted. 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 or %GST_STATE_PLAYING state after a flush,
@@ -2500,13 +2712,9 @@ complete:
  *
  * 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_full (GstElement * element, gboolean new_base_time)
+gst_element_lost_state (GstElement * element)
 {
   GstState old_state, new_state;
   GstMessage *message;
@@ -2538,14 +2746,11 @@ gst_element_lost_state_full (GstElement * element, gboolean new_base_time)
   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);
 
   _priv_gst_element_state_changed (element, new_state, new_state, new_state);
 
-  message =
-      gst_message_new_async_start (GST_OBJECT_CAST (element), new_base_time);
+  message = gst_message_new_async_start (GST_OBJECT_CAST (element));
   gst_element_post_message (element, message);
 
   return;
@@ -2559,31 +2764,13 @@ only_async_start:
   {
     GST_OBJECT_UNLOCK (element);
 
-    message = gst_message_new_async_start (GST_OBJECT_CAST (element), TRUE);
+    message = gst_message_new_async_start (GST_OBJECT_CAST (element));
     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);
-}
-
-/**
  * gst_element_set_state:
  * @element: a #GstElement to change state of.
  * @state: the element's new #GstState.
@@ -2596,7 +2783,8 @@ gst_element_lost_state (GstElement * element)
  * 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.
+ * of the state change or it can wait for a %GST_MESSAGE_ASYNC_DONE or
+ * %GST_MESSAGE_STATE_CHANGED on the bus.
  *
  * State changes to %GST_STATE_READY or %GST_STATE_NULL never return
  * #GST_STATE_CHANGE_ASYNC.
@@ -2757,12 +2945,16 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
 
   oclass = GST_ELEMENT_GET_CLASS (element);
 
+  GST_TRACER_ELEMENT_CHANGE_STATE_PRE (element, 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;
 
+  GST_TRACER_ELEMENT_CHANGE_STATE_POST (element, transition, ret);
+
   switch (ret) {
     case GST_STATE_CHANGE_FAILURE:
       GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
@@ -2794,14 +2986,14 @@ gst_element_change_state (GstElement * element, GstStateChange transition)
     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
+      /* we can commit the state now which will proceed 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
+      /* we can commit the state now which will proceed to
        * the next state */
       ret = gst_element_continue_state (element, ret);
       break;
@@ -2837,29 +3029,24 @@ invalid_return:
 }
 
 /* gst_iterator_fold functions for pads_activate
- * Stop the iterator if activating one pad failed. */
+ * Stop the iterator if activating one pad failed, but only if that pad
+ * has not been removed from the element. */
 static gboolean
-activate_pads (GstPad * pad, GValue * ret, gboolean * active)
+activate_pads (const GValue * vpad, GValue * ret, gboolean * active)
 {
+  GstPad *pad = g_value_get_object (vpad);
   gboolean cont = TRUE;
 
-  if (!(cont = gst_pad_set_active (pad, *active)))
-    g_value_set_boolean (ret, FALSE);
+  if (!gst_pad_set_active (pad, *active)) {
+    if (GST_PAD_PARENT (pad) != NULL) {
+      cont = FALSE;
+      g_value_set_boolean (ret, FALSE);
+    }
+  }
 
-  /* unref the object that was reffed for us by _fold */
-  gst_object_unref (pad);
   return cont;
 }
 
-/* set the caps on the pad to NULL */
-static gboolean
-clear_caps (GstPad * pad, GValue * ret, gboolean * active)
-{
-  gst_pad_set_caps (pad, NULL);
-  gst_object_unref (pad);
-  return TRUE;
-}
-
 /* returns false on error or early cutout of the fold, true if all
  * pads in @iter were (de)activated successfully. */
 static gboolean
@@ -2907,7 +3094,7 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   gboolean res;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
-      "pads_activate with active %d", active);
+      "%s pads", active ? "activate" : "deactivate");
 
   iter = gst_element_iterate_src_pads (element);
   res =
@@ -2925,19 +3112,8 @@ gst_element_pads_activate (GstElement * element, gboolean active)
   if (G_UNLIKELY (!res))
     goto sink_failed;
 
-  if (!active) {
-    /* clear the caps on all pads, this should never fail */
-    iter = gst_element_iterate_pads (element);
-    res =
-        iterator_activate_fold_with_resync (iter,
-        (GstIteratorFoldFunction) clear_caps, &active);
-    gst_iterator_free (iter);
-    if (G_UNLIKELY (!res))
-      goto caps_failed;
-  }
-
   GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
-      "pads_activate successful");
+      "pad %sactivation successful", active ? "" : "de");
 
   return TRUE;
 
@@ -2945,7 +3121,7 @@ gst_element_pads_activate (GstElement * element, gboolean active)
 src_failed:
   {
     GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
-        "source pads_activate failed");
+        "pad %sactivation failed", active ? "" : "de");
     return FALSE;
   }
 sink_failed:
@@ -2954,12 +3130,6 @@ sink_failed:
         "sink pads_activate failed");
     return FALSE;
   }
-caps_failed:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
-        "failed to clear caps on pads");
-    return FALSE;
-  }
 }
 
 /* is called with STATE_LOCK */
@@ -2968,7 +3138,6 @@ 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);
 
@@ -2997,21 +3166,34 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-    case GST_STATE_CHANGE_READY_TO_NULL:
+    case GST_STATE_CHANGE_READY_TO_NULL:{
+      GList *l;
+
       /* 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_CHANGE_FAILURE;
-      } else {
-        gst_element_set_base_time (element, 0);
       }
 
-      /* In null state release the reference to the clock */
+      /* Remove all non-persistent contexts */
       GST_OBJECT_LOCK (element);
-      clock_p = &element->clock;
-      gst_object_replace ((GstObject **) clock_p, NULL);
+      for (l = element->contexts; l;) {
+        GstContext *context = l->data;
+
+        if (!gst_context_is_persistent (context)) {
+          GList *next;
+
+          gst_context_unref (context);
+          next = l->next;
+          element->contexts = g_list_delete_link (element->contexts, l);
+          l = next;
+        } else {
+          l = l->next;
+        }
+      }
       GST_OBJECT_UNLOCK (element);
       break;
+    }
     default:
       /* this will catch real but unhandled state changes;
        * can only be caused by:
@@ -3044,8 +3226,8 @@ was_ok:
  *
  * Retrieves the factory that was used to create this element.
  *
- * Returns: (transfer none): the #GstElementFactory used for creating this
- *     element. no refcounting is needed.
+ * Returns: (transfer none) (nullable): the #GstElementFactory used for creating this
+ *     element or %NULL if element has not been registered (static element). no refcounting is needed.
  */
 GstElementFactory *
 gst_element_get_factory (GstElement * element)
@@ -3061,22 +3243,46 @@ gst_element_dispose (GObject * object)
   GstElement *element = GST_ELEMENT_CAST (object);
   GstClock **clock_p;
   GstBus **bus_p;
+  GstElementClass *oclass;
+  GList *walk;
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p dispose", element);
 
   if (GST_STATE (element) != GST_STATE_NULL)
     goto not_null;
 
-  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));
+  /* start by releasing all request pads, this might also remove some dynamic
+   * pads */
+  walk = element->pads;
+  while (walk) {
+    GstPad *pad = GST_PAD_CAST (walk->data);
+
+    walk = walk->next;
+
+    if (oclass->release_pad && GST_PAD_PAD_TEMPLATE (pad) &&
+        GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad))
+        == GST_PAD_REQUEST) {
+      GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+          "removing request pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+      oclass->release_pad (element, pad);
+
+      /* in case the release_pad function removed the next pad too */
+      if (walk && g_list_position (element->pads, walk) == -1)
+        walk = element->pads;
+    }
   }
-  if (G_UNLIKELY (element->pads != NULL)) {
-    g_critical ("could not remove pads from element %s",
-        GST_STR_NULL (GST_OBJECT_NAME (object)));
+  /* remove the remaining pads */
+  while (element->pads) {
+    GstPad *pad = GST_PAD_CAST (element->pads->data);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+        "removing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+    if (!gst_element_remove_pad (element, pad)) {
+      /* only happens when someone unparented our pad.. */
+      g_critical ("failed to remove pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+      break;
+    }
   }
 
   GST_OBJECT_LOCK (element);
@@ -3084,9 +3290,11 @@ gst_element_dispose (GObject * object)
   bus_p = &element->bus;
   gst_object_replace ((GstObject **) clock_p, NULL);
   gst_object_replace ((GstObject **) bus_p, NULL);
+  g_list_free_full (element->contexts, (GDestroyNotify) gst_context_unref);
   GST_OBJECT_UNLOCK (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p parent class dispose",
+      element);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 
@@ -3097,7 +3305,7 @@ not_null:
   {
     gboolean is_locked;
 
-    is_locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+    is_locked = GST_ELEMENT_IS_LOCKED_STATE (element);
     g_critical
         ("\nTrying to dispose element %s, but it is in %s%s instead of the NULL"
         " state.\n"
@@ -3117,164 +3325,17 @@ gst_element_finalize (GObject * object)
 {
   GstElement *element = GST_ELEMENT_CAST (object);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p finalize", element);
 
-  GST_STATE_LOCK (element);
-  if (element->state_cond)
-    g_cond_free (element->state_cond);
-  element->state_cond = NULL;
-  GST_STATE_UNLOCK (element);
-  g_static_rec_mutex_free (element->state_lock);
-  g_slice_free (GStaticRecMutex, element->state_lock);
-  element->state_lock = NULL;
+  g_cond_clear (&element->state_cond);
+  g_rec_mutex_clear (&element->state_lock);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p finalize parent",
+      element);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
-/**
- * gst_element_save_thyself:
- * @element: a #GstElement to save.
- * @parent: the xml parent node.
- *
- * Saves the element as part of the given XML structure.
- *
- * Returns: the new #xmlNodePtr.
- */
-static xmlNodePtr
-gst_element_save_thyself (GstObject * object, xmlNodePtr parent)
-{
-  GList *pads;
-  GstElementClass *oclass;
-  GParamSpec **specs, *spec;
-  guint nspecs;
-  guint i;
-  GValue value = { 0, };
-  GstElement *element;
-
-  g_return_val_if_fail (GST_IS_ELEMENT (object), parent);
-
-  element = GST_ELEMENT_CAST (object);
-
-  oclass = GST_ELEMENT_GET_CLASS (element);
-
-  xmlNewChild (parent, NULL, (xmlChar *) "name",
-      (xmlChar *) GST_ELEMENT_NAME (element));
-
-  if (oclass->elementfactory != NULL) {
-    GstElementFactory *factory = (GstElementFactory *) oclass->elementfactory;
-
-    xmlNewChild (parent, NULL, (xmlChar *) "type",
-        (xmlChar *) GST_PLUGIN_FEATURE (factory)->name);
-  }
-
-  /* params */
-  specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &nspecs);
-
-  for (i = 0; i < nspecs; i++) {
-    spec = specs[i];
-    if (spec->flags & G_PARAM_READABLE) {
-      xmlNodePtr param;
-      char *contents;
-
-      g_value_init (&value, spec->value_type);
-
-      g_object_get_property (G_OBJECT (element), spec->name, &value);
-      param = xmlNewChild (parent, NULL, (xmlChar *) "param", NULL);
-      xmlNewChild (param, NULL, (xmlChar *) "name", (xmlChar *) spec->name);
-
-      if (G_IS_PARAM_SPEC_STRING (spec))
-        contents = g_value_dup_string (&value);
-      else if (G_IS_PARAM_SPEC_ENUM (spec))
-        contents = g_strdup_printf ("%d", g_value_get_enum (&value));
-      else if (G_IS_PARAM_SPEC_INT64 (spec))
-        contents = g_strdup_printf ("%" G_GINT64_FORMAT,
-            g_value_get_int64 (&value));
-      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);
-      g_free (contents);
-
-      g_value_unset (&value);
-    }
-  }
-
-  g_free (specs);
-
-  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_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_previous (pads);
-  }
-
-  return parent;
-}
-
-static void
-gst_element_restore_thyself (GstObject * object, xmlNodePtr self)
-{
-  xmlNodePtr children;
-  GstElement *element;
-  gchar *name = NULL;
-  gchar *value = NULL;
-
-  element = GST_ELEMENT_CAST (object);
-  g_return_if_fail (element != NULL);
-
-  /* parameters */
-  children = self->xmlChildrenNode;
-  while (children) {
-    if (!strcmp ((char *) children->name, "param")) {
-      xmlNodePtr child = children->xmlChildrenNode;
-
-      while (child) {
-        if (!strcmp ((char *) child->name, "name")) {
-          name = (gchar *) xmlNodeGetContent (child);
-        } else if (!strcmp ((char *) child->name, "value")) {
-          value = (gchar *) xmlNodeGetContent (child);
-        }
-        child = child->next;
-      }
-      /* FIXME: can this just be g_object_set ? */
-      gst_util_set_object_arg (G_OBJECT (element), name, value);
-      /* g_object_set (G_OBJECT (element), name, value, NULL); */
-      g_free (name);
-      g_free (value);
-    }
-    children = children->next;
-  }
-
-  /* pads */
-  children = self->xmlChildrenNode;
-  while (children) {
-    if (!strcmp ((char *) children->name, "pad")) {
-      gst_pad_load_and_link (children, GST_OBJECT_CAST (element));
-    }
-    children = children->next;
-  }
-
-  if (GST_OBJECT_CLASS (parent_class)->restore_thyself)
-    (GST_OBJECT_CLASS (parent_class)->restore_thyself) (object, self);
-}
-#endif /* GST_DISABLE_LOADSAVE */
-
 static void
 gst_element_set_bus_func (GstElement * element, GstBus * bus)
 {
@@ -3293,7 +3354,7 @@ gst_element_set_bus_func (GstElement * element, GstBus * bus)
 /**
  * gst_element_set_bus:
  * @element: a #GstElement to set the bus of.
- * @bus: (transfer none): the #GstBus to set.
+ * @bus: (transfer none) (allow-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.
@@ -3320,7 +3381,8 @@ gst_element_set_bus (GstElement * element, GstBus * bus)
  * Returns the bus of the element. Note that only a #GstPipeline will provide a
  * bus for the application.
  *
- * Returns: (transfer full): the element's #GstBus. unref after usage.
+ * Returns: (transfer full) (nullable): the element's #GstBus. unref after
+ * usage.
  *
  * MT safe.
  */
@@ -3341,3 +3403,355 @@ gst_element_get_bus (GstElement * element)
 
   return result;
 }
+
+static void
+gst_element_set_context_default (GstElement * element, GstContext * context)
+{
+  const gchar *context_type;
+  GList *l;
+
+  g_return_if_fail (GST_IS_CONTEXT (context));
+  context_type = gst_context_get_context_type (context);
+  g_return_if_fail (context_type != NULL);
+
+  GST_OBJECT_LOCK (element);
+  for (l = element->contexts; l; l = l->next) {
+    GstContext *tmp = l->data;
+    const gchar *tmp_type = gst_context_get_context_type (tmp);
+
+    /* Always store newest context but never replace
+     * a persistent one by a non-persistent one */
+    if (g_strcmp0 (context_type, tmp_type) == 0 &&
+        (gst_context_is_persistent (context) ||
+            !gst_context_is_persistent (tmp))) {
+      gst_context_replace ((GstContext **) & l->data, context);
+      break;
+    }
+  }
+  /* Not found? Add */
+  if (l == NULL) {
+    element->contexts =
+        g_list_prepend (element->contexts, gst_context_ref (context));
+  }
+  GST_OBJECT_UNLOCK (element);
+}
+
+/**
+ * gst_element_set_context:
+ * @element: a #GstElement to set the context of.
+ * @context: (transfer none): the #GstContext to set.
+ *
+ * Sets the context of the element. Increases the refcount of the context.
+ *
+ * MT safe.
+ */
+void
+gst_element_set_context (GstElement * element, GstContext * context)
+{
+  GstElementClass *oclass;
+
+  g_return_if_fail (GST_IS_ELEMENT (element));
+  g_return_if_fail (GST_IS_CONTEXT (context));
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
+
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element,
+      "set context %p %" GST_PTR_FORMAT, context,
+      gst_context_get_structure (context));
+
+  if (oclass->set_context)
+    oclass->set_context (element, context);
+}
+
+/**
+ * gst_element_get_contexts:
+ * @element: a #GstElement to set the context of.
+ *
+ * Gets the contexts set on the element.
+ *
+ * MT safe.
+ *
+ * Returns: (element-type Gst.Context) (transfer full): List of #GstContext
+ *
+ * Since: 1.8
+ */
+GList *
+gst_element_get_contexts (GstElement * element)
+{
+  GList *ret;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  GST_OBJECT_LOCK (element);
+  ret = g_list_copy_deep (element->contexts, (GCopyFunc) gst_context_ref, NULL);
+  GST_OBJECT_UNLOCK (element);
+
+  return ret;
+}
+
+static gint
+_match_context_type (GstContext * c1, const gchar * context_type)
+{
+  const gchar *c1_type;
+
+  c1_type = gst_context_get_context_type (c1);
+
+  return g_strcmp0 (c1_type, context_type);
+}
+
+/**
+ * gst_element_get_context_unlocked:
+ * @element: a #GstElement to get the context of.
+ * @context_type: a name of a context to retrieve
+ *
+ * Gets the context with @context_type set on the element or NULL.
+ *
+ * Returns: (transfer full) (nullable): A #GstContext or NULL
+ *
+ * Since: 1.8
+ */
+GstContext *
+gst_element_get_context_unlocked (GstElement * element,
+    const gchar * context_type)
+{
+  GstContext *ret = NULL;
+  GList *node;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  node =
+      g_list_find_custom (element->contexts, context_type,
+      (GCompareFunc) _match_context_type);
+  if (node && node->data)
+    ret = gst_context_ref (node->data);
+
+  return ret;
+}
+
+/**
+ * gst_element_get_context:
+ * @element: a #GstElement to get the context of.
+ * @context_type: a name of a context to retrieve
+ *
+ * Gets the context with @context_type set on the element or NULL.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): A #GstContext or NULL
+ *
+ * Since: 1.8
+ */
+GstContext *
+gst_element_get_context (GstElement * element, const gchar * context_type)
+{
+  GstContext *ret = NULL;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+  GST_OBJECT_LOCK (element);
+  ret = gst_element_get_context_unlocked (element, context_type);
+  GST_OBJECT_UNLOCK (element);
+
+  return ret;
+}
+
+static void
+gst_element_property_post_notify_msg (GstElement * element, GObject * obj,
+    GParamSpec * pspec, gboolean include_value)
+{
+  GValue val = G_VALUE_INIT;
+  GValue *v;
+
+  GST_LOG_OBJECT (element, "property '%s' of object %" GST_PTR_FORMAT " has "
+      "changed, posting message with%s value", pspec->name, obj,
+      include_value ? "" : "out");
+
+  if (include_value && (pspec->flags & G_PARAM_READABLE) != 0) {
+    g_value_init (&val, pspec->value_type);
+    g_object_get_property (obj, pspec->name, &val);
+    v = &val;
+  } else {
+    v = NULL;
+  }
+  gst_element_post_message (element,
+      gst_message_new_property_notify (GST_OBJECT_CAST (obj), pspec->name, v));
+}
+
+static void
+gst_element_property_deep_notify_cb (GstElement * element, GObject * prop_obj,
+    GParamSpec * pspec, gpointer user_data)
+{
+  gboolean include_value = GPOINTER_TO_INT (user_data);
+
+  gst_element_property_post_notify_msg (element, prop_obj, pspec,
+      include_value);
+}
+
+static void
+gst_element_property_notify_cb (GObject * obj, GParamSpec * pspec,
+    gpointer user_data)
+{
+  gboolean include_value = GPOINTER_TO_INT (user_data);
+
+  gst_element_property_post_notify_msg (GST_ELEMENT_CAST (obj), obj, pspec,
+      include_value);
+}
+
+/**
+ * gst_element_add_property_notify_watch:
+ * @element: a #GstElement to watch for property changes
+ * @property_name: (allow-none): name of property to watch for changes, or
+ *     NULL to watch all properties
+ * @include_value: whether to include the new property value in the message
+ *
+ * Returns: a watch id, which can be used in connection with
+ *     gst_element_remove_property_notify_watch() to remove the watch again.
+ *
+ * Since: 1.10
+ */
+gulong
+gst_element_add_property_notify_watch (GstElement * element,
+    const gchar * property_name, gboolean include_value)
+{
+  const gchar *sep;
+  gchar *signal_name;
+  gulong id;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), 0);
+
+  sep = (property_name != NULL) ? "::" : NULL;
+  signal_name = g_strconcat ("notify", sep, property_name, NULL);
+  id = g_signal_connect (element, signal_name,
+      G_CALLBACK (gst_element_property_notify_cb),
+      GINT_TO_POINTER (include_value));
+  g_free (signal_name);
+
+  return id;
+}
+
+/**
+ * gst_element_add_property_deep_notify_watch:
+ * @element: a #GstElement to watch (recursively) for property changes
+ * @property_name: (allow-none): name of property to watch for changes, or
+ *     NULL to watch all properties
+ * @include_value: whether to include the new property value in the message
+ *
+ * Returns: a watch id, which can be used in connection with
+ *     gst_element_remove_property_notify_watch() to remove the watch again.
+ *
+ * Since: 1.10
+ */
+gulong
+gst_element_add_property_deep_notify_watch (GstElement * element,
+    const gchar * property_name, gboolean include_value)
+{
+  const gchar *sep;
+  gchar *signal_name;
+  gulong id;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), 0);
+
+  sep = (property_name != NULL) ? "::" : NULL;
+  signal_name = g_strconcat ("deep-notify", sep, property_name, NULL);
+  id = g_signal_connect (element, signal_name,
+      G_CALLBACK (gst_element_property_deep_notify_cb),
+      GINT_TO_POINTER (include_value));
+  g_free (signal_name);
+
+  return id;
+}
+
+/**
+ * gst_element_remove_property_notify_watch:
+ * @element: a #GstElement being watched for property changes
+ * @watch_id: watch id to remove
+ *
+ * Since: 1.10
+ */
+void
+gst_element_remove_property_notify_watch (GstElement * element, gulong watch_id)
+{
+  g_signal_handler_disconnect (element, watch_id);
+}
+
+typedef struct
+{
+  GstElement *element;
+  GstElementCallAsyncFunc func;
+  gpointer user_data;
+  GDestroyNotify destroy_notify;
+} GstElementCallAsyncData;
+
+static void
+gst_element_call_async_func (gpointer data, gpointer user_data)
+{
+  GstElementCallAsyncData *async_data = data;
+
+  async_data->func (async_data->element, async_data->user_data);
+  if (async_data->destroy_notify)
+    async_data->destroy_notify (async_data->user_data);
+  gst_object_unref (async_data->element);
+  g_free (async_data);
+}
+
+/**
+ * gst_element_call_async:
+ * @element: a #GstElement
+ * @func: Function to call asynchronously from another thread
+ * @user_data: Data to pass to @func
+ * @destroy_notify: GDestroyNotify for @user_data
+ *
+ * Calls @func from another thread and passes @user_data to it. This is to be
+ * used for cases when a state change has to be performed from a streaming
+ * thread, directly via gst_element_set_state() or indirectly e.g. via SEEK
+ * events.
+ *
+ * Calling those functions directly from the streaming thread will cause
+ * deadlocks in many situations, as they might involve waiting for the
+ * streaming thread to shut down from this very streaming thread.
+ *
+ * MT safe.
+ *
+ * Since: 1.10
+ */
+void
+gst_element_call_async (GstElement * element, GstElementCallAsyncFunc func,
+    gpointer user_data, GDestroyNotify destroy_notify)
+{
+  GstElementCallAsyncData *async_data;
+
+  g_return_if_fail (GST_IS_ELEMENT (element));
+
+  async_data = g_new0 (GstElementCallAsyncData, 1);
+  async_data->element = gst_object_ref (element);
+  async_data->func = func;
+  async_data->user_data = user_data;
+  async_data->destroy_notify = destroy_notify;
+
+  g_thread_pool_push (gst_element_pool, async_data, NULL);
+}
+
+void
+_priv_gst_element_cleanup (void)
+{
+  if (gst_element_pool) {
+    g_thread_pool_free (gst_element_pool, FALSE, TRUE);
+    gst_element_setup_thread_pool ();
+  }
+}
+
+GstStructure *
+gst_make_element_message_details (const char *name, ...)
+{
+  GstStructure *structure;
+  va_list varargs;
+
+  if (name == NULL)
+    return NULL;
+
+  va_start (varargs, name);
+  structure = gst_structure_new_valist ("details", name, varargs);
+  va_end (varargs);
+
+  return structure;
+}