pad: Print debug output from gst_pad_link_full() if preparing linking failed
[platform/upstream/gstreamer.git] / gst / gstpad.c
index a429226..b762465 100644 (file)
@@ -16,8 +16,8 @@
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 /**
  * SECTION:gstpad
@@ -38,7 +38,7 @@
  * 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,
+ * 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
@@ -47,7 +47,7 @@
  *
  * gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
  *
- * After two pads are retrieved from an element with gst_element_get_pad(),
+ * After two pads are retrieved from an element by gst_element_get_static_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
@@ -84,8 +84,6 @@
  * 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"
@@ -117,7 +115,8 @@ enum
   PAD_PROP_CAPS,
   PAD_PROP_DIRECTION,
   PAD_PROP_TEMPLATE,
-  /* FILL ME */
+  PAD_PROP_OFFSET
+      /* FILL ME */
 };
 
 #define GST_PAD_GET_PRIVATE(obj)  \
@@ -125,7 +124,7 @@ enum
 
 /* 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. */
+ * moved to the active event when the eventfunc returned %TRUE. */
 typedef struct
 {
   gboolean received;
@@ -136,6 +135,7 @@ struct _GstPadPrivate
 {
   guint events_cookie;
   GArray *events;
+  guint last_cookie;
 
   gint using;
   guint probe_list_cookie;
@@ -250,6 +250,38 @@ gst_flow_to_quark (GstFlowReturn ret)
   return 0;
 }
 
+/**
+ * gst_pad_link_get_name:
+ * @ret: a #GstPadLinkReturn to get the name of.
+ *
+ * Gets a string representing the given pad-link return.
+ *
+ * Returns: a static string with the name of the pad-link return.
+ *
+ * Since: 1.4
+ */
+const gchar *
+gst_pad_link_get_name (GstPadLinkReturn ret)
+{
+  switch (ret) {
+    case GST_PAD_LINK_OK:
+      return "ok";
+    case GST_PAD_LINK_WRONG_HIERARCHY:
+      return "wrong hierarchy";
+    case GST_PAD_LINK_WAS_LINKED:
+      return "was linked";
+    case GST_PAD_LINK_WRONG_DIRECTION:
+      return "wrong direction";
+    case GST_PAD_LINK_NOFORMAT:
+      return "no common format";
+    case GST_PAD_LINK_NOSCHED:
+      return "incompatible scheduling";
+    case GST_PAD_LINK_REFUSED:
+      return "refused";
+  }
+  g_return_val_if_reached ("unknown");
+}
+
 #define _do_init \
 { \
   gint i; \
@@ -324,6 +356,18 @@ gst_pad_class_init (GstPadClass * klass)
           "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstPad:offset:
+   *
+   * The offset that will be applied to the running time of the pad.
+   *
+   * Since: 1.6
+   */
+  g_object_class_install_property (gobject_class, PAD_PROP_OFFSET,
+      g_param_spec_int64 ("offset", "Offset",
+          "The running time offset of the pad", 0, G_MAXINT64, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gstobject_class->path_string_separator = ".";
 
   /* Register common function pointer descriptions */
@@ -356,28 +400,50 @@ gst_pad_init (GstPad * pad)
   g_hook_list_init (&pad->probes, sizeof (GstProbe));
 
   pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
+  pad->priv->events_cookie = 0;
+  pad->priv->last_cookie = -1;
+  pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
 }
 
 /* called when setting the pad inactive. It removes all sticky events from
- * the pad */
+ * the pad. must be called with object lock */
 static void
 remove_events (GstPad * pad)
 {
   guint i, len;
   GArray *events;
+  gboolean notify = FALSE;
 
   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);
+    GstEvent *event = ev->event;
+
+    ev->event = NULL;
+
+    if (event && GST_EVENT_TYPE (event) == GST_EVENT_CAPS)
+      notify = TRUE;
+
+    gst_event_unref (event);
   }
+
   GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
   g_array_set_size (events, 0);
   pad->priv->events_cookie++;
+
+  if (notify) {
+    GST_OBJECT_UNLOCK (pad);
+
+    GST_DEBUG_OBJECT (pad, "notify caps");
+    g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
+
+    GST_OBJECT_LOCK (pad);
+  }
 }
 
+/* should be called with object lock */
 static PadEvent *
 find_event_by_type (GstPad * pad, GstEventType type, guint idx)
 {
@@ -404,6 +470,7 @@ found:
   return ev;
 }
 
+/* should be called with OBJECT lock */
 static PadEvent *
 find_event (GstPad * pad, GstEvent * event)
 {
@@ -424,6 +491,7 @@ found:
   return ev;
 }
 
+/* should be called with OBJECT lock */
 static void
 remove_event_by_type (GstPad * pad, GstEventType type)
 {
@@ -455,8 +523,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
+ * 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 */
+/* should be called with srcpad and sinkpad LOCKS */
 static void
 schedule_events (GstPad * srcpad, GstPad * sinkpad)
 {
@@ -528,6 +597,7 @@ restart:
     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 */
+        gst_event_unref (ev->event);
         g_array_remove_index (events, i);
         len--;
         cookie = ++pad->priv->events_cookie;
@@ -549,23 +619,46 @@ restart:
 
 /* should be called with LOCK */
 static GstEvent *
-apply_pad_offset (GstPad * pad, GstEvent * event)
+_apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream)
 {
-  /* check if we need to adjust the segment */
-  if (pad->offset != 0) {
+  gint64 offset;
+
+  GST_DEBUG_OBJECT (pad, "apply pad offset %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (pad->offset));
+
+  if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
     GstSegment segment;
 
+    g_assert (!upstream);
+
     /* 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;
+    gst_segment_offset_running_time (&segment, segment.format, pad->offset);
     event = gst_event_new_segment (&segment);
   }
+
+  event = gst_event_make_writable (event);
+  offset = gst_event_get_running_time_offset (event);
+  if (upstream)
+    offset -= pad->offset;
+  else
+    offset += pad->offset;
+  gst_event_set_running_time_offset (event, offset);
+
   return event;
 }
 
+static inline GstEvent *
+apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream)
+{
+  if (G_UNLIKELY (pad->offset != 0))
+    return _apply_pad_offset (pad, event, upstream);
+  return event;
+}
+
+
 /* should be called with the OBJECT_LOCK */
 static GstCaps *
 get_pad_caps (GstPad * pad)
@@ -603,7 +696,9 @@ gst_pad_dispose (GObject * object)
 
   gst_pad_set_pad_template (pad, NULL);
 
+  GST_OBJECT_LOCK (pad);
   remove_events (pad);
+  GST_OBJECT_UNLOCK (pad);
 
   g_hook_list_clear (&pad->probes);
 
@@ -665,6 +760,9 @@ gst_pad_set_property (GObject * object, guint prop_id,
       gst_pad_set_pad_template (GST_PAD_CAST (object),
           (GstPadTemplate *) g_value_get_object (value));
       break;
+    case PAD_PROP_OFFSET:
+      gst_pad_set_offset (GST_PAD_CAST (object), g_value_get_int64 (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -689,6 +787,9 @@ gst_pad_get_property (GObject * object, guint prop_id,
     case PAD_PROP_TEMPLATE:
       g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object));
       break;
+    case PAD_PROP_OFFSET:
+      g_value_set_int64 (value, gst_pad_get_offset (GST_PAD_CAST (object)));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -697,15 +798,16 @@ gst_pad_get_property (GObject * object, guint prop_id,
 
 /**
  * gst_pad_new:
- * @name: the name of the new pad.
+ * @name: (allow-none): the name of the new pad.
  * @direction: the #GstPadDirection of the pad.
  *
  * Creates a new pad with the given name in the given direction.
- * If name is NULL, a guaranteed unique name (across all pads)
+ * If name is %NULL, a guaranteed unique name (across all pads)
  * will be assigned.
  * This function makes a copy of the name so you can safely free the name.
  *
- * Returns: (transfer floating): a new #GstPad, or NULL in case of an error.
+ * Returns: (transfer floating) (nullable): a new #GstPad, or %NULL in
+ * case of an error.
  *
  * MT safe.
  */
@@ -719,14 +821,15 @@ gst_pad_new (const gchar * name, GstPadDirection direction)
 /**
  * gst_pad_new_from_template:
  * @templ: the pad template to use
- * @name: the name of the element
+ * @name: (allow-none): the name of the pad
  *
  * Creates a new pad with the given name from the given template.
- * If name is NULL, a guaranteed unique name (across all pads)
+ * If name is %NULL, a guaranteed unique name (across all pads)
  * 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) (nullable): a new #GstPad, or %NULL in
+ * case of an error.
  */
 GstPad *
 gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
@@ -740,14 +843,15 @@ gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
 /**
  * gst_pad_new_from_static_template:
  * @templ: the #GstStaticPadTemplate to use
- * @name: the name of the element
+ * @name: the name of the pad
  *
  * Creates a new pad with the given name from the given static template.
- * If name is NULL, a guaranteed unique name (across all pads)
+ * If name is %NULL, a guaranteed unique name (across all pads)
  * 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) (nullable): a new #GstPad, or %NULL in
+ * case of an error.
  */
 GstPad *
 gst_pad_new_from_static_template (GstStaticPadTemplate * templ,
@@ -840,6 +944,7 @@ pre_activate (GstPad * pad, GstPadMode new_mode)
       GST_OBJECT_LOCK (pad);
       GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing");
       GST_PAD_SET_FLUSHING (pad);
+      pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
       GST_PAD_MODE (pad) = new_mode;
       /* unlock blocked pads so element can resume and stop */
       GST_PAD_BLOCK_BROADCAST (pad);
@@ -851,6 +956,7 @@ pre_activate (GstPad * pad, GstPadMode new_mode)
       GST_DEBUG_OBJECT (pad, "setting pad into %s mode, unset flushing",
           gst_pad_mode_get_name (new_mode));
       GST_PAD_UNSET_FLUSHING (pad);
+      pad->ABI.abi.last_flowret = GST_FLOW_OK;
       GST_PAD_MODE (pad) = new_mode;
       if (GST_PAD_IS_SINK (pad)) {
         GstPad *peer;
@@ -909,11 +1015,10 @@ post_activate (GstPad * pad, GstPadMode new_mode)
  * push or pull mode, just return. Otherwise dispatches to the pad's activate
  * function to perform the actual activation.
  *
- * If not @active, checks the pad's current mode and calls
- * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with a
- * FALSE argument.
+ * If not @active, calls gst_pad_activate_mode() with the pad's current mode
+ * and a %FALSE argument.
  *
- * Returns: #TRUE if the operation was successful.
+ * Returns: %TRUE if the operation was successful.
  *
  * MT safe.
  */
@@ -935,6 +1040,8 @@ gst_pad_set_active (GstPad * pad, gboolean active)
     if (old == GST_PAD_MODE_NONE) {
       GST_DEBUG_OBJECT (pad, "activating pad from none");
       ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad, parent);
+      if (ret)
+        pad->ABI.abi.last_flowret = GST_FLOW_OK;
     } else {
       GST_DEBUG_OBJECT (pad, "pad was active in %s mode",
           gst_pad_mode_get_name (old));
@@ -948,6 +1055,8 @@ gst_pad_set_active (GstPad * pad, gboolean active)
       GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode",
           gst_pad_mode_get_name (old));
       ret = gst_pad_activate_mode (pad, old, FALSE);
+      if (ret)
+        pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
     }
   }
 
@@ -990,7 +1099,7 @@ failed:
  *
  * If you don't know what this is, you probably don't want to call it.
  *
- * Returns: TRUE if the operation was successful.
+ * Returns: %TRUE if the operation was successful.
  *
  * MT safe.
  */
@@ -1055,6 +1164,9 @@ gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active)
       break;
   }
 
+  /* Mark pad as needing reconfiguration */
+  if (active)
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
   pre_activate (pad, new);
 
   if (GST_PAD_ACTIVATEMODEFUNC (pad)) {
@@ -1139,7 +1251,7 @@ failure:
  *
  * Query if a pad is active
  *
- * Returns: TRUE if the pad is active.
+ * Returns: %TRUE if the pad is active.
  *
  * MT safe.
  */
@@ -1157,6 +1269,34 @@ gst_pad_is_active (GstPad * pad)
   return result;
 }
 
+static void
+cleanup_hook (GstPad * pad, GHook * hook)
+{
+  GstPadProbeType type;
+
+  if (!G_HOOK_IS_VALID (hook))
+    return;
+
+  type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT;
+
+  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);
+
+    /* Might have new probes now that want to be called */
+    GST_PAD_BLOCK_BROADCAST (pad);
+
+    if (pad->num_blocked == 0) {
+      GST_DEBUG_OBJECT (pad, "last blocking probe removed, unblocking");
+      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKED);
+    }
+  }
+  g_hook_destroy_link (&pad->probes, hook);
+  pad->num_probes--;
+}
+
 /**
  * gst_pad_add_probe:
  * @pad: the #GstPad to add the probe to
@@ -1169,8 +1309,10 @@ gst_pad_is_active (GstPad * pad)
  * Be notified of different states of pads. The provided callback is called for
  * every state that matches @mask.
  *
- * Returns: an id or 0 on error. The id can be used to remove the probe with
- * gst_pad_remove_probe().
+ * Returns: an id or 0 if no probe is pending. The id can be used to remove the
+ * probe with gst_pad_remove_probe(). When using GST_PAD_PROBE_TYPE_IDLE it can
+ * happen that the probe can be run immediately and if the probe returns
+ * GST_PAD_PROBE_REMOVE this functions returns 0.
  *
  * MT safe.
  */
@@ -1225,6 +1367,9 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask,
     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);
+
+    /* Might have new probes now that want to be called */
+    GST_PAD_BLOCK_BROADCAST (pad);
   }
 
   /* call the callback if we need to be called for idle callbacks */
