Statistics
| Branch: | Revision:

janus-gateway / sdp-utils.c @ 5de69b28

History | View | Annotate | Download (37.3 KB)

1
/*! \file    sdp-utils.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU General Public License v3
4
 * \brief    SDP utilities
5
 * \details  Implementation of an internal SDP representation. Allows
6
 * to parse SDP strings to an internal janus_sdp object, the manipulation
7
 * of such object by playing with its properties, and a serialization
8
 * to an SDP string that can be passed around. Since they don't have any
9
 * core dependencies, these utilities can be used by plugins as well.
10
 * 
11
 * \ingroup core
12
 * \ref core
13
 */
14

    
15
#include <string.h>
16
 
17
#include "sdp-utils.h"
18
#include "utils.h"
19
#include "debug.h"
20

    
21
#define JANUS_BUFSIZE        8192
22

    
23
void janus_sdp_free(janus_sdp *sdp) {
24
        if(!sdp)
25
                return;
26
        g_free(sdp->o_name);
27
        g_free(sdp->o_addr);
28
        g_free(sdp->s_name);
29
        g_free(sdp->c_addr);
30
        GList *temp = sdp->attributes;
31
        while(temp) {
32
                janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
33
                janus_sdp_attribute_destroy(a);
34
                temp = temp->next;
35
        }
36
        g_list_free(sdp->attributes);
37
        sdp->attributes = NULL;
38
        temp = sdp->m_lines;
39
        while(temp) {
40
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
41
                janus_sdp_mline_destroy(m);
42
                temp = temp->next;
43
        }
44
        g_list_free(sdp->m_lines);
45
        sdp->m_lines = NULL;
46
        g_free(sdp);
47
}
48

    
49
janus_sdp_mline *janus_sdp_mline_create(janus_sdp_mtype type, guint16 port, const char *proto, janus_sdp_mdirection direction) {
50
        janus_sdp_mline *m = g_malloc0(sizeof(janus_sdp_mline));
51
        m->type = type;
52
        const char *type_str = janus_sdp_mtype_str(type);
53
        if(type_str == NULL) {
54
                JANUS_LOG(LOG_WARN, "Unknown media type, type_str will have to be set manually\n");
55
        } else {
56
                m->type_str = g_strdup(type_str);
57
        }
58
        m->port = port;
59
        m->proto = proto ? g_strdup(proto) : NULL;
60
        m->direction = direction;
61
        return m;
62
}
63

    
64
void janus_sdp_mline_destroy(janus_sdp_mline *mline) {
65
        if(!mline)
66
                return;
67
        g_free(mline->type_str);
68
        g_free(mline->proto);
69
        g_free(mline->c_addr);
70
        g_free(mline->b_name);
71
        g_list_free_full(mline->fmts, (GDestroyNotify)g_free);
72
        mline->fmts = NULL;
73
        g_list_free(mline->ptypes);
74
        mline->ptypes = NULL;
75
        GList *temp = mline->attributes;
76
        while(temp) {
77
                janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
78
                janus_sdp_attribute_destroy(a);
79
                temp = temp->next;
80
        }
81
        g_list_free(mline->attributes);
82
        g_free(mline);
83
}
84

    
85
janus_sdp_mline *janus_sdp_mline_find(janus_sdp *sdp, janus_sdp_mtype type) {
86
        if(sdp == NULL)
87
                return NULL;
88
        GList *ml = sdp->m_lines;
89
        while(ml) {
90
                janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
91
                if(m->type == type)
92
                        return m;
93
                ml = ml->next;
94
        }
95
        return NULL;
96
}
97

    
98
janus_sdp_attribute *janus_sdp_attribute_create(const char *name, const char *value, ...) {
99
        if(!name)
100
                return NULL;
101
        janus_sdp_attribute *a = g_malloc0(sizeof(janus_sdp_attribute));
102
        a->name = g_strdup(name);
103
        a->direction = JANUS_SDP_DEFAULT;
104
        if(value) {
105
                char buffer[512];
106
                va_list ap;
107
                va_start(ap, value);
108
                g_vsnprintf(buffer, sizeof(buffer), value, ap);
109
                va_end(ap);
110
                a->value = g_strdup(buffer);
111
        }
112
        return a;
113
}
114

    
115
void janus_sdp_attribute_destroy(janus_sdp_attribute *attr) {
116
        if(!attr)
117
                return;
118
        g_free(attr->name);
119
        g_free(attr->value);
120
        g_free(attr);
121
}
122

    
123
int janus_sdp_attribute_add_to_mline(janus_sdp_mline *mline, janus_sdp_attribute *attr) {
124
        if(!mline || !attr)
125
                return -1;
126
        mline->attributes = g_list_append(mline->attributes, attr);
127
        return 0;
128
}
129

    
130
janus_sdp_mtype janus_sdp_parse_mtype(const char *type) {
131
        if(type == NULL)
132
                return JANUS_SDP_OTHER;
133
        if(!strcasecmp(type, "audio"))
134
                return JANUS_SDP_AUDIO;
135
        if(!strcasecmp(type, "video"))
136
                return JANUS_SDP_VIDEO;
137
        if(!strcasecmp(type, "application"))
138
                return JANUS_SDP_APPLICATION;
139
        return JANUS_SDP_OTHER;
140
}
141

    
142
const char *janus_sdp_mtype_str(janus_sdp_mtype type) {
143
        switch(type) {
144
                case JANUS_SDP_AUDIO:
145
                        return "audio";
146
                case JANUS_SDP_VIDEO:
147
                        return "video";
148
                case JANUS_SDP_APPLICATION:
149
                        return "application";
150
                case JANUS_SDP_OTHER:
151
                default:
152
                        break;
153
        }
154
        return NULL;
155
}
156

    
157
janus_sdp_mdirection janus_sdp_parse_mdirection(const char *direction) {
158
        if(direction == NULL)
159
                return JANUS_SDP_INVALID;
160
        if(!strcasecmp(direction, "sendrecv"))
161
                return JANUS_SDP_SENDRECV;
162
        if(!strcasecmp(direction, "sendonly"))
163
                return JANUS_SDP_SENDONLY;
164
        if(!strcasecmp(direction, "recvonly"))
165
                return JANUS_SDP_RECVONLY;
166
        if(!strcasecmp(direction, "inactive"))
167
                return JANUS_SDP_INACTIVE;
168
        return JANUS_SDP_INVALID;
169
}
170

    
171
const char *janus_sdp_mdirection_str(janus_sdp_mdirection direction) {
172
        switch(direction) {
173
                case JANUS_SDP_DEFAULT:
174
                case JANUS_SDP_SENDRECV:
175
                        return "sendrecv";
176
                case JANUS_SDP_SENDONLY:
177
                        return "sendonly";
178
                case JANUS_SDP_RECVONLY:
179
                        return "recvonly";
180
                case JANUS_SDP_INACTIVE:
181
                        return "inactive";
182
                case JANUS_SDP_INVALID:
183
                default:
184
                        break;
185
        }
186
        return NULL;
187
}
188

    
189
janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) {
190
        if(!sdp)
191
                return NULL;
192
        if(strstr(sdp, "v=") != sdp) {
193
                if(error)
194
                        g_snprintf(error, errlen, "Invalid SDP (doesn't start with v=)");
195
                return NULL;
196
        }
197
        janus_sdp *imported = g_malloc0(sizeof(janus_sdp));
198
        imported->o_ipv4 = TRUE;
199
        imported->c_ipv4 = TRUE;
200

    
201
        gboolean success = TRUE;
202
        janus_sdp_mline *mline = NULL;
203

    
204
        gchar **parts = g_strsplit(sdp, "\r\n", -1);
205
        if(parts) {
206
                int index = 0;
207
                char *line = NULL;
208
                while(success && (line = parts[index]) != NULL) {
209
                        if(*line == '\0') {
210
                                index++;
211
                                continue;
212
                        }
213
                        if(strlen(line) < 3) {
214
                                if(error)
215
                                        g_snprintf(error, errlen, "Invalid line (%zu bytes): %s", strlen(line), line);
216
                                success = FALSE;
217
                                break;
218
                        }
219
                        if(*(line+1) != '=') {
220
                                if(error)
221
                                        g_snprintf(error, errlen, "Invalid line (2nd char is not '='): %s", line);
222
                                success = FALSE;
223
                                break;
224
                        }
225
                        char c = *line;
226
                        if(mline == NULL) {
227
                                /* Global stuff */
228
                                switch(c) {
229
                                        case 'v': {
230
                                                if(sscanf(line, "v=%d", &imported->version) != 1) {
231
                                                        if(error)
232
                                                                g_snprintf(error, errlen, "Invalid v= line: %s", line);
233
                                                        success = FALSE;
234
                                                        break;
235
                                                }
236
                                                break;
237
                                        }
238
                                        case 'o': {
239
                                                char name[256], addrtype[6], addr[256];
240
                                                if(sscanf(line, "o=%255s %"SCNu64" %"SCNu64" IN %5s %255s",
241
                                                                name, &imported->o_sessid, &imported->o_version, addrtype, addr) != 5) {
242
                                                        if(error)
243
                                                                g_snprintf(error, errlen, "Invalid o= line: %s", line);
244
                                                        success = FALSE;
245
                                                        break;
246
                                                }
247
                                                if(!strcasecmp(addrtype, "IP4"))
248
                                                        imported->o_ipv4 = TRUE;
249
                                                else if(!strcasecmp(addrtype, "IP6"))
250
                                                        imported->o_ipv4 = FALSE;
251
                                                else {
252
                                                        if(error)
253
                                                                g_snprintf(error, errlen, "Invalid o= line (unsupported protocol %s): %s", addrtype, line);
254
                                                        success = FALSE;
255
                                                        break;
256
                                                }
257
                                                imported->o_name = g_strdup(name);
258
                                                imported->o_addr = g_strdup(addr);
259
                                                break;
260
                                        }
261
                                        case 's': {
262
                                                imported->s_name = g_strdup(line+2);
263
                                                break;
264
                                        }
265
                                        case 't': {
266
                                                if(sscanf(line, "t=%"SCNu64" %"SCNu64, &imported->t_start, &imported->t_stop) != 2) {
267
                                                        if(error)
268
                                                                g_snprintf(error, errlen, "Invalid t= line: %s", line);
269
                                                        success = FALSE;
270
                                                        break;
271
                                                }
272
                                                break;
273
                                        }
274
                                        case 'c': {
275
                                                char addrtype[6], addr[256];
276
                                                if(sscanf(line, "c=IN %5s %255s", addrtype, addr) != 2) {
277
                                                        if(error)
278
                                                                g_snprintf(error, errlen, "Invalid c= line: %s", line);
279
                                                        success = FALSE;
280
                                                        break;
281
                                                }
282
                                                if(!strcasecmp(addrtype, "IP4"))
283
                                                        imported->c_ipv4 = TRUE;
284
                                                else if(!strcasecmp(addrtype, "IP6"))
285
                                                        imported->c_ipv4 = FALSE;
286
                                                else {
287
                                                        if(error)
288
                                                                g_snprintf(error, errlen, "Invalid c= line (unsupported protocol %s): %s", addrtype, line);
289
                                                        success = FALSE;
290
                                                        break;
291
                                                }
292
                                                imported->c_addr = g_strdup(addr);
293
                                                break;
294
                                        }
295
                                        case 'a': {
296
                                                janus_sdp_attribute *a = g_malloc0(sizeof(janus_sdp_attribute));
297
                                                line += 2;
298
                                                char *semicolon = strchr(line, ':');
299
                                                if(semicolon == NULL) {
300
                                                        a->name = g_strdup(line);
301
                                                        a->value = NULL;
302
                                                } else {
303
                                                        if(*(semicolon+1) == '\0') {
304
                                                                if(error)
305
                                                                        g_snprintf(error, errlen, "Invalid a= line: %s", line);
306
                                                                success = FALSE;
307
                                                                break;
308
                                                        }
309
                                                        *semicolon = '\0';
310
                                                        a->name = g_strdup(line);
311
                                                        a->value = g_strdup(semicolon+1);
312
                                                        a->direction = JANUS_SDP_DEFAULT;
313
                                                        *semicolon = ':';
314
                                                        if(strstr(line, "/sendonly"))
315
                                                                a->direction = JANUS_SDP_SENDONLY;
316
                                                        else if(strstr(line, "/recvonly"))
317
                                                                a->direction = JANUS_SDP_RECVONLY;
318
                                                        if(strstr(line, "/inactive"))
319
                                                                a->direction = JANUS_SDP_INACTIVE;
320
                                                }
321
                                                imported->attributes = g_list_append(imported->attributes, a);
322
                                                break;
323
                                        }
324
                                        case 'm': {
325
                                                janus_sdp_mline *m = g_malloc0(sizeof(janus_sdp_mline));
326
                                                /* Start with media type, port and protocol */
327
                                                char type[32];
328
                                                char proto[64];
329
                                                if(sscanf(line, "m=%31s %"SCNu16" %63s %*s", type, &m->port, proto) != 3) {
330
                                                        if(error)
331
                                                                g_snprintf(error, errlen, "Invalid m= line: %s", line);
332
                                                        success = FALSE;
333
                                                        break;
334
                                                }
335
                                                m->type = janus_sdp_parse_mtype(type);
336
                                                m->type_str = g_strdup(type);
337
                                                m->proto = g_strdup(proto);
338
                                                m->direction = JANUS_SDP_SENDRECV;
339
                                                m->c_ipv4 = TRUE;
340
                                                if(m->port > 0) {
341
                                                        /* Now let's check the payload types/formats */
342
                                                        gchar **mline_parts = g_strsplit(line+2, " ", -1);
343
                                                        if(!mline_parts) {
344
                                                                if(error)
345
                                                                        g_snprintf(error, errlen, "Invalid m= line (no payload types/formats): %s", line);
346
                                                                success = FALSE;
347
                                                                break;
348
                                                        }
349
                                                        int mindex = 0;
350
                                                        while(mline_parts[mindex]) {
351
                                                                if(mindex < 3) {
352
                                                                        /* We've parsed these before */
353
                                                                        mindex++;
354
                                                                        continue;
355
                                                                }
356
                                                                /* Add string fmt */
357
                                                                m->fmts = g_list_append(m->fmts, g_strdup(mline_parts[mindex]));
358
                                                                /* Add numeric payload type */
359
                                                                int ptype = atoi(mline_parts[mindex]);
360
                                                                m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(ptype));
361
                                                                mindex++;
362
                                                        }
363
                                                        g_strfreev(mline_parts);
364
                                                        if(m->fmts == NULL || m->ptypes == NULL) {
365
                                                                if(error)
366
                                                                        g_snprintf(error, errlen, "Invalid m= line (no payload types/formats): %s", line);
367
                                                                success = FALSE;
368
                                                                break;
369
                                                        }
370
                                                }
371
                                                /* Append to the list of m-lines */
372
                                                imported->m_lines = g_list_append(imported->m_lines, m);
373
                                                /* From now on, we parse this m-line */
374
                                                mline = m;
375
                                                break;
376
                                        }
377
                                        default:
378
                                                JANUS_LOG(LOG_WARN, "Ignoring '%c' property\n", c);
379
                                                break;
380
                                }
381
                        } else {
382
                                /* m-line stuff */
383
                                switch(c) {
384
                                        case 'c': {
385
                                                char addrtype[6], addr[256];
386
                                                if(sscanf(line, "c=IN %5s %255s", addrtype, addr) != 2) {
387
                                                        if(error)
388
                                                                g_snprintf(error, errlen, "Invalid c= line: %s", line);
389
                                                        success = FALSE;
390
                                                        break;
391
                                                }
392
                                                if(!strcasecmp(addrtype, "IP4"))
393
                                                        mline->c_ipv4 = TRUE;
394
                                                else if(!strcasecmp(addrtype, "IP6"))
395
                                                        mline->c_ipv4 = FALSE;
396
                                                else {
397
                                                        if(error)
398
                                                                g_snprintf(error, errlen, "Invalid c= line (unsupported protocol %s): %s", addrtype, line);
399
                                                        success = FALSE;
400
                                                        break;
401
                                                }
402
                                                mline->c_addr = g_strdup(addr);
403
                                                break;
404
                                        }
405
                                        case 'b': {
406
                                                line += 2;
407
                                                char *semicolon = strchr(line, ':');
408
                                                if(semicolon == NULL || (*(semicolon+1) == '\0')) {
409
                                                        if(error)
410
                                                                g_snprintf(error, errlen, "Invalid b= line: %s", line);
411
                                                        success = FALSE;
412
                                                        break;
413
                                                }
414
                                                *semicolon = '\0';
415
                                                mline->b_name = g_strdup(line);
416
                                                mline->b_value = atoi(semicolon+1);
417
                                                *semicolon = ':';
418
                                                break;
419
                                        }
420
                                        case 'a': {
421
                                                janus_sdp_attribute *a = g_malloc0(sizeof(janus_sdp_attribute));
422
                                                line += 2;
423
                                                char *semicolon = strchr(line, ':');
424
                                                if(semicolon == NULL) {
425
                                                        /* Is this a media direction attribute? */
426
                                                        janus_sdp_mdirection direction = janus_sdp_parse_mdirection(line);
427
                                                        if(direction != JANUS_SDP_INVALID) {
428
                                                                g_free(a);
429
                                                                mline->direction = direction;
430
                                                                break;
431
                                                        }
432
                                                        a->name = g_strdup(line);
433
                                                        a->value = NULL;
434
                                                } else {
435
                                                        if(*(semicolon+1) == '\0') {
436
                                                                if(error)
437
                                                                        g_snprintf(error, errlen, "Invalid a= line: %s", line);
438
                                                                success = FALSE;
439
                                                                break;
440
                                                        }
441
                                                        *semicolon = '\0';
442
                                                        a->name = g_strdup(line);
443
                                                        a->value = g_strdup(semicolon+1);
444
                                                        a->direction = JANUS_SDP_DEFAULT;
445
                                                        *semicolon = ':';
446
                                                        if(strstr(line, "/sendonly"))
447
                                                                a->direction = JANUS_SDP_SENDONLY;
448
                                                        else if(strstr(line, "/recvonly"))
449
                                                                a->direction = JANUS_SDP_RECVONLY;
450
                                                        if(strstr(line, "/inactive"))
451
                                                                a->direction = JANUS_SDP_INACTIVE;
452
                                                }
453
                                                mline->attributes = g_list_append(mline->attributes, a);
454
                                                break;
455
                                        }
456
                                        case 'm': {
457
                                                /* Current m-line ended, back to global parsing */
458
                                                mline = NULL;
459
                                                continue;
460
                                        }
461
                                        default:
462
                                                JANUS_LOG(LOG_WARN, "Ignoring '%c' property (m-line)\n", c);
463
                                                break;
464
                                }
465
                        }
466
                        index++;
467
                }
468
                g_strfreev(parts);
469
        }
