1999-12-05 02:03:40 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
1999-12-05 02:03:40 +00:00
*
2006-02-25 05:11:44 +00:00
* Copyright (C) 1999 - 2006, Digium, Inc.
1999-12-05 02:03:40 +00:00
*
2004-12-06 21:53:57 +00:00
* Mark Spencer <markster@digium.com>
1999-12-05 02:03:40 +00:00
*
2005-09-14 20:46:50 +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.
*
1999-12-05 02:03:40 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \brief Translate via the use of pseudo channels
2005-12-30 21:18:06 +00:00
*
* \author Mark Spencer <markster@digium.com>
1999-12-05 02:03:40 +00:00
*/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-04-29 17:24:58 +00:00
#include <sys/types.h>
2005-04-22 13:11:34 +00:00
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
2005-04-21 06:02:45 +00:00
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/logger.h"
#include "asterisk/translate.h"
2006-04-14 14:08:19 +00:00
#include "asterisk/module.h"
2005-04-21 06:02:45 +00:00
#include "asterisk/options.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/cli.h"
#include "asterisk/term.h"
1999-12-05 02:03:40 +00:00
2004-05-17 22:59:27 +00:00
#define MAX_RECALC 200 /* max sample recalc */
2006-04-08 21:40:57 +00:00
/*! \brief the list of translators */
2006-01-11 05:00:45 +00:00
static AST_LIST_HEAD_STATIC ( translators , ast_translator );
1999-12-05 02:03:40 +00:00
2006-04-08 21:40:57 +00:00
struct translator_path {
2005-11-14 19:00:38 +00:00
struct ast_translator * step ; /*!< Next step translator */
2006-02-25 05:11:44 +00:00
unsigned int cost ; /*!< Complete cost to destination */
unsigned int multistep ; /*!< Multiple conversions required for this translation */
1999-12-05 02:03:40 +00:00
};
2006-04-08 21:40:57 +00:00
/*! \brief a matrix that, for any pair of supported formats,
* indicates the total cost of translation and the first step.
* The full path can be reconstricted iterating on the matrix
* until step->dstfmt == desired_format.
2006-04-17 16:42:03 +00:00
*
* Array indexes are 'src' and 'dest', in that order.
2006-10-24 03:53:32 +00:00
*
* Note: the lock in the 'translators' list is also used to protect
* this structure.
2006-04-08 21:40:57 +00:00
*/
static struct translator_path tr_matrix [ MAX_FORMAT ][ MAX_FORMAT ];
1999-12-05 02:03:40 +00:00
2006-06-04 08:15:41 +00:00
/*! \todo
2006-04-08 21:40:57 +00:00
* TODO: sample frames for each supported input format.
* We build this on the fly, by taking an SLIN frame and using
* the existing converter to play with it.
*/
1999-12-05 02:03:40 +00:00
2006-06-04 08:15:41 +00:00
/*! \brief returns the index of the lowest bit set */
2006-06-13 20:59:15 +00:00
static force_inline int powerof ( unsigned int d )
1999-12-05 02:03:40 +00:00
{
2006-06-13 20:59:15 +00:00
int x = ffs ( d );
if ( x )
return x - 1 ;
ast_log ( LOG_WARNING , "No bits set? %d \n " , d );
1999-12-05 02:03:40 +00:00
return - 1 ;
}
2006-04-08 21:40:57 +00:00
/*
* wrappers around the translator routines.
*/
/*!
* \brief Allocate the descriptor, required outbuf space,
* and possibly also plc and desc.
*/
static void * newpvt ( struct ast_translator * t )
{
struct ast_trans_pvt * pvt ;
int len ;
int useplc = t -> plc_samples > 0 && t -> useplc ; /* cache, because it can change on the fly */
char * ofs ;
2006-04-14 14:08:19 +00:00
2006-04-08 21:40:57 +00:00
/*
* compute the required size adding private descriptor,
* plc, buffer, AST_FRIENDLY_OFFSET.
*/
len = sizeof ( * pvt ) + t -> desc_size ;
if ( useplc )
len += sizeof ( plc_state_t );
if ( t -> buf_size )
len += AST_FRIENDLY_OFFSET + t -> buf_size ;
pvt = ast_calloc ( 1 , len );
if ( ! pvt )
return NULL ;
pvt -> t = t ;
ofs = ( char * )( pvt + 1 ); /* pointer to data space */
if ( t -> desc_size ) { /* first comes the descriptor */
pvt -> pvt = ofs ;
ofs += t -> desc_size ;
}
if ( useplc ) { /* then plc state */
pvt -> plc = ( plc_state_t * ) ofs ;
ofs += sizeof ( plc_state_t );
}
if ( t -> buf_size ) /* finally buffer and header */
pvt -> outbuf = ofs + AST_FRIENDLY_OFFSET ;
/* call local init routine, if present */
2006-06-24 19:43:31 +00:00
if ( t -> newpvt && t -> newpvt ( pvt )) {
2006-04-08 21:40:57 +00:00
free ( pvt );
return NULL ;
}
2006-08-21 02:11:39 +00:00
ast_module_ref ( t -> module );
2006-04-08 21:40:57 +00:00
return pvt ;
}
static void destroy ( struct ast_trans_pvt * pvt )
{
struct ast_translator * t = pvt -> t ;
if ( t -> destroy )
t -> destroy ( pvt );
free ( pvt );
2006-08-21 02:11:39 +00:00
ast_module_unref ( t -> module );
2006-04-08 21:40:57 +00:00
}
2006-06-04 08:15:41 +00:00
/*! \brief framein wrapper, deals with plc and bound checks. */
2006-04-08 21:40:57 +00:00
static int framein ( struct ast_trans_pvt * pvt , struct ast_frame * f )
{
int16_t * dst = ( int16_t * ) pvt -> outbuf ;
int ret ;
int samples = pvt -> samples ; /* initial value */
2006-05-31 16:56:50 +00:00
/* Copy the last in jb timing info to the pvt */
pvt -> f . has_timing_info = f -> has_timing_info ;
pvt -> f . ts = f -> ts ;
pvt -> f . len = f -> len ;
pvt -> f . seqno = f -> seqno ;
2006-04-08 21:40:57 +00:00
if ( f -> samples == 0 ) {
ast_log ( LOG_WARNING , "no samples for %s \n " , pvt -> t -> name );
}
if ( pvt -> t -> buffer_samples ) { /* do not pass empty frames to callback */
if ( f -> datalen == 0 ) { /* perform PLC with nominal framesize of 20ms/160 samples */
if ( pvt -> plc ) {
int l = pvt -> t -> plc_samples ;
if ( pvt -> samples + l > pvt -> t -> buffer_samples ) {
ast_log ( LOG_WARNING , "Out of buffer space \n " );
return - 1 ;
}
l = plc_fillin ( pvt -> plc , dst + pvt -> samples , l );
pvt -> samples += l ;
2006-11-24 17:17:07 +00:00
pvt -> datalen = pvt -> samples * 2 ; /* SLIN has 2bytes for 1sample */
2006-04-08 21:40:57 +00:00
}
return 0 ;
}
if ( pvt -> samples + f -> samples > pvt -> t -> buffer_samples ) {
ast_log ( LOG_WARNING , "Out of buffer space \n " );
return - 1 ;
}
}
/* we require a framein routine, wouldn't know how to do
* it otherwise.
*/
ret = pvt -> t -> framein ( pvt , f );
/* possibly store data for plc */
if ( ! ret && pvt -> plc ) {
int l = pvt -> t -> plc_samples ;
if ( pvt -> samples < l )
l = pvt -> samples ;
plc_rx ( pvt -> plc , dst + pvt -> samples - l , l );
}
/* diagnostic ... */
if ( pvt -> samples == samples )
ast_log ( LOG_WARNING , "%s did not update samples %d \n " ,
pvt -> t -> name , pvt -> samples );
return ret ;
}
2006-06-04 08:15:41 +00:00
/*! \brief generic frameout routine.
2006-04-08 21:40:57 +00:00
* If samples and datalen are 0, take whatever is in pvt
* and reset them, otherwise take the values in the caller and
* leave alone the pvt values.
*/
struct ast_frame * ast_trans_frameout ( struct ast_trans_pvt * pvt ,
int datalen , int samples )
{
struct ast_frame * f = & pvt -> f ;
if ( samples )
f -> samples = samples ;
else {
if ( pvt -> samples == 0 )
return NULL ;
f -> samples = pvt -> samples ;
pvt -> samples = 0 ;
}
if ( datalen )
f -> datalen = datalen ;
else {
f -> datalen = pvt -> datalen ;
pvt -> datalen = 0 ;
}
f -> frametype = AST_FRAME_VOICE ;
f -> subclass = 1 << ( pvt -> t -> dstfmt );
f -> mallocd = 0 ;
f -> offset = AST_FRIENDLY_OFFSET ;
f -> src = pvt -> t -> name ;
f -> data = pvt -> outbuf ;
return f ;
}
static struct ast_frame * default_frameout ( struct ast_trans_pvt * pvt )
{
return ast_trans_frameout ( pvt , 0 , 0 );
}
/* end of callback wrappers and helpers */
1999-12-05 02:03:40 +00:00
void ast_translator_free_path ( struct ast_trans_pvt * p )
{
2006-04-08 21:40:57 +00:00
struct ast_trans_pvt * pn = p ;
while ( ( p = pn ) ) {
pn = p -> next ;
destroy ( p );
1999-12-05 02:03:40 +00:00
}
}
2006-06-04 08:15:41 +00:00
/*! \brief Build a chain of translators based upon the given source and dest formats */
2001-03-22 04:20:13 +00:00
struct ast_trans_pvt * ast_translator_build_path ( int dest , int source )
1999-12-05 02:03:40 +00:00
{
2006-04-08 21:40:57 +00:00
struct ast_trans_pvt * head = NULL , * tail = NULL ;
2005-07-25 19:10:38 +00:00
1999-12-05 02:03:40 +00:00
source = powerof ( source );
dest = powerof ( dest );
2005-07-25 19:10:38 +00:00
2006-10-24 03:53:32 +00:00
AST_LIST_LOCK ( & translators );
2006-03-29 04:09:31 +00:00
while ( source != dest ) {
2006-04-08 21:40:57 +00:00
struct ast_trans_pvt * cur ;
struct ast_translator * t = tr_matrix [ source ][ dest ]. step ;
if ( ! t ) {
2003-08-16 05:10:35 +00:00
ast_log ( LOG_WARNING , "No translator path from %s to %s \n " ,
ast_getformatname ( source ), ast_getformatname ( dest ));
2006-10-24 03:53:32 +00:00
AST_LIST_UNLOCK ( & translators );
2000-01-05 20:03:55 +00:00
return NULL ;
1999-12-05 02:03:40 +00:00
}
2006-04-08 21:40:57 +00:00
if ( ! ( cur = newpvt ( t ))) {
2005-07-25 19:10:38 +00:00
ast_log ( LOG_WARNING , "Failed to build translator step from %d to %d \n " , source , dest );
2006-04-08 21:40:57 +00:00
if ( head )
ast_translator_free_path ( head );
2006-10-24 03:53:32 +00:00
AST_LIST_UNLOCK ( & translators );
2005-07-25 19:10:38 +00:00
return NULL ;
}
2006-04-08 21:40:57 +00:00
if ( ! head )
head = cur ;
else
tail -> next = cur ;
tail = cur ;
cur -> nextin = cur -> nextout = ast_tv ( 0 , 0 );
2005-07-25 19:10:38 +00:00
/* Keep going if this isn't the final destination */
2006-04-08 21:40:57 +00:00
source = cur -> t -> dstfmt ;
1999-12-05 02:03:40 +00:00
}
2006-10-24 03:53:32 +00:00
AST_LIST_UNLOCK ( & translators );
2006-04-08 21:40:57 +00:00
return head ;
1999-12-05 02:03:40 +00:00
}
2006-04-08 21:40:57 +00:00
/*! \brief do the actual translation */
2001-03-22 04:20:13 +00:00
struct ast_frame * ast_translate ( struct ast_trans_pvt * path , struct ast_frame * f , int consume )
1999-12-05 02:03:40 +00:00
{
2006-04-08 21:40:57 +00:00
struct ast_trans_pvt * p = path ;
struct ast_frame * out = f ;
2004-04-20 15:28:58 +00:00
struct timeval delivery ;
2006-05-31 16:56:50 +00:00
int has_timing_info ;
long ts ;
long len ;
int seqno ;
has_timing_info = f -> has_timing_info ;
ts = f -> ts ;
len = f -> len ;
seqno = f -> seqno ;
2006-04-08 21:40:57 +00:00
/* XXX hmmm... check this below */
2005-07-15 23:00:47 +00:00
if ( ! ast_tvzero ( f -> delivery )) {
if ( ! ast_tvzero ( path -> nextin )) {
2004-04-05 20:47:44 +00:00
/* Make sure this is in line with what we were expecting */
2005-07-15 23:00:47 +00:00
if ( ! ast_tveq ( path -> nextin , f -> delivery )) {
2004-04-05 20:47:44 +00:00
/* The time has changed between what we expected and this
2005-07-25 19:41:39 +00:00
most recent time on the new packet. If we have a
valid prediction adjust our output time appropriately */
if ( ! ast_tvzero ( path -> nextout )) {
path -> nextout = ast_tvadd ( path -> nextout ,
ast_tvsub ( f -> delivery , path -> nextin ));
}
2005-07-15 23:00:47 +00:00
path -> nextin = f -> delivery ;
2004-04-05 20:47:44 +00:00
}
} else {
/* This is our first pass. Make sure the timing looks good */
2005-07-15 23:00:47 +00:00
path -> nextin = f -> delivery ;
path -> nextout = f -> delivery ;
2004-04-05 20:47:44 +00:00
}
/* Predict next incoming sample */
2005-07-15 23:00:47 +00:00
path -> nextin = ast_tvadd ( path -> nextin , ast_samp2tv ( f -> samples , 8000 ));
2004-04-05 19:45:53 +00:00
}
2005-07-15 23:00:47 +00:00
delivery = f -> delivery ;
2006-04-08 21:40:57 +00:00
for ( ; out && p ; p = p -> next ) {
framein ( p , out );
out = p -> t -> frameout ( p );
}
2001-03-22 04:20:13 +00:00
if ( consume )
ast_frfree ( f );
2006-04-08 21:40:57 +00:00
if ( out == NULL )
return NULL ;
/* we have a frame, play with times */
if ( ! ast_tvzero ( delivery )) {
/* Regenerate prediction after a discontinuity */
if ( ast_tvzero ( path -> nextout ))
path -> nextout = ast_tvnow ();
/* Use next predicted outgoing timestamp */
out -> delivery = path -> nextout ;
/* Predict next outgoing timestamp from samples in this
frame. */
path -> nextout = ast_tvadd ( path -> nextout , ast_samp2tv ( out -> samples , 8000 ));
} else {
out -> delivery = ast_tv ( 0 , 0 );
2006-05-31 16:56:50 +00:00
out -> has_timing_info = has_timing_info ;
if ( has_timing_info ) {
out -> ts = ts ;
out -> len = len ;
out -> seqno = seqno ;
}
1999-12-05 02:03:40 +00:00
}
2006-04-08 21:40:57 +00:00
/* Invalidate prediction if we're entering a silence period */
if ( out -> frametype == AST_FRAME_CNG )
path -> nextout = ast_tv ( 0 , 0 );
return out ;
1999-12-05 02:03:40 +00:00
}
2006-04-08 21:40:57 +00:00
/*! \brief compute the cost of a single translation step */
static void calc_cost ( struct ast_translator * t , int seconds )
2004-05-17 22:59:27 +00:00
{
int sofar = 0 ;
2006-04-08 21:40:57 +00:00
struct ast_trans_pvt * pvt ;
2005-07-15 23:00:47 +00:00
struct timeval start ;
2004-05-17 22:59:27 +00:00
int cost ;
2005-11-14 19:00:38 +00:00
2006-04-08 21:40:57 +00:00
if ( ! seconds )
seconds = 1 ;
2004-05-17 22:59:27 +00:00
/* If they don't make samples, give them a terrible score */
if ( ! t -> sample ) {
ast_log ( LOG_WARNING , "Translator '%s' does not produce sample frames. \n " , t -> name );
t -> cost = 99999 ;
return ;
}
2006-04-08 21:40:57 +00:00
pvt = newpvt ( t );
2004-05-17 22:59:27 +00:00
if ( ! pvt ) {
ast_log ( LOG_WARNING , "Translator '%s' appears to be broken and will probably fail. \n " , t -> name );
t -> cost = 99999 ;
return ;
}
2005-07-15 23:00:47 +00:00
start = ast_tvnow ();
2006-04-08 21:40:57 +00:00
/* Call the encoder until we've processed the required number of samples */
while ( sofar < seconds * 8000 ) {
struct ast_frame * f = t -> sample ();
2004-05-17 22:59:27 +00:00
if ( ! f ) {
ast_log ( LOG_WARNING , "Translator '%s' failed to produce a sample frame. \n " , t -> name );
2006-04-08 21:40:57 +00:00
destroy ( pvt );
2004-05-17 22:59:27 +00:00
t -> cost = 99999 ;
return ;
}
2006-04-08 21:40:57 +00:00
framein ( pvt , f );
2004-05-17 22:59:27 +00:00
ast_frfree ( f );
2006-07-10 19:05:48 +00:00
while (( f = t -> frameout ( pvt ))) {
2006-04-08 21:40:57 +00:00
sofar += f -> samples ;
ast_frfree ( f );
2004-05-17 22:59:27 +00:00
}
}
2005-07-15 23:00:47 +00:00
cost = ast_tvdiff_ms ( ast_tvnow (), start );
2006-04-08 21:40:57 +00:00
destroy ( pvt );
t -> cost = cost / seconds ;
2004-05-17 22:59:27 +00:00
if ( ! t -> cost )
t -> cost = 1 ;
}
2006-04-08 21:40:57 +00:00
/*!
* \brief rebuild a translation matrix.
* \note This function expects the list of translators to be locked
2006-01-11 05:00:45 +00:00
*/
2004-05-17 22:59:27 +00:00
static void rebuild_matrix ( int samples )
1999-12-05 02:03:40 +00:00
{
struct ast_translator * t ;
2006-07-10 19:05:48 +00:00
int x ; /* source format index */
int y ; /* intermediate format index */
int z ; /* destination format index */
2005-11-14 19:00:38 +00:00
1999-12-05 02:03:40 +00:00
if ( option_debug )
2005-11-14 19:00:38 +00:00
ast_log ( LOG_DEBUG , "Resetting translation matrix \n " );
2006-02-25 05:11:44 +00:00
1999-12-05 02:03:40 +00:00
bzero ( tr_matrix , sizeof ( tr_matrix ));
2006-04-18 21:39:20 +00:00
2006-04-08 21:40:57 +00:00
/* first, compute all direct costs */
2006-01-11 05:00:45 +00:00
AST_LIST_TRAVERSE ( & translators , t , list ) {
2006-10-31 21:47:48 +00:00
if ( ! t -> active )
continue ;
2006-04-08 21:40:57 +00:00
x = t -> srcfmt ;
z = t -> dstfmt ;
2006-02-25 05:11:44 +00:00
if ( samples )
2005-11-14 19:00:38 +00:00
calc_cost ( t , samples );
2004-05-17 22:59:27 +00:00
2006-04-08 21:40:57 +00:00
if ( ! tr_matrix [ x ][ z ]. step || t -> cost < tr_matrix [ x ][ z ]. cost ) {
tr_matrix [ x ][ z ]. step = t ;
tr_matrix [ x ][ z ]. cost = t -> cost ;
1999-12-05 02:03:40 +00:00
}
}
2006-04-18 21:39:20 +00:00
2006-04-08 21:40:57 +00:00
/*
* For each triple x, y, z of distinct formats, check if there is
* a path from x to z through y which is cheaper than what is
* currently known, and in case, update the matrix.
* Repeat until the matrix is stable.
*/
for (;;) {
int changed = 0 ;
2006-07-10 19:05:48 +00:00
for ( x = 0 ; x < MAX_FORMAT ; x ++ ) { /* source format */
for ( y = 0 ; y < MAX_FORMAT ; y ++ ) { /* intermediate format */
if ( x == y ) /* skip ourselves */
2006-02-25 05:11:44 +00:00
continue ;
2006-07-10 19:05:48 +00:00
for ( z = 0 ; z < MAX_FORMAT ; z ++ ) { /* dst format */
2006-04-08 21:40:57 +00:00
int newcost ;
2006-02-25 05:11:44 +00:00
2006-07-10 19:05:48 +00:00
if ( z == x || z == y ) /* skip null conversions */
2006-04-08 21:40:57 +00:00
continue ;
2006-07-10 19:05:48 +00:00
if ( ! tr_matrix [ x ][ y ]. step ) /* no path from x to y */
2006-04-08 21:40:57 +00:00
continue ;
2006-07-10 19:05:48 +00:00
if ( ! tr_matrix [ y ][ z ]. step ) /* no path from y to z */
2006-04-08 21:40:57 +00:00
continue ;
newcost = tr_matrix [ x ][ y ]. cost + tr_matrix [ y ][ z ]. cost ;
if ( tr_matrix [ x ][ z ]. step && newcost >= tr_matrix [ x ][ z ]. cost )
2006-07-10 19:05:48 +00:00
continue ; /* x->y->z is more expensive than
* the existing path */
2006-04-08 21:40:57 +00:00
/* ok, we can get from x to z via y with a cost that
is the sum of the transition from x to y and
from y to z */
tr_matrix [ x ][ z ]. step = tr_matrix [ x ][ y ]. step ;
tr_matrix [ x ][ z ]. cost = newcost ;
tr_matrix [ x ][ z ]. multistep = 1 ;
if ( option_debug )
ast_log ( LOG_DEBUG , "Discovered %d cost path from %s to %s, via %d \n " , tr_matrix [ x ][ z ]. cost , ast_getformatname ( x ), ast_getformatname ( z ), y );
changed ++ ;
2006-02-25 05:11:44 +00:00
}
}
}
2006-04-08 21:40:57 +00:00
if ( ! changed )
break ;
}
1999-12-05 02:03:40 +00:00
}
2005-11-14 19:00:38 +00:00
/*! \brief CLI "show translation" command handler */
2006-09-18 19:54:18 +00:00
static int show_translation_deprecated ( int fd , int argc , char * argv [])
2000-01-05 20:03:55 +00:00
{
2006-10-25 00:26:17 +00:00
#define SHOW_TRANS 13
2004-06-22 20:11:15 +00:00
int x , y , z ;
2006-08-16 03:48:36 +00:00
int curlen = 0 , longest = 0 ;
2006-04-08 21:40:57 +00:00
2004-05-17 22:59:27 +00:00
if ( argc > 4 )
2000-01-05 20:03:55 +00:00
return RESULT_SHOWUSAGE ;
2004-05-17 22:59:27 +00:00
2006-01-11 05:00:45 +00:00
AST_LIST_LOCK ( & translators );
2006-07-10 19:05:48 +00:00
if ( argv [ 2 ] && ! strcasecmp ( argv [ 2 ], "recalc" )) {
2004-06-22 20:11:15 +00:00
z = argv [ 3 ] ? atoi ( argv [ 3 ]) : 1 ;
2004-05-17 22:59:27 +00:00
2004-06-22 20:11:15 +00:00
if ( z <= 0 ) {
2006-07-10 19:05:48 +00:00
ast_cli ( fd , " C'mon let's be serious here... defaulting to 1. \n " );
2004-06-22 20:11:15 +00:00
z = 1 ;
}
2004-05-17 22:59:27 +00:00
2004-06-22 20:11:15 +00:00
if ( z > MAX_RECALC ) {
2006-07-10 19:05:48 +00:00
ast_cli ( fd , " Maximum limit of recalc exceeded by %d, truncating value to %d \n " , z - MAX_RECALC , MAX_RECALC );
2004-06-22 20:11:15 +00:00
z = MAX_RECALC ;
}
2006-07-10 19:05:48 +00:00
ast_cli ( fd , " Recalculating Codec Translation (number of sample seconds: %d) \n\n " , z );
2004-06-22 20:11:15 +00:00
rebuild_matrix ( z );
2004-05-17 22:59:27 +00:00
}
2006-08-30 16:04:24 +00:00
ast_cli ( fd , " Translation times between formats (in milliseconds) for one second of data \n " );
2006-08-16 03:48:36 +00:00
ast_cli ( fd , " Source Format (Rows) Destination Format (Columns) \n\n " );
/* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
for ( x = 0 ; x < SHOW_TRANS ; x ++ ) {
curlen = strlen ( ast_getformatname ( 1 << ( x + 1 )));
if ( curlen > longest )
longest = curlen ;
}
2005-11-14 19:00:38 +00:00
for ( x = - 1 ; x < SHOW_TRANS ; x ++ ) {
2006-10-31 22:02:15 +00:00
char line [ 120 ];
2006-04-08 21:40:57 +00:00
char * buf = line ;
2006-04-11 15:52:06 +00:00
size_t left = sizeof ( line ) - 1 ; /* one initial space */
2006-04-08 21:40:57 +00:00
/* next 2 lines run faster than using ast_build_string() */
* buf ++ = ' ' ;
* buf = '\0' ;
2006-07-10 19:05:48 +00:00
for ( y = - 1 ; y < SHOW_TRANS ; y ++ ) {
2006-08-16 03:48:36 +00:00
curlen = strlen ( ast_getformatname ( 1 << ( y )));
if ( x >= 0 && y >= 0 && tr_matrix [ x ][ y ]. step ) {
/* XXX 999 is a little hackish
We don't want this number being larger than the shortest (or current) codec
For now, that is "gsm" */
ast_build_string ( & buf , & left , "%*d" , curlen + 1 , tr_matrix [ x ][ y ]. cost > 999 ? 0 : tr_matrix [ x ][ y ]. cost );
} else if ( x == - 1 && y >= 0 ) {
/* Top row - use a dynamic size */
ast_build_string ( & buf , & left , "%*s" , curlen + 1 , ast_getformatname ( 1 << ( x + y + 1 )) );
} else if ( y == - 1 && x >= 0 ) {
/* Left column - use a static size. */
ast_build_string ( & buf , & left , "%*s" , longest , ast_getformatname ( 1 << ( x + y + 1 )) );
} else if ( x >= 0 && y >= 0 ) {
ast_build_string ( & buf , & left , "%*s" , curlen + 1 , "-" );
2006-04-08 21:40:57 +00:00
} else {
2006-08-16 03:48:36 +00:00
ast_build_string ( & buf , & left , "%*s" , longest , "" );
2006-04-08 21:40:57 +00:00
}
2000-01-05 20:03:55 +00:00
}
2006-04-08 21:40:57 +00:00
ast_build_string ( & buf , & left , " \n " );
2000-01-05 20:03:55 +00:00
ast_cli ( fd , line );
}
2006-01-11 05:00:45 +00:00
AST_LIST_UNLOCK ( & translators );
2000-01-05 20:03:55 +00:00
return RESULT_SUCCESS ;
}
2006-09-18 19:54:18 +00:00
static int show_translation ( int fd , int argc , char * argv [])
{
int x , y , z ;
int curlen = 0 , longest = 0 ;
if ( argc > 5 )
return RESULT_SHOWUSAGE ;
AST_LIST_LOCK ( & translators );
if ( argv [ 3 ] && ! strcasecmp ( argv [ 3 ], "recalc" )) {
z = argv [ 4 ] ? atoi ( argv [ 4 ]) : 1 ;
if ( z <= 0 ) {
ast_cli ( fd , " C'mon let's be serious here... defaulting to 1. \n " );
z = 1 ;
}
if ( z > MAX_RECALC ) {
ast_cli ( fd , " Maximum limit of recalc exceeded by %d, truncating value to %d \n " , z - MAX_RECALC , MAX_RECALC );
z = MAX_RECALC ;
}
ast_cli ( fd , " Recalculating Codec Translation (number of sample seconds: %d) \n\n " , z );
rebuild_matrix ( z );
}
ast_cli ( fd , " Translation times between formats (in milliseconds) for one second of data \n " );
ast_cli ( fd , " Source Format (Rows) Destination Format (Columns) \n\n " );
/* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
for ( x = 0 ; x < SHOW_TRANS ; x ++ ) {
curlen = strlen ( ast_getformatname ( 1 << ( x + 1 )));
if ( curlen > longest )
longest = curlen ;
}
for ( x = - 1 ; x < SHOW_TRANS ; x ++ ) {
2006-10-31 22:02:15 +00:00
char line [ 120 ];
2006-09-18 19:54:18 +00:00
char * buf = line ;
size_t left = sizeof ( line ) - 1 ; /* one initial space */
/* next 2 lines run faster than using ast_build_string() */
* buf ++ = ' ' ;
* buf = '\0' ;
for ( y = - 1 ; y < SHOW_TRANS ; y ++ ) {
curlen = strlen ( ast_getformatname ( 1 << ( y )));
if ( x >= 0 && y >= 0 && tr_matrix [ x ][ y ]. step ) {
/* XXX 999 is a little hackish
We don't want this number being larger than the shortest (or current) codec
For now, that is "gsm" */
ast_build_string ( & buf , & left , "%*d" , curlen + 1 , tr_matrix [ x ][ y ]. cost > 999 ? 0 : tr_matrix [ x ][ y ]. cost );
} else if ( x == - 1 && y >= 0 ) {
/* Top row - use a dynamic size */
ast_build_string ( & buf , & left , "%*s" , curlen + 1 , ast_getformatname ( 1 << ( x + y + 1 )) );
} else if ( y == - 1 && x >= 0 ) {
/* Left column - use a static size. */
ast_build_string ( & buf , & left , "%*s" , longest , ast_getformatname ( 1 << ( x + y + 1 )) );
} else if ( x >= 0 && y >= 0 ) {
ast_build_string ( & buf , & left , "%*s" , curlen + 1 , "-" );
} else {
ast_build_string ( & buf , & left , "%*s" , longest , "" );
}
}
ast_build_string ( & buf , & left , " \n " );
ast_cli ( fd , line );
}
AST_LIST_UNLOCK ( & translators );
return RESULT_SUCCESS ;
}
2000-01-05 20:03:55 +00:00
static char show_trans_usage [] =
2006-09-18 19:54:18 +00:00
"Usage: core show translation [recalc] [<recalc seconds>] \n "
2000-01-05 20:03:55 +00:00
" Displays known codec translators and the cost associated \n "
2005-11-14 19:00:38 +00:00
"with each conversion. If the argument 'recalc' is supplied along \n "
2004-05-17 22:59:27 +00:00
"with optional number of seconds to test a new test will be performed \n "
"as the chart is being displayed. \n " ;
2000-01-05 20:03:55 +00:00
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_show_translation_deprecated = {
{ "show" , "translation" , NULL },
show_translation_deprecated , NULL ,
NULL };
static struct ast_cli_entry cli_translate [] = {
{ { "core" , "show" , "translation" , NULL },
show_translation , "Display translation matrix" ,
show_trans_usage , NULL , & cli_show_translation_deprecated },
};
2000-01-05 20:03:55 +00:00
2006-06-04 08:15:41 +00:00
/*! \brief register codec translator */
2006-08-21 02:11:39 +00:00
int __ast_register_translator ( struct ast_translator * t , struct ast_module * mod )
1999-12-05 02:03:40 +00:00
{
2006-04-08 21:40:57 +00:00
static int added_cli = 0 ;
2006-10-24 23:45:19 +00:00
struct ast_translator * u ;
2006-04-08 21:40:57 +00:00
2006-08-21 02:11:39 +00:00
if ( ! mod ) {
2006-04-14 14:08:19 +00:00
ast_log ( LOG_WARNING , "Missing module pointer, you need to supply one \n " );
2006-04-08 21:40:57 +00:00
return - 1 ;
}
2006-08-21 02:11:39 +00:00
2006-10-31 21:36:17 +00:00
if ( ! t -> buf_size ) {
ast_log ( LOG_WARNING , "empty buf size, you need to supply one \n " );
return - 1 ;
}
2006-08-21 02:11:39 +00:00
2006-10-31 21:36:17 +00:00
t -> module = mod ;
2006-10-31 21:47:48 +00:00
t -> srcfmt = powerof ( t -> srcfmt );
t -> dstfmt = powerof ( t -> dstfmt );
t -> active = 1 ;
2006-10-31 21:36:17 +00:00
if ( t -> plc_samples ) {
if ( t -> buffer_samples < t -> plc_samples ) {
ast_log ( LOG_WARNING , "plc_samples %d buffer_samples %d \n " ,
t -> plc_samples , t -> buffer_samples );
2006-10-31 21:23:06 +00:00
return - 1 ;
}
2006-10-31 21:36:17 +00:00
if ( t -> dstfmt != AST_FORMAT_SLINEAR )
ast_log ( LOG_WARNING , "plc_samples %d format %x \n " ,
t -> plc_samples , t -> dstfmt );
2006-10-31 21:23:06 +00:00
}
2006-10-31 21:36:17 +00:00
if ( t -> srcfmt >= MAX_FORMAT ) {
ast_log ( LOG_WARNING , "Source format %s is larger than MAX_FORMAT \n " , ast_getformatname ( t -> srcfmt ));
return - 1 ;
}
2006-10-31 21:47:48 +00:00
2006-10-31 21:36:17 +00:00
if ( t -> dstfmt >= MAX_FORMAT ) {
ast_log ( LOG_WARNING , "Destination format %s is larger than MAX_FORMAT \n " , ast_getformatname ( t -> dstfmt ));
return - 1 ;
}
2006-10-31 21:47:48 +00:00
2006-10-31 21:36:17 +00:00
if ( t -> buf_size ) {
/*
* Align buf_size properly, rounding up to the machine-specific
* alignment for pointers.
*/
struct _test_align { void * a , * b ; } p ;
int align = ( char * ) & p . b - ( char * ) & p . a ;
2006-10-31 21:47:48 +00:00
2006-10-31 21:36:17 +00:00
t -> buf_size = (( t -> buf_size + align - 1 ) / align ) * align ;
}
2006-10-31 21:47:48 +00:00
2006-10-31 21:36:17 +00:00
if ( t -> frameout == NULL )
t -> frameout = default_frameout ;
calc_cost ( t , 1 );
2006-10-31 21:47:48 +00:00
2006-04-08 21:40:57 +00:00
if ( option_verbose > 1 ) {
char tmp [ 80 ];
2006-10-31 21:47:48 +00:00
2006-04-08 21:40:57 +00:00
ast_verbose ( VERBOSE_PREFIX_2 "Registered translator '%s' from format %s to %s, cost %d \n " ,
2006-10-31 21:47:48 +00:00
term_color ( tmp , t -> name , COLOR_MAGENTA , COLOR_BLACK , sizeof ( tmp )),
ast_getformatname ( 1 << t -> srcfmt ), ast_getformatname ( 1 << t -> dstfmt ), t -> cost );
2006-04-08 21:40:57 +00:00
}
2006-10-31 21:47:48 +00:00
2000-01-05 20:03:55 +00:00
if ( ! added_cli ) {
2006-09-18 19:54:18 +00:00
ast_cli_register_multiple ( cli_translate , sizeof ( cli_translate ) / sizeof ( struct ast_cli_entry ));
2000-01-05 20:03:55 +00:00
added_cli ++ ;
}
2006-10-24 23:45:19 +00:00
2006-10-31 21:47:48 +00:00
AST_LIST_LOCK ( & translators );
2006-10-24 23:45:19 +00:00
/* find any existing translators that provide this same srcfmt/dstfmt,
and put this one in order based on cost */
AST_LIST_TRAVERSE_SAFE_BEGIN ( & translators , u , list ) {
if (( u -> srcfmt == t -> srcfmt ) &&
( u -> dstfmt == t -> dstfmt ) &&
( u -> cost > t -> cost )) {
AST_LIST_INSERT_BEFORE_CURRENT ( & translators , t , list );
t = NULL ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
/* if no existing translator was found for this format combination,
add it to the beginning of the list */
if ( t )
AST_LIST_INSERT_HEAD ( & translators , t , list );
2004-05-17 22:59:27 +00:00
rebuild_matrix ( 0 );
2006-10-31 21:47:48 +00:00
2006-01-11 05:00:45 +00:00
AST_LIST_UNLOCK ( & translators );
2006-10-31 21:47:48 +00:00
1999-12-05 02:03:40 +00:00
return 0 ;
}
2005-11-14 19:00:38 +00:00
/*! \brief unregister codec translator */
1999-12-05 02:03:40 +00:00
int ast_unregister_translator ( struct ast_translator * t )
{
2004-04-21 04:26:38 +00:00
char tmp [ 80 ];
2006-01-11 05:00:45 +00:00
struct ast_translator * u ;
2006-10-30 22:19:55 +00:00
int found = 0 ;
2006-01-11 05:00:45 +00:00
AST_LIST_LOCK ( & translators );
AST_LIST_TRAVERSE_SAFE_BEGIN ( & translators , u , list ) {
1999-12-05 02:03:40 +00:00
if ( u == t ) {
2006-01-11 05:00:45 +00:00
AST_LIST_REMOVE_CURRENT ( & translators , list );
2004-04-21 04:26:38 +00:00
if ( option_verbose > 1 )
ast_verbose ( VERBOSE_PREFIX_2 "Unregistered translator '%s' from format %s to %s \n " , term_color ( tmp , t -> name , COLOR_MAGENTA , COLOR_BLACK , sizeof ( tmp )), ast_getformatname ( 1 << t -> srcfmt ), ast_getformatname ( 1 << t -> dstfmt ));
2006-10-30 22:19:55 +00:00
found = 1 ;
1999-12-05 02:03:40 +00:00
break ;
}
}
2006-10-24 23:45:19 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
2006-10-30 22:19:55 +00:00
if ( found )
rebuild_matrix ( 0 );
2006-01-11 05:00:45 +00:00
AST_LIST_UNLOCK ( & translators );
2006-10-30 22:19:55 +00:00
1999-12-05 02:03:40 +00:00
return ( u ? 0 : - 1 );
}
2006-10-31 21:47:48 +00:00
void ast_translator_activate ( struct ast_translator * t )
{
AST_LIST_LOCK ( & translators );
t -> active = 1 ;
rebuild_matrix ( 0 );
AST_LIST_UNLOCK ( & translators );
}
void ast_translator_deactivate ( struct ast_translator * t )
{
AST_LIST_LOCK ( & translators );
t -> active = 0 ;
rebuild_matrix ( 0 );
AST_LIST_UNLOCK ( & translators );
}
2005-11-14 19:00:38 +00:00
/*! \brief Calculate our best translator source format, given costs, and a desired destination */
2001-03-22 04:20:13 +00:00
int ast_translator_best_choice ( int * dst , int * srcs )
1999-12-05 02:03:40 +00:00
{
2000-01-05 20:03:55 +00:00
int x , y ;
2005-04-04 03:28:38 +00:00
int best = - 1 ;
int bestdst = 0 ;
2006-04-08 21:40:57 +00:00
int cur , cursrc ;
2005-04-04 03:28:38 +00:00
int besttime = INT_MAX ;
2006-02-25 05:11:44 +00:00
int beststeps = INT_MAX ;
2006-04-08 21:40:57 +00:00
int common = ( * dst ) & ( * srcs ); /* are there common formats ? */
if ( common ) { /* yes, pick one and return */
2006-07-10 19:05:48 +00:00
for ( cur = 1 , y = 0 ; y < MAX_FORMAT ; cur <<= 1 , y ++ ) {
2006-04-08 21:40:57 +00:00
if ( cur & common ) /* guaranteed to find one */
break ;
2004-12-06 21:53:57 +00:00
}
2006-04-08 21:40:57 +00:00
/* We are done, this is a common format to both. */
2006-07-10 19:05:48 +00:00
* srcs = * dst = cur ;
2006-04-08 21:40:57 +00:00
return 0 ;
} else { /* No, we will need to translate */
2006-01-11 05:00:45 +00:00
AST_LIST_LOCK ( & translators );
2006-07-10 19:05:48 +00:00
for ( cur = 1 , y = 0 ; y < MAX_FORMAT ; cur <<= 1 , y ++ ) {
2006-04-08 21:40:57 +00:00
if ( ! ( cur & * dst ))
2006-02-25 05:11:44 +00:00
continue ;
2006-07-10 19:05:48 +00:00
for ( cursrc = 1 , x = 0 ; x < MAX_FORMAT ; cursrc <<= 1 , x ++ ) {
2006-04-08 21:40:57 +00:00
if ( ! ( * srcs & cursrc ) || ! tr_matrix [ x ][ y ]. step ||
tr_matrix [ x ][ y ]. cost > besttime )
continue ; /* not existing or no better */
if ( tr_matrix [ x ][ y ]. cost < besttime ||
tr_matrix [ x ][ y ]. multistep < beststeps ) {
/* better than what we have so far */
best = cursrc ;
2006-02-25 05:11:44 +00:00
bestdst = cur ;
besttime = tr_matrix [ x ][ y ]. cost ;
beststeps = tr_matrix [ x ][ y ]. multistep ;
2004-12-06 21:53:57 +00:00
}
2006-02-25 05:11:44 +00:00
}
2004-12-06 21:53:57 +00:00
}
2006-01-11 05:00:45 +00:00
AST_LIST_UNLOCK ( & translators );
2006-04-08 21:40:57 +00:00
if ( best > - 1 ) {
* srcs = best ;
* dst = bestdst ;
best = 0 ;
}
return best ;
1999-12-05 02:03:40 +00:00
}
}
2006-04-17 16:42:03 +00:00
unsigned int ast_translate_path_steps ( unsigned int dest , unsigned int src )
{
2006-10-24 03:53:32 +00:00
unsigned int res = - 1 ;
2006-04-18 21:39:20 +00:00
/* convert bitwise format numbers into array indices */
src = powerof ( src );
dest = powerof ( dest );
2006-10-24 03:53:32 +00:00
AST_LIST_LOCK ( & translators );
if ( tr_matrix [ src ][ dest ]. step )
res = tr_matrix [ src ][ dest ]. multistep + 1 ;
AST_LIST_UNLOCK ( & translators );
return res ;
2006-04-17 16:42:03 +00:00
}
2006-07-10 19:05:48 +00:00
2006-10-24 03:45:42 +00:00
unsigned int ast_translate_available_formats ( unsigned int dest , unsigned int src )
{
unsigned int res = dest ;
unsigned int x ;
2006-10-26 16:31:05 +00:00
unsigned int src_audio = src & AST_FORMAT_AUDIO_MASK ;
unsigned int src_video = src & AST_FORMAT_VIDEO_MASK ;
2006-10-24 03:45:42 +00:00
2006-10-25 00:10:54 +00:00
/* if we don't have a source format, we just have to try all
possible destination formats */
if ( ! src )
return dest ;
2006-10-26 16:31:05 +00:00
/* If we have a source audio format, get its format index */
if ( src_audio )
src_audio = powerof ( src_audio );
/* If we have a source video format, get its format index */
if ( src_video )
src_video = powerof ( src_video );
2006-10-24 03:53:32 +00:00
AST_LIST_LOCK ( & translators );
2006-10-26 16:31:05 +00:00
/* For a given source audio format, traverse the list of
known audio formats to determine whether there exists
a translation path from the source format to the
destination format. */
for ( x = 1 ; src_audio && x < AST_FORMAT_MAX_AUDIO ; x <<= 1 ) {
2006-10-24 03:45:42 +00:00
/* if this is not a desired format, nothing to do */
if ( ! dest & x )
continue ;
/* if the source is supplying this format, then
we can leave it in the result */
if ( src & x )
continue ;
/* if we don't have a translation path from the src
to this format, remove it from the result */
2006-10-31 05:55:07 +00:00
if ( ! tr_matrix [ src_audio ][ powerof ( x )]. step ) {
2006-10-24 03:45:42 +00:00
res &= ~ x ;
2006-10-31 05:55:07 +00:00
continue ;
}
2006-10-30 22:19:55 +00:00
/* now check the opposite direction */
if ( ! tr_matrix [ powerof ( x )][ src_audio ]. step )
res &= ~ x ;
2006-10-24 03:45:42 +00:00
}
2006-10-26 16:31:05 +00:00
/* For a given source video format, traverse the list of
known video formats to determine whether there exists
a translation path from the source format to the
destination format. */
for (; src_video && x < AST_FORMAT_MAX_VIDEO ; x <<= 1 ) {
2006-10-24 03:45:42 +00:00
/* if this is not a desired format, nothing to do */
if ( ! dest & x )
continue ;
/* if the source is supplying this format, then
we can leave it in the result */
if ( src & x )
continue ;
/* if we don't have a translation path from the src
to this format, remove it from the result */
2006-10-31 05:55:07 +00:00
if ( ! tr_matrix [ src_video ][ powerof ( x )]. step ) {
2006-10-24 03:45:42 +00:00
res &= ~ x ;
2006-10-31 05:55:07 +00:00
continue ;
}
2006-10-30 22:19:55 +00:00
/* now check the opposite direction */
if ( ! tr_matrix [ powerof ( x )][ src_video ]. step )
res &= ~ x ;
2006-10-24 03:45:42 +00:00
}
2006-10-24 03:53:32 +00:00
AST_LIST_UNLOCK ( & translators );
2006-10-24 03:45:42 +00:00
return res ;
}