docs: improve API docs
[platform/upstream/gstreamer.git] / gst / gstpad.c
index f7802d5..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_query_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"
@@ -148,7 +175,7 @@ static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
 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, gboolean * stored);
+    GstEvent * event, GstPadProbeType type);
 
 static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
 
@@ -268,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
@@ -279,7 +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);
+      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,
@@ -427,6 +454,9 @@ remove_event_by_type (GstPad * pad, GstEventType type)
   }
 }
 
+/* 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)
 {
@@ -455,6 +485,7 @@ schedule_events (GstPad * srcpad, GstPad * sinkpad)
 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)
 {
@@ -490,6 +521,9 @@ restart:
       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)) {
@@ -1315,6 +1349,8 @@ gst_pad_check_reconfigure (GstPad * pad)
 
   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);
 
@@ -1339,6 +1375,14 @@ gst_pad_mark_reconfigure (GstPad * 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.
@@ -1368,6 +1412,14 @@ gst_pad_set_activate_function_full (GstPad * pad,
 }
 
 /**
+ * gst_pad_set_activatemode_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadActivateModeFunction to set.
+ *
+ * Calls gst_pad_set_activatemode_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
  * gst_pad_set_activatemode_function_full:
  * @pad: a #GstPad.
  * @activatemode: the #GstPadActivateModeFunction to set.
@@ -1395,6 +1447,14 @@ gst_pad_set_activatemode_function_full (GstPad * pad,
 }
 
 /**
+ * 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.
@@ -1422,6 +1482,14 @@ gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction 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.
@@ -1453,6 +1521,14 @@ gst_pad_set_chain_list_function_full (GstPad * pad,
 }
 
 /**
+ * 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.
@@ -1481,6 +1557,14 @@ gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get,
 }
 
 /**
+ * gst_pad_set_event_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadEventFunction to set.
+ *
+ * Calls gst_pad_set_event_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
  * gst_pad_set_event_function_full:
  * @pad: a #GstPad of either direction.
  * @event: the #GstPadEventFunction to set.
@@ -1506,6 +1590,14 @@ gst_pad_set_event_function_full (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.
@@ -1531,6 +1623,14 @@ gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
 }
 
 /**
+ * gst_pad_set_iterate_internal_links_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadIterIntLinkFunction to set.
+ *
+ * Calls gst_pad_set_iterate_internal_links_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
  * gst_pad_set_iterate_internal_links_function_full:
  * @pad: a #GstPad of either direction.
  * @iterintlink: the #GstPadIterIntLinkFunction to set.
@@ -1559,6 +1659,14 @@ gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
 }
 
 /**
+ * gst_pad_set_link_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadLinkFunction to set.
+ *
+ * Calls gst_pad_set_link_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
  * gst_pad_set_link_function_full:
  * @pad: a #GstPad.
  * @link: the #GstPadLinkFunction to set.
@@ -1594,6 +1702,14 @@ gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction 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.
@@ -2267,14 +2383,12 @@ gst_pad_set_caps (GstPad * pad, GstCaps * caps)
 GstCaps *
 gst_pad_get_pad_template_caps (GstPad * pad)
 {
-  static GstStaticCaps anycaps = GST_STATIC_CAPS ("ANY");
-
   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
 
   if (GST_PAD_PAD_TEMPLATE (pad))
     return gst_pad_template_get_caps (GST_PAD_PAD_TEMPLATE (pad));
 
-  return gst_static_caps_get (&anycaps);
+  return gst_caps_ref (GST_CAPS_ANY);
 }
 
 /**
@@ -2603,12 +2717,6 @@ gst_pad_event_default (GstPad * pad, GstObject * parent, GstEvent * event)
   GST_LOG_OBJECT (pad, "default event handler");
 
   switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_EOS:
-    {
-      GST_DEBUG_OBJECT (pad, "pausing task because of eos");
-      gst_pad_pause_task (pad);
-      break;
-    }
     case GST_EVENT_CAPS:
       forward = GST_PAD_IS_PROXY_CAPS (pad);
       result = TRUE;
@@ -2722,13 +2830,10 @@ gst_pad_query_caps_default (GstPad * pad, GstQuery * query)
     if ((result = get_pad_caps (pad)))
       goto filter_done_unlock;
   }
-  GST_OBJECT_UNLOCK (pad);
 
   /* this almost never happens */
   GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
-  result = gst_caps_new_empty ();
-
-  goto done;
+  result = GST_CAPS_ANY;
 
 filter_done_unlock:
   GST_OBJECT_UNLOCK (pad);
