onvif: Implement and test the Streaming Specification

https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf
This commit is contained in:
Mathieu Duponchelle 2019-04-05 00:48:07 +02:00
parent 7640cb8f21
commit 0f498eabf4
15 changed files with 1727 additions and 23 deletions

View file

@ -1833,6 +1833,8 @@ setup_play_mode (GstRTSPClient * client, GstRTSPContext * ctx,
GstSeekFlags flags = GST_SEEK_FLAG_NONE;
GstRTSPClientClass *klass = GST_RTSP_CLIENT_GET_CLASS (client);
GstRTSPStatusCode rtsp_status_code;
GstClockTime trickmode_interval = 0;
gboolean enable_rate_control = TRUE;
/* parse the range header if we have one */
res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
@ -1876,13 +1878,17 @@ setup_play_mode (GstRTSPClient * client, GstRTSPContext * ctx,
/* give the application a chance to tweak range, flags, or rate */
if (klass->adjust_play_mode != NULL) {
rtsp_status_code =
klass->adjust_play_mode (client, ctx, &range, &flags, &rate);
klass->adjust_play_mode (client, ctx, &range, &flags, &rate,
&trickmode_interval, &enable_rate_control);
if (rtsp_status_code != GST_RTSP_STS_OK)
goto adjust_play_mode_failed;
}
gst_rtsp_media_set_rate_control (ctx->media, enable_rate_control);
/* now do the seek with the seek options */
(void) gst_rtsp_media_seek_full_with_rate (ctx->media, range, flags, rate);
gst_rtsp_media_seek_trickmode (ctx->media, range, flags, rate,
trickmode_interval);
if (range != NULL)
gst_rtsp_range_free (range);
@ -2031,6 +2037,12 @@ handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
g_strdup_printf ("%1.3f", rate));
}
if (klass->adjust_play_response) {
code = klass->adjust_play_response (client, ctx);
if (code != GST_RTSP_STS_OK)
goto adjust_play_response_failed;
}
send_message (client, ctx, ctx->response, FALSE);
/* start playing after sending the response */
@ -2114,6 +2126,12 @@ get_rates_error:
send_generic_response (client, GST_RTSP_STS_INTERNAL_SERVER_ERROR, ctx);
return FALSE;
}
adjust_play_response_failed:
{
GST_ERROR ("client %p: failed to adjust play response", client);
send_generic_response (client, code, ctx);
return FALSE;
}
}
static void

View file

@ -109,7 +109,10 @@ struct _GstRTSPClient {
* RTSP response(ctx->response) via a call to gst_rtsp_message_init_response()
* @make_path_from_uri: called to create path from uri.
* @adjust_play_mode: called to give the application the possibility to adjust
* the range, seek flags, and/or rate. Since 1.18
* the range, seek flags, rate and rate-control. Since 1.18
* @adjust_play_response: called to give the implementation the possibility to
* adjust the response to a play request, for example if extra headers were
* parsed when #GstRTSPClientClass.adjust_play_mode was called. Since 1.18
* @tunnel_http_response: called when a response to the GET request is about to
* be sent for a tunneled connection. The response can be modified. Since: 1.4
*
@ -132,7 +135,12 @@ struct _GstRTSPClientClass {
GstRTSPContext * context,
GstRTSPTimeRange ** range,
GstSeekFlags * flags,
gdouble * rate);
gdouble * rate,
GstClockTime * trickmode_interval,
gboolean * enable_rate_control);
GstRTSPStatusCode (*adjust_play_response) (GstRTSPClient * client,
GstRTSPContext * context);
/* signals */
void (*closed) (GstRTSPClient *client);
void (*new_session) (GstRTSPClient *client, GstRTSPSession *session);
@ -169,7 +177,7 @@ struct _GstRTSPClientClass {
GstRTSPStatusCode (*pre_record_request) (GstRTSPClient *client, GstRTSPContext *ctx);
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE-17];
gpointer _gst_reserved[GST_PADDING_LARGE-18];
};
GST_RTSP_SERVER_API

View file

