Statistics
| Branch: | Revision:

janus-gateway / sdp.c @ 0351dafc

History | View | Annotate | Download (39.3 KB)

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 gateway. 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 "janus.h"
20
#include "ice.h"
21
#include "dtls.h"
22
#include "sdp.h"
23
#include "utils.h"
24
#include "debug.h"
25

    
26

    
27
/* SDP Initialization */
28
int janus_sdp_init(void) {
29
        /* Nothing to do here, maybe in the future */
30
        return 0;
31
}
32

    
33
void janus_sdp_deinit(void) {
34
        /* Nothing to do here, maybe in the future */
35
}
36

    
37

    
38
/* Pre-parse SDP: is this SDP valid? how many audio/video lines? any features to take into account? */
39
janus_sdp *janus_sdp_preparse(const char *jsep_sdp, int *audio, int *video, int *data, int *bundle, int *rtcpmux, int *trickle) {
40
        if(!jsep_sdp || !audio || !video || !data || !bundle || !rtcpmux || !trickle) {
41
                JANUS_LOG(LOG_ERR, "  Can't preparse, invalid arduments\n");
42
                return NULL;
43
        }
44
        janus_sdp *parsed_sdp = janus_sdp_import(jsep_sdp);
45
        if(!parsed_sdp) {
46
                JANUS_LOG(LOG_ERR, "  Error parsing SDP?\n");
47
                /* Invalid SDP */
48
                return NULL;
49
        }
50
        /* Look for m-lines */
51
        GList *temp = parsed_sdp->m_lines;
52
        while(temp) {
53
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
54
                if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
55
                        *audio = *audio + 1;
56
                } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
57
                        *video = *video + 1;
58
                }
59
                temp = temp->next;
60
        }
61
#ifdef HAVE_SCTP
62
        *data = (strstr(jsep_sdp, "DTLS/SCTP") && !strstr(jsep_sdp, " 0 DTLS/SCTP")) ? 1 : 0;        /* FIXME This is a really hacky way of checking... */
63
#else
64
        *data = 0;
65
#endif
66
        *bundle = strstr(jsep_sdp, "a=group:BUNDLE") ? 1 : 0;        /* FIXME This is a really hacky way of checking... */
67
        *rtcpmux = strstr(jsep_sdp, "a=rtcp-mux") ? 1 : 0;        /* FIXME Should we make this check per-medium? */
68
        //~ *trickle = (strstr(jsep_sdp, "trickle") || strstr(jsep_sdp, "google-ice") || strstr(jsep_sdp, "Mozilla")) ? 1 : 0;        /* FIXME This is a really hacky way of checking... */
69
        /* FIXME We're assuming trickle is always supported, see https://github.com/meetecho/janus-gateway/issues/83 */
70
        *trickle = 1;
71

    
72
        return parsed_sdp;
73
}
74

    
75
/* Parse SDP */
76
int janus_sdp_parse(void *ice_handle, janus_sdp *remote_sdp) {
77
        if(!ice_handle || !remote_sdp)
78
                return -1;
79
        janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
80
        janus_ice_stream *stream = NULL;
81
        gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
82
        int audio = 0, video = 0;
83
#ifdef HAVE_SCTP
84
        int data = 0;
85
#endif
86
        /* Ok, let's start with global attributes */
87
        GList *temp = remote_sdp->attributes;
88
        while(temp) {
89
                janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
90
                if(a && a->name) {
91
                        if(!strcasecmp(a->name, "fingerprint")) {
92
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->value);
93
                                if(strcasestr(a->value, "sha-256 ") == a->value) {
94
                                        rhashing = g_strdup("sha-256");
95
                                        rfingerprint = g_strdup(a->value + strlen("sha-256 "));
96
                                } else if(strcasestr(a->value, "sha-1 ") == a->value) {
97
                                        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);
98
                                        rhashing = g_strdup("sha-1");
99
                                        rfingerprint = g_strdup(a->value + strlen("sha-1 "));
100
                                } else {
101
                                        /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
102
                                        JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
103
                                }
104
                        } else if(!strcasecmp(a->name, "ice-ufrag")) {
105
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->value);
106
                                ruser = g_strdup(a->value);
107
                        } else if(!strcasecmp(a->name, "ice-pwd")) {
108
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->value);
109
                                rpass = g_strdup(a->value);
110
                        }
111
                }
112
                temp = temp->next;
113
        }
114
        /* Now go on with m-line and their attributes */
115
        temp = remote_sdp->m_lines;