@@ -2748,13 +2853,33 @@ filter_done:
     result = gst_caps_ref (result);
   }
 
-done:
   gst_query_set_caps_result (query, result);
   gst_caps_unref (result);
 
   return TRUE;
 }
 
+typedef struct
+{
+  GstQuery *query;
+  gboolean result;
+  gboolean dispatched;
+} QueryData;
+
+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));
+
+  data->result |= gst_pad_peer_query (pad, data->query);
+
+  data->dispatched = TRUE;
+
+  /* stop on first successful reply */
+  return data->result;
+}
+
 /**
  * gst_pad_query_default:
  * @pad: a #GstPad to call the default query handler on.
@@ -2801,9 +2926,27 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
       break;
   }
 
+  GST_DEBUG_OBJECT (pad, "%sforwarding %p (%s) query", (forward ? "" : "not "),
+      query, GST_QUERY_TYPE_NAME (query));
+
   if (forward) {
-    ret = gst_pad_forward
-        (pad, (GstPadForwardFunction) gst_pad_peer_query, query);
+    QueryData data;
+
+    data.query = query;
+    data.dispatched = FALSE;
+    data.result = FALSE;
+
+    gst_pad_forward (pad, (GstPadForwardFunction) query_forward_func, &data);
+
+    if (data.dispatched) {
+      ret = data.result;
+    } else {
+      /* nothing dispatched, assume drained */
+      if (GST_QUERY_TYPE (query) == GST_QUERY_DRAIN)
+        ret = TRUE;
+      else
+        ret = FALSE;
+    }
   }
   return ret;
 }
@@ -2849,6 +2992,8 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
       "hook %lu, cookie %u with flags 0x%08x matches", hook->hook_id,
       PROBE_COOKIE (hook), flags);
 
+  data->marshalled = TRUE;
+
   callback = (GstPadProbeCallback) hook->func;
   if (callback == NULL)
     return;
@@ -2860,7 +3005,6 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
   ret = callback (pad, info, hook->data);
 
   GST_OBJECT_LOCK (pad);
-  data->marshalled = TRUE;
 
   switch (ret) {
     case GST_PAD_PROBE_REMOVE:
@@ -2895,24 +3039,6 @@ no_match:
   }
 }
 
-#define PROBE_PRE_PULL(pad,mask,data,offs,size,label,probed,defaultval)    \
-  G_STMT_START {                                               \
-    if (G_UNLIKELY (pad->num_probes)) {                                \
-      /* we start with passing NULL as the data item */         \
-      GstPadProbeInfo info = { mask, 0, NULL, offs, size };     \
-      ret = do_probe_callbacks (pad, &info, defaultval);       \
-      /* store the possibly updated data item */                \
-      data = GST_PAD_PROBE_INFO_DATA (&info);                   \
-      /* if something went wrong, exit */                       \
-      if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK))        \
-        goto label;                                            \
-      /* otherwise check if the probe retured data */           \
-      if (G_UNLIKELY (data != NULL))                            \
-        goto probed;                                           \
-    }                                                          \
-  } G_STMT_END
-
-
 /* a probe that does not take or return any data */
 #define PROBE_NO_DATA(pad,mask,label,defaultval)                \
   G_STMT_START {                                               \
@@ -2925,21 +3051,24 @@ no_match:
     }                                                          \
   } G_STMT_END
 
-#define PROBE_FULL(pad,mask,data,offs,size,label,defaultval)    \
+#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, defaultval);       \
+      ret = do_probe_callbacks (pad, &info, GST_FLOW_OK);      \
+      /* store the possibly updated data item */                \
       data = GST_PAD_PROBE_INFO_DATA (&info);                   \
