Skip to content

Commit

Permalink
Fixes to RTSP latching procedure (fixes #1536, replaces #1851) (#1866)
Browse files Browse the repository at this point in the history
  • Loading branch information
lminiero committed Nov 26, 2019
1 parent d5e8210 commit a4ad2fe
Showing 1 changed file with 277 additions and 57 deletions.
334 changes: 277 additions & 57 deletions plugins/janus_streaming.c
Original file line number Diff line number Diff line change
Expand Up @@ -5543,6 +5543,7 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
char uri[1024];
char vtransport[1024];
char vhost[256];
vhost[0] = '\0';
int vsport = 0, vsport_rtcp = 0;
multiple_fds video_fds = {-1, -1};

Expand All @@ -5552,18 +5553,22 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
char acontrol[2048];
char atransport[1024];
char ahost[256];
ahost[0] = '\0';
int asport = 0, asport_rtcp = 0;
multiple_fds audio_fds = {-1, -1};

janus_mutex_lock(&mountpoints_mutex);
/* Parse both video and audio first before proceed to setup as curldata will be reused */
int vresult;
vresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "video", &vpt,
vtransport, vhost, vrtpmap, vfmtp, vcontrol, &source->video_iface, &video_fds);

int aresult;
aresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "audio", &apt,
atransport, ahost, artpmap, afmtp, acontrol, &source->audio_iface, &audio_fds);
int vresult = -1;
if(dovideo) {
vresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "video", &vpt,
vtransport, vhost, vrtpmap, vfmtp, vcontrol, &source->video_iface, &video_fds);
}
int aresult = -1;
if(doaudio) {
aresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "audio", &apt,
atransport, ahost, artpmap, afmtp, acontrol, &source->audio_iface, &audio_fds);
}
janus_mutex_unlock(&mountpoints_mutex);