116
        while(temp) {
117
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
118
                if(m->type == JANUS_SDP_AUDIO) {
119
                        if(handle->rtp_profile == NULL)
120
                                handle->rtp_profile = g_strdup(m->proto);
121
                        if(m->port > 0) {
122
                                audio++;
123
                                if(audio > 1) {
124
                                        temp = temp->next;
125
                                        continue;
126
                                }
127
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
128
                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
129
                        } else {
130
                                /* Audio rejected? */
131
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
132
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
133
                        }
134
                } else if(m->type == JANUS_SDP_VIDEO) {
135
                        if(handle->rtp_profile == NULL)
136
                                handle->rtp_profile = g_strdup(m->proto);
137
                        if(m->port > 0) {
138
                                video++;
139
                                if(video > 1) {
140
                                        temp = temp->next;
141
                                        continue;
142
                                }
143
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
144
                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
145
                                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
146
                                } else {
147
                                        gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
148
                                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
149
                                }
150
                        } else {
151
                                /* Video rejected? */
152
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video rejected by peer...\n", handle->handle_id);
153
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
154
                        }
155
#ifdef HAVE_SCTP
156
                } else if(m->type == JANUS_SDP_APPLICATION) {
157
                        /* Is this SCTP for DataChannels? */
158
                        if(!strcasecmp(m->proto, "DTLS/SCTP")) {
159
                                if(m->port > 0) {
160
                                        /* Yep */
161
                                        data++;
162
                                        if(data > 1) {
163
                                                temp = temp->next;
164
                                                continue;
165
                                        }
166
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
167
                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
168
                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
169
                                        } else {
170
                                                gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
171
                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
172
                                        }
173
                                        if(stream == NULL) {
174
                                                JANUS_LOG(LOG_WARN, "No valid stream for data??\n");
175
                                                temp = temp->next;
176
                                                continue;
177
                                        }
178
                                }
179
                        } else {
180
                                /* Data channels rejected? */
181
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels rejected by peer...\n", handle->handle_id);
182
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
183
                        }
184
#endif
185
                } else {
186
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
187
                        temp = temp->next;
188
                        continue;
189
                }
190
                /* Look for mid, ICE credentials and fingerprint first: check media attributes */
191
                GList *tempA = m->attributes;
192
                while(tempA) {
193
                        janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
194
                        if(a->name) {
195
                                if(!strcasecmp(a->name, "mid")) {
196
                                        /* Found mid attribute */
197
                                        if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
198
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
199
                                                handle->audio_mid = g_strdup(a->value);
200
                                        } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
201
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
202
                                                handle->video_mid = g_strdup(a->value);
203
#ifdef HAVE_SCTP
204
                                        } else if(m->type == JANUS_SDP_APPLICATION) {
205
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->value);
206
                                                handle->data_mid = g_strdup(a->value);
207
#endif
208
                                        }
209
                                } else if(!strcasecmp(a->name, "fingerprint")) {
210
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->value);
211
                                        if(strcasestr(a->value, "sha-256 ") == a->value) {
212
                                                if(rhashing)
213
                                                        g_free(rhashing);        /* FIXME We're overwriting the global one, if any */
214
                                                rhashing = g_strdup("sha-256");
215
                                                if(rfingerprint)
216
                                                        g_free(rfingerprint);        /* FIXME We're overwriting the global one, if any */
217
                                                rfingerprint = g_strdup(a->value + strlen("sha-256 "));
218
                                        } else if(strcasestr(a->value, "sha-1 ") == a->value) {
219
                                                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);
220
                                                if(rhashing)
221
                                                        g_free(rhashing);        /* FIXME We're overwriting the global one, if any */
222
                                                rhashing = g_strdup("sha-1");
223
                                                if(rfingerprint)
224
                                                        g_free(rfingerprint);        /* FIXME We're overwriting the global one, if any */
225
                                                rfingerprint = g_strdup(a->value + strlen("sha-1 "));
226
                                        } else {
227
                                                /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
228
                                                JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
229
                                        }
230
                                } else if(!strcasecmp(a->name, "setup")) {
231
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->value);
232
                                        if(!strcasecmp(a->value, "actpass") || !strcasecmp(a->value, "passive"))
233
                                                stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
234
                                        else if(!strcasecmp(a->value, "active"))
235
                                                stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
236
                                        /* TODO Handle holdconn... */
237
                                } else if(!strcasecmp(a->name, "ice-ufrag")) {
238
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->value);
239
                                        if(ruser)
240
                                                g_free(ruser);        /* FIXME We're overwriting the global one, if any */
241
                                        ruser = g_strdup(a->value);
242
                                } else if(!strcasecmp(a->name, "ice-pwd")) {
243
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->value);
244
                                        if(rpass)
245
                                                g_free(rpass);        /* FIXME We're overwriting the global one, if any */
246
                                        rpass = g_strdup(a->value);
247
                                }
248
                        }
249
                        tempA = tempA->next;
250
                }
251
                if(!ruser || !rpass || !rfingerprint || !rhashing) {
252
                        /* Missing mandatory information, failure... */
253
                        if(ruser)
254
                                g_free(ruser);
255
                        ruser = NULL;
256
                        if(rpass)
257
                                g_free(rpass);
258
                        rpass = NULL;
259
                        if(rhashing)
260
                                g_free(rhashing);
261
                        rhashing = NULL;
262
                        if(rfingerprint)
263
                                g_free(rfingerprint);
264
                        rfingerprint = NULL;
265
                        return -2;
266
                }
267
                /* FIXME We're replacing the fingerprint info, assuming it's going to be the same for all media */
268
                if(stream->remote_hashing != NULL)
269
                        g_free(stream->remote_hashing);
270
                stream->remote_hashing = g_strdup(rhashing);
271
                if(stream->remote_fingerprint != NULL)
272
                        g_free(stream->remote_fingerprint);
