docs: improve API docs
[platform/upstream/gstreamer.git] / gst / gstpad.c
index 206c545..2a0069b 100644 (file)
  * SECTION:gstpad
  * @short_description: Object contained by elements that allows links to
  *                     other elements
- * @see_also: #GstPadTemplate, #GstElement, #GstEvent
+ * @see_also: #GstPadTemplate, #GstElement, #GstEvent, #GstQuery, #GstBuffer
  *
  * A #GstElement is linked to other elements via "pads", which are extremely
  * light-weight generic link points.
- * After two pads are retrieved from an element with gst_element_get_pad(),
- * the pads can be link with gst_pad_link(). (For quick links,
- * you can also use gst_element_link(), which will make the obvious
- * link for you if it's straightforward.)
  *
- * Pads are typically created from a #GstPadTemplate with
- * gst_pad_new_from_template().
+ * Pads have a #GstPadDirection, source pads produce data, sink pads consume
+ * data.
  *
- * Pads have #GstCaps attached to it to describe the media type they are
- * capable of dealing with.  gst_pad_get_caps() and gst_pad_set_caps() are
- * used to manipulate the caps of the pads.
- * Pads created from a pad template cannot set capabilities that are
- * incompatible with the pad template capabilities.
+ * Pads are typically created from a #GstPadTemplate with
+ * gst_pad_new_from_template() and are then added to a #GstElement. This usually
+ * happens when the element is created but it can also happen dynamically based
+ * on the data that the element is processing or based on the pads that the
+ * application requests.
  *
  * Pads without pad templates can be created with gst_pad_new(),
  * which takes a direction and a name as an argument.  If the name is NULL,
  * then a guaranteed unique name will be assigned to it.
  *
+ * A #GstElement creating a pad will typically use the various
+ * gst_pad_set_*_function() calls to register callbacks for events, queries or
+ * dataflow on the pads.
+ *
  * gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
  *
- * A #GstElement creating a pad will typically use the various
- * gst_pad_set_*_function() calls to register callbacks for various events
- * on the pads.
+ * After two pads are retrieved from an element with gst_element_get_pad(),
+ * the pads can be linked with gst_pad_link(). (For quick links,
+ * you can also use gst_element_link(), which will make the obvious
+ * link for you if it's straightforward.). Pads can be unlinked again with
+ * gst_pad_unlink(). gst_pad_get_peer() can be used to check what the pad is
+ * linked to.
+ *
+ * Before dataflow is possible on the pads, they need to be activated with
+ * gst_pad_set_active().
+ *
+ * gst_pad_query() and gst_pad_peer_query() can be used to query various
+ * properties of the pad and the stream.
+ *
+ * To send a #GstEvent on a pad, use gst_pad_send_event() and
+ * gst_pad_push_event(). Some events will be sticky on the pad, meaning that
+ * after they pass on the pad they can be queried later with
+ * gst_pad_get_sticky_event() and gst_pad_sticky_events_foreach().
+ * gst_pad_get_current_caps() and gst_pad_has_current_caps() are convenience
+ * functions to query the current sticky CAPS event on a pad.
  *
  * 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() and
- * gst_pad_push_event().
+ * The dataflow, events and queries that happen on a pad can be monitored with
+ * probes that can be installed with gst_pad_add_probe(). gst_pad_is_blocked()
+ * can be used to check if a block probe is installed on the pad.
+ * gst_pad_is_blocking() checks if the blocking probe is currently blocking the
+ * pad. gst_pad_remove_probe() is used to remove a previously installed probe
+ * and unblock blocking probes if any.
+ *
+ * Pad have an offset that can be retrieved with gst_pad_get_offset(). This
+ * offset will be applied to the running_time of all data passing over the pad.
+ * gst_pad_set_offset() can be used to change the offset.
+ *
+ * Convenience functions exist to start, pause and stop the task on a pad with
+ * gst_pad_start_task(), gst_pad_pause_task() and gst_pad_stop_task()
+ * respectively.
  *
- * Last reviewed on 2006-07-06 (0.10.9)
+ * Last reviewed on 2012-03-29 (0.11.3)
  */
 
 #include "gst_private.h"
@@ -65,7 +93,6 @@
 #include "gstpad.h"
 #include "gstpadtemplate.h"
 #include "gstenumtypes.h"
-#include "gstmarshal.h"
 #include "gstutils.h"
 #include "gstinfo.h"
 #include "gsterror.h"
@@ -80,8 +107,6 @@ enum
 {
   PAD_LINKED,
   PAD_UNLINKED,
-  PAD_REQUEST_LINK,
-  PAD_HAVE_DATA,
   /* FILL ME */
   LAST_SIGNAL
 };
@@ -95,30 +120,46 @@ enum
   /* FILL ME */
 };
 
-typedef struct _GstPadPushCache GstPadPushCache;
-
-struct _GstPadPushCache
-{
-  GstPad *peer;                 /* reffed peer pad */
-  GstCaps *caps;                /* caps for this link */
-};
-
-static GstPadPushCache _pad_cache_invalid = { NULL, };
-
-#define PAD_CACHE_INVALID (&_pad_cache_invalid)
-
 #define GST_PAD_GET_PRIVATE(obj)  \
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PAD, GstPadPrivate))
 
-#define GST_PAD_CHAINLISTFUNC(pad) ((pad)->abidata.ABI.priv->chainlistfunc)
+/* we have a pending and an active event on the pad. On source pads only the
+ * active event is used. On sinkpads, events are copied to the pending entry and
+ * moved to the active event when the eventfunc returned TRUE. */
+typedef struct
+{
+  gboolean received;
+  GstEvent *event;
+} PadEvent;
 
 struct _GstPadPrivate
 {
-  GstPadChainListFunction chainlistfunc;
+  guint events_cookie;
+  GArray *events;
 
-  GstPadPushCache *cache_ptr;
+  gint using;
+  guint probe_list_cookie;
+  guint probe_cookie;
 };
 
+typedef struct
+{
+  GHook hook;
+  guint cookie;
+} GstProbe;
+
+#define PROBE_COOKIE(h) (((GstProbe *)(h))->cookie)
+
+typedef struct
+{
+  GstPad *pad;
+  GstPadProbeInfo *info;
+  gboolean dropped;
+  gboolean pass;
+  gboolean marshalled;
+  guint cookie;
+} ProbeMarshall;
+
 static void gst_pad_dispose (GObject * object);
 static void gst_pad_finalize (GObject * object);
 static void gst_pad_set_property (GObject * object, guint prop_id,
@@ -126,36 +167,23 @@ static void gst_pad_set_property (GObject * object, guint prop_id,
 static void gst_pad_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
-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);
-
-#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
-#ifdef GST_DISABLE_DEPRECATED
-#include <libxml/parser.h>
-#endif
-static xmlNodePtr gst_pad_save_thyself (GstObject * object, xmlNodePtr parent);
-void gst_pad_load_and_link (xmlNodePtr self, GstObject * parent);
-#endif
-
-/* Some deprecated stuff that we need inside here for
- * backwards compatibility */
-#ifdef GST_DISABLE_DEPRECATED
-#ifndef GST_REMOVE_DEPRECATED
-#define GST_PAD_INTLINKFUNC(pad)       (GST_PAD_CAST(pad)->intlinkfunc)
-GList *gst_pad_get_internal_links_default (GstPad * pad);
-#endif
-#endif
-
-static GstObjectClass *parent_class = NULL;
+static gboolean gst_pad_activate_default (GstPad * pad, GstObject * parent);
+static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
+    GstObject * parent, GstBufferList * list);
+
+static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad,
+    GstEvent * event, GstPadProbeType type);
+static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad,
+    GstEvent * event, GstPadProbeType type);
+
 static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
 
 static GParamSpec *pspec_caps = NULL;
 
 /* quarks for probe signals */
 static GQuark buffer_quark;
+static GQuark buffer_list_quark;
 static GQuark event_quark;
 
 typedef struct
@@ -167,11 +195,10 @@ typedef struct
 
 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},
-  {GST_FLOW_WRONG_STATE, "wrong-state", 0},
-  {GST_FLOW_UNEXPECTED, "unexpected", 0},
+  {GST_FLOW_FLUSHING, "flushing", 0},
+  {GST_FLOW_EOS, "eos", 0},
   {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
   {GST_FLOW_ERROR, "error", 0},
   {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
@@ -228,6 +255,7 @@ gst_flow_to_quark (GstFlowReturn ret)
   gint i; \
   \
   buffer_quark = g_quark_from_static_string ("buffer"); \
+  buffer_list_quark = g_quark_from_static_string ("bufferlist"); \
   event_quark = g_quark_from_static_string ("event"); \
   \
   for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {                   \
@@ -238,26 +266,9 @@ gst_flow_to_quark (GstFlowReturn ret)
       GST_DEBUG_BOLD | GST_DEBUG_FG_GREEN, "dataflow inside pads"); \
 }
 
+#define gst_pad_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init);
 
-static gboolean
-_gst_do_pass_data_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  gboolean ret = g_value_get_boolean (handler_return);
-
-  GST_DEBUG ("accumulated %d", ret);
-  g_value_set_boolean (return_accu, ret);
-
-  return ret;
-}
-
-static gboolean
-default_have_data (GstPad * pad, GstMiniObject * o)
-{
-  return TRUE;
-}
-
 static void
 gst_pad_class_init (GstPadClass * klass)
 {
@@ -269,8 +280,6 @@ gst_pad_class_init (GstPadClass * klass)
 
   g_type_class_add_private (klass, sizeof (GstPadPrivate));
 
-  parent_class = g_type_class_peek_parent (klass);
-
   gobject_class->dispose = gst_pad_dispose;
   gobject_class->finalize = gst_pad_finalize;
   gobject_class->set_property = gst_pad_set_property;
@@ -286,7 +295,7 @@ gst_pad_class_init (GstPadClass * klass)
   gst_pad_signals[PAD_LINKED] =
       g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
       G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
-      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
   /**
    * GstPad::unlinked:
    * @pad: the pad that emitted the signal
@@ -297,37 +306,7 @@ gst_pad_class_init (GstPadClass * klass)
   gst_pad_signals[PAD_UNLINKED] =
       g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
       G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
-      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
-  /**
-   * GstPad::request-link:
-   * @pad: the pad that emitted the signal
-   * @peer: the peer pad for which a connection is requested
-   *
-   * Signals that a pad connection has been requested.
-   */
-  gst_pad_signals[PAD_REQUEST_LINK] =
-      g_signal_new ("request-link", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstPadClass, request_link), NULL,
-      NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 0);
-
-  /**
-   * GstPad::have-data:
-   * @pad: the pad that emitted the signal
-   * @mini_obj: new data
-   *
-   * Signals that new data is available on the pad. This signal is used
-   * internally for implementing pad probes.
-   * See gst_pad_add_*_probe functions.
-   *
-   * Returns: %TRUE to keep the data, %FALSE to drop it
-   */
-  gst_pad_signals[PAD_HAVE_DATA] =
-      g_signal_new ("have-data", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-      G_STRUCT_OFFSET (GstPadClass, have_data),
-      _gst_do_pass_data_accumulator,
-      NULL, gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1,
-      GST_TYPE_MINI_OBJECT);
+      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
 
   pspec_caps = g_param_spec_boxed ("caps", "Caps",
       "The capabilities of the pad", GST_TYPE_CAPS,
@@ -338,75 +317,267 @@ gst_pad_class_init (GstPadClass * klass)
       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_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_STATIC_STRINGS));
 
-#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
-  gstobject_class->save_thyself =
-      ((gpointer (*)(GstObject * object,
-              gpointer self)) * GST_DEBUG_FUNCPTR (gst_pad_save_thyself));
-#endif
   gstobject_class->path_string_separator = ".";
 
   /* Register common function pointer descriptions */
   GST_DEBUG_REGISTER_FUNCPTR (gst_pad_activate_default);
   GST_DEBUG_REGISTER_FUNCPTR (gst_pad_event_default);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_get_query_types_default);
   GST_DEBUG_REGISTER_FUNCPTR (gst_pad_query_default);
-#ifndef GST_REMOVE_DEPRECATED
-  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_get_internal_links_default);
-#endif
   GST_DEBUG_REGISTER_FUNCPTR (gst_pad_iterate_internal_links_default);
-  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_acceptcaps_default);
-
-  /* from gstutils.c */
-  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_get_fixed_caps_func);
-
-  klass->have_data = default_have_data;
+  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
 }
 
 static void
 gst_pad_init (GstPad * pad)
 {
-  pad->abidata.ABI.priv = GST_PAD_GET_PRIVATE (pad);
+  pad->priv = GST_PAD_GET_PRIVATE (pad);
 
   GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
-  GST_PAD_PEER (pad) = NULL;
-
-  GST_PAD_CHAINFUNC (pad) = NULL;
-
-  GST_PAD_LINKFUNC (pad) = NULL;
-
-  GST_PAD_CAPS (pad) = NULL;
-  GST_PAD_GETCAPSFUNC (pad) = NULL;
 
   GST_PAD_ACTIVATEFUNC (pad) = gst_pad_activate_default;
   GST_PAD_EVENTFUNC (pad) = gst_pad_event_default;
-  GST_PAD_QUERYTYPEFUNC (pad) = gst_pad_get_query_types_default;
   GST_PAD_QUERYFUNC (pad) = gst_pad_query_default;
-#ifndef GST_REMOVE_DEPRECATED
-  GST_PAD_INTLINKFUNC (pad) = gst_pad_get_internal_links_default;
-#endif
   GST_PAD_ITERINTLINKFUNC (pad) = gst_pad_iterate_internal_links_default;
+  GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
+
+  GST_PAD_SET_FLUSHING (pad);
 
-  GST_PAD_ACCEPTCAPSFUNC (pad) = gst_pad_acceptcaps_default;
+  g_rec_mutex_init (&pad->stream_rec_lock);
 
-  pad->do_buffer_signals = 0;
-  pad->do_event_signals = 0;
+  g_cond_init (&pad->block_cond);
 
-  GST_PAD_SET_FLUSHING (pad);
+  g_hook_list_init (&pad->probes, sizeof (GstProbe));
+
+  pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
+}
+
+/* called when setting the pad inactive. It removes all sticky events from
+ * the pad */
+static void
+remove_events (GstPad * pad)
+{
+  guint i, len;
+  GArray *events;
+
+  events = pad->priv->events;
+
+  len = events->len;
+  for (i = 0; i < len; i++) {
+    PadEvent *ev = &g_array_index (events, PadEvent, i);
+    gst_event_unref (ev->event);
+  }
+  GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+  g_array_set_size (events, 0);
+  pad->priv->events_cookie++;
+}
+
+static PadEvent *
+find_event_by_type (GstPad * pad, GstEventType type, guint idx)
+{
+  guint i, len;
+  GArray *events;
+  PadEvent *ev;
+
+  events = pad->priv->events;
+  len = events->len;
+
+  for (i = 0; i < len; i++) {
+    ev = &g_array_index (events, PadEvent, i);
+    if (ev->event == NULL)
+      continue;
+
+    if (GST_EVENT_TYPE (ev->event) == type) {
+      if (idx == 0)
+        goto found;
+      idx--;
+    }
+  }
+  ev = NULL;
+found:
+  return ev;
+}
+
+static PadEvent *
+find_event (GstPad * pad, GstEvent * event)
+{
+  guint i, len;
+  GArray *events;
+  PadEvent *ev;
+
+  events = pad->priv->events;
+  len = events->len;
+
+  for (i = 0; i < len; i++) {
+    ev = &g_array_index (events, PadEvent, i);
+    if (event == ev->event)
+      goto found;
+  }
+  ev = NULL;
+found:
+  return ev;
+}
+
+static void
+remove_event_by_type (GstPad * pad, GstEventType type)
+{
+  guint i, len;
+  GArray *events;
+  PadEvent *ev;
+
+  events = pad->priv->events;
+  len = events->len;
+
+  i = 0;
+  while (i < len) {
+    ev = &g_array_index (events, PadEvent, i);
+    if (ev->event == NULL)
+      goto next;
+
+    if (GST_EVENT_TYPE (ev->event) != type)
+      goto next;
+
+    gst_event_unref (ev->event);
+    g_array_remove_index (events, i);
+    len--;
+    pad->priv->events_cookie++;
+    continue;
+
+  next:
+    i++;
+  }
+}
+
+/* check all events on srcpad against those on sinkpad. All events that are not
+ * on sinkpad are marked as received=FALSE and the PENDING_EVENTS is set on the
+ * srcpad so that the events will be sent next time */
+static void
+schedule_events (GstPad * srcpad, GstPad * sinkpad)
+{
+  gint i, len;
+  GArray *events;
+  PadEvent *ev;
+  gboolean pending = FALSE;
+
+  events = srcpad->priv->events;
+  len = events->len;
+
+  for (i = 0; i < len; i++) {
+    ev = &g_array_index (events, PadEvent, i);
+    if (ev->event == NULL)
+      continue;
+
+    if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
+      ev->received = FALSE;
+      pending = TRUE;
+    }
+  }
+  if (pending)
+    GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
+}
+
+typedef gboolean (*PadEventFunction) (GstPad * pad, PadEvent * ev,
+    gpointer user_data);
+
+/* should be called with pad LOCK */
+static void
+events_foreach (GstPad * pad, PadEventFunction func, gpointer user_data)
+{
+  guint i, len;
+  GArray *events;
+  gboolean ret;
+  guint cookie;
+
+  events = pad->priv->events;
+
+restart:
+  cookie = pad->priv->events_cookie;
+  i = 0;
+  len = events->len;
+  while (i < len) {
+    PadEvent *ev, ev_ret;
+
+    ev = &g_array_index (events, PadEvent, i);
+    if (G_UNLIKELY (ev->event == NULL))
+      goto next;
+
+    /* take aditional ref, func might release the lock */
+    ev_ret.event = gst_event_ref (ev->event);
+    ev_ret.received = ev->received;
+
+    ret = func (pad, &ev_ret, user_data);
+
+    /* recheck the cookie, lock might have been released and the list could have
+     * changed */
+    if (G_UNLIKELY (cookie != pad->priv->events_cookie)) {
+      if (G_LIKELY (ev_ret.event))
+        gst_event_unref (ev_ret.event);
+      goto restart;
+    }
+
+    /* store the received state */
+    ev->received = ev_ret.received;
+
+    /* if the event changed, we need to do something */
+    if (G_UNLIKELY (ev->event != ev_ret.event)) {
+      if (G_UNLIKELY (ev_ret.event == NULL)) {
+        /* function unreffed and set the event to NULL, remove it */
+        g_array_remove_index (events, i);
+        len--;
+        cookie = ++pad->priv->events_cookie;
+        continue;
+      } else {
+        /* function gave a new event for us */
+        gst_event_take (&ev->event, ev_ret.event);
+      }
+    } else {
+      /* just unref, nothing changed */
+      gst_event_unref (ev_ret.event);
+    }
+    if (!ret)
+      break;
+  next:
+    i++;
+  }
+}
 
-  pad->preroll_lock = g_mutex_new ();
-  pad->preroll_cond = g_cond_new ();
+/* should be called with LOCK */
+static GstEvent *
+apply_pad_offset (GstPad * pad, GstEvent * event)
+{
+  /* check if we need to adjust the segment */
+  if (pad->offset != 0) {
+    GstSegment segment;
+
+    /* copy segment values */
+    gst_event_copy_segment (event, &segment);
+    gst_event_unref (event);
+
+    /* adjust and make a new event with the offset applied */
+    segment.base += pad->offset;
+    event = gst_event_new_segment (&segment);
+  }
+  return event;
+}
+
+/* should be called with the OBJECT_LOCK */
+static GstCaps *
+get_pad_caps (GstPad * pad)
+{
+  GstCaps *caps = NULL;
+  PadEvent *ev;
 
-  /* FIXME 0.11: Store this directly in the instance struct */
-  pad->stream_rec_lock = g_slice_new (GStaticRecMutex);
-  g_static_rec_mutex_init (pad->stream_rec_lock);
+  ev = find_event_by_type (pad, GST_EVENT_CAPS, 0);
+  if (ev && ev->event)
+    gst_event_parse_caps (ev->event, &caps);
 
-  pad->block_cond = g_cond_new ();
+  return caps;
 }
 
 static void
@@ -430,15 +601,11 @@ gst_pad_dispose (GObject * object)
     gst_object_unref (peer);
   }
 
-  /* clear the caps */
-  gst_caps_replace (&GST_PAD_CAPS (pad), NULL);
-
   gst_pad_set_pad_template (pad, NULL);
 
-  if (pad->block_destroy_data && pad->block_data) {
-    pad->block_destroy_data (pad->block_data);
-    pad->block_data = NULL;
-  }
+  remove_events (pad);
+
+  g_hook_list_clear (&pad->probes);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -456,21 +623,30 @@ gst_pad_finalize (GObject * object)
     gst_object_unref (task);
   }
 
-  if (pad->stream_rec_lock) {
-    g_static_rec_mutex_free (pad->stream_rec_lock);
-    g_slice_free (GStaticRecMutex, pad->stream_rec_lock);
-    pad->stream_rec_lock = NULL;
-  }
-  if (pad->preroll_lock) {
-    g_mutex_free (pad->preroll_lock);
-    g_cond_free (pad->preroll_cond);
-    pad->preroll_lock = NULL;
-    pad->preroll_cond = NULL;
-  }
-  if (pad->block_cond) {
-    g_cond_free (pad->block_cond);
-    pad->block_cond = NULL;
-  }
+  if (pad->activatenotify)
+    pad->activatenotify (pad->activatedata);
+  if (pad->activatemodenotify)
+    pad->activatemodenotify (pad->activatemodedata);
+  if (pad->linknotify)
+    pad->linknotify (pad->linkdata);
+  if (pad->unlinknotify)
+    pad->unlinknotify (pad->unlinkdata);
+  if (pad->chainnotify)
+    pad->chainnotify (pad->chaindata);
+  if (pad->chainlistnotify)
+    pad->chainlistnotify (pad->chainlistdata);
+  if (pad->getrangenotify)
+    pad->getrangenotify (pad->getrangedata);
+  if (pad->eventnotify)
+    pad->eventnotify (pad->eventdata);
+  if (pad->querynotify)
+    pad->querynotify (pad->querydata);
+  if (pad->iterintlinknotify)
+    pad->iterintlinknotify (pad->iterintlinkdata);
+
+  g_rec_mutex_clear (&pad->stream_rec_lock);
+  g_cond_clear (&pad->block_cond);
+  g_array_free (pad->priv->events, TRUE);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -504,7 +680,7 @@ 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));
+      g_value_set_boxed (value, get_pad_caps (GST_PAD_CAST (object)));
       GST_OBJECT_UNLOCK (object);
       break;
     case PAD_PROP_DIRECTION:
@@ -529,7 +705,7 @@ gst_pad_get_property (GObject * object, guint prop_id,
  * will be assigned.
  * This function makes a copy of the name so you can safely free the name.
  *
- * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ * Returns: (transfer floating): a new #GstPad, or NULL in case of an error.
  *
  * MT safe.
  */
@@ -586,6 +762,20 @@ gst_pad_new_from_static_template (GstStaticPadTemplate * templ,
   return pad;
 }
 
+#define ACQUIRE_PARENT(pad, parent, label)                      \
+  G_STMT_START {                                                \
+    if (G_LIKELY ((parent = GST_OBJECT_PARENT (pad))))          \
+      gst_object_ref (parent);                                  \
+    else if (G_LIKELY (GST_PAD_NEEDS_PARENT (pad)))             \
+      goto label;                                               \
+  } G_STMT_END
+
+#define RELEASE_PARENT(parent)                                  \
+  G_STMT_START {                                                \
+    if (G_LIKELY (parent))                                      \
+      gst_object_unref (parent);                                \
+  } G_STMT_END
+
 /**
  * gst_pad_get_direction:
  * @pad: a #GstPad to get the direction of.
@@ -613,51 +803,72 @@ gst_pad_get_direction (GstPad * pad)
 }
 
 static gboolean
-gst_pad_activate_default (GstPad * pad)
+gst_pad_activate_default (GstPad * pad, GstObject * parent)
 {
-  return gst_pad_activate_push (pad, TRUE);
+  return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
 }
 
 static void
-pre_activate (GstPad * pad, GstActivateMode new_mode)
+pre_activate (GstPad * pad, GstPadMode new_mode)
 {
   switch (new_mode) {
-    case GST_ACTIVATE_PUSH:
-    case GST_ACTIVATE_PULL:
-      GST_OBJECT_LOCK (pad);
-      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE %d, unset flushing",
-          new_mode);
-      GST_PAD_UNSET_FLUSHING (pad);
-      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
-      GST_OBJECT_UNLOCK (pad);
-      break;
-    case GST_ACTIVATE_NONE:
+    case GST_PAD_MODE_NONE:
       GST_OBJECT_LOCK (pad);
-      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE NONE, set flushing");
-      _priv_gst_pad_invalidate_cache (pad);
+      GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing");
       GST_PAD_SET_FLUSHING (pad);
-      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
+      GST_PAD_MODE (pad) = new_mode;
       /* unlock blocked pads so element can resume and stop */
       GST_PAD_BLOCK_BROADCAST (pad);
       GST_OBJECT_UNLOCK (pad);
       break;
+    case GST_PAD_MODE_PUSH:
+    case GST_PAD_MODE_PULL:
+      GST_OBJECT_LOCK (pad);
+      GST_DEBUG_OBJECT (pad, "setting PAD_MODE %d, unset flushing", new_mode);
+      GST_PAD_UNSET_FLUSHING (pad);
+      GST_PAD_MODE (pad) = new_mode;
+      if (GST_PAD_IS_SINK (pad)) {
+        GstPad *peer;
+        /* make sure the peer src pad sends us all events */
+        if ((peer = GST_PAD_PEER (pad))) {
+          gst_object_ref (peer);
+          GST_OBJECT_UNLOCK (pad);
+
+          GST_DEBUG_OBJECT (pad, "reschedule events on peer %s:%s",
+              GST_DEBUG_PAD_NAME (peer));
+
+          GST_OBJECT_LOCK (peer);
+          schedule_events (peer, NULL);
+          GST_OBJECT_UNLOCK (peer);
+
+          gst_object_unref (peer);
+        } else {
+          GST_OBJECT_UNLOCK (pad);
+        }
+      } else {
+        GST_OBJECT_UNLOCK (pad);
+      }
+      break;
   }
 }
 
 static void
