gst/gstpad.c: Only sanity-check the buffer size if requested_caps == buffer_caps...
[platform/upstream/gstreamer.git] / gst / gstpad.c
index 9f00301..df8eba2 100644 (file)
  * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out
  * or pull in a buffer.
  *
- * To send a #GstEvent on a pad, use gst_pad_send_event().
- *
- * Last reviewed on 2005-11-23 (0.9.5)
+ * To send a #GstEvent on a pad, use gst_pad_send_event() and
+ * gst_pad_push_event().
  *
+ * Last reviewed on 2006-07-06 (0.10.9)
  */
 
 #include "gst_private.h"
@@ -108,6 +108,7 @@ static GstFlowReturn handle_pad_block (GstPad * pad);
 static GstCaps *gst_pad_get_caps_unlocked (GstPad * pad);
 static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ);
 static gboolean gst_pad_activate_default (GstPad * pad);
+static gboolean gst_pad_acceptcaps_default (GstPad * pad, GstCaps * caps);
 
 #ifndef GST_DISABLE_LOADSAVE
 static xmlNodePtr gst_pad_save_thyself (GstObject * object, xmlNodePtr parent);
@@ -122,12 +123,13 @@ static GQuark event_quark;
 
 typedef struct
 {
-  gint ret;
-  gchar *name;
+  const gint ret;
+  const gchar *name;
   GQuark quark;
 } GstFlowQuarks;
 
 static GstFlowQuarks flow_quarks[] = {
+  {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
   {GST_FLOW_RESEND, "resend", 0},
   {GST_FLOW_OK, "ok", 0},
   {GST_FLOW_NOT_LINKED, "not-linked", 0},
@@ -136,6 +138,7 @@ static GstFlowQuarks flow_quarks[] = {
   {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
   {GST_FLOW_ERROR, "error", 0},
   {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
+  {GST_FLOW_CUSTOM_ERROR, "custom-error", 0},
 
   {0, NULL, 0}
 };
@@ -146,13 +149,15 @@ static GstFlowQuarks flow_quarks[] = {
  *
  * Gets a string representing the given flow return.
  *
- * Returns: a string with the name of the flow return.
+ * Returns: a static string with the name of the flow return.
  */
 G_CONST_RETURN gchar *
 gst_flow_get_name (GstFlowReturn ret)
 {
   gint i;
 
+  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);
+
   for (i = 0; flow_quarks[i].name; i++) {
     if (ret == flow_quarks[i].ret)
       return flow_quarks[i].name;
@@ -174,6 +179,8 @@ gst_flow_to_quark (GstFlowReturn ret)
 {
   gint i;
 
+  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);
+
   for (i = 0; flow_quarks[i].name; i++) {
     if (ret == flow_quarks[i].ret)
       return flow_quarks[i].quark;
@@ -186,7 +193,7 @@ gst_pad_get_type (void)
 {
   static GType gst_pad_type = 0;
 
-  if (!gst_pad_type) {
+  if (G_UNLIKELY (gst_pad_type == 0)) {
     static const GTypeInfo pad_info = {
       sizeof (GstPadClass), NULL, NULL,
       (GClassInitFunc) gst_pad_class_init, NULL, NULL,
@@ -234,14 +241,12 @@ static void
 gst_pad_class_init (GstPadClass * klass)
 {
   GObjectClass *gobject_class;
-
-
   GstObjectClass *gstobject_class;
 
-  gobject_class = (GObjectClass *) klass;
-  gstobject_class = (GstObjectClass *) klass;
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstobject_class = GST_OBJECT_CLASS (klass);
 
-  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
+  parent_class = g_type_class_peek_parent (klass);
 
   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pad_dispose);
   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_pad_finalize);
@@ -301,17 +306,18 @@ gst_pad_class_init (GstPadClass * klass)
       NULL, gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1,
       GST_TYPE_MINI_OBJECT);
 
-  g_object_class_install_property (G_OBJECT_CLASS (klass), PAD_PROP_CAPS,
+  g_object_class_install_property (gobject_class, PAD_PROP_CAPS,
       g_param_spec_boxed ("caps", "Caps", "The capabilities of the pad",
-          GST_TYPE_CAPS, G_PARAM_READABLE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), PAD_PROP_DIRECTION,
+          GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
       g_param_spec_enum ("direction", "Direction", "The direction of the pad",
           GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), PAD_PROP_TEMPLATE,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  /* FIXME, Make G_PARAM_CONSTRUCT_ONLY when we fix ghostpads. */
+  g_object_class_install_property (gobject_class, PAD_PROP_TEMPLATE,
       g_param_spec_object ("template", "Template",
           "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
-          G_PARAM_READWRITE));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
 #ifndef GST_DISABLE_LOADSAVE
   gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_pad_save_thyself);
@@ -324,26 +330,29 @@ gst_pad_class_init (GstPadClass * klass)
 static void
 gst_pad_init (GstPad * pad)
 {
-  pad->direction = GST_PAD_UNKNOWN;
-  pad->peer = NULL;
+  GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
+  GST_PAD_PEER (pad) = NULL;
 
-  pad->chainfunc = NULL;
+  GST_PAD_CHAINFUNC (pad) = NULL;
 
-  pad->caps = NULL;
+  GST_PAD_LINKFUNC (pad) = NULL;
 
-  pad->linkfunc = NULL;
-  pad->getcapsfunc = NULL;
+  GST_PAD_CAPS (pad) = NULL;
+  GST_PAD_GETCAPSFUNC (pad) = NULL;
 
-  pad->activatefunc = GST_DEBUG_FUNCPTR (gst_pad_activate_default);
-  pad->eventfunc = GST_DEBUG_FUNCPTR (gst_pad_event_default);
-  pad->querytypefunc = GST_DEBUG_FUNCPTR (gst_pad_get_query_types_default);
-  pad->queryfunc = GST_DEBUG_FUNCPTR (gst_pad_query_default);
-  pad->intlinkfunc = GST_DEBUG_FUNCPTR (gst_pad_get_internal_links_default);
+  GST_PAD_ACTIVATEFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_activate_default);
+  GST_PAD_EVENTFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_event_default);
+  GST_PAD_QUERYTYPEFUNC (pad) =
+      GST_DEBUG_FUNCPTR (gst_pad_get_query_types_default);
+  GST_PAD_QUERYFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_query_default);
+  GST_PAD_INTLINKFUNC (pad) =
+      GST_DEBUG_FUNCPTR (gst_pad_get_internal_links_default);
+  GST_PAD_ACCEPTCAPSFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_acceptcaps_default);
 
   pad->do_buffer_signals = 0;
   pad->do_event_signals = 0;
 
-  GST_PAD_UNSET_FLUSHING (pad);
+  GST_PAD_SET_FLUSHING (pad);
 
   pad->preroll_lock = g_mutex_new ();
   pad->preroll_cond = g_cond_new ();
@@ -358,13 +367,22 @@ static void
 gst_pad_dispose (GObject * object)
 {
   GstPad *pad = GST_PAD (object);
+  GstPad *peer;
 
-  GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "dispose %s:%s",
-      GST_DEBUG_PAD_NAME (pad));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose");
 
-  /* we don't hold a ref to the peer so we can just set the
-   * peer to NULL. */
-  GST_PAD_PEER (pad) = NULL;
+  /* unlink the peer pad */
+  if ((peer = gst_pad_get_peer (pad))) {
+    /* window for MT unsafeness, someone else could unlink here
+     * and then we call unlink with wrong pads. The unlink
+     * function would catch this and safely return failed. */
+    if (GST_PAD_IS_SRC (pad))
+      gst_pad_unlink (pad, peer);
+    else
+      gst_pad_unlink (peer, pad);
+
+    gst_object_unref (peer);
+  }
 
   /* clear the caps */
   gst_caps_replace (&GST_PAD_CAPS (pad), NULL);
@@ -418,7 +436,7 @@ gst_pad_set_property (GObject * object, guint prop_id,
       break;
     case PAD_PROP_TEMPLATE:
       gst_pad_set_pad_template (GST_PAD_CAST (object),
-          (GstPadTemplate *) g_value_dup_gst_object (value));
+          (GstPadTemplate *) g_value_get_object (value));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -434,7 +452,9 @@ gst_pad_get_property (GObject * object, guint prop_id,
 
   switch (prop_id) {
     case PAD_PROP_CAPS:
+      GST_OBJECT_LOCK (object);
       g_value_set_boxed (value, GST_PAD_CAPS (object));
+      GST_OBJECT_UNLOCK (object);
       break;
     case PAD_PROP_DIRECTION:
       g_value_set_enum (value, GST_PAD_DIRECTION (object));
@@ -564,11 +584,11 @@ pre_activate (GstPad * pad, GstActivateMode new_mode)
       break;
     case GST_ACTIVATE_NONE:
       GST_OBJECT_LOCK (pad);
-      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE NONE, set flushing",
-          new_mode);
+      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE NONE, set flushing");
       GST_PAD_SET_FLUSHING (pad);
+      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
       /* unlock blocked pads so element can resume and stop */
-      GST_PAD_BLOCK_SIGNAL (pad);
+      GST_PAD_BLOCK_BROADCAST (pad);
       GST_OBJECT_UNLOCK (pad);
       break;
   }
@@ -585,11 +605,7 @@ post_activate (GstPad * pad, GstActivateMode new_mode)
     case GST_ACTIVATE_NONE:
       /* ensures that streaming stops */
       GST_PAD_STREAM_LOCK (pad);
-      /* while we're at it set activation mode */
-      GST_OBJECT_LOCK (pad);
-      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE %d", new_mode);
-      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
-      GST_OBJECT_UNLOCK (pad);
+      GST_DEBUG_OBJECT (pad, "stopped streaming");
       GST_PAD_STREAM_UNLOCK (pad);
       break;
   }
@@ -630,31 +646,43 @@ gst_pad_set_active (GstPad * pad, gboolean active)
   if (active) {
     switch (old) {
       case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad, "activating pad from push");
+        ret = TRUE;
+        break;
       case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad, "activating pad from pull");
         ret = TRUE;
         break;
       case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "activating pad from none");
         ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad);
         break;
     }
   } else {
     switch (old) {
       case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from push");
         ret = gst_pad_activate_push (pad, FALSE);
         break;
       case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
         ret = gst_pad_activate_pull (pad, FALSE);
         break;
       case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from none");
         ret = TRUE;
         break;
     }
   }
 
-  if (!active && !ret) {
+  if (!ret) {
     GST_OBJECT_LOCK (pad);
-    g_critical ("Failed to deactivate pad %s:%s, very bad",
-        GST_DEBUG_PAD_NAME (pad));
+    if (!active) {
+      g_critical ("Failed to deactivate pad %s:%s, very bad",
+          GST_DEBUG_PAD_NAME (pad));
+    } else {
+      GST_WARNING_OBJECT (pad, "Failed to activate pad");
+    }
     GST_OBJECT_UNLOCK (pad);
   }
 
@@ -668,9 +696,9 @@ gst_pad_set_active (GstPad * pad, gboolean active)
  *
  * Activates or deactivates the given pad in pull mode via dispatching to the
  * pad's activatepullfunc. For use from within pad activation functions only.
- * When called on sink pads, will first proxy the call to the peer pad, which is
- * expected to activate its internally linked pads from within its activate_pull
- * function.
+ * When called on sink pads, will first proxy the call to the peer pad, which
+ * is expected to activate its internally linked pads from within its
+ * activate_pull function.
  *
  * If you don't know what this is, you probably don't want to call it.
  *
@@ -690,25 +718,54 @@ gst_pad_activate_pull (GstPad * pad, gboolean active)
   old = GST_PAD_ACTIVATE_MODE (pad);
   GST_OBJECT_UNLOCK (pad);
 
-  if ((active && old == GST_ACTIVATE_PULL)
-      || (!active && old == GST_ACTIVATE_NONE))
-    goto was_ok;
-
   if (active) {
-    g_return_val_if_fail (old == GST_ACTIVATE_NONE, FALSE);
+    switch (old) {
+      case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad, "activating pad from pull, was ok");
+        goto was_ok;
+      case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad,
+            "activating pad from push, deactivate push first");
+        /* pad was activate in the wrong direction, deactivate it
+         * and reactivate it in pull mode */
+        if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
+          goto deactivate_failed;
+        /* fallthrough, pad is deactivated now. */
+      case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "activating pad from none");
+        break;
+    }
   } else {
-    g_return_val_if_fail (old == GST_ACTIVATE_PULL, FALSE);
+    switch (old) {
+      case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from none, was ok");
+        goto was_ok;
+      case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from push, weird");
+        /* pad was activated in the other direction, deactivate it
+         * in push mode, this should not happen... */
+        if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
+          goto deactivate_failed;
+        /* everything is fine now */
+        goto was_ok;
+      case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
+        break;
+    }
   }
 
   if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
     if ((peer = gst_pad_get_peer (pad))) {
-      if (!gst_pad_activate_pull (peer, active))
+      GST_DEBUG_OBJECT (pad, "calling peer");
+      if (G_UNLIKELY (!gst_pad_activate_pull (peer, active)))
         goto peer_failed;
       gst_object_unref (peer);
+    } else {
+      goto not_linked;
     }
   } else {
-    if (GST_PAD_GETRANGEFUNC (pad) == NULL)
-      goto failure;             /* Can't activate pull on a src without a 
+    if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
+      goto failure;             /* Can't activate pull on a src without a
                                    getrange function */
   }
 
@@ -716,7 +773,7 @@ gst_pad_activate_pull (GstPad * pad, gboolean active)
   pre_activate (pad, new);
 
   if (GST_PAD_ACTIVATEPULLFUNC (pad)) {
-    if (!GST_PAD_ACTIVATEPULLFUNC (pad) (pad, active))
+    if (G_UNLIKELY (!GST_PAD_ACTIVATEPULLFUNC (pad) (pad, active)))
       goto failure;
   } else {
     /* can happen for sinks of passthrough elements */
@@ -726,6 +783,7 @@ gst_pad_activate_pull (GstPad * pad, gboolean active)
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in pull mode",
       active ? "activated" : "deactivated");
+
   return TRUE;
 
 was_ok:
@@ -734,6 +792,13 @@ was_ok:
         active ? "activated" : "deactivated");
     return TRUE;
   }
