From c8f179948ec18e44fe8018d8ae8f2c0833905f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 9 Dec 2015 18:24:24 +0200 Subject: [PATCH] rtsp-media: Add property to decide if sending media should be stopped when a client disconnects without TEARDOWN Without TEARDOWN it might be desireable to keep the media running and continue sending data to the client, even if the RTSP connection itself is disconnected. Only do this for session medias that have only UDP transports. If there's at least on TCP transport, it will stop working and cause problems when the connection is disconnected. https://bugzilla.gnome.org/show_bug.cgi?id=758999 --- gst/rtsp-server/rtsp-client.c | 50 ++++++++++++++++--- gst/rtsp-server/rtsp-media-factory.c | 73 +++++++++++++++++++++++++++- gst/rtsp-server/rtsp-media-factory.h | 4 ++ gst/rtsp-server/rtsp-media.c | 70 +++++++++++++++++++++++++- gst/rtsp-server/rtsp-media.h | 3 ++ 5 files changed, 191 insertions(+), 9 deletions(-) diff --git a/gst/rtsp-server/rtsp-client.c b/gst/rtsp-server/rtsp-client.c index 0a33e45321..c7620a89ac 100644 --- a/gst/rtsp-server/rtsp-client.c +++ b/gst/rtsp-server/rtsp-client.c @@ -332,9 +332,38 @@ static GstRTSPFilterResult filter_session_media (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia, gpointer user_data) { - gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL); + gboolean *closed = user_data; + GstRTSPMedia *media; + guint i, n_streams; + gboolean is_all_udp = TRUE; - return GST_RTSP_FILTER_REMOVE; + media = gst_rtsp_session_media_get_media (sessmedia); + n_streams = gst_rtsp_media_n_streams (media); + + for (i = 0; i < n_streams; i++) { + GstRTSPStreamTransport *transport = + gst_rtsp_session_media_get_transport (sessmedia, i); + const GstRTSPTransport *rtsp_transport; + + if (!transport) + continue; + + rtsp_transport = gst_rtsp_stream_transport_get_transport (transport); + if (rtsp_transport + && rtsp_transport->lower_transport != GST_RTSP_LOWER_TRANS_UDP + && rtsp_transport->lower_transport != GST_RTSP_LOWER_TRANS_UDP_MCAST) { + is_all_udp = FALSE; + break; + } + } + + if (!is_all_udp || gst_rtsp_media_is_stop_on_disconnect (media)) { + gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL); + return GST_RTSP_FILTER_REMOVE; + } else { + *closed = FALSE; + return GST_RTSP_FILTER_KEEP; + } } static void @@ -395,11 +424,16 @@ static GstRTSPFilterResult cleanup_session (GstRTSPClient * client, GstRTSPSession * sess, gpointer user_data) { + gboolean *closed = user_data; + /* unlink all media managed in this session. This needs to happen * without the client lock, so we really want to do it here. */ - gst_rtsp_session_filter (sess, filter_session_media, client); + gst_rtsp_session_filter (sess, filter_session_media, user_data); - return GST_RTSP_FILTER_REMOVE; + if (*closed) + return GST_RTSP_FILTER_REMOVE; + else + return GST_RTSP_FILTER_KEEP; } static void @@ -3784,12 +3818,14 @@ static void client_watch_notify (GstRTSPClient * client) { GstRTSPClientPrivate *priv = client->priv; + gboolean closed = TRUE; GST_INFO ("client %p: watch destroyed", client); priv->watch = NULL; - /* remove all sessions and so drop the extra client ref */ - gst_rtsp_client_session_filter (client, cleanup_session, NULL); - g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL); + /* remove all sessions if the media says so and so drop the extra client ref */ + gst_rtsp_client_session_filter (client, cleanup_session, &closed); + if (closed) + g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL); g_object_unref (client); } diff --git a/gst/rtsp-server/rtsp-media-factory.c b/gst/rtsp-server/rtsp-media-factory.c index 087aa8ad35..ac6ce1f0c1 100644 --- a/gst/rtsp-server/rtsp-media-factory.c +++ b/gst/rtsp-server/rtsp-media-factory.c @@ -60,6 +60,7 @@ struct _GstRTSPMediaFactoryPrivate guint buffer_size; GstRTSPAddressPool *pool; GstRTSPTransportMode transport_mode; + gboolean stop_on_disconnect; GstClockTime rtx_time; guint latency; @@ -80,6 +81,7 @@ struct _GstRTSPMediaFactoryPrivate #define DEFAULT_BUFFER_SIZE 0x80000 #define DEFAULT_LATENCY 200 #define DEFAULT_TRANSPORT_MODE GST_RTSP_TRANSPORT_MODE_PLAY +#define DEFAULT_STOP_ON_DISCONNECT TRUE enum { @@ -93,6 +95,7 @@ enum PROP_BUFFER_SIZE, PROP_LATENCY, PROP_TRANSPORT_MODE, + PROP_STOP_ON_DISCONNECT, PROP_LAST }; @@ -202,6 +205,13 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass) GST_TYPE_RTSP_TRANSPORT_MODE, DEFAULT_TRANSPORT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_STOP_ON_DISCONNECT, + g_param_spec_boolean ("stop-on-disconnect", "Stop On Disconnect", + "If media from this factory should be stopped " + "when a client disconnects without TEARDOWN", + DEFAULT_STOP_ON_DISCONNECT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_rtsp_media_factory_signals[SIGNAL_MEDIA_CONSTRUCTED] = g_signal_new ("media-constructed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaFactoryClass, @@ -240,6 +250,7 @@ gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory) priv->buffer_size = DEFAULT_BUFFER_SIZE; priv->latency = DEFAULT_LATENCY; priv->transport_mode = DEFAULT_TRANSPORT_MODE; + priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT; g_mutex_init (&priv->lock); g_mutex_init (&priv->medias_lock); @@ -304,6 +315,10 @@ gst_rtsp_media_factory_get_property (GObject * object, guint propid, g_value_set_flags (value, gst_rtsp_media_factory_get_transport_mode (factory)); break; + case PROP_STOP_ON_DISCONNECT: + g_value_set_boolean (value, + gst_rtsp_media_factory_is_stop_on_disonnect (factory)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); } @@ -347,6 +362,10 @@ gst_rtsp_media_factory_set_property (GObject * object, guint propid, gst_rtsp_media_factory_set_transport_mode (factory, g_value_get_flags (value)); break; + case PROP_STOP_ON_DISCONNECT: + gst_rtsp_media_factory_set_stop_on_disconnect (factory, + g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); } @@ -862,6 +881,56 @@ gst_rtsp_media_factory_get_protocols (GstRTSPMediaFactory * factory) return res; } +/** + * gst_rtsp_media_factory_set_stop_on_disconnect: + * @factory: a #GstRTSPMediaFactory + * @stop_on_disconnect: the new value + * + * Configure if media created from this factory should be stopped + * when a client disconnects without sending TEARDOWN. + */ +void +gst_rtsp_media_factory_set_stop_on_disconnect (GstRTSPMediaFactory * factory, + gboolean stop_on_disconnect) +{ + GstRTSPMediaFactoryPrivate *priv; + + g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory)); + + priv = factory->priv; + + GST_RTSP_MEDIA_FACTORY_LOCK (factory); + priv->stop_on_disconnect = stop_on_disconnect; + GST_RTSP_MEDIA_FACTORY_UNLOCK (factory); +} + +/** + * gst_rtsp_media_factory_is_stop_on_disconnect: + * @factory: a #GstRTSPMediaFactory + * + * Get if media created from this factory should be stopped when a client + * disconnects without sending TEARDOWN. + * + * Returns: %TRUE if the media will be stopped when a client disconnects + * without sending TEARDOWN. + */ +gboolean +gst_rtsp_media_factory_is_stop_on_disonnect (GstRTSPMediaFactory * factory) +{ + GstRTSPMediaFactoryPrivate *priv; + gboolean result; + + g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), TRUE); + + priv = factory->priv; + + GST_RTSP_MEDIA_FACTORY_LOCK (factory); + result = priv->stop_on_disconnect; + GST_RTSP_MEDIA_FACTORY_UNLOCK (factory); + + return result; +} + /** * gst_rtsp_media_factory_set_retransmission_time: * @factory: a #GstRTSPMediaFactory @@ -1270,7 +1339,7 @@ static void default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media) { GstRTSPMediaFactoryPrivate *priv = factory->priv; - gboolean shared, eos_shutdown; + gboolean shared, eos_shutdown, stop_on_disconnect; guint size; GstRTSPSuspendMode suspend_mode; GstRTSPProfile profiles; @@ -1292,6 +1361,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media) rtx_time = priv->rtx_time; latency = priv->latency; transport_mode = priv->transport_mode; + stop_on_disconnect = priv->stop_on_disconnect; GST_RTSP_MEDIA_FACTORY_UNLOCK (factory); gst_rtsp_media_set_suspend_mode (media, suspend_mode); @@ -1303,6 +1373,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media) gst_rtsp_media_set_retransmission_time (media, rtx_time); gst_rtsp_media_set_latency (media, latency); gst_rtsp_media_set_transport_mode (media, transport_mode); + gst_rtsp_media_set_stop_on_disconnect (media, stop_on_disconnect); if ((pool = gst_rtsp_media_factory_get_address_pool (factory))) { gst_rtsp_media_set_address_pool (media, pool); diff --git a/gst/rtsp-server/rtsp-media-factory.h b/gst/rtsp-server/rtsp-media-factory.h index 0dc1067e27..e82b64a913 100644 --- a/gst/rtsp-server/rtsp-media-factory.h +++ b/gst/rtsp-server/rtsp-media-factory.h @@ -118,6 +118,10 @@ void gst_rtsp_media_factory_set_shared (GstRTSPMediaFacto gboolean shared); gboolean gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory *factory); +void gst_rtsp_media_factory_set_stop_on_disconnect (GstRTSPMediaFactory *factory, + gboolean stop_on_disconnect); +gboolean gst_rtsp_media_factory_is_stop_on_disonnect (GstRTSPMediaFactory *factory); + void gst_rtsp_media_factory_set_suspend_mode (GstRTSPMediaFactory *factory, GstRTSPSuspendMode mode); GstRTSPSuspendMode gst_rtsp_media_factory_get_suspend_mode (GstRTSPMediaFactory *factory); diff --git a/gst/rtsp-server/rtsp-media.c b/gst/rtsp-server/rtsp-media.c index 5a6f758ab0..61e68b2be9 100644 --- a/gst/rtsp-server/rtsp-media.c +++ b/gst/rtsp-server/rtsp-media.c @@ -102,6 +102,7 @@ struct _GstRTSPMediaPrivate GstRTSPAddressPool *pool; gboolean blocked; GstRTSPTransportMode transport_mode; + gboolean stop_on_disconnect; GstElement *element; GRecMutex state_lock; /* locking order: state lock, lock */ @@ -151,6 +152,7 @@ struct _GstRTSPMediaPrivate #define DEFAULT_TIME_PROVIDER FALSE #define DEFAULT_LATENCY 200 #define DEFAULT_TRANSPORT_MODE GST_RTSP_TRANSPORT_MODE_PLAY +#define DEFAULT_STOP_ON_DISCONNECT TRUE /* define to dump received RTCP packets */ #undef DUMP_STATS @@ -169,6 +171,7 @@ enum PROP_TIME_PROVIDER, PROP_LATENCY, PROP_TRANSPORT_MODE, + PROP_STOP_ON_DISCONNECT, PROP_LAST }; @@ -329,6 +332,13 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass) GST_TYPE_RTSP_TRANSPORT_MODE, DEFAULT_TRANSPORT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_STOP_ON_DISCONNECT, + g_param_spec_boolean ("stop-on-disconnect", "Stop On Disconnect", + "If this media pipeline should be stopped " + "when a client disconnects without TEARDOWN", + DEFAULT_STOP_ON_DISCONNECT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_rtsp_media_signals[SIGNAL_NEW_STREAM] = g_signal_new ("new-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaClass, new_stream), NULL, NULL, @@ -396,6 +406,7 @@ gst_rtsp_media_init (GstRTSPMedia * media) priv->buffer_size = DEFAULT_BUFFER_SIZE; priv->time_provider = DEFAULT_TIME_PROVIDER; priv->transport_mode = DEFAULT_TRANSPORT_MODE; + priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT; } static void @@ -472,6 +483,9 @@ gst_rtsp_media_get_property (GObject * object, guint propid, case PROP_TRANSPORT_MODE: g_value_set_flags (value, gst_rtsp_media_get_transport_mode (media)); break; + case PROP_STOP_ON_DISCONNECT: + g_value_set_boolean (value, gst_rtsp_media_is_stop_on_disconnect (media)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); } @@ -518,6 +532,10 @@ gst_rtsp_media_set_property (GObject * object, guint propid, case PROP_TRANSPORT_MODE: gst_rtsp_media_set_transport_mode (media, g_value_get_flags (value)); break; + case PROP_STOP_ON_DISCONNECT: + gst_rtsp_media_set_stop_on_disconnect (media, + g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); } @@ -1167,6 +1185,56 @@ gst_rtsp_media_get_buffer_size (GstRTSPMedia * media) return res; } +/** + * gst_rtsp_media_set_stop_on_disconnect: + * @media: a #GstRTSPMedia + * @stop_on_disconnect: the new value + * + * Set or unset if the pipeline for @media should be stopped when a + * client disconnects without sending TEARDOWN. + */ +void +gst_rtsp_media_set_stop_on_disconnect (GstRTSPMedia * media, + gboolean stop_on_disconnect) +{ + GstRTSPMediaPrivate *priv; + + g_return_if_fail (GST_IS_RTSP_MEDIA (media)); + + priv = media->priv; + + g_mutex_lock (&priv->lock); + priv->stop_on_disconnect = stop_on_disconnect; + g_mutex_unlock (&priv->lock); +} + +/** + * gst_rtsp_media_is_stop_on_disconnect: + * @media: a #GstRTSPMedia + * + * Check if the pipeline for @media will be stopped when a client disconnects + * without sending TEARDOWN. + * + * Returns: %TRUE if the media will be stopped when a client disconnects + * without sending TEARDOWN. + */ +gboolean +gst_rtsp_media_is_stop_on_disconnect (GstRTSPMedia * media) +{ + GstRTSPMediaPrivate *priv; + gboolean res; + + g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), TRUE); + + priv = media->priv; + + g_mutex_lock (&priv->lock); + res = priv->stop_on_disconnect; + g_mutex_unlock (&priv->lock); + + return res; +} + /** * gst_rtsp_media_set_retransmission_time: * @media: a #GstRTSPMedia @@ -2604,7 +2672,7 @@ default_prepare (GstRTSPMedia * media, GstRTSPThread * thread) /* do remainder in context */ source = g_idle_source_new (); g_source_set_callback (source, (GSourceFunc) start_prepare, - g_object_ref (media), (GDestroyNotify) g_object_unref); + g_object_ref (media), (GDestroyNotify) g_object_unref); g_source_attach (source, context); g_source_unref (source); diff --git a/gst/rtsp-server/rtsp-media.h b/gst/rtsp-server/rtsp-media.h index d554afe769..edf669f296 100644 --- a/gst/rtsp-server/rtsp-media.h +++ b/gst/rtsp-server/rtsp-media.h @@ -186,6 +186,9 @@ GstRTSPPermissions * gst_rtsp_media_get_permissions (GstRTSPMedia *media); void gst_rtsp_media_set_shared (GstRTSPMedia *media, gboolean shared); gboolean gst_rtsp_media_is_shared (GstRTSPMedia *media); +void gst_rtsp_media_set_stop_on_disconnect (GstRTSPMedia *media, gboolean stop_on_disconnect); +gboolean gst_rtsp_media_is_stop_on_disconnect (GstRTSPMedia *media); + void gst_rtsp_media_set_transport_mode (GstRTSPMedia *media, GstRTSPTransportMode mode); GstRTSPTransportMode gst_rtsp_media_get_transport_mode (GstRTSPMedia *media);