Skip to content

Commit

Permalink
appsrc: Add support for custom GstSegment
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
seungha-yang committed Jul 10, 2020
1 parent 9412e61 commit 64e768e
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 3 deletions.
12 changes: 12 additions & 0 deletions docs/plugins/gst_plugins_cache.json
Expand Up @@ -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,
Expand Down
102 changes: 99 additions & 3 deletions gst-libs/gst/app/gstappsrc.c
Expand Up @@ -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;
Expand All @@ -166,6 +169,7 @@ struct _GstAppSrcPrivate
guint64 max_latency;
gboolean emit_signals;
guint min_percent;
gboolean handle_segment_change;

Callbacks *callbacks;
};
Expand Down Expand Up @@ -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
{
Expand All @@ -218,6 +223,7 @@ enum
PROP_MIN_PERCENT,
PROP_CURRENT_LEVEL_BYTES,
PROP_DURATION,
PROP_HANDLE_SEGMENT_CHANGE,
PROP_LAST
};

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 64e768e

Please sign in to comment.