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 /*! \li \ref app_amd.c uses the configuration file \ref amd.conf
00032  * \addtogroup configuration_file Configuration Files
00033  */
00034 
00035 /*! 
00036  * \page amd.conf amd.conf
00037  * \verbinclude amd.conf.sample
00038  */
00039 
00040 /*** MODULEINFO
00041    <support_level>extended</support_level>
00042  ***/
00043 
00044 #include "asterisk.h"
00045 
00046 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 432921 $")
00047 
00048 #include "asterisk/module.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/dsp.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/config.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/format_cache.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 Length - max length of a single word.
00130                </value>
00131                <value name="MAXWORDS">
00132                   Word Count - maximum number of words.
00133                </value> 
00134             </variable>
00135          </variablelist>
00136       </description>
00137       <see-also>
00138          <ref type="application">WaitForSilence</ref>
00139          <ref type="application">WaitForNoise</ref>
00140       </see-also>
00141    </application>
00142 
00143  ***/
00144 
00145 static const char app[] = "AMD";
00146 
00147 #define STATE_IN_WORD       1
00148 #define STATE_IN_SILENCE    2
00149 
00150 /* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
00151 static int dfltInitialSilence       = 2500;
00152 static int dfltGreeting             = 1500;
00153 static int dfltAfterGreetingSilence = 800;
00154 static int dfltTotalAnalysisTime    = 5000;
00155 static int dfltMinimumWordLength    = 100;
00156 static int dfltBetweenWordsSilence  = 50;
00157 static int dfltMaximumNumberOfWords = 3;
00158 static int dfltSilenceThreshold     = 256;
00159 static int dfltMaximumWordLength    = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
00160 
00161 /* Set to the lowest ms value provided in amd.conf or application parameters */
00162 static int dfltMaxWaitTimeForFrame  = 50;
00163 
00164 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
00165 {
00166    int res = 0;
00167    struct ast_frame *f = NULL;
00168    struct ast_dsp *silenceDetector = NULL;
00169    int dspsilence = 0, framelength = 0;
00170    RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
00171    int inInitialSilence = 1;
00172    int inGreeting = 0;
00173    int voiceDuration = 0;
00174    int silenceDuration = 0;
00175    int iTotalTime = 0;
00176    int iWordsCount = 0;
00177    int currentState = STATE_IN_WORD;
00178    int consecutiveVoiceDuration = 0;
00179    char amdCause[256] = "", amdStatus[256] = "";
00180    char *parse = ast_strdupa(data);
00181 
00182    /* Lets set the initial values of the variables that will control the algorithm.
00183       The initial values are the default ones. If they are passed as arguments
00184       when invoking the application, then the default values will be overwritten
00185       by the ones passed as parameters. */
00186    int initialSilence       = dfltInitialSilence;
00187    int greeting             = dfltGreeting;
00188    int afterGreetingSilence = dfltAfterGreetingSilence;
00189    int totalAnalysisTime    = dfltTotalAnalysisTime;
00190    int minimumWordLength    = dfltMinimumWordLength;
00191    int betweenWordsSilence  = dfltBetweenWordsSilence;
00192    int maximumNumberOfWords = dfltMaximumNumberOfWords;
00193    int silenceThreshold     = dfltSilenceThreshold;
00194    int maximumWordLength    = dfltMaximumWordLength;
00195    int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
00196 
00197    AST_DECLARE_APP_ARGS(args,
00198       AST_APP_ARG(argInitialSilence);
00199       AST_APP_ARG(argGreeting);
00200       AST_APP_ARG(argAfterGreetingSilence);
00201       AST_APP_ARG(argTotalAnalysisTime);
00202       AST_APP_ARG(argMinimumWordLength);
00203       AST_APP_ARG(argBetweenWordsSilence);
00204       AST_APP_ARG(argMaximumNumberOfWords);
00205       AST_APP_ARG(argSilenceThreshold);
00206       AST_APP_ARG(argMaximumWordLength);
00207    );
00208 
00209    ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
00210       S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
00211       S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
00212       ast_format_get_name(ast_channel_readformat(chan)));
00213 
00214    /* Lets parse the arguments. */
00215    if (!ast_strlen_zero(parse)) {
00216       /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
00217       AST_STANDARD_APP_ARGS(args, parse);
00218       if (!ast_strlen_zero(args.argInitialSilence))
00219          initialSilence = atoi(args.argInitialSilence);
00220       if (!ast_strlen_zero(args.argGreeting))
00221          greeting = atoi(args.argGreeting);
00222       if (!ast_strlen_zero(args.argAfterGreetingSilence))
00223          afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00224       if (!ast_strlen_zero(args.argTotalAnalysisTime))
00225          totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00226       if (!ast_strlen_zero(args.argMinimumWordLength))
00227          minimumWordLength = atoi(args.argMinimumWordLength);
00228       if (!ast_strlen_zero(args.argBetweenWordsSilence))
00229          betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00230       if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00231          maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00232       if (!ast_strlen_zero(args.argSilenceThreshold))
00233          silenceThreshold = atoi(args.argSilenceThreshold);
00234       if (!ast_strlen_zero(args.argMaximumWordLength))
00235          maximumWordLength = atoi(args.argMaximumWordLength);
00236    } else {
00237       ast_debug(1, "AMD using the default parameters.\n");
00238    }
00239 
00240    /* Find lowest ms value, that will be max wait time for a frame */
00241    if (maxWaitTimeForFrame > initialSilence)
00242       maxWaitTimeForFrame = initialSilence;
00243    if (maxWaitTimeForFrame > greeting)
00244       maxWaitTimeForFrame = greeting;
00245    if (maxWaitTimeForFrame > afterGreetingSilence)
00246       maxWaitTimeForFrame = afterGreetingSilence;
00247    if (maxWaitTimeForFrame > totalAnalysisTime)
00248       maxWaitTimeForFrame = totalAnalysisTime;
00249    if (maxWaitTimeForFrame > minimumWordLength)
00250       maxWaitTimeForFrame = minimumWordLength;
00251    if (maxWaitTimeForFrame > betweenWordsSilence)
00252       maxWaitTimeForFrame = betweenWordsSilence;
00253 
00254    /* Now we're ready to roll! */
00255    ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00256       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
00257             initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00258             minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
00259 
00260    /* Set read format to signed linear so we get signed linear frames in */
00261    readFormat = ao2_bump(ast_channel_readformat(chan));
00262    if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
00263       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
00264       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00265       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00266       return;
00267    }
00268 
00269    /* Create a new DSP that will detect the silence */
00270    if (!(silenceDetector = ast_dsp_new())) {
00271       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
00272       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00273       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00274       return;
00275    }
00276 
00277    /* Set silence threshold to specified value */
00278    ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00279 
00280    /* Now we go into a loop waiting for frames from the channel */
00281    while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00282 
00283       /* If we fail to read in a frame, that means they hung up */
00284       if (!(f = ast_read(chan))) {
00285          ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
00286          ast_debug(1, "Got hangup\n");
00287          strcpy(amdStatus, "HANGUP");
00288          res = 1;
00289          break;
00290       }
00291 
00292       if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00293          /* If the total time exceeds the analysis time then give up as we are not too sure */
00294          if (f->frametype == AST_FRAME_VOICE) {
00295             framelength = (ast_codec_samples_count(f) / DEFAULT_SAMPLES_PER_MS);
00296          } else {
00297             framelength = 2 * maxWaitTimeForFrame;
00298          }
00299 
00300          iTotalTime += framelength;
00301          if (iTotalTime >= totalAnalysisTime) {
00302             ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00303             ast_frfree(f);
00304             strcpy(amdStatus , "NOTSURE");
00305             sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00306             break;
00307          }
00308 
00309          /* Feed the frame of audio into the silence detector and see if we get a result */
00310          if (f->frametype != AST_FRAME_VOICE)
00311             dspsilence += 2 * maxWaitTimeForFrame;
00312          else {
00313             dspsilence = 0;
00314             ast_dsp_silence(silenceDetector, f, &dspsilence);
00315          }
00316 
00317          if (dspsilence > 0) {
00318             silenceDuration = dspsilence;
00319             
00320             if (silenceDuration >= betweenWordsSilence) {
00321                if (currentState != STATE_IN_SILENCE ) {
00322                   ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
00323                }
00324                /* Find words less than word duration */
00325                if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
00326                   ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
00327                }
00328                currentState  = STATE_IN_SILENCE;
00329                consecutiveVoiceDuration = 0;
00330             }
00331 
00332             if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
00333                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00334                   ast_channel_name(chan), silenceDuration, initialSilence);
00335                ast_frfree(f);
00336                strcpy(amdStatus , "MACHINE");
00337                sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00338                res = 1;
00339                break;
00340             }
00341             
00342             if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
00343                ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00344                   ast_channel_name(chan), silenceDuration, afterGreetingSilence);
00345                ast_frfree(f);
00346                strcpy(amdStatus , "HUMAN");
00347                sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00348                res = 1;
00349                break;
00350             }
00351             
00352          } else {
00353             consecutiveVoiceDuration += framelength;
00354             voiceDuration += framelength;
00355 
00356             /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
00357                number of words if my previous state was Silence, which means that I moved into a word. */
00358             if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00359                iWordsCount++;
00360                ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00361                currentState = STATE_IN_WORD;
00362             }
00363             if (consecutiveVoiceDuration >= maximumWordLength){
00364                ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
00365                ast_frfree(f);
00366                strcpy(amdStatus , "MACHINE");
00367                sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
00368                break;
00369             }
00370             if (iWordsCount >= maximumNumberOfWords) {
00371                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00372                ast_frfree(f);
00373                strcpy(amdStatus , "MACHINE");
00374                sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00375                res = 1;
00376                break;
00377             }
00378 
00379             if (inGreeting == 1 && voiceDuration >= greeting) {
00380                ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
00381                ast_frfree(f);
00382                strcpy(amdStatus , "MACHINE");
00383                sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00384                res = 1;
00385                break;
00386             }
00387 
00388             if (voiceDuration >= minimumWordLength ) {
00389                if (silenceDuration > 0)
00390                   ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
00391                silenceDuration = 0;
00392             }
00393             if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
00394                /* Only go in here once to change the greeting flag when we detect the 1st word */
00395                if (silenceDuration > 0)
00396                   ast_verb(3, "AMD: Channel [%s]. Before Greeting Time:  silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
00397                inInitialSilence = 0;
00398                inGreeting = 1;
00399             }
00400             
00401          }
00402       }
00403       ast_frfree(f);
00404    }
00405    
00406    if (!res) {
00407       /* It took too long to get a frame back. Giving up. */
00408       ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00409       strcpy(amdStatus , "NOTSURE");
00410       sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00411    }
00412 
00413    /* Set the status and cause on the channel */
00414    pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00415    pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00416 
00417    /* Restore channel read format */
00418    if (readFormat && ast_set_read_format(chan, readFormat))
00419       ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
00420 
00421    /* Free the DSP used to detect silence */
00422    ast_dsp_free(silenceDetector);
00423 
00424    return;
00425 }
00426 
00427 
00428 static int amd_exec(struct ast_channel *chan, const char *data)
00429 {
00430    isAnsweringMachine(chan, data);
00431 
00432    return 0;
00433 }
00434 
00435 static int load_config(int reload)
00436 {
00437    struct ast_config *cfg = NULL;
00438    char *cat = NULL;
00439    struct ast_variable *var = NULL;
00440    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00441 
00442    dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00443 
00444    if (!(cfg = ast_config_load("amd.conf", config_flags))) {
00445       ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00446       return -1;
00447    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00448       return 0;
00449    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00450       ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format.  Aborting.\n");
00451       return -1;
00452    }
00453 
00454    cat = ast_category_browse(cfg, NULL);
00455 
00456    while (cat) {
00457       if (!strcasecmp(cat, "general") ) {
00458          var = ast_variable_browse(cfg, cat);
00459          while (var) {
00460             if (!strcasecmp(var->name, "initial_silence")) {
00461                dfltInitialSilence = atoi(var->value);
00462             } else if (!strcasecmp(var->name, "greeting")) {
00463                dfltGreeting = atoi(var->value);
00464             } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00465                dfltAfterGreetingSilence = atoi(var->value);
00466             } else if (!strcasecmp(var->name, "silence_threshold")) {
00467                dfltSilenceThreshold = atoi(var->value);
00468             } else if (!strcasecmp(var->name, "total_analysis_time")) {
00469                dfltTotalAnalysisTime = atoi(var->value);
00470             } else if (!strcasecmp(var->name, "min_word_length")) {
00471                dfltMinimumWordLength = atoi(var->value);
00472             } else if (!strcasecmp(var->name, "between_words_silence")) {
00473                dfltBetweenWordsSilence = atoi(var->value);
00474             } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00475                dfltMaximumNumberOfWords = atoi(var->value);
00476             } else if (!strcasecmp(var->name, "maximum_word_length")) {
00477                dfltMaximumWordLength = atoi(var->value);
00478 
00479             } else {
00480                ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00481                   app, cat, var->name, var->lineno);
00482             }
00483             var = var->next;
00484          }
00485       }
00486       cat = ast_category_browse(cfg, cat);
00487    }
00488 
00489    ast_config_destroy(cfg);
00490 
00491    ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00492       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
00493       dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00494       dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
00495 
00496    return 0;
00497 }
00498 
00499 static int unload_module(void)
00500 {
00501    return ast_unregister_application(app);
00502 }
00503 
00504 /*!
00505  * \brief Load the module
00506  *
00507  * Module loading including tests for configuration or dependencies.
00508  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
00509  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
00510  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
00511  * configuration file or other non-critical problem return 
00512  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
00513  */
00514 static int load_module(void)
00515 {
00516    if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
00517       return AST_MODULE_LOAD_DECLINE;
00518    }
00519 
00520    return AST_MODULE_LOAD_SUCCESS;
00521 }
00522 
00523 static int reload(void)
00524 {
00525    if (load_config(1))
00526       return AST_MODULE_LOAD_DECLINE;
00527    return AST_MODULE_LOAD_SUCCESS;
00528 }
00529 
00530 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00531       .support_level = AST_MODULE_SUPPORT_EXTENDED,
00532       .load = load_module,
00533       .unload = unload_module,
00534       .reload = reload,
00535 );

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6