rtsp-client: add property post-session-timeout

This is a TCP connection timeout for client connections, in seconds.
If a positive value is set for this property, the client connection
will be kept alive for this amount of seconds after the last session
timeout. For negative values of this property the connection timeout
handling is delegated to the system (just as it was before).

Fixes #83
This commit is contained in:
Zoltán Imets 2019-12-17 16:08:19 +01:00 committed by GStreamer Merge Bot
parent 0ed32e0d53
commit aa8126b239
2 changed files with 170 additions and 0 deletions

View file

@ -103,6 +103,7 @@ struct _GstRTSPClientPrivate
guint sessions_cookie;
gboolean drop_backlog;
gint post_session_timeout;
guint content_length_limit;
@ -130,6 +131,7 @@ static GHashTable *tunnels; /* protected by tunnels_lock */
#define DEFAULT_SESSION_POOL NULL
#define DEFAULT_MOUNT_POINTS NULL
#define DEFAULT_DROP_BACKLOG TRUE
#define DEFAULT_POST_SESSION_TIMEOUT -1
#define RTSP_CTRL_CB_INTERVAL 1
#define RTSP_CTRL_TIMEOUT_VALUE 60
@ -140,6 +142,7 @@ enum
PROP_SESSION_POOL,
PROP_MOUNT_POINTS,
PROP_DROP_BACKLOG,
PROP_POST_SESSION_TIMEOUT,
PROP_LAST
};
@ -255,6 +258,26 @@ gst_rtsp_client_class_init (GstRTSPClientClass * klass)
"Drop data when the backlog queue is full",
DEFAULT_DROP_BACKLOG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRTSPClient::post-session-timeout:
*
* An extra tcp timeout ( > 0) after session timeout, in seconds.
* The tcp connection will be kept alive until this timeout happens to give
* the client a possibility to reuse the connection.
* 0 means that the connection will be closed immediately after the session
* timeout.
*
* Default value is -1 seconds, meaning that we let the system close
* the connection.
*
* Since: 1.18
*/
g_object_class_install_property (gobject_class, PROP_POST_SESSION_TIMEOUT,
g_param_spec_int ("post-session-timeout", "Post Session Timeout",
"An extra TCP connection timeout after session timeout", G_MININT,
G_MAXINT, DEFAULT_POST_SESSION_TIMEOUT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_rtsp_client_signals[SIGNAL_CLOSED] =
g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL, NULL,
@ -588,6 +611,7 @@ gst_rtsp_client_init (GstRTSPClient * client)
priv->close_seq = 0;
priv->data_seqs = g_array_new (FALSE, FALSE, sizeof (DataSeq));
priv->drop_backlog = DEFAULT_DROP_BACKLOG;
priv->post_session_timeout = DEFAULT_POST_SESSION_TIMEOUT;
priv->transports =
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
g_object_unref);
@ -807,6 +831,9 @@ gst_rtsp_client_get_property (GObject * object, guint propid,
case PROP_DROP_BACKLOG:
g_value_set_boolean (value, priv->drop_backlog);
break;
case PROP_POST_SESSION_TIMEOUT:
g_value_set_int (value, priv->post_session_timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
}
@ -831,6 +858,11 @@ gst_rtsp_client_set_property (GObject * object, guint propid,
priv->drop_backlog = g_value_get_boolean (value);
g_mutex_unlock (&priv->lock);
break;
case PROP_POST_SESSION_TIMEOUT:
g_mutex_lock (&priv->lock);
priv->post_session_timeout = g_value_get_int (value);
g_mutex_unlock (&priv->lock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
}
@ -2531,6 +2563,23 @@ rtsp_ctrl_timeout_cb (gpointer user_data)
return res;
}
static gboolean
rtsp_ctrl_connection_timeout_cb (gpointer user_data)
{
GstRTSPClient *client = (GstRTSPClient *) user_data;
GstRTSPClientPrivate *priv = client->priv;
GST_DEBUG ("rtsp control connection timeout id=%u expired, closing client.",
priv->rtsp_ctrl_timeout_id);
g_mutex_lock (&priv->lock);
priv->rtsp_ctrl_timeout_id = 0;
priv->rtsp_ctrl_timeout_cnt = 0;
g_mutex_unlock (&priv->lock);
gst_rtsp_client_close (client);
return G_SOURCE_REMOVE;
}
static void
rtsp_ctrl_timeout_remove (GstRTSPClientPrivate * priv)
{
@ -3694,12 +3743,33 @@ client_session_removed (GstRTSPSessionPool * pool, GstRTSPSession * session,
GstRTSPClient * client)
{
GstRTSPClientPrivate *priv = client->priv;
GSource *timer_src;
GST_INFO ("client %p: session %p removed", client, session);
g_mutex_lock (&priv->lock);
client_unwatch_session (client, session, NULL);
g_mutex_unlock (&priv->lock);
if (!priv->sessions && priv->rtsp_ctrl_timeout_id == 0) {
if (priv->post_session_timeout > 0) {
g_mutex_lock (&priv->lock);
timer_src = g_timeout_source_new_seconds (priv->post_session_timeout);
g_source_set_callback (timer_src, rtsp_ctrl_connection_timeout_cb,
client, NULL);
priv->rtsp_ctrl_timeout_cnt = 0;
priv->rtsp_ctrl_timeout_id = g_source_attach (timer_src,
priv->watch_context);
g_source_unref (timer_src);
GST_DEBUG ("rtsp control setting up connection timeout id=%u.",
priv->rtsp_ctrl_timeout_id);
g_mutex_unlock (&priv->lock);
} else if (priv->post_session_timeout == 0) {
gst_rtsp_client_close (client);
}
}
}
/* Check for Require headers. Returns TRUE if there are no Require headers,

View file

@ -1643,6 +1643,105 @@ GST_START_TEST (test_play_multithreaded_timeout_session)
GST_END_TEST;
static void
new_connection_and_session_timeout_one (GstRTSPClient * client,
GstRTSPSession * session, gpointer user_data)
{
gint ps_timeout = 0;
g_object_set (G_OBJECT (client), "post-session-timeout", 1, NULL);
g_object_get (G_OBJECT (client), "post-session-timeout", &ps_timeout, NULL);
fail_unless_equals_int (ps_timeout, 1);
g_object_set (G_OBJECT (session), "extra-timeout", 0, NULL);
gst_rtsp_session_set_timeout (session, 1);
g_signal_handlers_disconnect_by_func (client,
new_connection_and_session_timeout_one, user_data);
}
GST_START_TEST (test_play_timeout_connection)
{
GstRTSPConnection *conn;
GstSDPMessage *sdp_message = NULL;
const GstSDPMedia *sdp_media;
const gchar *video_control;
GstRTSPRange client_port;
gchar *session = NULL;
GstRTSPTransport *video_transport = NULL;
GstRTSPSessionPool *pool;
GstRTSPThreadPool *thread_pool;
GstRTSPMessage *request;
GstRTSPMessage *response;
thread_pool = gst_rtsp_server_get_thread_pool (server);
g_object_unref (thread_pool);
pool = gst_rtsp_server_get_session_pool (server);
g_signal_connect (server, "client-connected",
G_CALLBACK (session_connected_new_session_cb),
new_connection_and_session_timeout_one);
start_server (FALSE);
conn = connect_to_server (test_port, TEST_MOUNT_POINT);
gst_rtsp_connection_set_remember_session_id (conn, FALSE);
sdp_message = do_describe (conn, TEST_MOUNT_POINT);
/* get control strings from DESCRIBE response */
fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
sdp_media = gst_sdp_message_get_media (sdp_message, 0);
video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
get_client_ports (&client_port);
/* do SETUP for video and audio */
fail_unless (do_setup (conn, video_control, &client_port, &session,
&video_transport) == GST_RTSP_STS_OK);
fail_unless (gst_rtsp_session_pool_get_n_sessions (pool) == 1);
/* send PLAY request and check that we get 200 OK */
fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
session) == GST_RTSP_STS_OK);
sleep (2);
fail_unless (gst_rtsp_session_pool_cleanup (pool) == 1);
sleep (2);
request = create_request (conn, GST_RTSP_TEARDOWN, NULL);
/* add headers */
if (session) {
gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session);
}
/* send request */
fail_unless (send_request (conn, request));
gst_rtsp_message_free (request);
iterate ();
/* read response */
response = read_response (conn);
fail_unless (response == NULL);
if (response) {
gst_rtsp_message_free (response);
}
/* clean up and iterate so the clean-up can finish */
g_object_unref (pool);
g_free (session);
gst_rtsp_transport_free (video_transport);
gst_sdp_message_free (sdp_message);
gst_rtsp_connection_free (conn);
stop_server ();
iterate ();
}
GST_END_TEST;
GST_START_TEST (test_no_session_timeout)
{
@ -2631,6 +2730,7 @@ rtspserver_suite (void)
tcase_add_test (tc, test_play_multithreaded_block_in_describe);
tcase_add_test (tc, test_play_multithreaded_timeout_client);
tcase_add_test (tc, test_play_multithreaded_timeout_session);
tcase_add_test (tc, test_play_timeout_connection);
tcase_add_test (tc, test_no_session_timeout);
tcase_add_test (tc, test_play_one_active_stream);
tcase_add_test (tc, test_play_disconnect);