diff options
Diffstat (limited to 'main/format.c')
-rw-r--r-- | main/format.c | 796 |
1 files changed, 789 insertions, 7 deletions
diff --git a/main/format.c b/main/format.c index d77d244a6..28b15ae21 100644 --- a/main/format.c +++ b/main/format.c @@ -4,6 +4,7 @@ * Copyright (C) 2010, Digium, Inc. * * David Vossel <dvossel@digium.com> + * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -21,6 +22,7 @@ * \brief Format API * * \author David Vossel <dvossel@digium.com> + * \author Mark Spencer <markster@digium.com> */ #include "asterisk.h" @@ -32,6 +34,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); #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" /*! This is the container for all the format attribute interfaces. * An ao2 container was chosen for fast lookup. */ @@ -51,6 +60,17 @@ struct interface_ao2_wrapper { ast_rwlock_t wraplock; }; +/*! \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; @@ -86,6 +106,23 @@ int ast_format_get_video_mark(const struct ast_format *format) return format->fattr.rtp_marker_bit; } +static int has_interface(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = format->id, + }; + + ast_rwlock_rdlock(&ilock); + if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + return 0; + } + ast_rwlock_unlock(&ilock); + ao2_ref(wrapper, -1); + return 1; +} + static struct interface_ao2_wrapper *find_interface(const struct ast_format *format) { struct interface_ao2_wrapper *wrapper; @@ -166,7 +203,7 @@ void ast_format_clear(struct ast_format *format) /*! \internal * \brief determine if a list of attribute key value pairs are set on a format */ -static int format_isset_helper(struct ast_format *format, va_list ap) +static int format_isset_helper(const struct ast_format *format, va_list ap) { int res; struct interface_ao2_wrapper *wrapper; @@ -189,18 +226,24 @@ static int format_isset_helper(struct ast_format *format, va_list ap) return -1; } - 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); + /* 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; + } ast_rwlock_unlock(&wrapper->wraplock); ao2_ref(wrapper, -1); - return (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; + return res; } -int ast_format_isset(struct ast_format *format, ... ) +int ast_format_isset(const struct ast_format *format, ... ) { va_list ap; int res; @@ -211,6 +254,29 @@ int ast_format_isset(struct ast_format *format, ... ) return res; } +int ast_format_get_value(const struct ast_format *format, int key, void *value) +{ + int res = 0; + struct interface_ao2_wrapper *wrapper; + if (!(wrapper = find_interface(format))) { + return -1; + } + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || + !wrapper->interface->format_attr_get_val) { + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return -1; + } + + res = wrapper->interface->format_attr_get_val(&format->fattr, key, value); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return res; +} /*! \internal * \brief cmp format attributes using an interface @@ -372,6 +438,8 @@ uint64_t ast_format_id_to_old_bitfield(enum ast_format_id id) /*! T.140 Text format - ITU T.140, RFC 4103 */ case AST_FORMAT_T140: return (1ULL << 27); + default: + return 0; /* not supported by old bitfield. */ } return 0; @@ -486,20 +554,709 @@ enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src) return 0; } +int ast_format_is_slinear(const struct ast_format *format) +{ + 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; +} + +enum ast_format_id ast_format_slin_by_rate(unsigned int rate) +{ + 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 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; + } + } + 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) = ')'; + } + 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; + } + 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))) { + + ast_format_copy(result, &f_list[x].format); + f_list = ast_format_list_destroy(f_list); + return result; + } + } + 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; +} + +int ast_format_rate(const struct ast_format *format) +{ + 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; + } + default: + return 8000; + } +} + +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: + return NULL; + } + + if ((a->argc < 3) || (a->argc > 4)) { + return CLI_SHOWUSAGE; + } + + 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"); + } + + 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; + } +} + +static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + 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; + } + + 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); + break; + } + } + + if (!found) { + ast_cli(a->fd, "Codec %d not found\n", format_id); + } + + f_list = ast_format_list_destroy(f_list); + return CLI_SUCCESS; +} + +/* 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"), +}; +int init_framer(void) +{ + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); + return 0; +} + +static int format_list_add_custom(struct ast_format_list *new) +{ + struct ast_format_list *entry; + 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); + return 0; +} + +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; +} +static int list_hash_cb(const void *obj, const int flags) +{ + return ao2_container_count(format_list); +} + +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); + } + + /* 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++; + } + ao2_iterator_destroy(&it); + + ast_rwlock_unlock(&format_list_array_lock); + return 0; +} +static int format_list_init(void) +{ + struct ast_format tmpfmt; + if (!(format_list = ao2_container_alloc(283, list_hash_cb, list_cmp_cb))) { + return -1; + } + /* 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, "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) */ + + return 0; +} + +int ast_format_list_init() +{ + 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; + } + + return 0; +init_list_cleanup: + + ast_rwlock_destroy(&format_list_array_lock); + ao2_ref(format_list, -1); + if (format_list_array) { + ao2_ref(format_list_array, -1); + } + return -1; +} + int ast_format_attr_init() { + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); if (ast_rwlock_init(&ilock)) { return -1; } + if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) { ast_rwlock_destroy(&ilock); + goto init_cleanup; + } + return 0; + +init_cleanup: + ast_rwlock_destroy(&ilock); + if (interfaces) { + ao2_ref(interfaces, -1); + } + return -1; +} + +static int custom_silk_format(struct ast_format_list *entry, unsigned int maxbitrate, int usedtx, int usefec, int packetloss_percentage) +{ + 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 %d\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; +} + +static int conf_process_format_name(const char *name, enum ast_format_id *id) +{ + if (!strcasecmp(name, "silk")) { + *id = AST_FORMAT_SILK; + } else { + *id = 0; + return -1; + } + return 0; +} + +static int conf_process_sample_rate(const char *rate, unsigned int *result) +{ + 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, "48000")) { + *result = 48000; + } else { + *result = 0; return -1; } + + return 0; +} +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 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, "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; + 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; } int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) { + 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, @@ -530,11 +1287,25 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf 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); + } + } + return 0; } int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) { + 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, @@ -554,5 +1325,16 @@ int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *inte 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(); + return 0; } |