@@ -1238,13 +1383,42 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask,
       GST_OBJECT_UNLOCK (pad);
     } else {
       GstPadProbeInfo info = { GST_PAD_PROBE_TYPE_IDLE, res, };
+      GstPadProbeReturn ret;
+
+      /* Keep another ref, the callback could destroy the pad */
+      gst_object_ref (pad);
 
       /* 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);
 
-      callback (pad, &info, user_data);
+      ret = callback (pad, &info, user_data);
+
+      GST_OBJECT_LOCK (pad);
+      switch (ret) {
+        case GST_PAD_PROBE_REMOVE:
+          /* remove the probe */
+          GST_DEBUG_OBJECT (pad, "asked to remove hook");
+          cleanup_hook (pad, hook);
+          res = 0;
+          break;
+        case GST_PAD_PROBE_DROP:
+          GST_DEBUG_OBJECT (pad, "asked to drop item");
+          break;
+        case GST_PAD_PROBE_PASS:
+          GST_DEBUG_OBJECT (pad, "asked to pass item");
+          break;
+        case GST_PAD_PROBE_OK:
+          GST_DEBUG_OBJECT (pad, "probe returned OK");
+          break;
+        default:
+          GST_DEBUG_OBJECT (pad, "probe returned %d", ret);
+          break;
+      }
+      GST_OBJECT_UNLOCK (pad);
+
+      gst_object_unref (pad);
     }
   } else {
     GST_OBJECT_UNLOCK (pad);
@@ -1252,28 +1426,6 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask,
   return res;
 }
 