470
        /* FIXME Do a last check: is all the stuff that's supposed to be there available? */
471
        if(imported->o_name == NULL || imported->o_addr == NULL || imported->s_name == NULL || imported->m_lines == NULL) {
472
                success = FALSE;
473
                if(error)
474
                        g_snprintf(error, errlen, "Missing mandatory lines (o=, s= or m=)");
475
        }
476
        /* If something wrong happened, free and return a failure */
477
        if(!success) {
478
                if(error)
479
                        JANUS_LOG(LOG_ERR, "%s\n", error);
480
                janus_sdp_free(imported);
481
                imported = NULL;
482
        }
483
        return imported;
484
}
485

    
486
int janus_sdp_remove_payload_type(janus_sdp *sdp, int pt) {
487
        if(!sdp || pt < 0)
488
                return -1;
489
        GList *ml = sdp->m_lines;
490
        while(ml) {
491
                janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
492
                /* Remove any reference from the m-line */
493
                m->ptypes = g_list_remove(m->ptypes, GINT_TO_POINTER(pt));
494
                /* Also remove all attributes that reference the same payload type */
495
                GList *ma = m->attributes;
496
                while(ma) {
497
                        janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
498
                        if(atoi(a->value) == pt) {
499
                                m->attributes = g_list_remove(m->attributes, a);
500
                                ma = m->attributes;
501
                                janus_sdp_attribute_destroy(a);
502
                                continue;
503
                        }
504
                        ma = ma->next;
505
                }
506
                ml = ml->next;
507
        }
508
        return 0;
509
}
510

    
511
int janus_sdp_get_codec_pt(janus_sdp *sdp, const char *codec) {
512
        if(sdp == NULL || codec == NULL)
513
                return -1;
514
        /* Check the format string (note that we only parse what browsers can negotiate) */
515
        gboolean video = FALSE;
516
        const char *format = NULL, *format2 = NULL;
517
        if(!strcasecmp(codec, "opus")) {
518
                format = "opus/48000/2";
519
                format2 = "OPUS/48000/2";
520
        } else if(!strcasecmp(codec, "pcmu")) {
521
                /* We know the payload type is 0: we just need to make sure it's there */
522
                format = "pcmu/8000";
523
                format2 = "PCMU/8000";
524
        } else if(!strcasecmp(codec, "pcma")) {
525
                /* We know the payload type is 8: we just need to make sure it's there */
526
                format = "pcma/8000";
527
                format2 = "PCMA/8000";
528
        } else if(!strcasecmp(codec, "g722")) {
529
                /* We know the payload type is 9: we just need to make sure it's there */
530
                format = "g722/8000";
531
                format2 = "G722/8000";
532
        } else if(!strcasecmp(codec, "isac16")) {
533
                format = "isac/16000";
534
                format2 = "ISAC/16000";
535
        } else if(!strcasecmp(codec, "isac32")) {
536
                format = "isac/32000";
537
                format2 = "ISAC/32000";
538
        } else if(!strcasecmp(codec, "dtmf")) {
539
                format = "telephone-event/8000";
540
                format2 = "TELEPHONE-EVENT/8000";
541
        } else if(!strcasecmp(codec, "vp8")) {
542
                video = TRUE;
543
                format = "vp8/90000";
544
                format2 = "VP8/90000";
545
        } else if(!strcasecmp(codec, "vp9")) {
546
                video = TRUE;
547
                format = "vp9/90000";
548
                format2 = "VP9/90000";
549
        } else if(!strcasecmp(codec, "h264")) {
550
                video = TRUE;
551
                format = "h264/90000";
552
                format2 = "H264/90000";
553
        } else {
554
                JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
555
                return -1;
556
        }
557
        /* Check all m->lines */
558
        GList *ml = sdp->m_lines;
559
        while(ml) {
560
                janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
561
                if((!video && m->type != JANUS_SDP_AUDIO) || (video && m->type != JANUS_SDP_VIDEO)) {
562
                        ml = ml->next;
563
                        continue;
564
                }
565
                /* Look in all rtpmap attributes */
566
                GList *ma = m->attributes;
567
                while(ma) {
568
                        janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
569
                        if(a->name != NULL && a->value != NULL && !strcasecmp(a->name, "rtpmap")) {
570
                                int pt = atoi(a->value);
571
                                if(strstr(a->value, format) || strstr(a->value, format2))
572
                                        return pt;
573
                        }
574
                        ma = ma->next;
575
                }
576
                ml = ml->next;
577
        }
578
        return -1;
579
}
580

    
581
const char *janus_sdp_get_codec_name(janus_sdp *sdp, int pt) {
582
        if(sdp == NULL || pt < 0)
583
                return NULL;
584
        if(pt == 0)
585
                return "pcmu";
586
        if(pt == 8)
587
                return "pcma";
588
        if(pt == 9)
589
                return "g722";
590
        GList *ml = sdp->m_lines;
591
        while(ml) {
592
                janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
593
                /* Look in all rtpmap attributes */
594
                GList *ma = m->attributes;
595
                while(ma) {
596
                        janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
597
                        if(a->name != NULL && a->value != NULL && !strcasecmp(a->name, "rtpmap")) {
598
                                int a_pt = atoi(a->value);
599
                                if(a_pt == pt) {
600
                                        /* Found! */
601
                                        if(strstr(a->value, "vp8") || strstr(a->value, "VP8"))
602
                                                return "vp8";
603
                                        if(strstr(a->value, "vp9") || strstr(a->value, "VP9"))
604
                                                return "vp9";
605
                                        if(strstr(a->value, "h264") || strstr(a->value, "H264"))
606
                                                return "h264";
607
                                        if(strstr(a->value, "opus") || strstr(a->value, "OPUS"))
608
                                                return "opus";
609
                                        if(strstr(a->value, "pcmu") || strstr(a->value, "PMCU"))
610
                                                return "pcmu";
611
                                        if(strstr(a->value, "pcma") || strstr(a->value, "PMCA"))
612
                                                return "pcma";
613
                                        if(strstr(a->value, "g722") || strstr(a->value, "G722"))
614
                                                return "g722";
615
                                        if(strstr(a->value, "isac/16") || strstr(a->value, "ISAC/16"))
616
                                                return "isac16";
617
                                        if(strstr(a->value, "isac/32") || strstr(a->value, "ISAC/32"))
618
                                                return "isac32";
619
                                        if(strstr(a->value, "telephone-event/8000") || strstr(a->value, "telephone-event/8000"))
620
                                                return "dtmf";
621
                                        JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", a->value);
622
                                        return NULL;
623
                                }
624
                        }
625
                        ma = ma->next;
626
                }
627
                ml = ml->next;
628
        }
629
        return NULL;
630
}
631

    
632
const char *janus_sdp_get_codec_rtpmap(const char *codec) {
633
        if(codec == NULL)
634
                return NULL;
635
        if(!strcasecmp(codec, "opus"))
636
                return "opus/48000/2";
637
        if(!strcasecmp(codec, "pcmu"))
638
                return "PCMU/8000";
639
        if(!strcasecmp(codec, "pcma"))
640
                return "PCMA/8000";
641
        if(!strcasecmp(codec, "g722"))
642
                return "G722/8000";
643
        if(!strcasecmp(codec, "isac16"))
644
                return "ISAC/16000";
645
        if(!strcasecmp(codec, "isac32"))
646
                return "ISAC/32000";
647
        if(!strcasecmp(codec, "dtmf"))
648
                return "telephone-event/8000";
649
        if(!strcasecmp(codec, "vp8"))
650
                return "VP8/90000";
651
        if(!strcasecmp(codec, "vp9"))
652
                return "VP9/90000";
653
        if(!strcasecmp(codec, "h264"))
654
                return "H264/90000";
655
        JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
656
        return NULL;
657
}
658

    
659
char *janus_sdp_write(janus_sdp *imported) {
660
        if(!imported)
661
                return NULL;
662
        gboolean success = TRUE;
663
        char *sdp = g_malloc0(JANUS_BUFSIZE), buffer[512];
664
        *sdp = '\0';
665
        /* v= */
666
        g_snprintf(buffer, sizeof(buffer), "v=%d\r\n", imported->version);
667
        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
668
        /* o= */
669
        g_snprintf(buffer, sizeof(buffer), "o=%s %"SCNu64" %"SCNu64" IN %s %s\r\n",
670
                imported->o_name, imported->o_sessid, imported->o_version,
671
                imported->o_ipv4 ? "IP4" : "IP6", imported->o_addr);
672
        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
673
        /* s= */
674
        g_snprintf(buffer, sizeof(buffer), "s=%s\r\n", imported->s_name);
675
        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
676
        /* t= */
677
        g_snprintf(buffer, sizeof(buffer), "t=%"SCNu64" %"SCNu64"\r\n", imported->t_start, imported->t_stop);
678
        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
679
        /* c= */
680
        if(imported->c_addr != NULL) {
681
                g_snprintf(buffer, sizeof(buffer), "c=IN %s %s\r\n",
682
                        imported->c_ipv4 ? "IP4" : "IP6", imported->c_addr);
683
                g_strlcat(sdp, buffer, JANUS_BUFSIZE);
684
        }
685
        /* a= */
686
        GList *temp = imported->attributes;
687
        while(temp) {
688
                janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
689
                if(a->value != NULL) {
690
                        g_snprintf(buffer, sizeof(buffer), "a=%s:%s\r\n", a->name, a->value);
691
                } else {
692
                        g_snprintf(buffer, sizeof(buffer), "a=%s\r\n", a->name);
693
                }
694
                g_strlcat(sdp, buffer, JANUS_BUFSIZE);
695
                temp = temp->next;
696
        }
697
        /* m= */
698
        temp = imported->m_lines;
699
        while(temp) {
700
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
701
                g_snprintf(buffer, sizeof(buffer), "m=%s %d %s", m->type_str, m->port, m->proto);
702
                g_strlcat(sdp, buffer, JANUS_BUFSIZE);
703
                if(m->port == 0) {
704
                        /* Remove all payload types/formats if we're rejecting the media */
705
                        g_list_free_full(m->fmts, (GDestroyNotify)g_free);
706
                        m->fmts = NULL;
707
                        g_list_free(m->ptypes);
708
                        m->ptypes = NULL;
709
                        m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(0));
710
                        g_strlcat(sdp, " 0", JANUS_BUFSIZE);
711
                } else {
712
                        if(m->proto != NULL && strstr(m->proto, "RTP") != NULL) {
713
                                /* RTP profile, use payload types */
714
                                GList *ptypes = m->ptypes;
715
                                while(ptypes) {
716
                                        g_snprintf(buffer, sizeof(buffer), " %d", GPOINTER_TO_INT(ptypes->data));
717
                                        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
718
                                        ptypes = ptypes->next;
719
                                }
720
                        } else {
721
                                /* Something else, use formats */
722
                                GList *fmts = m->fmts;
723
                                while(fmts) {
724
                                        g_snprintf(buffer, sizeof(buffer), " %s", (char *)(fmts->data));
725
                                        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
726
                                        fmts = fmts->next;
727
                                }
728
                        }
729
                }
730
                g_strlcat(sdp, "\r\n", JANUS_BUFSIZE);
731
                /* c= */
732
                if(m->c_addr != NULL) {
733
                        g_snprintf(buffer, sizeof(buffer), "c=IN %s %s\r\n",
734
                                m->c_ipv4 ? "IP4" : "IP6", m->c_addr);
735
                        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
736
                }
737
                if(m->port > 0) {
738
                        /* b= */
739
                        if(m->b_name != NULL) {
740
                                g_snprintf(buffer, sizeof(buffer), "b=%s:%d\r\n", m->b_name, m->b_value);
741
                                g_strlcat(sdp, buffer, JANUS_BUFSIZE);
742
                        }
743
                }
744
                /* a= (note that we don't format the direction if it's JANUS_SDP_DEFAULT) */
745
                const char *direction = m->direction != JANUS_SDP_DEFAULT ? janus_sdp_mdirection_str(m->direction) : NULL;
746
                if(direction != NULL) {
747
                        g_snprintf(buffer, sizeof(buffer), "a=%s\r\n", direction);
748
                        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
749
                }
750
                if(m->port == 0) {
751
                        /* No point going on */
752
                        temp = temp->next;
753
                        continue;
754
                }
755
                GList *temp2 = m->attributes;
756
                while(temp2) {
757
                        janus_sdp_attribute *a = (janus_sdp_attribute *)temp2->data;
758
                        if(a->value != NULL) {
759
                                g_snprintf(buffer, sizeof(buffer), "a=%s:%s\r\n", a->name, a->value);
760
                        } else {
761
                                g_snprintf(buffer, sizeof(buffer), "a=%s\r\n", a->name);
762
                        }
763
                        g_strlcat(sdp, buffer, JANUS_BUFSIZE);
764
                        temp2 = temp2->next;
765
                }
766
                temp = temp->next;
767
        }
