Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / gstpad.c
index af468de..74ebfd4 100644 (file)
@@ -145,6 +145,11 @@ static gboolean gst_pad_activate_default (GstPad * pad, GstObject * parent);
 static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
     GstObject * parent, GstBufferList * list);
 
+static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad,
+    GstEvent * event, GstPadProbeType type);
+static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad,
+    GstEvent * event, GstPadProbeType type, gboolean * stored);
+
 static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
 
 static GParamSpec *pspec_caps = NULL;
@@ -163,10 +168,9 @@ typedef struct
 
 static GstFlowQuarks flow_quarks[] = {
   {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
-  {GST_FLOW_RESEND, "resend", 0},
   {GST_FLOW_OK, "ok", 0},
   {GST_FLOW_NOT_LINKED, "not-linked", 0},
-  {GST_FLOW_WRONG_STATE, "wrong-state", 0},
+  {GST_FLOW_FLUSHING, "flushing", 0},
   {GST_FLOW_EOS, "eos", 0},
   {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
   {GST_FLOW_ERROR, "error", 0},
@@ -318,14 +322,13 @@ gst_pad_init (GstPad * pad)
 
   GST_PAD_SET_FLUSHING (pad);
 
-  g_static_rec_mutex_init (&pad->stream_rec_lock);
+  g_rec_mutex_init (&pad->stream_rec_lock);
 
-  pad->block_cond = g_cond_new ();
+  g_cond_init (&pad->block_cond);
 
   g_hook_list_init (&pad->probes, sizeof (GstProbe));
 
-  pad->priv->events = g_array_sized_new (FALSE, TRUE,
-      sizeof (PadEvent), GST_EVENT_MAX_STICKY);
+  pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
 }
 
 /* called when setting the pad inactive. It removes all sticky events from
@@ -587,28 +590,28 @@ gst_pad_finalize (GObject * object)
   }
 
   if (pad->activatenotify)
-    pad->activatenotify (pad);
+    pad->activatenotify (pad->activatedata);
   if (pad->activatemodenotify)
-    pad->activatemodenotify (pad);
+    pad->activatemodenotify (pad->activatemodedata);
   if (pad->linknotify)
-    pad->linknotify (pad);
+    pad->linknotify (pad->linkdata);
   if (pad->unlinknotify)
-    pad->unlinknotify (pad);
+    pad->unlinknotify (pad->unlinkdata);
   if (pad->chainnotify)
-    pad->chainnotify (pad);
+    pad->chainnotify (pad->chaindata);
   if (pad->chainlistnotify)
-    pad->chainlistnotify (pad);
+    pad->chainlistnotify (pad->chainlistdata);
   if (pad->getrangenotify)
-    pad->getrangenotify (pad);
+    pad->getrangenotify (pad->getrangedata);
   if (pad->eventnotify)
-    pad->eventnotify (pad);
+    pad->eventnotify (pad->eventdata);
   if (pad->querynotify)
-    pad->querynotify (pad);
+    pad->querynotify (pad->querydata);
   if (pad->iterintlinknotify)
-    pad->iterintlinknotify (pad);
+    pad->iterintlinknotify (pad->iterintlinkdata);
 
-  g_static_rec_mutex_free (&pad->stream_rec_lock);
-  g_cond_free (pad->block_cond);
+  g_rec_mutex_clear (&pad->stream_rec_lock);
+  g_cond_clear (&pad->block_cond);
   g_array_free (pad->priv->events, TRUE);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -668,7 +671,7 @@ gst_pad_get_property (GObject * object, guint prop_id,
  * will be assigned.
  * This function makes a copy of the name so you can safely free the name.
  *
- * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ * Returns: (transfer floating): a new #GstPad, or NULL in case of an error.
  *
  * MT safe.
  */
@@ -1166,7 +1169,7 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask,
           "pad is in use, delay idle callback");
       GST_OBJECT_UNLOCK (pad);
     } else {
-      GstPadProbeInfo info = { GST_PAD_PROBE_TYPE_IDLE, };
+      GstPadProbeInfo info = { GST_PAD_PROBE_TYPE_IDLE, res, };
 
       /* the pad is idle now, we can signal the idle callback now */
       GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
@@ -1339,6 +1342,7 @@ gst_pad_mark_reconfigure (GstPad * pad)
  * gst_pad_set_activate_function_full:
  * @pad: a #GstPad.
  * @activate: the #GstPadActivateFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @activate will not be used anymore.
  *
  * Sets the given activate function for @pad. The activate function will
@@ -1349,13 +1353,14 @@ gst_pad_mark_reconfigure (GstPad * pad)
  */
 void
 gst_pad_set_activate_function_full (GstPad * pad,
-    GstPadActivateFunction activate, GDestroyNotify notify)
+    GstPadActivateFunction activate, gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->activatenotify)
-    pad->activatenotify (pad);
+    pad->activatenotify (pad->activatedata);
   GST_PAD_ACTIVATEFUNC (pad) = activate;