+deactivate_failed:
+  {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+        "failed to %s in switch to pull from mode %d",
+        (active ? "activate" : "deactivate"), old);
+    return FALSE;
+  }
 peer_failed:
   {
     GST_OBJECT_LOCK (peer);
@@ -743,12 +808,20 @@ peer_failed:
     gst_object_unref (peer);
     return FALSE;
   }
+not_linked:
+  {
+    GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "can't activate unlinked sink "
+        "pad in pull mode");
+    return FALSE;
+  }
 failure:
   {
+    GST_OBJECT_LOCK (pad);
     GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in pull mode",
         active ? "activate" : "deactivate");
-    pre_activate (pad, GST_ACTIVATE_NONE);
-    post_activate (pad, GST_ACTIVATE_NONE);
+    GST_PAD_SET_FLUSHING (pad);
+    GST_PAD_ACTIVATE_MODE (pad) = old;
+    GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
 }
@@ -756,14 +829,14 @@ failure:
 /**
  * gst_pad_activate_push:
  * @pad: the #GstPad to activate or deactivate.
- * @active: whether or not the pad should be active.
+ * @active: whether the pad should be active or not.
  *
  * Activates or deactivates the given pad in push mode via dispatching to the
  * pad's activatepushfunc. For use from within pad activation functions only.
  *
  * If you don't know what this is, you probably don't want to call it.
  *
- * Returns: TRUE if the operation was successfull.
+ * Returns: %TRUE if the operation was successful.
  *
  * MT safe.
  */
@@ -780,21 +853,47 @@ gst_pad_activate_push (GstPad * pad, gboolean active)
   old = GST_PAD_ACTIVATE_MODE (pad);
   GST_OBJECT_UNLOCK (pad);
 
-  if ((active && old == GST_ACTIVATE_PUSH)
-      || (!active && old == GST_ACTIVATE_NONE))
-    goto was_ok;
-
   if (active) {
-    g_return_val_if_fail (old == GST_ACTIVATE_NONE, FALSE);
+    switch (old) {
+      case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad, "activating pad from push, was ok");
+        goto was_ok;
+      case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad,
+            "activating pad from push, deactivating pull first");
+        /* pad was activate in the wrong direction, deactivate it
+         * an reactivate it in push mode */
+        if (G_UNLIKELY (!gst_pad_activate_pull (pad, FALSE)))
+          goto deactivate_failed;
+        /* fallthrough, pad is deactivated now. */
+      case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "activating pad from none");
+        break;
+    }
   } else {
-    g_return_val_if_fail (old == GST_ACTIVATE_PUSH, FALSE);
+    switch (old) {
+      case GST_ACTIVATE_NONE:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from none, was ok");
+        goto was_ok;
+      case GST_ACTIVATE_PULL:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from pull, weird");
+        /* pad was activated in the other direction, deactivate it
+         * in pull mode, this should not happen... */
+        if (G_UNLIKELY (!gst_pad_activate_pull (pad, FALSE)))
+          goto deactivate_failed;
+        /* everything is fine now */
+        goto was_ok;
+      case GST_ACTIVATE_PUSH:
+        GST_DEBUG_OBJECT (pad, "deactivating pad from push");
+        break;
+    }
   }
 
   new = active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE;
   pre_activate (pad, new);
 
   if (GST_PAD_ACTIVATEPUSHFUNC (pad)) {
-    if (!GST_PAD_ACTIVATEPUSHFUNC (pad) (pad, active)) {
+    if (G_UNLIKELY (!GST_PAD_ACTIVATEPUSHFUNC (pad) (pad, active))) {
       goto failure;
     }
   } else {
@@ -813,12 +912,21 @@ was_ok:
         active ? "activated" : "deactivated");
     return TRUE;
   }
+deactivate_failed:
+  {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+        "failed to %s in switch to push from mode %d",
+        (active ? "activate" : "deactivate"), old);
+    return FALSE;
+  }
 failure:
   {
+    GST_OBJECT_LOCK (pad);
     GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in push mode",
         active ? "activate" : "deactivate");
-    pre_activate (pad, GST_ACTIVATE_NONE);
-    post_activate (pad, GST_ACTIVATE_NONE);
+    GST_PAD_SET_FLUSHING (pad);
+    GST_PAD_ACTIVATE_MODE (pad) = old;
+    GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
 }
@@ -866,9 +974,8 @@ gst_pad_is_active (GstPad * pad)
  * You can pass NULL as the callback to make this call block. Be careful with
  * this blocking call as it might not return for reasons stated above.
  *
- * Returns: TRUE if the pad could be blocked. This function can fail
- *   if wrong parameters were passed or the pad was already in the
- *   requested state.
+ * Returns: TRUE if the pad could be blocked. This function can fail if the
+ * wrong parameters were passed or the pad was already in the requested state.
  *
  * MT safe.
  */
@@ -876,7 +983,7 @@ gboolean
 gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
     GstPadBlockCallback callback, gpointer user_data)
 {
-  gboolean was_blocked;
+  gboolean was_blocked = FALSE;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
@@ -888,8 +995,7 @@ gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
     goto had_right_state;
 
   if (blocked) {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocking pad %s:%s",
-        GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocking pad");
 
     GST_OBJECT_FLAG_SET (pad, GST_PAD_BLOCKED);
     pad->block_callback = callback;
@@ -900,18 +1006,16 @@ gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
       GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocked");
     }
   } else {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocking pad %s:%s",
-        GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocking pad");
 
     GST_OBJECT_FLAG_UNSET (pad, GST_PAD_BLOCKED);
 
     pad->block_callback = callback;
     pad->block_data = user_data;
 
-    if (callback) {
-      GST_PAD_BLOCK_SIGNAL (pad);
-    } else {
-      GST_PAD_BLOCK_SIGNAL (pad);
+    GST_PAD_BLOCK_BROADCAST (pad);
+    if (!callback) {
+      /* no callback, wait for the unblock to happen */
       GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "waiting for unblock");
       GST_PAD_BLOCK_WAIT (pad);
       GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocked");
@@ -924,8 +1028,9 @@ gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
 had_right_state:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pad %s:%s was in right state", GST_DEBUG_PAD_NAME (pad));
+        "pad was in right state (%d)", was_blocked);
     GST_OBJECT_UNLOCK (pad);
+
     return FALSE;
   }
 }
@@ -939,9 +1044,8 @@ had_right_state:
  * a shortcut for gst_pad_set_blocked_async() with a NULL
  * callback.
  *
- * Returns: TRUE if the pad could be blocked. This function can fail
- *   wrong parameters were passed or the pad was already in the
- *   requested state.
+ * Returns: TRUE if the pad could be blocked. This function can fail if the
+ * wrong parameters were passed or the pad was already in the requested state.
  *
  * MT safe.
  */
@@ -957,7 +1061,7 @@ gst_pad_set_blocked (GstPad * pad, gboolean blocked)
  *
  * Checks if the pad is blocked or not. This function returns the
  * last requested state of the pad. It is not certain that the pad
- * is actually blocked at this point.
+ * is actually blocking at this point (see gst_pad_is_blocking()).
  *
  * Returns: TRUE if the pad is blocked.
  *
@@ -978,13 +1082,42 @@ gst_pad_is_blocked (GstPad * pad)
 }
 
 /**
+ * gst_pad_is_blocking:
+ * @pad: the #GstPad to query
+ *
+ * Checks if the pad is blocking or not. This is a guaranteed state
+ * of whether the pad is actually blocking on a #GstBuffer or a #GstEvent.
+ *
+ * Returns: TRUE if the pad is blocking.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.11
+ */
+gboolean
+gst_pad_is_blocking (GstPad * pad)
+{
+  gboolean result = FALSE;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), result);
+
+  GST_OBJECT_LOCK (pad);
+  /* the blocking flag is only valid if the pad is not flushing */
+  result = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKING) &&
+      !GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLUSHING);
+  GST_OBJECT_UNLOCK (pad);
+
+  return result;
+}
+
+/**
  * gst_pad_set_activate_function:
- * @pad: a sink #GstPad.
+ * @pad: a #GstPad.
  * @activate: the #GstPadActivateFunction to set.
  *
- * Sets the given activate function for the pad. The activate function will
- * dispatch to activate_push or activate_pull to perform the actual activation.
- * Only makes sense to set on sink pads.
+ * Sets the given activate function for @pad. The activate function will
+ * dispatch to gst_pad_activate_push() or gst_pad_activate_pull() to perform
+ * the actual activation. Only makes sense to set on sink pads.
  *
  * Call this function if your sink pad can start a pull-based task.
  */
@@ -994,13 +1127,13 @@ gst_pad_set_activate_function (GstPad * pad, GstPadActivateFunction activate)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_ACTIVATEFUNC (pad) = activate;
-  GST_CAT_DEBUG (GST_CAT_PADS, "activatefunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (activate));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatefunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (activate));
 }
 
 /**
  * gst_pad_set_activatepull_function:
- * @pad: a sink #GstPad.
+ * @pad: a #GstPad.
  * @activatepull: the #GstPadActivateModeFunction to set.
  *
  * Sets the given activate_pull function for the pad. An activate_pull function
@@ -1014,13 +1147,13 @@ gst_pad_set_activatepull_function (GstPad * pad,
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_ACTIVATEPULLFUNC (pad) = activatepull;
-  GST_CAT_DEBUG (GST_CAT_PADS, "activatepullfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (activatepull));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepullfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (activatepull));
 }
 
 /**
  * gst_pad_set_activatepush_function:
- * @pad: a sink #GstPad.
+ * @pad: a #GstPad.
  * @activatepush: the #GstPadActivateModeFunction to set.
  *
  * Sets the given activate_push function for the pad. An activate_push function
@@ -1033,8 +1166,8 @@ gst_pad_set_activatepush_function (GstPad * pad,
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_ACTIVATEPUSHFUNC (pad) = activatepush;
-  GST_CAT_DEBUG (GST_CAT_PADS, "activatepushfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (activatepush));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepushfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (activatepush));
 }
 
 /**
@@ -1043,7 +1176,7 @@ gst_pad_set_activatepush_function (GstPad * pad,
  * @chain: the #GstPadChainFunction to set.
  *
  * Sets the given chain function for the pad. The chain function is called to
- * process a #GstBuffer input buffer.
+ * process a #GstBuffer input buffer. see #GstPadChainFunction for more details.
  */
 void
 gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
@@ -1052,8 +1185,8 @@ gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
   g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK);
 
   GST_PAD_CHAINFUNC (pad) = chain;
-  GST_CAT_DEBUG (GST_CAT_PADS, "chainfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (chain));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (chain));
 }
 
 /**
@@ -1061,9 +1194,9 @@ gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
  * @pad: a source #GstPad.
  * @get: the #GstPadGetRangeFunction to set.
  *
- * Sets the given getrange function for the pad. The getrange function is called to
- * produce a new #GstBuffer to start the processing pipeline. Getrange functions cannot
- * return %NULL.
+ * Sets the given getrange function for the pad. The getrange function is
+ * called to produce a new #GstBuffer to start the processing pipeline. see
+ * #GstPadGetRangeFunction for a description of the getrange function.
  */
 void
 gst_pad_set_getrange_function (GstPad * pad, GstPadGetRangeFunction get)
@@ -1073,8 +1206,8 @@ gst_pad_set_getrange_function (GstPad * pad, GstPadGetRangeFunction get)
 
   GST_PAD_GETRANGEFUNC (pad) = get;
 
-  GST_CAT_DEBUG (GST_CAT_PADS, "getrangefunc for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (get));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getrangefunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (get));
 }
 
 /**
@@ -1082,8 +1215,8 @@ gst_pad_set_getrange_function (GstPad * pad, GstPadGetRangeFunction get)
  * @pad: a source #GstPad.
  * @check: the #GstPadCheckGetRangeFunction to set.
  *
- * Sets the given checkgetrange function for the pad. Implement this function on
- * a pad if you dynamically support getrange based scheduling on the pad.
+ * Sets the given checkgetrange function for the pad. Implement this function
+ * on a pad if you dynamically support getrange based scheduling on the pad.
  */
 void
 gst_pad_set_checkgetrange_function (GstPad * pad,
@@ -1094,13 +1227,13 @@ gst_pad_set_checkgetrange_function (GstPad * pad,
 
   GST_PAD_CHECKGETRANGEFUNC (pad) = check;
 
-  GST_CAT_DEBUG (GST_CAT_PADS, "checkgetrangefunc for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (check));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "checkgetrangefunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (check));
 }
 
 /**
  * gst_pad_set_event_function:
- * @pad: a source #GstPad.
+ * @pad: a #GstPad of either direction.
  * @event: the #GstPadEventFunction to set.
  *
  * Sets the given event handler for the pad.
@@ -1112,8 +1245,8 @@ gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event)
 
   GST_PAD_EVENTFUNC (pad) = event;
 
-  GST_CAT_DEBUG (GST_CAT_PADS, "eventfunc for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (event));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "eventfunc for set to %s",
+      GST_DEBUG_FUNCPTR_NAME (event));
 }
 
 /**
@@ -1130,8 +1263,8 @@ gst_pad_set_query_function (GstPad * pad, GstPadQueryFunction query)
 
   GST_PAD_QUERYFUNC (pad) = query;
 
-  GST_CAT_DEBUG (GST_CAT_PADS, "queryfunc for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (query));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "queryfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (query));
 }
 
 /**
@@ -1149,8 +1282,8 @@ gst_pad_set_query_type_function (GstPad * pad,
 
   GST_PAD_QUERYTYPEFUNC (pad) = type_func;
 
-  GST_CAT_DEBUG (GST_CAT_PADS, "querytypefunc for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (type_func));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "querytypefunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (type_func));
 }
 
 /**
@@ -1224,8 +1357,8 @@ gst_pad_set_internal_link_function (GstPad * pad, GstPadIntLinkFunction intlink)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_INTLINKFUNC (pad) = intlink;
-  GST_CAT_DEBUG (GST_CAT_PADS, "internal link for %s:%s  set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (intlink));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link set to %s",
+      GST_DEBUG_FUNCPTR_NAME (intlink));
 }
 
 /**
@@ -1251,8 +1384,8 @@ gst_pad_set_link_function (GstPad * pad, GstPadLinkFunction link)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_LINKFUNC (pad) = link;
-  GST_CAT_DEBUG (GST_CAT_PADS, "linkfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (link));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "linkfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (link));
 }
 
 /**
@@ -1269,8 +1402,8 @@ gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_UNLINKFUNC (pad) = unlink;
-  GST_CAT_DEBUG (GST_CAT_PADS, "unlinkfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (unlink));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "unlinkfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (unlink));
 }
 
 /**
@@ -1296,8 +1429,8 @@ gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink)
  * @getcaps should return the most specific caps it reasonably can, since this
  * helps with autoplugging.
  *
- * Note that the return value from @getcaps is owned by the caller, so the caller
- * should unref the caps after usage.
+ * Note that the return value from @getcaps is owned by the caller, so the
+ * caller should unref the caps after usage.
  */
 void
 gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps)
