CHANNEL(callid): Give dialplan access to the callid.
[asterisk/asterisk.git] / main / format_cap.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2014, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@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 /*! \file
20  *
21  * \brief Format Capabilities API
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 #include "asterisk/logger.h"
33 #include "asterisk/format.h"
34 #include "asterisk/format_cap.h"
35 #include "asterisk/format_cache.h"
36 #include "asterisk/codec.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/vector.h"
40 #include "asterisk/linkedlists.h"
41 #include "asterisk/utils.h"
42
43 /*! \brief Structure used for capability formats, adds framing */
44 struct format_cap_framed {
45         /*! \brief A pointer to the format */
46         struct ast_format *format;
47         /*! \brief The format framing size */
48         unsigned int framing;
49         /*! \brief Linked list information */
50         AST_LIST_ENTRY(format_cap_framed) entry;
51 };
52
53 /*! \brief Format capabilities structure, holds formats + preference order + etc */
54 struct ast_format_cap {
55         /*! \brief Vector of formats, indexed using the codec identifier */
56         AST_VECTOR(, struct format_cap_framed_list) formats;
57         /*! \brief Vector of formats, added in preference order */
58         AST_VECTOR(, struct format_cap_framed *) preference_order;
59         /*! \brief Global framing size, applies to all formats if no framing present on format */
60         unsigned int framing;
61 };
62
63 /*! \brief Linked list for formats */
64 AST_LIST_HEAD_NOLOCK(format_cap_framed_list, format_cap_framed);
65
66 /*! \brief Dummy empty list for when we are inserting a new list */
67 static const struct format_cap_framed_list format_cap_framed_list_empty = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
68
69 /*! \brief Destructor for format capabilities structure */
70 static void format_cap_destroy(void *obj)
71 {
72         struct ast_format_cap *cap = obj;
73         int idx;
74
75         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); idx++) {
76                 struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx);
77                 struct format_cap_framed *framed;
78
79                 while ((framed = AST_LIST_REMOVE_HEAD(list, entry))) {
80                         ao2_ref(framed, -1);
81                 }
82         }
83         AST_VECTOR_FREE(&cap->formats);
84
85         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); idx++) {
86                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx);
87
88                 /* This will always be non-null, unlike formats */
89                 ao2_ref(framed, -1);
90         }
91         AST_VECTOR_FREE(&cap->preference_order);
92 }
93
94 /*
95  * \brief Initialize values on an ast_format_cap
96  *
97  * \param cap ast_format_cap to initialize
98  * \param flags Unused.
99  * \retval 0 Success
100  * \retval -1 Failure
101  */
102 static inline int format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
103 {
104         if (AST_VECTOR_INIT(&cap->formats, 0)) {
105                 return -1;
106         }
107
108         /* TODO: Look at common usage of this and determine a good starting point */
109         if (AST_VECTOR_INIT(&cap->preference_order, 5)) {
110                 return -1;
111         }
112
113         cap->framing = UINT_MAX;
114         return 0;
115 }
116
117 struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags,
118         const char *tag, const char *file, int line, const char *func)
119 {
120         struct ast_format_cap *cap;
121
122         cap = __ao2_alloc(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK,
123                 tag, file, line, func);
124         if (!cap) {
125                 return NULL;
126         }
127
128         if (format_cap_init(cap, flags)) {
129                 ao2_ref(cap, -1);
130                 return NULL;
131         }
132
133         return cap;
134 }
135
136 void ast_format_cap_set_framing(struct ast_format_cap *cap, unsigned int framing)
137 {
138         cap->framing = framing;
139 }
140
141 /*! \brief Destructor for format capabilities framed structure */
142 static void format_cap_framed_destroy(void *obj)
143 {
144         struct format_cap_framed *framed = obj;
145
146         ao2_cleanup(framed->format);
147 }
148
149 static inline int format_cap_framed_init(struct format_cap_framed *framed, struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
150 {
151         struct format_cap_framed_list *list;
152
153         framed->framing = framing;
154
155         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
156                 if (AST_VECTOR_REPLACE(&cap->formats, ast_format_get_codec_id(format), format_cap_framed_list_empty)) {
157                         ao2_ref(framed, -1);
158                         return -1;
159                 }
160         }
161         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
162
163         /* Order doesn't matter for formats, so insert at the head for performance reasons */
164         ao2_ref(framed, +1);
165         AST_LIST_INSERT_HEAD(list, framed, entry);
166
167         /* This takes the allocation reference */
168         AST_VECTOR_APPEND(&cap->preference_order, framed);
169
170         cap->framing = MIN(cap->framing, framing ? framing : ast_format_get_default_ms(format));
171
172         return 0;
173 }
174
175 /*! \internal \brief Determine if \c format is in \c cap */
176 static int format_in_format_cap(struct ast_format_cap *cap, struct ast_format *format)
177 {
178         struct format_cap_framed *framed;
179         int i;
180
181         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
182                 framed = AST_VECTOR_GET(&cap->preference_order, i);
183
184                 if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
185                         return 1;
186                 }
187         }
188
189         return 0;
190 }
191
192 int __ast_format_cap_append(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing, const char *tag, const char *file, int line, const char *func)
193 {
194         struct format_cap_framed *framed;
195
196         ast_assert(format != NULL);
197
198         if (format_in_format_cap(cap, format)) {
199                 return 0;
200         }
201
202         framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
203         if (!framed) {
204                 return -1;
205         }
206
207         __ao2_ref(format, +1, tag, file, line, func);
208         framed->format = format;
209
210         return format_cap_framed_init(framed, cap, format, framing);
211 }
212
213 int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
214 {
215         int id;
216
217         for (id = 1; id < ast_codec_get_max(); ++id) {
218                 struct ast_codec *codec = ast_codec_get_by_id(id);
219                 struct ast_codec *codec2 = NULL;
220                 struct ast_format *format;
221                 int res;
222
223                 if (!codec) {
224                         continue;
225                 }
226
227                 if ((type != AST_MEDIA_TYPE_UNKNOWN) && codec->type != type) {
228                         ao2_ref(codec, -1);
229                         continue;
230                 }
231
232                 format = ast_format_cache_get(codec->name);
233
234                 if (format == ast_format_none) {
235                         ao2_ref(format, -1);
236                         ao2_ref(codec, -1);
237                         continue;
238                 }
239
240                 if (format) {
241                         codec2 = ast_format_get_codec(format);
242                 }
243                 if (codec != codec2) {
244                         ao2_cleanup(format);
245                         format = ast_format_create(codec);
246                 }
247                 ao2_cleanup(codec2);
248                 ao2_ref(codec, -1);
249
250                 if (!format) {
251                         return -1;
252                 }
253
254                 /* Use the global framing or default framing of the codec */
255                 res = ast_format_cap_append(cap, format, 0);
256                 ao2_ref(format, -1);
257
258                 if (res) {
259                         return -1;
260                 }
261         }
262
263         return 0;
264 }
265
266 int ast_format_cap_append_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
267         enum ast_media_type type)
268 {
269         int idx, res = 0;
270
271         /* NOTE:  The streams API is dependent on the formats being in "preference" order */
272         for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)) && !res; ++idx) {
273                 struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
274
275                 if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
276                         res = ast_format_cap_append(dst, framed->format, framed->framing);
277                 }
278         }
279
280         return res;
281 }
282
283 static int format_cap_replace(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
284 {
285         struct format_cap_framed *framed;
286         int i;
287
288         ast_assert(format != NULL);
289
290         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
291                 framed = AST_VECTOR_GET(&cap->preference_order, i);
292
293                 if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
294                         ao2_t_replace(framed->format, format, "replacing with new format");
295                         framed->framing = framing;
296                         return 0;
297                 }
298         }
299
300         return -1;
301 }
302
303 void ast_format_cap_replace_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
304         enum ast_media_type type)
305 {
306         int idx;
307
308         for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)); ++idx) {
309                 struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
310
311                 if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
312                         format_cap_replace(dst, framed->format, framed->framing);
313                 }
314         }
315 }
316
317 int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing)
318 {
319         int res = 0, all = 0, iter_allowing;
320         char *parse = NULL, *this = NULL, *psize = NULL;
321
322         if (!allowing && ast_strlen_zero(list)) {
323                 return 0;
324         }
325
326         parse = ast_strdupa(list);
327
328         /* If the list is being fed to us as a result of ast_format_cap_get_names,
329          * strip off the parenthesis and immediately apply the inverse of the
330          * allowing option
331          */
332         if (parse[0] == '(' && parse[strlen(parse) - 1] == ')') {
333                 parse++;
334                 parse[strlen(parse) - 1] = '\0';
335
336                 if (allowing) {
337                         ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
338                 } else {
339                         ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
340                 }
341         }
342
343
344         while ((this = ast_strip(strsep(&parse, ",|")))) {
345                 int framems = 0;
346                 struct ast_format *format = NULL;
347
348                 iter_allowing = allowing;
349                 if (*this == '!') {
350                         this++;
351                         iter_allowing = !allowing;
352                 }
353                 if ((psize = strrchr(this, ':'))) {
354                         *psize++ = '\0';
355                         ast_debug(1, "Packetization for codec: %s is %s\n", this, psize);
356                         if (!sscanf(psize, "%30d", &framems) || (framems < 0)) {
357                                 framems = 0;
358                                 res = -1;
359                                 ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this);
360                                 continue;
361                         }
362                 }
363                 all = strcasecmp(this, "all") ? 0 : 1;
364
365                 if (!all && !(format = ast_format_cache_get(this))) {
366                         ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", iter_allowing ? "allow" : "disallow", this);
367                         res = -1;
368                         continue;
369                 }
370
371                 if (cap) {
372                         if (iter_allowing) {
373                                 if (all) {
374                                         ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
375                                 } else {
376                                         ast_format_cap_append(cap, format, framems);
377                                 }
378                         } else {
379                                 if (all) {
380                                         ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
381                                 } else {
382                                         ast_format_cap_remove(cap, format);
383                                 }
384                         }
385                 }
386
387                 ao2_cleanup(format);
388         }
389         return res;
390 }
391
392 size_t ast_format_cap_count(const struct ast_format_cap *cap)
393 {
394         return AST_VECTOR_SIZE(&cap->preference_order);
395 }
396
397 struct ast_format *ast_format_cap_get_format(const struct ast_format_cap *cap, int position)
398 {
399         struct format_cap_framed *framed;
400
401         ast_assert(position < AST_VECTOR_SIZE(&cap->preference_order));
402
403         if (position >= AST_VECTOR_SIZE(&cap->preference_order)) {
404                 return NULL;
405         }
406
407         framed = AST_VECTOR_GET(&cap->preference_order, position);
408
409         ast_assert(framed->format != ast_format_none);
410         ao2_ref(framed->format, +1);
411         return framed->format;
412 }
413
414 struct ast_format *ast_format_cap_get_best_by_type(const struct ast_format_cap *cap, enum ast_media_type type)
415 {
416         int i;
417
418         if (type == AST_MEDIA_TYPE_UNKNOWN) {
419                 return ast_format_cap_get_format(cap, 0);
420         }
421
422         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
423                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
424
425                 if (ast_format_get_type(framed->format) == type) {
426                         ao2_ref(framed->format, +1);
427                         ast_assert(framed->format != ast_format_none);
428                         return framed->format;
429                 }
430         }
431
432         return NULL;
433 }
434
435 unsigned int ast_format_cap_get_framing(const struct ast_format_cap *cap)
436 {
437         return (cap->framing != UINT_MAX) ? cap->framing : 0;
438 }
439
440 unsigned int ast_format_cap_get_format_framing(const struct ast_format_cap *cap, const struct ast_format *format)
441 {
442         unsigned int framing;
443         struct format_cap_framed_list *list;
444         struct format_cap_framed *framed, *result = NULL;
445
446         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
447                 return 0;
448         }
449
450         framing = cap->framing != UINT_MAX ? cap->framing : ast_format_get_default_ms(format);
451         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
452
453         AST_LIST_TRAVERSE(list, framed, entry) {
454                 enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
455
456                 if (res == AST_FORMAT_CMP_NOT_EQUAL) {
457                         continue;
458                 }
459
460                 result = framed;
461
462                 if (res == AST_FORMAT_CMP_EQUAL) {
463                         break;
464                 }
465         }
466
467         if (result && result->framing) {
468                 framing = result->framing;
469         }
470
471         return framing;
472 }
473
474 /*!
475  * \brief format_cap_framed comparator for AST_VECTOR_REMOVE_CMP_ORDERED()
476  *
477  * \param elem Element to compare against
478  * \param value Value to compare with the vector element.
479  *
480  * \return 0 if element does not match.
481  * \return Non-zero if element matches.
482  */
483 #define FORMAT_CAP_FRAMED_ELEM_CMP(elem, value) ((elem)->format == (value))
484
485 /*!
486  * \brief format_cap_framed vector element cleanup.
487  *
488  * \param elem Element to cleanup
489  *
490  * \return Nothing
491  */
492 #define FORMAT_CAP_FRAMED_ELEM_CLEANUP(elem)  ao2_cleanup((elem))
493
494 int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
495 {
496         struct format_cap_framed_list *list;
497         struct format_cap_framed *framed;
498
499         ast_assert(format != NULL);
500
501         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
502                 return -1;
503         }
504
505         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
506
507         AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
508                 if (!FORMAT_CAP_FRAMED_ELEM_CMP(framed, format)) {
509                         continue;
510                 }
511
512                 AST_LIST_REMOVE_CURRENT(entry);
513                 FORMAT_CAP_FRAMED_ELEM_CLEANUP(framed);
514                 break;
515         }
516         AST_LIST_TRAVERSE_SAFE_END;
517
518         return AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, format,
519                 FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
520 }
521
522 void ast_format_cap_remove_by_type(struct ast_format_cap *cap, enum ast_media_type type)
523 {
524         int idx;
525
526         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); ++idx) {
527                 struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx);
528                 struct format_cap_framed *framed;
529
530                 AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
531                         if ((type != AST_MEDIA_TYPE_UNKNOWN) &&
532                                 ast_format_get_type(framed->format) != type) {
533                                 continue;
534                         }
535
536                         AST_LIST_REMOVE_CURRENT(entry);
537                         AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, framed->format,
538                                 FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
539                         ao2_ref(framed, -1);
540                 }
541                 AST_LIST_TRAVERSE_SAFE_END;
542         }
543 }
544
545 struct ast_format *ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format)
546 {
547         struct format_cap_framed_list *list;
548         struct format_cap_framed *framed;
549         struct ast_format *result = NULL;
550
551         ast_assert(format != NULL);
552
553         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
554                 return NULL;
555         }
556
557         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
558
559         AST_LIST_TRAVERSE(list, framed, entry) {
560                 enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
561
562                 if (res == AST_FORMAT_CMP_NOT_EQUAL) {
563                         continue;
564                 }
565
566                 /* Replace any current result, this one will also be a subset OR an exact match */
567                 ao2_cleanup(result);
568
569                 result = ast_format_joint(format, framed->format);
570
571                 /* If it's a match we can do no better so return asap */
572                 if (res == AST_FORMAT_CMP_EQUAL) {
573                         break;
574                 }
575         }
576
577         return result;
578 }
579
580 enum ast_format_cmp_res ast_format_cap_iscompatible_format(const struct ast_format_cap *cap,
581         const struct ast_format *format)
582 {
583         enum ast_format_cmp_res res = AST_FORMAT_CMP_NOT_EQUAL;
584         struct format_cap_framed_list *list;
585         struct format_cap_framed *framed;
586
587         ast_assert(format != NULL);
588
589         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
590                 return AST_FORMAT_CMP_NOT_EQUAL;
591         }
592
593         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
594
595         AST_LIST_TRAVERSE(list, framed, entry) {
596                 enum ast_format_cmp_res cmp = ast_format_cmp(format, framed->format);
597
598                 if (cmp == AST_FORMAT_CMP_NOT_EQUAL) {
599                         continue;
600                 }
601
602                 res = cmp;
603
604                 if (res == AST_FORMAT_CMP_EQUAL) {
605                         break;
606                 }
607         }
608
609         return res;
610 }
611
612 int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_type type)
613 {
614         int idx;
615
616         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); ++idx) {
617                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx);
618
619                 if (ast_format_get_type(framed->format) == type) {
620                         return 1;
621                 }
622         }
623
624         return 0;
625 }
626
627 int ast_format_cap_get_compatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2,
628         struct ast_format_cap *result)
629 {
630         int idx, res = 0;
631
632         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
633                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
634                 struct ast_format *format;
635
636                 format = ast_format_cap_get_compatible_format(cap2, framed->format);
637                 if (!format) {
638                         continue;
639                 }
640
641                 res = ast_format_cap_append(result, format, framed->framing);
642                 ao2_ref(format, -1);
643
644                 if (res) {
645                         break;
646                 }
647         }
648
649         return res;
650 }
651
652 int ast_format_cap_iscompatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
653 {
654         int idx;
655
656         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
657                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
658
659                 if (ast_format_cap_iscompatible_format(cap2, framed->format) != AST_FORMAT_CMP_NOT_EQUAL) {
660                         return 1;
661                 }
662         }
663
664         return 0;
665 }
666
667 static int internal_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
668 {
669         int idx;
670         struct ast_format *tmp;
671
672         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
673                 tmp = ast_format_cap_get_format(cap1, idx);
674
675                 if (ast_format_cap_iscompatible_format(cap2, tmp) != AST_FORMAT_CMP_EQUAL) {
676                         ao2_ref(tmp, -1);
677                         return 0;
678                 }
679
680                 ao2_ref(tmp, -1);
681         }
682
683         return 1;
684 }
685
686 int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
687 {
688         if (AST_VECTOR_SIZE(&cap1->preference_order) != AST_VECTOR_SIZE(&cap2->preference_order)) {
689                 return 0; /* if they are not the same size, they are not identical */
690         }
691
692         if (!internal_format_cap_identical(cap1, cap2)) {
693                 return 0;
694         }
695
696         return internal_format_cap_identical(cap2, cap1);
697 }
698
699 const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf)
700 {
701         int i;
702
703         ast_str_set(buf, 0, "(");
704
705         if (!cap || !AST_VECTOR_SIZE(&cap->preference_order)) {
706                 ast_str_append(buf, 0, "nothing)");
707                 return ast_str_buffer(*buf);
708         }
709
710         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); ++i) {
711                 int res;
712                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
713
714                 res = ast_str_append(buf, 0, "%s%s", ast_format_get_name(framed->format),
715                         i < AST_VECTOR_SIZE(&cap->preference_order) - 1 ? "|" : "");
716                 if (res < 0) {
717                         break;
718                 }
719         }
720         ast_str_append(buf, 0, ")");
721
722         return ast_str_buffer(*buf);
723 }
724
725 int ast_format_cap_empty(struct ast_format_cap *cap)
726 {
727         int count = ast_format_cap_count(cap);
728
729         if (count > 1) {
730                 return 0;
731         }
732
733         if (count == 0 || AST_VECTOR_GET(&cap->preference_order, 0)->format == ast_format_none) {
734                 return 1;
735         }
736
737         return 0;
738 }