Mon May 14 04:42:53 2007

Asterisk developer's documentation


app_nv_backgrounddetect.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Playback a file with audio detect
00005  * 
00006  * Copyright (C) 2004-2005, Newman Telecom, Inc. and Newman Ventures, Inc.
00007  *
00008  * Justin Newman <jnewman@newmantelecom.com>
00009  *
00010  * We would like to thank Newman Ventures, Inc. for funding this
00011  * Asterisk project.
00012  *
00013  * Newman Ventures <info@newmanventures.com>
00014  *
00015  * Portions Copyright:
00016  * Copyright (C) 2001, Linux Support Services, Inc.
00017  * Copyright (C) 2004, Digium, Inc.
00018  *
00019  * Matthew Fredrickson <creslin@linux-support.net>
00020  * Mark Spencer <markster@digium.com>
00021  *
00022  * This program is free software, distributed under the terms of
00023  * the GNU General Public License
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       /* Otherwise answer unless we're supposed to send this while on-hook */
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          if (!ignoretalk)
00164          features |= DSP_FEATURE_SILENCE_SUPPRESS;
00165        */
00166       if (!ignorefax)
00167          features |= DSP_FEATURE_FAX_DETECT;
00168       //if (!ignoredtmf)
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                      /* Fax tone -- Handle and return NULL */
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                            /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
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                         /* They entered a valid extension, or might be anyhow */
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                      /* We've been quiet a little while */
00252                      if (notsilent) {
00253                         /* We had heard some talking */
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                            /* Save detected talk time (in milliseconds) */
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                         /* Heard some audio, mark the begining of the token */
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");

Generated on Mon May 14 04:42:53 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1