aboutsummaryrefslogtreecommitdiffstats
path: root/main/format.c
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-22 23:04:49 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-22 23:04:49 +0000
commitf27e928f0588f5cbf85ac8202cef912efcc51a9c (patch)
treeb061487de973558358757bd1b6e457aaccf41638 /main/format.c
parent70442b4e1767b35ed1699d27cfc24109c617f445 (diff)
Media Project Phase2: SILK 8khz-24khz, SLINEAR 8khz-192khz, SPEEX 32khz, hd audio ConfBridge, and other stuff
-Functional changes 1. Dynamic global format list build by codecs defined in codecs.conf 2. SILK 8khz, 12khz, 16khz, and 24khz with custom attributes defined in codecs.conf 3. Negotiation of SILK attributes in chan_sip. 4. SPEEX 32khz with translation 5. SLINEAR 8khz, 12khz, 24khz, 32khz, 44.1khz, 48khz, 96khz, 192khz with translation using codec_resample.c 6. Various changes to RTP code required to properly handle the dynamic format list and formats with attributes. 7. ConfBridge now dynamically jumps to the best possible sample rate. This allows for conferences to take advantage of HD audio (Which sounds awesome) 8. Audiohooks are no longer limited to 8khz audio, and most effects have been updated to take advantage of this such as Volume, DENOISE, PITCH_SHIFT. 9. codec_resample now uses its own code rather than depending on libresample. -Organizational changes Global format list is moved from frame.c to format.c Various format specific functions moved from frame.c to format.c Review: https://reviewboard.asterisk.org/r/1104/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@308582 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/format.c')
-rw-r--r--main/format.c796
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;
}