+  pad->activatedata = user_data;
   pad->activatenotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatefunc set to %s",
@@ -1366,6 +1371,7 @@ gst_pad_set_activate_function_full (GstPad * pad,
  * gst_pad_set_activatemode_function_full:
  * @pad: a #GstPad.
  * @activatemode: the #GstPadActivateModeFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @activatemode will not be used anymore.
  *
  * Sets the given activate_mode function for the pad. An activate_mode function
@@ -1373,13 +1379,15 @@ gst_pad_set_activate_function_full (GstPad * pad,
  */
 void
 gst_pad_set_activatemode_function_full (GstPad * pad,
-    GstPadActivateModeFunction activatemode, GDestroyNotify notify)
+    GstPadActivateModeFunction activatemode, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->activatemodenotify)
-    pad->activatemodenotify (pad);
+    pad->activatemodenotify (pad->activatemodedata);
   GST_PAD_ACTIVATEMODEFUNC (pad) = activatemode;
+  pad->activatemodedata = user_data;
   pad->activatemodenotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatemodefunc set to %s",
@@ -1390,6 +1398,7 @@ gst_pad_set_activatemode_function_full (GstPad * pad,
  * gst_pad_set_chain_function_full:
  * @pad: a sink #GstPad.
  * @chain: the #GstPadChainFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @chain will not be used anymore.
  *
  * Sets the given chain function for the pad. The chain function is called to
@@ -1397,14 +1406,15 @@ gst_pad_set_activatemode_function_full (GstPad * pad,
  */
 void
 gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SINK (pad));
 
   if (pad->chainnotify)
-    pad->chainnotify (pad);
+    pad->chainnotify (pad->chaindata);
   GST_PAD_CHAINFUNC (pad) = chain;
+  pad->chaindata = user_data;
   pad->chainnotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainfunc set to %s",
@@ -1415,6 +1425,7 @@ gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
  * gst_pad_set_chain_list_function_full:
  * @pad: a sink #GstPad.
  * @chainlist: the #GstPadChainListFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @chainlist will not be used anymore.
  *
  * Sets the given chain list function for the pad. The chainlist function is
@@ -1425,14 +1436,16 @@ gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain,
  */
 void
 gst_pad_set_chain_list_function_full (GstPad * pad,
-    GstPadChainListFunction chainlist, GDestroyNotify notify)
+    GstPadChainListFunction chainlist, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SINK (pad));
 
   if (pad->chainlistnotify)
-    pad->chainlistnotify (pad);
+    pad->chainlistnotify (pad->chainlistdata);
   GST_PAD_CHAINLISTFUNC (pad) = chainlist;
+  pad->chainlistdata = user_data;
   pad->chainlistnotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainlistfunc set to %s",
@@ -1443,6 +1456,7 @@ gst_pad_set_chain_list_function_full (GstPad * pad,
  * gst_pad_set_getrange_function_full:
  * @pad: a source #GstPad.
  * @get: the #GstPadGetRangeFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @get will not be used anymore.
  *
  * Sets the given getrange function for the pad. The getrange function is
@@ -1451,14 +1465,15 @@ gst_pad_set_chain_list_function_full (GstPad * pad,
  */
 void
 gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_IS_SRC (pad));
 
   if (pad->getrangenotify)
-    pad->getrangenotify (pad);
+    pad->getrangenotify (pad->getrangedata);
   GST_PAD_GETRANGEFUNC (pad) = get;
+  pad->getrangedata = user_data;
   pad->getrangenotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getrangefunc set to %s",
@@ -1469,19 +1484,21 @@ gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get,
  * gst_pad_set_event_function_full:
  * @pad: a #GstPad of either direction.
  * @event: the #GstPadEventFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @event will not be used anymore.
  *
  * Sets the given event handler for the pad.
  */
 void
 gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->eventnotify)
-    pad->eventnotify (pad);
+    pad->eventnotify (pad->eventdata);
   GST_PAD_EVENTFUNC (pad) = event;
+  pad->eventdata = user_data;
   pad->eventnotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "eventfunc for set to %s",
@@ -1492,19 +1509,21 @@ gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event,
  * gst_pad_set_query_function_full:
  * @pad: a #GstPad of either direction.
  * @query: the #GstPadQueryFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @query will not be used anymore.
  *
  * Set the given query function for the pad.
  */
 void
 gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->querynotify)