@@ -1305,8 +1438,8 @@ gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_GETCAPSFUNC (pad) = getcaps;
-  GST_CAT_DEBUG (GST_CAT_PADS, "getcapsfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (getcaps));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getcapsfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (getcaps));
 }
 
 /**
@@ -1315,7 +1448,9 @@ gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps)
  * @acceptcaps: the #GstPadAcceptCapsFunction to set.
  *
  * Sets the given acceptcaps function for the pad.  The acceptcaps function
- * will be called to check if the pad can accept the given caps.
+ * will be called to check if the pad can accept the given caps. Setting the
+ * acceptcaps function to NULL restores the default behaviour of allowing
+ * any caps that matches the caps from gst_pad_get_caps.
  */
 void
 gst_pad_set_acceptcaps_function (GstPad * pad,
@@ -1324,8 +1459,8 @@ gst_pad_set_acceptcaps_function (GstPad * pad,
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_ACCEPTCAPSFUNC (pad) = acceptcaps;
-  GST_CAT_DEBUG (GST_CAT_PADS, "acceptcapsfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (acceptcaps));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "acceptcapsfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (acceptcaps));
 }
 
 /**
@@ -1344,8 +1479,8 @@ gst_pad_set_fixatecaps_function (GstPad * pad,
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_FIXATECAPSFUNC (pad) = fixatecaps;
-  GST_CAT_DEBUG (GST_CAT_PADS, "fixatecapsfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (fixatecaps));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "fixatecapsfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (fixatecaps));
 }
 
 /**
@@ -1355,7 +1490,7 @@ gst_pad_set_fixatecaps_function (GstPad * pad,
  *
  * Sets the given setcaps function for the pad.  The setcaps function
  * will be called whenever a buffer with a new media type is pushed or
- * pulled from the pad. The pad/element needs to update it's internal
+ * pulled from the pad. The pad/element needs to update its internal
  * structures to process the new media type. If this new type is not
  * acceptable, the setcaps function should return FALSE.
  */
@@ -1365,8 +1500,8 @@ gst_pad_set_setcaps_function (GstPad * pad, GstPadSetCapsFunction setcaps)
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_PAD_SETCAPSFUNC (pad) = setcaps;
-  GST_CAT_DEBUG (GST_CAT_PADS, "setcapsfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (setcaps));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "setcapsfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (setcaps));
 }
 
 /**
@@ -1385,8 +1520,8 @@ gst_pad_set_bufferalloc_function (GstPad * pad,
   g_return_if_fail (GST_PAD_IS_SINK (pad));
 
   GST_PAD_BUFFERALLOCFUNC (pad) = bufalloc;
-  GST_CAT_DEBUG (GST_CAT_PADS, "bufferallocfunc for %s:%s set to %s",
-      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (bufalloc));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "bufferallocfunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (bufalloc));
 }
 
 /**
@@ -1394,8 +1529,8 @@ gst_pad_set_bufferalloc_function (GstPad * pad,
  * @srcpad: the source #GstPad to unlink.
  * @sinkpad: the sink #GstPad to unlink.
  *
- * Unlinks the source pad from the sink pad. Will emit the "unlinked" signal on
- * both pads.
+ * Unlinks the source pad from the sink pad. Will emit the #GstPad::unlinked
+ * signal on both pads.
  *
  * Returns: TRUE if the pads were unlinked. This function returns FALSE if
  * the pads were not linked together.
@@ -1556,48 +1691,73 @@ was_empty:
  * This check is required so that we don't try to link
  * pads from elements in different bins without ghostpads.
  *
- * The LOCK should be helt on both pads
+ * The LOCK should be held on both pads
  */
 static gboolean
 gst_pad_link_check_hierarchy (GstPad * src, GstPad * sink)
 {
   GstObject *psrc, *psink;
-  gboolean res = TRUE;
 
   psrc = GST_OBJECT_PARENT (src);
   psink = GST_OBJECT_PARENT (sink);
 
   /* if one of the pads has no parent, we allow the link */
-  if (psrc && psink) {
-    /* if the parents are the same, we have a loop */
-    if (psrc == psink) {
-      GST_CAT_DEBUG (GST_CAT_CAPS, "pads have same parent %" GST_PTR_FORMAT,
-          psrc);
-      res = FALSE;
-      goto done;
-    }
-    /* if they both have a parent, we check the grandparents */
-    psrc = gst_object_get_parent (psrc);
-    psink = gst_object_get_parent (psink);
-
-    if (psrc != psink) {
-      /* if they have grandparents but they are not the same */
-      GST_CAT_DEBUG (GST_CAT_CAPS,
-          "pads have different grandparents %" GST_PTR_FORMAT " and %"
-          GST_PTR_FORMAT, psrc, psink);
-      res = FALSE;
-    }
-    if (psrc)
-      gst_object_unref (psrc);
-    if (psink)
-      gst_object_unref (psink);
+  if (G_UNLIKELY (psrc == NULL || psink == NULL))
+    goto no_parent;
+
+  /* only care about parents that are elements */
+  if (G_UNLIKELY (!GST_IS_ELEMENT (psrc) || !GST_IS_ELEMENT (psink)))
+    goto no_element_parent;
+
+  /* if the parents are the same, we have a loop */
+  if (G_UNLIKELY (psrc == psink))
+    goto same_parents;
+
+  /* if they both have a parent, we check the grandparents. We can not lock
+   * the parent because we hold on the child (pad) and the locking order is
+   * parent >> child. */
+  psrc = GST_OBJECT_PARENT (psrc);
+  psink = GST_OBJECT_PARENT (psink);
+
+  /* if they have grandparents but they are not the same */
+  if (G_UNLIKELY (psrc != psink))
+    goto wrong_grandparents;
+
+  return TRUE;
+
+  /* ERRORS */
+no_parent:
+  {
+    GST_CAT_DEBUG (GST_CAT_CAPS,
+        "one of the pads has no parent %" GST_PTR_FORMAT " and %"
+        GST_PTR_FORMAT, psrc, psink);
+    return TRUE;
+  }
+no_element_parent:
+  {
+    GST_CAT_DEBUG (GST_CAT_CAPS,
+        "one of the pads has no element parent %" GST_PTR_FORMAT " and %"
+        GST_PTR_FORMAT, psrc, psink);
+    return TRUE;
+  }
+same_parents:
+  {
+    GST_CAT_DEBUG (GST_CAT_CAPS, "pads have same parent %" GST_PTR_FORMAT,
+        psrc);
+    return FALSE;
+  }
+wrong_grandparents:
+  {
+    GST_CAT_DEBUG (GST_CAT_CAPS,
+        "pads have different grandparents %" GST_PTR_FORMAT " and %"
+        GST_PTR_FORMAT, psrc, psink);
+    return FALSE;
   }
-done:
-  return res;
 }
 
 /* FIXME leftover from an attempt at refactoring... */
-/* call with the two pads unlocked */
+/* call with the two pads unlocked, when this function returns GST_PAD_LINK_OK,
+ * the two pads will be locked in the srcpad, sinkpad order. */
 static GstPadLinkReturn
 gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad)
 {
@@ -1645,8 +1805,9 @@ not_srcpad:
   }
 src_was_linked:
   {
-    GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked",
-        GST_DEBUG_PAD_NAME (srcpad));
+    GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked to %s:%s",
+        GST_DEBUG_PAD_NAME (srcpad),
+        GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad)));
     /* we do not emit a warning in this case because unlinking cannot
      * be made MT safe.*/
     GST_OBJECT_UNLOCK (srcpad);
@@ -1661,8 +1822,9 @@ not_sinkpad:
   }
 sink_was_linked:
   {
-    GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked",
-        GST_DEBUG_PAD_NAME (sinkpad));
+    GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked to %s:%s",
+        GST_DEBUG_PAD_NAME (sinkpad),
+        GST_DEBUG_PAD_NAME (GST_PAD_PEER (sinkpad)));
     /* we do not emit a warning in this case because unlinking cannot
      * be made MT safe.*/
     GST_OBJECT_UNLOCK (sinkpad);
@@ -1708,6 +1870,10 @@ gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
   if (result != GST_PAD_LINK_OK)
     goto prepare_failed;
 
+  /* must set peers before calling the link function */
+  GST_PAD_PEER (srcpad) = sinkpad;
+  GST_PAD_PEER (sinkpad) = srcpad;
+
   GST_OBJECT_UNLOCK (sinkpad);
   GST_OBJECT_UNLOCK (srcpad);
 
@@ -1728,9 +1894,6 @@ gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
   GST_OBJECT_LOCK (sinkpad);
 
   if (result == GST_PAD_LINK_OK) {
-    GST_PAD_PEER (srcpad) = sinkpad;
-    GST_PAD_PEER (sinkpad) = srcpad;
-
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
 
@@ -1745,6 +1908,9 @@ gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
     GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed",
         GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
 
+    GST_PAD_PEER (srcpad) = NULL;
+    GST_PAD_PEER (sinkpad) = NULL;
+
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
   }
@@ -1759,10 +1925,13 @@ prepare_failed:
 static void
 gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ)
 {
+  GstPadTemplate **template_p;
+
   /* this function would need checks if it weren't static */
 
   GST_OBJECT_LOCK (pad);
-  gst_object_replace ((GstObject **) & pad->padtemplate, (GstObject *) templ);
+  template_p = &pad->padtemplate;
+  gst_object_replace ((GstObject **) template_p, (GstObject *) templ);
   GST_OBJECT_UNLOCK (pad);
 
   if (templ)
@@ -1795,12 +1964,13 @@ static GstCaps *
 gst_pad_get_caps_unlocked (GstPad * pad)
 {
   GstCaps *result = NULL;
+  GstPadTemplate *templ;
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)",
-      GST_DEBUG_PAD_NAME (pad), pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
 
   if (GST_PAD_GETCAPSFUNC (pad)) {
-    GST_CAT_DEBUG (GST_CAT_CAPS, "dispatching to pad getcaps function");
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "dispatching to pad getcaps function");
 
     GST_OBJECT_FLAG_SET (pad, GST_PAD_IN_GETCAPS);
     GST_OBJECT_UNLOCK (pad);
@@ -1812,9 +1982,8 @@ gst_pad_get_caps_unlocked (GstPad * pad)
       g_critical ("pad %s:%s returned NULL caps from getcaps function",
           GST_DEBUG_PAD_NAME (pad));
     } else {
-      GST_CAT_DEBUG (GST_CAT_CAPS,
-          "pad getcaps %s:%s returned %" GST_PTR_FORMAT,
-          GST_DEBUG_PAD_NAME (pad), result);
+      GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+          "pad getcaps returned %" GST_PTR_FORMAT, result);
 #ifndef G_DISABLE_ASSERT
       /* check that the returned caps are a real subset of the template caps */
       if (GST_PAD_PAD_TEMPLATE (pad)) {
@@ -1828,8 +1997,8 @@ gst_pad_get_caps_unlocked (GstPad * pad)
               " which are not a real subset of its template caps %"
               GST_PTR_FORMAT, result, templ_caps);
           g_warning
-              ("pad %s:%s returned caps that are not a real subset of its template caps",
-              GST_DEBUG_PAD_NAME (pad));
+              ("pad %s:%s returned caps which are not a real "
+              "subset of its template caps", GST_DEBUG_PAD_NAME (pad));
           temp = gst_caps_intersect (templ_caps, result);
           gst_caps_unref (result);
           result = temp;
@@ -1839,28 +2008,24 @@ gst_pad_get_caps_unlocked (GstPad * pad)
       goto done;
     }
   }
-  if (GST_PAD_PAD_TEMPLATE (pad)) {
-    GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
-
+  if ((templ = GST_PAD_PAD_TEMPLATE (pad))) {
     result = GST_PAD_TEMPLATE_CAPS (templ);
-    GST_CAT_DEBUG (GST_CAT_CAPS,
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
         "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, result,
         result);
 
     result = gst_caps_ref (result);
     goto done;
   }
-  if (GST_PAD_CAPS (pad)) {
-    result = GST_PAD_CAPS (pad);
-
-    GST_CAT_DEBUG (GST_CAT_CAPS,
+  if ((result = GST_PAD_CAPS (pad))) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
         "using pad caps %p %" GST_PTR_FORMAT, result, result);
 
     result = gst_caps_ref (result);
     goto done;
   }
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
   result = gst_caps_new_empty ();
 
 done:
@@ -1872,8 +2037,8 @@ done:
  * @pad: a  #GstPad to get the capabilities of.
  *
  * Gets the capabilities this pad can produce or consume.
- * Note that this method doesn't necessarily returns the caps set by
- * #gst_pad_set_caps - use #GST_PAD_CAPS for that instead.
+ * Note that this method doesn't necessarily return the caps set by
+ * gst_pad_set_caps() - use GST_PAD_CAPS() for that instead.
  * gst_pad_get_caps returns all possible caps a pad can operate with, using
  * the pad's get_caps function;
  * this returns the pad template caps if not explicitly set.
@@ -1891,10 +2056,14 @@ gst_pad_get_caps (GstPad * pad)
 
   GST_OBJECT_LOCK (pad);
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)",
-      GST_DEBUG_PAD_NAME (pad), pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
 
   result = gst_pad_get_caps_unlocked (pad);
+
+  /* be sure that we have a copy */
+  if (result)
+    result = gst_caps_make_writable (result);
+
   GST_OBJECT_UNLOCK (pad);
 
   return result;
