Thu Oct 11 06:33:31 2012

Asterisk developer's documentation


app_amd.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2003 - 2006, Aheeva Technology.
00005  *
00006  * Claude Klimos (claude.klimos@aheeva.com)
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  *
00018  * A license has been granted to Digium (via disclaimer) for the use of
00019  * this code.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief Answering machine detection
00025  *
00026  * \author Claude Klimos (claude.klimos@aheeva.com)
00027  *
00028  * \ingroup applications
00029  */
00030 
00031 /*!
00032  * \li The application app_amd uses the configuration file \ref amd.conf
00033  * \addtogroup configuration_file Configuration Files
00034  */
00035 
00036 /*! 
00037  * \page amd.conf amd.conf
00038  * \verbinclude amd.conf.sample
00039  */
00040 
00041 /*** MODULEINFO
00042    <support_level>extended</support_level>
00043  ***/
00044 
00045 #include "asterisk.h"
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374164 $")
00048 
00049 #include "asterisk/module.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/dsp.h"
00053 #include "asterisk/pbx.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/app.h"
00056 
00057 /*** DOCUMENTATION
00058    <application name="AMD" language="en_US">
00059       <synopsis>
00060          Attempt to detect answering machines.
00061       </synopsis>
00062       <syntax>
00063          <parameter name="initialSilence" required="false">
00064             <para>Is maximum initial silence duration before greeting.</para>
00065             <para>If this is exceeded set as MACHINE</para>
00066          </parameter>
00067          <parameter name="greeting" required="false">
00068             <para>is the maximum length of a greeting.</para>
00069             <para>If this is exceeded set as MACHINE</para>
00070          </parameter>
00071          <parameter name="afterGreetingSilence" required="false">
00072             <para>Is the silence after detecting a greeting.</para>
00073             <para>If this is exceeded set as HUMAN</para>
00074          </parameter>
00075          <parameter name="totalAnalysis Time" required="false">
00076             <para>Is the maximum time allowed for the algorithm</para>
00077             <para>to decide HUMAN or MACHINE</para>
00078          </parameter>
00079          <parameter name="miniumWordLength" required="false">
00080             <para>Is the minimum duration of Voice considered to be a word</para>
00081          </parameter>
00082          <parameter name="betweenWordSilence" required="false">
00083             <para>Is the minimum duration of silence after a word to
00084             consider the audio that follows to be a new word</para>
00085          </parameter>
00086          <parameter name="maximumNumberOfWords" required="false">
00087             <para>Is the maximum number of words in a greeting</para>
00088             <para>If this is exceeded set as MACHINE</para>
00089          </parameter>
00090          <parameter name="silenceThreshold" required="false">
00091             <para>How long do we consider silence</para>
00092          </parameter>
00093          <parameter name="maximumWordLength" required="false">
00094             <para>Is the maximum duration of a word to accept.</para>
00095             <para>If exceeded set as MACHINE</para>
00096          </parameter>
00097       </syntax>
00098       <description>
00099          <para>This application attempts to detect answering machines at the beginning
00100          of outbound calls. Simply call this application after the call
00101          has been answered (outbound only, of course).</para>
00102          <para>When loaded, AMD reads amd.conf and uses the parameters specified as
00103          default values. Those default values get overwritten when the calling AMD
00104          with parameters.</para>
00105          <para>This application sets the following channel variables:</para>
00106          <variablelist>
00107             <variable name="AMDSTATUS">
00108                <para>This is the status of the answering machine detection</para>
00109                <value name="MACHINE" />
00110                <value name="HUMAN" />
00111                <value name="NOTSURE" />
00112                <value name="HANGUP" />
00113             </variable>
00114             <variable name="AMDCAUSE">
00115                <para>Indicates the cause that led to the conclusion</para>
00116                <value name="TOOLONG">
00117                   Total Time.
00118                </value>
00119                <value name="INITIALSILENCE">
00120                   Silence Duration - Initial Silence.
00121                </value>
00122                <value name="HUMAN">
00123                   Silence Duration - afterGreetingSilence.
00124                </value>
00125                <value name="LONGGREETING">
00126                   Voice Duration - Greeting.
00127                </value>
00128                <value name="MAXWORDLENGTH">
00129                   Word Count - maximum number of words.
00130                </value> 
00131             </variable>
00132          </variablelist>
00133       </description>
00134       <see-also>
00135          <ref type="application">WaitForSilence</ref>
00136          <ref type="application">WaitForNoise</ref>
00137       </see-also>
00138    </application>
00139 
00140  ***/
00141 
00142 static const char app[] = "AMD";
00143 
00144 #define STATE_IN_WORD       1
00145 #define STATE_IN_SILENCE    2
00146 
00147 /* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
00148 static int dfltInitialSilence       = 2500;
00149 static int dfltGreeting             = 1500;
00150 static int dfltAfterGreetingSilence = 800;
00151 static int dfltTotalAnalysisTime    = 5000;
00152 static int dfltMinimumWordLength    = 100;
00153 static int dfltBetweenWordsSilence  = 50;
00154 static int dfltMaximumNumberOfWords = 3;
00155 static int dfltSilenceThreshold     = 256;
00156 static int dfltMaximumWordLength    = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
00157 
00158 /* Set to the lowest ms value provided in amd.conf or application parameters */
00159 static int dfltMaxWaitTimeForFrame  = 50;
00160 
00161 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
00162 {
00163    int res = 0;
00164    struct ast_frame *f = NULL;
00165    struct ast_dsp *silenceDetector = NULL;
00166    int dspsilence = 0, framelength = 0;
00167    struct ast_format readFormat;
00168    int inInitialSilence = 1;
00169    int inGreeting = 0;
00170    int voiceDuration = 0;
00171    int silenceDuration = 0;
00172    int iTotalTime = 0;
00173    int iWordsCount = 0;
00174    int currentState = STATE_IN_WORD;
00175    int consecutiveVoiceDuration = 0;
00176    char amdCause[256] = "", amdStatus[256] = "";
00177    char *parse = ast_strdupa(data);
00178 
00179    /* Lets set the initial values of the variables that will control the algorithm.
00180       The initial values are the default ones. If they are passed as arguments
00181       when invoking the application, then the default values will be overwritten
00182       by the ones passed as parameters. */
00183    int initialSilence       = dfltInitialSilence;
00184    int greeting             = dfltGreeting;
00185    int afterGreetingSilence = dfltAfterGreetingSilence;
00186    int totalAnalysisTime    = dfltTotalAnalysisTime;
00187    int minimumWordLength    = dfltMinimumWordLength;
00188    int betweenWordsSilence  = dfltBetweenWordsSilence;
00189    int maximumNumberOfWords = dfltMaximumNumberOfWords;
00190    int silenceThreshold     = dfltSilenceThreshold;
00191    int maximumWordLength    = dfltMaximumWordLength;
00192    int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
00193 
00194    AST_DECLARE_APP_ARGS(args,
00195       AST_APP_ARG(argInitialSilence);
00196       AST_APP_ARG(argGreeting);
00197       AST_APP_ARG(argAfterGreetingSilence);
00198       AST_APP_ARG(argTotalAnalysisTime);
00199       AST_APP_ARG(argMinimumWordLength);
00200       AST_APP_ARG(argBetweenWordsSilence);
00201       AST_APP_ARG(argMaximumNumberOfWords);
00202       AST_APP_ARG(argSilenceThreshold);
00203       AST_APP_ARG(argMaximumWordLength);
00204    );
00205 
00206    ast_format_clear(&readFormat);
00207    ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
00208       S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
00209       S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
00210       ast_getformatname(ast_channel_readformat(chan)));
00211 
00212    /* Lets parse the arguments. */
00213    if (!ast_strlen_zero(parse)) {
00214       /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
00215       AST_STANDARD_APP_ARGS(args, parse);
00216       if (!ast_strlen_zero(args.argInitialSilence))
00217          initialSilence = atoi(args.argInitialSilence);
00218       if (!ast_strlen_zero(args.argGreeting))
00219          greeting = atoi(args.argGreeting);
00220       if (!ast_strlen_zero(args.argAfterGreetingSilence))
00221          afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00222       if (!ast_strlen_zero(args.argTotalAnalysisTime))
00223          totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00224       if (!ast_strlen_zero(args.argMinimumWordLength))
00225          minimumWordLength = atoi(args.argMinimumWordLength);
00226       if (!ast_strlen_zero(args.argBetweenWordsSilence))
00227          betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00228       if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00229          maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00230       if (!ast_strlen_zero(args.argSilenceThreshold))
00231          silenceThreshold = atoi(args.argSilenceThreshold);
00232       if (!ast_strlen_zero(args.argMaximumWordLength))
00233          maximumWordLength = atoi(args.argMaximumWordLength);
00234    } else {
00235       ast_debug(1, "AMD using the default parameters.\n");
00236    }
00237 
00238    /* Find lowest ms value, that will be max wait time for a frame */
00239    if (maxWaitTimeForFrame > initialSilence)
00240       maxWaitTimeForFrame = initialSilence;
00241    if (maxWaitTimeForFrame > greeting)
00242       maxWaitTimeForFrame = greeting;
00243    if (maxWaitTimeForFrame > afterGreetingSilence)
00244       maxWaitTimeForFrame = afterGreetingSilence;
00245    if (maxWaitTimeForFrame > totalAnalysisTime)
00246       maxWaitTimeForFrame = totalAnalysisTime;
00247    if (maxWaitTimeForFrame > minimumWordLength)
00248       maxWaitTimeForFrame = minimumWordLength;
00249    if (maxWaitTimeForFrame > betweenWordsSilence)
00250       maxWaitTimeForFrame = betweenWordsSilence;
00251 
00252    /* Now we're ready to roll! */
00253    ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00254       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
00255             initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00256             minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
00257 
00258    /* Set read format to signed linear so we get signed linear frames in */
00259    ast_format_copy(&readFormat, ast_channel_readformat(chan));
00260    if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0 ) {
00261       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
00262       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00263       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00264       return;
00265    }
00266 
00267    /* Create a new DSP that will detect the silence */
00268    if (!(silenceDetector = ast_dsp_new())) {
00269       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
00270       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00271       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00272       return;
00273    }
00274 
00275    /* Set silence threshold to specified value */
00276    ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00277 
00278    /* Now we go into a loop waiting for frames from the channel */
00279    while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00280 
00281       /* If we fail to read in a frame, that means they hung up */
00282       if (!(f = ast_read(chan))) {
00283          ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
00284          ast_debug(1, "Got hangup\n");
00285          strcpy(amdStatus, "HANGUP");
00286          res = 1;
00287          break;
00288       }
00289 
00290       if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00291          /* If the total time exceeds the analysis time then give up as we are not too sure */
00292          if (f->frametype == AST_FRAME_VOICE) {
00293             framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00294          } else {
00295             framelength = 2 * maxWaitTimeForFrame;
00296          }
00297 
00298          iTotalTime += framelength;
00299          if (iTotalTime >= totalAnalysisTime) {
00300             ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00301             ast_frfree(f);
00302             strcpy(amdStatus , "NOTSURE");
00303             sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00304             break;
00305          }
00306 
00307          /* Feed the frame of audio into the silence detector and see if we get a result */
00308          if (f->frametype != AST_FRAME_VOICE)
00309             dspsilence += 2 * maxWaitTimeForFrame;
00310          else {
00311             dspsilence = 0;
00312             ast_dsp_silence(silenceDetector, f, &dspsilence);
00313          }
00314 
00315          if (dspsilence > 0) {
00316             silenceDuration = dspsilence;
00317             
00318             if (silenceDuration >= betweenWordsSilence) {
00319                if (currentState != STATE_IN_SILENCE ) {
00320                   ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
00321                }
00322                /* Find words less than word duration */
00323                if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
00324                   ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
00325                }
00326                currentState  = STATE_IN_SILENCE;
00327                consecutiveVoiceDuration = 0;
00328             }
00329 
00330             if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
00331                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00332                   ast_channel_name(chan), silenceDuration, initialSilence);
00333                ast_frfree(f);
00334                strcpy(amdStatus , "MACHINE");
00335                sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00336                res = 1;
00337                break;
00338             }
00339             
00340             if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
00341                ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00342                   ast_channel_name(chan), silenceDuration, afterGreetingSilence);
00343                ast_frfree(f);
00344                strcpy(amdStatus , "HUMAN");
00345                sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00346                res = 1;
00347                break;
00348             }
00349             
00350          } else {
00351             consecutiveVoiceDuration += framelength;
00352             voiceDuration += framelength;
00353 
00354             /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
00355                number of words if my previous state was Silence, which means that I moved into a word. */
00356             if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00357                iWordsCount++;
00358                ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00359                currentState = STATE_IN_WORD;
00360             }
00361             if (consecutiveVoiceDuration >= maximumWordLength){
00362                ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
00363                ast_frfree(f);
00364                strcpy(amdStatus , "MACHINE");
00365                sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
00366                break;
00367             }
00368             if (iWordsCount >= maximumNumberOfWords) {
00369                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00370                ast_frfree(f);
00371                strcpy(amdStatus , "MACHINE");
00372                sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00373                res = 1;
00374                break;
00375             }
00376 
00377             if (inGreeting == 1 && voiceDuration >= greeting) {
00378                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
00379                ast_frfree(f);
00380                strcpy(amdStatus , "MACHINE");
00381                sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00382                res = 1;
00383                break;
00384             }
00385 
00386             if (voiceDuration >= minimumWordLength ) {
00387                if (silenceDuration > 0)
00388                   ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
00389                silenceDuration = 0;
00390             }
00391             if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
00392                /* Only go in here once to change the greeting flag when we detect the 1st word */
00393                if (silenceDuration > 0)
00394                   ast_verb(3, "AMD: Channel [%s]. Before Greeting Time:  silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
00395                inInitialSilence = 0;
00396                inGreeting = 1;
00397             }
00398             
00399          }
00400       }
00401       ast_frfree(f);
00402    }
00403    
00404    if (!res) {
00405       /* It took too long to get a frame back. Giving up. */
00406       ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00407       strcpy(amdStatus , "NOTSURE");
00408       sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00409    }
00410 
00411    /* Set the status and cause on the channel */
00412    pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00413    pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00414 
00415    /* Restore channel read format */
00416    if (readFormat.id && ast_set_read_format(chan, &readFormat))
00417       ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
00418 
00419    /* Free the DSP used to detect silence */
00420    ast_dsp_free(silenceDetector);
00421 
00422    return;
00423 }
00424 
00425 
00426 static int amd_exec(struct ast_channel *chan, const char *data)
00427 {
00428    isAnsweringMachine(chan, data);
00429 
00430    return 0;
00431 }
00432 
00433 static int load_config(int reload)
00434 {
00435    struct ast_config *cfg = NULL;
00436    char *cat = NULL;
00437    struct ast_variable *var = NULL;
00438    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00439 
00440    dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00441 
00442    if (!(cfg = ast_config_load("amd.conf", config_flags))) {
00443       ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00444       return -1;
00445    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00446       return 0;
00447    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00448       ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format.  Aborting.\n");
00449       return -1;
00450    }
00451 
00452    cat = ast_category_browse(cfg, NULL);
00453 
00454    while (cat) {
00455       if (!strcasecmp(cat, "general") ) {
00456          var = ast_variable_browse(cfg, cat);
00457          while (var) {
00458             if (!strcasecmp(var->name, "initial_silence")) {
00459                dfltInitialSilence = atoi(var->value);
00460             } else if (!strcasecmp(var->name, "greeting")) {
00461                dfltGreeting = atoi(var->value);
00462             } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00463                dfltAfterGreetingSilence = atoi(var->value);
00464             } else if (!strcasecmp(var->name, "silence_threshold")) {
00465                dfltSilenceThreshold = atoi(var->value);
00466             } else if (!strcasecmp(var->name, "total_analysis_time")) {
00467                dfltTotalAnalysisTime = atoi(var->value);
00468             } else if (!strcasecmp(var->name, "min_word_length")) {
00469                dfltMinimumWordLength = atoi(var->value);
00470             } else if (!strcasecmp(var->name, "between_words_silence")) {
00471                dfltBetweenWordsSilence = atoi(var->value);
00472             } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00473                dfltMaximumNumberOfWords = atoi(var->value);
00474             } else if (!strcasecmp(var->name, "maximum_word_length")) {
00475                dfltMaximumWordLength = atoi(var->value);
00476 
00477             } else {
00478                ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00479                   app, cat, var->name, var->lineno);
00480             }
00481             var = var->next;
00482          }
00483       }
00484       cat = ast_category_browse(cfg, cat);
00485    }
00486 
00487    ast_config_destroy(cfg);
00488 
00489    ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00490       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
00491       dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00492       dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
00493 
00494    return 0;
00495 }
00496 
00497 static int unload_module(void)
00498 {
00499    return ast_unregister_application(app);
00500 }
00501 
00502 /*!
00503  * \brief Load the module
00504  *
00505  * Module loading including tests for configuration or dependencies.
00506  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
00507  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
00508  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
00509  * configuration file or other non-critical problem return 
00510  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
00511  */
00512 static int load_module(void)
00513 {
00514    if (load_config(0))
00515       return AST_MODULE_LOAD_DECLINE;
00516    if (ast_register_application_xml(app, amd_exec))
00517       return AST_MODULE_LOAD_FAILURE;
00518    return AST_MODULE_LOAD_SUCCESS;
00519 }
00520 
00521 static int reload(void)
00522 {
00523    if (load_config(1))
00524       return AST_MODULE_LOAD_DECLINE;
00525    return AST_MODULE_LOAD_SUCCESS;
00526 }
00527 
00528 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00529       .load = load_module,
00530       .unload = unload_module,
00531       .reload = reload,
00532 );

Generated on Thu Oct 11 06:33:31 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6