docs: improve API docs
[platform/upstream/gstreamer.git] / gst / gstpad.c
index ea4796a..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.
  *
- * Last reviewed on 2006-07-06 (0.10.9)
+ * 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 2012-03-29 (0.11.3)
  */
 
 #include "gst_private.h"
@@ -1347,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.
@@ -1376,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.
@@ -1403,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.
@@ -1430,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.
@@ -1461,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.
@@ -1489,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.
@@ -1514,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.
@@ -1539,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.
@@ -1567,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.
@@ -1602,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.
@@ -2609,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;
@@ -2767,8 +2869,6 @@ typedef struct
 static gboolean
 query_forward_func (GstPad * pad, QueryData * data)
 {
-  /* for each pad we send to, we should ref the query; it's up
-   * to downstream to unref again when handled. */
   GST_LOG_OBJECT (pad, "query peer %p (%s) of %s:%s",
       data->query, GST_EVENT_TYPE_NAME (data->query), GST_DEBUG_PAD_NAME (pad));
 
@@ -2826,6 +2926,9 @@ 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) {
     QueryData data;
 
@@ -2838,7 +2941,7 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
     if (data.dispatched) {
       ret = data.result;
     } else {
-      /* nothing dispatched, could be drained */
+      /* nothing dispatched, assume drained */
       if (GST_QUERY_TYPE (query) == GST_QUERY_DRAIN)
         ret = TRUE;
       else
@@ -2936,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 {                                               \
@@ -2966,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,
@@ -3137,11 +3225,21 @@ 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)
 {
-  GstFlowReturn *data = user_data;
+  PushStickyData *data = user_data;
   GstEvent *event = ev->event;
 
   if (ev->received) {
@@ -3150,10 +3248,10 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
     return TRUE;
   }
 
-  *data = gst_pad_push_event_unchecked (pad, gst_event_ref (event),
+  data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event),
       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
 
-  switch (*data) {
+  switch (data->ret) {
     case GST_FLOW_OK:
       ev->received = TRUE;
       GST_DEBUG_OBJECT (pad, "event %s marked received",
@@ -3161,16 +3259,21 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
       break;
     case GST_FLOW_NOT_LINKED:
       /* not linked is not a problem, we are sticky so the event will be
-       * sent later */
+       * sent later but only for non-EOS events */
       GST_DEBUG_OBJECT (pad, "pad was not linked");
-      *data = GST_FLOW_OK;
+      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 *data == GST_FLOW_OK;
+
+  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
@@ -3178,15 +3281,31 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
 static inline GstFlowReturn
 check_sticky (GstPad * pad)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
+  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, &ret);
+    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 ret;
+  return data.ret;
 }
 
 
@@ -3309,7 +3428,15 @@ probe_stopped:
     GST_OBJECT_UNLOCK (pad);
     if (G_UNLIKELY (serialized))
       GST_PAD_STREAM_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;
   }
 }
 
@@ -3420,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;
   }
 }
 
@@ -3443,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;
 
@@ -3504,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",
@@ -3652,6 +3798,9 @@ 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;
 
@@ -3697,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",
@@ -3816,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);
 
@@ -3829,10 +3986,13 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
     goto events_error;
 
-  /* 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);
+  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);
@@ -3845,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);
 
@@ -3856,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 */
@@ -3906,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));
@@ -3947,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.
@@ -3960,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);
 }
@@ -3972,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.
@@ -3982,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.
  */
@@ -3996,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)))
@@ -4008,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;
@@ -4020,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);
 
@@ -4029,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))
@@ -4037,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 */
@@ -4058,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;
   }
@@ -4074,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;
   }
 }
@@ -4184,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;
@@ -4192,6 +4424,11 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
       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
@@ -4273,6 +4510,11 @@ 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);
+
+    /* 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:
@@ -4301,7 +4543,7 @@ idle_probe_stopped:
 gboolean
 gst_pad_push_event (GstPad * pad, GstEvent * event)
 {
-  gboolean res;
+  gboolean res = FALSE;
   GstPadProbeType type;
   gboolean sticky, serialized;
 
@@ -4329,12 +4571,17 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
     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
@@ -4345,8 +4592,14 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
     /* 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);
-    res = TRUE;
   }
   GST_OBJECT_UNLOCK (pad);
 
@@ -4373,13 +4626,20 @@ flushed:
     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:
@@ -4387,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:
@@ -4403,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;
   }
 }
@@ -4449,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 */
@@ -4469,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);
@@ -4476,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)) {
@@ -4530,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);
   }
@@ -4550,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);