loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / sdp.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2017, Digium, Inc.
5  *
6  * George Joseph <gjoseph@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19
20 #include "asterisk.h"
21 #include "asterisk/utils.h"
22 #include "asterisk/netsock2.h"
23 #include "asterisk/codec.h"
24 #include "asterisk/format.h"
25 #include "asterisk/format_cap.h"
26 #include "asterisk/format_cache.h"
27 #include "asterisk/rtp_engine.h"
28 #include "asterisk/sdp_state.h"
29 #include "asterisk/sdp_options.h"
30 #include "asterisk/sdp_translator.h"
31 #include "asterisk/sdp.h"
32 #include "asterisk/vector.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/stream.h"
35 #include "sdp_private.h"
36
37 void ast_sdp_a_free(struct ast_sdp_a_line *a_line)
38 {
39         ast_free(a_line);
40 }
41
42 void ast_sdp_a_lines_free(struct ast_sdp_a_lines *a_lines)
43 {
44         if (!a_lines) {
45                 return;
46         }
47
48         AST_VECTOR_CALLBACK_VOID(a_lines, ast_sdp_a_free);
49         AST_VECTOR_FREE(a_lines);
50         ast_free(a_lines);
51 }
52
53 void ast_sdp_c_free(struct ast_sdp_c_line *c_line)
54 {
55         ast_free(c_line);
56 }
57
58 void ast_sdp_payload_free(struct ast_sdp_payload *payload)
59 {
60         ast_free(payload);
61 }
62
63 void ast_sdp_payloads_free(struct ast_sdp_payloads *payloads)
64 {
65         if (!payloads) {
66                 return;
67         }
68
69         AST_VECTOR_CALLBACK_VOID(payloads, ast_sdp_payload_free);
70         AST_VECTOR_FREE(payloads);
71         ast_free(payloads);
72 }
73
74 void ast_sdp_m_free(struct ast_sdp_m_line *m_line)
75 {
76         if (!m_line) {
77                 return;
78         }
79
80         ast_sdp_a_lines_free(m_line->a_lines);
81         ast_sdp_payloads_free(m_line->payloads);
82         ast_sdp_c_free(m_line->c_line);
83         ast_free(m_line);
84 }
85
86 void ast_sdp_m_lines_free(struct ast_sdp_m_lines *m_lines)
87 {
88         if (!m_lines) {
89                 return;
90         }
91
92         AST_VECTOR_CALLBACK_VOID(m_lines, ast_sdp_m_free);
93         AST_VECTOR_FREE(m_lines);
94         ast_free(m_lines);
95 }
96
97 void ast_sdp_o_free(struct ast_sdp_o_line *o_line)
98 {
99         ast_free(o_line);
100 }
101
102 void ast_sdp_s_free(struct ast_sdp_s_line *s_line)
103 {
104         ast_free(s_line);
105 }
106
107 void ast_sdp_t_free(struct ast_sdp_t_line *t_line)
108 {
109         ast_free(t_line);
110 }
111
112 static void ast_sdp_dtor(void *vdoomed)
113 {
114         struct ast_sdp *sdp = vdoomed;
115
116         ast_sdp_o_free(sdp->o_line);
117         ast_sdp_s_free(sdp->s_line);
118         ast_sdp_c_free(sdp->c_line);
119         ast_sdp_t_free(sdp->t_line);
120         ast_sdp_a_lines_free(sdp->a_lines);
121         ast_sdp_m_lines_free(sdp->m_lines);
122 }
123
124 #define COPY_STR_AND_ADVANCE(p, dest, source) \
125 ({ \
126         dest = p; \
127         strcpy(dest, source); \
128         p += (strlen(source) + 1); \
129 })
130
131 struct ast_sdp_a_line *ast_sdp_a_alloc(const char *name, const char *value)
132 {
133         struct ast_sdp_a_line *a_line;
134         size_t len;
135         char *p;
136
137         ast_assert(!ast_strlen_zero(name));
138
139         if (ast_strlen_zero(value)) {
140                 value = "";
141         }
142
143         len = sizeof(*a_line) + strlen(name) + strlen(value) + 2;
144         a_line = ast_calloc(1, len);
145         if (!a_line) {
146                 return NULL;
147         }
148
149         p = ((char *)a_line) + sizeof(*a_line);
150
151         COPY_STR_AND_ADVANCE(p, a_line->name, name);
152         COPY_STR_AND_ADVANCE(p, a_line->value, value);
153
154         return a_line;
155 }
156
157 struct ast_sdp_c_line *ast_sdp_c_alloc(const char *address_type, const char *address)
158 {
159         struct ast_sdp_c_line *c_line;
160         size_t len;
161         char *p;
162
163         ast_assert(!ast_strlen_zero(address_type) && !ast_strlen_zero(address));
164
165         len = sizeof(*c_line) + strlen(address_type) + strlen(address) + 2;
166         c_line = ast_calloc(1, len);
167         if (!c_line) {
168                 return NULL;
169         }
170
171         p = ((char *)c_line) + sizeof(*c_line);
172
173         COPY_STR_AND_ADVANCE(p, c_line->address_type, address_type);
174         COPY_STR_AND_ADVANCE(p, c_line->address, address);
175
176         return c_line;
177 }
178
179 struct ast_sdp_payload *ast_sdp_payload_alloc(const char *fmt)
180 {
181         struct ast_sdp_payload *payload;
182         size_t len;
183
184         ast_assert(!ast_strlen_zero(fmt));
185
186         len = sizeof(*payload) + strlen(fmt) + 1;
187         payload = ast_calloc(1, len);
188         if (!payload) {
189                 return NULL;
190         }
191
192         payload->fmt = ((char *)payload) + sizeof(*payload);
193         strcpy(payload->fmt, fmt);  /* Safe */
194
195         return payload;
196 }
197
198 struct ast_sdp_m_line *ast_sdp_m_alloc(const char *type, uint16_t port,
199         uint16_t port_count, const char *proto, struct ast_sdp_c_line *c_line)
200 {
201         struct ast_sdp_m_line *m_line;
202         size_t len;
203         char *p;
204
205         ast_assert(!ast_strlen_zero(type) && !ast_strlen_zero(proto));
206
207         len = sizeof(*m_line) + strlen(type) + strlen(proto) + 2;
208         m_line = ast_calloc(1, len);
209         if (!m_line) {
210                 return NULL;
211         }
212
213         m_line->a_lines = ast_calloc(1, sizeof(*m_line->a_lines));
214         if (!m_line->a_lines) {
215                 ast_sdp_m_free(m_line);
216                 return NULL;
217         }
218         if (AST_VECTOR_INIT(m_line->a_lines, 20)) {
219                 ast_sdp_m_free(m_line);
220                 return NULL;
221         }
222
223         m_line->payloads = ast_calloc(1, sizeof(*m_line->payloads));
224         if (!m_line->payloads) {
225                 ast_sdp_m_free(m_line);
226                 return NULL;
227         }
228         if (AST_VECTOR_INIT(m_line->payloads, 20)) {
229                 ast_sdp_m_free(m_line);
230                 return NULL;
231         }
232
233         p = ((char *)m_line) + sizeof(*m_line);
234
235         COPY_STR_AND_ADVANCE(p, m_line->type, type);
236         COPY_STR_AND_ADVANCE(p, m_line->proto, proto);
237         m_line->port = port;
238         m_line->port_count = port_count;
239         m_line->c_line = c_line;
240
241         return m_line;
242 }
243
244 struct ast_sdp_s_line *ast_sdp_s_alloc(const char *session_name)
245 {
246         struct ast_sdp_s_line *s_line;
247         size_t len;
248
249         if (ast_strlen_zero(session_name)) {
250                 session_name = " ";
251         }
252
253         len = sizeof(*s_line) + strlen(session_name) + 1;
254         s_line = ast_calloc(1, len);
255         if (!s_line) {
256                 return NULL;
257         }
258
259         s_line->session_name = ((char *)s_line) + sizeof(*s_line);
260         strcpy(s_line->session_name, session_name);  /* Safe */
261
262         return s_line;
263 }
264
265 struct ast_sdp_t_line *ast_sdp_t_alloc(uint64_t start_time, uint64_t stop_time)
266 {
267         struct ast_sdp_t_line *t_line;
268
269         t_line = ast_calloc(1, sizeof(*t_line));
270         if (!t_line) {
271                 return NULL;
272         }
273
274         t_line->start_time = start_time;
275         t_line->stop_time = stop_time;
276
277         return t_line;
278 }
279
280 struct ast_sdp_o_line *ast_sdp_o_alloc(const char *username, uint64_t session_id,
281         uint64_t session_version, const char *address_type, const char *address)
282 {
283         struct ast_sdp_o_line *o_line;
284         size_t len;
285         char *p;
286
287         ast_assert(!ast_strlen_zero(username) && !ast_strlen_zero(address_type)
288                 && !ast_strlen_zero(address));
289
290         len = sizeof(*o_line) + strlen(username) + strlen(address_type) + strlen(address) + 3;
291         o_line = ast_calloc(1, len);
292         if (!o_line) {
293                 return NULL;
294         }
295
296         o_line->session_id = session_id;
297         o_line->session_version = session_version;
298
299         p = ((char *)o_line) + sizeof(*o_line);
300
301         COPY_STR_AND_ADVANCE(p, o_line->username, username);
302         COPY_STR_AND_ADVANCE(p, o_line->address_type, address_type);
303         COPY_STR_AND_ADVANCE(p, o_line->address, address);
304
305         return o_line;
306 }
307
308 struct ast_sdp *ast_sdp_alloc(struct ast_sdp_o_line *o_line,
309         struct ast_sdp_c_line *c_line, struct ast_sdp_s_line *s_line,
310         struct ast_sdp_t_line *t_line)
311 {
312         struct ast_sdp *new_sdp;
313
314         new_sdp = ao2_alloc_options(sizeof(*new_sdp), ast_sdp_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
315         if (!new_sdp) {
316                 return NULL;
317         }
318
319         new_sdp->a_lines = ast_calloc(1, sizeof(*new_sdp->a_lines));
320         if (!new_sdp->a_lines) {
321                 ao2_ref(new_sdp, -1);
322                 return NULL;
323         }
324         if (AST_VECTOR_INIT(new_sdp->a_lines, 20)) {
325                 ao2_ref(new_sdp, -1);
326                 return NULL;
327         }
328
329         new_sdp->m_lines = ast_calloc(1, sizeof(*new_sdp->m_lines));
330         if (!new_sdp->m_lines) {
331                 ao2_ref(new_sdp, -1);
332                 return NULL;
333         }
334         if (AST_VECTOR_INIT(new_sdp->m_lines, 20)) {
335                 ao2_ref(new_sdp, -1);
336                 return NULL;
337         }
338
339         new_sdp->o_line = o_line;
340         new_sdp->c_line = c_line;
341         new_sdp->s_line = s_line;
342         new_sdp->t_line = t_line;
343
344         return new_sdp;
345 }
346
347 int ast_sdp_add_a(struct ast_sdp *sdp, struct ast_sdp_a_line *a_line)
348 {
349         ast_assert(sdp && a_line);
350
351         return AST_VECTOR_APPEND(sdp->a_lines, a_line);
352 }
353
354 int ast_sdp_get_a_count(const struct ast_sdp *sdp)
355 {
356         ast_assert(sdp != NULL);
357
358         return AST_VECTOR_SIZE(sdp->a_lines);
359 }
360
361 struct ast_sdp_a_line *ast_sdp_get_a(const struct ast_sdp *sdp, int index)
362 {
363         ast_assert(sdp != NULL);
364
365         return AST_VECTOR_GET(sdp->a_lines, index);
366 }
367
368 int ast_sdp_add_m(struct ast_sdp *sdp, struct ast_sdp_m_line *m_line)
369 {
370         ast_assert(sdp && m_line);
371
372         return AST_VECTOR_APPEND(sdp->m_lines, m_line);
373 }
374
375 int ast_sdp_get_m_count(const struct ast_sdp *sdp)
376 {
377         ast_assert(sdp != NULL);
378
379         return AST_VECTOR_SIZE(sdp->m_lines);
380 }
381
382 struct ast_sdp_m_line *ast_sdp_get_m(const struct ast_sdp *sdp, int index)
383 {
384         ast_assert(sdp != NULL);
385
386         return AST_VECTOR_GET(sdp->m_lines, index);
387 }
388
389 int ast_sdp_m_add_a(struct ast_sdp_m_line *m_line, struct ast_sdp_a_line *a_line)
390 {
391         ast_assert(m_line && a_line);
392
393         return AST_VECTOR_APPEND(m_line->a_lines, a_line);
394 }
395
396 int ast_sdp_m_get_a_count(const struct ast_sdp_m_line *m_line)
397 {
398         ast_assert(m_line != NULL);
399
400         return AST_VECTOR_SIZE(m_line->a_lines);
401 }
402
403 struct ast_sdp_a_line *ast_sdp_m_get_a(const struct ast_sdp_m_line *m_line, int index)
404 {
405         ast_assert(m_line != NULL);
406
407         return AST_VECTOR_GET(m_line->a_lines, index);
408 }
409
410 int ast_sdp_m_add_payload(struct ast_sdp_m_line *m_line, struct ast_sdp_payload *payload)
411 {
412         ast_assert(m_line && payload);
413
414         return AST_VECTOR_APPEND(m_line->payloads, payload);
415 }
416
417 int ast_sdp_m_get_payload_count(const struct ast_sdp_m_line *m_line)
418 {
419         ast_assert(m_line != NULL);
420
421         return AST_VECTOR_SIZE(m_line->payloads);
422 }
423
424 struct ast_sdp_payload *ast_sdp_m_get_payload(const struct ast_sdp_m_line *m_line, int index)
425 {
426         ast_assert(m_line != NULL);
427
428         return AST_VECTOR_GET(m_line->payloads, index);
429 }
430
431 static int sdp_m_add_fmtp(struct ast_sdp_m_line *m_line, const struct ast_format *format,
432         int rtp_code)
433 {
434         struct ast_str *fmtp0 = ast_str_alloca(256);
435         struct ast_sdp_a_line *a_line;
436         char *tmp;
437
438         ast_format_generate_sdp_fmtp(format, rtp_code, &fmtp0);
439         if (ast_str_strlen(fmtp0) == 0) {
440                 /* Format doesn't have fmtp attributes */
441                 return 0;
442         }
443
444         tmp = ast_str_buffer(fmtp0) + ast_str_strlen(fmtp0) - 1;
445         /* remove any carriage return line feeds */
446         while (*tmp == '\r' || *tmp == '\n') --tmp;
447         *++tmp = '\0';
448
449         /*
450          * ast...generate gives us everything, just need value
451          *
452          * It can also give multiple fmtp attribute lines. (silk does)
453          */
454         tmp = strchr(ast_str_buffer(fmtp0), ':');
455         if (tmp && tmp[1] != '\0') {
456                 tmp++;
457         } else {
458                 tmp = ast_str_buffer(fmtp0);
459         }
460
461         a_line = ast_sdp_a_alloc("fmtp", tmp);
462         if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
463                 return -1;
464         }
465
466         return 0;
467 }
468
469 static int sdp_m_add_rtpmap(struct ast_sdp_m_line *m_line,
470         const struct ast_sdp_options *options, int rtp_code, int asterisk_format,
471         const struct ast_format *format, int code)
472 {
473         char tmp[64];
474         const char *enc_name;
475         struct ast_sdp_payload *payload;
476         struct ast_sdp_a_line *a_line;
477
478         snprintf(tmp, sizeof(tmp), "%d", rtp_code);
479         payload = ast_sdp_payload_alloc(tmp);
480         if (!payload || ast_sdp_m_add_payload(m_line, payload)) {
481                 ast_sdp_payload_free(payload);
482                 return -1;
483         }
484
485         enc_name = ast_rtp_lookup_mime_subtype2(asterisk_format, format, code,
486                 options->g726_non_standard ? AST_RTP_OPT_G726_NONSTANDARD : 0);
487
488         snprintf(tmp, sizeof(tmp), "%d %s/%d%s%s", rtp_code, enc_name,
489                 ast_rtp_lookup_sample_rate2(asterisk_format, format, code),
490                 strcmp(enc_name, "opus") ? "" : "/", strcmp(enc_name, "opus") ? "" : "2");
491
492         a_line = ast_sdp_a_alloc("rtpmap", tmp);
493         if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
494                 ast_sdp_a_free(a_line);
495                 return -1;
496         }
497
498         return 0;
499 }
500
501 int ast_sdp_m_add_format(struct ast_sdp_m_line *m_line, const struct ast_sdp_options *options,
502         int rtp_code, int asterisk_format, const struct ast_format *format, int code)
503 {
504         return sdp_m_add_rtpmap(m_line, options, rtp_code, asterisk_format, format, code)
505                 || sdp_m_add_fmtp(m_line, format, rtp_code) ? -1 : 0;
506 }
507
508 static int sdp_find_a_common(const struct ast_sdp_a_lines *a_lines, int start,
509         const char *attr_name, int payload)
510 {
511         struct ast_sdp_a_line *a_line;
512         int idx;
513
514         ast_assert(-1 <= start);
515
516         for (idx = start + 1; idx < AST_VECTOR_SIZE(a_lines); ++idx) {
517                 int a_line_payload;
518
519                 a_line = AST_VECTOR_GET(a_lines, idx);
520                 if (strcmp(a_line->name, attr_name)) {
521                         continue;
522                 }
523
524                 if (payload >= 0) {
525                         int sscanf_res;
526
527                         sscanf_res = sscanf(a_line->value, "%30d", &a_line_payload);
528                         if (sscanf_res == 1 && payload == a_line_payload) {
529                                 return idx;
530                         }
531                 } else {
532                         return idx;
533                 }
534         }
535
536         return -1;
537 }
538
539 int ast_sdp_find_a_first(const struct ast_sdp *sdp, const char *attr_name, int payload)
540 {
541         return sdp_find_a_common(sdp->a_lines, -1, attr_name, payload);
542 }
543
544 int ast_sdp_find_a_next(const struct ast_sdp *sdp, int last, const char *attr_name, int payload)
545 {
546         return sdp_find_a_common(sdp->a_lines, last, attr_name, payload);
547 }
548
549 struct ast_sdp_a_line *ast_sdp_find_attribute(const struct ast_sdp *sdp,
550         const char *attr_name, int payload)
551 {
552         int idx;
553
554         idx = ast_sdp_find_a_first(sdp, attr_name, payload);
555         if (idx < 0) {
556                 return NULL;
557         }
558         return ast_sdp_get_a(sdp, idx);
559 }
560
561 int ast_sdp_m_find_a_first(const struct ast_sdp_m_line *m_line, const char *attr_name,
562         int payload)
563 {
564         return sdp_find_a_common(m_line->a_lines, -1, attr_name, payload);
565 }
566
567 int ast_sdp_m_find_a_next(const struct ast_sdp_m_line *m_line, int last,
568         const char *attr_name, int payload)
569 {
570         return sdp_find_a_common(m_line->a_lines, last, attr_name, payload);
571 }
572
573 struct ast_sdp_a_line *ast_sdp_m_find_attribute(const struct ast_sdp_m_line *m_line,
574         const char *attr_name, int payload)
575 {
576         int idx;
577
578         idx = ast_sdp_m_find_a_first(m_line, attr_name, payload);
579         if (idx < 0) {
580                 return NULL;
581         }
582         return ast_sdp_m_get_a(m_line, idx);
583 }
584
585 struct ast_sdp_rtpmap *ast_sdp_rtpmap_alloc(int payload, const char *encoding_name,
586         int clock_rate, const char *encoding_parameters)
587 {
588         struct ast_sdp_rtpmap *rtpmap;
589         char *buf_pos;
590
591         rtpmap = ast_calloc(1, sizeof(*rtpmap) + strlen(encoding_name) + strlen(encoding_parameters) + 2);
592         if (!rtpmap) {
593                 return NULL;
594         }
595
596         rtpmap->payload = payload;
597         rtpmap->clock_rate = clock_rate;
598
599         buf_pos = rtpmap->buf;
600         COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_name, encoding_name);
601         COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_parameters, encoding_parameters);
602
603         return rtpmap;
604 }
605
606 void ast_sdp_rtpmap_free(struct ast_sdp_rtpmap *rtpmap)
607 {
608         ast_free(rtpmap);
609 }
610
611 struct ast_sdp_rtpmap *ast_sdp_a_get_rtpmap(const struct ast_sdp_a_line *a_line)
612 {
613         char *value_copy;
614         char *slash;
615         int payload;
616         char encoding_name[64];
617         int clock_rate;
618         char *encoding_parameters;
619         struct ast_sdp_rtpmap *rtpmap;
620         int clock_rate_len;
621
622         value_copy = ast_strip(ast_strdupa(a_line->value));
623
624         if (sscanf(value_copy, "%30d %63s", &payload, encoding_name) != 2) {
625                 return NULL;
626         }
627
628         slash = strchr(encoding_name, '/');
629         if (!slash) {
630                 return NULL;
631         }
632         *slash++ = '\0';
633         if (ast_strlen_zero(encoding_name)) {
634                 return NULL;
635         }
636         if (sscanf(slash, "%30d%n", &clock_rate, &clock_rate_len) < 1) {
637                 return NULL;
638         }
639
640         slash += clock_rate_len;
641         if (!ast_strlen_zero(slash)) {
642                 if (*slash == '/') {
643                         *slash++ = '\0';
644                         encoding_parameters = slash;
645                         if (ast_strlen_zero(encoding_parameters)) {
646                                 return NULL;
647                         }
648                 } else {
649                         return NULL;
650                 }
651         } else {
652                 encoding_parameters = "";
653         }
654
655         rtpmap = ast_sdp_rtpmap_alloc(payload, encoding_name, clock_rate,
656                 encoding_parameters);
657
658         return rtpmap;
659 }
660
661 /*!
662  * \brief Turn an SDP attribute into an sdp_rtpmap structure
663  *
664  * \param m_line The media section where this attribute was found.
665  * \param payload The RTP payload to find an rtpmap for
666  * \param[out] rtpmap The rtpmap to fill in.
667  * \return Zero if successful, otherwise less than zero
668  */
669 static struct ast_sdp_rtpmap *sdp_payload_get_rtpmap(const struct ast_sdp_m_line *m_line, int payload)
670 {
671         struct ast_sdp_a_line *rtpmap_attr;
672
673         rtpmap_attr = ast_sdp_m_find_attribute(m_line, "rtpmap", payload);
674         if (!rtpmap_attr) {
675                 return NULL;
676         }
677
678         return ast_sdp_a_get_rtpmap(rtpmap_attr);
679 }
680
681 static void process_fmtp_value(const char *value, int payload, struct ast_rtp_codecs *codecs)
682 {
683         char *param;
684         char *param_start;
685         char *param_end;
686         size_t len;
687         struct ast_format *replace;
688         struct ast_format *format;
689
690         /*
691          * Extract the "a=fmtp:%d %s" attribute parameter string value which
692          * starts after the colon.
693          */
694         param_start = ast_skip_nonblanks(value);/* Skip payload type */
695         param_start = ast_skip_blanks(param_start);
696         param_end = ast_skip_nonblanks(param_start);
697         if (param_end == param_start) {
698                 /* There is no parameter string */
699                 return;
700         }
701         len = param_end - param_start;
702         param = ast_alloca(len + 1);
703         memcpy(param, param_start, len);
704         param[len] = '\0';
705
706         format = ast_rtp_codecs_get_payload_format(codecs, payload);
707         if (!format) {
708                 return;
709         }
710
711         replace = ast_format_parse_sdp_fmtp(format, param);
712         if (replace) {
713                 ast_rtp_codecs_payload_replace_format(codecs, payload, replace);
714                 ao2_ref(replace, -1);
715         }
716         ao2_ref(format, -1);
717 }
718
719 /*!
720  * \brief Find and process all fmtp attribute lines for a given payload
721  *
722  * \param m_line The stream on which to search for the fmtp attributes
723  * \param payload The specific fmtp attribute to search for
724  * \param codecs The current RTP codecs that have been built up
725  */
726 static void process_fmtp_lines(const struct ast_sdp_m_line *m_line, int payload,
727         struct ast_rtp_codecs *codecs)
728 {
729         const struct ast_sdp_a_line *a_line;
730         int idx;
731
732         idx = ast_sdp_m_find_a_first(m_line, "fmtp", payload);
733         for (; 0 <= idx; idx = ast_sdp_m_find_a_next(m_line, idx, "fmtp", payload)) {
734                 a_line = ast_sdp_m_get_a(m_line, idx);
735                 ast_assert(a_line != NULL);
736
737                 process_fmtp_value(a_line->value, payload, codecs);
738         }
739 }
740
741 /*!
742  * \internal
743  * \brief Determine the RTP stream direction in the given a lines.
744  * \since 15.0.0
745  *
746  * \param a_lines Attribute lines to search.
747  *
748  * \retval Stream direction on success.
749  * \retval AST_STREAM_STATE_REMOVED on failure.
750  *
751  * \return Nothing
752  */
753 static enum ast_stream_state get_a_line_direction(const struct ast_sdp_a_lines *a_lines)
754 {
755         if (a_lines) {
756                 enum ast_stream_state direction;
757                 int idx;
758                 const struct ast_sdp_a_line *a_line;
759
760                 for (idx = 0; idx < AST_VECTOR_SIZE(a_lines); ++idx) {
761                         a_line = AST_VECTOR_GET(a_lines, idx);
762                         direction = ast_stream_str2state(a_line->name);
763                         if (direction != AST_STREAM_STATE_REMOVED) {
764                                 return direction;
765                         }
766                 }
767         }
768
769         return AST_STREAM_STATE_REMOVED;
770 }
771
772 /*!
773  * \internal
774  * \brief Determine the RTP stream direction.
775  * \since 15.0.0
776  *
777  * \param a_lines The SDP media global attributes
778  * \param m_line The SDP media section to convert
779  *
780  * \return Stream direction
781  */
782 static enum ast_stream_state get_stream_direction(const struct ast_sdp_a_lines *a_lines, const struct ast_sdp_m_line *m_line)
783 {
784         enum ast_stream_state direction;
785
786         direction = get_a_line_direction(m_line->a_lines);
787         if (direction != AST_STREAM_STATE_REMOVED) {
788                 return direction;
789         }
790         direction = get_a_line_direction(a_lines);
791         if (direction != AST_STREAM_STATE_REMOVED) {
792                 return direction;
793         }
794         return AST_STREAM_STATE_SENDRECV;
795 }
796
797 /*
798  * Needed so we don't have an external function referenced as data.
799  * The dynamic linker doesn't handle that very well.
800  */
801 static void rtp_codecs_free(struct ast_rtp_codecs *codecs)
802 {
803         if (codecs) {
804                 ast_rtp_codecs_payloads_destroy(codecs);
805         }
806 }
807
808 /*!
809  * \brief Convert an SDP stream into an Asterisk stream
810  *
811  * Given an m-line from an SDP, convert it into an ast_stream structure.
812  * This takes formats, as well as clock-rate and fmtp attributes into account.
813  *
814  * \param a_lines The SDP media global attributes
815  * \param m_line The SDP media section to convert
816  * \param g726_non_standard Non-zero if G.726 is non-standard
817  *
818  * \retval NULL An error occurred
819  * \retval non-NULL The converted stream
820  */
821 static struct ast_stream *get_stream_from_m(const struct ast_sdp_a_lines *a_lines, const struct ast_sdp_m_line *m_line, int g726_non_standard)
822 {
823         int i;
824         int non_ast_fmts;
825         struct ast_rtp_codecs *codecs;
826         struct ast_format_cap *caps;
827         struct ast_stream *stream;
828         enum ast_rtp_options options;
829
830         caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
831         if (!caps) {
832                 return NULL;
833         }
834         stream = ast_stream_alloc(m_line->type, ast_media_type_from_str(m_line->type));
835         if (!stream) {
836                 ao2_ref(caps, -1);
837                 return NULL;
838         }
839
840         switch (ast_stream_get_type(stream)) {
841         case AST_MEDIA_TYPE_AUDIO:
842         case AST_MEDIA_TYPE_VIDEO:
843                 codecs = ast_calloc(1, sizeof(*codecs));
844                 if (!codecs || ast_rtp_codecs_payloads_initialize(codecs)) {
845                         rtp_codecs_free(codecs);
846                         ast_stream_free(stream);
847                         ao2_ref(caps, -1);
848                         return NULL;
849                 }
850                 ast_stream_set_data(stream, AST_STREAM_DATA_RTP_CODECS, codecs,
851                         (ast_stream_data_free_fn) rtp_codecs_free);
852
853                 if (!m_line->port) {
854                         /* Stream is declined.  There may not be any attributes. */
855                         ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);
856                         break;
857                 }
858
859                 options = g726_non_standard ? AST_RTP_OPT_G726_NONSTANDARD : 0;
860                 for (i = 0; i < ast_sdp_m_get_payload_count(m_line); ++i) {
861                         struct ast_sdp_payload *payload_s;
862                         struct ast_sdp_rtpmap *rtpmap;
863                         int payload;
864
865                         payload_s = ast_sdp_m_get_payload(m_line, i);
866                         sscanf(payload_s->fmt, "%30d", &payload);
867
868                         rtpmap = sdp_payload_get_rtpmap(m_line, payload);
869                         if (!rtpmap) {
870                                 /* No rtpmap attribute.  Try static payload type format assignment */
871                                 ast_rtp_codecs_payloads_set_m_type(codecs, NULL, payload);
872                                 continue;
873                         }
874
875                         if (!ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, payload,
876                                 m_line->type, rtpmap->encoding_name, options, rtpmap->clock_rate)) {
877                                 /* Successfully mapped the payload type to format */
878                                 process_fmtp_lines(m_line, payload, codecs);
879                         }
880                         ast_sdp_rtpmap_free(rtpmap);
881                 }
882
883                 ast_rtp_codecs_payload_formats(codecs, caps, &non_ast_fmts);
884
885                 ast_stream_set_state(stream, get_stream_direction(a_lines, m_line));
886                 break;
887         case AST_MEDIA_TYPE_IMAGE:
888                 if (!m_line->port) {
889                         /* Stream is declined.  There may not be any attributes. */
890                         ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);
891                         break;
892                 }
893
894                 for (i = 0; i < ast_sdp_m_get_payload_count(m_line); ++i) {
895                         struct ast_sdp_payload *payload;
896
897                         /* As we don't carry T.38 over RTP we do our own format check */
898                         payload = ast_sdp_m_get_payload(m_line, i);
899                         if (!strcasecmp(payload->fmt, "t38")) {
900                                 ast_format_cap_append(caps, ast_format_t38, 0);
901                                 ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV);
902                         }
903                 }
904                 break;
905         case AST_MEDIA_TYPE_UNKNOWN:
906         case AST_MEDIA_TYPE_TEXT:
907         case AST_MEDIA_TYPE_END:
908                 /* Consider these unsupported streams as declined */
909                 ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);
910                 break;
911         }
912
913         ast_stream_set_formats(stream, caps);
914         ao2_ref(caps, -1);
915
916         return stream;
917 }
918
919 struct ast_stream_topology *ast_get_topology_from_sdp(const struct ast_sdp *sdp, int g726_non_standard)
920 {
921         struct ast_stream_topology *topology;
922         int i;
923
924         topology = ast_stream_topology_alloc();
925         if (!topology) {
926                 return NULL;
927         }
928
929         for (i = 0; i < ast_sdp_get_m_count(sdp); ++i) {
930                 struct ast_stream *stream;
931
932                 stream = get_stream_from_m(sdp->a_lines, ast_sdp_get_m(sdp, i), g726_non_standard);
933                 if (!stream) {
934                         /*
935                          * The topology cannot match the SDP because
936                          * we failed to create a corresponding stream.
937                          */
938                         ast_stream_topology_free(topology);
939                         return NULL;
940                 }
941                 if (ast_stream_topology_append_stream(topology, stream) < 0) {
942                         /* Failed to add stream to topology */
943                         ast_stream_free(stream);
944                         ast_stream_topology_free(topology);
945                         return NULL;
946                 }
947         }
948
949         return topology;
950 }