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
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 277183 $")
00033
00034 #include "asterisk/module.h"
00035 #include "asterisk/lock.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/dsp.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/config.h"
00040 #include "asterisk/app.h"
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127 static const char app[] = "AMD";
00128
00129 #define STATE_IN_WORD 1
00130 #define STATE_IN_SILENCE 2
00131
00132
00133 static int dfltInitialSilence = 2500;
00134 static int dfltGreeting = 1500;
00135 static int dfltAfterGreetingSilence = 800;
00136 static int dfltTotalAnalysisTime = 5000;
00137 static int dfltMinimumWordLength = 100;
00138 static int dfltBetweenWordsSilence = 50;
00139 static int dfltMaximumNumberOfWords = 3;
00140 static int dfltSilenceThreshold = 256;
00141 static int dfltMaximumWordLength = 5000;
00142
00143
00144 static int dfltMaxWaitTimeForFrame = 50;
00145
00146 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
00147 {
00148 int res = 0;
00149 struct ast_frame *f = NULL;
00150 struct ast_dsp *silenceDetector = NULL;
00151 int dspsilence = 0, readFormat, framelength = 0;
00152 int inInitialSilence = 1;
00153 int inGreeting = 0;
00154 int voiceDuration = 0;
00155 int silenceDuration = 0;
00156 int iTotalTime = 0;
00157 int iWordsCount = 0;
00158 int currentState = STATE_IN_WORD;
00159 int previousState = STATE_IN_SILENCE;
00160 int consecutiveVoiceDuration = 0;
00161 char amdCause[256] = "", amdStatus[256] = "";
00162 char *parse = ast_strdupa(data);
00163
00164
00165
00166
00167
00168 int initialSilence = dfltInitialSilence;
00169 int greeting = dfltGreeting;
00170 int afterGreetingSilence = dfltAfterGreetingSilence;
00171 int totalAnalysisTime = dfltTotalAnalysisTime;
00172 int minimumWordLength = dfltMinimumWordLength;
00173 int betweenWordsSilence = dfltBetweenWordsSilence;
00174 int maximumNumberOfWords = dfltMaximumNumberOfWords;
00175 int silenceThreshold = dfltSilenceThreshold;
00176 int maximumWordLength = dfltMaximumWordLength;
00177 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
00178
00179 AST_DECLARE_APP_ARGS(args,
00180 AST_APP_ARG(argInitialSilence);
00181 AST_APP_ARG(argGreeting);
00182 AST_APP_ARG(argAfterGreetingSilence);
00183 AST_APP_ARG(argTotalAnalysisTime);
00184 AST_APP_ARG(argMinimumWordLength);
00185 AST_APP_ARG(argBetweenWordsSilence);
00186 AST_APP_ARG(argMaximumNumberOfWords);
00187 AST_APP_ARG(argSilenceThreshold);
00188 AST_APP_ARG(argMaximumWordLength);
00189 );
00190
00191 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", chan->name,
00192 S_COR(chan->caller.ani.number.valid, chan->caller.ani.number.str, "(N/A)"),
00193 S_COR(chan->redirecting.from.number.valid, chan->redirecting.from.number.str, "(N/A)"),
00194 ast_getformatname(chan->readformat));
00195
00196
00197 if (!ast_strlen_zero(parse)) {
00198
00199 AST_STANDARD_APP_ARGS(args, parse);
00200 if (!ast_strlen_zero(args.argInitialSilence))
00201 initialSilence = atoi(args.argInitialSilence);
00202 if (!ast_strlen_zero(args.argGreeting))
00203 greeting = atoi(args.argGreeting);
00204 if (!ast_strlen_zero(args.argAfterGreetingSilence))
00205 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00206 if (!ast_strlen_zero(args.argTotalAnalysisTime))
00207 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00208 if (!ast_strlen_zero(args.argMinimumWordLength))
00209 minimumWordLength = atoi(args.argMinimumWordLength);
00210 if (!ast_strlen_zero(args.argBetweenWordsSilence))
00211 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00212 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00213 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00214 if (!ast_strlen_zero(args.argSilenceThreshold))
00215 silenceThreshold = atoi(args.argSilenceThreshold);
00216 if (!ast_strlen_zero(args.argMaximumWordLength))
00217 maximumWordLength = atoi(args.argMaximumWordLength);
00218 } else {
00219 ast_debug(1, "AMD using the default parameters.\n");
00220 }
00221
00222
00223 if (maxWaitTimeForFrame > initialSilence)
00224 maxWaitTimeForFrame = initialSilence;
00225 if (maxWaitTimeForFrame > greeting)
00226 maxWaitTimeForFrame = greeting;
00227 if (maxWaitTimeForFrame > afterGreetingSilence)
00228 maxWaitTimeForFrame = afterGreetingSilence;
00229 if (maxWaitTimeForFrame > totalAnalysisTime)
00230 maxWaitTimeForFrame = totalAnalysisTime;
00231 if (maxWaitTimeForFrame > minimumWordLength)
00232 maxWaitTimeForFrame = minimumWordLength;
00233 if (maxWaitTimeForFrame > betweenWordsSilence)
00234 maxWaitTimeForFrame = betweenWordsSilence;
00235
00236
00237 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00238 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
00239 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00240 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
00241
00242
00243 readFormat = chan->readformat;
00244 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
00245 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
00246 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00247 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00248 return;
00249 }
00250
00251
00252 if (!(silenceDetector = ast_dsp_new())) {
00253 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
00254 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00255 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00256 return;
00257 }
00258
00259
00260 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00261
00262
00263 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00264
00265
00266 if (!(f = ast_read(chan))) {
00267 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
00268 ast_debug(1, "Got hangup\n");
00269 strcpy(amdStatus, "HANGUP");
00270 res = 1;
00271 break;
00272 }
00273
00274 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00275
00276 if (f->frametype == AST_FRAME_VOICE) {
00277 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00278 } else {
00279 framelength = 2 * maxWaitTimeForFrame;
00280 }
00281
00282 iTotalTime += framelength;
00283 if (iTotalTime >= totalAnalysisTime) {
00284 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name );
00285 ast_frfree(f);
00286 strcpy(amdStatus , "NOTSURE");
00287 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00288 break;
00289 }
00290
00291
00292 if (f->frametype != AST_FRAME_VOICE)
00293 dspsilence += 2 * maxWaitTimeForFrame;
00294 else {
00295 dspsilence = 0;
00296 ast_dsp_silence(silenceDetector, f, &dspsilence);
00297 }
00298
00299 if (dspsilence > 0) {
00300 silenceDuration = dspsilence;
00301
00302 if (silenceDuration >= betweenWordsSilence) {
00303 if (currentState != STATE_IN_SILENCE ) {
00304 previousState = currentState;
00305 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
00306 }
00307
00308 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
00309 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
00310 }
00311 currentState = STATE_IN_SILENCE;
00312 consecutiveVoiceDuration = 0;
00313 }
00314
00315 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
00316 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00317 chan->name, silenceDuration, initialSilence);
00318 ast_frfree(f);
00319 strcpy(amdStatus , "MACHINE");
00320 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00321 res = 1;
00322 break;
00323 }
00324
00325 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
00326 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00327 chan->name, silenceDuration, afterGreetingSilence);
00328 ast_frfree(f);
00329 strcpy(amdStatus , "HUMAN");
00330 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00331 res = 1;
00332 break;
00333 }
00334
00335 } else {
00336 consecutiveVoiceDuration += framelength;
00337 voiceDuration += framelength;
00338
00339
00340
00341 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00342 iWordsCount++;
00343 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
00344 previousState = currentState;
00345 currentState = STATE_IN_WORD;
00346 }
00347 if (consecutiveVoiceDuration >= maximumWordLength){
00348 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
00349 ast_frfree(f);
00350 strcpy(amdStatus , "MACHINE");
00351 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
00352 break;
00353 }
00354 if (iWordsCount >= maximumNumberOfWords) {
00355 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
00356 ast_frfree(f);
00357 strcpy(amdStatus , "MACHINE");
00358 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00359 res = 1;
00360 break;
00361 }
00362
00363 if (inGreeting == 1 && voiceDuration >= greeting) {
00364 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
00365 ast_frfree(f);
00366 strcpy(amdStatus , "MACHINE");
00367 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00368 res = 1;
00369 break;
00370 }
00371
00372 if (voiceDuration >= minimumWordLength ) {
00373 if (silenceDuration > 0)
00374 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
00375 silenceDuration = 0;
00376 }
00377 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
00378
00379 if (silenceDuration > 0)
00380 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
00381 inInitialSilence = 0;
00382 inGreeting = 1;
00383 }
00384
00385 }
00386 }
00387 ast_frfree(f);
00388 }
00389
00390 if (!res) {
00391
00392 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name);
00393 strcpy(amdStatus , "NOTSURE");
00394 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00395 }
00396
00397
00398 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00399 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00400
00401
00402 if (readFormat && ast_set_read_format(chan, readFormat))
00403 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
00404
00405
00406 ast_dsp_free(silenceDetector);
00407
00408 return;
00409 }
00410
00411
00412 static int amd_exec(struct ast_channel *chan, const char *data)
00413 {
00414 isAnsweringMachine(chan, data);
00415
00416 return 0;
00417 }
00418
00419 static int load_config(int reload)
00420 {
00421 struct ast_config *cfg = NULL;
00422 char *cat = NULL;
00423 struct ast_variable *var = NULL;
00424 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00425
00426 dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00427
00428 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
00429 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00430 return -1;
00431 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00432 return 0;
00433 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00434 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
00435 return -1;
00436 }
00437
00438 cat = ast_category_browse(cfg, NULL);
00439
00440 while (cat) {
00441 if (!strcasecmp(cat, "general") ) {
00442 var = ast_variable_browse(cfg, cat);
00443 while (var) {
00444 if (!strcasecmp(var->name, "initial_silence")) {
00445 dfltInitialSilence = atoi(var->value);
00446 } else if (!strcasecmp(var->name, "greeting")) {
00447 dfltGreeting = atoi(var->value);
00448 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00449 dfltAfterGreetingSilence = atoi(var->value);
00450 } else if (!strcasecmp(var->name, "silence_threshold")) {
00451 dfltSilenceThreshold = atoi(var->value);
00452 } else if (!strcasecmp(var->name, "total_analysis_time")) {
00453 dfltTotalAnalysisTime = atoi(var->value);
00454 } else if (!strcasecmp(var->name, "min_word_length")) {
00455 dfltMinimumWordLength = atoi(var->value);
00456 } else if (!strcasecmp(var->name, "between_words_silence")) {
00457 dfltBetweenWordsSilence = atoi(var->value);
00458 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00459 dfltMaximumNumberOfWords = atoi(var->value);
00460 } else if (!strcasecmp(var->name, "maximum_word_length")) {
00461 dfltMaximumWordLength = atoi(var->value);
00462
00463 } else {
00464 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00465 app, cat, var->name, var->lineno);
00466 }
00467 var = var->next;
00468 }
00469 }
00470 cat = ast_category_browse(cfg, cat);
00471 }
00472
00473 ast_config_destroy(cfg);
00474
00475 ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00476 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
00477 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00478 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
00479
00480 return 0;
00481 }
00482
00483 static int unload_module(void)
00484 {
00485 return ast_unregister_application(app);
00486 }
00487
00488 static int load_module(void)
00489 {
00490 if (load_config(0))
00491 return AST_MODULE_LOAD_DECLINE;
00492 if (ast_register_application_xml(app, amd_exec))
00493 return AST_MODULE_LOAD_FAILURE;
00494 return AST_MODULE_LOAD_SUCCESS;
00495 }
00496
00497 static int reload(void)
00498 {
00499 if (load_config(1))
00500 return AST_MODULE_LOAD_DECLINE;
00501 return AST_MODULE_LOAD_SUCCESS;
00502 }
00503
00504 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00505 .load = load_module,
00506 .unload = unload_module,
00507 .reload = reload,
00508 );