@@ -1906,9 +2075,9 @@ gst_pad_get_caps (GstPad * pad)
  *
  * Gets the capabilities of the peer connected to this pad.
  *
- * Returns: the #GstCaps of the peer pad. This function returns a new caps, so use
- * gst_caps_unref to get rid of it. this function returns NULL if there is no
- * peer pad.
+ * Returns: the #GstCaps of the peer pad. This function returns a new caps, so
+ * use gst_caps_unref to get rid of it. this function returns NULL if there is
+ * no peer pad.
  */
 GstCaps *
 gst_pad_peer_get_caps (GstPad * pad)
@@ -1920,8 +2089,7 @@ gst_pad_peer_get_caps (GstPad * pad)
 
   GST_OBJECT_LOCK (pad);
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "get peer caps of %s:%s (%p)",
-      GST_DEBUG_PAD_NAME (pad), pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get peer caps");
 
   peerpad = GST_PAD_PEER (pad);
   if (G_UNLIKELY (peerpad == NULL))
@@ -1952,10 +2120,17 @@ fixate_value (GValue * dest, const GValue * src)
   } else if (G_VALUE_TYPE (src) == GST_TYPE_DOUBLE_RANGE) {
     g_value_init (dest, G_TYPE_DOUBLE);
     g_value_set_double (dest, gst_value_get_double_range_min (src));
+  } else if (G_VALUE_TYPE (src) == GST_TYPE_FRACTION_RANGE) {
+    gst_value_init_and_copy (dest, gst_value_get_fraction_range_min (src));
   } else if (G_VALUE_TYPE (src) == GST_TYPE_LIST) {
     GValue temp = { 0 };
 
+    /* list could be empty */
+    if (gst_value_list_get_size (src) <= 0)
+      return FALSE;
+
     gst_value_init_and_copy (&temp, gst_value_list_get_value (src, 0));
+
     if (!fixate_value (dest, &temp))
       gst_value_init_and_copy (dest, &temp);
     g_value_unset (&temp);
@@ -2034,6 +2209,46 @@ gst_pad_fixate_caps (GstPad * pad, GstCaps * caps)
   }
 }
 
+/* Default accept caps implementation just checks against
+ * against the allowed caps for the pad */
+static gboolean
+gst_pad_acceptcaps_default (GstPad * pad, GstCaps * caps)
+{
+  /* get the caps and see if it intersects to something
+   * not empty */
+  GstCaps *intersect;
+  GstCaps *allowed;
+  gboolean result = FALSE;
+
+  GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
+
+  allowed = gst_pad_get_caps (pad);
+  if (!allowed)
+    goto nothing_allowed;
+
+  GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed);
+
+  intersect = gst_caps_intersect (allowed, caps);
+
+  GST_DEBUG_OBJECT (pad, "intersection %" GST_PTR_FORMAT, intersect);
+
+  result = !gst_caps_is_empty (intersect);
+  if (!result)
+    GST_DEBUG_OBJECT (pad, "intersection gave empty caps");
+
+  gst_caps_unref (allowed);
+  gst_caps_unref (intersect);
+
+  return result;
+
+  /* ERRORS */
+nothing_allowed:
+  {
+    GST_DEBUG_OBJECT (pad, "no caps allowed on the pad");
+    return FALSE;
+  }
+}
+
 /**
  * gst_pad_accept_caps:
  * @pad: a #GstPad to check
@@ -2048,6 +2263,7 @@ gst_pad_accept_caps (GstPad * pad, GstCaps * caps)
 {
   gboolean result;
   GstPadAcceptCapsFunction acceptfunc;
+  GstCaps *existing = NULL;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
@@ -2055,45 +2271,45 @@ gst_pad_accept_caps (GstPad * pad, GstCaps * caps)
   if (caps == NULL)
     return TRUE;
 
+  /* lock for checking the existing caps */
   GST_OBJECT_LOCK (pad);
   acceptfunc = GST_PAD_ACCEPTCAPSFUNC (pad);
-
-  GST_CAT_DEBUG (GST_CAT_CAPS, "pad accept caps of %s:%s (%p)",
-      GST_DEBUG_PAD_NAME (pad), pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "accept caps of %p", caps);
+  /* The current caps on a pad are trivially acceptable */
+  if (G_LIKELY ((existing = GST_PAD_CAPS (pad)))) {
+    if (caps == existing || gst_caps_is_equal (caps, existing))
+      goto is_same_caps;
+  }
   GST_OBJECT_UNLOCK (pad);
 
-  if (acceptfunc) {
+  if (G_LIKELY (acceptfunc)) {
     /* we can call the function */
     result = acceptfunc (pad, caps);
+    GST_DEBUG_OBJECT (pad, "acceptfunc returned %d", result);
   } else {
-    /* else see get the caps and see if it intersects to something
-     * not empty */
-    GstCaps *intersect;
-    GstCaps *allowed;
-
-    allowed = gst_pad_get_caps (pad);
-    if (allowed) {
-      intersect = gst_caps_intersect (allowed, caps);
-
-      result = !gst_caps_is_empty (intersect);
-
-      gst_caps_unref (allowed);
-      gst_caps_unref (intersect);
-    } else {
-      result = FALSE;
-    }
+    /* Only null if the element explicitly unset it */
+    result = gst_pad_acceptcaps_default (pad, caps);
+    GST_DEBUG_OBJECT (pad, "default acceptcaps returned %d", result);
   }
   return result;
+
+is_same_caps:
+  {
+    GST_DEBUG_OBJECT (pad, "pad had same caps");
+    GST_OBJECT_UNLOCK (pad);
+    return TRUE;
+  }
 }
 
 /**
  * gst_pad_peer_accept_caps:
- * @pad: a  #GstPad to check
+ * @pad: a  #GstPad to check the peer of
  * @caps: a #GstCaps to check on the pad
  *
- * Check if the given pad accepts the caps.
+ * Check if the peer of @pad accepts @caps. If @pad has no peer, this function
+ * returns TRUE.
  *
- * Returns: TRUE if the pad can accept the caps.
+ * Returns: TRUE if the peer of @pad can accept the caps or @pad has no peer.
  */
 gboolean
 gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps)
@@ -2105,16 +2321,20 @@ gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps)
 
   GST_OBJECT_LOCK (pad);
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "peer accept caps of %s:%s (%p)",
-      GST_DEBUG_PAD_NAME (pad), pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "peer accept caps of (%p)", pad);
 
   peerpad = GST_PAD_PEER (pad);
   if (G_UNLIKELY (peerpad == NULL))
     goto no_peer;
 
-  result = gst_pad_accept_caps (peerpad, caps);
+  gst_object_ref (peerpad);
+  /* release lock before calling external methods but keep ref to pad */
   GST_OBJECT_UNLOCK (pad);
 
+  result = gst_pad_accept_caps (peerpad, caps);
+
+  gst_object_unref (peerpad);
+
   return result;
 
 no_peer:
@@ -2150,12 +2370,15 @@ gst_pad_set_caps (GstPad * pad, GstCaps * caps)
   g_return_val_if_fail (caps == NULL || gst_caps_is_fixed (caps), FALSE);
 
   GST_OBJECT_LOCK (pad);
-  setcaps = GST_PAD_SETCAPSFUNC (pad);
-
   existing = GST_PAD_CAPS (pad);
+  if (existing == caps)
+    goto was_ok;
+
   if (gst_caps_is_equal (caps, existing))
     goto setting_same_caps;
 
+  setcaps = GST_PAD_SETCAPSFUNC (pad);
+
   /* call setcaps function to configure the pad only if the
    * caps is not NULL */
   if (setcaps != NULL && caps) {
@@ -2167,37 +2390,39 @@ gst_pad_set_caps (GstPad * pad, GstCaps * caps)
       GST_OBJECT_LOCK (pad);
       GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_SETCAPS);
     } else {
-      GST_CAT_DEBUG (GST_CAT_CAPS, "pad %s:%s was dispatching",
-          GST_DEBUG_PAD_NAME (pad));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad was dispatching");
     }
   }
 
   gst_caps_replace (&GST_PAD_CAPS (pad), caps);
-  GST_CAT_DEBUG (GST_CAT_CAPS, "%s:%s caps %" GST_PTR_FORMAT,
-      GST_DEBUG_PAD_NAME (pad), caps);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "caps %" GST_PTR_FORMAT, caps);
   GST_OBJECT_UNLOCK (pad);
 
   g_object_notify (G_OBJECT (pad), "caps");
 
   return TRUE;
 
+was_ok:
+  {
+    GST_OBJECT_UNLOCK (pad);
+    return TRUE;
+  }
 setting_same_caps:
   {
     gst_caps_replace (&GST_PAD_CAPS (pad), caps);
     GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
         "caps %" GST_PTR_FORMAT " same as existing, updating ptr only", caps);
     GST_OBJECT_UNLOCK (pad);
-
     return TRUE;
   }
-/* errors */
+
+  /* ERRORS */
 could_not_set:
   {
     GST_OBJECT_LOCK (pad);
     GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_SETCAPS);
-    GST_CAT_DEBUG (GST_CAT_CAPS,
-        "pad %s:%s, caps %" GST_PTR_FORMAT " could not be set",
-        GST_DEBUG_PAD_NAME (pad), caps);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "caps %" GST_PTR_FORMAT " could not be set", caps);
     GST_OBJECT_UNLOCK (pad);
 
     return FALSE;
@@ -2207,19 +2432,12 @@ could_not_set:
 static gboolean
 gst_pad_configure_sink (GstPad * pad, GstCaps * caps)
 {
-  GstPadAcceptCapsFunction acceptcaps;
-  GstPadSetCapsFunction setcaps;
   gboolean res;
 
-  acceptcaps = GST_PAD_ACCEPTCAPSFUNC (pad);
-  setcaps = GST_PAD_SETCAPSFUNC (pad);
+  /* See if pad accepts the caps */
+  if (!gst_pad_accept_caps (pad, caps))
+    goto not_accepted;
 
-  /* See if pad accepts the caps, by calling acceptcaps, only
-   * needed if no setcaps function */
-  if (setcaps == NULL && acceptcaps != NULL) {
-    if (!acceptcaps (pad, caps))
-      goto not_accepted;
-  }
   /* set caps on pad if call succeeds */
   res = gst_pad_set_caps (pad, caps);
   /* no need to unref the caps here, set_caps takes a ref and
@@ -2229,7 +2447,8 @@ gst_pad_configure_sink (GstPad * pad, GstCaps * caps)
 
 not_accepted:
   {
-    GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "caps %" GST_PTR_FORMAT " not accepted", caps);
     return FALSE;
   }
 }
@@ -2238,19 +2457,12 @@ not_accepted:
 static gboolean
 gst_pad_configure_src (GstPad * pad, GstCaps * caps, gboolean dosetcaps)
 {
-  GstPadAcceptCapsFunction acceptcaps;
-  GstPadSetCapsFunction setcaps;
   gboolean res;
 
-  acceptcaps = GST_PAD_ACCEPTCAPSFUNC (pad);
-  setcaps = GST_PAD_SETCAPSFUNC (pad);
+  /* See if pad accepts the caps */
+  if (!gst_pad_accept_caps (pad, caps))
+    goto not_accepted;
 
-  /* See if pad accepts the caps, by calling acceptcaps, only
-   * needed if no setcaps function */
-  if (setcaps == NULL && acceptcaps != NULL) {
-    if (!acceptcaps (pad, caps))
-      goto not_accepted;
-  }
   if (dosetcaps)
     res = gst_pad_set_caps (pad, caps);
   else
@@ -2260,7 +2472,8 @@ gst_pad_configure_src (GstPad * pad, GstCaps * caps, gboolean dosetcaps)
 
 not_accepted:
   {
-    GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "caps %" GST_PTR_FORMAT " not accepted", caps);
     return FALSE;
   }
 }
@@ -2271,8 +2484,8 @@ not_accepted:
  *
  * Gets the capabilities for @pad's template.
  *
- * Returns: the #GstCaps of this pad template. If you intend to keep a reference
- * on the caps, make a copy (see gst_caps_copy ()).
+ * Returns: the #GstCaps of this pad template. If you intend to keep a
+ * reference on the caps, make a copy (see gst_caps_copy ()).
  */
 const GstCaps *
 gst_pad_get_pad_template_caps (GstPad * pad)
@@ -2287,7 +2500,6 @@ gst_pad_get_pad_template_caps (GstPad * pad)
   return gst_static_caps_get (&anycaps);
 }
 
-
 /**
  * gst_pad_get_peer:
  * @pad: a #GstPad to get the peer of.
@@ -2317,41 +2529,41 @@ gst_pad_get_peer (GstPad * pad)
 
 /**
  * gst_pad_get_allowed_caps:
- * @srcpad: a #GstPad, it must a a source pad.
+ * @pad: a #GstPad.
  *
  * Gets the capabilities of the allowed media types that can flow through
- * @srcpad and its peer. The pad must be a source pad.
- * The caller must free the resulting caps.
+ * @pad and its peer.
+ *
+ * The allowed capabilities is calculated as the intersection of the results of
+ * calling gst_pad_get_caps() on @pad and its peer. The caller owns a reference
+ * on the resulting caps.
  *
- * Returns: the allowed #GstCaps of the pad link.  Free the caps when
- * you no longer need it. This function returns NULL when the @srcpad has no
- * peer.
+ * Returns: the allowed #GstCaps of the pad link. Unref the caps when you no
+ * longer need it. This function returns NULL when @pad has no peer.
  *
  * MT safe.
  */
 GstCaps *
-gst_pad_get_allowed_caps (GstPad * srcpad)
+gst_pad_get_allowed_caps (GstPad * pad)
 {
   GstCaps *mycaps;
   GstCaps *caps;
   GstCaps *peercaps;
   GstPad *peer;
 
-  g_return_val_if_fail (GST_IS_PAD (srcpad), NULL);
-  g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL);
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-  GST_OBJECT_LOCK (srcpad);
+  GST_OBJECT_LOCK (pad);
 
-  peer = GST_PAD_PEER (srcpad);
+  peer = GST_PAD_PEER (pad);
   if (G_UNLIKELY (peer == NULL))
     goto no_peer;
 
-  GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting allowed caps",
-      GST_DEBUG_PAD_NAME (srcpad));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps");
 
   gst_object_ref (peer);
