diff options
Diffstat (limited to 'main/format.c')
-rw-r--r-- | main/format.c | 1473 |
1 files changed, 201 insertions, 1272 deletions
diff --git a/main/format.c b/main/format.c index c4ad45b7f..83206f28c 100644 --- a/main/format.c +++ b/main/format.c @@ -1,10 +1,9 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2010, Digium, Inc. + * Copyright (C) 2014, Digium, Inc. * - * David Vossel <dvossel@digium.com> - * Mark Spencer <markster@digium.com> + * Joshua Colp <jcolp@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -17,12 +16,11 @@ * at the top of the source tree. */ -/*! - * \file - * \brief Format API +/*! \file * - * \author David Vossel <dvossel@digium.com> - * \author Mark Spencer <markster@digium.com> + * \brief Media Format API + * + * \author Joshua Colp <jcolp@digium.com> */ /*** MODULEINFO @@ -31,1420 +29,351 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include "asterisk/_private.h" +#include "asterisk/logger.h" +#include "asterisk/codec.h" #include "asterisk/format.h" #include "asterisk/astobj2.h" -#include "asterisk/lock.h" -#include "asterisk/frame.h" -#include "asterisk/utils.h" -#include "asterisk/cli.h" -#include "asterisk/rtp_engine.h" -#include "asterisk/config.h" - -#define FORMAT_CONFIG "codecs.conf" - -/*! - * \brief Container for all the format attribute interfaces. - * \note This container uses RWLOCKs instead of MUTEX locks. . - * \note An ao2 container was chosen for fast lookup. - */ -static struct ao2_container *interfaces; - -/*! a wrapper is used put interfaces into the ao2 container. */ -struct interface_ao2_wrapper { - enum ast_format_id id; - const struct ast_format_attr_interface *interface; +#include "asterisk/strings.h" + +/*! \brief Number of buckets to use for format interfaces (should be prime for performance reasons) */ +#define FORMAT_INTERFACE_BUCKETS 53 + +/*! \brief Definition of a media format */ +struct ast_format { + /*! Name of the format */ + const char *name; + /*! \brief Pointer to the codec in use for this format */ + struct ast_codec *codec; + /*! \brief Attribute specific data, implementation specific */ + void *attribute_data; + /*! \brief Pointer to the optional format interface */ + const struct ast_format_interface *interface; }; -/*! \brief Format List container, This container is never directly accessed outside - * of this file, and It only exists for building the format_list_array. */ -static struct ao2_container *format_list; -/*! \brief Format List array is a read only array protected by a read write lock. - * This array may be used outside this file with the use of reference counting to - * guarantee safety for access by multiple threads. */ -static struct ast_format_list *format_list_array; -static size_t format_list_array_len = 0; -/*! \brief Locks the format list array so a reference can be taken safely. */ -static ast_rwlock_t format_list_array_lock; - -static int interface_cmp_cb(void *obj, void *arg, int flags) -{ - struct interface_ao2_wrapper *wrapper1 = obj; - struct interface_ao2_wrapper *wrapper2 = arg; - - return (wrapper2->id == wrapper1->id) ? CMP_MATCH | CMP_STOP : 0; -} - -static int interface_hash_cb(const void *obj, const int flags) -{ - const struct interface_ao2_wrapper *wrapper = obj; - return wrapper->id; -} - -void ast_format_copy(struct ast_format *dst, const struct ast_format *src) -{ - *dst = *src; -} - -void ast_format_set_video_mark(struct ast_format *format) -{ - format->fattr.rtp_marker_bit = 1; -} - -int ast_format_get_video_mark(const struct ast_format *format) -{ - return format->fattr.rtp_marker_bit; -} - -static struct interface_ao2_wrapper *find_interface(const struct ast_format *format) -{ - struct interface_ao2_wrapper tmp_wrapper = { - .id = format->id, - }; +/*! \brief Structure used when registering a format interface */ +struct format_interface { + /*! \brief Pointer to the format interface itself */ + const struct ast_format_interface *interface; + /*! \brief Name of the codec the interface is for */ + char codec[0]; +}; - return ao2_find(interfaces, &tmp_wrapper, OBJ_POINTER); -} +/*! \brief Container for registered format interfaces */ +static struct ao2_container *interfaces; -static int has_interface(const struct ast_format *format) +static int format_interface_hash(const void *obj, int flags) { - struct interface_ao2_wrapper *wrapper; + const struct format_interface *format_interface; + const char *key; - wrapper = find_interface(format); - if (!wrapper) { + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + return ast_str_hash(key); + case OBJ_SEARCH_OBJECT: + format_interface = obj; + return ast_str_hash(format_interface->codec); + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); return 0; } - ao2_ref(wrapper, -1); - return 1; } -int ast_format_sdp_parse(struct ast_format *format, const char *attributes) +static int format_interface_cmp(void *obj, void *arg, int flags) { - struct interface_ao2_wrapper *wrapper; - int res; + const struct format_interface *left = obj; + const struct format_interface *right = arg; + const char *right_key = arg; + int cmp; - if (!(wrapper = find_interface(format))) { - return 0; + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + cmp = strcmp(left->codec, right->codec); + break; + case OBJ_SEARCH_KEY: + cmp = strcmp(left->codec, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncmp(left->codec, right_key, strlen(right_key)); + break; + default: + ast_assert(0); + cmp = 0; + break; } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_sdp_parse) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + if (cmp) { return 0; } - res = wrapper->interface->format_attr_sdp_parse(&format->fattr, attributes); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + return CMP_MATCH; } -void ast_format_sdp_generate(const struct ast_format *format, unsigned int payload, struct ast_str **str) +/*! \brief Function called when the process is shutting down */ +static void format_shutdown(void) { - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - return; - } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_sdp_generate) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return; - } - - wrapper->interface->format_attr_sdp_generate(&format->fattr, payload, str); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ao2_cleanup(interfaces); + interfaces = NULL; } -/*! \internal - * \brief set format attributes using an interface - */ -static int format_set_helper(struct ast_format *format, va_list ap) +int ast_format_init(void) { - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - ast_log(LOG_WARNING, "Could not find format interface to set.\n"); - return -1; - } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_set) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, FORMAT_INTERFACE_BUCKETS, format_interface_hash, + format_interface_cmp); + if (!interfaces) { return -1; } - wrapper->interface->format_attr_set(&format->fattr, ap); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ast_register_atexit(format_shutdown); return 0; } -struct ast_format *ast_format_append(struct ast_format *format, ... ) -{ - va_list ap; - va_start(ap, format); - format_set_helper(format, ap); - va_end(ap); - - return format; -} - -struct ast_format *ast_format_set(struct ast_format *format, enum ast_format_id id, int set_attributes, ... ) +int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod) { - /* initialize the structure before setting it. */ - ast_format_clear(format); - - format->id = id; + SCOPED_AO2WRLOCK(lock, interfaces); + struct format_interface *format_interface; - if (set_attributes) { - va_list ap; - va_start(ap, set_attributes); - format_set_helper(format, ap); - va_end(ap); + if (!interface->format_clone || !interface->format_destroy) { + ast_log(LOG_ERROR, "Format interface for codec '%s' does not implement required callbacks\n", codec); + return -1; } - return format; -} - -void ast_format_clear(struct ast_format *format) -{ - format->id = 0; - memset(&format->fattr, 0, sizeof(format->fattr)); -} - -/*! \internal - * \brief determine if a list of attribute key value pairs are set on a format - */ -static int format_isset_helper(const struct ast_format *format, va_list ap) -{ - int res; - struct interface_ao2_wrapper *wrapper; - struct ast_format tmp = { - .id = format->id, - .fattr = { { 0, }, }, - }; - - if (!(wrapper = find_interface(format))) { + format_interface = ao2_find(interfaces, codec, OBJ_SEARCH_KEY | OBJ_NOLOCK); + if (format_interface) { + ast_log(LOG_ERROR, "A format interface is already present for codec '%s'\n", codec); + ao2_ref(format_interface, -1); return -1; } - ao2_rdlock(wrapper); - if (!wrapper->interface || - !wrapper->interface->format_attr_set || - !wrapper->interface->format_attr_cmp) { - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + format_interface = ao2_alloc_options(sizeof(*format_interface) + strlen(codec) + 1, + NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!format_interface) { return -1; } + format_interface->interface = interface; + strcpy(format_interface->codec, codec); /* Safe */ - /* if isset is present, use that function, else just build a new - * format and use the cmp function */ - if (wrapper->interface->format_attr_isset) { - res = wrapper->interface->format_attr_isset(&format->fattr, ap); - } else { - wrapper->interface->format_attr_set(&tmp.fattr, ap); - /* use our tmp structure to tell if the attributes are set or not */ - res = wrapper->interface->format_attr_cmp(&tmp.fattr, &format->fattr); - res = (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; - } + ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK); + ao2_ref(format_interface, -1); - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ast_verb(2, "Registered format interface for codec '%s'\n", codec); - return res; + return 0; } -int ast_format_isset(const struct ast_format *format, ... ) +void *ast_format_get_attribute_data(const struct ast_format *format) { - va_list ap; - int res; - - va_start(ap, format); - res = format_isset_helper(format, ap); - va_end(ap); - return res; + return format->attribute_data; } -int ast_format_get_value(const struct ast_format *format, int key, void *value) +void ast_format_set_attribute_data(struct ast_format *format, void *attribute_data) { - int res = 0; - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - return -1; - } - ao2_rdlock(wrapper); - if (!wrapper->interface || - !wrapper->interface->format_attr_get_val) { - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return -1; - } - - res = wrapper->interface->format_attr_get_val(&format->fattr, key, value); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + format->attribute_data = attribute_data; } -/*! \internal - * \brief cmp format attributes using an interface - */ -static enum ast_format_cmp_res format_cmp_helper(const struct ast_format *format1, const struct ast_format *format2) +/*! \brief Destructor for media formats */ +static void format_destroy(void *obj) { - enum ast_format_cmp_res res = AST_FORMAT_CMP_EQUAL; - struct interface_ao2_wrapper *wrapper; + struct ast_format *format = obj; - if (!(wrapper = find_interface(format1))) { - return res; + if (format->interface) { + format->interface->format_destroy(format); } - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_cmp) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return res; - } - - res = wrapper->interface->format_attr_cmp(&format1->fattr, &format2->fattr); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + ao2_cleanup(format->codec); } -enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2) +struct ast_format *ast_format_create_named(const char *format_name, struct ast_codec *codec) { - if (format1->id != format2->id) { - return AST_FORMAT_CMP_NOT_EQUAL; - } - - return format_cmp_helper(format1, format2); -} + struct ast_format *format; + struct format_interface *format_interface; -/*! \internal - * \brief get joint format attributes using an interface - */ -static int format_joint_helper(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result) -{ - int res = 0; - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format1))) { - /* if no interface is present, we assume formats are joint by id alone */ - return res; + format = ao2_t_alloc_options(sizeof(*format), format_destroy, + AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, "")); + if (!format) { + return NULL; } + format->name = format_name; + format->codec = ao2_bump(codec); - ao2_rdlock(wrapper); - if (wrapper->interface && wrapper->interface->format_attr_get_joint) { - res = wrapper->interface->format_attr_get_joint(&format1->fattr, &format2->fattr, &result->fattr); + format_interface = ao2_find(interfaces, codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + format->interface = format_interface->interface; + ao2_ref(format_interface, -1); } - ao2_unlock(wrapper); - - ao2_ref(wrapper, -1); - return res; + return format; } -int ast_format_joint(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result) +struct ast_format *ast_format_clone(const struct ast_format *format) { - if (format1->id != format2->id) { - return -1; - } - result->id = format1->id; - return format_joint_helper(format1, format2, result); -} + struct ast_format *cloned = ast_format_create_named(format->name, format->codec); - -uint64_t ast_format_id_to_old_bitfield(enum ast_format_id id) -{ - switch (id) { - /*! G.723.1 compression */ - case AST_FORMAT_G723_1: - return (1ULL << 0); - /*! GSM compression */ - case AST_FORMAT_GSM: - return (1ULL << 1); - /*! Raw mu-law data (G.711) */ - case AST_FORMAT_ULAW: - return (1ULL << 2); - /*! Raw A-law data (G.711) */ - case AST_FORMAT_ALAW: - return (1ULL << 3); - /*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */ - case AST_FORMAT_G726_AAL2: - return (1ULL << 4); - /*! ADPCM (IMA) */ - case AST_FORMAT_ADPCM: - return (1ULL << 5); - /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ - case AST_FORMAT_SLINEAR: - return (1ULL << 6); - /*! LPC10, 180 samples/frame */ - case AST_FORMAT_LPC10: - return (1ULL << 7); - /*! G.729A audio */ - case AST_FORMAT_G729A: - return (1ULL << 8); - /*! SpeeX Free Compression */ - case AST_FORMAT_SPEEX: - return (1ULL << 9); - /*! iLBC Free Compression */ - case AST_FORMAT_ILBC: - return (1ULL << 10); - /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ - case AST_FORMAT_G726: - return (1ULL << 11); - /*! G.722 */ - case AST_FORMAT_G722: - return (1ULL << 12); - /*! G.722.1 (also known as Siren7, 32kbps assumed) */ - case AST_FORMAT_SIREN7: - return (1ULL << 13); - /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ - case AST_FORMAT_SIREN14: - return (1ULL << 14); - /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ - case AST_FORMAT_SLINEAR16: - return (1ULL << 15); - /*! G.719 (64 kbps assumed) */ - case AST_FORMAT_G719: - return (1ULL << 32); - /*! SpeeX Wideband (16kHz) Free Compression */ - case AST_FORMAT_SPEEX16: - return (1ULL << 33); - /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ - case AST_FORMAT_OPUS: - return (1ULL << 34); - /*! Raw mu-law data (G.711) */ - case AST_FORMAT_TESTLAW: - return (1ULL << 47); - - /*! H.261 Video */ - case AST_FORMAT_H261: - return (1ULL << 18); - /*! H.263 Video */ - case AST_FORMAT_H263: - return (1ULL << 19); - /*! H.263+ Video */ - case AST_FORMAT_H263_PLUS: - return (1ULL << 20); - /*! H.264 Video */ - case AST_FORMAT_H264: - return (1ULL << 21); - /*! MPEG4 Video */ - case AST_FORMAT_MP4_VIDEO: - return (1ULL << 22); - /*! VP8 Video */ - case AST_FORMAT_VP8: - return (1ULL << 23); - - /*! JPEG Images */ - case AST_FORMAT_JPEG: - return (1ULL << 16); - /*! PNG Images */ - case AST_FORMAT_PNG: - return (1ULL << 17); - - /*! T.140 RED Text format RFC 4103 */ - case AST_FORMAT_T140RED: - return (1ULL << 26); - /*! T.140 Text format - ITU T.140, RFC 4103 */ - case AST_FORMAT_T140: - return (1ULL << 27); - default: - return 0; /* not supported by old bitfield. */ + if (!cloned) { + return NULL; } - return 0; - -} -uint64_t ast_format_to_old_bitfield(const struct ast_format *format) -{ - return ast_format_id_to_old_bitfield(format->id); -} - -struct ast_format *ast_format_from_old_bitfield(struct ast_format *dst, uint64_t src) -{ - switch (src) { - /*! G.723.1 compression */ - case (1ULL << 0): - return ast_format_set(dst, AST_FORMAT_G723_1, 0); - /*! GSM compression */ - case (1ULL << 1): - return ast_format_set(dst, AST_FORMAT_GSM, 0); - /*! Raw mu-law data (G.711) */ - case (1ULL << 2): - return ast_format_set(dst, AST_FORMAT_ULAW, 0); - /*! Raw A-law data (G.711) */ - case (1ULL << 3): - return ast_format_set(dst, AST_FORMAT_ALAW, 0); - /*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */ - case (1ULL << 4): - return ast_format_set(dst, AST_FORMAT_G726_AAL2, 0); - /*! ADPCM (IMA) */ - case (1ULL << 5): - return ast_format_set(dst, AST_FORMAT_ADPCM, 0); - /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ - case (1ULL << 6): - return ast_format_set(dst, AST_FORMAT_SLINEAR, 0); - /*! LPC10, 180 samples/frame */ - case (1ULL << 7): - return ast_format_set(dst, AST_FORMAT_LPC10, 0); - /*! G.729A audio */ - case (1ULL << 8): - return ast_format_set(dst, AST_FORMAT_G729A, 0); - /*! SpeeX Free Compression */ - case (1ULL << 9): - return ast_format_set(dst, AST_FORMAT_SPEEX, 0); - /*! iLBC Free Compression */ - case (1ULL << 10): - return ast_format_set(dst, AST_FORMAT_ILBC, 0); - /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ - case (1ULL << 11): - return ast_format_set(dst, AST_FORMAT_G726, 0); - /*! G.722 */ - case (1ULL << 12): - return ast_format_set(dst, AST_FORMAT_G722, 0); - /*! G.722.1 (also known as Siren7, 32kbps assumed) */ - case (1ULL << 13): - return ast_format_set(dst, AST_FORMAT_SIREN7, 0); - /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ - case (1ULL << 14): - return ast_format_set(dst, AST_FORMAT_SIREN14, 0); - /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ - case (1ULL << 15): - return ast_format_set(dst, AST_FORMAT_SLINEAR16, 0); - /*! G.719 (64 kbps assumed) */ - case (1ULL << 32): - return ast_format_set(dst, AST_FORMAT_G719, 0); - /*! SpeeX Wideband (16kHz) Free Compression */ - case (1ULL << 33): - return ast_format_set(dst, AST_FORMAT_SPEEX16, 0); - /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ - case (1ULL << 34): - return ast_format_set(dst, AST_FORMAT_OPUS, 0); - /*! Raw mu-law data (G.711) */ - case (1ULL << 47): - return ast_format_set(dst, AST_FORMAT_TESTLAW, 0); - - /*! H.261 Video */ - case (1ULL << 18): - return ast_format_set(dst, AST_FORMAT_H261, 0); - /*! H.263 Video */ - case (1ULL << 19): - return ast_format_set(dst, AST_FORMAT_H263, 0); - /*! H.263+ Video */ - case (1ULL << 20): - return ast_format_set(dst, AST_FORMAT_H263_PLUS, 0); - /*! H.264 Video */ - case (1ULL << 21): - return ast_format_set(dst, AST_FORMAT_H264, 0); - /*! MPEG4 Video */ - case (1ULL << 22): - return ast_format_set(dst, AST_FORMAT_MP4_VIDEO, 0); - /*! VP8 Video */ - case (1ULL << 23): - return ast_format_set(dst, AST_FORMAT_VP8, 0); - - /*! JPEG Images */ - case (1ULL << 16): - return ast_format_set(dst, AST_FORMAT_JPEG, 0); - /*! PNG Images */ - case (1ULL << 17): - return ast_format_set(dst, AST_FORMAT_PNG, 0); - - /*! T.140 RED Text format RFC 4103 */ - case (1ULL << 26): - return ast_format_set(dst, AST_FORMAT_T140RED, 0); - /*! T.140 Text format - ITU T.140, RFC 4103 */ - case (1ULL << 27): - return ast_format_set(dst, AST_FORMAT_T140, 0); + if (cloned->interface && cloned->interface->format_clone(format, cloned)) { + ao2_ref(cloned, -1); + return NULL; } - ast_format_clear(dst); - return NULL; -} -enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src) -{ - struct ast_format dst; - if (ast_format_from_old_bitfield(&dst, src)) { - return dst.id; - } - return 0; + return cloned; } -int ast_format_is_slinear(const struct ast_format *format) +struct ast_format *ast_format_create(struct ast_codec *codec) { - if (format->id == AST_FORMAT_SLINEAR || - format->id == AST_FORMAT_SLINEAR12 || - format->id == AST_FORMAT_SLINEAR16 || - format->id == AST_FORMAT_SLINEAR24 || - format->id == AST_FORMAT_SLINEAR32 || - format->id == AST_FORMAT_SLINEAR44 || - format->id == AST_FORMAT_SLINEAR48 || - format->id == AST_FORMAT_SLINEAR96 || - format->id == AST_FORMAT_SLINEAR192) { - return 1; - } - return 0; + return ast_format_create_named(codec->name, codec); } -enum ast_format_id ast_format_slin_by_rate(unsigned int rate) +enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2) { - if (rate >= 192000) { - return AST_FORMAT_SLINEAR192; - } else if (rate >= 96000) { - return AST_FORMAT_SLINEAR96; - } else if (rate >= 48000) { - return AST_FORMAT_SLINEAR48; - } else if (rate >= 44100) { - return AST_FORMAT_SLINEAR44; - } else if (rate >= 32000) { - return AST_FORMAT_SLINEAR32; - } else if (rate >= 24000) { - return AST_FORMAT_SLINEAR24; - } else if (rate >= 16000) { - return AST_FORMAT_SLINEAR16; - } else if (rate >= 12000) { - return AST_FORMAT_SLINEAR12; - } - return AST_FORMAT_SLINEAR; -} + const struct ast_format_interface *interface; -const char* ast_getformatname(const struct ast_format *format) -{ - int x; - const char *ret = "unknown"; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - ret = f_list[x].name; - break; - } + if (format1 == NULL || format2 == NULL) { + return AST_FORMAT_CMP_NOT_EQUAL; } - f_list = ast_format_list_destroy(f_list); - return ret; -} - -char *ast_getformatname_multiple_byid(char *buf, size_t size, enum ast_format_id id) -{ - int x; - unsigned len; - char *start, *end = buf; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - if (!size) { - f_list = ast_format_list_destroy(f_list); - return buf; - } - snprintf(end, size, "("); - len = strlen(end); - end += len; - size -= len; - start = end; - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == id) { - snprintf(end, size, "%s|", f_list[x].name); - len = strlen(end); - end += len; - size -= len; - } - } - if (start == end) { - ast_copy_string(start, "nothing)", size); - } else if (size > 1) { - *(end - 1) = ')'; + if (format1 == format2) { + return AST_FORMAT_CMP_EQUAL; } - f_list = ast_format_list_destroy(f_list); - return buf; -} - -static struct ast_codec_alias_table { - const char *alias; - const char *realname; -} ast_codec_alias_table[] = { - { "slinear", "slin"}, - { "slinear16", "slin16"}, - { "g723.1", "g723"}, - { "g722.1", "siren7"}, - { "g722.1c", "siren14"}, -}; -static const char *ast_expand_codec_alias(const char *in) -{ - int x; - - for (x = 0; x < ARRAY_LEN(ast_codec_alias_table); x++) { - if (!strcmp(in,ast_codec_alias_table[x].alias)) - return ast_codec_alias_table[x].realname; + if (format1->codec != format2->codec) { + return AST_FORMAT_CMP_NOT_EQUAL; } - return in; -} - -struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) -{ - int x; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (!strcasecmp(f_list[x].name, name) || - !strcasecmp(f_list[x].name, ast_expand_codec_alias(name))) { + interface = format1->interface ? format1->interface : format2->interface; - ast_format_copy(result, &f_list[x].format); - f_list = ast_format_list_destroy(f_list); - return result; - } + if (interface) { + return interface->format_cmp(format1, format2); } - f_list = ast_format_list_destroy(f_list); - - return NULL; -} -const char *ast_codec2str(struct ast_format *format) -{ - int x; - const char *ret = "unknown"; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - ret = f_list[x].desc; - break; - } - } - f_list = ast_format_list_destroy(f_list); - return ret; + return AST_FORMAT_CMP_EQUAL; } -int ast_format_rate(const struct ast_format *format) +struct ast_format *ast_format_joint(const struct ast_format *format1, const struct ast_format *format2) { - switch (format->id) { - case AST_FORMAT_SLINEAR12: - return 12000; - case AST_FORMAT_SLINEAR24: - return 24000; - case AST_FORMAT_SLINEAR32: - return 32000; - case AST_FORMAT_SLINEAR44: - return 44100; - case AST_FORMAT_SLINEAR48: - return 48000; - case AST_FORMAT_SLINEAR96: - return 96000; - case AST_FORMAT_SLINEAR192: - return 192000; - case AST_FORMAT_G722: - case AST_FORMAT_SLINEAR16: - case AST_FORMAT_SIREN7: - case AST_FORMAT_SPEEX16: - return 16000; - case AST_FORMAT_SIREN14: - case AST_FORMAT_SPEEX32: - return 32000; - case AST_FORMAT_G719: - return 48000; - case AST_FORMAT_SILK: - if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_24KHZ, - AST_FORMAT_ATTR_END))) { - return 24000; - } else if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_16KHZ, - AST_FORMAT_ATTR_END))) { - return 16000; - } else if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_12KHZ, - AST_FORMAT_ATTR_END))) { - return 12000; - } else { - return 8000; - } - case AST_FORMAT_CELT: - { - int samplerate; - if (!(ast_format_get_value(format, - CELT_ATTR_KEY_SAMP_RATE, - &samplerate))) { - return samplerate; - } - } - /* Opus */ - case AST_FORMAT_OPUS: - return 48000; - default: - return 8000; - } -} + const struct ast_format_interface *interface; -static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int x, found=0; - size_t f_len; - const struct ast_format_list *f_list; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codecs [audio|video|image|text]"; - e->usage = - "Usage: core show codecs [audio|video|image|text]\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: + if (format1->codec != format2->codec) { return NULL; } - if ((a->argc < 3) || (a->argc > 4)) { - return CLI_SHOWUSAGE; + /* If the two formats are the same structure OR if the codec is the same and no attributes + * exist we can immediately return a format with reference count bumped up, since they are + * the same. + */ + if ((ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL && !format1->attribute_data && !format2->attribute_data)) { + return ao2_bump((struct ast_format*)format1); } - f_list = ast_format_list_get(&f_len); - if (!ast_opt_dont_warn) { - ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" - "\tIt does not indicate anything about your configuration.\n"); - } + interface = format1->interface ? format1->interface : format2->interface; - ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); - ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); - - for (x = 0; x < f_len; x++) { - if (a->argc == 4) { - if (!strcasecmp(a->argv[3], "audio")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "video")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "image")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_IMAGE) { - continue; - } - } else if (!strcasecmp(a->argv[3], "text")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_TEXT) { - continue; - } - } else { - continue; - } - } - - ast_cli(a->fd, "%8u %5s %8s (%s)\n", - f_list[x].format.id, - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_IMAGE) ? "image" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_VIDEO) ? "video" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_TEXT) ? "text" : - "(unk)", - f_list[x].name, - f_list[x].desc); - found = 1; - } - - f_list = ast_format_list_destroy(f_list); - if (!found) { - return CLI_SHOWUSAGE; - } else { - return CLI_SUCCESS; - } + /* If there is attribute data on either there has to be an interface */ + return interface->format_get_joint(format1, format2); } -static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +struct ast_format *ast_format_attribute_set(const struct ast_format *format, const char *name, const char *value) { - enum ast_format_id format_id; - int x, found = 0; - int type_punned_codec; - size_t f_len; - const struct ast_format_list *f_list; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codec"; - e->usage = - "Usage: core show codec <number>\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 4) { - return CLI_SHOWUSAGE; - } + const struct ast_format_interface *interface = format->interface; - if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { - return CLI_SHOWUSAGE; - } - format_id = type_punned_codec; - - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == format_id) { - found = 1; - ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, f_list[x].desc); + if (!interface) { + struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + interface = format_interface->interface; + ao2_ref(format_interface, -1); } } - if (!found) { - ast_cli(a->fd, "Codec %u not found\n", format_id); + if (!interface || !interface->format_attribute_set) { + return ao2_bump((struct ast_format*)format); } - f_list = ast_format_list_destroy(f_list); - return CLI_SUCCESS; + return interface->format_attribute_set(format, name, value); } -/* Builtin Asterisk CLI-commands for debugging */ -static struct ast_cli_entry my_clis[] = { - AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"), - AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"), -}; - -static int format_list_add_custom(struct ast_format_list *new) +struct ast_format *ast_format_parse_sdp_fmtp(const struct ast_format *format, const char *attributes) { - RAII_VAR(struct ast_format_list *, entry, NULL, ao2_cleanup); - if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { - return -1; - } - memcpy(entry, new, sizeof(struct ast_format_list)); - entry->custom_entry = 1; - ao2_link(format_list, entry); - return 0; -} -static int format_list_add_static( - const struct ast_format *format, - const char *name, - int samplespersecond, - const char *description, - int fr_len, - int min_ms, - int max_ms, - int inc_ms, - int def_ms, - unsigned int flags, - int cur_ms) -{ - struct ast_format_list *entry; - if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { - return -1; - } - ast_format_copy(&entry->format, format); - ast_copy_string(entry->name, name, sizeof(entry->name)); - ast_copy_string(entry->desc, description, sizeof(entry->desc)); - entry->samplespersecond = samplespersecond; - entry->fr_len = fr_len; - entry->min_ms = min_ms; - entry->max_ms = max_ms; - entry->inc_ms = inc_ms; - entry->def_ms = def_ms; - entry->flags = flags; - entry->cur_ms = cur_ms; - entry->custom_entry = 0; - - ao2_link(format_list, entry); - ao2_ref(entry, -1); - return 0; -} + const struct ast_format_interface *interface = format->interface; -static int list_all_custom(void *obj, void *arg, int flag) -{ - struct ast_format_list *entry = obj; - return entry->custom_entry ? CMP_MATCH : 0; -} - -static int list_cmp_cb(void *obj, void *arg, int flags) -{ - struct ast_format_list *entry1 = obj; - struct ast_format_list *entry2 = arg; - - return (ast_format_cmp(&entry1->format, &entry2->format) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH | CMP_STOP : 0; -} - -const struct ast_format_list *ast_format_list_get(size_t *size) -{ - struct ast_format_list *list; - ast_rwlock_rdlock(&format_list_array_lock); - ao2_ref(format_list_array, 1); - list = format_list_array; - *size = format_list_array_len; - ast_rwlock_unlock(&format_list_array_lock); - return list; -} -const struct ast_format_list *ast_format_list_destroy(const struct ast_format_list *list) -{ - ao2_ref((void *) list, -1); - return NULL; -} - -static int build_format_list_array(void) -{ - struct ast_format_list *tmp; - size_t arraysize = sizeof(struct ast_format_list) * ao2_container_count(format_list); - int i = 0; - struct ao2_iterator it; - - ast_rwlock_wrlock(&format_list_array_lock); - tmp = format_list_array; - if (!(format_list_array = ao2_alloc(arraysize, NULL))) { - format_list_array = tmp; - ast_rwlock_unlock(&format_list_array_lock); - return -1; - } - format_list_array_len = ao2_container_count(format_list); - if (tmp) { - ao2_ref(tmp, -1); + if (!interface) { + struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + interface = format_interface->interface; + ao2_ref(format_interface, -1); + } } - /* walk through the container adding elements to the static array */ - it = ao2_iterator_init(format_list, 0); - while ((tmp = ao2_iterator_next(&it)) && (i < format_list_array_len)) { - memcpy(&format_list_array[i], tmp, sizeof(struct ast_format_list)); - ao2_ref(tmp, -1); - i++; + if (!interface || !interface->format_parse_sdp_fmtp) { + return ao2_bump((struct ast_format*)format); } - ao2_iterator_destroy(&it); - ast_rwlock_unlock(&format_list_array_lock); - return 0; + return interface->format_parse_sdp_fmtp(format, attributes); } -static int format_list_init(void) +void ast_format_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str) { - struct ast_format tmpfmt; - if (!(format_list = ao2_container_alloc(1, NULL, list_cmp_cb))) { - return -1; + if (!format->interface || !format->interface->format_generate_sdp_fmtp) { + return; } - /* initiate static entries XXX DO NOT CHANGE THIS ORDER! */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), "g723", 8000, "G.723.1", 20, 30, 300, 30, 30, 0, 0); /*!< G723.1 */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), "gsm", 8000, "GSM", 33, 20, 300, 20, 20, 0, 0); /*!< codec_gsm.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_alaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), "adpcm" , 8000, "Dialogic ADPCM", 40, 10, 300, 10, 20, 0, 0); /*!< codec_adpcm.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0); /*!< Signed linear */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20, 0, 0); /*!< codec_lpc10.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), "speex", 8000, "SpeeX", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), "speex16", 16000, "SpeeX 16khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30, 0, 0); /*!< codec_ilbc.c */ /* inc=30ms - workaround */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), "g722", 16000, "G722", 80, 10, 150, 10, 20, 0, 0); /*!< codec_g722.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (16kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), "jpeg", 0, "JPEG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< See format_jpeg.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), "png", 0, "PNG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< PNG Image format */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), "h261", 0, "H.261 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.261 Video Passthrough */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), "h263", 0, "H.263 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.263 Passthrough support, see format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), "h263p", 0, "H.263+ Video", 0, 0, 0,0 ,0 ,0, 0); /*!< H.263plus passthrough support See format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), "h264", 0, "H.264 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), "mpeg4", 0, "MPEG4 Video", 0, 0, 0, 0, 0 ,0, 0); /*!< Passthrough support for MPEG4 */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), "red", 1, "T.140 Realtime Text with redundancy", 0, 0, 0,0 ,0 ,0, 0); /*!< Redundant T.140 Realtime Text */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), "t140", 0, "Passthrough T.140 Realtime Text", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support for T.140 Realtime Text */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_TESTLAW, 0), "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20, 0, 0); - - /* ORDER MAY CHANGE AFTER THIS POINT IN THE LIST */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), "speex32", 32000, "SpeeX 32khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR12, 0), "slin12", 12000, "16 bit Signed Linear PCM (12kHz)", 240, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (12kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR24, 0), "slin24", 24000, "16 bit Signed Linear PCM (24kHz)", 480, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (24kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR32, 0), "slin32", 32000, "16 bit Signed Linear PCM (32kHz)", 640, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (32kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR44, 0), "slin44", 44100, "16 bit Signed Linear PCM (44kHz)", 882, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (44.1kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR48, 0), "slin48", 48000, "16 bit Signed Linear PCM (48kHz)", 960, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (48kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR96, 0), "slin96", 96000, "16 bit Signed Linear PCM (96kHz)", 1920, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (96kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR192, 0), "slin192", 192000, "16 bit Signed Linear PCM (192kHz)", 3840, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (192kHz) */ - /* Opus (FIXME: real min is 3/5/10, real max is 120...) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), "opus", 48000, "Opus Codec", 10, 20, 60, 20, 20, 0, 0); /*!< codec_opus.c */ - /* VP8 (passthrough) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), "vp8", 0, "VP8 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ - return 0; + format->interface->format_generate_sdp_fmtp(format, payload, str); } -/*! - * \internal - * \brief Clean up resources on Asterisk shutdown - */ -static void format_list_shutdown(void) +unsigned int ast_format_get_codec_id(const struct ast_format *format) { - ast_rwlock_destroy(&format_list_array_lock); - if (format_list) { - ao2_t_ref(format_list, -1, "Unref format_list container in shutdown"); - format_list = NULL; - } - if (format_list_array) { - ao2_t_ref(format_list_array, -1, "Unref format_list_array in shutdown"); - format_list_array = NULL; - } + return format->codec->id; } -int ast_format_list_init(void) +const char *ast_format_get_name(const struct ast_format *format) { - if (ast_rwlock_init(&format_list_array_lock)) { - return -1; - } - if (format_list_init()) { - goto init_list_cleanup; - } - if (build_format_list_array()) { - goto init_list_cleanup; - } - - ast_register_atexit(format_list_shutdown); - return 0; -init_list_cleanup: - - format_list_shutdown(); - return -1; + return format->name; } -/*! - * \internal - * \brief Clean up resources on Asterisk shutdown - */ -static void format_attr_shutdown(void) +const char *ast_format_get_codec_name(const struct ast_format *format) { - ast_cli_unregister_multiple(my_clis, ARRAY_LEN(my_clis)); - if (interfaces) { - ao2_ref(interfaces, -1); - interfaces = NULL; - } + return format->codec->name; } -int ast_format_attr_init(void) +int ast_format_can_be_smoothed(const struct ast_format *format) { - interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, - 283, interface_hash_cb, interface_cmp_cb); - if (!interfaces) { - return -1; - } - - ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); - ast_register_cleanup(format_attr_shutdown); - return 0; + return format->codec->smooth; } -static int custom_celt_format(struct ast_format_list *entry, unsigned int maxbitrate, unsigned int framesize) +enum ast_media_type ast_format_get_type(const struct ast_format *format) { - if (!entry->samplespersecond) { - ast_log(LOG_WARNING, "Custom CELT format definition '%s' requires sample rate to be defined.\n", entry->name); - } - ast_format_set(&entry->format, AST_FORMAT_CELT, 0); - if (!has_interface(&entry->format)) { - return -1; - } - - snprintf(entry->desc, sizeof(entry->desc), "CELT Custom Format %ukhz", entry->samplespersecond/1000); - - ast_format_append(&entry->format, - CELT_ATTR_KEY_SAMP_RATE, entry->samplespersecond, - CELT_ATTR_KEY_MAX_BITRATE, maxbitrate, - CELT_ATTR_KEY_FRAME_SIZE, framesize, - AST_FORMAT_ATTR_END); - - entry->fr_len = 80; - entry->min_ms = 20; - entry->max_ms = 20; - entry->inc_ms = 20; - entry->def_ms = 20; - return 0; + return format->codec->type; } -static int custom_silk_format(struct ast_format_list *entry, unsigned int maxbitrate, int usedtx, int usefec, int packetloss_percentage) +unsigned int ast_format_get_default_ms(const struct ast_format *format) { - if (!entry->samplespersecond) { - ast_log(LOG_WARNING, "Custom SILK format definition '%s' requires sample rate to be defined.\n", entry->name); - } - ast_format_set(&entry->format, AST_FORMAT_SILK, 0); - - if (!has_interface(&entry->format)) { - return -1; - } - - switch (entry->samplespersecond) { - case 8000: - ast_copy_string(entry->desc, "SILK Custom Format 8khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_8KHZ, - AST_FORMAT_ATTR_END); - break; - case 12000: - ast_copy_string(entry->desc, "SILK Custom Format 12khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_12KHZ, - AST_FORMAT_ATTR_END); - break; - case 16000: - ast_copy_string(entry->desc, "SILK Custom Format 16khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_16KHZ, - AST_FORMAT_ATTR_END); - break; - case 24000: - ast_copy_string(entry->desc, "SILK Custom Format 24khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_24KHZ, - AST_FORMAT_ATTR_END); - break; - default: - ast_log(LOG_WARNING, "Custom SILK format definition '%s' can not support sample rate %u\n", entry->name, entry->samplespersecond); - return -1; - } - ast_format_append(&entry->format, - SILK_ATTR_KEY_MAX_BITRATE, maxbitrate, - SILK_ATTR_KEY_DTX, usedtx ? 1 : 0, - SILK_ATTR_KEY_FEC, usefec ? 1 : 0, - SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE, packetloss_percentage, - AST_FORMAT_ATTR_END); - - entry->fr_len = 80; - entry->min_ms = 20; - entry->max_ms = 20; - entry->inc_ms = 20; - entry->def_ms = 20; - return 0; + return format->codec->default_ms; } -static int conf_process_format_name(const char *name, enum ast_format_id *id) +unsigned int ast_format_get_minimum_ms(const struct ast_format *format) { - if (!strcasecmp(name, "silk")) { - *id = AST_FORMAT_SILK; - } else if (!strcasecmp(name, "celt")) { - *id = AST_FORMAT_CELT; - } else { - *id = 0; - return -1; - } - return 0; + return format->codec->minimum_ms; } -static int conf_process_sample_rate(const char *rate, unsigned int *result) +unsigned int ast_format_get_maximum_ms(const struct ast_format *format) { - if (!strcasecmp(rate, "8000")) { - *result = 8000; - } else if (!strcasecmp(rate, "12000")) { - *result = 12000; - } else if (!strcasecmp(rate, "16000")) { - *result = 16000; - } else if (!strcasecmp(rate, "24000")) { - *result = 24000; - } else if (!strcasecmp(rate, "32000")) { - *result = 32000; - } else if (!strcasecmp(rate, "44100")) { - *result = 44100; - } else if (!strcasecmp(rate, "48000")) { - *result = 48000; - } else if (!strcasecmp(rate, "96000")) { - *result = 96000; - } else if (!strcasecmp(rate, "192000")) { - *result = 192000; - } else { - *result = 0; - return -1; - } - - return 0; + return format->codec->maximum_ms; } -static int load_format_config(void) -{ - struct ast_flags config_flags = { 0, }; - struct ast_config *cfg = ast_config_load(FORMAT_CONFIG, config_flags); - struct ast_format_list entry; - struct ast_variable *var; - char *cat = NULL; - int add_it = 0; - - struct { - enum ast_format_id id; - unsigned int maxbitrate; - unsigned int framesize; - unsigned int packetloss_percentage; - int usefec; - int usedtx; - } settings; - - if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { - return 0; - } - - /* remove all custom formats from the AO2 Container. Note, this has no affect on the - * global format list until the list is rebuild. That is why this is okay to do while - * reloading the config. */ - ao2_callback(format_list, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, list_all_custom, NULL); - while ((cat = ast_category_browse(cfg, cat))) { - memset(&entry, 0, sizeof(entry)); - memset(&settings, 0, sizeof(settings)); - add_it = 0; - - if (!(ast_variable_retrieve(cfg, cat, "type"))) { - continue; - } - ast_copy_string(entry.name, cat, sizeof(entry.name)); - var = ast_variable_browse(cfg, cat); - for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - if (!strcasecmp(var->name, "type") && conf_process_format_name(var->value, &settings.id)) { - ast_log(LOG_WARNING, "Can not make custom format type for '%s' at line %d of %s\n", - var->value, var->lineno, FORMAT_CONFIG); - continue; - } else if (!strcasecmp(var->name, "samprate") && conf_process_sample_rate(var->value, &entry.samplespersecond)) { - ast_log(LOG_WARNING, "Sample rate '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } else if (!strcasecmp(var->name, "maxbitrate")) { - if (sscanf(var->value, "%30u", &settings.maxbitrate) != 1) { - ast_log(LOG_WARNING, "maxbitrate '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } else if (!strcasecmp(var->name, "framesize")) { - if (sscanf(var->value, "%30u", &settings.framesize) != 1) { - ast_log(LOG_WARNING, "framesize '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } else if (!strcasecmp(var->name, "dtx")) { - settings.usedtx = ast_true(var->value) ? 1 : 0; - } else if (!strcasecmp(var->name, "fec")) { - settings.usefec = ast_true(var->value) ? 1 : 0; - } else if (!strcasecmp(var->name, "packetloss_percentage")) { - if ((sscanf(var->value, "%30u", &settings.packetloss_percentage) != 1) || (settings.packetloss_percentage > 100)) { - ast_log(LOG_WARNING, "packetloss_percentage '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } - } - - switch (settings.id) { - case AST_FORMAT_SILK: - if (!(custom_silk_format(&entry, settings.maxbitrate, settings.usedtx, settings.usefec, settings.packetloss_percentage))) { - add_it = 1; - } - break; - case AST_FORMAT_CELT: - if (!(custom_celt_format(&entry, settings.maxbitrate, settings.framesize))) { - add_it = 1; - } - break; - default: - ast_log(LOG_WARNING, "Can not create custom format %s\n", entry.name); - } - - if (add_it) { - format_list_add_custom(&entry); - } - } - ast_config_destroy(cfg); - build_format_list_array(); - return 0; +unsigned int ast_format_get_minimum_bytes(const struct ast_format *format) +{ + return format->codec->minimum_bytes; } -int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) +unsigned int ast_format_get_sample_rate(const struct ast_format *format) { - int x; - size_t f_len; - const struct ast_format_list *f_list; - struct interface_ao2_wrapper *wrapper; - struct interface_ao2_wrapper tmp_wrapper = { - .id = interface->id, - }; - - /* - * Grab the write lock before checking for duplicates in - * anticipation of adding a new interface and to prevent a - * duplicate from sneaking in between the check and add. - */ - ao2_wrlock(interfaces); - - /* check for duplicates first*/ - if ((wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { - ao2_unlock(interfaces); - ast_log(LOG_WARNING, "Can not register attribute interface for format id %u, interface already exists.\n", interface->id); - ao2_ref(wrapper, -1); - return -1; - } - - wrapper = ao2_alloc_options(sizeof(*wrapper), NULL, AO2_ALLOC_OPT_LOCK_RWLOCK); - if (!wrapper) { - ao2_unlock(interfaces); - return -1; - } - - wrapper->interface = interface; - wrapper->id = interface->id; - - /* The write lock is already held. */ - ao2_link_flags(interfaces, wrapper, OBJ_NOLOCK); - ao2_unlock(interfaces); - - ao2_ref(wrapper, -1); - - /* This will find all custom formats in codecs.conf for this new registered interface */ - load_format_config(); - - /* update the RTP engine to all custom formats created for this interface */ - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == tmp_wrapper.id) { - ast_rtp_engine_load_format(&f_list[x].format); - } - } - f_list = ast_format_list_destroy(f_list); - return 0; + return format->codec->sample_rate; } -int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) +unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples) { - int x; - size_t f_len; - const struct ast_format_list *f_list; - struct interface_ao2_wrapper *wrapper; - struct interface_ao2_wrapper tmp_wrapper = { - .id = interface->id, - }; - - if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK)))) { - return -1; - } - - ao2_wrlock(wrapper); - wrapper->interface = NULL; - ao2_unlock(wrapper); - - ao2_ref(wrapper, -1); - - /* update the RTP engine to remove all custom formats created for this interface */ - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == tmp_wrapper.id) { - ast_rtp_engine_unload_format(&f_list[x].format); - } - } - - /* This will remove all custom formats previously created for this interface */ - load_format_config(); - f_list = ast_format_list_destroy(f_list); - return 0; + return ast_codec_determine_length(format->codec, samples); } |