273
                stream->remote_fingerprint = g_strdup(rfingerprint);
274
                /* Store the ICE username and password for this stream */
275
                if(stream->ruser != NULL)
276
                        g_free(stream->ruser);
277
                stream->ruser = g_strdup(ruser);
278
                if(stream->rpass != NULL)
279
                        g_free(stream->rpass);
280
                stream->rpass = g_strdup(rpass);
281
                /* Now look for candidates and other info */
282
                tempA = m->attributes;
283
                while(tempA) {
284
                        janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
285
                        if(a->name) {
286
                                if(!strcasecmp(a->name, "candidate")) {
287
                                        if(m->type == JANUS_SDP_VIDEO && handle->audio_id > 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
288
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a video candidate but we're bundling, ignoring...\n", handle->handle_id);
289
#ifdef HAVE_SCTP
290
                                        } else if(m->type == JANUS_SDP_APPLICATION && (handle->audio_id > 0 || handle->video_id > 0) && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
291
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a SCTP candidate but we're bundling, ignoring...\n", handle->handle_id);
292
#endif
293
                                        } else {
294
                                                int res = janus_sdp_parse_candidate(stream, (const char *)a->value, 0);
295
                                                if(res != 0) {
296
                                                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
297
                                                }
298
                                        }
299
                                }
300
                                if(!strcasecmp(a->name, "ssrc")) {
301
                                        int res = janus_sdp_parse_ssrc(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
302
                                        if(res != 0) {
303
                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC attribute... (%d)\n", handle->handle_id, res);
304
                                        }
305
                                }
306
#ifdef HAVE_SCTP
307
                                if(!strcasecmp(a->name, "sctpmap")) {
308
                                        /* TODO Parse sctpmap line to get the UDP-port value and the number of channels */
309
                                        JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->value);
310
                                }
311
#endif
312
                        }
313
                        tempA = tempA->next;
314
                }
315
                temp = temp->next;
316
        }
317
        if(ruser)
318
                g_free(ruser);
319
        ruser = NULL;
320
        if(rpass)
321
                g_free(rpass);
322
        rpass = NULL;
323
        if(rhashing)
324
                g_free(rhashing);
325
        rhashing = NULL;
326
        if(rfingerprint)
327
                g_free(rfingerprint);
328
        rfingerprint = NULL;
329
        return 0;        /* FIXME Handle errors better */
330
}
331

    
332
int janus_sdp_parse_candidate(void *ice_stream, const char *candidate, int trickle) {
333
        if(ice_stream == NULL || candidate == NULL)
334
                return -1;
335
        janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
336
        janus_ice_handle *handle = stream->handle;
337
        if(handle == NULL)
338
                return -2;
339
        janus_ice_component *component = NULL;
340
        if(strstr(candidate, "candidate:") == candidate) {
341
                /* Skipping the 'candidate:' prefix Firefox puts in trickle candidates */
342
                candidate += strlen("candidate:");
343
        }
344
        char rfoundation[32], rtransport[4], rip[40], rtype[6], rrelip[40];
345
        guint32 rcomponent, rpriority, rport, rrelport;
346
        int res = sscanf(candidate, "%31s %30u %3s %30u %39s %30u typ %5s %*s %39s %*s %30u",
347
                rfoundation, &rcomponent, rtransport, &rpriority,
348
                        rip, &rport, rtype, rrelip, &rrelport);
349
        if(res < 7) {
350
                /* Failed to parse this address, can it be IPv6? */
351
                if(!janus_ice_is_ipv6_enabled()) {
352
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] Received IPv6 candidate, but IPv6 support is disabled...\n", handle->handle_id);
353
                        return res;
354
                }
355
        }
356
        if(res >= 7) {
357
                /* Add remote candidate */
358
                component = g_hash_table_lookup(stream->components, GUINT_TO_POINTER(rcomponent));
359
                if(component == NULL) {
360
                        if(rcomponent == 2 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)) {
361
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
362
                        } else {
363
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"]   -- No such component %d in stream %d?\n", handle->handle_id, rcomponent, stream->stream_id);
364
                        }
365
                } else {
366
                        if(rcomponent == 2 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)) {
367
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
368
                                return 0;
369
                        }
370
                        //~ if(trickle) {
371
                                //~ if(component->dtls != NULL) {
372
                                        //~ /* This component is already ready, ignore this further candidate */
373
                                        //~ JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Ignoring this candidate, the component is already ready\n", handle->handle_id);
374
                                        //~ return 0;
375
                                //~ }
376
                        //~ }
377
                        component->component_id = rcomponent;
378
                        component->stream_id = stream->stream_id;
379
                        NiceCandidate *c = NULL;
380
                        if(!strcasecmp(rtype, "host")) {
381
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:host %s:%d\n",
382
                                        handle->handle_id, rcomponent, stream->stream_id, rip, rport);
383
                                /* Unless this is libnice >= 0.1.8, we only support UDP... */
384
                                if(!strcasecmp(rtransport, "udp")) {
385
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
386
#ifdef HAVE_LIBNICE_TCP
387
                                } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
388
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
389
#endif
390
                                } else {
391
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
392
                                }