-      if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK))        \
+      /* 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, GST_FLOW_OK);
-#define PROBE_PULL(pad,mask,data,offs,size,label)              \
-  PROBE_FULL(pad, mask, data, offs, size, label, GST_FLOW_OK);
+#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);
 
 static GstFlowReturn
 do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info,
@@ -3096,6 +3225,89 @@ done:
   GST_OBJECT_UNLOCK (pad);
 }
 
+typedef struct
+{
+  GstFlowReturn ret;
+
+  /* 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)
+{
+  PushStickyData *data = user_data;
+  GstEvent *event = ev->event;
+
+  if (ev->received) {
+    GST_DEBUG_OBJECT (pad, "event %s was already received",
+        GST_EVENT_TYPE_NAME (event));
+    return TRUE;
+  }
+
+  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;
+  }
+
+  if (data->ret != GST_FLOW_OK && GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+    data->was_eos = TRUE;
+
+  return data->ret == GST_FLOW_OK;
+}
+
+/* check sticky events and push them when needed. should be called
+ * with pad LOCK */
+static inline GstFlowReturn
+check_sticky (GstPad * pad)
+{
+  PushStickyData data = { GST_FLOW_OK, FALSE };
+
+  if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+
+    GST_DEBUG_OBJECT (pad, "pushing all sticky events");
+    events_foreach (pad, push_sticky, &data);
+
+    /* 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);
+
+      if (ev && !ev->received) {
+        data.ret = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
+            GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+      }
+    }
+  }
+  return data.ret;
+}
+
 
 /**
  * gst_pad_query:
@@ -3119,7 +3331,7 @@ gboolean
 gst_pad_query (GstPad * pad, GstQuery * query)
 {
   GstObject *parent;
-  gboolean res;
+  gboolean res, serialized;
   GstPadQueryFunction func;
   GstPadProbeType type;
   GstFlowReturn ret;
@@ -3127,13 +3339,23 @@ gst_pad_query (GstPad * pad, GstQuery * query)
   g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
 
-  GST_DEBUG_OBJECT (pad, "sending query %p (%s)", query,
-      GST_QUERY_TYPE_NAME (query));
-
-  if (GST_PAD_IS_SRC (pad))
+  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
+  } 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 |
@@ -3160,31 +3382,61 @@ gst_pad_query (GstPad * pad, GstQuery * query)
   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);
-    return FALSE;
+    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;
   }
 }
 
@@ -3208,21 +3460,36 @@ gst_pad_peer_query (GstPad * pad, GstQuery * query)
 {
   GstPad *peerpad;
   GstPadProbeType type;
-  gboolean res;
+  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 (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_QUERY_IS_DOWNSTREAM (query)))
+      goto wrong_direction;
     type = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM;
-  else
+  } 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;
+  }
+
   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);
@@ -3248,6 +3515,23 @@ gst_pad_peer_query (GstPad * pad, GstQuery * query)
   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;
+  }
+sticky_failed:
+  {
+    GST_WARNING_OBJECT (pad, "could not send sticky events");
+    GST_OBJECT_UNLOCK (pad);
+    return FALSE;
+  }
 no_peer:
   {
     GST_WARNING_OBJECT (pad, "pad has no peer");
@@ -3263,7 +3547,15 @@ probe_stopped:
   {
     GST_DEBUG_OBJECT (pad, "probe stopped: %s", gst_flow_get_name (ret));
     GST_OBJECT_UNLOCK (pad);
-    return FALSE;
+
+    /* 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;
   }
 }
 
@@ -3271,26 +3563,6 @@ probe_stopped:
  * Data passing functions
  */
 
-static gboolean
-push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
-{
-  GstFlowReturn *data = user_data;
-  gboolean stored;
-
-  if (ev->received) {
-    GST_DEBUG_OBJECT (pad, "event %s was already received",
-        GST_EVENT_TYPE_NAME (ev->event));
-    return TRUE;
-  }
-  GST_OBJECT_UNLOCK (pad);
-
-  *data = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
-      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, &stored);
-
-  GST_OBJECT_LOCK (pad);
-  return *data == GST_FLOW_OK;
-}
-
 /* this is the chain function that does not perform the additional argument
  * checking for that little extra speed.
  */
@@ -3306,6 +3578,9 @@ gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
+  if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+    goto eos;
+
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
     goto wrong_mode;
 
@@ -3367,6 +3642,14 @@ flushing:
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_FLUSHING;
   }
+eos:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "chaining, but pad was EOS");
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_EOS;
+  }
 wrong_mode:
   {
     g_critical ("chain on pad %s:%s but it was not in push mode",
@@ -3388,7 +3671,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -3515,19 +3798,14 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
+  if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+    goto eos;
+
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
     goto wrong_mode;
 
-  if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
-    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-
-    GST_DEBUG_OBJECT (pad, "pushing all sticky events");
-
-    ret = GST_FLOW_OK;
-    events_foreach (pad, push_sticky, &ret);
-    if (ret != GST_FLOW_OK)
-      goto events_error;
-  }
+  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+    goto events_error;
 
   /* do block probes */
   PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
@@ -3568,6 +3846,13 @@ flushing:
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_FLUSHING;
   }