-post_activate (GstPad * pad, GstActivateMode new_mode)
+post_activate (GstPad * pad, GstPadMode new_mode)
 {
   switch (new_mode) {
-    case GST_ACTIVATE_PUSH:
-    case GST_ACTIVATE_PULL:
-      /* nop */
-      break;
-    case GST_ACTIVATE_NONE:
+    case GST_PAD_MODE_NONE:
       /* ensures that streaming stops */
       GST_PAD_STREAM_LOCK (pad);
       GST_DEBUG_OBJECT (pad, "stopped streaming");
+      GST_OBJECT_LOCK (pad);
+      remove_events (pad);
+      GST_OBJECT_UNLOCK (pad);
       GST_PAD_STREAM_UNLOCK (pad);
       break;
+    case GST_PAD_MODE_PUSH:
+    case GST_PAD_MODE_PULL:
+      /* NOP */
+      break;
   }
 }
 
@@ -684,48 +895,56 @@ post_activate (GstPad * pad, GstActivateMode new_mode)
 gboolean
 gst_pad_set_active (GstPad * pad, gboolean active)
 {
-  GstActivateMode old;
+  GstObject *parent;
+  GstPadMode old;
   gboolean ret = FALSE;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
   GST_OBJECT_LOCK (pad);
-  old = GST_PAD_ACTIVATE_MODE (pad);
+  old = GST_PAD_MODE (pad);
+  ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
   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;
+    if (old == GST_PAD_MODE_NONE) {
+      GST_DEBUG_OBJECT (pad, "activating pad from none");
+      ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad, parent);
+    } else {
+      GST_DEBUG_OBJECT (pad, "pad was active in mode %d", old);
+      ret = TRUE;
     }
   } 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 (old == GST_PAD_MODE_NONE) {
+      GST_DEBUG_OBJECT (pad, "pad was inactive");
+      ret = TRUE;
+    } else {
+      GST_DEBUG_OBJECT (pad, "deactivating pad from mode %d", old);
+      ret = gst_pad_activate_mode (pad, old, FALSE);
     }
   }
 
-  if (!ret) {
+  RELEASE_PARENT (parent);
+
+  if (G_UNLIKELY (!ret))
+    goto failed;
+
+  if (!active) {
+    GST_OBJECT_LOCK (pad);
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+    GST_OBJECT_UNLOCK (pad);
+  }
+  return ret;
+
+  /* ERRORS */
+no_parent:
+  {
+    GST_DEBUG_OBJECT (pad, "no parent");
+    GST_OBJECT_UNLOCK (pad);
+    return FALSE;
+  }
+failed:
+  {
     GST_OBJECT_LOCK (pad);
     if (!active) {
       g_critical ("Failed to deactivate pad %s:%s, very bad",
@@ -734,21 +953,18 @@ gst_pad_set_active (GstPad * pad, gboolean active)
       GST_WARNING_OBJECT (pad, "Failed to activate pad");
     }
     GST_OBJECT_UNLOCK (pad);
+    return FALSE;
   }
-
-  return ret;
 }
 
 /**
- * gst_pad_activate_pull:
+ * gst_pad_activate_mode:
  * @pad: the #GstPad to activate or deactivate.
+ * @mode: the requested activation mode
  * @active: whether or not the pad should be 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.
+ * Activates or deactivates the given pad in @mode via dispatching to the
+ * pad's activatemodefunc. For use from within pad activation functions only.
  *
  * If you don't know what this is, you probably don't want to call it.
  *
@@ -757,79 +973,69 @@ gst_pad_set_active (GstPad * pad, gboolean active)
  * MT safe.
  */
 gboolean
-gst_pad_activate_pull (GstPad * pad, gboolean active)
+gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active)
 {
-  GstActivateMode old, new;
+  gboolean res = FALSE;
+  GstObject *parent;
+  GstPadMode old, new;
+  GstPadDirection dir;
   GstPad *peer;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
   GST_OBJECT_LOCK (pad);
-  old = GST_PAD_ACTIVATE_MODE (pad);
+  old = GST_PAD_MODE (pad);
+  dir = GST_PAD_DIRECTION (pad);
+  ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
-  if (active) {
-    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 {
-    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;
-    }
+  new = active ? mode : GST_PAD_MODE_NONE;
+
+  if (old == new)
+    goto was_ok;
+
+  if (active && old != mode) {
+    /* pad was activate in the wrong direction, deactivate it
+     * and reactivate it in the requested mode */
+    GST_DEBUG_OBJECT (pad, "deactivating pad from mode %d", old);
+    if (G_UNLIKELY (!gst_pad_activate_mode (pad, old, FALSE)))
+      goto deactivate_failed;
   }
 
-  if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
-    if ((peer = gst_pad_get_peer (pad))) {
-      GST_DEBUG_OBJECT (pad, "calling peer");
-      if (G_UNLIKELY (!gst_pad_activate_pull (peer, active)))
-        goto peer_failed;
-      gst_object_unref (peer);
-    } else {
-      /* there is no peer, this is only fatal when we activate. When we
-       * deactivate, we must assume the application has unlinked the peer and
-       * will deactivate it eventually. */
-      if (active)
-        goto not_linked;
-      else
-        GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
-    }
-  } else {
-    if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
-      goto failure;             /* Can't activate pull on a src without a
+  switch (mode) {
+    case GST_PAD_MODE_PULL:
+    {
+      if (dir == GST_PAD_SINK) {
+        if ((peer = gst_pad_get_peer (pad))) {
+          GST_DEBUG_OBJECT (pad, "calling peer");
+          if (G_UNLIKELY (!gst_pad_activate_mode (peer, mode, active)))
+            goto peer_failed;
+          gst_object_unref (peer);
+        } else {
+          /* there is no peer, this is only fatal when we activate. When we
+           * deactivate, we must assume the application has unlinked the peer and
+           * will deactivate it eventually. */
+          if (active)
+            goto not_linked;
+          else
+            GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
+        }
+      } else {
+        if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
+          goto failure;         /* Can't activate pull on a src without a
                                    getrange function */
+      }
+      break;
+    }
+    default:
+      break;
   }
 
-  new = active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE;
   pre_activate (pad, new);
 
-  if (GST_PAD_ACTIVATEPULLFUNC (pad)) {
-    if (G_UNLIKELY (!GST_PAD_ACTIVATEPULLFUNC (pad) (pad, active)))
+  if (GST_PAD_ACTIVATEMODEFUNC (pad)) {
+    if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode,
+                active)))
       goto failure;
   } else {
     /* can happen for sinks of passthrough elements */
@@ -837,362 +1043,238 @@ gst_pad_activate_pull (GstPad * pad, gboolean active)
 
   post_activate (pad, new);
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in pull mode",
-      active ? "activated" : "deactivated");
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in mode %d",
+      active ? "activated" : "deactivated", mode);
 
-  return TRUE;
+exit_success:
+  res = TRUE;
+exit:
+  RELEASE_PARENT (parent);
 
+  return res;
+
+no_parent:
+  {
+    GST_DEBUG_OBJECT (pad, "no parent");
+    GST_OBJECT_UNLOCK (pad);
+    return FALSE;
+  }
 was_ok:
   {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in pull mode",
-        active ? "activated" : "deactivated");
-    return TRUE;
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in mode %d",
+        active ? "activated" : "deactivated", mode);
+    goto exit_success;
   }
 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;
+        "failed to %s in switch to mode %d from mode %d",
+        (active ? "activate" : "deactivate"), mode, old);
+    goto exit;
   }
 peer_failed:
   {
     GST_OBJECT_LOCK (peer);
     GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
-        "activate_pull on peer (%s:%s) failed", GST_DEBUG_PAD_NAME (peer));
+        "activate_mode on peer (%s:%s) failed", GST_DEBUG_PAD_NAME (peer));
     GST_OBJECT_UNLOCK (peer);
     gst_object_unref (peer);
-    return FALSE;
+    goto exit;
   }
 not_linked:
   {
     GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "can't activate unlinked sink "
         "pad in pull mode");
-    return FALSE;
+    goto exit;
   }
 failure:
   {
     GST_OBJECT_LOCK (pad);
-    GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in pull mode",
-        active ? "activate" : "deactivate");
-    _priv_gst_pad_invalidate_cache (pad);
+    GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in mode %d",
+        active ? "activate" : "deactivate", mode);
     GST_PAD_SET_FLUSHING (pad);
-    GST_PAD_ACTIVATE_MODE (pad) = old;
+    GST_PAD_MODE (pad) = old;
     GST_OBJECT_UNLOCK (pad);
-    return FALSE;
+    goto exit;
   }
 }
 
 /**
- * gst_pad_activate_push:
- * @pad: the #GstPad to activate or deactivate.
- * @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.
+ * gst_pad_is_active:
+ * @pad: the #GstPad to query
  *
- * If you don't know what this is, you probably don't want to call it.
+ * Query if a pad is active
  *
- * Returns: %TRUE if the operation was successful.
+ * Returns: TRUE if the pad is active.
  *
  * MT safe.
  */
 gboolean
-gst_pad_activate_push (GstPad * pad, gboolean active)
+gst_pad_is_active (GstPad * pad)
 {
-  GstActivateMode old, new;
+  gboolean result = FALSE;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "trying to set %s in push mode",
-      active ? "activated" : "deactivated");
 
   GST_OBJECT_LOCK (pad);
-  old = GST_PAD_ACTIVATE_MODE (pad);
+  result = GST_PAD_IS_ACTIVE (pad);
   GST_OBJECT_UNLOCK (pad);
 
-  if (active) {
-    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 {
-    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 (G_UNLIKELY (!GST_PAD_ACTIVATEPUSHFUNC (pad) (pad, active))) {
-      goto failure;
-    }
-  } else {
-    /* quite ok, element relies on state change func to prepare itself */
-  }
-
-  post_activate (pad, new);
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in push mode",
-      active ? "activated" : "deactivated");
-  return TRUE;
-
-was_ok:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in push mode",
-        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");
-    _priv_gst_pad_invalidate_cache (pad);
-    GST_PAD_SET_FLUSHING (pad);
-    GST_PAD_ACTIVATE_MODE (pad) = old;
-    GST_OBJECT_UNLOCK (pad);
-    return FALSE;
-  }
-}
-
-/**
- * gst_pad_is_active:
- * @pad: the #GstPad to query
- *
- * Query if a pad is active
- *
- * Returns: TRUE if the pad is active.
- *
- * MT safe.
- */
-gboolean
-gst_pad_is_active (GstPad * pad)
-{
-  gboolean result = FALSE;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-
-  GST_OBJECT_LOCK (pad);
-  result = GST_PAD_MODE_ACTIVATE (GST_PAD_ACTIVATE_MODE (pad));
-  GST_OBJECT_UNLOCK (pad);
-
-  return result;
-}
+  return result;
+}
 
 /**
- * gst_pad_set_blocked_async_full:
- * @pad: the #GstPad to block or unblock
- * @blocked: boolean indicating whether the pad should be blocked or unblocked
- * @callback: #GstPadBlockCallback that will be called when the
- *            operation succeeds
+ * gst_pad_add_probe:
+ * @pad: the #GstPad to add the probe to
+ * @mask: the probe mask
+ * @callback: #GstPadProbeCallback that will be called with notifications of
+ *           the pad state
  * @user_data: (closure): user data passed to the callback
  * @destroy_data: #GDestroyNotify for user_data
  *
- * Blocks or unblocks the dataflow on a pad. The provided callback
- * is called when the operation succeeds; this happens right before the next
- * attempt at pushing a buffer on the pad.
- *
- * This can take a while as the pad can only become blocked when real dataflow
- * is happening.
- * When the pipeline is stalled, for example in PAUSED, this can
- * take an indeterminate amount of time.
- * 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.
+ * Be notified of different states of pads. The provided callback is called for
+ * every state that matches @mask.
  *
- * <note>
- *  Pad block handlers are only called for source pads in push mode
- *  and sink pads in pull mode.
- * </note>
- *
- * 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.
+ * Returns: an id or 0 on error. The id can be used to remove the probe with
+ * gst_pad_remove_probe().
  *
  * MT safe.
- *
- * Since: 0.10.23
  */
-gboolean
-gst_pad_set_blocked_async_full (GstPad * pad, gboolean blocked,
-    GstPadBlockCallback callback, gpointer user_data,
+gulong
+gst_pad_add_probe (GstPad * pad, GstPadProbeType mask,
+    GstPadProbeCallback callback, gpointer user_data,
     GDestroyNotify destroy_data)
 {
-  gboolean was_blocked = FALSE;
+  GHook *hook;
+  gulong res;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (GST_IS_PAD (pad), 0);
+  g_return_val_if_fail (mask != 0, 0);
 
   GST_OBJECT_LOCK (pad);
 
-  was_blocked = GST_PAD_IS_BLOCKED (pad);
-
-  if (G_UNLIKELY (was_blocked == blocked))
-    goto had_right_state;
-
-  if (G_UNLIKELY (
-          (GST_PAD_ACTIVATE_MODE (pad) == GST_ACTIVATE_PUSH) &&
-          (GST_PAD_DIRECTION (pad) != GST_PAD_SRC)))
-    goto wrong_direction;
-  if (G_UNLIKELY (
-          (GST_PAD_ACTIVATE_MODE (pad) == GST_ACTIVATE_PULL) &&
-          (GST_PAD_DIRECTION (pad) != GST_PAD_SINK)))
-    goto wrong_direction;
-
-  if (blocked) {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocking pad");
-
-    _priv_gst_pad_invalidate_cache (pad);
-    GST_OBJECT_FLAG_SET (pad, GST_PAD_BLOCKED);
+  /* make a new probe */
+  hook = g_hook_alloc (&pad->probes);
+
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "adding probe for mask 0x%08x",
+      mask);
+
+  /* when no contraints are given for the types, assume all types are
+   * acceptable */
+  if ((mask & GST_PAD_PROBE_TYPE_ALL_BOTH) == 0)
+    mask |= GST_PAD_PROBE_TYPE_ALL_BOTH;
+  if ((mask & GST_PAD_PROBE_TYPE_SCHEDULING) == 0)
+    mask |= GST_PAD_PROBE_TYPE_SCHEDULING;
+
+  /* store our flags and other fields */
+  hook->flags |= (mask << G_HOOK_FLAG_USER_SHIFT);
+  hook->func = callback;
+  hook->data = user_data;
+  hook->destroy = destroy_data;
+  PROBE_COOKIE (hook) = (pad->priv->probe_cookie - 1);
+
+  /* add the probe */
+  g_hook_prepend (&pad->probes, hook);
+  pad->num_probes++;
+  /* incremenent cookie so that the new hook get's called */
+  pad->priv->probe_list_cookie++;
+
+  /* get the id of the hook, we return this and it can be used to remove the
+   * probe later */
+  res = hook->hook_id;
+
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got probe id %lu", res);
+
+  if (mask & GST_PAD_PROBE_TYPE_BLOCKING) {
+    /* we have a block probe */
+    pad->num_blocked++;
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_BLOCKED);
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "added blocking probe, "
+        "now %d blocking probes", pad->num_blocked);
+  }
+
+  /* call the callback if we need to be called for idle callbacks */
+  if ((mask & GST_PAD_PROBE_TYPE_IDLE) && (callback != NULL)) {
+    if (pad->priv->using > 0) {
+      /* the pad is in use, we can't signal the idle callback yet. Since we set the
+       * flag above, the last thread to leave the push will do the callback. New
+       * threads going into the push will block. */
+      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+          "pad is in use, delay idle callback");
+      GST_OBJECT_UNLOCK (pad);
+    } else {
+      GstPadProbeInfo info = { GST_PAD_PROBE_TYPE_IDLE, res, };
 
-    if (pad->block_destroy_data && pad->block_data)
-      pad->block_destroy_data (pad->block_data);
+      /* the pad is idle now, we can signal the idle callback now */
+      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+          "pad is idle, trigger idle callback");
+      GST_OBJECT_UNLOCK (pad);
 
-    pad->block_callback = callback;
-    pad->block_data = user_data;
-    pad->block_destroy_data = destroy_data;
-    pad->abidata.ABI.block_callback_called = FALSE;
-    if (!callback) {
-      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "waiting for block");
-      GST_PAD_BLOCK_WAIT (pad);
-      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocked");
+      callback (pad, &info, user_data);
     }
   } else {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocking pad");
-
-    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_BLOCKED);
-
-    if (pad->block_destroy_data && pad->block_data)
-      pad->block_destroy_data (pad->block_data);
-
-    pad->block_callback = callback;
-    pad->block_data = user_data;
-    pad->block_destroy_data = destroy_data;
-    pad->abidata.ABI.block_callback_called = FALSE;
-
-    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");
-    }
+    GST_OBJECT_UNLOCK (pad);
   }
-  GST_OBJECT_UNLOCK (pad);
-
-  return TRUE;
-
-/* Errors */
+  return res;
+}
 
-had_right_state:
-  {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pad was in right state (%d)", was_blocked);
-    GST_OBJECT_UNLOCK (pad);
+static void
+cleanup_hook (GstPad * pad, GHook * hook)
+{
+  GstPadProbeType type;
 
-    return FALSE;
-  }
-wrong_direction:
-  {
-    GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "pad block on the wrong pad, "
-        "block src pads in push mode and sink pads in pull mode.");
-    GST_OBJECT_UNLOCK (pad);
+  type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT;
 
-    return FALSE;
+  if (type & GST_PAD_PROBE_TYPE_BLOCKING) {
+    /* unblock when we remove the last blocking probe */
+    pad->num_blocked--;
+    GST_DEBUG_OBJECT (pad, "remove blocking probe, now %d left",
+        pad->num_blocked);
+    if (pad->num_blocked == 0) {
+      GST_DEBUG_OBJECT (pad, "last blocking probe removed, unblocking");
+      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKED);
+      GST_PAD_BLOCK_BROADCAST (pad);
+    }
   }
+  g_hook_destroy_link (&pad->probes, hook);
+  pad->num_probes--;
 }
 
 /**
- * gst_pad_set_blocked_async:
- * @pad: the #GstPad to block or unblock
- * @blocked: boolean indicating whether the pad should be blocked or unblocked
- * @callback: #GstPadBlockCallback that will be called when the
- *            operation succeeds
- * @user_data: (closure): user data passed to the callback
- *
- * Blocks or unblocks the dataflow on a pad. The provided callback
- * is called when the operation succeeds; this happens right before the next
- * attempt at pushing a buffer on the pad.
+ * gst_pad_remove_probe:
+ * @pad: the #GstPad with the probe
+ * @id: the probe id to remove
  *
- * This can take a while as the pad can only become blocked when real dataflow
- * is happening.
- * When the pipeline is stalled, for example in PAUSED, this can
- * take an indeterminate amount of time.
- * 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.
- *
- * <note>
- *  Pad block handlers are only called for source pads in push mode
- *  and sink pads in pull mode.
- * </note>
- *
- * 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.
+ * Remove the probe with @id from @pad.
  *
  * MT safe.
  */
-gboolean
-gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
-    GstPadBlockCallback callback, gpointer user_data)
+void
+gst_pad_remove_probe (GstPad * pad, gulong id)
 {
-  return gst_pad_set_blocked_async_full (pad, blocked,
-      callback, user_data, NULL);
-}
+  GHook *hook;
 
-/**
- * gst_pad_set_blocked:
- * @pad: the #GstPad to block or unblock
- * @blocked: boolean indicating we should block or unblock
- *
- * Blocks or unblocks the dataflow on a pad. This function is
- * a shortcut for gst_pad_set_blocked_async() with a NULL
- * callback.
- *
- * <note>
- *  Pad blocks are only possible for source pads in push mode
- *  and sink pads in pull mode.
- * </note>
- *
- * 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.
- */
-gboolean
-gst_pad_set_blocked (GstPad * pad, gboolean blocked)
-{
-  return gst_pad_set_blocked_async (pad, blocked, NULL, NULL);
+  g_return_if_fail (GST_IS_PAD (pad));
+
+  GST_OBJECT_LOCK (pad);
+
+  hook = g_hook_get (&pad->probes, id);
+  if (hook == NULL)
+    goto not_found;
+
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "removing hook %ld",
+      hook->hook_id);
+  cleanup_hook (pad, hook);
+  GST_OBJECT_UNLOCK (pad);
+
+  return;
+
+not_found:
+  {
+    GST_OBJECT_UNLOCK (pad);
+    g_warning ("%s: pad `%p' has no probe with id `%lu'", G_STRLOC, pad, id);
+    return;
+  }
 }
 
 /**
@@ -1215,7 +1297,7 @@ gst_pad_is_blocked (GstPad * pad)
   g_return_val_if_fail (GST_IS_PAD (pad), result);
 
   GST_OBJECT_LOCK (pad);
-  result = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKED);
+  result = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_BLOCKED);
   GST_OBJECT_UNLOCK (pad);
 
   return result;
@@ -1243,17 +1325,69 @@ gst_pad_is_blocking (GstPad * pad)
 
   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);
+  result = GST_PAD_IS_BLOCKING (pad) && !GST_PAD_IS_FLUSHING (pad);
   GST_OBJECT_UNLOCK (pad);
 
   return result;
 }
 
 /**
+ * gst_pad_check_reconfigure:
+ * @pad: the #GstPad to check
+ *
+ * Check and clear the #GST_PAD_FLAG_NEED_RECONFIGURE flag on @pad and return %TRUE
+ * if the flag was set.
+ *
+ * Returns: %TRUE is the GST_PAD_FLAG_NEED_RECONFIGURE flag was set on @pad.
+ */
+gboolean
+gst_pad_check_reconfigure (GstPad * pad)
+{
+  gboolean reconfigure;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+  GST_OBJECT_LOCK (pad);
+  reconfigure = GST_PAD_NEEDS_RECONFIGURE (pad);
+  if (reconfigure)
+    GST_DEBUG_OBJECT (pad, "remove RECONFIGURE flag");
+  GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+  GST_OBJECT_UNLOCK (pad);
+
+  return reconfigure;
+}
+
+/**
+ * gst_pad_mark_reconfigure:
+ * @pad: the #GstPad to mark
+ *
+ * Mark a pad for needing reconfiguration. The next call to
+ * gst_pad_check_reconfigure() will return %TRUE after this call.
+ */
+void
+gst_pad_mark_reconfigure (GstPad * pad)
+{
+  g_return_if_fail (GST_IS_PAD (pad));
+
+  GST_OBJECT_LOCK (pad);
+  GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+  GST_OBJECT_UNLOCK (pad);
+}
+
+/**
  * gst_pad_set_activate_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadActivateFunction to set.
+ *
+ * Calls gst_pad_set_activate_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
+ * gst_pad_set_activate_function_full:
  * @pad: a #GstPad.
  * @activate: the #GstPadActivateFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @activate will not be used anymore.
  *
  * Sets the given activate function for @pad. The activate function will
  * dispatch to gst_pad_activate_push() or gst_pad_activate_pull() to perform
@@ -1262,77 +1396,105 @@ gst_pad_is_blocking (GstPad * pad)
  * Call this function if your sink pad can start a pull-based task.
  */
 void
-gst_pad_set_activate_function (GstPad * pad, GstPadActivateFunction activate)
+gst_pad_set_activate_function_full (GstPad * pad,
+    GstPadActivateFunction activate, gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->activatenotify)
+    pad->activatenotify (pad->activatedata);
   GST_PAD_ACTIVATEFUNC (pad) = activate;
+  pad->activatedata = user_data;
+  pad->activatenotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatefunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (activate));
 }
 
 /**
- * gst_pad_set_activatepull_function:
- * @pad: a #GstPad.
- * @activatepull: the #GstPadActivateModeFunction to set.
+ * gst_pad_set_activatemode_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadActivateModeFunction to set.
  *
- * Sets the given activate_pull function for the pad. An activate_pull function
- * prepares the element and any upstream connections for pulling. See XXX
- * part-activation.txt for details.
+ * Calls gst_pad_set_activatemode_function_full() with NULL for the user_data and
+ * notify.
  */
-void
-gst_pad_set_activatepull_function (GstPad * pad,
-    GstPadActivateModeFunction activatepull)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  GST_PAD_ACTIVATEPULLFUNC (pad) = activatepull;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepullfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (activatepull));
-}
-
 /**
- * gst_pad_set_activatepush_function:
+ * gst_pad_set_activatemode_function_full:
  * @pad: a #GstPad.
- * @activatepush: the #GstPadActivateModeFunction to set.
+ * @activatemode: the #GstPadActivateModeFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @activatemode will not be used anymore.
  *
- * Sets the given activate_push function for the pad. An activate_push function
- * prepares the element for pushing. See XXX part-activation.txt for details.
+ * Sets the given activate_mode function for the pad. An activate_mode function
+ * prepares the element for data passing.
  */
 void
-gst_pad_set_activatepush_function (GstPad * pad,
-    GstPadActivateModeFunction activatepush)
+gst_pad_set_activatemode_function_full (GstPad * pad,
+    GstPadActivateModeFunction activatemode, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
-  GST_PAD_ACTIVATEPUSHFUNC (pad) = activatepush;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepushfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (activatepush));
+  if (pad->activatemodenotify)
+    pad->activatemodenotify (pad->activatemodedata);
+  GST_PAD_ACTIVATEMODEFUNC (pad) = activatemode;
+  pad->activatemodedata = user_data;
+  pad->activatemodenotify = notify;
+
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatemodefunc set to %s",
+      GST_DEBUG_FUNCPTR_NAME (activatemode));
 }
 
 /**
  * gst_pad_set_chain_function:
+ * @p: a sink #GstPad.
+ * @f: the #GstPadChainFunction to set.
+ *
+ * Calls gst_pad_set_chain_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
+ * gst_pad_set_chain_function_full:
  * @pad: a sink #GstPad.
  * @chain: the #GstPadChainFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @chain will not be used anymore.
  *
  * Sets the given chain function for the pad. The chain function is called to
  * process a #GstBuffer input buffer. see #GstPadChainFunction for more details.
  */
 void
-gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
+gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SINK (pad));
 
+  if (pad->chainnotify)
+    pad->chainnotify (pad->chaindata);
   GST_PAD_CHAINFUNC (pad) = chain;
+  pad->chaindata = user_data;
+  pad->chainnotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainfunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (chain));
 }
 
 /**
  * gst_pad_set_chain_list_function:
+ * @p: a sink #GstPad.
+ * @f: the #GstPadChainListFunction to set.
+ *
+ * Calls gst_pad_set_chain_list_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
+ * gst_pad_set_chain_list_function_full:
  * @pad: a sink #GstPad.
  * @chainlist: the #GstPadChainListFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @chainlist will not be used anymore.
  *
  * Sets the given chain list function for the pad. The chainlist function is
  * called to process a #GstBufferList input buffer list. See
@@ -1341,72 +1503,87 @@ gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
  * Since: 0.10.24
  */
 void
-gst_pad_set_chain_list_function (GstPad * pad,
-    GstPadChainListFunction chainlist)
+gst_pad_set_chain_list_function_full (GstPad * pad,
+    GstPadChainListFunction chainlist, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SINK (pad));
 
+  if (pad->chainlistnotify)
+    pad->chainlistnotify (pad->chainlistdata);
   GST_PAD_CHAINLISTFUNC (pad) = chainlist;
+  pad->chainlistdata = user_data;
+  pad->chainlistnotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainlistfunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (chainlist));
 }
 
 /**
  * gst_pad_set_getrange_function:
+ * @p: a source #GstPad.
+ * @f: the #GstPadGetRangeFunction to set.
+ *
+ * Calls gst_pad_set_getrange_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
+ * gst_pad_set_getrange_function_full:
  * @pad: a source #GstPad.
  * @get: the #GstPadGetRangeFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @get will not be used anymore.
  *
  * 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)
+gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SRC (pad));
 
+  if (pad->getrangenotify)
+    pad->getrangenotify (pad->getrangedata);
   GST_PAD_GETRANGEFUNC (pad) = get;
+  pad->getrangedata = user_data;
+  pad->getrangenotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getrangefunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (get));
 }
 
 /**
- * gst_pad_set_checkgetrange_function:
- * @pad: a source #GstPad.
- * @check: the #GstPadCheckGetRangeFunction to set.
+ * gst_pad_set_event_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadEventFunction 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.
+ * Calls gst_pad_set_event_function_full() with NULL for the user_data and
+ * notify.
  */
-void
-gst_pad_set_checkgetrange_function (GstPad * pad,
-    GstPadCheckGetRangeFunction check)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (GST_PAD_IS_SRC (pad));
-
-  GST_PAD_CHECKGETRANGEFUNC (pad) = check;
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "checkgetrangefunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (check));
-}
-
 /**
- * gst_pad_set_event_function:
+ * gst_pad_set_event_function_full:
  * @pad: a #GstPad of either direction.
  * @event: the #GstPadEventFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @event will not be used anymore.
  *
  * Sets the given event handler for the pad.
  */
 void
-gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event)
+gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->eventnotify)
+    pad->eventnotify (pad->eventdata);
   GST_PAD_EVENTFUNC (pad) = event;
+  pad->eventdata = user_data;
+  pad->eventnotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "eventfunc for set to %s",
       GST_DEBUG_FUNCPTR_NAME (event));
@@ -1414,151 +1591,87 @@ gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event)
 
 /**
  * gst_pad_set_query_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadQueryFunction to set.
+ *
+ * Calls gst_pad_set_query_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
+ * gst_pad_set_query_function_full:
  * @pad: a #GstPad of either direction.
  * @query: the #GstPadQueryFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @query will not be used anymore.
  *
  * Set the given query function for the pad.
  */
 void
-gst_pad_set_query_function (GstPad * pad, GstPadQueryFunction query)
+gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->querynotify)
+    pad->querynotify (pad->querydata);
   GST_PAD_QUERYFUNC (pad) = query;
+  pad->querydata = user_data;
+  pad->querynotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "queryfunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (query));
 }
 
 /**
- * gst_pad_set_query_type_function:
- * @pad: a #GstPad of either direction.
- * @type_func: the #GstPadQueryTypeFunction to set.
- *
- * Set the given query type function for the pad.
- */
-void
-gst_pad_set_query_type_function (GstPad * pad,
-    GstPadQueryTypeFunction type_func)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  GST_PAD_QUERYTYPEFUNC (pad) = type_func;
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "querytypefunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (type_func));
-}
-
-/**
- * gst_pad_get_query_types:
- * @pad: a #GstPad.
- *
- * Get an array of supported queries that can be performed
- * on this pad.
- *
- * Returns: (transfer none) (array zero-terminated=1): a zero-terminated array
- *     of #GstQueryType.
- */
-const GstQueryType *
-gst_pad_get_query_types (GstPad * pad)
-{
-  GstPadQueryTypeFunction func;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  if (G_UNLIKELY ((func = GST_PAD_QUERYTYPEFUNC (pad)) == NULL))
-    goto no_func;
-
-  return func (pad);
-
-no_func:
-  {
-    return NULL;
-  }
-}
-
-static gboolean
-gst_pad_get_query_types_dispatcher (GstPad * pad, const GstQueryType ** data)
-{
-  *data = gst_pad_get_query_types (pad);
-
-  return TRUE;
-}
-
-/**
- * gst_pad_get_query_types_default:
- * @pad: a #GstPad.
- *
- * Invoke the default dispatcher for the query types on
- * the pad.
+ * gst_pad_set_iterate_internal_links_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadIterIntLinkFunction to set.
  *
- * Returns: (transfer none) (array zero-terminated=1): a zero-terminated array
- *     of #GstQueryType, or NULL if none of the internally-linked pads has a
- *     query types function.
+ * Calls gst_pad_set_iterate_internal_links_function_full() with NULL
+ * for the user_data and notify.
  */
-const GstQueryType *
-gst_pad_get_query_types_default (GstPad * pad)
-{
-  GstQueryType *result = NULL;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  gst_pad_dispatcher (pad, (GstPadDispatcherFunction)
-      gst_pad_get_query_types_dispatcher, &result);
-
-  return result;
-}
-
 /**
- * gst_pad_set_iterate_internal_links_function:
+ * gst_pad_set_iterate_internal_links_function_full:
  * @pad: a #GstPad of either direction.
  * @iterintlink: the #GstPadIterIntLinkFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @iterintlink will not be used anymore.
  *
  * Sets the given internal link iterator function for the pad.
  *
  * Since: 0.10.21
  */
 void
-gst_pad_set_iterate_internal_links_function (GstPad * pad,
-    GstPadIterIntLinkFunction iterintlink)
+gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
+    GstPadIterIntLinkFunction iterintlink, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->iterintlinknotify)
+    pad->iterintlinknotify (pad->iterintlinkdata);
   GST_PAD_ITERINTLINKFUNC (pad) = iterintlink;
+  pad->iterintlinkdata = user_data;
+  pad->iterintlinknotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link iterator set to %s",
       GST_DEBUG_FUNCPTR_NAME (iterintlink));
 }
 
 /**
- * gst_pad_set_internal_link_function:
- * @pad: a #GstPad of either direction.
- * @intlink: the #GstPadIntLinkFunction to set.
- *
- * Sets the given internal link function for the pad.
+ * gst_pad_set_link_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadLinkFunction to set.
  *
- * Deprecated: Use the thread-safe gst_pad_set_iterate_internal_links_function()
+ * Calls gst_pad_set_link_function_full() with NULL
+ * for the user_data and notify.
  */
-#ifndef GST_REMOVE_DEPRECATED
-#ifdef GST_DISABLE_DEPRECATED
-void
-gst_pad_set_internal_link_function (GstPad * pad,
-    GstPadIntLinkFunction intlink);
-#endif
-void
-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_OBJECT (GST_CAT_PADS, pad, "internal link set to %s",
-      GST_DEBUG_FUNCPTR_NAME (intlink));
-}
-#endif /* GST_REMOVE_DEPRECATED */
-
 /**
- * gst_pad_set_link_function:
+ * gst_pad_set_link_function_full:
  * @pad: a #GstPad.
  * @link: the #GstPadLinkFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @link will not be used anymore.
  *
  * Sets the given link function for the pad. It will be called when
  * the pad is linked with another pad.
@@ -1573,169 +1686,73 @@ gst_pad_set_internal_link_function (GstPad * pad, GstPadIntLinkFunction intlink)
  * of the peer sink pad, if present.
  */
 void
-gst_pad_set_link_function (GstPad * pad, GstPadLinkFunction link)
+gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->linknotify)
+    pad->linknotify (pad->linkdata);
   GST_PAD_LINKFUNC (pad) = link;
+  pad->linkdata = user_data;
+  pad->linknotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "linkfunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (link));
 }
 
 /**
  * gst_pad_set_unlink_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadUnlinkFunction to set.
+ *
+ * Calls gst_pad_set_unlink_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
+ * gst_pad_set_unlink_function_full:
  * @pad: a #GstPad.
  * @unlink: the #GstPadUnlinkFunction to set.
+ * @user_data: user_data passed to @notify
+ * @notify: notify called when @unlink will not be used anymore.
  *
  * Sets the given unlink function for the pad. It will be called
  * when the pad is unlinked.
  */
 void
-gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink)
+gst_pad_set_unlink_function_full (GstPad * pad, GstPadUnlinkFunction unlink,
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
+  if (pad->unlinknotify)
+    pad->unlinknotify (pad->unlinkdata);
   GST_PAD_UNLINKFUNC (pad) = unlink;
+  pad->unlinkdata = user_data;
+  pad->unlinknotify = notify;
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "unlinkfunc set to %s",
       GST_DEBUG_FUNCPTR_NAME (unlink));
 }
 
 /**
- * gst_pad_set_getcaps_function:
- * @pad: a #GstPad.
- * @getcaps: the #GstPadGetCapsFunction to set.
- *
- * Sets the given getcaps function for the pad. @getcaps should return the
- * allowable caps for a pad in the context of the element's state, its link to
- * other elements, and the devices or files it has opened. These caps must be a
- * subset of the pad template caps. In the NULL state with no links, @getcaps
- * should ideally return the same caps as the pad template. In rare
- * circumstances, an object property can affect the caps returned by @getcaps,
- * but this is discouraged.
- *
- * You do not need to call this function if @pad's allowed caps are always the
- * same as the pad template caps. This can only be true if the padtemplate
- * has fixed simple caps.
- *
- * For most filters, the caps returned by @getcaps is directly affected by the
- * allowed caps on other pads. For demuxers and decoders, the caps returned by
- * the srcpad's getcaps function is directly related to the stream data. Again,
- * @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.
+ * gst_pad_unlink:
+ * @srcpad: the source #GstPad to unlink.
+ * @sinkpad: the sink #GstPad to unlink.
+ *
+ * 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.
+ *
+ * MT safe.
  */
-void
-gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps)
+gboolean
+gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
 {
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  GST_PAD_GETCAPSFUNC (pad) = getcaps;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getcapsfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (getcaps));
-}
-
-/**
- * gst_pad_set_acceptcaps_function:
- * @pad: a #GstPad.
- * @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. 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,
-    GstPadAcceptCapsFunction acceptcaps)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  GST_PAD_ACCEPTCAPSFUNC (pad) = acceptcaps;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "acceptcapsfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (acceptcaps));
-}
-
-/**
- * gst_pad_set_fixatecaps_function:
- * @pad: a #GstPad.
- * @fixatecaps: the #GstPadFixateCapsFunction to set.
- *
- * Sets the given fixatecaps function for the pad.  The fixatecaps function
- * will be called whenever the default values for a GstCaps needs to be
- * filled in.
- */
-void
-gst_pad_set_fixatecaps_function (GstPad * pad,
-    GstPadFixateCapsFunction fixatecaps)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  GST_PAD_FIXATECAPSFUNC (pad) = fixatecaps;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "fixatecapsfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (fixatecaps));
-}
-
-/**
- * gst_pad_set_setcaps_function:
- * @pad: a #GstPad.
- * @setcaps: the #GstPadSetCapsFunction to set.
- *
- * 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 its internal
- * structures to process the new media type. If this new type is not
- * acceptable, the setcaps function should return FALSE.
- */
-void
-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_OBJECT (GST_CAT_PADS, pad, "setcapsfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (setcaps));
-}
-
-/**
- * gst_pad_set_bufferalloc_function:
- * @pad: a sink #GstPad.
- * @bufalloc: the #GstPadBufferAllocFunction to set.
- *
- * Sets the given bufferalloc function for the pad. Note that the
- * bufferalloc function can only be set on sinkpads.
- */
-void
-gst_pad_set_bufferalloc_function (GstPad * pad,
-    GstPadBufferAllocFunction bufalloc)
-{
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (GST_PAD_IS_SINK (pad));
-
-  GST_PAD_BUFFERALLOCFUNC (pad) = bufalloc;
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "bufferallocfunc set to %s",
-      GST_DEBUG_FUNCPTR_NAME (bufalloc));
-}
-
-/**
- * gst_pad_unlink:
- * @srcpad: the source #GstPad to unlink.
- * @sinkpad: the sink #GstPad to unlink.
- *
- * 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.
- *
- * MT safe.
- */
-gboolean
-gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
-{
-  gboolean result = FALSE;
-  GstElement *parent = NULL;
+  gboolean result = FALSE;
+  GstElement *parent = NULL;
 
   g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
   g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), FALSE);
@@ -1761,7 +1778,6 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
   }
 
   GST_OBJECT_LOCK (srcpad);
-
   GST_OBJECT_LOCK (sinkpad);
 
   if (G_UNLIKELY (GST_PAD_PEER (srcpad) != sinkpad))
@@ -1774,8 +1790,6 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
     GST_PAD_UNLINKFUNC (sinkpad) (sinkpad);
   }
 
-  _priv_gst_pad_invalidate_cache (srcpad);
-
   /* first clear peers */
   GST_PAD_PEER (srcpad) = NULL;
   GST_PAD_PEER (sinkpad) = NULL;
@@ -1856,8 +1870,14 @@ gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink,
 
   /* Doing the expensive caps checking takes priority over only checking the template caps */
   if (flags & GST_PAD_LINK_CHECK_CAPS) {
-    srccaps = gst_pad_get_caps_unlocked (src);
-    sinkcaps = gst_pad_get_caps_unlocked (sink);
+    GST_OBJECT_UNLOCK (sink);
+    GST_OBJECT_UNLOCK (src);
+
+    srccaps = gst_pad_query_caps (src, NULL);
+    sinkcaps = gst_pad_query_caps (sink, NULL);
+
+    GST_OBJECT_LOCK (src);
+    GST_OBJECT_LOCK (sink);
   } else {
     /* If one of the two pads doesn't have a template, consider the intersection
      * as valid.*/
@@ -2097,6 +2117,7 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
 {
   GstPadLinkReturn result;
   GstElement *parent;
+  GstPadLinkFunction srcfunc, sinkfunc;
 
   g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED);
   g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), GST_PAD_LINK_WRONG_DIRECTION);
@@ -2105,8 +2126,8 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
       GST_PAD_LINK_WRONG_DIRECTION);
 
   /* Notify the parent early. See gst_pad_unlink for details. */
-  if ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad)))) {
-    if (GST_IS_ELEMENT (parent)) {
+  if (G_LIKELY ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad))))) {
+    if (G_LIKELY (GST_IS_ELEMENT (parent))) {
       gst_element_post_message (parent,
           gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
               GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, TRUE));
@@ -2119,44 +2140,81 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
   /* prepare will also lock the two pads */
   result = gst_pad_link_prepare (srcpad, sinkpad, flags);
 
-  if (result != GST_PAD_LINK_OK)
+  if (G_UNLIKELY (result != GST_PAD_LINK_OK))
     goto done;
 
   /* must set peers before calling the link function */
   GST_PAD_PEER (srcpad) = sinkpad;
   GST_PAD_PEER (sinkpad) = srcpad;
 
+  /* check events, when something is different, mark pending */
+  schedule_events (srcpad, sinkpad);
+
+  /* get the link functions */
+  srcfunc = GST_PAD_LINKFUNC (srcpad);
+  sinkfunc = GST_PAD_LINKFUNC (sinkpad);
+
+  if (G_UNLIKELY (srcfunc || sinkfunc)) {
+    /* custom link functions, execute them */
+    GST_OBJECT_UNLOCK (sinkpad);
+    GST_OBJECT_UNLOCK (srcpad);
+
+    if (srcfunc) {
+      /* this one will call the peer link function */
+      result = srcfunc (srcpad, sinkpad);
+    } else if (sinkfunc) {
+      /* if no source link function, we need to call the sink link
+       * function ourselves. */
+      result = sinkfunc (sinkpad, srcpad);
+    }
+
+    GST_OBJECT_LOCK (srcpad);
+    GST_OBJECT_LOCK (sinkpad);
+
+    /* we released the lock, check if the same pads are linked still */
+    if (GST_PAD_PEER (srcpad) != sinkpad || GST_PAD_PEER (sinkpad) != srcpad)
+      goto concurrent_link;
+
+    if (G_UNLIKELY (result != GST_PAD_LINK_OK))
+      goto link_failed;
+  }
   GST_OBJECT_UNLOCK (sinkpad);
   GST_OBJECT_UNLOCK (srcpad);
 
-  /* FIXME released the locks here, concurrent thread might link
-   * something else. */
-  if (GST_PAD_LINKFUNC (srcpad)) {
-    /* this one will call the peer link function */
-    result = GST_PAD_LINKFUNC (srcpad) (srcpad, sinkpad);
-  } else if (GST_PAD_LINKFUNC (sinkpad)) {
-    /* if no source link function, we need to call the sink link
-     * function ourselves. */
-    result = GST_PAD_LINKFUNC (sinkpad) (sinkpad, srcpad);
-  } else {
-    result = GST_PAD_LINK_OK;
+  /* fire off a signal to each of the pads telling them
+   * that they've been linked */
+  g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
+  g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);
+
+  GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
+      GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+  gst_pad_send_event (srcpad, gst_event_new_reconfigure ());
+
+done:
+  if (G_LIKELY (parent)) {
+    gst_element_post_message (parent,
+        gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
+            GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
+    gst_object_unref (parent);
   }
 
-  GST_OBJECT_LOCK (srcpad);
-  GST_OBJECT_LOCK (sinkpad);
+  return result;
 
-  if (result == GST_PAD_LINK_OK) {
+  /* ERRORS */
+concurrent_link:
+  {
+    GST_CAT_INFO (GST_CAT_PADS, "concurrent link between %s:%s and %s:%s",
+        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
 
-    /* fire off a signal to each of the pads telling them
-     * that they've been linked */
-    g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
-    g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);
-
-    GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
-        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
-  } else {
+    /* The other link operation succeeded first */
+    result = GST_PAD_LINK_WAS_LINKED;
+    goto done;
+  }
+link_failed:
+  {
     GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed",
         GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
 
@@ -2165,17 +2223,9 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
 
     GST_OBJECT_UNLOCK (sinkpad);
     GST_OBJECT_UNLOCK (srcpad);
-  }
 
-done:
-  if (parent) {
-    gst_element_post_message (parent,
-        gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
-            GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
-    gst_object_unref (parent);
+    goto done;
   }
-
-  return result;
 }
 
 /**
@@ -2218,1517 +2268,1047 @@ gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ)
  *
  * Gets the template for @pad.
  *
- * Returns: (transfer none): the #GstPadTemplate from which this pad was
- *     instantiated, or %NULL if this pad has no template.
- *
- * FIXME: currently returns an unrefcounted padtemplate.
+ * Returns: (transfer full): the #GstPadTemplate from which this pad was
+ *     instantiated, or %NULL if this pad has no template. Unref after
+ *     usage.
  */
 GstPadTemplate *
 gst_pad_get_pad_template (GstPad * pad)
 {
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  return GST_PAD_PAD_TEMPLATE (pad);
-}
-
-
-/* should be called with the pad LOCK held */
-/* refs the caps, so caller is responsible for getting it unreffed */
-static GstCaps *
-gst_pad_get_caps_unlocked (GstPad * pad)
-{
-  GstCaps *result = NULL;
   GstPadTemplate *templ;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
-
-  if (GST_PAD_GETCAPSFUNC (pad)) {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "dispatching to pad getcaps function");
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-    GST_OBJECT_FLAG_SET (pad, GST_PAD_IN_GETCAPS);
-    GST_OBJECT_UNLOCK (pad);
-    result = GST_PAD_GETCAPSFUNC (pad) (pad);
-    GST_OBJECT_LOCK (pad);
-    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_GETCAPS);
+  templ = GST_PAD_PAD_TEMPLATE (pad);
 
-    if (result == NULL) {
-      g_critical ("pad %s:%s returned NULL caps from getcaps function",
-          GST_DEBUG_PAD_NAME (pad));
-    } else {
-      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)) {
-        const GstCaps *templ_caps =
-            GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad));
-        if (!gst_caps_is_subset (result, templ_caps)) {
-          GstCaps *temp;
-
-          GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, pad,
-              "pad returned caps %" GST_PTR_FORMAT
-              " which are not a real subset of its template caps %"
-              GST_PTR_FORMAT, result, templ_caps);
-          g_warning
-              ("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;
-        }
-      }
-#endif
-      goto done;
-    }
-  }
-  if ((templ = GST_PAD_PAD_TEMPLATE (pad))) {
-    result = GST_PAD_TEMPLATE_CAPS (templ);
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, result,
-        result);
+  return (templ ? gst_object_ref (templ) : NULL);
+}
 
-    result = gst_caps_ref (result);
-    goto done;
-  }
-  if ((result = GST_PAD_CAPS (pad))) {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "using pad caps %p %" GST_PTR_FORMAT, result, result);
+/**
+ * gst_pad_has_current_caps:
+ * @pad: a  #GstPad to check
+ *
+ * Check if @pad has caps set on it with a #GST_EVENT_CAPS event.
+ *
+ * Returns: TRUE when @pad has caps associated with it.
+ */
+gboolean
+gst_pad_has_current_caps (GstPad * pad)
+{
+  gboolean result;
+  GstCaps *caps;
 
-    result = gst_caps_ref (result);
-    goto done;
-  }
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
 
-  /* this almost never happens */
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
-  result = gst_caps_new_empty ();
+  GST_OBJECT_LOCK (pad);
+  caps = get_pad_caps (pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+      "check current pad caps %" GST_PTR_FORMAT, caps);
+  result = (caps != NULL);
+  GST_OBJECT_UNLOCK (pad);
 
-done:
   return result;
 }
 
-/* FIXME-0.11: what about making this the default and using
- * gst_caps_make_writable() explicitly where needed
- */
 /**
- * gst_pad_get_caps_reffed:
- * @pad: a  #GstPad to get the capabilities of.
- *
- * Gets the capabilities this pad can produce or consume. Preferred function if
- * one only wants to read or intersect the caps.
+ * gst_pad_get_current_caps:
+ * @pad: a  #GstPad to get the current capabilities of.
  *
- * Returns: (transfer full): the caps of the pad with incremented ref-count.
+ * Gets the capabilities currently configured on @pad with the last
+ * #GST_EVENT_CAPS event.
  *
- * Since: 0.10.26
+ * Returns: the current caps of the pad with incremented ref-count.
  */
 GstCaps *
-gst_pad_get_caps_reffed (GstPad * pad)
+gst_pad_get_current_caps (GstPad * pad)
 {
-  GstCaps *result = NULL;
+  GstCaps *result;
 
   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
   GST_OBJECT_LOCK (pad);
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
-
-  result = gst_pad_get_caps_unlocked (pad);
-
+  if ((result = get_pad_caps (pad)))
+    gst_caps_ref (result);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+      "get current pad caps %" GST_PTR_FORMAT, result);
   GST_OBJECT_UNLOCK (pad);
 
   return result;
 }
 
 /**
- * gst_pad_get_caps:
- * @pad: a  #GstPad to get the capabilities of.
+ * gst_pad_set_caps:
+ * @pad: a  #GstPad to set the capabilities of.
+ * @caps: (transfer none): a #GstCaps to set.
  *
- * Gets the capabilities this pad can produce or consume.
- * 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.
+ * Sets the capabilities of this pad. The caps must be fixed. Any previous
+ * caps on the pad will be unreffed. This function refs the caps so you should
+ * unref if as soon as you don't need it anymore.
+ * It is possible to set NULL caps, which will make the pad unnegotiated
+ * again.
  *
- * Returns: (transfer full): a newly allocated copy of the #GstCaps of this pad
+ * Returns: TRUE if the caps could be set. FALSE if the caps were not fixed
+ * or bad parameters were provided to this function.
  *
  * MT safe.
  */
-GstCaps *
-gst_pad_get_caps (GstPad * pad)
+gboolean
+gst_pad_set_caps (GstPad * pad, GstCaps * caps)
 {
-  GstCaps *result = gst_pad_get_caps_reffed (pad);
+  GstEvent *event;
+  gboolean res = TRUE;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (caps != NULL && gst_caps_is_fixed (caps), FALSE);
 
-  /* be sure that we have a copy */
-  if (G_LIKELY (result))
-    result = gst_caps_make_writable (result);
+  event = gst_event_new_caps (caps);
 
-  return result;
+  if (GST_PAD_IS_SRC (pad))
+    res = gst_pad_push_event (pad, event);
+  else
+    res = gst_pad_send_event (pad, event);
+
+  return res;
 }
 
-/* FIXME-0.11: what about making this the default and using
- * gst_caps_make_writable() explicitly where needed
- */
 /**
- * gst_pad_peer_get_caps_reffed:
- * @pad: a  #GstPad to get the capabilities of.
- *
- * Gets the capabilities of the peer connected to this pad. Preferred function
- * if one only wants to read or intersect the caps.
+ * gst_pad_get_pad_template_caps:
+ * @pad: a #GstPad to get the template capabilities from.
  *
- * Returns: (transfer full): the caps of the pad with incremented ref-count
+ * Gets the capabilities for @pad's template.
  *
- * Since: 0.10.26
+ * Returns: (transfer full): the #GstCaps of this pad template.
+ * Unref after usage.
  */
 GstCaps *