@ -51,7 +51,7 @@
*
* The state of the media can be controlled with gst_rtsp_media_set_state ().
* 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.
* or gst_rtsp_media_seek_trickmode() 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
@ -146,6 +146,7 @@ struct _GstRTSPMediaPrivate
gboolean do_retransmission; /* protected by lock */
guint latency; /* protected by lock */
GstClock *clock; /* protected by lock */
gboolean do_rate_control; /* protected by lock */
GstRTSPPublishClockMode publish_clock_mode;
/* Dynamic element handling */
@ -167,6 +168,7 @@ struct _GstRTSPMediaPrivate
#define DEFAULT_STOP_ON_DISCONNECT TRUE
#define DEFAULT_MAX_MCAST_TTL 255
#define DEFAULT_BIND_MCAST_ADDRESS FALSE
#define DEFAULT_DO_RATE_CONTROL TRUE
#define DEFAULT_DO_RETRANSMISSION FALSE
@ -471,6 +473,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
priv->max_mcast_ttl = DEFAULT_MAX_MCAST_TTL;
priv->bind_mcast_address = DEFAULT_BIND_MCAST_ADDRESS;
priv->do_rate_control = DEFAULT_DO_RATE_CONTROL;
}
static void
@ -2293,6 +2296,7 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
gst_rtsp_stream_set_retransmission_time (stream, priv->rtx_time);
gst_rtsp_stream_set_buffer_size (stream, priv->buffer_size);
gst_rtsp_stream_set_publish_clock_mode (stream, priv->publish_clock_mode);
gst_rtsp_stream_set_rate_control (stream, priv->do_rate_control);
g_ptr_array_add (priv->streams, stream);
@ -2662,13 +2666,15 @@ gst_rtsp_media_get_status (GstRTSPMedia * media)
}
/**
* gst_rtsp_media_seek_full_with_rate:
* gst_rtsp_media_seek_trickmode:
* @media: a #GstRTSPMedia
* @range: (transfer none): a #GstRTSPTimeRange
* @flags: The minimal set of #GstSeekFlags to use
* @rate: the rate to use in the seek
* @trickmode_interval: The trickmode interval to use for KEY_UNITS trick mode
*
* Seek the pipeline of @media to @range with the given @flags and @rate.
* Seek the pipeline of @media to @range with the given @flags and @rate,
* and @trickmode_interval.
* @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).
@ -2678,8 +2684,9 @@ gst_rtsp_media_get_status (GstRTSPMedia * media)
* Since: 1.18
*/
gboolean
gst_rtsp_media_seek_full_with_rate (GstRTSPMedia * media,
GstRTSPTimeRange * range, GstSeekFlags flags, gdouble rate)
gst_rtsp_media_seek_trickmode (GstRTSPMedia * media,
GstRTSPTimeRange * range, GstSeekFlags flags, gdouble rate,
GstClockTime trickmode_interval)
{
GstRTSPMediaClass *klass;
GstRTSPMediaPrivate *priv;
@ -2789,6 +2796,8 @@ gst_rtsp_media_seek_full_with_rate (GstRTSPMedia * media,
GST_DEBUG ("no position change, no flags set by caller, so not seeking");
res = TRUE;
} else {
GstEvent *seek_event;
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
if (rate < 0.0) {
@ -2801,8 +2810,12 @@ gst_rtsp_media_seek_full_with_rate (GstRTSPMedia * media,
stop_type = temp_type;
}
res = gst_element_seek (priv->pipeline, rate, GST_FORMAT_TIME,
flags, start_type, start, stop_type, stop);
seek_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, start_type,
start, stop_type, stop);
gst_event_set_seek_trickmode_interval (seek_event, trickmode_interval);
res = gst_element_send_event (priv->pipeline, seek_event);
/* and block for the seek to complete */
GST_INFO ("done seeking %d", res);
@ -2880,7 +2893,7 @@ 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);
return gst_rtsp_media_seek_trickmode (media, range, flags, 1.0, 0);
}
/**
@ -2896,8 +2909,8 @@ gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
gboolean
gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range)
{
return gst_rtsp_media_seek_full_with_rate (media, range, GST_SEEK_FLAG_NONE,
1.0);
return gst_rtsp_media_seek_trickmode (media, range, GST_SEEK_FLAG_NONE,
1.0, 0);
}
static void
@ -4728,3 +4741,59 @@ gst_rtsp_media_is_receive_only (GstRTSPMedia * media)
return receive_only;
}
/**
* gst_rtsp_media_set_rate_control:
*
* Define whether @media will follow the Rate-Control=no behaviour as specified
* in the ONVIF replay spec.
*
* Since: 1.18
*/
void
gst_rtsp_media_set_rate_control (GstRTSPMedia * media, gboolean enabled)
{
GstRTSPMediaPrivate *priv;
guint i;
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
GST_LOG_OBJECT (media, "%s rate control", enabled ? "Enabling" : "Disabling");
priv = media->priv;
g_mutex_lock (&priv->lock);
priv->do_rate_control = enabled;
for (i = 0; i < priv->streams->len; i++) {
GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
gst_rtsp_stream_set_rate_control (stream, enabled);
}
g_mutex_unlock (&priv->lock);
}
/**
* gst_rtsp_media_get_rate_control:
*
* Returns: whether @media will follow the Rate-Control=no behaviour as specified
* in the ONVIF replay spec.
*
* Since: 1.18
*/
gboolean
gst_rtsp_media_get_rate_control (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv;
gboolean res;
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
priv = media->priv;
g_mutex_lock (&priv->lock);
res = priv->do_rate_control;
g_mutex_unlock (&priv->lock);
return res;
}

