00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <asterisk.h>
00028 #include <stdio.h>
00029 #include <asterisk/lock.h>
00030 #include <asterisk/file.h>
00031 #include <asterisk/logger.h>
00032 #include <asterisk/channel.h>
00033 #include <asterisk/pbx.h>
00034 #include <asterisk/module.h>
00035 #include <asterisk/translate.h>
00036 #include <asterisk/utils.h>
00037 #include <asterisk/dsp.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040
00041 static char *app = "NVBackgroundDetect";
00042
00043 static char *synopsis = "Background a file with talk and fax detect (IAX and SIP too)";
00044
00045 static char *descrip =
00046 " NVBackgroundDetect(filename[|options[|sildur[|mindur|[maxdur]]]]):\n"
00047 "Plays filename, waiting for interruption from fax tones (on IAX and SIP too),\n"
00048 "a digit, or non-silence. Audio is monitored in the receive direction. If\n"
00049 "digits interrupt, they must be the start of a valid extension unless the\n"
00050 "option is included to ignore. If fax is detected, it will jump to the\n"
00051 "'fax' extension. If a period of non-silence is greater than 'mindur' ms,\n"
00052 "yet less than 'maxdur' ms is followed by silence at least 'sildur' ms\n"
00053 "then the app is aborted and processing jumps to the 'talk' extension.\n"
00054 "If all undetected, control will continue at the next priority.\n"
00055 " options:\n"
00056 " 'n': Attempt on-hook if unanswered (default=no)\n"
00057 " 'x': DTMF digits terminate without extension (default=no)\n"
00058 " 'd': Ignore DTMF digit detection (default=no)\n"
00059 " 'f': Ignore fax detection (default=no)\n"
00060 " 't': Ignore talk detection (default=no)\n"
00061 " sildur: Silence ms after mindur/maxdur before aborting (default=1000)\n"
00062 " mindur: Minimum non-silence ms needed (default=100)\n"
00063 " maxdur: Maximum non-silence ms allowed (default=0/forever)\n"
00064 "Returns -1 on hangup, and 0 on successful completion with no exit conditions.\n\n" "For questions or comments, please e-mail support@newmantelecom.com.\n";
00065
00066 #define CALLERID_FIELD cid.cid_num
00067
00068 static int nv_background_detect_exec(struct ast_channel *chan, void *data)
00069 {
00070 int res = 0;
00071 struct ast_module_user *u;
00072 char tmp[256] = "\0";
00073 char *p = NULL;
00074 char *filename = NULL;
00075 char *options = NULL;
00076 char *silstr = NULL;
00077 char *minstr = NULL;
00078 char *maxstr = NULL;
00079 struct ast_frame *fr = NULL;
00080 struct ast_frame *fr2 = NULL;
00081 int notsilent = 0;
00082 struct timeval start = { 0, 0 }, end = {
00083 0, 0};
00084 int sildur = 1000;
00085 int mindur = 100;
00086 int maxdur = -1;
00087 int skipanswer = 0;
00088 int noextneeded = 0;
00089 int ignoredtmf = 0;
00090 int ignorefax = 0;
00091 int ignoretalk = 0;
00092 int x = 0;
00093 int origrformat = 0;
00094 int features = 0;
00095 struct ast_dsp *dsp = NULL;
00096
00097 pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "");
00098 pbx_builtin_setvar_helper(chan, "FAXEXTEN", "");
00099 pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "");
00100 pbx_builtin_setvar_helper(chan, "TALK_DETECTED", "");
00101
00102 if (!data || ast_strlen_zero((char *) data)) {
00103 ast_log(LOG_WARNING, "NVBackgroundDetect requires an argument (filename)\n");
00104 return -1;
00105 }
00106
00107 strncpy(tmp, (char *) data, sizeof(tmp) - 1);
00108 p = tmp;
00109
00110 filename = strsep(&p, "|");
00111 options = strsep(&p, "|");
00112 silstr = strsep(&p, "|");
00113 minstr = strsep(&p, "|");
00114 maxstr = strsep(&p, "|");
00115
00116 if (options) {
00117 if (strchr(options, 'n'))
00118 skipanswer = 1;
00119 if (strchr(options, 'x'))
00120 noextneeded = 1;
00121 if (strchr(options, 'd'))
00122 ignoredtmf = 1;
00123 if (strchr(options, 'f'))
00124 ignorefax = 1;
00125 if (strchr(options, 't'))
00126 ignoretalk = 1;
00127 }
00128
00129 if (silstr) {
00130 if ((sscanf(silstr, "%d", &x) == 1) && (x > 0))
00131 sildur = x;
00132 }
00133
00134 if (minstr) {
00135 if ((sscanf(minstr, "%d", &x) == 1) && (x > 0))
00136 mindur = x;
00137 }
00138
00139 if (maxstr) {
00140 if ((sscanf(maxstr, "%d", &x) == 1) && (x > 0))
00141 maxdur = x;
00142 }
00143
00144 ast_log(LOG_DEBUG, "Preparing detect of '%s' (sildur=%dms, mindur=%dms, maxdur=%dms)\n", tmp, sildur, mindur, maxdur);
00145
00146 u = ast_module_user_add(chan);
00147 if (chan->_state != AST_STATE_UP && !skipanswer) {
00148
00149 res = ast_answer(chan);
00150 }
00151 if (!res) {
00152 origrformat = chan->readformat;
00153 if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)))
00154 ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
00155 }
00156 if (!(dsp = ast_dsp_new())) {
00157 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
00158 res = -1;
00159 }
00160
00161 if (dsp) {
00162
00163
00164
00165
00166 if (!ignorefax)
00167 features |= DSP_FEATURE_FAX_DETECT;
00168
00169 features |= DSP_FEATURE_DTMF_DETECT;
00170
00171 ast_dsp_set_threshold(dsp, 256);
00172 ast_dsp_set_features(dsp, features | DSP_DIGITMODE_RELAXDTMF);
00173 ast_dsp_digitmode(dsp, DSP_DIGITMODE_DTMF);
00174 }
00175
00176 if (!res) {
00177 ast_stopstream(chan);
00178 res = ast_streamfile(chan, tmp, chan->language);
00179 if (!res) {
00180 while (chan->stream) {
00181 res = ast_sched_wait(chan->sched);
00182 if ((res < 0) && !chan->timingfunc) {
00183 res = 0;
00184 break;
00185 }
00186 if (res < 0)
00187 res = 1000;
00188 res = ast_waitfor(chan, res);
00189 if (res < 0) {
00190 ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
00191 break;
00192 } else if (res > 0) {
00193 fr = ast_read(chan);
00194 if (!fr) {
00195 ast_log(LOG_DEBUG, "Got hangup\n");
00196 res = -1;
00197 break;
00198 }
00199
00200 fr2 = ast_dsp_process(chan, dsp, fr);
00201 if (!fr2) {
00202 ast_log(LOG_WARNING, "Bad DSP received (what happened?)\n");
00203 fr2 = fr;
00204 }
00205
00206 if (fr2->frametype == AST_FRAME_DTMF) {
00207 if (fr2->subclass == 'f' && !ignorefax) {
00208
00209 ast_log(LOG_DEBUG, "Fax detected on %s\n", chan->name);
00210 if (strcmp(chan->exten, "fax")) {
00211 ast_log(LOG_NOTICE, "Redirecting %s to fax extension\n", chan->name);
00212 pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "1");
00213 pbx_builtin_setvar_helper(chan, "FAXEXTEN", chan->exten);
00214 if (ast_exists_extension(chan, chan->context, "fax", 1, chan->CALLERID_FIELD)) {
00215
00216 strncpy(chan->exten, "fax", sizeof(chan->exten) - 1);
00217 chan->priority = 0;
00218 } else
00219 ast_log(LOG_WARNING, "Fax detected, but no fax extension\n");
00220 } else
00221 ast_log(LOG_WARNING, "Already in a fax extension, not redirecting\n");
00222
00223 res = 0;
00224 ast_frfree(fr);
00225 break;
00226 } else if (!ignoredtmf) {
00227 ast_log(LOG_DEBUG, "DTMF detected on %s\n", chan->name);
00228 char t[2];
00229 t[0] = fr2->subclass;
00230 t[1] = '\0';
00231 if (noextneeded || ast_canmatch_extension(chan, chan->context, t, 1, chan->CALLERID_FIELD)) {
00232 pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "1");
00233
00234 if (noextneeded) {
00235 ast_log(LOG_NOTICE, "DTMF received (not matching to exten)\n");
00236 res = 0;
00237 } else {
00238 ast_log(LOG_NOTICE, "DTMF received (matching to exten)\n");
00239 res = fr2->subclass;
00240 }
00241 ast_frfree(fr);
00242 break;
00243 } else
00244 ast_log(LOG_DEBUG, "Valid extension requested and DTMF did not match\n");
00245 }
00246 } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR) && !ignoretalk) {
00247 int totalsilence;
00248 int ms;
00249 res = ast_dsp_silence(dsp, fr, &totalsilence);
00250 if (res && (totalsilence > sildur)) {
00251
00252 if (notsilent) {
00253
00254 gettimeofday(&end, NULL);
00255 ms = (end.tv_sec - start.tv_sec) * 1000;
00256 ms += (end.tv_usec - start.tv_usec) / 1000;
00257 ms -= sildur;
00258 if (ms < 0)
00259 ms = 0;
00260 if ((ms > mindur) && ((maxdur < 0) || (ms < maxdur))) {
00261 char ms_str[10];
00262 ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms);
00263 ast_log(LOG_NOTICE, "Redirecting %s to talk extension\n", chan->name);
00264
00265
00266 sprintf(ms_str, "%d", ms);
00267 pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
00268
00269 if (ast_exists_extension(chan, chan->context, "talk", 1, chan->CALLERID_FIELD)) {
00270 strncpy(chan->exten, "talk", sizeof(chan->exten) - 1);
00271 chan->priority = 0;
00272 } else
00273 ast_log(LOG_WARNING, "Talk detected, but no talk extension\n");
00274 res = 0;
00275 ast_frfree(fr);
00276 break;
00277 } else
00278 ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms);
00279 notsilent = 0;
00280 }
00281 } else {
00282 if (!notsilent) {
00283
00284 gettimeofday(&start, NULL);
00285 ast_log(LOG_DEBUG, "Start of voice token!\n");
00286 notsilent = 1;
00287 }
00288 }
00289 }
00290 ast_frfree(fr);
00291 }
00292 ast_sched_runq(chan->sched);
00293 }
00294 ast_stopstream(chan);
00295 } else {
00296 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *) data);
00297 res = 0;
00298 }
00299 } else
00300 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00301
00302 if (res > -1) {
00303 if (origrformat && ast_set_read_format(chan, origrformat)) {
00304 ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", chan->name, ast_getformatname(origrformat));
00305 }
00306 }
00307
00308 if (dsp)
00309 ast_dsp_free(dsp);
00310
00311 ast_module_user_remove(u);
00312
00313 return res;
00314 }
00315
00316 static int unload_module(void)
00317 {
00318 return ast_unregister_application(app);
00319 }
00320
00321 static int load_module(void)
00322 {
00323 return ast_register_application(app, nv_background_detect_exec, synopsis, descrip);
00324 }
00325
00326 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Newman's playback with talk and fax detection");