Merge "CLI: Remove special handling of 'core set verbose' from rasterisk."
[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         /* This takes the allocation reference */
164         if (AST_VECTOR_APPEND(&cap->preference_order, framed)) {
165                 ao2_ref(framed, -1);
166                 return -1;
167         }
168
169         /* Order doesn't matter for formats, so insert at the head for performance reasons */
170         ao2_ref(framed, +1);
171         AST_LIST_INSERT_HEAD(list, framed, entry);
172
173         cap->framing = MIN(cap->framing, framing ? framing : ast_format_get_default_ms(format));
174
175         return 0;
176 }
177
178 /*! \internal \brief Determine if \c format is in \c cap */
179 static int format_in_format_cap(struct ast_format_cap *cap, struct ast_format *format)
180 {
181         struct format_cap_framed *framed;
182         int i;
183
184         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
185                 framed = AST_VECTOR_GET(&cap->preference_order, i);
186
187                 if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
188                         return 1;
189                 }
190         }
191
192         return 0;
193 }
194
195 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)
196 {
197         struct format_cap_framed *framed;
198
199         ast_assert(format != NULL);
200
201         if (format_in_format_cap(cap, format)) {
202                 return 0;
203         }
204
205         framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
206         if (!framed) {
207                 return -1;
208         }
209
210         __ao2_ref(format, +1, tag, file, line, func);
211         framed->format = format;
212
213         return format_cap_framed_init(framed, cap, format, framing);
214 }
215
216 int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
217 {
218         int id;
219
220         for (id = 1; id < ast_codec_get_max(); ++id) {
221                 struct ast_codec *codec = ast_codec_get_by_id(id);
222                 struct ast_codec *codec2 = NULL;
223                 struct ast_format *format;
224                 int res;
225
226                 if (!codec) {
227                         continue;
228                 }
229
230                 if ((type != AST_MEDIA_TYPE_UNKNOWN) && codec->type != type) {
231                         ao2_ref(codec, -1);
232                         continue;
233                 }
234
235                 format = ast_format_cache_get(codec->name);
236
237                 if (format == ast_format_none) {
238                         ao2_ref(format, -1);
239                         ao2_ref(codec, -1);
240                         continue;
241                 }
242
243                 if (format) {
244                         codec2 = ast_format_get_codec(format);
245                 }
246                 if (codec != codec2) {
247                         ao2_cleanup(format);
248                         format = ast_format_create(codec);
249                 }
250                 ao2_cleanup(codec2);
251                 ao2_ref(codec, -1);
252
253                 if (!format) {
254                         return -1;
255                 }
256
257                 /* Use the global framing or default framing of the codec */
258                 res = ast_format_cap_append(cap, format, 0);
259                 ao2_ref(format, -1);
260
261                 if (res) {
262                         return -1;
263                 }
264         }
265
266         return 0;
267 }
268
269 int ast_format_cap_append_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
270         enum ast_media_type type)
271 {
272         int idx, res = 0;
273
274         /* NOTE:  The streams API is dependent on the formats being in "preference" order */
275         for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)) && !res; ++idx) {
276                 struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
277
278                 if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
279                         res = ast_format_cap_append(dst, framed->format, framed->framing);
280                 }
281         }
282
283         return res;
284 }
285
286 static int format_cap_replace(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
287 {
288         struct format_cap_framed *framed;
289         int i;
290
291         ast_assert(format != NULL);
292
293         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
294                 framed = AST_VECTOR_GET(&cap->preference_order, i);
295
296                 if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
297                         ao2_t_replace(framed->format, format, "replacing with new format");
298                         framed->framing = framing;
299                         return 0;
300                 }
301         }
302
303         return -1;
304 }
305
306 void ast_format_cap_replace_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
307         enum ast_media_type type)
308 {
309         int idx;
310
311         for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)); ++idx) {
312                 struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
313
314                 if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
315                         format_cap_replace(dst, framed->format, framed->framing);
316                 }
317         }
318 }
319
320 int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing)
321 {
322         int res = 0, all = 0, iter_allowing;
323         char *parse = NULL, *this = NULL, *psize = NULL;
324
325         if (!allowing && ast_strlen_zero(list)) {
326                 return 0;
327         }
328
329         parse = ast_strdupa(list);
330
331         /* If the list is being fed to us as a result of ast_format_cap_get_names,
332          * strip off the parenthesis and immediately apply the inverse of the
333          * allowing option
334          */
335         if (parse[0] == '(' && parse[strlen(parse) - 1] == ')') {
336                 parse++;
337                 parse[strlen(parse) - 1] = '\0';
338
339                 if (allowing) {
340                         ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
341                 } else {
342                         ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
343                 }
344         }
345
346
347         while ((this = ast_strip(strsep(&parse, ",|")))) {
348                 int framems = 0;
349                 struct ast_format *format = NULL;
350
351                 iter_allowing = allowing;
352                 if (*this == '!') {
353                         this++;
354                         iter_allowing = !allowing;
355                 }
356                 if ((psize = strrchr(this, ':'))) {
357                         *psize++ = '\0';
358                         ast_debug(1, "Packetization for codec: %s is %s\n", this, psize);
359                         if (!sscanf(psize, "%30d", &framems) || (framems < 0)) {
360                                 framems = 0;
361                                 res = -1;
362                                 ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this);
363                                 continue;
364                         }
365                 }
366                 all = strcasecmp(this, "all") ? 0 : 1;
367
368                 if (!all && !(format = ast_format_cache_get(this))) {
369                         ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", iter_allowing ? "allow" : "disallow", this);
370                         res = -1;
371                         continue;
372                 }
373
374                 if (cap) {
375                         if (iter_allowing) {
376                                 if (all) {
377                                         ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
378                                 } else {
379                                         ast_format_cap_append(cap, format, framems);
380                                 }
381                         } else {
382                                 if (all) {
383                                         ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
384                                 } else {
385                                         ast_format_cap_remove(cap, format);
386                                 }
387                         }
388                 }
389
390                 ao2_cleanup(format);
391         }
392         return res;
393 }
394
395 size_t ast_format_cap_count(const struct ast_format_cap *cap)
396 {
397         return AST_VECTOR_SIZE(&cap->preference_order);
398 }
399
400 struct ast_format *ast_format_cap_get_format(const struct ast_format_cap *cap, int position)
401 {
402         struct format_cap_framed *framed;
403
404         ast_assert(position < AST_VECTOR_SIZE(&cap->preference_order));
405
406         if (position >= AST_VECTOR_SIZE(&cap->preference_order)) {
407                 return NULL;
408         }
409
410         framed = AST_VECTOR_GET(&cap->preference_order, position);
411
412         ast_assert(framed->format != ast_format_none);
413         ao2_ref(framed->format, +1);
414         return framed->format;
415 }
416
417 struct ast_format *ast_format_cap_get_best_by_type(const struct ast_format_cap *cap, enum ast_media_type type)
418 {
419         int i;
420
421         if (type == AST_MEDIA_TYPE_UNKNOWN) {
422                 return ast_format_cap_get_format(cap, 0);
423         }
424
425         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
426                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
427
428                 if (ast_format_get_type(framed->format) == type) {
429                         ao2_ref(framed->format, +1);
430                         ast_assert(framed->format != ast_format_none);
431                         return framed->format;
432                 }
433         }
434
435         return NULL;
436 }
437
438 unsigned int ast_format_cap_get_framing(const struct ast_format_cap *cap)
439 {
440         return (cap->framing != UINT_MAX) ? cap->framing : 0;
441 }
442
443 unsigned int ast_format_cap_get_format_framing(const struct ast_format_cap *cap, const struct ast_format *format)
444 {
445         unsigned int framing;
446         struct format_cap_framed_list *list;
447         struct format_cap_framed *framed, *result = NULL;
448
449         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
450                 return 0;
451         }
452
453         framing = cap->framing != UINT_MAX ? cap->framing : ast_format_get_default_ms(format);
454         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
455
456         AST_LIST_TRAVERSE(list, framed, entry) {
457                 enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
458
459                 if (res == AST_FORMAT_CMP_NOT_EQUAL) {
460                         continue;
461                 }
462
463                 result = framed;
464
465                 if (res == AST_FORMAT_CMP_EQUAL) {
466                         break;
467                 }
468         }
469
470         if (result && result->framing) {
471                 framing = result->framing;
472         }
473
474         return framing;
475 }
476
477 /*!
478  * \brief format_cap_framed comparator for AST_VECTOR_REMOVE_CMP_ORDERED()
479  *
480  * \param elem Element to compare against
481  * \param value Value to compare with the vector element.
482  *
483  * \return 0 if element does not match.
484  * \return Non-zero if element matches.
485  */
486 #define FORMAT_CAP_FRAMED_ELEM_CMP(elem, value) ((elem)->format == (value))
487
488 /*!
489  * \brief format_cap_framed vector element cleanup.
490  *
491  * \param elem Element to cleanup
492  *
493  * \return Nothing
494  */
495 #define FORMAT_CAP_FRAMED_ELEM_CLEANUP(elem)  ao2_cleanup((elem))
496
497 int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
498 {
499         struct format_cap_framed_list *list;
500         struct format_cap_framed *framed;
501
502         ast_assert(format != NULL);
503
504         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
505                 return -1;
506         }
507
508         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
509
510         AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
511                 if (!FORMAT_CAP_FRAMED_ELEM_CMP(framed, format)) {
512                         continue;
513                 }
514
515                 AST_LIST_REMOVE_CURRENT(entry);
516                 FORMAT_CAP_FRAMED_ELEM_CLEANUP(framed);
517                 break;
518         }
519         AST_LIST_TRAVERSE_SAFE_END;
520
521         return AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, format,
522                 FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
523 }
524
525 void ast_format_cap_remove_by_type(struct ast_format_cap *cap, enum ast_media_type type)
526 {
527         int idx;
528
529         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); ++idx) {
530                 struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx);
531                 struct format_cap_framed *framed;
532
533                 AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
534                         if ((type != AST_MEDIA_TYPE_UNKNOWN) &&
535                                 ast_format_get_type(framed->format) != type) {
536                                 continue;
537                         }
538
539                         AST_LIST_REMOVE_CURRENT(entry);
540                         AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, framed->format,
541                                 FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
542                         ao2_ref(framed, -1);
543                 }
544                 AST_LIST_TRAVERSE_SAFE_END;
545         }
546 }
547
548 struct ast_format *ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format)
549 {
550         struct format_cap_framed_list *list;
551         struct format_cap_framed *framed;
552         struct ast_format *result = NULL;
553
554         ast_assert(format != NULL);
555
556         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
557                 return NULL;
558         }
559
560         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
561
562         AST_LIST_TRAVERSE(list, framed, entry) {
563                 enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
564
565                 if (res == AST_FORMAT_CMP_NOT_EQUAL) {
566                         continue;
567                 }
568
569                 /* Replace any current result, this one will also be a subset OR an exact match */
570                 ao2_cleanup(result);
571
572                 result = ast_format_joint(format, framed->format);
573
574                 /* If it's a match we can do no better so return asap */
575                 if (res == AST_FORMAT_CMP_EQUAL) {
576                         break;
577                 }
578         }
579
580         return result;
581 }
582
583 enum ast_format_cmp_res ast_format_cap_iscompatible_format(const struct ast_format_cap *cap,
584         const struct ast_format *format)
585 {
586         enum ast_format_cmp_res res = AST_FORMAT_CMP_NOT_EQUAL;
587         struct format_cap_framed_list *list;
588         struct format_cap_framed *framed;
589
590         ast_assert(format != NULL);
591
592         if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
593                 return AST_FORMAT_CMP_NOT_EQUAL;
594         }
595
596         list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
597
598         AST_LIST_TRAVERSE(list, framed, entry) {
599                 enum ast_format_cmp_res cmp = ast_format_cmp(format, framed->format);
600
601                 if (cmp == AST_FORMAT_CMP_NOT_EQUAL) {
602                         continue;
603                 }
604
605                 res = cmp;
606
607                 if (res == AST_FORMAT_CMP_EQUAL) {
608                         break;
609                 }
610         }
611
612         return res;
613 }
614
615 int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_type type)
616 {
617         int idx;
618
619         for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); ++idx) {
620                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx);
621
622                 if (ast_format_get_type(framed->format) == type) {
623                         return 1;
624                 }
625         }
626
627         return 0;
628 }
629
630 int ast_format_cap_get_compatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2,
631         struct ast_format_cap *result)
632 {
633         int idx, res = 0;
634
635         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
636                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
637                 struct ast_format *format;
638
639                 format = ast_format_cap_get_compatible_format(cap2, framed->format);
640                 if (!format) {
641                         continue;
642                 }
643
644                 res = ast_format_cap_append(result, format, framed->framing);
645                 ao2_ref(format, -1);
646
647                 if (res) {
648                         break;
649                 }
650         }
651
652         return res;
653 }
654
655 int ast_format_cap_iscompatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
656 {
657         int idx;
658
659         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
660                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
661
662                 if (ast_format_cap_iscompatible_format(cap2, framed->format) != AST_FORMAT_CMP_NOT_EQUAL) {
663                         return 1;
664                 }
665         }
666
667         return 0;
668 }
669
670 static int internal_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
671 {
672         int idx;
673         struct ast_format *tmp;
674
675         for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
676                 tmp = ast_format_cap_get_format(cap1, idx);
677
678                 if (ast_format_cap_iscompatible_format(cap2, tmp) != AST_FORMAT_CMP_EQUAL) {
679                         ao2_ref(tmp, -1);
680                         return 0;
681                 }
682
683                 ao2_ref(tmp, -1);
684         }
685
686         return 1;
687 }
688
689 int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
690 {
691         if (AST_VECTOR_SIZE(&cap1->preference_order) != AST_VECTOR_SIZE(&cap2->preference_order)) {
692                 return 0; /* if they are not the same size, they are not identical */
693         }
694
695         if (!internal_format_cap_identical(cap1, cap2)) {
696                 return 0;
697         }
698
699         return internal_format_cap_identical(cap2, cap1);
700 }
701
702 const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf)
703 {
704         int i;
705
706         ast_str_set(buf, 0, "(");
707
708         if (!cap || !AST_VECTOR_SIZE(&cap->preference_order)) {
709                 ast_str_append(buf, 0, "nothing)");
710                 return ast_str_buffer(*buf);
711         }
712
713         for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); ++i) {
714                 int res;
715                 struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
716
717                 res = ast_str_append(buf, 0, "%s%s", ast_format_get_name(framed->format),
718                         i < AST_VECTOR_SIZE(&cap->preference_order) - 1 ? "|" : "");
719                 if (res < 0) {
720                         break;
721                 }
722         }
723         ast_str_append(buf, 0, ")");
724
725         return ast_str_buffer(*buf);
726 }
727
728 int ast_format_cap_empty(struct ast_format_cap *cap)
729 {
730         int count = ast_format_cap_count(cap);
731
732         if (count > 1) {
733                 return 0;
734         }
735
736         if (count == 0 || AST_VECTOR_GET(&cap->preference_order, 0)->format == ast_format_none) {
737                 return 1;
738         }
739
740         return 0;
741 }