/*! \internal \brief Enable talk detection on the channel */ static int set_talk_detect(struct ast_channel *chan, int dsp_silence_threshold, int dsp_talking_threshold) { struct ast_datastore *datastore = NULL; struct talk_detect_params *td_params; SCOPED_CHANNELLOCK(chan_lock, chan); datastore = ast_channel_datastore_find(chan, &talk_detect_datastore, NULL); if (!datastore) { datastore = ast_datastore_alloc(&talk_detect_datastore, NULL); if (!datastore) { return -1; } td_params = ast_calloc(1, sizeof(*td_params)); if (!td_params) { ast_datastore_free(datastore); return -1; } ast_audiohook_init(&td_params->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "TALK_DETECT", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); td_params->audiohook.manipulate_callback = talk_detect_audiohook_cb; ast_set_flag(&td_params->audiohook, AST_AUDIOHOOK_TRIGGER_READ); td_params->dsp = ast_dsp_new_with_rate(ast_format_get_sample_rate(ast_channel_rawreadformat(chan))); if (!td_params->dsp) { ast_datastore_free(datastore); ast_free(td_params); return -1; } datastore->data = td_params; ast_channel_datastore_add(chan, datastore); ast_audiohook_attach(chan, &td_params->audiohook); } else { /* Talk detection already enabled; update existing settings */ td_params = datastore->data; } td_params->dsp_talking_threshold = dsp_talking_threshold; td_params->dsp_silence_threshold = dsp_silence_threshold; ast_dsp_set_threshold(td_params->dsp, td_params->dsp_talking_threshold); return 0; }
static int nv_detectfax_exec(struct ast_channel *chan, const char *data) { int res = 0; char tmp[256] = "\0"; char *p = NULL; char *waitstr = NULL; char *options = NULL; char *silstr = NULL; char *minstr = NULL; char *maxstr = NULL; struct ast_frame *fr = NULL; struct ast_frame *fr2 = NULL; int notsilent = 0; struct timeval start = {0, 0}, end = {0, 0}; int waitdur = 4; int sildur = 1000; int mindur = 100; int maxdur = -1; int skipanswer = 0; int noextneeded = 0; int ignoredtmf = 0; int ignorefax = 0; int ignoretalk = 0; int x = 0; struct ast_format* origrformat = NULL; int features = 0; time_t timeout = 0; struct ast_dsp *dsp = NULL; struct ast_format_cap *cap; struct ast_format linearFormat; /* linear format capabilities */ ast_format_set(&linearFormat, AST_FORMAT_SLINEAR, 0); cap = ast_format_cap_alloc_nolock(); ast_format_cap_add(cap, &linearFormat); /* done */ pbx_builtin_setvar_helper(chan, "FAX_DETECTED", ""); pbx_builtin_setvar_helper(chan, "FAXEXTEN", ""); pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", ""); pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ""); if (data || !ast_strlen_zero(data)) { strncpy(tmp, data, sizeof(tmp)-1); } p = tmp; waitstr = strsep(&p, ","); options = strsep(&p, ","); silstr = strsep(&p, ","); minstr = strsep(&p, ","); maxstr = strsep(&p, ","); if (waitstr) { if ((sscanf(waitstr, "%d", &x) == 1) && (x > 0)) waitdur = x; } if (options) { if (strchr(options, 'n')) skipanswer = 1; if (strchr(options, 'x')) noextneeded = 1; if (strchr(options, 'd')) ignoredtmf = 1; if (strchr(options, 'f')) ignorefax = 1; if (strchr(options, 't')) ignoretalk = 1; } if (silstr) { if ((sscanf(silstr, "%d", &x) == 1) && (x > 0)) sildur = x; } if (minstr) { if ((sscanf(minstr, "%d", &x) == 1) && (x > 0)) mindur = x; } if (maxstr) { if ((sscanf(maxstr, "%d", &x) == 1) && (x > 0)) maxdur = x; } ast_log(LOG_DEBUG, "Preparing detect of fax (waitdur=%dms, sildur=%dms, mindur=%dms, maxdur=%dms)\n", waitdur, sildur, mindur, maxdur); // LOCAL_USER_ADD(u); // if (chan->_state != AST_STATE_UP && !skipanswer) { if (ast_channel_state(chan) != AST_STATE_UP && !skipanswer) { /* Otherwise answer unless we're supposed to send this while on-hook */ res = ast_answer(chan); } if (!res) { // origrformat = chan->readformat; origrformat = ast_channel_readformat(chan); // if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR))) if ((res = ast_set_read_format_from_cap(chan, cap)) ){ ast_log(LOG_WARNING, "Unable to set read format to linear!\n"); } } if (!(dsp = ast_dsp_new())) { ast_log(LOG_WARNING, "Unable to allocate DSP!\n"); res = -1; } if (dsp) { if (!ignoretalk) ; /* features |= DSP_FEATURE_SILENCE_SUPPRESS; */ if (!ignorefax) features |= DSP_FEATURE_FAX_DETECT; //if (!ignoredtmf) features |= DSP_FEATURE_DIGIT_DETECT; ast_dsp_set_threshold(dsp, 256); ast_dsp_set_features(dsp, features | DSP_DIGITMODE_RELAXDTMF); ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_DTMF); } if (!res) { if (waitdur > 0) timeout = time(NULL) + (time_t)waitdur; while(ast_waitfor(chan, -1) > -1) { if (waitdur > 0 && time(NULL) > timeout) { res = 0; break; } fr = ast_read(chan); if (!fr) { ast_log(LOG_DEBUG, "Got hangup\n"); res = -1; break; } fr2 = ast_dsp_process(chan, dsp, fr); if (!fr2) { ast_log(LOG_WARNING, "Bad DSP received (what happened?)\n"); fr2 = fr; } if (fr2->frametype == AST_FRAME_DTMF) { if (fr2->subclass.integer == 'f' && !ignorefax) { /* Fax tone -- Handle and return NULL */ ast_log(LOG_DEBUG, "Fax detected on %s\n", ast_channel_name(chan)); ast_log(LOG_DEBUG, "Fax detected on %s\n", ast_channel_name(chan)); if (strcmp(ast_channel_exten(chan), "fax")) { ast_log(LOG_NOTICE, "Redirecting %s to fax extension\n", ast_channel_name(chan)); pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "1"); pbx_builtin_setvar_helper(chan,"FAXEXTEN",ast_channel_exten(chan)); if (ast_exists_extension(chan, ast_channel_context(chan), "fax", 1, ast_channel_caller(chan)->id.number.str)) { /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ // strncpy(ast_channel_exten(chan), "fax", sizeof(ast_channel_exten(chan))-1); // chan->priority = 0; ast_channel_exten_set(chan, "fax"); ast_channel_priority_set(chan, 0); } else ast_log(LOG_WARNING, "Fax detected, but no fax extension\n"); } else ast_log(LOG_WARNING, "Already in a fax extension, not redirecting\n"); res = 0; ast_frfree(fr); break; } else if (!ignoredtmf) { ast_log(LOG_DEBUG, "DTMF detected on %s\n", ast_channel_name(chan)); char t[2]; t[0] = fr2->subclass.integer; t[1] = '\0'; if (noextneeded || ast_canmatch_extension(chan, ast_channel_context(chan), t, 1, ast_channel_caller(chan)->id.number.str)) { pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "1"); /* They entered a valid extension, or might be anyhow */ if (noextneeded) { ast_log(LOG_NOTICE, "DTMF received (not matching to exten)\n"); res = 0; } else { ast_log(LOG_NOTICE, "DTMF received (matching to exten)\n"); res = fr2->subclass.integer; } ast_frfree(fr); break; } else ast_log(LOG_DEBUG, "Valid extension requested and DTMF did not match\n"); } // } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR) && !ignoretalk) { } else if ((fr->frametype == AST_FRAME_VOICE) && ( ast_format_cap_iscompatible(cap, &fr->subclass.format)) && !ignoretalk) { int totalsilence; int ms; res = ast_dsp_silence(dsp, fr, &totalsilence); if (res && (totalsilence > sildur)) { /* We've been quiet a little while */ if (notsilent) { /* We had heard some talking */ gettimeofday(&end, NULL); ms = (end.tv_sec - start.tv_sec) * 1000; ms += (end.tv_usec - start.tv_usec) / 1000; ms -= sildur; if (ms < 0) ms = 0; if ((ms > mindur) && ((maxdur < 0) || (ms < maxdur))) { char ms_str[10]; ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms); ast_log(LOG_NOTICE, "Redirecting %s to talk extension\n", ast_channel_name(chan)); /* Save detected talk time (in milliseconds) */ sprintf(ms_str, "%d", ms); pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str); if (ast_exists_extension(chan, ast_channel_context(chan), "talk", 1, ast_channel_caller(chan)->id.number.str)) { // strncpy(ast_channel_exten(chan), "talk", sizeof(ast_channel_exten(chan)) - 1); // chan->priority = 0; ast_channel_exten_set(chan, "talk"); ast_channel_priority_set(chan, 0); } else ast_log(LOG_WARNING, "Talk detected, but no talk extension\n"); res = 0; ast_frfree(fr); break; } else ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms); notsilent = 0; } } else { if (!notsilent) { /* Heard some audio, mark the begining of the token */ gettimeofday(&start, NULL); ast_log(LOG_DEBUG, "Start of voice token!\n"); notsilent = 1; } } } ast_frfree(fr); } } else ast_log(LOG_WARNING, "Could not answer channel '%s'\n", ast_channel_name(chan)); if (res > -1) { if (origrformat && ast_set_read_format(chan, origrformat)) { ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", ast_channel_name(chan), ast_getformatname(origrformat)); } } if (dsp) ast_dsp_free(dsp); // LOCAL_USER_REMOVE(u); ast_format_cap_destroy(cap); return res; }
static int do_waiting(struct ast_channel *chan, int maxsilence) { struct ast_frame *f; int totalsilence = 0; int dspsilence = 0; int gotsilence = 0; static int silencethreshold = 128; int rfmt = 0; int res = 0; struct ast_dsp *sildet; /* silence detector dsp */ time_t start, now; time(&start); rfmt = chan->readformat; /* Set to linear mode */ 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; } 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); /* Await silence... */ f = NULL; for(;;) { res = ast_waitfor(chan, 2000); if (!res) { ast_log(LOG_WARNING, "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) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { totalsilence = dspsilence; time(&start); } else { totalsilence = 0; } if (totalsilence >= maxsilence) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Exiting with %dms silence > %dms required\n", totalsilence, maxsilence); /* Ended happily with silence */ gotsilence = 1; pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE"); ast_log(LOG_DEBUG, "WAITSTATUS was set to SILENCE\n"); ast_frfree(f); break; } else if ( difftime(time(&now),start) >= maxsilence/1000 ) { pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT"); ast_log(LOG_DEBUG, "WAITSTATUS was set to TIMEOUT\n"); ast_frfree(f); break; } } ast_frfree(f); } if (rfmt && ast_set_read_format(chan, rfmt)) { ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); } ast_dsp_free(sildet); return gotsilence; }
static int do_waiting(struct ast_channel *chan, int timereqd, time_t waitstart, int timeout, int wait_for_silence) { struct ast_frame *f = NULL; int dsptime = 0; struct ast_format rfmt; int res = 0; struct ast_dsp *sildet; /* silence detector dsp */ time_t now; /*Either silence or noise calc depending on wait_for_silence flag*/ int (*ast_dsp_func)(struct ast_dsp*, struct ast_frame*, int*) = wait_for_silence ? ast_dsp_silence : ast_dsp_noise; ast_format_copy(&rfmt, &chan->readformat); /* Set to linear mode */ if ((res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) < 0) { ast_log(LOG_WARNING, "Unable to set channel to linear mode, giving up\n"); return -1; } /* Create the silence detector */ if (!(sildet = ast_dsp_new())) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE)); /* Await silence... */ for (;;) { /* Start with no silence received */ dsptime = 0; res = ast_waitfor(chan, timereqd); /* Must have gotten a hangup; let's exit */ if (res < 0) { pbx_builtin_setvar_helper(chan, "WAITSTATUS", "HANGUP"); break; } /* We waited and got no frame; sounds like digital silence or a muted digital channel */ if (res == 0) { dsptime = timereqd; } else { /* Looks like we did get a frame, so let's check it out */ if (!(f = ast_read(chan))) { pbx_builtin_setvar_helper(chan, "WAITSTATUS", "HANGUP"); break; } if (f->frametype == AST_FRAME_VOICE) { ast_dsp_func(sildet, f, &dsptime); } ast_frfree(f); } ast_verb(6, "Got %dms %s < %dms required\n", dsptime, wait_for_silence ? "silence" : "noise", timereqd); if (dsptime >= timereqd) { ast_verb(3, "Exiting with %dms %s >= %dms required\n", dsptime, wait_for_silence ? "silence" : "noise", timereqd); /* Ended happily with silence */ res = 1; pbx_builtin_setvar_helper(chan, "WAITSTATUS", wait_for_silence ? "SILENCE" : "NOISE"); ast_debug(1, "WAITSTATUS was set to %s\n", wait_for_silence ? "SILENCE" : "NOISE"); break; } if (timeout && (difftime(time(&now), waitstart) >= timeout)) { pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT"); ast_debug(1, "WAITSTATUS was set to TIMEOUT\n"); res = 0; break; } } if (rfmt.id && ast_set_read_format(chan, &rfmt)) { ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(&rfmt), chan->name); } ast_dsp_free(sildet); return res; }
static int nv_background_detect_exec(struct ast_channel *chan, void *data) { int res = 0; struct ast_module_user *u; char tmp[256] = "\0"; char *p = NULL; char *filename = NULL; char *options = NULL; char *silstr = NULL; char *minstr = NULL; char *maxstr = NULL; struct ast_frame *fr = NULL; struct ast_frame *fr2 = NULL; int notsilent = 0; struct timeval start = {0, 0}, end = {0, 0}; int sildur = 1000; int mindur = 100; int maxdur = -1; int skipanswer = 0; int noextneeded = 0; int ignoredtmf = 0; int ignorefax = 0; int ignoretalk = 0; int x = 0; int origrformat = 0; int features = 0; struct ast_dsp *dsp = NULL; pbx_builtin_setvar_helper(chan, "FAX_DETECTED", ""); pbx_builtin_setvar_helper(chan, "FAXEXTEN", ""); pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", ""); pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ""); if (!data || ast_strlen_zero((char *)data)) { ast_log(LOG_WARNING, "NVBackgroundDetect requires an argument (filename)\n"); return -1; } strncpy(tmp, (char *)data, sizeof(tmp)-1); p = tmp; filename = strsep(&p, "|"); options = strsep(&p, "|"); silstr = strsep(&p, "|"); minstr = strsep(&p, "|"); maxstr = strsep(&p, "|"); if (options) { if (strchr(options, 'n')) skipanswer = 1; if (strchr(options, 'x')) noextneeded = 1; if (strchr(options, 'd')) ignoredtmf = 1; if (strchr(options, 'f')) ignorefax = 1; if (strchr(options, 't')) ignoretalk = 1; } if (silstr) { if ((sscanf(silstr, "%d", &x) == 1) && (x > 0)) sildur = x; } if (minstr) { if ((sscanf(minstr, "%d", &x) == 1) && (x > 0)) mindur = x; } if (maxstr) { if ((sscanf(maxstr, "%d", &x) == 1) && (x > 0)) maxdur = x; } ast_log(LOG_DEBUG, "Preparing detect of '%s' (sildur=%dms, mindur=%dms, maxdur=%dms)\n", tmp, sildur, mindur, maxdur); u = ast_module_user_add(chan); if (chan->_state != AST_STATE_UP && !skipanswer) { /* Otherwise answer unless we're supposed to send this while on-hook */ res = ast_answer(chan); } if (!res) { origrformat = chan->readformat; if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR))) ast_log(LOG_WARNING, "Unable to set read format to linear!\n"); } if (!(dsp = ast_dsp_new())) { ast_log(LOG_WARNING, "Unable to allocate DSP!\n"); res = -1; } if (dsp) { if (!ignoretalk) ; /* features |= DSP_FEATURE_SILENCE_SUPPRESS; */ if (!ignorefax) features |= DSP_FEATURE_FAX_DETECT; //if (!ignoredtmf) features |= DSP_FEATURE_DTMF_DETECT; ast_dsp_set_threshold(dsp, 256); ast_dsp_set_features(dsp, features | DSP_DIGITMODE_RELAXDTMF); ast_dsp_digitmode(dsp, DSP_DIGITMODE_DTMF); } if (!res) { ast_stopstream(chan); res = ast_streamfile(chan, tmp, chan->language); if (!res) { while(chan->stream) { res = ast_sched_wait(chan->sched); if ((res < 0) && !chan->timingfunc) { res = 0; break; } if (res < 0) res = 1000; res = ast_waitfor(chan, res); if (res < 0) { ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name); break; } else if (res > 0) { fr = ast_read(chan); if (!fr) { ast_log(LOG_DEBUG, "Got hangup\n"); res = -1; break; } fr2 = ast_dsp_process(chan, dsp, fr); if (!fr2) { ast_log(LOG_WARNING, "Bad DSP received (what happened?)\n"); fr2 = fr; } if (fr2->frametype == AST_FRAME_DTMF) { if (fr2->subclass == 'f' && !ignorefax) { /* Fax tone -- Handle and return NULL */ ast_log(LOG_DEBUG, "Fax detected on %s\n", chan->name); if (strcmp(chan->exten, "fax")) { ast_log(LOG_NOTICE, "Redirecting %s to fax extension\n", chan->name); pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "1"); pbx_builtin_setvar_helper(chan,"FAXEXTEN",chan->exten); if (ast_exists_extension(chan, chan->context, "fax", 1, chan->CALLERID_FIELD)) { /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ strncpy(chan->exten, "fax", sizeof(chan->exten)-1); chan->priority = 0; } else ast_log(LOG_WARNING, "Fax detected, but no fax extension\n"); } else ast_log(LOG_WARNING, "Already in a fax extension, not redirecting\n"); res = 0; ast_frfree(fr); break; } else if (!ignoredtmf) { char t[2]; t[0] = fr2->subclass; t[1] = '\0'; ast_log(LOG_DEBUG, "DTMF detected on %s\n", chan->name); if (noextneeded || ast_canmatch_extension(chan, chan->context, t, 1, chan->CALLERID_FIELD)) { pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "1"); /* They entered a valid extension, or might be anyhow */ if (noextneeded) { ast_log(LOG_NOTICE, "DTMF received (not matching to exten)\n"); res = 0; } else { ast_log(LOG_NOTICE, "DTMF received (matching to exten)\n"); res = fr2->subclass; } ast_frfree(fr); break; } else ast_log(LOG_DEBUG, "Valid extension requested and DTMF did not match\n"); } } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR) && !ignoretalk) { int totalsilence; int ms; res = ast_dsp_silence(dsp, fr, &totalsilence); if (res && (totalsilence > sildur)) { /* We've been quiet a little while */ if (notsilent) { /* We had heard some talking */ gettimeofday(&end, NULL); ms = (end.tv_sec - start.tv_sec) * 1000; ms += (end.tv_usec - start.tv_usec) / 1000; ms -= sildur; if (ms < 0) ms = 0; if ((ms > mindur) && ((maxdur < 0) || (ms < maxdur))) { char ms_str[10]; ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms); ast_log(LOG_NOTICE, "Redirecting %s to talk extension\n", chan->name); /* Save detected talk time (in milliseconds) */ sprintf(ms_str, "%d", ms); pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str); if (ast_exists_extension(chan, chan->context, "talk", 1, chan->CALLERID_FIELD)) { strncpy(chan->exten, "talk", sizeof(chan->exten) - 1); chan->priority = 0; } else ast_log(LOG_WARNING, "Talk detected, but no talk extension\n"); res = 0; ast_frfree(fr); break; } else ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms); notsilent = 0; } } else { if (!notsilent) { /* Heard some audio, mark the begining of the token */ gettimeofday(&start, NULL); ast_log(LOG_DEBUG, "Start of voice token!\n"); notsilent = 1; } } } ast_frfree(fr); } ast_sched_runq(chan->sched); } ast_stopstream(chan); } else { ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data); res = 0; } } else ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); if (res > -1) { if (origrformat && ast_set_read_format(chan, origrformat)) { ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", chan->name, ast_getformatname(origrformat)); } } if (dsp) ast_dsp_free(dsp); ast_module_user_remove(u); return res; }
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_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 do_waiting(struct ast_channel *chan, int silencereqd, time_t waitstart, int timeout) { struct ast_frame *f; int dspsilence = 0; static int silencethreshold = 128; int rfmt = 0; int res = 0; struct ast_dsp *sildet; /* silence detector dsp */ time_t now; rfmt = chan->readformat; /* Set to linear mode */ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set channel to linear mode, giving up\n"); return -1; } 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); /* Await silence... */ f = NULL; for(;;) { /* Start with no silence received */ dspsilence = 0; res = ast_waitfor(chan, silencereqd); /* Must have gotten a hangup; let's exit */ if (res < 0) { f = NULL; break; } /* We waited and got no frame; sounds like digital silence or a muted digital channel */ if (res == 0) { dspsilence = silencereqd; } else { /* Looks like we did get a frame, so let's check it out */ f = ast_read(chan); if (!f) break; if (f && f->frametype == AST_FRAME_VOICE) { ast_dsp_silence(sildet, f, &dspsilence); } if (f) { ast_frfree(f); } } if (option_verbose > 6) ast_verbose(VERBOSE_PREFIX_3 "Got %dms silence< %dms required\n", dspsilence, silencereqd); if (dspsilence >= silencereqd) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Exiting with %dms silence >= %dms required\n", dspsilence, silencereqd); /* Ended happily with silence */ res = 1; pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE"); ast_log(LOG_DEBUG, "WAITSTATUS was set to SILENCE\n"); break; } if ( timeout && (difftime(time(&now),waitstart) >= timeout) ) { pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT"); ast_log(LOG_DEBUG, "WAITSTATUS was set to TIMEOUT\n"); res = 0; break; } } if (rfmt && ast_set_read_format(chan, rfmt)) { ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); } 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; }