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
*
2005-01-21 07:06:25 +00:00
* Copyright (C) 1999 - 2005, 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-09-14 20:46:50 +00:00
*
1999-12-05 02:03:40 +00:00
*/
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-06-06 20:27:51 +00:00
#include "asterisk.h"
2005-06-06 22:12:19 +00:00
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-06-06 20:27:51 +00:00
2005-04-21 06:02:45 +00:00
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/logger.h"
#include "asterisk/translate.h"
#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 */
2005-11-14 19:00:38 +00:00
/*! \note
This could all be done more efficiently *IF* we chained packets together
2000-01-05 20:03:55 +00:00
by default, but it would also complicate virtually every application. */
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( list_lock );
1999-12-05 02:03:40 +00:00
static struct ast_translator * list = NULL ;
struct ast_translator_dir {
2005-11-14 19:00:38 +00:00
struct ast_translator * step ; /*!< Next step translator */
int cost ; /*!< Complete cost to destination */
1999-12-05 02:03:40 +00:00
};
2000-01-05 20:03:55 +00:00
struct ast_frame_delivery {
struct ast_frame * f ;
struct ast_channel * chan ;
int fd ;
struct translator_pvt * owner ;
struct ast_frame_delivery * prev ;
struct ast_frame_delivery * next ;
};
1999-12-05 02:03:40 +00:00
static struct ast_translator_dir tr_matrix [ MAX_FORMAT ][ MAX_FORMAT ];
struct ast_trans_pvt {
struct ast_translator * step ;
struct ast_translator_pvt * state ;
struct ast_trans_pvt * next ;
2004-04-05 19:45:53 +00:00
struct timeval nextin ;
struct timeval nextout ;
1999-12-05 02:03:40 +00:00
};
static int powerof ( int d )
{
int x ;
for ( x = 0 ; x < 32 ; x ++ )
if (( 1 << x ) & d )
return x ;
ast_log ( LOG_WARNING , "Powerof %d: No power?? \n " , d );
return - 1 ;
}
void ast_translator_free_path ( struct ast_trans_pvt * p )
{
2004-03-20 10:55:57 +00:00
struct ast_trans_pvt * pl , * pn ;
pn = p ;
while ( pn ) {
pl = pn ;
pn = pn -> next ;
1999-12-05 02:03:40 +00:00
if ( pl -> state && pl -> step -> destroy )
pl -> step -> destroy ( pl -> state );
free ( pl );
}
}
2005-11-14 19:00:38 +00:00
/*! Build a set of translators based upon the given source and destination 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
{
struct ast_trans_pvt * tmpr = NULL , * tmp = 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
1999-12-05 02:03:40 +00:00
while ( source != dest ) {
2005-07-25 19:10:38 +00:00
if ( ! tr_matrix [ source ][ dest ]. step ) {
2000-01-05 20:03:55 +00:00
/* We shouldn't have allocated any memory */
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 ));
2000-01-05 20:03:55 +00:00
return NULL ;
1999-12-05 02:03:40 +00:00
}
2005-07-25 19:10:38 +00:00
if ( tmp ) {
tmp -> next = malloc ( sizeof ( * tmp ));
tmp = tmp -> next ;
} else
tmp = malloc ( sizeof ( * tmp ));
if ( ! tmp ) {
ast_log ( LOG_WARNING , "Out of memory \n " );
if ( tmpr )
ast_translator_free_path ( tmpr );
return NULL ;
}
/* Set the root, if it doesn't exist yet... */
if ( ! tmpr )
tmpr = tmp ;
tmp -> next = NULL ;
tmp -> nextin = tmp -> nextout = ast_tv ( 0 , 0 );
tmp -> step = tr_matrix [ source ][ dest ]. step ;
tmp -> state = tmp -> step -> newpvt ();
if ( ! tmp -> state ) {
ast_log ( LOG_WARNING , "Failed to build translator step from %d to %d \n " , source , dest );
ast_translator_free_path ( tmpr );
return NULL ;
}
/* Keep going if this isn't the final destination */
source = tmp -> step -> dstfmt ;
1999-12-05 02:03:40 +00:00
}
return tmpr ;
}
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
{
struct ast_trans_pvt * p ;
struct ast_frame * out ;
2004-04-20 15:28:58 +00:00
struct timeval delivery ;
1999-12-05 02:03:40 +00:00
p = path ;
/* Feed the first frame into the first translator */
p -> step -> framein ( p -> state , f );
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 ;
2001-03-22 04:20:13 +00:00
if ( consume )
ast_frfree ( f );
1999-12-05 02:03:40 +00:00
while ( p ) {
2001-03-22 04:20:13 +00:00
out = p -> step -> frameout ( p -> state );
/* If we get nothing out, return NULL */
if ( ! out )
return NULL ;
/* If there is a next state, feed it in there. If not,
return this frame */
if ( p -> next )
p -> next -> step -> framein ( p -> next -> state , out );
2004-03-19 15:03:43 +00:00
else {
2005-07-15 23:00:47 +00:00
if ( ! ast_tvzero ( delivery )) {
2005-07-25 19:41:39 +00:00
/* Regenerate prediction after a discontinuity */
if ( ast_tvzero ( path -> nextout ))
path -> nextout = ast_tvnow ();
2004-04-05 20:47:44 +00:00
/* Use next predicted outgoing timestamp */
2005-07-15 23:00:47 +00:00
out -> delivery = path -> nextout ;
2004-04-05 20:47:44 +00:00
/* Predict next outgoing timestamp from samples in this
frame. */
2005-07-15 23:00:47 +00:00
path -> nextout = ast_tvadd ( path -> nextout , ast_samp2tv ( out -> samples , 8000 ));
2004-04-05 20:47:44 +00:00
} else {
2005-07-15 23:00:47 +00:00
out -> delivery = ast_tv ( 0 , 0 );
2004-04-05 19:45:53 +00:00
}
2005-07-25 19:41:39 +00:00
/* Invalidate prediction if we're entering a silence period */
if ( out -> frametype == AST_FRAME_CNG )
path -> nextout = ast_tv ( 0 , 0 );
2001-03-22 04:20:13 +00:00
return out ;
2004-03-19 15:03:43 +00:00
}
1999-12-05 02:03:40 +00:00
p = p -> next ;
}
2001-03-22 04:20:13 +00:00
ast_log ( LOG_WARNING , "I should never get here... \n " );
1999-12-05 02:03:40 +00:00
return NULL ;
}
2004-05-17 22:59:27 +00:00
2005-11-14 19:00:38 +00:00
static void calc_cost ( struct ast_translator * t , int samples )
2004-05-17 22:59:27 +00:00
{
int sofar = 0 ;
struct ast_translator_pvt * pvt ;
struct ast_frame * f , * out ;
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
2004-05-17 22:59:27 +00:00
if ( ! samples )
2005-11-14 19:00:38 +00:00
samples = 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 ;
}
2004-08-01 14:19:04 +00:00
pvt = t -> newpvt ();
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 ();
2004-05-17 22:59:27 +00:00
/* Call the encoder until we've processed one second of time */
while ( sofar < samples * 8000 ) {
f = t -> sample ();
if ( ! f ) {
ast_log ( LOG_WARNING , "Translator '%s' failed to produce a sample frame. \n " , t -> name );
t -> destroy ( pvt );
t -> cost = 99999 ;
return ;
}
t -> framein ( pvt , f );
ast_frfree ( f );
while (( out = t -> frameout ( pvt ))) {
sofar += out -> samples ;
ast_frfree ( out );
}
}
2005-07-15 23:00:47 +00:00
cost = ast_tvdiff_ms ( ast_tvnow (), start );
2004-05-17 22:59:27 +00:00
t -> destroy ( pvt );
t -> cost = cost / samples ;
if ( ! t -> cost )
t -> cost = 1 ;
}
2005-11-14 19:00:38 +00:00
/*! \brief Use the list of translators to build a translation matrix */
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 ;
int changed ;
int x , y , z ;
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 " );
1999-12-05 02:03:40 +00:00
bzero ( tr_matrix , sizeof ( tr_matrix ));
t = list ;
while ( t ) {
2005-11-14 19:00:38 +00:00
if ( samples )
calc_cost ( t , samples );
2004-05-17 22:59:27 +00:00
1999-12-05 02:03:40 +00:00
if ( ! tr_matrix [ t -> srcfmt ][ t -> dstfmt ]. step ||
tr_matrix [ t -> srcfmt ][ t -> dstfmt ]. cost > t -> cost ) {
tr_matrix [ t -> srcfmt ][ t -> dstfmt ]. step = t ;
tr_matrix [ t -> srcfmt ][ t -> dstfmt ]. cost = t -> cost ;
}
t = t -> next ;
}
do {
changed = 0 ;
/* Don't you just love O(N^3) operations? */
for ( x = 0 ; x < MAX_FORMAT ; x ++ ) /* For each source format */
for ( y = 0 ; y < MAX_FORMAT ; y ++ ) /* And each destination format */
2005-11-14 19:00:38 +00:00
if ( x != y ) /* Except ourselves, of course */
1999-12-05 02:03:40 +00:00
for ( z = 0 ; z < MAX_FORMAT ; z ++ ) /* And each format it might convert to */
if (( x != z ) && ( y != z )) /* Don't ever convert back to us */
if ( tr_matrix [ x ][ y ]. step && /* We can convert from x to y */
tr_matrix [ y ][ z ]. step && /* And from y to z and... */
( ! tr_matrix [ x ][ z ]. step || /* Either there isn't an x->z conversion */
( tr_matrix [ x ][ y ]. cost +
tr_matrix [ y ][ z ]. cost < /* Or we're cheaper than the existing */
tr_matrix [ x ][ z ]. cost ) /* solution */
)) {
/* 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 = tr_matrix [ x ][ y ]. cost +
tr_matrix [ y ][ z ]. cost ;
if ( option_debug )
2003-08-16 05:10:35 +00:00
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 );
1999-12-05 02:03:40 +00:00
changed ++ ;
}
} while ( changed );
}
2004-05-17 22:59:27 +00:00
1999-12-05 02:03:40 +00:00
2005-11-14 19:00:38 +00:00
/*! \brief CLI "show translation" command handler */
2000-01-05 20:03:55 +00:00
static int show_translation ( int fd , int argc , char * argv [])
{
2003-08-16 05:10:35 +00:00
#define SHOW_TRANS 11
2004-06-22 20:11:15 +00:00
int x , y , z ;
2000-01-05 20:03:55 +00:00
char line [ 80 ];
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
2004-06-22 20:11:15 +00:00
if ( argv [ 2 ] && ! strcasecmp ( argv [ 2 ], "recalc" )) {
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 ) {
ast_cli ( fd , " C'mon let's be serious here... defaulting to 1. \n " );
z = 1 ;
}
2004-05-17 22:59:27 +00:00
2004-06-22 20:11:15 +00:00
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 );
2004-05-17 22:59:27 +00:00
}
2003-08-16 05:10:35 +00:00
ast_cli ( fd , " Translation times between formats (in milliseconds) \n " );
ast_cli ( fd , " Source Format (Rows) Destination Format(Columns) \n\n " );
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & list_lock );
2005-11-14 19:00:38 +00:00
for ( x = - 1 ; x < SHOW_TRANS ; x ++ ) {
2004-07-14 07:44:19 +00:00
/* next 2 lines run faster than using strcpy() */
line [ 0 ] = ' ' ;
line [ 1 ] = '\0' ;
2003-08-16 05:10:35 +00:00
for ( y =- 1 ; y < SHOW_TRANS ; y ++ ) {
2004-02-25 04:24:51 +00:00
if ( x >= 0 && y >= 0 && tr_matrix [ x ][ y ]. step )
2004-02-26 01:12:38 +00:00
snprintf ( line + strlen ( line ), sizeof ( line ) - strlen ( line ), " %5d" , tr_matrix [ x ][ y ]. cost >= 99999 ? tr_matrix [ x ][ y ]. cost - 99999 : tr_matrix [ x ][ y ]. cost );
2000-01-05 20:03:55 +00:00
else
2004-02-25 04:24:51 +00:00
if ((( x == - 1 && y >= 0 ) || ( y == - 1 && x >= 0 ))) {
2003-08-16 05:10:35 +00:00
snprintf ( line + strlen ( line ), sizeof ( line ) - strlen ( line ),
2004-02-25 04:24:51 +00:00
" %5s" , ast_getformatname ( 1 << ( x + y + 1 )) );
2004-02-26 01:12:38 +00:00
} else if ( x != - 1 && y != - 1 ) {
2004-02-25 04:24:51 +00:00
snprintf ( line + strlen ( line ), sizeof ( line ) - strlen ( line ), " -" );
2004-02-26 01:12:38 +00:00
} else {
2004-02-25 04:24:51 +00:00
snprintf ( line + strlen ( line ), sizeof ( line ) - strlen ( line ), " " );
2003-08-16 05:10:35 +00:00
}
2000-01-05 20:03:55 +00:00
}
snprintf ( line + strlen ( line ), sizeof ( line ) - strlen ( line ), " \n " );
ast_cli ( fd , line );
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & list_lock );
2000-01-05 20:03:55 +00:00
return RESULT_SUCCESS ;
}
static int added_cli = 0 ;
static char show_trans_usage [] =
2004-05-17 22:59:27 +00:00
"Usage: 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
static struct ast_cli_entry show_trans =
{ { "show" , "translation" , NULL }, show_translation , "Display translation matrix" , show_trans_usage };
1999-12-05 02:03:40 +00:00
int ast_register_translator ( struct ast_translator * t )
{
2002-05-18 02:35:06 +00:00
char tmp [ 80 ];
1999-12-05 02:03:40 +00:00
t -> srcfmt = powerof ( t -> srcfmt );
t -> dstfmt = powerof ( t -> dstfmt );
2005-07-05 14:00:03 +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 ;
}
if ( t -> dstfmt >= MAX_FORMAT ) {
ast_log ( LOG_WARNING , "Destination format %s is larger than MAX_FORMAT \n " , ast_getformatname ( t -> dstfmt ));
1999-12-05 02:03:40 +00:00
return - 1 ;
}
2004-05-17 22:59:27 +00:00
calc_cost ( t , 1 );
1999-12-05 02:03:40 +00:00
if ( option_verbose > 1 )
2003-08-16 15:47:53 +00:00
ast_verbose ( VERBOSE_PREFIX_2 "Registered translator '%s' from format %s to %s, cost %d \n " , term_color ( tmp , t -> name , COLOR_MAGENTA , COLOR_BLACK , sizeof ( tmp )), ast_getformatname ( 1 << t -> srcfmt ), ast_getformatname ( 1 << t -> dstfmt ), t -> cost );
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & list_lock );
2000-01-05 20:03:55 +00:00
if ( ! added_cli ) {
ast_cli_register ( & show_trans );
added_cli ++ ;
}
1999-12-05 02:03:40 +00:00
t -> next = list ;
list = t ;
2004-05-17 22:59:27 +00:00
rebuild_matrix ( 0 );
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & list_lock );
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 ];
1999-12-05 02:03:40 +00:00
struct ast_translator * u , * ul = NULL ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & list_lock );
1999-12-05 02:03:40 +00:00
u = list ;
while ( u ) {
if ( u == t ) {
if ( ul )
ul -> next = u -> next ;
else
list = u -> next ;
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 ));
1999-12-05 02:03:40 +00:00
break ;
}
2000-01-05 20:03:55 +00:00
ul = u ;
1999-12-05 02:03:40 +00:00
u = u -> next ;
}
2004-05-17 22:59:27 +00:00
rebuild_matrix ( 0 );
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & list_lock );
1999-12-05 02:03:40 +00:00
return ( u ? 0 : - 1 );
}
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 ;
2000-01-05 20:03:55 +00:00
int cur = 1 ;
2005-04-04 03:28:38 +00:00
int besttime = INT_MAX ;
int common ;
if (( common = ( * dst ) & ( * srcs ))) {
2004-12-06 21:53:57 +00:00
/* We have a format in common */
2005-04-04 03:28:38 +00:00
for ( y = 0 ; y < MAX_FORMAT ; y ++ ) {
if ( cur & common ) {
2004-12-06 21:53:57 +00:00
/* This is a common format to both. Pick it if we don't have one already */
2005-04-04 03:28:38 +00:00
besttime = 0 ;
2004-12-06 21:53:57 +00:00
bestdst = cur ;
best = cur ;
1999-12-05 02:03:40 +00:00
}
2004-12-06 21:53:57 +00:00
cur = cur << 1 ;
}
} else {
/* We will need to translate */
ast_mutex_lock ( & list_lock );
2005-04-04 03:28:38 +00:00
for ( y = 0 ; y < MAX_FORMAT ; y ++ ) {
2004-12-06 21:53:57 +00:00
if ( cur & * dst )
2005-04-04 03:28:38 +00:00
for ( x = 0 ; x < MAX_FORMAT ; x ++ ) {
if (( * srcs & ( 1 << x )) && /* x is a valid source format */
tr_matrix [ x ][ y ]. step && /* There's a step */
( tr_matrix [ x ][ y ]. cost < besttime )) { /* It's better than what we have so far */
best = 1 << x ;
bestdst = cur ;
besttime = tr_matrix [ x ][ y ]. cost ;
}
2004-12-06 21:53:57 +00:00
}
cur = cur << 1 ;
}
ast_mutex_unlock ( & list_lock );
1999-12-05 02:03:40 +00:00
}
2001-03-22 04:20:13 +00:00
if ( best > - 1 ) {
* srcs = best ;
* dst = bestdst ;
best = 0 ;
}
1999-12-05 02:03:40 +00:00
return best ;
}