1 /*! \file sdp.c
2 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 * \copyright GNU General Public License v3
4 * \brief SDP processing
5 * \details Implementation of an SDP
6 * parser/merger/generator in the server. Each SDP coming from peers is
7 * stripped/anonymized before it is passed to the plugins: all
8 * DTLS/ICE/transport related information is removed, only leaving the
9 * relevant information in place. SDP coming from plugins is stripped/anonymized
10 * as well, and merged with the proper DTLS/ICE/transport information before
11 * it is sent to the peers. The actual SDP processing (parsing SDP strings,
12 * representation of SDP as an internal format, and so on) is done via
13 * the tools provided in sdp-utils.h.
14 *
15 * \ingroup protocols
16 * \ref protocols
17 */
18
19 #include <netdb.h>
20
21 #include <gio/gio.h>
22
23 #include "janus.h"
24 #include "ice.h"
25 #include "sdp.h"
26 #include "utils.h"
27 #include "ip-utils.h"
28 #include "debug.h"
29 #include "events.h"
30
31
32 /* Pre-parse SDP: is this SDP valid? how many audio/video lines? any features to take into account? */
janus_sdp_preparse(void * ice_handle,const char * jsep_sdp,char * error_str,size_t errlen,int * audio,int * video,int * data)33 janus_sdp *janus_sdp_preparse(void *ice_handle, const char *jsep_sdp, char *error_str, size_t errlen,
34 int *audio, int *video, int *data) {
35 if(!ice_handle || !jsep_sdp || !audio || !video || !data) {
36 JANUS_LOG(LOG_ERR, " Can't preparse, invalid arguments\n");
37 return NULL;
38 }
39 janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
40 janus_sdp *parsed_sdp = janus_sdp_parse(jsep_sdp, error_str, errlen);
41 if(!parsed_sdp) {
42 JANUS_LOG(LOG_ERR, " Error parsing SDP? %s\n", error_str ? error_str : "(unknown reason)");
43 /* Invalid SDP */
44 return NULL;
45 }
46 /* Look for m-lines */
47 GList *temp = parsed_sdp->m_lines;
48 while(temp) {
49 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
50 if(m->type == JANUS_SDP_AUDIO) {
51 *audio = *audio + 1;
52 } else if(m->type == JANUS_SDP_VIDEO) {
53 *video = *video + 1;
54 }
55 /* Preparse the mid as well, and check if bundle-only is used */
56 GList *tempA = m->attributes;
57 while(tempA) {
58 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
59 if(a->name) {
60 if(!strcasecmp(a->name, "bundle-only") && m->port == 0) {
61 /* Port 0 but bundle-only is used, don't disable this m-line */
62 m->port = 9;
63 } else if(!strcasecmp(a->name, "mid")) {
64 /* Found mid attribute */
65 if(a->value == NULL) {
66 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid mid attribute (no value)\n", handle->handle_id);
67 janus_sdp_destroy(parsed_sdp);
68 return NULL;
69 }
70 if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
71 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
72 if(strlen(a->value) > 16) {
73 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Audio mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
74 janus_sdp_destroy(parsed_sdp);
75 return NULL;
76 }
77 if(handle->audio_mid == NULL)
78 handle->audio_mid = g_strdup(a->value);
79 if(handle->stream_mid == NULL)
80 handle->stream_mid = handle->audio_mid;
81 } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
82 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
83 if(strlen(a->value) > 16) {
84 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Video mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
85 janus_sdp_destroy(parsed_sdp);
86 return NULL;
87 }
88 if(handle->video_mid == NULL)
89 handle->video_mid = g_strdup(a->value);
90 if(handle->stream_mid == NULL)
91 handle->stream_mid = handle->video_mid;
92 }
93 }
94 }
95 /* If the m-line is disabled don't actually increase the count */
96 if(m->port == 0) {
97 if(m->type == JANUS_SDP_AUDIO) {
98 *audio = *audio - 1;
99 } else if(m->type == JANUS_SDP_VIDEO) {
100 *video = *video - 1;
101 }
102 }
103 tempA = tempA->next;
104 }
105 temp = temp->next;
106 }
107 #ifdef HAVE_SCTP
108 *data = (strstr(jsep_sdp, "DTLS/SCTP") && !strstr(jsep_sdp, " 0 DTLS/SCTP") &&
109 !strstr(jsep_sdp, " 0 UDP/DTLS/SCTP")) ? 1 : 0; /* FIXME This is a really hacky way of checking... */
110 #else
111 *data = 0;
112 #endif
113
114 return parsed_sdp;
115 }
116
117 /* Parse SDP */
janus_sdp_process(void * ice_handle,janus_sdp * remote_sdp,gboolean rids_hml,gboolean update)118 int janus_sdp_process(void *ice_handle, janus_sdp *remote_sdp, gboolean rids_hml, gboolean update) {
119 if(!ice_handle || !remote_sdp)
120 return -1;
121 janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
122 janus_ice_stream *stream = handle->stream;
123 if(!stream)
124 return -1;
125 gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
126 int audio = 0, video = 0;
127 #ifdef HAVE_SCTP
128 int data = 0;
129 #endif
130 gboolean rtx = FALSE;
131 /* Ok, let's start with global attributes */
132 GList *temp = remote_sdp->attributes;
133 while(temp) {
134 janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
135 if(a && a->name && a->value) {
136 if(!strcasecmp(a->name, "fingerprint")) {
137 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->value);
138 if(strcasestr(a->value, "sha-256 ") == a->value) {
139 rhashing = g_strdup("sha-256");
140 rfingerprint = g_strdup(a->value + strlen("sha-256 "));
141 } else if(strcasestr(a->value, "sha-1 ") == a->value) {
142 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
143 rhashing = g_strdup("sha-1");
144 rfingerprint = g_strdup(a->value + strlen("sha-1 "));
145 } else {
146 /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
147 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
148 }
149 } else if(!strcasecmp(a->name, "ice-ufrag")) {
150 JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global): %s\n", handle->handle_id, a->value);
151 ruser = g_strdup(a->value);
152 } else if(!strcasecmp(a->name, "ice-pwd")) {
153 JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global): %s\n", handle->handle_id, a->value);
154 rpass = g_strdup(a->value);
155 }
156 }
157 temp = temp->next;
158 }
159 /* Now go on with m-line and their attributes */
160 int mlines = 0;
161 temp = remote_sdp->m_lines;
162 while(temp) {
163 mlines++;
164 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
165 if(m->type == JANUS_SDP_AUDIO) {
166 if(handle->rtp_profile == NULL && m->proto != NULL)
167 handle->rtp_profile = g_strdup(m->proto);
168 audio++;
169 if(audio > 1) {
170 temp = temp->next;
171 continue;
172 }
173 if(m->port > 0) {
174 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
175 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
176 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
177 stream->audio_ssrc = janus_random_uint32(); /* FIXME Should we look for conflicts? */
178 if(stream->audio_rtcp_ctx == NULL) {
179 stream->audio_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
180 stream->audio_rtcp_ctx->tb = 48000; /* May change later */
181 }
182 }
183 gboolean receiving = (stream->audio_recv == TRUE);
184 switch(m->direction) {
185 case JANUS_SDP_INACTIVE:
186 case JANUS_SDP_INVALID:
187 stream->audio_send = FALSE;
188 stream->audio_recv = FALSE;
189 break;
190 case JANUS_SDP_SENDONLY:
191 /* A sendonly peer means recvonly for Janus */
192 stream->audio_send = FALSE;
193 stream->audio_recv = TRUE;
194 break;
195 case JANUS_SDP_RECVONLY:
196 /* A recvonly peer means sendonly for Janus */
197 stream->audio_send = TRUE;
198 stream->audio_recv = FALSE;
199 break;
200 case JANUS_SDP_SENDRECV:
201 case JANUS_SDP_DEFAULT:
202 default:
203 stream->audio_send = TRUE;
204 stream->audio_recv = TRUE;
205 break;
206 }
207 if(receiving != stream->audio_recv)
208 janus_ice_notify_media_stopped(handle);
209 if(m->ptypes != NULL) {
210 g_list_free(stream->audio_payload_types);
211 stream->audio_payload_types = g_list_copy(m->ptypes);
212 }
213 } else {
214 /* Audio rejected? */
215 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
216 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
217 }
218 } else if(m->type == JANUS_SDP_VIDEO) {
219 if(handle->rtp_profile == NULL && m->proto != NULL)
220 handle->rtp_profile = g_strdup(m->proto);
221 video++;
222 if(video > 1) {
223 temp = temp->next;
224 continue;
225 }
226 if(m->port > 0) {
227 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
228 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
229 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
230 stream->video_ssrc = janus_random_uint32(); /* FIXME Should we look for conflicts? */
231 if(stream->video_rtcp_ctx[0] == NULL) {
232 stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
233 stream->video_rtcp_ctx[0]->tb = 90000; /* May change later */
234 }
235 }
236 gboolean receiving = (stream->video_recv == TRUE);
237 switch(m->direction) {
238 case JANUS_SDP_INACTIVE:
239 case JANUS_SDP_INVALID:
240 stream->video_send = FALSE;
241 stream->video_recv = FALSE;
242 break;
243 case JANUS_SDP_SENDONLY:
244 /* A sendonly peer means recvonly for Janus */
245 stream->video_send = FALSE;
246 stream->video_recv = TRUE;
247 break;
248 case JANUS_SDP_RECVONLY:
249 /* A recvonly peer means sendonly for Janus */
250 stream->video_send = TRUE;
251 stream->video_recv = FALSE;
252 break;
253 case JANUS_SDP_SENDRECV:
254 case JANUS_SDP_DEFAULT:
255 default:
256 stream->video_send = TRUE;
257 stream->video_recv = TRUE;
258 break;
259 }
260 if(receiving != stream->video_recv)
261 janus_ice_notify_media_stopped(handle);
262 if(m->ptypes != NULL) {
263 g_list_free(stream->video_payload_types);
264 stream->video_payload_types = g_list_copy(m->ptypes);
265 }
266 } else {
267 /* Video rejected? */
268 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video rejected by peer...\n", handle->handle_id);
269 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
270 }
271 #ifdef HAVE_SCTP
272 } else if(m->type == JANUS_SDP_APPLICATION) {
273 /* Is this SCTP for DataChannels? */
274 if(!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP")) {
275 data++;
276 if(data > 1) {
277 temp = temp->next;
278 continue;
279 }
280 if(m->port > 0) {
281 /* Yep */
282 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
283 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
284 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
285 }
286 if(!strcasecmp(m->proto, "UDP/DTLS/SCTP")) {
287 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
288 } else {
289 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
290 }
291 } else {
292 /* Data channels rejected? */
293 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels rejected by peer...\n", handle->handle_id);
294 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
295 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
296 }
297 } else {
298 /* Unsupported data channels format. */
299 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels format %s unsupported, skipping\n", handle->handle_id, m->proto);
300 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
301 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
302 }
303 #endif
304 } else {
305 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
306 }
307 if(stream == NULL) {
308 temp = temp->next;
309 continue;
310 }
311 /* Look for mid, ICE credentials and fingerprint first: check media attributes */
312 GList *tempA = m->attributes;
313 while(tempA) {
314 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
315 if(a->name && a->value) {
316 if(!strcasecmp(a->name, "mid")) {
317 /* Found mid attribute */
318 if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
319 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
320 if(strlen(a->value) > 16) {
321 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Audio mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
322 return -2;
323 }
324 if(handle->audio_mid == NULL)
325 handle->audio_mid = g_strdup(a->value);
326 if(handle->stream_mid == NULL)
327 handle->stream_mid = handle->audio_mid;
328 } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
329 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
330 if(strlen(a->value) > 16) {
331 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Video mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
332 return -2;
333 }
334 if(handle->video_mid == NULL)
335 handle->video_mid = g_strdup(a->value);
336 if(handle->stream_mid == NULL)
337 handle->stream_mid = handle->video_mid;
338 #ifdef HAVE_SCTP
339 } else if(m->type == JANUS_SDP_APPLICATION) {
340 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->value);
341 if(handle->data_mid == NULL)
342 handle->data_mid = g_strdup(a->value);
343 if(handle->stream_mid == NULL)
344 handle->stream_mid = handle->data_mid;
345 #endif
346 }
347 } else if(!strcasecmp(a->name, "fingerprint")) {
348 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->value);
349 if(strcasestr(a->value, "sha-256 ") == a->value) {
350 g_free(rhashing); /* FIXME We're overwriting the global one, if any */
351 rhashing = g_strdup("sha-256");
352 g_free(rfingerprint); /* FIXME We're overwriting the global one, if any */
353 rfingerprint = g_strdup(a->value + strlen("sha-256 "));
354 } else if(strcasestr(a->value, "sha-1 ") == a->value) {
355 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
356 g_free(rhashing); /* FIXME We're overwriting the global one, if any */
357 rhashing = g_strdup("sha-1");
358 g_free(rfingerprint); /* FIXME We're overwriting the global one, if any */
359 rfingerprint = g_strdup(a->value + strlen("sha-1 "));
360 } else {
361 /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
362 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
363 }
364 } else if(!strcasecmp(a->name, "setup")) {
365 JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local): %s\n", handle->handle_id, a->value);
366 if(!update) {
367 if(!strcasecmp(a->value, "actpass") || !strcasecmp(a->value, "passive")) {
368 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting connect state (DTLS client)\n", handle->handle_id);
369 stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
370 } else if(!strcasecmp(a->value, "active")) {
371 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting accept state (DTLS server)\n", handle->handle_id);
372 stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
373 }
374 if(stream->component && stream->component->dtls)
375 stream->component->dtls->dtls_role = stream->dtls_role;
376 }
377 /* TODO Handle holdconn... */
378 } else if(!strcasecmp(a->name, "ice-ufrag")) {
379 JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local): %s\n", handle->handle_id, a->value);
380 g_free(ruser); /* FIXME We're overwriting the global one, if any */
381 ruser = g_strdup(a->value);
382 } else if(!strcasecmp(a->name, "ice-pwd")) {
383 JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local): %s\n", handle->handle_id, a->value);
384 g_free(rpass); /* FIXME We're overwriting the global one, if any */
385 rpass = g_strdup(a->value);
386 }
387 }
388 tempA = tempA->next;
389 }
390 if(mlines == 1) {
391 if(!ruser || !rpass || (janus_is_webrtc_encryption_enabled() && (!rfingerprint || !rhashing))) {
392 /* Missing mandatory information, failure... */
393 JANUS_LOG(LOG_ERR, "[%"SCNu64"] SDP missing mandatory information\n", handle->handle_id);
394 JANUS_LOG(LOG_ERR, "[%"SCNu64"] %p, %p, %p, %p\n", handle->handle_id, ruser, rpass, rfingerprint, rhashing);
395 if(ruser)
396 g_free(ruser);
397 ruser = NULL;
398 if(rpass)
399 g_free(rpass);
400 rpass = NULL;
401 if(rhashing)
402 g_free(rhashing);
403 rhashing = NULL;
404 if(rfingerprint)
405 g_free(rfingerprint);
406 rfingerprint = NULL;
407 return -2;
408 }
409 /* If we received the ICE credentials for the first time, enforce them */
410 if(ruser && !stream->ruser && rpass && !stream->rpass) {
411 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting remote credentials...\n", handle->handle_id);
412 if(!nice_agent_set_remote_credentials(handle->agent, handle->stream_id, ruser, rpass)) {
413 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to set remote credentials!\n", handle->handle_id);
414 }
415 } else
416 /* If this is a renegotiation, check if this is an ICE restart */
417 if((ruser && stream->ruser && strcmp(ruser, stream->ruser)) ||
418 (rpass && stream->rpass && strcmp(rpass, stream->rpass))) {
419 JANUS_LOG(LOG_INFO, "[%"SCNu64"] ICE restart detected\n", handle->handle_id);
420 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES);
421 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART);
422 }
423 /* Store fingerprint and hashing */
424 if(janus_is_webrtc_encryption_enabled()) {
425 g_free(stream->remote_hashing);
426 stream->remote_hashing = g_strdup(rhashing);
427 g_free(stream->remote_fingerprint);
428 stream->remote_fingerprint = g_strdup(rfingerprint);
429 }
430 /* Store the ICE username and password for this stream */
431 g_free(stream->ruser);
432 stream->ruser = g_strdup(ruser);
433 g_free(stream->rpass);
434 stream->rpass = g_strdup(rpass);
435 }
436 /* Is simulcasting enabled, using rid? (we need to check this before parsing SSRCs) */
437 tempA = m->attributes;
438 stream->rids_hml = rids_hml;
439 while(tempA) {
440 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
441 if(a->name && !strcasecmp(a->name, "rid") && a->value) {
442 /* This attribute is used for simulcasting */
443 char rid[16];
444 if(sscanf(a->value, "%15s send", rid) != 1) {
445 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse rid attribute...\n", handle->handle_id);
446 } else {
447 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsed rid: %s\n", handle->handle_id, rid);
448 if(stream->rid[rids_hml ? 2 : 0] == NULL) {
449 stream->rid[rids_hml ? 2 : 0] = g_strdup(rid);
450 } else if(stream->rid[1] == NULL) {
451 stream->rid[1] = g_strdup(rid);
452 } else if(stream->rid[rids_hml ? 0 : 2] == NULL) {
453 stream->rid[rids_hml ? 0 : 2] = g_strdup(rid);
454 } else {
455 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Too many RTP Stream IDs, ignoring '%s'...\n", handle->handle_id, rid);
456 }
457 }
458 } else if(a->name && !strcasecmp(a->name, "simulcast") && a->value) {
459 /* Firefox and Chrome signal simulcast support differently */
460 stream->legacy_rid = strstr(a->value, "rid=") ? TRUE : FALSE;
461 }
462 tempA = tempA->next;
463 }
464 /* If rid is involved, check how many of them we have (it may be less than 3) */
465 if(stream->rid[0] == NULL && stream->rid[2] != NULL) {
466 stream->rid[0] = stream->rid[1];
467 stream->rid[1] = stream->rid[2];
468 stream->rid[2] = NULL;
469 }
470 if(stream->rid[0] == NULL && stream->rid[1] != NULL) {
471 stream->rid[0] = stream->rid[1];
472 stream->rid[1] = NULL;
473 }
474 /* Let's start figuring out the SSRCs, and any grouping that may be there */
475 stream->audio_ssrc_peer_new = 0;
476 stream->video_ssrc_peer_new[0] = 0;
477 stream->video_ssrc_peer_new[1] = 0;
478 stream->video_ssrc_peer_new[2] = 0;
479 stream->video_ssrc_peer_rtx_new[0] = 0;
480 stream->video_ssrc_peer_rtx_new[1] = 0;
481 stream->video_ssrc_peer_rtx_new[2] = 0;
482 /* Any SSRC SIM group? */
483 tempA = m->attributes;
484 while(tempA) {
485 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
486 if(a->name && a->value) {
487 if(!strcasecmp(a->name, "ssrc-group") && strstr(a->value, "SIM")) {
488 int res = janus_sdp_parse_ssrc_group(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
489 if(res != 0) {
490 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC SIM group attribute... (%d)\n", handle->handle_id, res);
491 }
492 }
493 }
494 tempA = tempA->next;
495 }
496 /* Any SSRC FID group? */
497 tempA = m->attributes;
498 while(tempA) {
499 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
500 if(a->name && a->value) {
501 if(!strcasecmp(a->name, "ssrc-group") && strstr(a->value, "FID")) {
502 int res = janus_sdp_parse_ssrc_group(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
503 if(res != 0) {
504 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC FID group attribute... (%d)\n", handle->handle_id, res);
505 }
506 }
507 }
508 tempA = tempA->next;
509 }
510 /* Any SSRC in general? */
511 tempA = m->attributes;
512 while(tempA) {
513 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
514 if(a->name && a->value) {
515 if(!strcasecmp(a->name, "ssrc")) {
516 int res = janus_sdp_parse_ssrc(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
517 if(res != 0) {
518 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC attribute... (%d)\n", handle->handle_id, res);
519 }
520 }
521 }
522 tempA = tempA->next;
523 }
524 /* Now look for candidates and other info */
525 tempA = m->attributes;
526 while(tempA) {
527 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
528 if(a->name) {
529 if(!strcasecmp(a->name, "candidate")) {
530 if(m->type == JANUS_SDP_AUDIO && mlines > 1) {
531 JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is an audio candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
532 } else if(m->type == JANUS_SDP_VIDEO && mlines > 1) {
533 JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a video candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
534 #ifdef HAVE_SCTP
535 } else if(m->type == JANUS_SDP_APPLICATION && mlines > 1) {
536 JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a SCTP candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
537 #endif
538 } else {
539 int res = janus_sdp_parse_candidate(stream, (const char *)a->value, 0);
540 if(res != 0) {
541 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
542 }
543 }
544 } else if(!strcasecmp(a->name, "rtcp-fb")) {
545 if(a->value && strstr(a->value, "nack") && stream->component) {
546 if(m->type == JANUS_SDP_AUDIO) {
547 /* Enable NACKs for audio */
548 stream->component->do_audio_nacks = TRUE;
549 } else if(m->type == JANUS_SDP_VIDEO) {
550 /* Enable NACKs for video */
551 stream->component->do_video_nacks = TRUE;
552 }
553 }
554 } else if(!strcasecmp(a->name, "fmtp")) {
555 if(a->value && strstr(a->value, "apt=")) {
556 /* RFC4588 rtx payload type mapping */
557 int ptype = -1, rtx_ptype = -1;
558 if(sscanf(a->value, "%d apt=%d", &rtx_ptype, &ptype) != 2) {
559 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse fmtp/apt attribute...\n", handle->handle_id);
560 } else {
561 rtx = TRUE;
562 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
563 if(stream->rtx_payload_types == NULL)
564 stream->rtx_payload_types = g_hash_table_new(NULL, NULL);
565 g_hash_table_insert(stream->rtx_payload_types, GINT_TO_POINTER(ptype), GINT_TO_POINTER(rtx_ptype));
566 }
567 }
568 } else if(!strcasecmp(a->name, "rtpmap")) {
569 if(a->value) {
570 int ptype = atoi(a->value);
571 if(ptype > -1) {
572 char *cr = strchr(a->value, '/');
573 if(cr != NULL) {
574 cr++;
575 uint32_t clock_rate = 0;
576 if(janus_string_to_uint32(cr, &clock_rate) == 0) {
577 if(stream->clock_rates == NULL)
578 stream->clock_rates = g_hash_table_new(NULL, NULL);
579 g_hash_table_insert(stream->clock_rates, GINT_TO_POINTER(ptype), GUINT_TO_POINTER(clock_rate));
580 }
581 }
582 }
583 }
584 }
585 #ifdef HAVE_SCTP
586 else if(!strcasecmp(a->name, "sctpmap")) {
587 /* We don't really care */
588 JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->value);
589 }
590 #endif
591 }
592 tempA = tempA->next;
593 }
594 /* Any change in SSRCs we should be aware of? */
595 if(m->type == JANUS_SDP_AUDIO) {
596 if(stream->audio_ssrc_peer_new > 0) {
597 if(stream->audio_ssrc_peer > 0 && stream->audio_ssrc_peer != stream->audio_ssrc_peer_new) {
598 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Audio SSRC changed: %"SCNu32" --> %"SCNu32"\n",
599 handle->handle_id, stream->audio_ssrc_peer, stream->audio_ssrc_peer_new);
600 /* FIXME Reset the RTCP context */
601 janus_ice_component *component = stream->component;
602 janus_mutex_lock(&component->mutex);
603 if(stream->audio_rtcp_ctx) {
604 memset(stream->audio_rtcp_ctx, 0, sizeof(*stream->audio_rtcp_ctx));
605 stream->audio_rtcp_ctx->tb = 48000; /* May change later */
606 }
607 if(component->last_seqs_audio)
608 janus_seq_list_free(&component->last_seqs_audio);
609 janus_mutex_unlock(&component->mutex);
610 }
611 stream->audio_ssrc_peer = stream->audio_ssrc_peer_new;
612 stream->audio_ssrc_peer_new = 0;
613 }
614 } else if(m->type == JANUS_SDP_VIDEO) {
615 int vindex = 0;
616 for(vindex=0; vindex<3; vindex++) {
617 if(stream->video_ssrc_peer_new[vindex] > 0) {
618 if(stream->video_ssrc_peer[vindex] > 0 && stream->video_ssrc_peer[vindex] != stream->video_ssrc_peer_new[vindex]) {
619 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Video SSRC (#%d) changed: %"SCNu32" --> %"SCNu32"\n",
620 handle->handle_id, vindex, stream->video_ssrc_peer[vindex], stream->video_ssrc_peer_new[vindex]);
621 /* FIXME Reset the RTCP context */
622 janus_ice_component *component = stream->component;
623 if(component != NULL) {
624 janus_mutex_lock(&component->mutex);
625 if(stream->video_rtcp_ctx[vindex]) {
626 memset(stream->video_rtcp_ctx[vindex], 0, sizeof(*stream->video_rtcp_ctx[vindex]));
627 stream->video_rtcp_ctx[vindex]->tb = 90000;
628 }
629 if(component->last_seqs_video[vindex])
630 janus_seq_list_free(&component->last_seqs_video[vindex]);
631 janus_mutex_unlock(&component->mutex);
632 }
633 }
634 stream->video_ssrc_peer[vindex] = stream->video_ssrc_peer_new[vindex];
635 stream->video_ssrc_peer_new[vindex] = 0;
636 }
637 /* Do the same with the related rtx SSRC, if any */
638 if(stream->video_ssrc_peer_rtx_new[vindex] > 0) {
639 if(stream->video_ssrc_peer_rtx[vindex] > 0 && stream->video_ssrc_peer_rtx[vindex] != stream->video_ssrc_peer_rtx_new[vindex]) {
640 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Video SSRC (#%d rtx) changed: %"SCNu32" --> %"SCNu32"\n",
641 handle->handle_id, vindex, stream->video_ssrc_peer_rtx[vindex], stream->video_ssrc_peer_rtx_new[vindex]);
642 }
643 stream->video_ssrc_peer_rtx[vindex] = stream->video_ssrc_peer_rtx_new[vindex];
644 stream->video_ssrc_peer_rtx_new[vindex] = 0;
645 if(stream->video_ssrc_rtx == 0)
646 stream->video_ssrc_rtx = janus_random_uint32(); /* FIXME Should we look for conflicts? */
647 }
648 }
649 if((stream->video_ssrc_peer[1] || stream->rid[1] != NULL) && stream->video_rtcp_ctx[1] == NULL) {
650 stream->video_rtcp_ctx[1] = g_malloc0(sizeof(rtcp_context));
651 stream->video_rtcp_ctx[1]->tb = 90000;
652 }
653 if((stream->video_ssrc_peer[2] || stream->rid[rids_hml ? 2 : 0] != NULL) && stream->video_rtcp_ctx[2] == NULL) {
654 stream->video_rtcp_ctx[2] = g_malloc0(sizeof(rtcp_context));
655 stream->video_rtcp_ctx[2]->tb = 90000;
656 }
657 }
658 temp = temp->next;
659 }
660 /* Disable RFC4588 if the peer didn't negotiate it */
661 if(!rtx) {
662 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
663 stream->video_ssrc_rtx = 0;
664 }
665 /* Cleanup */
666 g_free(ruser);
667 g_free(rpass);
668 g_free(rhashing);
669 g_free(rfingerprint);
670
671 return 0; /* FIXME Handle errors better */
672 }
673
674 typedef struct janus_sdp_mdns_candidate {
675 janus_ice_handle *handle;
676 char *candidate, *local;
677 GCancellable *cancellable;
678 } janus_sdp_mdns_candidate;
janus_sdp_mdns_resolved(GObject * source_object,GAsyncResult * res,gpointer user_data)679 static void janus_sdp_mdns_resolved(GObject *source_object, GAsyncResult *res, gpointer user_data) {
680 /* This callback is invoked when the address is resolved */
681 janus_sdp_mdns_candidate *mc = (janus_sdp_mdns_candidate *)user_data;
682 GResolver *resolver = g_resolver_get_default();
683 GError *error = NULL;
684 GList *list = g_resolver_lookup_by_name_finish(resolver, res, &error);
685 if(mc == NULL) {
686 g_resolver_free_addresses(list);
687 g_object_unref(resolver);
688 return;
689 }
690 char *resolved = NULL;
691 if(error != NULL || list == NULL || list->data == NULL) {
692 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Error resolving mDNS address (%s): %s\n",
693 mc->handle->handle_id, mc->local, error ? error->message : "no results");
694 } else {
695 resolved = g_inet_address_to_string((GInetAddress *)list->data);
696 JANUS_LOG(LOG_VERB, "[%"SCNu64"] mDNS address (%s) resolved: %s\n",
697 mc->handle->handle_id, mc->local, resolved);
698 }
699 g_resolver_free_addresses(list);
700 g_object_unref(resolver);
701 if(resolved != NULL && mc->handle->stream && mc->handle->app_handle &&
702 !g_atomic_int_get(&mc->handle->app_handle->stopped) &&
703 !g_atomic_int_get(&mc->handle->destroyed)) {
704 /* Replace the .local address with the resolved one in the candidate string */
705 mc->candidate = janus_string_replace(mc->candidate, mc->local, resolved);
706 /* Parse the candidate again */
707 janus_mutex_lock(&mc->handle->mutex);
708 (void)janus_sdp_parse_candidate(mc->handle->stream, mc->candidate, 1);
709 janus_mutex_unlock(&mc->handle->mutex);
710 }
711 g_free(resolved);
712 /* Get rid of the helper struct */
713 janus_refcount_decrease(&mc->handle->ref);
714 g_free(mc->candidate);
715 g_free(mc->local);
716 g_free(mc);
717 }
718
janus_sdp_parse_candidate(void * ice_stream,const char * candidate,int trickle)719 int janus_sdp_parse_candidate(void *ice_stream, const char *candidate, int trickle) {
720 if(ice_stream == NULL || candidate == NULL)
721 return -1;
722 janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
723 janus_ice_handle *handle = stream->handle;
724 if(handle == NULL)
725 return -2;
726 janus_ice_component *component = NULL;
727 if(strlen(candidate) == 0 || strstr(candidate, "end-of-candidates")) {
728 /* FIXME Should we do something with this? */
729 JANUS_LOG(LOG_VERB, "[%"SCNu64"] end-of-candidates received\n", handle->handle_id);
730 return 0;
731 }
732 if(strstr(candidate, "candidate:") == candidate) {
733 /* Skipping the 'candidate:' prefix Firefox puts in trickle candidates */
734 candidate += strlen("candidate:");
735 }
736 char rfoundation[33], rtransport[4], rip[50], rtype[6], rrelip[40];
737 guint32 rcomponent, rpriority, rport, rrelport;
738 int res = sscanf(candidate, "%32s %30u %3s %30u %49s %30u typ %5s %*s %39s %*s %30u",
739 rfoundation, &rcomponent, rtransport, &rpriority,
740 rip, &rport, rtype, rrelip, &rrelport);
741 if(res < 7) {
742 /* Failed to parse this address, can it be IPv6? */
743 if(!janus_ice_is_ipv6_enabled()) {
744 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Received IPv6 candidate, but IPv6 support is disabled...\n", handle->handle_id);
745 return res;
746 }
747 }
748 if(res >= 7) {
749 if(strstr(rip, ".local")) {
750 /* The IP is actually an mDNS address, try to resolve it
751 * https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-04 */
752 if(!janus_ice_is_mdns_enabled()) {
753 /* ...unless mDNS resolution is disabled, in which case ignore this candidate */
754 JANUS_LOG(LOG_VERB, "[%"SCNu64"] mDNS candidate ignored\n", handle->handle_id);
755 return 0;
756 }
757 /* We'll resolve this address asynchronously, in order not to keep this thread busy */
758 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Resolving mDNS address (%s) asynchronously\n",
759 handle->handle_id, rip);
760 janus_sdp_mdns_candidate *mc = g_malloc(sizeof(janus_sdp_mdns_candidate));
761 janus_refcount_increase(&handle->ref);
762 mc->handle = handle;
763 mc->candidate = g_strdup(candidate);
764 mc->local = g_strdup(rip);
765 mc->cancellable = NULL;
766 GResolver *resolver = g_resolver_get_default();
767 g_resolver_lookup_by_name_async(resolver, rip, NULL,
768 (GAsyncReadyCallback)janus_sdp_mdns_resolved, mc);
769 g_object_unref(resolver);
770 return 0;
771 }
772 /* Add remote candidate */
773 component = stream->component;
774 if(component == NULL || rcomponent > 1) {
775 JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
776 } else {
777 //~ if(trickle) {
778 //~ if(component->dtls != NULL) {
779 //~ /* This component is already ready, ignore this further candidate */
780 //~ JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Ignoring this candidate, the component is already ready\n", handle->handle_id);
781 //~ return 0;
782 //~ }
783 //~ }
784 component->component_id = rcomponent;
785 component->stream_id = stream->stream_id;
786 NiceCandidate *c = NULL;
787 if(!strcasecmp(rtype, "host")) {
788 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Adding remote candidate component:%d stream:%d type:host %s:%d\n",
789 handle->handle_id, rcomponent, stream->stream_id, rip, rport);
790 /* Unless this is libnice >= 0.1.8, we only support UDP... */
791 if(!strcasecmp(rtransport, "udp")) {
792 c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
793 #ifdef HAVE_LIBNICE_TCP
794 } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
795 c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
796 #endif
797 } else {
798 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
799 }
800 } else if(!strcasecmp(rtype, "srflx")) {
801 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Adding remote candidate component:%d stream:%d type:srflx %s:%d --> %s:%d \n",
802 handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
803 /* Unless this is libnice >= 0.1.8, we only support UDP... */
804 if(!strcasecmp(rtransport, "udp")) {
805 c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
806 #ifdef HAVE_LIBNICE_TCP
807 } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
808 c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
809 #endif
810 } else {
811 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
812 }
813 } else if(!strcasecmp(rtype, "prflx")) {
814 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Adding remote candidate component:%d stream:%d type:prflx %s:%d --> %s:%d\n",
815 handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
816 /* Unless this is libnice >= 0.1.8, we only support UDP... */
817 if(!strcasecmp(rtransport, "udp")) {
818 c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
819 #ifdef HAVE_LIBNICE_TCP
820 } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
821 c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
822 #endif
823 } else {
824 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
825 }
826 } else if(!strcasecmp(rtype, "relay")) {
827 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Adding remote candidate component:%d stream:%d type:relay %s:%d --> %s:%d\n",
828 handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
829 /* We only support UDP/TCP/TLS... */
830 if(strcasecmp(rtransport, "udp") && strcasecmp(rtransport, "tcp") && strcasecmp(rtransport, "tls")) {
831 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
832 } else {
833 c = nice_candidate_new(NICE_CANDIDATE_TYPE_RELAYED);
834 }
835 } else {
836 /* FIXME What now? */
837 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Unknown remote candidate type:%s for component:%d stream:%d!\n",
838 handle->handle_id, rtype, rcomponent, stream->stream_id);
839 }
840 if(c != NULL) {
841 c->component_id = rcomponent;
842 c->stream_id = stream->stream_id;
843 #ifndef HAVE_LIBNICE_TCP
844 c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
845 #else
846 if(!strcasecmp(rtransport, "udp")) {
847 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Transport: UDP\n", handle->handle_id);
848 c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
849 } else {
850 /* Check the type (https://tools.ietf.org/html/rfc6544#section-4.5) */
851 const char *type = NULL;
852 int ctype = 0;
853 if(strstr(candidate, "tcptype active")) {
854 type = "active";
855 ctype = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
856 } else if(strstr(candidate, "tcptype passive")) {
857 type = "passive";
858 ctype = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
859 } else if(strstr(candidate, "tcptype so")) {
860 type = "so";
861 ctype = NICE_CANDIDATE_TRANSPORT_TCP_SO;
862 } else {
863 /* TODO: We should actually stop here... */
864 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Missing tcptype info for the TCP candidate!\n", handle->handle_id);
865 }
866 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Transport: TCP (%s)\n", handle->handle_id, type);
867 c->transport = ctype;
868 }
869 #endif
870 g_strlcpy(c->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION);
871 c->priority = rpriority;
872 gboolean added = nice_address_set_from_string(&c->addr, rip);
873 if(!added) {
874 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Invalid address '%s', skipping %s candidate (%s)\n",
875 handle->handle_id, rip, rtype, candidate);
876 nice_candidate_free(c);
877 return 0;
878 }
879 nice_address_set_port(&c->addr, rport);
880 if(c->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || c->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) {
881 added = nice_address_set_from_string(&c->base_addr, rrelip);
882 if(added)
883 nice_address_set_port(&c->base_addr, rrelport);
884 } else if(c->type == NICE_CANDIDATE_TYPE_RELAYED) {
885 /* FIXME Do we really need the base address for TURN? */
886 added = nice_address_set_from_string(&c->base_addr, rrelip);
887 if(added)
888 nice_address_set_port(&c->base_addr, rrelport);
889 }
890 if(!added) {
891 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Invalid base address '%s', skipping %s candidate (%s)\n",
892 handle->handle_id, rrelip, rtype, candidate);
893 nice_candidate_free(c);
894 return 0;
895 }
896 component->candidates = g_slist_append(component->candidates, c);
897 JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Candidate added to the list! (%u elements for %d/%d)\n", handle->handle_id,
898 g_slist_length(component->candidates), stream->stream_id, component->component_id);
899 /* Save for the summary, in case we need it */
900 component->remote_candidates = g_slist_append(component->remote_candidates, g_strdup(candidate));
901 /* Notify event handlers */
902 if(janus_events_is_enabled()) {
903 janus_session *session = (janus_session *)handle->session;
904 json_t *info = json_object();
905 json_object_set_new(info, "remote-candidate", json_string(candidate));
906 json_object_set_new(info, "stream_id", json_integer(stream->stream_id));
907 json_object_set_new(info, "component_id", json_integer(component->component_id));
908 janus_events_notify_handlers(JANUS_EVENT_TYPE_WEBRTC, JANUS_EVENT_SUBTYPE_WEBRTC_RCAND,
909 session->session_id, handle->handle_id, handle->opaque_id, info);
910 }
911 /* See if we need to process this */
912 if(trickle) {
913 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)) {
914 /* This is a trickle candidate and ICE has started, we should process it right away */
915 if(!component->process_started) {
916 /* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
917 JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE already started for this component, setting candidates we have up to now\n", handle->handle_id);
918 janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
919 } else {
920 /* Queue the candidate, we'll process it in the loop */
921 janus_ice_add_remote_candidate(handle, c);
922 }
923 } else {
924 /* ICE hasn't started yet: to make sure we're not stuck, also check if we stopped processing the SDP */
925 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)) {
926 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
927 /* This is a trickle candidate and ICE has started, we should process it right away */
928 if(!component->process_started) {
929 /* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
930 JANUS_LOG(LOG_VERB, "[%"SCNu64"] SDP processed but ICE not started yet for this component, setting candidates we have up to now\n", handle->handle_id);
931 janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
932 } else {
933 /* Queue the candidate, we'll process it in the loop */
934 janus_ice_add_remote_candidate(handle, c);
935 }
936 } else {
937 /* Still processing the offer/answer: queue the trickle candidate for now, we'll process it later */
938 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Queueing trickle candidate, status is not START yet\n", handle->handle_id);
939 }
940 }
941 }
942 }
943 }
944 } else {
945 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate (res=%d)...\n", handle->handle_id, res);
946 return res;
947 }
948 return 0;
949 }
950
janus_sdp_parse_ssrc_group(void * ice_stream,const char * group_attr,int video)951 int janus_sdp_parse_ssrc_group(void *ice_stream, const char *group_attr, int video) {
952 if(ice_stream == NULL || group_attr == NULL)
953 return -1;
954 janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
955 janus_ice_handle *handle = stream->handle;
956 if(handle == NULL)
957 return -2;
958 if(!video) /* We only do rtx for video, return */
959 return 0;
960 if(stream->rid[0] != NULL) {
961 /* Simulcasting is rid-based, don't parse SSRCs for now */
962 return 0;
963 }
964 gboolean fid = strstr(group_attr, "FID") != NULL;
965 gboolean sim = strstr(group_attr, "SIM") != NULL;
966 guint64 ssrc = 0;
967 guint32 first_ssrc = 0;
968 gchar **list = g_strsplit(group_attr, " ", -1);
969 gchar *index = list[0];
970 if(index != NULL) {
971 int i=0;
972 while(index != NULL) {
973 if(i > 0 && strlen(index) > 0) {
974 ssrc = g_ascii_strtoull(index, NULL, 0);
975 switch(i) {
976 case 1:
977 first_ssrc = ssrc;
978 if(stream->video_ssrc_peer_new[0] == ssrc || stream->video_ssrc_peer_new[1] == ssrc
979 || stream->video_ssrc_peer_new[2] == ssrc) {
980 JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Already parsed this SSRC: %"SCNu64" (%s group)\n",
981 handle->handle_id, ssrc, (fid ? "FID" : (sim ? "SIM" : "??")));
982 } else {
983 if(stream->video_ssrc_peer_new[0] == 0) {
984 stream->video_ssrc_peer_new[0] = ssrc;
985 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[0]);
986 } else {
987 /* We already have a video SSRC: check if rid is involved, and we'll keep track of this for simulcasting */
988 if(stream->rid[0]) {
989 if(stream->video_ssrc_peer_new[1] == 0) {
990 stream->video_ssrc_peer_new[1] = ssrc;
991 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[1]);
992 } else if(stream->video_ssrc_peer_new[2] == 0) {
993 stream->video_ssrc_peer_new[2] = ssrc;
994 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[2]);
995 } else {
996 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with video SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
997 }
998 }
999 }
1000 }
1001 break;
1002 case 2:
1003 if(fid) {
1004 if(stream->video_ssrc_peer_new[0] == first_ssrc && stream->video_ssrc_peer_rtx_new[0] == 0) {
1005 stream->video_ssrc_peer_rtx_new[0] = ssrc;
1006 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[0]);
1007 } else if(stream->video_ssrc_peer_new[1] == first_ssrc && stream->video_ssrc_peer_rtx_new[1] == 0) {
1008 stream->video_ssrc_peer_rtx_new[1] = ssrc;
1009 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1 rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[1]);
1010 } else if(stream->video_ssrc_peer_new[2] == first_ssrc && stream->video_ssrc_peer_rtx_new[2] == 0) {
1011 stream->video_ssrc_peer_rtx_new[2] = ssrc;
1012 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2 rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[2]);
1013 } else {
1014 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with rtx SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1015 }
1016 } else if(sim) {
1017 stream->video_ssrc_peer_new[1] = ssrc;
1018 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[1]);
1019 } else {
1020 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1021 }
1022 break;
1023 case 3:
1024 if(fid) {
1025 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Found one too many retransmission SSRC (rtx): %"SCNu64"\n", handle->handle_id, ssrc);
1026 } else if(sim) {
1027 stream->video_ssrc_peer_new[2] = ssrc;
1028 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[2]);
1029 } else {
1030 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1031 }
1032 break;
1033 default:
1034 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with video SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1035 break;
1036 }
1037 }
1038 i++;
1039 index = list[i];
1040 }
1041 }
1042 g_clear_pointer(&list, g_strfreev);
1043 return 0;
1044 }
1045
janus_sdp_parse_ssrc(void * ice_stream,const char * ssrc_attr,int video)1046 int janus_sdp_parse_ssrc(void *ice_stream, const char *ssrc_attr, int video) {
1047 if(ice_stream == NULL || ssrc_attr == NULL)
1048 return -1;
1049 janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
1050 janus_ice_handle *handle = stream->handle;
1051 if(handle == NULL)
1052 return -2;
1053 guint64 ssrc = g_ascii_strtoull(ssrc_attr, NULL, 0);
1054 if(ssrc == 0 || ssrc > G_MAXUINT32)
1055 return -3;
1056 if(video) {
1057 if(stream->rid[0] != NULL) {
1058 /* Simulcasting is rid-based, only keep track of a single SSRC for fallback */
1059 if(stream->video_ssrc_peer_temp == 0) {
1060 stream->video_ssrc_peer_temp = ssrc;
1061 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Peer video fallback SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_temp);
1062 }
1063 return 0;
1064 }
1065 if(stream->video_ssrc_peer_new[0] == 0) {
1066 stream->video_ssrc_peer_new[0] = ssrc;
1067 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[0]);
1068 }
1069 } else {
1070 if(stream->audio_ssrc_peer_new == 0) {
1071 stream->audio_ssrc_peer_new = ssrc;
1072 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer audio SSRC: %"SCNu32"\n", handle->handle_id, stream->audio_ssrc_peer_new);
1073 }
1074 }
1075 return 0;
1076 }
1077
janus_sdp_anonymize(janus_sdp * anon)1078 int janus_sdp_anonymize(janus_sdp *anon) {
1079 if(anon == NULL)
1080 return -1;
1081 int audio = 0, video = 0, data = 0;
1082 /* o= */
1083 if(anon->o_addr != NULL) {
1084 g_free(anon->o_addr);
1085 anon->o_ipv4 = TRUE;
1086 anon->o_addr = g_strdup("1.1.1.1");
1087 }
1088 /* a= */
1089 GList *temp = anon->attributes;
1090 while(temp) {
1091 janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
1092 /* These are attributes we handle ourselves, the plugins don't need them */
1093 if(!strcasecmp(a->name, "ice-ufrag")
1094 || !strcasecmp(a->name, "ice-pwd")
1095 || !strcasecmp(a->name, "ice-options")
1096 || !strcasecmp(a->name, "fingerprint")
1097 || !strcasecmp(a->name, "group")
1098 || !strcasecmp(a->name, "msid-semantic")
1099 || !strcasecmp(a->name, "extmap-allow-mixed")
1100 || !strcasecmp(a->name, "rtcp-rsize")) {
1101 anon->attributes = g_list_remove(anon->attributes, a);
1102 temp = anon->attributes;
1103 janus_sdp_attribute_destroy(a);
1104 continue;
1105 }
1106 temp = temp->next;
1107 continue;
1108 }
1109 /* m= */
1110 temp = anon->m_lines;
1111 while(temp) {
1112 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1113 if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
1114 audio++;
1115 m->port = audio == 1 ? 9 : 0;
1116 } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
1117 video++;
1118 m->port = video == 1 ? 9 : 0;
1119 } else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
1120 if(m->proto != NULL && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP"))) {
1121 data++;
1122 m->port = data == 1 ? 9 : 0;
1123 } else {
1124 m->port = 0;
1125 }
1126 } else {
1127 m->port = 0;
1128 }
1129 /* c= */
1130 if(m->c_addr != NULL) {
1131 g_free(m->c_addr);
1132 m->c_ipv4 = TRUE;
1133 m->c_addr = g_strdup("1.1.1.1");
1134 }
1135 /* a= */
1136 GList *tempA = m->attributes;
1137 while(tempA) {
1138 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1139 if(!a->name) {
1140 tempA = tempA->next;
1141 continue;
1142 }
1143 /* These are attributes we handle ourselves, the plugins don't need them */
1144 if(!strcasecmp(a->name, "ice-ufrag")
1145 || !strcasecmp(a->name, "ice-pwd")
1146 || !strcasecmp(a->name, "ice-options")
1147 || !strcasecmp(a->name, "crypto")
1148 || !strcasecmp(a->name, "fingerprint")
1149 || !strcasecmp(a->name, "setup")
1150 || !strcasecmp(a->name, "connection")
1151 || !strcasecmp(a->name, "group")
1152 || !strcasecmp(a->name, "mid")
1153 || !strcasecmp(a->name, "msid")
1154 || !strcasecmp(a->name, "msid-semantic")
1155 || !strcasecmp(a->name, "rid")
1156 || !strcasecmp(a->name, "simulcast")
1157 || !strcasecmp(a->name, "rtcp")
1158 || !strcasecmp(a->name, "rtcp-mux")
1159 || !strcasecmp(a->name, "rtcp-rsize")
1160 || !strcasecmp(a->name, "candidate")
1161 || !strcasecmp(a->name, "end-of-candidates")
1162 || !strcasecmp(a->name, "ssrc")
1163 || !strcasecmp(a->name, "ssrc-group")
1164 || !strcasecmp(a->name, "sctpmap")
1165 || !strcasecmp(a->name, "sctp-port")
1166 || !strcasecmp(a->name, "max-message-size")) {
1167 m->attributes = g_list_remove(m->attributes, a);
1168 tempA = m->attributes;
1169 janus_sdp_attribute_destroy(a);
1170 continue;
1171 }
1172 tempA = tempA->next;
1173 }
1174 /* We don't support encrypted RTP extensions yet, so get rid of them */
1175 tempA = m->attributes;
1176 while(tempA) {
1177 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1178 if(a->value && strstr(a->value, JANUS_RTP_EXTMAP_ENCRYPTED)) {
1179 m->attributes = g_list_remove(m->attributes, a);
1180 tempA = m->attributes;
1181 janus_sdp_attribute_destroy(a);
1182 continue;
1183 }
1184 tempA = tempA->next;
1185 }
1186 /* Also remove attributes/formats we know we don't support (or don't want to support) now */
1187 tempA = m->attributes;
1188 GList *purged_ptypes = NULL;
1189 while(tempA) {
1190 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1191 if(a->value && (strstr(a->value, "red/90000") || strstr(a->value, "ulpfec/90000") ||
1192 strstr(a->value, "flexfec-03/90000") || strstr(a->value, "rtx/90000"))) {
1193 int ptype = atoi(a->value);
1194 if(ptype < 0) {
1195 JANUS_LOG(LOG_ERR, "Invalid payload type (%d)\n", ptype);
1196 } else {
1197 JANUS_LOG(LOG_VERB, "Will remove payload type %d (%s)\n", ptype, a->value);
1198 purged_ptypes = g_list_append(purged_ptypes, GINT_TO_POINTER(ptype));
1199 }
1200 }
1201 tempA = tempA->next;
1202 }
1203 if(purged_ptypes) {
1204 tempA = purged_ptypes;
1205 while(tempA) {
1206 int ptype = GPOINTER_TO_INT(tempA->data);
1207 janus_sdp_remove_payload_type(anon, ptype);
1208 tempA = tempA->next;
1209 }
1210 g_list_free(purged_ptypes);
1211 purged_ptypes = NULL;
1212 }
1213 temp = temp->next;
1214 }
1215
1216 JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1217 JANUS_LOG(LOG_VERB, " >> Anonymized\n");
1218 JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1219
1220 return 0;
1221 }
1222
janus_sdp_merge(void * ice_handle,janus_sdp * anon,gboolean offer)1223 char *janus_sdp_merge(void *ice_handle, janus_sdp *anon, gboolean offer) {
1224 if(ice_handle == NULL || anon == NULL)
1225 return NULL;
1226 janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
1227 janus_ice_stream *stream = handle->stream;
1228 if(stream == NULL)
1229 return NULL;
1230 char *rtp_profile = handle->rtp_profile ? handle->rtp_profile : (char *)"UDP/TLS/RTP/SAVPF";
1231 if(!janus_is_webrtc_encryption_enabled())
1232 rtp_profile = (char *)"RTP/AVPF";
1233 gboolean ipv4 = !strstr(janus_get_public_ip(0), ":");
1234 /* Origin o= */
1235 gint64 sessid = janus_get_real_time();
1236 if(anon->o_name == NULL)
1237 anon->o_name = g_strdup("-");
1238 if(anon->o_sessid == 0 || anon->o_version == 0) {
1239 anon->o_sessid = sessid;
1240 anon->o_version = 1;
1241 }
1242 anon->o_ipv4 = ipv4;
1243 g_free(anon->o_addr);
1244 anon->o_addr = g_strdup(janus_get_public_ip(0));
1245 /* Session name s= */
1246 if(anon->s_name == NULL)
1247 anon->s_name = g_strdup("Meetecho Janus");
1248 /* Chrome doesn't like global c= lines, remove it */
1249 g_free(anon->c_addr);
1250 anon->c_addr = NULL;
1251 /* bundle: add new global attribute */
1252 char buffer[2048], buffer_part[512];
1253 buffer[0] = '\0';
1254 buffer_part[0] = '\0';
1255 g_snprintf(buffer, sizeof(buffer), "BUNDLE");
1256 /* Iterate on available media */
1257 int audio = 0;
1258 int video = 0;
1259 #ifdef HAVE_SCTP
1260 int data = 0;
1261 #endif
1262 GList *temp = anon->m_lines;
1263 while(temp) {
1264 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1265 if(m->type == JANUS_SDP_AUDIO) {
1266 audio++;
1267 if(audio == 1 && m->port > 0) {
1268 g_snprintf(buffer_part, sizeof(buffer_part),
1269 " %s", handle->audio_mid ? handle->audio_mid : "audio");
1270 janus_strlcat(buffer, buffer_part, sizeof(buffer));
1271 }
1272 } else if(m->type == JANUS_SDP_VIDEO) {
1273 video++;
1274 if(video == 1 && m->port > 0) {
1275 g_snprintf(buffer_part, sizeof(buffer_part),
1276 " %s", handle->video_mid ? handle->video_mid : "video");
1277 janus_strlcat(buffer, buffer_part, sizeof(buffer));
1278 }
1279 #ifdef HAVE_SCTP
1280 } else if(m->type == JANUS_SDP_APPLICATION) {
1281 if(m->proto && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP")))
1282 data++;
1283 if(data == 1 && m->port > 0) {
1284 g_snprintf(buffer_part, sizeof(buffer_part),
1285 " %s", handle->data_mid ? handle->data_mid : "data");
1286 janus_strlcat(buffer, buffer_part, sizeof(buffer));
1287 }
1288 #endif
1289 }
1290 temp = temp->next;
1291 }
1292 /* Global attributes: start with group */
1293 GList *first = anon->attributes;
1294 janus_sdp_attribute *a = janus_sdp_attribute_create("group", "%s", buffer);
1295 anon->attributes = g_list_insert_before(anon->attributes, first, a);
1296 /* msid-semantic: add new global attribute */
1297 a = janus_sdp_attribute_create("msid-semantic", " WMS janus");
1298 anon->attributes = g_list_insert_before(anon->attributes, first, a);
1299 /* ICE Full or Lite? */
1300 if(janus_ice_is_ice_lite_enabled()) {
1301 /* Janus is acting in ICE Lite mode, advertize this */
1302 a = janus_sdp_attribute_create("ice-lite", NULL);
1303 anon->attributes = g_list_insert_before(anon->attributes, first, a);
1304 }
1305 /* Media lines now */
1306 audio = 0;
1307 video = 0;
1308 #ifdef HAVE_SCTP
1309 data = 0;
1310 #endif
1311 temp = anon->m_lines;
1312 while(temp) {
1313 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1314 first = m->attributes;
1315 /* Overwrite RTP profile for audio and video */
1316 if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
1317 g_free(m->proto);
1318 m->proto = g_strdup(rtp_profile);
1319 }
1320 /* Media connection c= */
1321 g_free(m->c_addr);
1322 m->c_ipv4 = ipv4;
1323 m->c_addr = g_strdup(janus_get_public_ip(0));
1324 /* Check if we need to refuse the media or not */
1325 if(m->type == JANUS_SDP_AUDIO) {
1326 audio++;
1327 /* Audio */
1328 if(audio > 1) {
1329 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have one already)\n", handle->handle_id);
1330 m->port = 0;
1331 }
1332 if(m->port == 0) {
1333 m->direction = JANUS_SDP_INACTIVE;
1334 stream->audio_ssrc = 0;
1335 }
1336 if(audio == 1) {
1337 gboolean receiving = (stream->audio_recv == TRUE);
1338 switch(m->direction) {
1339 case JANUS_SDP_INACTIVE:
1340 stream->audio_send = FALSE;
1341 stream->audio_recv = FALSE;
1342 break;
1343 case JANUS_SDP_SENDONLY:
1344 stream->audio_send = TRUE;
1345 stream->audio_recv = FALSE;
1346 break;
1347 case JANUS_SDP_RECVONLY:
1348 stream->audio_send = FALSE;
1349 stream->audio_recv = TRUE;
1350 break;
1351 case JANUS_SDP_SENDRECV:
1352 case JANUS_SDP_DEFAULT:
1353 default:
1354 stream->audio_send = TRUE;
1355 stream->audio_recv = TRUE;
1356 break;
1357 }
1358 if(receiving != stream->audio_recv)
1359 janus_ice_notify_media_stopped(handle);
1360 }
1361 } else if(m->type == JANUS_SDP_VIDEO) {
1362 video++;
1363 /* Video */
1364 if(video > 1) {
1365 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have one already)\n", handle->handle_id);
1366 m->port = 0;
1367 }
1368 if(m->port == 0) {
1369 m->direction = JANUS_SDP_INACTIVE;
1370 stream->video_ssrc = 0;
1371 }
1372 if(video == 1) {
1373 gboolean receiving = (stream->video_recv == TRUE);
1374 switch(m->direction) {
1375 case JANUS_SDP_INACTIVE:
1376 stream->video_send = FALSE;
1377 stream->video_recv = FALSE;
1378 break;
1379 case JANUS_SDP_SENDONLY:
1380 stream->video_send = TRUE;
1381 stream->video_recv = FALSE;
1382 break;
1383 case JANUS_SDP_RECVONLY:
1384 stream->video_send = FALSE;
1385 stream->video_recv = TRUE;
1386 break;
1387 case JANUS_SDP_SENDRECV:
1388 case JANUS_SDP_DEFAULT:
1389 default:
1390 stream->video_send = TRUE;
1391 stream->video_recv = TRUE;
1392 break;
1393 }
1394 if(receiving != stream->video_recv)
1395 janus_ice_notify_media_stopped(handle);
1396 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX)) {
1397 /* Add RFC4588 stuff */
1398 if(stream->rtx_payload_types && g_hash_table_size(stream->rtx_payload_types) > 0) {
1399 janus_sdp_attribute *a = NULL;
1400 GList *ptypes = g_list_copy(m->ptypes), *tempP = ptypes;
1401 while(tempP) {
1402 int ptype = GPOINTER_TO_INT(tempP->data);
1403 int rtx_ptype = GPOINTER_TO_INT(g_hash_table_lookup(stream->rtx_payload_types, GINT_TO_POINTER(ptype)));
1404 if(rtx_ptype > 0) {
1405 m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(rtx_ptype));
1406 a = janus_sdp_attribute_create("rtpmap", "%d rtx/90000", rtx_ptype);
1407 m->attributes = g_list_append(m->attributes, a);
1408 a = janus_sdp_attribute_create("fmtp", "%d apt=%d", rtx_ptype, ptype);
1409 m->attributes = g_list_append(m->attributes, a);
1410 }
1411 tempP = tempP->next;
1412 }
1413 g_list_free(ptypes);
1414 }
1415 }
1416 }
1417 #ifdef HAVE_SCTP
1418 } else if(m->type == JANUS_SDP_APPLICATION) {
1419 /* Is this SCTP for DataChannels? */
1420 if(m->port > 0 && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP"))) {
1421 /* Yep */
1422 data++;
1423 if(data > 1) {
1424 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have one already)\n", handle->handle_id);
1425 m->port = 0;
1426 m->direction = JANUS_SDP_INACTIVE;
1427 temp = temp->next;
1428 continue;
1429 }
1430 } else {
1431 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
1432 m->port = 0;
1433 m->direction = JANUS_SDP_INACTIVE;
1434 temp = temp->next;
1435 continue;
1436 }
1437 #endif
1438 } else {
1439 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
1440 m->port = 0;
1441 m->direction = JANUS_SDP_INACTIVE;
1442 temp = temp->next;
1443 continue;
1444 }
1445 /* a=mid:(audio|video|data) */
1446 if(m->type == JANUS_SDP_AUDIO && audio == 1) {
1447 a = janus_sdp_attribute_create("mid", "%s", handle->audio_mid);
1448 m->attributes = g_list_insert_before(m->attributes, first, a);
1449 } else if(m->type == JANUS_SDP_VIDEO && video == 1) {
1450 a = janus_sdp_attribute_create("mid", "%s", handle->video_mid);
1451 m->attributes = g_list_insert_before(m->attributes, first, a);
1452 #ifdef HAVE_SCTP
1453 } else if(m->type == JANUS_SDP_APPLICATION && data == 1) {
1454 if(!strcasecmp(m->proto, "UDP/DTLS/SCTP"))
1455 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
1456 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP)) {
1457 a = janus_sdp_attribute_create("sctpmap", "5000 webrtc-datachannel 16");
1458 m->attributes = g_list_insert_before(m->attributes, first, a);
1459 } else {
1460 a = janus_sdp_attribute_create("sctp-port", "5000");
1461 m->attributes = g_list_insert_before(m->attributes, first, a);
1462 }
1463 a = janus_sdp_attribute_create("mid", "%s", handle->data_mid);
1464 m->attributes = g_list_insert_before(m->attributes, first, a);
1465 #endif
1466 }
1467 if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
1468 a = janus_sdp_attribute_create("rtcp-mux", NULL);
1469 m->attributes = g_list_insert_before(m->attributes, first, a);
1470 }
1471 /* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
1472 gchar *ufrag = NULL;
1473 gchar *password = NULL;
1474 nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
1475 a = janus_sdp_attribute_create("ice-ufrag", "%s", ufrag);
1476 m->attributes = g_list_insert_before(m->attributes, first, a);
1477 a = janus_sdp_attribute_create("ice-pwd", "%s", password);
1478 m->attributes = g_list_insert_before(m->attributes, first, a);
1479 g_free(ufrag);
1480 g_free(password);
1481 a = janus_sdp_attribute_create("ice-options", "trickle");
1482 m->attributes = g_list_insert_before(m->attributes, first, a);
1483 if(janus_is_webrtc_encryption_enabled()) {
1484 a = janus_sdp_attribute_create("fingerprint", "sha-256 %s", janus_dtls_get_local_fingerprint());
1485 m->attributes = g_list_insert_before(m->attributes, first, a);
1486 a = janus_sdp_attribute_create("setup", "%s", janus_get_dtls_srtp_role(offer ? JANUS_DTLS_ROLE_ACTPASS : stream->dtls_role));
1487 m->attributes = g_list_insert_before(m->attributes, first, a);
1488 }
1489 /* Add last attributes, rtcp and ssrc (msid) */
1490 if(m->type == JANUS_SDP_VIDEO && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
1491 (m->direction == JANUS_SDP_DEFAULT || m->direction == JANUS_SDP_SENDRECV || m->direction == JANUS_SDP_SENDONLY)) {
1492 /* Add FID group to negotiate the RFC4588 stuff */
1493 a = janus_sdp_attribute_create("ssrc-group", "FID %"SCNu32" %"SCNu32, stream->video_ssrc, stream->video_ssrc_rtx);
1494 m->attributes = g_list_append(m->attributes, a);
1495 }
1496 if(m->type == JANUS_SDP_AUDIO) {
1497 a = janus_sdp_attribute_create("msid", "janus janusa0");
1498 m->attributes = g_list_append(m->attributes, a);
1499 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->audio_ssrc);
1500 m->attributes = g_list_append(m->attributes, a);
1501 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusa0", stream->audio_ssrc);
1502 m->attributes = g_list_append(m->attributes, a);
1503 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->audio_ssrc);
1504 m->attributes = g_list_append(m->attributes, a);
1505 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusa0", stream->audio_ssrc);
1506 m->attributes = g_list_append(m->attributes, a);
1507 } else if(m->type == JANUS_SDP_VIDEO) {
1508 a = janus_sdp_attribute_create("msid", "janus janusv0");
1509 m->attributes = g_list_append(m->attributes, a);
1510 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->video_ssrc);
1511 m->attributes = g_list_append(m->attributes, a);
1512 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc);
1513 m->attributes = g_list_append(m->attributes, a);
1514 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc);
1515 m->attributes = g_list_append(m->attributes, a);
1516 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc);
1517 m->attributes = g_list_append(m->attributes, a);
1518 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
1519 (m->direction == JANUS_SDP_DEFAULT || m->direction == JANUS_SDP_SENDRECV || m->direction == JANUS_SDP_SENDONLY)) {
1520 /* Add rtx SSRC group to negotiate the RFC4588 stuff */
1521 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->video_ssrc_rtx);
1522 m->attributes = g_list_append(m->attributes, a);
1523 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc_rtx);
1524 m->attributes = g_list_append(m->attributes, a);
1525 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc_rtx);
1526 m->attributes = g_list_append(m->attributes, a);
1527 a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc_rtx);
1528 m->attributes = g_list_append(m->attributes, a);
1529 }
1530 }
1531 /* FIXME If the peer is Firefox and is negotiating simulcasting, add the rid attributes */
1532 if(m->type == JANUS_SDP_VIDEO && stream->rid[0] != NULL) {
1533 char rids[50];
1534 rids[0] = '\0';
1535 int i=0, index=0;
1536 for(i=2; i>=0; i--) {
1537 index = (stream->rids_hml ? i : (2-i));
1538 if(stream->rid[index] == NULL)
1539 continue;
1540 a = janus_sdp_attribute_create("rid", "%s recv", stream->rid[index]);
1541 m->attributes = g_list_append(m->attributes, a);
1542 if(strlen(rids) == 0) {
1543 janus_strlcat(rids, stream->rid[index], sizeof(rids));
1544 } else {
1545 janus_strlcat(rids, ";", sizeof(rids));
1546 janus_strlcat(rids, stream->rid[index], sizeof(rids));
1547 }
1548 }
1549 if(stream->legacy_rid) {
1550 a = janus_sdp_attribute_create("simulcast", " recv rid=%s", rids);
1551 } else {
1552 a = janus_sdp_attribute_create("simulcast", "recv %s", rids);
1553 }
1554 m->attributes = g_list_append(m->attributes, a);
1555 }
1556 if(!janus_ice_is_full_trickle_enabled()) {
1557 /* And now the candidates (but only if we're half-trickling) */
1558 janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 1);
1559 /* Since we're half-trickling, we need to notify the peer that these are all the
1560 * candidates we have for this media stream, via an end-of-candidates attribute:
1561 * https://tools.ietf.org/html/draft-ietf-mmusic-trickle-ice-02#section-4.1 */
1562 janus_sdp_attribute *end = janus_sdp_attribute_create("end-of-candidates", NULL);
1563 m->attributes = g_list_append(m->attributes, end);
1564 }
1565 /* Next */
1566 temp = temp->next;
1567 }
1568
1569 char *sdp = janus_sdp_write(anon);
1570
1571 JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1572 JANUS_LOG(LOG_VERB, " >> Merged (%zu bytes)\n", strlen(sdp));
1573 JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1574 JANUS_LOG(LOG_VERB, "%s\n", sdp);
1575
1576 return sdp;
1577 }
1578