Files
agc_2-X/extras/app_conference/conference.c
T
mattf bd9f137fa4 restructuring of files 6 - install script will not function at this point
git-svn-id: svn://192.168.202.10@22 3d104415-ff17-0410-8863-d5cf3c621b8a
2006-07-07 23:25:55 +00:00

1411 lines
32 KiB
C

// $Id: conference.c,v 1.7 2005/10/27 17:53:35 stevek Exp $
/*
* app_conference
*
* A channel independent conference application for Asterisk
*
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
*
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
*
* This program may be modified and distributed under the
* terms of the GNU Public License.
*
*/
#include "conference.h"
#include "regex.h"
//
// static variables
//
// single-linked list of current conferences
static struct ast_conference *conflist = NULL ;
// mutex for synchronizing access to conflist
AST_MUTEX_DEFINE_STATIC(conflist_lock);
//static ast_mutex_t conflist_lock = AST_MUTEX_INITIALIZER ;
// mutex for synchronizing calls to start_conference() and remove_conf()
AST_MUTEX_DEFINE_STATIC(start_stop_conf_lock);
//static ast_mutex_t start_stop_conf_lock = AST_MUTEX_INITIALIZER ;
static int conference_count = 0 ;
//
// main conference function
//
void conference_exec( struct ast_conference *conf )
{
struct ast_conf_member *member, *temp_member , *membertest;
struct conf_frame *cfr, *spoken_frames, *send_frames ;
// count number of speakers, number of listeners
int speaker_count ;
int listener_count ;
ast_log( AST_CONF_DEBUG, "[ $Revision: 1.7 $ ] entered conference_exec, name => %s\n", conf->name ) ;
// timer timestamps
struct timeval base, curr, notify ;
gettimeofday( &base, NULL ) ;
gettimeofday( &notify, NULL ) ;
// holds differences of curr and base
long time_diff = 0 ;
long time_sleep = 0 ;
int since_last_slept = 0 ;
//
// variables for checking thread frequency
//
// count to AST_CONF_FRAMES_PER_SECOND
int tf_count = 0 ;
long tf_diff = 0 ;
float tf_frequency = 0.0 ;
struct timeval tf_base, tf_curr ;
gettimeofday( &tf_base, NULL ) ;
//
// main conference thread loop
//
while ( 42 == 42 )
{
// update the current timestamp
gettimeofday( &curr, NULL ) ;
// calculate difference in timestamps
time_diff = usecdiff( &curr, &base ) ;
// calculate time we should sleep
time_sleep = AST_CONF_FRAME_INTERVAL - time_diff ;
if ( time_sleep > 0 )
{
// sleep for sleep_time ( as milliseconds )
usleep( time_sleep * 1000 ) ;
// reset since last slept counter
since_last_slept = 0 ;
continue ;
}
else
{
// long sleep warning
if (
since_last_slept == 0
&& time_diff > AST_CONF_CONFERENCE_SLEEP * 2
)
{
ast_log(
AST_CONF_DEBUG,
"long scheduling delay, time_diff => %ld, AST_CONF_FRAME_INTERVAL => %d\n",
time_diff, AST_CONF_FRAME_INTERVAL
) ;
}
// increment times since last slept
++since_last_slept ;
// sleep every other time
if ( since_last_slept % 2 )
usleep( 0 ) ;
}
// adjust the timer base ( it will be used later to timestamp outgoing frames )
add_milliseconds( &base, AST_CONF_FRAME_INTERVAL ) ;
//
// check thread frequency
//
if ( ++tf_count >= AST_CONF_FRAMES_PER_SECOND )
{
// update current timestamp
gettimeofday( &tf_curr, NULL ) ;
// compute timestamp difference
tf_diff = usecdiff( &tf_curr, &tf_base ) ;
// compute sampling frequency
tf_frequency = ( float )( tf_diff ) / ( float )( tf_count ) ;
if (
( tf_frequency <= ( float )( AST_CONF_FRAME_INTERVAL - 1 ) )
|| ( tf_frequency >= ( float )( AST_CONF_FRAME_INTERVAL + 1 ) )
)
{
ast_log(
LOG_WARNING,
"processed frame frequency variation, name => %s, tf_count => %d, tf_diff => %ld, tf_frequency => %2.4f\n",
conf->name, tf_count, tf_diff, tf_frequency
) ;
}
// reset values
tf_base = tf_curr ;
tf_count = 0 ;
}
//-----------------//
// INCOMING FRAMES //
//-----------------//
// ast_log( AST_CONF_DEBUG, "PROCESSING FRAMES, conference => %s, step => %d, ms => %ld\n",
// conf->name, step, ( base.tv_usec / 20000 ) ) ;
// acquire conference mutex
TIMELOG(ast_mutex_lock( &conf->lock ),1,"conf thread conf lock");
// update the current delivery time
conf->delivery_time = base ;
//
// loop through the list of members
// ( conf->memberlist is a single-linked list )
//
// ast_log( AST_CONF_DEBUG, "begin processing incoming audio, name => %s\n", conf->name ) ;
// reset speaker and listener count
speaker_count = 0 ;
listener_count = 0 ;
// get list of conference members
member = conf->memberlist ;
// reset pointer lists
spoken_frames = NULL ;
// loop over member list to retrieve queued frames
while ( member != NULL )
{
// acquire member mutex
TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ;
// check for dead members
if ( member->remove_flag == 1 )
{
// leave message for other members of the conference
membertest = conf->memberlist ;
int silent_exit = 0 ;
if (member->quiet_entry_exit == -1)
{
silent_exit = 1 ;
}
while (membertest != NULL)
{
if (silent_exit < 1)
{
if(!strcmp(membertest->channel_name, member->channel_name))
{
ast_log( LOG_ERROR, "skipping leave message on %s\n", membertest->channel_name ) ;
}
else
{
if (!basic_play_sound ( membertest->channel_name, "leave" ))
{
ast_log( LOG_ERROR, "playing conference leave message FAILED on %s\n", membertest->channel_name ) ;
}
}
}
else
{
ast_log( LOG_NOTICE, "skipping all entry messages on %s\n", membertest->channel_name ) ;
}
membertest = membertest->next;
}
ast_log( LOG_NOTICE, "found member slated for removal, channel => %s\n", member->channel_name ) ;
temp_member = member->next ;
remove_member( member, conf ) ;
member = temp_member ;
continue ;
}
// get speaking member's audio frames,
if ( member->type == 'L' )
{
// listeners never have frames
cfr = NULL ;
}
else
{
// tell member the number of frames we're going to need ( used to help dropping algorithm )
member->inFramesNeeded = ( time_diff / AST_CONF_FRAME_INTERVAL ) - 1 ;
// !!! TESTING !!!
if (
conf->debug_flag == 1
&& member->inFramesNeeded > 0
)
{
ast_log( AST_CONF_DEBUG, "channel => %s, inFramesNeeded => %d, inFramesCount => %d\n",
member->channel_name, member->inFramesNeeded, member->inFramesCount ) ;
}
// non-listener member should have frames,
// unless silence detection dropped them
cfr = get_incoming_frame( member ) ;
}
// handle retrieved frames
if ( cfr == NULL )
{
// this member is listen-only, or has not spoken
// ast_log( AST_CONF_DEBUG, "silent member, channel => %s\n", member->channel_name ) ;
// !!! TESTING !!!
#if 0
if ( member->speaking_state == 1 )
{
ast_log( AST_CONF_DEBUG, "member has stopped speaking, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
#endif
if ( conf->debug_flag == 1 )
{
ast_log( AST_CONF_DEBUG, "member is silent, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
// mark member as silent
member->speaking_state = 0 ;
// count the listeners
++listener_count ;
}
else if ( cfr->fr == NULL )
{
ast_log( AST_CONF_DEBUG, "got incoming conf_frame with null ast_frame\n" ) ;
// !!! TESTING !!!
#if 0
if ( member->speaking_state == 1 )
{
ast_log( AST_CONF_DEBUG, "member has stopped speaking, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
#endif
if ( conf->debug_flag == 1 )
{
ast_log( AST_CONF_DEBUG, "member is silent, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
// mark member as silent
member->speaking_state = 0 ;
// count the listeners
++listener_count ;
}
else
{
// this speaking member has spoken
// ast_log( AST_CONF_DEBUG, "speaking member, channel => %s\n", member->channel_name ) ;
// append the frame to the list of spoken frames
if ( spoken_frames != NULL )
{
// add new frame to end of list
cfr->next = spoken_frames ;
spoken_frames->prev = cfr ;
}
// point the list at the new frame
spoken_frames = cfr ;
// !!! TESTING !!!
#if 0
if ( member->speaking_state == 0 )
{
ast_log( AST_CONF_DEBUG, "member has started speaking, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
#endif
if ( conf->debug_flag == 1 )
{
ast_log( AST_CONF_DEBUG, "member is speaking, channel => %s, incoming => %d, outgoing => %d\n",
member->channel_name, member->inFramesCount, member->outFramesCount ) ;
}
// mark member as speaker
member->speaking_state = 1 ;
member->speaking_state_notify = 1 ;
// count the speakers
++speaker_count ;
}
// release member mutex
ast_mutex_unlock( &member->lock ) ;
// adjust our pointer to the next inline
member = member->next ;
}
//
// break, if we have no more members
//
if ( conf->membercount == 0 )
{
ast_log( LOG_NOTICE, "removing conference, count => %d, name => %s\n", conf->membercount, conf->name ) ;
remove_conf( conf ) ; // stop the conference
break ; // break from main processing loop
}
// ast_log( AST_CONF_DEBUG, "finished processing incoming audio, name => %s\n", conf->name ) ;
//---------------//
// MIXING FRAMES //
//---------------//
// mix frames and get batch of outgoing frames
send_frames = mix_frames( spoken_frames, speaker_count, listener_count ) ;
// accounting: if there are frames, count them as one incoming frame
if ( send_frames != NULL )
{
// set delivery timestamp
// set_conf_frame_delivery( send_frames, base ) ;
// ast_log( AST_CONF_DEBUG, "base => %ld.%ld %d\n", base.tv_sec, base.tv_usec, ( int )( base.tv_usec / 1000 ) ) ;
conf->stats.frames_in++ ;
}
//-----------------//
// OUTGOING FRAMES //
//-----------------//
//
// queue send frames
//
// ast_log( AST_CONF_DEBUG, "begin queueing outgoing audio, name => %s\n", conf->name ) ;
//
// loop over member list to queue outgoing frames
//
for ( member = conf->memberlist ; member != NULL ; member = member->next )
{
// skip members that are not ready
if ( member->ready_for_outgoing == 0 )
continue ;
if ( member->speaking_state == 0 )
{
// queue listener frame
queue_frame_for_listener( conf, member, send_frames ) ;
}
else
{
// queue speaker frame
queue_frame_for_speaker( conf, member, send_frames ) ;
}
}
// ast_log( AST_CONF_DEBUG, "end queueing outgoing audio, name => %s\n", conf->name ) ;
//---------//
// CLEANUP //
//---------//
// clean up send frames
while ( send_frames != NULL )
{
// accouting: count all frames and mixed frames
if ( send_frames->member == NULL )
conf->stats.frames_out++ ;
else
conf->stats.frames_mixed++ ;
// delete the frame
send_frames = delete_conf_frame( send_frames ) ;
}
//
// notify the manager of state changes every 500 milliseconds
//
if ( ( usecdiff( &curr, &notify ) / AST_CONF_NOTIFICATION_SLEEP ) >= 1 )
{
// send the notifications
send_state_change_notifications( conf->memberlist ) ;
// increment the notification timer base
add_milliseconds( &notify, AST_CONF_NOTIFICATION_SLEEP ) ;
}
// release conference mutex
ast_mutex_unlock( &conf->lock ) ;
// !!! TESTING !!!
// usleep( 1 ) ;
}
// end while ( 42 == 42 )
//
// exit the conference thread
//
ast_log( AST_CONF_DEBUG, "exit conference_exec\n" ) ;
// exit the thread
pthread_exit( NULL ) ;
return ;
}
//
// manange conference functions
//
// called by app_conference.c:load_module()
void init_conference( void )
{
ast_mutex_init( &start_stop_conf_lock ) ;
ast_mutex_init( &conflist_lock ) ;
}
struct ast_conference* start_conference( struct ast_conf_member* member )
{
// check input
if ( member == NULL )
{
ast_log( LOG_WARNING, "unable to handle null member\n" ) ;
return NULL ;
}
struct ast_conference* conf = NULL ;
// acquire mutex
ast_mutex_lock( &start_stop_conf_lock ) ;
// look for an existing conference
ast_log( AST_CONF_DEBUG, "attempting to find requested conference\n" ) ;
conf = find_conf( member->id ) ;
// unable to find an existing conference, try to create one
if ( conf == NULL )
{
// create a new conference
ast_log( AST_CONF_DEBUG, "attempting to create requested conference\n" ) ;
// create the new conference with one member
conf = create_conf( member->id, member ) ;
// return an error if create_conf() failed
if ( conf == NULL )
{
ast_log( LOG_ERROR, "unable to find or create requested conference\n" ) ;
ast_mutex_unlock( &start_stop_conf_lock ) ; // release mutex
return NULL ;
}
}
else
{
//
// existing conference found, add new member to the conference
//
// once we call add_member(), this thread
// is responsible for calling delete_member()
//
add_member( member, conf ) ;
}
// release mutex
ast_mutex_unlock( &start_stop_conf_lock ) ;
return conf ;
}
struct ast_conference* find_conf( const char* name )
{
// no conferences exist
if ( conflist == NULL )
{
ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
return NULL ;
}
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
struct ast_conference *conf = conflist ;
// loop through conf list
while ( conf != NULL )
{
if ( strncasecmp( (char*)&(conf->name), name, 80 ) == 0 )
{
// found conf name match
ast_log( AST_CONF_DEBUG, "found conference in conflist, name => %s\n", name ) ;
break ;
}
conf = conf->next ;
}
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
if ( conf == NULL )
{
ast_log( AST_CONF_DEBUG, "unable to find conference in conflist, name => %s\n", name ) ;
}
return conf ;
}
struct ast_conference* create_conf( char* name, struct ast_conf_member* member )
{
ast_log( AST_CONF_DEBUG, "entered create_conf, name => %s\n", name ) ;
//
// allocate memory for conference
//
struct ast_conference *conf = malloc( sizeof( struct ast_conference ) ) ;
if ( conf == NULL )
{
ast_log( LOG_ERROR, "unable to malloc ast_conference\n" ) ;
return NULL ;
}
//
// initialize conference
//
conf->next = NULL ;
conf->memberlist = NULL ;
conf->membercount = 0 ;
conf->conference_thread = -1 ;
conf->debug_flag = 0 ;
// zero stats
memset( &conf->stats, 0x0, sizeof( ast_conference_stats ) ) ;
// record start time
gettimeofday( &conf->stats.time_entered, NULL ) ;
// copy name to conference
strncpy( (char*)&(conf->name), name, sizeof(conf->name) - 1 ) ;
strncpy( (char*)&(conf->stats.name), name, sizeof(conf->name) - 1 ) ;
// initialize mutexes
ast_mutex_init( &conf->lock ) ;
// build translation paths
int c;
for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
{
if ( c == AC_SLINEAR_INDEX )
conf->from_slinear_paths[ c ] = NULL;
else
conf->from_slinear_paths[ c ] =ast_translator_build_path( 1 << c, AST_FORMAT_SLINEAR);
}
// add the initial member
add_member( member, conf ) ;
//
// prepend new conference to conflist
//
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
conf->next = conflist ;
conflist = conf ;
ast_log( AST_CONF_DEBUG, "added new conference to conflist, name => %s\n", name ) ;
//
// spawn thread for new conference, using conference_exec( conf )
//
// acquire conference mutexes
ast_mutex_lock( &conf->lock ) ;
if ( pthread_create( &conf->conference_thread, NULL, (void*)conference_exec, conf ) == 0 )
{
// detach the thread so it doesn't leak
pthread_detach( conf->conference_thread ) ;
// release conference mutexes
ast_mutex_unlock( &conf->lock ) ;
ast_log( AST_CONF_DEBUG, "started conference thread for conference, name => %s\n", conf->name ) ;
}
else
{
ast_log( LOG_ERROR, "unable to start conference thread for conference %s\n", conf->name ) ;
conf->conference_thread = -1 ;
// release conference mutexes
ast_mutex_unlock( &conf->lock ) ;
// clean up conference
free( conf ) ;
conf = NULL ;
}
// count new conference
if ( conf != NULL )
++conference_count ;
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
return conf ;
}
void remove_conf( struct ast_conference *conf )
{
// ast_log( AST_CONF_DEBUG, "attempting to remove conference, name => %s\n", conf->name ) ;
struct ast_conference *conf_current = conflist ;
struct ast_conference *conf_temp = NULL ;
// acquire mutex
ast_mutex_lock( &start_stop_conf_lock ) ;
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
// loop through list of conferences
while ( conf_current != NULL )
{
// if conf_current point to the passed conf,
if ( conf_current == conf )
{
if ( conf_temp == NULL )
{
// this is the first conf in the list, so we just point
// conflist past the current conf to the next
conflist = conf_current->next ;
}
else
{
// this is not the first conf in the list, so we need to
// point the preceeding conf to the next conf in the list
conf_temp->next = conf_current->next ;
}
//
// do some frame clean up
//
int c;
for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
{
// free the translation paths
if ( conf_current->from_slinear_paths[ c ] != NULL )
{
ast_translator_free_path( conf_current->from_slinear_paths[ c ] ) ;
conf_current->from_slinear_paths[ c ] = NULL ;
}
}
// calculate time in conference
struct timeval time_exited ;
gettimeofday( &time_exited, NULL ) ;
// total time converted to seconds
long tt = ( usecdiff( &time_exited, &conf_current->stats.time_entered ) / 1000 ) ;
// report accounting information
ast_log( LOG_NOTICE, "conference accounting, fi => %ld, fo => %ld, fm => %ld, tt => %ld\n",
conf_current->stats.frames_in, conf_current->stats.frames_out, conf_current->stats.frames_mixed, tt ) ;
ast_log( AST_CONF_DEBUG, "removed conference, name => %s\n", conf_current->name ) ;
ast_mutex_unlock( &conf_current->lock ) ;
free( conf_current ) ;
conf_current = NULL ;
break ;
}
// save a refence to the soon to be previous conf
conf_temp = conf_current ;
// move conf_current to the next in the list
conf_current = conf_current->next ;
}
// count new conference
--conference_count ;
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
// release mutex
ast_mutex_unlock( &start_stop_conf_lock ) ;
return ;
}
//
// member-related functions
//
void add_member( struct ast_conf_member *member, struct ast_conference *conf )
{
if ( conf == NULL )
{
ast_log( LOG_ERROR, "unable to add member to NULL conference\n" ) ;
return ;
}
// acquire the conference lock
ast_mutex_lock( &conf->lock ) ;
member->next = conf->memberlist ; // next is now list
conf->memberlist = member ; // member is now at head of list
// update conference stats
count_member( member, conf, 1 ) ;
ast_log( AST_CONF_DEBUG, "member added to conference, name => %s\n", conf->name ) ;
// release the conference lock
ast_mutex_unlock( &conf->lock ) ;
return ;
}
int remove_member( struct ast_conf_member* member, struct ast_conference* conf )
{
// check for member
if ( member == NULL )
{
ast_log( LOG_WARNING, "unable to remove NULL member\n" ) ;
return -1 ;
}
// check for conference
if ( conf == NULL )
{
ast_log( LOG_WARNING, "unable to remove member from NULL conference\n" ) ;
return -1 ;
}
//
// loop through the member list looking
// for the requested member
//
struct ast_conf_member *member_list = conf->memberlist ;
struct ast_conf_member *member_temp = NULL ;
int count = -1 ; // default return code
while ( member_list != NULL )
{
if ( member_list == member )
{
//
// log some accounting information
//
// calculate time in conference
struct timeval time_exited ;
gettimeofday( &time_exited, NULL ) ;
long tt = ( usecdiff( &time_exited, &member->time_entered ) / 1000 ) ; // convert to seconds
ast_log(
LOG_NOTICE,
"member accounting, channel => %s, te => %ld, fi => %ld, fid => %ld, fo => %ld, fod => %ld, tt => %ld\n",
member->channel_name,
member->time_entered.tv_sec, member->frames_in, member->frames_in_dropped,
member->frames_out, member->frames_out_dropped, tt
) ;
//
// if this is the first member in the linked-list,
// skip over the first member in the list, else
//
// point the previous 'next' to the current 'next',
// thus skipping the current member in the list
//
if ( member_temp == NULL )
conf->memberlist = member->next ;
else
member_temp->next = member->next ;
// update conference stats
count = count_member( member, conf, 0 ) ;
// delete the member
delete_member( member ) ;
ast_log( AST_CONF_DEBUG, "removed member from conference, name => %s, remaining => %d\n", conf->name, conf->membercount ) ;
break ;
}
// save a pointer to the current member,
// and then point to the next member in the list
member_temp = member_list ;
member_list = member_list->next ;
}
// return -1 on error, or the number of members
// remaining if the requested member was deleted
return count ;
}
int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member )
{
if ( member == NULL || conf == NULL )
{
ast_log( LOG_WARNING, "unable to count member\n" ) ;
return -1 ;
}
short delta = ( add_member == 1 ) ? 1 : -1 ;
// member type
if ( memberIsModerator( member ) == 1 )
{
conf->stats.moderators += delta ;
}
else
{
// count non-moderators as listeners
conf->stats.listeners += delta ;
}
// connection type
if ( memberIsPhoneClient( member ) == 1 )
{
conf->stats.phone += delta ;
}
else if ( memberIsIaxClient( member ) == 1 )
{
conf->stats.iaxclient += delta ;
}
else if ( memberIsSIPClient( member ) == 1 )
{
conf->stats.sip += delta ;
}
// increment member count
conf->membercount += delta ;
return conf->membercount ;
}
//
// queue incoming frame functions
//
int queue_frame_for_speaker(
struct ast_conference* conf,
struct ast_conf_member* member,
conf_frame* frame
)
{
//
// check inputs
//
if ( conf == NULL )
{
ast_log( LOG_WARNING, "unable to queue speaker frame with null conference\n" ) ;
return -1 ;
}
if ( member == NULL )
{
ast_log( LOG_WARNING, "unable to queue speaker frame with null member\n" ) ;
return -1 ;
}
//
// loop over spoken frames looking for member's appropriate match
//
short found_flag = 0 ;
struct ast_frame* qf ;
for ( ; frame != NULL ; frame = frame->next )
{
if ( frame->member != member )
continue ;
if ( frame->fr == NULL )
{
ast_log( LOG_WARNING, "unable to queue speaker frame with null data\n" ) ;
continue ;
}
//
// convert and queue frame
//
// short-cut pointer to the ast_frame
qf = frame->fr ;
// acquire member lock
TIMELOG(ast_mutex_lock( &member->lock ),1,"queue_frame_for_speaker: memberlock") ;
if ( qf->subclass == member->write_format )
{
// frame is already in correct format, so just queue it
queue_outgoing_frame( member, qf, conf->delivery_time ) ;
}
else
{
//
// convert frame to member's write format
// ( calling ast_frdup() to make sure the translator's copy sticks around )
//
qf = convert_frame_from_slinear( member->from_slinear, ast_frdup( qf ) ) ;
if ( qf != NULL )
{
// queue frame
queue_outgoing_frame( member, qf, conf->delivery_time ) ;
// free frame ( the translator's copy )
ast_frfree( qf ) ;
}
else
{
ast_log( LOG_WARNING, "unable to translate outgoing speaker frame, channel => %s\n", member->channel_name ) ;
}
}
// release member lock
ast_mutex_unlock( &member->lock ) ;
// set found flag
found_flag = 1 ;
// we found the frame, skip to the next member
break ;
}
// queue a silent frame
if ( found_flag == 0 )
queue_silent_frame( conf, member ) ;
return 0 ;
}
int queue_frame_for_listener(
struct ast_conference* conf,
struct ast_conf_member* member,
conf_frame* frame
)
{
//
// check inputs
//
if ( conf == NULL )
{
ast_log( LOG_WARNING, "unable to queue listener frame with null conference\n" ) ;
return -1 ;
}
if ( member == NULL )
{
ast_log( LOG_WARNING, "unable to queue listener frame with null member\n" ) ;
return -1 ;
}
//
// loop over spoken frames looking for member's appropriate match
//
short found_flag = 0 ;
struct ast_frame* qf ;
for ( ; frame != NULL ; frame = frame->next )
{
// we're looking for a null or matching member
if ( frame->member != NULL && frame->member != member )
continue ;
if ( frame->fr == NULL )
{
ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ;
continue ;
}
// acquire member lock
TIMELOG(ast_mutex_lock( &member->lock ),1,"queue_frame_for_listener") ;
// first, try for a pre-converted frame
qf = frame->converted[ member->write_format_index ] ;
/*
if ( qf != NULL && (member->smooth_size_out > 0)) {
if (qf->datalen != member->smooth_size_out ) {
//ast_log (AST_CONF_DEBUG, "ignoring and freeing previously stored frame, with datalen=>%d != smooth_size_out=>%d\n",qf->datalen,member->smooth_size_out);
ast_frfree( qf ) ;
qf = NULL ;
}
}
*/
// convert ( and store ) the frame
if ( qf == NULL )
{
// make a copy of the slinear version of the frame
qf = ast_frdup( frame->fr ) ;
if ( qf == NULL )
{
ast_log( LOG_WARNING, "unable to duplicate frame\n" ) ;
continue ;
}
// convert using the conference's translation path
qf = convert_frame_from_slinear( conf->from_slinear_paths[ member->write_format_index ], qf ) ;
if ( qf == NULL )
ast_log( LOG_WARNING, "unable to translate frame for listener, channel => %s , member->write_format => %d , member->write_format_index %d , qf->frametype -> %d , qf->subclass -> %d, qf->datalen=> %d, qf->samples =>%d\n", member->channel_name , member->write_format, member->write_format_index, qf->frametype, qf->subclass , qf->datalen, qf->samples) ;
// store the converted frame
// ( the frame will be free'd next time through the loop )
frame->converted[ member->write_format_index ] = qf ;
//ast_log (AST_CONF_DEBUG, "storing converted frame into index=>%d, qf->frametype=>%d, qf->subclass=%d, qf->datalen=%d \n",member->write_format_index, qf->frametype, qf->subclass, qf->datalen);
}
if ( qf != NULL )
{
// duplicate the frame before queue'ing it
// ( since this member doesn't own this _shared_ frame )
// qf = ast_frdup( qf ) ;
if ( queue_outgoing_frame( member, qf, conf->delivery_time ) != 0 )
{
// free the new frame if it couldn't be queue'd
ast_frfree( qf ) ;
qf = NULL ;
}
}
else
{
ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ;
}
// release member lock
ast_mutex_unlock( &member->lock ) ;
// set found flag
found_flag = 1 ;
// break from for loop
break ;
}
// queue a silent frame
if ( found_flag == 0 )
queue_silent_frame( conf, member ) ;
return 0 ;
}
int queue_silent_frame(
struct ast_conference* conf,
struct ast_conf_member* member
)
{
#ifdef APP_CONFERENCE_DEBUG
//
// check inputs
//
if ( conf == NULL )
{
ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null conference\n" ) ;
return -1 ;
}
if ( member == NULL )
{
ast_log( AST_CONF_DEBUG, "unable to queue silent frame for null member\n" ) ;
return -1 ;
}
#endif // APP_CONFERENCE_DEBUG
//
// initialize static variables
//
static conf_frame* silent_frame = NULL ;
static struct ast_frame* qf = NULL ;
if ( silent_frame == NULL )
{
if ( ( silent_frame = get_silent_frame() ) == NULL )
{
ast_log( LOG_WARNING, "unable to initialize static silent frame\n" ) ;
return -1 ;
}
}
// acquire member lock
TIMELOG(ast_mutex_lock( &member->lock ),1,"queue_silent_frame") ;
// get the appropriate silent frame
qf = silent_frame->converted[ member->write_format_index ] ;
if ( qf == NULL )
{
//
// we need to do this to avoid echo on the speaker's line.
// translators seem to be single-purpose, i.e. they
// can't be used simultaneously for multiple audio streams
//
struct ast_trans_pvt* trans = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ;
if ( trans != NULL )
{
// attempt ( five times ) to get a silent frame
// to make sure we provice the translator with enough data
int c;
for ( c = 0 ; c < 5 ; ++c )
{
// translate the frame
qf = ast_translate( trans, silent_frame->fr, 0 ) ;
// break if we get a frame
if ( qf != NULL ) break ;
}
if ( qf != NULL )
{
// isolate the frame so we can keep it around after trans is free'd
qf = ast_frisolate( qf ) ;
// cache the new, isolated frame
silent_frame->converted[ member->write_format_index ] = qf ;
}
ast_translator_free_path( trans ) ;
}
}
//
// queue the frame, if it's not null,
// otherwise there was an error
//
if ( qf != NULL )
{
queue_outgoing_frame( member, qf, conf->delivery_time ) ;
}
else
{
ast_log( LOG_ERROR, "unable to translate outgoing silent frame, channel => %s\n", member->channel_name ) ;
}
// release member lock
ast_mutex_unlock( &member->lock ) ;
return 0 ;
}
//
// get conference stats
//
//
// returns: -1 => error, 0 => debugging off, 1 => debugging on
// state: on => 1, off => 0, toggle => -1
//
int set_conference_debugging( const char* name, int state )
{
if ( name == NULL )
return -1 ;
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
struct ast_conference *conf = conflist ;
int new_state = -1 ;
// loop through conf list
while ( conf != NULL )
{
if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
{
// lock conference
// ast_mutex_lock( &(conf->lock) ) ;
// toggle or set the state
if ( state == -1 )
conf->debug_flag = ( conf->debug_flag == 0 ) ? 1 : 0 ;
else
conf->debug_flag = ( state == 0 ) ? 0 : 1 ;
new_state = conf->debug_flag ;
// unlock conference
// ast_mutex_unlock( &(conf->lock) ) ;
break ;
}
conf = conf->next ;
}
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
return new_state ;
}
int get_conference_count( void )
{
return conference_count ;
}
int get_conference_stats( ast_conference_stats* stats, int requested )
{
// no conferences exist
if ( conflist == NULL )
{
ast_log( AST_CONF_DEBUG, "conflist has not yet been initialize\n" ) ;
return 0 ;
}
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
// compare the number of requested to the number of available conferences
requested = ( get_conference_count() < requested ) ? get_conference_count() : requested ;
//
// loop through conf list
//
struct ast_conference* conf = conflist ;
int count = 0 ;
while ( count <= requested && conf != NULL )
{
// copy stats struct to array
stats[ count ] = conf->stats ;
conf = conf->next ;
++count ;
}
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
return count ;
}
int get_conference_stats_by_name( ast_conference_stats* stats, const char* name )
{
// no conferences exist
if ( conflist == NULL )
{
ast_log( AST_CONF_DEBUG, "conflist has not yet been initialized, name => %s\n", name ) ;
return 0 ;
}
// make sure stats is null
stats = NULL ;
// acquire mutex
ast_mutex_lock( &conflist_lock ) ;
struct ast_conference *conf = conflist ;
// loop through conf list
while ( conf != NULL )
{
if ( strncasecmp( (const char*)&(conf->name), name, 80 ) == 0 )
{
// copy stats for found conference
*stats = conf->stats ;
break ;
}
conf = conf->next ;
}
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
return ( stats == NULL ) ? 0 : 1 ;
}
struct ast_conf_member *find_member ( char *chan, int lock)
{
struct ast_conf_member *found = NULL;
struct ast_conf_member *member;
struct ast_conference *conf;
ast_mutex_lock( &conflist_lock ) ;
conf = conflist;
// loop through conf list
while ( conf != NULL && !found )
{
// lock conference
ast_mutex_lock( &conf->lock );
member = conf->memberlist ;
while (member != NULL)
{
if(!strcmp(member->channel_name, chan)) {
found = member;
if(lock)
ast_mutex_lock(&member->lock);
break;
}
member = member->next;
}
// unlock conference
ast_mutex_unlock( &conf->lock );
conf = conf->next ;
}
// release mutex
ast_mutex_unlock( &conflist_lock ) ;
return found;
}