-    pad->querynotify (pad);
+    pad->querynotify (pad->querydata);
   GST_PAD_QUERYFUNC (pad) = query;
+  pad->querydata = user_data;
   pad->querynotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "queryfunc set to %s",
@@ -1515,6 +1534,7 @@ gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
  * gst_pad_set_iterate_internal_links_function_full:
  * @pad: a #GstPad of either direction.
  * @iterintlink: the #GstPadIterIntLinkFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @iterintlink will not be used anymore.
  *
  * Sets the given internal link iterator function for the pad.
@@ -1523,13 +1543,15 @@ gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query,
  */
 void
 gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
-    GstPadIterIntLinkFunction iterintlink, GDestroyNotify notify)
+    GstPadIterIntLinkFunction iterintlink, gpointer user_data,
+    GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->iterintlinknotify)
-    pad->iterintlinknotify (pad);
+    pad->iterintlinknotify (pad->iterintlinkdata);
   GST_PAD_ITERINTLINKFUNC (pad) = iterintlink;
+  pad->iterintlinkdata = user_data;
   pad->iterintlinknotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link iterator set to %s",
@@ -1540,6 +1562,7 @@ gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
  * gst_pad_set_link_function_full:
  * @pad: a #GstPad.
  * @link: the #GstPadLinkFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @link will not be used anymore.
  *
  * Sets the given link function for the pad. It will be called when
@@ -1556,13 +1579,14 @@ gst_pad_set_iterate_internal_links_function_full (GstPad * pad,
  */
 void
 gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->linknotify)
-    pad->linknotify (pad);
+    pad->linknotify (pad->linkdata);
   GST_PAD_LINKFUNC (pad) = link;
+  pad->linkdata = user_data;
   pad->linknotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "linkfunc set to %s",
@@ -1573,6 +1597,7 @@ gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link,
  * gst_pad_set_unlink_function_full:
  * @pad: a #GstPad.
  * @unlink: the #GstPadUnlinkFunction to set.
+ * @user_data: user_data passed to @notify
  * @notify: notify called when @unlink will not be used anymore.
  *
  * Sets the given unlink function for the pad. It will be called
@@ -1580,13 +1605,14 @@ gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link,
  */
 void
 gst_pad_set_unlink_function_full (GstPad * pad, GstPadUnlinkFunction unlink,
-    GDestroyNotify notify)
+    gpointer user_data, GDestroyNotify notify)
 {
   g_return_if_fail (GST_IS_PAD (pad));
 
   if (pad->unlinknotify)
-    pad->unlinknotify (pad);
+    pad->unlinknotify (pad->unlinkdata);
   GST_PAD_UNLINKFUNC (pad) = unlink;
+  pad->unlinkdata = user_data;
   pad->unlinknotify = notify;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "unlinkfunc set to %s",
@@ -2489,7 +2515,7 @@ gst_pad_forward (GstPad * pad, GstPadForwardFunction forward,
         intpad = g_value_get_object (&item);
 
         /* if already pushed, skip. FIXME, find something faster to tag pads */
-        if (g_list_find (pushed_pads, intpad)) {
+        if (intpad == NULL || g_list_find (pushed_pads, intpad)) {
           g_value_reset (&item);
           break;
         }
@@ -2632,6 +2658,9 @@ 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);
 
@@ -2743,12 +2772,15 @@ done:
 gboolean
 gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
 {
-  gboolean forward = TRUE, ret = FALSE;
+  gboolean forward, ret = FALSE;
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_SCHEDULING:
       forward = FALSE;
       break;
+    case GST_QUERY_ALLOCATION:
+      forward = GST_PAD_IS_PROXY_ALLOCATION (pad);
+      break;
     case GST_QUERY_ACCEPT_CAPS:
       ret = gst_pad_query_accept_caps_default (pad, query);
       forward = FALSE;
@@ -2764,8 +2796,8 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query)
     case GST_QUERY_JITTER:
     case GST_QUERY_RATE:
     case GST_QUERY_CONVERT:
-    case GST_QUERY_ALLOCATION:
     default:
+      forward = TRUE;
       break;
   }
 
@@ -2785,9 +2817,6 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
   GstPadProbeCallback callback;
   GstPadProbeReturn ret;
 
-  GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "hook %lu, cookie %u checking", hook->hook_id, PROBE_COOKIE (hook));
-
   /* if we have called this callback, do nothing */
   if (PROBE_COOKIE (hook) == data->cookie) {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
@@ -2811,14 +2840,21 @@ 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;
+  /* only probes that have GST_PAD_PROBE_TYPE_EVENT_FLUSH set */
+  if ((type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) &&
+      (flags & GST_PAD_PROBE_TYPE_EVENT_FLUSH & type) == 0)
+    goto no_match;
 
   GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-      "hook %lu with flags 0x%08x matches", hook->hook_id, flags);
