Mon Mar 31 07:37:55 2008

Asterisk developer's documentation


app_nv_faxdetect.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Fax detection application for all channel types.
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 #include <asterisk.h>
00027 
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/dsp.h>
00037 #include <asterisk/utils.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040 static char *app = "NVFaxDetect";
00041 static char *synopsis = "Detects fax sounds on all channel types (IAX and SIP too)";
00042 static char *descrip = "  NVFaxDetect([waitdur[|options[|sildur[|mindur[|maxdur]]]]]):\n"
00043    "This application listens for fax tones (on IAX and SIP channels too)\n"
00044    "for waitdur seconds of time. In addition, it can be interrupted by digits,\n"
00045    "or non-silence. Audio is only monitored in the receive direction. If\n"
00046    "digits interrupt, they must be the start of a valid extension unless the\n"
00047    "option is included to ignore. If fax is detected, it will jump to the\n"
00048    "'fax' extension. If a period of non-silence greater than 'mindur' ms,\n"
00049    "yet less than 'maxdur' ms is followed by silence at least 'sildur' ms\n"
00050    "then the app is aborted and processing jumps to the 'talk' extension.\n"
00051    "If all undetected, control will continue at the next priority.\n"
00052    "      waitdur:  Maximum number of seconds to wait (default=4)\n"
00053    "      options:\n"
00054    "        'n':  Attempt on-hook if unanswered (default=no)\n"
00055    "        'x':  DTMF digits terminate without extension (default=no)\n"
00056    "        'd':  Ignore DTMF digit detection (default=no)\n"
00057    "        'f':  Ignore fax detection (default=no)\n"
00058    "        't':  Ignore talk detection (default=no)\n"
00059    "      sildur:  Silence ms after mindur/maxdur before aborting (default=1000)\n"
00060    "      mindur:  Minimum non-silence ms needed (default=100)\n"
00061    "      maxdur:  Maximum non-silence ms allowed (default=0/forever)\n"
00062    "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";
00063 
00064 #define CALLERID_FIELD cid.cid_num
00065 static int nv_detectfax_exec(struct ast_channel *chan, void *data)
00066 {
00067    int res = 0;
00068    struct ast_module_user *u;
00069    char tmp[256] = "\0";
00070    char *p = NULL;
00071    char *waitstr = NULL;
00072    char *options = NULL;
00073    char *silstr = NULL;
00074    char *minstr = NULL;
00075    char *maxstr = NULL;
00076    struct ast_frame *fr = NULL;
00077    struct ast_frame *fr2 = NULL;
00078    int notsilent = 0;
00079    struct timeval start = { 0, 0 }, end = {
00080    0, 0};
00081    int waitdur = 4;
00082    int sildur = 1000;
00083    int mindur = 100;
00084    int maxdur = -1;
00085    int skipanswer = 0;
00086    int noextneeded = 0;
00087    int ignoredtmf = 0;
00088    int ignorefax = 0;
00089    int ignoretalk = 0;
00090    int x = 0;
00091    int origrformat = 0;
00092    int features = 0;
00093    time_t timeout = 0;
00094    struct ast_dsp *dsp = NULL;
00095    pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "");
00096    pbx_builtin_setvar_helper(chan, "FAXEXTEN", "");
00097    pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "");
00098    pbx_builtin_setvar_helper(chan, "TALK_DETECTED", "");
00099    if (data || !ast_strlen_zero((char *) data)) {
00100       strncpy(tmp, (char *) data, sizeof(tmp) - 1);
00101    }
00102    p = tmp;
00103    waitstr = strsep(&p, "|");
00104    options = strsep(&p, "|");
00105    silstr = strsep(&p, "|");
00106    minstr = strsep(&p, "|");
00107    maxstr = strsep(&p, "|");
00108    if (waitstr) {
00109       if ((sscanf(waitstr, "%d", &x) == 1) && (x > 0))
00110          waitdur = x;
00111    }
00112    if (options) {
00113       if (strchr(options, 'n'))
00114          skipanswer = 1;
00115       if (strchr(options, 'x'))
00116          noextneeded = 1;
00117       if (strchr(options, 'd'))
00118          ignoredtmf = 1;
00119       if (strchr(options, 'f'))
00120          ignorefax = 1;
00121       if (strchr(options, 't'))
00122          ignoretalk = 1;
00123    }
00124    if (silstr) {
00125       if ((sscanf(silstr, "%d", &x) == 1) && (x > 0))
00126          sildur = x;
00127    }
00128    if (minstr) {
00129       if ((sscanf(minstr, "%d", &x) == 1) && (x > 0))
00130          mindur = x;
00131    }
00132    if (maxstr) {
00133       if ((sscanf(maxstr, "%d", &x) == 1) && (x > 0))
00134          maxdur = x;
00135    }
00136    ast_log(LOG_DEBUG, "Preparing detect of fax (waitdur=%dms, sildur=%dms, mindur=%dms, maxdur=%dms)\n", waitdur, sildur, mindur, maxdur);
00137    u = ast_module_user_add(chan);
00138    if (chan->_state != AST_STATE_UP && !skipanswer) {
00139 
00140       /* Otherwise answer unless we're supposed to send this while on-hook */
00141       res = ast_answer(chan);
00142    }
00143    if (!res) {
00144       origrformat = chan->readformat;
00145       if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)))
00146          ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
00147    }
00148    if (!(dsp = ast_dsp_new())) {
00149       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
00150       res = -1;
00151    }
00152    if (dsp) {
00153       /*
00154          if (!ignoretalk)
00155          features |= DSP_FEATURE_SILENCE_SUPPRESS;
00156        */
00157       if (!ignorefax)
00158          features |= DSP_FEATURE_FAX_DETECT;
00159 
00160       //if (!ignoredtmf)
00161       features |= DSP_FEATURE_DTMF_DETECT;
00162       ast_dsp_set_threshold(dsp, 256);
00163       ast_dsp_set_features(dsp, features | DSP_DIGITMODE_RELAXDTMF);
00164       ast_dsp_digitmode(dsp, DSP_DIGITMODE_DTMF);
00165    }
00166    if (!res) {
00167       if (waitdur > 0)
00168          timeout = time(NULL) + (time_t) waitdur;
00169       while (ast_waitfor(chan, -1) > -1) {
00170          if (waitdur > 0 && time(NULL) > timeout) {
00171             res = 0;
00172             break;
00173          }
00174          fr = ast_read(chan);
00175          if (!fr) {
00176             ast_log(LOG_DEBUG, "Got hangup\n");
00177             res = -1;
00178             break;
00179          }
00180          fr2 = ast_dsp_process(chan, dsp, fr);
00181          if (!fr2) {
00182             ast_log(LOG_WARNING, "Bad DSP received (what happened?)\n");
00183             fr2 = fr;
00184          }
00185          if (fr2->frametype == AST_FRAME_DTMF) {
00186             if (fr2->subclass == 'f' && !ignorefax) {
00187 
00188                /* Fax tone -- Handle and return NULL */
00189                ast_log(LOG_DEBUG, "Fax detected on %s\n", chan->name);
00190                if (strcmp(chan->exten, "fax")) {
00191                   ast_log(LOG_NOTICE, "Redirecting %s to fax extension\n", chan->name);
00192                   pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "1");
00193                   pbx_builtin_setvar_helper(chan, "FAXEXTEN", chan->exten);
00194                   if (ast_exists_extension(chan, chan->context, "fax", 1, chan->CALLERID_FIELD)) {
00195 
00196                      /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
00197                      strncpy(chan->exten, "fax", sizeof(chan->exten) - 1);
00198                      chan->priority = 0;
00199                   } else
00200                      ast_log(LOG_WARNING, "Fax detected, but no fax extension\n");
00201                } else
00202                   ast_log(LOG_WARNING, "Already in a fax extension, not redirecting\n");
00203                res = 0;
00204                ast_frfree(fr);
00205                break;
00206             } else if (!ignoredtmf) {
00207                ast_log(LOG_DEBUG, "DTMF detected on %s\n", chan->name);
00208                char t[2];
00209                t[0] = fr2->subclass;
00210                t[1] = '\0';
00211                if (noextneeded || ast_canmatch_extension(chan, chan->context, t, 1, chan->CALLERID_FIELD)) {
00212                   pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "1");
00213 
00214                   /* They entered a valid extension, or might be anyhow */
00215                   if (noextneeded) {
00216                      ast_log(LOG_NOTICE, "DTMF received (not matching to exten)\n");
00217                      res = 0;
00218                   } else {
00219                      ast_log(LOG_NOTICE, "DTMF received (matching to exten)\n");
00220                      res = fr2->subclass;
00221                   }
00222                   ast_frfree(fr);
00223                   break;
00224                } else
00225                   ast_log(LOG_DEBUG, "Valid extension requested and DTMF did not match\n");
00226             }
00227          } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR) && !ignoretalk) {
00228             int totalsilence;
00229             int ms;
00230             res = ast_dsp_silence(dsp, fr, &totalsilence);
00231             if (res && (totalsilence > sildur)) {
00232 
00233                /* We've been quiet a little while */
00234                if (notsilent) {
00235 
00236                   /* We had heard some talking */
00237                   gettimeofday(&end, NULL);
00238                   ms = (end.tv_sec - start.tv_sec) * 1000;
00239                   ms += (end.tv_usec - start.tv_usec) / 1000;
00240                   ms -= sildur;
00241                   if (ms < 0)
00242                      ms = 0;
00243                   if ((ms > mindur) && ((maxdur < 0) || (ms < maxdur))) {
00244                      char ms_str[10];
00245                      ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms);
00246                      ast_log(LOG_NOTICE, "Redirecting %s to talk extension\n", chan->name);
00247 
00248                      /* Save detected talk time (in milliseconds) */
00249                      sprintf(ms_str, "%d", ms);
00250                      pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
00251                      if (ast_exists_extension(chan, chan->context, "talk", 1, chan->CALLERID_FIELD)) {
00252                         strncpy(chan->exten, "talk", sizeof(chan->exten) - 1);
00253                         chan->priority = 0;
00254                      } else
00255                         ast_log(LOG_WARNING, "Talk detected, but no talk extension\n");
00256                      res = 0;
00257                      ast_frfree(fr);
00258                      break;
00259                   } else
00260                      ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms);
00261                   notsilent = 0;
00262                }
00263             } else {
00264                if (!notsilent) {
00265 
00266                   /* Heard some audio, mark the begining of the token */
00267                   gettimeofday(&start, NULL);
00268                   ast_log(LOG_DEBUG, "Start of voice token!\n");
00269                   notsilent = 1;
00270                }
00271             }
00272          }
00273          ast_frfree(fr);
00274       }
00275    } else
00276       ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00277    if (res > -1) {
00278       if (origrformat && ast_set_read_format(chan, origrformat)) {
00279          ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", chan->name, ast_getformatname(origrformat));
00280       }
00281    }
00282    if (dsp)
00283       ast_dsp_free(dsp);
00284    ast_module_user_remove(u);
00285    return res;
00286 }
00287 static int unload_module(void)
00288 {
00289    ast_module_user_hangup_all();
00290    return ast_unregister_application(app);
00291 }
00292 static int load_module(void)
00293 {
00294    return ast_register_application(app, nv_detectfax_exec, synopsis, descrip);
00295 }
00296 
00297 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Newman's fax detection application");

Generated on Mon Mar 31 07:37:55 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1