-gst_pad_peer_get_caps_reffed (GstPad * pad)
+gst_pad_get_pad_template_caps (GstPad * pad)
 {
-  GstPad *peerpad;
-  GstCaps *result = NULL;
-
   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-  GST_OBJECT_LOCK (pad);
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get peer caps");
+  if (GST_PAD_PAD_TEMPLATE (pad))
+    return gst_pad_template_get_caps (GST_PAD_PAD_TEMPLATE (pad));
 
-  peerpad = GST_PAD_PEER (pad);
-  if (G_UNLIKELY (peerpad == NULL))
-    goto no_peer;
+  return gst_caps_ref (GST_CAPS_ANY);
+}
 
-  gst_object_ref (peerpad);
-  GST_OBJECT_UNLOCK (pad);
+/**
+ * gst_pad_get_peer:
+ * @pad: a #GstPad to get the peer of.
+ *
+ * Gets the peer of @pad. This function refs the peer pad so
+ * you need to unref it after use.
+ *
+ * Returns: (transfer full): the peer #GstPad. Unref after usage.
+ *
+ * MT safe.
+ */
+GstPad *
+gst_pad_get_peer (GstPad * pad)
+{
+  GstPad *result;
 
-  result = gst_pad_get_caps_reffed (peerpad);
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-  gst_object_unref (peerpad);
+  GST_OBJECT_LOCK (pad);
+  result = GST_PAD_PEER (pad);
+  if (result)
+    gst_object_ref (result);
+  GST_OBJECT_UNLOCK (pad);
 
   return result;
-
-no_peer:
-  {
-    GST_OBJECT_UNLOCK (pad);
-    return NULL;
-  }
 }
 
 /**
- * gst_pad_peer_get_caps:
- * @pad: a  #GstPad to get the peer capabilities of.
+ * gst_pad_get_allowed_caps:
+ * @pad: a #GstPad.
+ *
+ * Gets the capabilities of the allowed media types that can flow through
+ * @pad and its peer.
+ *
+ * The allowed capabilities is calculated as the intersection of the results of
+ * calling gst_pad_query_caps() on @pad and its peer. The caller owns a reference
+ * on the resulting caps.
  *
- * Gets the capabilities of the peer connected to this pad. Similar to
- * gst_pad_get_caps().
+ * Returns: (transfer full): 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.
  *
- * Returns: (transfer full): a newly allocated copy of the #GstCaps of the
- *     peer pad. Use gst_caps_unref() to get rid of it. This function
- *     returns %NULL if there is no peer pad.
+ * MT safe.
  */
 GstCaps *
-gst_pad_peer_get_caps (GstPad * pad)
+gst_pad_get_allowed_caps (GstPad * pad)
 {
-  GstPad *peerpad;
-  GstCaps *result = NULL;
+  GstCaps *mycaps;
+  GstCaps *caps;
+  GstCaps *peercaps;
+  GstPad *peer;
 
   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
   GST_OBJECT_LOCK (pad);
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get peer caps");
-
-  peerpad = GST_PAD_PEER (pad);
-  if (G_UNLIKELY (peerpad == NULL))
+  peer = GST_PAD_PEER (pad);
+  if (G_UNLIKELY (peer == NULL))
     goto no_peer;
 
-  gst_object_ref (peerpad);
-  GST_OBJECT_UNLOCK (pad);
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps");
 
-  result = gst_pad_get_caps (peerpad);
+  gst_object_ref (peer);
+  GST_OBJECT_UNLOCK (pad);
+  mycaps = gst_pad_query_caps (pad, NULL);
 
-  gst_object_unref (peerpad);
+  peercaps = gst_pad_query_caps (peer, NULL);
+  gst_object_unref (peer);
 
-  return result;
+  caps = gst_caps_intersect (mycaps, peercaps);
+  gst_caps_unref (peercaps);
+  gst_caps_unref (mycaps);
+
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT,
+      caps);
+
+  return caps;
 
 no_peer:
   {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
     GST_OBJECT_UNLOCK (pad);
-    return NULL;
-  }
-}
-
-static gboolean
-fixate_value (GValue * dest, const GValue * src)
-{
-  if (G_VALUE_TYPE (src) == GST_TYPE_INT_RANGE) {
-    g_value_init (dest, G_TYPE_INT);
-    g_value_set_int (dest, gst_value_get_int_range_min (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);
-  } else if (G_VALUE_TYPE (src) == GST_TYPE_ARRAY) {
-    gboolean res = FALSE;
-    guint n, len;
-
-    len = gst_value_array_get_size (src);
-    g_value_init (dest, GST_TYPE_ARRAY);
-    for (n = 0; n < len; n++) {
-      GValue kid = { 0 };
-      const GValue *orig_kid = gst_value_array_get_value (src, n);
-
-      if (!fixate_value (&kid, orig_kid))
-        gst_value_init_and_copy (&kid, orig_kid);
-      else
-        res = TRUE;
-      gst_value_array_append_value (dest, &kid);
-      g_value_unset (&kid);
-    }
-
-    if (!res)
-      g_value_unset (dest);
-
-    return res;
-  } else {
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-static gboolean
-gst_pad_default_fixate (GQuark field_id, const GValue * value, gpointer data)
-{
-  GstStructure *s = data;
-  GValue v = { 0 };
 
-  if (fixate_value (&v, value)) {
-    gst_structure_id_set_value (s, field_id, &v);
-    g_value_unset (&v);
+    return NULL;
   }
-
-  return TRUE;
 }
 
 /**
- * gst_pad_fixate_caps:
- * @pad: a  #GstPad to fixate
- * @caps: the  #GstCaps to fixate
+ * gst_pad_iterate_internal_links_default:
+ * @pad: the #GstPad to get the internal links of.
+ * @parent: the parent of @pad or NULL
+ *
+ * Iterate the list of pads to which the given pad is linked to inside of
+ * the parent element.
+ * This is the default handler, and thus returns an iterator of all of the
+ * pads inside the parent element with opposite direction.
+ *
+ * The caller must free this iterator after use with gst_iterator_free().
+ *
+ * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each
+ * returned pad with gst_object_unref().
  *
- * Fixate a caps on the given pad. Modifies the caps in place, so you should
- * make sure that the caps are actually writable (see gst_caps_make_writable()).
+ * Since: 0.10.21
  */
-void
-gst_pad_fixate_caps (GstPad * pad, GstCaps * caps)
+GstIterator *
+gst_pad_iterate_internal_links_default (GstPad * pad, GstObject * parent)
 {
-  GstPadFixateCapsFunction fixatefunc;
-  GstStructure *s;
+  GstIterator *res;
+  GList **padlist;
+  guint32 *cookie;
+  GMutex *lock;
+  gpointer owner;
+  GstElement *eparent;
 
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (caps != NULL);
-  g_return_if_fail (!gst_caps_is_empty (caps));
-  /* FIXME-0.11: do not allow fixating any-caps
-   * g_return_if_fail (!gst_caps_is_any (caps));
-   */
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-  if (gst_caps_is_fixed (caps) || gst_caps_is_any (caps))
-    return;
+  if (parent != NULL && GST_IS_ELEMENT (parent)) {
+    eparent = GST_ELEMENT_CAST (gst_object_ref (parent));
+  } else {
+    GST_OBJECT_LOCK (pad);
+    eparent = GST_PAD_PARENT (pad);
+    if (!eparent || !GST_IS_ELEMENT (eparent))
+      goto no_parent;
 
-  fixatefunc = GST_PAD_FIXATECAPSFUNC (pad);
-  if (fixatefunc) {
-    fixatefunc (pad, caps);
+    gst_object_ref (eparent);
+    GST_OBJECT_UNLOCK (pad);
   }
 
-  /* default fixation */
-  gst_caps_truncate (caps);
-  s = gst_caps_get_structure (caps, 0);
-  gst_structure_foreach (s, gst_pad_default_fixate, s);
-}
-
-/* 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 *allowed;
-  gboolean result = FALSE;
-
-  GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
+  if (pad->direction == GST_PAD_SRC)
+    padlist = &eparent->sinkpads;
+  else
+    padlist = &eparent->srcpads;
 
-  allowed = gst_pad_get_caps_reffed (pad);
-  if (!allowed)
-    goto nothing_allowed;
+  GST_DEBUG_OBJECT (pad, "Making iterator");
 
-  GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed);
+  cookie = &eparent->pads_cookie;
+  owner = eparent;
+  lock = GST_OBJECT_GET_LOCK (eparent);
 
-  result = gst_caps_can_intersect (allowed, caps);
+  res = gst_iterator_new_list (GST_TYPE_PAD,
+      lock, cookie, padlist, (GObject *) owner, NULL);
 
-  gst_caps_unref (allowed);
+  gst_object_unref (owner);
 
-  return result;
+  return res;
 
   /* ERRORS */
-nothing_allowed:
+no_parent:
   {
-    GST_DEBUG_OBJECT (pad, "no caps allowed on the pad");
-    return FALSE;
+    GST_OBJECT_UNLOCK (pad);
+    GST_DEBUG_OBJECT (pad, "no parent element");
+    return NULL;
   }
 }
 
 /**
- * gst_pad_accept_caps:
- * @pad: a #GstPad to check
- * @caps: a #GstCaps to check on the pad
+ * gst_pad_iterate_internal_links:
+ * @pad: the GstPad to get the internal links of.
  *
- * Check if the given pad accepts the caps.
+ * Gets an iterator for the pads to which the given pad is linked to inside
+ * of the parent element.
+ *
+ * Each #GstPad element yielded by the iterator will have its refcount increased,
+ * so unref after use.
+ *
+ * Free-function: gst_iterator_free
+ *
+ * Returns: (transfer full): a new #GstIterator of #GstPad or %NULL when the
+ *     pad does not have an iterator function configured. Use
+ *     gst_iterator_free() after usage.
  *
- * Returns: TRUE if the pad can accept the caps.
+ * Since: 0.10.21
  */
-gboolean
-gst_pad_accept_caps (GstPad * pad, GstCaps * caps)
+GstIterator *
+gst_pad_iterate_internal_links (GstPad * pad)
 {
-  gboolean result;
-  GstPadAcceptCapsFunction acceptfunc;
-  GstCaps *existing = NULL;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  GstIterator *res = NULL;
+  GstObject *parent;
 
-  /* any pad can be unnegotiated */
-  if (caps == NULL)
-    return TRUE;
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
-  /* lock for checking the existing caps */
   GST_OBJECT_LOCK (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;
-  }
-  acceptfunc = GST_PAD_ACCEPTCAPSFUNC (pad);
+  ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
-  if (G_LIKELY (acceptfunc)) {
-    /* we can call the function */
-    result = acceptfunc (pad, caps);
-    GST_DEBUG_OBJECT (pad, "acceptfunc returned %d", result);
-  } else {
-    /* Only null if the element explicitly unset it */
-    result = gst_pad_acceptcaps_default (pad, caps);
-    GST_DEBUG_OBJECT (pad, "default acceptcaps returned %d", result);
-  }
+  if (GST_PAD_ITERINTLINKFUNC (pad))
+    res = GST_PAD_ITERINTLINKFUNC (pad) (pad, parent);
 
-  return result;
+  RELEASE_PARENT (parent);
+
+  return res;
 
-is_same_caps:
+  /* ERRORS */
+no_parent:
   {
-    GST_DEBUG_OBJECT (pad, "pad had same caps");
+    GST_DEBUG_OBJECT (pad, "no parent");
     GST_OBJECT_UNLOCK (pad);
-    return TRUE;
+    return FALSE;
   }
 }
 
 /**
- * gst_pad_peer_accept_caps:
- * @pad: a  #GstPad to check the peer of
- * @caps: a #GstCaps to check on the pad
+ * gst_pad_forward:
+ * @pad: a #GstPad
+ * @forward: a #GstPadForwardFunction
+ * @user_data: user data passed to @forward
+ *
+ * Calls @forward for all internally linked pads of @pad. This function deals with
+ * dynamically changing internal pads and will make sure that the @forward
+ * function is only called once for each pad.
  *
- * Check if the peer of @pad accepts @caps. If @pad has no peer, this function
- * returns TRUE.
+ * When @forward returns TRUE, no further pads will be processed.
  *
- * Returns: TRUE if the peer of @pad can accept the caps or @pad has no peer.
+ * Returns: TRUE if one of the dispatcher functions returned TRUE.
  */
 gboolean
-gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps)
+gst_pad_forward (GstPad * pad, GstPadForwardFunction forward,
+    gpointer user_data)
 {
-  GstPad *peerpad;
-  gboolean result;
+  gboolean result = FALSE;
+  GstIterator *iter;
+  gboolean done = FALSE;
+  GValue item = { 0, };
+  GList *pushed_pads = NULL;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  iter = gst_pad_iterate_internal_links (pad);
 
-  GST_OBJECT_LOCK (pad);
+  if (!iter)
+    goto no_iter;
+
+  while (!done) {
+    switch (gst_iterator_next (iter, &item)) {
+      case GST_ITERATOR_OK:
+      {
+        GstPad *intpad;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "peer accept caps of (%p)", pad);
+        intpad = g_value_get_object (&item);
 
-  peerpad = GST_PAD_PEER (pad);
-  if (G_UNLIKELY (peerpad == NULL))
-    goto no_peer;
+        /* if already pushed, skip. FIXME, find something faster to tag pads */
+        if (intpad == NULL || g_list_find (pushed_pads, intpad)) {
+          g_value_reset (&item);
+          break;
+        }
 
-  gst_object_ref (peerpad);
-  /* release lock before calling external methods but keep ref to pad */
-  GST_OBJECT_UNLOCK (pad);
+        GST_LOG_OBJECT (pad, "calling forward function on pad %s:%s",
+            GST_DEBUG_PAD_NAME (intpad));
+        done = result = forward (intpad, user_data);
 
-  result = gst_pad_accept_caps (peerpad, caps);
+        pushed_pads = g_list_prepend (pushed_pads, intpad);
 
-  gst_object_unref (peerpad);
+        g_value_reset (&item);
+        break;
+      }
+      case GST_ITERATOR_RESYNC:
+        /* We don't reset the result here because we don't push the event
+         * again on pads that got the event already and because we need
+         * to consider the result of the previous pushes */
+        gst_iterator_resync (iter);
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_ERROR_OBJECT (pad, "Could not iterate over internally linked pads");
+        done = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+    }
+  }
+  g_value_unset (&item);
+  gst_iterator_free (iter);
+
+  g_list_free (pushed_pads);
 
+no_iter:
   return result;
+}
 
-no_peer:
-  {
-    GST_OBJECT_UNLOCK (pad);
-    return TRUE;
-  }
+typedef struct
+{
+  GstEvent *event;
+  gboolean result;
+  gboolean dispatched;
+} EventData;
+
+static gboolean
+event_forward_func (GstPad * pad, EventData * data)
+{
+  /* 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 pushing event %p (%s) to %s:%s",
+      data->event, GST_EVENT_TYPE_NAME (data->event), GST_DEBUG_PAD_NAME (pad));
+
+  data->result |= gst_pad_push_event (pad, gst_event_ref (data->event));
+
+  data->dispatched = TRUE;
+
+  /* don't stop */
+  return FALSE;
 }
 
 /**
- * gst_pad_set_caps:
- * @pad: a  #GstPad to set the capabilities of.
- * @caps: (transfer none): a #GstCaps to set.
+ * gst_pad_event_default:
+ * @pad: a #GstPad to call the default event handler on.
+ * @parent: the parent of @pad or NULL
+ * @event: (transfer full): the #GstEvent to handle.
  *
- * Sets the capabilities of this pad. The caps must be fixed. Any previous
- * caps on the pad will be unreffed. This function refs the caps so you should
- * unref if as soon as you don't need it anymore.
- * It is possible to set NULL caps, which will make the pad unnegotiated
- * again.
+ * Invokes the default event handler for the given pad.
  *
- * Returns: TRUE if the caps could be set. FALSE if the caps were not fixed
- * or bad parameters were provided to this function.
+ * The EOS event will pause the task associated with @pad before it is forwarded
+ * to all internally linked pads,
  *
- * MT safe.
+ * The the event is sent to all pads internally linked to @pad. This function
+ * takes ownership of @event.
+ *
+ * Returns: TRUE if the event was sent successfully.
  */
 gboolean
-gst_pad_set_caps (GstPad * pad, GstCaps * caps)
+gst_pad_event_default (GstPad * pad, GstObject * parent, GstEvent * event)
 {
-  GstPadSetCapsFunction setcaps;
-  GstCaps *existing;
+  gboolean result, forward = TRUE;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (caps == NULL || gst_caps_is_fixed (caps), FALSE);
-
-  GST_OBJECT_LOCK (pad);
-  existing = GST_PAD_CAPS (pad);
-  if (existing == caps)
-    goto was_ok;
-
-  if (gst_caps_is_equal (caps, existing))
-    goto setting_same_caps;
+  g_return_val_if_fail (event != NULL, FALSE);
 
-  setcaps = GST_PAD_SETCAPSFUNC (pad);
+  GST_LOG_OBJECT (pad, "default event handler");
 
-  /* call setcaps function to configure the pad only if the
-   * caps is not NULL */
-  if (setcaps != NULL && caps) {
-    if (!GST_PAD_IS_IN_SETCAPS (pad)) {
-      GST_OBJECT_FLAG_SET (pad, GST_PAD_IN_SETCAPS);
-      GST_OBJECT_UNLOCK (pad);
-      if (!setcaps (pad, caps))
-        goto could_not_set;
-      GST_OBJECT_LOCK (pad);
-      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_SETCAPS);
-    } else {
-      GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad was dispatching");
-    }
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+      forward = GST_PAD_IS_PROXY_CAPS (pad);
+      result = TRUE;
+      break;
+    default:
+      break;
   }
 
-  gst_caps_replace (&GST_PAD_CAPS (pad), caps);
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "caps %p %" GST_PTR_FORMAT, caps,
-      caps);
-  GST_OBJECT_UNLOCK (pad);
+  if (forward) {
+    EventData data;
 
-#if GLIB_CHECK_VERSION(2,26,0)
-  g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
-#else
-  g_object_notify ((GObject *) pad, "caps");
-#endif
+    data.event = event;
+    data.dispatched = FALSE;
+    data.result = FALSE;
 
-  return TRUE;
+    gst_pad_forward (pad, (GstPadForwardFunction) event_forward_func, &data);
 
-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 %p %" GST_PTR_FORMAT " same as existing, updating ptr only", caps,
-        caps);
-    GST_OBJECT_UNLOCK (pad);
-    return TRUE;
+    /* for sinkpads without a parent element or without internal links, nothing
+     * will be dispatched but we still want to return TRUE. */
+    if (data.dispatched)
+      result = data.result;
+    else
+      result = TRUE;
   }
 
-  /* ERRORS */
-could_not_set:
-  {
-    GST_OBJECT_LOCK (pad);
-    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_SETCAPS);
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "caps %" GST_PTR_FORMAT " could not be set", caps);
-    GST_OBJECT_UNLOCK (pad);
+  gst_event_unref (event);
 
-    return FALSE;
-  }
+  return result;
 }
 
+/* Default accept caps implementation just checks against
+ * the allowed caps for the pad */
 static gboolean
-gst_pad_configure_sink (GstPad * pad, GstCaps * caps)
+gst_pad_query_accept_caps_default (GstPad * pad, GstQuery * query)
 {
-  gboolean res;
+  /* get the caps and see if it intersects to something not empty */
+  GstCaps *caps, *allowed;
+  gboolean result;
 
-  /* See if pad accepts the caps */
-  if (!gst_caps_can_intersect (caps, gst_pad_get_pad_template_caps (pad)))
-    goto not_accepted;
+  GST_DEBUG_OBJECT (pad, "query accept-caps %" GST_PTR_FORMAT, query);
 
-  /* 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
-   * our ref goes away when we leave this function. */
+  /* first forward the query to internally linked pads when we are dealing with
+   * a PROXY CAPS */
+  if (GST_PAD_IS_PROXY_CAPS (pad)) {
+    if ((result = gst_pad_proxy_query_accept_caps (pad, query))) {
+      goto done;
+    }
+  }
 
-  return res;
+  GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pad,
+      "fallback ACCEPT_CAPS query, consider implementing a specialized version");
 
-not_accepted:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "caps %" GST_PTR_FORMAT " not accepted", caps);
-    return FALSE;
+  allowed = gst_pad_query_caps (pad, NULL);
+  gst_query_parse_accept_caps (query, &caps);
+
+  if (allowed) {
+    GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed);
+    result = gst_caps_is_subset (caps, allowed);
+    gst_caps_unref (allowed);
+  } else {
+    GST_DEBUG_OBJECT (pad, "no caps allowed on the pad");
+    result = FALSE;
   }
+  gst_query_set_accept_caps_result (query, result);
+
+done:
+  return TRUE;
 }
 
-/* returns TRUE if the src pad could be configured to accept the given caps */
+/* Default caps implementation */
 static gboolean
-gst_pad_configure_src (GstPad * pad, GstCaps * caps, gboolean dosetcaps)
+gst_pad_query_caps_default (GstPad * pad, GstQuery * query)
 {
-  gboolean res;
+  GstCaps *result = NULL, *filter;
+  GstPadTemplate *templ;
+  gboolean fixed_caps;
 
-  if (dosetcaps) {
-    /* See if pad accepts the caps */
-    if (!gst_pad_accept_caps (pad, caps))
-      goto not_accepted;
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
 
-    res = gst_pad_set_caps (pad, caps);
-  } else {
-    res = TRUE;
+  gst_query_parse_caps (query, &filter);
+
+  /* first try to proxy if we must */
+  if (GST_PAD_IS_PROXY_CAPS (pad)) {
+    if ((gst_pad_proxy_query_caps (pad, query))) {
+      gst_query_parse_caps_result (query, &result);
+      goto filter_done;
+    }
   }
-  return res;
 
-not_accepted:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "caps %" GST_PTR_FORMAT " not accepted", caps);
-    return FALSE;
-  }
-}
+  /* no proxy or it failed, do default handling */
+  fixed_caps = GST_PAD_IS_FIXED_CAPS (pad);
 
-/**
- * gst_pad_get_pad_template_caps:
- * @pad: a #GstPad to get the template capabilities from.
- *
- * Gets the capabilities for @pad's template.
- *
- * Returns: (transfer none): 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)
-{
-  static GstStaticCaps anycaps = GST_STATIC_CAPS ("ANY");
+  GST_OBJECT_LOCK (pad);
+  if (fixed_caps) {
+    /* fixed caps, try the negotiated caps first */
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "fixed pad caps: trying pad caps");
+    if ((result = get_pad_caps (pad)))
+      goto filter_done_unlock;
+  }
 
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  if ((templ = GST_PAD_PAD_TEMPLATE (pad))) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "trying pad template caps");
+    if ((result = GST_PAD_TEMPLATE_CAPS (templ)))
+      goto filter_done_unlock;
+  }
 
-  if (GST_PAD_PAD_TEMPLATE (pad))
-    return GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad));
+  if (!fixed_caps) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "non-fixed pad caps: trying pad caps");
+    /* non fixed caps, try the negotiated caps */
+    if ((result = get_pad_caps (pad)))
+      goto filter_done_unlock;
+  }
 
-  return gst_static_caps_get (&anycaps);
-}
+  /* this almost never happens */
+  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
+  result = GST_CAPS_ANY;
 
-/**
- * gst_pad_get_peer:
- * @pad: a #GstPad to get the peer of.
- *
- * Gets the peer of @pad. This function refs the peer pad so
- * you need to unref it after use.
- *
- * Returns: (transfer full): the peer #GstPad. Unref after usage.
- *
- * MT safe.
- */
-GstPad *
-gst_pad_get_peer (GstPad * pad)
-{
-  GstPad *result;
+filter_done_unlock:
+  GST_OBJECT_UNLOCK (pad);
 
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+filter_done:
+  /* run the filter on the result */
+  if (filter) {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "using caps %p %" GST_PTR_FORMAT " with filter %p %"
+        GST_PTR_FORMAT, result, result, filter, filter);
+    result = gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "result %p %" GST_PTR_FORMAT,
+        result, result);
+  } else {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "using caps %p %" GST_PTR_FORMAT, result, result);
+    result = gst_caps_ref (result);
+  }
 
-  GST_OBJECT_LOCK (pad);
-  result = GST_PAD_PEER (pad);
-  if (result)
-    gst_object_ref (result);
-  GST_OBJECT_UNLOCK (pad);
+  gst_query_set_caps_result (query, result);
+  gst_caps_unref (result);
 
-  return result;
+  return TRUE;
 }
 
-/**
- * gst_pad_get_allowed_caps:
- * @pad: a #GstPad.
- *
- * Gets the capabilities of the allowed media types that can flow through
- * @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: (transfer full): 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 * pad)
+typedef struct
 {
-  GstCaps *mycaps;
-  GstCaps *caps;
-  GstCaps *peercaps;
-  GstPad *peer;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  GST_OBJECT_LOCK (pad);
-
-  peer = GST_PAD_PEER (pad);
-  if (G_UNLIKELY (peer == NULL))
-    goto no_peer;
-
-  GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps");
-
-  gst_object_ref (peer);
-  GST_OBJECT_UNLOCK (pad);
-  mycaps = gst_pad_get_caps_reffed (pad);
-
-  peercaps = gst_pad_get_caps_reffed (peer);
-  gst_object_unref (peer);
-
-  caps = gst_caps_intersect (mycaps, peercaps);
-  gst_caps_unref (peercaps);
-  gst_caps_unref (mycaps);
+  GstQuery *query;
+  gboolean result;
+  gboolean dispatched;
+} QueryData;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT,
-      caps);
+static gboolean
+query_forward_func (GstPad * pad, QueryData * data)
+{
+  GST_LOG_OBJECT (pad, "query peer %p (%s) of %s:%s",
+      data->query, GST_EVENT_TYPE_NAME (data->query), GST_DEBUG_PAD_NAME (pad));
 
-  return caps;
+  data->result |= gst_pad_peer_query (pad, data->query);
 
-no_peer:
-  {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
-    GST_OBJECT_UNLOCK (pad);
+  data->dispatched = TRUE;
 
-    return NULL;
-  }
+  /* stop on first successful reply */
+  return data->result;
 }
 
 /**
- * gst_pad_get_negotiated_caps:
- * @pad: a #GstPad.
- *
- * Gets the capabilities of the media type that currently flows through @pad
- * and its peer.
- *
- * This function can be used on both src and sinkpads. Note that srcpads are
- * always negotiated before sinkpads so it is possible that the negotiated caps
- * on the srcpad do not match the negotiated caps of the peer.
+ * gst_pad_query_default:
+ * @pad: a #GstPad to call the default query handler on.
+ * @parent: the parent of @pad or NULL
+ * @query: (transfer none): the #GstQuery to handle.
  *
- * Returns: (transfer full): 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.
+ * 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.
  *
- * MT safe.
+ * Returns: TRUE if the query was performed successfully.
  */
