// $Id: member.c 885 2007-06-27 15:41:18Z sbalea $ /* * app_conference * * A channel independent conference application for Asterisk * * Copyright (C) 2002, 2003 Junghanns.NET GmbH * Copyright (C) 2003, 2004 HorizonLive.com, Inc. * Copyright (C) 2005, 2006 HorizonWimba, Inc. * Copyright (C) 2007 Wimba, Inc. * * Klaus-Peter Junghanns * * Video Conferencing support added by * Neil Stratford * Copyright (C) 2005, 2005 Vipadia Limited * * VAD driven video conferencing, text message support * and miscellaneous enhancements added by * Mihai Balea * * This program may be modified and distributed under the * terms of the GNU General Public License. You should have received * a copy of the GNU General Public License along with this * program; if not, write to the Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "asterisk/autoconfig.h" #include "member.h" #include "asterisk/musiconhold.h" // process an incoming frame. Returns 0 normally, 1 if hangup was received. static int process_incoming(struct ast_conf_member *member, struct ast_conference *conf, struct ast_frame *f) { int silent_frame = 0; #ifdef VIDEO struct ast_conf_member *src_member ; #endif // In Asterisk 1.4 AST_FRAME_DTMF is equivalent to AST_FRAME_DTMF_END if (f->frametype == AST_FRAME_DTMF) { #ifdef VIDEO if (member->dtmf_switch) { ast_mutex_lock( &member->lock ) ; switch (f->subclass) { case '0' :member->req_id=0; break; case '1' :member->req_id=1; break; case '2' :member->req_id=2; break; case '3' :member->req_id=3; break; case '4' :member->req_id=4; break; case '5' :member->req_id=5; break; case '6' :member->req_id=6; break; case '7' :member->req_id=7; break; case '8' :member->req_id=8; break; case '9' :member->req_id=9; break; case '*' : if (member->mute_video == 0 && member->mute_audio == 0) { member->mute_video = 1; member->mute_audio = 1; } else if (member->mute_video == 1 && member->mute_audio == 1) { member->mute_video = 0; member->mute_audio = 0; } break; } member->conference = 1; // switch me ast_mutex_unlock( &member->lock ) ; } #endif if (member->dtmf_relay) { // output to manager... manager_event( EVENT_FLAG_CALL, "ConferenceDTMF", "ConferenceName: %s\r\n" "Type: %s\r\n" "UniqueID: %s\r\n" "Channel: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" "Key: %c\r\n" "Count: %d\r\n" "Flags: %s\r\n" "Mute: %d\r\n", conf->name, member->type, member->uniqueid, member->channel_name, member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", member->chan->cid.cid_name ? member->chan->cid.cid_name : "unknown", f->subclass, conf->membercount, member->flags, member->mute_audio ) ; } #ifdef DTMF if (!member->mute_audio && #ifdef VIDEO !member->dtmf_switch && #endif !member->dtmf_relay) { // relay this to the listening channels queue_incoming_dtmf_frame( member, f ); } #endif } #ifdef DTMF else if (f->frametype == AST_FRAME_DTMF_BEGIN) { if (!member->mute_audio && #ifdef VIDEO !member->dtmf_switch && #endif !member->dtmf_relay) { // relay this to the listening channels queue_incoming_dtmf_frame( member, f ); } } #endif #ifdef VIDEO ast_mutex_lock( &member->lock ) ; // Handle a local or remote conference if (member->conference) { int req_id = member->req_id; ast_mutex_unlock( &member->lock ); // this will return NULL or a locked member src_member = check_active_video(req_id,conf); // Stream a picture to the recipient if no active video if (!src_member) { // Mihai: we don't want to send video here, we cannot negotiate codec // and we don't know what codec the conference is using //if (member->norecv_video == 0) //{ // if(!ast_streamfile(member->chan,"novideo",member->chan->language)) // { // ast_waitstream(member->chan,""); // } //} } else { // Send a FIR to the new sender ast_indicate(src_member->chan,AST_CONTROL_VIDUPDATE); // we will have locked in check_active_video() ast_mutex_unlock( &src_member->lock); } ast_mutex_lock( &member->lock ); member->conference = 0; } ast_mutex_unlock( &member->lock ); #endif if ((f->frametype == AST_FRAME_VOICE && (member->mute_audio == 1 || (member->soundq != NULL && member->muted == 1) ) ) #ifdef VIDEO || (f->frametype == AST_FRAME_VIDEO && member->mute_video == 1 ) #endif ) { // this is a listen-only user, ignore the frame //ast_log( AST_CONF_DEBUG, "Listen only user frame"); ast_frfree( f ) ; f = NULL ; } else if ( f->frametype == AST_FRAME_VOICE ) { //ast_log( AST_CONF_DEBUG, "Got voice frame"); // reset silence detection flag silent_frame = 0 ; // accounting: count the incoming frame member->frames_in++ ; #if ( SILDET == 2 ) // // make sure we have a valid dsp and frame type // if ( member->dsp != NULL && f->subclass == AST_FORMAT_SLINEAR && f->datalen == AST_CONF_FRAME_DATA_SIZE ) { // send the frame to the preprocessor int spx_ret; spx_ret = speex_preprocess( member->dsp, CASTDATA2PTR(f->data, void), NULL ); #ifdef DEBUG_USE_TIMELOG TIMELOG(spx_ret, 3, "speex_preprocess"); #endif if ( spx_ret == 0 ) { // // we ignore the preprocessor's outcome if we've seen voice frames // in within the last AST_CONF_SKIP_SPEEX_PREPROCESS frames // if ( member->ignore_speex_count > 0 ) { // ast_log( AST_CONF_DEBUG, "ignore_speex_count => %d\n", ignore_speex_count ) ; // skip speex_preprocess(), and decrement counter --member->ignore_speex_count ; } else { // set silent_frame flag silent_frame = 1 ; } } else { // voice detected, reset skip count member->ignore_speex_count = AST_CONF_SKIP_SPEEX_PREPROCESS ; } } #endif if ( !silent_frame ) queue_incoming_frame( member, f ); // free the original frame ast_frfree( f ) ; f = NULL ; } #ifdef VIDEO else if (f->frametype == AST_FRAME_VIDEO) { queue_incoming_video_frame( member, f ); // free the original frame ast_frfree( f ) ; f = NULL ; } #endif else if ( f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP ) { // hangup received // free the frame ast_frfree( f ) ; f = NULL ; // break out of the while ( 42 == 42 ) return 1; } #ifdef VIDEO else if ( f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_VIDUPDATE ) { // say we have switched to cause a FIR to // be sent to the sender ast_mutex_lock( &member->lock ) ; member->conference = 1; ast_mutex_unlock( &member->lock ) ; // free the original frame ast_frfree( f ) ; f = NULL ; } #endif #ifdef VIDEO #ifdef TEXT else if ( f->frametype == AST_FRAME_TEXT && member->does_text ) { if ( strncmp(CASTDATA2PTR(f->data, char), AST_CONF_CONTROL_CAMERA_DISABLED, strlen(AST_CONF_CONTROL_CAMERA_DISABLED)) == 0 ) { ast_mutex_lock(&member->lock); manager_event(EVENT_FLAG_CALL, "ConferenceCameraDisabled", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); member->no_camera = 1; ast_mutex_unlock(&member->lock); } else if ( strncmp(CASTDATA2PTR(f->data, char), AST_CONF_CONTROL_CAMERA_ENABLED, strlen(AST_CONF_CONTROL_CAMERA_ENABLED)) == 0 ) { ast_mutex_lock(&member->lock); manager_event(EVENT_FLAG_CALL, "ConferenceCameraEnabled", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); member->no_camera = 0; ast_mutex_unlock(&member->lock); } else if ( strncmp(CASTDATA2PTR(f->data, char), AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_STOP_VIDEO_TRANSMIT)) == 0 ) { ast_mutex_lock(&member->lock); manager_event(EVENT_FLAG_CALL, "ConferenceStopVideoTransmit", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); member->norecv_video = 1; ast_mutex_unlock(&member->lock); } else if ( strncmp(CASTDATA2PTR(f->data, char), AST_CONF_CONTROL_START_VIDEO_TRANSMIT, strlen(AST_CONF_CONTROL_START_VIDEO_TRANSMIT)) == 0 ) { ast_mutex_lock(&member->lock); manager_event(EVENT_FLAG_CALL, "ConferenceStartVideoTransmit", "ConferenceName: %s\r\nChannel: %s\r\n", conf->name, member->channel_name); member->norecv_video = 0; ast_mutex_unlock(&member->lock); } ast_frfree(f); f = NULL; } #endif #endif else { // undesirables ast_frfree( f ) ; f = NULL ; } return 0; } // get the next frame from the soundq; must be called with member locked. static struct ast_frame *get_next_soundframe(struct ast_conf_member *member, struct ast_frame *exampleframe) { struct ast_frame *f; again: ast_mutex_unlock(&member->lock); f=(member->soundq->stream && !member->soundq->stopped ? ast_readframe(member->soundq->stream) : NULL); if(!f) { // we're done with this sound; remove it from the queue, and try again struct ast_conf_soundq *toboot = member->soundq; if (!toboot->stopped && !toboot->stream) { toboot->stream = ast_openstream(member->chan, toboot->name, NULL); //ast_log( LOG_WARNING, "trying to play sound: name = %s, stream = %p\n", toboot->name, toboot->stream); if (toboot->stream) { member->chan->stream = NULL; goto again; } //ast_log( LOG_WARNING, "trying to play sound, %s not found!?", toboot->name); } if (toboot->stream) { ast_closestream(toboot->stream); //ast_log( LOG_WARNING, "finished playing a sound: name = %s, stream = %p\n", toboot->name, toboot->stream); // notify applications via mgr interface that this sound has been played manager_event( EVENT_FLAG_CALL, "ConferenceSoundComplete", "ConferenceName: %s\r\n" "Channel: %s\r\n" "Sound: %s\r\n", member->conf_name, member->channel_name, toboot->name ); } ast_mutex_lock( &member->lock ) ; member->soundq = toboot->next; free(toboot); if(member->soundq) goto again; ast_mutex_unlock(&member->lock); // if we get here, we've gotten to the end of the queue; reset write format if ( ast_set_write_format( member->chan, member->write_format ) < 0 ) { ast_log( LOG_ERROR, "unable to set write format to %d\n", member->write_format ) ; } } else { // copy delivery from exampleframe f->delivery = exampleframe->delivery; } return f; } // process outgoing frames for the channel, playing either normal conference audio, // or requested sounds static int process_outgoing(struct ast_conf_member *member) { conf_frame* cf ; // frame read from the output queue struct ast_frame *f; for(;;) { // acquire member mutex and grab a frame. cf = get_outgoing_frame( member ) ; // if there's no frames exit the loop. if ( !cf ) { break; } struct ast_frame *realframe = f = cf->fr; // if we're playing sounds, we can just replace the frame with the // next sound frame, and send it instead ast_mutex_lock( &member->lock ) ; if ( member->soundq ) { f = get_next_soundframe(member, f); if ( !f ) { // if we didn't get anything, just revert to "normal" f = realframe; } } else { if (member->moh_flag) { member->muted = 1; member->ready_for_outgoing = 0; delete_conf_frame( cf ) ; ast_moh_start(member->chan, NULL, NULL); ast_mutex_unlock(&member->lock); return 0; } ast_mutex_unlock(&member->lock); } #ifdef DEBUG_FRAME_TIMESTAMPS // !!! TESTING !!! int delivery_diff = usecdiff( &f->delivery, &member->lastsent_timeval ) ; if ( delivery_diff != AST_CONF_FRAME_INTERVAL ) { ast_log( AST_CONF_DEBUG, "unanticipated delivery time, delivery_diff => %d, delivery.tv_usec => %ld\n", delivery_diff, f->delivery.tv_usec ) ; } // !!! TESTING !!! if ( f->delivery.tv_sec < member->lastsent_timeval.tv_sec || ( f->delivery.tv_sec == member->lastsent_timeval.tv_sec && f->delivery.tv_usec <= member->lastsent_timeval.tv_usec ) ) { ast_log( LOG_WARNING, "queued frame timestamped in the past, %ld.%ld <= %ld.%ld\n", f->delivery.tv_sec, f->delivery.tv_usec, member->lastsent_timeval.tv_sec, member->lastsent_timeval.tv_usec ) ; } member->lastsent_timeval = f->delivery ; #endif #ifdef DEBUG_USE_TIMELOG TIMELOG( ast_write( member->chan, f ), 10, "member: ast_write"); #else // send the voice frame if ( ast_write( member->chan, f ) == 0 ) { struct timeval tv = ast_tvnow(); ast_log( AST_CONF_DEBUG, "SENT VOICE FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; } else { if ( member->chan->_softhangup ) return 1; // log 'dropped' outgoing frame ast_log( LOG_ERROR, "unable to write voice frame to channel, channel => %s\n", member->channel_name ) ; // accounting: count dropped outgoing frames member->frames_out_dropped++ ; } #endif // clean up frame delete_conf_frame( cf ) ; // free sound frame if ( f != realframe ) ast_frfree(f) ; } #ifdef VIDEO // Do the same for video, suck it dry for(;;) { // grab a frame. cf = get_outgoing_video_frame( member ) ; // if there's no frames exit the loop. if(!cf){ break; } f = cf->fr; // send the video frame if ( ast_write_video( member->chan, f ) == 1 ) { struct timeval tv = ast_tvnow(); ast_log( AST_CONF_DEBUG, "SENT VIDEO FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; } else { if ( member->chan->_softhangup ) return 1; // log 'dropped' outgoing frame ast_log( AST_CONF_DEBUG, "unable to write video frame to channel, channel => %s\n", member->channel_name ) ; // accounting: count dropped outgoing frames member->video_frames_out_dropped++ ; } // clean up frame delete_conf_frame( cf ) ; } #endif #ifdef DTMF // Do the same for dtmf, suck it dry for(;;) { // acquire member mutex and grab a frame. cf = get_outgoing_dtmf_frame( member ) ; // if there's no frames exit the loop. if(!cf) break; // send the dtmf frame if ( ast_write( member->chan, cf->fr ) == 0 ) { struct timeval tv = ast_tvnow(); ast_log( AST_CONF_DEBUG, "SENT DTMF FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; } else { if ( member->chan->_softhangup ) return 1; // log 'dropped' outgoing frame ast_log( AST_CONF_DEBUG, "unable to write dtmf frame to channel, channel => %s\n", member->channel_name ) ; // accounting: count dropped outgoing frames member->dtmf_frames_out_dropped++ ; } // clean up frame delete_conf_frame( cf ) ; } #endif #ifdef TEXT // Do the same for text, hell, why not? for(;;) { // acquire member mutex and grab a frame. cf = get_outgoing_text_frame( member ) ; // if there's no frames exit the loop. if(!cf) break; // send the text frame if ( ast_write( member->chan, cf->fr ) == 0 ) { struct timeval tv = ast_tvnow(); ast_log( AST_CONF_DEBUG, "SENT TEXT FRAME, channel => %s, frames_out => %ld, s => %ld, ms => %ld\n", member->channel_name, member->frames_out, tv.tv_sec, tv.tv_usec ) ; } else { if ( member->chan->_softhangup ) return 1; // log 'dropped' outgoing frame ast_log( AST_CONF_DEBUG, "unable to write text frame to channel, channel => %s\n", member->channel_name ) ; // accounting: count dropped outgoing frames member->text_frames_out_dropped++ ; } // clean up frame delete_conf_frame( cf ) ; } #endif return 0; } // // main member thread function // int member_exec( struct ast_channel* chan, void* data ) { // struct timeval start, end ; // start = ast_tvnow(); struct ast_conference *conf ; struct ast_conf_member *member ; struct ast_frame *f ; // frame received from ast_read() int left = 0 ; int res; ast_log( AST_CONF_DEBUG, "Begin processing member thread, channel => %s\n", chan->name ) ; // // If the call has not yet been answered, answer the call // Note: asterisk apps seem to check _state, but it seems like it's safe // to just call ast_answer. It will just do nothing if it is up. // it will also return -1 if the channel is a zombie, or has hung up. // res = ast_answer( chan ) ; if ( res ) { ast_log( LOG_ERROR, "unable to answer call\n" ) ; return -1 ; } // // create a new member for the conference // // ast_log( AST_CONF_DEBUG, "creating new member, id => %s, flags => %s, p => %s\n", // id, flags, priority ) ; member = create_member( chan, (const char*)( data ) ) ; // flags, atoi( priority ) ) ; // unable to create member, return an error if ( member == NULL ) { ast_log( LOG_ERROR, "unable to create member\n" ) ; return -1 ; } // // setup asterisk read/write formats // #if 0 ast_log( AST_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n", chan->name, chan->dnid, chan->callerid, chan->ani ) ; ast_log( AST_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n", chan->name, chan->nativeformats, member->read_format, member->write_format ) ; #endif if ( ast_set_read_format( chan, member->read_format ) < 0 ) { ast_log( LOG_ERROR, "unable to set read format to signed linear\n" ) ; delete_member( member ) ; return -1 ; } if ( ast_set_write_format( chan, member->write_format ) < 0 ) // AST_FORMAT_SLINEAR, chan->nativeformats { ast_log( LOG_ERROR, "unable to set write format to signed linear\n" ) ; delete_member( member ) ; return -1 ; } // // setup a conference for the new member // char* max_users_flag = 0 ; conf = join_conference( member, max_users_flag ) ; if ( conf == NULL ) { ast_log( LOG_ERROR, "unable to setup member conference\n" ) ; delete_member( member) ; return (*max_users_flag ? 0 : -1 ) ; } // // if spying, setup spyer/spyee // struct ast_conf_member *spyee = NULL; if ( member->spyee_channel_name != NULL ) { spyee = member->spy_partner = find_member(member->spyee_channel_name); if ( spyee != NULL && spyee->spy_partner == NULL ) { spyee->spy_partner = member; ast_mutex_unlock( &spyee->lock ) ; //ast_log( AST_CONF_DEBUG, "Start spyer %s, spyee is %s\n", member->channel_name, member->spyee_channel_name) ; } else { if ( spyee != NULL ) { if ( !--spyee->use_count && spyee->delete_flag ) ast_cond_signal ( &spyee->delete_var ) ; ast_mutex_unlock( &spyee->lock ) ; } pbx_builtin_setvar_helper(member->chan, "KONFERENCE", "SPYFAILED" ); //ast_log( AST_CONF_DEBUG, "Failed to start spyer %s, spyee is %s\n", member->channel_name, member->spyee_channel_name) ; remove_member( member, conf ) ; return 0 ; } } // add member to channel table member->bucket = &(channel_table[hash(member->chan->name) % CHANNEL_TABLE_SIZE]); AST_LIST_LOCK (member->bucket ) ; AST_LIST_INSERT_HEAD (member->bucket, member, hash_entry) ; AST_LIST_UNLOCK (member->bucket ) ; //ast_log( AST_CONF_DEBUG, "Added %s to the channel table, bucket => %ld\n", member->chan->name, member->bucket - channel_table) ; manager_event( EVENT_FLAG_CALL, "ConferenceJoin", "ConferenceName: %s\r\n" "Type: %s\r\n" "UniqueID: %s\r\n" "Member: %d\r\n" "Flags: %s\r\n" "Channel: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n" "Moderators: %d\r\n" "Count: %d\r\n", conf->name, member->type, member->uniqueid, member->id, member->flags, member->channel_name, member->chan->cid.cid_num ? member->chan->cid.cid_num : "unknown", member->chan->cid.cid_name ? member->chan->cid.cid_name: "unknown", conf->stats.moderators, conf->membercount ) ; // Store the CID information if ( member->chan->cid.cid_num ) { if ( (member->callerid = malloc(strlen(member->chan->cid.cid_num)+1)) ) memcpy(member->callerid,member->chan->cid.cid_num, strlen(member->chan->cid.cid_num)+1); } else member->callerid = NULL; if ( member->chan->cid.cid_name ) { if ( (member->callername = malloc(strlen(member->chan->cid.cid_name)+1)) ) memcpy(member->callername, member->chan->cid.cid_name, strlen(member->chan->cid.cid_name)+1); } else member->callername = NULL; // // process loop for new member ( this runs in it's own thread ) // ast_log( AST_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ; // timer timestamps struct timeval base, curr ; base = ast_tvnow(); // tell conference_exec we're ready for frames member->ready_for_outgoing = 1 ; while ( 42 == 42 ) { // make sure we have a channel to process if ( chan == NULL ) { ast_log( LOG_NOTICE, "member channel has closed\n" ) ; break ; } //-----------------// // INCOMING FRAMES // //-----------------// // wait for an event on this channel left = ast_waitfor( chan, AST_CONF_WAITFOR_LATENCY ) ; //ast_log( AST_CONF_DEBUG, "received event on channel, name => %s, left => %d\n", chan->name, left ) ; if ( left < 0 ) { // an error occured ast_log( LOG_NOTICE, "an error occured waiting for a frame, channel => %s, error => %d\n", chan->name, left ) ; break; // out of the 42==42 } else if ( left == 0 ) { // no frame has arrived yet // ast_log( LOG_NOTICE, "no frame available from channel, channel => %s\n", chan->name ) ; } else if ( left > 0 ) { // a frame has come in before the latency timeout // was reached, so we process the frame f = ast_read( chan ) ; if ( f == NULL ) { if (conf->debug_flag) { ast_log( LOG_NOTICE, "unable to read from channel, channel => %s\n", chan->name ) ; // They probably want to hangup... } break ; } // actually process the frame: break if we got hangup. if(process_incoming(member, conf, f)) break; } if (conf->kick_flag || member->kick_flag) { pbx_builtin_setvar_helper(member->chan, "KONFERENCE", "KICKED" ); break; } //-----------------// // OUTGOING FRAMES // //-----------------// // update the current timestamps curr = ast_tvnow(); if ( !process_outgoing(member) ) // back to process incoming frames continue ; else // they probably hungup... break ; } ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld\n", member->time_entered.tv_sec ) ; // // clean up // #ifdef DEBUG_OUTPUT_PCM // !!! TESTING !!! if ( incoming_fh != NULL ) fclose( incoming_fh ) ; #endif // end = ast_tvnow(); // int expected_frames = ( int )( floor( (double)( msecdiff( &end, &start ) / AST_CONF_FRAME_INTERVAL ) ) ) ; // ast_log( AST_CONF_DEBUG, "expected_frames => %d\n", expected_frames ) ; // // if spying sever connection to spyee // if ( spyee != NULL ) { ast_mutex_lock ( &spyee->lock ) ; spyee->spy_partner = NULL; if ( !--spyee->use_count && spyee->delete_flag ) ast_cond_signal ( &spyee->delete_var ) ; ast_mutex_unlock ( &spyee->lock ) ; //ast_log( AST_CONF_DEBUG, "End spyer %s, spyee is %s\n", member->channel_name, member->spyee_channel_name) ; } remove_member( member, conf ) ; return 0 ; } #ifdef VIDEO struct ast_conf_member *check_active_video( int id, struct ast_conference *conf ) { struct ast_conf_member *member; // acquire the conference lock ast_rwlock_rdlock( &conf->lock ) ; member = conf->memberlist; while (member) { if (member->id == id) { // lock this member ast_mutex_lock( &member->lock ) ; ast_rwlock_unlock( &conf->lock ) ; return member; } member = member->next; } ast_rwlock_unlock( &conf->lock ) ; return NULL; } #endif // // manange member functions // struct ast_conf_member* create_member( struct ast_channel *chan, const char* data ) { // // check input // if ( chan == NULL ) { ast_log( LOG_ERROR, "unable to create member with null channel\n" ) ; return NULL ; } if ( chan->name == NULL ) { ast_log( LOG_ERROR, "unable to create member with null channel name\n" ) ; return NULL ; } // // allocate memory for new conference member // struct ast_conf_member *member = calloc( 1, sizeof( struct ast_conf_member ) ) ; if ( member == NULL ) { ast_log( LOG_ERROR, "unable to malloc ast_conf_member\n" ) ; return NULL ; } // initialize mutex ast_mutex_init( &member->lock ) ; // initialize cv ast_cond_init( &member->delete_var, NULL ) ; // initialize flag and count member->delete_flag = member->use_count = 0 ; // Default values for parameters that can get overwritten by dialplan arguments #ifdef VIDEO member->video_start_timeout = AST_CONF_VIDEO_START_TIMEOUT; member->video_stop_timeout = AST_CONF_VIDEO_STOP_TIMEOUT; #endif member->priority = 0; member->vad_prob_start = AST_CONF_PROB_START; member->vad_prob_continue = AST_CONF_PROB_CONTINUE; member->max_users = AST_CONF_MAX_USERS; member->type = NULL; member->spyee_channel_name = NULL; // // initialize member with passed data values // char argstr[256] ; // copy the passed data strncpy( argstr, data, sizeof(argstr) - 1 ) ; // point to the copied data char *stringp = argstr; ast_log( AST_CONF_DEBUG, "attempting to parse passed params, stringp => %s\n", stringp ) ; // parse the id char *token; if ( ( token = strsep( &stringp, argument_delimiter ) ) != NULL ) { member->conf_name = malloc( strlen( token ) + 1 ) ; strcpy( member->conf_name, token ) ; } else { ast_log( LOG_ERROR, "create_member unable to parse member data: channel name = %s, data = %s\n", chan->name, data ) ; free( member ) ; return NULL ; } // parse the flags if ( ( token = strsep( &stringp, argument_delimiter ) ) != NULL ) { member->flags = malloc( strlen( token ) + 1 ) ; strcpy( member->flags, token ) ; } else { // make member->flags something member->flags = malloc( sizeof( char ) ) ; memset( member->flags, 0x0, sizeof( char ) ) ; } while ( (token = strsep(&stringp, argument_delimiter )) != NULL ) { static const char arg_priority[] = "priority"; static const char arg_vad_prob_start[] = "vad_prob_start"; static const char arg_vad_prob_continue[] = "vad_prob_continue"; #ifdef VIDEO static const char arg_video_start_timeout[] = "video_start_timeout"; static const char arg_video_stop_timeout[] = "video_stop_timeout"; #endif static const char arg_max_users[] = "max_users"; static const char arg_conf_type[] = "type"; static const char arg_chanspy[] = "spy"; char *value = token; const char *key = strsep(&value, "="); if ( key == NULL || value == NULL ) { ast_log(LOG_WARNING, "Incorrect argument %s\n", token); continue; } if ( strncasecmp(key, arg_priority, sizeof(arg_priority) - 1) == 0 ) { member->priority = strtol(value, (char **)NULL, 10); ast_log(AST_CONF_DEBUG, "priority = %d\n", member->priority); } else if ( strncasecmp(key, arg_vad_prob_start, sizeof(arg_vad_prob_start) - 1) == 0 ) { member->vad_prob_start = strtof(value, (char **)NULL); ast_log(AST_CONF_DEBUG, "vad_prob_start = %f\n", member->vad_prob_start); } else if ( strncasecmp(key, arg_vad_prob_continue, sizeof(arg_vad_prob_continue) - 1) == 0 ) { member->vad_prob_continue = strtof(value, (char **)NULL); ast_log(AST_CONF_DEBUG, "vad_prob_continue = %f\n", member->vad_prob_continue); #ifdef VIDEO } else if ( strncasecmp(key, arg_video_start_timeout, sizeof(arg_video_start_timeout) - 1) == 0 ) { member->video_start_timeout = strtol(value, (char **)NULL, 10); ast_log(AST_CONF_DEBUG, "video_start_timeout = %d\n", member->video_start_timeout); } else if ( strncasecmp(key, arg_video_stop_timeout, sizeof(arg_video_stop_timeout) - 1) == 0 ) { member->video_stop_timeout = strtol(value, (char **)NULL, 10); ast_log(AST_CONF_DEBUG, "video_stop_timeout = %d\n", member->video_stop_timeout); #endif } else if ( strncasecmp(key, arg_max_users, sizeof(arg_max_users) - 1) == 0 ) { member->max_users = strtol(value, (char **)NULL, 10); ast_log(AST_CONF_DEBUG, "max_users = %d\n", member->max_users); } else if ( strncasecmp(key, arg_conf_type, sizeof(arg_conf_type) - 1) == 0 ) { member->type = malloc( strlen( value ) + 1 ) ; strcpy( member->type, value ) ; ast_log(AST_CONF_DEBUG, "type = %s\n", member->type); } else if ( strncasecmp(key, arg_chanspy, sizeof(arg_chanspy) - 1) == 0 ) { member->spyee_channel_name = malloc( strlen( value ) + 1 ) ; strcpy( member->spyee_channel_name, value ) ; ast_log(AST_CONF_DEBUG, "spyee channel name is %s\n", member->spyee_channel_name); } else { ast_log(LOG_WARNING, "unknown parameter %s with value %s\n", key, value); } } // // initialize member with default values // // keep pointer to member's channel member->chan = chan ; // copy the channel name member->channel_name = malloc( strlen( chan->name ) + 1 ) ; strcpy( member->channel_name, chan->name ) ; // copy the uniqueid member->uniqueid = malloc( strlen( chan->uniqueid ) + 1 ) ; strcpy( member->uniqueid, chan->uniqueid ) ; // set default if no type parameter if (!member->type) { member->type = malloc( strlen( AST_CONF_TYPE_DEFAULT ) + 1 ) ; strcpy( member->type, AST_CONF_TYPE_DEFAULT ) ; ast_log(AST_CONF_DEBUG, "type = %s\n", member->type); } // spy_partner default is NULL member->spy_partner = NULL; // ( default can be overridden by passed flags ) member->mute_audio = 0; #ifdef VIDEO member->mute_video = 0; #endif member->talk_volume = 0; member->listen_volume = 0; member->norecv_audio = 0; #ifdef VIDEO member->norecv_video = 0; member->no_camera = 0; #endif // moderator? member->ismoderator = 0; member->kick_conferees = 0; // ready flag member->ready_for_outgoing = 0 ; // incoming frame queue member->inFrames = NULL ; member->inFramesTail = NULL ; member->inFramesCount = 0 ; #ifdef VIDEO member->inVideoFrames = NULL ; member->inVideoFramesTail = NULL ; member->inVideoFramesCount = 0 ; #endif #ifdef DTMF member->inDTMFFrames = NULL ; member->inDTMFFramesTail = NULL ; member->inDTMFFramesCount = 0 ; #endif #ifdef TEXT member->inTextFrames = NULL ; member->inTextFramesTail = NULL ; member->inTextFramesCount = 0 ; #endif #ifdef VIDEO member->conference = 1; // we have switched req_id member->dtmf_switch = 0; // no dtmf switch by default #endif member->dtmf_relay = 0; // no dtmf relay by default // start of day video ids #ifdef VIDEO member->req_id = -1; #endif member->id = -1; member->first_frame_received = 0; // cause a FIR after NAT delay // last frame caching member->inFramesRepeatLast = 0 ; member->inFramesLast = NULL ; member->okayToCacheLast = 0 ; // outgoing frame queue member->outFrames = NULL ; member->outFramesTail = NULL ; member->outFramesCount = 0 ; #ifdef VIDEO member->outVideoFrames = NULL ; member->outVideoFramesTail = NULL ; member->outVideoFramesCount = 0 ; #endif #ifdef DTMF member->outDTMFFrames = NULL ; member->outDTMFFramesTail = NULL ; member->outDTMFFramesCount = 0 ; #endif #ifdef TEXT member->outTextFrames = NULL ; member->outTextFramesTail = NULL ; member->outTextFramesCount = 0 ; #endif // ( not currently used ) // member->samplesperframe = AST_CONF_BLOCK_SAMPLES ; // used for determining need to mix frames // and for management interface notification // and for VAD based video switching member->speaking_state_notify = 0 ; member->speaking_state = 0 ; member->local_speaking_state = 0; member->last_state_change = (struct timeval){0, 0}; member->speaker_count = 0; #ifdef VIDEO member->driven_member = NULL; member->video_broadcast_active = 0; member->last_video_frame_time = (struct timeval){0, 0}; member->video_started = 0; #endif // linked-list pointers member->next = NULL ; #ifndef VIDEO member->prev = NULL ; #endif member->bucket = NULL ; // account data member->frames_in = 0 ; member->frames_in_dropped = 0 ; member->frames_out = 0 ; member->frames_out_dropped = 0 ; #ifdef VIDEO member->video_frames_in = 0 ; member->video_frames_in_dropped = 0 ; member->video_frames_out = 0 ; member->video_frames_out_dropped = 0 ; #endif #ifdef DTMF member->dtmf_frames_in = 0 ; member->dtmf_frames_in_dropped = 0 ; member->dtmf_frames_out = 0 ; member->dtmf_frames_out_dropped = 0 ; #endif #ifdef TEXT member->text_frames_in = 0 ; member->text_frames_in_dropped = 0 ; member->text_frames_out = 0 ; member->text_frames_out_dropped = 0 ; #endif // for counting sequentially dropped frames member->sequential_drops = 0 ; member->since_dropped = 0 ; // flags member->kick_flag = 0; // record start time // init dropped frame timestamps // init state change timestamp member->time_entered = member->last_in_dropped = member->last_out_dropped = member->last_state_change = ast_tvnow(); // // parse passed flags // // silence detection flags w/ defaults member->vad_flag = 0 ; member->denoise_flag = 0 ; member->agc_flag = 0 ; // is this member using the telephone? member->via_telephone = 0 ; // moh if only member flag member->hold_flag = 0 ; // temp pointer to flags string char* flags = member->flags ; int i; for ( i = 0 ; i < strlen( flags ) ; ++i ) { #ifdef VIDEO if (flags[i] >= (int)'0' && flags[i] <= (int)'9') { if (member->req_id < 0) { member->req_id = flags[i] - (int)'0'; } else { int newid = flags[i] - (int)'0'; // need to boot anyone with this id already // will happen in add_member member->id = newid; } } else #endif { // allowed flags are C, c, L, l, V, D, A, C, X, R, T, t, M, S, z, o, F, H // mute/no_recv options switch ( flags[i] ) { #ifdef VIDEO case 'C': member->mute_video = 1; break ; case 'c': member->norecv_video = 1; break ; #endif case 'L': member->mute_audio = 1; break ; case 'l': member->norecv_audio = 1; break; // speex preprocessing options case 'V': member->vad_flag = 1 ; break ; case 'D': member->denoise_flag = 1 ; break ; case 'A': member->agc_flag = 1 ; break ; // dtmf/moderator/video switching options #ifdef VIDEO case 'X': member->dtmf_switch = 1; break; #endif case 'R': member->dtmf_relay = 1; break; #ifdef VIDEO case 'S': member->vad_switch = 1; break; case 'F': member->force_vad_switch = 1; break; #endif case 'M': member->ismoderator = 1; break; #ifdef VIDEO case 'N': member->no_camera = 1; break; #endif #ifdef TEXT case 't': member->does_text = 1; break; #endif #ifdef VIDEO case 'z': member->vad_linger = 1; break; case 'o': member->does_chat_mode = 1; break; #endif case 'x': member->kick_conferees = 1; break; //Telephone connection case 'a': member->vad_flag = 1 ; case 'T': member->via_telephone = 1; break; case 'H': member->hold_flag = 1; break; default: break ; } } } // set the dsp to null so silence detection is disabled by default member->dsp = NULL ; #if ( SILDET == 2 ) // // configure silence detection and preprocessing // if the user is coming in via the telephone, // and is not listen-only // if ( member->via_telephone == 1) { // create a speex preprocessor member->dsp = speex_preprocess_state_init( AST_CONF_BLOCK_SAMPLES, AST_CONF_SAMPLE_RATE ) ; if ( member->dsp == NULL ) { ast_log( LOG_WARNING, "unable to initialize member dsp, channel => %s\n", chan->name ) ; } else { ast_log( AST_CONF_DEBUG, "member dsp initialized, channel => %s, v => %d, d => %d, a => %d\n", chan->name, member->vad_flag, member->denoise_flag, member->agc_flag ) ; // set speex preprocessor options speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_VAD, &(member->vad_flag) ) ; speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_DENOISE, &(member->denoise_flag) ) ; speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_AGC, &(member->agc_flag) ) ; speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_START, &member->vad_prob_start ) ; speex_preprocess_ctl( member->dsp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &member->vad_prob_continue ) ; ast_log( AST_CONF_DEBUG, "speech_prob_start => %f, speech_prob_continue => %f\n", member->dsp->speech_prob_start, member->dsp->speech_prob_continue ) ; } } #endif // // read, write, and translation options // // set member's audio formats, taking dsp preprocessing into account // ( chan->nativeformats, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FORMAT_GSM ) member->read_format = ( member->dsp == NULL ) ? chan->nativeformats : AST_FORMAT_SLINEAR ; member->write_format = chan->nativeformats; // 1.2 or 1.3+ #ifdef AST_FORMAT_AUDIO_MASK member->read_format &= AST_FORMAT_AUDIO_MASK; member->write_format &= AST_FORMAT_AUDIO_MASK; #endif // translation paths ( ast_translator_build_path() returns null if formats match ) member->to_slinear = ast_translator_build_path( AST_FORMAT_SLINEAR, member->read_format ) ; member->from_slinear = ast_translator_build_path( member->write_format, AST_FORMAT_SLINEAR ) ; ast_log( AST_CONF_DEBUG, "AST_FORMAT_SLINEAR => %d\n", AST_FORMAT_SLINEAR ) ; // index for converted_frames array switch ( member->write_format ) { case AST_FORMAT_SLINEAR: member->write_format_index = AC_SLINEAR_INDEX ; break ; case AST_FORMAT_ULAW: member->write_format_index = AC_ULAW_INDEX ; break ; case AST_FORMAT_ALAW: member->write_format_index = AC_ALAW_INDEX ; break ; case AST_FORMAT_GSM: member->write_format_index = AC_GSM_INDEX ; break ; case AST_FORMAT_SPEEX: member->write_format_index = AC_SPEEX_INDEX; break; #ifdef AC_USE_G729A case AST_FORMAT_G729A: member->write_format_index = AC_G729A_INDEX; break; #endif default: member->write_format_index = 0 ; } // index for converted_frames array switch ( member->read_format ) { case AST_FORMAT_SLINEAR: member->read_format_index = AC_SLINEAR_INDEX ; break ; case AST_FORMAT_ULAW: member->read_format_index = AC_ULAW_INDEX ; break ; case AST_FORMAT_ALAW: member->read_format_index = AC_ALAW_INDEX ; break ; case AST_FORMAT_GSM: member->read_format_index = AC_GSM_INDEX ; break ; case AST_FORMAT_SPEEX: member->read_format_index = AC_SPEEX_INDEX; break; #ifdef AC_USE_G729A case AST_FORMAT_G729A: member->read_format_index = AC_G729A_INDEX; break; #endif default: member->read_format_index = 0 ; } // smoother defaults. member->smooth_multiple =1; member->smooth_size_in = -1; member->smooth_size_out = -1; member->inSmoother= NULL; member->outPacker= NULL; switch (member->read_format){ /* these assumptions may be incorrect */ case AST_FORMAT_ULAW: case AST_FORMAT_ALAW: member->smooth_size_in = 160; //bytes member->smooth_size_out = 160; //samples break; case AST_FORMAT_GSM: /* member->smooth_size_in = 33; //bytes member->smooth_size_out = 160;//samples */ break; case AST_FORMAT_SPEEX: case AST_FORMAT_G729A: /* this assumptions are wrong member->smooth_multiple = 2 ; // for testing, force to dual frame member->smooth_size_in = 39; // bytes member->smooth_size_out = 160; // samples */ break; case AST_FORMAT_SLINEAR: member->smooth_size_in = 320; //bytes member->smooth_size_out = 160; //samples break; default: member->inSmoother = NULL; //don't use smoother for this type. //ast_log( AST_CONF_DEBUG, "smoother is NULL for member->read_format => %d\n", member->read_format); } if (member->smooth_size_in > 0){ member->inSmoother = ast_smoother_new(member->smooth_size_in); ast_log( AST_CONF_DEBUG, "created smoother(%d) for %d\n", member->smooth_size_in , member->read_format); } // // finish up // ast_log( AST_CONF_DEBUG, "created member, type => %s, priority => %d, readformat => %d\n", member->type, member->priority, chan->readformat ) ; return member ; } struct ast_conf_member* delete_member( struct ast_conf_member* member ) { // !!! NO RETURN TEST !!! // do { sleep(1) ; } while (1) ; // !!! CRASH TEST !!! // *((int *)0) = 0; if ( member == NULL ) { ast_log( LOG_WARNING, "unable to the delete null member\n" ) ; return NULL ; } ast_mutex_lock ( &member->lock ) ; member->delete_flag = 1 ; if ( member->use_count ) ast_cond_wait ( &member->delete_var, &member->lock ) ; ast_mutex_unlock ( &member->lock ) ; #ifdef VIDEO // If member is driving another member, make sure its speaker count is correct if ( member->driven_member != NULL && member->speaking_state == 1 ) decrement_speaker_count(member->driven_member, 1); #endif // // clean up member flags // if ( member->flags != NULL ) { // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "freeing member flags, name => %s\n", member->channel_name ) ; free( member->flags ) ; } // // delete the members frames // conf_frame* cf ; // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "deleting member input frames, name => %s\n", member->channel_name ) ; // incoming frames cf = member->inFrames ; while ( cf != NULL ) { cf = delete_conf_frame( cf ) ; } if (member->inSmoother != NULL) ast_smoother_free(member->inSmoother); #ifdef VIDEO cf = member->inVideoFrames ; while ( cf != NULL ) { cf = delete_conf_frame( cf ) ; } #endif // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "deleting member output frames, name => %s\n", member->channel_name ) ; // outgoing frames cf = member->outFrames ; while ( cf != NULL ) { cf = delete_conf_frame( cf ) ; } #ifdef VIDEO cf = member->outVideoFrames ; while ( cf != NULL ) { cf = delete_conf_frame( cf ) ; } #endif #if ( SILDET == 2 ) if ( member->dsp != NULL ) { // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "destroying member preprocessor, name => %s\n", member->channel_name ) ; speex_preprocess_state_destroy( member->dsp ) ; } #endif // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "freeing member translator paths, name => %s\n", member->channel_name ) ; // free the mixing translators ast_translator_free_path( member->to_slinear ) ; ast_translator_free_path( member->from_slinear ) ; // get a pointer to the next // member so we can return it struct ast_conf_member* nm = member->next ; // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "freeing member channel name, name => %s\n", member->channel_name ) ; // free the member's copy for the channel name free( member->channel_name ) ; // free the member's copy of the uniqueid free( member->uniqueid ) ; // free the member's copy of the conference name free(member->conf_name); // free the member's copy of the conference type free(member->type); // free the member's copy of the spyee channel name free(member->spyee_channel_name); // free the member's memory free(member->callerid); free(member->callername); // clear all sounds struct ast_conf_soundq *sound = member->soundq; struct ast_conf_soundq *next; while ( sound ) { next = sound->next; if ( sound->stream ) ast_closestream( sound->stream ); free( sound ); sound = next; } // !!! DEBUGING !!! ast_log( AST_CONF_DEBUG, "freeing member\n" ) ; free( member ) ; member = NULL ; return nm ; } // // incoming frame functions // #ifdef VIDEO conf_frame* get_incoming_video_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } ast_mutex_lock(&member->lock); if ( member->inVideoFramesCount == 0 ) { ast_mutex_unlock(&member->lock); return NULL ; } // // return the next frame in the queue // conf_frame* cfr = NULL ; // get first frame in line cfr = member->inVideoFramesTail ; // if it's the only frame, reset the queue, // else, move the second frame to the front if ( member->inVideoFramesTail == member->inVideoFrames ) { member->inVideoFramesTail = NULL ; member->inVideoFrames = NULL ; } else { // move the pointer to the next frame member->inVideoFramesTail = member->inVideoFramesTail->prev ; // reset it's 'next' pointer if ( member->inVideoFramesTail != NULL ) member->inVideoFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decrement frame count member->inVideoFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } #endif #ifdef DTMF conf_frame* get_incoming_dtmf_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } ast_mutex_lock(&member->lock); if ( member->inDTMFFramesCount == 0 ) { ast_mutex_unlock(&member->lock); return NULL ; } // // return the next frame in the queue // conf_frame* cfr = NULL ; // get first frame in line cfr = member->inDTMFFramesTail ; // if it's the only frame, reset the queue, // else, move the second frame to the front if ( member->inDTMFFramesTail == member->inDTMFFrames ) { member->inDTMFFramesTail = NULL ; member->inDTMFFrames = NULL ; } else { // move the pointer to the next frame member->inDTMFFramesTail = member->inDTMFFramesTail->prev ; // reset it's 'next' pointer if ( member->inDTMFFramesTail != NULL ) member->inDTMFFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->inDTMFFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } #endif conf_frame* get_incoming_frame( struct ast_conf_member *member ) { conf_frame *cf_result; // // sanity checks // if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } ast_mutex_lock(&member->lock); // // repeat last frame a couple times to smooth transition // #ifdef AST_CONF_CACHE_LAST_FRAME if ( member->inFramesCount == 0 ) { // nothing to do if there's no cached frame if ( member->inFramesLast == NULL ) { ast_mutex_unlock(&member->lock); return NULL ; } // turn off 'okay to cache' flag member->okayToCacheLast = 0 ; if ( member->inFramesRepeatLast >= AST_CONF_CACHE_LAST_FRAME ) { // already used this frame AST_CONF_CACHE_LAST_FRAME times // reset repeat count member->inFramesRepeatLast = 0 ; // clear the cached frame delete_conf_frame( member->inFramesLast ) ; member->inFramesLast = NULL ; // return null ast_mutex_unlock(&member->lock); return NULL ; } else { ast_log( AST_CONF_DEBUG, "repeating cached frame, channel => %s, inFramesRepeatLast => %d\n", member->channel_name, member->inFramesRepeatLast ) ; // increment counter member->inFramesRepeatLast++ ; // return a copy of the cached frame cf_result = copy_conf_frame( member->inFramesLast ) ; ast_mutex_unlock(&member->lock); return cf_result; } } else if ( member->okayToCacheLast == 0 && member->inFramesCount >= 3 ) { ast_log( AST_CONF_DEBUG, "enabling cached frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inFramesCount, member->outFramesCount ) ; // turn on 'okay to cache' flag member->okayToCacheLast = 1 ; } #else if ( member->inFramesCount == 0 ) { ast_mutex_unlock(&member->lock); return NULL ; } #endif // AST_CONF_CACHE_LAST_FRAME // // return the next frame in the queue // conf_frame* cfr = NULL ; // get first frame in line cfr = member->inFramesTail ; // if it's the only frame, reset the queue, // else, move the second frame to the front if ( member->inFramesTail == member->inFrames ) { member->inFramesTail = NULL ; member->inFrames = NULL ; } else { // move the pointer to the next frame member->inFramesTail = member->inFramesTail->prev ; // reset it's 'next' pointer if ( member->inFramesTail != NULL ) member->inFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->inFramesCount-- ; #ifdef AST_CONF_CACHE_LAST_FRAME // copy frame if queue is now empty if ( member->inFramesCount == 0 && member->okayToCacheLast == 1 ) { // reset repeat count member->inFramesRepeatLast = 0 ; // clear cached frame if ( member->inFramesLast != NULL ) { delete_conf_frame( member->inFramesLast ) ; member->inFramesLast = NULL ; } // cache new frame member->inFramesLast = copy_conf_frame( cfr ) ; } #endif // AST_CONF_CACHE_LAST_FRAME ast_mutex_unlock(&member->lock); return cfr ; } #ifdef VIDEO int queue_incoming_video_frame( struct ast_conf_member* member, const struct ast_frame* fr ) { // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } // lock the member ast_mutex_lock(&member->lock); if (!member->first_frame_received) { // nat=yes will be correct now member->first_frame_received = 1; member->conference = 1; } // We have to drop if the queue is full! if ( member->inVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE ) { ast_log( AST_CONF_DEBUG, "unable to queue incoming VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount ) ; ast_mutex_unlock(&member->lock); return -1 ; } // // create new conf frame from passed data frame // // ( member->inFrames may be null at this point ) conf_frame* cfr = create_conf_frame( member, member->inVideoFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ast_mutex_unlock(&member->lock); return -1 ; } // copy frame data pointer to conf frame // cfr->fr = fr ; // // add new frame to speaking members incoming frame queue // ( i.e. save this frame data, so we can distribute it in conference_exec later ) // if ( member->inVideoFrames == NULL ) { // this is the first frame in the buffer member->inVideoFramesTail = cfr ; member->inVideoFrames = cfr ; } else { // put the new frame at the head of the list member->inVideoFrames = cfr ; } // increment member frame count member->inVideoFramesCount++ ; ast_mutex_unlock(&member->lock); // Everything has gone okay! return 0; } #endif #ifdef DTMF int queue_incoming_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) { //ast_log( AST_CONF_DEBUG, "queue incoming video frame\n"); // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } ast_mutex_lock(&member->lock); // We have to drop if the queue is full! if ( member->inDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE ) { ast_log( AST_CONF_DEBUG, "unable to queue incoming DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount ) ; ast_mutex_unlock(&member->lock); return -1 ; } // // create new conf frame from passed data frame // // ( member->inFrames may be null at this point ) conf_frame* cfr = create_conf_frame( member, member->inDTMFFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ast_mutex_unlock(&member->lock); return -1 ; } // copy frame data pointer to conf frame // cfr->fr = fr ; // // add new frame to speaking members incoming frame queue // ( i.e. save this frame data, so we can distribute it in conference_exec later ) // if ( member->inDTMFFrames == NULL ) { // this is the first frame in the buffer member->inDTMFFramesTail = cfr ; member->inDTMFFrames = cfr ; } else { // put the new frame at the head of the list member->inDTMFFrames = cfr ; } // increment member frame count member->inDTMFFramesCount++ ; ast_mutex_unlock(&member->lock); // Everything has gone okay! return 0; } #endif int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) { // // sanity checks // // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } ast_mutex_lock(&member->lock); if ( member->inFramesCount > member->inFramesNeeded ) { if ( member->inFramesCount > AST_CONF_QUEUE_DROP_THRESHOLD ) { struct timeval curr = ast_tvnow(); // time since last dropped frame long diff = ast_tvdiff_ms(curr, member->last_in_dropped); // number of milliseconds which must pass between frame drops // ( 15 frames => -100ms, 10 frames => 400ms, 5 frames => 900ms, 0 frames => 1400ms, etc. ) long time_limit = 1000 - ( ( member->inFramesCount - AST_CONF_QUEUE_DROP_THRESHOLD ) * 100 ) ; if ( diff >= time_limit ) { // count sequential drops member->sequential_drops++ ; ast_log( AST_CONF_DEBUG, "dropping frame from input buffer, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inFramesCount, member->outFramesCount ) ; // accounting: count dropped incoming frames member->frames_in_dropped++ ; // reset frames since dropped member->since_dropped = 0 ; // delete the frame delete_conf_frame( get_incoming_frame( member ) ) ; member->last_in_dropped = ast_tvnow(); } else { /* ast_log( AST_CONF_DEBUG, "input buffer larger than drop threshold, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inFramesCount, member->outFramesCount ) ; */ } } } // // if we have to drop frames, we'll drop new frames // because it's easier ( and doesn't matter much anyway ). // if ( member->inFramesCount >= AST_CONF_MAX_QUEUE ) { // count sequential drops member->sequential_drops++ ; ast_log( AST_CONF_DEBUG, "unable to queue incoming frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inFramesCount, member->outFramesCount ) ; // accounting: count dropped incoming frames member->frames_in_dropped++ ; // reset frames since dropped member->since_dropped = 0 ; ast_mutex_unlock(&member->lock); return -1 ; } // reset sequential drops member->sequential_drops = 0 ; // increment frames since dropped member->since_dropped++ ; // // create new conf frame from passed data frame // // ( member->inFrames may be null at this point ) if (member->inSmoother == NULL ){ conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ast_mutex_unlock(&member->lock); return -1 ; } // // add new frame to speaking members incoming frame queue // ( i.e. save this frame data, so we can distribute it in conference_exec later ) // if ( member->inFrames == NULL ) { member->inFramesTail = cfr ; } member->inFrames = cfr ; member->inFramesCount++ ; } else { //feed frame(fr) into the smoother // smoother tmp frame struct ast_frame *sfr; int multiple = 1; int i=0; #if 0 if ( (member->smooth_size_in > 0 ) && (member->smooth_size_in * member->smooth_multiple != fr->datalen) ) { ast_log( AST_CONF_DEBUG, "resetting smooth_size_in. old size=> %d, multiple =>%d, datalen=> %d\n", member->smooth_size_in, member->smooth_multiple, fr->datalen ); if ( fr->datalen % member->smooth_multiple != 0) { // if datalen not divisible by smooth_multiple, assume we're just getting normal encoding. // ast_log(AST_CONF_DEBUG,"smooth_multiple does not divide datalen. changing smooth size from %d to %d, multiple => 1\n", member->smooth_size_in, fr->datalen); member->smooth_size_in = fr->datalen; member->smooth_multiple = 1; } else { // assume a fixed multiple, so divide into datalen. int newsmooth = fr->datalen / member->smooth_multiple ; // ast_log(AST_CONF_DEBUG,"datalen is divisible by smooth_multiple, changing smooth size from %d to %d\n", member->smooth_size_in, newsmooth); member->smooth_size_in = newsmooth; } //free input smoother. if (member->inSmoother != NULL) ast_smoother_free(member->inSmoother); //make new input smoother. member->inSmoother = ast_smoother_new(member->smooth_size_in); } #endif ast_smoother_feed( member->inSmoother, fr ); ast_log (AST_CONF_DEBUG, "SMOOTH:Feeding frame into inSmoother, timestamp => %ld.%ld\n", fr->delivery.tv_sec, fr->delivery.tv_usec); if ( multiple > 1 ) fr->samples /= multiple; // read smoothed version of frames, add to queue while( ( sfr = ast_smoother_read( member->inSmoother ) ) ){ ++i; ast_log( AST_CONF_DEBUG , "\treading new frame [%d] from smoother, inFramesCount[%d], \n\tsfr->frametype -> %d , sfr->subclass -> %d , sfr->datalen => %d sfr->samples => %d\n", i , member->inFramesCount , sfr->frametype, sfr->subclass, sfr->datalen, sfr->samples); ast_log (AST_CONF_DEBUG, "SMOOTH:Reading frame from inSmoother, i=>%d, timestamp => %ld.%ld\n",i, sfr->delivery.tv_sec, sfr->delivery.tv_usec); conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to malloc conf_frame\n" ) ; ast_mutex_unlock(&member->lock); return -1 ; } // // add new frame to speaking members incoming frame queue // ( i.e. save this frame data, so we can distribute it in conference_exec later ) // if ( member->inFrames == NULL ) { member->inFramesTail = cfr ; } member->inFrames = cfr ; member->inFramesCount++ ; } } ast_mutex_unlock(&member->lock); return 0 ; } // // outgoing frame functions // conf_frame* get_outgoing_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } conf_frame* cfr ; // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ast_mutex_lock(&member->lock); if ( member->outFramesCount > AST_CONF_MIN_QUEUE ) { cfr = member->outFramesTail ; // if it's the only frame, reset the queu, // else, move the second frame to the front if ( member->outFramesTail == member->outFrames ) { member->outFrames = NULL ; member->outFramesTail = NULL ; } else { // move the pointer to the next frame member->outFramesTail = member->outFramesTail->prev ; // reset it's 'next' pointer if ( member->outFramesTail != NULL ) member->outFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->outFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } ast_mutex_unlock(&member->lock); return NULL ; } int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) { // accounting: count the number of outgoing frames for this member member->frames_out++ ; // // we have to drop frames, so we'll drop new frames // because it's easier ( and doesn't matter much anyway ). // if ( member->outFramesCount >= AST_CONF_MAX_QUEUE ) { ast_log( AST_CONF_DEBUG, "unable to queue outgoing frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inFramesCount, member->outFramesCount ) ; // accounting: count dropped outgoing frames member->frames_out_dropped++ ; return -1 ; } // // create new conf frame from passed data frame // conf_frame* cfr = create_conf_frame( member, member->outFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; // accounting: count dropped outgoing frames member->frames_out_dropped++ ; return -1 ; } // set delivery timestamp cfr->fr->delivery = delivery ; // // add new frame to speaking members incoming frame queue // ( i.e. save this frame data, so we can distribute it in conference_exec later ) // if ( member->outFrames == NULL ) { member->outFramesTail = cfr ; } member->outFrames = cfr ; member->outFramesCount++ ; // return success return 0 ; } int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) { // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } if ( ( member->outPacker == NULL ) && ( member->smooth_multiple > 1 ) && ( member->smooth_size_out > 0 ) ){ //ast_log (AST_CONF_DEBUG, "creating outPacker with size => %d \n\t( multiple => %d ) * ( size => %d )\n", member->smooth_multiple * member-> smooth_size_out, member->smooth_multiple , member->smooth_size_out); member->outPacker = ast_packer_new( member->smooth_multiple * member->smooth_size_out); } if (member->outPacker == NULL ){ return __queue_outgoing_frame( member, fr, delivery ) ; } else { struct ast_frame *sfr; int exitval = 0; //ast_log (AST_CONF_DEBUG, "sending fr into outPacker, datalen=>%d, samples=>%d\n",fr->datalen, fr->samples); ast_packer_feed( member->outPacker , fr ); while( (sfr = ast_packer_read( member->outPacker ) ) ) { //ast_log (AST_CONF_DEBUG, "read sfr from outPacker, datalen=>%d, samples=>%d\n",sfr->datalen, sfr->samples); if ( __queue_outgoing_frame( member, sfr, delivery ) == -1 ) { exitval = -1; } } return exitval; } } // // outgoing frame functions // #ifdef VIDEO conf_frame* get_outgoing_video_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } conf_frame* cfr ; ast_mutex_lock(&member->lock); // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; if ( member->outVideoFramesCount > AST_CONF_MIN_QUEUE ) { cfr = member->outVideoFramesTail ; // if it's the only frame, reset the queu, // else, move the second frame to the front if ( member->outVideoFramesTail == member->outVideoFrames ) { member->outVideoFrames = NULL ; member->outVideoFramesTail = NULL ; } else { // move the pointer to the next frame member->outVideoFramesTail = member->outVideoFramesTail->prev ; // reset it's 'next' pointer if ( member->outVideoFramesTail != NULL ) member->outVideoFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->outVideoFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } ast_mutex_unlock(&member->lock); return NULL ; } int queue_outgoing_video_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) { // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } ast_mutex_lock(&member->lock); // accounting: count the number of outgoing frames for this member member->video_frames_out++ ; // // we have to drop frames, so we'll drop new frames // because it's easier ( and doesn't matter much anyway ). // if ( member->outVideoFramesCount >= AST_CONF_MAX_VIDEO_QUEUE) { ast_log( AST_CONF_DEBUG, "unable to queue outgoing VIDEO frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inVideoFramesCount, member->outVideoFramesCount ) ; // accounting: count dropped outgoing frames member->video_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } // // create new conf frame from passed data frame // conf_frame* cfr = create_conf_frame( member, member->outVideoFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; // accounting: count dropped outgoing frames member->video_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } // set delivery timestamp #ifdef VIDEO_SETTIMESTAMP cfr->fr->delivery = delivery ; #else cfr->fr->delivery.tv_sec = 0; cfr->fr->delivery.tv_usec = 0; #endif //ast_log (LOG_WARNING,"%d\n",cfr->fr->seqno); #ifdef RTP_SEQNO_ZERO cfr->fr->seqno = 0; #endif if ( member->outVideoFrames == NULL ) { // this is the first frame in the buffer member->outVideoFramesTail = cfr ; member->outVideoFrames = cfr ; } else { // put the new frame at the head of the list member->outVideoFrames = cfr ; } // increment member frame count member->outVideoFramesCount++ ; ast_mutex_unlock(&member->lock); // return success return 0 ; } #endif #ifdef DTMF conf_frame* get_outgoing_dtmf_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } conf_frame* cfr ; // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ast_mutex_lock(&member->lock); if ( member->outDTMFFramesCount > AST_CONF_MIN_QUEUE ) { cfr = member->outDTMFFramesTail ; // if it's the only frame, reset the queu, // else, move the second frame to the front if ( member->outDTMFFramesTail == member->outDTMFFrames ) { member->outDTMFFrames = NULL ; member->outDTMFFramesTail = NULL ; } else { // move the pointer to the next frame member->outDTMFFramesTail = member->outDTMFFramesTail->prev ; // reset it's 'next' pointer if ( member->outDTMFFramesTail != NULL ) member->outDTMFFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->outDTMFFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } ast_mutex_unlock(&member->lock); return NULL ; } #endif #ifdef TEXT conf_frame* get_outgoing_text_frame( struct ast_conf_member *member ) { if ( member == NULL ) { ast_log( LOG_WARNING, "unable to get frame from null member\n" ) ; return NULL ; } conf_frame* cfr ; // ast_log( AST_CONF_DEBUG, "getting member frames, count => %d\n", member->outFramesCount ) ; ast_mutex_lock(&member->lock); if ( member->outTextFramesCount > AST_CONF_MIN_QUEUE ) { cfr = member->outTextFramesTail ; // if it's the only frame, reset the queu, // else, move the second frame to the front if ( member->outTextFramesTail == member->outTextFrames ) { member->outTextFrames = NULL ; member->outTextFramesTail = NULL ; } else { // move the pointer to the next frame member->outTextFramesTail = member->outTextFramesTail->prev ; // reset it's 'next' pointer if ( member->outTextFramesTail != NULL ) member->outTextFramesTail->next = NULL ; } // separate the conf frame from the list cfr->next = NULL ; cfr->prev = NULL ; // decriment frame count member->outTextFramesCount-- ; ast_mutex_unlock(&member->lock); return cfr ; } ast_mutex_unlock(&member->lock); return NULL ; } #endif #ifdef DTMF int queue_outgoing_dtmf_frame( struct ast_conf_member* member, const struct ast_frame* fr ) { // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } ast_mutex_lock(&member->lock); // accounting: count the number of outgoing frames for this member member->dtmf_frames_out++ ; // // we have to drop frames, so we'll drop new frames // because it's easier ( and doesn't matter much anyway ). // if ( member->outDTMFFramesCount >= AST_CONF_MAX_DTMF_QUEUE) { ast_log( AST_CONF_DEBUG, "unable to queue outgoing DTMF frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inDTMFFramesCount, member->outDTMFFramesCount ) ; // accounting: count dropped outgoing frames member->dtmf_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } // // create new conf frame from passed data frame // conf_frame* cfr = create_conf_frame( member, member->outDTMFFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; // accounting: count dropped outgoing frames member->dtmf_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } #ifdef RTP_SEQNO_ZERO cfr->fr->seqno = 0; #endif if ( member->outDTMFFrames == NULL ) { // this is the first frame in the buffer member->outDTMFFramesTail = cfr ; member->outDTMFFrames = cfr ; } else { // put the new frame at the head of the list member->outDTMFFrames = cfr ; } // increment member frame count member->outDTMFFramesCount++ ; ast_mutex_unlock(&member->lock); // return success return 0 ; } #endif #ifdef TEXT int queue_outgoing_text_frame( struct ast_conf_member* member, const struct ast_frame* fr) { // check on frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to queue null frame\n" ) ; return -1 ; } // check on member if ( member == NULL ) { ast_log( LOG_ERROR, "unable to queue frame for null member\n" ) ; return -1 ; } ast_mutex_lock(&member->lock); // accounting: count the number of outgoing frames for this member member->text_frames_out++ ; // // we have to drop frames, so we'll drop new frames // because it's easier ( and doesn't matter much anyway ). // if ( member->outTextFramesCount >= AST_CONF_MAX_TEXT_QUEUE) { ast_log( AST_CONF_DEBUG, "unable to queue outgoing text frame, channel => %s, incoming => %d, outgoing => %d\n", member->channel_name, member->inTextFramesCount, member->outTextFramesCount ) ; // accounting: count dropped outgoing frames member->text_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } // // create new conf frame from passed data frame // conf_frame* cfr = create_conf_frame( member, member->outTextFrames, fr ) ; if ( cfr == NULL ) { ast_log( LOG_ERROR, "unable to create new conf frame\n" ) ; // accounting: count dropped outgoing frames member->text_frames_out_dropped++ ; ast_mutex_unlock(&member->lock); return -1 ; } #ifdef RTP_SEQNO_ZERO cfr->fr->seqno = 0; #endif if ( member->outTextFrames == NULL ) { // this is the first frame in the buffer member->outTextFramesTail = cfr ; member->outTextFrames = cfr ; } else { // put the new frame at the head of the list member->outTextFrames = cfr ; } // increment member frame count member->outTextFramesCount++ ; ast_mutex_unlock(&member->lock); // return success return 0 ; } #endif // // manager functions // void send_state_change_notifications( struct ast_conf_member* member ) { // ast_log( AST_CONF_DEBUG, "sending state change notification\n" ) ; // loop through list of members, sending state changes while ( member != NULL ) { // has the state changed since last time through this loop? if ( member->speaking_state_notify ) { manager_event( EVENT_FLAG_CALL, "ConferenceState", "ConferenceName: %s\r\n" "Channel: %s\r\n" "Flags: %s\r\n" "State: %s\r\n", member->conf_name, member->channel_name, member->flags, ( ( member->speaking_state == 1 ) ? "speaking" : "silent" ) ) ; ast_log( AST_CONF_DEBUG, "member state changed, channel => %s, state => %d, incoming => %d, outgoing => %d\n", member->channel_name, member->speaking_state, member->inFramesCount, member->outFramesCount ) ; member->speaking_state_notify = 0; } // move the pointer to the next member member = member->next ; } return ; } // // ast_packer, adapted from ast_smoother // pack multiple frames together into one packet on the wire. // #define PACKER_SIZE 8000 #define PACKER_QUEUE 10 // store at most 10 complete packets in the queue struct ast_packer { int framesize; // number of frames per packet on the wire. int size; int packet_index; int format; int readdata; int optimizablestream; int flags; float samplesperbyte; struct ast_frame f; struct timeval delivery; char data[PACKER_SIZE]; char framedata[PACKER_SIZE + AST_FRIENDLY_OFFSET]; int samples; int sample_queue[PACKER_QUEUE]; int len_queue[PACKER_QUEUE]; struct ast_frame *opt; int len; }; void ast_packer_reset(struct ast_packer *s, int framesize) { memset(s, 0, sizeof(struct ast_packer)); s->framesize = framesize; s->packet_index=0; s->len=0; } struct ast_packer *ast_packer_new(int framesize) { struct ast_packer *s; if (framesize < 1) return NULL; s = malloc(sizeof(struct ast_packer)); if (s) ast_packer_reset(s, framesize); return s; } int ast_packer_get_flags(struct ast_packer *s) { return s->flags; } void ast_packer_set_flags(struct ast_packer *s, int flags) { s->flags = flags; } int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f) { if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Huh? Can't pack a non-voice frame!\n"); return -1; } if (!s->format) { s->format = f->subclass; s->samples=0; } else if (s->format != f->subclass) { ast_log(LOG_WARNING, "Packer was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass); return -1; } if (s->len + f->datalen > PACKER_SIZE) { ast_log(LOG_WARNING, "Out of packer space\n"); return -1; } if (s->packet_index >= PACKER_QUEUE ){ ast_log(LOG_WARNING, "Out of packer queue space\n"); return -1; } memcpy(s->data + s->len, CASTDATA2PTR(f->data, void), f->datalen); /* If either side is empty, reset the delivery time */ if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) || (!s->delivery.tv_sec && !s->delivery.tv_usec)) s->delivery = f->delivery; s->len += f->datalen; //packer stuff s->len_queue[s->packet_index] += f->datalen; s->sample_queue[s->packet_index] += f->samples; s->samples += f->samples; if (s->samples > s->framesize ) ++s->packet_index; return 0; } struct ast_frame *ast_packer_read(struct ast_packer *s) { struct ast_frame *opt; int len; /* IF we have an optimization frame, send it */ if (s->opt) { opt = s->opt; s->opt = NULL; return opt; } /* Make sure we have enough data */ if (s->samples < s->framesize ){ return NULL; } len = s->len_queue[0]; if (len > s->len) len = s->len; /* Make frame */ s->f.frametype = AST_FRAME_VOICE; s->f.subclass = s->format; SETDATA2PTR(s->f.data, s->framedata + AST_FRIENDLY_OFFSET); s->f.offset = AST_FRIENDLY_OFFSET; s->f.datalen = len; s->f.samples = s->sample_queue[0]; s->f.delivery = s->delivery; /* Fill Data */ memcpy(CASTDATA2PTR(s->f.data, void), s->data, len); s->len -= len; /* Move remaining data to the front if applicable */ if (s->len) { /* In principle this should all be fine because if we are sending G.729 VAD, the next timestamp will take over anyawy */ memmove(s->data, s->data + len, s->len); if (s->delivery.tv_sec || s->delivery.tv_usec) { /* If we have delivery time, increment it, otherwise, leave it at 0 */ s->delivery.tv_sec += s->sample_queue[0] / 8000.0; s->delivery.tv_usec += (((int)(s->sample_queue[0])) % 8000) * 125; if (s->delivery.tv_usec > 1000000) { s->delivery.tv_usec -= 1000000; s->delivery.tv_sec += 1; } } } int j; s->samples -= s->sample_queue[0]; if( s->packet_index > 0 ){ for (j=0; jpacket_index -1 ; j++){ s->len_queue[j]=s->len_queue[j+1]; s->sample_queue[j]=s->sample_queue[j+1]; } s->len_queue[s->packet_index]=0; s->sample_queue[s->packet_index]=0; s->packet_index--; } else { s->len_queue[0]=0; s->sample_queue[0]=0; } /* Return frame */ return &s->f; } void ast_packer_free(struct ast_packer *s) { free(s); } 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 this member is a spyer, only queue frames from the spyee if ( ( member->spyee_channel_name != NULL && frame->member == NULL ) && ( frame->spy_partner == NULL || frame->spy_partner != member ) ) continue ; if ( frame->fr == NULL ) { ast_log( LOG_WARNING, "unknown error queueing frame for listener, frame->fr == NULL\n" ) ; continue ; } // first, try for a pre-converted frame qf = (member->listen_volume == 0 && member->spy_partner == NULL? frame->converted[ member->write_format_index ] : 0); // convert ( and store ) the frame if ( qf == NULL ) { // make a copy of the slinear version of the frame qf = ast_frdup( frame->fr ) ; if (member->listen_volume != 0) { ast_frame_adjust_volume(qf, member->listen_volume); } 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 ) ; // store the converted frame // ( the frame will be free'd next time through the loop ) if (member->listen_volume == 0 && member->spy_partner == NULL) { frame->converted[ member->write_format_index ] = qf ; } } 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 // XXX NEILS - WOULD BE FREED IN CLEANUPast_frfree( qf ) ; //qf = NULL ; } if (member->listen_volume != 0 || member->spy_partner != NULL) { // free frame ( the translator's copy ) ast_frfree( qf ) ; } } else { ast_log( LOG_WARNING, "unable to translate outgoing listener frame, channel => %s\n", member->channel_name ) ; } // 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_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 ; if ( (qf->subclass == member->write_format) && (member->listen_volume == 0) ) { // frame is already in correct format, so just queue it queue_outgoing_frame( member, qf, conf->delivery_time ) ; } else { // make a copy of the slinear version of the frame qf = ast_frdup( qf ) ; if (member->listen_volume != 0) { ast_frame_adjust_volume(frame->fr, member->listen_volume); } // // 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, 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 ) ; } } // 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_silent_frame( struct ast_conference* conf, struct ast_conf_member* member ) { int c; #ifdef APP_KONFERENCE_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_KONFERENCE_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 ; } } // 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 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 ) ; } return 0 ; } void member_process_outgoing_frames(struct ast_conference* conf, struct ast_conf_member *member, struct conf_frame *send_frames) { ast_mutex_lock(&member->lock); // skip members that are not ready if ( member->ready_for_outgoing == 0 ) { ast_mutex_unlock(&member->lock); return ; } // skip no receive audio clients if ( member->norecv_audio ) { ast_mutex_unlock(&member->lock); return; } if ( member->local_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_mutex_unlock(&member->lock); } // Functions that will increase and decrease speaker_count in a secure way, locking the member mutex if required // Will also set speaking_state flag. // Returns the previous speaking state int increment_speaker_count(struct ast_conf_member *member, int lock) { int old_state; if ( lock ) ast_mutex_lock(&member->lock); old_state = member->speaking_state; member->speaker_count++; member->speaking_state = 1; ast_log(AST_CONF_DEBUG, "Increment speaker count: id=%d, count=%d\n", member->id, member->speaker_count); // If this is a state change, update the timestamp if ( old_state == 0 ) { member->speaking_state_notify = 1; member->last_state_change = ast_tvnow(); } if ( lock ) ast_mutex_unlock(&member->lock); return old_state; } int decrement_speaker_count(struct ast_conf_member *member, int lock) { int old_state; if ( lock ) ast_mutex_lock(&member->lock); old_state = member->speaking_state; if ( member->speaker_count > 0 ) member->speaker_count--; if ( member->speaker_count == 0 ) member->speaking_state = 0; ast_log(AST_CONF_DEBUG, "Decrement speaker count: id=%d, count=%d\n", member->id, member->speaker_count); // If this is a state change, update the timestamp if ( old_state == 1 && member->speaking_state == 0 ) { member->speaking_state_notify = 1; member->last_state_change = ast_tvnow(); } if ( lock ) ast_mutex_unlock(&member->lock); return old_state; } void member_process_spoken_frames(struct ast_conference* conf, struct ast_conf_member *member, struct conf_frame **spoken_frames, long time_diff, int *listener_count, int *speaker_count ) { struct conf_frame *cfr; // acquire member mutex TIMELOG(ast_mutex_lock( &member->lock ),1,"conf thread member lock") ; // 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 || cfr->fr == NULL ) { // Decrement speaker count for us and for driven members // This happens only for the first missed frame, since we want to // decrement only on state transitions if ( member->local_speaking_state == 1 ) { decrement_speaker_count(member, 0); member->local_speaking_state = 0; // If we're driving another member, decrement its speaker count as well #ifdef VIDEO if ( member->driven_member != NULL ) decrement_speaker_count(member->driven_member, 1); #endif } // count the listeners (*listener_count)++ ; } else { // 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 ; // Increment speaker count for us and for driven members // This happens only on the first received frame, since we want to // increment only on state transitions if ( member->local_speaking_state == 0 ) { increment_speaker_count(member, 0); member->local_speaking_state = 1; #ifdef VIDEO // If we're driving another member, increment its speaker count as well if ( member->driven_member != NULL ) increment_speaker_count(member->driven_member, 1); #endif } // count the speakers (*speaker_count)++ ; } // release member mutex ast_mutex_unlock( &member->lock ) ; return; } #ifdef VIDEO int is_video_eligible(struct ast_conf_member *member) { if ( member == NULL ) return 0; return !member->no_camera && !member->mute_video && !member->via_telephone; } // Member start and stop video methods void start_video(struct ast_conf_member *member) { if ( member == NULL || member->video_started || !is_video_eligible(member)) return; #ifdef TEXT send_text_message_to_member(member, AST_CONF_CONTROL_START_VIDEO); #endif member->video_started = 1; } void stop_video(struct ast_conf_member *member) { if ( member == NULL || !member->video_started ) return; #ifdef TEXT send_text_message_to_member(member, AST_CONF_CONTROL_STOP_VIDEO); #endif member->video_started = 0; } #endif