768
        if(!success) {
769
                /* FIXME Never happens right now? */
770
                g_free(sdp);
771
                sdp = NULL;
772
        }
773
        return sdp;
774
}
775

    
776
janus_sdp *janus_sdp_new(const char *name, const char *address) {
777
        janus_sdp *sdp = g_malloc0(sizeof(janus_sdp));
778
        /* Fill in some predefined stuff */
779
        sdp->version = 0;
780
        sdp->o_name = g_strdup("-");
781
        sdp->o_sessid = janus_get_real_time();
782
        sdp->o_version = 1;
783
        sdp->o_ipv4 = TRUE;
784
        sdp->o_addr = g_strdup(address ? address : "127.0.0.1");
785
        sdp->s_name = g_strdup(name ? name : "Janus session");
786
        sdp->t_start = 0;
787
        sdp->t_stop = 0;
788
        sdp->c_ipv4 = TRUE;
789
        sdp->c_addr = g_strdup(address ? address : "127.0.0.1");
790
        /* Done */
791
        return sdp;
792
}
793

    
794
janus_sdp *janus_sdp_generate_offer(const char *name, const char *address, ...) {
795
        /* This method has a variable list of arguments, telling us what we should offer */
796
        va_list args;
797
        va_start(args, address);
798
        /* Let's see what we should do with the media */
799
        gboolean do_audio = TRUE, do_video = TRUE, do_data = TRUE,
800
                audio_dtmf = FALSE, video_rtcpfb = TRUE, h264_fmtp = TRUE;
801
        const char *audio_codec = NULL, *video_codec = NULL;
802
        int audio_pt = 111, video_pt = 96;
803
        janus_sdp_mdirection audio_dir = JANUS_SDP_SENDRECV, video_dir = JANUS_SDP_SENDRECV;
804
        int property = va_arg(args, int);
805
        while(property != JANUS_SDP_OA_DONE) {
806
                if(property == JANUS_SDP_OA_AUDIO) {
807
                        do_audio = va_arg(args, gboolean);
808
                } else if(property == JANUS_SDP_OA_VIDEO) {
809
                        do_video = va_arg(args, gboolean);
810
                } else if(property == JANUS_SDP_OA_DATA) {
811
                        do_data = va_arg(args, gboolean);
812
                } else if(property == JANUS_SDP_OA_AUDIO_DIRECTION) {
813
                        audio_dir = va_arg(args, janus_sdp_mdirection);
814
                } else if(property == JANUS_SDP_OA_VIDEO_DIRECTION) {
815
                        video_dir = va_arg(args, janus_sdp_mdirection);
816
                } else if(property == JANUS_SDP_OA_AUDIO_CODEC) {
817
                        audio_codec = va_arg(args, char *);
818
                } else if(property == JANUS_SDP_OA_VIDEO_CODEC) {
819
                        video_codec = va_arg(args, char *);
820
                } else if(property == JANUS_SDP_OA_AUDIO_PT) {
821
                        audio_pt = va_arg(args, int);
822
                } else if(property == JANUS_SDP_OA_VIDEO_PT) {
823
                        video_pt = va_arg(args, int);
824
                } else if(property == JANUS_SDP_OA_AUDIO_DTMF) {
825
                        audio_dtmf = va_arg(args, gboolean);
826
                } else if(property == JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS) {
827
                        video_rtcpfb = va_arg(args, gboolean);
828
                } else if(property == JANUS_SDP_OA_VIDEO_H264_FMTP) {
829
                        h264_fmtp = va_arg(args, gboolean);
830
                } else {
831
                        JANUS_LOG(LOG_WARN, "Unknown property %d for preparing SDP answer, ignoring...\n", property);
832
                }
833
                property = va_arg(args, int);
834
        }
835
        if(audio_codec == NULL)
836
                audio_codec = "opus";
837
        const char *audio_rtpmap = janus_sdp_get_codec_rtpmap(audio_codec);
838
        if(do_audio && audio_rtpmap == NULL) {
839
                JANUS_LOG(LOG_ERR, "Unsupported audio codec '%s', can't prepare an offer\n", audio_codec);
840
                return NULL;
841
        }
842
        if(video_codec == NULL)
843
                video_codec = "vp8";
844
        const char *video_rtpmap = janus_sdp_get_codec_rtpmap(video_codec);
845
        if(do_video && video_rtpmap == NULL) {
846
                JANUS_LOG(LOG_ERR, "Unsupported video codec '%s', can't prepare an offer\n", video_codec);
847
                return NULL;
848
        }
849
#ifndef HAVE_SCTP
850
        do_data = FALSE;
851
#endif
852

    
853
        /* Create a new janus_sdp object */
854
        janus_sdp *offer = janus_sdp_new(name, address);
855
        /* Now add all the media we should */
856
        if(do_audio) {
857
                janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_AUDIO, 1, "UDP/TLS/RTP/SAVPF", audio_dir);
858
                m->c_ipv4 = TRUE;
859
                m->c_addr = g_strdup(offer->c_addr);
860
                /* Add the selected audio codec */
861
                m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(audio_pt));