-GstCaps *
-gst_pad_get_negotiated_caps (GstPad * pad)
-{
-  GstCaps *caps;
-  GstPad *peer;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  GST_OBJECT_LOCK (pad);
-
-  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
-    goto no_peer;
-
-  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_OBJECT (GST_CAT_CAPS, pad, "negotiated caps %" GST_PTR_FORMAT,
-      caps);
-
-  return caps;
-
-no_peer:
-  {
-    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_buffer_alloc_unchecked (GstPad * pad, guint64 offset, gint size,
-    GstCaps * caps, GstBuffer ** buf)
+gboolean
+gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
 {
-  GstFlowReturn ret;
-  GstPadBufferAllocFunction bufferallocfunc;
-
-  GST_OBJECT_LOCK (pad);
-  /* when the pad is flushing we cannot give a buffer */
-  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
-    goto flushing;
-
-  bufferallocfunc = pad->bufferallocfunc;
+  gboolean forward, ret = FALSE;
 
-  if (offset == GST_BUFFER_OFFSET_NONE) {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
-        "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 for size %d offset %"
-        G_GUINT64_FORMAT, GST_DEBUG_FUNCPTR_NAME (bufferallocfunc),
-        bufferallocfunc, size, offset);
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_SCHEDULING:
+      forward = FALSE;
+      break;
+    case GST_QUERY_ALLOCATION:
+      forward = GST_PAD_IS_PROXY_ALLOCATION (pad);
+      break;
+    case GST_QUERY_ACCEPT_CAPS:
+      ret = gst_pad_query_accept_caps_default (pad, query);
+      forward = FALSE;
+      break;
+    case GST_QUERY_CAPS:
+      ret = gst_pad_query_caps_default (pad, query);
+      forward = FALSE;
+      break;
+    case GST_QUERY_POSITION:
+    case GST_QUERY_SEEKING:
+    case GST_QUERY_FORMATS:
+    case GST_QUERY_LATENCY:
+    case GST_QUERY_JITTER:
+    case GST_QUERY_RATE:
+    case GST_QUERY_CONVERT:
+    default:
+      forward = TRUE;
+      break;
   }
-  GST_OBJECT_UNLOCK (pad);
 
-  /* 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 error;
+  GST_DEBUG_OBJECT (pad, "%sforwarding %p (%s) query", (forward ? "" : "not "),
+      query, GST_QUERY_TYPE_NAME (query));
 
-  /* no error, but NULL buffer means fallback to the default */
-  if (G_UNLIKELY (*buf == NULL))
-    goto fallback;
+  if (forward) {
+    QueryData data;
 
-  /* If the buffer alloc function didn't set up the caps like it should,
-   * do it for it */
-  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;
+    data.query = query;
+    data.dispatched = FALSE;
+    data.result = FALSE;
 
-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");
+    gst_pad_forward (pad, (GstPadForwardFunction) query_forward_func, &data);
 
-    if ((*buf = gst_buffer_try_new_and_alloc (size))) {
-      GST_BUFFER_OFFSET (*buf) = offset;
-      gst_buffer_set_caps (*buf, caps);
-      return GST_FLOW_OK;
+    if (data.dispatched) {
+      ret = data.result;
     } else {
-      GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
-          "out of memory allocating %d bytes", size);
-      return GST_FLOW_ERROR;
+      /* nothing dispatched, assume drained */
+      if (GST_QUERY_TYPE (query) == GST_QUERY_DRAIN)
+        ret = TRUE;
+      else
+        ret = FALSE;
     }
   }
+  return ret;
 }
 
-/* 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)
+static void
+probe_hook_marshal (GHook * hook, ProbeMarshall * data)
 {
-  GstPad *peer;
-  GstFlowReturn ret;
-  GstCaps *newcaps;
-  gboolean caps_changed;
+  GstPad *pad = data->pad;
+  GstPadProbeInfo *info = data->info;
+  GstPadProbeType type, flags;
+  GstPadProbeCallback callback;
+  GstPadProbeReturn ret;
 
-  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);
+  /* if we have called this callback, do nothing */
+  if (PROBE_COOKIE (hook) == data->cookie) {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "hook %lu, cookie %u already called", hook->hook_id,
+        PROBE_COOKIE (hook));
+    return;
+  }
 
-  GST_DEBUG_OBJECT (pad, "offset %" G_GUINT64_FORMAT ", size %d, caps %"
-      GST_PTR_FORMAT, offset, size, caps);
+  PROBE_COOKIE (hook) = data->cookie;
 
-  GST_OBJECT_LOCK (pad);
-  while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad)))
-    if ((ret = handle_pad_block (pad)) != GST_FLOW_OK)
-      goto flushed;
+  flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT;
+  type = info->type;
 
-  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
-    goto no_peer;
+  /* one of the data types */
+  if ((flags & GST_PAD_PROBE_TYPE_ALL_BOTH & type) == 0)
+    goto no_match;
+  /* one of the scheduling types */
+  if ((flags & GST_PAD_PROBE_TYPE_SCHEDULING & type) == 0)
+    goto no_match;
+  /* one of the blocking types must match */
+  if ((type & GST_PAD_PROBE_TYPE_BLOCKING) &&
+      (flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0)
+    goto no_match;
+  /* only probes that have GST_PAD_PROBE_TYPE_EVENT_FLUSH set */
+  if ((type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) &&
+      (flags & GST_PAD_PROBE_TYPE_EVENT_FLUSH & type) == 0)
+    goto no_match;
 
-  gst_object_ref (peer);
-  GST_OBJECT_UNLOCK (pad);
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+      "hook %lu, cookie %u with flags 0x%08x matches", hook->hook_id,
+      PROBE_COOKIE (hook), flags);
 
-  ret = gst_pad_buffer_alloc_unchecked (peer, offset, size, caps, buf);
-  gst_object_unref (peer);
+  data->marshalled = TRUE;
 
-  if (G_UNLIKELY (ret != GST_FLOW_OK))
-    goto peer_error;
+  callback = (GstPadProbeCallback) hook->func;
+  if (callback == NULL)
+    return;
 
-  /* FIXME, move capnego this into a base class? */
-  newcaps = GST_BUFFER_CAPS (*buf);
+  info->id = hook->hook_id;
 
-  /* 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;
+  ret = callback (pad, info, hook->data);
 
-  return ret;
+  GST_OBJECT_LOCK (pad);
 
-flushed:
-  {
-    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_OBJECT (GST_CAT_PADS, pad,
-        "called bufferallocfunc but had no peer");
-    GST_OBJECT_UNLOCK (pad);
-    return GST_FLOW_NOT_LINKED;
-  }
-peer_error:
-  {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "alloc function returned error %s", gst_flow_get_name (ret));
-    return ret;
+  switch (ret) {
+    case GST_PAD_PROBE_REMOVE:
+      /* remove the probe */
+      GST_DEBUG_OBJECT (pad, "asked to remove hook");
+      cleanup_hook (pad, hook);
+      break;
+    case GST_PAD_PROBE_DROP:
+      /* need to drop the data, make sure other probes don't get called
+       * anymore */
+      GST_DEBUG_OBJECT (pad, "asked to drop item");
+      info->type = GST_PAD_PROBE_TYPE_INVALID;
+      data->dropped = TRUE;
+      break;
+    case GST_PAD_PROBE_PASS:
+      /* inform the pad block to let things pass */
+      GST_DEBUG_OBJECT (pad, "asked to pass item");
+      data->pass = TRUE;
+      break;
+    default:
+      GST_DEBUG_OBJECT (pad, "probe returned %d", ret);
+      break;
   }
-not_negotiated:
+  return;
+
+no_match:
   {
-    gst_buffer_unref (*buf);
-    *buf = NULL;
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "alloc function returned unacceptable buffer");
-    return GST_FLOW_NOT_NEGOTIATED;
-  }
-wrong_size_fallback:
-  {
-    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;
-    }
+        "hook %lu, cookie %u with flags 0x%08x does not match %08x",
+        hook->hook_id, PROBE_COOKIE (hook), flags, info->type);
+    return;
   }
 }
 
-/**
- * gst_pad_alloc_buffer:
- * @pad: a source #GstPad
- * @offset: the offset of the new buffer in the stream
- * @size: the size of the new buffer
- * @caps: the caps of the new buffer
- * @buf: a newly allocated buffer
- *
- * 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. If the caps changed, it is
- * possible that the buffer returned in @buf is not of the right size for the
- * new format, @buf needs to be unreffed and reallocated if this is the case.
- *
- * Returns: a result code indicating success of the operation. Any
- * 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)
-{
-  return gst_pad_alloc_buffer_full (pad, offset, size, caps, buf, FALSE);
-}
+/* a probe that does not take or return any data */
+#define PROBE_NO_DATA(pad,mask,label,defaultval)                \
+  G_STMT_START {                                               \
+    if (G_UNLIKELY (pad->num_probes)) {                                \
+      /* pass NULL as the data item */                          \
+      GstPadProbeInfo info = { mask, 0, NULL, 0, 0 };           \
+      ret = do_probe_callbacks (pad, &info, defaultval);       \
+      if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK))        \
+        goto label;                                            \
+    }                                                          \
+  } G_STMT_END
+
+#define PROBE_FULL(pad,mask,data,offs,size,label)               \
+  G_STMT_START {                                               \
+    if (G_UNLIKELY (pad->num_probes)) {                                \
+      /* pass the data item */                                  \
+      GstPadProbeInfo info = { mask, 0, data, offs, size };     \
+      ret = do_probe_callbacks (pad, &info, GST_FLOW_OK);      \
+      /* store the possibly updated data item */                \
+      data = GST_PAD_PROBE_INFO_DATA (&info);                   \
+      /* if something went wrong, exit */                       \
+      if (G_UNLIKELY (ret != GST_FLOW_OK))                     \
+        goto label;                                            \
+    }                                                          \
+  } G_STMT_END
+
+#define PROBE_PUSH(pad,mask,data,label)                                 \
+  PROBE_FULL(pad, mask, data, -1, -1, label);
+#define PROBE_PULL(pad,mask,data,offs,size,label)               \
+  PROBE_FULL(pad, mask, data, offs, size, label);
 
-/**
- * gst_pad_alloc_buffer_and_set_caps:
- * @pad: a source #GstPad
- * @offset: the offset of the new buffer in the stream
- * @size: the size of the new buffer
- * @caps: (transfer none): the caps of the new buffer
- * @buf: (out callee-allocates): a newly allocated buffer
- *
- * In addition to the function gst_pad_alloc_buffer(), this function
- * automatically calls gst_pad_set_caps() when the caps of the
- * newly allocated buffer are different from the @pad caps.
- *
- * After a renegotiation, the size of the new buffer returned in @buf could
- * be of the wrong size for the new format and must be unreffed an reallocated
- * in that case.
- *
- * Returns: a result code indicating success of the operation. Any
- * 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)
+static GstFlowReturn
+do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info,
+    GstFlowReturn defaultval)
 {
-  return gst_pad_alloc_buffer_full (pad, offset, size, caps, buf, TRUE);
-}
+  ProbeMarshall data;
+  guint cookie;
+  gboolean is_block;
 
+  data.pad = pad;
+  data.info = info;
+  data.pass = FALSE;
+  data.marshalled = FALSE;
+  data.dropped = FALSE;
+  data.cookie = ++pad->priv->probe_cookie;
 
-#ifndef GST_REMOVE_DEPRECATED
-typedef struct
-{
-  GList *list;
-  guint32 cookie;
-} IntLinkIterData;
+  is_block =
+      (info->type & GST_PAD_PROBE_TYPE_BLOCK) == GST_PAD_PROBE_TYPE_BLOCK;
 
-static void
-int_link_iter_data_free (IntLinkIterData * data)
-{
-  g_list_free (data->list);
-  g_slice_free (IntLinkIterData, data);
-}
-#endif
+again:
+  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+      "do probes cookie %u", data.cookie);
+  cookie = pad->priv->probe_list_cookie;
 
-static GstIteratorItem
-iterate_pad (GstIterator * it, GstPad * pad)
-{
-  gst_object_ref (pad);
-  return GST_ITERATOR_ITEM_PASS;
-}
+  g_hook_list_marshal (&pad->probes, TRUE,
+      (GHookMarshaller) probe_hook_marshal, &data);
 
-/**
- * gst_pad_iterate_internal_links_default:
- * @pad: the #GstPad to get the internal links of.
- *
- * Iterate the list of pads to which the given pad is linked to inside of
- * the parent element.
- * This is the default handler, and thus returns an iterator of all of the
- * pads inside the parent element with opposite direction.
- *
- * The caller must free this iterator after use with gst_iterator_free().
- *
- * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each
- * returned pad with gst_object_unref().
- *
- * Since: 0.10.21
- */
-GstIterator *
-gst_pad_iterate_internal_links_default (GstPad * pad)
-{
-  GstIterator *res;
-  GList **padlist;
-  guint32 *cookie;
-  GMutex *lock;
-  gpointer owner;
-  GstIteratorDisposeFunction dispose;
+  /* if the list changed, call the new callbacks (they will not have their
+   * cookie set to data.cookie */
+  if (cookie != pad->priv->probe_list_cookie) {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "probe list changed, restarting");
+    goto again;
+  }
 
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  /* the first item that dropped will stop the hooks and then we drop here */
+  if (data.dropped)
+    goto dropped;
 
-#ifndef GST_REMOVE_DEPRECATED
-  /* when we get here, the default handler for the iterate links is called,
-   * which means that the user has not installed a custom one. We first check if
-   * there is maybe a custom legacy function we can call. */
-  if (GST_PAD_INTLINKFUNC (pad) &&
-      GST_PAD_INTLINKFUNC (pad) != gst_pad_get_internal_links_default) {
-    IntLinkIterData *data;
-
-    /* make an iterator for the list. We can't protect the list with a
-     * cookie. If we would take the cookie of the parent element, we need to
-     * have a parent, which is not required for GST_PAD_INTLINKFUNC(). We could
-     * cache the per-pad list and invalidate the list when a new call to
-     * INTLINKFUNC() returned a different list but then this would only work if
-     * two concurrent iterators were used and the last iterator would still be
-     * thread-unsafe. Just don't use this method anymore. */
-    data = g_slice_new (IntLinkIterData);
-    data->list = ((GstPadIntLinkFunction) GST_PAD_INTLINKFUNC (pad)) (pad);
-    data->cookie = 0;
-
-    GST_WARNING_OBJECT (pad, "Making unsafe iterator");
-
-    cookie = &data->cookie;
-    padlist = &data->list;
-    owner = data;
-    dispose = (GstIteratorDisposeFunction) int_link_iter_data_free;
-    /* reuse the pad lock, it's all we have here */
-    lock = GST_OBJECT_GET_LOCK (pad);
-  } else
-#endif
-  {
-    GstElement *parent;
+  /* if no handler matched and we are blocking, let the item pass */
+  if (!data.marshalled && is_block)
+    goto passed;
 
-    GST_OBJECT_LOCK (pad);
-    parent = GST_PAD_PARENT (pad);
-    if (!parent || !GST_IS_ELEMENT (parent))
-      goto no_parent;
+  /* At this point, all handlers returned either OK or PASS. If one handler
+   * returned PASS, let the item pass */
+  if (data.pass)
+    goto passed;
 
-    gst_object_ref (parent);
-    GST_OBJECT_UNLOCK (pad);
+  if (is_block) {
+    while (GST_PAD_IS_BLOCKED (pad)) {
+      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+          "we are blocked %d times", pad->num_blocked);
 
-    if (pad->direction == GST_PAD_SRC)
-      padlist = &parent->sinkpads;
-    else
-      padlist = &parent->srcpads;
+      /* we might have released the lock */
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+        goto flushing;
 
-    GST_DEBUG_OBJECT (pad, "Making iterator");
+      /* 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_FLAG_BLOCKING);
+      GST_PAD_BLOCK_WAIT (pad);
+      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKING);
+      GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "We got unblocked");
 
-    cookie = &parent->pads_cookie;
-    owner = parent;
-    dispose = (GstIteratorDisposeFunction) gst_object_unref;
-    lock = GST_OBJECT_GET_LOCK (parent);
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+        goto flushing;
+    }
   }
 
-  res = gst_iterator_new_list (GST_TYPE_PAD,
-      lock, cookie, padlist, owner, (GstIteratorItemFunction) iterate_pad,
-      dispose);
-
-  return res;
+  return defaultval;
 
   /* ERRORS */
-no_parent:
+flushing:
   {
-    GST_OBJECT_UNLOCK (pad);
-    GST_DEBUG_OBJECT (pad, "no parent element");
-    return NULL;
+    GST_DEBUG_OBJECT (pad, "pad is flushing");
+    return GST_FLOW_FLUSHING;
+  }
+dropped:
+  {
+    GST_DEBUG_OBJECT (pad, "data is dropped");
+    return GST_FLOW_CUSTOM_SUCCESS;
+  }
+passed:
+  {
+    /* FIXME : Should we return FLOW_OK or the defaultval ?? */
+    GST_DEBUG_OBJECT (pad, "data is passed");
+    return GST_FLOW_OK;
   }
 }
 
+/* pad offsets */
+
 /**
- * gst_pad_iterate_internal_links:
- * @pad: the GstPad to get the internal links of.
- *
- * Gets an iterator for the pads to which the given pad is linked to inside
- * of the parent element.
- *
- * Each #GstPad element yielded by the iterator will have its refcount increased,
- * so unref after use.
- *
- * Free-function: gst_iterator_free
+ * gst_pad_get_offset:
+ * @pad: a #GstPad
  *
- * Returns: (transfer full): a new #GstIterator of #GstPad or %NULL when the
- *     pad does not have an iterator function configured. Use
- *     gst_iterator_free() after usage.
+ * Get the offset applied to the running time of @pad. @pad has to be a source
+ * pad.
  *
- * Since: 0.10.21
+ * Returns: the offset.
  */
-GstIterator *
-gst_pad_iterate_internal_links (GstPad * pad)
+gint64
+gst_pad_get_offset (GstPad * pad)
 {
-  GstIterator *res = NULL;
+  gint64 result;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  if (GST_PAD_ITERINTLINKFUNC (pad))
-    res = GST_PAD_ITERINTLINKFUNC (pad) (pad);
+  g_return_val_if_fail (GST_IS_PAD (pad), 0);
 
-  return res;
-}
+  GST_OBJECT_LOCK (pad);
+  result = pad->offset;
+  GST_OBJECT_UNLOCK (pad);
 
-#ifndef GST_REMOVE_DEPRECATED
-static void
-add_unref_pad_to_list (GstPad * pad, GList ** list)
-{
-  *list = g_list_prepend (*list, pad);
-  gst_object_unref (pad);
+  return result;
 }
-#endif
 
 /**
- * gst_pad_get_internal_links_default:
- * @pad: the #GstPad to get the internal links of.
- *
- * Gets a list of pads to which the given pad is linked to
- * inside of the parent element.
- * This is the default handler, and thus returns a list of all of the
- * pads inside the parent element with opposite direction.
- *
- * The caller must free this list after use with g_list_free().
- *
- * Returns: (transfer full) (element-type Gst.Pad): a newly allocated #GList
- *     of pads, or NULL if the pad has no parent.
+ * gst_pad_set_offset:
+ * @pad: a #GstPad
+ * @offset: the offset
  *
- * Not MT safe.
- *
- * Deprecated: This function does not ref the pads in the list so that they
- * could become invalid by the time the application accesses them. It's also
- * possible that the list changes while handling the pads, which the caller of
- * this function is unable to know. Use the thread-safe
- * gst_pad_iterate_internal_links_default() instead.
+ * Set the offset that will be applied to the running time of @pad.
  */
-#ifndef GST_REMOVE_DEPRECATED
-GList *
-gst_pad_get_internal_links_default (GstPad * pad)
+void
+gst_pad_set_offset (GstPad * pad, gint64 offset)
 {
-  GList *res = NULL;
-  GstElement *parent;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  GST_WARNING_OBJECT (pad, "Unsafe internal links used");
-
-  /* when we get here, the default handler for get_internal_links is called,
-   * which means that the user has not installed a custom one. We first check if
-   * there is maybe a custom iterate function we can call. */
-  if (GST_PAD_ITERINTLINKFUNC (pad) &&
-      GST_PAD_ITERINTLINKFUNC (pad) != gst_pad_iterate_internal_links_default) {
-    GstIterator *it;
-    GstIteratorResult ires;
-    gboolean done = FALSE;
-
-    it = gst_pad_iterate_internal_links (pad);
-    /* loop over the iterator and put all elements into a list, we also
-     * immediately unref them, which is bad. */
-    do {
-      ires = gst_iterator_foreach (it, (GFunc) add_unref_pad_to_list, &res);
-      switch (ires) {
-        case GST_ITERATOR_OK:
-        case GST_ITERATOR_DONE:
-        case GST_ITERATOR_ERROR:
-          done = TRUE;
-          break;
-        case GST_ITERATOR_RESYNC:
-          /* restart, discard previous list */
-          gst_iterator_resync (it);
-          g_list_free (res);
-          res = NULL;
-          break;
-      }
-    } while (!done);
+  PadEvent *ev;
 
-    gst_iterator_free (it);
-  } else {
-    /* lock pad, check and ref parent */
-    GST_OBJECT_LOCK (pad);
-    parent = GST_PAD_PARENT (pad);
-    if (!parent || !GST_IS_ELEMENT (parent))
-      goto no_parent;
-
-    parent = gst_object_ref (parent);
-    GST_OBJECT_UNLOCK (pad);
-
-    /* now lock the parent while we copy the pads */
-    GST_OBJECT_LOCK (parent);
-    if (pad->direction == GST_PAD_SRC)
-      res = g_list_copy (parent->sinkpads);
-    else
-      res = g_list_copy (parent->srcpads);
-    GST_OBJECT_UNLOCK (parent);
+  g_return_if_fail (GST_IS_PAD (pad));
 
-    gst_object_unref (parent);
-  }
+  GST_OBJECT_LOCK (pad);
+  /* if nothing changed, do nothing */
+  if (pad->offset == offset)
+    goto done;
 
-  /* At this point pads can be changed and unreffed. Nothing we can do about it
-   * because for compatibility reasons this function cannot ref the pads or
-   * notify the app that the list changed. */
+  pad->offset = offset;
+  GST_DEBUG_OBJECT (pad, "changed offset to %" G_GINT64_FORMAT, offset);
 
-  return res;
+  /* sinkpads will apply their offset the next time a segment
+   * event is received. */
+  if (GST_PAD_IS_SINK (pad))
+    goto done;
 
-no_parent:
-  {
-    GST_DEBUG_OBJECT (pad, "no parent");
-    GST_OBJECT_UNLOCK (pad);
-    return NULL;
+  /* resend the last segment event on next buffer push */
+  if ((ev = find_event_by_type (pad, GST_EVENT_SEGMENT, 0))) {
+    ev->received = FALSE;
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
   }
-}
-#endif /* GST_REMOVE_DEPRECATED */
-
-/**
- * gst_pad_get_internal_links:
- * @pad: the #GstPad to get the internal links of.
- *
- * Gets a list of pads to which the given pad is linked to
- * inside of the parent element.
- * The caller must free this list after use.
- *
- * Not MT safe.
- *
- * Returns: (transfer full) (element-type Gst.Pad): a newly allocated #GList
- *     of pads, free with g_list_free().
- *
- * Deprecated: This function does not ref the pads in the list so that they
- * could become invalid by the time the application accesses them. It's also
- * possible that the list changes while handling the pads, which the caller of
- * this function is unable to know. Use the thread-safe
- * gst_pad_iterate_internal_links() instead.
- */
-#ifndef GST_REMOVE_DEPRECATED
-#ifdef GST_DISABLE_DEPRECATED
-GList *gst_pad_get_internal_links (GstPad * pad);
-#endif
-GList *
-gst_pad_get_internal_links (GstPad * pad)
-{
-  GList *res = NULL;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
-  GST_WARNING_OBJECT (pad, "Calling unsafe internal links");
-
-  if (GST_PAD_INTLINKFUNC (pad))
-    res = ((GstPadIntLinkFunction) GST_PAD_INTLINKFUNC (pad)) (pad);
 
-  return res;
+done:
+  GST_OBJECT_UNLOCK (pad);
 }
-#endif /* GST_REMOVE_DEPRECATED */
 
-static gboolean
-gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event)
+typedef struct
 {
-  gboolean result = FALSE;
-  GstIterator *iter;
-  gboolean done = FALSE;
-  gpointer item;
-  GstPad *eventpad;
-  GList *pushed_pads = NULL;
-
-  GST_INFO_OBJECT (pad, "Sending event %p (%s) to all internally linked pads",
-      event, GST_EVENT_TYPE_NAME (event));
-
-  iter = gst_pad_iterate_internal_links (pad);
-
-  if (!iter)
-    goto no_iter;
-
-  while (!done) {
-    switch (gst_iterator_next (iter, &item)) {
-      case GST_ITERATOR_OK:
-        eventpad = GST_PAD_CAST (item);
-
-        /* if already pushed,  skip */
-        if (g_list_find (pushed_pads, eventpad)) {
-          gst_object_unref (item);
-          break;
-        }
-
-        if (GST_PAD_IS_SRC (eventpad)) {
-          /* 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_NAME (event),
-              GST_DEBUG_PAD_NAME (eventpad));
-          gst_event_ref (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_NAME (event),
-              GST_DEBUG_PAD_NAME (eventpad));
-          result = gst_pad_push_event (eventpad, event);
-          done = TRUE;
-          event = NULL;
-        }
-
-        pushed_pads = g_list_prepend (pushed_pads, eventpad);
-
-        gst_object_unref (item);
-        break;
-      case GST_ITERATOR_RESYNC:
-        /* We don't reset the result here because we don't push the event
-         * again on pads that got the event already and because we need
-         * to consider the result of the previous pushes */
-        gst_iterator_resync (iter);
-        break;
-      case GST_ITERATOR_ERROR:
-        GST_ERROR_OBJECT (pad, "Could not iterate over internally linked pads");
-        done = TRUE;
-        break;
-      case GST_ITERATOR_DONE:
-        done = TRUE;
-        break;
-    }
-  }
-  gst_iterator_free (iter);
-
-no_iter:
-
-  /* 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. */
-  if (!pushed_pads)
-    result = GST_PAD_IS_SINK (pad);
-
-  g_list_free (pushed_pads);
-
-  /* we handled the incoming event so we unref once */
-  if (event) {
-    GST_LOG_OBJECT (pad, "handled event %p, unreffing", event);
-    gst_event_unref (event);
-  }
-
-  return result;
-}
+  GstFlowReturn ret;
 
-/**
- * gst_pad_event_default:
- * @pad: a #GstPad to call the default event handler on.
- * @event: (transfer full): the #GstEvent to handle.
- *
- * Invokes the default event handler for the given pad. End-of-stream and
- * discontinuity events are handled specially, and then the event 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 an event.
- * Multi-sinkpad elements should implement custom event handlers.
- *
- * Returns: TRUE if the event was sent successfully.
- */
-gboolean
-gst_pad_event_default (GstPad * pad, GstEvent * event)
+  /* If TRUE and ret is not OK this means
+   * that pushing the EOS event failed
+   */
+  gboolean was_eos;
+} PushStickyData;
+
+/* should be called with pad LOCK */
+static gboolean
+push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
 {
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
+  PushStickyData *data = user_data;
+  GstEvent *event = ev->event;
 
-  GST_LOG_OBJECT (pad, "default event handler");
+  if (ev->received) {
+    GST_DEBUG_OBJECT (pad, "event %s was already received",
+        GST_EVENT_TYPE_NAME (event));
+    return TRUE;
+  }
 
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_EOS:
-    {
-      GST_DEBUG_OBJECT (pad, "pausing task because of eos");
-      gst_pad_pause_task (pad);
-    }
-      /* fall thru */
+  data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event),
+      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+
+  switch (data->ret) {
+    case GST_FLOW_OK:
+      ev->received = TRUE;
+      GST_DEBUG_OBJECT (pad, "event %s marked received",
+          GST_EVENT_TYPE_NAME (event));
+      break;
+    case GST_FLOW_NOT_LINKED:
+      /* not linked is not a problem, we are sticky so the event will be
+       * sent later but only for non-EOS events */
+      GST_DEBUG_OBJECT (pad, "pad was not linked");
+      if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
+        data->ret = GST_FLOW_OK;
+      /* fallthrough */
     default:
+      GST_DEBUG_OBJECT (pad, "mark pending events");
+      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
       break;
   }
 
-  return gst_pad_event_default_dispatch (pad, event);
+  if (data->ret != GST_FLOW_OK && GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+    data->was_eos = TRUE;
+
+  return data->ret == GST_FLOW_OK;
 }
 
-/**
- * gst_pad_dispatcher:
- * @pad: a #GstPad to dispatch.
- * @dispatch: the #GstPadDispatcherFunction to call.
- * @data: (closure): gpointer user data passed to the dispatcher function.
- *
- * 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.
- *
- * Returns: TRUE if one of the dispatcher functions returned TRUE.
- */
-gboolean
-gst_pad_dispatcher (GstPad * pad, GstPadDispatcherFunction dispatch,
-    gpointer data)
+/* check sticky events and push them when needed. should be called
+ * with pad LOCK */
+static inline GstFlowReturn
+check_sticky (GstPad * pad)
 {
-  gboolean res = FALSE;
-  GstIterator *iter = NULL;
-  gboolean done = FALSE;
-  gpointer item;
+  PushStickyData data = { GST_FLOW_OK, FALSE };
 
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (dispatch != NULL, FALSE);
+  if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
 
-  iter = gst_pad_iterate_internal_links (pad);
+    GST_DEBUG_OBJECT (pad, "pushing all sticky events");
+    events_foreach (pad, push_sticky, &data);
 
-  if (!iter)
-    goto no_iter;
+    /* If there's an EOS event we must push it downstream
+     * even if sending a previous sticky event failed.
+     * Otherwise the pipeline might wait forever for EOS.
+     *
+     * Only do this if pushing another event than the EOS
+     * event failed.
+     */
+    if (data.ret != GST_FLOW_OK && !data.was_eos) {
+      PadEvent *ev = find_event_by_type (pad, GST_EVENT_EOS, 0);
 
-  while (!done) {
-    switch (gst_iterator_next (iter, &item)) {
-      case GST_ITERATOR_OK:
-      {
-        GstPad *int_pad = GST_PAD_CAST (item);
-        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));
-          done = res = dispatch (int_peer, data);
-          gst_object_unref (int_peer);
-        } else {
-          GST_DEBUG_OBJECT (int_pad, "no peer");
-        }
+      if (ev && !ev->received) {
+        data.ret = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
+            GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
       }
-        gst_object_unref (item);
-        break;
-      case GST_ITERATOR_RESYNC:
-        gst_iterator_resync (iter);
-        break;
-      case GST_ITERATOR_ERROR:
-        done = TRUE;
-        GST_ERROR_OBJECT (pad, "Could not iterate internally linked pads");
-        break;
-      case GST_ITERATOR_DONE:
-        done = TRUE;
-        break;
     }
   }
-  gst_iterator_free (iter);
-
-  GST_DEBUG_OBJECT (pad, "done, result %d", res);
-
-no_iter:
-
-  return res;
+  return data.ret;
 }
 