-static void
-cleanup_hook (GstPad * pad, GHook * hook)
-{
-  GstPadProbeType type;
-
-  type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT;
-
-  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_remove_probe:
  * @pad: the #GstPad with the probe
@@ -1319,7 +1471,7 @@ not_found:
  * last requested state of the pad. It is not certain that the pad
  * is actually blocking at this point (see gst_pad_is_blocking()).
  *
- * Returns: TRUE if the pad is blocked.
+ * Returns: %TRUE if the pad is blocked.
  *
  * MT safe.
  */
@@ -1344,7 +1496,7 @@ gst_pad_is_blocked (GstPad * pad)
  * Checks if the pad is blocking or not. This is a guaranteed state
  * of whether the pad is actually blocking on a #GstBuffer or a #GstEvent.
  *
- * Returns: TRUE if the pad is blocking.
+ * Returns: %TRUE if the pad is blocking.
  *
  * MT safe.
  */
@@ -1405,9 +1557,10 @@ gst_pad_check_reconfigure (GstPad * pad)
 
   GST_OBJECT_LOCK (pad);
   reconfigure = GST_PAD_NEEDS_RECONFIGURE (pad);
-  if (reconfigure)
+  if (reconfigure) {
     GST_DEBUG_OBJECT (pad, "remove RECONFIGURE flag");
-  GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
+  }
   GST_OBJECT_UNLOCK (pad);
 
   return reconfigure;
@@ -1435,7 +1588,7 @@ gst_pad_mark_reconfigure (GstPad * pad)
  * @p: a #GstPad.
  * @f: the #GstPadActivateFunction to set.
  *
- * Calls gst_pad_set_activate_function_full() with NULL for the user_data and
+ * Calls gst_pad_set_activate_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1446,8 +1599,8 @@ gst_pad_mark_reconfigure (GstPad * pad)
  * @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
- * the actual activation. Only makes sense to set on sink pads.
+ * dispatch to gst_pad_activate_mode() to perform the actual activation.
+ * Only makes sense to set on sink pads.
  *
  * Call this function if your sink pad can start a pull-based task.
  */
@@ -1472,7 +1625,7 @@ gst_pad_set_activate_function_full (GstPad * pad,
  * @p: a #GstPad.
  * @f: the #GstPadActivateModeFunction to set.
  *
- * Calls gst_pad_set_activatemode_function_full() with NULL for the user_data and
+ * Calls gst_pad_set_activatemode_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1507,7 +1660,7 @@ gst_pad_set_activatemode_function_full (GstPad * pad,
  * @p: a sink #GstPad.
  * @f: the #GstPadChainFunction to set.
  *
- * Calls gst_pad_set_chain_function_full() with NULL for the user_data and
+ * Calls gst_pad_set_chain_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1542,7 +1695,7 @@ gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
  * @p: a sink #GstPad.
  * @f: the #GstPadChainListFunction to set.
  *
- * Calls gst_pad_set_chain_list_function_full() with NULL for the user_data and
+ * Calls gst_pad_set_chain_list_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1579,7 +1732,7 @@ gst_pad_set_chain_list_function_full (GstPad * pad,
  * @p: a source #GstPad.
  * @f: the #GstPadGetRangeFunction to set.
  *
- * Calls gst_pad_set_getrange_function_full() with NULL for the user_data and
+ * Calls gst_pad_set_getrange_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1615,7 +1768,7 @@ gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get,
  * @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
+ * Calls gst_pad_set_event_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1648,7 +1801,7 @@ gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event,
  * @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
+ * Calls gst_pad_set_query_function_full() with %NULL for the user_data and
  * notify.
  */
 /**
@@ -1681,7 +1834,7 @@ gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
  * @p: a #GstPad of either direction.
  * @f: the #GstPadIterIntLinkFunction to set.
  *
- * Calls gst_pad_set_iterate_internal_links_function_full() with NULL
+ * Calls gst_pad_set_iterate_internal_links_function_full() with %NULL
  * for the user_data and notify.
  */
 /**
@@ -1715,7 +1868,7 @@ gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
  * @p: a #GstPad.
  * @f: the #GstPadLinkFunction to set.
  *
- * Calls gst_pad_set_link_function_full() with NULL
+ * Calls gst_pad_set_link_function_full() with %NULL
  * for the user_data and notify.
  */
 /**
@@ -1758,7 +1911,7 @@ gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link,
  * @p: a #GstPad.
  * @f: the #GstPadUnlinkFunction to set.
  *
- * Calls gst_pad_set_unlink_function_full() with NULL
+ * Calls gst_pad_set_unlink_function_full() with %NULL
  * for the user_data and notify.
  */
 /**
@@ -1795,7 +1948,7 @@ gst_pad_set_unlink_function_full (GstPad * pad, GstPadUnlinkFunction 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
+ * Returns: %TRUE if the pads were unlinked. This function returns %FALSE if
  * the pads were not linked together.
  *
  * MT safe.
@@ -1897,7 +2050,7 @@ not_linked_together:
  *
  * Checks if a @pad is linked to another pad or not.
  *
- * Returns: TRUE if the pad is linked, FALSE otherwise.
+ * Returns: %TRUE if the pad is linked, %FALSE otherwise.
  *
  * MT safe.
  */
@@ -1961,7 +2114,7 @@ gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink,
       sinkcaps);
 
   /* if we have caps on both pads we can check the intersection. If one
-   * of the caps is NULL, we return TRUE. */
+   * of the caps is %NULL, we return %TRUE. */
   if (G_UNLIKELY (srccaps == NULL || sinkcaps == NULL)) {
     if (srccaps)
       gst_caps_unref (srccaps);
@@ -1976,7 +2129,7 @@ gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink,
 
 done:
   GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible",
-      (compatible ? "" : "not"));
+      (compatible ? "" : "not "));
 
   return compatible;
 }
@@ -2127,7 +2280,7 @@ no_format:
  * Checks if the source pad and the sink pad are compatible so they can be
  * linked.
  *
- * Returns: TRUE if the pads can be linked.
+ * Returns: %TRUE if the pads can be linked.
  */
 gboolean
 gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad)
@@ -2202,8 +2355,12 @@ 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 (G_UNLIKELY (result != GST_PAD_LINK_OK))
+  if (G_UNLIKELY (result != GST_PAD_LINK_OK)) {
+    GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
+        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
+        gst_pad_link_get_name (result));
     goto done;
+  }
 
   /* must set peers before calling the link function */
   GST_PAD_PEER (srcpad) = sinkpad;
@@ -2286,8 +2443,9 @@ concurrent_link:
   }
 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));
+    GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
+        GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
+        gst_pad_link_get_name (result));
 
     GST_PAD_PEER (srcpad) = NULL;
     GST_PAD_PEER (sinkpad) = NULL;
@@ -2339,9 +2497,9 @@ gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ)
  *
  * Gets the template for @pad.
  *
- * Returns: (transfer full): the #GstPadTemplate from which this pad was
- *     instantiated, or %NULL if this pad has no template. Unref after
- *     usage.
+ * Returns: (transfer full) (nullable): 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)
@@ -2361,7 +2519,7 @@ gst_pad_get_pad_template (GstPad * pad)
  *
  * Check if @pad has caps set on it with a #GST_EVENT_CAPS event.
  *
- * Returns: TRUE when @pad has caps associated with it.
+ * Returns: %TRUE when @pad has caps associated with it.
  */
 gboolean
 gst_pad_has_current_caps (GstPad * pad)
@@ -2465,9 +2623,9 @@ gst_pad_get_peer (GstPad * pad)
  * calling gst_pad_query_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.
+ * Returns: (transfer full) (nullable): 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.
  */
@@ -2476,7 +2634,6 @@ gst_pad_get_allowed_caps (GstPad * pad)
 {
   GstCaps *mycaps;
   GstCaps *caps;
-  GstCaps *peercaps;
   GstPad *peer;
 
   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
@@ -2492,11 +2649,9 @@ gst_pad_get_allowed_caps (GstPad * pad)
   GST_OBJECT_UNLOCK (pad);
   mycaps = gst_pad_query_caps (pad, NULL);
 
-  peercaps = gst_pad_query_caps (peer, NULL);
+  caps = gst_pad_query_caps (peer, mycaps);
   gst_object_unref (peer);
 
-  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,
@@ -2516,7 +2671,7 @@ no_peer:
 /**
  * gst_pad_iterate_internal_links_default:
  * @pad: the #GstPad to get the internal links of.
- * @parent: the parent of @pad or NULL
+ * @parent: (allow-none): the parent of @pad or %NULL
  *
  * Iterate the list of pads to which the given pad is linked to inside of
  * the parent element.
@@ -2525,8 +2680,8 @@ no_peer:
  *
  * 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().
+ * Returns: (nullable): a #GstIterator of #GstPad, or %NULL if @pad
+ * has no parent. Unref each returned pad with gst_object_unref().
  */
 GstIterator *
 gst_pad_iterate_internal_links_default (GstPad * pad, GstObject * parent)
@@ -2591,9 +2746,9 @@ no_parent:
  *
  * 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: (transfer full) (nullable): a new #GstIterator of #GstPad
+ *     or %NULL when the pad does not have an iterator function
+ *     configured. Use gst_iterator_free() after usage.
  */
 GstIterator *
 gst_pad_iterate_internal_links (GstPad * pad)
@@ -2619,7 +2774,7 @@ no_parent:
   {
     GST_DEBUG_OBJECT (pad, "no parent");
     GST_OBJECT_UNLOCK (pad);
-    return FALSE;
+    return NULL;
   }
 }
 