862
                janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", audio_pt, audio_rtpmap);
863
                m->attributes = g_list_append(m->attributes, a);
864
                /* Check if we need to add a payload type for DTMF tones (telephone-event/8000) */
865
                if(audio_dtmf) {
866
                        /* We do */
867
                        int dtmf_pt = 126;
868
                        m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(dtmf_pt));
869
                        janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", dtmf_pt, janus_sdp_get_codec_rtpmap("dtmf"));
870
                        m->attributes = g_list_append(m->attributes, a);
871
                }
872
                offer->m_lines = g_list_append(offer->m_lines, m);
873
        }
874
        if(do_video) {
875
                janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_VIDEO, 1, "UDP/TLS/RTP/SAVPF", video_dir);
876
                m->c_ipv4 = TRUE;
877
                m->c_addr = g_strdup(offer->c_addr);
878
                /* Add the selected video codec */
879
                m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(video_pt));
880
                janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", video_pt, video_rtpmap);
881
                m->attributes = g_list_append(m->attributes, a);
882
                if(!strcasecmp(video_codec, "h264") && h264_fmtp) {
883
                        /* If it's H.264 and we were asked to, add the default fmtp profile as well */
884
                        a = janus_sdp_attribute_create("fmtp", "%d profile-level-id=42e01f;packetization-mode=1", video_pt);
885
                        m->attributes = g_list_append(m->attributes, a);
886
                }