+eos:
+  {
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was EOS");
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_EOS;
+  }
 wrong_mode:
   {
     g_critical ("pushing on pad %s:%s but it was not activated in push mode",
@@ -3595,7 +3880,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -3687,6 +3972,7 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   GstFlowReturn ret;
   GstPadGetRangeFunction getrangefunc;
   GstObject *parent;
+  GstBuffer *res_buf;
 
   GST_PAD_STREAM_LOCK (pad);
 
@@ -3697,21 +3983,16 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
     goto wrong_mode;
 
-  if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
-    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-
-    GST_DEBUG_OBJECT (pad, "pushing all sticky events");
+  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+    goto events_error;
 
-    ret = GST_FLOW_OK;
-    events_foreach (pad, push_sticky, &ret);
-    if (ret != GST_FLOW_OK)
-      goto events_error;
-  }
+  res_buf = *buffer;
 
-  /* when one of the probes returns a buffer, probed_data will be called and we
-   * skip calling the getrange function */
-  PROBE_PRE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
-      *buffer, offset, size, probe_stopped, probed_data, GST_FLOW_OK);
+  /* 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);
@@ -3724,7 +4005,7 @@ 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, parent, offset, size, buffer);
+  ret = getrangefunc (pad, parent, offset, size, &res_buf);
 
   RELEASE_PARENT (parent);
 
@@ -3735,11 +4016,13 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   GST_OBJECT_LOCK (pad);
 probed_data:
   PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
-      *buffer, offset, size, probe_stopped_unref);
+      res_buf, offset, size, probe_stopped_unref);
   GST_OBJECT_UNLOCK (pad);
 
   GST_PAD_STREAM_UNLOCK (pad);
 
+  *buffer = res_buf;
+
   return ret;
 
   /* ERRORS */
@@ -3785,24 +4068,40 @@ 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,
         "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;
+    if (*buffer == NULL)
+      gst_buffer_unref (res_buf);
     return ret;
   }
 get_range_failed:
   {
     GST_PAD_STREAM_UNLOCK (pad);
-    *buffer = NULL;
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
         (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
         pad, "getrange failed, flow: %s", gst_flow_get_name (ret));
@@ -3826,6 +4125,23 @@ get_range_failed:
  * 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.
@@ -3839,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);
 }
@@ -3851,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.
@@ -3861,11 +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.
  *
+ * 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.
  */
@@ -3875,10 +4205,13 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
 {
   GstPad *peer;
   GstFlowReturn ret;
+  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)))
@@ -3887,10 +4220,13 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
     goto wrong_mode;
 
-  /* when one of the probes returns a buffer, probed_data will be called and we
-   * skip calling the peer getrange function */
-  PROBE_PRE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
-      *buffer, offset, size, pre_probe_stopped, probed_data, GST_FLOW_OK);
+  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);
 
   if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
     goto not_linked;
@@ -3899,7 +4235,7 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
   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);
 
@@ -3908,7 +4244,7 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
   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,
-        post_probe_stopped, ret);
+        probe_stopped_unref, ret);
   }
 
   if (G_UNLIKELY (ret != GST_FLOW_OK))
@@ -3916,10 +4252,12 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
 
 probed_data:
   PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
-      *buffer, offset, size, post_probe_stopped);
+      res_buf, offset, size, probe_stopped_unref);
 
   GST_OBJECT_UNLOCK (pad);
 
+  *buffer = res_buf;
+
   return ret;
 
   /* ERROR recovery here */
@@ -3937,10 +4275,23 @@ wrong_mode:
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_ERROR;
   }
-pre_probe_stopped:
+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;
   }
@@ -3953,21 +4304,22 @@ not_linked:
   }
 pull_range_failed:
   {
-    *buffer = NULL;
     GST_OBJECT_UNLOCK (pad);
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
         (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
         pad, "pullrange failed, flow: %s", gst_flow_get_name (ret));
     return ret;
   }
-post_probe_stopped:
+probe_stopped_unref:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "post probe returned %s", gst_flow_get_name (ret));
     GST_OBJECT_UNLOCK (pad);
-    if (ret == GST_FLOW_OK)
-      gst_buffer_unref (*buffer);
-    *buffer = NULL;
+    /* 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;
   }
 }
@@ -4037,24 +4389,19 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
   return res;
 }
 
+/* should be called with pad LOCK */
 static GstFlowReturn
 gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
