X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstpad.c;h=b762465b8e838df1b140210ed468540155f6c57b;hb=36f7b1365873496103306e2a18073fa3cc63c306;hp=92891fabc67b13af7c4fc029e091dcb0c47f28d9;hpb=d630a115f9a35ea36bf7654164abb2f89ca6f4f7;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstpad.c b/gst/gstpad.c index 92891fa..b762465 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -16,8 +16,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstpad @@ -38,7 +38,7 @@ * application requests. * * Pads without pad templates can be created with gst_pad_new(), - * which takes a direction and a name as an argument. If the name is NULL, + * which takes a direction and a name as an argument. If the name is %NULL, * then a guaranteed unique name will be assigned to it. * * A #GstElement creating a pad will typically use the various @@ -47,7 +47,7 @@ * * gst_pad_get_parent() will retrieve the #GstElement that owns the pad. * - * After two pads are retrieved from an element with gst_element_get_pad(), + * After two pads are retrieved from an element by gst_element_get_static_pad(), * the pads can be linked with gst_pad_link(). (For quick links, * you can also use gst_element_link(), which will make the obvious * link for you if it's straightforward.). Pads can be unlinked again with @@ -84,8 +84,6 @@ * Convenience functions exist to start, pause and stop the task on a pad with * gst_pad_start_task(), gst_pad_pause_task() and gst_pad_stop_task() * respectively. - * - * Last reviewed on 2012-03-29 (0.11.3) */ #include "gst_private.h" @@ -117,7 +115,8 @@ enum PAD_PROP_CAPS, PAD_PROP_DIRECTION, PAD_PROP_TEMPLATE, - /* FILL ME */ + PAD_PROP_OFFSET + /* FILL ME */ }; #define GST_PAD_GET_PRIVATE(obj) \ @@ -125,7 +124,7 @@ enum /* we have a pending and an active event on the pad. On source pads only the * active event is used. On sinkpads, events are copied to the pending entry and - * moved to the active event when the eventfunc returned TRUE. */ + * moved to the active event when the eventfunc returned %TRUE. */ typedef struct { gboolean received; @@ -136,6 +135,7 @@ struct _GstPadPrivate { guint events_cookie; GArray *events; + guint last_cookie; gint using; guint probe_list_cookie; @@ -250,6 +250,38 @@ gst_flow_to_quark (GstFlowReturn ret) return 0; } +/** + * gst_pad_link_get_name: + * @ret: a #GstPadLinkReturn to get the name of. + * + * Gets a string representing the given pad-link return. + * + * Returns: a static string with the name of the pad-link return. + * + * Since: 1.4 + */ +const gchar * +gst_pad_link_get_name (GstPadLinkReturn ret) +{ + switch (ret) { + case GST_PAD_LINK_OK: + return "ok"; + case GST_PAD_LINK_WRONG_HIERARCHY: + return "wrong hierarchy"; + case GST_PAD_LINK_WAS_LINKED: + return "was linked"; + case GST_PAD_LINK_WRONG_DIRECTION: + return "wrong direction"; + case GST_PAD_LINK_NOFORMAT: + return "no common format"; + case GST_PAD_LINK_NOSCHED: + return "incompatible scheduling"; + case GST_PAD_LINK_REFUSED: + return "refused"; + } + g_return_val_if_reached ("unknown"); +} + #define _do_init \ { \ gint i; \ @@ -324,6 +356,18 @@ gst_pad_class_init (GstPadClass * klass) "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstPad:offset: + * + * The offset that will be applied to the running time of the pad. + * + * Since: 1.6 + */ + g_object_class_install_property (gobject_class, PAD_PROP_OFFSET, + g_param_spec_int64 ("offset", "Offset", + "The running time offset of the pad", 0, G_MAXINT64, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstobject_class->path_string_separator = "."; /* Register common function pointer descriptions */ @@ -356,28 +400,50 @@ gst_pad_init (GstPad * pad) g_hook_list_init (&pad->probes, sizeof (GstProbe)); pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16); + pad->priv->events_cookie = 0; + pad->priv->last_cookie = -1; + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; } /* called when setting the pad inactive. It removes all sticky events from - * the pad */ + * the pad. must be called with object lock */ static void remove_events (GstPad * pad) { guint i, len; GArray *events; + gboolean notify = FALSE; events = pad->priv->events; len = events->len; for (i = 0; i < len; i++) { PadEvent *ev = &g_array_index (events, PadEvent, i); - gst_event_unref (ev->event); + GstEvent *event = ev->event; + + ev->event = NULL; + + if (event && GST_EVENT_TYPE (event) == GST_EVENT_CAPS) + notify = TRUE; + + gst_event_unref (event); } + GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); g_array_set_size (events, 0); pad->priv->events_cookie++; + + if (notify) { + GST_OBJECT_UNLOCK (pad); + + GST_DEBUG_OBJECT (pad, "notify caps"); + g_object_notify_by_pspec ((GObject *) pad, pspec_caps); + + GST_OBJECT_LOCK (pad); + } } +/* should be called with object lock */ static PadEvent * find_event_by_type (GstPad * pad, GstEventType type, guint idx) { @@ -404,6 +470,7 @@ found: return ev; } +/* should be called with OBJECT lock */ static PadEvent * find_event (GstPad * pad, GstEvent * event) { @@ -424,6 +491,7 @@ found: return ev; } +/* should be called with OBJECT lock */ static void remove_event_by_type (GstPad * pad, GstEventType type) { @@ -455,8 +523,9 @@ remove_event_by_type (GstPad * pad, GstEventType type) } /* check all events on srcpad against those on sinkpad. All events that are not - * on sinkpad are marked as received=FALSE and the PENDING_EVENTS is set on the + * on sinkpad are marked as received=%FALSE and the PENDING_EVENTS is set on the * srcpad so that the events will be sent next time */ +/* should be called with srcpad and sinkpad LOCKS */ static void schedule_events (GstPad * srcpad, GstPad * sinkpad) { @@ -528,6 +597,7 @@ restart: if (G_UNLIKELY (ev->event != ev_ret.event)) { if (G_UNLIKELY (ev_ret.event == NULL)) { /* function unreffed and set the event to NULL, remove it */ + gst_event_unref (ev->event); g_array_remove_index (events, i); len--; cookie = ++pad->priv->events_cookie; @@ -549,23 +619,46 @@ restart: /* should be called with LOCK */ static GstEvent * -apply_pad_offset (GstPad * pad, GstEvent * event) +_apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream) { - /* check if we need to adjust the segment */ - if (pad->offset != 0) { + gint64 offset; + + GST_DEBUG_OBJECT (pad, "apply pad offset %" GST_TIME_FORMAT, + GST_TIME_ARGS (pad->offset)); + + if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { GstSegment segment; + g_assert (!upstream); + /* copy segment values */ gst_event_copy_segment (event, &segment); gst_event_unref (event); - /* adjust and make a new event with the offset applied */ - segment.base += pad->offset; + gst_segment_offset_running_time (&segment, segment.format, pad->offset); event = gst_event_new_segment (&segment); } + + event = gst_event_make_writable (event); + offset = gst_event_get_running_time_offset (event); + if (upstream) + offset -= pad->offset; + else + offset += pad->offset; + gst_event_set_running_time_offset (event, offset); + return event; } +static inline GstEvent * +apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream) +{ + if (G_UNLIKELY (pad->offset != 0)) + return _apply_pad_offset (pad, event, upstream); + return event; +} + + /* should be called with the OBJECT_LOCK */ static GstCaps * get_pad_caps (GstPad * pad) @@ -603,7 +696,9 @@ gst_pad_dispose (GObject * object) gst_pad_set_pad_template (pad, NULL); + GST_OBJECT_LOCK (pad); remove_events (pad); + GST_OBJECT_UNLOCK (pad); g_hook_list_clear (&pad->probes); @@ -665,6 +760,9 @@ gst_pad_set_property (GObject * object, guint prop_id, gst_pad_set_pad_template (GST_PAD_CAST (object), (GstPadTemplate *) g_value_get_object (value)); break; + case PAD_PROP_OFFSET: + gst_pad_set_offset (GST_PAD_CAST (object), g_value_get_int64 (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -689,6 +787,9 @@ gst_pad_get_property (GObject * object, guint prop_id, case PAD_PROP_TEMPLATE: g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object)); break; + case PAD_PROP_OFFSET: + g_value_set_int64 (value, gst_pad_get_offset (GST_PAD_CAST (object))); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -697,15 +798,16 @@ gst_pad_get_property (GObject * object, guint prop_id, /** * gst_pad_new: - * @name: the name of the new pad. + * @name: (allow-none): the name of the new pad. * @direction: the #GstPadDirection of the pad. * * Creates a new pad with the given name in the given direction. - * If name is NULL, a guaranteed unique name (across all pads) + * If name is %NULL, a guaranteed unique name (across all pads) * will be assigned. * This function makes a copy of the name so you can safely free the name. * - * Returns: (transfer floating): a new #GstPad, or NULL in case of an error. + * Returns: (transfer floating) (nullable): a new #GstPad, or %NULL in + * case of an error. * * MT safe. */ @@ -719,14 +821,15 @@ gst_pad_new (const gchar * name, GstPadDirection direction) /** * gst_pad_new_from_template: * @templ: the pad template to use - * @name: the name of the element + * @name: (allow-none): the name of the pad * * Creates a new pad with the given name from the given template. - * If name is NULL, a guaranteed unique name (across all pads) + * If name is %NULL, a guaranteed unique name (across all pads) * will be assigned. * This function makes a copy of the name so you can safely free the name. * - * Returns: (transfer full): a new #GstPad, or NULL in case of an error. + * Returns: (transfer floating) (nullable): a new #GstPad, or %NULL in + * case of an error. */ GstPad * gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) @@ -740,14 +843,15 @@ gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) /** * gst_pad_new_from_static_template: * @templ: the #GstStaticPadTemplate to use - * @name: the name of the element + * @name: the name of the pad * * Creates a new pad with the given name from the given static template. - * If name is NULL, a guaranteed unique name (across all pads) + * If name is %NULL, a guaranteed unique name (across all pads) * will be assigned. * This function makes a copy of the name so you can safely free the name. * - * Returns: (transfer full): a new #GstPad, or NULL in case of an error. + * Returns: (transfer floating) (nullable): a new #GstPad, or %NULL in + * case of an error. */ GstPad * gst_pad_new_from_static_template (GstStaticPadTemplate * templ, @@ -840,6 +944,7 @@ pre_activate (GstPad * pad, GstPadMode new_mode) GST_OBJECT_LOCK (pad); GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing"); GST_PAD_SET_FLUSHING (pad); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; GST_PAD_MODE (pad) = new_mode; /* unlock blocked pads so element can resume and stop */ GST_PAD_BLOCK_BROADCAST (pad); @@ -851,6 +956,7 @@ pre_activate (GstPad * pad, GstPadMode new_mode) GST_DEBUG_OBJECT (pad, "setting pad into %s mode, unset flushing", gst_pad_mode_get_name (new_mode)); GST_PAD_UNSET_FLUSHING (pad); + pad->ABI.abi.last_flowret = GST_FLOW_OK; GST_PAD_MODE (pad) = new_mode; if (GST_PAD_IS_SINK (pad)) { GstPad *peer; @@ -910,9 +1016,9 @@ post_activate (GstPad * pad, GstPadMode new_mode) * function to perform the actual activation. * * If not @active, calls gst_pad_activate_mode() with the pad's current mode - * and a FALSE argument. + * and a %FALSE argument. * - * Returns: #TRUE if the operation was successful. + * Returns: %TRUE if the operation was successful. * * MT safe. */ @@ -934,6 +1040,8 @@ gst_pad_set_active (GstPad * pad, gboolean active) if (old == GST_PAD_MODE_NONE) { GST_DEBUG_OBJECT (pad, "activating pad from none"); ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad, parent); + if (ret) + pad->ABI.abi.last_flowret = GST_FLOW_OK; } else { GST_DEBUG_OBJECT (pad, "pad was active in %s mode", gst_pad_mode_get_name (old)); @@ -947,6 +1055,8 @@ gst_pad_set_active (GstPad * pad, gboolean active) GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode", gst_pad_mode_get_name (old)); ret = gst_pad_activate_mode (pad, old, FALSE); + if (ret) + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; } } @@ -989,7 +1099,7 @@ failed: * * If you don't know what this is, you probably don't want to call it. * - * Returns: TRUE if the operation was successful. + * Returns: %TRUE if the operation was successful. * * MT safe. */ @@ -1054,6 +1164,9 @@ gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active) break; } + /* Mark pad as needing reconfiguration */ + if (active) + GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE); pre_activate (pad, new); if (GST_PAD_ACTIVATEMODEFUNC (pad)) { @@ -1138,7 +1251,7 @@ failure: * * Query if a pad is active * - * Returns: TRUE if the pad is active. + * Returns: %TRUE if the pad is active. * * MT safe. */ @@ -1156,6 +1269,34 @@ gst_pad_is_active (GstPad * pad) return result; } +static void +cleanup_hook (GstPad * pad, GHook * hook) +{ + GstPadProbeType type; + + if (!G_HOOK_IS_VALID (hook)) + return; + + type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT; + + if (type & GST_PAD_PROBE_TYPE_BLOCKING) { + /* unblock when we remove the last blocking probe */ + pad->num_blocked--; + GST_DEBUG_OBJECT (pad, "remove blocking probe, now %d left", + pad->num_blocked); + + /* Might have new probes now that want to be called */ + GST_PAD_BLOCK_BROADCAST (pad); + + if (pad->num_blocked == 0) { + GST_DEBUG_OBJECT (pad, "last blocking probe removed, unblocking"); + GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKED); + } + } + g_hook_destroy_link (&pad->probes, hook); + pad->num_probes--; +} + /** * gst_pad_add_probe: * @pad: the #GstPad to add the probe to @@ -1168,8 +1309,10 @@ gst_pad_is_active (GstPad * pad) * Be notified of different states of pads. The provided callback is called for * every state that matches @mask. * - * Returns: an id or 0 on error. The id can be used to remove the probe with - * gst_pad_remove_probe(). + * Returns: an id or 0 if no probe is pending. The id can be used to remove the + * probe with gst_pad_remove_probe(). When using GST_PAD_PROBE_TYPE_IDLE it can + * happen that the probe can be run immediately and if the probe returns + * GST_PAD_PROBE_REMOVE this functions returns 0. * * MT safe. */ @@ -1224,6 +1367,9 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask, GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_BLOCKED); GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "added blocking probe, " "now %d blocking probes", pad->num_blocked); + + /* Might have new probes now that want to be called */ + GST_PAD_BLOCK_BROADCAST (pad); } /* call the callback if we need to be called for idle callbacks */ @@ -1237,13 +1383,42 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask, GST_OBJECT_UNLOCK (pad); } else { GstPadProbeInfo info = { GST_PAD_PROBE_TYPE_IDLE, res, }; + GstPadProbeReturn ret; + + /* Keep another ref, the callback could destroy the pad */ + gst_object_ref (pad); /* the pad is idle now, we can signal the idle callback now */ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad is idle, trigger idle callback"); GST_OBJECT_UNLOCK (pad); - callback (pad, &info, user_data); + ret = callback (pad, &info, user_data); + + GST_OBJECT_LOCK (pad); + switch (ret) { + case GST_PAD_PROBE_REMOVE: + /* remove the probe */ + GST_DEBUG_OBJECT (pad, "asked to remove hook"); + cleanup_hook (pad, hook); + res = 0; + break; + case GST_PAD_PROBE_DROP: + GST_DEBUG_OBJECT (pad, "asked to drop item"); + break; + case GST_PAD_PROBE_PASS: + GST_DEBUG_OBJECT (pad, "asked to pass item"); + break; + case GST_PAD_PROBE_OK: + GST_DEBUG_OBJECT (pad, "probe returned OK"); + break; + default: + GST_DEBUG_OBJECT (pad, "probe returned %d", ret); + break; + } + GST_OBJECT_UNLOCK (pad); + + gst_object_unref (pad); } } else { GST_OBJECT_UNLOCK (pad); @@ -1251,31 +1426,6 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask, return res; } -static void -cleanup_hook (GstPad * pad, GHook * hook) -{ - GstPadProbeType type; - - if (!G_HOOK_IS_VALID (hook)) - return; - - type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT; - - if (type & GST_PAD_PROBE_TYPE_BLOCKING) { - /* unblock when we remove the last blocking probe */ - pad->num_blocked--; - GST_DEBUG_OBJECT (pad, "remove blocking probe, now %d left", - pad->num_blocked); - if (pad->num_blocked == 0) { - GST_DEBUG_OBJECT (pad, "last blocking probe removed, unblocking"); - GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKED); - GST_PAD_BLOCK_BROADCAST (pad); - } - } - g_hook_destroy_link (&pad->probes, hook); - pad->num_probes--; -} - /** * gst_pad_remove_probe: * @pad: the #GstPad with the probe @@ -1321,7 +1471,7 @@ not_found: * last requested state of the pad. It is not certain that the pad * is actually blocking at this point (see gst_pad_is_blocking()). * - * Returns: TRUE if the pad is blocked. + * Returns: %TRUE if the pad is blocked. * * MT safe. */ @@ -1346,7 +1496,7 @@ gst_pad_is_blocked (GstPad * pad) * Checks if the pad is blocking or not. This is a guaranteed state * of whether the pad is actually blocking on a #GstBuffer or a #GstEvent. * - * Returns: TRUE if the pad is blocking. + * Returns: %TRUE if the pad is blocking. * * MT safe. */ @@ -1407,9 +1557,10 @@ gst_pad_check_reconfigure (GstPad * pad) GST_OBJECT_LOCK (pad); reconfigure = GST_PAD_NEEDS_RECONFIGURE (pad); - if (reconfigure) + if (reconfigure) { GST_DEBUG_OBJECT (pad, "remove RECONFIGURE flag"); - GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE); + GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE); + } GST_OBJECT_UNLOCK (pad); return reconfigure; @@ -1437,7 +1588,7 @@ gst_pad_mark_reconfigure (GstPad * pad) * @p: a #GstPad. * @f: the #GstPadActivateFunction to set. * - * Calls gst_pad_set_activate_function_full() with NULL for the user_data and + * Calls gst_pad_set_activate_function_full() with %NULL for the user_data and * notify. */ /** @@ -1474,7 +1625,7 @@ gst_pad_set_activate_function_full (GstPad * pad, * @p: a #GstPad. * @f: the #GstPadActivateModeFunction to set. * - * Calls gst_pad_set_activatemode_function_full() with NULL for the user_data and + * Calls gst_pad_set_activatemode_function_full() with %NULL for the user_data and * notify. */ /** @@ -1509,7 +1660,7 @@ gst_pad_set_activatemode_function_full (GstPad * pad, * @p: a sink #GstPad. * @f: the #GstPadChainFunction to set. * - * Calls gst_pad_set_chain_function_full() with NULL for the user_data and + * Calls gst_pad_set_chain_function_full() with %NULL for the user_data and * notify. */ /** @@ -1544,7 +1695,7 @@ gst_pad_set_chain_function_full (GstPad * pad, GstPadChainFunction chain, * @p: a sink #GstPad. * @f: the #GstPadChainListFunction to set. * - * Calls gst_pad_set_chain_list_function_full() with NULL for the user_data and + * Calls gst_pad_set_chain_list_function_full() with %NULL for the user_data and * notify. */ /** @@ -1581,7 +1732,7 @@ gst_pad_set_chain_list_function_full (GstPad * pad, * @p: a source #GstPad. * @f: the #GstPadGetRangeFunction to set. * - * Calls gst_pad_set_getrange_function_full() with NULL for the user_data and + * Calls gst_pad_set_getrange_function_full() with %NULL for the user_data and * notify. */ /** @@ -1617,7 +1768,7 @@ gst_pad_set_getrange_function_full (GstPad * pad, GstPadGetRangeFunction get, * @p: a #GstPad of either direction. * @f: the #GstPadEventFunction to set. * - * Calls gst_pad_set_event_function_full() with NULL for the user_data and + * Calls gst_pad_set_event_function_full() with %NULL for the user_data and * notify. */ /** @@ -1650,7 +1801,7 @@ gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event, * @p: a #GstPad of either direction. * @f: the #GstPadQueryFunction to set. * - * Calls gst_pad_set_query_function_full() with NULL for the user_data and + * Calls gst_pad_set_query_function_full() with %NULL for the user_data and * notify. */ /** @@ -1683,7 +1834,7 @@ gst_pad_set_query_function_full (GstPad * pad, GstPadQueryFunction query, * @p: a #GstPad of either direction. * @f: the #GstPadIterIntLinkFunction to set. * - * Calls gst_pad_set_iterate_internal_links_function_full() with NULL + * Calls gst_pad_set_iterate_internal_links_function_full() with %NULL * for the user_data and notify. */ /** @@ -1717,7 +1868,7 @@ gst_pad_set_iterate_internal_links_function_full (GstPad * pad, * @p: a #GstPad. * @f: the #GstPadLinkFunction to set. * - * Calls gst_pad_set_link_function_full() with NULL + * Calls gst_pad_set_link_function_full() with %NULL * for the user_data and notify. */ /** @@ -1760,7 +1911,7 @@ gst_pad_set_link_function_full (GstPad * pad, GstPadLinkFunction link, * @p: a #GstPad. * @f: the #GstPadUnlinkFunction to set. * - * Calls gst_pad_set_unlink_function_full() with NULL + * Calls gst_pad_set_unlink_function_full() with %NULL * for the user_data and notify. */ /** @@ -1797,7 +1948,7 @@ gst_pad_set_unlink_function_full (GstPad * pad, GstPadUnlinkFunction unlink, * Unlinks the source pad from the sink pad. Will emit the #GstPad::unlinked * signal on both pads. * - * Returns: TRUE if the pads were unlinked. This function returns FALSE if + * Returns: %TRUE if the pads were unlinked. This function returns %FALSE if * the pads were not linked together. * * MT safe. @@ -1899,7 +2050,7 @@ not_linked_together: * * Checks if a @pad is linked to another pad or not. * - * Returns: TRUE if the pad is linked, FALSE otherwise. + * Returns: %TRUE if the pad is linked, %FALSE otherwise. * * MT safe. */ @@ -1963,7 +2114,7 @@ gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink, sinkcaps); /* if we have caps on both pads we can check the intersection. If one - * of the caps is NULL, we return TRUE. */ + * of the caps is %NULL, we return %TRUE. */ if (G_UNLIKELY (srccaps == NULL || sinkcaps == NULL)) { if (srccaps) gst_caps_unref (srccaps); @@ -1978,7 +2129,7 @@ gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink, done: GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible", - (compatible ? "" : "not")); + (compatible ? "" : "not ")); return compatible; } @@ -2129,7 +2280,7 @@ no_format: * Checks if the source pad and the sink pad are compatible so they can be * linked. * - * Returns: TRUE if the pads can be linked. + * Returns: %TRUE if the pads can be linked. */ gboolean gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad) @@ -2204,8 +2355,12 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags) /* prepare will also lock the two pads */ result = gst_pad_link_prepare (srcpad, sinkpad, flags); - if (G_UNLIKELY (result != GST_PAD_LINK_OK)) + if (G_UNLIKELY (result != GST_PAD_LINK_OK)) { + GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), + gst_pad_link_get_name (result)); goto done; + } /* must set peers before calling the link function */ GST_PAD_PEER (srcpad) = sinkpad; @@ -2288,8 +2443,9 @@ concurrent_link: } link_failed: { - GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); + GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), + gst_pad_link_get_name (result)); GST_PAD_PEER (srcpad) = NULL; GST_PAD_PEER (sinkpad) = NULL; @@ -2341,9 +2497,9 @@ gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ) * * Gets the template for @pad. * - * Returns: (transfer full): the #GstPadTemplate from which this pad was - * instantiated, or %NULL if this pad has no template. Unref after - * usage. + * Returns: (transfer full) (nullable): the #GstPadTemplate from which + * this pad was instantiated, or %NULL if this pad has no + * template. Unref after usage. */ GstPadTemplate * gst_pad_get_pad_template (GstPad * pad) @@ -2363,7 +2519,7 @@ gst_pad_get_pad_template (GstPad * pad) * * Check if @pad has caps set on it with a #GST_EVENT_CAPS event. * - * Returns: TRUE when @pad has caps associated with it. + * Returns: %TRUE when @pad has caps associated with it. */ gboolean gst_pad_has_current_caps (GstPad * pad) @@ -2467,9 +2623,9 @@ gst_pad_get_peer (GstPad * pad) * calling gst_pad_query_caps() on @pad and its peer. The caller owns a reference * on the resulting caps. * - * Returns: (transfer full): the allowed #GstCaps of the pad link. Unref the - * caps when you no longer need it. This function returns NULL when @pad - * has no peer. + * Returns: (transfer full) (nullable): the allowed #GstCaps of the + * pad link. Unref the caps when you no longer need it. This + * function returns %NULL when @pad has no peer. * * MT safe. */ @@ -2478,7 +2634,6 @@ gst_pad_get_allowed_caps (GstPad * pad) { GstCaps *mycaps; GstCaps *caps; - GstCaps *peercaps; GstPad *peer; g_return_val_if_fail (GST_IS_PAD (pad), NULL); @@ -2494,11 +2649,9 @@ gst_pad_get_allowed_caps (GstPad * pad) GST_OBJECT_UNLOCK (pad); mycaps = gst_pad_query_caps (pad, NULL); - peercaps = gst_pad_query_caps (peer, NULL); + caps = gst_pad_query_caps (peer, mycaps); gst_object_unref (peer); - caps = gst_caps_intersect (mycaps, peercaps); - gst_caps_unref (peercaps); gst_caps_unref (mycaps); GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT, @@ -2518,7 +2671,7 @@ no_peer: /** * gst_pad_iterate_internal_links_default: * @pad: the #GstPad to get the internal links of. - * @parent: the parent of @pad or NULL + * @parent: (allow-none): the parent of @pad or %NULL * * Iterate the list of pads to which the given pad is linked to inside of * the parent element. @@ -2527,8 +2680,8 @@ no_peer: * * The caller must free this iterator after use with gst_iterator_free(). * - * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each - * returned pad with gst_object_unref(). + * Returns: (nullable): a #GstIterator of #GstPad, or %NULL if @pad + * has no parent. Unref each returned pad with gst_object_unref(). */ GstIterator * gst_pad_iterate_internal_links_default (GstPad * pad, GstObject * parent) @@ -2593,9 +2746,9 @@ no_parent: * * Free-function: gst_iterator_free * - * Returns: (transfer full): a new #GstIterator of #GstPad or %NULL when the - * pad does not have an iterator function configured. Use - * gst_iterator_free() after usage. + * Returns: (transfer full) (nullable): a new #GstIterator of #GstPad + * or %NULL when the pad does not have an iterator function + * configured. Use gst_iterator_free() after usage. */ GstIterator * gst_pad_iterate_internal_links (GstPad * pad) @@ -2621,7 +2774,7 @@ no_parent: { GST_DEBUG_OBJECT (pad, "no parent"); GST_OBJECT_UNLOCK (pad); - return FALSE; + return NULL; } } @@ -2635,9 +2788,9 @@ no_parent: * dynamically changing internal pads and will make sure that the @forward * function is only called once for each pad. * - * When @forward returns TRUE, no further pads will be processed. + * When @forward returns %TRUE, no further pads will be processed. * - * Returns: TRUE if one of the dispatcher functions returned TRUE. + * Returns: %TRUE if one of the dispatcher functions returned %TRUE. */ gboolean gst_pad_forward (GstPad * pad, GstPadForwardFunction forward, @@ -2727,7 +2880,7 @@ event_forward_func (GstPad * pad, EventData * data) /** * gst_pad_event_default: * @pad: a #GstPad to call the default event handler on. - * @parent: the parent of @pad or NULL + * @parent: (allow-none): the parent of @pad or %NULL * @event: (transfer full): the #GstEvent to handle. * * Invokes the default event handler for the given pad. @@ -2735,10 +2888,10 @@ event_forward_func (GstPad * pad, EventData * data) * The EOS event will pause the task associated with @pad before it is forwarded * to all internally linked pads, * - * The the event is sent to all pads internally linked to @pad. This function + * The event is sent to all pads internally linked to @pad. This function * takes ownership of @event. * - * Returns: TRUE if the event was sent successfully. + * Returns: %TRUE if the event was sent successfully. */ gboolean gst_pad_event_default (GstPad * pad, GstObject * parent, GstEvent * event) @@ -2804,15 +2957,23 @@ gst_pad_query_accept_caps_default (GstPad * pad, GstQuery * query) GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pad, "fallback ACCEPT_CAPS query, consider implementing a specialized version"); - allowed = gst_pad_query_caps (pad, NULL); gst_query_parse_accept_caps (query, &caps); + allowed = gst_pad_query_caps (pad, caps); if (allowed) { - GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed); - result = gst_caps_is_subset (caps, allowed); + if (GST_PAD_IS_ACCEPT_INTERSECT (pad)) { + GST_DEBUG_OBJECT (pad, + "allowed caps intersect %" GST_PTR_FORMAT ", caps %" GST_PTR_FORMAT, + allowed, caps); + result = gst_caps_can_intersect (caps, allowed); + } else { + GST_DEBUG_OBJECT (pad, "allowed caps subset %" GST_PTR_FORMAT ", caps %" + GST_PTR_FORMAT, allowed, caps); + result = gst_caps_is_subset (caps, allowed); + } gst_caps_unref (allowed); } else { - GST_DEBUG_OBJECT (pad, "no caps allowed on the pad"); + GST_DEBUG_OBJECT (pad, "no compatible caps allowed on the pad"); result = FALSE; } gst_query_set_accept_caps_result (query, result); @@ -2893,6 +3054,123 @@ done: return TRUE; } +/* Default latency implementation */ +typedef struct +{ + gboolean live; + GstClockTime min, max; +} LatencyFoldData; + +static gboolean +query_latency_default_fold (const GValue * item, GValue * ret, + gpointer user_data) +{ + GstPad *pad = g_value_get_object (item), *peer; + LatencyFoldData *fold_data = user_data; + GstQuery *query; + gboolean res = FALSE; + + query = gst_query_new_latency (); + + peer = gst_pad_get_peer (pad); + if (peer) { + res = gst_pad_peer_query (pad, query); + } else { + GST_LOG_OBJECT (pad, "No peer pad found, ignoring this pad"); + } + + if (res) { + gboolean live; + GstClockTime min, max; + + gst_query_parse_latency (query, &live, &min, &max); + + GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT + " max:%" G_GINT64_FORMAT, live ? "true" : "false", min, max); + + if (live) { + if (min > fold_data->min) + fold_data->min = min; + + if (fold_data->max == GST_CLOCK_TIME_NONE) + fold_data->max = max; + else if (max < fold_data->max) + fold_data->max = max; + + fold_data->live = TRUE; + } + } else if (peer) { + GST_DEBUG_OBJECT (pad, "latency query failed"); + g_value_set_boolean (ret, FALSE); + } + + gst_query_unref (query); + if (peer) + gst_object_unref (peer); + + return TRUE; +} + +static gboolean +gst_pad_query_latency_default (GstPad * pad, GstQuery * query) +{ + GstIterator *it; + GstIteratorResult res; + GValue ret = G_VALUE_INIT; + gboolean query_ret; + LatencyFoldData fold_data; + + it = gst_pad_iterate_internal_links (pad); + if (!it) { + GST_DEBUG_OBJECT (pad, "Can't iterate internal links"); + return FALSE; + } + + g_value_init (&ret, G_TYPE_BOOLEAN); + +retry: + fold_data.live = FALSE; + fold_data.min = 0; + fold_data.max = GST_CLOCK_TIME_NONE; + + g_value_set_boolean (&ret, TRUE); + res = gst_iterator_fold (it, query_latency_default_fold, &ret, &fold_data); + switch (res) { + case GST_ITERATOR_OK: + g_assert_not_reached (); + break; + case GST_ITERATOR_DONE: + break; + case GST_ITERATOR_ERROR: + g_value_set_boolean (&ret, FALSE); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (it); + goto retry; + default: + g_assert_not_reached (); + break; + } + gst_iterator_free (it); + + query_ret = g_value_get_boolean (&ret); + if (query_ret) { + GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT + " max:%" G_GINT64_FORMAT, fold_data.live ? "true" : "false", + fold_data.min, fold_data.max); + + if (fold_data.min > fold_data.max) { + GST_ERROR_OBJECT (pad, "minimum latency bigger than maximum latency"); + } + + gst_query_set_latency (query, fold_data.live, fold_data.min, fold_data.max); + } else { + GST_LOG_OBJECT (pad, "latency query failed"); + } + + return query_ret; +} + typedef struct { GstQuery *query; @@ -2917,7 +3195,7 @@ query_forward_func (GstPad * pad, QueryData * data) /** * gst_pad_query_default: * @pad: a #GstPad to call the default query handler on. - * @parent: the parent of @pad or NULL + * @parent: (allow-none): the parent of @pad or %NULL * @query: (transfer none): the #GstQuery to handle. * * Invokes the default query handler for the given pad. @@ -2926,7 +3204,7 @@ query_forward_func (GstPad * pad, QueryData * data) * @pad, only one will be sent the query. * Multi-sinkpad elements should implement custom query handlers. * - * Returns: TRUE if the query was performed successfully. + * Returns: %TRUE if the query was performed successfully. */ gboolean gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query) @@ -2948,10 +3226,13 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query) ret = gst_pad_query_caps_default (pad, query); forward = FALSE; break; + case GST_QUERY_LATENCY: + ret = gst_pad_query_latency_default (pad, query); + forward = FALSE; + break; case GST_QUERY_POSITION: case GST_QUERY_SEEKING: case GST_QUERY_FORMATS: - case GST_QUERY_LATENCY: case GST_QUERY_JITTER: case GST_QUERY_RATE: case GST_QUERY_CONVERT: @@ -2993,6 +3274,7 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) GstPadProbeType type, flags; GstPadProbeCallback callback; GstPadProbeReturn ret; + gpointer original_data; /* if we have called this callback, do nothing */ if (PROBE_COOKIE (hook) == data->cookie) { @@ -3006,9 +3288,11 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT; type = info->type; + original_data = info->data; - /* one of the data types */ - if ((flags & GST_PAD_PROBE_TYPE_ALL_BOTH & type) == 0) + /* one of the data types for non-idle probes */ + if ((type & GST_PAD_PROBE_TYPE_IDLE) == 0 + && (flags & GST_PAD_PROBE_TYPE_ALL_BOTH & type) == 0) goto no_match; /* one of the scheduling types */ if ((flags & GST_PAD_PROBE_TYPE_SCHEDULING & type) == 0) @@ -3017,6 +3301,9 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) if ((type & GST_PAD_PROBE_TYPE_BLOCKING) && (flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0) goto no_match; + if ((type & GST_PAD_PROBE_TYPE_BLOCKING) == 0 && + (flags & GST_PAD_PROBE_TYPE_BLOCKING)) + goto no_match; /* only probes that have GST_PAD_PROBE_TYPE_EVENT_FLUSH set */ if ((type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) && (flags & GST_PAD_PROBE_TYPE_EVENT_FLUSH & type) == 0) @@ -3040,6 +3327,12 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) GST_OBJECT_LOCK (pad); + if (original_data != NULL && info->data == NULL) { + GST_DEBUG_OBJECT (pad, "data item in pad probe info was dropped"); + info->type = GST_PAD_PROBE_TYPE_INVALID; + data->dropped = TRUE; + } + switch (ret) { case GST_PAD_PROBE_REMOVE: /* remove the probe */ @@ -3058,6 +3351,9 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) GST_DEBUG_OBJECT (pad, "asked to pass item"); data->pass = TRUE; break; + case GST_PAD_PROBE_OK: + GST_DEBUG_OBJECT (pad, "probe returned OK"); + break; default: GST_DEBUG_OBJECT (pad, "probe returned %d", ret); break; @@ -3077,10 +3373,11 @@ no_match: #define PROBE_NO_DATA(pad,mask,label,defaultval) \ G_STMT_START { \ if (G_UNLIKELY (pad->num_probes)) { \ + GstFlowReturn pval = defaultval; \ /* pass NULL as the data item */ \ GstPadProbeInfo info = { mask, 0, NULL, 0, 0 }; \ ret = do_probe_callbacks (pad, &info, defaultval); \ - if (G_UNLIKELY (ret != defaultval && ret != GST_FLOW_OK)) \ + if (G_UNLIKELY (ret != pval && ret != GST_FLOW_OK)) \ goto label; \ } \ } G_STMT_END @@ -3171,6 +3468,14 @@ again: GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKING); GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "We got unblocked"); + /* if the list changed, call the new callbacks (they will not have their + * cookie set to data.cookie */ + if (cookie != pad->priv->probe_list_cookie) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "probe list changed, restarting"); + goto again; + } + if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) goto flushing; } @@ -3222,6 +3527,13 @@ gst_pad_get_offset (GstPad * pad) return result; } +static gboolean +mark_event_not_received (GstPad * pad, PadEvent * ev, gpointer user_data) +{ + ev->received = FALSE; + return TRUE; +} + /** * gst_pad_set_offset: * @pad: a #GstPad @@ -3232,8 +3544,6 @@ gst_pad_get_offset (GstPad * pad) void gst_pad_set_offset (GstPad * pad, gint64 offset) { - PadEvent *ev; - g_return_if_fail (GST_IS_PAD (pad)); GST_OBJECT_LOCK (pad); @@ -3244,16 +3554,9 @@ gst_pad_set_offset (GstPad * pad, gint64 offset) pad->offset = offset; GST_DEBUG_OBJECT (pad, "changed offset to %" G_GINT64_FORMAT, offset); - /* sinkpads will apply their offset the next time a segment - * event is received. */ - if (GST_PAD_IS_SINK (pad)) - goto done; - - /* resend the last segment event on next buffer push */ - if ((ev = find_event_by_type (pad, GST_EVENT_SEGMENT, 0))) { - ev->received = FALSE; - GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); - } + /* resend all sticky events with updated offset on next buffer push */ + events_foreach (pad, mark_event_not_received, NULL); + GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); done: GST_OBJECT_UNLOCK (pad); @@ -3267,6 +3570,12 @@ typedef struct * that pushing the EOS event failed */ gboolean was_eos; + + /* If called for an event this is + * the event that would be pushed + * next. Don't forward sticky events + * that would come after that */ + GstEvent *event; } PushStickyData; /* should be called with pad LOCK */ @@ -3282,8 +3591,17 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data) return TRUE; } - data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event), - GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM); + /* If we're called because of an sticky event, only forward + * events that would come before this new event and the + * event itself */ + if (data->event && GST_EVENT_IS_STICKY (data->event) && + GST_EVENT_TYPE (data->event) <= GST_EVENT_SEGMENT && + GST_EVENT_TYPE (data->event) < GST_EVENT_TYPE (event)) { + data->ret = GST_FLOW_CUSTOM_SUCCESS_1; + } else { + data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event), + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM); + } switch (data->ret) { case GST_FLOW_OK: @@ -3298,6 +3616,13 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data) GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); data->ret = GST_FLOW_OK; break; + case GST_FLOW_CUSTOM_SUCCESS_1: + /* event was ignored and should be sent later */ + GST_DEBUG_OBJECT (pad, "event %s was ignored, mark pending", + GST_EVENT_TYPE_NAME (event)); + GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); + data->ret = GST_FLOW_OK; + break; case GST_FLOW_NOT_LINKED: /* not linked is not a problem, we are sticky so the event will be * sent later but only for non-EOS events */ @@ -3322,9 +3647,9 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data) /* check sticky events and push them when needed. should be called * with pad LOCK */ static inline GstFlowReturn -check_sticky (GstPad * pad) +check_sticky (GstPad * pad, GstEvent * event) { - PushStickyData data = { GST_FLOW_OK, FALSE }; + PushStickyData data = { GST_FLOW_OK, FALSE, event }; if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) { GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); @@ -3372,7 +3697,7 @@ check_sticky (GstPad * pad) * * Please also note that some queries might need a running pipeline to work. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_query (GstPad * pad, GstQuery * query) @@ -3477,11 +3802,8 @@ probe_stopped: GST_PAD_STREAM_UNLOCK (pad); /* if a probe dropped, we don't sent it further but assume that the probe - * answered the query and return TRUE */ - if (ret == GST_FLOW_CUSTOM_SUCCESS) - res = TRUE; - else - res = FALSE; + * did not answer the query and return FALSE */ + res = FALSE; return res; } @@ -3497,7 +3819,7 @@ probe_stopped: * The caller is responsible for both the allocation and deallocation of * the query structure. * - * Returns: TRUE if the query could be performed. This function returns %FALSE + * Returns: %TRUE if the query could be performed. This function returns %FALSE * if @pad has no peer. */ gboolean @@ -3531,7 +3853,7 @@ gst_pad_peer_query (GstPad * pad, GstQuery * query) if (GST_PAD_IS_SRC (pad) && serialized) { /* all serialized queries on the srcpad trigger push of * sticky events */ - if (!check_sticky (pad) == GST_FLOW_OK) + if (check_sticky (pad, NULL) != GST_FLOW_OK) goto sticky_failed; } @@ -3579,7 +3901,7 @@ sticky_failed: } no_peer: { - GST_WARNING_OBJECT (pad, "pad has no peer"); + GST_INFO_OBJECT (pad, "pad has no peer"); GST_OBJECT_UNLOCK (pad); return FALSE; } @@ -3594,11 +3916,8 @@ probe_stopped: GST_OBJECT_UNLOCK (pad); /* if a probe dropped, we don't sent it further but assume that the probe - * answered the query and return TRUE */ - if (ret == GST_FLOW_CUSTOM_SUCCESS) - res = TRUE; - else - res = FALSE; + * did not answer the query and return FALSE */ + res = FALSE; return res; } @@ -3629,6 +3948,22 @@ gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data) if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH)) goto wrong_mode; +#ifndef G_DISABLE_ASSERT + if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) { + if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) { + g_warning (G_STRLOC + ":%s:<%s:%s> Got data flow before stream-start event", + G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); + } + if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) { + g_warning (G_STRLOC + ":%s:<%s:%s> Got data flow before segment event", + G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); + } + pad->priv->last_cookie = pad->priv->events_cookie; + } +#endif + PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped); PROBE_PUSH (pad, type, data, probe_stopped); @@ -3716,7 +4051,7 @@ probe_stopped: ret = GST_FLOW_OK; break; default: - GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret)); + GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret)); break; } return ret; @@ -3742,7 +4077,7 @@ no_function: * The function returns #GST_FLOW_FLUSHING if the pad was flushing. * * If the buffer type is not acceptable for @pad (as negotiated with a - * preceeding GST_EVENT_CAPS event), this function returns + * preceding GST_EVENT_CAPS event), this function returns * #GST_FLOW_NOT_NEGOTIATED. * * The function proceeds calling the chain function installed on @pad (see @@ -3776,7 +4111,7 @@ gst_pad_chain_list_default (GstPad * pad, GstObject * parent, GstBuffer *buffer; GstFlowReturn ret; - GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer"); + GST_INFO_OBJECT (pad, "chaining each buffer in list individually"); len = gst_buffer_list_length (list); @@ -3847,14 +4182,30 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data) if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH)) goto wrong_mode; - if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK) +#ifndef G_DISABLE_ASSERT + if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) { + if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) { + g_warning (G_STRLOC + ":%s:<%s:%s> Got data flow before stream-start event", + G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); + } + if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) { + g_warning (G_STRLOC + ":%s:<%s:%s> Got data flow before segment event", + G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); + } + pad->priv->last_cookie = pad->priv->events_cookie; + } +#endif + + if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK) goto events_error; /* do block probes */ PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped); /* recheck sticky events because the probe might have cause a relink */ - if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK) + if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK) goto events_error; /* do post-blocking probes */ @@ -3873,6 +4224,7 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data) gst_object_unref (peer); GST_OBJECT_LOCK (pad); + pad->ABI.abi.last_flowret = ret; pad->priv->using--; if (pad->priv->using == 0) { /* pad is not active anymore, trigger idle callbacks */ @@ -3889,6 +4241,7 @@ flushing: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was flushing"); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; GST_OBJECT_UNLOCK (pad); gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); return GST_FLOW_FLUSHING; @@ -3896,6 +4249,7 @@ flushing: eos: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but pad was EOS"); + pad->ABI.abi.last_flowret = GST_FLOW_EOS; GST_OBJECT_UNLOCK (pad); gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); return GST_FLOW_EOS; @@ -3904,6 +4258,7 @@ wrong_mode: { g_critical ("pushing on pad %s:%s but it was not activated in push mode", GST_DEBUG_PAD_NAME (pad)); + pad->ABI.abi.last_flowret = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (pad); gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); return GST_FLOW_ERROR; @@ -3912,6 +4267,7 @@ events_error: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "error pushing events, return %s", gst_flow_get_name (ret)); + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); return ret; @@ -3919,7 +4275,10 @@ events_error: probe_stopped: { GST_OBJECT_UNLOCK (pad); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); + pad->ABI.abi.last_flowret = + ret == GST_FLOW_CUSTOM_SUCCESS ? GST_FLOW_OK : ret; + if (data != NULL) + gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); switch (ret) { case GST_FLOW_CUSTOM_SUCCESS: @@ -3927,7 +4286,7 @@ probe_stopped: ret = GST_FLOW_OK; break; default: - GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret)); + GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret)); break; } return ret; @@ -3936,6 +4295,7 @@ not_linked: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but it was not linked"); + pad->ABI.abi.last_flowret = GST_FLOW_NOT_LINKED; GST_OBJECT_UNLOCK (pad); gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); return GST_FLOW_NOT_LINKED; @@ -4028,7 +4388,7 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size, if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PULL)) goto wrong_mode; - if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK) + if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK) goto events_error; res_buf = *buffer; @@ -4040,7 +4400,7 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size, res_buf, offset, size, probe_stopped); /* recheck sticky events because the probe might have cause a relink */ - if (G_UNLIKELY ((ret = check_sticky (pad))) != GST_FLOW_OK) + if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK) goto events_error; ACQUIRE_PARENT (pad, parent, no_parent); @@ -4058,14 +4418,15 @@ gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size, RELEASE_PARENT (parent); + GST_OBJECT_LOCK (pad); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto get_range_failed; /* can only fire the signal if we have a valid buffer */ - GST_OBJECT_LOCK (pad); probed_data: PROBE_PULL (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_BUFFER, res_buf, offset, size, probe_stopped_unref); + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); @@ -4079,6 +4440,7 @@ flushing: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "getrange, but pad was flushing"); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); return GST_FLOW_FLUSHING; @@ -4087,6 +4449,7 @@ wrong_mode: { g_critical ("getrange on pad %s:%s but it was not activated in pull mode", GST_DEBUG_PAD_NAME (pad)); + pad->ABI.abi.last_flowret = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); return GST_FLOW_ERROR; @@ -4094,6 +4457,7 @@ wrong_mode: events_error: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "error pushing events"); + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); return ret; @@ -4101,6 +4465,7 @@ events_error: no_parent: { GST_DEBUG_OBJECT (pad, "no parent"); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); return GST_FLOW_FLUSHING; @@ -4130,6 +4495,7 @@ probe_stopped: ret = GST_FLOW_EOS; } } + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); @@ -4142,6 +4508,7 @@ probe_stopped_unref: /* if we drop here, it signals EOS */ if (ret == GST_FLOW_CUSTOM_SUCCESS) ret = GST_FLOW_EOS; + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); if (*buffer == NULL) @@ -4150,6 +4517,8 @@ probe_stopped_unref: } get_range_failed: { + pad->ABI.abi.last_flowret = ret; + GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_UNLOCK (pad); GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING, (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING, @@ -4174,7 +4543,7 @@ get_range_failed: * installed (see gst_pad_set_getrange_function()) this function returns * #GST_FLOW_NOT_SUPPORTED. * - * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be + * If @buffer points to a variable holding %NULL, a valid new #GstBuffer will be * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer * must be freed with gst_buffer_unref() after usage. * @@ -4191,7 +4560,7 @@ get_range_failed: * When this function returns any other result value than #GST_FLOW_OK, @buffer * will be unchanged. * - * This is a lowlevel function. Usualy gst_pad_pull_range() is used. + * This is a lowlevel function. Usually gst_pad_pull_range() is used. * * Returns: a #GstFlowReturn from the pad. * @@ -4204,8 +4573,8 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size, g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR); g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR); g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (*buffer == NULL - || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR); + g_return_val_if_fail (*buffer == NULL || (GST_IS_BUFFER (*buffer) + && gst_buffer_get_size (*buffer) >= size), GST_FLOW_ERROR); return gst_pad_get_range_unchecked (pad, offset, size, buffer); } @@ -4228,10 +4597,10 @@ gst_pad_get_range (GstPad * pad, guint64 offset, guint size, * See gst_pad_get_range() for a list of return values and for the * semantics of the arguments of this function. * - * If @buffer points to a variable holding NULL, a valid new #GstBuffer will be + * If @buffer points to a variable holding %NULL, a valid new #GstBuffer will be * placed in @buffer when this function returns #GST_FLOW_OK. The new buffer * must be freed with gst_buffer_unref() after usage. When this function - * returns any other result value, @buffer will still point to NULL. + * returns any other result value, @buffer will still point to %NULL. * * When @buffer points to a variable that points to a valid #GstBuffer, the * buffer will be filled with the result data when this function returns @@ -4259,8 +4628,8 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR); g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR); g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (*buffer == NULL - || GST_IS_BUFFER (*buffer), GST_FLOW_ERROR); + g_return_val_if_fail (*buffer == NULL || (GST_IS_BUFFER (*buffer) + && gst_buffer_get_size (*buffer) >= size), GST_FLOW_ERROR); GST_OBJECT_LOCK (pad); if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) @@ -4290,6 +4659,7 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, GST_OBJECT_LOCK (pad); pad->priv->using--; + pad->ABI.abi.last_flowret = ret; if (pad->priv->using == 0) { /* pad is not active anymore, trigger idle callbacks */ PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PULL | GST_PAD_PROBE_TYPE_IDLE, @@ -4314,6 +4684,7 @@ flushing: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pullrange, but pad was flushing"); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; GST_OBJECT_UNLOCK (pad); return GST_FLOW_FLUSHING; } @@ -4321,6 +4692,7 @@ wrong_mode: { g_critical ("pullrange on pad %s:%s but it was not activated in pull mode", GST_DEBUG_PAD_NAME (pad)); + pad->ABI.abi.last_flowret = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (pad); return GST_FLOW_ERROR; } @@ -4341,6 +4713,7 @@ probe_stopped: ret = GST_FLOW_EOS; } } + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); return ret; } @@ -4348,11 +4721,13 @@ not_linked: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pulling range, but it was not linked"); + pad->ABI.abi.last_flowret = GST_FLOW_NOT_LINKED; GST_OBJECT_UNLOCK (pad); return GST_FLOW_NOT_LINKED; } pull_range_failed: { + pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING, (ret >= GST_FLOW_EOS) ? GST_LEVEL_INFO : GST_LEVEL_WARNING, @@ -4363,10 +4738,14 @@ probe_stopped_unref: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "post probe returned %s", gst_flow_get_name (ret)); - GST_OBJECT_UNLOCK (pad); + /* if we drop here, it signals EOS */ if (ret == GST_FLOW_CUSTOM_SUCCESS) ret = GST_FLOW_EOS; + + pad->ABI.abi.last_flowret = ret; + GST_OBJECT_UNLOCK (pad); + if (*buffer == NULL) gst_buffer_unref (res_buf); return ret; @@ -4374,16 +4753,30 @@ probe_stopped_unref: } /* must be called with pad object lock */ -static gboolean -gst_pad_store_sticky_event (GstPad * pad, GstEvent * event) +static GstFlowReturn +store_sticky_event (GstPad * pad, GstEvent * event) { guint i, len; GstEventType type; GArray *events; gboolean res = FALSE; const gchar *name = NULL; + gboolean insert = TRUE; type = GST_EVENT_TYPE (event); + + /* Store all sticky events except SEGMENT/EOS when we're flushing, + * otherwise they can be dropped and nothing would ever resend them. + * Only do that for activated pads though, everything else is a bug! + */ + if (G_UNLIKELY (GST_PAD_MODE (pad) == GST_PAD_MODE_NONE + || (GST_PAD_IS_FLUSHING (pad) && (type == GST_EVENT_SEGMENT + || type == GST_EVENT_EOS)))) + goto flushed; + + if (G_UNLIKELY (GST_PAD_IS_EOS (pad))) + goto eos; + if (type & GST_EVENT_TYPE_STICKY_MULTI) name = gst_structure_get_name (gst_event_get_structure (event)); @@ -4404,14 +4797,30 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event) /* overwrite */ if ((res = gst_event_replace (&ev->event, event))) ev->received = FALSE; + + insert = FALSE; + break; + } + + if (type < GST_EVENT_TYPE (ev->event) || (type != GST_EVENT_TYPE (ev->event) + && GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS)) { + /* STREAM_START, CAPS and SEGMENT must be delivered in this order. By + * storing the sticky ordered we can check that this is respected. */ + if (G_UNLIKELY (GST_EVENT_TYPE (ev->event) <= GST_EVENT_SEGMENT + || GST_EVENT_TYPE (ev->event) == GST_EVENT_EOS)) + g_warning (G_STRLOC + ":%s:<%s:%s> Sticky event misordering, got '%s' before '%s'", + G_STRFUNC, GST_DEBUG_PAD_NAME (pad), + gst_event_type_get_name (GST_EVENT_TYPE (ev->event)), + gst_event_type_get_name (type)); break; } } - if (i == len) { + if (insert) { PadEvent ev; ev.event = gst_event_ref (event); ev.received = FALSE; - g_array_append_val (events, ev); + g_array_insert_val (events, i, ev); res = TRUE; } @@ -4434,7 +4843,64 @@ gst_pad_store_sticky_event (GstPad * pad, GstEvent * event) break; } } - return res; + if (type == GST_EVENT_EOS) { + GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS); + pad->ABI.abi.last_flowret = GST_FLOW_EOS; + } + + return GST_PAD_IS_FLUSHING (pad) ? GST_FLOW_FLUSHING : GST_FLOW_OK; + + /* ERRORS */ +flushed: + { + GST_DEBUG_OBJECT (pad, "pad is flushing"); + return GST_FLOW_FLUSHING; + } +eos: + { + GST_DEBUG_OBJECT (pad, "pad is EOS"); + return GST_FLOW_EOS; + } +} + +/** + * gst_pad_store_sticky_event: + * @pad: a #GstPad + * @event: a #GstEvent + * + * Store the sticky @event on @pad + * + * Returns: #GST_FLOW_OK on success, #GST_FLOW_FLUSHING when the pad + * was flushing or #GST_FLOW_EOS when the pad was EOS. + * + * Since: 1.2 + */ +GstFlowReturn +gst_pad_store_sticky_event (GstPad * pad, GstEvent * event) +{ + GstFlowReturn ret; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + g_return_val_if_fail (GST_IS_EVENT (event), FALSE); + + GST_OBJECT_LOCK (pad); + ret = store_sticky_event (pad, event); + GST_OBJECT_UNLOCK (pad); + + return ret; +} + +static gboolean +sticky_changed (GstPad * pad, PadEvent * ev, gpointer user_data) +{ + PushStickyData *data = user_data; + + /* Forward all sticky events before our current one that are pending */ + if (ev->event != data->event + && GST_EVENT_TYPE (ev->event) < GST_EVENT_TYPE (data->event)) + return push_sticky (pad, ev, data); + + return TRUE; } /* should be called with pad LOCK */ @@ -4446,6 +4912,10 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, GstPad *peerpad; GstEventType event_type; + /* pass the adjusted event on. We need to do this even if + * there is no peer pad because of the probes. */ + event = apply_pad_offset (pad, event, GST_PAD_IS_SINK (pad)); + /* Two checks to be made: * . (un)set the FLUSHING flag for flushing events, * . handle pad blocking */ @@ -4458,12 +4928,17 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH; break; case GST_EVENT_FLUSH_STOP: + if (G_UNLIKELY (!GST_PAD_IS_ACTIVE (pad))) + goto inactive; + GST_PAD_UNSET_FLUSHING (pad); /* Remove sticky EOS events */ GST_LOG_OBJECT (pad, "Removing pending EOS events"); remove_event_by_type (pad, GST_EVENT_EOS); + remove_event_by_type (pad, GST_EVENT_SEGMENT); GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS); + pad->ABI.abi.last_flowret = GST_FLOW_OK; type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH; break; @@ -4478,11 +4953,6 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, */ switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT: - /* pass the adjusted segment event on. We need to do this even if - * there is no peer pad because of the probes. */ - event = apply_pad_offset (pad, event); - break; case GST_EVENT_RECONFIGURE: if (GST_PAD_IS_SINK (pad)) GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE); @@ -4499,6 +4969,18 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, /* send probes after modifying the events above */ PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped); + /* recheck sticky events because the probe might have cause a relink */ + if (GST_PAD_HAS_PENDING_EVENTS (pad) && GST_PAD_IS_SRC (pad) + && (GST_EVENT_IS_SERIALIZED (event) + || GST_EVENT_IS_STICKY (event))) { + PushStickyData data = { GST_FLOW_OK, FALSE, event }; + GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); + + /* Push all sticky events before our current one + * that have changed */ + events_foreach (pad, sticky_changed, &data); + } + /* now check the peer pad */ peerpad = GST_PAD_PEER (pad); if (peerpad == NULL) @@ -4509,14 +4991,15 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, GST_OBJECT_UNLOCK (pad); GST_LOG_OBJECT (pad, "sending event %p (%s) to peerpad %" GST_PTR_FORMAT, - event, GST_EVENT_TYPE_NAME (event), peerpad); + event, gst_event_type_get_name (event_type), peerpad); ret = gst_pad_send_event_unchecked (peerpad, event, type); /* Note: we gave away ownership of the event at this point but we can still * print the old pointer */ - GST_LOG_OBJECT (pad, "sent event %p to peerpad %" GST_PTR_FORMAT ", ret %s", - event, peerpad, gst_flow_get_name (ret)); + GST_LOG_OBJECT (pad, + "sent event %p (%s) to peerpad %" GST_PTR_FORMAT ", ret %s", event, + gst_event_type_get_name (event_type), peerpad, gst_flow_get_name (ret)); gst_object_unref (peerpad); @@ -4536,6 +5019,12 @@ flushed: gst_event_unref (event); return GST_FLOW_FLUSHING; } +inactive: + { + GST_DEBUG_OBJECT (pad, "flush-stop on inactive pad"); + gst_event_unref (event); + return GST_FLOW_FLUSHING; + } probe_stopped: { GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); @@ -4546,14 +5035,15 @@ probe_stopped: GST_DEBUG_OBJECT (pad, "dropped event"); break; default: - GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret)); + GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret)); break; } return ret; } not_linked: { - GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked"); + GST_DEBUG_OBJECT (pad, "Dropping event %s because pad is not linked", + gst_event_type_get_name (GST_EVENT_TYPE (event))); GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); gst_event_unref (event); @@ -4579,10 +5069,10 @@ idle_probe_stopped: * mainly used by elements to send events to their peer * elements. * - * This function takes owership of the provided event so you should + * This function takes ownership of the provided event so you should * gst_event_ref() it if you want to reuse the event after this call. * - * Returns: TRUE if the event was handled. + * Returns: %TRUE if the event was handled. * * MT safe. */ @@ -4613,26 +5103,22 @@ gst_pad_push_event (GstPad * pad, GstEvent * event) serialized = GST_EVENT_IS_SERIALIZED (event); if (sticky) { - /* can't store on flushing pads */ - if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) - goto flushed; - - if (G_UNLIKELY (GST_PAD_IS_EOS (pad))) - goto eos; - /* srcpad sticky events are stored immediately, the received flag is set * to FALSE and will be set to TRUE when we can successfully push the * event to the peer pad */ - if (gst_pad_store_sticky_event (pad, event)) { - GST_DEBUG_OBJECT (pad, "event %s updated", GST_EVENT_TYPE_NAME (event)); + switch (store_sticky_event (pad, event)) { + case GST_FLOW_FLUSHING: + goto flushed; + case GST_FLOW_EOS: + goto eos; + default: + break; } - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) - GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS); } if (GST_PAD_IS_SRC (pad) && (serialized || sticky)) { /* all serialized or sticky events on the srcpad trigger push of * sticky events */ - res = (check_sticky (pad) == GST_FLOW_OK); + res = (check_sticky (pad, event) == GST_FLOW_OK); } if (!sticky) { GstFlowReturn ret; @@ -4726,6 +5212,9 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, GstObject *parent; GST_OBJECT_LOCK (pad); + + event = apply_pad_offset (pad, event, GST_PAD_IS_SRC (pad)); + if (GST_PAD_IS_SINK (pad)) serialized = GST_EVENT_IS_SERIALIZED (event); else @@ -4738,21 +5227,28 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event)); /* can't even accept a flush begin event when flushing */ - if (GST_PAD_IS_FLUSHING (pad)) + if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) goto flushing; GST_PAD_SET_FLUSHING (pad); GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "set flush flag"); break; case GST_EVENT_FLUSH_STOP: - if (G_LIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_NONE)) { - GST_PAD_UNSET_FLUSHING (pad); - GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag"); - } + /* we can't accept flush-stop on inactive pads else the flushing flag + * would be cleared and it would look like the pad can accept data. + * Also, some elements restart a streaming thread in flush-stop which we + * can't allow on inactive pads */ + if (G_UNLIKELY (!GST_PAD_IS_ACTIVE (pad))) + goto inactive; + + GST_PAD_UNSET_FLUSHING (pad); + GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag"); /* Remove pending EOS events */ - GST_LOG_OBJECT (pad, "Removing pending EOS events"); + GST_LOG_OBJECT (pad, "Removing pending EOS and SEGMENT events"); remove_event_by_type (pad, GST_EVENT_EOS); + remove_event_by_type (pad, GST_EVENT_SEGMENT); GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS); + pad->ABI.abi.last_flowret = GST_FLOW_OK; GST_OBJECT_UNLOCK (pad); /* grab stream lock */ @@ -4787,24 +5283,16 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, if (G_UNLIKELY (GST_PAD_IS_EOS (pad))) goto eos; } - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT: - event = apply_pad_offset (pad, event); - break; - default: - break; - } - - /* now do the probe */ - PROBE_PUSH (pad, - type | GST_PAD_PROBE_TYPE_PUSH | - GST_PAD_PROBE_TYPE_BLOCK, event, probe_stopped); - - PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped); break; } + /* now do the probe */ + PROBE_PUSH (pad, + type | GST_PAD_PROBE_TYPE_PUSH | + GST_PAD_PROBE_TYPE_BLOCK, event, probe_stopped); + + PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped); + if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL)) goto no_function; @@ -4838,21 +5326,16 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, if (sticky) { if (ret == GST_FLOW_OK) { GST_OBJECT_LOCK (pad); - /* can't store on flushing pads */ - if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) - goto flushing; - - if (G_UNLIKELY (GST_PAD_IS_EOS (pad))) - goto eos; - /* after the event function accepted the event, we can store the sticky * event on the pad */ - if (gst_pad_store_sticky_event (pad, event)) { - GST_DEBUG_OBJECT (pad, "event %s updated", GST_EVENT_TYPE_NAME (event)); + switch (store_sticky_event (pad, event)) { + case GST_FLOW_FLUSHING: + goto flushing; + case GST_FLOW_EOS: + goto eos; + default: + break; } - if (event_type == GST_EVENT_EOS) - GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_EOS); - GST_OBJECT_UNLOCK (pad); } gst_event_unref (event); @@ -4874,6 +5357,16 @@ flushing: gst_event_unref (event); return GST_FLOW_FLUSHING; } +inactive: + { + GST_OBJECT_UNLOCK (pad); + if (need_unlock) + GST_PAD_STREAM_UNLOCK (pad); + GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad, + "Received flush-stop on inactive pad. Discarding"); + gst_event_unref (event); + return GST_FLOW_FLUSHING; + } eos: { GST_OBJECT_UNLOCK (pad); @@ -4897,7 +5390,7 @@ probe_stopped: ret = GST_FLOW_OK; break; default: - GST_DEBUG_OBJECT (pad, "an error occured %s", gst_flow_get_name (ret)); + GST_DEBUG_OBJECT (pad, "an error occurred %s", gst_flow_get_name (ret)); break; } return ret; @@ -4955,10 +5448,10 @@ precheck_failed: * plugin doesn't need to bother itself with this information; the core handles * all necessary locks and checks. * - * This function takes owership of the provided event so you should + * This function takes ownership of the provided event so you should * gst_event_ref() it if you want to reuse the event after this call. * - * Returns: TRUE if the event was handled. + * Returns: %TRUE if the event was handled. */ gboolean gst_pad_send_event (GstPad * pad, GstEvent * event) @@ -5042,8 +5535,9 @@ gst_pad_get_element_private (GstPad * pad) * Returns a new reference of the sticky event of type @event_type * from the event. * - * Returns: (transfer full): a #GstEvent of type @event_type or NULL when no - * event of @event_type was on @pad. Unref after usage. + * Returns: (transfer full) (nullable): a #GstEvent of type + * @event_type or %NULL when no event of @event_type was on + * @pad. Unref after usage. */ GstEvent * gst_pad_get_sticky_event (GstPad * pad, GstEventType event_type, guint idx) @@ -5073,13 +5567,15 @@ static gboolean foreach_dispatch_function (GstPad * pad, PadEvent * ev, gpointer user_data) { ForeachDispatch *data = user_data; - gboolean ret; + gboolean ret = TRUE; - GST_OBJECT_UNLOCK (pad); + if (ev->event) { + GST_OBJECT_UNLOCK (pad); - ret = data->func (pad, &ev->event, data->user_data); + ret = data->func (pad, &ev->event, data->user_data); - GST_OBJECT_LOCK (pad); + GST_OBJECT_LOCK (pad); + } return ret; } @@ -5236,7 +5732,7 @@ concurrent_stop: * function executed by the task is finished if this function is not * called from the task function. * - * Returns: a TRUE if the task could be paused or FALSE when the pad + * Returns: a %TRUE if the task could be paused or %FALSE when the pad * has no task. */ gboolean @@ -5285,7 +5781,7 @@ no_task: * Regardless of whether the pad has a task, the stream lock is acquired and * released so as to ensure that streaming through this pad has finished. * - * Returns: a TRUE if the task could be stopped or FALSE on error. + * Returns: a %TRUE if the task could be stopped or %FALSE on error. */ gboolean gst_pad_stop_task (GstPad * pad) @@ -5404,3 +5900,23 @@ gst_pad_probe_info_get_buffer_list (GstPadProbeInfo * info) return GST_PAD_PROBE_INFO_BUFFER_LIST (info); } + +/** + * gst_pad_get_last_flow_return: + * @pad: the #GstPad + * + * Gets the #GstFlowReturn return from the last data passed by this pad. + * + * Since: 1.4 + */ +GstFlowReturn +gst_pad_get_last_flow_return (GstPad * pad) +{ + GstFlowReturn ret; + + GST_OBJECT_LOCK (pad); + ret = GST_PAD_LAST_FLOW_RETURN (pad); + GST_OBJECT_UNLOCK (pad); + + return ret; +}