static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag) { /* Initialize the file if not already done so */ char *ext = NULL; char *last_slash = NULL; if (!ast_strlen_zero(filename)) { if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) { *oflags = O_CREAT | O_WRONLY; *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; last_slash = strrchr(filename, '/'); if ((ext = strrchr(filename, '.')) && (ext > last_slash)) { *(ext++) = '\0'; } else { ext = "raw"; } if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) { ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext); *errflag = 1; } else { struct ast_filestream *tmp = *fs; mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format)); } } } }
/*! \brief Start monitoring a channel * \param chan ast_channel struct to record * \param format_spec file format to use for recording * \param fname_base filename base to record to * \param need_lock whether to lock the channel mutex * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used * Creates the file to record, if no format is specified it assumes WAV * It also sets channel variable __MONITORED=yes * \retval 0 on success * \retval -1 on failure */ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action) { int res = 0; LOCK_IF_NEEDED(chan, need_lock); if (!(ast_channel_monitor(chan))) { struct ast_channel_monitor *monitor; char *channel_name, *p; /* Create monitoring directory if needed */ ast_mkdir(ast_config_AST_MONITOR_DIR, 0777); if (!(monitor = ast_calloc(1, sizeof(*monitor)))) { UNLOCK_IF_NEEDED(chan, need_lock); return -1; } /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR; const char *absolute_suffix = *fname_base == '/' ? "" : "/"; snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in", absolute, absolute_suffix, fname_base); snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out", absolute, absolute_suffix, fname_base); snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s", absolute, absolute_suffix, fname_base); /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = ast_strdupa(monitor->filename_base); ast_mkdir(dirname(name), 0777); } } else { ast_mutex_lock(&monitorlock); snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu", ast_config_AST_MONITOR_DIR, seq); snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu", ast_config_AST_MONITOR_DIR, seq); seq++; ast_mutex_unlock(&monitorlock); /* Replace all '/' chars from the channel name with '-' chars. */ channel_name = ast_strdupa(ast_channel_name(chan)); for (p = channel_name; (p = strchr(p, '/')); ) { *p = '-'; } snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s", ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name); monitor->filename_changed = 1; } monitor->stop = ast_monitor_stop; /* Determine file format */ if (!ast_strlen_zero(format_spec)) { monitor->format = ast_strdup(format_spec); } else { monitor->format = ast_strdup("wav"); } /* open files */ if (stream_action & X_REC_IN) { if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) ast_filedelete(monitor->read_filename, NULL); if (!(monitor->read_stream = ast_writefile(monitor->read_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->read_filename); ast_free(monitor); UNLOCK_IF_NEEDED(chan, need_lock); return -1; } } else monitor->read_stream = NULL; if (stream_action & X_REC_OUT) { if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) { ast_filedelete(monitor->write_filename, NULL); } if (!(monitor->write_stream = ast_writefile(monitor->write_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->write_filename); if (monitor->read_stream) { ast_closestream(monitor->read_stream); } ast_free(monitor); UNLOCK_IF_NEEDED(chan, need_lock); return -1; } } else monitor->write_stream = NULL; ast_channel_monitor_set(chan, monitor); ast_monitor_set_state(chan, AST_MONITOR_RUNNING); /* so we know this call has been monitored in case we need to bill for it or something */ pbx_builtin_setvar_helper(chan, "__MONITORED","true"); ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart", "Channel: %s\r\n" "Uniqueid: %s\r\n", ast_channel_name(chan), ast_channel_uniqueid(chan)); } else { ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan)); res = -1; } UNLOCK_IF_NEEDED(chan, need_lock); return res; }
/*! * \brief Convert a file from one format to another * \param e CLI entry * \param cmd command number * \param a list of cli arguments * \retval CLI_SUCCESS on success. * \retval CLI_SHOWUSAGE or CLI_FAILURE on failure. */ static char *handle_cli_file_convert(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *ret = CLI_FAILURE; struct ast_filestream *fs_in = NULL, *fs_out = NULL; struct ast_frame *f; struct timeval start; int cost; char *file_in = NULL, *file_out = NULL; char *name_in, *ext_in, *name_out, *ext_out; switch (cmd) { case CLI_INIT: e->command = "file convert"; e->usage = "Usage: file convert <file_in> <file_out>\n" " Convert from file_in to file_out. If an absolute path\n" " is not given, the default Asterisk sounds directory\n" " will be used.\n\n" " Example:\n" " file convert tt-weasels.gsm tt-weasels.ulaw\n"; return NULL; case CLI_GENERATE: return NULL; } /* ugly, can be removed when CLI entries have ast_module pointers */ ast_module_ref(ast_module_info->self); if (a->argc != 4 || ast_strlen_zero(a->argv[2]) || ast_strlen_zero(a->argv[3])) { ret = CLI_SHOWUSAGE; goto fail_out; } file_in = ast_strdupa(a->argv[2]); file_out = ast_strdupa(a->argv[3]); if (split_ext(file_in, &name_in, &ext_in)) { ast_cli(a->fd, "'%s' is an invalid filename!\n", a->argv[2]); goto fail_out; } if (!(fs_in = ast_readfile(name_in, ext_in, NULL, O_RDONLY, 0, 0))) { ast_cli(a->fd, "Unable to open input file: %s\n", a->argv[2]); goto fail_out; } if (split_ext(file_out, &name_out, &ext_out)) { ast_cli(a->fd, "'%s' is an invalid filename!\n", a->argv[3]); goto fail_out; } if (!(fs_out = ast_writefile(name_out, ext_out, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) { ast_cli(a->fd, "Unable to open output file: %s\n", a->argv[3]); goto fail_out; } start = ast_tvnow(); while ((f = ast_readframe(fs_in))) { if (ast_writestream(fs_out, f)) { ast_frfree(f); ast_cli(a->fd, "Failed to convert %s.%s to %s.%s!\n", name_in, ext_in, name_out, ext_out); goto fail_out; } ast_frfree(f); } cost = ast_tvdiff_ms(ast_tvnow(), start); ast_cli(a->fd, "Converted %s.%s to %s.%s in %dms\n", name_in, ext_in, name_out, ext_out, cost); ret = CLI_SUCCESS; fail_out: if (fs_out) { ast_closestream(fs_out); if (ret != CLI_SUCCESS) ast_filedelete(name_out, ext_out); } if (fs_in) ast_closestream(fs_in); ast_module_unref(ast_module_info->self); return ret; }
/* Start monitoring a channel */ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock) { int res = 0; char tmp[256]; LOCK_IF_NEEDED(chan, need_lock); if (!(chan->monitor)) { struct ast_channel_monitor *monitor; char *channel_name, *p; /* Create monitoring directory if needed */ if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) { if (errno != EEXIST) { ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n", strerror(errno)); } } if (!(monitor = ast_calloc(1, sizeof(*monitor)))) { UNLOCK_IF_NEEDED(chan, need_lock); return -1; } /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = strdup(fname_base); snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name)); free(name); ast_safe_system(tmp); } snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base); snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base); ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base)); } else { ast_mutex_lock(&monitorlock); snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld", ast_config_AST_MONITOR_DIR, seq); snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld", ast_config_AST_MONITOR_DIR, seq); seq++; ast_mutex_unlock(&monitorlock); channel_name = ast_strdupa(chan->name); while ((p = strchr(channel_name, '/'))) { *p = '-'; } snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s", ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name); monitor->filename_changed = 1; } monitor->stop = ast_monitor_stop; /* Determine file format */ if (!ast_strlen_zero(format_spec)) { monitor->format = strdup(format_spec); } else { monitor->format = strdup("wav"); } /* open files */ if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) { ast_filedelete(monitor->read_filename, NULL); } if (!(monitor->read_stream = ast_writefile(monitor->read_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->read_filename); free(monitor); ast_channel_unlock(chan); return -1; } if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) { ast_filedelete(monitor->write_filename, NULL); } if (!(monitor->write_stream = ast_writefile(monitor->write_filename, monitor->format, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_log(LOG_WARNING, "Could not create file %s\n", monitor->write_filename); ast_closestream(monitor->read_stream); free(monitor); ast_channel_unlock(chan); return -1; } chan->monitor = monitor; ast_monitor_set_state(chan, AST_MONITOR_RUNNING); /* so we know this call has been monitored in case we need to bill for it or something */ pbx_builtin_setvar_helper(chan, "__MONITORED","true"); } else { ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n", chan->name); res = -1; } UNLOCK_IF_NEEDED(chan, need_lock); return res; }
/*! \brief Convert a file from one format to another */ static int cli_audio_convert(int fd, int argc, char *argv[]) { int ret = RESULT_FAILURE; struct ast_filestream *fs_in = NULL, *fs_out = NULL; struct ast_frame *f; struct timeval start; int cost; char *file_in = NULL, *file_out = NULL; char *name_in, *ext_in, *name_out, *ext_out; ast_atomic_fetchadd_int(&me->usecnt, +1); if (argc != 3 || ast_strlen_zero(argv[1]) || ast_strlen_zero(argv[2])) { ret = RESULT_SHOWUSAGE; goto fail_out; } file_in = ast_strdupa(argv[1]); file_out = ast_strdupa(argv[2]); if (split_ext(file_in, &name_in, &ext_in)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[1]); goto fail_out; } if (!(fs_in = ast_readfile(name_in, ext_in, NULL, O_RDONLY, 0, 0))) { ast_cli(fd, "Unable to open input file: %s\n", argv[1]); goto fail_out; } if (split_ext(file_out, &name_out, &ext_out)) { ast_cli(fd, "'%s' is an invalid filename!\n", argv[2]); goto fail_out; } if (!(fs_out = ast_writefile(name_out, ext_out, NULL, O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) { ast_cli(fd, "Unable to open output file: %s\n", argv[2]); goto fail_out; } start = ast_tvnow(); while ((f = ast_readframe(fs_in))) { if (ast_writestream(fs_out, f)) { ast_cli(fd, "Failed to convert %s.%s to %s.%s!\n", name_in, ext_in, name_out, ext_out); goto fail_out; } } cost = ast_tvdiff_ms(ast_tvnow(), start); ast_cli(fd, "Converted %s.%s to %s.%s in %dms\n", name_in, ext_in, name_out, ext_out, cost); ret = RESULT_SUCCESS; fail_out: if (fs_out) { ast_closestream(fs_out); if (ret != RESULT_SUCCESS) ast_filedelete(name_out, ext_out); } if (fs_in) ast_closestream(fs_in); ast_atomic_fetchadd_int(&me->usecnt, -1); return ret; }
static void *mixmonitor_thread(void *obj) { struct mixmonitor *mixmonitor = obj; struct ast_filestream **fs = NULL; unsigned int oflags; char *ext; int errflag = 0; ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name); fs = &mixmonitor->mixmonitor_ds->fs; /* The audiohook must enter and exit the loop locked */ ast_audiohook_lock(&mixmonitor->audiohook); while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) { struct ast_frame *fr = NULL; ast_audiohook_trigger_wait(&mixmonitor->audiohook); if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) break; if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) continue; /* audiohook lock is not required for the next block. * Unlock it, but remember to lock it before looping or exiting */ ast_audiohook_unlock(&mixmonitor->audiohook); ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) { /* Initialize the file if not already done so */ if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) { oflags = O_CREAT | O_WRONLY; oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; if ((ext = strrchr(mixmonitor->filename, '.'))) *(ext++) = '\0'; else ext = "raw"; if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) { ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); errflag = 1; } } /* Write out the frame(s) */ if (*fs) { struct ast_frame *cur; for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) { ast_writestream(*fs, cur); } } } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); /* All done! free it. */ ast_frame_free(fr, 0); ast_audiohook_lock(&mixmonitor->audiohook); } ast_audiohook_unlock(&mixmonitor->audiohook); /* Datastore cleanup. close the filestream and wait for ds destruction */ ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds); if (!mixmonitor->mixmonitor_ds->destruction_ok) { ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); /* kill the audiohook */ destroy_monitor_audiohook(mixmonitor); if (mixmonitor->post_process) { ast_verb(2, "Executing [%s]\n", mixmonitor->post_process); ast_safe_system(mixmonitor->post_process); } ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name); mixmonitor_free(mixmonitor); return NULL; }
static void *mixmonitor_thread(void *obj) { struct mixmonitor *mixmonitor = obj; struct ast_filestream *fs = NULL; unsigned int oflags; char *ext; int errflag = 0; if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); ast_audiohook_lock(&mixmonitor->audiohook); while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { struct ast_frame *fr = NULL; ast_audiohook_trigger_wait(&mixmonitor->audiohook); if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) break; if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) continue; ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) { ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); /* Initialize the file if not already done so */ if (!fs && !errflag) { oflags = O_CREAT | O_WRONLY; oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; if ((ext = strrchr(mixmonitor->filename, '.'))) *(ext++) = '\0'; else ext = "raw"; if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) { ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); errflag = 1; } } /* Write out the frame */ if (fs) ast_writestream(fs, fr); } else { ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } /* All done! free it. */ ast_frame_free(fr, 0); } ast_audiohook_detach(&mixmonitor->audiohook); ast_audiohook_unlock(&mixmonitor->audiohook); ast_audiohook_destroy(&mixmonitor->audiohook); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); if (fs) ast_closestream(fs); if (mixmonitor->post_process) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); ast_safe_system(mixmonitor->post_process); } ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!mixmonitor->mixmonitor_ds->destruction_ok) { ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock); ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition); ast_free(mixmonitor->mixmonitor_ds); free(mixmonitor); return NULL; }
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence) { int d = 0; char *fmts; char comment[256]; int x, fmtcnt=1, res=-1,outmsg=0; struct ast_frame *f; struct ast_filestream *others[MAX_OTHER_FORMATS]; struct ast_filestream *realfiles[MAX_OTHER_FORMATS]; char *sfmt[MAX_OTHER_FORMATS]; char *stringp=NULL; time_t start, end; struct ast_dsp *sildet; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int gotsilence = 0; /* did we timeout for silence? */ int rfmt=0; char prependfile[80]; if (silencethreshold < 0) silencethreshold = global_silence_threshold; if (maxsilence < 0) maxsilence = global_maxsilence; /* barf if no pointer passed to store duration in */ if (duration == NULL) { ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n"); return -1; } ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt); snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name); if (playfile || beep) { if (!beep) d = ast_play_and_wait(chan, playfile); if (d > -1) d = ast_streamfile(chan, "beep",chan->language); if (!d) d = ast_waitstream(chan,""); if (d < 0) return -1; } strncpy(prependfile, recordfile, sizeof(prependfile) -1); strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1); fmts = ast_strdupa(fmt); stringp=fmts; strsep(&stringp, "|"); ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts); sfmt[0] = ast_strdupa(fmts); while((fmt = strsep(&stringp, "|"))) { if (fmtcnt > MAX_OTHER_FORMATS - 1) { ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n"); break; } sfmt[fmtcnt++] = ast_strdupa(fmt); } time(&start); end=start; /* pre-initialize end to be same as start in case we never get into loop */ for (x=0; x<fmtcnt; x++) { others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700); ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]); if (!others[x]) { break; } } sildet = ast_dsp_new(); /* Create the silence detector */ if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, silencethreshold); if (maxsilence > 0) { rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); return -1; } } if (x == fmtcnt) { /* Loop forever, writing the packets we read to the writer(s), until we read a # or get a hangup */ f = NULL; for(;;) { res = ast_waitfor(chan, 2000); if (!res) { ast_log(LOG_DEBUG, "One waitfor failed, trying another\n"); /* Try one more time in case of masq */ res = ast_waitfor(chan, 2000); if (!res) { ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name); res = -1; } } if (res < 0) { f = NULL; break; } f = ast_read(chan); if (!f) break; if (f->frametype == AST_FRAME_VOICE) { /* write each format */ for (x=0; x<fmtcnt; x++) { if (!others[x]) break; res = ast_writestream(others[x], f); } /* Silence Detection */ if (maxsilence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) totalsilence = dspsilence; else totalsilence = 0; if (totalsilence > maxsilence) { /* Ended happily with silence */ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000); ast_frfree(f); gotsilence = 1; outmsg=2; break; } } /* Exit on any error */ if (res) { ast_log(LOG_WARNING, "Error writing frame\n"); ast_frfree(f); break; } } else if (f->frametype == AST_FRAME_VIDEO) { /* Write only once */ ast_writestream(others[0], f); } else if (f->frametype == AST_FRAME_DTMF) { /* stop recording with any digit */ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); res = 't'; outmsg = 2; ast_frfree(f); break; } if (maxtime) { time(&end); if (maxtime < (end - start)) { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); res = 't'; outmsg=2; ast_frfree(f); break; } } ast_frfree(f); } if (end == start) time(&end); if (!f) { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); res = -1; outmsg=1; #if 0 /* delete all the prepend files */ for (x=0; x<fmtcnt; x++) { if (!others[x]) break; ast_closestream(others[x]); ast_filedelete(prependfile, sfmt[x]); } #endif } } else { ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); } *duration = end - start; #if 0 if (outmsg > 1) { #else if (outmsg) { #endif struct ast_frame *fr; for (x=0; x<fmtcnt; x++) { snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]); realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0); if (!others[x] || !realfiles[x]) break; if (totalsilence) ast_stream_rewind(others[x], totalsilence-200); else ast_stream_rewind(others[x], 200); ast_truncstream(others[x]); /* add the original file too */ while ((fr = ast_readframe(realfiles[x]))) { ast_writestream(others[x],fr); } ast_closestream(others[x]); ast_closestream(realfiles[x]); ast_filerename(prependfile, recordfile, sfmt[x]); #if 0 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile); #endif ast_filedelete(prependfile, sfmt[x]); } } if (rfmt) { if (ast_set_read_format(chan, rfmt)) { ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); } } if (outmsg) { if (outmsg > 1) { /* Let them know it worked */ ast_streamfile(chan, "auth-thankyou", chan->language); ast_waitstream(chan, ""); } } return res; } int ast_lock_path(const char *path) { char *s; char *fs; int res; int fd; time_t start; s = alloca(strlen(path) + 10); fs = alloca(strlen(path) + 20); if (!fs || !s) { ast_log(LOG_WARNING, "Out of memory!\n"); return -1; } snprintf(fs, strlen(path) + 19, "%s/%s-%08x", path, ".lock", rand()); fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) { fprintf(stderr, "Unable to create lock file: %s\n", strerror(errno)); return -1; } close(fd); snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock"); time(&start); while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5)) usleep(1); if (res < 0) { ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno)); } unlink(fs); ast_log(LOG_DEBUG, "Locked path '%s'\n", path); return res; }
int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec) { int res; struct ast_filestream *writer; int rfmt; int totalms=0, total; struct ast_frame *f; struct ast_dsp *sildet; /* Play prompt if requested */ if (prompt) { res = ast_streamfile(c, prompt, c->language); if (res < 0) return res; res = ast_waitstream(c,""); if (res < 0) return res; } rfmt = c->readformat; res = ast_set_read_format(c, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); return -1; } sildet = ast_dsp_new(); if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666); if (!writer) { ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt); ast_dsp_free(sildet); return -1; } for(;;) { if ((res = ast_waitfor(c, 2000)) < 0) { ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt); break; } if (res) { f = ast_read(c); if (!f) { ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt); break; } if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) { /* Ended happily with DTMF */ ast_frfree(f); break; } else if (f->frametype == AST_FRAME_VOICE) { ast_dsp_silence(sildet, f, &total); if (total > silence) { /* Ended happily with silence */ ast_frfree(f); break; } totalms += f->samples / 8; if (totalms > maxsec * 1000) { /* Ended happily with too much stuff */ ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec); ast_frfree(f); break; } } ast_frfree(f); } } res = ast_set_read_format(c, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name); ast_dsp_free(sildet); ast_closestream(writer); return 0; }
int ast_play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path) { char *fmts; int d; char comment[256]; int x, fmtcnt=1, res=-1,outmsg=0; struct ast_frame *f; struct ast_filestream *others[MAX_OTHER_FORMATS]; char *sfmt[MAX_OTHER_FORMATS]; char *stringp=NULL; time_t start, end; struct ast_dsp *sildet=NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int gotsilence = 0; /* did we timeout for silence? */ int rfmt=0; if (silencethreshold < 0) silencethreshold = global_silence_threshold; if (maxsilence < 0) maxsilence = global_maxsilence; /* barf if no pointer passed to store duration in */ if (duration == NULL) { ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n"); return -1; } ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt); snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name); if (playfile) { d = ast_play_and_wait(chan, playfile); if (d > -1) d = ast_streamfile(chan, "beep",chan->language); if (!d) d = ast_waitstream(chan,""); if (d < 0) return -1; } fmts = ast_strdupa(fmt); stringp=fmts; strsep(&stringp, "|"); ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts); sfmt[0] = ast_strdupa(fmts); while((fmt = strsep(&stringp, "|"))) { if (fmtcnt > MAX_OTHER_FORMATS - 1) { ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n"); break; } sfmt[fmtcnt++] = ast_strdupa(fmt); } time(&start); end=start; /* pre-initialize end to be same as start in case we never get into loop */ for (x=0; x<fmtcnt; x++) { others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700); ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]); if (!others[x]) { break; } } if (path) ast_unlock_path(path); if (maxsilence > 0) { sildet = ast_dsp_new(); /* Create the silence detector */ if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, silencethreshold); rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); ast_dsp_free(sildet); return -1; } } if (x == fmtcnt) { /* Loop forever, writing the packets we read to the writer(s), until we read a # or get a hangup */ f = NULL; for(;;) { res = ast_waitfor(chan, 2000); if (!res) { ast_log(LOG_DEBUG, "One waitfor failed, trying another\n"); /* Try one more time in case of masq */ res = ast_waitfor(chan, 2000); if (!res) { ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name); res = -1; } } if (res < 0) { f = NULL; break; } f = ast_read(chan); if (!f) break; if (f->frametype == AST_FRAME_VOICE) { /* write each format */ for (x=0; x<fmtcnt; x++) { res = ast_writestream(others[x], f); } /* Silence Detection */ if (maxsilence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) totalsilence = dspsilence; else totalsilence = 0; if (totalsilence > maxsilence) { /* Ended happily with silence */ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000); ast_frfree(f); gotsilence = 1; outmsg=2; break; } } /* Exit on any error */ if (res) { ast_log(LOG_WARNING, "Error writing frame\n"); ast_frfree(f); break; } } else if (f->frametype == AST_FRAME_VIDEO) { /* Write only once */ ast_writestream(others[0], f); } else if (f->frametype == AST_FRAME_DTMF) { if (f->subclass == '#') { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); res = '#'; outmsg = 2; ast_frfree(f); break; } } if (f->subclass == '0') { /* Check for a '0' during message recording also, in case caller wants operator */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass); res = '0'; outmsg = 0; ast_frfree(f); break; } if (maxtime) { time(&end); if (maxtime < (end - start)) { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); outmsg = 2; res = 't'; ast_frfree(f); break; } } ast_frfree(f); } if (end == start) time(&end); if (!f) { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); res = -1; outmsg=1; } } else { ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); } *duration = end - start; for (x=0; x<fmtcnt; x++) { if (!others[x]) break; if (res > 0) { if (totalsilence) ast_stream_rewind(others[x], totalsilence-200); else ast_stream_rewind(others[x], 200); } ast_truncstream(others[x]); ast_closestream(others[x]); } if (rfmt) { if (ast_set_read_format(chan, rfmt)) { ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); } } if (outmsg > 1) { /* Let them know recording is stopped */ if(!ast_streamfile(chan, "auth-thankyou", chan->language)) ast_waitstream(chan, ""); } if (sildet) ast_dsp_free(sildet); return res; }
static int record_exec(struct ast_channel *chan, void *data) { int res = 0; int count = 0; int percentflag = 0; char *filename, *ext = NULL, *silstr, *maxstr, *options; char *vdata, *p; int i = 0; char tmp[256]; struct ast_filestream *s = '\0'; struct localuser *u; struct ast_frame *f = NULL; struct ast_dsp *sildet = NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int silence = 0; /* amount of silence to allow */ int gotsilence = 0; /* did we timeout for silence? */ int maxduration = 0; /* max duration of recording in milliseconds */ int gottimeout = 0; /* did we timeout for maxduration exceeded? */ int option_skip = 0; int option_noanswer = 0; int option_append = 0; int terminator = '#'; int option_quiet = 0; int rfmt = 0; int flags; int waitres; struct ast_silence_generator *silgen = NULL; /* The next few lines of code parse out the filename and header from the input string */ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ ast_log(LOG_WARNING, "Record requires an argument (filename)\n"); return -1; } LOCAL_USER_ADD(u); /* Yay for strsep being easy */ vdata = ast_strdupa(data); if (!vdata) { ast_log(LOG_ERROR, "Out of memory\n"); LOCAL_USER_REMOVE(u); return -1; } p = vdata; filename = strsep(&p, "|"); silstr = strsep(&p, "|"); maxstr = strsep(&p, "|"); options = strsep(&p, "|"); if (filename) { if (strstr(filename, "%d")) percentflag = 1; ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */ if (!ext) ext = strchr(filename, ':'); if (ext) { *ext = '\0'; ext++; } } if (!ext) { ast_log(LOG_WARNING, "No extension specified to filename!\n"); LOCAL_USER_REMOVE(u); return -1; } if (silstr) { if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) { silence = i * 1000; } else if (!ast_strlen_zero(silstr)) { ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr); } } if (maxstr) { if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1)) /* Convert duration to milliseconds */ maxduration = i * 1000; else if (!ast_strlen_zero(maxstr)) ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr); } if (options) { /* Retain backwards compatibility with old style options */ if (!strcasecmp(options, "skip")) option_skip = 1; else if (!strcasecmp(options, "noanswer")) option_noanswer = 1; else { if (strchr(options, 's')) option_skip = 1; if (strchr(options, 'n')) option_noanswer = 1; if (strchr(options, 'a')) option_append = 1; if (strchr(options, 't')) terminator = '*'; if (strchr(options, 'q')) option_quiet = 1; } } /* done parsing */ /* these are to allow the use of the %d in the config file for a wild card of sort to create a new file with the inputed name scheme */ if (percentflag) { do { snprintf(tmp, sizeof(tmp), filename, count); count++; } while ( ast_fileexists(tmp, ext, chan->language) != -1 ); pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp); } else strncpy(tmp, filename, sizeof(tmp)-1); /* end of routine mentioned */ if (chan->_state != AST_STATE_UP) { if (option_skip) { /* At the user's option, skip if the line is not up */ LOCAL_USER_REMOVE(u); return 0; } else if (!option_noanswer) { /* Otherwise answer unless we're supposed to record while on-hook */ res = ast_answer(chan); } } if (res) { ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); goto out; } if (!option_quiet) { /* Some code to play a nice little beep to signify the start of the record operation */ res = ast_streamfile(chan, "beep", chan->language); if (!res) { res = ast_waitstream(chan, ""); } else { ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name); } ast_stopstream(chan); } /* The end of beep code. Now the recording starts */ if (silence > 0) { rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); LOCAL_USER_REMOVE(u); return -1; } sildet = ast_dsp_new(); if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); LOCAL_USER_REMOVE(u); return -1; } ast_dsp_set_threshold(sildet, 256); } flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY; s = ast_writefile( tmp, ext, NULL, flags , 0, 0644); if (!s) { ast_log(LOG_WARNING, "Could not create file %s\n", filename); goto out; } if (option_transmit_silence_during_record) silgen = ast_channel_start_silence_generator(chan); /* Request a video update */ ast_indicate(chan, AST_CONTROL_VIDUPDATE); if (maxduration <= 0) maxduration = -1; while ((waitres = ast_waitfor(chan, maxduration)) > -1) { if (maxduration > 0) { if (waitres == 0) { gottimeout = 1; break; } maxduration = waitres; } f = ast_read(chan); if (!f) { res = -1; break; } if (f->frametype == AST_FRAME_VOICE) { res = ast_writestream(s, f); if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); ast_frfree(f); break; } if (silence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { totalsilence = dspsilence; } else { totalsilence = 0; } if (totalsilence > silence) { /* Ended happily with silence */ ast_frfree(f); gotsilence = 1; break; } } } else if (f->frametype == AST_FRAME_VIDEO) { res = ast_writestream(s, f); if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); ast_frfree(f); break; } } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == terminator)) { ast_frfree(f); break; } ast_frfree(f); } if (!f) { ast_log(LOG_DEBUG, "Got hangup\n"); res = -1; } if (gotsilence) { ast_stream_rewind(s, silence-1000); ast_truncstream(s); } else if (!gottimeout) { /* Strip off the last 1/4 second of it */ ast_stream_rewind(s, 250); ast_truncstream(s); } ast_closestream(s); if (silgen) ast_channel_stop_silence_generator(chan, silgen); out: if ((silence > 0) && rfmt) { res = ast_set_read_format(chan, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); if (sildet) ast_dsp_free(sildet); } LOCAL_USER_REMOVE(u); return res; }