-  GST_OBJECT_UNLOCK (srcpad);
-  mycaps = gst_pad_get_caps (srcpad);
+  GST_OBJECT_UNLOCK (pad);
+  mycaps = gst_pad_get_caps (pad);
 
   peercaps = gst_pad_get_caps (peer);
   gst_object_unref (peer);
@@ -2360,15 +2572,15 @@ gst_pad_get_allowed_caps (GstPad * srcpad)
   gst_caps_unref (peercaps);
   gst_caps_unref (mycaps);
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "allowed caps %" GST_PTR_FORMAT, caps);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT,
+      caps);
 
   return caps;
 
 no_peer:
   {
-    GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer",
-        GST_DEBUG_PAD_NAME (srcpad));
-    GST_OBJECT_UNLOCK (srcpad);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
+    GST_OBJECT_UNLOCK (pad);
 
     return NULL;
   }
@@ -2385,7 +2597,7 @@ no_peer:
  * always negotiated before sinkpads so it is possible that the negotiated caps
  * on the srcpad do not match the negotiated caps of the peer.
  *
- * Returns: the negotiated #GstCaps of the pad link.  Free the caps when
+ * Returns: the negotiated #GstCaps of the pad link.  Unref the caps when
  * you no longer need it. This function returns NULL when the @pad has no
  * peer or is not negotiated yet.
  *
@@ -2404,154 +2616,213 @@ gst_pad_get_negotiated_caps (GstPad * pad)
   if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
     goto no_peer;
 
-  GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting negotiated caps",
-      GST_DEBUG_PAD_NAME (pad));
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting negotiated caps");
 
   caps = GST_PAD_CAPS (pad);
   if (caps)
     gst_caps_ref (caps);
   GST_OBJECT_UNLOCK (pad);
 
-  GST_CAT_DEBUG (GST_CAT_CAPS, "negotiated caps %" GST_PTR_FORMAT, caps);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "negotiated caps %" GST_PTR_FORMAT,
+      caps);
 
   return caps;
 
 no_peer:
   {
-    GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer",
-        GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
     GST_OBJECT_UNLOCK (pad);
 
     return NULL;
   }
 }
 
+/* calls the buffer_alloc function on the given pad */
 static GstFlowReturn
-gst_pad_alloc_buffer_full (GstPad * pad, guint64 offset, gint size,
-    GstCaps * caps, GstBuffer ** buf, gboolean setcaps)
+gst_pad_buffer_alloc_unchecked (GstPad * pad, guint64 offset, gint size,
+    GstCaps * caps, GstBuffer ** buf)
 {
-  GstPad *peer;
   GstFlowReturn ret;
   GstPadBufferAllocFunction bufferallocfunc;
-  gboolean caps_changed;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
-  g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
-  g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
 
   GST_OBJECT_LOCK (pad);
-  while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad)))
-    if ((ret = handle_pad_block (pad)) != GST_FLOW_OK)
-      goto flushed;
-
-  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
-    goto no_peer;
-
-  gst_object_ref (peer);
-  GST_OBJECT_UNLOCK (pad);
-
-  if (G_LIKELY ((bufferallocfunc = peer->bufferallocfunc) == NULL))
-    goto fallback;
-
-  GST_OBJECT_LOCK (peer);
-  /* when the peer is flushing we cannot give a buffer */
-  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (peer)))
+  /* when the pad is flushing we cannot give a buffer */
+  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
+  bufferallocfunc = pad->bufferallocfunc;
+
   if (offset == GST_BUFFER_OFFSET_NONE) {
     GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
-        "calling bufferallocfunc &%s (@%p) of peer pad %s:%s for size %d offset NONE",
-        GST_DEBUG_FUNCPTR_NAME (bufferallocfunc),
-        &bufferallocfunc, GST_DEBUG_PAD_NAME (peer), size);
+        "calling bufferallocfunc &%s (@%p) for size %d offset NONE",
+        GST_DEBUG_FUNCPTR_NAME (bufferallocfunc), bufferallocfunc, size);
   } else {
     GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
-        "calling bufferallocfunc &%s (@%p) of peer pad %s:%s for size %d offset %"
+        "calling bufferallocfunc &%s (@%p) of for size %d offset %"
         G_GUINT64_FORMAT, GST_DEBUG_FUNCPTR_NAME (bufferallocfunc),
-        &bufferallocfunc, GST_DEBUG_PAD_NAME (peer), size, offset);
+        bufferallocfunc, size, offset);
   }
-  GST_OBJECT_UNLOCK (peer);
+  GST_OBJECT_UNLOCK (pad);
 
-  ret = bufferallocfunc (peer, offset, size, caps, buf);
+  /* G_LIKELY for now since most elements don't implement a buffer alloc
+   * function and there is no default alloc proxy function as this is usually
+   * not possible. */
+  if (G_LIKELY (bufferallocfunc == NULL))
+    goto fallback;
+
+  ret = bufferallocfunc (pad, offset, size, caps, buf);
 
   if (G_UNLIKELY (ret != GST_FLOW_OK))
-    goto peer_error;
+    goto error;
+
+  /* no error, but NULL buffer means fallback to the default */
   if (G_UNLIKELY (*buf == NULL))
     goto fallback;
 
   /* If the buffer alloc function didn't set up the caps like it should,
    * do it for it */
-  if (caps && (GST_BUFFER_CAPS (*buf) == NULL)) {
-    GST_WARNING ("Buffer allocation function for pad % " GST_PTR_FORMAT
-        " did not set up caps. Setting", peer);
-
+  if (G_UNLIKELY (caps && (GST_BUFFER_CAPS (*buf) == NULL))) {
+    GST_WARNING_OBJECT (pad,
+        "Buffer allocation function did not set caps. Setting");
     gst_buffer_set_caps (*buf, caps);
   }
+  return ret;
 
-do_caps:
-  gst_object_unref (peer);
-
-  /* FIXME, move capnego this into a base class? */
-  caps = GST_BUFFER_CAPS (*buf);
-  caps_changed = caps && caps != GST_PAD_CAPS (pad);
-  /* we got a new datatype on the pad, see if it can handle it */
-  if (G_UNLIKELY (caps_changed)) {
-    GST_DEBUG ("caps changed to %" GST_PTR_FORMAT, caps);
-    if (G_UNLIKELY (!gst_pad_configure_src (pad, caps, setcaps)))
+flushing:
+  {
+    /* pad was flushing */
+    GST_OBJECT_UNLOCK (pad);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "pad was flushing");
+    return GST_FLOW_WRONG_STATE;
+  }
+error:
+  {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+        "alloc function returned error (%d) %s", ret, gst_flow_get_name (ret));
+    return ret;
+  }
+fallback:
+  {
+    /* fallback case, allocate a buffer of our own, add pad caps. */
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "fallback buffer alloc");
+
+    if ((*buf = gst_buffer_try_new_and_alloc (size))) {
+      GST_BUFFER_OFFSET (*buf) = offset;
+      gst_buffer_set_caps (*buf, caps);
+      return GST_FLOW_OK;
+    } else {
+      GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+          "out of memory allocating %d bytes", size);
+      return GST_FLOW_ERROR;
+    }
+  }
+}
+
+/* FIXME 0.11: size should be unsigned */
+static GstFlowReturn
+gst_pad_alloc_buffer_full (GstPad * pad, guint64 offset, gint size,
+    GstCaps * caps, GstBuffer ** buf, gboolean setcaps)
+{
+  GstPad *peer;
+  GstFlowReturn ret;
+  GstCaps *newcaps;
+  gboolean caps_changed;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
+  g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
+  g_return_val_if_fail (size >= 0, GST_FLOW_ERROR);
+
+  GST_DEBUG_OBJECT (pad, "offset %" G_GUINT64_FORMAT ", size %d", offset, size);
+
+  GST_OBJECT_LOCK (pad);
+  while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad)))
+    if ((ret = handle_pad_block (pad)) != GST_FLOW_OK)
+      goto flushed;
+
+  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
+    goto no_peer;
+
+  gst_object_ref (peer);
+  GST_OBJECT_UNLOCK (pad);
+
+  ret = gst_pad_buffer_alloc_unchecked (peer, offset, size, caps, buf);
+  gst_object_unref (peer);
+
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
+    goto peer_error;
+
+  /* FIXME, move capnego this into a base class? */
+  newcaps = GST_BUFFER_CAPS (*buf);
+
+  /* Lock for checking caps, pretty pointless as the _pad_push() function might
+   * change it concurrently, one of the problems with automatic caps setting in
+   * pad_alloc_and_set_caps. Worst case, if does a check too much, but only
+   * when there is heavy renegotiation going on in both directions. */
+  GST_OBJECT_LOCK (pad);
+  caps_changed = newcaps && newcaps != GST_PAD_CAPS (pad);
+  GST_OBJECT_UNLOCK (pad);
+
+  /* we got a new datatype on the pad, see if it can handle it */
+  if (G_UNLIKELY (caps_changed)) {
+    GST_DEBUG_OBJECT (pad,
+        "caps changed from %" GST_PTR_FORMAT " to %p %" GST_PTR_FORMAT,
+        GST_PAD_CAPS (pad), newcaps, newcaps);
+    if (G_UNLIKELY (!gst_pad_configure_src (pad, newcaps, setcaps)))
       goto not_negotiated;
   }
+
+  /* sanity check (only if caps are the same) */
+  if (G_LIKELY (newcaps == caps) && G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size))
+    goto wrong_size_fallback;
+
   return ret;
 
 flushed:
   {
-    GST_CAT_DEBUG (GST_CAT_PADS, "%s:%s pad block stopped by flush",
-        GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "pad block stopped by flush");
     GST_OBJECT_UNLOCK (pad);
     return ret;
   }
 no_peer:
   {
     /* pad has no peer */
-    GST_CAT_DEBUG (GST_CAT_PADS,
-        "%s:%s called bufferallocfunc but had no peer",
-        GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+        "called bufferallocfunc but had no peer");
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_NOT_LINKED;
   }
-flushing:
-  {
-    /* peer was flushing */
-    GST_OBJECT_UNLOCK (peer);
-    gst_object_unref (peer);
-    GST_CAT_DEBUG (GST_CAT_PADS,
-        "%s:%s called bufferallocfunc but peer was flushing",
-        GST_DEBUG_PAD_NAME (pad));
-    return GST_FLOW_WRONG_STATE;
-  }
-  /* fallback case, allocate a buffer of our own, add pad caps. */
-fallback:
+peer_error:
   {
-    GST_CAT_DEBUG (GST_CAT_PADS,
-        "%s:%s fallback buffer alloc", GST_DEBUG_PAD_NAME (pad));
-    *buf = gst_buffer_new_and_alloc (size);
-    GST_BUFFER_OFFSET (*buf) = offset;
-    gst_buffer_set_caps (*buf, caps);
-
-    ret = GST_FLOW_OK;
-
-    goto do_caps;
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "alloc function returned error %s", gst_flow_get_name (ret));
+    return ret;
   }
 not_negotiated:
   {
+    gst_buffer_unref (*buf);
+    *buf = NULL;
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "alloc function returned unacceptable buffer");
     return GST_FLOW_NOT_NEGOTIATED;
   }
-peer_error:
+wrong_size_fallback:
   {
-    gst_object_unref (peer);
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "alloc function returned error %s", gst_flow_get_name (ret));
-    return ret;
+    GST_CAT_ERROR_OBJECT (GST_CAT_PADS, pad, "buffer returned by alloc "
+        "function is too small (%u < %d), doing fallback buffer alloc",
+        GST_BUFFER_SIZE (*buf), size);
+
+    gst_buffer_unref (*buf);
+
+    if ((*buf = gst_buffer_try_new_and_alloc (size))) {
+      GST_BUFFER_OFFSET (*buf) = offset;
+      gst_buffer_set_caps (*buf, caps);
+      return GST_FLOW_OK;
+    } else {
+      GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+          "out of memory allocating %d bytes", size);
+      return GST_FLOW_ERROR;
+    }
   }
 }
 
@@ -2566,19 +2837,20 @@ peer_error:
  * Allocates a new, empty buffer optimized to push to pad @pad.  This
  * function only works if @pad is a source pad and has a peer.
  *
+ * A new, empty #GstBuffer will be put in the @buf argument.
  * You need to check the caps of the buffer after performing this
  * function and renegotiate to the format if needed.
  *
- * A new, empty #GstBuffer will be put in the @buf argument.
- *
  * Returns: a result code indicating success of the operation. Any
- * result code other than GST_FLOW_OK is an error and @buf should
+ * result code other than #GST_FLOW_OK is an error and @buf should
  * not be used.
  * An error can occur if the pad is not connected or when the downstream
  * peer elements cannot provide an acceptable buffer.
  *
  * MT safe.
  */
+
+/* FIXME 0.11: size should be unsigned */
 GstFlowReturn
 gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size, GstCaps * caps,
     GstBuffer ** buf)
@@ -2599,13 +2871,15 @@ gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size, GstCaps * caps,
  * newly allocated buffer are different from the @pad caps.
  *
  * Returns: a result code indicating success of the operation. Any
- * result code other than GST_FLOW_OK is an error and @buf should
+ * result code other than #GST_FLOW_OK is an error and @buf should
  * not be used.
  * An error can occur if the pad is not connected or when the downstream
  * peer elements cannot provide an acceptable buffer.
  *
  * MT safe.
  */
+
+/* FIXME 0.11: size should be unsigned */
 GstFlowReturn
 gst_pad_alloc_buffer_and_set_caps (GstPad * pad, guint64 offset, gint size,
     GstCaps * caps, GstBuffer ** buf)
@@ -2641,7 +2915,7 @@ gst_pad_get_internal_links_default (GstPad * pad)
 
   parent = GST_PAD_PARENT (pad);
   if (!parent)
-    return NULL;
+    goto no_parent;
 
   parent_pads = parent->pads;
 
@@ -2649,13 +2923,19 @@ gst_pad_get_internal_links_default (GstPad * pad)
     GstPad *parent_pad = GST_PAD_CAST (parent_pads->data);
 
     if (parent_pad->direction != direction) {
+      GST_DEBUG_OBJECT (pad, "adding pad %s:%s",
+          GST_DEBUG_PAD_NAME (parent_pad));
       res = g_list_prepend (res, parent_pad);
     }
