mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 02:30:35 +00:00
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:
parent
e788fc4e88
commit
421ac85150
5 changed files with 209 additions and 32 deletions
|
@ -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,7 +2740,12 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
|
|||
}
|
||||
|
||||
start_type = stop_type = GST_SEEK_TYPE_NONE;
|
||||
start = stop = GST_CLOCK_TIME_NONE;
|
||||
|
||||
/* 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);
|
||||
|
@ -2698,6 +2754,7 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
|
|||
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;
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue