X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstpad.c;h=3e177f26e745c0fdc45abb8612afc775db478717;hb=b89b1802df44829a0c034db5807bc893ad3c7774;hp=89212135be2b12406124982bc20353f6b2209f0f;hpb=2dfa548f3645844082c3db65d96d87255701b3ad;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstpad.c b/gst/gstpad.c index 8921213..3e177f2 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -21,6 +21,7 @@ */ /** * SECTION:gstpad + * @title: GstPad * @short_description: Object contained by elements that allows links to * other elements * @see_also: #GstPadTemplate, #GstElement, #GstEvent, #GstQuery, #GstBuffer @@ -120,8 +121,7 @@ enum /* FILL ME */ }; -#define GST_PAD_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PAD, GstPadPrivate)) +#define _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH (GST_PAD_PROBE_TYPE_ALL_BOTH | GST_PAD_PROBE_TYPE_EVENT_FLUSH) /* 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 @@ -140,21 +140,23 @@ struct _GstPadPrivate gint using; guint probe_list_cookie; - guint probe_cookie; /* counter of how many idle probes are running directly from the add_probe * call. Used to block any data flowing in the pad while the idle callback * Doesn't finish its work */ gint idle_running; + + /* conditional and variable used to ensure pads only get (de)activated + * by a single thread at a time. Protected by the object lock */ + GCond activation_cond; + gboolean in_activation; }; typedef struct { GHook hook; - guint cookie; } GstProbe; -#define PROBE_COOKIE(h) (((GstProbe *)(h))->cookie) #define GST_PAD_IS_RUNNING_IDLE_PROBE(p) \ (((GstPad *)(p))->priv->idle_running > 0) @@ -166,7 +168,11 @@ typedef struct gboolean pass; gboolean handled; gboolean marshalled; - guint cookie; + + GHook **called_probes; + guint n_called_probes; + guint called_probes_size; + gboolean retry; } ProbeMarshall; static void gst_pad_dispose (GObject * object); @@ -186,6 +192,9 @@ static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad, static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, GstPadProbeType type); +static gboolean activate_mode_internal (GstPad * pad, GstObject * parent, + GstPadMode mode, gboolean active); + static guint gst_pad_signals[LAST_SIGNAL] = { 0 }; static GParamSpec *pspec_caps = NULL; @@ -308,7 +317,8 @@ gst_pad_link_get_name (GstPadLinkReturn ret) } #define gst_pad_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init); +G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, + G_ADD_PRIVATE (GstPad) _do_init); static void gst_pad_class_init (GstPadClass * klass) @@ -319,8 +329,6 @@ gst_pad_class_init (GstPadClass * klass) gobject_class = G_OBJECT_CLASS (klass); gstobject_class = GST_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GstPadPrivate)); - gobject_class->dispose = gst_pad_dispose; gobject_class->finalize = gst_pad_finalize; gobject_class->set_property = gst_pad_set_property; @@ -390,7 +398,7 @@ gst_pad_class_init (GstPadClass * klass) static void gst_pad_init (GstPad * pad) { - pad->priv = GST_PAD_GET_PRIVATE (pad); + pad->priv = gst_pad_get_instance_private (pad); GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN; @@ -411,6 +419,8 @@ gst_pad_init (GstPad * pad) pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16); pad->priv->events_cookie = 0; pad->priv->last_cookie = -1; + g_cond_init (&pad->priv->activation_cond); + pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; } @@ -591,7 +601,7 @@ restart: if (G_UNLIKELY (ev->event == NULL)) goto next; - /* take aditional ref, func might release the lock */ + /* take additional ref, func might release the lock */ ev_ret.event = gst_event_ref (ev->event); ev_ret.received = ev->received; @@ -634,12 +644,13 @@ restart: /* should be called with LOCK */ static GstEvent * -_apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream) +_apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream, + gint64 pad_offset) { gint64 offset; - GST_DEBUG_OBJECT (pad, "apply pad offset %" GST_TIME_FORMAT, - GST_TIME_ARGS (pad->offset)); + GST_DEBUG_OBJECT (pad, "apply pad offset %" GST_STIME_FORMAT, + GST_STIME_ARGS (pad_offset)); if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { GstSegment segment; @@ -650,16 +661,16 @@ _apply_pad_offset (GstPad * pad, GstEvent * event, gboolean upstream) gst_event_copy_segment (event, &segment); gst_event_unref (event); - gst_segment_offset_running_time (&segment, segment.format, 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; + offset -= pad_offset; else - offset += pad->offset; + offset += pad_offset; gst_event_set_running_time_offset (event, offset); return event; @@ -669,11 +680,10 @@ 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 _apply_pad_offset (pad, event, upstream, pad->offset); return event; } - /* should be called with the OBJECT_LOCK */ static GstCaps * get_pad_caps (GstPad * pad) @@ -694,7 +704,7 @@ gst_pad_dispose (GObject * object) GstPad *pad = GST_PAD_CAST (object); GstPad *peer; - GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose"); + GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "%p dispose", pad); /* unlink the peer pad */ if ((peer = gst_pad_get_peer (pad))) { @@ -756,6 +766,7 @@ gst_pad_finalize (GObject * object) g_rec_mutex_clear (&pad->stream_rec_lock); g_cond_clear (&pad->block_cond); + g_cond_clear (&pad->priv->activation_cond); g_array_free (pad->priv->events, TRUE); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -849,9 +860,13 @@ gst_pad_new (const gchar * name, GstPadDirection direction) GstPad * gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) { + GType pad_type = + GST_PAD_TEMPLATE_GTYPE (templ) == + G_TYPE_NONE ? GST_TYPE_PAD : GST_PAD_TEMPLATE_GTYPE (templ); + g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL); - return g_object_new (GST_TYPE_PAD, + return g_object_new (pad_type, "name", name, "direction", templ->direction, "template", templ, NULL); } @@ -924,7 +939,9 @@ gst_pad_get_direction (GstPad * pad) static gboolean gst_pad_activate_default (GstPad * pad, GstObject * parent) { - return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + return activate_mode_internal (pad, parent, GST_PAD_MODE_PUSH, TRUE); } /** @@ -951,12 +968,22 @@ gst_pad_mode_get_name (GstPadMode mode) return "unknown"; } -static void +/* Returns TRUE if pad wasn't already in the new_mode */ +static gboolean pre_activate (GstPad * pad, GstPadMode new_mode) { switch (new_mode) { case GST_PAD_MODE_NONE: GST_OBJECT_LOCK (pad); + while (G_UNLIKELY (pad->priv->in_activation)) + g_cond_wait (&pad->priv->activation_cond, GST_OBJECT_GET_LOCK (pad)); + if (new_mode == GST_PAD_MODE (pad)) { + GST_WARNING_OBJECT (pad, + "Pad is already in the process of being deactivated"); + GST_OBJECT_UNLOCK (pad); + return FALSE; + } + pad->priv->in_activation = TRUE; GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing"); GST_PAD_SET_FLUSHING (pad); pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; @@ -968,6 +995,15 @@ pre_activate (GstPad * pad, GstPadMode new_mode) case GST_PAD_MODE_PUSH: case GST_PAD_MODE_PULL: GST_OBJECT_LOCK (pad); + while (G_UNLIKELY (pad->priv->in_activation)) + g_cond_wait (&pad->priv->activation_cond, GST_OBJECT_GET_LOCK (pad)); + if (new_mode == GST_PAD_MODE (pad)) { + GST_WARNING_OBJECT (pad, + "Pad is already in the process of being activated"); + GST_OBJECT_UNLOCK (pad); + return FALSE; + } + pad->priv->in_activation = TRUE; GST_DEBUG_OBJECT (pad, "setting pad into %s mode, unset flushing", gst_pad_mode_get_name (new_mode)); GST_PAD_UNSET_FLUSHING (pad); @@ -996,6 +1032,7 @@ pre_activate (GstPad * pad, GstPadMode new_mode) } break; } + return TRUE; } static void @@ -1003,6 +1040,11 @@ post_activate (GstPad * pad, GstPadMode new_mode) { switch (new_mode) { case GST_PAD_MODE_NONE: + GST_OBJECT_LOCK (pad); + pad->priv->in_activation = FALSE; + g_cond_broadcast (&pad->priv->activation_cond); + GST_OBJECT_UNLOCK (pad); + /* ensures that streaming stops */ GST_PAD_STREAM_LOCK (pad); GST_DEBUG_OBJECT (pad, "stopped streaming"); @@ -1013,6 +1055,10 @@ post_activate (GstPad * pad, GstPadMode new_mode) break; case GST_PAD_MODE_PUSH: case GST_PAD_MODE_PULL: + GST_OBJECT_LOCK (pad); + pad->priv->in_activation = FALSE; + g_cond_broadcast (&pad->priv->activation_cond); + GST_OBJECT_UNLOCK (pad); /* NOP */ break; } @@ -1069,7 +1115,7 @@ gst_pad_set_active (GstPad * pad, gboolean active) } else { GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode", gst_pad_mode_get_name (old)); - ret = gst_pad_activate_mode (pad, old, FALSE); + ret = activate_mode_internal (pad, parent, old, FALSE); if (ret) pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; } @@ -1103,36 +1149,18 @@ failed: } } -/** - * gst_pad_activate_mode: - * @pad: the #GstPad to activate or deactivate. - * @mode: the requested activation mode - * @active: whether or not the pad should be active. - * - * Activates or deactivates the given pad in @mode via dispatching to the - * pad's activatemodefunc. For use from within pad activation functions only. - * - * If you don't know what this is, you probably don't want to call it. - * - * Returns: %TRUE if the operation was successful. - * - * MT safe. - */ -gboolean -gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active) +static gboolean +activate_mode_internal (GstPad * pad, GstObject * parent, GstPadMode mode, + gboolean active) { gboolean res = FALSE; - GstObject *parent; GstPadMode old, new; GstPadDirection dir; GstPad *peer; - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - GST_OBJECT_LOCK (pad); old = GST_PAD_MODE (pad); dir = GST_PAD_DIRECTION (pad); - ACQUIRE_PARENT (pad, parent, no_parent); GST_OBJECT_UNLOCK (pad); new = active ? mode : GST_PAD_MODE_NONE; @@ -1146,8 +1174,9 @@ gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active) GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode", gst_pad_mode_get_name (old)); - if (G_UNLIKELY (!gst_pad_activate_mode (pad, old, FALSE))) + if (G_UNLIKELY (!activate_mode_internal (pad, parent, old, FALSE))) goto deactivate_failed; + old = GST_PAD_MODE_NONE; } switch (mode) { @@ -1182,17 +1211,21 @@ gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active) /* 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)) { - if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode, - active))) - goto failure; - } else { - /* can happen for sinks of passthrough elements */ - } + /* pre_activate returns TRUE if we weren't already in the process of + * switching to the 'new' mode */ + if (pre_activate (pad, new)) { - post_activate (pad, new); + if (GST_PAD_ACTIVATEMODEFUNC (pad)) { + if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode, + active))) + goto failure; + } else { + /* can happen for sinks of passthrough elements */ + } + + post_activate (pad, new); + } GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in %s mode", active ? "activated" : "deactivated", gst_pad_mode_get_name (mode)); @@ -1209,16 +1242,8 @@ exit_success: } exit: - RELEASE_PARENT (parent); - return res; -no_parent: - { - GST_DEBUG_OBJECT (pad, "no parent"); - GST_OBJECT_UNLOCK (pad); - return FALSE; - } was_ok: { GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in %s mode", @@ -1255,12 +1280,70 @@ failure: active ? "activate" : "deactivate", gst_pad_mode_get_name (mode)); GST_PAD_SET_FLUSHING (pad); GST_PAD_MODE (pad) = old; + pad->priv->in_activation = FALSE; + g_cond_broadcast (&pad->priv->activation_cond); GST_OBJECT_UNLOCK (pad); goto exit; } } /** + * gst_pad_activate_mode: + * @pad: the #GstPad to activate or deactivate. + * @mode: the requested activation mode + * @active: whether or not the pad should be active. + * + * Activates or deactivates the given pad in @mode via dispatching to the + * pad's activatemodefunc. For use from within pad activation functions only. + * + * If you don't know what this is, you probably don't want to call it. + * + * Returns: %TRUE if the operation was successful. + * + * MT safe. + */ +gboolean +gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active) +{ + GstObject *parent; + gboolean res; + GstPadMode old, new; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + GST_OBJECT_LOCK (pad); + + old = GST_PAD_MODE (pad); + new = active ? mode : GST_PAD_MODE_NONE; + if (old == new) + goto was_ok; + + ACQUIRE_PARENT (pad, parent, no_parent); + + GST_OBJECT_UNLOCK (pad); + + res = activate_mode_internal (pad, parent, mode, active); + + RELEASE_PARENT (parent); + + return res; + +was_ok: + { + GST_OBJECT_UNLOCK (pad); + GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in %s mode", + active ? "activated" : "deactivated", gst_pad_mode_get_name (mode)); + return TRUE; + } +no_parent: + { + GST_WARNING_OBJECT (pad, "no parent"); + GST_OBJECT_UNLOCK (pad); + return FALSE; + } +} + +/** * gst_pad_is_active: * @pad: the #GstPad to query * @@ -1324,6 +1407,13 @@ cleanup_hook (GstPad * pad, GHook * hook) * Be notified of different states of pads. The provided callback is called for * every state that matches @mask. * + * Probes are called in groups: First GST_PAD_PROBE_TYPE_BLOCK probes are + * called, then others, then finally GST_PAD_PROBE_TYPE_IDLE. The only + * exception here are GST_PAD_PROBE_TYPE_IDLE probes that are called + * immediately if the pad is already idle while calling gst_pad_add_probe(). + * In each of the groups, probes are called in the order in which they were + * added. + * * 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 @@ -1350,9 +1440,9 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask, GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "adding probe for mask 0x%08x", mask); - /* when no contraints are given for the types, assume all types are + /* when no constraints are given for the types, assume all types are * acceptable */ - if ((mask & GST_PAD_PROBE_TYPE_ALL_BOTH) == 0) + if ((mask & _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH) == 0) mask |= GST_PAD_PROBE_TYPE_ALL_BOTH; if ((mask & GST_PAD_PROBE_TYPE_SCHEDULING) == 0) mask |= GST_PAD_PROBE_TYPE_SCHEDULING; @@ -1362,12 +1452,11 @@ gst_pad_add_probe (GstPad * pad, GstPadProbeType mask, hook->func = callback; hook->data = user_data; hook->destroy = destroy_data; - PROBE_COOKIE (hook) = (pad->priv->probe_cookie - 1); /* add the probe */ g_hook_append (&pad->probes, hook); pad->num_probes++; - /* incremenent cookie so that the new hook get's called */ + /* incremenent cookie so that the new hook gets called */ pad->priv->probe_list_cookie++; /* get the id of the hook, we return this and it can be used to remove the @@ -1819,6 +1908,53 @@ gst_pad_set_event_function_full (GstPad * pad, GstPadEventFunction event, GST_DEBUG_FUNCPTR_NAME (event)); } +static gboolean +event_wrap (GstPad * pad, GstObject * object, GstEvent * event) +{ + GstFlowReturn ret; + + ret = GST_PAD_EVENTFULLFUNC (pad) (pad, object, event); + if (ret == GST_FLOW_OK) + return TRUE; + return FALSE; +} + +/** + * gst_pad_set_event_full_function: + * @p: a #GstPad of either direction. + * @f: the #GstPadEventFullFunction to set. + * + * Calls gst_pad_set_event_full_function_full() with %NULL for the user_data and + * notify. + */ +/** + * gst_pad_set_event_full_function_full: + * @pad: a #GstPad of either direction. + * @event: the #GstPadEventFullFunction 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. + * + * Since: 1.8 + */ +void +gst_pad_set_event_full_function_full (GstPad * pad, + GstPadEventFullFunction event, gpointer user_data, GDestroyNotify notify) +{ + g_return_if_fail (GST_IS_PAD (pad)); + + if (pad->eventnotify) + pad->eventnotify (pad->eventdata); + GST_PAD_EVENTFULLFUNC (pad) = event; + GST_PAD_EVENTFUNC (pad) = event_wrap; + pad->eventdata = user_data; + pad->eventnotify = notify; + + GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "eventfullfunc for set to %s", + GST_DEBUG_FUNCPTR_NAME (event)); +} + /** * gst_pad_set_query_function: * @p: a #GstPad of either direction. @@ -2366,6 +2502,8 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags) g_return_val_if_fail (GST_PAD_IS_SINK (sinkpad), GST_PAD_LINK_WRONG_DIRECTION); + GST_TRACER_PAD_LINK_PRE (srcpad, sinkpad); + /* Notify the parent early. See gst_pad_unlink for details. */ if (G_LIKELY ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad))))) { if (G_LIKELY (GST_IS_ELEMENT (parent))) { @@ -2443,7 +2581,8 @@ gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags) GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - gst_pad_send_event (srcpad, gst_event_new_reconfigure ()); + if (!(flags & GST_PAD_LINK_CHECK_NO_RECONFIGURE)) + gst_pad_send_event (srcpad, gst_event_new_reconfigure ()); done: if (G_LIKELY (parent)) { @@ -2453,6 +2592,7 @@ done: gst_object_unref (parent); } + GST_TRACER_PAD_LINK_POST (srcpad, sinkpad, result); return result; /* ERRORS */ @@ -2498,13 +2638,7 @@ link_failed: GstPadLinkReturn gst_pad_link (GstPad * srcpad, GstPad * sinkpad) { - GstPadLinkReturn ret; - - GST_TRACER_PAD_LINK_PRE (srcpad, sinkpad); - ret = gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT); - GST_TRACER_PAD_LINK_POST (srcpad, sinkpad, ret); - - return ret; + return gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT); } static void @@ -2578,7 +2712,8 @@ gst_pad_has_current_caps (GstPad * pad) * Gets the capabilities currently configured on @pad with the last * #GST_EVENT_CAPS event. * - * Returns: the current caps of the pad with incremented ref-count. + * Returns: (transfer full) (nullable): the current caps of the pad with + * incremented ref-count or %NULL when pad has no caps. Unref after usage. */ GstCaps * gst_pad_get_current_caps (GstPad * pad) @@ -2624,7 +2759,7 @@ gst_pad_get_pad_template_caps (GstPad * pad) * Gets the peer of @pad. This function refs the peer pad so * you need to unref it after use. * - * Returns: (transfer full): the peer #GstPad. Unref after usage. + * Returns: (transfer full) (nullable): the peer #GstPad. Unref after usage. * * MT safe. */ @@ -2681,16 +2816,25 @@ gst_pad_get_allowed_caps (GstPad * pad) /* Query peer caps */ query = gst_query_new_caps (mycaps); - gst_pad_peer_query (pad, query); + if (!gst_pad_peer_query (pad, query)) { + GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "Caps query failed"); + goto end; + } + gst_query_parse_caps_result (query, &caps); + if (caps == NULL) { + g_warn_if_fail (caps != NULL); + goto end; + } gst_caps_ref (caps); - gst_query_unref (query); - - gst_caps_unref (mycaps); GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT, caps); +end: + gst_query_unref (query); + gst_caps_unref (mycaps); + return caps; no_peer: @@ -2975,7 +3119,7 @@ static gboolean gst_pad_query_accept_caps_default (GstPad * pad, GstQuery * query) { /* get the caps and see if it intersects to something not empty */ - GstCaps *caps, *allowed; + GstCaps *caps, *allowed = NULL; gboolean result; GST_DEBUG_OBJECT (pad, "query accept-caps %" GST_PTR_FORMAT, query); @@ -2984,17 +3128,22 @@ gst_pad_query_accept_caps_default (GstPad * pad, GstQuery * query) * a PROXY CAPS */ if (GST_PAD_IS_PROXY_CAPS (pad)) { result = gst_pad_proxy_query_accept_caps (pad, query); - goto done; + if (result) + allowed = gst_pad_get_pad_template_caps (pad); + else + goto done; } - GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pad, - "fallback ACCEPT_CAPS query, consider implementing a specialized version"); - gst_query_parse_accept_caps (query, &caps); - if (GST_PAD_IS_ACCEPT_TEMPLATE (pad)) - allowed = gst_pad_get_pad_template_caps (pad); - else - allowed = gst_pad_query_caps (pad, caps); + if (!allowed) { + if (GST_PAD_IS_ACCEPT_TEMPLATE (pad)) { + allowed = gst_pad_get_pad_template_caps (pad); + } else { + GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pad, + "fallback ACCEPT_CAPS query, consider implementing a specialized version"); + allowed = gst_pad_query_caps (pad, caps); + } + } if (allowed) { if (GST_PAD_IS_ACCEPT_INTERSECT (pad)) { @@ -3302,6 +3451,8 @@ gst_pad_query_default (GstPad * pad, GstObject * parent, GstQuery * query) return ret; } +#define N_STACK_ALLOCATE_PROBES (16) + static void probe_hook_marshal (GHook * hook, ProbeMarshall * data) { @@ -3311,28 +3462,70 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) GstPadProbeCallback callback; GstPadProbeReturn ret; gpointer original_data; + guint i; - /* if we have called this callback, do nothing */ - if (PROBE_COOKIE (hook) == data->cookie) { - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "hook %lu, cookie %u already called", hook->hook_id, - PROBE_COOKIE (hook)); - return; + /* if we have called this callback, do nothing. But only check + * if we're actually calling probes a second time */ + if (data->retry) { + for (i = 0; i < data->n_called_probes; i++) { + if (data->called_probes[i] == hook) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "hook %lu already called", hook->hook_id); + return; + } + } } - PROBE_COOKIE (hook) = data->cookie; + /* reallocate on the heap if we had more than 16 probes */ + if (data->n_called_probes == data->called_probes_size) { + if (data->called_probes_size > N_STACK_ALLOCATE_PROBES) { + data->called_probes_size *= 2; + data->called_probes = + g_renew (GHook *, data->called_probes, data->called_probes_size); + } else { + GHook **tmp = data->called_probes; + + data->called_probes_size *= 2; + data->called_probes = g_new (GHook *, data->called_probes_size); + memcpy (data->called_probes, tmp, + N_STACK_ALLOCATE_PROBES * sizeof (GHook *)); + } + } + data->called_probes[data->n_called_probes++] = hook; flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT; type = info->type; original_data = info->data; - /* 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) goto no_match; + + if (G_UNLIKELY (data->handled)) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "probe previously returned HANDLED, not calling again"); + goto no_match; + } else if (G_UNLIKELY (data->dropped)) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "probe previously returned DROPPED, not calling again"); + goto no_match; + } + + if (type & GST_PAD_PROBE_TYPE_PUSH) { + /* one of the data types for non-idle probes */ + if ((type & GST_PAD_PROBE_TYPE_IDLE) == 0 + && (flags & _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH & type) == 0) + goto no_match; + } else if (type & GST_PAD_PROBE_TYPE_PULL) { + /* one of the data types for non-idle probes */ + if ((type & GST_PAD_PROBE_TYPE_BLOCKING) == 0 + && (flags & _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH & type) == 0) + goto no_match; + } else { + /* Type must have PULL or PUSH probe types */ + g_assert_not_reached (); + } + /* one of the blocking types must match */ if ((type & GST_PAD_PROBE_TYPE_BLOCKING) && (flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0) @@ -3346,8 +3539,7 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) goto no_match; GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "hook %lu, cookie %u with flags 0x%08x matches", hook->hook_id, - PROBE_COOKIE (hook), flags); + "hook %lu with flags 0x%08x matches", hook->hook_id, flags); data->marshalled = TRUE; @@ -3357,12 +3549,18 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) info->id = hook->hook_id; + if ((flags & GST_PAD_PROBE_TYPE_IDLE)) + pad->priv->idle_running++; + GST_OBJECT_UNLOCK (pad); ret = callback (pad, info, hook->data); GST_OBJECT_LOCK (pad); + if ((flags & GST_PAD_PROBE_TYPE_IDLE)) + pad->priv->idle_running--; + 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; @@ -3403,8 +3601,8 @@ probe_hook_marshal (GHook * hook, ProbeMarshall * data) no_match: { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "hook %lu, cookie %u with flags 0x%08x does not match %08x", - hook->hook_id, PROBE_COOKIE (hook), flags, info->type); + "hook %lu with flags 0x%08x does not match %08x", + hook->hook_id, flags, info->type); return; } } @@ -3489,6 +3687,7 @@ do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info, ProbeMarshall data; guint cookie; gboolean is_block; + GHook *called_probes[N_STACK_ALLOCATE_PROBES]; data.pad = pad; data.info = info; @@ -3496,7 +3695,14 @@ do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info, data.handled = FALSE; data.marshalled = FALSE; data.dropped = FALSE; - data.cookie = ++pad->priv->probe_cookie; + + /* We stack-allocate for N_STACK_ALLOCATE_PROBES hooks as a first step. If more are needed, + * we will re-allocate with g_malloc(). This should usually never be needed + */ + data.called_probes = called_probes; + data.n_called_probes = 0; + data.called_probes_size = N_STACK_ALLOCATE_PROBES; + data.retry = FALSE; is_block = (info->type & GST_PAD_PROBE_TYPE_BLOCK) == GST_PAD_PROBE_TYPE_BLOCK; @@ -3507,18 +3713,18 @@ do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info, } again: - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "do probes cookie %u", data.cookie); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "do probes"); cookie = pad->priv->probe_list_cookie; g_hook_list_marshal (&pad->probes, TRUE, (GHookMarshaller) probe_hook_marshal, &data); - /* if the list changed, call the new callbacks (they will not have their - * cookie set to data.cookie */ + /* if the list changed, call the new callbacks (they will not be in + * called_probes yet) */ if (cookie != pad->priv->probe_list_cookie) { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "probe list changed, restarting"); + data.retry = TRUE; goto again; } @@ -3560,11 +3766,12 @@ 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 the list changed, call the new callbacks (they will not be in + * called_probes yet) */ if (cookie != pad->priv->probe_list_cookie) { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "probe list changed, restarting"); + data.retry = TRUE; goto again; } @@ -3573,28 +3780,39 @@ again: } } + if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) + g_free (data.called_probes); + return defaultval; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pad, "pad is flushing"); + if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) + g_free (data.called_probes); return GST_FLOW_FLUSHING; } dropped: { GST_DEBUG_OBJECT (pad, "data is dropped"); + if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) + g_free (data.called_probes); return GST_FLOW_CUSTOM_SUCCESS; } passed: { /* FIXME : Should we return FLOW_OK or the defaultval ?? */ GST_DEBUG_OBJECT (pad, "data is passed"); + if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) + g_free (data.called_probes); return GST_FLOW_OK; } handled: { GST_DEBUG_OBJECT (pad, "data was handled"); + if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) + g_free (data.called_probes); return GST_FLOW_CUSTOM_SUCCESS_1; } } @@ -3649,7 +3867,8 @@ gst_pad_set_offset (GstPad * pad, gint64 offset) goto done; pad->offset = offset; - GST_DEBUG_OBJECT (pad, "changed offset to %" G_GINT64_FORMAT, offset); + GST_DEBUG_OBJECT (pad, "changed offset to %" GST_STIME_FORMAT, + GST_STIME_ARGS (offset)); /* resend all sticky events with updated offset on next buffer push */ events_foreach (pad, mark_event_not_received, NULL); @@ -3698,6 +3917,8 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data) } else { data->ret = gst_pad_push_event_unchecked (pad, gst_event_ref (event), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM); + if (data->ret == GST_FLOW_CUSTOM_SUCCESS_1) + data->ret = GST_FLOW_OK; } switch (data->ret) { @@ -3722,11 +3943,12 @@ push_sticky (GstPad * pad, PadEvent * ev, gpointer user_data) break; case GST_FLOW_NOT_LINKED: /* not linked is not a problem, we are sticky so the event will be - * sent later but only for non-EOS events */ + * rescheduled to be sent later on re-link, but only for non-EOS events */ GST_DEBUG_OBJECT (pad, "pad was not linked, mark pending"); - if (GST_EVENT_TYPE (event) != GST_EVENT_EOS) + if (GST_EVENT_TYPE (event) != GST_EVENT_EOS) { data->ret = GST_FLOW_OK; - GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); + ev->received = TRUE; + } break; default: GST_DEBUG_OBJECT (pad, "result %s, mark pending events", @@ -3844,7 +4066,7 @@ gst_pad_query (GstPad * pad, GstQuery * query) GST_DEBUG_OBJECT (pad, "sent query %p (%s), result %d", query, GST_QUERY_TYPE_NAME (query), res); - GST_TRACER_PAD_QUERY_POST (pad, res, query); + GST_TRACER_PAD_QUERY_POST (pad, query, res); if (res != TRUE) goto query_failed; @@ -4337,6 +4559,10 @@ gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data) /* do post-blocking probes */ PROBE_HANDLE (pad, type, data, probe_stopped, probe_handled); + /* recheck sticky events because the probe might have cause a relink */ + if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK) + goto events_error; + if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL)) goto not_linked; @@ -4926,8 +5152,9 @@ store_sticky_event (GstPad * pad, GstEvent * event) /* Unset the EOS flag when received STREAM_START event, so pad can * store sticky event and then push it later */ if (type == GST_EVENT_STREAM_START) { - GST_LOG_OBJECT (pad, "Removing pending EOS events"); + GST_LOG_OBJECT (pad, "Removing pending EOS and StreamGroupDone events"); remove_event_by_type (pad, GST_EVENT_EOS); + remove_event_by_type (pad, GST_EVENT_STREAM_GROUP_DONE); GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS); } @@ -5023,7 +5250,7 @@ eos: /** * gst_pad_store_sticky_event: * @pad: a #GstPad - * @event: a #GstEvent + * @event: (transfer none): a #GstEvent * * Store the sticky @event on @pad * @@ -5068,6 +5295,7 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, GstFlowReturn ret; GstPad *peerpad; GstEventType event_type; + gint64 old_pad_offset = pad->offset; /* pass the adjusted event on. We need to do this even if * there is no peer pad because of the probes. */ @@ -5093,6 +5321,7 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, /* Remove sticky EOS events */ GST_LOG_OBJECT (pad, "Removing pending EOS events"); remove_event_by_type (pad, GST_EVENT_EOS); + remove_event_by_type (pad, GST_EVENT_STREAM_GROUP_DONE); 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; @@ -5119,6 +5348,17 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, } PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_BLOCK, 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); + } break; } } @@ -5138,6 +5378,15 @@ gst_pad_push_event_unchecked (GstPad * pad, GstEvent * event, events_foreach (pad, sticky_changed, &data); } + /* the pad offset might've been changed by any of the probes above. It + * would've been taken into account when repushing any of the sticky events + * above but not for our current event here */ + if (G_UNLIKELY (old_pad_offset != pad->offset)) { + event = + _apply_pad_offset (pad, event, GST_PAD_IS_SINK (pad), + pad->offset - old_pad_offset); + } + /* now check the peer pad */ peerpad = GST_PAD_PEER (pad); if (peerpad == NULL) @@ -5377,10 +5626,13 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, GstEventType event_type; gboolean serialized, need_unlock = FALSE, sticky; GstPadEventFunction eventfunc; + GstPadEventFullFunction eventfullfunc = NULL; GstObject *parent; + gint64 old_pad_offset; GST_OBJECT_LOCK (pad); + old_pad_offset = pad->offset; event = apply_pad_offset (pad, event, GST_PAD_IS_SRC (pad)); if (GST_PAD_IS_SINK (pad)) @@ -5400,6 +5652,8 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, GST_PAD_SET_FLUSHING (pad); GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "set flush flag"); + GST_PAD_BLOCK_BROADCAST (pad); + type |= GST_PAD_PROBE_TYPE_EVENT_FLUSH; break; case GST_EVENT_FLUSH_STOP: /* we can't accept flush-stop on inactive pads else the flushing flag @@ -5414,6 +5668,7 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, /* Remove 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_STREAM_GROUP_DONE); 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; @@ -5441,6 +5696,7 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, /* Remove sticky EOS events */ GST_LOG_OBJECT (pad, "Removing pending EOS events"); remove_event_by_type (pad, GST_EVENT_EOS); + remove_event_by_type (pad, GST_EVENT_STREAM_GROUP_DONE); GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS); break; default: @@ -5472,7 +5728,18 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, PROBE_PUSH (pad, type | GST_PAD_PROBE_TYPE_PUSH, event, probe_stopped); - if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL)) + /* the pad offset might've been changed by any of the probes above. It + * would've been taken into account when repushing any of the sticky events + * above but not for our current event here */ + if (G_UNLIKELY (old_pad_offset != pad->offset)) { + event = + _apply_pad_offset (pad, event, GST_PAD_IS_SRC (pad), + pad->offset - old_pad_offset); + } + + eventfullfunc = GST_PAD_EVENTFULLFUNC (pad); + eventfunc = GST_PAD_EVENTFUNC (pad); + if (G_UNLIKELY (eventfunc == NULL && eventfullfunc == NULL)) goto no_function; ACQUIRE_PARENT (pad, parent, no_parent); @@ -5485,7 +5752,9 @@ gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event, if (sticky) gst_event_ref (event); - if (eventfunc (pad, parent, event)) { + if (eventfullfunc) { + ret = eventfullfunc (pad, parent, event); + } else if (eventfunc (pad, parent, event)) { ret = GST_FLOW_OK; } else { /* something went wrong */ @@ -5700,7 +5969,7 @@ gst_pad_set_element_private (GstPad * pad, gpointer priv) * Gets the private data of a pad. * No locking is performed in this function. * - * Returns: (transfer none): a #gpointer to the private data. + * Returns: (transfer none) (nullable): a #gpointer to the private data. */ gpointer gst_pad_get_element_private (GstPad * pad) @@ -5932,6 +6201,9 @@ gst_pad_pause_task (GstPad * pad) if (task == NULL) goto no_task; res = gst_task_set_state (task, GST_TASK_PAUSED); + /* unblock activation waits if any */ + pad->priv->in_activation = FALSE; + g_cond_broadcast (&pad->priv->activation_cond); GST_OBJECT_UNLOCK (pad); /* wait for task function to finish, this lock is recursive so it does nothing @@ -5950,6 +6222,42 @@ no_task: } /** + * gst_pad_get_task_state: + * @pad: the #GstPad to get task state from + * + * Get @pad task state. If no task is currently + * set, #GST_TASK_STOPPED is returned. + * + * Returns: The current state of @pad's task. + * + * Since: 1.12 + */ +GstTaskState +gst_pad_get_task_state (GstPad * pad) +{ + GstTask *task; + GstTaskState res; + + g_return_val_if_fail (GST_IS_PAD (pad), GST_TASK_STOPPED); + + GST_OBJECT_LOCK (pad); + task = GST_PAD_TASK (pad); + if (task == NULL) + goto no_task; + res = gst_task_get_state (task); + GST_OBJECT_UNLOCK (pad); + + return res; + +no_task: + { + GST_DEBUG_OBJECT (pad, "pad has no task"); + GST_OBJECT_UNLOCK (pad); + return GST_TASK_STOPPED; + } +} + +/** * gst_pad_stop_task: * @pad: the #GstPad to stop the task of * @@ -5981,6 +6289,9 @@ gst_pad_stop_task (GstPad * pad) goto no_task; GST_PAD_TASK (pad) = NULL; res = gst_task_set_state (task, GST_TASK_STOPPED); + /* unblock activation waits if any */ + pad->priv->in_activation = FALSE; + g_cond_broadcast (&pad->priv->activation_cond); GST_OBJECT_UNLOCK (pad); GST_PAD_STREAM_LOCK (pad); @@ -6024,7 +6335,7 @@ join_failed: * gst_pad_probe_info_get_event: * @info: a #GstPadProbeInfo * - * Returns: (transfer none): The #GstEvent from the probe + * Returns: (transfer none) (nullable): The #GstEvent from the probe */ GstEvent * @@ -6041,7 +6352,7 @@ gst_pad_probe_info_get_event (GstPadProbeInfo * info) * gst_pad_probe_info_get_query: * @info: a #GstPadProbeInfo * - * Returns: (transfer none): The #GstQuery from the probe + * Returns: (transfer none) (nullable): The #GstQuery from the probe */ GstQuery * @@ -6057,7 +6368,7 @@ gst_pad_probe_info_get_query (GstPadProbeInfo * info) * gst_pad_probe_info_get_buffer: * @info: a #GstPadProbeInfo * - * Returns: (transfer none): The #GstBuffer from the probe + * Returns: (transfer none) (nullable): The #GstBuffer from the probe */ GstBuffer * @@ -6072,7 +6383,7 @@ gst_pad_probe_info_get_buffer (GstPadProbeInfo * info) * gst_pad_probe_info_get_buffer_list: * @info: a #GstPadProbeInfo * - * Returns: (transfer none): The #GstBufferlist from the probe + * Returns: (transfer none) (nullable): The #GstBufferList from the probe */ GstBufferList *