+      "hook %lu, cookie %u with flags 0x%08x matches", hook->hook_id,
+      PROBE_COOKIE (hook), flags);
 
   callback = (GstPadProbeCallback) hook->func;
   if (callback == NULL)
     return;
 
+  info->id = hook->hook_id;
+
   GST_OBJECT_UNLOCK (pad);
 
   ret = callback (pad, info, hook->data);
@@ -2853,8 +2889,8 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data)
 no_match:
   {
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "hook %lu with flags 0x%08x does not match %08x", hook->hook_id,
-        flags, info->type);
+        "hook %lu, cookie %u with flags 0x%08x does not match %08x",
+        hook->hook_id, PROBE_COOKIE (hook), flags, info->type);
     return;
   }
 }
@@ -2863,7 +2899,7 @@ no_match:
   G_STMT_START {                                               \
     if (G_UNLIKELY (pad->num_probes)) {                                \
       /* we start with passing NULL as the data item */         \
-      GstPadProbeInfo info = { mask, NULL, offs, size };        \
+      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);                   \
@@ -2882,7 +2918,7 @@ no_match:
   G_STMT_START {                                               \
     if (G_UNLIKELY (pad->num_probes)) {                                \
       /* pass NULL as the data item */                          \
-      GstPadProbeInfo info = { mask, NULL, 0, 0 };              \
+      GstPadProbeInfo info = { mask, 0, NULL, 0, 0 };           \
       ret = do_probe_callbacks (pad, &info, defaultval);       \
       if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK))        \
         goto label;                                            \
@@ -2892,7 +2928,7 @@ no_match:
 #define PROBE_FULL(pad,mask,data,offs,size,label,defaultval)    \
   G_STMT_START {                                               \
     if (G_UNLIKELY (pad->num_probes)) {                                \
-      GstPadProbeInfo info = { mask, data, offs, size };        \
+      GstPadProbeInfo info = { mask, 0, data, offs, size };     \
       ret = do_probe_callbacks (pad, &info, defaultval);       \
       data = GST_PAD_PROBE_INFO_DATA (&info);                   \
       if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK))        \
@@ -2983,7 +3019,7 @@ again:
 flushing:
   {
     GST_DEBUG_OBJECT (pad, "pad is flushing");
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 dropped:
   {
@@ -3238,8 +3274,8 @@ probe_stopped:
 static gboolean
 push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
 {
-  gboolean res;
   GstFlowReturn *data = user_data;
+  gboolean stored;
 
   if (ev->received) {
     GST_DEBUG_OBJECT (pad, "event %s was already received",
@@ -3248,13 +3284,11 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
   }
   GST_OBJECT_UNLOCK (pad);
 
-  res = gst_pad_push_event (pad, gst_event_ref (ev->event));
+  *data = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
+      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, &stored);
 
   GST_OBJECT_LOCK (pad);
-  if (!res)
-    *data = GST_FLOW_ERROR;
-
-  return res;
+  return *data == GST_FLOW_OK;
 }
 
 /* this is the chain function that does not perform the additional argument
@@ -3272,6 +3306,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_MODE (pad) != GST_PAD_MODE_PUSH))
+    goto wrong_mode;
+
   PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
 
   PROBE_PUSH (pad, type, data, probe_stopped);
@@ -3328,7 +3365,17 @@ flushing:
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+        ("chain on pad %s:%s but it was not in push mode",
+            GST_DEBUG_PAD_NAME (pad)));
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_ERROR;
   }
 probe_stopped:
   {
@@ -3350,10 +3397,8 @@ probe_stopped:
 no_function:
   {
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
-        "pushing, but not chainhandler");
     GST_ELEMENT_ERROR (parent, CORE, PAD, (NULL),
-        ("push on pad %s:%s but it has no chainfunction",
+        ("chain on pad %s:%s but it has no chainfunction",
             GST_DEBUG_PAD_NAME (pad)));
     GST_PAD_STREAM_UNLOCK (pad);
     return GST_FLOW_NOT_SUPPORTED;
@@ -3368,7 +3413,7 @@ no_function:
  *
  * Chain a buffer to @pad.
  *
- * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ * The function returns #GST_FLOW_FLUSHING if the pad was flushing.
  *
  * If the buffer type is not acceptable for @pad (as negotiated with a
  * preceeding GST_EVENT_CAPS event), this function returns
@@ -3432,7 +3477,7 @@ gst_pad_chain_list_default (GstPad * pad, GstObject * parent,
  *
  * Chain a bufferlist to @pad.
  *
- * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ * The function returns #GST_FLOW_FLUSHING if the pad was flushing.
  *
  * If @pad was not negotiated properly with a CAPS event, this function
  * returns #GST_FLOW_NOT_NEGOTIATED.
@@ -3472,6 +3517,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_MODE (pad) != GST_PAD_MODE_PUSH))
+    goto wrong_mode;
+
   if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
     GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
 
@@ -3520,11 +3568,21 @@ flushing:
         "pushing, but pad was flushing");
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+        ("pushing on pad %s:%s but it was not activated in push mode",
+            GST_DEBUG_PAD_NAME (pad)));
+    GST_OBJECT_UNLOCK (pad);
+    gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+    return GST_FLOW_ERROR;
   }
 events_error:
   {
-    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "error pushing events");
+    GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+        "error pushing events, return %s", gst_flow_get_name (ret));
     GST_OBJECT_UNLOCK (pad);
     gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
     return ret;
@@ -3639,6 +3697,9 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
+  if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
+    goto wrong_mode;
+
   if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
     GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
 
@@ -3691,7 +3752,16 @@ flushing:
         "getrange, but pad was flushing");
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+        ("getrange on pad %s:%s but it was not activated in pull mode",
+            GST_DEBUG_PAD_NAME (pad)));
+    GST_OBJECT_UNLOCK (pad);
+    GST_PAD_STREAM_UNLOCK (pad);
+    return GST_FLOW_ERROR;
   }
 events_error:
   {
@@ -3705,7 +3775,7 @@ no_parent:
     GST_DEBUG_OBJECT (pad, "no parent");
     GST_OBJECT_UNLOCK (pad);
     GST_PAD_STREAM_UNLOCK (pad);
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
   }
 no_function:
   {
@@ -3753,7 +3823,7 @@ get_range_failed:
  * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer,
  *     returns #GST_FLOW_ERROR if %NULL.
  *
- * When @pad is flushing this function returns #GST_FLOW_WRONG_STATE
+ * When @pad is flushing this function returns #GST_FLOW_FLUSHING
  * immediately and @buffer is %NULL.
  *
  * Calls the getrange function of @pad, see #GstPadGetRangeFunction for a
@@ -3819,6 +3889,9 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
   if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
     goto flushing;
 
+  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,
@@ -3860,7 +3933,15 @@ flushing:
     GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
         "pullrange, but pad was flushing");
     GST_OBJECT_UNLOCK (pad);
-    return GST_FLOW_WRONG_STATE;
+    return GST_FLOW_FLUSHING;
+  }
+wrong_mode:
+  {
+    GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+        ("gpulltange on pad %s:%s but it was not activated in pull mode",
+            GST_DEBUG_PAD_NAME (pad)));
+    GST_OBJECT_UNLOCK (pad);
+    return GST_FLOW_ERROR;
   }
 pre_probe_stopped:
   {
@@ -3962,68 +4043,30 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
   return res;
 }
 
-/**
- * gst_pad_push_event:
- * @pad: a #GstPad to push the event to.
- * @event: (transfer full): the #GstEvent to send to the pad.
- *
- * Sends the event to the peer of the given pad. This function is
- * mainly used by elements to send events to their peer
- * elements.
- *
- * This function takes owership of the provided event so you should
- * gst_event_ref() it if you want to reuse the event after this call.
- *
- * Returns: TRUE if the event was handled.
- *
- * MT safe.
- */
-gboolean
-gst_pad_push_event (GstPad * pad, GstEvent * event)
+static GstFlowReturn
+gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
+    GstPadProbeType type, gboolean * stored)
 {
   GstFlowReturn ret;
   GstPad *peerpad;
-  gboolean result;
-  gboolean stored = FALSE;
-  GstPadProbeType type;
+  GstEventType event_type;
   gboolean sticky;
 
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
-
-  if (GST_PAD_IS_SRC (pad)) {
-    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
-      goto wrong_direction;
-    sticky = GST_EVENT_IS_STICKY (event);
-    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
-  } else if (GST_PAD_IS_SINK (pad)) {
-    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
-      goto wrong_direction;
-    /* events pushed on sinkpad never are sticky */
-    sticky = FALSE;
-    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
-  } else
-    goto unknown_direction;
+  sticky = GST_EVENT_IS_STICKY (event);
 
   GST_OBJECT_LOCK (pad);
 
   /* Two checks to be made:
    * . (un)set the FLUSHING flag for flushing events,
    * . handle pad blocking */
-  switch (GST_EVENT_TYPE (event)) {
+  event_type = GST_EVENT_TYPE (event);
+  *stored = FALSE;
+  switch (event_type) {
     case GST_EVENT_FLUSH_START:
       GST_PAD_SET_FLUSHING (pad);
 
-      if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
-        /* flush start will have set the FLUSHING flag and will then
-         * unlock all threads doing a GCond wait on the blocking pad. This
-         * will typically unblock the STREAMING thread blocked on a pad. */
-        GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-start, "
-            "doing block signal.");
-        GST_PAD_BLOCK_BROADCAST (pad);
-        goto flushed;
-      }
+      GST_PAD_BLOCK_BROADCAST (pad);
+      type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       break;
     case GST_EVENT_FLUSH_STOP:
       GST_PAD_UNSET_FLUSHING (pad);
@@ -4032,10 +4075,7 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
       GST_LOG_OBJECT (pad, "Removing pending EOS events");
       remove_event_by_type (pad, GST_EVENT_EOS);
 
-      if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
-        GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-stop");
-        goto flushed;
-      }
+      type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH;
       break;
     default:
     {
@@ -4052,7 +4092,7 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
           GST_DEBUG_OBJECT (pad, "event %s updated",
               GST_EVENT_TYPE_NAME (event));
         }
-        stored = TRUE;
+        *stored = TRUE;
       }
 
       switch (GST_EVENT_TYPE (event)) {
@@ -4089,19 +4129,19 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
   GST_LOG_OBJECT (pad, "sending event %p (%s) to peerpad %" GST_PTR_FORMAT,
       event, GST_EVENT_TYPE_NAME (event), peerpad);
 
-  result = gst_pad_send_event (peerpad, event);
+  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 ", result %d", event, peerpad,
-      result);
+      "sent event %p to peerpad %" GST_PTR_FORMAT ", ret %s", event, peerpad,
+      gst_flow_get_name (ret));
 
   gst_object_unref (peerpad);
 
   GST_OBJECT_LOCK (pad);
   if (sticky) {
-    if (result) {
+    if (ret == GST_FLOW_OK) {
       PadEvent *ev;
 
       if ((ev = find_event (pad, event)))
@@ -4117,55 +4157,112 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
   if (pad->priv->using == 0) {
     /* pad is not active anymore, trigger idle callbacks */
     PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_IDLE,
-        idle_probe_stopped, GST_FLOW_OK);
+        idle_probe_stopped, ret);
   }
   GST_OBJECT_UNLOCK (pad);
 
-  return result | stored;
+  return ret;
 
   /* ERROR handling */
-wrong_direction:
-  {
-    g_warning ("pad %s:%s pushing %s event in wrong direction",
-        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
-    gst_event_unref (event);
-    return FALSE;
-  }
-unknown_direction:
-  {
-    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
-    gst_event_unref (event);
-    return FALSE;
-  }
 flushed:
   {
     GST_DEBUG_OBJECT (pad, "We're flushing");
     GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
-    return stored;
+    return GST_FLOW_FLUSHING;
   }
 probe_stopped:
   {
-    GST_DEBUG_OBJECT (pad, "Probe returned %s", gst_flow_get_name (ret));
     GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
     GST_OBJECT_UNLOCK (pad);
     gst_event_unref (event);
-    return stored;
+
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped event");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
+  }
+not_linked:
+  {
+    GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
+    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+    GST_OBJECT_UNLOCK (pad);
+    gst_event_unref (event);
+    return sticky ? GST_FLOW_OK : GST_FLOW_NOT_LINKED;
   }
 idle_probe_stopped:
   {
     GST_DEBUG_OBJECT (pad, "Idle probe returned %s", gst_flow_get_name (ret));
     GST_OBJECT_UNLOCK (pad);
+    return ret;
+  }
+}
+
+/**
+ * gst_pad_push_event:
+ * @pad: a #GstPad to push the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Sends the event to the peer of the given pad. This function is
+ * mainly used by elements to send events to their peer
+ * elements.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
+ * Returns: TRUE if the event was handled.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_push_event (GstPad * pad, GstEvent * event)
+{
+  gboolean res;
+  GstPadProbeType type;
+  gboolean stored;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+
+  if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
+  } else if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
+      goto wrong_direction;
+    /* events pushed on sinkpad never are sticky */
+    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
+  } else
+    goto unknown_direction;
+
+  if (gst_pad_push_event_unchecked (pad, event, type, &stored) != GST_FLOW_OK)
+    res = stored ? TRUE : FALSE;
+  else
+    res = TRUE;
+
+  return res;
+
+  /* ERROR handling */
+wrong_direction:
+  {
+    g_warning ("pad %s:%s pushing %s event in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
     gst_event_unref (event);
-    return stored;
+    return FALSE;
   }
-not_linked:
+unknown_direction:
   {
-    GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
-    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
-    GST_OBJECT_UNLOCK (pad);
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
     gst_event_unref (event);
-    return stored;
+    return FALSE;
   }
 }
 