+
 /**
  * gst_pad_query:
  * @pad: a #GstPad to invoke the default query on.
@@ -3750,23 +3330,114 @@ no_iter:
 gboolean
 gst_pad_query (GstPad * pad, GstQuery * query)
 {
+  GstObject *parent;
+  gboolean res, serialized;
   GstPadQueryFunction func;
+  GstPadProbeType type;
+  GstFlowReturn ret;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
 
-  GST_DEBUG_OBJECT (pad, "sending query %p", query);
+  if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_QUERY_IS_UPSTREAM (query)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_QUERY_UPSTREAM;
+  } else if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_QUERY_IS_DOWNSTREAM (query)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM;
+  } else
+    goto unknown_direction;
+
+  GST_DEBUG_OBJECT (pad, "doing query %p (%s)", query,
+      GST_QUERY_TYPE_NAME (query));
+
+  serialized = GST_QUERY_IS_SERIALIZED (query);
+  if (G_UNLIKELY (serialized))
+    GST_PAD_STREAM_LOCK (pad);
+
+  GST_OBJECT_LOCK (pad);
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH |
+      GST_PAD_PROBE_TYPE_BLOCK, query, probe_stopped);
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, query, probe_stopped);
+
+  ACQUIRE_PARENT (pad, parent, no_parent);
+  GST_OBJECT_UNLOCK (pad);
 
   if ((func = GST_PAD_QUERYFUNC (pad)) == NULL)
     goto no_func;
 
-  return func (pad, query);
+  res = func (pad, parent, query);
+
+  RELEASE_PARENT (parent);
+
+  GST_DEBUG_OBJECT (pad, "sent query %p (%s), result %d", query,
+      GST_QUERY_TYPE_NAME (query), res);
+
+  if (res != TRUE)
+    goto query_failed;
+
+  GST_OBJECT_LOCK (pad);
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PULL, query, probe_stopped);
+  GST_OBJECT_UNLOCK (pad);
+
+  if (G_UNLIKELY (serialized))
+    GST_PAD_STREAM_UNLOCK (pad);
+
+  return res;
 
+  /* ERRORS */
+wrong_direction:
+  {
+    g_warning ("pad %s:%s query %s in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_QUERY_TYPE_NAME (query));
+    return FALSE;
+  }
+unknown_direction:
+  {
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+    return FALSE;
+  }
+no_parent:
+  {
+    GST_DEBUG_OBJECT (pad, "had no parent");
+    GST_OBJECT_UNLOCK (pad);
+    if (G_UNLIKELY (serialized))
+      GST_PAD_STREAM_UNLOCK (pad);
+    return FALSE;
+  }
 no_func:
   {
     GST_DEBUG_OBJECT (pad, "had no query function");
+    RELEASE_PARENT (parent);
+    if (G_UNLIKELY (serialized))
+      GST_PAD_STREAM_UNLOCK (pad);
+    return FALSE;
+  }
+query_failed:
+  {
+    GST_DEBUG_OBJECT (pad, "query failed");
+    if (G_UNLIKELY (serialized))
+      GST_PAD_STREAM_UNLOCK (pad);
     return FALSE;
   }
+probe_stopped:
+  {
+    GST_DEBUG_OBJECT (pad, "probe stopped: %s", gst_flow_get_name (ret));
+    GST_OBJECT_UNLOCK (pad);
+    if (G_UNLIKELY (serialized))
+      GST_PAD_STREAM_UNLOCK (pad);
+
+    /* if a probe dropped, we don't sent it further but assume that the probe
+     * answered the query and return TRUE */
+    if (ret == GST_FLOW_CUSTOM_SUCCESS)
+      res = TRUE;
+    else
+      res = FALSE;
+
+    return res;
+  }
 }
 
 /**
@@ -3788,14 +3459,40 @@ gboolean
 gst_pad_peer_query (GstPad * pad, GstQuery * query)
 {
   GstPad *peerpad;
-  gboolean result;
+  GstPadProbeType type;
+  gboolean res, serialized;
+  GstFlowReturn ret;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
 
+  if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_QUERY_IS_DOWNSTREAM (query)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM;
+  } else if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_QUERY_IS_UPSTREAM (query)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_QUERY_UPSTREAM;
+  } else
+    goto unknown_direction;
+
+  GST_DEBUG_OBJECT (pad, "peer query %p (%s)", query,
+      GST_QUERY_TYPE_NAME (query));
+
+  serialized = GST_QUERY_IS_SERIALIZED (query);
+
   GST_OBJECT_LOCK (pad);
+  if (GST_PAD_IS_SRC (pad) && serialized) {
+    /* all serialized queries on the srcpad trigger push of
+     * sticky events */
+    if (!check_sticky (pad) == GST_FLOW_OK)
+      goto sticky_failed;
+  }
 
-  GST_DEBUG_OBJECT (pad, "peer query");
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH |
+      GST_PAD_PROBE_TYPE_BLOCK, query, probe_stopped);
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, query, probe_stopped);
 
   peerpad = GST_PAD_PEER (pad);
   if (G_UNLIKELY (peerpad == NULL))
@@ -3804,334 +3501,61 @@ gst_pad_peer_query (GstPad * pad, GstQuery * query)
   gst_object_ref (peerpad);
   GST_OBJECT_UNLOCK (pad);
 
-  result = gst_pad_query (peerpad, query);
+  res = gst_pad_query (peerpad, query);
 
   gst_object_unref (peerpad);
 
-  return result;
+  if (res != TRUE)
+    goto query_failed;
+
+  GST_OBJECT_LOCK (pad);
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PULL, query, probe_stopped);
+  GST_OBJECT_UNLOCK (pad);
+
+  return res;
 
   /* ERRORS */
-no_peer:
+wrong_direction:
   {
-    GST_WARNING_OBJECT (pad, "pad has no peer");
-    GST_OBJECT_UNLOCK (pad);
+    g_warning ("pad %s:%s query %s in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_QUERY_TYPE_NAME (query));
     return FALSE;
   }
-}
-
-/**
- * gst_pad_query_default:
- * @pad: a #GstPad to call the default query handler on.
- * @query: (transfer none): 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
- * @pad, only one will be sent the query.
- * Multi-sinkpad elements should implement custom query handlers.
- *
- * Returns: TRUE if the query was performed successfully.
- */
-gboolean
-gst_pad_query_default (GstPad * pad, GstQuery * query)
-{
-  switch (GST_QUERY_TYPE (query)) {
-    case GST_QUERY_POSITION:
-    case GST_QUERY_SEEKING:
-    case GST_QUERY_FORMATS:
-    case GST_QUERY_LATENCY:
-    case GST_QUERY_JITTER:
-    case GST_QUERY_RATE:
-    case GST_QUERY_CONVERT:
-    default:
-      return gst_pad_dispatcher
-          (pad, (GstPadDispatcherFunction) gst_pad_query, query);
-  }
-}
-
-#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
-/* FIXME: why isn't this on a GstElement ? */
-/**
- * gst_pad_load_and_link:
- * @self: an #xmlNodePtr to read the description from.
- * @parent: the #GstObject element that owns the pad.
- *
- * Reads the pad definition from the XML node and links the given pad
- * in the element to a pad of an element up in the hierarchy.
- */
-void
-gst_pad_load_and_link (xmlNodePtr self, GstObject * parent)
-{
-  xmlNodePtr field = self->xmlChildrenNode;
-  GstPad *pad = NULL, *targetpad;
-  GstPadTemplate *tmpl;
-  gchar *peer = NULL;
-  gchar **split;
-  GstElement *target;
-  GstObject *grandparent;
-  gchar *name = NULL;
-
-  while (field) {
-    if (!strcmp ((char *) field->name, "name")) {
-      name = (gchar *) xmlNodeGetContent (field);
-      pad = gst_element_get_static_pad (GST_ELEMENT (parent), name);
-      if ((!pad) || ((tmpl = gst_pad_get_pad_template (pad))
-              && (GST_PAD_REQUEST == GST_PAD_TEMPLATE_PRESENCE (tmpl))))
-        pad = gst_element_get_request_pad (GST_ELEMENT (parent), name);
-      g_free (name);
-    } else if (!strcmp ((char *) field->name, "peer")) {
-      peer = (gchar *) xmlNodeGetContent (field);
-    }
-    field = field->next;
-  }
-  g_return_if_fail (pad != NULL);
-
-  if (peer == NULL)
-    return;
-
-  split = g_strsplit (peer, ".", 2);
-
-  if (split[0] == NULL || split[1] == NULL) {
-    GST_CAT_DEBUG_OBJECT (GST_CAT_XML, pad,
-        "Could not parse peer '%s', leaving unlinked", peer);
-
-    g_free (peer);
-    return;
-  }
-  g_free (peer);
-
-  g_return_if_fail (split[0] != NULL);
-  g_return_if_fail (split[1] != NULL);
-
-  grandparent = gst_object_get_parent (parent);
-
-  if (grandparent && GST_IS_BIN (grandparent)) {
-    target = gst_bin_get_by_name_recurse_up (GST_BIN (grandparent), split[0]);
-  } else
-    goto cleanup;
-
-  if (target == NULL)
-    goto cleanup;
-
-  targetpad = gst_element_get_static_pad (target, split[1]);
-  if (!targetpad)
-    targetpad = gst_element_get_request_pad (target, split[1]);
-
-  if (targetpad == NULL)
-    goto cleanup;
-
-  if (gst_pad_get_direction (pad) == GST_PAD_SRC)
-    gst_pad_link (pad, targetpad);
-  else
-    gst_pad_link (targetpad, pad);
-
-cleanup:
-  g_strfreev (split);
-}
-
-/**
- * gst_pad_save_thyself:
- * @pad: a #GstPad to save.
- * @parent: the parent #xmlNodePtr to save the description in.
- *
- * Saves the pad into an xml representation.
- *
- * Returns: the #xmlNodePtr representation of the pad.
- */
-static xmlNodePtr
-gst_pad_save_thyself (GstObject * object, xmlNodePtr parent)
-{
-  GstPad *pad;
-  GstPad *peer;
-
-  g_return_val_if_fail (GST_IS_PAD (object), NULL);
-
-  pad = GST_PAD_CAST (object);
-
-  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");
+unknown_direction:
+  {
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+    return FALSE;
   }
-
-  if (GST_PAD_PEER (pad) != NULL) {
-    gchar *content;
-
-    peer = GST_PAD_PEER (pad);
-    /* first check to see if the peer's parent's parent is the same */
-    /* we just save it off */
-    content = g_strdup_printf ("%s.%s",
-        GST_OBJECT_NAME (GST_PAD_PARENT (peer)), GST_PAD_NAME (peer));
-    xmlNewChild (parent, NULL, (xmlChar *) "peer", (xmlChar *) content);
-    g_free (content);
-  } else
-    xmlNewChild (parent, NULL, (xmlChar *) "peer", NULL);
-
-  return parent;
-}
-
-#if 0
-/**
- * gst_ghost_pad_save_thyself:
- * @pad: a ghost #GstPad to save.
- * @parent: the parent #xmlNodePtr to save the description in.
- *
- * Saves the ghost pad into an xml representation.
- *
- * Returns: the #xmlNodePtr representation of the pad.
- */
-xmlNodePtr
-gst_ghost_pad_save_thyself (GstPad * pad, xmlNodePtr parent)
-{
-  xmlNodePtr self;
-
-  g_return_val_if_fail (GST_IS_GHOST_PAD (pad), NULL);
-
-  self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
-  xmlNewChild (self, NULL, (xmlChar *) "name", (xmlChar *) GST_PAD_NAME (pad));
-  xmlNewChild (self, NULL, (xmlChar *) "parent",
-      (xmlChar *) GST_OBJECT_NAME (GST_PAD_PARENT (pad)));
-
-  /* FIXME FIXME FIXME! */
-
-  return self;
-}
-#endif /* 0 */
-#endif /* GST_DISABLE_LOADSAVE */
-
-/*
- * 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.
- */
-static GstFlowReturn
-handle_pad_block (GstPad * pad)
-{
-  GstPadBlockCallback callback;
-  gpointer user_data;
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  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;
-
-  /* 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);
-
-  while (GST_PAD_IS_BLOCKED (pad)) {
-    do {
-      /* we either have a callback installed to notify the block or
-       * some other thread is doing a GCond wait. */
-      callback = pad->block_callback;
-      pad->abidata.ABI.block_callback_called = TRUE;
-      if (callback) {
-        /* there is a callback installed, call it. We release the
-         * lock so that the callback can do something useful 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 {
-        /* no callback, signal the thread that is doing a GCond wait
-         * if any. */
-        GST_PAD_BLOCK_BROADCAST (pad);
-      }
-    } while (pad->abidata.ABI.block_callback_called == FALSE
-        && GST_PAD_IS_BLOCKED (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.   */
-    if (!GST_PAD_IS_BLOCKED (pad))
-      break;
-
-    /* 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;
+sticky_failed:
+  {
+    GST_WARNING_OBJECT (pad, "could not send sticky events");
+    GST_OBJECT_UNLOCK (pad);
+    return FALSE;
   }
-
-  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;
+no_peer:
+  {
+    GST_WARNING_OBJECT (pad, "pad has no peer");
     GST_OBJECT_UNLOCK (pad);
-    callback (pad, FALSE, user_data);
-    GST_OBJECT_LOCK (pad);
-  } else {
-    /* we need to signal the thread waiting on the GCond */
-    GST_PAD_BLOCK_BROADCAST (pad);
+    return FALSE;
   }
-
-  gst_object_unref (pad);
-
-  return ret;
-
-flushingnonref:
+query_failed:
   {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad was flushing");
-    return GST_FLOW_WRONG_STATE;
+    GST_DEBUG_OBJECT (pad, "query failed");
+    return FALSE;
   }
-flushing:
+probe_stopped:
   {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad became flushing");
-    gst_object_unref (pad);
-    return GST_FLOW_WRONG_STATE;
+    GST_DEBUG_OBJECT (pad, "probe stopped: %s", gst_flow_get_name (ret));
+    GST_OBJECT_UNLOCK (pad);
+
+    /* if a probe dropped, we don't sent it further but assume that the probe
+     * answered the query and return TRUE */
+    if (ret == GST_FLOW_CUSTOM_SUCCESS)
+      res = TRUE;
+    else
+      res = FALSE;
+
+    return res;
   }
 }
 
@@ -4139,78 +3563,14 @@ flushing:
  * Data passing functions
  */
 
-static gboolean
-gst_pad_emit_have_data_signal (GstPad * pad, GstMiniObject * obj)
-{
-  GValue ret = { 0 };
-  GValue args[2] = { {0}, {0} };
-  gboolean res;
-  GQuark detail;
-
-  /* init */
-  g_value_init (&ret, G_TYPE_BOOLEAN);
-  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);
-  gst_value_set_mini_object (&args[1], obj);
-
-  if (GST_IS_EVENT (obj))
-    detail = event_quark;
-  else
-    detail = buffer_quark;
-
-  /* actually emit */
-  g_signal_emitv (args, gst_pad_signals[PAD_HAVE_DATA], detail, &ret);
-  res = g_value_get_boolean (&ret);
-
-  /* clean up */
-  g_value_unset (&ret);
-  g_value_unset (&args[0]);
-  g_value_unset (&args[1]);
-
-  return res;
-}
-
-static void
-gst_pad_data_unref (gboolean is_buffer, void *data)
-{
-  if (G_LIKELY (is_buffer)) {
-    gst_buffer_unref (data);
-  } else {
-    gst_buffer_list_unref (data);
-  }
-}
-
-static GstCaps *
-gst_pad_data_get_caps (gboolean is_buffer, void *data)
-{
-  GstCaps *caps;
-
-  if (G_LIKELY (is_buffer)) {
-    caps = GST_BUFFER_CAPS (data);
-  } else {
-    GstBuffer *buf;
-
-    if ((buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (data), 0, 0)))
-      caps = GST_BUFFER_CAPS (buf);
-    else
-      caps = NULL;
-  }
-  return caps;
-}
-
 /* this is the chain function that does not perform the additional argument
  * checking for that little extra speed.
  */
 static inline GstFlowReturn
-gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data,
-    GstPadPushCache * cache)
+gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
 {
-  GstCaps *caps;
-  gboolean caps_changed;
   GstFlowReturn ret;
-  gboolean emit_signal;
+  GstObject *parent;
 
   GST_PAD_STREAM_LOCK (pad);
 
@@ -4218,39 +3578,25 @@ gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data,
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
-  caps = gst_pad_data_get_caps (is_buffer, data);
-  caps_changed = caps && caps != GST_PAD_CAPS (pad);
+  if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+    goto eos;
 
-  emit_signal = GST_PAD_DO_BUFFER_SIGNALS (pad) > 0;
-  GST_OBJECT_UNLOCK (pad);
+  if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
+    goto wrong_mode;
 
-  /* see if the signal should be emited, we emit before caps nego as
-   * we might drop the buffer and do capsnego for nothing. */
-  if (G_UNLIKELY (emit_signal)) {
-    cache = NULL;
-    if (G_LIKELY (is_buffer)) {
-      if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (data)))
-        goto dropping;
-    } else {
-      /* chain all groups in the buffer list one by one to avoid problems with
-       * buffer probes that push buffers or events */
-      goto chain_groups;
-    }
-  }
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
 
-  /* 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 to %p %" GST_PTR_FORMAT, caps, caps);
-    if (G_UNLIKELY (!gst_pad_configure_sink (pad, caps)))
-      goto not_negotiated;
-  }
+  PROBE_PUSH (pad, type, data, probe_stopped);
+
+  parent = GST_OBJECT_PARENT (pad);
+  GST_OBJECT_UNLOCK (pad);
 
   /* NOTE: we read the chainfunc unlocked.
    * we cannot hold the lock for the pad so we might send
    * the data to the wrong function. This is not really a
    * problem since functions are assigned at creation time
    * and don't change that often... */
-  if (G_LIKELY (is_buffer)) {
+  if (G_LIKELY (type & GST_PAD_PROBE_TYPE_BUFFER)) {
     GstPadChainFunction chainfunc;
 
     if (G_UNLIKELY ((chainfunc = GST_PAD_CHAINFUNC (pad)) == NULL))
@@ -4260,12 +3606,7 @@ gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data,
         "calling chainfunction &%s with buffer %" GST_PTR_FORMAT,
         GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_BUFFER (data));
 
-    if (cache) {
-      cache->peer = gst_object_ref (pad);
-      cache->caps = caps ? gst_caps_ref (caps) : NULL;
-    }
-
-    ret = chainfunc (pad, GST_BUFFER_CAST (data));
+    ret = chainfunc (pad, parent, GST_BUFFER_CAST (data));
 
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "called chainfunction &%s with buffer %p, returned %s",
@@ -4274,13 +3615,13 @@ gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data,
     GstPadChainListFunction chainlistfunc;
 
     if (G_UNLIKELY ((chainlistfunc = GST_PAD_CHAINLISTFUNC (pad)) == NULL))
-      goto chain_groups;
+      goto no_function;
 
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "calling chainlistfunction &%s",
         GST_DEBUG_FUNCPTR_NAME (chainlistfunc));
 
-    ret = chainlistfunc (pad, GST_BUFFER_LIST_CAST (data));
+    ret = chainlistfunc (pad, parent, GST_BUFFER_LIST_CAST (data));
 
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "called chainlistfunction &%s, returned %s",
@@ -4291,74 +3632,55 @@ gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data,
 
   return ret;
 