View file

@ -388,10 +388,11 @@ gboolean gst_rtsp_media_seek_full (GstRTSPMedia *media,
GstSeekFlags flags);
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_seek_full_with_rate (GstRTSPMedia *media,
GstRTSPTimeRange *range,
GstSeekFlags flags,
gdouble rate);
gboolean gst_rtsp_media_seek_trickmode (GstRTSPMedia *media,
GstRTSPTimeRange *range,
GstSeekFlags flags,
gdouble rate,
GstClockTime trickmode_interval);
GST_RTSP_SERVER_API
GstClockTimeDiff gst_rtsp_media_seekable (GstRTSPMedia *media);
@ -420,6 +421,12 @@ gboolean gst_rtsp_media_complete_pipeline (GstRTSPMedia * media, GP
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_is_receive_only (GstRTSPMedia * media);
GST_RTSP_SERVER_API
void gst_rtsp_media_set_rate_control (GstRTSPMedia * media, gboolean enabled);
GST_RTSP_SERVER_API
gboolean gst_rtsp_media_get_rate_control (GstRTSPMedia * media);
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMedia, gst_object_unref)
#endif

View file

@ -37,11 +37,14 @@ gst_rtsp_onvif_client_check_requirements (GstRTSPClient * client,
GstRTSPMediaFactory *factory = NULL;
gchar *path = NULL;
gboolean has_backchannel = FALSE;
gboolean has_replay = FALSE;
GString *unsupported = g_string_new ("");
while (*requirements) {
if (strcmp (*requirements, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT) == 0) {
has_backchannel = TRUE;
} else if (strcmp (*requirements, GST_RTSP_ONVIF_REPLAY_REQUIREMENT) == 0) {
has_replay = TRUE;
} else {
if (unsupported->len)
g_string_append (unsupported, ", ");
@ -75,6 +78,22 @@ gst_rtsp_onvif_client_check_requirements (GstRTSPClient * client,
}
}
if (has_replay && !GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory)) {
if (unsupported->len)
g_string_append (unsupported, ", ");
g_string_append (unsupported, GST_RTSP_ONVIF_REPLAY_REQUIREMENT);
} else if (has_replay) {
GstRTSPOnvifMediaFactory *onvif_factory =
GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
if (!gst_rtsp_onvif_media_factory_has_replay_support (onvif_factory)) {
if (unsupported->len)
g_string_append (unsupported, ", ");
g_string_append (unsupported, GST_RTSP_ONVIF_REPLAY_REQUIREMENT);
}
}
out:
if (path)
g_free (path);
@ -86,15 +105,115 @@ out:
return g_string_free (unsupported, FALSE);
}
static GstRTSPStatusCode
gst_rtsp_onvif_client_adjust_play_mode (GstRTSPClient * client,
GstRTSPContext * ctx, GstRTSPTimeRange ** range, GstSeekFlags * flags,
gdouble * rate, GstClockTime * trickmode_interval,
gboolean * enable_rate_control)
{
GstRTSPStatusCode ret = GST_RTSP_STS_BAD_REQUEST;
gchar **split = NULL;
gchar *str;
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_FRAMES,
&str, 0) == GST_RTSP_OK) {
split = g_strsplit (str, "/", 2);
if (!g_strcmp0 (split[0], "intra")) {
if (split[1]) {
guint64 interval;
gchar *end;
interval = g_ascii_strtoull (split[1], &end, 10);
if (!end || *end != '\0') {
GST_ERROR ("Unexpected interval value %s", split[1]);
goto done;
}
*trickmode_interval = interval * GST_MSECOND;
}
*flags |= GST_SEEK_FLAG_TRICKMODE_KEY_UNITS;
} else if (!g_strcmp0 (split[0], "predicted")) {
if (split[1]) {
GST_ERROR ("Predicted frames mode does not allow an interval (%s)",
str);
goto done;
}
*flags |= GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED;
} else {
GST_ERROR ("Invalid frames mode (%s)", str);
goto done;
}
}
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RATE_CONTROL,
&str, 0) == GST_RTSP_OK) {
if (!g_strcmp0 (str, "no")) {
*enable_rate_control = FALSE;
} else if (!g_strcmp0 (str, "yes")) {
*enable_rate_control = TRUE;
} else {
GST_ERROR ("Invalid rate control header: %s", str);
goto done;
}
}
ret = GST_RTSP_STS_OK;
done:
if (split)
g_strfreev (split);
return ret;
}
static GstRTSPStatusCode
gst_rtsp_onvif_client_adjust_play_response (GstRTSPClient * client,
GstRTSPContext * ctx)
{
GstRTSPStatusCode ret = GST_RTSP_STS_OK;
gchar *str;
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RATE_CONTROL,
&str, 0) == GST_RTSP_OK) {
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_RATE_CONTROL,
gst_rtsp_media_get_rate_control (ctx->media) ? "yes" : "no");
}
return ret;
}
static void
gst_rtsp_onvif_client_class_init (GstRTSPOnvifClientClass * klass)
{
GstRTSPClientClass *client_klass = (GstRTSPClientClass *) klass;
client_klass->check_requirements = gst_rtsp_onvif_client_check_requirements;
client_klass->adjust_play_mode = gst_rtsp_onvif_client_adjust_play_mode;
client_klass->adjust_play_response =
gst_rtsp_onvif_client_adjust_play_response;
}
static void
gst_rtsp_onvif_client_init (GstRTSPOnvifClient * client)
{
}
/**
* gst_rtsp_onvif_client_new:
*
* Create a new #GstRTSPOnvifClient instance.
*
* Returns: (transfer full): a new #GstRTSPOnvifClient
* Since: 1.18
*/
GstRTSPClient *
gst_rtsp_onvif_client_new (void)
{
GstRTSPClient *result;
result = g_object_new (GST_TYPE_RTSP_ONVIF_CLIENT, NULL);
return result;
}

