static int start_monitor_action(struct mansession *s, struct message *m) { struct ast_channel *c = NULL; char *name = astman_get_header(m, "Channel"); char *fname = astman_get_header(m, "File"); char *format = astman_get_header(m, "Format"); char *mix = astman_get_header(m, "Mix"); char *d; if (ast_strlen_zero(name)) { astman_send_error(s, m, "No channel specified"); return 0; } c = ast_get_channel_by_name_locked(name); if (!c) { astman_send_error(s, m, "No such channel"); return 0; } if (ast_strlen_zero(fname)) { /* No filename base specified, default to channel name as per CLI */ fname = malloc (FILENAME_MAX); if (!fname) { astman_send_error(s, m, "Could not start monitoring channel"); ast_mutex_unlock(&c->lock); return 0; } memset(fname, 0, FILENAME_MAX); ast_copy_string(fname, c->name, FILENAME_MAX); /* Channels have the format technology/channel_name - have to replace that / */ if ((d=strchr(fname, '/'))) *d='-'; } if (ast_monitor_start(c, format, fname, 1)) { if (ast_monitor_change_fname(c, fname, 1)) { astman_send_error(s, m, "Could not start monitoring channel"); ast_mutex_unlock(&c->lock); return 0; } } if (ast_true(mix)) { ast_monitor_setjoinfiles(c, 1); } ast_mutex_unlock(&c->lock); astman_send_ack(s, m, "Started monitoring channel"); return 0; }
static int start_monitor_exec(struct ast_channel *chan, void *data) { char *arg = NULL; char *format = NULL; char *fname_base = NULL; char *options = NULL; char *delay = NULL; char *urlprefix = NULL; char tmp[256]; int joinfiles = 0; int waitforbridge = 0; int res = 0; /* Parse arguments. */ if (!ast_strlen_zero((char*)data)) { arg = ast_strdupa((char*)data); format = arg; fname_base = strchr(arg, '|'); if (fname_base) { *fname_base = 0; fname_base++; if ((options = strchr(fname_base, '|'))) { *options = 0; options++; if (strchr(options, 'm')) joinfiles = 1; if (strchr(options, 'b')) waitforbridge = 1; } } arg = strchr(format,':'); if (arg) { *arg++ = 0; urlprefix = arg; } } if (urlprefix) { snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base, ((strcmp(format,"gsm")) ? "wav" : "gsm")); if (!chan->cdr && !(chan->cdr = ast_cdr_alloc())) return -1; ast_cdr_setuserfield(chan, tmp); } if (waitforbridge) { /* We must remove the "b" option if listed. In principle none of the following could give NULL results, but we check just to be pedantic. Reconstructing with checks for 'm' option does not work if we end up adding more options than 'm' in the future. */ delay = ast_strdupa(data); options = strrchr(delay, '|'); if (options) { arg = strchr(options, 'b'); if (arg) { *arg = 'X'; pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay); } } return 0; } res = ast_monitor_start(chan, format, fname_base, 1); if (res < 0) res = ast_monitor_change_fname(chan, fname_base, 1); ast_monitor_setjoinfiles(chan, joinfiles); return res; }
static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message) { char *touch_filename; size_t len; int x; enum set_touch_variables_res set_touch_res; RAII_VAR(char *, touch_format, NULL, ast_free); RAII_VAR(char *, touch_monitor, NULL, ast_free); RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free); set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix); switch (set_touch_res) { case SET_TOUCH_SUCCESS: break; case SET_TOUCH_UNSET: set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix); if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) { return; } break; case SET_TOUCH_ALLOC_FAILURE: return; } if (!ast_strlen_zero(touch_monitor)) { len = strlen(touch_monitor) + 50; touch_filename = ast_alloca(len); snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long) time(NULL), touch_monitor); } else { char *caller_chan_id; char *peer_chan_id; caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid, ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan))); peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid, ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan))); len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50; touch_filename = ast_alloca(len); snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long) time(NULL), caller_chan_id, peer_chan_id); } for (x = 0; x < strlen(touch_filename); x++) { if (touch_filename[x] == '/') { touch_filename[x] = '-'; } } ast_verb(3, "AutoMonitor used to record call. Filename: %s\n", touch_filename); if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT)) { ast_verb(3, "automon feature was tried by '%s' but monitor failed to start.\n", ast_channel_name(bridge_channel->chan)); return; } if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); } if (!ast_strlen_zero(start_message)) { ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL); ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL); } pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); }
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 ; }