123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2014, Digium, Inc.
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*! \file
- *
- * \brief Format Capabilities API
- *
- * \author Joshua Colp <jcolp@digium.com>
- */
- /*** MODULEINFO
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include "asterisk/logger.h"
- #include "asterisk/format.h"
- #include "asterisk/format_cap.h"
- #include "asterisk/format_cache.h"
- #include "asterisk/codec.h"
- #include "asterisk/astobj2.h"
- #include "asterisk/strings.h"
- #include "asterisk/vector.h"
- #include "asterisk/linkedlists.h"
- #include "asterisk/utils.h"
- /*! \brief Structure used for capability formats, adds framing */
- struct format_cap_framed {
- /*! \brief A pointer to the format */
- struct ast_format *format;
- /*! \brief The format framing size */
- unsigned int framing;
- /*! \brief Linked list information */
- AST_LIST_ENTRY(format_cap_framed) entry;
- };
- /*! \brief Format capabilities structure, holds formats + preference order + etc */
- struct ast_format_cap {
- /*! \brief Vector of formats, indexed using the codec identifier */
- AST_VECTOR(, struct format_cap_framed_list) formats;
- /*! \brief Vector of formats, added in preference order */
- AST_VECTOR(, struct format_cap_framed *) preference_order;
- /*! \brief Global framing size, applies to all formats if no framing present on format */
- unsigned int framing;
- };
- /*! \brief Linked list for formats */
- AST_LIST_HEAD_NOLOCK(format_cap_framed_list, format_cap_framed);
- /*! \brief Dummy empty list for when we are inserting a new list */
- static const struct format_cap_framed_list format_cap_framed_list_empty = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
- /*! \brief Destructor for format capabilities structure */
- static void format_cap_destroy(void *obj)
- {
- struct ast_format_cap *cap = obj;
- int idx;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); idx++) {
- struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx);
- struct format_cap_framed *framed;
- while ((framed = AST_LIST_REMOVE_HEAD(list, entry))) {
- ao2_ref(framed, -1);
- }
- }
- AST_VECTOR_FREE(&cap->formats);
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); idx++) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx);
- /* This will always be non-null, unlike formats */
- ao2_ref(framed, -1);
- }
- AST_VECTOR_FREE(&cap->preference_order);
- }
- static inline void format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
- {
- AST_VECTOR_INIT(&cap->formats, 0);
- /* TODO: Look at common usage of this and determine a good starting point */
- AST_VECTOR_INIT(&cap->preference_order, 5);
- cap->framing = UINT_MAX;
- }
- struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags)
- {
- struct ast_format_cap *cap;
- cap = ao2_alloc_options(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
- if (!cap) {
- return NULL;
- }
- format_cap_init(cap, flags);
- return cap;
- }
- struct ast_format_cap *__ast_format_cap_alloc_debug(enum ast_format_cap_flags flags, const char *tag, const char *file, int line, const char *func)
- {
- struct ast_format_cap *cap;
- cap = __ao2_alloc_debug(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(tag, "ast_format_cap_alloc"), file, line, func, 1);
- if (!cap) {
- return NULL;
- }
- format_cap_init(cap, flags);
- return cap;
- }
- void ast_format_cap_set_framing(struct ast_format_cap *cap, unsigned int framing)
- {
- cap->framing = framing;
- }
- /*! \brief Destructor for format capabilities framed structure */
- static void format_cap_framed_destroy(void *obj)
- {
- struct format_cap_framed *framed = obj;
- ao2_cleanup(framed->format);
- }
- static inline int format_cap_framed_init(struct format_cap_framed *framed, struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
- {
- struct format_cap_framed_list *list;
- framed->framing = framing;
- if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
- if (AST_VECTOR_INSERT(&cap->formats, ast_format_get_codec_id(format), format_cap_framed_list_empty)) {
- ao2_ref(framed, -1);
- return -1;
- }
- }
- list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
- /* Order doesn't matter for formats, so insert at the head for performance reasons */
- ao2_ref(framed, +1);
- AST_LIST_INSERT_HEAD(list, framed, entry);
- /* This takes the allocation reference */
- AST_VECTOR_APPEND(&cap->preference_order, framed);
- cap->framing = MIN(cap->framing, framing ? framing : ast_format_get_default_ms(format));
- return 0;
- }
- /*! \internal \brief Determine if \c format is in \c cap */
- static int format_in_format_cap(struct ast_format_cap *cap, struct ast_format *format)
- {
- struct format_cap_framed *framed;
- int i;
- for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
- framed = AST_VECTOR_GET(&cap->preference_order, i);
- if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
- return 1;
- }
- }
- return 0;
- }
- int __ast_format_cap_append(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
- {
- struct format_cap_framed *framed;
- ast_assert(format != NULL);
- if (format_in_format_cap(cap, format)) {
- return 0;
- }
- framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
- if (!framed) {
- return -1;
- }
- framed->format = ao2_bump(format);
- return format_cap_framed_init(framed, cap, format, framing);
- }
- int __ast_format_cap_append_debug(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing, const char *tag, const char *file, int line, const char *func)
- {
- struct format_cap_framed *framed;
- ast_assert(format != NULL);
- if (format_in_format_cap(cap, format)) {
- return 0;
- }
- framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
- if (!framed) {
- return -1;
- }
- __ao2_ref_debug(format, +1, S_OR(tag, "ast_format_cap_append"), file, line, func);
- framed->format = format;
- return format_cap_framed_init(framed, cap, format, framing);
- }
- int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
- {
- int id;
- for (id = 1; id < ast_codec_get_max(); ++id) {
- struct ast_codec *codec = ast_codec_get_by_id(id);
- struct ast_format *format;
- int res;
- if (!codec) {
- continue;
- }
- if ((type != AST_MEDIA_TYPE_UNKNOWN) && codec->type != type) {
- ao2_ref(codec, -1);
- continue;
- }
- format = ast_format_create(codec);
- ao2_ref(codec, -1);
- if (!format) {
- return -1;
- }
- /* Use the global framing or default framing of the codec */
- res = ast_format_cap_append(cap, format, 0);
- ao2_ref(format, -1);
- if (res) {
- return -1;
- }
- }
- return 0;
- }
- int ast_format_cap_append_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
- enum ast_media_type type)
- {
- int idx, res = 0;
- for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)) && !res; ++idx) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
- if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
- res = ast_format_cap_append(dst, framed->format, framed->framing);
- }
- }
- return res;
- }
- static int format_cap_replace(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing)
- {
- struct format_cap_framed *framed;
- int i;
- ast_assert(format != NULL);
- for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
- framed = AST_VECTOR_GET(&cap->preference_order, i);
- if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) {
- ao2_t_replace(framed->format, format, "replacing with new format");
- framed->framing = framing;
- return 0;
- }
- }
- return -1;
- }
- void ast_format_cap_replace_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src,
- enum ast_media_type type)
- {
- int idx;
- for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)); ++idx) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx);
- if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) {
- format_cap_replace(dst, framed->format, framed->framing);
- }
- }
- }
- int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing)
- {
- int res = 0, all = 0, iter_allowing;
- char *parse = NULL, *this = NULL, *psize = NULL;
- if (!allowing && ast_strlen_zero(list)) {
- return 0;
- }
- parse = ast_strdupa(list);
- while ((this = strsep(&parse, ","))) {
- int framems = 0;
- struct ast_format *format = NULL;
- iter_allowing = allowing;
- if (*this == '!') {
- this++;
- iter_allowing = !allowing;
- }
- if ((psize = strrchr(this, ':'))) {
- *psize++ = '\0';
- ast_debug(1, "Packetization for codec: %s is %s\n", this, psize);
- if (!sscanf(psize, "%30d", &framems) || (framems < 0)) {
- framems = 0;
- res = -1;
- ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this);
- continue;
- }
- }
- all = strcasecmp(this, "all") ? 0 : 1;
- if (!all && !(format = ast_format_cache_get(this))) {
- ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", iter_allowing ? "allow" : "disallow", this);
- res = -1;
- continue;
- }
- if (cap) {
- if (iter_allowing) {
- if (all) {
- ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
- } else {
- ast_format_cap_append(cap, format, framems);
- }
- } else {
- if (all) {
- ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
- } else {
- ast_format_cap_remove(cap, format);
- }
- }
- }
- ao2_cleanup(format);
- }
- return res;
- }
- size_t ast_format_cap_count(const struct ast_format_cap *cap)
- {
- return AST_VECTOR_SIZE(&cap->preference_order);
- }
- struct ast_format *ast_format_cap_get_format(const struct ast_format_cap *cap, int position)
- {
- struct format_cap_framed *framed;
- ast_assert(position < AST_VECTOR_SIZE(&cap->preference_order));
- if (position >= AST_VECTOR_SIZE(&cap->preference_order)) {
- return NULL;
- }
- framed = AST_VECTOR_GET(&cap->preference_order, position);
- ast_assert(framed->format != ast_format_none);
- ao2_ref(framed->format, +1);
- return framed->format;
- }
- struct ast_format *ast_format_cap_get_best_by_type(const struct ast_format_cap *cap, enum ast_media_type type)
- {
- int i;
- if (type == AST_MEDIA_TYPE_UNKNOWN) {
- return ast_format_cap_get_format(cap, 0);
- }
- for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
- if (ast_format_get_type(framed->format) == type) {
- ao2_ref(framed->format, +1);
- ast_assert(framed->format != ast_format_none);
- return framed->format;
- }
- }
- return NULL;
- }
- unsigned int ast_format_cap_get_framing(const struct ast_format_cap *cap)
- {
- return (cap->framing != UINT_MAX) ? cap->framing : 0;
- }
- unsigned int ast_format_cap_get_format_framing(const struct ast_format_cap *cap, const struct ast_format *format)
- {
- unsigned int framing;
- struct format_cap_framed_list *list;
- struct format_cap_framed *framed, *result = NULL;
- if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
- return 0;
- }
- framing = cap->framing != UINT_MAX ? cap->framing : ast_format_get_default_ms(format);
- list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
- AST_LIST_TRAVERSE(list, framed, entry) {
- enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
- if (res == AST_FORMAT_CMP_NOT_EQUAL) {
- continue;
- }
- result = framed;
- if (res == AST_FORMAT_CMP_EQUAL) {
- break;
- }
- }
- if (result && result->framing) {
- framing = result->framing;
- }
- return framing;
- }
- /*!
- * \brief format_cap_framed comparator for AST_VECTOR_REMOVE_CMP_ORDERED()
- *
- * \param elem Element to compare against
- * \param value Value to compare with the vector element.
- *
- * \return 0 if element does not match.
- * \return Non-zero if element matches.
- */
- #define FORMAT_CAP_FRAMED_ELEM_CMP(elem, value) ((elem)->format == (value))
- /*!
- * \brief format_cap_framed vector element cleanup.
- *
- * \param elem Element to cleanup
- *
- * \return Nothing
- */
- #define FORMAT_CAP_FRAMED_ELEM_CLEANUP(elem) ao2_cleanup((elem))
- int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
- {
- struct format_cap_framed_list *list;
- struct format_cap_framed *framed;
- ast_assert(format != NULL);
- if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
- return -1;
- }
- list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
- AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
- if (!FORMAT_CAP_FRAMED_ELEM_CMP(framed, format)) {
- continue;
- }
- AST_LIST_REMOVE_CURRENT(entry);
- FORMAT_CAP_FRAMED_ELEM_CLEANUP(framed);
- break;
- }
- AST_LIST_TRAVERSE_SAFE_END;
- return AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, format,
- FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
- }
- void ast_format_cap_remove_by_type(struct ast_format_cap *cap, enum ast_media_type type)
- {
- int idx;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); ++idx) {
- struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx);
- struct format_cap_framed *framed;
- AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) {
- if ((type != AST_MEDIA_TYPE_UNKNOWN) &&
- ast_format_get_type(framed->format) != type) {
- continue;
- }
- AST_LIST_REMOVE_CURRENT(entry);
- AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, framed->format,
- FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP);
- ao2_ref(framed, -1);
- }
- AST_LIST_TRAVERSE_SAFE_END;
- }
- }
- struct ast_format *ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format)
- {
- struct format_cap_framed_list *list;
- struct format_cap_framed *framed;
- struct ast_format *result = NULL;
- ast_assert(format != NULL);
- if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
- return NULL;
- }
- list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
- AST_LIST_TRAVERSE(list, framed, entry) {
- enum ast_format_cmp_res res = ast_format_cmp(format, framed->format);
- if (res == AST_FORMAT_CMP_NOT_EQUAL) {
- continue;
- }
- /* Replace any current result, this one will also be a subset OR an exact match */
- ao2_cleanup(result);
- result = ast_format_joint(format, framed->format);
- /* If it's a match we can do no better so return asap */
- if (res == AST_FORMAT_CMP_EQUAL) {
- break;
- }
- }
- return result;
- }
- enum ast_format_cmp_res ast_format_cap_iscompatible_format(const struct ast_format_cap *cap,
- const struct ast_format *format)
- {
- enum ast_format_cmp_res res = AST_FORMAT_CMP_NOT_EQUAL;
- struct format_cap_framed_list *list;
- struct format_cap_framed *framed;
- ast_assert(format != NULL);
- if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
- return AST_FORMAT_CMP_NOT_EQUAL;
- }
- list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format));
- AST_LIST_TRAVERSE(list, framed, entry) {
- enum ast_format_cmp_res cmp = ast_format_cmp(format, framed->format);
- if (cmp == AST_FORMAT_CMP_NOT_EQUAL) {
- continue;
- }
- res = cmp;
- if (res == AST_FORMAT_CMP_EQUAL) {
- break;
- }
- }
- return res;
- }
- int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_type type)
- {
- int idx;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); ++idx) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx);
- if (ast_format_get_type(framed->format) == type) {
- return 1;
- }
- }
- return 0;
- }
- int ast_format_cap_get_compatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2,
- struct ast_format_cap *result)
- {
- int idx, res = 0;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
- struct ast_format *format;
- format = ast_format_cap_get_compatible_format(cap2, framed->format);
- if (!format) {
- continue;
- }
- res = ast_format_cap_append(result, format, framed->framing);
- ao2_ref(format, -1);
- if (res) {
- break;
- }
- }
- return res;
- }
- int ast_format_cap_iscompatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
- {
- int idx;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx);
- if (ast_format_cap_iscompatible_format(cap2, framed->format) != AST_FORMAT_CMP_NOT_EQUAL) {
- return 1;
- }
- }
- return 0;
- }
- static int internal_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
- {
- int idx;
- struct ast_format *tmp;
- for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) {
- tmp = ast_format_cap_get_format(cap1, idx);
- if (ast_format_cap_iscompatible_format(cap2, tmp) != AST_FORMAT_CMP_EQUAL) {
- ao2_ref(tmp, -1);
- return 0;
- }
- ao2_ref(tmp, -1);
- }
- return 1;
- }
- int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
- {
- if (AST_VECTOR_SIZE(&cap1->preference_order) != AST_VECTOR_SIZE(&cap2->preference_order)) {
- return 0; /* if they are not the same size, they are not identical */
- }
- if (!internal_format_cap_identical(cap1, cap2)) {
- return 0;
- }
- return internal_format_cap_identical(cap2, cap1);
- }
- const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf)
- {
- int i;
- ast_str_set(buf, 0, "(");
- if (!AST_VECTOR_SIZE(&cap->preference_order)) {
- ast_str_append(buf, 0, "nothing)");
- return ast_str_buffer(*buf);
- }
- for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); ++i) {
- int res;
- struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i);
- res = ast_str_append(buf, 0, "%s%s", ast_format_get_name(framed->format),
- i < AST_VECTOR_SIZE(&cap->preference_order) - 1 ? "|" : "");
- if (res < 0) {
- break;
- }
- }
- ast_str_append(buf, 0, ")");
- return ast_str_buffer(*buf);
- }
- int ast_format_cap_empty(struct ast_format_cap *cap)
- {
- int count = ast_format_cap_count(cap);
- if (count > 1) {
- return 0;
- }
- if (count == 0 || AST_VECTOR_GET(&cap->preference_order, 0)->format == ast_format_none) {
- return 1;
- }
- return 0;
- }
|