static int conf_play_soundfile( struct cw_conf_member *member, char * file ) { int res = 0; if ( member->dont_play_any_sound ) return 0; if ( !member->chan ) return 0; cw_stopstream(member->chan); queue_incoming_silent_frame(member,3); if ( ( strrchr(file,'/')!=NULL ) || (cw_fileexists(file, NULL, member->chan->language) > 0) ) { res = cw_streamfile(member->chan, file, member->chan->language); if (!res) { res = cw_waitstream(member->chan, CW_DIGIT_ANY); cw_stopstream(member->chan); } //cw_log(LOG_DEBUG, "Soundfile found %s - %d\n", file, cw_fileexists(file, NULL, member->chan->language) ); } else cw_log(LOG_DEBUG, "Soundfile not found %s - lang: %s\n", file, member->chan->language ); cw_set_write_format( member->chan, CW_FORMAT_SLINEAR ); return res; }
static void add_member( struct cw_conference *conf, struct cw_conf_member *member ) { char cnum[80]; if ( conf == NULL ) { cw_log( LOG_ERROR, "unable to add member to NULL conference\n" ) ; return ; } // acquire the conference lock cw_mutex_lock( &conf->lock ) ; member->next = conf->memberlist ; // next is now list conf->memberlist = member ; // member is now at head of list conf->membercount ++; // release the conference lock cw_mutex_unlock( &conf->lock ) ; if ((member->cid.cid_num != NULL) ) strncpy( cnum, member->cid.cid_num, sizeof(cnum) ); else cnum[0] = '\0'; queue_incoming_silent_frame(member,2); if(!member->beep_only_mode){ add_command_to_queue( conf, member, CONF_ACTION_QUEUE_NUMBER , 1, cnum ); add_command_to_queue( conf, member, CONF_ACTION_QUEUE_SOUND , 1, "conf-hasjoin" ); } else { add_command_to_queue( conf, member, CONF_ACTION_QUEUE_SOUND , 1, "beep" ); } cw_log( CW_CONF_DEBUG, "member added to conference, name => %s\n", conf->name ) ; manager_event( EVENT_FLAG_CALL, APP_CONFERENCE_MANID"Join", "Channel: %s\r\n", member->channel_name ) ; return ; }
int conf_play_soundqueue( struct cw_conf_member *member ) { int res = 0; cw_stopstream(member->chan); queue_incoming_silent_frame(member,3); struct cw_conf_soundq *toplay, *delitem; cw_generator_deactivate(member->chan); cw_mutex_lock(&member->lock); toplay = member->soundq; while ( ( toplay != NULL) && ( res == 0 ) ) { manager_event( EVENT_FLAG_CALL, APP_CONFERENCE_MANID"Sound", "Channel: %s\r\n" "Sound: %s\r\n", member->channel_name, toplay->name ); res = conf_play_soundfile( member, toplay->name ); if (res) break; delitem = toplay; toplay = toplay->next; member->soundq = toplay; free(delitem); } cw_generator_activate(member->chan,&membergen,member); cw_mutex_unlock(&member->lock); if (res != 0) conference_stop_sounds( member ); return res; }
/* ***************************************************************************** MANAGER UTILS ****************************************************************************/ void send_state_change_notifications( struct cw_conf_member* member ) { #if ( APP_NCONFERENCE_DEBUG == 1 ) cw_log(CW_CONF_DEBUG, "Member on channel %s. State changed to %s.\n", member->chan->name, ( ( member->is_speaking == 1 ) ? "speaking" : "silent" ) ); #endif manager_event( EVENT_FLAG_CALL, APP_CONFERENCE_MANID"State", "Channel: %s\r\n" "State: %s\r\n", member->chan->name, ( ( member->is_speaking == 1 ) ? "speaking" : "silent" ) ) ; if ( member->is_speaking == 0 ) queue_incoming_silent_frame(member,2); }
int conference_parse_admin_command(struct cw_conf_member *member) { char action; char *parameters; int res = 0; action=member->dtmf_buffer[0]; parameters=member->dtmf_buffer+1; switch (action) { case '1': queue_incoming_silent_frame( member, 2 ); res = conf_do_originate(member,parameters); break; case '4': if ( parameters[0] == '0' ) add_command_to_queue( member->conf, member, CONF_ACTION_ENABLE_SOUNDS , 0, "" ); else if ( parameters[0] == '1' ) add_command_to_queue( member->conf, member, CONF_ACTION_ENABLE_SOUNDS , 1, "" ); else conference_queue_sound( member, "beeperr" ); break; case '5': if ( parameters[0] == '0' ) add_command_to_queue( member->conf, member, CONF_ACTION_MUTE_ALL , 0, "" ); else if ( parameters[0] == '1' ) add_command_to_queue( member->conf, member, CONF_ACTION_MUTE_ALL , 1, "" ); else conference_queue_sound( member, "beeperr" ); break; case '6': if ( parameters[0] == '0' ) add_command_to_queue( member->conf, member, CONF_ACTION_PLAYMOH , 0, "" ); else if ( parameters[0] == '1' ) add_command_to_queue( member->conf, member, CONF_ACTION_PLAYMOH , 1, "" ); else conference_queue_sound( member, "beeperr" ); break; case '7': if ( parameters[0] == '0' ) { member->conf->is_locked = 0; conference_queue_sound( member, "conf-unlockednow" ); } else if ( parameters[0] == '1' ) { member->conf->is_locked = 1; conference_queue_sound( member, "conf-lockednow" ); } else { conference_queue_sound( member, "beep" ); } break; case '9': res = conference_set_pin(member,parameters); conference_queue_sound( member, "beep" ); break; case '0': if ( parameters[0] == '0' ) add_command_to_queue( member->conf, member, CONF_ACTION_HANGUP , 0, "" ); else if ( parameters[0] == '1' ) add_command_to_queue( member->conf, member, CONF_ACTION_HANGUP , 1, "" ); else if ( parameters[0] == '2' ) add_command_to_queue( member->conf, member, CONF_ACTION_HANGUP , 2, "" ); else conference_queue_sound( member, "beeperr" ); break; default: cw_log(CW_CONF_DEBUG,"Admin mode: Action: %c parameters: %s. Invalid or unknown\n", action, parameters); break; } return 1; }
static void conference_exec( struct cw_conference *conf ) { struct cw_conf_member *member, *temp_member ; struct timeval empty_start = {0,0}, tv = {0,0} ; cw_log( CW_CONF_DEBUG, "Entered conference_exec, name => %s\n", conf->name ) ; // // main conference thread loop // while ( 1 ) { //Acquire the mutex cw_mutex_lock( &conf->lock ); // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { cw_mutex_lock( &member->lock ) ; // check for dead members if ( member->remove_flag == 1 ) { char cnum[80]; cw_log( CW_CONF_DEBUG, "found member slated for removal, channel => %s\n", member->channel_name ) ; temp_member = member->next ; if (member->cid.cid_num != NULL) strncpy( cnum,member->cid.cid_num,sizeof(cnum) ); else cnum[0] = '\0'; queue_incoming_silent_frame(member,2); remove_member( conf, member ) ; member = temp_member ; if(member && member->beep_only_mode == 0){ add_command_to_queue( conf, NULL, CONF_ACTION_QUEUE_NUMBER , 1, cnum ); add_command_to_queue( conf, NULL, CONF_ACTION_QUEUE_SOUND , 1, "conf-hasleft" ); } else if (member && member->beep_only_mode == 1) { add_command_to_queue( conf, NULL, CONF_ACTION_QUEUE_SOUND , 1, "beeperr" ); } continue ; } cw_mutex_unlock( &member->lock ) ; // adjust our pointer to the next inline member = member->next ; } // // break, if we have no more members // if ( conf->memberlist == NULL ) { if ( empty_start.tv_sec == 0 ) gettimeofday( &empty_start, NULL ); if (conf->auto_destroy) { cw_log( CW_CONF_DEBUG, "removing conference, count => %d, name => %s\n", conf->membercount, conf->name ) ; remove_conf( conf ) ; // stop the conference break ; // break from main processing loop } else { //cw_log( CW_CONF_DEBUG, "Delaying conference removal. Auto destroy is not set.\n" ) ; gettimeofday( &tv, NULL ) ; if ( tv.tv_sec - empty_start.tv_sec > CW_CONF_DESTROY_TIME ) conf->auto_destroy=1; } } else { empty_start.tv_sec = 0; } // // Check for the commands to be executed by the conference // if (conf->command_queue) cw_conf_command_execute( conf ); //---------// // CLEANUP // //---------// // release conference mutex cw_mutex_unlock( &conf->lock ) ; //Sleep for 1ms (relax) usleep(1000); } // end while ( 1 ) // // exit the conference thread // cw_log( CW_CONF_DEBUG, "exit conference_exec\n" ) ; // exit the thread pthread_exit( NULL ) ; return ; }
static void cw_conf_command_execute( struct cw_conference *conf ) { struct cw_conf_command_queue *cq; struct cw_conf_member *member = NULL ; cq = get_command_from_queue(conf); if ( cq == NULL ) return; cw_log(CW_CONF_DEBUG,"Parsing Command Queue for conference %s\n",conf->name); switch (cq->command) { case CONF_ACTION_MUTE_ALL: // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { if (member != cq->issuedby) { cw_mutex_lock( &member->lock ) ; queue_incoming_silent_frame(member,2); member->talk_mute = cq->param_number; cw_log(CW_CONF_DEBUG,"(CQ) Member Talk MUTE set to %d\n",member->talk_mute); if (cq->param_number) conference_queue_sound( member, "conf-muted" ); else conference_queue_sound( member, "conf-unmuted" ); cw_mutex_unlock( &member->lock ) ; } // adjust our pointer to the next inline member = member->next ; } break; case CONF_ACTION_ENABLE_SOUNDS: // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { cw_mutex_lock( &member->lock ) ; queue_incoming_silent_frame(member,2); member->dont_play_any_sound = cq->param_number; cw_log(CW_CONF_DEBUG,"(CQ) Member Talk Disable sounds set to %d\n",member->dont_play_any_sound); cw_mutex_unlock( &member->lock ) ; // adjust our pointer to the next inline member = member->next ; } break; case CONF_ACTION_QUEUE_SOUND: // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { if (member != cq->issuedby) { cw_mutex_lock( &member->lock ) ; queue_incoming_silent_frame(member,2); if ( !(member->quiet_mode == 1 && cq->param_number) ) conference_queue_sound( member, cq->param_text ); cw_mutex_unlock( &member->lock ) ; // adjust our pointer to the next inline } member = member->next ; } break; case CONF_ACTION_QUEUE_NUMBER: // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { if (member != cq->issuedby) { cw_mutex_lock( &member->lock ) ; queue_incoming_silent_frame(member,2); if ( !(member->quiet_mode == 1 && cq->param_number) ) conference_queue_number( member, cq->param_text ); cw_mutex_unlock( &member->lock ) ; // adjust our pointer to the next inline } member = member->next ; } break; case CONF_ACTION_PLAYMOH: // get list of conference members member = conf->memberlist ; // loop over member list to retrieve queued frames while ( member != NULL ) { if (member != cq->issuedby) {; cw_mutex_lock( &member->lock ) ; if ( cq->param_number == 1) { member->force_on_hold = 1; } else { member->force_on_hold = -1; } cw_mutex_unlock( &member->lock ) ; cw_log(CW_CONF_DEBUG,"(CQ) Member: playing moh set to %d\n",cq->param_number); // adjust our pointer to the next inline } member = member->next ; } break; case CONF_ACTION_HANGUP: member = conf->memberlist ; // Scan all the members while ( member != NULL ) { // If it's not me and we don't have to kick all members if (member != cq->issuedby) { cw_mutex_lock( &member->lock ) ; queue_incoming_silent_frame(member,2); if (cq->param_number == 0) conference_queue_sound( member, "goodbye" ); else conference_queue_sound( member, "conf-kicked" ); member->force_remove_flag = 1; cw_log(CW_CONF_DEBUG,"(CQ) Conf %s Member Kicked: %s\n",conf->name, member->channel_name); cw_mutex_unlock( &member->lock ) ; if (cq->param_number == 1) break; } // adjust our pointer to the next inline member = member->next ; } break; default: cw_log( LOG_WARNING, "Conference %s : don't know how to execute command %d\n", conf->name, cq->command) ; break; } // Free the struct we got free(cq); }
int member_exec( struct cw_channel* chan, int argc, char **argv ) { int left = 0 ; int res; struct cw_conference *conf = NULL; struct cw_conf_member *member = NULL; struct cw_frame *f = NULL; cw_log( CW_CONF_DEBUG, "Launching NConference %s\n", "$Revision: 5349 $" ) ; if (chan->_state != CW_STATE_UP) if ( (res = cw_answer( chan )) ) { cw_log( LOG_ERROR, "unable to answer call\n" ) ; return -1 ; } member = create_member( chan, argc, argv ) ; // unable to create member, return an error if ( member == NULL ) { cw_log( LOG_ERROR, "unable to create member\n" ) ; return -1 ; } // // setup CallWeaver read/write formats // cw_log(CW_CONF_DEBUG, "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n", chan->name ? chan->name : "----", chan->cid.cid_dnid ? chan->cid.cid_dnid : "----", chan->cid.cid_num ? chan->cid.cid_num : "----", chan->cid.cid_ani ? chan->cid.cid_ani : "----"); cw_log(CW_CONF_DEBUG, "CHANNEL CODECS, CHANNEL => %s, NATIVE => %d, READ => %d, WRITE => %d\n", chan->name, chan->nativeformats, member->read_format, member->write_format); if ( cw_set_read_format( chan, member->read_format ) < 0 ) { cw_log( LOG_ERROR, "unable to set read format.\n" ) ; delete_member( member ) ; return -1 ; } // for right now, we'll send everything as slinear if ( cw_set_write_format( chan, member->write_format ) < 0 ) { cw_log( LOG_ERROR, "unable to set write format.\n" ) ; delete_member( member ) ; return -1 ; } // // setup a conference for the new member // conf = start_conference( member ) ; if ( conf == NULL ) { cw_log( LOG_ERROR, "unable to setup member conference\n" ) ; delete_member( member) ; return -1 ; } else { if (conf->is_locked && (member->type != MEMBERTYPE_MASTER) ) { if ( strcmp(conf->pin,member->pin) ) { conference_queue_sound(member,"conf-locked"); conf_play_soundqueue( member ); member->force_remove_flag = 1 ; } } else { member->conf = conf; if ( member->type == MEMBERTYPE_MASTER ) conf->auto_destroy = member->auto_destroy; } } if ( member->type == MEMBERTYPE_MASTER ) { conf->auto_destroy = member->auto_destroy; if ( strlen( member->pin ) > 0 ) { strncpy(conf->pin,member->pin,sizeof(conf->pin)); cw_log( CW_CONF_DEBUG, "Conference pin set to => %s\n", conf->pin ) ; } } // // process loop for new member ( this runs in it's own thread // cw_log( CW_CONF_DEBUG, "begin member event loop, channel => %s\n", chan->name ) ; // Activate the generator for the channel. res = cw_conf_member_genactivate( member ); if ( !res ) { member->force_remove_flag = 1; cw_log( CW_CONF_DEBUG, "member marked for removal => %s\n", chan->name ) ; } //Play the join info messages if (!member->force_remove_flag && !member->quiet_mode) { conference_queue_sound( member, "conf-youareinconfnum" ); conference_queue_number( member, member->id ); } // The member at the very beginningis speaking member->is_speaking = 1 ; // tell conference_exec we're ready for frames member->active_flag = 1 ; //Main loop. while ( !member->force_remove_flag || member->soundq != NULL ) { usleep(1000); // make sure we have a channel to process if ( chan == NULL ) { cw_log( LOG_NOTICE, "member channel has closed\n" ) ; break ; } //-----------------// // INCOMING FRAMES // //-----------------// if ( member->force_remove_flag == 1 ) { // break to the loop break; } // wait for an event on this channel int waittime = ( member->framelen == 0 ) ? CW_CONF_WAITFOR_TIME : member->framelen; left = cw_waitfor( chan, waittime ) ; f = NULL; if ( left < 0 ) { // an error occured cw_log( LOG_NOTICE, "an error occured waiting for a frame, channel => %s, error => %d\n", chan->name, left ) ; } else if ( left == 0 ) { // No frame avalaible member->lostframecount ++; // We have lost a frame. // In this case, we queue some silence // Sothat if we keep loosing frames, // there will be no glitching in the conference. // Set the speaking state to 0. if ( member->lostframecount == 1 ) { queue_incoming_silent_frame(member,1); } member->is_speaking = 0; } else if ( left > 0 ) { // a frame has come in before the latency timeout // was reached, so we process the frame // let's reset the lost frame count if ( member->lostframecount ) { member->lostframecount = 0; // If vad is not enabled, then set the speaking state back to 1 if ( !member->enable_vad ) member->is_speaking = 1; } f = cw_read( chan ) ; if ( f == NULL ) { cw_log( CW_CONF_DEBUG, "unable to read from channel, channel => %s. Got Hangup.\n", chan->name ) ; queue_incoming_silent_frame(member,5); member->is_speaking = 0; break ; } else { /* cw_log( CW_CONF_DEBUG, "Read (PRE dsp), channel => %s, datalen: %d samplefreq: %ld len: %ld samples %d class: %d\n", chan->name, f->datalen, member->samplefreq, f->len, f->samples, f->subclass) ; */ if ( member->samplefreq == 0 && f->samples != 0 ) { if ( ( f->len == 0 ) && ( f->datalen == 320 ) && ( f->samples == 160 ) ) member->framelen = 20; // This is probably chan_zap not setting the correct len. else member->framelen = f->len; // frame length in milliseconds member->datalen = f->datalen; // frame length in milliseconds member->samples = f->samples; // number of samples in framelen member->samplefreq = (int)(member->samples/member->framelen)*1000; // calculated sample frequency cw_log( CW_CONF_DEBUG, "MEMBER FRAME DATA: datalen %d samples %d len(ms) %ld, offset: %d \n", f->datalen, f->samples, f->len, f->offset ); /* // Try to initialize the smoother, only once queue_incoming_silent_frame(member); if ( member->smooth_size_in < 0 ) { member->smooth_size_in = f->samples ; cw_log( CW_CONF_DEBUG, "Initializing Smooother.\n"); member->inSmoother = cw_smoother_new(member->smooth_size_in); if ( member->inSmoother == NULL ) cw_log( CW_CONF_DEBUG, "Smoother initialization failed\n"); } */ } if ( ( (member->framelen != f->len ) && ( f->len !=0 ) ) || ( (member->samples != f->samples ) && ( f->samples !=0 ) && ( f->len !=0 ) ) ) { cw_log( CW_CONF_DEBUG, "FRAME CHANGE : samples %d len(ms) %ld\n", f->samples, f->len ); cw_log( CW_CONF_DEBUG, "FRAME SHOULDBE: samples %d len(ms) %ld\n", member->samples, member->framelen ); if (member->samples == 0 ) { member->framelen = f->len; // frame length in milliseconds member->datalen = f->datalen; // frame length in milliseconds member->samples = f->samples; // number of samples in framelen member->samplefreq = (int) ( f->samples/f->len)*1000; // calculated sample frequency } } // This fix is for chan_zap // Chan_zap NEVER sets framelen value. // Probably when adding support to 16Khz we should add a check for this. if ( ( member->framelen == 0 ) && ( member->datalen == 320 ) && ( member->samples == 160 ) ) member->framelen = 20; } } // actually process the frame. res = process_incoming(member, f); if (member->force_remove_flag) member->remove_flag = 1 ; } // // clean up // if ( member != NULL ) member->remove_flag = 1 ; cw_log( CW_CONF_DEBUG, "end member event loop, time_entered => %ld - removal: %d\n", member->time_entered.tv_sec, member->remove_flag ) ; //cw_log( CW_CONF_DEBUG, "Deactivating generator - Channel => %s\n", member->chan->name ) ; cw_generator_deactivate(chan); return -1 ; }
static int process_incoming(struct cw_conf_member *member, struct cw_frame *f) { int res; // Play our sound queue, if not empty. if (member->soundq) { // Free the frame. if ( f != NULL ) { cw_fr_free( f ) ; } res = conf_play_soundqueue( member ); if (res != 0) { queue_incoming_silent_frame(member,2); // send the DTMF event to the MGR interface.. manager_event( EVENT_FLAG_CALL, APP_CONFERENCE_MANID"DTMF", "Channel: %s\r\n" "Key: %c\r\n", member->channel_name, res ) ; parse_dtmf_option( member, res); } return res; } // // Moderator forces MOH management // if ( member->force_on_hold == 1 ) { cw_moh_start(member->chan,""); member->force_on_hold = 0 ; } else if ( member->force_on_hold == -1 ) { cw_moh_stop(member->chan); cw_generator_activate(member->chan,&membergen,member); member->force_on_hold = 0 ; } // // MOH When the user is alone in the conference // if ( member->conf->membercount == 1 && member->is_on_hold == 0 && member->skip_moh_when_alone == 0 ) { cw_moh_start(member->chan,""); member->is_on_hold = 1 ; return 0; } if ( member->conf->membercount > 1 && member->is_on_hold == 1 && member->skip_moh_when_alone == 0 ) { cw_moh_stop(member->chan); cw_generator_activate(member->chan,&membergen,member); member->is_on_hold = 0 ; return 0; } if ( member->force_remove_flag == 1 ) { return 0; } // If we don't have any frame to parse, then return if ( f == NULL ) { return 0; } // Actions based on the content of the frame if ( f->frametype == CW_FRAME_DTMF && member->manage_dtmf ) { queue_incoming_silent_frame(member,2); // send the DTMF event to the MGR interface.. manager_event( EVENT_FLAG_CALL, APP_CONFERENCE_MANID"DTMF", "Channel: %s\r\n" "Key: %c\r\n", member->channel_name, f->subclass ) ; parse_dtmf_option( member, f->subclass); cw_fr_free(f); } else if ( (member->type == MEMBERTYPE_LISTENER) || (member->talk_mute) ) { // this is a listen-only user, or it's muted. // Ignore the frame cw_fr_free( f ) ; } else if ( f->frametype == CW_FRAME_VOICE ) { // ********************************************************************************** VOICE int old_speaking_state = member->is_speaking; #if ENABLE_VAD if ( member->talk_mute == 1 ) member->is_speaking = 0; if ( member->enable_vad && f->subclass == CW_FORMAT_SLINEAR && f->samples > 0 ) { // and if it's time to check what the member is doing if ( member->skip_voice_detection <= 0 || member->is_speaking ) { int rees; rees = vad_is_talk( f->data, f->datalen, &member->silence_nr, 20); // send the frame to the preprocessor if ( rees != 0 ) { // voice detected, reset skip count if ( member->framelen != 0 ) member->skip_voice_detection = (CW_CONF_SKIP_MS_AFTER_VOICE_DETECTION / member->framelen); else // Let's suppose that 20ms as a framelen is not too different from the real situation member->skip_voice_detection = 20; member->is_speaking=1; } else { // member is silent member->is_speaking=0; if ( member->framelen != 0 ) member->skip_voice_detection = ( CW_CONF_SKIP_MS_WHEN_SILENT / member->framelen ); else member->skip_voice_detection = 5; } } --member->skip_voice_detection ; } if (old_speaking_state != member ->is_speaking) send_state_change_notifications(member); #endif // volume of the frame is modified after the VAD has been done if (member->talk_volume != 0) set_talk_volume(member, f, 1); if ( member->is_speaking && queue_incoming_frame( member, f ) != 0 ) cw_log( CW_CONF_DEBUG, "dropped incoming frame, channel => %s\n", member->channel_name ) ; // free the original frame cw_fr_free( f ) ; } else if ( f->frametype == CW_FRAME_CONTROL && f->subclass == CW_CONTROL_HANGUP ) { // hangup received, queue silence && free the frame queue_incoming_silent_frame(member,2); cw_fr_free( f ) ; } else { // Unmanaged frame #if ( APP_NCONFERENCE_DEBUG == 1 ) cw_log(LOG_WARNING,"Freeing unknown frame: type %d member %s \n", f->frametype, member->chan->name ); #endif cw_fr_free( f ) ; } return 0; }
int member_exec( struct ast_channel* chan, void* data ) { int left = 0 ; int res; struct ast_conference *conf = NULL; struct ast_conf_member *member = NULL; struct ast_frame *f = NULL; struct ast_app *app = NULL; ast_log( AST_CONF_DEBUG, "Launching NConference %s\n", "$Revision: 2325 $" ) ; // make sure we have a channel to process if ( chan == NULL ) { ast_log( LOG_NOTICE, "member channel has closed\n" ) ; return -1 ; } if (chan->_state != AST_STATE_UP) if ( (res = ast_answer( chan )) ) { ast_log( LOG_ERROR, "unable to answer call\n" ) ; return -1 ; } member = create_member( chan, (const char*)( data ) ) ; // unable to create member, return an error if ( member == NULL ) { ast_log( LOG_ERROR, "unable to create member\n" ) ; return -1 ; } // // setup Openpbx read/write formats // //ast_log( AST_CONF_DEBUG, // "CHANNEL INFO, CHANNEL => %s, DNID => %s, CALLER_ID => %s, ANI => %s\n", // chan->name, chan->cid.cid_dnid, chan->cid.cid_num, chan->cid.cid_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 ) ; if ( ast_set_read_format( chan, member->read_format ) < 0 ) { ast_log( LOG_ERROR, "unable to set read format.\n" ) ; delete_member( member ) ; return -1 ; } // for right now, we'll send everything as slinear if ( ast_set_write_format( chan, member->write_format ) < 0 ) { ast_log( LOG_ERROR, "unable to set write format.\n" ) ; delete_member( member ) ; return -1 ; } // // setup a conference for the new member // conf = start_conference( member ) ; if ( conf == NULL ) { ast_log( LOG_ERROR, "unable to setup member conference\n" ) ; delete_member( member) ; return -1 ; } else { if (conf->is_locked && (member->type != MEMBERTYPE_MASTER) ) { if (strlen(conf->pin) == 0 || strncmp(conf->pin,member->pin,sizeof(conf->pin)) ) { /* Conference is Locked and an invalid PIN was entered */ remove_member(conf, member); return -2 ; } } else { member->conf = conf; if ( member->type == MEMBERTYPE_MASTER ) conf->auto_destroy = member->auto_destroy; } } if ( member->type == MEMBERTYPE_MASTER ) { conf->auto_destroy = member->auto_destroy; if ( strlen( member->pin ) > 0 ) { strncpy(conf->pin,member->pin,sizeof(conf->pin)); ast_log( AST_CONF_DEBUG, "Conference pin set to => %s\n", conf->pin ) ; } } // // 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 ) ; // Add user to monitor if (member->monitor) { const char * const monitorfilename = pbx_builtin_getvar_helper(chan, "MONITOR_FILENAME"); if (monitorfilename) { ast_monitor_start(chan, NULL, monitorfilename, 1); } else if (chan->cdr) { ast_monitor_start(chan, NULL, chan->cdr->uniqueid, 1); } else { char tmpid[256]; snprintf(tmpid, sizeof(tmpid), "chan-%x", rand()); ast_monitor_start(chan, NULL, tmpid, 1); } if (member->monitor_join) { ast_monitor_setjoinfiles(chan, 1); } } // Run AGI script if (member->agi) { char * agi = pbx_builtin_getvar_helper(chan, "AGI_CONF_JOIN"); if (agi) { app = pbx_findapp("agi"); if (app) { pbx_exec(chan, app, agi, 1); } } else { ast_log(LOG_WARNING, "AGI requested, but AGI_CONF_JOIN missing.\n"); } conf->agi = 1 ; } // Activate the generator for the channel. res = ast_conf_member_genactivate( member ); if ( !res ) { member->force_remove_flag = 1; ast_log( AST_CONF_DEBUG, "member marked for removal => %s\n", chan->name ) ; } //Play the join info messages if (!member->force_remove_flag && !member->quiet_mode && !member->mute_incoming_sounds) { if (ast_strlen_zero(member->intro_sounds)) { conference_queue_sound( member, "conf-youareinconfnum" ); conference_queue_number( member, member->id ); } } // The member at the very beginningis speaking member->is_speaking = 1 ; // tell conference_exec we're ready for frames member->active_flag = 1 ; //Main loop. while ( !member->force_remove_flag || member->soundq != NULL ) { #ifdef SOLARIS struct timespec rqtp; rqtp.tv_sec = 0; rqtp.tv_nsec = 1000000; if (nanosleep(&rqtp, (struct timespec *) NULL) == -1) { ast_log(LOG_NOTICE, "Nanosleep timer errored.\n"); } #else usleep(1000); #endif //-----------------// // INCOMING FRAMES // //-----------------// if ( member->force_remove_flag == 1 ) { // break to the loop break; } // wait for an event on this channel int waittime = ( member->framelen == 0 ) ? AST_CONF_WAITFOR_TIME : member->framelen; left = ast_waitfor( chan, waittime ) ; 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 ) ; } else if ( left == 0 ) { // No frame avalaible member->lostframecount ++; // We have lost a frame. // In this case, we queue some silence // Sothat if we keep loosing frames, // there will be no glitching in the conference. // Set the speaking state to 0. if ( member->lostframecount == 1 ) { queue_incoming_silent_frame(member,1); } member->is_speaking = 0; } else if ( left > 0 ) { // a frame has come in before the latency timeout // was reached, so we process the frame // let's reset the lost frame count if ( member->lostframecount ) { member->lostframecount = 0; // If vad is not enabled, then set the speaking state back to 1 if ( !member->enable_vad ) member->is_speaking = 1; } f = ast_read( chan ) ; if ( f == NULL ) { ast_log( AST_CONF_DEBUG, "unable to read from channel, channel => %s. Got Hangup.\n", chan->name ) ; queue_incoming_silent_frame(member,5); member->is_speaking = 0; break ; } else { /* ast_log( AST_CONF_DEBUG, "Read (PRE dsp), channel => %s, datalen: %d samplefreq: %ld len: %ld samples %d class: %d\n", chan->name, f->datalen, member->samplefreq, f->len, f->samples, f->subclass) ; */ #if 0 == 1 if ( member->samplefreq == 0 && f->samples != 0 ) { if ( ( f->len == 0 ) && ( f->datalen == 320 ) && ( f->samples == 160 ) ) member->framelen = 20; // This is probably chan_zap not setting the correct len. else member->framelen = f->len; // frame length in milliseconds member->datalen = f->datalen; // frame length in milliseconds member->samples = f->samples; // number of samples in framelen member->samplefreq = (int)(member->samples/member->framelen)*1000; // calculated sample frequency ast_log( AST_CONF_DEBUG, "MEMBER FRAME DATA: datalen %d samples %d len(ms) %ld, offset: %d \n", f->datalen, f->samples, f->len, f->offset ); } if ( ( (member->framelen != f->len ) && ( f->len !=0 ) ) || ( (member->samples != f->samples ) && ( f->samples !=0 ) && ( f->len !=0 ) ) ) { ast_log( AST_CONF_DEBUG, "FRAME CHANGE : samples %d len(ms) %ld\n", f->samples, f->len ); ast_log( AST_CONF_DEBUG, "FRAME SHOULDBE: samples %d len(ms) %ld\n", member->samples, member->framelen ); if (member->samples == 0 ) { member->framelen = f->len; // frame length in milliseconds member->datalen = f->datalen; // frame length in milliseconds member->samples = f->samples; // number of samples in framelen member->samplefreq = (int) ( f->samples/f->len)*1000; // calculated sample frequency } } #endif member->framelen = f->datalen; // This fix is for chan_zap // Chan_zap NEVER sets framelen value. // Probably when adding support to 16Khz we should add a check for this. if ( ( member->framelen == 0 ) && ( member->datalen == 320 ) && ( member->samples == 160 ) ) member->framelen = 20; } } // actually process the frame. res = process_incoming(member, f); if (member->force_remove_flag) member->remove_flag = 1 ; } // Run AGI script if (member->agi) { char * agi = pbx_builtin_getvar_helper(chan, "AGI_CONF_LEAVE"); if (agi) { app = pbx_findapp("agi"); if (app) { pbx_exec(chan, app, agi, 1); } } else { ast_log(LOG_WARNING, "AGI requested, but AGI_CONF_LEAVE missing.\n"); } } if (conf->agi) handle_conf_agi_end(conf->name, member); member->remove_flag = 1 ; ast_log( AST_CONF_DEBUG, "end member event loop, time_entered => %ld - removal: %d\n", member->time_entered.tv_sec, member->remove_flag ) ; //ast_log( AST_CONF_DEBUG, "Deactivating generator - Channel => %s\n", member->chan->name ) ; ast_generator_deactivate(chan); if (member->hangup_go_on) { ast_log(AST_CONF_DEBUG, "Hangup go on!\n"); return 0; } return -1 ; }