* SECTION:gstpad
* @short_description: Object contained by elements that allows links to
* other elements
- * @see_also: #GstPadTemplate, #GstElement, #GstEvent
+ * @see_also: #GstPadTemplate, #GstElement, #GstEvent, #GstQuery, #GstBuffer
*
* A #GstElement is linked to other elements via "pads", which are extremely
* light-weight generic link points.
- * After two pads are retrieved from an element with gst_element_get_pad(),
- * the pads can be link with gst_pad_link(). (For quick links,
- * you can also use gst_element_link(), which will make the obvious
- * link for you if it's straightforward.)
*
- * Pads are typically created from a #GstPadTemplate with
- * gst_pad_new_from_template().
+ * Pads have a #GstPadDirection, source pads produce data, sink pads consume
+ * data.
*
- * Pads have #GstCaps attached to it to describe the media type they are
- * capable of dealing with. gst_pad_query_caps() and gst_pad_set_caps() are
- * used to manipulate the caps of the pads.
- * Pads created from a pad template cannot set capabilities that are
- * incompatible with the pad template capabilities.
+ * Pads are typically created from a #GstPadTemplate with
+ * gst_pad_new_from_template() and are then added to a #GstElement. This usually
+ * happens when the element is created but it can also happen dynamically based
+ * on the data that the element is processing or based on the pads that the
+ * application requests.
*
* Pads without pad templates can be created with gst_pad_new(),
* which takes a direction and a name as an argument. If the name is NULL,
* then a guaranteed unique name will be assigned to it.
*
+ * A #GstElement creating a pad will typically use the various
+ * gst_pad_set_*_function() calls to register callbacks for events, queries or
+ * dataflow on the pads.
+ *
* gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
*
- * A #GstElement creating a pad will typically use the various
- * gst_pad_set_*_function() calls to register callbacks for various events
- * on the pads.
+ * After two pads are retrieved from an element with gst_element_get_pad(),
+ * the pads can be linked with gst_pad_link(). (For quick links,
+ * you can also use gst_element_link(), which will make the obvious
+ * link for you if it's straightforward.). Pads can be unlinked again with
+ * gst_pad_unlink(). gst_pad_get_peer() can be used to check what the pad is
+ * linked to.
+ *
+ * Before dataflow is possible on the pads, they need to be activated with
+ * gst_pad_set_active().
+ *
+ * gst_pad_query() and gst_pad_peer_query() can be used to query various
+ * properties of the pad and the stream.
+ *
+ * To send a #GstEvent on a pad, use gst_pad_send_event() and
+ * gst_pad_push_event(). Some events will be sticky on the pad, meaning that
+ * after they pass on the pad they can be queried later with
+ * gst_pad_get_sticky_event() and gst_pad_sticky_events_foreach().
+ * gst_pad_get_current_caps() and gst_pad_has_current_caps() are convenience
+ * functions to query the current sticky CAPS event on a pad.
*
* GstElements will use gst_pad_push() and gst_pad_pull_range() to push out
* or pull in a buffer.
*
- * To send a #GstEvent on a pad, use gst_pad_send_event() and
- * gst_pad_push_event().
+ * The dataflow, events and queries that happen on a pad can be monitored with
+ * probes that can be installed with gst_pad_add_probe(). gst_pad_is_blocked()
+ * can be used to check if a block probe is installed on the pad.
+ * gst_pad_is_blocking() checks if the blocking probe is currently blocking the
+ * pad. gst_pad_remove_probe() is used to remove a previously installed probe
+ * and unblock blocking probes if any.
+ *
+ * Pad have an offset that can be retrieved with gst_pad_get_offset(). This
+ * offset will be applied to the running_time of all data passing over the pad.
+ * gst_pad_set_offset() can be used to change the offset.
+ *
+ * Convenience functions exist to start, pause and stop the task on a pad with
+ * gst_pad_start_task(), gst_pad_pause_task() and gst_pad_stop_task()
+ * respectively.
*
- * Last reviewed on 2006-07-06 (0.10.9)
+ * Last reviewed on 2012-03-29 (0.11.3)
*/
#include "gst_private.h"
#include "gstpad.h"
#include "gstpadtemplate.h"
#include "gstenumtypes.h"
-#include "gstmarshal.h"
#include "gstutils.h"
#include "gstinfo.h"
#include "gsterror.h"
struct _GstPadPrivate
{
- PadEvent events[GST_EVENT_MAX_STICKY];
+ guint events_cookie;
+ GArray *events;
gint using;
guint probe_list_cookie;
static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
GstObject * parent, GstBufferList * list);
+static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad,
+ GstEvent * event, GstPadProbeType type);
+static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad,
+ GstEvent * event, GstPadProbeType type);
+
static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
static GParamSpec *pspec_caps = NULL;
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},
gst_pad_signals[PAD_LINKED] =
g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
- gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+ g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
/**
* GstPad::unlinked:
* @pad: the pad that emitted the signal
gst_pad_signals[PAD_UNLINKED] =
g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
- gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+ g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
pspec_caps = g_param_spec_boxed ("caps", "Caps",
"The capabilities of the pad", GST_TYPE_CAPS,
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));
-}
-static void
-clear_event (GstPad * pad, guint idx)
-{
- gst_event_replace (&pad->priv->events[idx].event, NULL);
- pad->priv->events[idx].received = FALSE;
+ pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
}
/* called when setting the pad inactive. It removes all sticky events from
* the pad */
static void
-clear_events (GstPad * pad)
+remove_events (GstPad * pad)
{
- guint i;
+ guint i, len;
+ GArray *events;
- for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
- clear_event (pad, i);
+ events = pad->priv->events;
+ len = events->len;
+ for (i = 0; i < len; i++) {
+ PadEvent *ev = &g_array_index (events, PadEvent, i);
+ gst_event_unref (ev->event);
+ }
GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+ g_array_set_size (events, 0);
+ pad->priv->events_cookie++;
}
-static gboolean
-reschedule_event (GstPad * pad, guint idx)
+static PadEvent *
+find_event_by_type (GstPad * pad, GstEventType type, guint idx)
{
- if (pad->priv->events[idx].event) {
- pad->priv->events[idx].received = FALSE;
- return TRUE;
+ guint i, len;
+ GArray *events;
+ PadEvent *ev;
+
+ events = pad->priv->events;
+ len = events->len;
+
+ for (i = 0; i < len; i++) {
+ ev = &g_array_index (events, PadEvent, i);
+ if (ev->event == NULL)
+ continue;
+
+ if (GST_EVENT_TYPE (ev->event) == type) {
+ if (idx == 0)
+ goto found;
+ idx--;
+ }
+ }
+ ev = NULL;
+found:
+ return ev;
+}
+
+static PadEvent *
+find_event (GstPad * pad, GstEvent * event)
+{
+ guint i, len;
+ GArray *events;
+ PadEvent *ev;
+
+ events = pad->priv->events;
+ len = events->len;
+
+ for (i = 0; i < len; i++) {
+ ev = &g_array_index (events, PadEvent, i);
+ if (event == ev->event)
+ goto found;
+ }
+ ev = NULL;
+found:
+ return ev;
+}
+
+static void
+remove_event_by_type (GstPad * pad, GstEventType type)
+{
+ guint i, len;
+ GArray *events;
+ PadEvent *ev;
+
+ events = pad->priv->events;
+ len = events->len;
+
+ i = 0;
+ while (i < len) {
+ ev = &g_array_index (events, PadEvent, i);
+ if (ev->event == NULL)
+ goto next;
+
+ if (GST_EVENT_TYPE (ev->event) != type)
+ goto next;
+
+ gst_event_unref (ev->event);
+ g_array_remove_index (events, i);
+ len--;
+ pad->priv->events_cookie++;
+ continue;
+
+ next:
+ i++;
}
- return FALSE;
}
+/* check all events on srcpad against those on sinkpad. All events that are not
+ * on sinkpad are marked as received=FALSE and the PENDING_EVENTS is set on the
+ * srcpad so that the events will be sent next time */
static void
-reschedule_events (GstPad * pad)
+schedule_events (GstPad * srcpad, GstPad * sinkpad)
{
- guint i;
+ gint i, len;
+ GArray *events;
+ PadEvent *ev;
gboolean pending = FALSE;
- for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
- pending |= reschedule_event (pad, i);
+ events = srcpad->priv->events;
+ len = events->len;
+ for (i = 0; i < len; i++) {
+ ev = &g_array_index (events, PadEvent, i);
+ if (ev->event == NULL)
+ continue;
+
+ if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
+ ev->received = FALSE;
+ pending = TRUE;
+ }
+ }
if (pending)
- GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+ GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
}
+typedef gboolean (*PadEventFunction) (GstPad * pad, PadEvent * ev,
+ gpointer user_data);
+
+/* should be called with pad LOCK */
+static void
+events_foreach (GstPad * pad, PadEventFunction func, gpointer user_data)
+{
+ guint i, len;
+ GArray *events;
+ gboolean ret;
+ guint cookie;
+
+ events = pad->priv->events;
+
+restart:
+ cookie = pad->priv->events_cookie;
+ i = 0;
+ len = events->len;
+ while (i < len) {
+ PadEvent *ev, ev_ret;
+
+ ev = &g_array_index (events, PadEvent, i);
+ if (G_UNLIKELY (ev->event == NULL))
+ goto next;
+
+ /* take aditional ref, func might release the lock */
+ ev_ret.event = gst_event_ref (ev->event);
+ ev_ret.received = ev->received;
+
+ ret = func (pad, &ev_ret, user_data);
+
+ /* recheck the cookie, lock might have been released and the list could have
+ * changed */
+ if (G_UNLIKELY (cookie != pad->priv->events_cookie)) {
+ if (G_LIKELY (ev_ret.event))
+ gst_event_unref (ev_ret.event);
+ goto restart;
+ }
+
+ /* store the received state */
+ ev->received = ev_ret.received;
+
+ /* if the event changed, we need to do something */
+ if (G_UNLIKELY (ev->event != ev_ret.event)) {
+ if (G_UNLIKELY (ev_ret.event == NULL)) {
+ /* function unreffed and set the event to NULL, remove it */
+ g_array_remove_index (events, i);
+ len--;
+ cookie = ++pad->priv->events_cookie;
+ continue;
+ } else {
+ /* function gave a new event for us */
+ gst_event_take (&ev->event, ev_ret.event);
+ }
+ } else {
+ /* just unref, nothing changed */
+ gst_event_unref (ev_ret.event);
+ }
+ if (!ret)
+ break;
+ next:
+ i++;
+ }
+}
/* should be called with LOCK */
static GstEvent *
get_pad_caps (GstPad * pad)
{
GstCaps *caps = NULL;
- GstEvent *event;
- guint idx;
+ PadEvent *ev;
- idx = GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_CAPS);
- /* we can only use the caps when we have successfully send the caps
- * event to the event function and is thus in the active entry */
- if ((event = pad->priv->events[idx].event))
- gst_event_parse_caps (event, &caps);
+ ev = find_event_by_type (pad, GST_EVENT_CAPS, 0);
+ if (ev && ev->event)
+ gst_event_parse_caps (ev->event, &caps);
return caps;
}
gst_pad_set_pad_template (pad, NULL);
- clear_events (pad);
+ remove_events (pad);
g_hook_list_clear (&pad->probes);
}
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);
}
* 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.
*/
GST_DEBUG_PAD_NAME (peer));
GST_OBJECT_LOCK (peer);
- reschedule_events (peer);
+ schedule_events (peer, NULL);
GST_OBJECT_UNLOCK (peer);
gst_object_unref (peer);
GST_PAD_STREAM_LOCK (pad);
GST_DEBUG_OBJECT (pad, "stopped streaming");
GST_OBJECT_LOCK (pad);
- clear_events (pad);
+ remove_events (pad);
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
break;
"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,
GST_OBJECT_LOCK (pad);
reconfigure = GST_PAD_NEEDS_RECONFIGURE (pad);
+ if (reconfigure)
+ GST_DEBUG_OBJECT (pad, "remove RECONFIGURE flag");
GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
GST_OBJECT_UNLOCK (pad);
}
/**
+ * gst_pad_set_activate_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadActivateFunction to set.
+ *
+ * Calls gst_pad_set_activate_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_activate_function_full:
* @pad: a #GstPad.
* @activate: the #GstPadActivateFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @activate will not be used anymore.
*
* Sets the given activate function for @pad. The activate function will
*/
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",
}
/**
+ * gst_pad_set_activatemode_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadActivateModeFunction to set.
+ *
+ * Calls gst_pad_set_activatemode_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_activatemode_function_full:
* @pad: a #GstPad.
* @activatemode: the #GstPadActivateModeFunction to set.
+ * @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
*/
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",
}
/**
+ * gst_pad_set_chain_function:
+ * @p: a sink #GstPad.
+ * @f: the #GstPadChainFunction to set.
+ *
+ * Calls gst_pad_set_chain_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_chain_function_full:
* @pad: a sink #GstPad.
* @chain: the #GstPadChainFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @chain will not be used anymore.
*
* Sets the given chain function for the pad. The chain function is called to
*/
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",
}
/**
+ * gst_pad_set_chain_list_function:
+ * @p: a sink #GstPad.
+ * @f: the #GstPadChainListFunction to set.
+ *
+ * Calls gst_pad_set_chain_list_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_chain_list_function_full:
* @pad: a sink #GstPad.
* @chainlist: the #GstPadChainListFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @chainlist will not be used anymore.
*
* Sets the given chain list function for the pad. The chainlist function is
*/
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",
}
/**
+ * gst_pad_set_getrange_function:
+ * @p: a source #GstPad.
+ * @f: the #GstPadGetRangeFunction to set.
+ *
+ * Calls gst_pad_set_getrange_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_getrange_function_full:
* @pad: a source #GstPad.
* @get: the #GstPadGetRangeFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @get will not be used anymore.
*
* Sets the given getrange function for the pad. The getrange function is
*/
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",
}
/**
+ * gst_pad_set_event_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadEventFunction to set.
+ *
+ * Calls gst_pad_set_event_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_event_function_full:
* @pad: a #GstPad of either direction.
* @event: the #GstPadEventFunction to set.
+ * @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",
}
/**
+ * gst_pad_set_query_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadQueryFunction to set.
+ *
+ * Calls gst_pad_set_query_function_full() with NULL for the user_data and
+ * notify.
+ */
+/**
* gst_pad_set_query_function_full:
* @pad: a #GstPad of either direction.
* @query: the #GstPadQueryFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @query will not be used anymore.
*
* Set the given query function for the pad.
*/
void
gst_pad_set_query_function_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",
}
/**
+ * gst_pad_set_iterate_internal_links_function:
+ * @p: a #GstPad of either direction.
+ * @f: the #GstPadIterIntLinkFunction to set.
+ *
+ * Calls gst_pad_set_iterate_internal_links_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
* gst_pad_set_iterate_internal_links_function_full:
* @pad: a #GstPad of either direction.
* @iterintlink: the #GstPadIterIntLinkFunction to set.
+ * @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.
*/
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",
}
/**
+ * gst_pad_set_link_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadLinkFunction to set.
+ *
+ * Calls gst_pad_set_link_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
* gst_pad_set_link_function_full:
* @pad: a #GstPad.
* @link: the #GstPadLinkFunction to set.
+ * @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
*/
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",
}
/**
+ * gst_pad_set_unlink_function:
+ * @p: a #GstPad.
+ * @f: the #GstPadUnlinkFunction to set.
+ *
+ * Calls gst_pad_set_unlink_function_full() with NULL
+ * for the user_data and notify.
+ */
+/**
* gst_pad_set_unlink_function_full:
* @pad: a #GstPad.
* @unlink: the #GstPadUnlinkFunction to set.
+ * @user_data: user_data passed to @notify
* @notify: notify called when @unlink will not be used anymore.
*
* Sets the given unlink function for the pad. It will be called
*/
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",
GstPadLinkReturn result;
GstElement *parent;
GstPadLinkFunction srcfunc, sinkfunc;
- guint idx;
g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), GST_PAD_LINK_WRONG_DIRECTION);
GST_PAD_PEER (sinkpad) = srcpad;
/* check events, when something is different, mark pending */
- for (idx = 0; idx < GST_EVENT_MAX_STICKY; idx++) {
- PadEvent *srcev, *sinkev;
-
- srcev = &srcpad->priv->events[idx];
- sinkev = &sinkpad->priv->events[idx];
-
- if (srcev->event != sinkev->event) {
- GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
- break;
- }
- }
+ schedule_events (srcpad, sinkpad);
/* get the link functions */
srcfunc = GST_PAD_LINKFUNC (srcpad);
GstCaps *
gst_pad_get_pad_template_caps (GstPad * pad)
{
- static GstStaticCaps anycaps = GST_STATIC_CAPS ("ANY");
-
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
if (GST_PAD_PAD_TEMPLATE (pad))
return gst_pad_template_get_caps (GST_PAD_PAD_TEMPLATE (pad));
- return gst_static_caps_get (&anycaps);
+ return gst_caps_ref (GST_CAPS_ANY);
}
/**
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;
}
GST_LOG_OBJECT (pad, "default event handler");
switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- {
- GST_DEBUG_OBJECT (pad, "pausing task because of eos");
- gst_pad_pause_task (pad);
- break;
- }
case GST_EVENT_CAPS:
forward = GST_PAD_IS_PROXY_CAPS (pad);
result = TRUE;
}
}
+ 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);
if ((result = get_pad_caps (pad)))
goto filter_done_unlock;
}
- GST_OBJECT_UNLOCK (pad);
/* this almost never happens */
GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
- result = gst_caps_new_empty ();
-
- goto done;
+ result = GST_CAPS_ANY;
filter_done_unlock:
GST_OBJECT_UNLOCK (pad);
result = gst_caps_ref (result);
}
-done:
gst_query_set_caps_result (query, result);
gst_caps_unref (result);
return TRUE;
}
+typedef struct
+{
+ GstQuery *query;
+ gboolean result;
+ gboolean dispatched;
+} QueryData;
+
+static gboolean
+query_forward_func (GstPad * pad, QueryData * data)
+{
+ GST_LOG_OBJECT (pad, "query peer %p (%s) of %s:%s",
+ data->query, GST_EVENT_TYPE_NAME (data->query), GST_DEBUG_PAD_NAME (pad));
+
+ data->result |= gst_pad_peer_query (pad, data->query);
+
+ data->dispatched = TRUE;
+
+ /* stop on first successful reply */
+ return data->result;
+}
+
/**
* gst_pad_query_default:
* @pad: a #GstPad to call the default query handler on.
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;
case GST_QUERY_JITTER:
case GST_QUERY_RATE:
case GST_QUERY_CONVERT:
- case GST_QUERY_ALLOCATION:
default:
+ forward = TRUE;
break;
}
+ GST_DEBUG_OBJECT (pad, "%sforwarding %p (%s) query", (forward ? "" : "not "),
+ query, GST_QUERY_TYPE_NAME (query));
+
if (forward) {
- ret = gst_pad_forward
- (pad, (GstPadForwardFunction) gst_pad_peer_query, query);
+ QueryData data;
+
+ data.query = query;
+ data.dispatched = FALSE;
+ data.result = FALSE;
+
+ gst_pad_forward (pad, (GstPadForwardFunction) query_forward_func, &data);
+
+ if (data.dispatched) {
+ ret = data.result;
+ } else {
+ /* nothing dispatched, assume drained */
+ if (GST_QUERY_TYPE (query) == GST_QUERY_DRAIN)
+ ret = TRUE;
+ else
+ ret = FALSE;
+ }
}
return ret;
}
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,
/* one of the scheduling types */
if ((flags & GST_PAD_PROBE_TYPE_SCHEDULING & type) == 0)
goto no_match;
- /* all of the blocking types must match */
- if ((flags & GST_PAD_PROBE_TYPE_BLOCKING) !=
- (type & GST_PAD_PROBE_TYPE_BLOCKING))
+ /* one of the blocking types must match */
+ if ((type & GST_PAD_PROBE_TYPE_BLOCKING) &&
+ (flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0)
+ goto no_match;
+ /* only probes that have GST_PAD_PROBE_TYPE_EVENT_FLUSH set */
+ if ((type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) &&
+ (flags & GST_PAD_PROBE_TYPE_EVENT_FLUSH & type) == 0)
goto no_match;
GST_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);
+
+ data->marshalled = TRUE;
callback = (GstPadProbeCallback) hook->func;
if (callback == NULL)
return;
+ info->id = hook->hook_id;
+
GST_OBJECT_UNLOCK (pad);
ret = callback (pad, info, hook->data);
GST_OBJECT_LOCK (pad);
- data->marshalled = TRUE;
switch (ret) {
case GST_PAD_PROBE_REMOVE:
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;
}
}
-#define PROBE_PRE_PULL(pad,mask,data,offs,size,label,probed,defaultval) \
- G_STMT_START { \
- if (G_UNLIKELY (pad->num_probes)) { \
- /* we start with passing NULL as the data item */ \
- GstPadProbeInfo info = { mask, NULL, offs, size }; \
- ret = do_probe_callbacks (pad, &info, defaultval); \
- /* store the possibly updated data item */ \
- data = GST_PAD_PROBE_INFO_DATA (&info); \
- /* if something went wrong, exit */ \
- if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK)) \
- goto label; \
- /* otherwise check if the probe retured data */ \
- if (G_UNLIKELY (data != NULL)) \
- goto probed; \
- } \
- } G_STMT_END
-
-
/* a probe that does not take or return any data */
#define PROBE_NO_DATA(pad,mask,label,defaultval) \
G_STMT_START { \
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; \
} \
} G_STMT_END
-#define PROBE_FULL(pad,mask,data,offs,size,label,defaultval) \
+#define PROBE_FULL(pad,mask,data,offs,size,label) \
G_STMT_START { \
if (G_UNLIKELY (pad->num_probes)) { \
- GstPadProbeInfo info = { mask, data, offs, size }; \
- ret = do_probe_callbacks (pad, &info, defaultval); \
+ /* pass the data item */ \
+ GstPadProbeInfo info = { mask, 0, data, offs, size }; \
+ ret = do_probe_callbacks (pad, &info, GST_FLOW_OK); \
+ /* store the possibly updated data item */ \
data = GST_PAD_PROBE_INFO_DATA (&info); \
- if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK)) \
+ /* if something went wrong, exit */ \
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) \
goto label; \
} \
} G_STMT_END
-#define PROBE_PUSH(pad,mask,data,label) \
- PROBE_FULL(pad, mask, data, -1, -1, label, GST_FLOW_OK);
-#define PROBE_PULL(pad,mask,data,offs,size,label) \
- PROBE_FULL(pad, mask, data, offs, size, label, GST_FLOW_OK);
+#define PROBE_PUSH(pad,mask,data,label) \
+ PROBE_FULL(pad, mask, data, -1, -1, label);
+#define PROBE_PULL(pad,mask,data,offs,size,label) \
+ PROBE_FULL(pad, mask, data, offs, size, label);
static GstFlowReturn
do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info,
flushing:
{
GST_DEBUG_OBJECT (pad, "pad is flushing");
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
dropped:
{
void
gst_pad_set_offset (GstPad * pad, gint64 offset)
{
- guint idx;
+ PadEvent *ev;
g_return_if_fail (GST_IS_PAD (pad));
if (GST_PAD_IS_SINK (pad))
goto done;
- /* the index of the segment event in the array */
- idx = GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_SEGMENT);
-
/* resend the last segment event on next buffer push */
- if (pad->priv->events[idx].event) {
- pad->priv->events[idx].received = FALSE;
+ if ((ev = find_event_by_type (pad, GST_EVENT_SEGMENT, 0))) {
+ ev->received = FALSE;
GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
}
GST_OBJECT_UNLOCK (pad);
}
+typedef struct
+{
+ GstFlowReturn ret;
+
+ /* If TRUE and ret is not OK this means
+ * that pushing the EOS event failed
+ */
+ gboolean was_eos;
+} PushStickyData;
+
+/* should be called with pad LOCK */
+static gboolean
+push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data)
+{
+ PushStickyData *data = user_data;
+ GstEvent *event = ev->event;
+
+ if (ev->received) {
+ GST_DEBUG_OBJECT (pad, "event %s was already received",
+ GST_EVENT_TYPE_NAME (event));
+ return TRUE;
+ }
+
+ data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event),
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+
+ switch (data->ret) {
+ case GST_FLOW_OK:
+ ev->received = TRUE;
+ GST_DEBUG_OBJECT (pad, "event %s marked received",
+ GST_EVENT_TYPE_NAME (event));
+ break;
+ case GST_FLOW_NOT_LINKED:
+ /* not linked is not a problem, we are sticky so the event will be
+ * sent later but only for non-EOS events */
+ GST_DEBUG_OBJECT (pad, "pad was not linked");
+ if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
+ data->ret = GST_FLOW_OK;
+ /* fallthrough */
+ default:
+ GST_DEBUG_OBJECT (pad, "mark pending events");
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+ break;
+ }
+
+ if (data->ret != GST_FLOW_OK && GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+ data->was_eos = TRUE;
+
+ return data->ret == GST_FLOW_OK;
+}
+
+/* check sticky events and push them when needed. should be called
+ * with pad LOCK */
+static inline GstFlowReturn
+check_sticky (GstPad * pad)
+{
+ PushStickyData data = { GST_FLOW_OK, FALSE };
+
+ if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+
+ GST_DEBUG_OBJECT (pad, "pushing all sticky events");
+ events_foreach (pad, push_sticky, &data);
+
+ /* If there's an EOS event we must push it downstream
+ * even if sending a previous sticky event failed.
+ * Otherwise the pipeline might wait forever for EOS.
+ *
+ * Only do this if pushing another event than the EOS
+ * event failed.
+ */
+ if (data.ret != GST_FLOW_OK && !data.was_eos) {
+ PadEvent *ev = find_event_by_type (pad, GST_EVENT_EOS, 0);
+
+ if (ev && !ev->received) {
+ data.ret = gst_pad_push_event_unchecked (pad, gst_event_ref (ev->event),
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM);
+ }
+ }
+ }
+ return data.ret;
+}
+
/**
* gst_pad_query:
gst_pad_query (GstPad * pad, GstQuery * query)
{
GstObject *parent;
- gboolean res;
+ gboolean res, serialized;
GstPadQueryFunction func;
GstPadProbeType type;
GstFlowReturn ret;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
- GST_DEBUG_OBJECT (pad, "sending query %p (%s)", query,
- GST_QUERY_TYPE_NAME (query));
-
- if (GST_PAD_IS_SRC (pad))
+ if (GST_PAD_IS_SRC (pad)) {
+ if (G_UNLIKELY (!GST_QUERY_IS_UPSTREAM (query)))
+ goto wrong_direction;
type = GST_PAD_PROBE_TYPE_QUERY_UPSTREAM;
- else
+ } else if (GST_PAD_IS_SINK (pad)) {
+ if (G_UNLIKELY (!GST_QUERY_IS_DOWNSTREAM (query)))
+ goto wrong_direction;
type = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM;
+ } else
+ goto unknown_direction;
+
+ GST_DEBUG_OBJECT (pad, "doing query %p (%s)", query,
+ GST_QUERY_TYPE_NAME (query));
+
+ serialized = GST_QUERY_IS_SERIALIZED (query);
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_LOCK (pad);
GST_OBJECT_LOCK (pad);
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH |
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PULL, query, probe_stopped);
GST_OBJECT_UNLOCK (pad);
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_UNLOCK (pad);
+
return res;
/* ERRORS */
+wrong_direction:
+ {
+ g_warning ("pad %s:%s query %s in wrong direction",
+ GST_DEBUG_PAD_NAME (pad), GST_QUERY_TYPE_NAME (query));
+ return FALSE;
+ }
+unknown_direction:
+ {
+ g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+ return FALSE;
+ }
no_parent:
{
GST_DEBUG_OBJECT (pad, "had no parent");
GST_OBJECT_UNLOCK (pad);
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_UNLOCK (pad);
return FALSE;
}
no_func:
{
GST_DEBUG_OBJECT (pad, "had no query function");
RELEASE_PARENT (parent);
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_UNLOCK (pad);
return FALSE;
}
query_failed:
{
GST_DEBUG_OBJECT (pad, "query failed");
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_UNLOCK (pad);
return FALSE;
}
probe_stopped:
{
GST_DEBUG_OBJECT (pad, "probe stopped: %s", gst_flow_get_name (ret));
GST_OBJECT_UNLOCK (pad);
- return FALSE;
+ if (G_UNLIKELY (serialized))
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ /* if a probe dropped, we don't sent it further but assume that the probe
+ * answered the query and return TRUE */
+ if (ret == GST_FLOW_CUSTOM_SUCCESS)
+ res = TRUE;
+ else
+ res = FALSE;
+
+ return res;
}
}
{
GstPad *peerpad;
GstPadProbeType type;
- gboolean res;
+ gboolean res, serialized;
GstFlowReturn ret;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
- if (GST_PAD_IS_SRC (pad))
+ if (GST_PAD_IS_SRC (pad)) {
+ if (G_UNLIKELY (!GST_QUERY_IS_DOWNSTREAM (query)))
+ goto wrong_direction;
type = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM;
- else
+ } else if (GST_PAD_IS_SINK (pad)) {
+ if (G_UNLIKELY (!GST_QUERY_IS_UPSTREAM (query)))
+ goto wrong_direction;
type = GST_PAD_PROBE_TYPE_QUERY_UPSTREAM;
-
+ } else
+ goto unknown_direction;
+
GST_DEBUG_OBJECT (pad, "peer query %p (%s)", query,
GST_QUERY_TYPE_NAME (query));
+ serialized = GST_QUERY_IS_SERIALIZED (query);
+
GST_OBJECT_LOCK (pad);
+ if (GST_PAD_IS_SRC (pad) && serialized) {
+ /* all serialized queries on the srcpad trigger push of
+ * sticky events */
+ if (!check_sticky (pad) == GST_FLOW_OK)
+ goto sticky_failed;
+ }
+
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH |
GST_PAD_PROBE_TYPE_BLOCK, query, probe_stopped);
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, query, probe_stopped);
return res;
/* ERRORS */
+wrong_direction:
+ {
+ g_warning ("pad %s:%s query %s in wrong direction",
+ GST_DEBUG_PAD_NAME (pad), GST_QUERY_TYPE_NAME (query));
+ return FALSE;
+ }
+unknown_direction:
+ {
+ g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+ return FALSE;
+ }
+sticky_failed:
+ {
+ GST_WARNING_OBJECT (pad, "could not send sticky events");
+ GST_OBJECT_UNLOCK (pad);
+ return FALSE;
+ }
no_peer:
{
GST_WARNING_OBJECT (pad, "pad has no peer");
{
GST_DEBUG_OBJECT (pad, "probe stopped: %s", gst_flow_get_name (ret));
GST_OBJECT_UNLOCK (pad);
- return FALSE;
+
+ /* if a probe dropped, we don't sent it further but assume that the probe
+ * answered the query and return TRUE */
+ if (ret == GST_FLOW_CUSTOM_SUCCESS)
+ res = TRUE;
+ else
+ res = FALSE;
+
+ return res;
}
}
* Data passing functions
*/
-/* should be called with the LOCK */
-static inline GstFlowReturn
-gst_pad_push_sticky_events (GstPad * pad)
-{
- GstFlowReturn ret;
- gint i;
-
-restart:
- ret = GST_FLOW_OK;
-
- GST_DEBUG_OBJECT (pad, "pushing out all sticky events");
- for (i = 0; i < GST_EVENT_MAX_STICKY; i++) {
- gboolean res;
- PadEvent *ev;
- GstEvent *event;
-
- ev = &pad->priv->events[i];
-
- /* skip without active event */
- if ((event = ev->event) == NULL)
- continue;
-
- if (ev->received) {
- GST_DEBUG_OBJECT (pad, "event %s was already received",
- GST_EVENT_TYPE_NAME (event));
- continue;
- }
-
- gst_event_ref (event);
- GST_OBJECT_UNLOCK (pad);
-
- res = gst_pad_push_event (pad, event);
-
- GST_OBJECT_LOCK (pad);
- if (!res) {
- ret = GST_FLOW_ERROR;
- break;
- }
- /* things could have changed while we release the lock, check if we still
- * are handling the same event, if we don't something changed and we have
- * to try again. FIXME. we need a cookie here. */
- if (event != ev->event) {
- GST_DEBUG_OBJECT (pad, "events changed, restarting");
- goto restart;
- }
- }
-
- return ret;
-}
-
/* this is the chain function that does not perform the additional argument
* checking for that little extra speed.
*/
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
+ if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+ goto eos;
+
+ if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
+ goto wrong_mode;
+
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
PROBE_PUSH (pad, type, data, probe_stopped);
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;
+ }
+eos:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "chaining, but pad was EOS");
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_EOS;
+ }
+wrong_mode:
+ {
+ g_critical ("chain on pad %s:%s but it was not in push mode",
+ 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:
{
ret = GST_FLOW_OK;
break;
default:
- GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+ GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
break;
}
return ret;
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",
- GST_DEBUG_PAD_NAME (pad)));
+ g_critical ("chain on pad %s:%s but it has no chainfunction",
+ GST_DEBUG_PAD_NAME (pad));
GST_PAD_STREAM_UNLOCK (pad);
return GST_FLOW_NOT_SUPPORTED;
}
*
* 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
*
* 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.
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
- if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
- GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+ if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+ goto eos;
- if ((ret = gst_pad_push_sticky_events (pad)))
- goto events_error;
- }
+ if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
+ goto wrong_mode;
+
+ if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+ goto events_error;
/* do block probes */
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped);
"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;
+ }
+eos:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was EOS");
+ GST_OBJECT_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_EOS;
+ }
+wrong_mode:
+ {
+ g_critical ("pushing on pad %s:%s but it was not activated in push mode",
+ 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;
ret = GST_FLOW_OK;
break;
default:
- GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+ GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
break;
}
return ret;
GstFlowReturn ret;
GstPadGetRangeFunction getrangefunc;
GstObject *parent;
+ GstBuffer *res_buf;
GST_PAD_STREAM_LOCK (pad);
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
- if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) {
- GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
+ if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
+ goto wrong_mode;
- if ((ret = gst_pad_push_sticky_events (pad)))
- goto events_error;
- }
+ if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK)
+ goto events_error;
+
+ res_buf = *buffer;
- /* when one of the probes returns a buffer, probed_data will be called and we
- * skip calling the getrange function */
- PROBE_PRE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
- *buffer, offset, size, probe_stopped, probed_data, GST_FLOW_OK);
+ /* when one of the probes returns DROPPED, probe_stopped will be called
+ * and we skip calling the getrange function, res_buf should then contain a
+ * valid result buffer */
+ PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
+ res_buf, offset, size, probe_stopped);
ACQUIRE_PARENT (pad, parent, no_parent);
GST_OBJECT_UNLOCK (pad);
G_GUINT64_FORMAT ", size %u",
GST_DEBUG_FUNCPTR_NAME (getrangefunc), offset, size);
- ret = getrangefunc (pad, parent, offset, size, buffer);
+ ret = getrangefunc (pad, parent, offset, size, &res_buf);
RELEASE_PARENT (parent);
GST_OBJECT_LOCK (pad);
probed_data:
PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
- *buffer, offset, size, probe_stopped_unref);
+ res_buf, offset, size, probe_stopped_unref);
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
+ *buffer = res_buf;
+
return ret;
/* ERRORS */
"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:
+ {
+ g_critical ("getrange on pad %s:%s but it was not activated in pull mode",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ return GST_FLOW_ERROR;
}
events_error:
{
GST_DEBUG_OBJECT (pad, "no parent");
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
no_function:
{
- GST_ELEMENT_ERROR (parent, CORE, PAD, (NULL),
- ("getrange on pad %s:%s but it has no getrangefunction",
- GST_DEBUG_PAD_NAME (pad)));
+ g_critical ("getrange on pad %s:%s but it has no getrangefunction",
+ GST_DEBUG_PAD_NAME (pad));
RELEASE_PARENT (parent);
GST_PAD_STREAM_UNLOCK (pad);
return GST_FLOW_NOT_SUPPORTED;
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe returned %s", gst_flow_get_name (ret));
+ if (ret == GST_FLOW_CUSTOM_SUCCESS) {
+ if (res_buf) {
+ /* the probe filled the buffer and asks us to not call the getrange
+ * anymore, we continue with the post probes then. */
+ GST_DEBUG_OBJECT (pad, "handled buffer");
+ ret = GST_FLOW_OK;
+ goto probed_data;
+ } else {
+ /* no buffer, we are EOS */
+ GST_DEBUG_OBJECT (pad, "no buffer, return EOS");
+ ret = GST_FLOW_EOS;
+ }
+ }
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
+
return ret;
}
probe_stopped_unref:
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe returned %s", gst_flow_get_name (ret));
+ /* if we drop here, it signals EOS */
+ if (ret == GST_FLOW_CUSTOM_SUCCESS)
+ ret = GST_FLOW_EOS;
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
- gst_buffer_unref (*buffer);
- *buffer = NULL;
+ if (*buffer == NULL)
+ gst_buffer_unref (res_buf);
return ret;
}
get_range_failed:
{
GST_PAD_STREAM_UNLOCK (pad);
- *buffer = NULL;
GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
(ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
pad, "getrange failed, flow: %s", gst_flow_get_name (ret));
* @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
* installed (see gst_pad_set_getrange_function()) this function returns
* #GST_FLOW_NOT_SUPPORTED.
*
+ * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be
+ * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer
+ * must be freed with gst_buffer_unref() after usage.
+ *
+ * When @buffer points to a variable that points to a valid #GstBuffer, the
+ * buffer will be filled with the result data when this function returns
+ * #GST_FLOW_OK. If the provided buffer is larger than @size, only
+ * @size bytes will be filled in the result buffer and its size will be updated
+ * accordingly.
+ *
+ * Note that less than @size bytes can be returned in @buffer when, for example,
+ * an EOS condition is near or when @buffer is not large enough to hold @size
+ * bytes. The caller should check the result buffer size to get the result size.
+ *
+ * When this function returns any other result value than #GST_FLOW_OK, @buffer
+ * will be unchanged.
+ *
* This is a lowlevel function. Usualy gst_pad_pull_range() is used.
*
* Returns: a #GstFlowReturn from the pad.
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (*buffer == NULL
+ || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR);
return gst_pad_get_range_unchecked (pad, offset, size, buffer);
}
* @buffer: (out callee-allocates): a pointer to hold the #GstBuffer, returns
* GST_FLOW_ERROR if %NULL.
*
- * Pulls a @buffer from the peer pad.
+ * Pulls a @buffer from the peer pad or fills up a provided buffer.
*
* This function will first trigger the pad block signal if it was
* installed.
* See gst_pad_get_range() for a list of return values and for the
* semantics of the arguments of this function.
*
+ * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be
+ * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer
+ * must be freed with gst_buffer_unref() after usage. When this function
+ * returns any other result value, @buffer will still point to NULL.
+ *
+ * When @buffer points to a variable that points to a valid #GstBuffer, the
+ * buffer will be filled with the result data when this function returns
+ * #GST_FLOW_OK. When this function returns any other result value,
+ * @buffer will be unchanged. If the provided buffer is larger than @size, only
+ * @size bytes will be filled in the result buffer and its size will be updated
+ * accordingly.
+ *
+ * Note that less than @size bytes can be returned in @buffer when, for example,
+ * an EOS condition is near or when @buffer is not large enough to hold @size
+ * bytes. The caller should check the result buffer size to get the result size.
+ *
* Returns: a #GstFlowReturn from the peer pad.
- * When this function returns #GST_FLOW_OK, @buffer will contain a valid
- * #GstBuffer that should be freed with gst_buffer_unref() after usage.
- * @buffer may not be used or freed when any other return value than
- * #GST_FLOW_OK is returned.
*
* MT safe.
*/
{
GstPad *peer;
GstFlowReturn ret;
+ GstBuffer *res_buf;
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (*buffer == NULL
+ || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR);
GST_OBJECT_LOCK (pad);
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
- /* when one of the probes returns a buffer, probed_data will be called and we
- * skip calling the peer getrange function */
- PROBE_PRE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
- *buffer, offset, size, pre_probe_stopped, probed_data, GST_FLOW_OK);
+ if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL))
+ goto wrong_mode;
+
+ res_buf = *buffer;
+
+ /* when one of the probes returns DROPPED, probe_stopped will be
+ * called and we skip calling the peer getrange function. *buffer should then
+ * contain a valid buffer */
+ PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BLOCK,
+ res_buf, offset, size, probe_stopped);
if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
goto not_linked;
pad->priv->using++;
GST_OBJECT_UNLOCK (pad);
- ret = gst_pad_get_range_unchecked (peer, offset, size, buffer);
+ ret = gst_pad_get_range_unchecked (peer, offset, size, &res_buf);
gst_object_unref (peer);
if (pad->priv->using == 0) {
/* pad is not active anymore, trigger idle callbacks */
PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_IDLE,
- post_probe_stopped, ret);
+ probe_stopped_unref, ret);
}
if (G_UNLIKELY (ret != GST_FLOW_OK))
probed_data:
PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER,
- *buffer, offset, size, post_probe_stopped);
+ res_buf, offset, size, probe_stopped_unref);
GST_OBJECT_UNLOCK (pad);
+ *buffer = res_buf;
+
return ret;
/* ERROR recovery here */
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:
+ {
+ g_critical ("pullrange on pad %s:%s but it was not activated in pull mode",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ return GST_FLOW_ERROR;
}
-pre_probe_stopped:
+probe_stopped:
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pre probe returned %s",
gst_flow_get_name (ret));
+ if (ret == GST_FLOW_CUSTOM_SUCCESS) {
+ if (res_buf) {
+ /* the probe filled the buffer and asks us to not forward to the peer
+ * anymore, we continue with the post probes then */
+ GST_DEBUG_OBJECT (pad, "handled buffer");
+ ret = GST_FLOW_OK;
+ goto probed_data;
+ } else {
+ /* no buffer, we are EOS then */
+ GST_DEBUG_OBJECT (pad, "no buffer, return EOS");
+ ret = GST_FLOW_EOS;
+ }
+ }
GST_OBJECT_UNLOCK (pad);
return ret;
}
}
pull_range_failed:
{
- *buffer = NULL;
GST_OBJECT_UNLOCK (pad);
GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
(ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
pad, "pullrange failed, flow: %s", gst_flow_get_name (ret));
return ret;
}
-post_probe_stopped:
+probe_stopped_unref:
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"post probe returned %s", gst_flow_get_name (ret));
GST_OBJECT_UNLOCK (pad);
- if (ret == GST_FLOW_OK)
- gst_buffer_unref (*buffer);
- *buffer = NULL;
+ /* if we drop here, it signals EOS */
+ if (ret == GST_FLOW_CUSTOM_SUCCESS)
+ ret = GST_FLOW_EOS;
+ if (*buffer == NULL)
+ gst_buffer_unref (res_buf);
return ret;
}
}
static gboolean
gst_pad_store_sticky_event (GstPad * pad, GstEvent * event, gboolean locked)
{
- guint idx;
- gboolean ret;
+ guint i, len;
+ GstEventType type;
+ GArray *events;
+ gboolean res = FALSE;
+ const gchar *name = NULL;
+
+ type = GST_EVENT_TYPE (event);
+ if (type & GST_EVENT_TYPE_STICKY_MULTI)
+ name = gst_structure_get_name (gst_event_get_structure (event));
+
+ events = pad->priv->events;
+ len = events->len;
+
+ for (i = 0; i < len; i++) {
+ PadEvent *ev = &g_array_index (events, PadEvent, i);
+
+ if (ev->event == NULL)
+ continue;
+
+ if (type == GST_EVENT_TYPE (ev->event)) {
+ /* matching types, check matching name if needed */
+ if (name && !gst_event_has_name (ev->event, name))
+ continue;
+
+ /* overwrite */
+ if ((res = gst_event_replace (&ev->event, event)))
+ ev->received = FALSE;
+ break;
+ }
+ }
+ if (i == len) {
+ PadEvent ev;
+ ev.event = gst_event_ref (event);
+ ev.received = FALSE;
+ g_array_append_val (events, ev);
+ res = TRUE;
+ }
- idx = GST_EVENT_STICKY_IDX (event);
+ if (res) {
+ pad->priv->events_cookie++;
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
- ret = gst_event_replace (&pad->priv->events[idx].event, event);
+ GST_LOG_OBJECT (pad, "stored sticky event %s", GST_EVENT_TYPE_NAME (event));
- if (ret) {
- GST_LOG_OBJECT (pad, "stored sticky event %s at index %u",
- GST_EVENT_TYPE_NAME (event), idx);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
if (locked)
break;
}
}
- return ret;
+ 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)
+/* should be called with pad LOCK */
+static GstFlowReturn
+gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event,
+ GstPadProbeType type)
{
GstFlowReturn ret;
GstPad *peerpad;
- gboolean result;
- gboolean stored = FALSE;
- GstPadProbeType type;
- guint idx = 0;
- 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;
- if ((sticky = GST_EVENT_IS_STICKY (event)))
- idx = GST_EVENT_STICKY_IDX (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;
-
- GST_OBJECT_LOCK (pad);
- peerpad = GST_PAD_PEER (pad);
+ GstEventType event_type;
/* Two checks to be made:
* . (un)set the FLUSHING flag for flushing events,
* . handle pad blocking */
- switch (GST_EVENT_TYPE (event)) {
+ event_type = GST_EVENT_TYPE (event);
+ switch (event_type) {
case GST_EVENT_FLUSH_START:
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);
/* Remove sticky EOS events */
GST_LOG_OBJECT (pad, "Removing pending EOS events");
- clear_event (pad, GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_EOS));
+ remove_event_by_type (pad, GST_EVENT_EOS);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_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:
{
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushed;
- /* store the event on the pad, but only on srcpads. We always store the
- * event exactly how it was sent */
- if (sticky) {
- /* srcpad sticky events are store immediately, we set the pending flag
- * to TRUE, it will be set to FALSE 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));
- pad->priv->events[idx].received = FALSE;
- }
- /* the peerpad might have changed. Things we checked above could not
- * have changed. */
- peerpad = GST_PAD_PEER (pad);
- stored = TRUE;
- }
+ /* No need to check for EOS here as either the caller (gst_pad_push_event())
+ * checked already or this is called as part of pushing sticky events,
+ * in which case we still want to forward the EOS event downstream.
+ */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped);
/* now check the peer pad */
+ peerpad = GST_PAD_PEER (pad);
if (peerpad == NULL)
goto not_linked;
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) {
- pad->priv->events[idx].received = TRUE;
- GST_DEBUG_OBJECT (pad, "event cleared pending");
- } else {
- GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
- GST_DEBUG_OBJECT (pad, "mark pending events");
- }
- }
pad->priv->using--;
if (pad->priv->using == 0) {
/* pad is not active anymore, trigger idle callbacks */
PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_IDLE,
- idle_probe_stopped, 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;
- }
-idle_probe_stopped:
- {
- GST_DEBUG_OBJECT (pad, "Idle probe returned %s", gst_flow_get_name (ret));
- 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, "an error occured %s", gst_flow_get_name (ret));
+ break;
+ }
+ return ret;
}
not_linked:
{
GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS);
- GST_OBJECT_UNLOCK (pad);
gst_event_unref (event);
- return stored;
- }
-}
-
-/* Check if we can call the event function with the given event */
-static GstFlowReturn
-pre_eventfunc_check (GstPad * pad, GstEvent * event)
-{
- GstCaps *caps, *templ;
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_CAPS:
- {
- /* backwards compatibility mode for caps */
- gst_event_parse_caps (event, &caps);
- /* See if pad accepts the caps */
- templ = gst_pad_get_pad_template_caps (pad);
- if (!gst_caps_is_subset (caps, templ))
- goto not_accepted;
+ /* unlinked pads should not influence latency configuration */
+ if (event_type == GST_EVENT_LATENCY)
+ return GST_FLOW_OK;
- gst_caps_unref (templ);
- break;
- }
- default:
- break;
+ return GST_FLOW_NOT_LINKED;
}
- return GST_FLOW_OK;
-
- /* ERRORS */
-not_accepted:
+idle_probe_stopped:
{
- gst_caps_unref (templ);
- GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
- "caps %" GST_PTR_FORMAT " not accepted", caps);
- return GST_FLOW_NOT_NEGOTIATED;
+ GST_DEBUG_OBJECT (pad, "Idle probe returned %s", gst_flow_get_name (ret));
+ return ret;
}
}
/**
- * gst_pad_send_event:
- * @pad: a #GstPad to send the event to.
+ * gst_pad_push_event:
+ * @pad: a #GstPad to push the event to.
* @event: (transfer full): the #GstEvent to send to the pad.
*
- * Sends the event to the pad. This function can be used
- * by applications to send events in the pipeline.
- *
- * If @pad is a source pad, @event should be an upstream event. If @pad is a
- * sink pad, @event should be a downstream event. For example, you would not
- * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
- * Furthermore, some downstream events have to be serialized with data flow,
- * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
- * the event needs to be serialized with data flow, this function will take the
- * pad's stream lock while calling its event function.
- *
- * To find out whether an event type is upstream, downstream, or downstream and
- * serialized, see #GstEventTypeFlags, gst_event_type_get_flags(),
- * #GST_EVENT_IS_UPSTREAM, #GST_EVENT_IS_DOWNSTREAM, and
- * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or
- * plugin doesn't need to bother itself with this information; the core handles
- * all necessary locks and checks.
+ * Sends the event to the peer of the given pad. This function is
+ * mainly used by elements to send events to their peer
+ * elements.
*
* This function takes owership of the provided event so you should
* gst_event_ref() it if you want to reuse the event after this call.
*
* Returns: TRUE if the event was handled.
+ *
+ * MT safe.
*/
gboolean
-gst_pad_send_event (GstPad * pad, GstEvent * event)
+gst_pad_push_event (GstPad * pad, GstEvent * event)
{
- GstFlowReturn ret;
- gboolean result = FALSE;
- gboolean serialized, need_unlock = FALSE, sticky;
- GstPadEventFunction eventfunc;
- GstObject *parent;
+ gboolean res = FALSE;
GstPadProbeType type;
+ gboolean sticky, serialized;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
- if (GST_PAD_IS_SINK (pad)) {
+ if (GST_PAD_IS_SRC (pad)) {
if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
goto wrong_direction;
- serialized = GST_EVENT_IS_SERIALIZED (event);
- sticky = GST_EVENT_IS_STICKY (event);
type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
- } else if (GST_PAD_IS_SRC (pad)) {
+ } else if (GST_PAD_IS_SINK (pad)) {
if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
goto wrong_direction;
- /* events on srcpad never are serialized and sticky */
- serialized = sticky = FALSE;
+ /* events pushed on sinkpad never are sticky */
type = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM;
} else
goto unknown_direction;
GST_OBJECT_LOCK (pad);
+ sticky = GST_EVENT_IS_STICKY (event);
+ serialized = GST_EVENT_IS_SERIALIZED (event);
+
+ if (sticky) {
+ /* can't store on flushing pads */
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushed;
+
+ if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+ goto eos;
+
+ /* srcpad sticky events are store immediately, the received flag is set
+ * to FALSE and will be set to TRUE when we can successfully push the
+ * event to the peer pad */
+ if (gst_pad_store_sticky_event (pad, event, TRUE)) {
+ GST_DEBUG_OBJECT (pad, "event %s updated", GST_EVENT_TYPE_NAME (event));
+ }
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS);
+ }
+ if (GST_PAD_IS_SRC (pad) && (serialized || sticky)) {
+ /* all serialized or sticky events on the srcpad trigger push of
+ * sticky events */
+ res = (check_sticky (pad) == GST_FLOW_OK);
+ }
+ if (!sticky) {
+ /* other events are pushed right away */
+ res = (gst_pad_push_event_unchecked (pad, event, type) == GST_FLOW_OK);
+ } else {
+ /* Errors in sticky event pushing are no problem and ignored here
+ * as they will cause more meaningful errors during data flow.
+ * For EOS events, that are not followed by data flow, we still
+ * return FALSE here though.
+ */
+ if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
+ res = TRUE;
+ gst_event_unref (event);
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return res;
+
+ /* ERROR handling */
+wrong_direction:
+ {
+ g_warning ("pad %s:%s pushing %s event in wrong direction",
+ GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+ gst_event_unref (event);
+ return FALSE;
+ }
+unknown_direction:
+ {
+ g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+ gst_event_unref (event);
+ return FALSE;
+ }
+flushed:
+ {
+ GST_DEBUG_OBJECT (pad, "We're flushing");
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+eos:
+ {
+ GST_DEBUG_OBJECT (pad, "We're EOS");
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+}
+
+/* Check if we can call the event function with the given event */
+static GstFlowReturn
+pre_eventfunc_check (GstPad * pad, GstEvent * event)
+{
+ GstCaps *caps;
+
switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ /* backwards compatibility mode for caps */
+ gst_event_parse_caps (event, &caps);
+
+ if (!gst_pad_query_accept_caps (pad, caps))
+ goto not_accepted;
+ break;
+ }
+ default:
+ break;
+ }
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+not_accepted:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "caps %" GST_PTR_FORMAT " not accepted", caps);
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+}
+
+static GstFlowReturn
+gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,
+ GstPadProbeType type)
+{
+ GstFlowReturn ret;
+ GstEventType event_type;
+ gboolean serialized, need_unlock = FALSE, sticky;
+ GstPadEventFunction eventfunc;
+ GstObject *parent;
+
+ GST_OBJECT_LOCK (pad);
+ if (GST_PAD_IS_SINK (pad))
+ serialized = GST_EVENT_IS_SERIALIZED (event);
+ else
+ serialized = FALSE;
+ sticky = GST_EVENT_IS_STICKY (event);
+ event_type = GST_EVENT_TYPE (event);
+ switch (event_type) {
case GST_EVENT_FLUSH_START:
GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
"have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
}
/* Remove pending EOS events */
GST_LOG_OBJECT (pad, "Removing pending EOS events");
- clear_event (pad, GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_EOS));
+ remove_event_by_type (pad, GST_EVENT_EOS);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
GST_OBJECT_UNLOCK (pad);
/* grab stream lock */
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;
if (serialized) {
+ if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+ goto eos;
+
/* lock order: STREAM_LOCK, LOCK, recheck flushing. */
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_LOCK (pad);
GST_OBJECT_LOCK (pad);
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
+
+ if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
+ goto eos;
}
switch (GST_EVENT_TYPE (event)) {
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);
-
+ if (event_type == GST_EVENT_EOS)
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS);
+ }
gst_event_unref (event);
}
if (need_unlock)
GST_PAD_STREAM_UNLOCK (pad);
- GST_DEBUG_OBJECT (pad, "sent event, result %d", result);
-
- return result;
+ return ret;
/* ERROR handling */
-wrong_direction:
+flushing:
{
- g_warning ("pad %s:%s sending %s event in wrong direction",
- GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+ GST_OBJECT_UNLOCK (pad);
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+ "Received event on flushing pad. Discarding");
gst_event_unref (event);
- return FALSE;
+ return GST_FLOW_FLUSHING;
}
-unknown_direction:
+eos:
{
- 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_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+ "Received event on EOS pad. Discarding");
gst_event_unref (event);
- return FALSE;
+ return GST_FLOW_EOS;
}
-no_function:
+probe_stopped:
{
- g_warning ("pad %s:%s has no event handler, file a bug.",
- GST_DEBUG_PAD_NAME (pad));
GST_OBJECT_UNLOCK (pad);
if (need_unlock)
GST_PAD_STREAM_UNLOCK (pad);
gst_event_unref (event);
- return FALSE;
+
+ switch (ret) {
+ case GST_FLOW_CUSTOM_SUCCESS:
+ GST_DEBUG_OBJECT (pad, "dropped event");
+ ret = GST_FLOW_OK;
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret));
+ break;
+ }
+ return ret;
}
-precheck_failed:
+no_function:
{
- GST_DEBUG_OBJECT (pad, "pre event check failed");
- RELEASE_PARENT (parent);
+ g_warning ("pad %s:%s has no event handler, file a bug.",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
if (need_unlock)
GST_PAD_STREAM_UNLOCK (pad);
gst_event_unref (event);
- return FALSE;
+ return GST_FLOW_NOT_SUPPORTED;
}
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;
}
* gst_pad_get_sticky_event:
* @pad: the #GstPad to get the event from.
* @event_type: the #GstEventType that should be retrieved.
+ * @idx: the index of the event
*
* Returns a new reference of the sticky event of type @event_type
* from the event.
*
- * Returns: (transfer full): a #GstEvent of type @event_type. Unref after usage.
+ * Returns: (transfer full): a #GstEvent of type @event_type or NULL when no
+ * event of @event_type was on @pad. Unref after usage.
*/
GstEvent *
-gst_pad_get_sticky_event (GstPad * pad, GstEventType event_type)
+gst_pad_get_sticky_event (GstPad * pad, GstEventType event_type, guint idx)
{
GstEvent *event = NULL;
- guint idx;
+ PadEvent *ev;
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
g_return_val_if_fail ((event_type & GST_EVENT_TYPE_STICKY) != 0, NULL);
- idx = GST_EVENT_STICKY_IDX_TYPE (event_type);
-
GST_OBJECT_LOCK (pad);
- if ((event = pad->priv->events[idx].event)) {
+ ev = find_event_by_type (pad, event_type, idx);
+ if (ev && (event = ev->event))
gst_event_ref (event);
- }
GST_OBJECT_UNLOCK (pad);
return event;
}
+typedef struct
+{
+ GstPadStickyEventsForeachFunction func;
+ gpointer user_data;
+} ForeachDispatch;
+
+static gboolean
+foreach_dispatch_function (GstPad * pad, PadEvent * ev, gpointer user_data)
+{
+ ForeachDispatch *data = user_data;
+ gboolean ret;
+
+ GST_OBJECT_UNLOCK (pad);
+
+ ret = data->func (pad, &ev->event, data->user_data);
+
+ GST_OBJECT_LOCK (pad);
+
+ return ret;
+}
+
/**
* gst_pad_sticky_events_foreach:
* @pad: the #GstPad that should be used for iteration.
- * @foreach_func: (scope call): the #GstPadStickyEventsForeachFunction that should be called for every event.
+ * @foreach_func: (scope call): the #GstPadStickyEventsForeachFunction that
+ * should be called for every event.
* @user_data: (closure): the optional user data.
*
- * Iterates all active sticky events on @pad and calls @foreach_func for every
- * event. If @foreach_func returns something else than GST_FLOW_OK the iteration
- * is immediately stopped.
- *
- * Returns: GST_FLOW_OK if iteration was successful
+ * Iterates all sticky events on @pad and calls @foreach_func for every
+ * event. If @foreach_func returns %FALSE the iteration is immediately stopped.
*/
-GstFlowReturn
+void
gst_pad_sticky_events_foreach (GstPad * pad,
GstPadStickyEventsForeachFunction foreach_func, gpointer user_data)
{
- GstFlowReturn ret = GST_FLOW_OK;
- guint i;
- GstEvent *event;
-
- g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
- g_return_val_if_fail (foreach_func != NULL, GST_FLOW_ERROR);
-
- GST_OBJECT_LOCK (pad);
-
-restart:
- for (i = 0; i < GST_EVENT_MAX_STICKY; i++) {
- gboolean res;
- PadEvent *ev;
+ ForeachDispatch data;
- ev = &pad->priv->events[i];
-
- /* skip without active event */
- if ((event = ev->event) == NULL)
- continue;
-
- gst_event_ref (event);
- GST_OBJECT_UNLOCK (pad);
-
- res = foreach_func (pad, event, user_data);
-
- GST_OBJECT_LOCK (pad);
- gst_event_unref (event);
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (foreach_func != NULL);
- if (res != GST_FLOW_OK) {
- ret = res;
- break;
- }
+ data.func = foreach_func;
+ data.user_data = user_data;
- /* things could have changed while we release the lock, check if we still
- * are handling the same event, if we don't something changed and we have
- * to try again. FIXME. we need a cookie here. */
- if (event != ev->event) {
- GST_DEBUG_OBJECT (pad, "events changed, restarting");
- goto restart;
- }
- }
+ GST_OBJECT_LOCK (pad);
+ events_foreach (pad, foreach_dispatch_function, &data);
GST_OBJECT_UNLOCK (pad);
-
- return ret;
}
static void
task = gst_task_new (func, data);
gst_task_set_lock (task, GST_PAD_GET_STREAM_LOCK (pad));
gst_task_set_thread_callbacks (task, &thr_callbacks, pad, NULL);
- GST_DEBUG_OBJECT (pad, "created task");
+ GST_INFO_OBJECT (pad, "created task %p", task);
GST_PAD_TASK (pad) = task;
gst_object_ref (task);
/* release lock to post the message */