-    GstPadProbeType type, gboolean * stored)
+    GstPadProbeType type)
 {
   GstFlowReturn ret;
   GstPad *peerpad;
   GstEventType event_type;
-  gboolean sticky;
-
-  sticky = GST_EVENT_IS_STICKY (event);
-
-  GST_OBJECT_LOCK (pad);
 
   /* Two checks to be made:
    * . (un)set the FLUSHING flag for flushing events,
    * . handle pad blocking */
   event_type = GST_EVENT_TYPE (event);
-  *stored = FALSE;
   switch (event_type) {
     case GST_EVENT_FLUSH_START:
       GST_PAD_SET_FLUSHING (pad);
@@ -4068,6 +4415,7 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
       /* 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;
@@ -4076,18 +4424,10 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
       if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushed;
 
-      /* store the event on the pad, but only on srcpads. We always store the
-       * event exactly how it was sent */
-      if (sticky) {
-        /* 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));
-        }
-        *stored = TRUE;
-      }
+      /* 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:
@@ -4134,41 +4474,24 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
   gst_object_unref (peerpad);
 
   GST_OBJECT_LOCK (pad);
-  if (sticky) {
-    if (ret == GST_FLOW_OK) {
-      PadEvent *ev;
-
-      if ((ev = find_event (pad, event)))
-        ev->received = TRUE;
-
-      GST_DEBUG_OBJECT (pad, "event marked received");
-    } else {
-      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-      GST_DEBUG_OBJECT (pad, "mark pending events");
-    }
-  }
   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);
   }
-  GST_OBJECT_UNLOCK (pad);
-
   return ret;
 
   /* ERROR handling */
 flushed:
   {
     GST_DEBUG_OBJECT (pad, "We're flushing");
-    GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
     return GST_FLOW_FLUSHING;
   }
 probe_stopped:
   {
     GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-    GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
 
     switch (ret) {
@@ -4177,7 +4500,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -4186,14 +4509,17 @@ not_linked:
   {
     GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
     GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-    GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
-    return sticky ? GST_FLOW_OK : GST_FLOW_NOT_LINKED;
+
+    /* unlinked pads should not influence latency configuration */
+    if (event_type == GST_EVENT_LATENCY)
+      return GST_FLOW_OK;
+
+    return GST_FLOW_NOT_LINKED;
   }
 idle_probe_stopped:
   {
     GST_DEBUG_OBJECT (pad, "Idle probe returned %s", gst_flow_get_name (ret));
-    GST_OBJECT_UNLOCK (pad);
     return ret;
   }
 }
@@ -4217,12 +4543,11 @@ idle_probe_stopped:
 gboolean
 gst_pad_push_event (GstPad * pad, GstEvent * event)
 {
-  gboolean res;
+  gboolean res = FALSE;
   GstPadProbeType type;
-  gboolean stored;
+  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);
 
   if (GST_PAD_IS_SRC (pad)) {
@@ -4237,10 +4562,46 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
   } else
     goto unknown_direction;
 
-  if (gst_pad_push_event_unchecked (pad, event, type, &stored) != GST_FLOW_OK)
-    res = stored ? TRUE : FALSE;
-  else
-    res = TRUE;
+  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;
 
@@ -4258,13 +4619,27 @@ unknown_direction:
     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;
+  }
 }
 
 /* Check if we can call the event function with the given event */
 static GstFlowReturn
 pre_eventfunc_check (GstPad * pad, GstEvent * event)
 {
-  GstCaps *caps, *templ;
+  GstCaps *caps;
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_CAPS:
@@ -4272,12 +4647,8 @@ pre_eventfunc_check (GstPad * pad, GstEvent * event)
       /* backwards compatibility mode for caps */
       gst_event_parse_caps (event, &caps);
 
-      /* See if pad accepts the caps */
-      templ = gst_pad_get_pad_template_caps (pad);
-      if (!gst_caps_is_subset (caps, templ))
+      if (!gst_pad_query_accept_caps (pad, caps))
         goto not_accepted;
-
-      gst_caps_unref (templ);
       break;
     }
     default:
@@ -4288,11 +4659,8 @@ pre_eventfunc_check (GstPad * pad, GstEvent * event)
   /* ERRORS */
 not_accepted:
   {
-    gst_caps_unref (templ);
     GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
         "caps %" GST_PTR_FORMAT " not accepted", caps);
-    GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
-        "no intersection with template %" GST_PTR_FORMAT, templ);
     return GST_FLOW_NOT_NEGOTIATED;
   }
 }
@@ -4334,6 +4702,7 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
       /* 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 */
@@ -4354,6 +4723,9 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
         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);
@@ -4361,6 +4733,9 @@ gst_pad_send_event_unchecked (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)) {
@@ -4415,6 +4790,8 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
       /* 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);
   }
@@ -4435,6 +4812,16 @@ flushing:
     gst_event_unref (event);
     return GST_FLOW_FLUSHING;
   }
+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:
   {
     GST_OBJECT_UNLOCK (pad);
@@ -4448,7 +4835,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -4753,7 +5140,7 @@ gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer 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 */