-
     parent_pads = g_list_next (parent_pads);
   }
-
   return res;
+
+no_parent:
+  {
+    GST_DEBUG_OBJECT (pad, "no parent");
+    return NULL;
+  }
 }
 
 /**
@@ -2690,13 +2970,22 @@ gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event)
   GList *orig, *pads;
   gboolean result;
 
-  GST_INFO_OBJECT (pad, "Sending event %p to all internally linked pads",
-      event);
-
-  result = (GST_PAD_DIRECTION (pad) == GST_PAD_SINK);
+  GST_INFO_OBJECT (pad, "Sending event %p (%s) to all internally linked pads",
+      event, GST_EVENT_TYPE_NAME (event));
 
   orig = pads = gst_pad_get_internal_links (pad);
 
+  if (!pads) {
+    /* If this is a sinkpad and we don't have pads to send the event to, we
+     * return TRUE. This is so that when using the default handler on a sink
+     * element, we don't fail to push it. */
+    result = (GST_PAD_DIRECTION (pad) == GST_PAD_SINK);
+  } else {
+    /* we have pads, the result will be TRUE if one of the pads handled the
+     * event in the code below. */
+    result = FALSE;
+  }
+
   while (pads) {
     GstPad *eventpad = GST_PAD_CAST (pads->data);
 
@@ -2706,16 +2995,14 @@ gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event)
       /* for each pad we send to, we should ref the event; it's up
        * to downstream to unref again when handled. */
       GST_LOG_OBJECT (pad, "Reffing and sending event %p (%s) to %s:%s",
-          event, gst_event_type_get_name (GST_EVENT_TYPE (event)),
-          GST_DEBUG_PAD_NAME (eventpad));
+          event, GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (eventpad));
       gst_event_ref (event);
-      gst_pad_push_event (eventpad, event);
+      result |= gst_pad_push_event (eventpad, event);
     } else {
       /* we only send the event on one pad, multi-sinkpad elements
        * should implement a handler */
       GST_LOG_OBJECT (pad, "sending event %p (%s) to one sink pad %s:%s",
-          event, gst_event_type_get_name (GST_EVENT_TYPE (event)),
-          GST_DEBUG_PAD_NAME (eventpad));
+          event, GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (eventpad));
       result = gst_pad_push_event (eventpad, event);
       goto done;
     }
@@ -2755,6 +3042,7 @@ gst_pad_event_default (GstPad * pad, GstEvent * event)
       GST_DEBUG_OBJECT (pad, "pausing task because of eos");
       gst_pad_pause_task (pad);
     }
+      /* fall thru */
     default:
       break;
   }
@@ -2768,8 +3056,8 @@ gst_pad_event_default (GstPad * pad, GstEvent * event)
  * @dispatch: the #GstDispatcherFunction to call.
  * @data: gpointer user data passed to the dispatcher function.
  *
- * Invokes the given dispatcher function on all pads that are
- * internally linked to the given pad.
+ * Invokes the given dispatcher function on each respective peer of
+ * all pads that are internally linked to the given pad.
  * The GstPadDispatcherFunction should return TRUE when no further pads
  * need to be processed.
  *
@@ -2789,17 +3077,22 @@ gst_pad_dispatcher (GstPad * pad, GstPadDispatcherFunction dispatch,
 
   while (int_pads) {
     GstPad *int_pad = GST_PAD_CAST (int_pads->data);
-    GstPad *int_peer = GST_PAD_PEER (int_pad);
+    GstPad *int_peer = gst_pad_get_peer (int_pad);
 
     if (int_peer) {
+      GST_DEBUG_OBJECT (int_pad, "dispatching to peer %s:%s",
+          GST_DEBUG_PAD_NAME (int_peer));
       res = dispatch (int_peer, data);
+      gst_object_unref (int_peer);
       if (res)
         break;
+    } else {
+      GST_DEBUG_OBJECT (int_pad, "no peer");
     }
     int_pads = g_list_next (int_pads);
   }
-
   g_list_free (orig);
+  GST_DEBUG_OBJECT (pad, "done, result %d", res);
 
   return res;
 }
@@ -2827,7 +3120,7 @@ gst_pad_query (GstPad * pad, GstQuery * query)
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
 
-  GST_DEBUG ("sending query %p to pad %s:%s", query, GST_DEBUG_PAD_NAME (pad));
+  GST_DEBUG_OBJECT (pad, "sending query %p", query);
 
   if ((func = GST_PAD_QUERYFUNC (pad)) == NULL)
     goto no_func;
@@ -2836,7 +3129,57 @@ gst_pad_query (GstPad * pad, GstQuery * query)
 
 no_func:
   {
-    GST_DEBUG ("pad had no query function");
+    GST_DEBUG_OBJECT (pad, "had no query function");
+    return FALSE;
+  }
+}
+
+/**
+ * gst_pad_peer_query:
+ * @pad: a #GstPad to invoke the peer query on.
+ * @query: the #GstQuery to perform.
+ *
+ * Performs gst_pad_query() on the peer of @pad.
+ *
+ * The caller is responsible for both the allocation and deallocation of
+ * the query structure.
+ *
+ * Returns: TRUE if the query could be performed. This function returns %FALSE
+ * if @pad has no peer.
+ *
+ * Since: 0.10.15
+ */
+gboolean
+gst_pad_peer_query (GstPad * pad, GstQuery * query)
+{
+  GstPad *peerpad;
+  gboolean result;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
+
+  GST_OBJECT_LOCK (pad);
+
+  GST_DEBUG_OBJECT (pad, "peer query");
+
+  peerpad = GST_PAD_PEER (pad);
+  if (G_UNLIKELY (peerpad == NULL))
+    goto no_peer;
+
+  gst_object_ref (peerpad);
+  GST_OBJECT_UNLOCK (pad);
+
+  result = gst_pad_query (peerpad, query);
+
+  gst_object_unref (peerpad);
+
+  return result;
+
+  /* ERRORS */
+no_peer:
+  {
+    GST_WARNING_OBJECT (pad, "pad has no peer");
+    GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
 }
@@ -2846,9 +3189,9 @@ no_func:
  * @pad: a #GstPad to call the default query handler on.
  * @query: the #GstQuery to handle.
  *
- * Invokes the default query handler for the given pad. 
- * The query is sent to all pads internally linked to @pad. Note that 
- * if there are many possible sink pads that are internally linked to 
+ * Invokes the default query handler for the given pad.
+ * The query is sent to all pads internally linked to @pad. Note that
+ * if there are many possible sink pads that are internally linked to
  * @pad, only one will be sent the query.
  * Multi-sinkpad elements should implement custom query handlers.
  *
@@ -2895,7 +3238,9 @@ gst_pad_load_and_link (xmlNodePtr self, GstObject * parent)
   while (field) {
     if (!strcmp ((char *) field->name, "name")) {
       name = (gchar *) xmlNodeGetContent (field);
-      pad = gst_element_get_pad (GST_ELEMENT (parent), name);
+      pad = gst_element_get_static_pad (GST_ELEMENT (parent), name);
+      if (!pad)
+        pad = gst_element_get_request_pad (GST_ELEMENT (parent), name);
       g_free (name);
     } else if (!strcmp ((char *) field->name, "peer")) {
       peer = (gchar *) xmlNodeGetContent (field);
@@ -2910,9 +3255,8 @@ gst_pad_load_and_link (xmlNodePtr self, GstObject * parent)
   split = g_strsplit (peer, ".", 2);
 
   if (split[0] == NULL || split[1] == NULL) {
-    GST_CAT_DEBUG (GST_CAT_XML,
-        "Could not parse peer '%s' for pad %s:%s, leaving unlinked",
-        peer, GST_DEBUG_PAD_NAME (pad));
+    GST_CAT_DEBUG_OBJECT (GST_CAT_XML, pad,
+        "Could not parse peer '%s', leaving unlinked", peer);
 
     g_free (peer);
     return;
@@ -2932,7 +3276,9 @@ gst_pad_load_and_link (xmlNodePtr self, GstObject * parent)
   if (target == NULL)
     goto cleanup;
 
-  targetpad = gst_element_get_pad (target, split[1]);
+  targetpad = gst_element_get_static_pad (target, split[1]);
+  if (!pad)
+    targetpad = gst_element_get_request_pad (target, split[1]);
 
   if (targetpad == NULL)
     goto cleanup;
@@ -2964,6 +3310,15 @@ gst_pad_save_thyself (GstObject * object, xmlNodePtr parent)
 
   xmlNewChild (parent, NULL, (xmlChar *) "name",
       (xmlChar *) GST_PAD_NAME (pad));
+
+  if (GST_PAD_IS_SRC (pad)) {
+    xmlNewChild (parent, NULL, (xmlChar *) "direction", (xmlChar *) "source");
+  } else if (GST_PAD_IS_SINK (pad)) {
+    xmlNewChild (parent, NULL, (xmlChar *) "direction", (xmlChar *) "sink");
+  } else {
+    xmlNewChild (parent, NULL, (xmlChar *) "direction", (xmlChar *) "unknown");
+  }
+
   if (GST_PAD_PEER (pad) != NULL) {
     gchar *content;
 
@@ -3010,7 +3365,31 @@ gst_ghost_pad_save_thyself (GstPad * pad, xmlNodePtr parent)
 #endif /* GST_DISABLE_LOADSAVE */
 
 /*
- * should be called with pad lock held
+ * should be called with pad OBJECT_LOCK and STREAM_LOCK held.
+ * GST_PAD_IS_BLOCKED (pad) == TRUE when this function is
+ * called.
+ *
+ * This function performs the pad blocking when an event, buffer push
+ * or buffer_alloc is performed on a _SRC_ pad. It blocks the
+ * streaming thread after informing the pad has been blocked.
+ *
+ * An application can with this method wait and block any streaming
+ * thread and perform operations such as seeking or linking.
+ *
+ * Two methods are available for notifying the application of the
+ * block:
+ * - the callback method, which happens in the STREAMING thread with
+ *   the STREAM_LOCK held. With this method, the most useful way of
+ *   dealing with the callback is to post a message to the main thread
+ *   where the pad block can then be handled outside of the streaming
+ *   thread. With the last method one can perform all operations such
+ *   as doing a state change, linking, unblocking, seeking etc on the
+ *   pad.
+ * - the GCond signal method, which makes any thread unblock when
+ *   the pad block happens.
+ *
+ * During the actual blocking state, the GST_PAD_BLOCKING flag is set.
+ * The GST_PAD_BLOCKING flag is unset when the pad was unblocked.
  *
  * MT safe.
  */
@@ -3021,48 +3400,89 @@ handle_pad_block (GstPad * pad)
   gpointer user_data;
   GstFlowReturn ret = GST_FLOW_OK;
 
-  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "signal block taken on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "signal block taken");
+
+  /* flushing, don't bother trying to block and return WRONG_STATE
+   * right away */
+  if (GST_PAD_IS_FLUSHING (pad))
+    goto flushingnonref;
 
-  /* need to grab extra ref for the callbacks */
+  /* we grab an extra ref for the callbacks, this is probably not
+   * needed (callback code does not have a ref and cannot unref). I
+   * think this was done to make it possible to unref the element in
+   * the callback, which is in the end totally impossible as it
+   * requires grabbing the STREAM_LOCK and OBJECT_LOCK which are
+   * all taken when calling this function. */
   gst_object_ref (pad);
 
+  /* we either have a callback installed to notify the block or
+   * some other thread is doing a GCond wait. */
   callback = pad->block_callback;
   if (callback) {
+    /* there is a callback installed, call it. We release the
+     * lock so that the callback can do something usefull with the
+     * pad */
     user_data = pad->block_data;
     GST_OBJECT_UNLOCK (pad);
     callback (pad, TRUE, user_data);
     GST_OBJECT_LOCK (pad);
+
+    /* we released the lock, recheck flushing */
+    if (GST_PAD_IS_FLUSHING (pad))
+      goto flushing;
   } else {
-    GST_PAD_BLOCK_SIGNAL (pad);
+    /* no callback, signal the thread that is doing a GCond wait
+     * if any. */
+    GST_PAD_BLOCK_BROADCAST (pad);
   }
 
+  /* OBJECT_LOCK could have been released when we did the callback, which
+   * then could have made the pad unblock so we need to check the blocking
+   * condition again.   */
   while (GST_PAD_IS_BLOCKED (pad)) {
-    if (GST_PAD_IS_FLUSHING (pad))
-      goto flushing;
+    /* now we block the streaming thread. It can be unlocked when we
+     * deactivate the pad (which will also set the FLUSHING flag) or
+     * when the pad is unblocked. A flushing event will also unblock
+     * the pad after setting the FLUSHING flag. */
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "Waiting to be unblocked or set flushing");
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_BLOCKING);
     GST_PAD_BLOCK_WAIT (pad);
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_BLOCKING);
+
+    /* see if we got unblocked by a flush or not */
     if (GST_PAD_IS_FLUSHING (pad))
       goto flushing;
   }
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got unblocked");
 
+  /* when we get here, the pad is unblocked again and we perform
+   * the needed unblock code. */
   callback = pad->block_callback;
   if (callback) {
+    /* we need to call the callback */
     user_data = pad->block_data;
     GST_OBJECT_UNLOCK (pad);
     callback (pad, FALSE, user_data);
     GST_OBJECT_LOCK (pad);
   } else {
-    GST_PAD_BLOCK_SIGNAL (pad);
+    /* we need to signal the thread waiting on the GCond */
+    GST_PAD_BLOCK_BROADCAST (pad);
   }
 
   gst_object_unref (pad);
 
   return ret;
 
+flushingnonref:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad was flushing");
+    return GST_FLOW_WRONG_STATE;
+  }
 flushing:
   {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad became flushing");
     gst_object_unref (pad);
     return GST_FLOW_WRONG_STATE;
   }
@@ -3085,7 +3505,7 @@ gst_pad_emit_have_data_signal (GstPad * pad, GstMiniObject * obj)
   g_value_set_boolean (&ret, TRUE);
   g_value_init (&args[0], GST_TYPE_PAD);
   g_value_set_object (&args[0], pad);
-  g_value_init (&args[1], GST_TYPE_MINI_OBJECT);        // G_TYPE_POINTER);
+  g_value_init (&args[1], GST_TYPE_MINI_OBJECT);
   gst_value_set_mini_object (&args[1], obj);
 
   if (GST_IS_EVENT (obj))
@@ -3105,19 +3525,11 @@ gst_pad_emit_have_data_signal (GstPad * pad, GstMiniObject * obj)
   return res;
 }
 
