Statistics
| Branch: | Revision:

janus-gateway / sdp.c @ 5e9e29e0

History | View | Annotate | Download (25.8 KB)

1
/*! \file    sdp.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU Affero General Public License v3
4
 * \brief    SDP processing
5
 * \details  Implementation (based on the Sofia-SDP stack) of the 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.
12
 * 
13
 * \ingroup protocols
14
 * \ref protocols
15
 */
16
 
17
#include "janus.h"
18
#include "ice.h"
19
#include "dtls.h"
20
#include "sdp.h"
21
#include "utils.h"
22

    
23

    
24
static su_home_t *home = NULL;
25

    
26

    
27
/* SDP Initialization */
28
int janus_sdp_init() {
29
        home = su_home_new(sizeof(su_home_t));
30
        if(su_home_init(home) < 0) {
31
                JANUS_PRINT("Ops, error setting up sofia-sdp?\n");
32
                return -1;
33
        }
34
        return 0;
35
}
36

    
37
void janus_sdp_deinit() {
38
        su_home_deinit(home);
39
}
40

    
41

    
42
/* SDP parser */
43
void janus_sdp_free(janus_sdp *sdp) {
44
        if(!sdp)
45
                return;
46
        sdp_parser_t *parser = (sdp_parser_t *)sdp->parser;
47
        if(parser)
48
                sdp_parser_free(parser);
49
        sdp->parser = NULL;
50
        sdp->sdp = NULL;
51
        free(sdp);
52
        sdp = NULL;
53
}
54

    
55
/* Pre-parse SDP: is this SDP valid? how many audio/video lines? */
56
janus_sdp *janus_sdp_preparse(const char *jsep_sdp, int *audio, int *video) {
57
        sdp_parser_t *parser = sdp_parse(home, jsep_sdp, strlen(jsep_sdp), 0);
58
        sdp_session_t *parsed_sdp = sdp_session(parser);
59
        if(!parsed_sdp) {
60
                JANUS_DEBUG("  Error parsing SDP? %s\n", sdp_parsing_error(parser));
61
                sdp_parser_free(parser);
62
                /* Invalid SDP */
63
                return NULL;
64
        }
65
        sdp_media_t *m = parsed_sdp->sdp_media;
66
        while(m) {
67
                if(m->m_type == sdp_media_audio) {
68
                        *audio = *audio + 1;
69
                } else if(m->m_type == sdp_media_video) {
70
                        *video = *video + 1;
71
                }
72
                m = m->m_next;
73
        }
74
        janus_sdp *sdp = (janus_sdp *)calloc(1, sizeof(janus_sdp));
75
        if(sdp == NULL) {
76
                JANUS_DEBUG("Memory error!\n");
77
                return NULL;
78
        }
79

    
80
        sdp->parser = parser;
81
        sdp->sdp = parsed_sdp;
82
        return sdp;
83
}
84

    
85
/* Parse SDP */
86
int janus_sdp_parse(janus_ice_handle *handle, janus_sdp *sdp) {
87
        if(!handle || !sdp)
88
                return -1;
89
        //~ sdp_parser_t *parser = (sdp_parser_t *)sdp->parser;
90
        //~ if(!parser)
91
                //~ return -1;
92
        sdp_session_t *remote_sdp = (sdp_session_t *)sdp->sdp;
93
        if(!remote_sdp)
94
                return -1;
95
        janus_ice_stream *stream = NULL;
96
        janus_ice_component *component = NULL;
97
        gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
98
        int audio = 0, video = 0;
99
        gint rstream = 0;
100
        /* Ok, let's start */
101
        sdp_attribute_t *a = remote_sdp->sdp_attributes;
102
        while(a) {
103
                if(a->a_name) {
104
                        if(!strcasecmp(a->a_name, "fingerprint")) {
105
                                JANUS_PRINT("[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->a_value);
106
                                if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
107
                                        rhashing = g_strdup("sha-256");
108
                                        rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
109
                                } else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
110
                                        JANUS_PRINT("[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
111
                                        rhashing = g_strdup("sha-1");
112
                                        rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
113
                                } else {
114
                                        /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
115
                                        JANUS_PRINT("[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
116
                                }
117
                        } else if(!strcasecmp(a->a_name, "ice-ufrag")) {
118
                                JANUS_PRINT("[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->a_value);
119
                                ruser = g_strdup(a->a_value);
120
                        } else if(!strcasecmp(a->a_name, "ice-pwd")) {
121
                                JANUS_PRINT("[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->a_value);
122
                                rpass = g_strdup(a->a_value);
123
                        }
124
                }
125
                a = a->a_next;
126
        }
127
        sdp_media_t *m = remote_sdp->sdp_media;
128
        while(m) {
129
                /* What media type is this? */
130
                if(m->m_type == sdp_media_audio) {
131
                        audio++;
132
                        if(audio > 1) {
133
                                m = m->m_next;
134
                                continue;
135
                        }
136
                        JANUS_PRINT("[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
137
                        rstream = handle->audio_id;
138
                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
139
                } else if(m->m_type == sdp_media_video) {
140
                        video++;
141
                        if(video > 1) {
142
                                m = m->m_next;
143
                                continue;
144
                        }
145
                        JANUS_PRINT("[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
146
                        rstream = handle->video_id;
147
                        stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
148
                } else {
149
                        JANUS_PRINT("[%"SCNu64"] Skipping unsupported media line...\n", handle->handle_id);
150
                        m = m->m_next;
151
                        continue;
152
                }
153
                /* Look for ICE credentials and fingerprint first: check media attributes */
154
                a = m->m_attributes;
155
                while(a) {
156
                        if(a->a_name) {
157
                                if(!strcasecmp(a->a_name, "fingerprint")) {
158
                                        JANUS_PRINT("[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->a_value);
159
                                        if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
160
                                                if(rhashing)
161
                                                        g_free(rhashing);        /* FIXME We're overwriting the global one, if any */
162
                                                rhashing = g_strdup("sha-256");
163
                                                if(rfingerprint)
164
                                                        g_free(rfingerprint);        /* FIXME We're overwriting the global one, if any */
165
                                                rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
166
                                        } else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
167
                                                JANUS_PRINT("[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
168
                                                if(rhashing)
169
                                                        g_free(rhashing);        /* FIXME We're overwriting the global one, if any */
170
                                                rhashing = g_strdup("sha-1");
171
                                                if(rfingerprint)
172
                                                        g_free(rfingerprint);        /* FIXME We're overwriting the global one, if any */
173
                                                rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
174
                                        } else {
175
                                                /* FIXME We should handle this somehow anyway... OpenSSL supports them all */
176
                                                JANUS_PRINT("[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
177
                                        }
178
                                } else if(!strcasecmp(a->a_name, "setup")) {
179
                                        JANUS_PRINT("[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->a_value);
180
                                        if(!strcasecmp(a->a_value, "actpass") || !strcasecmp(a->a_value, "passive"))
181
                                                stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
182
                                        else if(!strcasecmp(a->a_value, "active"))
183
                                                stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
184
                                        /* TODO Handle holdconn... */
185
                                } else if(!strcasecmp(a->a_name, "ice-ufrag")) {
186
                                        JANUS_PRINT("[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->a_value);
187
                                        if(ruser)
188
                                                g_free(ruser);        /* FIXME We're overwriting the global one, if any */
189
                                        ruser = g_strdup(a->a_value);
190
                                } else if(!strcasecmp(a->a_name, "ice-pwd")) {
191
                                        JANUS_PRINT("[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->a_value);
192
                                        if(rpass)
193
                                                g_free(rpass);        /* FIXME We're overwriting the global one, if any */
194
                                        rpass = g_strdup(a->a_value);
195
                                }
196
                        }
197
                        a = a->a_next;
198
                }
199
                if(!ruser || !rpass || !rfingerprint || !rhashing) {
200
                        /* Missing mandatory information, failure... */
201
                        if(ruser)
202
                                g_free(ruser);
203
                        ruser = NULL;
204
                        if(rpass)
205
                                g_free(rpass);
206
                        rpass = NULL;
207
                        if(rhashing)
208
                                g_free(rhashing);
209
                        rhashing = NULL;
210
                        if(rfingerprint)
211
                                g_free(rfingerprint);
212
                        rfingerprint = NULL;
213
                        return -2;
214
                }
215
                handle->remote_hashing = g_strdup(rhashing);
216
                handle->remote_fingerprint = g_strdup(rfingerprint);
217
                /* Now look for candidates and codec info */
218
                a = m->m_attributes;
219
                while(a) {
220
                        if(a->a_name) {
221
                                if(!strcasecmp(a->a_name, "candidate")) {
222
                                        char rfoundation[32], rtransport[4], rip[24], rtype[6], rrelip[24];
223
                                        guint32 rcomponent, rpriority, rport, rrelport;
224
                                        int res = 0;
225
                                        if((res = sscanf(a->a_value, "%31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u",
226
                                                rfoundation, &rcomponent, rtransport, &rpriority,
227
                                                        rip, &rport, rtype, rrelip, &rrelport)) >= 7) {
228
                                                /* Add remote candidate */
229
                                                JANUS_PRINT("[%"SCNu64"] Adding remote candidate for component %d to stream %d\n", handle->handle_id, rcomponent, rstream);
230
                                                component = g_hash_table_lookup(stream->components, GUINT_TO_POINTER(rcomponent));
231
                                                if(component == NULL) {
232
                                                        JANUS_DEBUG("[%"SCNu64"] No such component %d in stream %d?\n", handle->handle_id, rcomponent, rstream);
233
                                                } else {
234
                                                        component->component_id = rcomponent;
235
                                                        component->stream_id = rstream;
236
                                                        NiceCandidate *c = NULL;
237
                                                        if(!strcasecmp(rtype, "host")) {
238
                                                                JANUS_PRINT("[%"SCNu64"]  Adding host candidate... %s:%d\n", handle->handle_id, rip, rport);
239
                                                                /* We only support UDP... */
240
                                                                if(strcasecmp(rtransport, "udp")) {
241
                                                                        JANUS_DEBUG("[%"SCNu64"]    Unsupported transport %s!\n", handle->handle_id, rtransport);
242
                                                                } else {
243
                                                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
244
                                                                }
245
                                                        } else if(!strcasecmp(rtype, "srflx")) {
246
                                                                JANUS_PRINT("[%"SCNu64"]  Adding srflx candidate... %s:%d --> %s:%d \n", handle->handle_id, rrelip, rrelport, rip, rport);
247
                                                                /* We only support UDP... */
248
                                                                if(strcasecmp(rtransport, "udp")) {
249
                                                                        JANUS_DEBUG("[%"SCNu64"]    Unsupported transport %s!\n", handle->handle_id, rtransport);
250
                                                                }else {
251
                                                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
252
                                                                }
253
                                                        } else if(!strcasecmp(rtype, "prflx")) {
254
                                                                JANUS_PRINT("[%"SCNu64"]  Adding prflx candidate... %s:%d --> %s:%d\n", handle->handle_id, rrelip, rrelport, rip, rport);
255
                                                                /* We only support UDP... */
256
                                                                if(strcasecmp(rtransport, "udp")) {
257
                                                                        JANUS_DEBUG("[%"SCNu64"]    Unsupported transport %s!\n", handle->handle_id, rtransport);
258
                                                                } else {
259
                                                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
260
                                                                }
261
                                                        } else if(!strcasecmp(rtype, "relay")) {
262
                                                                JANUS_PRINT("[%"SCNu64"]  Adding relay candidate... %s:%d --> %s:%d\n", handle->handle_id, rrelip, rrelport, rip, rport);
263
                                                                /* We only support UDP/TCP/TLS... */
264
                                                                if(strcasecmp(rtransport, "udp") && strcasecmp(rtransport, "tcp") && strcasecmp(rtransport, "tls")) {
265
                                                                        JANUS_DEBUG("[%"SCNu64"]    Unsupported transport %s!\n", handle->handle_id, rtransport);
266
                                                                } else {
267
                                                                        c = nice_candidate_new(NICE_CANDIDATE_TYPE_RELAYED);
268
                                                                }
269
                                                        } else {
270
                                                                /* FIXME What now? */
271
                                                                JANUS_DEBUG("[%"SCNu64"]  Unknown candidate type %s!\n", handle->handle_id, rtype);
272
                                                        }
273
                                                        if(c != NULL) {
274
                                                                c->component_id = rcomponent;
275
                                                                c->stream_id = rstream;
276
                                                                c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
277
                                                                strncpy(c->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION);
278
                                                                c->priority = rpriority;
279
                                                                nice_address_set_from_string(&c->addr, rip);
280
                                                                nice_address_set_port(&c->addr, rport);
281
                                                                c->username = g_strdup(ruser);
282
                                                                c->password = g_strdup(rpass);
283
                                                                if(c->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || c->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) {
284
                                                                        nice_address_set_from_string(&c->base_addr, rrelip);
285
                                                                        nice_address_set_port(&c->base_addr, rrelport);
286
                                                                } else if(c->type == NICE_CANDIDATE_TYPE_RELAYED) {
287
                                                                        /* FIXME Do we really need the base address for TURN? */
288
                                                                        nice_address_set_from_string(&c->base_addr, rrelip);
289
                                                                        nice_address_set_port(&c->base_addr, rrelport);
290
                                                                }
291
                                                                component->candidates = g_slist_append(component->candidates, c);
292
                                                                JANUS_PRINT("[%"SCNu64"]    Candidate added to the list! (%u elements for %d/%d)\n", handle->handle_id,
293
                                                                        g_slist_length(component->candidates), stream->stream_id, component->component_id);
294
                                                        }
295
                                                }
296
                                        } else {
297
                                                JANUS_DEBUG("[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
298
                                        }
299
                                }
300
                        }
301
                        a = a->a_next;
302
                }
303
                m = m->m_next;
304
        }
305
        if(ruser)
306
                g_free(ruser);
307
        ruser = NULL;
308
        if(rpass)
309
                g_free(rpass);
310
        rpass = NULL;
311
        if(rhashing)
312
                g_free(rhashing);
313
        rhashing = NULL;
314
        if(rfingerprint)
315
                g_free(rfingerprint);
316
        rfingerprint = NULL;
317
        return 0;        /* FIXME Handle errors better */
318
}
319

    
320
char *janus_sdp_anonymize(const char *sdp) {
321
        if(sdp == NULL)
322
                return NULL;
323
        //~ su_home_t home[1] = { SU_HOME_INIT(home) };
324
        sdp_session_t *anon = NULL;
325
        sdp_parser_t *parser = sdp_parse(home, sdp, strlen(sdp), 0);
326
        if(!(anon = sdp_session(parser))) {
327
                JANUS_DEBUG("Error parsing/merging SDP: %s\n", sdp_parsing_error(parser));
328
                return NULL;
329
        }
330
                //~ /* o= */
331
        //~ if(anon->sdp_origin && anon->sdp_origin->o_username) {
332
                //~ free(anon->sdp_origin->o_username);
333
                //~ anon->sdp_origin->o_username = strdup("JANUS");
334
        //~ }
335
                /* c= */
336
        if(anon->sdp_connection && anon->sdp_connection->c_address) {
337
                //~ free(anon->sdp_connection->c_address);
338
                anon->sdp_connection->c_address = strdup("1.1.1.1");
339
        }
340
                /* a= */
341
        if(anon->sdp_attributes) {
342
                /* FIXME These are attributes we handle ourselves, the plugins don't need them */
343
                while(sdp_attribute_find(anon->sdp_attributes, "ice-ufrag"))
344
                        sdp_attribute_remove(&anon->sdp_attributes, "ice-ufrag");
345
                while(sdp_attribute_find(anon->sdp_attributes, "ice-pwd"))
346
                        sdp_attribute_remove(&anon->sdp_attributes, "ice-pwd");
347
                while(sdp_attribute_find(anon->sdp_attributes, "ice-options"))
348
                        sdp_attribute_remove(&anon->sdp_attributes, "ice-options");
349
                while(sdp_attribute_find(anon->sdp_attributes, "fingerprint"))
350
                        sdp_attribute_remove(&anon->sdp_attributes, "fingerprint");
351
                while(sdp_attribute_find(anon->sdp_attributes, "group"))
352
                        sdp_attribute_remove(&anon->sdp_attributes, "group");
353
                while(sdp_attribute_find(anon->sdp_attributes, "msid-semantic"))
354
                        sdp_attribute_remove(&anon->sdp_attributes, "msid-semantic");
355
        }
356
                /* m= */
357
        int a_sendrecv = 0, v_sendrecv = 0;
358
        if(anon->sdp_media) {
359
                int audio = 0, video = 0;
360
                sdp_media_t *m = anon->sdp_media;
361
                while(m) {
362
                        if(m->m_type == sdp_media_audio) {
363
                                audio++;
364
                                m->m_port = audio == 1 ? 1 : 0;
365
                        } else if(m->m_type == sdp_media_video) {
366
                                video++;
367
                                m->m_port = audio == 1 ? 1 : 0;
368
                        } else {
369
                                m->m_port = 0;
370
                        }
371
                                /* c= */
372
                        if(m->m_connections) {
373
                                sdp_connection_t *c = m->m_connections;
374
                                while(c) {
375
                                        if(c->c_address) {
376
                                                //~ free(c->c_address);
377
                                                c->c_address = strdup("1.1.1.1");
378
                                        }
379
                                        c = c->c_next;
380
                                }
381
                        }
382
                                /* a= */
383
                        if(m->m_attributes) {
384
                                /* FIXME These are attributes we handle ourselves, the plugins don't need them */
385
                                while(sdp_attribute_find(m->m_attributes, "ice-ufrag"))
386
                                        sdp_attribute_remove(&m->m_attributes, "ice-ufrag");
387
                                while(sdp_attribute_find(m->m_attributes, "ice-pwd"))
388
                                        sdp_attribute_remove(&m->m_attributes, "ice-pwd");
389
                                while(sdp_attribute_find(m->m_attributes, "ice-options"))
390
                                        sdp_attribute_remove(&m->m_attributes, "ice-options");
391
                                while(sdp_attribute_find(m->m_attributes, "crypto"))
392
                                        sdp_attribute_remove(&m->m_attributes, "crypto");
393
                                while(sdp_attribute_find(m->m_attributes, "fingerprint"))
394
                                        sdp_attribute_remove(&m->m_attributes, "fingerprint");
395
                                while(sdp_attribute_find(m->m_attributes, "setup"))
396
                                        sdp_attribute_remove(&m->m_attributes, "setup");
397
                                while(sdp_attribute_find(m->m_attributes, "connection"))
398
                                        sdp_attribute_remove(&m->m_attributes, "connection");
399
                                while(sdp_attribute_find(m->m_attributes, "group"))
400
                                        sdp_attribute_remove(&m->m_attributes, "group");
401
                                while(sdp_attribute_find(m->m_attributes, "msid-semantic"))
402
                                        sdp_attribute_remove(&m->m_attributes, "msid-semantic");
403
                                while(sdp_attribute_find(m->m_attributes, "rtcp"))
404
                                        sdp_attribute_remove(&m->m_attributes, "rtcp");
405
                                while(sdp_attribute_find(m->m_attributes, "rtcp-mux"))
406
                                        sdp_attribute_remove(&m->m_attributes, "rtcp-mux");
407
                                while(sdp_attribute_find(m->m_attributes, "candidate"))
408
                                        sdp_attribute_remove(&m->m_attributes, "candidate");
409
                                while(sdp_attribute_find(m->m_attributes, "ssrc"))
410
                                        sdp_attribute_remove(&m->m_attributes, "ssrc");
411
                                while(sdp_attribute_find(m->m_attributes, "extmap"))        /* TODO Actually implement RTP extensions */
412
                                        sdp_attribute_remove(&m->m_attributes, "extmap");
413
                        }
414
                        /* FIXME sendrecv hack: sofia-sdp doesn't print sendrecv, but we want it to */
415
                        if(m->m_mode == sdp_sendrecv) {
416
                                m->m_mode = sdp_inactive;
417
                                if(m->m_type == sdp_media_audio)
418
                                        a_sendrecv = 1;
419
                                else if(m->m_type == sdp_media_video)
420
                                        v_sendrecv = 1;
421
                        }
422
                        m = m->m_next;
423
                }
424
        }
425
        char buf[BUFSIZE];
426
        sdp_printer_t *printer = sdp_print(home, anon, buf, BUFSIZE, 0);
427
        if(sdp_message(printer)) {
428
                int retval = sdp_message_size(printer);
429
                sdp_printer_free(printer);
430
                /* FIXME Take care of the sendrecv hack */
431
                if(a_sendrecv || v_sendrecv) {
432
                        char *replace = strstr(buf, "a=inactive");
433
                        while(replace != NULL) {
434
                                memcpy(replace, "a=sendrecv", strlen("a=sendrecv"));
435
                                replace++;
436
                                replace = strstr(replace, "a=inactive");
437
                        }
438
                }
439
                JANUS_PRINT(" -------------------------------------------\n");
440
                JANUS_PRINT("  >> Anonymized (%zu --> %d bytes)\n", strlen(sdp), retval);
441
                JANUS_PRINT(" -------------------------------------------\n");
442
                JANUS_PRINT("%s\n", buf);
443
                return g_strdup(buf);
444
        } else {
445
                JANUS_DEBUG("Error anonymizing SDP: %s\n", sdp_printing_error(printer));
446
                return NULL;
447
        }
448
}
449

    
450
char *janus_sdp_merge(janus_ice_handle *handle, const char *origsdp) {
451
        if(handle == NULL || origsdp == NULL)
452
                return NULL;
453
        //~ su_home_t home[1] = { SU_HOME_INIT(home) };
454
        sdp_session_t *anon = NULL;
455
        sdp_parser_t *parser = sdp_parse(home, origsdp, strlen(origsdp), 0);
456
        if(!(anon = sdp_session(parser))) {
457
                JANUS_DEBUG("[%"SCNu64"] Error parsing/merging SDP: %s\n", handle->handle_id, sdp_parsing_error(parser));
458
                return NULL;
459
        }
460
        /* Prepare SDP to merge */
461
        gchar buffer[200];
462
        memset(buffer, 0, 200);
463
        char *sdp = (char*)calloc(BUFSIZE, sizeof(char));
464
        if(sdp == NULL) {
465
                JANUS_DEBUG("Memory error!\n");
466
                return NULL;
467
        }
468
        sdp[0] = '\0';
469
        /* Version v= */
470
        g_strlcat(sdp,
471
                "v=0\r\n", BUFSIZE);
472
        /* Origin o= */
473
        if(anon->sdp_origin) {
474
                g_sprintf(buffer,
475
                        "o=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n",        /* FIXME Should we fix the address? */
476
                                anon->sdp_origin->o_username ? anon->sdp_origin->o_username : "-",
477
                                anon->sdp_origin->o_id, anon->sdp_origin->o_version);
478
                g_strlcat(sdp, buffer, BUFSIZE);
479
        } else {
480
                gint64 sessid = janus_get_monotonic_time();
481
                gint64 version = sessid;        /* FIXME This needs to be increased when it changes, so time should be ok */
482
                g_sprintf(buffer,
483
                        "o=%s %"SCNi64" %"SCNi64" IN IP4 127.0.0.1\r\n",        /* FIXME Should we fix the address? */
484
                                "-", sessid, version);
485
                g_strlcat(sdp, buffer, BUFSIZE);
486
        }
487
        /* Session name s= */
488
        g_sprintf(buffer,
489
                "s=%s\r\n", anon->sdp_subject ? anon->sdp_subject : "Meetecho Janus");
490
        g_strlcat(sdp, buffer, BUFSIZE);
491
        /* Timing t= */
492
        g_sprintf(buffer,
493
                "t=%lu %lu\r\n", anon->sdp_time ? anon->sdp_time->t_start : 0, anon->sdp_time ? anon->sdp_time->t_stop : 0);
494
        g_strlcat(sdp, buffer, BUFSIZE);
495
        /* Any global bandwidth? */
496
        //~ if(anon->sdp_bandwidths) {
497
                //~ g_sprintf(buffer,
498
                        //~ "b=%s:%"SCNu64"\r\n",
499
                                //~ anon->sdp_bandwidths->b_modifier_name ? anon->sdp_bandwidths->b_modifier_name : "AS",
500
                                //~ anon->sdp_bandwidths->b_value);
501
                //~ g_strlcat(sdp, buffer, BUFSIZE);
502
        //~ }
503
        /* msid-semantic: add new global attribute */
504
        g_strlcat(sdp,
505
                "a=msid-semantic: WMS janus\r\n",
506
                BUFSIZE);
507
        //~ /* Connection c= (global) */
508
        //~ if(anon->sdp_connection) {
509
                //~ g_sprintf(buffer,
510
                        //~ "c=IN IP4 %s\r\n", janus_get_public_ip());
511
                //~ g_strlcat(sdp, buffer, BUFSIZE);
512
        //~ }
513
        /* DTLS fingerprint a= (global) */
514
        g_sprintf(buffer,
515
                "a=fingerprint:sha-256 %s\r\n", janus_dtls_get_local_fingerprint());
516
        g_strlcat(sdp, buffer, BUFSIZE);
517
        /* Copy other global attributes, if any */
518
        if(anon->sdp_attributes) {
519
                sdp_attribute_t *a = anon->sdp_attributes;
520
                while(a) {
521
                        if(a->a_value == NULL) {
522
                                g_sprintf(buffer,
523
                                        "a=%s\r\n", a->a_name);
524
                                g_strlcat(sdp, buffer, BUFSIZE);
525
                        } else {
526
                                g_sprintf(buffer,
527
                                        "a=%s:%s\r\n", a->a_name, a->a_value);
528
                                g_strlcat(sdp, buffer, BUFSIZE);
529
                        }
530
                        a = a->a_next;
531
                }
532
        }
533
        /* Media lines now */
534
        if(anon->sdp_media) {
535
                int audio = 0, video = 0;
536
                sdp_media_t *m = anon->sdp_media;
537
                janus_ice_stream *stream = NULL;
538
                while(m) {
539
                        if(m->m_type == sdp_media_audio) {
540
                                audio++;
541
                                if(audio > 1 || !handle->audio_id) {
542
                                        JANUS_DEBUG("[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id);
543
                                        g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE);
544
                                        m = m->m_next;
545
                                        continue;
546
                                }
547
                                /* Audio */
548
                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
549
                                if(stream == NULL) {
550
                                        JANUS_DEBUG("[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
551
                                        g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE);
552
                                        m = m->m_next;
553
                                        continue;
554
                                }
555
                                g_strlcat(sdp, "m=audio ARTPP RTP/SAVPF", BUFSIZE);
556
                        } else if(m->m_type == sdp_media_video) {
557
                                video++;
558
                                if(video > 1 || !handle->video_id) {
559
                                        JANUS_DEBUG("[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video, handle->video_id);
560
                                        g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE);
561
                                        m = m->m_next;
562
                                        continue;
563
                                }
564
                                /* Video */
565
                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
566
                                if(stream == NULL) {
567
                                        JANUS_DEBUG("[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
568
                                        g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE);
569
                                        m = m->m_next;
570
                                        continue;
571
                                }
572
                                g_strlcat(sdp, "m=video VRTPP RTP/SAVPF", BUFSIZE);
573
                        } else {
574
                                JANUS_DEBUG("[%"SCNu64"] Skipping unsupported media line...\n", handle->handle_id);
575
                                g_sprintf(buffer,
576
                                        "m=%s 0 %s 0\r\n",
577
                                        m->m_type_name, m->m_proto_name);
578
                                g_strlcat(sdp, buffer, BUFSIZE);
579
                                m = m->m_next;
580
                                continue;
581
                        }
582
                        /* Add formats now */
583
                        if(!m->m_rtpmaps) {
584
                                JANUS_PRINT("[%"SCNu64"] No RTP maps?? trying formats...\n", handle->handle_id);
585
                                if(!m->m_format) {
586
                                        JANUS_DEBUG("[%"SCNu64"] No formats either?? this sucks!\n", handle->handle_id);
587
                                        g_strlcat(sdp, " 0", BUFSIZE);        /* FIXME Won't work apparently */
588
                                } else {
589
                                        sdp_list_t *fmt = m->m_format;
590
                                        while(fmt) {
591
                                                g_sprintf(buffer, " %s", fmt->l_text);
592
                                                g_strlcat(sdp, buffer, BUFSIZE);
593
                                                fmt = fmt->l_next;
594
                                        }
595
                                }
596
                        } else {
597
                                sdp_rtpmap_t *r = m->m_rtpmaps;
598
                                while(r) {
599
                                        g_sprintf(buffer, " %d", r->rm_pt);
600
                                        g_strlcat(sdp, buffer, BUFSIZE);
601
                                        r = r->rm_next;
602
                                }
603
                        }
604
                        g_strlcat(sdp, "\r\n", BUFSIZE);
605
                        /* Any bandwidth? */
606
                        if(m->m_bandwidths) {
607
                                g_sprintf(buffer,
608
                                        "b=%s:%lu\r\n",        /* FIXME Are we doing this correctly? */
609
                                                m->m_bandwidths->b_modifier_name ? m->m_bandwidths->b_modifier_name : "AS",
610
                                                m->m_bandwidths->b_value);
611
                                g_strlcat(sdp, buffer, BUFSIZE);
612
                        }
613
                        /* Media connection c= */
614
                        //~ if(m->m_connections) {
615
                                g_sprintf(buffer,
616
                                        "c=IN IP4 %s\r\n", janus_get_public_ip());
617
                                g_strlcat(sdp, buffer, BUFSIZE);
618
                        //~ }
619
                        /* What is the direction? */
620
                        switch(m->m_mode) {
621
                                case sdp_inactive:
622
                                        g_strlcat(sdp, "a=inactive\r\n", BUFSIZE);
623
                                        break;
624
                                case sdp_sendonly:
625
                                        g_strlcat(sdp, "a=sendonly\r\n", BUFSIZE);
626
                                        break;
627
                                case sdp_recvonly:
628
                                        g_strlcat(sdp, "a=recvonly\r\n", BUFSIZE);
629
                                        break;
630
                                case sdp_sendrecv:
631
                                default:
632
                                        g_strlcat(sdp, "a=sendrecv\r\n", BUFSIZE);
633
                                        break;
634
                        }
635
                        /* RTCP */
636
                        g_sprintf(buffer, "a=rtcp:%s IN IP4 %s\r\n",
637
                                m->m_type == sdp_media_audio ? "ARTCP" : "VRTCP", janus_get_public_ip());
638
                        g_strlcat(sdp, buffer, BUFSIZE);
639
                        /* RTP maps */
640
                        if(m->m_rtpmaps) {
641
                                sdp_rtpmap_t *rm = NULL;
642
                                for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
643
                                        g_sprintf(buffer, "a=rtpmap:%u %s/%lu%s%s\r\n",
644
                                                rm->rm_pt, rm->rm_encoding, rm->rm_rate,
645
                                                rm->rm_params ? "/" : "", 
646
                                                rm->rm_params ? rm->rm_params : "");
647
                                        g_strlcat(sdp, buffer, BUFSIZE);
648
                                }
649
                                for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
650
                                        if(rm->rm_fmtp) {
651
                                                g_sprintf(buffer, "a=fmtp:%u %s\r\n", rm->rm_pt, rm->rm_fmtp);
652
                                                g_strlcat(sdp, buffer, BUFSIZE);
653
                                        }
654
                                }
655
                        }
656
                        /* ICE ufrag and pwd, DTLS setup and connection a= */
657
                        gchar *ufrag = NULL;
658
                        gchar *password = NULL;
659
                        nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
660
                        memset(buffer, 0, 100);
661
                        g_sprintf(buffer,
662
                                "a=ice-ufrag:%s\r\n"
663
                                "a=ice-pwd:%s\r\n"
664
                                "a=setup:%s\r\n"
665
                                "a=connection:new\r\n",
666
                                        ufrag, password,
667
                                        janus_get_dtls_srtp_role(stream->dtls_role));
668
                        g_strlcat(sdp, buffer, BUFSIZE);
669
                        /* Copy existing media attributes, if any */
670
                        if(m->m_attributes) {
671
                                sdp_attribute_t *a = m->m_attributes;
672
                                while(a) {
673
                                        if(a->a_value == NULL) {
674
                                                g_sprintf(buffer,
675
                                                        "a=%s\r\n", a->a_name);
676
                                                g_strlcat(sdp, buffer, BUFSIZE);
677
                                        } else {
678
                                                g_sprintf(buffer,
679
                                                        "a=%s:%s\r\n", a->a_name, a->a_value);
680
                                                g_strlcat(sdp, buffer, BUFSIZE);
681
                                        }
682
                                        a = a->a_next;
683
                                }
684
                        }
685
                        /* Add last attributes, rtcp and ssrc (msid) */
686
                        if(m->m_type == sdp_media_audio) {
687
                                g_sprintf(buffer,
688
                                        //~ "a=rtcp:ARTCP IN IP4 %s\r\n"
689
                                        "a=ssrc:%i cname:janusaudio\r\n"
690
                                        "a=ssrc:%i msid:janus janusa0\r\n"
691
                                        "a=ssrc:%i mslabel:janus\r\n"
692
                                        "a=ssrc:%i label:janusa0\r\n",
693
                                                //~ janus_get_public_ip(),
694
                                                stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc);
695
                                g_strlcat(sdp, buffer, BUFSIZE);
696
                        } else if(m->m_type == sdp_media_video) {
697
                                g_sprintf(buffer,
698
                                        //~ "a=rtcp:VRTCP IN IP4 %s\r\n"
699
                                        "a=ssrc:%i cname:janusvideo\r\n"
700
                                        "a=ssrc:%i msid:janus janusv0\r\n"
701
                                        "a=ssrc:%i mslabel:janus\r\n"
702
                                        "a=ssrc:%i label:janusv0\r\n",
703
                                                //~ janus_get_public_ip(),
704
                                                stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc);
705
                                g_strlcat(sdp, buffer, BUFSIZE);
706
                        }
707
                        /* And now the candidates */
708
                        janus_ice_setup_candidate(handle, sdp, stream->stream_id, 1);
709
                        janus_ice_setup_candidate(handle, sdp, stream->stream_id, 2);
710
                        /* Next */
711
                        m = m->m_next;
712
                }
713
        }
714
        JANUS_PRINT(" -------------------------------------------\n");
715
        JANUS_PRINT("  >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp));
716
        JANUS_PRINT(" -------------------------------------------\n");
717
        JANUS_PRINT("%s\n", sdp);
718
        return sdp;
719
}