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 .
*/
/*
*
* Translate via the use of pseudo channels
*
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 */
2000-01-05 20:03:55 +00:00
/* This could all be done more efficiently *IF* we chained packets together
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 {
struct ast_translator * step ; /* Next step translator */
2005-07-05 14:00:03 +00:00
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-07-25 19:10: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
static void calc_cost ( struct ast_translator * t , int samples )
{
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 ;
if ( ! samples )
samples = 1 ;
/* 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 ;
}
static void rebuild_matrix ( int samples )
1999-12-05 02:03:40 +00:00
{
struct ast_translator * t ;
int changed ;
int x , y , z ;
if ( option_debug )
ast_log ( LOG_DEBUG , " Reseting translation matrix \n " ) ;
/* Use the list of translators to build a translation matrix */
bzero ( tr_matrix , sizeof ( tr_matrix ) ) ;
t = list ;
while ( t ) {
2004-05-17 22:59:27 +00:00
if ( samples )
calc_cost ( t , samples ) ;
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 */
if ( x ! = y ) /* Except ourselves, of course */
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
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 ) ;
2003-08-16 05:10:35 +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 "
2004-05-17 22:59:27 +00:00
" with each conversion. if the arguement 'recalc' is supplied along \n "
" 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 ;
}
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 ) ;
}
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
{
/* Calculate our best source format, given costs, and a desired destination */
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 ;
}