@@ -2633,9 +2788,9 @@ no_parent:
  * dynamically changing internal pads and will make sure that the @forward
  * function is only called once for each pad.
  *
- * When @forward returns TRUE, no further pads will be processed.
+ * When @forward returns %TRUE, no further pads will be processed.
  *
- * Returns: TRUE if one of the dispatcher functions returned TRUE.
+ * Returns: %TRUE if one of the dispatcher functions returned %TRUE.
  */
 gboolean
 gst_pad_forward (GstPad * pad, GstPadForwardFunction forward,
@@ -2725,7 +2880,7 @@ event_forward_func (GstPad * pad, EventData * data)
 /**
  * gst_pad_event_default:
  * @pad: a #GstPad to call the default event handler on.
- * @parent: the parent of @pad or NULL
+ * @parent: (allow-none): the parent of @pad or %NULL
  * @event: (transfer full): the #GstEvent to handle.
  *
  * Invokes the default event handler for the given pad.
@@ -2733,10 +2888,10 @@ event_forward_func (GstPad * pad, EventData * data)
  * The EOS event will pause the task associated with @pad before it is forwarded
  * to all internally linked pads,
  *
- * The the event is sent to all pads internally linked to @pad. This function
+ * 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.
+ * Returns: %TRUE if the event was sent successfully.
  */
 gboolean
 gst_pad_event_default (GstPad * pad, GstObject * parent, GstEvent * event)
@@ -2802,15 +2957,23 @@ gst_pad_query_accept_caps_default (GstPad * pad, GstQuery * query)
   GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pad,
       "fallback ACCEPT_CAPS query, consider implementing a specialized version");
 
-  allowed = gst_pad_query_caps (pad, NULL);
   gst_query_parse_accept_caps (query, &caps);
+  allowed = gst_pad_query_caps (pad, caps);
 
   if (allowed) {
-    GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed);
-    result = gst_caps_is_subset (caps, allowed);
+    if (GST_PAD_IS_ACCEPT_INTERSECT (pad)) {
+      GST_DEBUG_OBJECT (pad,
+          "allowed caps intersect %" GST_PTR_FORMAT ", caps %" GST_PTR_FORMAT,
+          allowed, caps);
+      result = gst_caps_can_intersect (caps, allowed);
+    } else {
+      GST_DEBUG_OBJECT (pad, "allowed caps subset %" GST_PTR_FORMAT ", caps %"
+          GST_PTR_FORMAT, allowed, caps);
+      result = gst_caps_is_subset (caps, allowed);
+    }
     gst_caps_unref (allowed);
   } else {
-    GST_DEBUG_OBJECT (pad, "no caps allowed on the pad");
+    GST_DEBUG_OBJECT (pad, "no compatible caps allowed on the pad");
     result = FALSE;
   }
   gst_query_set_accept_caps_result (query, result);
@@ -2891,6 +3054,123 @@ done:
   return TRUE;
 }
 