887
                if(video_rtcpfb) {
888
                        /* Add rtcp-fb attributes */
889
                        a = janus_sdp_attribute_create("rtcp-fb", "%d ccm fir", video_pt);
890
                        m->attributes = g_list_append(m->attributes, a);
891
                        a = janus_sdp_attribute_create("rtcp-fb", "%d nack", video_pt);
892
                        m->attributes = g_list_append(m->attributes, a);
893
                        a = janus_sdp_attribute_create("rtcp-fb", "%d nack pli", video_pt);
894
                        m->attributes = g_list_append(m->attributes, a);
895
                        a = janus_sdp_attribute_create("rtcp-fb", "%d goog-remb", video_pt);
896
                        m->attributes = g_list_append(m->attributes, a);
897
                }
898
                offer->m_lines = g_list_append(offer->m_lines, m);
899
        }
900
        if(do_data) {
901
                janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_APPLICATION, 1, "DTLS/SCTP", JANUS_SDP_DEFAULT);
902
                m->c_ipv4 = TRUE;
903
                m->c_addr = g_strdup(offer->c_addr);
904
                m->fmts = g_list_append(m->fmts, g_strdup("5000"));
905
                /* Add an sctmap attribute */
906
                janus_sdp_attribute *aa = janus_sdp_attribute_create("sctmap", "5000 webrtc-datachannel 16");
