mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 09:40:37 +00:00
appsrc: Add support for custom GstSegment
Add property "handle-segment-change" for user to allow pushing custom segment event. For now, this property can work only for time format GstSegment. This property can be useful in case application controls timeline of stream such as there is timestamp discontinuity but playback is expected to be continuous. Multi-period scenario of MPEG-DASH is an example of this use case. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/663>
This commit is contained in:
parent
9412e61150
commit
64e768e836
2 changed files with 111 additions and 3 deletions
|
@ -504,6 +504,18 @@
|
|||
"type": "GstFormat",
|
||||
"writable": true
|
||||
},
|
||||
"handle-segment-change": {
|
||||
"blurb": "Whether to detect and handle changed time format GstSegment in GstSample. User should set valid GstSegment in GstSample. Must set format property as \"time\" to enable this property",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "false",
|
||||
"mutable": "ready",
|
||||
"readable": true,
|
||||
"type": "gboolean",
|
||||
"writable": true
|
||||
},
|
||||
"is-live": {
|
||||
"blurb": "Whether to act as a live source",
|
||||
"conditionally-available": false,
|
||||
|
|
|
@ -146,6 +146,9 @@ struct _GstAppSrcPrivate
|
|||
|
||||
GstCaps *last_caps;
|
||||
GstCaps *current_caps;
|
||||
GstSegment last_segment;
|
||||
GstSegment current_segment;
|
||||
gboolean pending_custom_segment;
|
||||
|
||||
gint64 size;
|
||||
GstClockTime duration;
|
||||
|
@ -166,6 +169,7 @@ struct _GstAppSrcPrivate
|
|||
guint64 max_latency;
|
||||
gboolean emit_signals;
|
||||
guint min_percent;
|
||||
gboolean handle_segment_change;
|
||||
|
||||
Callbacks *callbacks;
|
||||
};
|
||||
|
@ -201,6 +205,7 @@ enum
|
|||
#define DEFAULT_PROP_MIN_PERCENT 0
|
||||
#define DEFAULT_PROP_CURRENT_LEVEL_BYTES 0
|
||||
#define DEFAULT_PROP_DURATION GST_CLOCK_TIME_NONE
|
||||
#define DEFAULT_PROP_HANDLE_SEGMENT_CHANGE FALSE
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -218,6 +223,7 @@ enum
|
|||
PROP_MIN_PERCENT,
|
||||
PROP_CURRENT_LEVEL_BYTES,
|
||||
PROP_DURATION,
|
||||
PROP_HANDLE_SEGMENT_CHANGE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
|
@ -438,6 +444,29 @@ gst_app_src_class_init (GstAppSrcClass * klass)
|
|||
0, G_MAXUINT64, DEFAULT_PROP_DURATION,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstAppSrc:handle-segment-change:
|
||||
*
|
||||
* When enabled, appsrc will check GstSegment in GstSample which was
|
||||
* pushed via gst_app_src_push_sample() or "push-sample" signal action.
|
||||
* If a GstSegment is changed, corresponding segment event will be followed
|
||||
* by next data flow.
|
||||
*
|
||||
* FIXME: currently only GST_FORMAT_TIME format is supported and therefore
|
||||
* GstAppSrc::format should be time. However, possibly #GstAppSrc can support
|
||||
* other formats.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_HANDLE_SEGMENT_CHANGE,
|
||||
g_param_spec_boolean ("handle-segment-change", "Handle Segment Change",
|
||||
"Whether to detect and handle changed time format GstSegment in "
|
||||
"GstSample. User should set valid GstSegment in GstSample. "
|
||||
"Must set format property as \"time\" to enable this property",
|
||||
DEFAULT_PROP_HANDLE_SEGMENT_CHANGE,
|
||||
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstAppSrc::need-data:
|
||||
* @appsrc: the appsrc element that emitted the signal
|
||||
|
@ -614,6 +643,7 @@ gst_app_src_init (GstAppSrc * appsrc)
|
|||
priv->max_latency = DEFAULT_PROP_MAX_LATENCY;
|
||||
priv->emit_signals = DEFAULT_PROP_EMIT_SIGNALS;
|
||||
priv->min_percent = DEFAULT_PROP_MIN_PERCENT;
|
||||
priv->handle_segment_change = DEFAULT_PROP_HANDLE_SEGMENT_CHANGE;
|
||||
|
||||
gst_base_src_set_live (GST_BASE_SRC (appsrc), DEFAULT_PROP_IS_LIVE);
|
||||
}
|
||||
|
@ -760,6 +790,9 @@ gst_app_src_set_property (GObject * object, guint prop_id,
|
|||
case PROP_DURATION:
|
||||
gst_app_src_set_duration (appsrc, g_value_get_uint64 (value));
|
||||
break;
|
||||
case PROP_HANDLE_SEGMENT_CHANGE:
|
||||
priv->handle_segment_change = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -823,6 +856,9 @@ gst_app_src_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
case PROP_DURATION:
|
||||
g_value_set_uint64 (value, gst_app_src_get_duration (appsrc));
|
||||
break;
|
||||
case PROP_HANDLE_SEGMENT_CHANGE:
|
||||
g_value_set_boolean (value, priv->handle_segment_change);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -895,6 +931,9 @@ gst_app_src_start (GstBaseSrc * bsrc)
|
|||
g_mutex_unlock (&priv->mutex);
|
||||
|
||||
gst_base_src_set_format (bsrc, priv->format);
|
||||
gst_segment_init (&priv->last_segment, priv->format);
|
||||
gst_segment_init (&priv->current_segment, priv->format);
|
||||
priv->pending_custom_segment = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -1056,6 +1095,9 @@ gst_app_src_do_seek (GstBaseSrc * src, GstSegment * segment)
|
|||
GST_DEBUG_OBJECT (appsrc, "flushing queue");
|
||||
g_mutex_lock (&priv->mutex);
|
||||
gst_app_src_flush_queued (appsrc, TRUE);
|
||||
gst_segment_copy_into (segment, &priv->last_segment);
|
||||
gst_segment_copy_into (segment, &priv->current_segment);
|
||||
priv->pending_custom_segment = FALSE;
|
||||
g_mutex_unlock (&priv->mutex);
|
||||
priv->is_eos = FALSE;
|
||||
} else {
|
||||
|
@ -1258,11 +1300,9 @@ gst_app_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
|
|||
*buf = GST_BUFFER (obj);
|
||||
buf_size = gst_buffer_get_size (*buf);
|
||||
GST_LOG_OBJECT (appsrc, "have buffer %p of size %u", *buf, buf_size);
|
||||
} else {
|
||||
} else if (GST_IS_BUFFER_LIST (obj)) {
|
||||
GstBufferList *buffer_list;
|
||||
|
||||
g_assert (GST_IS_BUFFER_LIST (obj));
|
||||
|
||||
buffer_list = GST_BUFFER_LIST (obj);
|
||||
|
||||
buf_size = gst_buffer_list_calculate_size (buffer_list);
|
||||
|
@ -1272,6 +1312,29 @@ gst_app_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
|
|||
|
||||
gst_base_src_submit_buffer_list (bsrc, buffer_list);
|
||||
*buf = NULL;
|
||||
} else if (GST_IS_EVENT (obj)) {
|
||||
GstEvent *event = GST_EVENT (obj);
|
||||
const GstSegment *segment = NULL;
|
||||
|
||||
gst_event_parse_segment (event, &segment);
|
||||
g_assert (segment != NULL);
|
||||
|
||||
if (!gst_segment_is_equal (&priv->current_segment, segment)) {
|
||||
GST_DEBUG_OBJECT (appsrc,
|
||||
"Update new segment %" GST_PTR_FORMAT, event);
|
||||
if (!gst_base_src_new_segment (bsrc, segment)) {
|
||||
GST_ERROR_OBJECT (appsrc,
|
||||
"Couldn't set new segment %" GST_PTR_FORMAT, event);
|
||||
ret = GST_FLOW_ERROR;
|
||||
break;
|
||||
}
|
||||
gst_segment_copy_into (segment, &priv->current_segment);
|
||||
}
|
||||
|
||||
gst_event_unref (event);
|
||||
continue;
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
priv->queued_bytes -= buf_size;
|
||||
|
@ -1897,6 +1960,14 @@ gst_app_src_push_internal (GstAppSrc * appsrc, GstBuffer * buffer,
|
|||
break;
|
||||
}
|
||||
|
||||
if (priv->pending_custom_segment) {
|
||||
GstEvent *event = gst_event_new_segment (&priv->last_segment);
|
||||
|
||||
GST_DEBUG_OBJECT (appsrc, "enqueue new segment %" GST_PTR_FORMAT, event);
|
||||
gst_queue_array_push_tail (priv->queue, event);
|
||||
priv->pending_custom_segment = FALSE;
|
||||
}
|
||||
|
||||
if (buflist != NULL) {
|
||||
GST_DEBUG_OBJECT (appsrc, "queueing buffer list %p", buflist);
|
||||
if (!steal_ref)
|
||||
|
@ -1947,6 +2018,7 @@ gst_app_src_push_buffer_full (GstAppSrc * appsrc, GstBuffer * buffer,
|
|||
static GstFlowReturn
|
||||
gst_app_src_push_sample_internal (GstAppSrc * appsrc, GstSample * sample)
|
||||
{
|
||||
GstAppSrcPrivate *priv = appsrc->priv;
|
||||
GstBufferList *buffer_list;
|
||||
GstBuffer *buffer;
|
||||
GstCaps *caps;
|
||||
|
@ -1960,6 +2032,30 @@ gst_app_src_push_sample_internal (GstAppSrc * appsrc, GstSample * sample)
|
|||
GST_WARNING_OBJECT (appsrc, "received sample without caps");
|
||||
}
|
||||
|
||||
if (priv->handle_segment_change && priv->format == GST_FORMAT_TIME) {
|
||||
GstSegment *segment = gst_sample_get_segment (sample);
|
||||
|
||||
if (segment->format != GST_FORMAT_TIME) {
|
||||
GST_LOG_OBJECT (appsrc, "format %s is not supported",
|
||||
gst_format_get_name (segment->format));
|
||||
goto handle_buffer;
|
||||
}
|
||||
|
||||
g_mutex_lock (&priv->mutex);
|
||||
if (gst_segment_is_equal (&priv->last_segment, segment)) {
|
||||
GST_LOG_OBJECT (appsrc, "segment wasn't changed");
|
||||
g_mutex_unlock (&priv->mutex);
|
||||
goto handle_buffer;
|
||||
}
|
||||
|
||||
/* will be pushed to queue with next buffer/buffer-list */
|
||||
gst_segment_copy_into (segment, &priv->last_segment);
|
||||
priv->pending_custom_segment = TRUE;
|
||||
g_mutex_unlock (&priv->mutex);
|
||||
}
|
||||
|
||||
handle_buffer:
|
||||
|
||||
buffer = gst_sample_get_buffer (sample);
|
||||
if (buffer != NULL)
|
||||
return gst_app_src_push_buffer_full (appsrc, buffer, FALSE);
|
||||
|
|
Loading…
Reference in a new issue