+/* Default latency implementation */
+typedef struct
+{
+  gboolean live;
+  GstClockTime min, max;
+} LatencyFoldData;
+
+static gboolean
+query_latency_default_fold (const GValue * item, GValue * ret,
+    gpointer user_data)
+{
+  GstPad *pad = g_value_get_object (item), *peer;
+  LatencyFoldData *fold_data = user_data;
+  GstQuery *query;
+  gboolean res = FALSE;
+
+  query = gst_query_new_latency ();
+
+  peer = gst_pad_get_peer (pad);
+  if (peer) {
+    res = gst_pad_peer_query (pad, query);
+  } else {
+    GST_LOG_OBJECT (pad, "No peer pad found, ignoring this pad");
+  }
+
+  if (res) {
+    gboolean live;
+    GstClockTime min, max;
+
+    gst_query_parse_latency (query, &live, &min, &max);
+
+    GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT
+        " max:%" G_GINT64_FORMAT, live ? "true" : "false", min, max);
+
+    if (live) {
+      if (min > fold_data->min)
+        fold_data->min = min;
+
+      if (fold_data->max == GST_CLOCK_TIME_NONE)
+        fold_data->max = max;
+      else if (max < fold_data->max)
+        fold_data->max = max;
+
+      fold_data->live = TRUE;
+    }
+  } else if (peer) {
+    GST_DEBUG_OBJECT (pad, "latency query failed");
+    g_value_set_boolean (ret, FALSE);
+  }
+
+  gst_query_unref (query);
+  if (peer)
+    gst_object_unref (peer);
+
+  return TRUE;
+}
+
+static gboolean
+gst_pad_query_latency_default (GstPad * pad, GstQuery * query)
+{
+  GstIterator *it;
+  GstIteratorResult res;
+  GValue ret = G_VALUE_INIT;
+  gboolean query_ret;
+  LatencyFoldData fold_data;
+
+  it = gst_pad_iterate_internal_links (pad);
+  if (!it) {
+    GST_DEBUG_OBJECT (pad, "Can't iterate internal links");
+    return FALSE;
+  }
+
+  g_value_init (&ret, G_TYPE_BOOLEAN);
+
+retry:
+  fold_data.live = FALSE;
+  fold_data.min = 0;
+  fold_data.max = GST_CLOCK_TIME_NONE;
+
+  g_value_set_boolean (&ret, TRUE);
+  res = gst_iterator_fold (it, query_latency_default_fold, &ret, &fold_data);
+  switch (res) {
+    case GST_ITERATOR_OK:
+      g_assert_not_reached ();
+      break;
+    case GST_ITERATOR_DONE:
+      break;
+    case GST_ITERATOR_ERROR:
+      g_value_set_boolean (&ret, FALSE);
+      break;
+    case GST_ITERATOR_RESYNC:
+      gst_iterator_resync (it);
+      goto retry;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+  gst_iterator_free (it);
+
+  query_ret = g_value_get_boolean (&ret);
+  if (query_ret) {
+    GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT
+        " max:%" G_GINT64_FORMAT, fold_data.live ? "true" : "false",
+        fold_data.min, fold_data.max);
+
+    if (fold_data.min > fold_data.max) {
+      GST_ERROR_OBJECT (pad, "minimum latency bigger than maximum latency");
+    }
+
+    gst_query_set_latency (query, fold_data.live, fold_data.min, fold_data.max);
+  } else {
+    GST_LOG_OBJECT (pad, "latency query failed");
+  }
+
+  return query_ret;
+}
+
 typedef struct
 {
   GstQuery *query;
@@ -2915,7 +3195,7 @@ query_forward_func (GstPad * pad, QueryData * data)
 /**
  * gst_pad_query_default:
  * @pad: a #GstPad to call the default query handler on.
- * @parent: the parent of @pad or NULL
+ * @parent: (allow-none): the parent of @pad or %NULL
  * @query: (transfer none): the #GstQuery to handle.
  *
  * Invokes the default query handler for the given pad.
@@ -2924,7 +3204,7 @@ query_forward_func (GstPad * pad, QueryData * data)
  * @pad, only one will be sent the query.
  * Multi-sinkpad elements should implement custom query handlers.
  *
- * Returns: TRUE if the query was performed successfully.
+ * Returns: %TRUE if the query was performed successfully.
  */
 gboolean
 gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
@@ -2946,10 +3226,13 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
       ret = gst_pad_query_caps_default (pad, query);
       forward = FALSE;
       break;
+    case GST_QUERY_LATENCY:
+      ret = gst_pad_query_latency_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:
@@ -2991,6 +3274,7 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
   GstPadProbeType type, flags;
   GstPadProbeCallback callback;
   GstPadProbeReturn ret;
+  gpointer original_data;
 
   /* if we have called this callback, do nothing */
   if (PROBE_COOKIE (hook) == data->cookie) {
@@ -3004,9 +3288,11 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
 
   flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT;
   type = info->type;
+  original_data = info->data;
 
-  /* one of the data types */
-  if ((flags & GST_PAD_PROBE_TYPE_ALL_BOTH & type) == 0)
+  /* one of the data types for non-idle probes */
+  if ((type & GST_PAD_PROBE_TYPE_IDLE) == 0
+      && (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)
@@ -3015,6 +3301,9 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
   if ((type & GST_PAD_PROBE_TYPE_BLOCKING) &&
       (flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0)
     goto no_match;
+  if ((type & GST_PAD_PROBE_TYPE_BLOCKING) == 0 &&
+      (flags & GST_PAD_PROBE_TYPE_BLOCKING))
+    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)
@@ -3038,6 +3327,12 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
 
   GST_OBJECT_LOCK (pad);
 
+  if (original_data != NULL && info->data == NULL) {
+    GST_DEBUG_OBJECT (pad, "data item in pad probe info was dropped");
+    info->type = GST_PAD_PROBE_TYPE_INVALID;
+    data->dropped = TRUE;
+  }
+
   switch (ret) {
     case GST_PAD_PROBE_REMOVE:
       /* remove the probe */
@@ -3056,6 +3351,9 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
       GST_DEBUG_OBJECT (pad, "asked to pass item");
       data->pass = TRUE;
       break;
+    case GST_PAD_PROBE_OK:
+      GST_DEBUG_OBJECT (pad, "probe returned OK");
+      break;
     default:
       GST_DEBUG_OBJECT (pad, "probe returned %d", ret);
       break;
@@ -3075,10 +3373,11 @@ no_match:
 #define PROBE_NO_DATA(pad,mask,label,defaultval)                \
   G_STMT_START {                                               \
     if (G_UNLIKELY (pad->num_probes)) {                                \
+      GstFlowReturn pval = defaultval;                         \
       /* 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))        \
+      if (G_UNLIKELY (ret != pval && ret != GST_FLOW_OK))      \
         goto label;                                            \
     }                                                          \
   } G_STMT_END
@@ -3169,6 +3468,14 @@ again:
       GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKING);
       GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "We got unblocked");
 
+      /* 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;
+      }
+
       if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushing;
     }
@@ -3220,6 +3527,13 @@ gst_pad_get_offset (GstPad * pad)
   return result;
 }
 
+static gboolean
+mark_event_not_received (GstPad * pad, PadEvent * ev, gpointer user_data)
+{
+  ev->received = FALSE;
+  return TRUE;
+}
+
 /**
  * gst_pad_set_offset:
  * @pad: a #GstPad
@@ -3230,8 +3544,6 @@ gst_pad_get_offset (GstPad * pad)
 void
 gst_pad_set_offset (GstPad * pad, gint64 offset)
 {
-  PadEvent *ev;
-
   g_return_if_fail (GST_IS_PAD (pad));
 
   GST_OBJECT_LOCK (pad);
@@ -3242,16 +3554,9 @@ gst_pad_set_offset (GstPad * pad, gint64 offset)
   pad->offset = offset;
   GST_DEBUG_OBJECT (pad, "changed offset to %" G_GINT64_FORMAT, offset);
 
-  /* sinkpads will apply their offset the next time a segment
-   * event is received. */
-  if (GST_PAD_IS_SINK (pad))
-    goto done;
-
-  /* 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);
-  }
+  /* resend all sticky events with updated offset on next buffer push */
+  events_foreach (pad, mark_event_not_received, NULL);
+  GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
 
 done:
   GST_OBJECT_UNLOCK (pad);
@@ -3265,6 +3570,12 @@ typedef struct
    * that pushing the EOS event failed
    */
   gboolean was_eos;
+
+  /* If called for an event this is
+   * the event that would be pushed
+   * next. Don't forward sticky events
+   * that would come after that */
+  GstEvent *event;
 } PushStickyData;
 
 /* should be called with pad LOCK */
@@ -3280,8 +3591,17 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
     return TRUE;
   }
 
-  data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event),
-      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+  /* If we're called because of an sticky event, only forward
+   * events that would come before this new event and the
+   * event itself */
+  if (data->event && GST_EVENT_IS_STICKY (data->event) &&
+      GST_EVENT_TYPE (data->event) <= GST_EVENT_SEGMENT &&
+      GST_EVENT_TYPE (data->event) < GST_EVENT_TYPE (event)) {
+    data->ret = GST_FLOW_CUSTOM_SUCCESS_1;
+  } else {
+    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:
@@ -3289,15 +3609,31 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
       GST_DEBUG_OBJECT (pad, "event %s marked received",
           GST_EVENT_TYPE_NAME (event));
       break;
+    case GST_FLOW_CUSTOM_SUCCESS:
+      /* we can't assume the event is received when it was dropped */
+      GST_DEBUG_OBJECT (pad, "event %s was dropped, mark pending",
+          GST_EVENT_TYPE_NAME (event));
+      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+      data->ret = GST_FLOW_OK;
+      break;
+    case GST_FLOW_CUSTOM_SUCCESS_1:
+      /* event was ignored and should be sent later */
+      GST_DEBUG_OBJECT (pad, "event %s was ignored, mark pending",
+          GST_EVENT_TYPE_NAME (event));
+      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+      data->ret = GST_FLOW_OK;
+      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");
+      GST_DEBUG_OBJECT (pad, "pad was not linked, mark pending");
       if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
         data->ret = GST_FLOW_OK;
-      /* fallthrough */
+      GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+      break;
     default:
-      GST_DEBUG_OBJECT (pad, "mark pending events");
+      GST_DEBUG_OBJECT (pad, "result %s, mark pending events",
+          gst_flow_get_name (data->ret));
       GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
       break;
   }
@@ -3311,9 +3647,9 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
 /* check sticky events and push them when needed. should be called
  * with pad LOCK */
 static inline GstFlowReturn
-check_sticky (GstPad * pad)
+check_sticky (GstPad * pad, GstEvent * event)
 {
-  PushStickyData data = { GST_FLOW_OK, FALSE };
+  PushStickyData data = { GST_FLOW_OK, FALSE, event };
 
   if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
     GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
@@ -3334,6 +3670,10 @@ check_sticky (GstPad * pad)
       if (ev && !ev->received) {
         data.ret = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
             GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+        /* the event could have been dropped. Because this can only
+         * happen if the user asked for it, it's not an error */
+        if (data.ret == GST_FLOW_CUSTOM_SUCCESS)
+          data.ret = GST_FLOW_OK;
       }
     }
   }
@@ -3357,7 +3697,7 @@ check_sticky (GstPad * pad)
  *
  * Please also note that some queries might need a running pipeline to work.
  *
- * Returns: TRUE if the query could be performed.
+ * Returns: %TRUE if the query could be performed.
  */
 gboolean
 gst_pad_query (GstPad * pad, GstQuery * query)
@@ -3462,11 +3802,8 @@ probe_stopped:
       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;
+     * did not answer the query and return FALSE */
+    res = FALSE;
 
     return res;
   }
@@ -3482,7 +3819,7 @@ probe_stopped:
  * The caller is responsible for both the allocation and deallocation of
  * the query structure.
  *
- * Returns: TRUE if the query could be performed. This function returns %FALSE
+ * Returns: %TRUE if the query could be performed. This function returns %FALSE
  * if @pad has no peer.
  */
 gboolean
@@ -3516,7 +3853,7 @@ gst_pad_peer_query (GstPad * pad, GstQuery * query)
   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)
+    if (check_sticky (pad, NULL) != GST_FLOW_OK)
       goto sticky_failed;
   }
 
@@ -3564,7 +3901,7 @@ sticky_failed:
   }
 no_peer:
   {
-    GST_WARNING_OBJECT (pad, "pad has no peer");
+    GST_INFO_OBJECT (pad, "pad has no peer");
     GST_OBJECT_UNLOCK (pad);
     return FALSE;
   }
@@ -3579,11 +3916,8 @@ probe_stopped:
     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;
+     * did not answer the query and return FALSE */
+    res = FALSE;
 
     return res;
   }
@@ -3614,6 +3948,22 @@ gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
     goto wrong_mode;
 