907
                m->attributes = g_list_append(m->attributes, aa);
908
                offer->m_lines = g_list_append(offer->m_lines, m);
909
        }
910

    
911
        /* Done */
912
        va_end(args);
913

    
914
        return offer;
915
}
916

    
917
janus_sdp *janus_sdp_generate_answer(janus_sdp *offer, ...) {
918
        if(offer == NULL)
919
                return NULL;
920

    
921
        /* This method has a variable list of arguments, telling us how we should respond */
922
        va_list args;
923
        va_start(args, offer);
924
        /* Let's see what we should do with the media */
925
        gboolean do_audio = TRUE, do_video = TRUE, do_data = TRUE,
926
                audio_dtmf = FALSE, video_rtcpfb = TRUE, h264_fmtp = TRUE;
927
        const char *audio_codec = NULL, *video_codec = NULL;
928
        janus_sdp_mdirection audio_dir = JANUS_SDP_SENDRECV, video_dir = JANUS_SDP_SENDRECV;
929
        int property = va_arg(args, int);
930
        while(property != JANUS_SDP_OA_DONE) {
931
                if(property == JANUS_SDP_OA_AUDIO) {
932
                        do_audio = va_arg(args, gboolean);
933
                } else if(property == JANUS_SDP_OA_VIDEO) {
934
                        do_video = va_arg(args, gboolean);
935
                } else if(property == JANUS_SDP_OA_DATA) {
936
                        do_data = va_arg(args, gboolean);
937
                } else if(property == JANUS_SDP_OA_AUDIO_DIRECTION) {
938
                        audio_dir = va_arg(args, janus_sdp_mdirection);
939
                } else if(property == JANUS_SDP_OA_VIDEO_DIRECTION) {
940
                        video_dir = va_arg(args, janus_sdp_mdirection);
941
                } else if(property == JANUS_SDP_OA_AUDIO_CODEC) {
942
                        audio_codec = va_arg(args, char *);
943
                } else if(property == JANUS_SDP_OA_VIDEO_CODEC) {
944
                        video_codec = va_arg(args, char *);
945
                } else if(property == JANUS_SDP_OA_AUDIO_DTMF) {
946
                        audio_dtmf = va_arg(args, gboolean);
947
                } else if(property == JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS) {
948
                        video_rtcpfb = va_arg(args, gboolean);
949
                } else if(property == JANUS_SDP_OA_VIDEO_H264_FMTP) {
950
                        h264_fmtp = va_arg(args, gboolean);
951
                } else {
952
                        JANUS_LOG(LOG_WARN, "Unknown property %d for preparing SDP answer, ignoring...\n", property);
953
                }
954
                property = va_arg(args, int);
955
        }
956
#ifndef HAVE_SCTP
957
        do_data = FALSE;
958
#endif
959

    
960
        janus_sdp *answer = g_malloc0(sizeof(janus_sdp));
961
        /* Start by copying some of the headers */
962
        answer->version = offer->version;
963
        answer->o_name = g_strdup(offer->o_name ? offer->o_name : "-");
964
        answer->o_sessid = offer->o_sessid;
965
        answer->o_version = offer->o_version;
966
        answer->o_ipv4 = offer->o_ipv4;
967
        answer->o_addr = g_strdup(offer->o_addr ? offer->o_addr : "127.0.0.1");
968
        answer->s_name = g_strdup(offer->s_name ? offer->s_name : "Janus session");
969
        answer->t_start = 0;
970
        answer->t_stop = 0;
971
        answer->c_ipv4 = offer->c_ipv4;
972
        answer->c_addr = g_strdup(offer->c_addr ? offer->c_addr : "127.0.0.1");
973

    
974
        /* Now iterate on all media, and let's see what we should do */
975
        int audio = 0, video = 0, data = 0;
976
        GList *temp = offer->m_lines;
977
        while(temp) {
978
                janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
979
                /* For each m-line we parse, we'll need a corresponding one in the answer */
980
                janus_sdp_mline *am = g_malloc0(sizeof(janus_sdp_mline));
981
                am->type = m->type;
982
                am->type_str = m->type_str ? g_strdup(m->type_str) : NULL;
983
                am->proto = g_strdup(m->proto ? m->proto : "UDP/TLS/RTP/SAVPF");
984
                am->port = m->port;
985
                am->c_ipv4 = m->c_ipv4;
986
                am->c_addr = g_strdup(am->c_addr ? am->c_addr : "127.0.0.1");
987
                am->direction = JANUS_SDP_INACTIVE;        /* We'll change this later */
988
                /* Append to the list of m-lines in the answer */
989
                answer->m_lines = g_list_append(answer->m_lines, am);
990
                /* Let's see what this is */
991
                if(m->type == JANUS_SDP_AUDIO) {
992
                        if(m->port > 0) {
993
                                audio++;
994
                        }
995
                        if(!do_audio || audio > 1) {
996
                                /* Reject */
997
                                am->port = 0;
998
                                temp = temp->next;
999
                                continue;
1000
                        }
1001
                } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
1002
                        if(m->port > 0) {
1003
                                video++;
1004
                        }
1005
                        if(!do_video || video > 1) {
1006
                                /* Reject */
1007
                                am->port = 0;
1008
                                temp = temp->next;
1009
                                continue;
1010
                        }
1011
                } else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
1012
                        if(m->port > 0) {
1013
                                data++;
1014
                        }
1015
                        if(!do_data || data > 1) {
1016
                                /* Reject */
1017
                                am->port = 0;
1018
                                temp = temp->next;
1019
                                continue;
1020
                        }
1021
                }
