Skip to content

Commit

Permalink
Making the timeout parameters for streaming plugin for RTSP play out …
Browse files Browse the repository at this point in the history
…configurable (#2598)
  • Loading branch information
pontscho committed Mar 25, 2021
1 parent 7c9d522 commit fbf1060
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 18 deletions.
14 changes: 13 additions & 1 deletion conf/janus.plugin.streaming.jcfg.sample.in
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,16 @@
# url = RTSP stream URL (only for restreaming RTSP)
# rtsp_user = RTSP authorization username (only if type=rtsp)
# rtsp_pwd = RTSP authorization password (only if type=rtsp)
# rtspiface = network interface or IP address to bind to, if any (binds to all otherwise), when receiving RTSP streams
# rtsp_failcheck = whether an error should be returned if connecting to the RTSP server fails (default=true)
# rtspiface = network interface or IP address to bind to, if any (binds to all otherwise), when receiving RTSP streams
# rtsp_reconnect_delay = after n seconds passed and no media assumed, the RTSP server has gone and schedule a reconnect (default=5s)
# rtsp_session_timeout = by default the streaming plugin will check the RTSP connection with an OPTIONS query,
# the value of the timeout comes from the RTSP session initializer and by default
# this session timeout is the half of this value In some cases this value can be too high (for example more than one minute)
# because of the media server. In that case this plugin will calculate the timeout with this
# formula: timeout = min(session_timeout, rtsp_session_timeout / 2). (default=0s)
# rtsp_timeout = communication timeout (CURLOPT_TIMEOUT) for cURL call gathering the RTSP information (default=10s)
# rtsp_conn_timeout = connection timeout for cURL (CURLOPT_CONNECTTIMEOUT) call gathering the RTSP information (default=5s)
#
# Notice that, for 'rtsp' mountpoints, normally the plugin uses the exact
# SDP rtpmap and fmtp attributes the remote camera or RTSP server sent.
Expand Down Expand Up @@ -249,4 +257,8 @@ file-ondemand-sample: {
#rtsp_user = "username"
#rtsp_pwd = "password"
#secret = "adminpwd"
#reconnect_delay = 5
#session_timeout = 0
# rtsp_timeout = 10
# rtsp_conn_timeout = 5
#}
97 changes: 80 additions & 17 deletions plugins/janus_streaming.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ rtsp_user = RTSP authorization username, if needed
rtsp_pwd = RTSP authorization password, if needed
rtsp_failcheck = whether an error should be returned if connecting to the RTSP server fails (default=true)
rtspiface = network interface IP address or device name to listen on when receiving RTSP streams
rtsp_reconnect_delay = after n seconds passed and no media assumed, the RTSP server has gone and schedule a reconnect (default=5s)
rtsp_session_timeout = by default the streaming plugin will check the RTSP connection with an OPTIONS query,
the value of the timeout comes from the RTSP session initializer and by default
this session timeout is the half of this value In some cases this value can be too high (for example more than one minute)
because of the media server. In that case this plugin will calculate the timeout with this
formula: timeout = min(session_timeout, rtsp_session_timeout / 2). (default=0s)
rtsp_timeout = communication timeout (CURLOPT_TIMEOUT) for cURL call gathering the RTSP information (default=10s)
rtsp_conn_timeout = connection timeout for cURL (CURLOPT_CONNECTTIMEOUT) call gathering the RTSP information (default=5s)
\endverbatim
*
* \section streamapi Streaming API
Expand Down Expand Up @@ -721,6 +729,11 @@ rtspiface = network interface IP address or device name to listen on when receiv
#include "../utils.h"
#include "../ip-utils.h"

/* Default settings */
#define JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT 0 /* Overwrite the RTSP session timeout. If set to zero, the RTSP timeout is derived from a session. */
#define JANUS_STREAMING_DEFAULT_RECONNECT_DELAY 5 /* Reconnecting delay in seconds. */
#define JANUS_STREAMING_DEFAULT_CURL_TIMEOUT 10L /* Communication timeout for cURL. */
#define JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT 5L /* Connection timeout for cURL. */

/* Plugin information */
#define JANUS_STREAMING_VERSION 8
Expand Down Expand Up @@ -855,6 +868,10 @@ static struct janus_json_parameter rtsp_parameters[] = {
{"url", JSON_STRING, 0},
{"rtsp_user", JSON_STRING, 0},
{"rtsp_pwd", JSON_STRING, 0},
{"rtsp_reconnect_delay", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
{"rtsp_session_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
{"rtsp_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
{"rtsp_conn_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
{"audiortpmap", JSON_STRING, 0},
{"audiofmtp", JSON_STRING, 0},
{"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
Expand Down Expand Up @@ -1066,10 +1083,14 @@ typedef struct janus_streaming_rtp_source {
janus_streaming_buffer *curldata;
char *rtsp_url;
char *rtsp_username, *rtsp_password;
int ka_timeout;
gint64 ka_timeout;
char *rtsp_ahost, *rtsp_vhost;
gboolean reconnecting;
gint64 reconnect_timer;
gint64 reconnect_delay;
gint64 session_timeout;
int rtsp_timeout;
int rtsp_conn_timeout;
janus_mutex rtsp_mutex;
#endif
janus_streaming_rtp_keyframe keyframe;
Expand Down Expand Up @@ -1188,9 +1209,9 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
gboolean doaudio, int audiopt, char *artpmap, char *afmtp,
gboolean dovideo, int videopt, char *vrtpmap, char *vfmtp, gboolean bufferkf,
const janus_network_address *iface, int threads,
gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
gboolean error_on_failure);


typedef struct janus_streaming_message {
janus_plugin_session *handle;
char *transaction;
Expand Down Expand Up @@ -2101,6 +2122,10 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
janus_config_item *iface = janus_config_get(config, cat, janus_config_type_item, "rtspiface");
janus_config_item *failerr = janus_config_get(config, cat, janus_config_type_item, "rtsp_failcheck");
janus_config_item *threads = janus_config_get(config, cat, janus_config_type_item, "threads");
janus_config_item *reconnect_delay = janus_config_get(config, cat, janus_config_type_item, "rtsp_reconnect_delay");
janus_config_item *session_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_session_timeout");
janus_config_item *rtsp_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_timeout");
janus_config_item *rtsp_conn_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_conn_timeout");
janus_network_address iface_value;
if(file == NULL || file->value == NULL) {
JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', missing mandatory information...\n", cat->name);
Expand Down Expand Up @@ -2153,6 +2178,10 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
bufferkf,
iface && iface->value ? &iface_value : NULL,
(threads && threads->value) ? atoi(threads->value) : 0,
((reconnect_delay && reconnect_delay->value) ? atoi(reconnect_delay->value) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC,
((session_timeout && session_timeout->value) ? atoi(session_timeout->value) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC,
((rtsp_timeout && rtsp_timeout->value) ? atoi(rtsp_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT),
((rtsp_conn_timeout && rtsp_conn_timeout->value) ? atoi(rtsp_conn_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT),
error_on_failure)) == NULL) {
JANUS_LOG(LOG_ERR, "Error creating 'rtsp' mountpoint '%s'...\n", cat->name);
cl = cl->next;
Expand Down Expand Up @@ -3162,6 +3191,10 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
json_t *iface = json_object_get(root, "rtspiface");
json_t *threads = json_object_get(root, "threads");
json_t *failerr = json_object_get(root, "rtsp_failcheck");
json_t *reconnect_delay = json_object_get(root, "rtsp_reconnect_delay");
json_t *session_timeout = json_object_get(root, "rtsp_session_timeout");
json_t *rtsp_timeout = json_object_get(root, "rtsp_timeout");
json_t *rtsp_conn_timeout = json_object_get(root, "rtsp_conn_timeout");
if(failerr == NULL) /* For an old typo, we support the legacy syntax too */
failerr = json_object_get(root, "rtsp_check");
gboolean doaudio = audio ? json_is_true(audio) : FALSE;
Expand Down Expand Up @@ -3205,6 +3238,10 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi
(char *)json_string_value(videortpmap), (char *)json_string_value(videofmtp),
videobufferkf ? json_is_true(videobufferkf) : FALSE,
&multicast_iface, (threads ? json_integer_value(threads) : 0),
((reconnect_delay ? json_integer_value(reconnect_delay) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC),
((session_timeout ? json_integer_value(session_timeout) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC),
(rtsp_timeout ? json_integer_value(rtsp_timeout) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT),
(rtsp_conn_timeout ? json_integer_value(rtsp_conn_timeout) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT),
error_on_failure);
janus_mutex_lock(&mountpoints_mutex);
g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
Expand Down Expand Up @@ -5598,7 +5635,7 @@ static int janus_streaming_allocate_port_pair(const char *name, const char *medi
static int janus_streaming_get_fd_port(int fd) {
struct sockaddr_in6 server = { 0 };
socklen_t len = sizeof(server);
if(getsockname(fd, &server, &len) == -1) {
if(getsockname(fd, (struct sockaddr *)&server, &len) == -1) {
return -1;
}

Expand Down Expand Up @@ -6328,6 +6365,11 @@ static int janus_streaming_rtsp_parse_sdp(const char *buffer, const char *name,
return 0;
}

/* Helper function to calculating the minimum value if 'a' is bigger than zero */
static inline gint64 janus_streaming_min_if(gint64 a, gint64 b) {
return a > 0 ? (a > b ? b : a) : b;
}

/* Static helper to connect to an RTSP server, considering we might do this either
* when creating a new mountpoint, or when reconnecting after some failure */
static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp) {
Expand All @@ -6350,8 +6392,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_URL, source->rtsp_url);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, source->rtsp_timeout);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, source->rtsp_conn_timeout);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 0L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
#if CURL_AT_LEAST_VERSION(7, 66, 0)
Expand Down Expand Up @@ -6403,7 +6445,6 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
}
JANUS_LOG(LOG_VERB, "DESCRIBE answer:%s\n", curldata->buffer);
/* Parse the SDP we just got to figure out the negotiated media */
int ka_timeout = 0;
int vpt = -1;
char vrtpmap[2048];
vrtpmap[0] = '\0';
Expand Down Expand Up @@ -6574,8 +6615,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
} else if(is_session) {
if(!strcasecmp(name, "timeout")) {
/* Take note of the timeout, for keep-alives */
ka_timeout = atoi(value);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %d\n", ka_timeout);
source->ka_timeout = janus_streaming_min_if(source->session_timeout, atoi(value) / 2 * G_USEC_PER_SEC);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %"SCNi64" ms\n", source->ka_timeout / 1000);
}
}
}
Expand Down Expand Up @@ -6747,8 +6788,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
} else if(is_session) {
if(!strcasecmp(name, "timeout")) {
/* Take note of the timeout, for keep-alives */
ka_timeout = atoi(value);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %d\n", ka_timeout);
source->ka_timeout = janus_streaming_min_if(source->session_timeout, atoi(value) / 2 * G_USEC_PER_SEC);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %"SCNi64" ms\n", source->ka_timeout / 1000);
}
}
}
Expand Down Expand Up @@ -6850,7 +6891,6 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
source->rtsp_vhost = g_strdup(vhost);
source->curl = curl;
source->curldata = curldata;
source->ka_timeout = ka_timeout;
return 0;
}

Expand Down Expand Up @@ -6951,6 +6991,7 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
gboolean doaudio, int acodec, char *artpmap, char *afmtp,
gboolean dovideo, int vcodec, char *vrtpmap, char *vfmtp, gboolean bufferkf,
const janus_network_address *iface, int threads,
gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
gboolean error_on_failure) {
char id_num[30];
if(!string_ids) {
Expand All @@ -6961,6 +7002,23 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
JANUS_LOG(LOG_ERR, "Can't add 'rtsp' stream, missing url...\n");
return NULL;
}
if(reconnect_delay < 0) {
JANUS_LOG(LOG_ERR, "rtsp_reconnect_delay can't be smaller than zero.\n");
return NULL;
}
if(session_timeout < 0) {
JANUS_LOG(LOG_ERR, "rtsp_session_timeout can't be smaller than zero.\n");
return NULL;
}
if(rtsp_timeout < 0) {
JANUS_LOG(LOG_ERR, "rtsp_timeout can't be smaller than zero.\n");
return NULL;
}
if(rtsp_conn_timeout < 0) {
JANUS_LOG(LOG_ERR, "rtsp_conn_timeout can't be smaller than zero.\n");
return NULL;
}

JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled");

/* Create an RTP source for the media we'll get */
Expand Down Expand Up @@ -7025,6 +7083,11 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
live_rtsp_source->keyframe.latest_keyframe = NULL;
live_rtsp_source->keyframe.temp_keyframe = NULL;
live_rtsp_source->keyframe.temp_ts = 0;
live_rtsp_source->ka_timeout = session_timeout;
live_rtsp_source->reconnect_delay = reconnect_delay;
live_rtsp_source->session_timeout = session_timeout;
live_rtsp_source->rtsp_timeout = rtsp_timeout;
live_rtsp_source->rtsp_conn_timeout = rtsp_conn_timeout;
janus_mutex_init(&live_rtsp_source->keyframe.mutex);
live_rtsp_source->reconnect_timer = 0;
janus_mutex_init(&live_rtsp_source->rtsp_mutex);
Expand Down Expand Up @@ -7130,6 +7193,7 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
gboolean doaudio, int acodec, char *audiortpmap, char *audiofmtp,
gboolean dovideo, int vcodec, char *videortpmap, char *videofmtp, gboolean bufferkf,
const janus_network_address *iface, int threads,
gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
gboolean error_on_failure) {
JANUS_LOG(LOG_ERR, "RTSP need libcurl\n");
return NULL;
Expand Down Expand Up @@ -7503,7 +7567,7 @@ static void *janus_streaming_relay_thread(void *data) {
gint64 now = janus_get_monotonic_time(), before = now, ka_timeout = 0;
if(source->rtsp) {
source->reconnect_timer = now;
ka_timeout = ((gint64)source->ka_timeout*G_USEC_PER_SEC)/2;
ka_timeout = source->ka_timeout;
}
#endif
/* Loop */
Expand All @@ -7519,8 +7583,8 @@ static void *janus_streaming_relay_thread(void *data) {
continue;
}
now = janus_get_monotonic_time();
if(!source->reconnecting && (now - source->reconnect_timer > 5*G_USEC_PER_SEC)) {
/* 5 seconds passed and no media? Assume the RTSP server has gone and schedule a reconnect */
if(!source->reconnecting && (now - source->reconnect_timer > source->reconnect_delay)) {
/* Assume the RTSP server has gone and schedule a reconnect */
JANUS_LOG(LOG_WARN, "[%s] %"SCNi64"s passed with no media, trying to reconnect the RTSP stream\n",
name, (now - source->reconnect_timer)/G_USEC_PER_SEC);
audio_fd = -1;
Expand Down Expand Up @@ -7582,7 +7646,7 @@ static void *janus_streaming_relay_thread(void *data) {
data_fd = source->data_fd;
audio_rtcp_fd = source->audio_rtcp_fd;
video_rtcp_fd = source->video_rtcp_fd;
ka_timeout = ((gint64)source->ka_timeout*G_USEC_PER_SEC)/2;
ka_timeout = source->ka_timeout;
}
}
source->reconnect_timer = janus_get_monotonic_time();
Expand All @@ -7592,7 +7656,7 @@ static void *janus_streaming_relay_thread(void *data) {
}
if(audio_fd < 0 && video_fd[0] < 0 && video_fd[1] < 0 && video_fd[2] < 0 && data_fd < 0) {
/* No socket, we may be in the process of reconnecting, or waiting to reconnect */
g_usleep(5000000);
g_usleep(source->reconnect_delay);
continue;
}
/* We may also need to occasionally send a OPTIONS request as a keep-alive */
Expand Down Expand Up @@ -8485,7 +8549,6 @@ static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data)
return;
}


static void janus_streaming_relay_rtcp_packet(gpointer data, gpointer user_data) {
janus_streaming_rtp_relay_packet *packet = (janus_streaming_rtp_relay_packet *)user_data;
if(!packet || !packet->data || packet->length < 1) {
Expand Down

0 comments on commit fbf1060

Please sign in to comment.