Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making the timeout parameters for streaming plugin for RTSP play out configurable #2598

Merged
merged 11 commits into from
Mar 25, 2021
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
pontscho marked this conversation as resolved.
Show resolved Hide resolved
#session_timeout = 0
pontscho marked this conversation as resolved.
Show resolved Hide resolved
# rtsp_timeout = 10
pontscho marked this conversation as resolved.
Show resolved Hide resolved
# rtsp_conn_timeout = 5
pontscho marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the stuff you added to the rtsp-test sample can be removed, as it's already documented on top.

Copy link
Contributor Author

@pontscho pontscho Mar 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S***. I've added a wrong config file earlier and I lost in that changes. I've fixed it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem! 😄 Thanks for the quick fixes!

#}
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)
pontscho marked this conversation as resolved.
Show resolved Hide resolved
\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");
pontscho marked this conversation as resolved.
Show resolved Hide resolved
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;
}
pontscho marked this conversation as resolved.
Show resolved Hide resolved

/* 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