-/**
- * gst_pad_chain:
- * @pad: a sink #GstPad.
- * @buffer: the #GstBuffer to send.
- *
- * Chain a buffer to @pad.
- *
- * Returns: a #GstFlowReturn from the pad.
- *
- * MT safe.
+/* this is the chain function that does not perform the additional argument
+ * checking for that little extra speed.
  */
-GstFlowReturn
-gst_pad_chain (GstPad * pad, GstBuffer * buffer)
+static inline GstFlowReturn
+gst_pad_chain_unchecked (GstPad * pad, GstBuffer * buffer)
 {
   GstCaps *caps;
   gboolean caps_changed;
@@ -3125,12 +3537,6 @@ gst_pad_chain (GstPad * pad, GstBuffer * buffer)
   GstFlowReturn ret;
   gboolean emit_signal;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
-  g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK,
-      GST_FLOW_ERROR);
-  g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
-  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
-
   GST_PAD_STREAM_LOCK (pad);
 
   GST_OBJECT_LOCK (pad);
@@ -3152,7 +3558,7 @@ gst_pad_chain (GstPad * pad, GstBuffer * buffer)
 
   /* we got a new datatype on the pad, see if it can handle it */
   if (G_UNLIKELY (caps_changed)) {
-    GST_DEBUG ("caps changed to %" GST_PTR_FORMAT, caps);
+    GST_DEBUG_OBJECT (pad, "caps changed to %p %" GST_PTR_FORMAT, caps, caps);
     if (G_UNLIKELY (!gst_pad_configure_sink (pad, caps)))
       goto not_negotiated;
   }
@@ -3166,15 +3572,13 @@ gst_pad_chain (GstPad * pad, GstBuffer * buffer)
     goto no_function;
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "calling chainfunction &%s of pad %s:%s",
-      GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_DEBUG_PAD_NAME (pad));
+      "calling chainfunction &%s", GST_DEBUG_FUNCPTR_NAME (chainfunc));
 
   ret = chainfunc (pad, buffer);
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "called chainfunction &%s of pad %s:%s, returned %s",
-      GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_DEBUG_PAD_NAME (pad),
-      gst_flow_get_name (ret));
+      "called chainfunction &%s, returned %s",
+      GST_DEBUG_FUNCPTR_NAME (chainfunc), gst_flow_get_name (ret));
 
   GST_PAD_STREAM_UNLOCK (pad);
 
@@ -3214,17 +3618,68 @@ no_function:
         ("push on pad %s:%s but it has no chainfunction",
             GST_DEBUG_PAD_NAME (pad)));
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_ERROR;
+    return GST_FLOW_NOT_SUPPORTED;
   }
 }
 
 /**
+ * gst_pad_chain:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @buffer: the #GstBuffer to send, return GST_FLOW_ERROR if not.
+ *
+ * Chain a buffer to @pad.
+ *
+ * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ *
+ * If the caps on @buffer are different from the current caps on @pad, this
+ * function will call any setcaps function (see gst_pad_set_setcaps_function())
+ * installed on @pad. If the new caps are not acceptable for @pad, this
+ * function returns #GST_FLOW_NOT_NEGOTIATED.
+ *
+ * The function proceeds calling the chain function installed on @pad (see
+ * gst_pad_set_chain_function()) and the return value of that function is
+ * returned to the caller. #GST_FLOW_NOT_SUPPORTED is returned if @pad has no
+ * chain function.
+ *
+ * In all cases, success or failure, the caller loses its reference to @buffer
+ * after calling this function.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * MT safe.
+ */
+GstFlowReturn
+gst_pad_chain (GstPad * pad, GstBuffer * buffer)
+{
+  g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK,
+      GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  return gst_pad_chain_unchecked (pad, buffer);
+}
+
+/**
  * gst_pad_push:
- * @pad: a source #GstPad.
- * @buffer: the #GstBuffer to push.
+ * @pad: a source #GstPad, returns #GST_FLOW_ERROR if not.
+ * @buffer: the #GstBuffer to push returns GST_FLOW_ERROR if not.
  *
  * Pushes a buffer to the peer of @pad.
- * buffer probes will be triggered before the buffer gets pushed.
+ *
+ * This function will call an installed pad block before triggering any
+ * installed pad probes.
+ *
+ * If the caps on @buffer are different from the currently configured caps on
+ * @pad, this function will call any installed setcaps function on @pad (see
+ * gst_pad_set_setcaps_function()). In case of failure to renegotiate the new
+ * format, this function returns #GST_FLOW_NOT_NEGOTIATED.
+ *
+ * The function proceeds calling gst_pad_chain() on the peer pad and returns
+ * the value from that function. If @pad has no peer, #GST_FLOW_NOT_LINKED will
+ * be returned.
+ *
+ * In all cases, success or failure, the caller loses its reference to @buffer
+ * after calling this function.
  *
  * Returns: a #GstFlowReturn from the peer pad.
  *
@@ -3236,15 +3691,17 @@ gst_pad_push (GstPad * pad, GstBuffer * buffer)
   GstPad *peer;
   GstFlowReturn ret;
 
+  GstCaps *caps;
+  gboolean caps_changed;
+
   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
   g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC, GST_FLOW_ERROR);
-  g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
   g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
 
   GST_OBJECT_LOCK (pad);
 
   /* FIXME: this check can go away; pad_set_blocked could be implemented with
-   * probes completely */
+   * probes completely or probes with an extended pad block. */
   while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad)))
     if ((ret = handle_pad_block (pad)) != GST_FLOW_OK)
       goto flushed;
@@ -3265,10 +3722,27 @@ gst_pad_push (GstPad * pad, GstBuffer * buffer)
 
   if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
     goto not_linked;
+
+  /* take ref to peer pad before releasing the lock */
   gst_object_ref (peer);
+
+  /* Before pushing the buffer to the peer pad, ensure that caps
+   * are set on this pad */
+  caps = GST_BUFFER_CAPS (buffer);
+  caps_changed = caps && caps != GST_PAD_CAPS (pad);
+
   GST_OBJECT_UNLOCK (pad);
 
-  ret = gst_pad_chain (peer, buffer);
+  /* we got a new datatype from the pad, it had better handle it */
+  if (G_UNLIKELY (caps_changed)) {
+    GST_DEBUG_OBJECT (pad,
+        "caps changed from %" GST_PTR_FORMAT " to %p %" GST_PTR_FORMAT,
+        GST_PAD_CAPS (pad), caps, caps);
+    if (G_UNLIKELY (!gst_pad_configure_src (pad, caps, TRUE)))
+      goto not_negotiated;
+  }
+
+  ret = gst_pad_chain_unchecked (peer, buffer);
 
   gst_object_unref (peer);
 
@@ -3296,13 +3770,21 @@ not_linked:
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_NOT_LINKED;
   }
+not_negotiated:
+  {
+    gst_buffer_unref (buffer);
+    gst_object_unref (peer);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "element pushed buffer then refused to accept the caps");
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
 }
 
 /**
  * gst_pad_check_pull_range:
  * @pad: a sink #GstPad.
  *
- * Checks if a #gst_pad_pull_range() can be performed on the peer
+ * Checks if a gst_pad_pull_range() can be performed on the peer
  * source pad. This function is used by plugins that want to check
  * if they can use random access on the peer source pad.
  *
@@ -3337,6 +3819,8 @@ gst_pad_check_pull_range (GstPad * pad)
   if (G_LIKELY ((checkgetrangefunc = peer->checkgetrangefunc) == NULL)) {
     /* FIXME, kindoff ghetto */
     ret = GST_PAD_GETRANGEFUNC (peer) != NULL;
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "no checkgetrangefunc, assuming %d", ret);
   } else {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "calling checkgetrangefunc %s of peer pad %s:%s",
@@ -3352,6 +3836,8 @@ gst_pad_check_pull_range (GstPad * pad)
   /* ERROR recovery here */
 wrong_direction:
   {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "checking pull range, but pad must be a sinkpad");
     GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
@@ -3366,12 +3852,24 @@ not_connected:
 
 /**
  * gst_pad_get_range:
- * @pad: a src #GstPad.
+ * @pad: a src #GstPad, returns #GST_FLOW_ERROR if not.
  * @offset: The start offset of the buffer
  * @size: The length of the buffer
- * @buffer: a pointer to hold the #GstBuffer.
+ * @buffer: a pointer to hold the #GstBuffer, returns #GST_FLOW_ERROR if %NULL.
+ *
+ * When @pad is flushing this function returns #GST_FLOW_WRONG_STATE
+ * immediatly.
  *
- * Calls the getrange function of @pad.
+ * Calls the getrange function of @pad, see #GstPadGetRangeFunction for a
+ * description of a getrange function. If @pad has no getrange function
+ * installed (see gst_pad_set_getrange_function()) this function returns
+ * #GST_FLOW_NOT_SUPPORTED.
+ *
+ * @buffer's caps must either be unset or the same as what is already
+ * configured on @pad. Renegotiation within a running pull-mode pipeline is not
+ * supported.
+ *
+ * This is a lowlevel function. Usualy gst_pad_pull_range() is used.
  *
  * Returns: a #GstFlowReturn from the pad.
  *
@@ -3402,10 +3900,9 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
     goto no_function;
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "calling getrangefunc %s of peer pad %s:%s, offset %"
+      "calling getrangefunc %s, offset %"
       G_GUINT64_FORMAT ", size %u",
-      GST_DEBUG_FUNCPTR_NAME (getrangefunc), GST_DEBUG_PAD_NAME (pad),
-      offset, size);
+      GST_DEBUG_FUNCPTR_NAME (getrangefunc), offset, size);
 
   ret = getrangefunc (pad, offset, size, buffer);
 
@@ -3417,6 +3914,23 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
 
   GST_PAD_STREAM_UNLOCK (pad);
 
+  if (G_LIKELY (ret == GST_FLOW_OK)) {
+    GstCaps *caps;
+    gboolean caps_changed;
+
+    GST_OBJECT_LOCK (pad);
+    /* Before pushing the buffer to the peer pad, ensure that caps
+     * are set on this pad */
+    caps = GST_BUFFER_CAPS (*buffer);
+    caps_changed = caps && caps != GST_PAD_CAPS (pad);
+    GST_OBJECT_UNLOCK (pad);
+
+    /* we got a new datatype from the pad not supported in a running pull-mode
+     * pipeline */
+    if (G_UNLIKELY (caps_changed))
+      goto not_negotiated;
+  }
+
   return ret;
 
   /* ERRORS */
@@ -3434,30 +3948,64 @@ no_function:
         ("pullrange on pad %s:%s but it has no getrangefunction",
             GST_DEBUG_PAD_NAME (pad)));
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_ERROR;
+    return GST_FLOW_NOT_SUPPORTED;
   }
 dropping:
   {
-    GST_DEBUG ("Dropping data after FALSE probe return");
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "Dropping data after FALSE probe return");
     GST_PAD_STREAM_UNLOCK (pad);
     gst_buffer_unref (*buffer);
     *buffer = NULL;
     return GST_FLOW_UNEXPECTED;
   }
+not_negotiated:
+  {
+    /* ideally we want to use the commented-out code, but currently demuxers
+     * and typefind do not follow part-negotiation.txt. When switching into
+     * pull mode, typefind should probably return the found caps from
+     * getcaps(), and demuxers should do the setcaps(). */
+
+#if 0
+    gst_buffer_unref (*buffer);
+    *buffer = NULL;
+    GST_CAT_WARNING_OBJECT (GST_CAT_SCHEDULING, pad,
+        "getrange returned buffer of different caps");
+    return GST_FLOW_NOT_NEGOTIATED;
+#endif
+    GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "getrange returned buffer of different caps");
+    return ret;
+  }
 }
 
 
 /**
  * gst_pad_pull_range:
- * @pad: a sink #GstPad.
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
  * @offset: The start offset of the buffer
  * @size: The length of the buffer
- * @buffer: a pointer to hold the #GstBuffer.
+ * @buffer: a pointer to hold the #GstBuffer, returns GST_FLOW_ERROR if %NULL.
+ *
+ * Pulls a @buffer from the peer pad.
+ *
+ * This function will first trigger the pad block signal if it was
+ * installed.
  *
- * Pulls a buffer from the peer pad. @pad must be a linked
- * sinkpad.
+ * When @pad is not linked #GST_FLOW_NOT_LINKED is returned else this
+ * function returns the result of gst_pad_get_range() on the peer pad.
+ * See gst_pad_get_range() for a list of return values and for the
+ * semantics of the arguments of this function.
+ *
+ * @buffer's caps must either be unset or the same as what is already
+ * configured on @pad. Renegotiation within a running pull-mode pipeline is not
+ * supported.
  *
  * Returns: a #GstFlowReturn from the peer pad.
+ * When this function returns #GST_FLOW_OK, @buffer will contain a valid
+ * #GstBuffer that should be freed with gst_buffer_unref() after usage.
+ * @buffer may not be used or freed when any other return value than
+ * #GST_FLOW_OK is returned.
  *
  * MT safe.
  */
@@ -3498,6 +4046,23 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
     if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (*buffer)))
       goto dropping;
   }
+
+  if (G_LIKELY (ret == GST_FLOW_OK)) {
+    GstCaps *caps;
+    gboolean caps_changed;
+
+    GST_OBJECT_LOCK (pad);
+    /* Before pushing the buffer to the peer pad, ensure that caps
+     * are set on this pad */
+    caps = GST_BUFFER_CAPS (*buffer);
+    caps_changed = caps && caps != GST_PAD_CAPS (pad);
+    GST_OBJECT_UNLOCK (pad);
+
+    /* we got a new datatype on the pad, see if it can handle it */
+    if (G_UNLIKELY (caps_changed))
+      goto not_negotiated;
+  }
+
   return ret;
 
   /* ERROR recovery here */