1022
                if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
1023
                        janus_sdp_mdirection target_dir = m->type == JANUS_SDP_AUDIO ? audio_dir : video_dir;
1024
                        /* What is the direction we were offered? And how were we asked to react?
1025
                         * Adapt the direction in our answer accordingly */
1026
                        switch(m->direction) {
1027
                                case JANUS_SDP_RECVONLY:
1028
                                        if(target_dir == JANUS_SDP_SENDRECV || target_dir == JANUS_SDP_SENDONLY) {
1029
                                                /* Peer is recvonly, we'll only send */
1030
                                                am->direction = JANUS_SDP_SENDONLY;
1031
                                        } else {
1032
                                                /* Peer is recvonly, but we're not ok to send, so reply with inactive */
1033
                                                JANUS_LOG(LOG_WARN, "%s offered as '%s', but we need '%s': using 'inactive'\n",
1034
                                                        m->type == JANUS_SDP_AUDIO ? "Audio" : "Video",
1035
                                                        janus_sdp_mdirection_str(m->direction), janus_sdp_mdirection_str(target_dir));
1036
                                                am->direction = JANUS_SDP_INACTIVE;
1037
                                        }
1038
                                        break;
1039
                                case JANUS_SDP_SENDONLY:
1040
                                        if(target_dir == JANUS_SDP_SENDRECV || target_dir == JANUS_SDP_RECVONLY) {
1041
                                                /* Peer is sendonly, we'll only receive */
1042
                                                am->direction = JANUS_SDP_RECVONLY;
1043
                                        } else {
1044
                                                /* Peer is sendonly, but we're not ok to receive, so reply with inactive */
1045
                                                JANUS_LOG(LOG_WARN, "%s offered as '%s', but we need '%s': using 'inactive'\n",
1046
                                                        m->type == JANUS_SDP_AUDIO ? "Audio" : "Video",
1047
                                                        janus_sdp_mdirection_str(m->direction), janus_sdp_mdirection_str(target_dir));
1048
                                                am->direction = JANUS_SDP_INACTIVE;
1049
                                        }
1050
                                        break;
1051
                                case JANUS_SDP_INACTIVE:
1052
                                        /* Peer inactive, set inactive in the answer to */
1053
                                        am->direction = JANUS_SDP_INACTIVE;
1054
                                        break;
1055
                                case JANUS_SDP_SENDRECV:
1056
                                default:
1057
                                        /* The peer is fine with everything, so use our constraint */
1058
                                        am->direction = target_dir;
1059
                                        break;
1060
                        }
