rtsp-media: allow specifying rate when seeking

Add new function gst_rtsp_media_seek_full_with_rate() which allows the
caller to specify the rate for the seek. Also added functions in
rtsp-stream and rtsp-media for retreiving current rate and applied rate.

https://bugzilla.gnome.org/show_bug.cgi?id=754575
This commit is contained in:
Branko Subasic 2018-09-27 19:09:01 +02:00 committed by Mathieu Duponchelle
parent e788fc4e88
commit 421ac85150
5 changed files with 209 additions and 32 deletions

View file

@ -50,7 +50,8 @@
* #GstRTSPSession and #GstRTSPSessionMedia.
*
* The state of the media can be controlled with gst_rtsp_media_set_state ().
* Seeking can be done with gst_rtsp_media_seek().
* Seeking can be done with gst_rtsp_media_seek(), or gst_rtsp_media_seek_full()
* or gst_rtsp_media_seek_full_with_rate() for finer control of the seek.
*
* With gst_rtsp_media_unprepare() the pipeline is stopped and shut down. When
* gst_rtsp_media_set_eos_shutdown() an EOS will be sent to the pipeline to
@ -2554,6 +2555,51 @@ conversion_failed:
}
}
/**
* gst_rtsp_media_get_rates:
* @media: a #GstRTSPMedia
* @rate (allow-none): the rate of the current segment
* @applied_rate (allow-none): the applied_rate of the current segment
*
* Get the rate and applied_rate of the current segment.
*
* Returns: %FALSE if looking up the rate and applied rate failed. Otherwise
* %TRUE is returned and @rate and @applied_rate are set to the rate and
* applied_rate of the current segment.
* Since: 1.18
*/
gboolean
gst_rtsp_media_get_rates (GstRTSPMedia * media, gdouble * rate,
gdouble * applied_rate)
{
GstRTSPMediaPrivate *priv;
GstRTSPStream *stream;
gboolean result = TRUE;
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
if (!rate && !applied_rate) {
GST_WARNING_OBJECT (media, "rate and applied_rate are both NULL");
return FALSE;
}
priv = media->priv;
g_mutex_lock (&priv->lock);
g_assert (priv->streams->len > 0);
stream = g_ptr_array_index (priv->streams, 0);
if (!gst_rtsp_stream_get_rates (stream, rate, applied_rate)) {
GST_WARNING_OBJECT (media,
"failed to obtain rate and applied_rate from first stream");
result = FALSE;
}
g_mutex_unlock (&priv->lock);
return result;
}
static void
stream_update_blocked (GstRTSPStream * stream, GstRTSPMedia * media)
{
@ -2635,22 +2681,24 @@ gst_rtsp_media_get_status (GstRTSPMedia * media)
}
/**
* gst_rtsp_media_seek_full:
* gst_rtsp_media_seek_full_with_rate:
* @media: a #GstRTSPMedia
* @range: (transfer none): a #GstRTSPTimeRange
* @flags: The minimal set of #GstSeekFlags to use
* @rate: the rate to use in the seek
*
* Seek the pipeline of @media to @range. @media must be prepared with
* gst_rtsp_media_prepare(). In order to perform the seek operation,
* the pipeline must contain all needed transport parts (transport sinks).
* Seek the pipeline of @media to @range with the given @flags and @rate.
* @media must be prepared with gst_rtsp_media_prepare().
* In order to perform the seek operation, the pipeline must contain all
* needed transport parts (transport sinks).
*
* Returns: %TRUE on success.
*
* Since: 1.14
* Since: 1.18
*/
gboolean
gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
GstSeekFlags flags)
gst_rtsp_media_seek_full_with_rate (GstRTSPMedia * media,
GstRTSPTimeRange * range, GstSeekFlags flags, gdouble rate)
{
GstRTSPMediaClass *klass;
GstRTSPMediaPrivate *priv;
@ -2658,12 +2706,15 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
GstClockTime start, stop;
GstSeekType start_type, stop_type;
gint64 current_position;
gboolean force_seek;
klass = GST_RTSP_MEDIA_GET_CLASS (media);
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
g_return_val_if_fail (range != NULL, FALSE);
g_return_val_if_fail (klass->convert_range != NULL, FALSE);
/* if there's a range then klass->convert_range must be set */
g_return_val_if_fail (range == NULL || klass->convert_range != NULL, FALSE);
GST_DEBUG ("flags=%x rate=%f", flags, rate);
priv = media->priv;
@ -2689,15 +2740,21 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
}
start_type = stop_type = GST_SEEK_TYPE_NONE;
start = stop = GST_CLOCK_TIME_NONE;
if (!klass->convert_range (media, range, GST_RTSP_RANGE_NPT))
goto not_supported;
gst_rtsp_range_get_times (range, &start, &stop);
/* if caller provided a range convert it to NPT format
* if no range provided the seek is assumed to be the same position but with
* e.g. the rate changed */
if (range != NULL) {
if (!klass->convert_range (media, range, GST_RTSP_RANGE_NPT))
goto not_supported;
gst_rtsp_range_get_times (range, &start, &stop);
GST_INFO ("got %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
GST_INFO ("current %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (priv->range_start), GST_TIME_ARGS (priv->range_stop));
GST_INFO ("got %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
GST_INFO ("current %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (priv->range_start), GST_TIME_ARGS (priv->range_stop));
}
current_position = -1;
if (klass->query_position)
@ -2713,24 +2770,24 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
else if (stop != GST_CLOCK_TIME_NONE)
stop_type = GST_SEEK_TYPE_SET;
if (start != GST_CLOCK_TIME_NONE || stop != GST_CLOCK_TIME_NONE) {
gboolean had_flags = flags != 0;
/* we force a seek if any seek flag is set, or if the the rate
* is non-standard, i.e. not 1.0 */
force_seek = flags != GST_SEEK_FLAG_NONE || rate != 1.0;
if (start != GST_CLOCK_TIME_NONE || stop != GST_CLOCK_TIME_NONE || force_seek) {
gboolean had_flags = flags != GST_SEEK_FLAG_NONE;
GST_INFO ("seeking to %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
/* depends on the current playing state of the pipeline. We might need to
* queue this until we get EOS. */
if (had_flags)
flags |= GST_SEEK_FLAG_FLUSH;
else
flags = GST_SEEK_FLAG_FLUSH;
flags |= GST_SEEK_FLAG_FLUSH;
/* if range start was not supplied we must continue from current position.
* but since we're doing a flushing seek, let us query the current position
* so we end up at exactly the same position after the seek. */
if (range->min.type == GST_RTSP_TIME_END) { /* Yepp, that's right! */
if (range == NULL || range->min.type == GST_RTSP_TIME_END) {
if (current_position == -1) {
GST_WARNING ("current position unknown");
} else {
@ -2748,14 +2805,14 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
flags |= GST_SEEK_FLAG_KEY_UNIT;
}
if (start == current_position && stop_type == GST_SEEK_TYPE_NONE) {
GST_DEBUG ("not seeking because no position change");
if (start == current_position && stop_type == GST_SEEK_TYPE_NONE &&
!force_seek) {
GST_DEBUG ("no position change, no flags set by caller, so not seeking");
res = TRUE;
} else {
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
/* FIXME, we only do forwards playback, no trick modes yet */
res = gst_element_seek (priv->pipeline, 1.0, GST_FORMAT_TIME,
res = gst_element_seek (priv->pipeline, rate, GST_FORMAT_TIME,
flags, start_type, start, stop_type, stop);
/* and block for the seek to complete */
@ -2819,6 +2876,23 @@ preroll_failed:
}
}
/**
* gst_rtsp_media_seek_full:
* @media: a #GstRTSPMedia
* @range: (transfer none): a #GstRTSPTimeRange
* @flags: The minimal set of #GstSeekFlags to use
*
* Seek the pipeline of @media to @range with the given @flags.
* @media must be prepared with gst_rtsp_media_prepare().
*
* Returns: %TRUE on success.
*/
gboolean
gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
GstSeekFlags flags)
{
return gst_rtsp_media_seek_full_with_rate (media, range, flags, 1.0);
}
/**
* gst_rtsp_media_seek:
@ -2833,10 +2907,10 @@ preroll_failed:
gboolean
gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range)
{
return gst_rtsp_media_seek_full (media, range, 0);
return gst_rtsp_media_seek_full_with_rate (media, range, GST_SEEK_FLAG_NONE,
1.0);
}
static void
stream_collect_blocking (GstRTSPStream * stream, gboolean * blocked)
{

View file

@ -387,6 +387,12 @@ gboolean gst_rtsp_media_seek_full (GstRTSPMedia *media,
GstRTSPTimeRange *range,
GstSeekFlags flags);
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_seek_full_with_rate (GstRTSPMedia *media,
GstRTSPTimeRange *range,
GstSeekFlags flags,
gdouble rate);
GST_RTSP_SERVER_API
GstClockTimeDiff gst_rtsp_media_seekable (GstRTSPMedia *media);
@ -395,6 +401,11 @@ gchar * gst_rtsp_media_get_range_string (GstRTSPMedia *media,
gboolean play,
GstRTSPRangeUnit unit);
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_get_rates (GstRTSPMedia * media,
gdouble * rate,
gdouble * applied_rate);
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_set_state (GstRTSPMedia *media, GstState state,
GPtrArray *transports);

View file

@ -4127,6 +4127,70 @@ no_stats:
}
}
/**
* gst_rtsp_stream_get_rates:
* @stream: a #GstRTSPStream
* @rate: (allow-none): the configured rate
* @applied_rate: (allow-none): the configured applied_rate
*
* Retrieve the current rate and/or applied_rate.
*
* Returns: %TRUE if rate and/or applied_rate could be determined.
* Since: 1.18
*/
gboolean
gst_rtsp_stream_get_rates (GstRTSPStream * stream, gdouble * rate,
gdouble * applied_rate)
{
GstRTSPStreamPrivate *priv;
GstEvent *event;
const GstSegment *segment;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
if (!rate && !applied_rate) {
GST_WARNING_OBJECT (stream, "rate and applied_rate are both NULL");
return FALSE;
}
priv = stream->priv;
g_mutex_lock (&priv->lock);
if (!priv->send_rtp_sink)
goto no_rtp_sink_pad;
event = gst_pad_get_sticky_event (priv->send_rtp_sink, GST_EVENT_SEGMENT, 0);
if (!event)
goto no_sticky_event;
gst_event_parse_segment (event, &segment);
if (rate)
*rate = segment->rate;
if (applied_rate)
*applied_rate = segment->applied_rate;
gst_event_unref (event);
g_mutex_unlock (&priv->lock);
return TRUE;
/* ERRORS */
no_rtp_sink_pad:
{
GST_WARNING_OBJECT (stream, "no send_rtp_sink pad yet");
g_mutex_unlock (&priv->lock);
return FALSE;
}
no_sticky_event:
{
GST_WARNING_OBJECT (stream, "no segment event on send_rtp_sink pad");
g_mutex_unlock (&priv->lock);
return FALSE;
}
}
/**
* gst_rtsp_stream_get_caps:
* @stream: a #GstRTSPStream

View file

@ -198,6 +198,11 @@ gboolean gst_rtsp_stream_get_rtpinfo (GstRTSPStream *stream,
guint *clock_rate,
GstClockTime *running_time);
GST_RTSP_SERVER_API
gboolean gst_rtsp_stream_get_rates (GstRTSPStream * stream,
gdouble * rate,
gdouble * applied_rate);
GST_RTSP_SERVER_API
GstCaps * gst_rtsp_stream_get_caps (GstRTSPStream *stream);

View file

@ -60,6 +60,8 @@ GST_START_TEST (test_media_seek)
GstRTSPThreadPool *pool;
GstRTSPThread *thread;
GstRTSPTransport *transport;
gdouble rate = 0;
gdouble applied_rate = 0;
factory = gst_rtsp_media_factory_new ();
fail_if (gst_rtsp_media_factory_is_shared (factory));
@ -98,9 +100,30 @@ GST_START_TEST (test_media_seek)
str = gst_rtsp_media_get_range_string (media, FALSE, GST_RTSP_RANGE_NPT);
fail_unless (g_str_equal (str, "npt=5-"));
g_free (str);
/* seeking without rate should result in rate == 1.0 */
fail_unless (gst_rtsp_media_seek (media, range));
fail_unless (gst_rtsp_media_get_rates (media, &rate, &applied_rate));
fail_unless (rate == 1.0);
fail_unless (applied_rate == 1.0);
/* seeking with rate set to 1.5 should result in rate == 1.5 */
fail_unless (gst_rtsp_media_seek_full_with_rate (media, range,
GST_SEEK_FLAG_NONE, 1.5));
fail_unless (gst_rtsp_media_get_rates (media, &rate, &applied_rate));
fail_unless (rate == 1.5);
fail_unless (applied_rate == 1.0);
/* seeking with rate set to -2.0 should result in rate == -2.0 */
fail_unless (gst_rtsp_range_parse ("npt=5-10", &range) == GST_RTSP_OK);
fail_unless (gst_rtsp_media_seek_full_with_rate (media, range,
GST_SEEK_FLAG_NONE, -2.0));
fail_unless (gst_rtsp_media_get_rates (media, &rate, &applied_rate));
fail_unless (rate == -2.0);
fail_unless (applied_rate == 1.0);
gst_rtsp_range_free (range);
g_free (str);
fail_unless (gst_rtsp_media_unprepare (media));
g_object_unref (media);