2011-02-03 16:22:10 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010, Digium, Inc.
*
* David Vossel <dvossel@digium.com>
2011-02-22 23:04:49 +00:00
* Mark Spencer <markster@digium.com>
2011-02-03 16:22:10 +00:00
*
* 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>
2011-02-22 23:04:49 +00:00
* \author Mark Spencer <markster@digium.com>
2011-02-03 16:22:10 +00:00
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2011-02-03 16:22:10 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" );
#include "asterisk/_private.h"
#include "asterisk/format.h"
#include "asterisk/astobj2.h"
#include "asterisk/lock.h"
2011-02-22 23:04:49 +00:00
#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"
2011-02-03 16:22:10 +00:00
2012-02-28 18:15:34 +00:00
/*!
* \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.
*/
2011-02-03 16:22:10 +00:00
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 ;
};
2011-02-22 23:04:49 +00:00
/*! \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 ;
2011-02-03 16:22:10 +00:00
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 )
{
2012-03-13 16:50:06 +00:00
* dst = * src ;
2011-02-03 16:22:10 +00:00
}
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 ;
}
2011-10-31 17:51:22 +00:00
static struct interface_ao2_wrapper * find_interface ( const struct ast_format * format )
2011-02-22 23:04:49 +00:00
{
struct interface_ao2_wrapper tmp_wrapper = {
. id = format -> id ,
};
2012-02-28 18:15:34 +00:00
return ao2_find ( interfaces , & tmp_wrapper , OBJ_POINTER );
2011-02-22 23:04:49 +00:00
}
2011-10-31 17:51:22 +00:00
static int has_interface ( const struct ast_format * format )
2011-02-03 16:22:10 +00:00
{
struct interface_ao2_wrapper * wrapper ;
2011-10-31 17:51:22 +00:00
wrapper = find_interface ( format );
if ( ! wrapper ) {
return 0 ;
2011-02-03 16:22:10 +00:00
}
2011-10-31 17:51:22 +00:00
ao2_ref ( wrapper , - 1 );
return 1 ;
2011-02-03 16:22:10 +00:00
}
2012-07-13 16:49:40 +00:00
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 );
}
2011-02-03 16:22:10 +00:00
/*! \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 ;
}
2012-02-28 18:15:34 +00:00
ao2_rdlock ( wrapper );
2011-02-03 16:22:10 +00:00
if ( ! wrapper -> interface || ! wrapper -> interface -> format_attr_set ) {
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
return - 1 ;
}
wrapper -> interface -> format_attr_set ( & format -> fattr , ap );
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
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
*/
2011-02-22 23:04:49 +00:00
static int format_isset_helper ( const struct ast_format * format , va_list ap )
2011-02-03 16:22:10 +00:00
{
int res ;
struct interface_ao2_wrapper * wrapper ;
struct ast_format tmp = {
. id = format -> id ,
. fattr = { { 0 , }, },
};
if ( ! ( wrapper = find_interface ( format ))) {
return - 1 ;
}
2012-02-28 18:15:34 +00:00
ao2_rdlock ( wrapper );
2011-02-03 16:22:10 +00:00
if ( ! wrapper -> interface ||
! wrapper -> interface -> format_attr_set ||
! wrapper -> interface -> format_attr_cmp ) {
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
return - 1 ;
}
2011-02-22 23:04:49 +00:00
/* 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 ;
}
2011-02-03 16:22:10 +00:00
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
2011-02-22 23:04:49 +00:00
return res ;
2011-02-03 16:22:10 +00:00
}
2011-02-22 23:04:49 +00:00
int ast_format_isset ( const struct ast_format * format , ... )
2011-02-03 16:22:10 +00:00
{
va_list ap ;
int res ;
va_start ( ap , format );
res = format_isset_helper ( format , ap );
va_end ( ap );
return res ;
}
2011-02-22 23:04:49 +00:00
int ast_format_get_value ( const struct ast_format * format , int key , void * value )
{
int res = 0 ;
struct interface_ao2_wrapper * wrapper ;
2012-02-28 18:15:34 +00:00
2011-02-22 23:04:49 +00:00
if ( ! ( wrapper = find_interface ( format ))) {
return - 1 ;
}
2012-02-28 18:15:34 +00:00
ao2_rdlock ( wrapper );
2011-02-22 23:04:49 +00:00
if ( ! wrapper -> interface ||
! wrapper -> interface -> format_attr_get_val ) {
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-22 23:04:49 +00:00
ao2_ref ( wrapper , - 1 );
return - 1 ;
}
res = wrapper -> interface -> format_attr_get_val ( & format -> fattr , key , value );
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-22 23:04:49 +00:00
ao2_ref ( wrapper , - 1 );
return res ;
}
2011-02-03 16:22:10 +00:00
/*! \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 ;
}
2012-02-28 18:15:34 +00:00
ao2_rdlock ( wrapper );
2011-02-03 16:22:10 +00:00
if ( ! wrapper -> interface || ! wrapper -> interface -> format_attr_cmp ) {
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
return res ;
}
res = wrapper -> interface -> format_attr_cmp ( & format1 -> fattr , & format2 -> fattr );
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
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 ;
}
2012-02-28 18:15:34 +00:00
ao2_rdlock ( wrapper );
2011-02-03 16:22:10 +00:00
if ( wrapper -> interface && wrapper -> interface -> format_attr_get_joint ) {
res = wrapper -> interface -> format_attr_get_joint ( & format1 -> fattr , & format2 -> fattr , & result -> fattr );
}
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
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 );
2011-02-22 23:04:49 +00:00
default :
return 0 ; /* not supported by old bitfield. */
2011-02-03 16:22:10 +00:00
}
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 ;
}
2011-02-22 23:04:49 +00:00
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 ;
}
2011-07-07 19:39:17 +00:00
case AST_FORMAT_CELT :
{
int samplerate ;
if ( ! ( ast_format_get_value ( format ,
CELT_ATTR_KEY_SAMP_RATE ,
& samplerate ))) {
return samplerate ;
}
}
2011-02-22 23:04:49 +00:00
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 "
" \t It 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 )
{
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 );
2012-10-02 01:47:16 +00:00
ao2_ref ( entry , - 1 );
2011-02-22 23:04:49 +00:00
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 ;
}
2012-10-02 01:47:16 +00:00
2011-02-22 23:04:49 +00:00
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 ;
}
2012-10-02 01:47:16 +00:00
/*! \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 ;
}
}
2011-10-31 17:51:22 +00:00
int ast_format_list_init ( void )
2011-02-22 23:04:49 +00:00
{
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 ;
}
2012-10-02 01:47:16 +00:00
ast_register_atexit ( format_list_shutdown );
2011-02-22 23:04:49 +00:00
return 0 ;
init_list_cleanup :
2012-10-02 01:47:16 +00:00
format_list_shutdown ();
2011-02-22 23:04:49 +00:00
return - 1 ;
}
2012-10-02 01:47:16 +00:00
/*! \internal \brief Clean up resources on Asterisk shutdown */
static void format_attr_shutdown ( void )
{
2012-12-05 03:19:08 +00:00
ast_cli_unregister_multiple ( my_clis , ARRAY_LEN ( my_clis ));
2012-10-02 01:47:16 +00:00
if ( interfaces ) {
ao2_ref ( interfaces , - 1 );
interfaces = NULL ;
}
}
2011-10-31 17:51:22 +00:00
int ast_format_attr_init ( void )
2011-02-03 16:22:10 +00:00
{
2012-02-28 18:15:34 +00:00
interfaces = ao2_container_alloc_options ( AO2_ALLOC_OPT_LOCK_RWLOCK ,
283 , interface_hash_cb , interface_cmp_cb );
if ( ! interfaces ) {
return - 1 ;
2011-02-22 23:04:49 +00:00
}
2012-12-05 03:19:08 +00:00
ast_cli_register_multiple ( my_clis , ARRAY_LEN ( my_clis ));
2012-10-02 01:47:16 +00:00
ast_register_atexit ( format_attr_shutdown );
2011-02-22 23:04:49 +00:00
return 0 ;
}
2011-07-07 19:39:17 +00:00
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 ;
}
2011-02-22 23:04:49 +00:00
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 ;
2011-07-07 19:39:17 +00:00
} else if ( ! strcasecmp ( name , "celt" )) {
* id = AST_FORMAT_CELT ;
2011-02-22 23:04:49 +00:00
} 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 ;
2011-07-07 19:39:17 +00:00
} else if ( ! strcasecmp ( rate , "44100" )) {
* result = 44100 ;
2011-02-22 23:04:49 +00:00
} else if ( ! strcasecmp ( rate , "48000" )) {
* result = 48000 ;
2011-07-07 19:39:17 +00:00
} else if ( ! strcasecmp ( rate , "96000" )) {
* result = 96000 ;
} else if ( ! strcasecmp ( rate , "192000" )) {
* result = 192000 ;
2011-02-22 23:04:49 +00:00
} else {
* result = 0 ;
2011-02-03 16:22:10 +00:00
return - 1 ;
}
2011-02-22 23:04:49 +00:00
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 ;
2011-07-07 19:39:17 +00:00
unsigned int framesize ;
2011-02-22 23:04:49 +00:00
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 );
}
2011-07-07 19:39:17 +00:00
} 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 );
}
2011-02-22 23:04:49 +00:00
} 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 ;
2011-07-07 19:39:17 +00:00
case AST_FORMAT_CELT :
if ( ! ( custom_celt_format ( & entry , settings . maxbitrate , settings . framesize ))) {
add_it = 1 ;
}
break ;
2011-02-22 23:04:49 +00:00
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 ();
2011-02-03 16:22:10 +00:00
return 0 ;
}
int ast_format_attr_reg_interface ( const struct ast_format_attr_interface * interface )
{
2011-02-22 23:04:49 +00:00
int x ;
size_t f_len ;
const struct ast_format_list * f_list ;
2011-02-03 16:22:10 +00:00
struct interface_ao2_wrapper * wrapper ;
struct interface_ao2_wrapper tmp_wrapper = {
. id = interface -> id ,
};
2011-10-31 17:51:22 +00:00
/*
* 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.
*/
2012-02-28 18:15:34 +00:00
ao2_wrlock ( interfaces );
2011-10-31 17:51:22 +00:00
/* check for duplicates first*/
2011-02-03 16:22:10 +00:00
if (( wrapper = ao2_find ( interfaces , & tmp_wrapper , ( OBJ_POINTER | OBJ_NOLOCK )))) {
2012-02-28 18:15:34 +00:00
ao2_unlock ( interfaces );
2011-02-03 16:22:10 +00:00
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 ;
}
2012-02-28 18:15:34 +00:00
wrapper = ao2_alloc_options ( sizeof ( * wrapper ), NULL , AO2_ALLOC_OPT_LOCK_RWLOCK );
if ( ! wrapper ) {
ao2_unlock ( interfaces );
2011-02-03 16:22:10 +00:00
return - 1 ;
}
wrapper -> interface = interface ;
wrapper -> id = interface -> id ;
2011-10-31 17:51:22 +00:00
/* The write lock is already held. */
2012-02-28 18:15:34 +00:00
ao2_link_flags ( interfaces , wrapper , OBJ_NOLOCK );
ao2_unlock ( interfaces );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
2011-02-22 23:04:49 +00:00
/* 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 );
}
}
2012-10-02 01:47:16 +00:00
f_list = ast_format_list_destroy ( f_list );
2011-02-03 16:22:10 +00:00
return 0 ;
}
int ast_format_attr_unreg_interface ( const struct ast_format_attr_interface * interface )
{
2011-02-22 23:04:49 +00:00
int x ;
size_t f_len ;
const struct ast_format_list * f_list ;
2011-02-03 16:22:10 +00:00
struct interface_ao2_wrapper * wrapper ;
struct interface_ao2_wrapper tmp_wrapper = {
. id = interface -> id ,
};
2012-02-28 18:15:34 +00:00
if ( ! ( wrapper = ao2_find ( interfaces , & tmp_wrapper , ( OBJ_POINTER | OBJ_UNLINK )))) {
2011-02-03 16:22:10 +00:00
return - 1 ;
}
2012-02-28 18:15:34 +00:00
ao2_wrlock ( wrapper );
2011-02-03 16:22:10 +00:00
wrapper -> interface = NULL ;
2012-02-28 18:15:34 +00:00
ao2_unlock ( wrapper );
2011-02-03 16:22:10 +00:00
ao2_ref ( wrapper , - 1 );
2011-02-22 23:04:49 +00:00
/* 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 ();
2012-10-02 01:47:16 +00:00
f_list = ast_format_list_destroy ( f_list );
2011-02-03 16:22:10 +00:00
return 0 ;
}