View file

@ -59,4 +59,7 @@ struct GstRTSPOnvifClient
GST_RTSP_SERVER_API
GType gst_rtsp_onvif_client_get_type (void);
GST_RTSP_SERVER_API
GstRTSPClient * gst_rtsp_onvif_client_new (void);
#endif /* __GST_RTSP_ONVIF_CLIENT_H__ */

View file

@ -50,6 +50,7 @@ struct GstRTSPOnvifMediaFactoryPrivate
GMutex lock;
gchar *backchannel_launch;
guint backchannel_bandwidth;
gboolean has_replay_support;
};
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPOnvifMediaFactory,
@ -447,6 +448,42 @@ gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory *
return FALSE;
}
/**
* gst_rtsp_onvif_media_factory_has_replay_support:
*
* Returns: %TRUE if ONVIF replay is supported by the media factory.
*
* Since: 1.18
*/
gboolean
gst_rtsp_onvif_media_factory_has_replay_support (GstRTSPOnvifMediaFactory *
factory)
{
gboolean has_replay_support;
g_mutex_lock (&factory->priv->lock);
has_replay_support = factory->priv->has_replay_support;
g_mutex_unlock (&factory->priv->lock);
return has_replay_support;
}
/**
* gst_rtsp_onvif_media_factory_set_replay_support:
*
* Set to %TRUE if ONVIF replay is supported by the media factory.
*
* Since: 1.18
*/
void
gst_rtsp_onvif_media_factory_set_replay_support (GstRTSPOnvifMediaFactory *
factory, gboolean has_replay_support)
{
g_mutex_lock (&factory->priv->lock);
factory->priv->has_replay_support = has_replay_support;
g_mutex_unlock (&factory->priv->lock);
}
/**
* gst_rtsp_onvif_media_factory_set_backchannel_bandwidth:
* @factory: a #GstRTSPMediaFactory

View file

@ -74,6 +74,12 @@ gchar * gst_rtsp_onvif_media_factory_get_backchannel_launch (GstRTSPOnvifMediaFa
GST_RTSP_SERVER_API
gboolean gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory * factory);
GST_RTSP_SERVER_API
gboolean gst_rtsp_onvif_media_factory_has_replay_support (GstRTSPOnvifMediaFactory * factory);
GST_RTSP_SERVER_API
void gst_rtsp_onvif_media_factory_set_replay_support (GstRTSPOnvifMediaFactory * factory, gboolean has_replay_support);
GST_RTSP_SERVER_API
void gst_rtsp_onvif_media_factory_set_backchannel_bandwidth (GstRTSPOnvifMediaFactory * factory, guint bandwidth);
GST_RTSP_SERVER_API

View file

@ -131,6 +131,16 @@ gst_rtsp_onvif_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
if (res) {
GstSDPMedia *smedia =
&g_array_index (sdp->medias, GstSDPMedia, sdp->medias->len - 1);
gchar *x_onvif_track, *media_str;
media_str =
g_ascii_strup (gst_structure_get_string (s, "media"), -1);
x_onvif_track =
g_strdup_printf ("%s%03d", media_str, sdp->medias->len - 1);
gst_sdp_media_add_attribute (smedia, "x-onvif-track",
x_onvif_track);
g_free (x_onvif_track);
g_free (media_str);
if (sinkpad) {
GstRTSPOnvifMedia *onvif_media = GST_RTSP_ONVIF_MEDIA (media);

View file

@ -62,6 +62,7 @@ GST_RTSP_SERVER_API
GstRTSPServer *gst_rtsp_onvif_server_new (void);
#define GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT "www.onvif.org/ver20/backchannel"
#define GST_RTSP_ONVIF_REPLAY_REQUIREMENT "onvif-replay"
#include "rtsp-onvif-client.h"
#include "rtsp-onvif-media-factory.h"

View file

@ -130,6 +130,9 @@ struct _GstRTSPStreamPrivate
guint rtx_pt;
GstClockTime rtx_time;
/* rate control */
gboolean do_rate_control;
/* Forward Error Correction with RFC 5109 */
GstElement *ulpfec_decoder;
GstElement *ulpfec_encoder;
@ -190,6 +193,7 @@ struct _GstRTSPStreamPrivate
GST_RTSP_LOWER_TRANS_TCP
#define DEFAULT_MAX_MCAST_TTL 255
#define DEFAULT_BIND_MCAST_ADDRESS FALSE
#define DEFAULT_DO_RATE_CONTROL TRUE
enum
{
@ -291,6 +295,7 @@ gst_rtsp_stream_init (GstRTSPStream * stream)
priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
priv->max_mcast_ttl = DEFAULT_MAX_MCAST_TTL;
priv->bind_mcast_address = DEFAULT_BIND_MCAST_ADDRESS;
priv->do_rate_control = DEFAULT_DO_RATE_CONTROL;
g_mutex_init (&priv->lock);
@ -3378,6 +3383,11 @@ create_sender_part (GstRTSPStream * stream, const GstRTSPTransport * transport)
return FALSE;
}
if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->payloader),
"onvif-no-rate-control"))
g_object_set (priv->payloader, "onvif-no-rate-control",
!priv->do_rate_control, NULL);
for (i = 0; i < 2; i++) {
gboolean link_tee = FALSE;
/* For the sender we create this bit of pipeline for both
@ -3436,6 +3446,9 @@ create_sender_part (GstRTSPStream * stream, const GstRTSPTransport * transport)
g_object_set (priv->appsink[i], "emit-signals", FALSE, "buffer-list",
TRUE, "max-buffers", 1, NULL);
if (i == 0)
g_object_set (priv->appsink[i], "sync", priv->do_rate_control, NULL);
/* we need to set sync and preroll to FALSE for the sink to avoid
* deadlock. This is only needed for sink sending RTCP data. */
if (i == 1)
@ -3765,6 +3778,9 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
g_signal_connect (priv->session, "on-sender-ssrc-active",
(GCallback) on_sender_ssrc_active, stream);
g_object_set (priv->session, "disable-sr-timestamp", !priv->do_rate_control,
NULL);
if (priv->srcpad) {
/* be notified of caps changes */
priv->caps_sig = g_signal_connect (priv->send_src[0], "notify::caps",
@ -5919,3 +5935,53 @@ gst_rtsp_stream_get_ulpfec_percentage (GstRTSPStream * stream)
return res;
}
/**
* gst_rtsp_stream_set_rate_control:
*
* Define whether @stream will follow the Rate-Control=no behaviour as specified
* in the ONVIF replay spec.
*
* Since: 1.18
*/
void
gst_rtsp_stream_set_rate_control (GstRTSPStream * stream, gboolean enabled)
{
GST_DEBUG_OBJECT (stream, "%s rate control",
enabled ? "Enabling" : "Disabling");
g_mutex_lock (&stream->priv->lock);
stream->priv->do_rate_control = enabled;
if (stream->priv->appsink[0])
g_object_set (stream->priv->appsink[0], "sync", enabled, NULL);
if (stream->priv->payloader
&& g_object_class_find_property (G_OBJECT_GET_CLASS (stream->priv->
payloader), "onvif-no-rate-control"))
g_object_set (stream->priv->payloader, "onvif-no-rate-control", !enabled,
NULL);
if (stream->priv->session) {
g_object_set (stream->priv->session, "disable-sr-timestamp", !enabled,
NULL);
}
g_mutex_unlock (&stream->priv->lock);
}
/**
* gst_rtsp_stream_get_rate_control:
*
* Returns: whether @stream will follow the Rate-Control=no behaviour as specified
* in the ONVIF replay spec.
*
* Since: 1.18
*/
gboolean
gst_rtsp_stream_get_rate_control (GstRTSPStream * stream)
{
gboolean ret;
g_mutex_lock (&stream->priv->lock);
ret = stream->priv->do_rate_control;
g_mutex_unlock (&stream->priv->lock);
return ret;
}

View file

@ -359,6 +359,12 @@ void gst_rtsp_stream_set_ulpfec_percentage (GstRTSPStream *stream,
GST_RTSP_SERVER_API
guint gst_rtsp_stream_get_ulpfec_percentage (GstRTSPStream *stream);
GST_RTSP_SERVER_API
void gst_rtsp_stream_set_rate_control (GstRTSPStream * stream, gboolean enabled);
GST_RTSP_SERVER_API
gboolean gst_rtsp_stream_get_rate_control (GstRTSPStream * stream);
/**
* GstRTSPStreamTransportFilterFunc:
* @stream: a #GstRTSPStream object

View file

@ -109,16 +109,16 @@ GST_START_TEST (test_media_seek)
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_seek_trickmode (media, range,
GST_SEEK_FLAG_NONE, 1.5, 0));
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=10-5", &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_seek_trickmode (media, range,
GST_SEEK_FLAG_NONE, -2.0, 0));
fail_unless (gst_rtsp_media_get_rates (media, &rate, &applied_rate));
fail_unless (rate == -2.0);
fail_unless (applied_rate == 1.0);

1353
tests/check/gst/onvif.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,7 @@ rtsp_server_tests = [
'gst/stream',
'gst/threadpool',
'gst/token',
'gst/onvif',
]
if not get_option('rtspclientsink').disabled()