if(vresult == -1 && aresult == -1) {
Expand Down Expand Up @@ -5609,33 +5614,139 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
return -5;
}
JANUS_LOG(LOG_VERB, "SETUP answer:%s\n", curldata->buffer);
const char *timeout = strstr(curldata->buffer, ";timeout=");
size_t attr_len = strlen(";timeout=");
/* Check also the attribute in the form "; timeout" */
if (timeout == NULL) {
timeout = strstr(curldata->buffer, "; timeout=");
attr_len = strlen("; timeout=");
/* Find the Transport header and parse it */
char *header = strstr(curldata->buffer, "Transport:");
if(header == NULL)
header = strstr(curldata->buffer, "transport:");
if(header != NULL) {
char *end = strchr(header, '\r');
if(end != NULL)
*end = '\0';
end = strchr(header, '\n');
if(end != NULL)
*end = '\0';
/* Iterate on all params */
char *p = header, param[100], *pi = NULL;
int read = 0;
gboolean first = TRUE;
while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
if(first) {
/* Skip */
first = FALSE;
} else {
pi = param;
while(*pi == ' ')
pi++;
char name[50], value[50];
if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
if(!strcasecmp(name, "ssrc")) {
/* Take note of the video SSRC */
uint32_t ssrc = strtol(value, NULL, 16);
JANUS_LOG(LOG_VERB, " -- SSRC (video): %"SCNu32"\n", ssrc);
source->video_ssrc = ssrc;
} else if(!strcasecmp(name, "source")) {
/* If we got an address via c-line, replace it */
g_snprintf(vhost, sizeof(vhost), "%s", value);
JANUS_LOG(LOG_VERB, " -- Source (video): %s\n", vhost);
} else if(!strcasecmp(name, "server_port")) {
/* Take note of the server port */
char *dash = NULL;
vsport = strtol(value, &dash, 10);
vsport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
JANUS_LOG(LOG_VERB, " -- RTP port (video): %d\n", vsport);
JANUS_LOG(LOG_VERB, " -- RTCP port (video): %d\n", vsport_rtcp);
}
}
}
/* Move to the next param */
p += read;
if(*p != ';')
break;
while(*p == ';')
p++;
}
}
if(timeout != NULL) {
/* There's a timeout to take into account: take note of the
* value for sending OPTIONS keepalives later on */
ka_timeout = atoi(timeout+attr_len);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %d\n", ka_timeout);
/* Find the Session header and parse it */
header = strstr(curldata->buffer, "Session:");

This comment has been minimized.

Copy link
@kefir266

kefir266 Jan 15, 2020

It doesn't work for Axios cameras because of ends of line x0A I believe
How to fix it quick? @lminiero
Thnx

00000000│ 52 54 53 50 2f 31 2e 30 ┊ 20 32 30 30 20 4f 4b 0a │RTSP/1.0┊ 200 OK_│
│00000010│ 43 53 65 71 3a 20 32 0a ┊ 54 72 61 6e 73 70 6f 72 │CSeq: 2_┊Transpor│
│00000020│ 74 3a 20 52 54 50 2f 41 ┊ 56 50 3b 75 6e 69 63 61 │t: RTP/A┊VP;unica│
│00000030│ 73 74 3b 63 6c 69 65 6e ┊ 74 5f 70 6f 72 74 3d 31 │st;clien┊t_port=1│
│00000040│ 30 30 30 30 2d 31 30 30 ┊ 30 31 3b 73 65 72 76 65 │0000-100┊01;serve│
│00000050│ 72 5f 70 6f 72 74 3d 35 ┊ 30 30 33 32 2d 35 30 30 │r_port=5┊0032-500│
│00000060│ 33 33 3b 73 73 72 63 3d ┊ 32 31 43 33 37 36 46 45 │33;ssrc=┊21C376FE│
│00000070│ 3b 6d 6f 64 65 3d 22 50 ┊ 4c 41 59 22 0a 53 65 72 │;mode="P┊LAY"Ser│
│00000080│ 76 65 72 3a 20 47 53 74 ┊ 72 65 61 6d 65 72 20 52 │ver: GSt┊reamer R│
│00000090│ 54 53 50 20 73 65 72 76 ┊ 65 72 0a 53 65 73 73 69 │TSP serv┊er_Sessi│
│000000a0│ 6f 6e 3a 20 33 4b 6f 72 ┊ 71 70 77 50 5a 43 68 55 │on: 3Kor┊qpwPZChU│
│000000b0│ 70 31 4e 39 3b 74 69 6d ┊ 65 6f 75 74 3d 36 30 0a │p1N9;tim┊eout=60_│
│000000c0│ 44 61 74 65 3a 20 57 65 ┊ 64 2c 20 31 35 20 4a 61 │Date: We┊d, 15 Ja│
│000000d0│ 6e 20 32 30 32 30 20 31 ┊ 31 3a 35 34 3a 34 30 20 │n 2020 1┊1:54:40 │
│000000e0│ 47 4d 54 0a ┊ │GMT
┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴──────

This comment has been minimized.

Copy link
@kefir266

kefir266 Jan 15, 2020

There should be something like this:

//			if(end != NULL)
//				*end = '\0';
//			end = strchr(header, '\n');
//			if(end != NULL)
//				*end = '\0';

otherwise header = strstr(curldata->buffer, "Session:"); never would work because of \0 after 'Transport:' processing
this way for axios work very well, but I have camera that has such params in Transport:
'Transport: RTP/AVP;unicast;mode=play;client_port=10000-10001;server_port=20408-20409;ssrc=289aeb8e'
and after this changes it become unstable

[2448211519024429]     Just got some NACKS (1) we should handle...
[ice.c:janus_ice_cb_nice_recv:2777] [2448211519024429]   >> 304
[2448211519024429]   >> >> Can't retransmit packet 304, we don't have it...
[2448211519024429]  Got an RTCP packet
[2448211519024429] Incoming RTCP, bundling: this is video (no audio has been negotiated)
   Parsing compound packet (total of 60 bytes)
     #1 RR (201)
jitter=756.000000, fraction=3, loss=49
       RTCP PT 201, length: 32 bytes
     #2 NACK -- RTPFB (205)
[rtcp.c:janus_rtcp_fix_ssrc:413]         Got 4 nacks
[rtcp.c:janus_rtcp_fix_ssrc:428] [0] 246 / 1101111101111111

[rtcp.c:janus_rtcp_fix_ssrc:428] [1] 263 / 1101111111111101

[rtcp.c:janus_rtcp_fix_ssrc:428] [2] 280 / 1111111111110111

[rtcp.c:janus_rtcp_fix_ssrc:428] [3] 297 / 1000001000000000

       RTCP PT 205, length: 28 bytes
[2448211519024429] Got video RTCP (60 bytes)
[rtcp.c:janus_rtcp_get_nacks:1010]         Got 4 nacks
[rtcp.c:janus_rtcp_get_nacks:1028] [0] 246 / 1101111101111111

[rtcp.c:janus_rtcp_get_nacks:1028] [1] 263 / 1101111111111101

[rtcp.c:janus_rtcp_get_nacks:1028] [2] 280 / 1111111111110111

[rtcp.c:janus_rtcp_get_nacks:1028] [3] 297 / 1000001000000000

[2448211519024429]     Just got some NACKS (49) we should handle...
[ice.c:janus_ice_cb_nice_recv:2777] [2448211519024429]   >> 246
[2448211519024429]   >> >> Can't retransmit packet 246, we don't have it...
[ice.c:janus_ice_cb_nice_recv:2777] [2448211519024429]   >> 247
[2448211519024429]   >> >> Can't retransmit packet 247, we don't have it...

This comment has been minimized.

Copy link
@lminiero

lminiero Jan 15, 2020

Author Member

Please stop commenting on closed issues, and as explained clearly in the guidelines, don't post code snippets or logs inline (as they mess up issues references, like yours did: fixed it for you). If you have a fix, please consider submitting a pull request and I'll gladly review.

if(header == NULL)
header = strstr(curldata->buffer, "session:");
if(header != NULL) {
char *end = strchr(header, '\r');
if(end != NULL)
*end = '\0';
end = strchr(header, '\n');
if(end != NULL)
*end = '\0';
/* Iterate on all params */
char *p = header, param[100], *pi = NULL;
int read = 0;
gboolean first = TRUE;
while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
if(first) {
/* Skip */
first = FALSE;
} else {
pi = param;
while(*pi == ' ')
pi++;
char name[50], value[50];
if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
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);
}
}
}
/* Move to the next param */
p += read;
if(*p != ';')
break;
while(*p == ';')
p++;
}
}
/* Get the RTP server port, which we'll need for the latching packet */
const char *vssrc = strstr(curldata->buffer, ";ssrc=");
if(vssrc != NULL) {
uint32_t ssrc = strtol(vssrc+strlen(";ssrc="), NULL, 16);
JANUS_LOG(LOG_VERB, " -- SSRC (video): %"SCNu32"\n", ssrc);
source->video_ssrc = ssrc;
#if CURL_AT_LEAST_VERSION(7, 62, 0)
/* If we don't have a host yet (no c-line, no source in Transport), use the server address */
if(strlen(vhost) == 0 || !strcmp(vhost, "0.0.0.0")) {
JANUS_LOG(LOG_WARN, "No c-line or source for RTSP video address, resolving server address...\n");
CURLU *url = curl_url();
if(url != NULL) {
CURLUcode code = curl_url_set(url, CURLUPART_URL, source->rtsp_url, 0);
if(code == 0) {
char *host = NULL;
code = curl_url_get(url, CURLUPART_HOST, &host, 0);
if(code == 0) {
/* Resolve the address */
struct addrinfo *info = NULL, *start = NULL;
janus_network_address addr;
janus_network_address_string_buffer addr_buf;
if(getaddrinfo(host, NULL, NULL, &info) == 0) {
start = info;
while(info != NULL) {
if(janus_network_address_from_sockaddr(info->ai_addr, &addr) == 0 &&
janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
/* Resolved */
g_snprintf(vhost, sizeof(vhost), "%s",
janus_network_address_string_from_buffer(&addr_buf));
JANUS_LOG(LOG_WARN, " -- %s\n", vhost);
break;
}
info = info->ai_next;
}
}
if(start)
freeaddrinfo(start);
curl_free(host);
}
}
curl_url_cleanup(url);
}
}
const char *server_ports = strstr(curldata->buffer, ";server_port=");
if(server_ports != NULL) {
char *dash = NULL;
vsport = strtol(server_ports+strlen(";server_port="), &dash, 10);
vsport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
JANUS_LOG(LOG_VERB, " -- RTP port (video): %d\n", vsport);
JANUS_LOG(LOG_VERB, " -- RTCP port (video): %d\n", vsport_rtcp);
#endif
if(strlen(vhost) == 0 || !strcmp(vhost, "0.0.0.0")) {
/* Still nothing... */
JANUS_LOG(LOG_WARN, "No host address for the RTSP video stream, no latching will be performed\n");
}
}

Expand Down Expand Up @@ -5677,35 +5788,144 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp
return -6;
}
JANUS_LOG(LOG_VERB, "SETUP answer:%s\n", curldata->buffer);
const char *timeout = strstr(curldata->buffer, ";timeout=");
size_t attr_len = strlen(";timeout=");
/* Check also the attribute in the form "; timeout" */
if (timeout == NULL) {
timeout = strstr(curldata->buffer, "; timeout=");
attr_len = strlen("; timeout=");
/* Find the Transport header and parse it */
char *header = strstr(curldata->buffer, "Transport:");
if(header == NULL)
header = strstr(curldata->buffer, "transport:");
if(header != NULL) {
char *end = strchr(header, '\r');
if(end != NULL)
*end = '\0';
end = strchr(header, '\n');
if(end != NULL)
*end = '\0';
/* Iterate on all params */
char *p = header, param[100], *pi = NULL;
int read = 0;
gboolean first = TRUE;
while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
if(first) {
/* Skip */
first = FALSE;
} else {
pi = param;
while(*pi == ' ')
pi++;
char name[50], value[50];
if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
if(!strcasecmp(name, "ssrc")) {
/* Take note of the audio SSRC */
uint32_t ssrc = strtol(value, NULL, 16);
JANUS_LOG(LOG_VERB, " -- SSRC (audio): %"SCNu32"\n", ssrc);
source->audio_ssrc = ssrc;
} else if(!strcasecmp(name, "source")) {
/* If we got an address via c-line, replace it */
g_snprintf(ahost, sizeof(ahost), "%s", value);
JANUS_LOG(LOG_VERB, " -- Source (audio): %s\n", ahost);
} else if(!strcasecmp(name, "server_port")) {
/* Take note of the server port */
char *dash = NULL;
asport = strtol(value, &dash, 10);
asport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
JANUS_LOG(LOG_VERB, " -- RTP port (audio): %d\n", vsport);
JANUS_LOG(LOG_VERB, " -- RTCP port (audio): %d\n", vsport_rtcp);
}
}
}
/* Move to the next param */
p += read;
if(*p != ';')
break;
while(*p == ';')
p++;
}
}
if(timeout != NULL) {
/* There's a timeout to take into account: take note of the
* value for sending OPTIONS keepalives later on */
int temp_timeout = atoi(timeout+attr_len);
JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %d\n", temp_timeout);
if(temp_timeout > 0 && temp_timeout < ka_timeout)
ka_timeout = temp_timeout;
/* Find the Session header and parse it */
header = strstr(curldata->buffer, "Session:");
if(header == NULL)
header = strstr(curldata->buffer, "session:");
if(header != NULL) {
char *end = strchr(header, '\r');
if(end != NULL)
*end = '\0';
end = strchr(header, '\n');
if(end != NULL)
*end = '\0';
/* Iterate on all params */
char *p = header, param[100], *pi = NULL;
int read = 0;
gboolean first = TRUE;
while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
if(first) {
/* Skip */
first = FALSE;
} else {
pi = param;
while(*pi == ' ')
pi++;
char name[50], value[50];
if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
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);
}
}
}
/* Move to the next param */
p += read;
if(*p != ';')
break;
while(*p == ';')
p++;
}
}
/* Get the RTP server port, which we'll need for the latching packet */
const char *assrc = strstr(curldata->buffer, ";ssrc=");
if(assrc != NULL) {
uint32_t ssrc = strtol(assrc+strlen(";ssrc="), NULL, 16);
JANUS_LOG(LOG_VERB, " -- SSRC (audio): %"SCNu32"\n", ssrc);
source->audio_ssrc = ssrc;
/* If we don't have a host yet (no c-line, no source in Transport), use the server address */
if(strlen(ahost) == 0 || !strcmp(ahost, "0.0.0.0")) {
if(strlen(vhost) > 0 && strcmp(vhost, "0.0.0.0")) {
JANUS_LOG(LOG_WARN, "No c-line or source for RTSP audio stream, copying the video address (%s)\n", vhost);
g_snprintf(ahost, sizeof(ahost), "%s", vhost);
} else {
#if CURL_AT_LEAST_VERSION(7, 62, 0)
JANUS_LOG(LOG_WARN, "No c-line or source for RTSP audio stream, resolving server address...\n");
CURLU *url = curl_url();
if(url != NULL) {
CURLUcode code = curl_url_set(url, CURLUPART_URL, source->rtsp_url, 0);
if(code == 0) {
char *host = NULL;
code = curl_url_get(url, CURLUPART_HOST, &host, 0);
if(code == 0) {
/* Resolve the address */
struct addrinfo *info = NULL, *start = NULL;
janus_network_address addr;
janus_network_address_string_buffer addr_buf;
if(getaddrinfo(host, NULL, NULL, &info) == 0) {
start = info;
while(info != NULL) {
if(janus_network_address_from_sockaddr(info->ai_addr, &addr) == 0 &&
janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
/* Resolved */
g_snprintf(ahost, sizeof(ahost), "%s",
janus_network_address_string_from_buffer(&addr_buf));
JANUS_LOG(LOG_WARN, " -- %s\n", ahost);
break;
}
info = info->ai_next;
}
}
if(start)
freeaddrinfo(start);
curl_free(host);
}
}
curl_url_cleanup(url);
}
#endif
}
}
const char *server_ports = strstr(curldata->buffer, ";server_port=");
if(server_ports != NULL) {
char *dash = NULL;
asport = strtol(server_ports+strlen(";server_port="), &dash, 10);
asport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
JANUS_LOG(LOG_VERB, " -- RTP port (audio): %d\n", asport);
JANUS_LOG(LOG_VERB, " -- RTCP port (audio): %d\n", asport_rtcp);
if(strlen(ahost) == 0 || !strcmp(ahost, "0.0.0.0")) {
/* Still nothing... */
JANUS_LOG(LOG_WARN, "No host address for the RTSP audio stream, no latching will be performed\n");
}
}

Expand Down

0 comments on commit a4ad2fe

Please sign in to comment.