+#ifndef G_DISABLE_ASSERT
+  if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) {
+    if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) {
+      g_warning (G_STRLOC
+          ":%s:<%s:%s> Got data flow before stream-start event",
+          G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
+    }
+    if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) {
+      g_warning (G_STRLOC
+          ":%s:<%s:%s> Got data flow before segment event",
+          G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
+    }
+    pad->priv->last_cookie = pad->priv->events_cookie;
+  }
+#endif
+
   PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
 
   PROBE_PUSH (pad, type, data, probe_stopped);
@@ -3701,7 +4051,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -3727,7 +4077,7 @@ no_function:
  * The function returns #GST_FLOW_FLUSHING if the pad was flushing.
  *
  * If the buffer type is not acceptable for @pad (as negotiated with a
- * preceeding GST_EVENT_CAPS event), this function returns
+ * preceding GST_EVENT_CAPS event), this function returns
  * #GST_FLOW_NOT_NEGOTIATED.
  *
  * The function proceeds calling the chain function installed on @pad (see
@@ -3761,7 +4111,7 @@ gst_pad_chain_list_default (GstPad * pad, GstObject * parent,
   GstBuffer *buffer;
   GstFlowReturn ret;
 
-  GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
+  GST_INFO_OBJECT (pad, "chaining each buffer in list individually");
 
   len = gst_buffer_list_length (list);
 
@@ -3832,14 +4182,30 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
   if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
     goto wrong_mode;
 
-  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+#ifndef G_DISABLE_ASSERT
+  if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) {
+    if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) {
+      g_warning (G_STRLOC
+          ":%s:<%s:%s> Got data flow before stream-start event",
+          G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
+    }
+    if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) {
+      g_warning (G_STRLOC
+          ":%s:<%s:%s> Got data flow before segment event",
+          G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
+    }
+    pad->priv->last_cookie = pad->priv->events_cookie;
+  }
+#endif
+
+  if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
     goto events_error;
 
   /* do block probes */
   PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
 
   /* recheck sticky events because the probe might have cause a relink */
-  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+  if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
     goto events_error;
 
   /* do post-blocking probes */
@@ -3858,6 +4224,7 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
   gst_object_unref (peer);
 
   GST_OBJECT_LOCK (pad);
+  pad->ABI.abi.last_flowret = ret;
   pad->priv->using--;
   if (pad->priv->using == 0) {
     /* pad is not active anymore, trigger idle callbacks */
@@ -3874,6 +4241,7 @@ flushing:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pushing, but pad was flushing");
+    pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_FLUSHING;
@@ -3881,6 +4249,7 @@ flushing:
 eos:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was EOS");
+    pad->ABI.abi.last_flowret = GST_FLOW_EOS;
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_EOS;
@@ -3889,6 +4258,7 @@ wrong_mode:
   {
     g_critical ("pushing on pad %s:%s but it was not activated in push mode",
         GST_DEBUG_PAD_NAME (pad));
+    pad->ABI.abi.last_flowret = GST_FLOW_ERROR;
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_ERROR;
@@ -3897,6 +4267,7 @@ events_error:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "error pushing events, return %s", gst_flow_get_name (ret));
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return ret;
@@ -3904,7 +4275,10 @@ events_error:
 probe_stopped:
   {
     GST_OBJECT_UNLOCK (pad);
-    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    pad->ABI.abi.last_flowret =
+        ret == GST_FLOW_CUSTOM_SUCCESS ? GST_FLOW_OK : ret;
+    if (data != NULL)
+      gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
 
     switch (ret) {
       case GST_FLOW_CUSTOM_SUCCESS:
@@ -3912,7 +4286,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -3921,6 +4295,7 @@ not_linked:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pushing, but it was not linked");
+    pad->ABI.abi.last_flowret = GST_FLOW_NOT_LINKED;
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return GST_FLOW_NOT_LINKED;
@@ -4013,7 +4388,7 @@ 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 ((ret = check_sticky (pad))) != GST_FLOW_OK)
+  if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
     goto events_error;
 
   res_buf = *buffer;
@@ -4025,7 +4400,7 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
       res_buf, offset, size, probe_stopped);
 
   /* recheck sticky events because the probe might have cause a relink */
-  if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+  if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
     goto events_error;
 
   ACQUIRE_PARENT (pad, parent, no_parent);
@@ -4043,14 +4418,15 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
 
   RELEASE_PARENT (parent);
 
+  GST_OBJECT_LOCK (pad);
   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);
 probed_data:
   PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
       res_buf, offset, size, probe_stopped_unref);
+  pad->ABI.abi.last_flowret = ret;
   GST_OBJECT_UNLOCK (pad);
 
   GST_PAD_STREAM_UNLOCK (pad);
@@ -4064,6 +4440,7 @@ flushing:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "getrange, but pad was flushing");
+    pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_FLUSHING;
@@ -4072,6 +4449,7 @@ wrong_mode:
   {
     g_critical ("getrange on pad %s:%s but it was not activated in pull mode",
         GST_DEBUG_PAD_NAME (pad));
+    pad->ABI.abi.last_flowret = GST_FLOW_ERROR;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_ERROR;
@@ -4079,6 +4457,7 @@ wrong_mode:
 events_error:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "error pushing events");
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     return ret;
@@ -4086,6 +4465,7 @@ events_error:
 no_parent:
   {
     GST_DEBUG_OBJECT (pad, "no parent");
+    pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_FLUSHING;
@@ -4115,6 +4495,7 @@ probe_stopped:
         ret = GST_FLOW_EOS;
       }
     }
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
 
@@ -4127,6 +4508,7 @@ probe_stopped_unref:
     /* if we drop here, it signals EOS */
     if (ret == GST_FLOW_CUSTOM_SUCCESS)
       ret = GST_FLOW_EOS;
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     if (*buffer == NULL)
@@ -4135,6 +4517,8 @@ probe_stopped_unref:
   }
 get_range_failed:
   {
+    pad->ABI.abi.last_flowret = ret;
+    GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
         (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
@@ -4159,7 +4543,7 @@ 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
+ * 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.
  *
@@ -4176,7 +4560,7 @@ get_range_failed:
  * 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.
+ * This is a lowlevel function. Usually gst_pad_pull_range() is used.
  *
  * Returns: a #GstFlowReturn from the pad.
  *
@@ -4189,8 +4573,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);
+  g_return_val_if_fail (*buffer == NULL || (GST_IS_BUFFER (*buffer)
+          && gst_buffer_get_size (*buffer) >= size), GST_FLOW_ERROR);
 
   return gst_pad_get_range_unchecked (pad, offset, size, buffer);
 }
@@ -4213,10 +4597,10 @@ 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
+ * 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.
+ * 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
@@ -4244,8 +4628,8 @@ gst_pad_pull_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_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);
+  g_return_val_if_fail (*buffer == NULL || (GST_IS_BUFFER (*buffer)
+          && gst_buffer_get_size (*buffer) >= size), GST_FLOW_ERROR);
 
   GST_OBJECT_LOCK (pad);
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
@@ -4275,6 +4659,7 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
 
   GST_OBJECT_LOCK (pad);
   pad->priv->using--;
+  pad->ABI.abi.last_flowret = ret;
   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,
@@ -4299,6 +4684,7 @@ flushing:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pullrange, but pad was flushing");
+    pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_FLUSHING;
   }
@@ -4306,6 +4692,7 @@ wrong_mode:
   {
     g_critical ("pullrange on pad %s:%s but it was not activated in pull mode",
         GST_DEBUG_PAD_NAME (pad));
+    pad->ABI.abi.last_flowret = GST_FLOW_ERROR;
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_ERROR;
   }
@@ -4326,6 +4713,7 @@ probe_stopped:
         ret = GST_FLOW_EOS;
       }
     }
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     return ret;
   }
@@ -4333,11 +4721,13 @@ not_linked:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pulling range, but it was not linked");
+    pad->ABI.abi.last_flowret = GST_FLOW_NOT_LINKED;
     GST_OBJECT_UNLOCK (pad);
     return GST_FLOW_NOT_LINKED;
   }
 pull_range_failed:
   {
+    pad->ABI.abi.last_flowret = ret;
     GST_OBJECT_UNLOCK (pad);
     GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
         (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
@@ -4348,26 +4738,45 @@ 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 we drop here, it signals EOS */
     if (ret == GST_FLOW_CUSTOM_SUCCESS)
       ret = GST_FLOW_EOS;
+
+    pad->ABI.abi.last_flowret = ret;
+    GST_OBJECT_UNLOCK (pad);
+
     if (*buffer == NULL)
       gst_buffer_unref (res_buf);
     return ret;
   }
 }
 
