PAD_PROP_CAPS,
PAD_PROP_DIRECTION,
PAD_PROP_TEMPLATE,
- /* FILL ME */
+ PAD_PROP_OFFSET
+ /* FILL ME */
};
#define GST_PAD_GET_PRIVATE(obj) \
"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 */
{
guint i, len;
GArray *events;
+ gboolean notify = FALSE;
events = pad->priv->events;
ev->event = NULL;
- if (event && GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
- GST_OBJECT_UNLOCK (pad);
+ if (event && GST_EVENT_TYPE (event) == GST_EVENT_CAPS)
+ notify = TRUE;
- GST_DEBUG_OBJECT (pad, "notify caps");
- g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
-
- GST_OBJECT_LOCK (pad);
- }
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 */
/* 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)
{
- /* check if we need to adjust the segment */
- if (pad->offset != 0) {
- gint64 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_TIME_FORMAT,
+ GST_TIME_ARGS (pad->offset));
- if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
- GstSegment segment;
+ if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
+ GstSegment segment;
- g_assert (!upstream);
+ g_assert (!upstream);
- /* copy segment values */
- gst_event_copy_segment (event, &segment);
- gst_event_unref (event);
-
- gst_segment_offset_running_time (&segment, segment.format, pad->offset);
- event = gst_event_new_segment (&segment);
- }
+ /* copy segment values */
+ gst_event_copy_segment (event, &segment);
+ gst_event_unref (event);
- 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);
+ 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)
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;
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;
/**
* 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.
* 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.
*/
/**
* 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)
* 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.
*/
GstPad *
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)
* 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.
*/
GstPad *
gst_pad_new_from_static_template (GstStaticPadTemplate * templ,
*
* 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
- * happend that the probe can be run immediately and if the probe returns
+ * happen that the probe can be run immediately and if the probe returns
* GST_PAD_PROBE_REMOVE this functions returns 0.
*
* MT safe.
/* 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;
*
* 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)
* 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.
*/
*
* 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)
*
* 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)
{
GST_DEBUG_OBJECT (pad, "no parent");
GST_OBJECT_UNLOCK (pad);
- return FALSE;
+ return NULL;
}
}
* 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.
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;
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:
GstPadProbeType type, flags;
GstPadProbeCallback callback;
GstPadProbeReturn ret;
+ gpointer original_data;
/* if we have called this callback, do nothing */
if (PROBE_COOKIE (hook) == data->cookie) {
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
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 */
#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
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;
}
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;
}
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);
GST_OBJECT_UNLOCK (pad);
pad->ABI.abi.last_flowret =
ret == GST_FLOW_CUSTOM_SUCCESS ? GST_FLOW_OK : ret;
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ if (data != NULL)
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
switch (ret) {
case GST_FLOW_CUSTOM_SUCCESS:
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);
}
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)))
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 */
/* 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 (%s) peerpad %" GST_PTR_FORMAT ", ret %s", event,
+ "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);
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);
"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 and SEGMENT events");
remove_event_by_type (pad, GST_EVENT_EOS);
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);
* 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)