@@ -3510,11 +4075,30 @@ not_connected:
   }
 dropping:
   {
-    GST_DEBUG ("Dropping data after FALSE probe return");
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "Dropping data after FALSE probe return");
     gst_buffer_unref (*buffer);
     *buffer = NULL;
     return GST_FLOW_UNEXPECTED;
   }
+not_negotiated:
+  {
+    /* ideally we want to use the commented-out code, but currently demuxers
+     * and typefind do not follow part-negotiation.txt. When switching into
+     * pull mode, typefind should probably return the found caps from
+     * getcaps(), and demuxers should do the setcaps(). */
+
+#if 0
+    gst_buffer_unref (*buffer);
+    *buffer = NULL;
+    GST_CAT_WARNING_OBJECT (GST_CAT_SCHEDULING, pad,
+        "pullrange returned buffer of different caps");
+    return GST_FLOW_NOT_NEGOTIATED;
+#endif
+    GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "pullrange returned buffer of different caps");
+    return ret;
+  }
 }
 
 /**
@@ -3526,6 +4110,9 @@ dropping:
  * mainly used by elements to send events to their peer
  * elements.
  *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
  * Returns: TRUE if the event was handled.
  *
  * MT safe.
@@ -3540,24 +4127,45 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
   g_return_val_if_fail (event != NULL, FALSE);
   g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
 
+  GST_LOG_OBJECT (pad, "event: %s", GST_EVENT_TYPE_NAME (event));
+
   GST_OBJECT_LOCK (pad);
+
+  /* Two checks to be made:
+   * . (un)set the FLUSHING flag for flushing events,
+   * . handle pad blocking */
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
       GST_PAD_SET_FLUSHING (pad);
+
+      if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+        /* flush start will have set the FLUSHING flag and will then
+         * unlock all threads doing a GCond wait on the blocking pad. This
+         * will typically unblock the STREAMING thread blocked on a pad. */
+        GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-start, "
+            "doing block signal.");
+        GST_PAD_BLOCK_BROADCAST (pad);
+        goto flushed;
+      }
       break;
     case GST_EVENT_FLUSH_STOP:
       GST_PAD_UNSET_FLUSHING (pad);
+
+      /* if we are blocked, flush away the FLUSH_STOP event */
+      if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+        GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-stop");
+        goto flushed;
+      }
       break;
     default:
+      while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+        /* block the event as long as the pad is blocked */
+        if (handle_pad_block (pad) != GST_FLOW_OK)
+          goto flushed;
+      }
       break;
   }
 
-  if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
-    if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
-      GST_PAD_BLOCK_SIGNAL (pad);
-    }
-  }
-
   if (G_UNLIKELY (GST_PAD_DO_EVENT_SIGNALS (pad) > 0)) {
     GST_OBJECT_UNLOCK (pad);
 
@@ -3570,14 +4178,17 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
   if (peerpad == NULL)
     goto not_linked;
 
-  GST_LOG_OBJECT (peerpad, "sending event on peerpad");
+  GST_LOG_OBJECT (pad, "sending event %s to peerpad %" GST_PTR_FORMAT,
+      GST_EVENT_TYPE_NAME (event), peerpad);
   gst_object_ref (peerpad);
   GST_OBJECT_UNLOCK (pad);
 
   result = gst_pad_send_event (peerpad, event);
 
+  /* Note: we gave away ownership of the event at this point */
+  GST_LOG_OBJECT (pad, "sent event to peerpad %" GST_PTR_FORMAT ", result %d",
+      peerpad, result);
   gst_object_unref (peerpad);
-  GST_LOG_OBJECT (peerpad, "sent event on peerpad");
 
   return result;
 
@@ -3590,10 +4201,19 @@ dropping:
   }
 not_linked:
   {
+    GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
     gst_event_unref (event);
     GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
+flushed:
+  {
+    GST_DEBUG_OBJECT (pad,
+        "Not forwarding event since we're flushing and blocking");
+    gst_event_unref (event);
+    GST_OBJECT_UNLOCK (pad);
+    return TRUE;
+  }
 }
 
 /**
@@ -3615,9 +4235,12 @@ not_linked:
  * To find out whether an event type is upstream, downstream, or downstream and
  * serialized, see #GstEventTypeFlags, gst_event_type_get_flags(),
  * #GST_EVENT_IS_UPSTREAM, #GST_EVENT_IS_DOWNSTREAM, and
- * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or plugin
- * doesn't need to bother itself with this information; the core handles all
- * necessary locks and checks.
+ * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or
+ * plugin doesn't need to bother itself with this information; the core handles
+ * all necessary locks and checks.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
  *
  * Returns: TRUE if the event was handled.
  */
@@ -3626,78 +4249,105 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
 {
   gboolean result = FALSE;
   GstPadEventFunction eventfunc;
-  gboolean emit_signal, serialized;
+  gboolean serialized, need_unlock = FALSE;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
   GST_OBJECT_LOCK (pad);
-  if (GST_PAD_IS_SINK (pad) && !GST_EVENT_IS_DOWNSTREAM (event))
-    goto wrong_direction;
-  if (GST_PAD_IS_SRC (pad) && !GST_EVENT_IS_UPSTREAM (event))
-    goto wrong_direction;
+  if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
+      goto wrong_direction;
+    serialized = GST_EVENT_IS_SERIALIZED (event);
+  } else if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
+      goto wrong_direction;
+    /* events on srcpad never are serialized */
+    serialized = FALSE;
+  } else
+    goto unknown_direction;
 
-  if (GST_EVENT_SRC (event) == NULL) {
+  if (G_UNLIKELY (GST_EVENT_SRC (event) == NULL)) {
     GST_LOG_OBJECT (pad, "event had no source, setting pad as event source");
     GST_EVENT_SRC (event) = gst_object_ref (pad);
   }
 
+  /* pad signals */
+  if (G_UNLIKELY (GST_PAD_DO_EVENT_SIGNALS (pad) > 0)) {
+    GST_OBJECT_UNLOCK (pad);
+
+    if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT_CAST (event)))
+      goto dropping;
+
+    GST_OBJECT_LOCK (pad);
+  }
+
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
-      GST_CAT_DEBUG (GST_CAT_EVENT,
-          "have event type %d (FLUSH_START) on pad %s:%s",
-          GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (pad));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
+          "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
 
       /* can't even accept a flush begin event when flushing */
       if (GST_PAD_IS_FLUSHING (pad))
         goto flushing;
       GST_PAD_SET_FLUSHING (pad);
-      GST_CAT_DEBUG (GST_CAT_EVENT, "set flush flag");
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "set flush flag");
       break;
     case GST_EVENT_FLUSH_STOP:
       GST_PAD_UNSET_FLUSHING (pad);
-      GST_CAT_DEBUG (GST_CAT_EVENT, "cleared flush flag");
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag");
+      GST_OBJECT_UNLOCK (pad);
+      /* grab stream lock */
+      GST_PAD_STREAM_LOCK (pad);
+      need_unlock = TRUE;
+      GST_OBJECT_LOCK (pad);
       break;
     default:
-      GST_CAT_DEBUG (GST_CAT_EVENT, "have event type %s on pad %s:%s",
-          gst_event_type_get_name (GST_EVENT_TYPE (event)),
-          GST_DEBUG_PAD_NAME (pad));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "have event type %s",
+          GST_EVENT_TYPE_NAME (event));
 
-      if (GST_PAD_IS_FLUSHING (pad))
+      /* make this a little faster, no point in grabbing the lock
+       * if the pad is allready flushing. */
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushing;
+
+      if (serialized) {
+        /* lock order: STREAM_LOCK, LOCK, recheck flushing. */
+        GST_OBJECT_UNLOCK (pad);
+        GST_PAD_STREAM_LOCK (pad);
+        need_unlock = TRUE;
+        GST_OBJECT_LOCK (pad);
+        if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+          goto flushing;
+      }
       break;
   }
-
-  if ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL)
+  if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL))
     goto no_function;
 
-  emit_signal = GST_PAD_DO_EVENT_SIGNALS (pad) > 0;
   GST_OBJECT_UNLOCK (pad);
 
-  /* have to check if it's a sink pad, because e.g. CUSTOM_BOTH is serialized
-     when going down but not when going up */
-  serialized = GST_EVENT_IS_SERIALIZED (event) && GST_PAD_IS_SINK (pad);
-
-  if (G_UNLIKELY (emit_signal)) {
-    if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (event)))
-      goto dropping;
-  }
-
-  if (serialized)
-    GST_PAD_STREAM_LOCK (pad);
+  result = eventfunc (pad, event);
 
-  result = eventfunc (GST_PAD_CAST (pad), event);
-
-  if (serialized)
+  if (need_unlock)
     GST_PAD_STREAM_UNLOCK (pad);
 
+  GST_DEBUG_OBJECT (pad, "sent event, result %d", result);
+
   return result;
 
   /* ERROR handling */
 wrong_direction:
   {
-    g_warning ("pad %s:%s sending event in wrong direction",
-        GST_DEBUG_PAD_NAME (pad));
+    g_warning ("pad %s:%s sending %s event in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+    GST_OBJECT_UNLOCK (pad);
+    gst_event_unref (event);
+    return FALSE;
+  }
+unknown_direction:
+  {
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
     GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
     return FALSE;
@@ -3707,19 +4357,24 @@ no_function:
     g_warning ("pad %s:%s has no event handler, file a bug.",
         GST_DEBUG_PAD_NAME (pad));
     GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
     return FALSE;
   }
 flushing:
   {
     GST_OBJECT_UNLOCK (pad);
-    GST_CAT_INFO (GST_CAT_EVENT, "Received event on flushing pad. Discarding");
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
+    GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+        "Received event on flushing pad. Discarding");
     gst_event_unref (event);
     return FALSE;
   }
 dropping:
   {
-    GST_DEBUG ("Dropping event after FALSE probe return");
+    GST_DEBUG_OBJECT (pad, "Dropping event after FALSE probe return");
     gst_event_unref (event);
     return FALSE;
   }
@@ -3761,12 +4416,12 @@ gst_pad_get_element_private (GstPad * pad)
  * @func: the task function to call
  * @data: data passed to the task function
  *
- * Starts a task that repeadedly calls @func with @data. This function
- * is nostly used in the pad activation function to start the
- * dataflow. This function will automatically acauire the STREAM_LOCK of
- * the pad before calling @func.
+ * Starts a task that repeatedly calls @func with @data. This function
+ * is mostly used in pad activation functions to start the dataflow.
+ * The #GST_PAD_STREAM_LOCK of @pad will automatically be acquired
+ * before @func is called.
  *
- * Returns: a TRUE if the task could be started.
+ * Returns: a %TRUE if the task could be started.
  */
 gboolean
 gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer data)
@@ -3776,12 +4431,15 @@ gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer data)
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (func != NULL, FALSE);
 
+  GST_DEBUG_OBJECT (pad, "start task");
+
   GST_OBJECT_LOCK (pad);
   task = GST_PAD_TASK (pad);
   if (task == NULL) {
     task = gst_task_create (func, data);
     gst_task_set_lock (task, GST_PAD_GET_STREAM_LOCK (pad));
     GST_PAD_TASK (pad) = task;
+    GST_DEBUG_OBJECT (pad, "created task");
   }
   gst_task_start (task);
   GST_OBJECT_UNLOCK (pad);
@@ -3793,8 +4451,9 @@ gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer data)
  * gst_pad_pause_task:
  * @pad: the #GstPad to pause the task of
  *
- * Pause the task of @pad. This function will also make sure that the
- * function executed by the task will effectively stop.
+ * Pause the task of @pad. This function will also wait until the
+ * function executed by the task is finished if this function is not
+ * called from the task function.
  *
  * Returns: a TRUE if the task could be paused or FALSE when the pad
  * has no task.
@@ -3806,6 +4465,8 @@ gst_pad_pause_task (GstPad * pad)
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
+  GST_DEBUG_OBJECT (pad, "pause task");
+
   GST_OBJECT_LOCK (pad);
   task = GST_PAD_TASK (pad);
   if (task == NULL)
@@ -3813,6 +4474,8 @@ gst_pad_pause_task (GstPad * pad)
   gst_task_pause (task);
   GST_OBJECT_UNLOCK (pad);
 
+  /* wait for task function to finish, this lock is recursive so it does nothing
+   * when the pause is called from the task itself */
   GST_PAD_STREAM_LOCK (pad);
   GST_PAD_STREAM_UNLOCK (pad);
 
@@ -3835,7 +4498,7 @@ no_task:
  * from the GstTaskFunction.
  *
  * This function will deadlock if called from the GstTaskFunction of
- * the task. Use #gst_task_pause() instead.
+ * the task. Use gst_task_pause() instead.
  *
  * Regardless of whether the pad has a task, the stream lock is acquired and
  * released so as to ensure that streaming through this pad has finished.
@@ -3849,6 +4512,8 @@ gst_pad_stop_task (GstPad * pad)
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
+  GST_DEBUG_OBJECT (pad, "stop task");
+
   GST_OBJECT_LOCK (pad);
   task = GST_PAD_TASK (pad);
   if (task == NULL)
@@ -3860,7 +4525,8 @@ gst_pad_stop_task (GstPad * pad)
   GST_PAD_STREAM_LOCK (pad);
   GST_PAD_STREAM_UNLOCK (pad);
 
-  gst_task_join (task);
+  if (!gst_task_join (task))
+    goto join_failed;
 
   gst_object_unref (task);
 
@@ -3868,11 +4534,27 @@ gst_pad_stop_task (GstPad * pad)
 
 no_task:
   {
+    GST_DEBUG_OBJECT (pad, "no task");
     GST_OBJECT_UNLOCK (pad);
 
     GST_PAD_STREAM_LOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
 
+    /* this is not an error */
     return TRUE;
   }
+join_failed:
+  {
+    /* this is bad, possibly the application tried to join the task from
+     * the task's thread. We install the task again so that it will be stopped
+     * again from the right thread next time hopefully. */
+    GST_OBJECT_LOCK (pad);
+    GST_DEBUG_OBJECT (pad, "join failed");
+    /* we can only install this task if there was no other task */
+    if (GST_PAD_TASK (pad) == NULL)
+      GST_PAD_TASK (pad) = task;
+    GST_OBJECT_UNLOCK (pad);
+
+    return FALSE;
+  }
 }