-static gboolean
-gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
+/* must be called with pad object lock */
+static GstFlowReturn
+store_sticky_event (GstPad * pad, GstEvent * event)
 {
   guint i, len;
   GstEventType type;
   GArray *events;
   gboolean res = FALSE;
   const gchar *name = NULL;
+  gboolean insert = TRUE;
 
   type = GST_EVENT_TYPE (event);
+
+  /* Store all sticky events except SEGMENT/EOS when we're flushing,
+   * otherwise they can be dropped and nothing would ever resend them.
+   * Only do that for activated pads though, everything else is a bug!
+   */
+  if (G_UNLIKELY (GST_PAD_MODE (pad) == GST_PAD_MODE_NONE
+          || (GST_PAD_IS_FLUSHING (pad) && (type == GST_EVENT_SEGMENT
+                  || type == GST_EVENT_EOS))))
+    goto flushed;
+
+  if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+    goto eos;
+
   if (type & GST_EVENT_TYPE_STICKY_MULTI)
     name = gst_structure_get_name (gst_event_get_structure (event));
 
@@ -4388,14 +4797,30 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
       /* overwrite */
       if ((res = gst_event_replace (&ev->event, event)))
         ev->received = FALSE;
+
+      insert = FALSE;
+      break;
+    }
+
+    if (type < GST_EVENT_TYPE (ev->event) || (type != GST_EVENT_TYPE (ev->event)
+            && GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS)) {
+      /* STREAM_START, CAPS and SEGMENT must be delivered in this order. By
+       * storing the sticky ordered we can check that this is respected. */
+      if (G_UNLIKELY (GST_EVENT_TYPE (ev->event) <= GST_EVENT_SEGMENT
+              || GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS))
+        g_warning (G_STRLOC
+            ":%s:<%s:%s> Sticky event misordering, got '%s' before '%s'",
+            G_STRFUNC, GST_DEBUG_PAD_NAME (pad),
+            gst_event_type_get_name (GST_EVENT_TYPE (ev->event)),
+            gst_event_type_get_name (type));
       break;
     }
   }
-  if (i == len) {
+  if (insert) {
     PadEvent ev;
     ev.event = gst_event_ref (event);
     ev.received = FALSE;
-    g_array_append_val (events, ev);
+    g_array_insert_val (events, i, ev);
     res = TRUE;
   }
 
@@ -4407,20 +4832,75 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
 
     switch (GST_EVENT_TYPE (event)) {
       case GST_EVENT_CAPS:
-        if (locked)
-          GST_OBJECT_UNLOCK (pad);
+        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);
+        GST_OBJECT_LOCK (pad);
         break;
       default:
         break;
     }
   }
-  return res;
+  if (type == GST_EVENT_EOS) {
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS);
+    pad->ABI.abi.last_flowret = GST_FLOW_EOS;
+  }
+
+  return GST_PAD_IS_FLUSHING (pad) ? GST_FLOW_FLUSHING : GST_FLOW_OK;
+
+  /* ERRORS */
+flushed:
+  {
+    GST_DEBUG_OBJECT (pad, "pad is flushing");
+    return GST_FLOW_FLUSHING;
+  }
+eos:
+  {
+    GST_DEBUG_OBJECT (pad, "pad is EOS");
+    return GST_FLOW_EOS;
+  }
+}
+
+/**
+ * gst_pad_store_sticky_event:
+ * @pad: a #GstPad
+ * @event: a #GstEvent
+ *
+ * Store the sticky @event on @pad
+ *
+ * Returns: #GST_FLOW_OK on success, #GST_FLOW_FLUSHING when the pad
+ * was flushing or #GST_FLOW_EOS when the pad was EOS.
+ *
+ * Since: 1.2
+ */
+GstFlowReturn
+gst_pad_store_sticky_event (GstPad * pad, GstEvent * event)
+{
+  GstFlowReturn ret;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+
+  GST_OBJECT_LOCK (pad);
+  ret = store_sticky_event (pad, event);
+  GST_OBJECT_UNLOCK (pad);
+
+  return ret;
+}
+
+static gboolean
+sticky_changed (GstPad * pad, PadEvent * ev, gpointer user_data)
+{
+  PushStickyData *data = user_data;
+
+  /* Forward all sticky events before our current one that are pending */
+  if (ev->event != data->event
+      && GST_EVENT_TYPE (ev->event) < GST_EVENT_TYPE (data->event))
+    return push_sticky (pad, ev, data);
+
+  return TRUE;
 }
 
 /* should be called with pad LOCK */
@@ -4432,6 +4912,10 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
   GstPad *peerpad;
   GstEventType event_type;
 
+  /* pass the adjusted event on. We need to do this even if
+   * there is no peer pad because of the probes. */
+  event = apply_pad_offset (pad, event, GST_PAD_IS_SINK (pad));
+
   /* Two checks to be made:
    * . (un)set the FLUSHING flag for flushing events,
    * . handle pad blocking */
@@ -4444,12 +4928,17 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
       type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       break;
     case GST_EVENT_FLUSH_STOP:
+      if (G_UNLIKELY (!GST_PAD_IS_ACTIVE (pad)))
+        goto inactive;
+
       GST_PAD_UNSET_FLUSHING (pad);
 
       /* Remove sticky EOS events */
       GST_LOG_OBJECT (pad, "Removing pending EOS events");
       remove_event_by_type (pad, GST_EVENT_EOS);
+      remove_event_by_type (pad, GST_EVENT_SEGMENT);
       GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
+      pad->ABI.abi.last_flowret = GST_FLOW_OK;
 
       type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       break;
@@ -4464,11 +4953,6 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
        */
 
       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);
@@ -4485,6 +4969,18 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
   /* send probes after modifying the events above */
   PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped);
 
+  /* recheck sticky events because the probe might have cause a relink */
+  if (GST_PAD_HAS_PENDING_EVENTS (pad) && GST_PAD_IS_SRC (pad)
+      && (GST_EVENT_IS_SERIALIZED (event)
+          || GST_EVENT_IS_STICKY (event))) {
+    PushStickyData data = { GST_FLOW_OK, FALSE, event };
+    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+
+    /* Push all sticky events before our current one
+     * that have changed */
+    events_foreach (pad, sticky_changed, &data);
+  }
+
   /* now check the peer pad */
   peerpad = GST_PAD_PEER (pad);
   if (peerpad == NULL)
@@ -4495,14 +4991,15 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
   GST_OBJECT_UNLOCK (pad);
 
   GST_LOG_OBJECT (pad, "sending event %p (%s) to peerpad %" GST_PTR_FORMAT,
-      event, GST_EVENT_TYPE_NAME (event), peerpad);
+      event, gst_event_type_get_name (event_type), 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));
+  GST_LOG_OBJECT (pad,
+      "sent event %p (%s) to peerpad %" GST_PTR_FORMAT ", ret %s", event,
+      gst_event_type_get_name (event_type), peerpad, gst_flow_get_name (ret));
 
   gst_object_unref (peerpad);
 
@@ -4522,6 +5019,12 @@ flushed:
     gst_event_unref (event);
     return GST_FLOW_FLUSHING;
   }
+inactive:
+  {
+    GST_DEBUG_OBJECT (pad, "flush-stop on inactive pad");
+    gst_event_unref (event);
+    return GST_FLOW_FLUSHING;
+  }
 probe_stopped:
   {
     GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
@@ -4530,17 +5033,17 @@ probe_stopped:
     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));
