asterisk/main/format.c
Matthew Jordan 272dd008d0 Fix memory leak while loading modules, adding formats, and destroying endpoints
This patch fixes three memory leaks
 * When we load a module with the LOAD_PRIORITY flag, we remove its entry from
   the load order list. Unfortunately, we don't free the memory associated with
   entry in the list. This patch corrects that and properly frees the memory
   for the module in the list.

 * When adding a custom format (such as SILK or CELT), the routine for adding
   the format was leaking a reference. RAII_VAR cleans this up properly.

 * We now de-ref the channel_snapshot appropriately when an endpoint is
   disposed of
........

Merged revisions 391489 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 391507 from http://svn.asterisk.org/svn/asterisk/branches/11


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@391521 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-06-12 02:29:08 +00:00

1430 lines
42 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* 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
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Format API
*
* \author David Vossel <dvossel@digium.com>
* \author Mark Spencer <markster@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/_private.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;
};
/*! \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,
};
return ao2_find(interfaces, &tmp_wrapper, OBJ_POINTER);
}
static int has_interface(const struct ast_format *format)
{
struct interface_ao2_wrapper *wrapper;
wrapper = find_interface(format);
if (!wrapper) {
return 0;
}
ao2_ref(wrapper, -1);
return 1;
}
int ast_format_sdp_parse(struct ast_format *format, const char *attributes)
{
struct interface_ao2_wrapper *wrapper;
int res;
if (!(wrapper = find_interface(format))) {
return 0;
}
ao2_rdlock(wrapper);
if (!(wrapper->interface || !wrapper->interface->format_attr_sdp_parse)) {
ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return 0;
}
res = wrapper->interface->format_attr_sdp_parse(&format->fattr, attributes);
ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
}
void ast_format_sdp_generate(const struct ast_format *format, unsigned int payload, struct ast_str **str)
{
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);
}
/*! \internal
* \brief set format attributes using an interface
*/
static int format_set_helper(struct ast_format *format, va_list ap)
{
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);
return -1;
}
wrapper->interface->format_attr_set(&format->fattr, ap);
ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
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, ... )
{
/* initialize the structure before setting it. */
ast_format_clear(format);
format->id = id;
if (set_attributes) {
va_list ap;
va_start(ap, set_attributes);
format_set_helper(format, ap);
va_end(ap);
}
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))) {
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);
return -1;
}
/* 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_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
}
int ast_format_isset(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;
}
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;
}
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;
}
/*! \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)
{
enum ast_format_cmp_res res = AST_FORMAT_CMP_EQUAL;
struct interface_ao2_wrapper *wrapper;
if (!(wrapper = find_interface(format1))) {
return res;
}
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;
}
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
{
if (format1->id != format2->id) {
return AST_FORMAT_CMP_NOT_EQUAL;
}
return format_cmp_helper(format1, format2);
}
/*! \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;
}
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);
}
ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
}
int ast_format_joint(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result)
{
if (format1->id != format2->id) {
return -1;
}
result->id = format1->id;
return format_joint_helper(format1, format2, result);
}
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);
/*! 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);
/*! 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. */
}
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);
/*! 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);
/*! 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);
}
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;
}
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;
}
case AST_FORMAT_CELT:
{
int samplerate;
if (!(ast_format_get_value(format,
CELT_ATTR_KEY_SAMP_RATE,
&samplerate))) {
return samplerate;
}
}
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);
}
}
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"),
};
static int format_list_add_custom(struct ast_format_list *new)
{
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;
}
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;
}
/*! \internal \brief Clean up resources on Asterisk shutdown */
static void format_list_shutdown(void)
{
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;
}
}
int ast_format_list_init(void)
{
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;
}
/*! \internal \brief Clean up resources on Asterisk shutdown */
static void format_attr_shutdown(void)
{
ast_cli_unregister_multiple(my_clis, ARRAY_LEN(my_clis));
if (interfaces) {
ao2_ref(interfaces, -1);
interfaces = NULL;
}
}
int ast_format_attr_init(void)
{
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_atexit(format_attr_shutdown);
return 0;
}
static int custom_celt_format(struct ast_format_list *entry, unsigned int maxbitrate, unsigned int framesize)
{
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 %dkhz", 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;
}
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 if (!strcasecmp(name, "celt")) {
*id = AST_FORMAT_CELT;
} 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, "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;
}
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;
}
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,
};
/*
* 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 %d, 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;
}
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,
};
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;
}