-chain_groups:
-  {
-    GstBufferList *list;
-    GstBufferListIterator *it;
-    GstBuffer *group;
-
-    GST_PAD_STREAM_UNLOCK (pad);
-
-    GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
-
-    list = GST_BUFFER_LIST_CAST (data);
-    it = gst_buffer_list_iterate (list);
-
-    if (gst_buffer_list_iterator_next_group (it)) {
-      do {
-        group = gst_buffer_list_iterator_merge_group (it);
-        if (group == NULL) {
-          group = gst_buffer_new ();
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
-        } else {
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining group");
-        }
-        ret = gst_pad_chain_data_unchecked (pad, TRUE, group, NULL);
-      } while (ret == GST_FLOW_OK && gst_buffer_list_iterator_next_group (it));
-    } else {
-      GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
-      ret = gst_pad_chain_data_unchecked (pad, TRUE, gst_buffer_new (), NULL);
-    }
-
-    gst_buffer_list_iterator_free (it);
-    gst_buffer_list_unref (list);
-
-    return ret;
-  }
-
   /* ERRORS */
 flushing:
   {
-    gst_pad_data_unref (is_buffer, data);
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pushing, but pad was flushing");
+        "chaining, but pad was flushing");
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_WRONG_STATE;
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_FLUSHING;
   }
-dropping:
+eos:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_DEBUG_OBJECT (pad, "Dropping buffer due to FALSE probe return");
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "chaining, but pad was EOS");
+    GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_OK;
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_EOS;
   }
-not_negotiated:
+wrong_mode:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pushing data but pad did not accept");
+    g_critical ("chain on pad %s:%s but it was not in push mode",
+        GST_DEBUG_PAD_NAME (pad));
+    GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_NOT_NEGOTIATED;
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_ERROR;
+  }
+probe_stopped:
+  {
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped buffer");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
   }
 no_function:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pushing, but not chainhandler");
-    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
-        ("push on pad %s:%s but it has no chainfunction",
-            GST_DEBUG_PAD_NAME (pad)));
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    g_critical ("chain on pad %s:%s but it has no chainfunction",
+        GST_DEBUG_PAD_NAME (pad));
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_NOT_SUPPORTED;
   }
@@ -4372,12 +3694,11 @@ no_function:
  *
  * Chain a buffer to @pad.
  *
- * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ * The function returns #GST_FLOW_FLUSHING 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.
+ * If the buffer type is not acceptable for @pad (as negotiated with a
+ * preceeding GST_EVENT_CAPS event), 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
@@ -4398,7 +3719,35 @@ gst_pad_chain (GstPad * pad, GstBuffer * buffer)
   g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
   g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
 
-  return gst_pad_chain_data_unchecked (pad, TRUE, buffer, NULL);
+  return gst_pad_chain_data_unchecked (pad,
+      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH, buffer);
+}
+
+static GstFlowReturn
+gst_pad_chain_list_default (GstPad * pad, GstObject * parent,
+    GstBufferList * list)
+{
+  guint i, len;
+  GstBuffer *buffer;
+  GstFlowReturn ret;
+
+  GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
+
+  len = gst_buffer_list_length (list);
+
+  ret = GST_FLOW_OK;
+  for (i = 0; i < len; i++) {
+    buffer = gst_buffer_list_get (list, i);
+    ret =
+        gst_pad_chain_data_unchecked (pad,
+        GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH,
+        gst_buffer_ref (buffer));
+    if (ret != GST_FLOW_OK)
+      break;
+  }
+  gst_buffer_list_unref (list);
+
+  return ret;
 }
 
 /**
@@ -4409,12 +3758,10 @@ gst_pad_chain (GstPad * pad, GstBuffer * buffer)
  *
  * Chain a bufferlist to @pad.
  *
- * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ * The function returns #GST_FLOW_FLUSHING if the pad was flushing.
  *
- * If the caps on the first buffer of @list 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.
+ * If @pad was not negotiated properly with a CAPS event, this function
+ * returns #GST_FLOW_NOT_NEGOTIATED.
  *
  * The function proceeds calling the chainlist function installed on @pad (see
  * gst_pad_set_chain_list_function()) and the return value of that function is
@@ -4437,207 +3784,115 @@ gst_pad_chain_list (GstPad * pad, GstBufferList * list)
   g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
 
-  return gst_pad_chain_data_unchecked (pad, FALSE, list, NULL);
+  return gst_pad_chain_data_unchecked (pad,
+      GST_PAD_PROBE_TYPE_BUFFER_LIST | GST_PAD_PROBE_TYPE_PUSH, list);
 }
 
 static GstFlowReturn
-gst_pad_push_data (GstPad * pad, gboolean is_buffer, void *data,
-    GstPadPushCache * cache)
+gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
 {
   GstPad *peer;
   GstFlowReturn ret;
-  GstCaps *caps;
-  gboolean caps_changed;
 
   GST_OBJECT_LOCK (pad);
+  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+    goto flushing;
 
-  /* FIXME: this check can go away; pad_set_blocked could be implemented with
-   * 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;
+  if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+    goto eos;
 
-  /* we emit signals on the pad arg, the peer will have a chance to
-   * emit in the _chain() function */
-  if (G_UNLIKELY (GST_PAD_DO_BUFFER_SIGNALS (pad) > 0)) {
-    cache = NULL;
-    /* unlock before emitting */
-    GST_OBJECT_UNLOCK (pad);
+  if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
+    goto wrong_mode;
 
-    if (G_LIKELY (is_buffer)) {
-      /* if the signal handler returned FALSE, it means we should just drop the
-       * buffer */
-      if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (data)))
-        goto dropped;
-    } else {
-      /* push all buffers in the list */
-      goto push_groups;
-    }
-    GST_OBJECT_LOCK (pad);
-  }
+  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+    goto events_error;
 
-  /* Before pushing the buffer to the peer pad, ensure that caps
-   * are set on this pad */
-  caps = gst_pad_data_get_caps (is_buffer, data);
-  caps_changed = caps && caps != GST_PAD_CAPS (pad);
+  /* do block probes */
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
 
-  /* we got a new datatype from the pad, it had better handle it */
-  if (G_UNLIKELY (caps_changed)) {
-    /* unlock before setting */
-    GST_OBJECT_UNLOCK (pad);
-    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_set_caps (pad, caps)))
-      goto not_negotiated;
-    GST_OBJECT_LOCK (pad);
-  }
+  /* do post-blocking probes */
+  PROBE_PUSH (pad, type, data, probe_stopped);
 
   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);
+  pad->priv->using++;
   GST_OBJECT_UNLOCK (pad);
 
-  ret = gst_pad_chain_data_unchecked (peer, is_buffer, data, cache);
+  ret = gst_pad_chain_data_unchecked (peer, type, data);
 
   gst_object_unref (peer);
 
+  GST_OBJECT_LOCK (pad);
+  pad->priv->using--;
+  if (pad->priv->using == 0) {
+    /* pad is not active anymore, trigger idle callbacks */
+    PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_IDLE,
+        probe_stopped, ret);
+  }
+  GST_OBJECT_UNLOCK (pad);
+
   return ret;
 
-push_groups:
+  /* ERROR recovery here */
+  /* ERRORS */
+flushing:
   {
-    GstBufferList *list;
-    GstBufferListIterator *it;
-    GstBuffer *group;
-
-    GST_INFO_OBJECT (pad, "pushing each group in list as a merged buffer");
-
-    list = GST_BUFFER_LIST_CAST (data);
-    it = gst_buffer_list_iterate (list);
-
-    if (gst_buffer_list_iterator_next_group (it)) {
-      do {
-        group = gst_buffer_list_iterator_merge_group (it);
-        if (group == NULL) {
-          group = gst_buffer_new ();
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "pushing empty group");
-        } else {
-          GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "pushing group");
-        }
-        ret = gst_pad_push_data (pad, TRUE, group, NULL);
-      } while (ret == GST_FLOW_OK && gst_buffer_list_iterator_next_group (it));
-    } else {
-      GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "pushing empty group");
-      ret = gst_pad_push_data (pad, TRUE, gst_buffer_new (), NULL);
-    }
-
-    gst_buffer_list_iterator_free (it);
-    gst_buffer_list_unref (list);
-
-    return ret;
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "pushing, but pad was flushing");
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_FLUSHING;
   }
-
-  /* ERROR recovery here */
-flushed:
+eos:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_DEBUG_OBJECT (pad, "pad block stopped by flush");
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was EOS");
     GST_OBJECT_UNLOCK (pad);
-    return ret;
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_EOS;
   }
-dropped:
+wrong_mode:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_DEBUG_OBJECT (pad, "Dropping buffer due to FALSE probe return");
-    return GST_FLOW_OK;
+    g_critical ("pushing on pad %s:%s but it was not activated in push mode",
+        GST_DEBUG_PAD_NAME (pad));
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_ERROR;
   }
-not_linked:
+events_error:
   {
-    gst_pad_data_unref (is_buffer, data);
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pushing, but it was not linked");
+        "error pushing events, return %s", gst_flow_get_name (ret));
     GST_OBJECT_UNLOCK (pad);
-    return GST_FLOW_NOT_LINKED;
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return ret;
   }
-not_negotiated:
+probe_stopped:
   {
-    gst_pad_data_unref (is_buffer, data);
-    GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "element pushed data then refused to accept the caps");
-    return GST_FLOW_NOT_NEGOTIATED;
-  }
-}
-
-static inline GstPadPushCache *
-pad_take_cache (GstPad * pad, gpointer * cache_ptr)
-{
-  GstPadPushCache *cache;
-
-  /* try to get the cached data */
-  do {
-    cache = g_atomic_pointer_get (cache_ptr);
-    /* now try to replace the pointer with NULL to mark that we are busy
-     * with it */
-  } while (!g_atomic_pointer_compare_and_exchange (cache_ptr, cache, NULL));
-
-  /* we could have a leftover invalid entry */
-  if (G_UNLIKELY (cache == PAD_CACHE_INVALID))
-    cache = NULL;
-
-  return cache;
-}
-
-static inline void
-pad_free_cache (GstPadPushCache * cache)
-{
-  gst_object_unref (cache->peer);
-  if (cache->caps)
-    gst_caps_unref (cache->caps);
-  g_slice_free (GstPadPushCache, cache);
-}
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
 
-static inline void
-pad_put_cache (GstPad * pad, GstPadPushCache * cache, gpointer * cache_ptr)
-{
-  /* put it back */
-  if (!g_atomic_pointer_compare_and_exchange (cache_ptr, NULL, cache)) {
-    /* something changed, clean up our cache */
-    pad_free_cache (cache);
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped buffer");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
   }
-}
-
-/* must be called with the pad lock */
-void
-_priv_gst_pad_invalidate_cache (GstPad * pad)
-{
-  GstPadPushCache *cache;
-  gpointer *cache_ptr;
-
-  GST_LOG_OBJECT (pad, "Invalidating pad cache");
-
-  /* we hold the pad lock here so we can get the peer and it stays
-   * alive during this call */
-  if (GST_PAD_IS_SINK (pad)) {
-    if (!(pad = GST_PAD_PEER (pad)))
-      return;
+not_linked:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "pushing, but it was not linked");
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_NOT_LINKED;
   }
-
-  cache_ptr = (gpointer *) & pad->abidata.ABI.priv->cache_ptr;
-
-  /* try to get the cached data */
-  do {
-    cache = g_atomic_pointer_get (cache_ptr);
-    /* now try to replace the pointer with INVALID. If nothing is busy with this
-     * caps, we get the cache and clean it up. If something is busy, we replace
-     * with INVALID so that when the function finishes and tries to put the
-     * cache back, it'll fail and cleanup */
-  } while (!g_atomic_pointer_compare_and_exchange (cache_ptr, cache,
-          PAD_CACHE_INVALID));
-
-  if (G_LIKELY (cache && cache != PAD_CACHE_INVALID))
-    pad_free_cache (cache);
 }
 
 /**
@@ -4648,13 +3903,8 @@ _priv_gst_pad_invalidate_cache (GstPad * pad)
  *
  * Pushes a buffer to the peer of @pad.
  *
- * 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.
+ * This function will call installed block probes before triggering any
+ * installed data probes.
  *
  * 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
@@ -4670,81 +3920,12 @@ _priv_gst_pad_invalidate_cache (GstPad * pad)
 GstFlowReturn
 gst_pad_push (GstPad * pad, GstBuffer * buffer)
 {
-  GstPadPushCache *cache;
-  GstFlowReturn ret;
-  gpointer *cache_ptr;
-  GstPad *peer;
-  GstCaps *caps;
-
   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 (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
 
-  cache_ptr = (gpointer *) & pad->abidata.ABI.priv->cache_ptr;
-
-  cache = pad_take_cache (pad, cache_ptr);
-
-  if (G_UNLIKELY (cache == NULL))
-    goto slow_path;
-
-  /* check caps */
-  caps = GST_BUFFER_CAPS (buffer);
-  if (G_UNLIKELY (caps && caps != cache->caps)) {
-    pad_free_cache (cache);
-    goto slow_path;
-  }
-
-  peer = cache->peer;
-
-  GST_PAD_STREAM_LOCK (peer);
-  if (G_UNLIKELY (g_atomic_pointer_get (cache_ptr) == PAD_CACHE_INVALID))
-    goto invalid;
-
-  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "calling chainfunction &%s with buffer %" GST_PTR_FORMAT,
-      GST_DEBUG_FUNCPTR_NAME (GST_PAD_CHAINFUNC (peer)), buffer);
-
-  ret = GST_PAD_CHAINFUNC (peer) (peer, buffer);
-
-  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "called chainfunction &%s with buffer %p, returned %s",
-      GST_DEBUG_FUNCPTR_NAME (GST_PAD_CHAINFUNC (peer)), buffer,
-      gst_flow_get_name (ret));
-
-  GST_PAD_STREAM_UNLOCK (peer);
-
-  pad_put_cache (pad, cache, cache_ptr);
-
-  return ret;
-
-  /* slow path */
-slow_path:
-  {
-    GstPadPushCache scache = { NULL, };
-
-    GST_LOG_OBJECT (pad, "Taking slow path");
-
-    ret = gst_pad_push_data (pad, TRUE, buffer, &scache);
-
-    if (scache.peer) {
-      GstPadPushCache *ncache;
-
-      GST_LOG_OBJECT (pad, "Caching push data");
-
-      /* make cache structure */
-      ncache = g_slice_new (GstPadPushCache);
-      *ncache = scache;
-
-      pad_put_cache (pad, ncache, cache_ptr);
-    }
-    return ret;
-  }
-invalid:
-  {
-    GST_PAD_STREAM_UNLOCK (peer);
-    pad_free_cache (cache);
-    goto slow_path;
-  }
+  return gst_pad_push_data (pad,
+      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH, buffer);
 }
 
 /**
@@ -4755,18 +3936,8 @@ invalid:
  *
  * Pushes a buffer list to the peer of @pad.
  *
- * This function will call an installed pad block before triggering any
- * installed pad probes.
- *
- * If the caps on the first buffer in the first group of @list 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.
- *
- * If there are any probes installed on @pad every group of the buffer list
- * will be merged into a normal #GstBuffer and pushed via gst_pad_push and the
- * buffer list will be unreffed.
+ * This function will call installed block probes before triggering any
+ * installed data probes.
  *
  * The function proceeds calling the chain function on the peer pad and returns
  * the value from that function. If @pad has no peer, #GST_FLOW_NOT_LINKED will
@@ -4777,156 +3948,21 @@ invalid:
  * In all cases, success or failure, the caller loses its reference to @list
  * after calling this function.
  *
- * Returns: a #GstFlowReturn from the peer pad.
- *
- * MT safe.
- *
- * Since: 0.10.24
- */
-GstFlowReturn
-gst_pad_push_list (GstPad * pad, GstBufferList * list)
-{
-  GstBuffer *buf;
-  GstPadPushCache *cache;
-  GstFlowReturn ret;
-  gpointer *cache_ptr;
-  GstPad *peer;
-  GstCaps *caps;
-
-  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 (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
-
-  cache_ptr = (gpointer *) & pad->abidata.ABI.priv->cache_ptr;
-
-  cache = pad_take_cache (pad, cache_ptr);
-
-  if (G_UNLIKELY (cache == NULL))
-    goto slow_path;
-
-  /* check caps */
-  if ((buf = gst_buffer_list_get (list, 0, 0)))
-    caps = GST_BUFFER_CAPS (buf);
-  else
-    caps = NULL;
-
-  if (G_UNLIKELY (caps && caps != cache->caps)) {
-    pad_free_cache (cache);
-    goto slow_path;
-  }
-
-  peer = cache->peer;
-
-  GST_PAD_STREAM_LOCK (peer);
-  if (G_UNLIKELY (g_atomic_pointer_get (cache_ptr) == PAD_CACHE_INVALID))
-    goto invalid;
-
-  ret = GST_PAD_CHAINLISTFUNC (peer) (peer, list);
-
-  GST_PAD_STREAM_UNLOCK (peer);
-
-  pad_put_cache (pad, cache, cache_ptr);
-
-  return ret;
-
-  /* slow path */
-slow_path:
-  {
-    GstPadPushCache scache = { NULL, };
-
-    GST_LOG_OBJECT (pad, "Taking slow path");
-
-    ret = gst_pad_push_data (pad, FALSE, list, &scache);
-
-    if (scache.peer) {
-      GstPadPushCache *ncache;
-
-      GST_LOG_OBJECT (pad, "Caching push data");
-
-      /* make cache structure */
-      ncache = g_slice_new (GstPadPushCache);
-      *ncache = scache;
-
-      pad_put_cache (pad, ncache, cache_ptr);
-    }
-    return ret;
-  }
-invalid:
-  {
-    GST_PAD_STREAM_UNLOCK (peer);
-    pad_free_cache (cache);
-    goto slow_path;
-  }
-}
-
-/**
- * gst_pad_check_pull_range:
- * @pad: a sink #GstPad.
- *
- * 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.
- *
- * The peer sourcepad can implement a custom #GstPadCheckGetRangeFunction
- * if it needs to perform some logic to determine if pull_range is
- * possible.
- *
- * Returns: a gboolean with the result.
+ * Returns: a #GstFlowReturn from the peer pad.
  *
  * MT safe.
+ *
+ * Since: 0.10.24
  */
-gboolean
-gst_pad_check_pull_range (GstPad * pad)
+GstFlowReturn
+gst_pad_push_list (GstPad * pad, GstBufferList * list)
 {
-  GstPad *peer;
-  gboolean ret;
-  GstPadCheckGetRangeFunction checkgetrangefunc;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-
-  GST_OBJECT_LOCK (pad);
-  if (!GST_PAD_IS_SINK (pad))
-    goto wrong_direction;
-
-  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
-    goto not_connected;
-
-  gst_object_ref (peer);
-  GST_OBJECT_UNLOCK (pad);
-
-  /* see note in above function */
-  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",
-        GST_DEBUG_FUNCPTR_NAME (checkgetrangefunc), GST_DEBUG_PAD_NAME (peer));
-
-    ret = checkgetrangefunc (peer);
-  }
-
-  gst_object_unref (peer);
-
-  return ret;
+  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 (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
 
-  /* 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;
-  }
-not_connected:
-  {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "checking pull range, but it was not linked");
-    GST_OBJECT_UNLOCK (pad);
-    return FALSE;
-  }
+  return gst_pad_push_data (pad,
+      GST_PAD_PROBE_TYPE_BUFFER_LIST | GST_PAD_PROBE_TYPE_PUSH, list);
 }
 
 static GstFlowReturn
@@ -4935,9 +3971,8 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
 {
   GstFlowReturn ret;
   GstPadGetRangeFunction getrangefunc;
-  gboolean emit_signal;
-  GstCaps *caps;
-  gboolean caps_changed;
+  GstObject *parent;
+  GstBuffer *res_buf;
 
   GST_PAD_STREAM_LOCK (pad);
 
@@ -4945,7 +3980,21 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
-  emit_signal = GST_PAD_DO_BUFFER_SIGNALS (pad) > 0;
+  if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
+    goto wrong_mode;
+
+  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+    goto events_error;
+
+  res_buf = *buffer;
+
+  /* when one of the probes returns DROPPED, probe_stopped will be called
+   * and we skip calling the getrange function, res_buf should then contain a
+   * valid result buffer */
+  PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
+      res_buf, offset, size, probe_stopped);
+
+  ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
   if (G_UNLIKELY ((getrangefunc = GST_PAD_GETRANGEFUNC (pad)) == NULL))
@@ -4956,76 +4005,108 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
       G_GUINT64_FORMAT ", size %u",
       GST_DEBUG_FUNCPTR_NAME (getrangefunc), offset, size);
 
-  ret = getrangefunc (pad, offset, size, buffer);
-
-  /* can only fire the signal if we have a valid buffer */
-  if (G_UNLIKELY (emit_signal) && (ret == GST_FLOW_OK)) {
-    if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (*buffer)))
-      goto dropping;
-  }
+  ret = getrangefunc (pad, parent, offset, size, &res_buf);
 
-  GST_PAD_STREAM_UNLOCK (pad);
+  RELEASE_PARENT (parent);
 
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto get_range_failed;
 
+  /* can only fire the signal if we have a valid buffer */
   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);
+probed_data:
+  PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
+      res_buf, offset, size, probe_stopped_unref);
   GST_OBJECT_UNLOCK (pad);
 
-  if (G_UNLIKELY (caps_changed)) {
-    GST_DEBUG_OBJECT (pad, "caps changed to %p %" GST_PTR_FORMAT, caps, caps);
-    /* this should usually work because the element produced the buffer */
-    if (G_UNLIKELY (!gst_pad_configure_src (pad, caps, TRUE)))
-      goto not_negotiated;
-  }
+  GST_PAD_STREAM_UNLOCK (pad);
+
+  *buffer = res_buf;
+
   return ret;
 
   /* ERRORS */
 flushing:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pulling range, but pad was flushing");
+        "getrange, but pad was flushing");
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    g_critical ("getrange on pad %s:%s but it was not activated in pull mode",
+        GST_DEBUG_PAD_NAME (pad));
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    return GST_FLOW_ERROR;
+  }
+events_error:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "error pushing events");
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    return ret;
+  }
+no_parent:
+  {
+    GST_DEBUG_OBJECT (pad, "no parent");
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 no_function:
   {
-    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
-        ("pullrange on pad %s:%s but it has no getrangefunction",
-            GST_DEBUG_PAD_NAME (pad)));
+    g_critical ("getrange on pad %s:%s but it has no getrangefunction",
+        GST_DEBUG_PAD_NAME (pad));
+    RELEASE_PARENT (parent);
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_NOT_SUPPORTED;
   }
-dropping:
+probe_stopped:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "probe returned %s", gst_flow_get_name (ret));
+    if (ret == GST_FLOW_CUSTOM_SUCCESS) {
+      if (res_buf) {
+        /* the probe filled the buffer and asks us to not call the getrange
+         * anymore, we continue with the post probes then. */
+        GST_DEBUG_OBJECT (pad, "handled buffer");
+        ret = GST_FLOW_OK;
+        goto probed_data;
+      } else {
+        /* no buffer, we are EOS */
+        GST_DEBUG_OBJECT (pad, "no buffer, return EOS");
+        ret = GST_FLOW_EOS;
+      }
+    }
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+
+    return ret;
+  }
+probe_stopped_unref:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "Dropping data after FALSE probe return");
+        "probe returned %s", gst_flow_get_name (ret));
+    /* if we drop here, it signals EOS */
+    if (ret == GST_FLOW_CUSTOM_SUCCESS)
+      ret = GST_FLOW_EOS;
+    GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    gst_buffer_unref (*buffer);
-    *buffer = NULL;
-    return GST_FLOW_UNEXPECTED;
+    if (*buffer == NULL)
+      gst_buffer_unref (res_buf);
+    return ret;
   }
 get_range_failed:
   {
-    *buffer = NULL;
+    GST_PAD_STREAM_UNLOCK (pad);
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
-        (ret >= GST_FLOW_UNEXPECTED) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
+        (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
         pad, "getrange failed, flow: %s", gst_flow_get_name (ret));
     return ret;
   }
-not_negotiated:
-  {
-    gst_buffer_unref (*buffer);
-    *buffer = NULL;
-    GST_CAT_WARNING_OBJECT (GST_CAT_SCHEDULING, pad,
-        "getrange returned buffer of unaccaptable caps");
-    return GST_FLOW_NOT_NEGOTIATED;
-  }
 }
 
 /**
@@ -5036,7 +4117,7 @@ not_negotiated:
  * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer,
  *     returns #GST_FLOW_ERROR if %NULL.
  *
- * When @pad is flushing this function returns #GST_FLOW_WRONG_STATE
+ * When @pad is flushing this function returns #GST_FLOW_FLUSHING
  * immediately and @buffer is %NULL.
  *
  * Calls the getrange function of @pad, see #GstPadGetRangeFunction for a
@@ -5044,6 +4125,23 @@ not_negotiated:
  * installed (see gst_pad_set_getrange_function()) this function returns
  * #GST_FLOW_NOT_SUPPORTED.
  *
+ * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be
+ * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer
+ * must be freed with gst_buffer_unref() after usage.
+ *
+ * When @buffer points to a variable that points to a valid #GstBuffer, the
+ * buffer will be filled with the result data when this function returns
+ * #GST_FLOW_OK. If the provided buffer is larger than @size, only
+ * @size bytes will be filled in the result buffer and its size will be updated
+ * accordingly.
+ *
+ * Note that less than @size bytes can be returned in @buffer when, for example,
+ * an EOS condition is near or when @buffer is not large enough to hold @size
+ * bytes. The caller should check the result buffer size to get the result size.
+ *
+ * When this function returns any other result value than #GST_FLOW_OK, @buffer
+ * will be unchanged.
+ *
  * This is a lowlevel function. Usualy gst_pad_pull_range() is used.
  *
  * Returns: a #GstFlowReturn from the pad.
@@ -5057,6 +4155,8 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
   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 (buffer != NULL, GST_FLOW_ERROR);
+  g_return_val_if_fail (*buffer == NULL
+      || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR);
 
   return gst_pad_get_range_unchecked (pad, offset, size, buffer);
 }
@@ -5069,7 +4169,7 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
  * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer, returns
  *     GST_FLOW_ERROR if %NULL.
  *
- * Pulls a @buffer from the peer pad.
+ * Pulls a @buffer from the peer pad or fills up a provided buffer.
  *
  * This function will first trigger the pad block signal if it was
  * installed.
@@ -5079,15 +4179,23 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
  * 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.
+ * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be
+ * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer
+ * must be freed with gst_buffer_unref() after usage. When this function
+ * returns any other result value, @buffer will still point to NULL.
+ *
+ * When @buffer points to a variable that points to a valid #GstBuffer, the
+ * buffer will be filled with the result data when this function returns
+ * #GST_FLOW_OK. When this function returns any other result value,
+ * @buffer will be unchanged. If the provided buffer is larger than @size, only
+ * @size bytes will be filled in the result buffer and its size will be updated
+ * accordingly.
+ *
+ * Note that less than @size bytes can be returned in @buffer when, for example,
+ * an EOS condition is near or when @buffer is not large enough to hold @size
+ * bytes. The caller should check the result buffer size to get the result size.
  *
  * 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.
  */
