mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 13:41:48 +00:00
rtspsrc: expose and implement is-live property
This is useful to support the ONVIF case: when is-live is set to FALSE and onvif-rate-control is no, the client can control the rate of delivery and arrange for the server to block and still keep sending when unblocked, without requiring back and forth PAUSE / PLAY requests. This enables, amongst other things, fast frame stepping on the client side. When is-live is FALSE, we don't use a manager at all. This case was actually already pretty well handled by the current code. The standard manager, rtpbin, is simply no longer needed in this case. Applications can instantiate a downloadbuffer after rtspsrc if needed.
This commit is contained in:
parent
75f53631e5
commit
5c7423d73c
2 changed files with 92 additions and 20 deletions
|
@ -282,6 +282,7 @@ gst_rtsp_backchannel_get_type (void)
|
||||||
#define DEFAULT_TEARDOWN_TIMEOUT (100 * GST_MSECOND)
|
#define DEFAULT_TEARDOWN_TIMEOUT (100 * GST_MSECOND)
|
||||||
#define DEFAULT_ONVIF_MODE FALSE
|
#define DEFAULT_ONVIF_MODE FALSE
|
||||||
#define DEFAULT_ONVIF_RATE_CONTROL TRUE
|
#define DEFAULT_ONVIF_RATE_CONTROL TRUE
|
||||||
|
#define DEFAULT_IS_LIVE TRUE
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -328,7 +329,8 @@ enum
|
||||||
PROP_BACKCHANNEL,
|
PROP_BACKCHANNEL,
|
||||||
PROP_TEARDOWN_TIMEOUT,
|
PROP_TEARDOWN_TIMEOUT,
|
||||||
PROP_ONVIF_MODE,
|
PROP_ONVIF_MODE,
|
||||||
PROP_ONVIF_RATE_CONTROL
|
PROP_ONVIF_RATE_CONTROL,
|
||||||
|
PROP_IS_LIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
|
#define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
|
||||||
|
@ -977,6 +979,22 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
|
||||||
DEFAULT_ONVIF_RATE_CONTROL,
|
DEFAULT_ONVIF_RATE_CONTROL,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstRtspSrc:is-live
|
||||||
|
*
|
||||||
|
* Whether to act as a live source. This is useful in combination with
|
||||||
|
* #GstRtspSrc:onvif-rate-control set to %FALSE and usage of the TCP
|
||||||
|
* protocol. In that situation, data delivery rate can be entirely
|
||||||
|
* controlled from the client side, enabling features such as frame
|
||||||
|
* stepping and instantaneous rate changes.
|
||||||
|
*
|
||||||
|
* Since: 1.18
|
||||||
|
*/
|
||||||
|
g_object_class_install_property (gobject_class, PROP_IS_LIVE,
|
||||||
|
g_param_spec_boolean ("is-live", "Is live",
|
||||||
|
"Whether to act as a live source",
|
||||||
|
DEFAULT_IS_LIVE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstRTSPSrc::handle-request:
|
* GstRTSPSrc::handle-request:
|
||||||
* @rtspsrc: a #GstRTSPSrc
|
* @rtspsrc: a #GstRTSPSrc
|
||||||
|
@ -1379,6 +1397,7 @@ gst_rtspsrc_init (GstRTSPSrc * src)
|
||||||
src->teardown_timeout = DEFAULT_TEARDOWN_TIMEOUT;
|
src->teardown_timeout = DEFAULT_TEARDOWN_TIMEOUT;
|
||||||
src->onvif_mode = DEFAULT_ONVIF_MODE;
|
src->onvif_mode = DEFAULT_ONVIF_MODE;
|
||||||
src->onvif_rate_control = DEFAULT_ONVIF_RATE_CONTROL;
|
src->onvif_rate_control = DEFAULT_ONVIF_RATE_CONTROL;
|
||||||
|
src->is_live = DEFAULT_IS_LIVE;
|
||||||
|
|
||||||
/* get a list of all extensions */
|
/* get a list of all extensions */
|
||||||
src->extensions = gst_rtsp_ext_list_get ();
|
src->extensions = gst_rtsp_ext_list_get ();
|
||||||
|
@ -1723,6 +1742,9 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
|
||||||
case PROP_ONVIF_RATE_CONTROL:
|
case PROP_ONVIF_RATE_CONTROL:
|
||||||
rtspsrc->onvif_rate_control = g_value_get_boolean (value);
|
rtspsrc->onvif_rate_control = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_IS_LIVE:
|
||||||
|
rtspsrc->is_live = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -1893,6 +1915,9 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
case PROP_ONVIF_RATE_CONTROL:
|
case PROP_ONVIF_RATE_CONTROL:
|
||||||
g_value_set_boolean (value, rtspsrc->onvif_rate_control);
|
g_value_set_boolean (value, rtspsrc->onvif_rate_control);
|
||||||
break;
|
break;
|
||||||
|
case PROP_IS_LIVE:
|
||||||
|
g_value_set_boolean (value, rtspsrc->is_live);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -2853,11 +2878,14 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
|
||||||
stream->discont = TRUE;
|
stream->discont = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* and continue playing if needed */
|
/* and continue playing if needed. If we are not acting as a live source,
|
||||||
|
* then only the RTSP PLAYING state, set earlier, matters. */
|
||||||
GST_OBJECT_LOCK (src);
|
GST_OBJECT_LOCK (src);
|
||||||
playing = (GST_STATE_PENDING (src) == GST_STATE_VOID_PENDING
|
if (src->is_live) {
|
||||||
&& GST_STATE (src) == GST_STATE_PLAYING)
|
playing = (GST_STATE_PENDING (src) == GST_STATE_VOID_PENDING
|
||||||
|| (GST_STATE_PENDING (src) == GST_STATE_PLAYING);
|
&& GST_STATE (src) == GST_STATE_PLAYING)
|
||||||
|
|| (GST_STATE_PENDING (src) == GST_STATE_PLAYING);
|
||||||
|
}
|
||||||
GST_OBJECT_UNLOCK (src);
|
GST_OBJECT_UNLOCK (src);
|
||||||
|
|
||||||
if (src->version >= GST_RTSP_VERSION_2_0) {
|
if (src->version >= GST_RTSP_VERSION_2_0) {
|
||||||
|
@ -3029,7 +3057,7 @@ gst_rtspsrc_handle_internal_src_query (GstPad * pad, GstObject * parent,
|
||||||
{
|
{
|
||||||
/* we are live with a min latency of 0 and unlimited max latency, this
|
/* we are live with a min latency of 0 and unlimited max latency, this
|
||||||
* result will be updated by the session manager if there is any. */
|
* result will be updated by the session manager if there is any. */
|
||||||
gst_query_set_latency (query, TRUE, 0, -1);
|
gst_query_set_latency (query, src->is_live, 0, -1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -3502,7 +3530,10 @@ on_timeout (GObject * session, GObject * source, GstRTSPStream * stream)
|
||||||
"stream-number", G_TYPE_INT, stream->id, "ssrc", G_TYPE_UINT,
|
"stream-number", G_TYPE_INT, stream->id, "ssrc", G_TYPE_UINT,
|
||||||
stream->ssrc, NULL)));
|
stream->ssrc, NULL)));
|
||||||
|
|
||||||
on_timeout_common (session, source, stream);
|
/* In non-live mode, timeouts can occur if we are PAUSED, this doesn't mean
|
||||||
|
* the stream is EOS, it may simply be blocked */
|
||||||
|
if (src->is_live || !src->interleaved)
|
||||||
|
on_timeout_common (session, source, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -3830,6 +3861,9 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream,
|
||||||
gchar *name;
|
gchar *name;
|
||||||
GstStateChangeReturn ret;
|
GstStateChangeReturn ret;
|
||||||
|
|
||||||
|
if (!src->is_live)
|
||||||
|
goto use_no_manager;
|
||||||
|
|
||||||
/* find a manager */
|
/* find a manager */
|
||||||
if (gst_rtsp_transport_get_manager (transport->trans, &manager, 0) < 0)
|
if (gst_rtsp_transport_get_manager (transport->trans, &manager, 0) < 0)
|
||||||
goto no_manager;
|
goto no_manager;
|
||||||
|
@ -5338,6 +5372,11 @@ gst_rtspsrc_handle_data (GstRTSPSrc * src, GstRTSPMessage * message)
|
||||||
GstRTSPStream *ostream = (GstRTSPStream *) streams->data;
|
GstRTSPStream *ostream = (GstRTSPStream *) streams->data;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
|
||||||
|
/* Activate in advance so that the stream-start event is registered */
|
||||||
|
if (stream->srcpad) {
|
||||||
|
gst_pad_set_active (stream->srcpad, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
stream_id =
|
stream_id =
|
||||||
g_strdup_printf ("%s/%d", g_checksum_get_string (cs), ostream->id);
|
g_strdup_printf ("%s/%d", g_checksum_get_string (cs), ostream->id);
|
||||||
event = gst_event_new_stream_start (stream_id);
|
event = gst_event_new_stream_start (stream_id);
|
||||||
|
@ -7986,6 +8025,12 @@ gst_rtspsrc_open (GstRTSPSrc * src, gboolean async)
|
||||||
if ((ret = gst_rtspsrc_open_from_sdp (src, src->sdp, async)) < 0)
|
if ((ret = gst_rtspsrc_open_from_sdp (src, src->sdp, async)) < 0)
|
||||||
goto open_failed;
|
goto open_failed;
|
||||||
|
|
||||||
|
if (src->initial_seek) {
|
||||||
|
if (!gst_rtspsrc_perform_seek (src, src->initial_seek))
|
||||||
|
goto initial_seek_failed;
|
||||||
|
gst_event_replace (&src->initial_seek, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (async)
|
if (async)
|
||||||
gst_rtspsrc_loop_end_cmd (src, CMD_OPEN, ret);
|
gst_rtspsrc_loop_end_cmd (src, CMD_OPEN, ret);
|
||||||
|
@ -8005,6 +8050,13 @@ open_failed:
|
||||||
src->open_error = TRUE;
|
src->open_error = TRUE;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
initial_seek_failed:
|
||||||
|
{
|
||||||
|
GST_WARNING_OBJECT (src, "Failed to perform initial seek");
|
||||||
|
ret = GST_RTSP_ERROR;
|
||||||
|
src->open_error = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstRTSPResult
|
static GstRTSPResult
|
||||||
|
@ -9028,17 +9080,22 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
|
||||||
/* first attempt, don't ignore timeouts */
|
/* first attempt, don't ignore timeouts */
|
||||||
rtspsrc->ignore_timeout = FALSE;
|
rtspsrc->ignore_timeout = FALSE;
|
||||||
rtspsrc->open_error = FALSE;
|
rtspsrc->open_error = FALSE;
|
||||||
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, 0);
|
if (rtspsrc->is_live)
|
||||||
|
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, 0);
|
||||||
|
else
|
||||||
|
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PLAY, 0);
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
set_manager_buffer_mode (rtspsrc);
|
set_manager_buffer_mode (rtspsrc);
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
/* unblock the tcp tasks and make the loop waiting */
|
if (rtspsrc->is_live) {
|
||||||
if (gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_WAIT, CMD_LOOP)) {
|
/* unblock the tcp tasks and make the loop waiting */
|
||||||
/* make sure it is waiting before we send PAUSE or PLAY below */
|
if (gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_WAIT, CMD_LOOP)) {
|
||||||
GST_RTSP_STREAM_LOCK (rtspsrc);
|
/* make sure it is waiting before we send PAUSE or PLAY below */
|
||||||
GST_RTSP_STREAM_UNLOCK (rtspsrc);
|
GST_RTSP_STREAM_LOCK (rtspsrc);
|
||||||
|
GST_RTSP_STREAM_UNLOCK (rtspsrc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
@ -9056,16 +9113,22 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
|
||||||
ret = GST_STATE_CHANGE_SUCCESS;
|
ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
if (rtspsrc->is_live)
|
||||||
|
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||||
|
else
|
||||||
|
ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PLAY, 0);
|
if (rtspsrc->is_live)
|
||||||
|
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PLAY, 0);
|
||||||
ret = GST_STATE_CHANGE_SUCCESS;
|
ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
/* send pause request and keep the idle task around */
|
if (rtspsrc->is_live) {
|
||||||
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PAUSE, CMD_LOOP);
|
/* send pause request and keep the idle task around */
|
||||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_PAUSE, CMD_LOOP);
|
||||||
|
}
|
||||||
|
ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
gst_rtspsrc_loop_send_cmd_and_wait (rtspsrc, CMD_CLOSE, CMD_ALL,
|
gst_rtspsrc_loop_send_cmd_and_wait (rtspsrc, CMD_CLOSE, CMD_ALL,
|
||||||
|
@ -9109,8 +9172,14 @@ gst_rtspsrc_send_event (GstElement * element, GstEvent * event)
|
||||||
rtspsrc = GST_RTSPSRC (element);
|
rtspsrc = GST_RTSPSRC (element);
|
||||||
|
|
||||||
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
|
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
|
||||||
res = gst_rtspsrc_perform_seek (rtspsrc, event);
|
if (rtspsrc->state >= GST_RTSP_STATE_READY) {
|
||||||
gst_event_unref (event);
|
res = gst_rtspsrc_perform_seek (rtspsrc, event);
|
||||||
|
gst_event_unref (event);
|
||||||
|
} else {
|
||||||
|
/* Store for later use */
|
||||||
|
res = TRUE;
|
||||||
|
rtspsrc->initial_seek = event;
|
||||||
|
}
|
||||||
} else if (GST_EVENT_IS_DOWNSTREAM (event)) {
|
} else if (GST_EVENT_IS_DOWNSTREAM (event)) {
|
||||||
res = gst_rtspsrc_push_event (rtspsrc, event);
|
res = gst_rtspsrc_push_event (rtspsrc, event);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -277,6 +277,7 @@ struct _GstRTSPSrc {
|
||||||
GstClockTime teardown_timeout;
|
GstClockTime teardown_timeout;
|
||||||
gboolean onvif_mode;
|
gboolean onvif_mode;
|
||||||
gboolean onvif_rate_control;
|
gboolean onvif_rate_control;
|
||||||
|
gboolean is_live;
|
||||||
|
|
||||||
/* state */
|
/* state */
|
||||||
GstRTSPState state;
|
GstRTSPState state;
|
||||||
|
@ -320,6 +321,8 @@ struct _GstRTSPSrc {
|
||||||
|
|
||||||
GstRTSPVersion default_version;
|
GstRTSPVersion default_version;
|
||||||
GstRTSPVersion version;
|
GstRTSPVersion version;
|
||||||
|
|
||||||
|
GstEvent *initial_seek;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstRTSPSrcClass {
|
struct _GstRTSPSrcClass {
|
||||||
|
|
Loading…
Reference in a new issue