393
                        } else if(!strcasecmp(rtype, "srflx")) {
394
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:srflx %s:%d --> %s:%d \n",
395
                                        handle->handle_id, rcomponent, stream->stream_id,  rrelip, rrelport, rip, rport);
396
                                /* Unless this is libnice >= 0.1.8, we only support UDP... */
397
                                if(!strcasecmp(rtransport, "udp")) {
398
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
399
#ifdef HAVE_LIBNICE_TCP
400
                                } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
401
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
402
#endif
403
                                } else {
404
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
405
                                }
406
                        } else if(!strcasecmp(rtype, "prflx")) {
407
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:prflx %s:%d --> %s:%d\n",
408
                                        handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
409
                                /* Unless this is libnice >= 0.1.8, we only support UDP... */
410
                                if(!strcasecmp(rtransport, "udp")) {
411
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
412
#ifdef HAVE_LIBNICE_TCP
413
                                } else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
414
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
415
#endif
416
                                } else {
417
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
418
                                }
419
                        } else if(!strcasecmp(rtype, "relay")) {
420
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:relay %s:%d --> %s:%d\n",
421
                                        handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
422
                                /* We only support UDP/TCP/TLS... */
423
                                if(strcasecmp(rtransport, "udp") && strcasecmp(rtransport, "tcp") && strcasecmp(rtransport, "tls")) {
424
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
425
                                } else {
426
                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_RELAYED);
427
                                }
428
                        } else {
429
                                /* FIXME What now? */
430
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Unknown remote candidate type:%s for component:%d stream:%d!\n",
431
                                        handle->handle_id, rtype, rcomponent, stream->stream_id);
432
                        }
433
                        if(c != NULL) {
434
                                c->component_id = rcomponent;
435
                                c->stream_id = stream->stream_id;
436
#ifndef HAVE_LIBNICE_TCP
437
                                c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
438
#else
439
                                if(!strcasecmp(rtransport, "udp")) {
440
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: UDP\n", handle->handle_id);
441
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
442
                                } else {
443
                                        /* Check the type (https://tools.ietf.org/html/rfc6544#section-4.5) */
444
                                        const char *type = NULL;
445
                                        int ctype = 0;
446
                                        if(strstr(candidate, "tcptype active")) {
447
                                                type = "active";
448
                                                ctype = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
449
                                        } else if(strstr(candidate, "tcptype passive")) {
450
                                                type = "passive";
451
                                                ctype = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
452
                                        } else if(strstr(candidate, "tcptype so")) {
453
                                                type = "so";
454
                                                ctype = NICE_CANDIDATE_TRANSPORT_TCP_SO;
455
                                        } else {
456
                                                /* TODO: We should actually stop here... */
457
                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Missing tcptype info for the TCP candidate!\n", handle->handle_id);
458
                                        }
459
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: TCP (%s)\n", handle->handle_id, type);
460
                                        c->transport = ctype;
461
                                }
462
#endif
463
                                strncpy(c->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION);
464
                                c->priority = rpriority;
465
                                nice_address_set_from_string(&c->addr, rip);
466
                                nice_address_set_port(&c->addr, rport);
467
                                c->username = g_strdup(stream->ruser);
468
                                c->password = g_strdup(stream->rpass);
469
                                if(c->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || c->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) {
470
                                        nice_address_set_from_string(&c->base_addr, rrelip);
471
                                        nice_address_set_port(&c->base_addr, rrelport);
472
                                } else if(c->type == NICE_CANDIDATE_TYPE_RELAYED) {
473
                                        /* FIXME Do we really need the base address for TURN? */
474
                                        nice_address_set_from_string(&c->base_addr, rrelip);
475
                                        nice_address_set_port(&c->base_addr, rrelport);
476
                                }
477
                                component->candidates = g_slist_append(component->candidates, c);
478
                                JANUS_LOG(LOG_HUGE, "[%"SCNu64"]    Candidate added to the list! (%u elements for %d/%d)\n", handle->handle_id,
479
                                        g_slist_length(component->candidates), stream->stream_id, component->component_id);
480
                                /* Save for the summary, in case we need it */
481
                                component->remote_candidates = g_slist_append(component->remote_candidates, g_strdup(candidate));
482
                                if(trickle) {
483
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)) {
484
                                                /* This is a trickle candidate and ICE has started, we should process it right away */
485
                                                if(!component->process_started) {
486
                                                        /* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
487
                                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE already started for this component, setting candidates we have up to now\n", handle->handle_id);
488
                                                        janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
489
                                                } else {
490
                                                        GSList *candidates = NULL;
491
                                                        candidates = g_slist_append(candidates, c);
492
                                                        if(nice_agent_set_remote_candidates(handle->agent, stream->stream_id, component->component_id, candidates) < 1) {
493
                                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to add trickle candidate :-(\n", handle->handle_id);
494
                                                        } else {
495
                                                                JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Trickle candidate added!\n", handle->handle_id);
496
                                                        }
497
                                                        g_slist_free(candidates);
498
                                                }
499
                                        } else {
500
                                                /* ICE hasn't started yet: to make sure we're not stuck, also check if we stopped processing the SDP */
501
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)) {
502
                                                        janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
503
                                                        /* This is a trickle candidate and ICE has started, we should process it right away */
504
                                                        if(!component->process_started) {
505
                                                                /* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
506
                                                                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);
507
                                                                janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
508
                                                        } else {
509
                                                                GSList *candidates = NULL;
510
                                                                candidates = g_slist_append(candidates, c);
511
                                                                if(nice_agent_set_remote_candidates(handle->agent, stream->stream_id, component->component_id, candidates) < 1) {
512
                                                                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to add trickle candidate :-(\n", handle->handle_id);
513
                                                                } else {
514
                                                                        JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Trickle candidate added!\n", handle->handle_id);
515
                                                                }
516
                                                                g_slist_free(candidates);
517
                                                        }
518
                                                } else {
519
                                                        /* Still processing the offer/answer: queue the trickle candidate for now, we'll process it later */
520
                                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Queueing trickle candidate, status is not START yet\n", handle->handle_id);
521
                                                }
522
                                        }