@@ -5097,59 +4205,97 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
 {
   GstPad *peer;
   GstFlowReturn ret;
-  gboolean emit_signal;
-  GstCaps *caps;
-  gboolean caps_changed;
+  GstBuffer *res_buf;
 
   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
   g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
   g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+  g_return_val_if_fail (*buffer == NULL
+      || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR);
 
   GST_OBJECT_LOCK (pad);
+  if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+    goto flushing;
 
-  while (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad)))
-    handle_pad_block (pad);
+  if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
+    goto wrong_mode;
 
-  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
-    goto not_connected;
+  res_buf = *buffer;
+
+  /* when one of the probes returns DROPPED, probe_stopped will be
+   * called and we skip calling the peer getrange function. *buffer should then
+   * contain a valid buffer */
+  PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
+      res_buf, offset, size, probe_stopped);
 
-  /* signal emision for the pad, peer has chance to emit when
-   * we call _get_range() */
-  emit_signal = GST_PAD_DO_BUFFER_SIGNALS (pad) > 0;
+  if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
+    goto not_linked;
 
   gst_object_ref (peer);
+  pad->priv->using++;
   GST_OBJECT_UNLOCK (pad);
 
-  ret = gst_pad_get_range_unchecked (peer, offset, size, buffer);
+  ret = gst_pad_get_range_unchecked (peer, offset, size, &res_buf);
 
   gst_object_unref (peer);
 
+  GST_OBJECT_LOCK (pad);
+  pad->priv->using--;
+  if (pad->priv->using == 0) {
+    /* pad is not active anymore, trigger idle callbacks */
+    PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_IDLE,
+        probe_stopped_unref, ret);
+  }
+
   if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto pull_range_failed;
 
-  /* can only fire the signal if we have a valid buffer */
-  if (G_UNLIKELY (emit_signal)) {
-    if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT (*buffer)))
-      goto dropping;
-  }
+probed_data:
+  PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
+      res_buf, offset, size, probe_stopped_unref);
 
-  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)) {
-    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;
-  }
+  *buffer = res_buf;
+
   return ret;
 
   /* ERROR recovery here */
-not_connected:
+flushing:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "pullrange, but pad was flushing");
+    GST_OBJECT_UNLOCK (pad);
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    g_critical ("pullrange on pad %s:%s but it was not activated in pull mode",
+        GST_DEBUG_PAD_NAME (pad));
+    GST_OBJECT_UNLOCK (pad);
+    return GST_FLOW_ERROR;
+  }
+probe_stopped:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pre probe returned %s",
+        gst_flow_get_name (ret));
+    if (ret == GST_FLOW_CUSTOM_SUCCESS) {
+      if (res_buf) {
+        /* the probe filled the buffer and asks us to not forward to the peer
+         * anymore, we continue with the post probes then */
+        GST_DEBUG_OBJECT (pad, "handled buffer");
+        ret = GST_FLOW_OK;
+        goto probed_data;
+      } else {
+        /* no buffer, we are EOS then */
+        GST_DEBUG_OBJECT (pad, "no buffer, return EOS");
+        ret = GST_FLOW_EOS;
+      }
+    }
+    GST_OBJECT_UNLOCK (pad);
+    return ret;
+  }
+not_linked:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pulling range, but it was not linked");
@@ -5158,219 +4304,385 @@ not_connected:
   }
 pull_range_failed:
   {
-    *buffer = NULL;
+    GST_OBJECT_UNLOCK (pad);
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
-        (ret >= GST_FLOW_UNEXPECTED) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
+        (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
         pad, "pullrange failed, flow: %s", gst_flow_get_name (ret));
     return ret;
   }
-dropping:
+probe_stopped_unref:
   {
     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:
-  {
-    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;
+        "post probe returned %s", gst_flow_get_name (ret));
+    GST_OBJECT_UNLOCK (pad);
+    /* if we drop here, it signals EOS */
+    if (ret == GST_FLOW_CUSTOM_SUCCESS)
+      ret = GST_FLOW_EOS;
+    if (*buffer == NULL)
+      gst_buffer_unref (res_buf);
+    return ret;
   }
 }
 
-/**
- * gst_pad_push_event:
- * @pad: a #GstPad to push the event to.
- * @event: (transfer full): the #GstEvent to send to the pad.
- *
- * Sends the event to the peer of the given pad. This function is
- * 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.
- */
-gboolean
-gst_pad_push_event (GstPad * pad, GstEvent * event)
+static gboolean
+gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
 {
-  GstPad *peerpad;
-  gboolean result;
+  guint i, len;
+  GstEventType type;
+  GArray *events;
+  gboolean res = FALSE;
+  const gchar *name = NULL;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+  type = GST_EVENT_TYPE (event);
+  if (type & GST_EVENT_TYPE_STICKY_MULTI)
+    name = gst_structure_get_name (gst_event_get_structure (event));
 
-  GST_LOG_OBJECT (pad, "event: %s", GST_EVENT_TYPE_NAME (event));
+  events = pad->priv->events;
+  len = events->len;
 
-  GST_OBJECT_LOCK (pad);
+  for (i = 0; i < len; i++) {
+    PadEvent *ev = &g_array_index (events, PadEvent, i);
+
+    if (ev->event == NULL)
+      continue;
+
+    if (type == GST_EVENT_TYPE (ev->event)) {
+      /* matching types, check matching name if needed */
+      if (name && !gst_event_has_name (ev->event, name))
+        continue;
+
+      /* overwrite */
+      if ((res = gst_event_replace (&ev->event, event)))
+        ev->received = FALSE;
+      break;
+    }
+  }
+  if (i == len) {
+    PadEvent ev;
+    ev.event = gst_event_ref (event);
+    ev.received = FALSE;
+    g_array_append_val (events, ev);
+    res = TRUE;
+  }
+
+  if (res) {
+    pad->priv->events_cookie++;
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+
+    GST_LOG_OBJECT (pad, "stored sticky event %s", GST_EVENT_TYPE_NAME (event));
+
+    switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_CAPS:
+        if (locked)
+          GST_OBJECT_UNLOCK (pad);
+
+        GST_DEBUG_OBJECT (pad, "notify caps");
+        g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
+
+        if (locked)
+          GST_OBJECT_LOCK (pad);
+        break;
+      default:
+        break;
+    }
+  }
+  return res;
+}
+
+/* should be called with pad LOCK */
+static GstFlowReturn
+gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
+    GstPadProbeType type)
+{
+  GstFlowReturn ret;
+  GstPad *peerpad;
+  GstEventType event_type;
 
   /* Two checks to be made:
    * . (un)set the FLUSHING flag for flushing events,
    * . handle pad blocking */
-  switch (GST_EVENT_TYPE (event)) {
+  event_type = GST_EVENT_TYPE (event);
+  switch (event_type) {
     case GST_EVENT_FLUSH_START:
-      _priv_gst_pad_invalidate_cache (pad);
       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;
-      }
+      GST_PAD_BLOCK_BROADCAST (pad);
+      type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       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;
-      }
+      /* Remove sticky EOS events */
+      GST_LOG_OBJECT (pad, "Removing pending EOS events");
+      remove_event_by_type (pad, GST_EVENT_EOS);
+      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
+
+      type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       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;
+    {
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+        goto flushed;
+
+      /* No need to check for EOS here as either the caller (gst_pad_push_event())
+       * checked already or this is called as part of pushing sticky events,
+       * in which case we still want to forward the EOS event downstream.
+       */
+
+      switch (GST_EVENT_TYPE (event)) {
+        case GST_EVENT_SEGMENT:
+          /* pass the adjusted segment event on. We need to do this even if
+           * there is no peer pad because of the probes. */
+          event = apply_pad_offset (pad, event);
+          break;
+        case GST_EVENT_RECONFIGURE:
+          if (GST_PAD_IS_SINK (pad))
+            GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+          break;
+        default:
+          break;
       }
+      PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH |
+          GST_PAD_PROBE_TYPE_BLOCK, event, probe_stopped);
       break;
+    }
   }
 
-  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);
-  }
-
-  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 (event)))
-      goto dropping;
+  /* send probes after modifying the events above */
+  PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped);
 
-    GST_OBJECT_LOCK (pad);
-  }
+  /* now check the peer pad */
   peerpad = GST_PAD_PEER (pad);
   if (peerpad == NULL)
     goto not_linked;
 
-  GST_LOG_OBJECT (pad,
-      "sending event %s (%" GST_PTR_FORMAT ") to peerpad %" GST_PTR_FORMAT,
-      GST_EVENT_TYPE_NAME (event), event, peerpad);
   gst_object_ref (peerpad);
+  pad->priv->using++;
   GST_OBJECT_UNLOCK (pad);
 
-  result = gst_pad_send_event (peerpad, event);
+  GST_LOG_OBJECT (pad, "sending event %p (%s) to peerpad %" GST_PTR_FORMAT,
+      event, GST_EVENT_TYPE_NAME (event), peerpad);
+
+  ret = gst_pad_send_event_unchecked (peerpad, event, type);
+
+  /* Note: we gave away ownership of the event at this point but we can still
+   * print the old pointer */
+  GST_LOG_OBJECT (pad,
+      "sent event %p to peerpad %" GST_PTR_FORMAT ", ret %s", event, peerpad,
+      gst_flow_get_name (ret));
 
-  /* 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);
 
-  return result;
+  GST_OBJECT_LOCK (pad);
+  pad->priv->using--;
+  if (pad->priv->using == 0) {
+    /* pad is not active anymore, trigger idle callbacks */
+    PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_IDLE,
+        idle_probe_stopped, ret);
+  }
+  return ret;
 
   /* ERROR handling */
-dropping:
+flushed:
   {
-    GST_DEBUG_OBJECT (pad, "Dropping event after FALSE probe return");
+    GST_DEBUG_OBJECT (pad, "We're flushing");
     gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_FLUSHING;
+  }
+probe_stopped:
+  {
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+    gst_event_unref (event);
+
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped event");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
   }
 not_linked:
   {
     GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
     gst_event_unref (event);
-    GST_OBJECT_UNLOCK (pad);
-    return FALSE;
+
+    /* unlinked pads should not influence latency configuration */
+    if (event_type == GST_EVENT_LATENCY)
+      return GST_FLOW_OK;
+
+    return GST_FLOW_NOT_LINKED;
   }
-flushed:
+idle_probe_stopped:
   {
-    GST_DEBUG_OBJECT (pad,
-        "Not forwarding event since we're flushing and blocking");
-    gst_event_unref (event);
-    GST_OBJECT_UNLOCK (pad);
-    return TRUE;
+    GST_DEBUG_OBJECT (pad, "Idle probe returned %s", gst_flow_get_name (ret));
+    return ret;
   }
 }
 
 /**
- * gst_pad_send_event:
- * @pad: a #GstPad to send the event to.
+ * gst_pad_push_event:
+ * @pad: a #GstPad to push the event to.
  * @event: (transfer full): the #GstEvent to send to the pad.
  *
- * Sends the event to the pad. This function can be used
- * by applications to send events in the pipeline.
- *
- * If @pad is a source pad, @event should be an upstream event. If @pad is a
- * sink pad, @event should be a downstream event. For example, you would not
- * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
- * Furthermore, some downstream events have to be serialized with data flow,
- * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
- * the event needs to be serialized with data flow, this function will take the
- * pad's stream lock while calling its event function.
- *
- * 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.
+ * Sends the event to the peer of the given pad. This function is
+ * 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.
  */
 gboolean
-gst_pad_send_event (GstPad * pad, GstEvent * event)
+gst_pad_push_event (GstPad * pad, GstEvent * event)
 {
-  gboolean result = FALSE;
-  GstPadEventFunction eventfunc;
-  gboolean serialized, need_unlock = FALSE;
+  gboolean res = FALSE;
+  GstPadProbeType type;
+  gboolean sticky, serialized;
 
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
 
-  GST_OBJECT_LOCK (pad);
-  if (GST_PAD_IS_SINK (pad)) {
+  if (GST_PAD_IS_SRC (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)) {
+    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
+  } else if (GST_PAD_IS_SINK (pad)) {
     if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
       goto wrong_direction;
-    /* events on srcpad never are serialized */
-    serialized = FALSE;
+    /* events pushed on sinkpad never are sticky */
+    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
   } else
     goto unknown_direction;
 
-  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);
+  GST_OBJECT_LOCK (pad);
+  sticky = GST_EVENT_IS_STICKY (event);
+  serialized = GST_EVENT_IS_SERIALIZED (event);
+
+  if (sticky) {
+    /* can't store on flushing pads */
+    if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+      goto flushed;
+
+    if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+      goto eos;
+
+    /* srcpad sticky events are store immediately, the received flag is set
+     * to FALSE and will be set to TRUE when we can successfully push the
+     * event to the peer pad */
+    if (gst_pad_store_sticky_event (pad, event, TRUE)) {
+      GST_DEBUG_OBJECT (pad, "event %s updated", GST_EVENT_TYPE_NAME (event));
+    }
+    if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS);
+  }
+  if (GST_PAD_IS_SRC (pad) && (serialized || sticky)) {
+    /* all serialized or sticky events on the srcpad trigger push of
+     * sticky events */
+    res = (check_sticky (pad) == GST_FLOW_OK);
+  }
+  if (!sticky) {
+    /* other events are pushed right away */
+    res = (gst_pad_push_event_unchecked (pad, event, type) == GST_FLOW_OK);
+  } else {
+    /* Errors in sticky event pushing are no problem and ignored here
+     * as they will cause more meaningful errors during data flow.
+     * For EOS events, that are not followed by data flow, we still
+     * return FALSE here though.
+     */
+    if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
+      res = TRUE;
+    gst_event_unref (event);
   }
+  GST_OBJECT_UNLOCK (pad);
+
+  return res;
 
-  /* pad signals */
-  if (G_UNLIKELY (GST_PAD_DO_EVENT_SIGNALS (pad) > 0)) {
+  /* ERROR handling */
+wrong_direction:
+  {
+    g_warning ("pad %s:%s pushing %s event in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+    gst_event_unref (event);
+    return FALSE;
+  }
+unknown_direction:
+  {
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+    gst_event_unref (event);
+    return FALSE;
+  }
+flushed:
+  {
+    GST_DEBUG_OBJECT (pad, "We're flushing");
+    GST_OBJECT_UNLOCK (pad);
+    gst_event_unref (event);
+    return FALSE;
+  }
+eos:
+  {
+    GST_DEBUG_OBJECT (pad, "We're EOS");
     GST_OBJECT_UNLOCK (pad);
+    gst_event_unref (event);
+    return FALSE;
+  }
+}
 
-    if (!gst_pad_emit_have_data_signal (pad, GST_MINI_OBJECT_CAST (event)))
-      goto dropping;
+/* Check if we can call the event function with the given event */
+static GstFlowReturn
+pre_eventfunc_check (GstPad * pad, GstEvent * event)
+{
+  GstCaps *caps;
 
-    GST_OBJECT_LOCK (pad);
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+    {
+      /* backwards compatibility mode for caps */
+      gst_event_parse_caps (event, &caps);
+
+      if (!gst_pad_query_accept_caps (pad, caps))
+        goto not_accepted;
+      break;
+    }
+    default:
+      break;
   }
+  return GST_FLOW_OK;
 
-  switch (GST_EVENT_TYPE (event)) {
+  /* ERRORS */
+not_accepted:
+  {
+    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+        "caps %" GST_PTR_FORMAT " not accepted", caps);
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+}
+
+static GstFlowReturn
+gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
+    GstPadProbeType type)
+{
+  GstFlowReturn ret;
+  GstEventType event_type;
+  gboolean serialized, need_unlock = FALSE, sticky;
+  GstPadEventFunction eventfunc;
+  GstObject *parent;
+
+  GST_OBJECT_LOCK (pad);
+  if (GST_PAD_IS_SINK (pad))
+    serialized = GST_EVENT_IS_SERIALIZED (event);
+  else
+    serialized = FALSE;
+  sticky = GST_EVENT_IS_STICKY (event);
+  event_type = GST_EVENT_TYPE (event);
+  switch (event_type) {
     case GST_EVENT_FLUSH_START:
       GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
           "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
@@ -5379,31 +4691,41 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
       if (GST_PAD_IS_FLUSHING (pad))
         goto flushing;
 
-      _priv_gst_pad_invalidate_cache (pad);
       GST_PAD_SET_FLUSHING (pad);
       GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "set flush flag");
       break;
     case GST_EVENT_FLUSH_STOP:
-      if (G_LIKELY (GST_PAD_ACTIVATE_MODE (pad) != GST_ACTIVATE_NONE)) {
+      if (G_LIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_NONE)) {
         GST_PAD_UNSET_FLUSHING (pad);
         GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag");
       }
+      /* Remove pending EOS events */
+      GST_LOG_OBJECT (pad, "Removing pending EOS events");
+      remove_event_by_type (pad, GST_EVENT_EOS);
+      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
+
       GST_OBJECT_UNLOCK (pad);
       /* grab stream lock */
       GST_PAD_STREAM_LOCK (pad);
       need_unlock = TRUE;
       GST_OBJECT_LOCK (pad);
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+        goto flushing;
       break;
+    case GST_EVENT_RECONFIGURE:
+      if (GST_PAD_IS_SRC (pad))
+        GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
     default:
-      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "have event type %s",
-          GST_EVENT_TYPE_NAME (event));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
+          "have event type %" GST_PTR_FORMAT, event);
 
-      /* make this a little faster, no point in grabbing the lock
-       * if the pad is already flushing. */
       if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushing;
 
       if (serialized) {
+        if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+          goto eos;
+
         /* lock order: STREAM_LOCK, LOCK, recheck flushing. */
         GST_OBJECT_UNLOCK (pad);
         GST_PAD_STREAM_LOCK (pad);
@@ -5411,38 +4733,112 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
         GST_OBJECT_LOCK (pad);
         if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
           goto flushing;
+
+        if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+          goto eos;
+      }
+
+      switch (GST_EVENT_TYPE (event)) {
+        case GST_EVENT_SEGMENT:
+          event = apply_pad_offset (pad, event);
+          break;
+        default:
+          break;
       }
+
+      /* now do the probe */
+      PROBE_PUSH (pad,
+          type | GST_PAD_PROBE_TYPE_PUSH |
+          GST_PAD_PROBE_TYPE_BLOCK, event, probe_stopped);
+
+      PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped);
       break;
   }
+
   if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL))
     goto no_function;
 
+  ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
-  result = eventfunc (pad, event);
+  ret = pre_eventfunc_check (pad, event);
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
+    goto precheck_failed;
+
+  if (sticky)
+    gst_event_ref (event);
+
+  if (eventfunc (pad, parent, event)) {
+    ret = GST_FLOW_OK;
+  } else {
+    /* something went wrong */
+    switch (event_type) {
+      case GST_EVENT_CAPS:
+        ret = GST_FLOW_NOT_NEGOTIATED;
+        break;
+      default:
+        ret = GST_FLOW_ERROR;
+        break;
+    }
+  }
+  RELEASE_PARENT (parent);
+
+  GST_DEBUG_OBJECT (pad, "sent event, ret %s", gst_flow_get_name (ret));
+
+  if (sticky) {
+    if (ret == GST_FLOW_OK) {
+      /* after the event function accepted the event, we can store the sticky
+       * event on the pad */
+      gst_pad_store_sticky_event (pad, event, FALSE);
+      if (event_type == GST_EVENT_EOS)
+        GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS);
+    }
+    gst_event_unref (event);
+  }
 
   if (need_unlock)
     GST_PAD_STREAM_UNLOCK (pad);
 
-  GST_DEBUG_OBJECT (pad, "sent event, result %d", result);
-
-  return result;
+  return ret;
 
   /* ERROR handling */
-wrong_direction:
+flushing:
   {
-    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);
+    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;
+    return GST_FLOW_FLUSHING;
   }
-unknown_direction:
+eos:
+  {
+    GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
+    GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+        "Received event on EOS pad. Discarding");
+    gst_event_unref (event);
+    return GST_FLOW_EOS;
+  }
+probe_stopped:
   {
-    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
     GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
-    return FALSE;
+
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped event");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
   }
 no_function:
   {
@@ -5452,21 +4848,94 @@ no_function:
     if (need_unlock)
       GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_NOT_SUPPORTED;
   }
-flushing:
+no_parent:
   {
+    GST_DEBUG_OBJECT (pad, "no parent");
     GST_OBJECT_UNLOCK (pad);
     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 GST_FLOW_FLUSHING;
+  }
+precheck_failed:
+  {
+    GST_DEBUG_OBJECT (pad, "pre event check failed");
+    RELEASE_PARENT (parent);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
+    gst_event_unref (event);
+    return ret;
+  }
+}
+
+/**
+ * gst_pad_send_event:
+ * @pad: a #GstPad to send the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Sends the event to the pad. This function can be used
+ * by applications to send events in the pipeline.
+ *
+ * If @pad is a source pad, @event should be an upstream event. If @pad is a
+ * sink pad, @event should be a downstream event. For example, you would not
+ * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
+ * Furthermore, some downstream events have to be serialized with data flow,
+ * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
+ * the event needs to be serialized with data flow, this function will take the
+ * pad's stream lock while calling its event function.
+ *
+ * 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.
+ *
+ * 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.
+ */
+gboolean
+gst_pad_send_event (GstPad * pad, GstEvent * event)
+{
+  gboolean result;
+  GstPadProbeType type;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
+  } else if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
+  } else
+    goto unknown_direction;
+
+  if (gst_pad_send_event_unchecked (pad, event, type) != GST_FLOW_OK)
+    result = FALSE;
+  else
+    result = TRUE;
+
+  return result;
+
+  /* ERROR handling */
+wrong_direction:
+  {
+    g_warning ("pad %s:%s sending %s event in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
     gst_event_unref (event);
     return FALSE;
   }
-dropping:
+unknown_direction:
   {
-    GST_DEBUG_OBJECT (pad, "Dropping event after FALSE probe return");
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
     gst_event_unref (event);
     return FALSE;
   }
@@ -5502,6 +4971,84 @@ gst_pad_get_element_private (GstPad * pad)
   return pad->element_private;
 }
 
+/**
+ * gst_pad_get_sticky_event:
+ * @pad: the #GstPad to get the event from.
+ * @event_type: the #GstEventType that should be retrieved.
+ * @idx: the index of the event
+ *
+ * Returns a new reference of the sticky event of type @event_type
+ * from the event.
+ *
+ * Returns: (transfer full): a #GstEvent of type @event_type or NULL when no
+ * event of @event_type was on @pad. Unref after usage.
+ */
+GstEvent *
+gst_pad_get_sticky_event (GstPad * pad, GstEventType event_type, guint idx)
+{
+  GstEvent *event = NULL;
+  PadEvent *ev;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  g_return_val_if_fail ((event_type & GST_EVENT_TYPE_STICKY) != 0, NULL);
+
+  GST_OBJECT_LOCK (pad);
+  ev = find_event_by_type (pad, event_type, idx);
+  if (ev && (event = ev->event))
+    gst_event_ref (event);
+  GST_OBJECT_UNLOCK (pad);
+
+  return event;
+}
+
+typedef struct
+{
+  GstPadStickyEventsForeachFunction func;
+  gpointer user_data;
+} ForeachDispatch;
+
+static gboolean
+foreach_dispatch_function (GstPad * pad, PadEvent * ev, gpointer user_data)
+{
+  ForeachDispatch *data = user_data;
+  gboolean ret;
+
+  GST_OBJECT_UNLOCK (pad);
+
+  ret = data->func (pad, &ev->event, data->user_data);
+
+  GST_OBJECT_LOCK (pad);
+
+  return ret;
+}
+
+/**
+ * gst_pad_sticky_events_foreach:
+ * @pad: the #GstPad that should be used for iteration.
+ * @foreach_func: (scope call): the #GstPadStickyEventsForeachFunction that
+ *                should be called for every event.
+ * @user_data: (closure): the optional user data.
+ *
+ * Iterates all sticky events on @pad and calls @foreach_func for every
+ * event. If @foreach_func returns %FALSE the iteration is immediately stopped.
+ */
+void
+gst_pad_sticky_events_foreach (GstPad * pad,
+    GstPadStickyEventsForeachFunction foreach_func, gpointer user_data)
+{
+  ForeachDispatch data;
+
+  g_return_if_fail (GST_IS_PAD (pad));
+  g_return_if_fail (foreach_func != NULL);
+
+  data.func = foreach_func;
+  data.user_data = user_data;
+
+  GST_OBJECT_LOCK (pad);
+  events_foreach (pad, foreach_dispatch_function, &data);
+  GST_OBJECT_UNLOCK (pad);
+}
+
 static void
 do_stream_status (GstPad * pad, GstStreamStatusType type,
     GThread * thread, GstTask * task)
@@ -5590,10 +5137,10 @@ gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer data)
   GST_OBJECT_LOCK (pad);
   task = GST_PAD_TASK (pad);
   if (task == NULL) {
-    task = gst_task_create (func, data);
+    task = gst_task_new (func, data);
     gst_task_set_lock (task, GST_PAD_GET_STREAM_LOCK (pad));
     gst_task_set_thread_callbacks (task, &thr_callbacks, pad, NULL);
-    GST_DEBUG_OBJECT (pad, "created task");
+    GST_INFO_OBJECT (pad, "created task %p", task);
     GST_PAD_TASK (pad) = task;
     gst_object_ref (task);
     /* release lock to post the message */