diff --git a/girs/GstRtspServer-1.0.gir b/girs/GstRtspServer-1.0.gir
index 8dcbc918b6..549e616ff7 100644
--- a/girs/GstRtspServer-1.0.gir
+++ b/girs/GstRtspServer-1.0.gir
@@ -3767,6 +3767,34 @@ g_object_unref() after usage.
+
+ Get ensure-keyunit-on-start flag.
+
+
+ The ensure-keyunit-on-start flag.
+
+
+
+
+ a #GstRTSPMedia
+
+
+
+
+
+ Get ensure-keyunit-on-start-timeout time.
+
+
+ The ensure-keyunit-on-start-timeout time.
+
+
+
+
+ a #GstRTSPMedia
+
+
+
+
Get the latency that is used for receiving media.
@@ -4415,6 +4443,48 @@ INADDR_ANY.
+
+ Set whether or not a keyunit should be ensured when a client connects. It
+will also configure the streams to drop delta units to ensure that they start
+on a keyunit.
+
+Note that this will only affect non-shared medias for now.
+
+
+
+
+
+
+ a #GstRTSPMedia
+
+
+
+ the new value
+
+
+
+
+
+ Sets the maximum allowed time before the first keyunit is considered
+expired.
+
+Note that this will only have an effect when ensure-keyunit-on-start is
+enabled.
+
+
+
+
+
+
+ a #GstRTSPMedia
+
+
+
+ the new value
+
+
+
+
Set or unset if an EOS event will be sent to the pipeline for @media before
it is unprepared.
@@ -4857,6 +4927,22 @@ when the media was not in the suspended state.
+
+ Whether or not a keyunit should be ensured when a client connects. It
+will also configure the streams to drop delta units to ensure that they start
+on a keyunit.
+
+Note that this will only affect non-shared medias for now.
+
+
+
+ The maximum allowed time before the first keyunit is considered
+expired.
+
+Note that this will only have an effect when ensure-keyunit-on-start is
+enabled.
+
+
@@ -5568,6 +5654,34 @@ of all medias created from this factory.
+
+ Get ensure-keyunit-on-start flag.
+
+
+ The ensure-keyunit-on-start flag.
+
+
+
+
+ a #GstRTSPMediaFactory
+
+
+
+
+
+ Get ensure-keyunit-on-start-timeout time.
+
+
+ The ensure-keyunit-on-start-timeout time.
+
+
+
+
+ a #GstRTSPMediaFactory
+
+
+
+
Get the latency that is used for receiving media
@@ -5928,6 +6042,41 @@ receiving media
+
+ If media from this factory should ensure a key unit when a client connects.
+
+
+
+
+
+
+ a #GstRTSPMediaFactory
+
+
+
+ the new value
+
+
+
+
+
+ Configures medias from this factory to consider keyunits older than timeout
+to be expired. Expired keyunits will be discarded.
+
+
+
+
+
+
+ a #GstRTSPMediaFactory
+
+
+
+ the new value
+
+
+
+
Configure if media created from this factory will have an EOS sent to the
pipeline before shutdown.
@@ -6212,6 +6361,25 @@ when a client disconnects without sending TEARDOWN.
Whether the created media should send and receive RTCP
+
+ If media from this factory should ensure a key unit when a client connects.
+
+This property will ensure that the stream always starts on a key unit
+instead of a delta unit which the client would not be able to decode.
+
+Note that this will only affect non-shared medias for now.
+
+
+
+ Timeout in milliseconds used to determine if a keyunit should be discarded
+when a client connects.
+
+If the timeout has been reached a new keyframe will be forced, otherwise
+the currently blocking keyframe will be used.
+
+This options is only relevant when ensure-keyunit-on-start is enabled.
+
+
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/meson.build b/subprojects/gst-rtsp-server/gst/rtsp-server/meson.build
index 15cb124763..5b830a720f 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/meson.build
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/meson.build
@@ -55,7 +55,7 @@ rtsp_server_headers = files(
install_headers(rtsp_server_headers, subdir : 'gstreamer-1.0/gst/rtsp-server')
-gst_rtsp_server_deps = [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep, gst_dep]
+gst_rtsp_server_deps = [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep, gst_dep, gstvideo_dep]
gst_rtsp_server = library('gstrtspserver-@0@'.format(api_version),
rtsp_server_sources,
include_directories : rtspserver_incs,
@@ -104,6 +104,6 @@ gst_libraries += [[pkg_name, library_def]]
gst_rtsp_server_dep = declare_dependency(link_with : gst_rtsp_server,
include_directories : rtspserver_incs,
sources : rtsp_server_gen_sources,
- dependencies : [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep])
+ dependencies : [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep, gstvideo_dep])
meson.override_dependency(pkg_name, gst_rtsp_server_dep)
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.c b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.c
index 9f0a121c42..ac432924d1 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.c
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.c
@@ -59,6 +59,8 @@ struct _GstRTSPMediaFactoryPrivate
GstRTSPProfile profiles;
GstRTSPLowerTrans protocols;
guint buffer_size;
+ gboolean ensure_keyunit_on_start;
+ guint ensure_keyunit_on_start_timeout;
gint dscp_qos;
GstRTSPAddressPool *pool;
GstRTSPTransportMode transport_mode;
@@ -90,6 +92,8 @@ struct _GstRTSPMediaFactoryPrivate
#define DEFAULT_PROTOCOLS GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
GST_RTSP_LOWER_TRANS_TCP
#define DEFAULT_BUFFER_SIZE 0x80000
+#define DEFAULT_ENSURE_KEYUNIT_ON_START FALSE
+#define DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT 100
#define DEFAULT_LATENCY 200
#define DEFAULT_MAX_MCAST_TTL 255
#define DEFAULT_BIND_MCAST_ADDRESS FALSE
@@ -109,6 +113,8 @@ enum
PROP_PROFILES,
PROP_PROTOCOLS,
PROP_BUFFER_SIZE,
+ PROP_ENSURE_KEYUNIT_ON_START,
+ PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT,
PROP_LATENCY,
PROP_TRANSPORT_MODE,
PROP_STOP_ON_DISCONNECT,
@@ -214,6 +220,48 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
"The kernel UDP buffer size to use", 0, G_MAXUINT,
DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstRTSPMediaFactory:ensure-keyunit-on-start:
+ *
+ * If media from this factory should ensure a key unit when a client connects.
+ *
+ * This property will ensure that the stream always starts on a key unit
+ * instead of a delta unit which the client would not be able to decode.
+ *
+ * Note that this will only affect non-shared medias for now.
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class, PROP_ENSURE_KEYUNIT_ON_START,
+ g_param_spec_boolean ("ensure-keyunit-on-start",
+ "Ensure keyunit on start",
+ "If media from this factory should ensure a key unit when a client "
+ "connects.",
+ DEFAULT_ENSURE_KEYUNIT_ON_START,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstRTSPMediaFactory:ensure-keyunit-on-start-timeout:
+ *
+ * Timeout in milliseconds used to determine if a keyunit should be discarded
+ * when a client connects.
+ *
+ * If the timeout has been reached a new keyframe will be forced, otherwise
+ * the currently blocking keyframe will be used.
+ *
+ * This options is only relevant when ensure-keyunit-on-start is enabled.
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT,
+ g_param_spec_uint ("ensure-keyunit-on-start-timeout",
+ "Timeout for discarding old keyunit on start",
+ "Timeout in milliseconds used to determine if a keyunit should be "
+ "discarded when a client connects.", 0, G_MAXUINT,
+ DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (gobject_class, PROP_LATENCY,
g_param_spec_uint ("latency", "Latency",
"Latency used for receiving media in milliseconds", 0, G_MAXUINT,
@@ -304,6 +352,9 @@ gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
priv->profiles = DEFAULT_PROFILES;
priv->protocols = DEFAULT_PROTOCOLS;
priv->buffer_size = DEFAULT_BUFFER_SIZE;
+ priv->ensure_keyunit_on_start = DEFAULT_ENSURE_KEYUNIT_ON_START;
+ priv->ensure_keyunit_on_start_timeout =
+ DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT;
priv->latency = DEFAULT_LATENCY;
priv->transport_mode = DEFAULT_TRANSPORT_MODE;
priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
@@ -373,6 +424,14 @@ gst_rtsp_media_factory_get_property (GObject * object, guint propid,
g_value_set_uint (value,
gst_rtsp_media_factory_get_buffer_size (factory));
break;
+ case PROP_ENSURE_KEYUNIT_ON_START:
+ g_value_set_boolean (value,
+ gst_rtsp_media_factory_get_ensure_keyunit_on_start (factory));
+ break;
+ case PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT:
+ g_value_set_uint (value,
+ gst_rtsp_media_factory_get_ensure_keyunit_on_start_timeout (factory));
+ break;
case PROP_LATENCY:
g_value_set_uint (value, gst_rtsp_media_factory_get_latency (factory));
break;
@@ -438,6 +497,14 @@ gst_rtsp_media_factory_set_property (GObject * object, guint propid,
gst_rtsp_media_factory_set_buffer_size (factory,
g_value_get_uint (value));
break;
+ case PROP_ENSURE_KEYUNIT_ON_START:
+ gst_rtsp_media_factory_set_ensure_keyunit_on_start (factory,
+ g_value_get_boolean (value));
+ break;
+ case PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT:
+ gst_rtsp_media_factory_set_ensure_keyunit_on_start_timeout (factory,
+ g_value_get_uint (value));
+ break;
case PROP_LATENCY:
gst_rtsp_media_factory_set_latency (factory, g_value_get_uint (value));
break;
@@ -853,6 +920,111 @@ gst_rtsp_media_factory_get_buffer_size (GstRTSPMediaFactory * factory)
return result;
}
+/**
+ * gst_rtsp_media_factory_set_ensure_keyunit_on_start:
+ * @factory: a #GstRTSPMediaFactory
+ * @ensure_keyunit_on_start: the new value
+ *
+ * If media from this factory should ensure a key unit when a client connects.
+ *
+ * Since: 1.24
+ */
+void
+gst_rtsp_media_factory_set_ensure_keyunit_on_start (GstRTSPMediaFactory *
+ factory, gboolean ensure_keyunit_on_start)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ priv->ensure_keyunit_on_start = ensure_keyunit_on_start;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+}
+
+/**
+ * gst_rtsp_media_factory_get_ensure_keyunit_on_start:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Get ensure-keyunit-on-start flag.
+ *
+ * Returns: The ensure-keyunit-on-start flag.
+ *
+ * Since: 1.24
+ */
+gboolean
+gst_rtsp_media_factory_get_ensure_keyunit_on_start (GstRTSPMediaFactory *
+ factory)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ result = priv->ensure_keyunit_on_start;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+
+ return result;
+}
+
+/**
+ * gst_rtsp_media_factory_set_ensure_keyunit_on_start_timeout:
+ * @factory: a #GstRTSPMediaFactory
+ * @timeout: the new value
+ *
+ * Configures medias from this factory to consider keyunits older than timeout
+ * to be expired. Expired keyunits will be discarded.
+ *
+ * Since: 1.24
+ */
+void
+gst_rtsp_media_factory_set_ensure_keyunit_on_start_timeout (GstRTSPMediaFactory
+ * factory, guint timeout)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ priv->ensure_keyunit_on_start_timeout = timeout;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+}
+
+/**
+ * gst_rtsp_media_factory_get_ensure_keyunit_on_start_timeout:
+ * @factory: a #GstRTSPMediaFactory
+ *
+ * Get ensure-keyunit-on-start-timeout time.
+ *
+ * Returns: The ensure-keyunit-on-start-timeout time.
+ *
+ * Since: 1.24
+ */
+guint
+gst_rtsp_media_factory_get_ensure_keyunit_on_start_timeout (GstRTSPMediaFactory
+ * factory)
+{
+ GstRTSPMediaFactoryPrivate *priv;
+ guint result;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
+
+ priv = factory->priv;
+
+ GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+ result = priv->ensure_keyunit_on_start_timeout;
+ GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+
+ return result;
+}
+
/**
* gst_rtsp_media_factory_set_dscp_qos:
* @factory: a #GstRTSPMediaFactory
@@ -1878,6 +2050,8 @@ default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
/* We need to call this prior to collecting streams */
gst_rtsp_media_set_enable_rtcp (media, enable_rtcp);
+ gst_rtsp_media_set_ensure_keyunit_on_start (media,
+ gst_rtsp_media_factory_get_ensure_keyunit_on_start (factory));
gst_rtsp_media_collect_streams (media);
@@ -1928,6 +2102,8 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
GstRTSPMediaFactoryPrivate *priv = factory->priv;
gboolean shared, eos_shutdown, stop_on_disconnect;
guint size;
+ gboolean ensure_keyunit_on_start;
+ guint ensure_keyunit_on_start_timeout;
gint dscp_qos;
GstRTSPSuspendMode suspend_mode;
GstRTSPProfile profiles;
@@ -1949,6 +2125,8 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
shared = priv->shared;
eos_shutdown = priv->eos_shutdown;
size = priv->buffer_size;
+ ensure_keyunit_on_start = priv->ensure_keyunit_on_start;
+ ensure_keyunit_on_start_timeout = priv->ensure_keyunit_on_start_timeout;
dscp_qos = priv->dscp_qos;
profiles = priv->profiles;
protocols = priv->protocols;
@@ -1966,6 +2144,9 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
gst_rtsp_media_set_shared (media, shared);
gst_rtsp_media_set_eos_shutdown (media, eos_shutdown);
gst_rtsp_media_set_buffer_size (media, size);
+ gst_rtsp_media_set_ensure_keyunit_on_start (media, ensure_keyunit_on_start);
+ gst_rtsp_media_set_ensure_keyunit_on_start_timeout (media,
+ ensure_keyunit_on_start_timeout);
gst_rtsp_media_set_dscp_qos (media, dscp_qos);
gst_rtsp_media_set_profiles (media, profiles);
gst_rtsp_media_set_protocols (media, protocols);
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.h b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.h
index 8e847fda33..242b0f46c9 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.h
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media-factory.h
@@ -191,6 +191,21 @@ void gst_rtsp_media_factory_set_buffer_size (GstRTSPMediaFacto
GST_RTSP_SERVER_API
guint gst_rtsp_media_factory_get_buffer_size (GstRTSPMediaFactory * factory);
+GST_RTSP_SERVER_API
+void gst_rtsp_media_factory_set_ensure_keyunit_on_start (GstRTSPMediaFactory * factory,
+ gboolean ensure_keyunit_on_start);
+
+GST_RTSP_SERVER_API
+gboolean gst_rtsp_media_factory_get_ensure_keyunit_on_start (GstRTSPMediaFactory * factory);
+
+GST_RTSP_SERVER_API
+void gst_rtsp_media_factory_set_ensure_keyunit_on_start_timeout (GstRTSPMediaFactory * factory,
+ guint timeout);
+
+GST_RTSP_SERVER_API
+guint gst_rtsp_media_factory_get_ensure_keyunit_on_start_timeout (GstRTSPMediaFactory * factory);
+
+
GST_RTSP_SERVER_API
void gst_rtsp_media_factory_set_retransmission_time (GstRTSPMediaFactory * factory,
GstClockTime time);
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.c b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.c
index c70fb97304..66a640e95a 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.c
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.c
@@ -77,6 +77,8 @@
#include
#include
+#include
+
#define AES_128_KEY_LEN 16
#define AES_256_KEY_LEN 32
@@ -107,6 +109,10 @@ struct _GstRTSPMediaPrivate
gboolean reused;
gboolean eos_shutdown;
guint buffer_size;
+ gboolean ensure_keyunit_on_start;
+ guint ensure_keyunit_on_start_timeout;
+ gboolean keyunit_is_expired; /* if the blocking keyunit has expired */
+ GSource *keyunit_expiration_source;
gint dscp_qos;
GstRTSPAddressPool *pool;
gchar *multicast_iface;
@@ -172,6 +178,8 @@ struct _GstRTSPMediaPrivate
GST_RTSP_LOWER_TRANS_TCP
#define DEFAULT_EOS_SHUTDOWN FALSE
#define DEFAULT_BUFFER_SIZE 0x80000
+#define DEFAULT_ENSURE_KEYUNIT_ON_START FALSE
+#define DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT 100
#define DEFAULT_DSCP_QOS (-1)
#define DEFAULT_TIME_PROVIDER FALSE
#define DEFAULT_LATENCY 200
@@ -197,6 +205,8 @@ enum
PROP_PROTOCOLS,
PROP_EOS_SHUTDOWN,
PROP_BUFFER_SIZE,
+ PROP_ENSURE_KEYUNIT_ON_START,
+ PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT,
PROP_ELEMENT,
PROP_TIME_PROVIDER,
PROP_LATENCY,
@@ -371,6 +381,44 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
"The kernel UDP buffer size to use", 0, G_MAXUINT,
DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+/**
+ * GstRTSPMedia:ensure-keyunit-on-start:
+ *
+ * Whether or not a keyunit should be ensured when a client connects. It
+ * will also configure the streams to drop delta units to ensure that they start
+ * on a keyunit.
+ *
+ * Note that this will only affect non-shared medias for now.
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class, PROP_ENSURE_KEYUNIT_ON_START,
+ g_param_spec_boolean ("ensure-keyunit-on-start",
+ "Ensure keyunit on start",
+ "Whether the stream will ensure a keyunit when a client connects.",
+ DEFAULT_ENSURE_KEYUNIT_ON_START,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+/**
+ * GstRTSPMedia:ensure-keyunit-on-start-timeout:
+ *
+ * The maximum allowed time before the first keyunit is considered
+ * expired.
+ *
+ * Note that this will only have an effect when ensure-keyunit-on-start is
+ * enabled.
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT,
+ g_param_spec_uint ("ensure-keyunit-on-start-timeout",
+ "Timeout for discarding old keyunit on start",
+ "Timeout in milliseconds used to determine if a keyunit should be "
+ "discarded when a client connects.", 0, G_MAXUINT,
+ DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (gobject_class, PROP_ELEMENT,
g_param_spec_object ("element", "The Element",
"The GstBin to use for streaming the media", GST_TYPE_ELEMENT,
@@ -504,6 +552,11 @@ gst_rtsp_media_init (GstRTSPMedia * media)
priv->protocols = DEFAULT_PROTOCOLS;
priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
priv->buffer_size = DEFAULT_BUFFER_SIZE;
+ priv->ensure_keyunit_on_start = DEFAULT_ENSURE_KEYUNIT_ON_START;
+ priv->ensure_keyunit_on_start_timeout =
+ DEFAULT_ENSURE_KEYUNIT_ON_START_TIMEOUT;
+ priv->keyunit_is_expired = FALSE;
+ priv->keyunit_expiration_source = NULL;
priv->time_provider = DEFAULT_TIME_PROVIDER;
priv->transport_mode = DEFAULT_TRANSPORT_MODE;
priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
@@ -554,6 +607,12 @@ gst_rtsp_media_finalize (GObject * obj)
g_cond_clear (&priv->cond);
g_rec_mutex_clear (&priv->state_lock);
+ if (priv->keyunit_expiration_source != NULL) {
+ g_source_destroy (priv->keyunit_expiration_source);
+ g_source_unref (priv->keyunit_expiration_source);
+ priv->keyunit_expiration_source = NULL;
+ }
+
G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj);
}
@@ -588,6 +647,14 @@ gst_rtsp_media_get_property (GObject * object, guint propid,
case PROP_BUFFER_SIZE:
g_value_set_uint (value, gst_rtsp_media_get_buffer_size (media));
break;
+ case PROP_ENSURE_KEYUNIT_ON_START:
+ g_value_set_boolean (value,
+ gst_rtsp_media_get_ensure_keyunit_on_start (media));
+ break;
+ case PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT:
+ g_value_set_uint (value,
+ gst_rtsp_media_get_ensure_keyunit_on_start_timeout (media));
+ break;
case PROP_TIME_PROVIDER:
g_value_set_boolean (value, gst_rtsp_media_is_time_provider (media));
break;
@@ -649,6 +716,14 @@ gst_rtsp_media_set_property (GObject * object, guint propid,
case PROP_BUFFER_SIZE:
gst_rtsp_media_set_buffer_size (media, g_value_get_uint (value));
break;
+ case PROP_ENSURE_KEYUNIT_ON_START:
+ gst_rtsp_media_set_ensure_keyunit_on_start (media,
+ g_value_get_boolean (value));
+ break;
+ case PROP_ENSURE_KEYUNIT_ON_START_TIMEOUT:
+ gst_rtsp_media_set_ensure_keyunit_on_start_timeout (media,
+ g_value_get_uint (value));
+ break;
case PROP_TIME_PROVIDER:
gst_rtsp_media_use_time_provider (media, g_value_get_boolean (value));
break;
@@ -1487,6 +1562,116 @@ gst_rtsp_media_get_buffer_size (GstRTSPMedia * media)
return res;
}
+/**
+ * gst_rtsp_media_set_ensure_keyunit_on_start:
+ * @media: a #GstRTSPMedia
+ * @ensure_keyunit_on_start: the new value
+ *
+ * Set whether or not a keyunit should be ensured when a client connects. It
+ * will also configure the streams to drop delta units to ensure that they start
+ * on a keyunit.
+ *
+ * Note that this will only affect non-shared medias for now.
+ *
+ * Since: 1.24
+ */
+void
+gst_rtsp_media_set_ensure_keyunit_on_start (GstRTSPMedia * media,
+ gboolean ensure_keyunit_on_start)
+{
+ GstRTSPMediaPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ priv->ensure_keyunit_on_start = ensure_keyunit_on_start;
+ g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_media_get_ensure_keyunit_on_start:
+ * @media: a #GstRTSPMedia
+ *
+ * Get ensure-keyunit-on-start flag.
+ *
+ * Returns: The ensure-keyunit-on-start flag.
+ *
+ * Since: 1.24
+ */
+gboolean
+gst_rtsp_media_get_ensure_keyunit_on_start (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ result = priv->ensure_keyunit_on_start;
+ g_mutex_unlock (&priv->lock);
+
+ return result;
+}
+
+/**
+ * gst_rtsp_media_set_ensure_keyunit_on_start_timeout:
+ * @media: a #GstRTSPMedia
+ * @timeout: the new value
+ *
+ * Sets the maximum allowed time before the first keyunit is considered
+ * expired.
+ *
+ * Note that this will only have an effect when ensure-keyunit-on-start is
+ * enabled.
+ *
+ * Since: 1.24
+ */
+void
+gst_rtsp_media_set_ensure_keyunit_on_start_timeout (GstRTSPMedia * media,
+ guint timeout)
+{
+ GstRTSPMediaPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ priv->ensure_keyunit_on_start_timeout = timeout;
+ g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_media_get_ensure_keyunit_on_start_timeout
+ * @media: a #GstRTSPMedia
+ *
+ * Get ensure-keyunit-on-start-timeout time.
+ *
+ * Returns: The ensure-keyunit-on-start-timeout time.
+ *
+ * Since: 1.24
+ */
+guint
+gst_rtsp_media_get_ensure_keyunit_on_start_timeout (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+ guint result;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ result = priv->ensure_keyunit_on_start_timeout;
+ g_mutex_unlock (&priv->lock);
+
+ return result;
+}
+
static void
do_set_dscp_qos (GstRTSPStream * stream, gint * dscp_qos)
{
@@ -2502,6 +2687,7 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
gst_rtsp_stream_set_protocols (stream, priv->protocols);
gst_rtsp_stream_set_retransmission_time (stream, priv->rtx_time);
gst_rtsp_stream_set_buffer_size (stream, priv->buffer_size);
+ gst_rtsp_stream_set_drop_delta_units (stream, priv->ensure_keyunit_on_start);
gst_rtsp_stream_set_publish_clock_mode (stream, priv->publish_clock_mode);
gst_rtsp_stream_set_rate_control (stream, priv->do_rate_control);
@@ -2837,6 +3023,23 @@ media_streams_set_blocked (GstRTSPMedia * media, gboolean blocked)
priv->blocking_msg_received = 0;
}
+static void
+stream_install_drop_probe (GstRTSPStream * stream, gpointer user_data)
+{
+ if (!gst_rtsp_stream_is_complete (stream))
+ return;
+
+ gst_rtsp_stream_install_drop_probe (stream);
+}
+
+static void
+media_streams_install_drop_probe (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+
+ g_ptr_array_foreach (priv->streams, (GFunc) stream_install_drop_probe, NULL);
+}
+
static void
gst_rtsp_media_set_status (GstRTSPMedia * media, GstRTSPMediaStatus status)
{
@@ -4590,6 +4793,15 @@ do_set_seqnum (GstRTSPStream * stream)
}
}
+static gboolean
+enable_keyunit_expired (GstRTSPMedia * media)
+{
+ GST_DEBUG_OBJECT (media, "keyunit has expired");
+ media->priv->keyunit_is_expired = TRUE;
+
+ return G_SOURCE_REMOVE;
+}
+
/* call with state_lock */
static gboolean
default_suspend (GstRTSPMedia * media)
@@ -4629,6 +4841,20 @@ default_suspend (GstRTSPMedia * media)
if (ret != GST_STATE_CHANGE_FAILURE && ret != GST_STATE_CHANGE_ASYNC)
priv->expected_async_done = FALSE;
+ /* set expiration date on buffer in case of delayed PLAY request */
+ if (priv->ensure_keyunit_on_start) {
+ /* no need to install the timer if configured to trigger immediately */
+ if (priv->ensure_keyunit_on_start_timeout == 0) {
+ enable_keyunit_expired (media);
+ } else {
+ priv->keyunit_expiration_source =
+ g_timeout_source_new (priv->ensure_keyunit_on_start_timeout);
+ g_source_set_callback (priv->keyunit_expiration_source,
+ G_SOURCE_FUNC (enable_keyunit_expired), (gpointer) media, NULL);
+ g_source_attach (priv->keyunit_expiration_source, priv->thread->context);
+ }
+ }
+
return TRUE;
/* ERRORS */
@@ -4684,6 +4910,7 @@ gst_rtsp_media_suspend (GstRTSPMedia * media)
}
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_SUSPENDED);
+
done:
g_rec_mutex_unlock (&priv->state_lock);
@@ -4710,6 +4937,79 @@ suspend_failed:
}
}
+/* Call with state_lock */
+static gboolean
+ensure_new_keyunit (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv = media->priv;
+ gboolean preroll_ok;
+ gboolean is_blocking = FALSE;
+
+ /* nothing to be done without complete senders */
+ if (get_num_complete_sender_streams (media) == 0) {
+ GST_DEBUG_OBJECT (media, "no complete senders, skipping force keyunit");
+ return TRUE;
+ }
+
+ is_blocking = media_streams_blocking (media);
+
+ /* if we unsuspend before the keyunit is expired remove the timer so that
+ * no future buffer is marked as expired */
+ if (is_blocking && !priv->keyunit_is_expired) {
+ GST_DEBUG_OBJECT (media, "using currently blocking keyunit");
+ g_source_destroy (priv->keyunit_expiration_source);
+ g_source_unref (priv->keyunit_expiration_source);
+ priv->keyunit_expiration_source = NULL;
+
+ return TRUE;
+ }
+
+ /* set the media to preparing, thus requiring a successful preroll before
+ * completing unsuspend. */
+ gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
+ GST_DEBUG_OBJECT (media, "ensuring new keyunit, doing preroll");
+ if (!start_preroll (media))
+ goto start_failed;
+
+ if (is_blocking) {
+ /* if we end up here then the keyunit has expired and the timer callback
+ * has been removed so reset the flag */
+ priv->keyunit_is_expired = FALSE;
+
+ /* install a probe that will drop the currently blocking keyunit on all
+ * complete streams. */
+ GST_DEBUG_OBJECT (media, "media is blocking. Installing drop probe");
+ media_streams_install_drop_probe (media);
+ }
+
+ /* force the keyunit from src */
+ GST_DEBUG_OBJECT (media, "sending force keyunit event");
+ gst_element_send_event (priv->element,
+ gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
+ TRUE, 0));
+
+ /* wait preroll */
+ g_rec_mutex_unlock (&priv->state_lock);
+ preroll_ok = wait_preroll (media);
+ g_rec_mutex_lock (&priv->state_lock);
+
+ if (!preroll_ok)
+ goto preroll_failed;
+
+ return TRUE;
+
+start_failed:
+ {
+ GST_WARNING ("failed to preroll pipeline");
+ return FALSE;
+ }
+preroll_failed:
+ {
+ GST_WARNING ("failed while waiting to preroll pipeline");
+ return FALSE;
+ }
+}
+
/* call with state_lock */
static gboolean
default_unsuspend (GstRTSPMedia * media)
@@ -4749,14 +5049,19 @@ default_unsuspend (GstRTSPMedia * media)
if (!preroll_ok)
goto preroll_failed;
+ break;
}
default:
break;
}
+ if (gst_rtsp_media_get_ensure_keyunit_on_start (media)) {
+ return ensure_new_keyunit (media);
+ }
+
return TRUE;
- /* ERRORS */
+/* ERRORS */
start_failed:
{
GST_WARNING ("failed to preroll pipeline");
@@ -4764,7 +5069,7 @@ start_failed:
}
preroll_failed:
{
- GST_WARNING ("failed to preroll pipeline");
+ GST_WARNING ("failed while waiting to preroll pipeline");
return FALSE;
}
}
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.h b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.h
index 02607c03d1..e410bf020c 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.h
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-media.h
@@ -278,6 +278,20 @@ void gst_rtsp_media_set_buffer_size (GstRTSPMedia *media, guin
GST_RTSP_SERVER_API
guint gst_rtsp_media_get_buffer_size (GstRTSPMedia *media);
+GST_RTSP_SERVER_API
+void gst_rtsp_media_set_ensure_keyunit_on_start (GstRTSPMedia* media,
+ gboolean ensure_keyunit_on_start);
+
+GST_RTSP_SERVER_API
+gboolean gst_rtsp_media_get_ensure_keyunit_on_start (GstRTSPMedia* media);
+
+GST_RTSP_SERVER_API
+void gst_rtsp_media_set_ensure_keyunit_on_start_timeout (GstRTSPMedia* media,
+ guint timeout);
+
+GST_RTSP_SERVER_API
+guint gst_rtsp_media_get_ensure_keyunit_on_start_timeout (GstRTSPMedia* media);
+
GST_RTSP_SERVER_API
void gst_rtsp_media_set_retransmission_time (GstRTSPMedia *media, GstClockTime time);
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-onvif-media-factory.c b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-onvif-media-factory.c
index 420ca052f4..ba8335c12e 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-onvif-media-factory.c
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-onvif-media-factory.c
@@ -146,6 +146,10 @@ gst_rtsp_onvif_media_factory_construct (GstRTSPMediaFactory * factory,
g_object_new (media_gtype, "element", element,
"transport-mode", GST_RTSP_TRANSPORT_MODE_PLAY, NULL);
+ /* we need to call this prior to collecting streams */
+ gst_rtsp_media_set_ensure_keyunit_on_start (media,
+ gst_rtsp_media_factory_get_ensure_keyunit_on_start (factory));
+
/* this adds the non-backchannel streams */
gst_rtsp_media_collect_streams (media);
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-server-internal.h b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-server-internal.h
index 6015d6840e..00a45219e6 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-server-internal.h
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-server-internal.h
@@ -63,6 +63,10 @@ gboolean gst_rtsp_stream_is_tcp_receiver (GstRTSPStream * stream
void gst_rtsp_media_set_enable_rtcp (GstRTSPMedia *media, gboolean enable);
void gst_rtsp_stream_set_enable_rtcp (GstRTSPStream *stream, gboolean enable);
+void gst_rtsp_stream_set_drop_delta_units (GstRTSPStream * stream, gboolean drop);
+
+gboolean gst_rtsp_stream_install_drop_probe (GstRTSPStream * stream);
+
G_END_DECLS
#endif /* __GST_RTSP_SERVER_INTERNAL_H__ */
diff --git a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-stream.c b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-stream.c
index b1348158f9..bf606698ca 100644
--- a/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-stream.c
+++ b/subprojects/gst-rtsp-server/gst/rtsp-server/rtsp-stream.c
@@ -230,6 +230,14 @@ struct _GstRTSPStreamPrivate
gulong block_early_rtcp_probe;
GstPad *block_early_rtcp_pad_ipv6;
gulong block_early_rtcp_probe_ipv6;
+
+ /* set to drop delta units in blocking pad */
+ gboolean drop_delta_units;
+
+ /* used to indicate that the drop probe has dropped a buffer and should be
+ * removed */
+ gboolean remove_drop_probe;
+
};
#define DEFAULT_CONTROL NULL
@@ -357,6 +365,8 @@ gst_rtsp_stream_init (GstRTSPStream * stream)
priv->block_early_rtcp_probe = 0;
priv->block_early_rtcp_pad_ipv6 = NULL;
priv->block_early_rtcp_probe_ipv6 = 0;
+ priv->drop_delta_units = FALSE;
+ priv->remove_drop_probe = FALSE;
}
typedef struct _UdpClientAddrInfo UdpClientAddrInfo;
@@ -4317,7 +4327,7 @@ gst_rtsp_stream_get_rtpinfo (GstRTSPStream * stream,
else
g_object_get (priv->appsink[0], "last-sample", &last_sample, NULL);
- if (last_sample) {
+ if (last_sample && !priv->blocking) {
GstCaps *caps;
GstBuffer *buffer;
GstSegment *segment;
@@ -4373,6 +4383,8 @@ gst_rtsp_stream_get_rtpinfo (GstRTSPStream * stream,
gst_sample_unref (last_sample);
}
} else if (priv->blocking) {
+ if (last_sample != NULL)
+ gst_sample_unref (last_sample);
if (seq) {
if (!priv->blocked_buffer)
goto stats;
@@ -5326,6 +5338,14 @@ rtp_pad_blocking (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
gst_rtp_buffer_unmap (&rtp);
}
priv->position = GST_BUFFER_TIMESTAMP (buffer);
+ if (priv->drop_delta_units) {
+ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
+ g_assert (!priv->blocking);
+ GST_DEBUG_OBJECT (pad, "dropping delta-unit buffer");
+ ret = GST_PAD_PROBE_DROP;
+ goto done;
+ }
+ }
} else if ((info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST)) {
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
@@ -5338,12 +5358,20 @@ rtp_pad_blocking (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
gst_rtp_buffer_unmap (&rtp);
}
priv->position = GST_BUFFER_TIMESTAMP (buffer);
+ if (priv->drop_delta_units) {
+ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
+ g_assert (!priv->blocking);
+ GST_DEBUG_OBJECT (pad, "dropping delta-unit buffer");
+ ret = GST_PAD_PROBE_DROP;
+ goto done;
+ }
+ }
} else if ((info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM)) {
if (GST_EVENT_TYPE (info->data) == GST_EVENT_GAP) {
gst_event_parse_gap (info->data, &priv->position, NULL);
} else {
ret = GST_PAD_PROBE_PASS;
- g_mutex_unlock (&priv->lock);
+ GST_WARNING ("Passing event.");
goto done;
}
} else {
@@ -5371,6 +5399,11 @@ rtp_pad_blocking (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
gst_event_unref (event);
}
+ /* make sure to block on the correct frame type */
+ if (priv->drop_delta_units) {
+ g_assert (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+ }
+
priv->blocking = TRUE;
GST_DEBUG_OBJECT (pad, "Now blocking");
@@ -5378,14 +5411,44 @@ rtp_pad_blocking (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
GST_DEBUG_OBJECT (stream, "position: %" GST_TIME_FORMAT,
GST_TIME_ARGS (priv->position));
- g_mutex_unlock (&priv->lock);
-
gst_element_post_message (priv->payloader,
gst_message_new_element (GST_OBJECT_CAST (priv->payloader),
gst_structure_new ("GstRTSPStreamBlocking", "is_complete",
G_TYPE_BOOLEAN, priv->is_complete, NULL)));
-
done:
+ g_mutex_unlock (&priv->lock);
+ return ret;
+}
+
+/* this probe will drop a single buffer. It is used when an old buffer is
+ * blocking the pipeline, such as between a DESCRIBE and a PLAY request. */
+static GstPadProbeReturn
+drop_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstRTSPStreamPrivate *priv;
+ GstRTSPStream *stream;
+ /* drop an old buffer stuck in a blocked pipeline */
+ GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
+
+ stream = user_data;
+ priv = stream->priv;
+
+ g_mutex_lock (&priv->lock);
+
+ if ((info->type & GST_PAD_PROBE_TYPE_BUFFER ||
+ info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST)) {
+ /* if a buffer has been dropped then remove this probe */
+ if (priv->remove_drop_probe) {
+ priv->remove_drop_probe = FALSE;
+ ret = GST_PAD_PROBE_REMOVE;
+ } else {
+ priv->blocking = FALSE;
+ priv->remove_drop_probe = TRUE;
+ }
+ } else {
+ ret = GST_PAD_PROBE_PASS;
+ }
+ g_mutex_unlock (&priv->lock);
return ret;
}
@@ -5423,6 +5486,26 @@ done:
return ret;
}
+static void
+install_drop_probe (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+
+ priv = stream->priv;
+
+ /* if receiver */
+ if (priv->sinkpad)
+ return;
+
+ /* install for data channel only */
+ if (priv->send_src[0]) {
+ gst_pad_add_probe (priv->send_src[0],
+ GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
+ GST_PAD_PROBE_TYPE_BUFFER_LIST |
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, drop_probe,
+ g_object_ref (stream), g_object_unref);
+ }
+}
static void
set_blocked (GstRTSPStream * stream, gboolean blocked)
@@ -5499,6 +5582,34 @@ gst_rtsp_stream_set_blocked (GstRTSPStream * stream, gboolean blocked)
return TRUE;
}
+/**
+ * gst_rtsp_stream_install_drop_probe:
+ * @stream: a #GstRTSPStream
+ *
+ * This probe can be installed when the currently blocking buffer should be
+ * dropped. When it has successfully dropped the buffer, it will remove itself.
+ * The goal is to avoid sending old data, typically when there has been a delay
+ * between a DESCRIBE and a PLAY request.
+ *
+ * Returns: %TRUE on success
+ *
+ * Since: 1.24
+ */
+gboolean
+gst_rtsp_stream_install_drop_probe (GstRTSPStream * stream)
+{
+ GstRTSPStreamPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+ priv = stream->priv;
+ g_mutex_lock (&priv->lock);
+ install_drop_probe (stream);
+ g_mutex_unlock (&priv->lock);
+
+ return TRUE;
+}
+
/**
* gst_rtsp_stream_ublock_linked:
* @stream: a #GstRTSPStream
@@ -6415,3 +6526,25 @@ gst_rtsp_stream_unblock_rtcp (GstRTSPStream * stream)
}
g_mutex_unlock (&priv->lock);
}
+
+/**
+ * gst_rtsp_stream_set_drop_delta_units:
+ * @stream: a #GstRTSPStream
+ * @drop: TRUE if delta unit frames are supposed to be dropped.
+ *
+ * Decide whether the blocking probe is supposed to drop delta units at the
+ * beginning of a stream.
+ *
+ * Since: 1.24
+ */
+void
+gst_rtsp_stream_set_drop_delta_units (GstRTSPStream * stream, gboolean drop)
+{
+ GstRTSPStreamPrivate *priv;
+
+ g_return_if_fail (GST_IS_RTSP_STREAM (stream));
+ priv = stream->priv;
+ g_mutex_lock (&priv->lock);
+ priv->drop_delta_units = drop;
+ g_mutex_unlock (&priv->lock);
+}
diff --git a/subprojects/gst-rtsp-server/meson.build b/subprojects/gst-rtsp-server/meson.build
index d402c5d1ac..fd21b9a7b2 100644
--- a/subprojects/gst-rtsp-server/meson.build
+++ b/subprojects/gst-rtsp-server/meson.build
@@ -146,6 +146,8 @@ gstsdp_dep = dependency('gstreamer-sdp-1.0', version : gst_req,
fallback : ['gst-plugins-base', 'sdp_dep'])
gstapp_dep = dependency('gstreamer-app-1.0', version : gst_req,
fallback : ['gst-plugins-base', 'app_dep'])
+gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req,
+ fallback : ['gst-plugins-base', 'video_dep'])
gstnet_dep = dependency('gstreamer-net-1.0', version : gst_req,
fallback : ['gstreamer', 'gst_net_dep'])
if host_machine.system() != 'windows'
diff --git a/subprojects/gst-rtsp-server/tests/check/meson.build b/subprojects/gst-rtsp-server/tests/check/meson.build
index ca658f9dda..2112da3962 100644
--- a/subprojects/gst-rtsp-server/tests/check/meson.build
+++ b/subprojects/gst-rtsp-server/tests/check/meson.build
@@ -57,7 +57,8 @@ foreach test_name : rtsp_server_tests
exe = executable(test_name, fname,
include_directories : rtspserver_incs,
c_args : rtspserver_args + test_c_args,
- dependencies : [gstcheck_dep, gstrtsp_dep, gstrtp_dep, gst_rtsp_server_dep]
+ dependencies : [gstcheck_dep, gstrtsp_dep, gstrtp_dep, gst_rtsp_server_dep,
+ gstvideo_dep]
)
test(test_name, exe,
env : env,