@@ -4200,68 +4297,30 @@ 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;
   }
 }
 
-/**
- * gst_pad_send_event:
- * @pad: a #GstPad to send the event to.
- * @event: (transfer full): the #GstEvent to send to the pad.
- *
- * Sends the event to the pad. This function can be used
- * by applications to send events in the pipeline.
- *
- * If @pad is a source pad, @event should be an upstream event. If @pad is a
- * sink pad, @event should be a downstream event. For example, you would not
- * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
- * Furthermore, some downstream events have to be serialized with data flow,
- * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
- * the event needs to be serialized with data flow, this function will take the
- * pad's stream lock while calling its event function.
- *
- * To find out whether an event type is upstream, downstream, or downstream and
- * serialized, see #GstEventTypeFlags, gst_event_type_get_flags(),
- * #GST_EVENT_IS_UPSTREAM, #GST_EVENT_IS_DOWNSTREAM, and
- * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or
- * plugin doesn't need to bother itself with this information; the core handles
- * all necessary locks and checks.
- *
- * This function takes owership of the provided event so you should
- * gst_event_ref() it if you want to reuse the event after this call.
- *
- * Returns: TRUE if the event was handled.
- */
-gboolean
-gst_pad_send_event (GstPad * pad, GstEvent * event)
+static GstFlowReturn
+gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
+    GstPadProbeType type)
 {
   GstFlowReturn ret;
-  gboolean result = FALSE;
+  GstEventType event_type;
   gboolean serialized, need_unlock = FALSE, sticky;
   GstPadEventFunction eventfunc;
   GstObject *parent;
-  GstPadProbeType type;
-
-  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-  if (GST_PAD_IS_SINK (pad)) {
-    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
-      goto wrong_direction;
-    serialized = GST_EVENT_IS_SERIALIZED (event);
-    sticky = GST_EVENT_IS_STICKY (event);
-    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
-  } else if (GST_PAD_IS_SRC (pad)) {
-    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
-      goto wrong_direction;
-    /* events on srcpad never are serialized and sticky */
-    serialized = sticky = FALSE;
-    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
-  } else
-    goto unknown_direction;
 
   GST_OBJECT_LOCK (pad);
-  switch (GST_EVENT_TYPE (event)) {
+  if (GST_PAD_IS_SINK (pad))
+    serialized = GST_EVENT_IS_SERIALIZED (event);
+  else
+    serialized = FALSE;
+  sticky = GST_EVENT_IS_STICKY (event);
+  event_type = GST_EVENT_TYPE (event);
+  switch (event_type) {
     case GST_EVENT_FLUSH_START:
       GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
           "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
@@ -4294,8 +4353,8 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
       if (GST_PAD_IS_SRC (pad))
         GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
     default:
-      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "have event type %s",
-          GST_EVENT_TYPE_NAME (event));
+      GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
+          "have event type %" GST_PTR_FORMAT, event);
 
       if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
         goto flushing;
@@ -4333,45 +4392,72 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
   ACQUIRE_PARENT (pad, parent, no_parent);
   GST_OBJECT_UNLOCK (pad);
 
-  if (G_UNLIKELY (pre_eventfunc_check (pad, event) != GST_FLOW_OK))
+  ret = pre_eventfunc_check (pad, event);
+  if (G_UNLIKELY (ret != GST_FLOW_OK))
     goto precheck_failed;
 
   if (sticky)
     gst_event_ref (event);
 
-  result = eventfunc (pad, parent, event);
-
+  if (eventfunc (pad, parent, event)) {
+    ret = GST_FLOW_OK;
+  } else {
+    /* something went wrong */
+    switch (event_type) {
+      case GST_EVENT_CAPS:
+        ret = GST_FLOW_NOT_NEGOTIATED;
+        break;
+      default:
+        ret = GST_FLOW_ERROR;
+        break;
+    }
+  }
   RELEASE_PARENT (parent);
 
+  GST_DEBUG_OBJECT (pad, "sent event, ret %s", gst_flow_get_name (ret));
+
   if (sticky) {
-    if (result)
+    if (ret == GST_FLOW_OK) {
       /* after the event function accepted the event, we can store the sticky
        * event on the pad */
       gst_pad_store_sticky_event (pad, event, FALSE);
-
+    }
     gst_event_unref (event);
   }
 
   if (need_unlock)
     GST_PAD_STREAM_UNLOCK (pad);
 
-  GST_DEBUG_OBJECT (pad, "sent event, result %d", result);
-
-  return result;
+  return ret;
 
   /* ERROR handling */
-wrong_direction:
+flushing:
   {
-    g_warning ("pad %s:%s sending %s event in wrong direction",
-        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+    GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
+    GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+        "Received event on flushing pad. Discarding");
     gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_FLUSHING;
   }
-unknown_direction:
+probe_stopped:
   {
-    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+    GST_OBJECT_UNLOCK (pad);
+    if (need_unlock)
+      GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
-    return FALSE;
+
+    switch (ret) {
+      case GST_FLOW_CUSTOM_SUCCESS:
+        GST_DEBUG_OBJECT (pad, "dropped event");
+        ret = GST_FLOW_OK;
+        break;
+      default:
+        GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+        break;
+    }
+    return ret;
   }
 no_function:
   {
@@ -4381,16 +4467,7 @@ no_function:
     if (need_unlock)
       GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
-    return FALSE;
-  }
-precheck_failed:
-  {
-    GST_DEBUG_OBJECT (pad, "pre event check failed");
-    RELEASE_PARENT (parent);
-    if (need_unlock)
-      GST_PAD_STREAM_UNLOCK (pad);
-    gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_NOT_SUPPORTED;
   }
 no_parent:
   {
@@ -4399,24 +4476,85 @@ no_parent:
     if (need_unlock)
       GST_PAD_STREAM_UNLOCK (pad);
     gst_event_unref (event);
-    return FALSE;
+    return GST_FLOW_FLUSHING;
   }
-flushing:
+precheck_failed:
   {
-    GST_OBJECT_UNLOCK (pad);
+    GST_DEBUG_OBJECT (pad, "pre event check failed");
+    RELEASE_PARENT (parent);
     if (need_unlock)
       GST_PAD_STREAM_UNLOCK (pad);
-    GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
-        "Received event on flushing pad. Discarding");
+    gst_event_unref (event);
+    return ret;
+  }
+}
+
+/**
+ * gst_pad_send_event:
+ * @pad: a #GstPad to send the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Sends the event to the pad. This function can be used
+ * by applications to send events in the pipeline.
+ *
+ * If @pad is a source pad, @event should be an upstream event. If @pad is a
+ * sink pad, @event should be a downstream event. For example, you would not
+ * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
+ * Furthermore, some downstream events have to be serialized with data flow,
+ * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
+ * the event needs to be serialized with data flow, this function will take the
+ * pad's stream lock while calling its event function.
+ *
+ * To find out whether an event type is upstream, downstream, or downstream and
+ * serialized, see #GstEventTypeFlags, gst_event_type_get_flags(),
+ * #GST_EVENT_IS_UPSTREAM, #GST_EVENT_IS_DOWNSTREAM, and
+ * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or
+ * plugin doesn't need to bother itself with this information; the core handles
+ * all necessary locks and checks.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+gboolean
+gst_pad_send_event (GstPad * pad, GstEvent * event)
+{
+  gboolean result;
+  GstPadProbeType type;
+
+  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (GST_PAD_IS_SINK (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
+  } else if (GST_PAD_IS_SRC (pad)) {
+    if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
+      goto wrong_direction;
+    type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
+  } else
+    goto unknown_direction;
+
+  if (gst_pad_send_event_unchecked (pad, event, type) != GST_FLOW_OK)
+    result = FALSE;
+  else
+    result = TRUE;
+
+  return result;
+
+  /* ERROR handling */
+wrong_direction:
+  {
+    g_warning ("pad %s:%s sending %s event in wrong direction",
+        GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
     gst_event_unref (event);
     return FALSE;
   }
-probe_stopped:
+unknown_direction:
   {
-    GST_DEBUG_OBJECT (pad, "probe returned %s", gst_flow_get_name (ret));
-    GST_OBJECT_UNLOCK (pad);
-    if (need_unlock)
-      GST_PAD_STREAM_UNLOCK (pad);
+    g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
     gst_event_unref (event);
     return FALSE;
   }