523
                                }
524
                        }
525
                }
526
        } else {
527
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate (res=%d)...\n", handle->handle_id, res);
528
                return res;
529
        }
530
        return 0;
531
}
532

    
533
int janus_sdp_parse_ssrc(void *ice_stream, const char *ssrc_attr, int video) {
534
        if(ice_stream == NULL || ssrc_attr == NULL)
535
                return -1;
536
        janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
537
        janus_ice_handle *handle = stream->handle;
538
        if(handle == NULL)
539
                return -2;
540
        guint32 ssrc = atol(ssrc_attr);
541
        if(ssrc == 0)
542
                return -3;
543
        if(video) {
544
                if(stream->video_ssrc_peer == 0) {
545
                        stream->video_ssrc_peer = ssrc;
546
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %u\n", handle->handle_id, stream->video_ssrc_peer);
547
                } else if(stream->video_ssrc_peer != ssrc) {
548
                        /* FIXME We assume the second SSRC we get is the one Chrome associates with retransmissions, e.g.
549
                         *         a=ssrc-group:FID 586466331 2053167359 (SSRC SSRC-rtx)
550
                         * SSRC group FID: https://tools.ietf.org/html/rfc3388#section-7 */
551
                        stream->video_ssrc_peer_rtx = ssrc;
552
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (rtx): %u\n", handle->handle_id, stream->video_ssrc_peer_rtx);
553
                }
554
        } else {
555
                if(stream->audio_ssrc_peer == 0) {
556
                        stream->audio_ssrc_peer = ssrc;
557
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer audio SSRC: %u\n", handle->handle_id, stream->audio_ssrc_peer);
558
                }
559
        }
560
        return 0;
561
}
562

    
563
char *janus_sdp_anonymize(const char *sdp) {
564
        if(sdp == NULL)
565
                return NULL;
566
        janus_sdp *anon = janus_sdp_import(sdp);
567
        if(anon == NULL) {
568
                JANUS_LOG(LOG_ERR, "Error parsing/anonymizing SDP\n");
569
                return NULL;
570
        }
571
        int audio = 0, video = 0;
572
#ifdef HAVE_SCTP
573
        int data = 0;
574
#endif
575
                /* o= */
576
        if(anon->o_addr != NULL) {
577
                g_free(anon->o_addr);
578
                anon->o_ipv4 = TRUE;
579
                anon->o_addr = g_strdup("1.1.1.1");
580
        }
581
                /* a= */
582
        GList *temp = anon->attributes;
583
        while(temp) {
584
                janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
585
                /* These are attributes we handle ourselves, the plugins don't need them */
586
                if(!strcasecmp(a->name, "ice-ufrag")
587
                                || !strcasecmp(a->name, "ice-pwd")
588
                                || !strcasecmp(a->name, "ice-options")
589
                                || !strcasecmp(a->name, "fingerprint")
590
                                || !strcasecmp(a->name, "group")
591
                                || !strcasecmp(a->name, "msid-semantic")
592
                                || !strcasecmp(a->name, "rtcp-rsize")) {
593
                        anon->attributes = g_list_remove(anon->attributes, a);
594
                        temp = anon->attributes;
595
                        g_free(a->name);
596
                        g_free(a->value);
597
                        g_free(a);
598
                        continue;
599
                }
600
                temp = temp->next;
601
                continue;
602
        }
603
                /* m= */
604
        temp = anon->m_lines;
605
        while(temp) {
606
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
607
                if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
608
                        audio++;
609
                        m->port = audio == 1 ? 9 : 0;
610
                } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
611
                        video++;
612
                        m->port = video == 1 ? 9 : 0;
613
#ifdef HAVE_SCTP
614
                } else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
615
                        data++;
616
                        m->port = data == 1 ? 9 : 0;
617
#endif
618
                } else {
619
                        m->port = 0;
620
                }
621
                        /* c= */
622
                if(m->c_addr != NULL) {
623
                        g_free(m->c_addr);
624
                        m->c_ipv4 = TRUE;
625
                        m->c_addr = g_strdup("1.1.1.1");
626
                }
627
                        /* a= */
628
                GList *tempA = m->attributes;
629
                while(tempA) {
630
                        janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
631
                        /* These are attributes we handle ourselves, the plugins don't need them */
632
                        if(!strcasecmp(a->name, "ice-ufrag")
633
                                        || !strcasecmp(a->name, "ice-pwd")
634
                                        || !strcasecmp(a->name, "ice-options")
635
                                        || !strcasecmp(a->name, "crypto")
636
                                        || !strcasecmp(a->name, "fingerprint")
637
                                        || !strcasecmp(a->name, "setup")
638
                                        || !strcasecmp(a->name, "connection")
639
                                        || !strcasecmp(a->name, "group")
640
                                        || !strcasecmp(a->name, "mid")
641
                                        || !strcasecmp(a->name, "msid")
642
                                        || !strcasecmp(a->name, "msid-semantic")
643
                                        || !strcasecmp(a->name, "rtcp")
644
                                        || !strcasecmp(a->name, "rtcp-mux")
645
                                        || !strcasecmp(a->name, "rtcp-rsize")
646
                                        || !strcasecmp(a->name, "candidate")
647
                                        || !strcasecmp(a->name, "ssrc")
648
                                        || !strcasecmp(a->name, "ssrc-group")
649
                                        || !strcasecmp(a->name, "extmap")        /* TODO Actually implement RTP extensions */
650
                                        || !strcasecmp(a->name, "sctpmap")) {
651
                                m->attributes = g_list_remove(m->attributes, a);
652
                                tempA = m->attributes;
653
                                g_free(a->name);
654
                                g_free(a->value);
655
                                g_free(a);
656
                                continue;
657
                        }
658
                        /* Also remove attributes/formats we know we don't support now */
659
                        if(strstr(a->value, "red/90000") || strstr(a->value, "ulpfec/90000") || strstr(a->value, "rtx/90000")) {
660
                                int ptype = atoi(a->value);
661
                                JANUS_LOG(LOG_VERB, "Will remove payload type %d\n", ptype);
662
                                /* Remove this attribute */
663
                                m->attributes = g_list_remove(m->attributes, a);
664
                                tempA = m->attributes;
665
                                g_free(a->name);
666
                                g_free(a->value);
667
                                g_free(a);
668
                                /* Also remove other attributes with the same payload type, and any reference from the m-line */
669
                                m->ptypes = g_list_remove(m->ptypes, GINT_TO_POINTER(ptype));
670
                                GList *tempAA = m->attributes;
671
                                while(tempAA) {
672
                                        janus_sdp_attribute *aa = (janus_sdp_attribute *)tempAA->data;
673
                                        if(atoi(aa->value) == ptype) {
674
                                                if(tempAA == tempA)
675
                                                        tempA = tempA->next;
676
                                                m->attributes = g_list_remove(m->attributes, aa);
677
                                                tempAA = m->attributes;
678
                                                g_free(aa->name);
679
                                                g_free(aa->value);
680
                                                g_free(aa);
681
                                                continue;
682
                                        }
683
                                        tempAA = tempAA->next;
684
                                }
685
                        }
686
                        tempA = tempA->next;
687
                }
688
                temp = temp->next;
689
        }
690
        char *buf = janus_sdp_export(anon);
691
        janus_sdp_free(anon);
692
        if(buf != NULL) {
693
                JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
694
                JANUS_LOG(LOG_VERB, "  >> Anonymized (%zu --> %zu bytes)\n", strlen(sdp), strlen(buf));
695
                JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
696
                JANUS_LOG(LOG_VERB, "%s\n", buf);
697
                return buf;
698
        } else {
699
                JANUS_LOG(LOG_ERR, "Error anonymizing SDP\n");
700
                return NULL;
701
        }
702
}
703

    
704
char *janus_sdp_merge(void *ice_handle, const char *origsdp) {
705
        if(ice_handle == NULL || origsdp == NULL)
706
                return NULL;
707
        janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
708
        janus_ice_stream *stream = NULL;
709
        janus_sdp *anon = janus_sdp_import(origsdp);
710
        if(anon == NULL) {
711
                JANUS_LOG(LOG_ERR, "Error parsing/merging SDP\n");
712
                return NULL;
713
        }
714
        char *rtp_profile = handle->rtp_profile ? handle->rtp_profile : (char *)"RTP/SAVPF";
715
        /* FIXME Any Plan B to take into account? */
716
        int planb = strstr(origsdp, "a=planb:") ? 1 : 0;
717
        if(planb) {
718
                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B);
719
        } else {
720
                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B);
721
        }
722
        gboolean ipv4 = !strstr(janus_get_public_ip(), ":");
723
        /* Origin o= */
724
        gint64 sessid = janus_get_real_time();
725
        if(anon->o_name == NULL)
726
                anon->o_name = g_strdup("-");
727
        if(anon->o_sessid == 0 || anon->o_version == 0) {
728
                anon->o_sessid = sessid;
729
                anon->o_version = 1;
730
        }
731
        anon->o_ipv4 = ipv4;
732
        g_free(anon->o_addr);
733
        anon->o_addr = g_strdup(janus_get_public_ip());
734
        /* Session name s= */
735
        if(anon->s_name == NULL)
736
                anon->s_name = g_strdup("Meetecho Janus");