1061
                        /* Look for the right codec and stick to that */
1062
                        const char *codec = m->type == JANUS_SDP_AUDIO ? audio_codec : video_codec;
1063
                        if(codec == NULL) {
1064
                                /* FIXME User didn't provide a codec to accept? Let's see if Opus (for audio)
1065
                                 * of VP8 (for video) were negotiated: if so, use them, otherwise let's
1066
                                 * pick some other codec we know about among the ones that were offered.
1067
                                 * Notice that if it's not a codec we understand, we reject the medium,
1068
                                 * as browsers would reject it anyway. If you need more flexibility you'll
1069
                                 * have to generate an answer yourself, rather than automatically... */
1070
                                codec = m->type == JANUS_SDP_AUDIO ? "opus" : "vp8";
1071
                                if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1072
                                        /* We couldn't find our preferred codec, let's try something else */
1073
                                        if(m->type == JANUS_SDP_AUDIO) {
1074
                                                /* Opus not found, maybe mu-law? */
1075
                                                codec = "pcmu";
1076
                                                if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1077
                                                        /* mu-law not found, maybe a-law? */
1078
                                                        codec = "pcma";
1079
                                                        if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1080
                                                                /* a-law not found, maybe G.722? */
1081
                                                                codec = "722";
1082
                                                                if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1083
                                                                        /* G.722 not found, maybe isac32? */
1084
                                                                        codec = "isac32";
1085
                                                                        if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1086
                                                                                /* isac32 not found, maybe isac16? */
1087
                                                                                codec = "isac16";
1088
                                                                        }
1089
                                                                }
1090
                                                        }
1091
                                                }
1092
                                        } else {
1093
                                                /* VP8 not found, maybe VP9? */
1094
                                                codec = "vp9";
1095
                                                if(janus_sdp_get_codec_pt(offer, codec) < 0) {
1096
                                                        /* VP9 not found either, maybe H.264? */
1097
                                                        codec = "h264";
1098
                                                }
1099
                                        }
1100
                                }
1101
                        }
1102
                        int pt = janus_sdp_get_codec_pt(offer, codec);
1103
                        if(pt < 0) {
1104
                                /* Reject */
1105
                                JANUS_LOG(LOG_WARN, "Couldn't find codec we needed (%s) in the offer, rejecting %s\n",
1106
                                        codec, m->type == JANUS_SDP_AUDIO ? "audio" : "video");
1107
                                am->port = 0;
1108
                                am->direction = JANUS_SDP_INACTIVE;
1109
                                temp = temp->next;
1110
                                continue;
1111
                        }
1112
                        am->ptypes = g_list_append(am->ptypes, GINT_TO_POINTER(pt));
1113
                        /* Add the related attributes */
1114
                        if(m->type == JANUS_SDP_AUDIO) {
1115
                                /* Add rtpmap attribute */
1116
                                janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", pt, janus_sdp_get_codec_rtpmap(codec));
1117
                                am->attributes = g_list_append(am->attributes, a);
1118
                                /* Check if we need to add a payload type for DTMF tones (telephone-event/8000) */
1119
                                if(audio_dtmf) {
1120
                                        int dtmf_pt = janus_sdp_get_codec_pt(offer, "dtmf");
1121
                                        if(dtmf_pt >= 0) {
1122
                                                /* We do */
1123
                                                am->ptypes = g_list_append(am->ptypes, GINT_TO_POINTER(dtmf_pt));
1124
                                                janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", dtmf_pt, janus_sdp_get_codec_rtpmap("dtmf"));
1125
                                                am->attributes = g_list_append(am->attributes, a);
1126
                                        }
1127
                                }
1128
                        } else {
1129
                                /* Add rtpmap attribute */
1130
                                janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", pt, janus_sdp_get_codec_rtpmap(codec));
1131
                                am->attributes = g_list_append(am->attributes, a);
1132
                                if(!strcasecmp(codec, "h264") && h264_fmtp) {
1133
                                        /* If it's H.264 and we were asked to, add the default fmtp profile as well */
1134
                                        a = janus_sdp_attribute_create("fmtp", "%d profile-level-id=42e01f;packetization-mode=1", pt);
1135
                                        am->attributes = g_list_append(am->attributes, a);
1136
                                }
1137
                                if(video_rtcpfb) {
1138
                                        /* Add rtcp-fb attributes */
1139
                                        a = janus_sdp_attribute_create("rtcp-fb", "%d ccm fir", pt);
1140
                                        am->attributes = g_list_append(am->attributes, a);
1141
                                        a = janus_sdp_attribute_create("rtcp-fb", "%d nack", pt);
1142
                                        am->attributes = g_list_append(am->attributes, a);
1143
                                        a = janus_sdp_attribute_create("rtcp-fb", "%d nack pli", pt);
1144
                                        am->attributes = g_list_append(am->attributes, a);
1145
                                        a = janus_sdp_attribute_create("rtcp-fb", "%d goog-remb", pt);
1146
                                        am->attributes = g_list_append(am->attributes, a);
1147
                                }
1148
                        }
1149
                } else {
1150
                        /* This is for data, add formats and an sctmap attribute */
1151
                        am->direction = JANUS_SDP_DEFAULT;
1152
                        GList *fmt = m->fmts;
1153
                        while(fmt) {
1154
                                char *fmt_str = (char *)fmt->data;
1155
                                if(fmt_str)
1156
                                        am->fmts = g_list_append(am->fmts, g_strdup(fmt_str));
1157
                                fmt = fmt->next;
1158
                        }
1159
                        janus_sdp_attribute *aa = janus_sdp_attribute_create("sctmap", "5000 webrtc-datachannel 16");
1160
                        am->attributes = g_list_append(am->attributes, aa);
1161
                }
1162
                temp = temp->next;
1163
        }
1164

    
1165
        /* Done */
1166
        va_end(args);
1167

    
1168
        return answer;
1169
}