+        GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
   }
 not_linked:
   {
-    GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
+    GST_DEBUG_OBJECT (pad, "Dropping event %s because pad is not linked",
+        gst_event_type_get_name (GST_EVENT_TYPE (event)));
     GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
     gst_event_unref (event);
 
@@ -4566,10 +5069,10 @@ idle_probe_stopped:
  * mainly used by elements to send events to their peer
  * elements.
  *
- * This function takes owership of the provided event so you should
+ * This function takes ownership of the provided event so you should
  * gst_event_ref() it if you want to reuse the event after this call.
  *
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
  *
  * MT safe.
  */
@@ -4600,30 +5103,30 @@ gst_pad_push_event (GstPad * pad, GstEvent * 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
+    /* srcpad sticky events are stored 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));
+    switch (store_sticky_event (pad, event)) {
+      case GST_FLOW_FLUSHING:
+        goto flushed;
+      case GST_FLOW_EOS:
+        goto eos;
+      default:
+        break;
     }
-    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);
+    res = (check_sticky (pad, event) == GST_FLOW_OK);
   }
   if (!sticky) {
+    GstFlowReturn ret;
+
     /* other events are pushed right away */
-    res = (gst_pad_push_event_unchecked (pad, event, type) == GST_FLOW_OK);
+    ret = gst_pad_push_event_unchecked (pad, event, type);
+    /* dropped events by a probe are not an error */
+    res = (ret == GST_FLOW_OK || ret == GST_FLOW_CUSTOM_SUCCESS);
   } else {
     /* Errors in sticky event pushing are no problem and ignored here
      * as they will cause more meaningful errors during data flow.
@@ -4709,6 +5212,9 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
   GstObject *parent;
 
   GST_OBJECT_LOCK (pad);
+
+  event = apply_pad_offset (pad, event, GST_PAD_IS_SRC (pad));
+
   if (GST_PAD_IS_SINK (pad))
     serialized = GST_EVENT_IS_SERIALIZED (event);
   else
@@ -4721,21 +5227,28 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
           "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
 
       /* can't even accept a flush begin event when flushing */
-      if (GST_PAD_IS_FLUSHING (pad))
+      if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushing;
 
       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_MODE (pad) != GST_PAD_MODE_NONE)) {
-        GST_PAD_UNSET_FLUSHING (pad);
-        GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag");
-      }
+      /* we can't accept flush-stop on inactive pads else the flushing flag
+       * would be cleared and it would look like the pad can accept data.
+       * Also, some elements restart a streaming thread in flush-stop which we
+       * can't allow on inactive pads */
+      if (G_UNLIKELY (!GST_PAD_IS_ACTIVE (pad)))
+        goto inactive;
+
+      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");
+      GST_LOG_OBJECT (pad, "Removing pending EOS and SEGMENT events");
       remove_event_by_type (pad, GST_EVENT_EOS);
+      remove_event_by_type (pad, GST_EVENT_SEGMENT);
       GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
+      pad->ABI.abi.last_flowret = GST_FLOW_OK;
 
       GST_OBJECT_UNLOCK (pad);
       /* grab stream lock */
@@ -4770,24 +5283,16 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
         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;
   }
 
+  /* 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);
+
   if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL))
     goto no_function;
 
@@ -4820,11 +5325,18 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
 
   if (sticky) {
     if (ret == GST_FLOW_OK) {
+      GST_OBJECT_LOCK (pad);
       /* 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);
+      switch (store_sticky_event (pad, event)) {
+        case GST_FLOW_FLUSHING:
+          goto flushing;
+        case GST_FLOW_EOS:
+          goto eos;
+        default:
+          break;
+      }
+      GST_OBJECT_UNLOCK (pad);
     }
     gst_event_unref (event);
   }
@@ -4845,6 +5357,16 @@ flushing:
     gst_event_unref (event);
     return GST_FLOW_FLUSHING;
   }
+inactive:
+  {
+    GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
+    GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+        "Received flush-stop on inactive pad. Discarding");
+    gst_event_unref (event);
+    return GST_FLOW_FLUSHING;
+  }
 eos:
   {
     GST_OBJECT_UNLOCK (pad);
@@ -4868,7 +5390,7 @@ probe_stopped:
         ret = GST_FLOW_OK;
         break;
       default:
-        GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+        GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret));
         break;
     }
     return ret;
@@ -4926,10 +5448,10 @@ precheck_failed:
  * 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
+ * This function takes ownership of the provided event so you should
  * gst_event_ref() it if you want to reuse the event after this call.
  *
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
  */
 gboolean
 gst_pad_send_event (GstPad * pad, GstEvent * event)
@@ -5013,8 +5535,9 @@ gst_pad_get_element_private (GstPad * pad)
  * 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.
+ * Returns: (transfer full) (nullable): 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)
@@ -5044,13 +5567,15 @@ static gboolean
 foreach_dispatch_function (GstPad * pad, PadEvent * ev, gpointer user_data)
 {
   ForeachDispatch *data = user_data;
-  gboolean ret;
+  gboolean ret = TRUE;
 
-  GST_OBJECT_UNLOCK (pad);
+  if (ev->event) {
+    GST_OBJECT_UNLOCK (pad);
 
-  ret = data->func (pad, &ev->event, data->user_data);
+    ret = data->func (pad, &ev->event, data->user_data);
 
-  GST_OBJECT_LOCK (pad);
+    GST_OBJECT_LOCK (pad);
+  }
 
   return ret;
 }
@@ -5207,7 +5732,7 @@ concurrent_stop:
  * function executed by the task is finished if this function is not
  * called from the task function.
  *
- * Returns: a TRUE if the task could be paused or FALSE when the pad
+ * Returns: a %TRUE if the task could be paused or %FALSE when the pad
  * has no task.
  */
 gboolean
@@ -5256,7 +5781,7 @@ no_task:
  * Regardless of whether the pad has a task, the stream lock is acquired and
  * released so as to ensure that streaming through this pad has finished.
  *
- * Returns: a TRUE if the task could be stopped or FALSE on error.
+ * Returns: a %TRUE if the task could be stopped or %FALSE on error.
  */
 gboolean
 gst_pad_stop_task (GstPad * pad)
@@ -5312,3 +5837,86 @@ join_failed:
     return FALSE;
   }
 }
+
+/**
+ * gst_pad_probe_info_get_event:
+ * @info: a #GstPadProbeInfo
+ *
+ * Returns: (transfer none): The #GstEvent from the probe
+ */
+
+GstEvent *
+gst_pad_probe_info_get_event (GstPadProbeInfo * info)
+{
+  g_return_val_if_fail (info->type & (GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM |
+          GST_PAD_PROBE_TYPE_EVENT_UPSTREAM), NULL);
+
+  return GST_PAD_PROBE_INFO_EVENT (info);
+}
+
+
+/**
+ * gst_pad_probe_info_get_query:
+ * @info: a #GstPadProbeInfo
+ *
+ * Returns: (transfer none): The #GstQuery from the probe
+ */
+
+GstQuery *
+gst_pad_probe_info_get_query (GstPadProbeInfo * info)
+{
+  g_return_val_if_fail (info->type & (GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM |
+          GST_PAD_PROBE_TYPE_QUERY_UPSTREAM), NULL);
+
+  return GST_PAD_PROBE_INFO_QUERY (info);
+}
+
+/**
+ * gst_pad_probe_info_get_buffer:
+ * @info: a #GstPadProbeInfo
+ *
+ * Returns: (transfer none): The #GstBuffer from the probe
+ */
+
+GstBuffer *
+gst_pad_probe_info_get_buffer (GstPadProbeInfo * info)
+{
+  g_return_val_if_fail (info->type & GST_PAD_PROBE_TYPE_BUFFER, NULL);
+
+  return GST_PAD_PROBE_INFO_BUFFER (info);
+}
+
+/**
+ * gst_pad_probe_info_get_bufferlist:
+ * @info: a #GstPadProbeInfo
+ *
+ * Returns: (transfer none): The #GstBufferlist from the probe
+ */
+
+GstBufferList *
+gst_pad_probe_info_get_buffer_list (GstPadProbeInfo * info)
+{
+  g_return_val_if_fail (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST, NULL);
+
+  return GST_PAD_PROBE_INFO_BUFFER_LIST (info);
+}
+
+/**
+ * gst_pad_get_last_flow_return:
+ * @pad: the #GstPad
+ *
+ * Gets the #GstFlowReturn return from the last data passed by this pad.
+ *
+ * Since: 1.4
+ */
+GstFlowReturn
+gst_pad_get_last_flow_return (GstPad * pad)
+{
+  GstFlowReturn ret;
+
+  GST_OBJECT_LOCK (pad);
+  ret = GST_PAD_LAST_FLOW_RETURN (pad);
+  GST_OBJECT_UNLOCK (pad);
+
+  return ret;
+}