737
        /* bundle: add new global attribute */
738
        int audio = (strstr(origsdp, "m=audio") != NULL);
739
        int video = (strstr(origsdp, "m=video") != NULL);
740
#ifdef HAVE_SCTP
741
        int data = (strstr(origsdp, "DTLS/SCTP") && !strstr(origsdp, " 0 DTLS/SCTP"));
742
#else
743
        int data = 0;
744
#endif
745
        char buffer[2048], buffer_part[512];
746
        buffer[0] = '\0';
747
        buffer_part[0] = '\0';
748
        g_snprintf(buffer, sizeof(buffer), "BUNDLE");
749
        if(audio) {
750
                g_snprintf(buffer_part, sizeof(buffer_part),
751
                        " %s", handle->audio_mid ? handle->audio_mid : "audio");
752
                g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
753
        }
754
        if(video) {
755
                g_snprintf(buffer_part, sizeof(buffer_part),
756
                        " %s", handle->video_mid ? handle->video_mid : "video");
757
                g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
758
        }
759
        if(data) {
760
                g_snprintf(buffer_part, sizeof(buffer_part),
761
                        " %s", handle->data_mid ? handle->data_mid : "data");
762
                g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
763
        }
764
        /* Global attributes: start with group */
765
        GList *first = anon->attributes;
766
#pragma GCC diagnostic ignored "-Wformat-security"
767
        janus_sdp_attribute *a = janus_sdp_attribute_create("group", buffer);
768
#pragma GCC diagnostic warning "-Wformat-security"
769
        anon->attributes = g_list_insert_before(anon->attributes, first, a);
770
        /* msid-semantic: add new global attribute */
771
        a = janus_sdp_attribute_create("msid-semantic", " WMS janus");
772
        anon->attributes = g_list_insert_before(anon->attributes, first, a);
773
        /* ICE Full or Lite? */
774
        if(janus_ice_is_ice_lite_enabled()) {
775
                /* Janus is acting in ICE Lite mode, advertize this */
776
                a = janus_sdp_attribute_create("ice-lite", NULL);
777
                anon->attributes = g_list_insert_before(anon->attributes, first, a);
778
        }
779
        /* Media lines now */
780
        audio = 0;
781
        video = 0;
782
#ifdef HAVE_SCTP
783
        data = 0;
784
#endif
785
        GList *temp = anon->m_lines;
786
        while(temp) {
787
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
788
                first = m->attributes;
789
                /* Overwrite RTP profile for audio and video */
790
                if(m->type != JANUS_SDP_APPLICATION) {
791
                        g_free(m->proto);
792
                        m->proto = g_strdup(rtp_profile);
793
                }
794
                /* Media connection c= */
795
                g_free(m->c_addr);
796
                m->c_ipv4 = ipv4;
797
                m->c_addr = g_strdup(janus_get_public_ip());
798
                /* Check if we need to refuse the media or not */
799
                if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
800
                        audio++;
801
                        if(audio > 1 || !handle->audio_id) {
802
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id);
803
                                m->port = 0;
804
                                g_free(m->direction);
805
                                m->direction = g_strdup("inactive");
806
                                temp = temp->next;
807
                                continue;
808
                        }
809
                        /* Audio */
810
                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
811
                        if(stream == NULL) {
812
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
813
                                m->port = 0;
814
                                g_free(m->direction);
815
                                m->direction = g_strdup("inactive");
816
                                temp = temp->next;
817
                                continue;
818
                        }
819
                } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
820
                        video++;
821
                        gint id = handle->video_id;
822
                        if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
823
                                id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
824
                        if(video > 1 || !id) {
825
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video,
826
                                        janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? handle->audio_id : handle->video_id);
827
                                m->port = 0;
828
                                g_free(m->direction);
829
                                m->direction = g_strdup("inactive");
830
                                temp = temp->next;
831
                                continue;
832
                        }
833
                        /* Video */
834
                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
835
                        if(stream == NULL) {
836
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, id);
837
                                m->port = 0;
838
                                g_free(m->direction);
839
                                m->direction = g_strdup("inactive");
840
                                temp = temp->next;
841
                                continue;
842
                        }
843
#ifdef HAVE_SCTP
844
                } else if(m->type == JANUS_SDP_APPLICATION) {
845
                        /* Is this SCTP for DataChannels? */
846
                        if(!strcasecmp(m->proto, "DTLS/SCTP") && m->port > 0) {
847
                                /* Yep */
848
                                data++;
849
                                gint id = handle->data_id;
850
                                if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
851
                                        id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
852
                                if(data > 1 || !id) {
853
                                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have %d SCTP lines, and the id is %d)\n", handle->handle_id, data, id);
854
                                        m->port = 0;
855
                                        g_free(m->direction);
856
                                        m->direction = g_strdup("inactive");
857
                                        temp = temp->next;
858
                                        continue;
859
                                }
860
                                /* SCTP */
861
                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
862
                                if(stream == NULL) {
863
                                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (invalid stream %d)\n", handle->handle_id, id);
864
                                        m->port = 0;
865
                                        g_free(m->direction);
866
                                        m->direction = g_strdup("inactive");
867
                                        temp = temp->next;
868
                                        continue;
869
                                }
870
                        } else {
871
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
872
                                m->port = 0;
873
                                g_free(m->direction);
874
                                m->direction = g_strdup("inactive");
875
                                temp = temp->next;
876
                                continue;
877
                        }
878
#endif
879
                } else {
880
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
881
                        m->port = 0;
882
                        g_free(m->direction);
883
                        m->direction = g_strdup("inactive");
884
                        temp = temp->next;
885
                        continue;
886
                }
887
                /* a=mid:(audio|video|data) */
888
                if(m->type == JANUS_SDP_AUDIO) {
889
#pragma GCC diagnostic ignored "-Wformat-security"
890
                        a = janus_sdp_attribute_create("mid", handle->audio_mid ? handle->audio_mid : "audio");
891
#pragma GCC diagnostic warning "-Wformat-security"
892
                        m->attributes = g_list_insert_before(m->attributes, first, a);
893
                } else if(m->type == JANUS_SDP_VIDEO) {
894
#pragma GCC diagnostic ignored "-Wformat-security"
895
                        a = janus_sdp_attribute_create("mid", handle->video_mid ? handle->video_mid : "video");
896
#pragma GCC diagnostic warning "-Wformat-security"
897
                        m->attributes = g_list_insert_before(m->attributes, first, a);
898
#ifdef HAVE_SCTP
899
                } else if(m->type == JANUS_SDP_APPLICATION) {
900
                        /* FIXME sctpmap and webrtc-datachannel should be dynamic */
901
                        a = janus_sdp_attribute_create("sctpmap", "5000 webrtc-datachannel 16");
902
                        m->attributes = g_list_insert_before(m->attributes, first, a);
903
#pragma GCC diagnostic ignored "-Wformat-security"
904
                        a = janus_sdp_attribute_create("mid", handle->data_mid ? handle->data_mid : "data");
905
#pragma GCC diagnostic warning "-Wformat-security"
906
                        m->attributes = g_list_insert_before(m->attributes, first, a);
907
#endif
908
                }
909
                if(m->type != JANUS_SDP_APPLICATION) {
910
                        a = janus_sdp_attribute_create("rtcp-mux", NULL);
911
                        m->attributes = g_list_insert_before(m->attributes, first, a);
912
                }
913
                /* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
914
                gchar *ufrag = NULL;
915
                gchar *password = NULL;
916
                nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
917
                a = janus_sdp_attribute_create("ice-ufrag", "%s", ufrag);
918
                m->attributes = g_list_insert_before(m->attributes, first, a);
919
                a = janus_sdp_attribute_create("ice-pwd", "%s", password);
920
                m->attributes = g_list_insert_before(m->attributes, first, a);
921
                g_free(ufrag);
922
                g_free(password);
923
                a = janus_sdp_attribute_create("ice-options", "trickle");
924
                m->attributes = g_list_insert_before(m->attributes, first, a);
925
                a = janus_sdp_attribute_create("fingerprint", "sha-256 %s", janus_dtls_get_local_fingerprint());
926
                m->attributes = g_list_insert_before(m->attributes, first, a);
927
                a = janus_sdp_attribute_create("setup", "%s", janus_get_dtls_srtp_role(stream->dtls_role));
928
                m->attributes = g_list_insert_before(m->attributes, first, a);
929
                /* Add last attributes, rtcp and ssrc (msid) */
930
                if(m->type == JANUS_SDP_AUDIO &&
931
                                (m->direction == NULL || !strcasecmp(m->direction, "sendrecv") || !strcasecmp(m->direction, "sendonly"))) {
932
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janusaudio", stream->audio_ssrc);
933
                        m->attributes = g_list_append(m->attributes, a);
934
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusa0", stream->audio_ssrc);
935
                        m->attributes = g_list_append(m->attributes, a);
936
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->audio_ssrc);
937
                        m->attributes = g_list_append(m->attributes, a);
938
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusa0", stream->audio_ssrc);
939
                        m->attributes = g_list_append(m->attributes, a);
940
                } else if(m->type == JANUS_SDP_VIDEO &&
941
                                (m->direction == NULL || !strcasecmp(m->direction, "sendrecv") || !strcasecmp(m->direction, "sendonly"))) {
942
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janusvideo", stream->video_ssrc);
943
                        m->attributes = g_list_append(m->attributes, a);
944
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc);
945
                        m->attributes = g_list_append(m->attributes, a);
946
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc);
947
                        m->attributes = g_list_append(m->attributes, a);
948
                        a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc);
949
                        m->attributes = g_list_append(m->attributes, a);
950
                }
951
                /* And now the candidates */
952
                janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 1);
953
                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) &&
954
                                m->type != JANUS_SDP_APPLICATION)
955
                        janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 2);
956
                /* Next */
957
                temp = temp->next;
958
        }
959

    
960
        char *sdp = janus_sdp_export(anon);
961
        janus_sdp_free(anon);
962

    
963
        JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
964
        JANUS_LOG(LOG_VERB, "  >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp));
965
        JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
966
        JANUS_LOG(LOG_VERB, "%s\n", sdp);
967

    
968
        return sdp;
969
}