Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Asterisk -- A telephony toolkit for Linux. |
3 |
* |
4 |
* Playback a file with audio detect |
5 |
* |
6 |
* Copyright (C) 2004-2005, Newman Telecom, Inc. and Newman Ventures, Inc. |
7 |
* |
8 |
* Justin Newman <jnewman@newmantelecom.com> |
9 |
* |
10 |
* We would like to thank Newman Ventures, Inc. for funding this |
11 |
* Asterisk project. |
12 |
* |
13 |
* Newman Ventures <info@newmanventures.com> |
14 |
* |
15 |
* Portions Copyright: |
16 |
* Copyright (C) 2001, Linux Support Services, Inc. |
17 |
* Copyright (C) 2004, Digium, Inc. |
18 |
* |
19 |
* Matthew Fredrickson <creslin@linux-support.net> |
20 |
* Mark Spencer <markster@digium.com> |
21 |
* |
22 |
* This program is free software, distributed under the terms of |
23 |
* the GNU General Public License |
24 |
*/ |
25 |
|
26 |
#include "asterisk.h" |
27 |
|
28 |
#include <asterisk/lock.h> |
29 |
#include <asterisk/file.h> |
30 |
#include <asterisk/logger.h> |
31 |
#include <asterisk/channel.h> |
32 |
#include <asterisk/pbx.h> |
33 |
#include <asterisk/module.h> |
34 |
#include <asterisk/translate.h> |
35 |
#include <asterisk/utils.h> |
36 |
#include <asterisk/dsp.h> |
37 |
|
38 |
static char *app = "NVBackgroundDetect"; |
39 |
|
40 |
static char *synopsis = "Background a file with talk and fax detect (IAX and SIP too)"; |
41 |
|
42 |
static char *descrip = |
43 |
" NVBackgroundDetect(filename[|options[|sildur[|mindur|[maxdur]]]]):\n" |
44 |
"Plays filename, waiting for interruption from fax tones (on IAX and SIP too),\n" |
45 |
"a digit, or non-silence. Audio is monitored in the receive direction. If\n" |
46 |
"digits interrupt, they must be the start of a valid extension unless the\n" |
47 |
"option is included to ignore. If fax is detected, it will jump to the\n" |
48 |
"'fax' extension. If a period of non-silence is greater than 'mindur' ms,\n" |
49 |
"yet less than 'maxdur' ms is followed by silence at least 'sildur' ms\n" |
50 |
"then the app is aborted and processing jumps to the 'talk' extension.\n" |
51 |
"If all undetected, control will continue at the next priority.\n" |
52 |
" options:\n" |
53 |
" 'n': Attempt on-hook if unanswered (default=no)\n" |
54 |
" 'x': DTMF digits terminate without extension (default=no)\n" |
55 |
" 'd': Ignore DTMF digit detection (default=no)\n" |
56 |
" 'f': Ignore fax detection (default=no)\n" |
57 |
" 't': Ignore talk detection (default=no)\n" |
58 |
" sildur: Silence ms after mindur/maxdur before aborting (default=1000)\n" |
59 |
" mindur: Minimum non-silence ms needed (default=100)\n" |
60 |
" maxdur: Maximum non-silence ms allowed (default=0/forever)\n" |
61 |
"Returns -1 on hangup, and 0 on successful completion with no exit conditions.\n\n" |
62 |
"For questions or comments, please e-mail support@newmantelecom.com.\n"; |
63 |
|
64 |
// Use the second one for recent Asterisk releases |
65 |
#define CALLERID_FIELD cid.cid_num |
66 |
//#define CALLERID_FIELD callerid |
67 |
|
68 |
static int nv_background_detect_exec(struct ast_channel *chan, void *data) |
69 |
{ |
70 |
int res = 0; |
71 |
char tmp[256] = "\0"; |
72 |
char *p = NULL; |
73 |
char *filename = NULL; |
74 |
char *options = NULL; |
75 |
char *silstr = NULL; |
76 |
char *minstr = NULL; |
77 |
char *maxstr = NULL; |
78 |
struct ast_frame *fr = NULL; |
79 |
struct ast_frame *fr2 = NULL; |
80 |
int notsilent = 0; |
81 |
struct timeval start = {0, 0}, end = {0, 0}; |
82 |
int sildur = 1000; |
83 |
int mindur = 100; |
84 |
int maxdur = -1; |
85 |
int skipanswer = 0; |
86 |
int noextneeded = 0; |
87 |
int ignoredtmf = 0; |
88 |
int ignorefax = 0; |
89 |
int ignoretalk = 0; |
90 |
int x = 0; |
91 |
int origrformat = 0; |
92 |
int features = 0; |
93 |
struct ast_dsp *dsp = NULL; |
94 |
|
95 |
pbx_builtin_setvar_helper(chan, "FAX_DETECTED", ""); |
96 |
pbx_builtin_setvar_helper(chan, "FAXEXTEN", ""); |
97 |
pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", ""); |
98 |
pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ""); |
99 |
|
100 |
if (!data || ast_strlen_zero((char *)data)) { |
101 |
ast_log(LOG_WARNING, "NVBackgroundDetect requires an argument (filename)\n"); |
102 |
return -1; |
103 |
} |
104 |
|
105 |
strncpy(tmp, (char *)data, sizeof(tmp)-1); |
106 |
p = tmp; |
107 |
|
108 |
filename = strsep(&p, "|"); |
109 |
options = strsep(&p, "|"); |
110 |
silstr = strsep(&p, "|"); |
111 |
minstr = strsep(&p, "|"); |
112 |
maxstr = strsep(&p, "|"); |
113 |
|
114 |
if (options) { |
115 |
if (strchr(options, 'n')) |
116 |
skipanswer = 1; |
117 |
if (strchr(options, 'x')) |
118 |
noextneeded = 1; |
119 |
if (strchr(options, 'd')) |
120 |
ignoredtmf = 1; |
121 |
if (strchr(options, 'f')) |
122 |
ignorefax = 1; |
123 |
if (strchr(options, 't')) |
124 |
ignoretalk = 1; |
125 |
} |
126 |
|
127 |
if (silstr) { |
128 |
if ((sscanf(silstr, "%d", &x) == 1) && (x > 0)) |
129 |
sildur = x; |
130 |
} |
131 |
|
132 |
if (minstr) { |
133 |
if ((sscanf(minstr, "%d", &x) == 1) && (x > 0)) |
134 |
mindur = x; |
135 |
} |
136 |
|
137 |
if (maxstr) { |
138 |
if ((sscanf(maxstr, "%d", &x) == 1) && (x > 0)) |
139 |
maxdur = x; |
140 |
} |
141 |
|
142 |
ast_log(LOG_DEBUG, "Preparing detect of '%s' (sildur=%dms, mindur=%dms, maxdur=%dms)\n", |
143 |
tmp, sildur, mindur, maxdur); |
144 |
|
145 |
// LOCAL_USER_ADD(u); |
146 |
if (chan->_state != AST_STATE_UP && !skipanswer) { |
147 |
/* Otherwise answer unless we're supposed to send this while on-hook */ |
148 |
res = ast_answer(chan); |
149 |
} |
150 |
if (!res) { |
151 |
origrformat = chan->readformat; |
152 |
if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR))) |
153 |
ast_log(LOG_WARNING, "Unable to set read format to linear!\n"); |
154 |
} |
155 |
if (!(dsp = ast_dsp_new())) { |
156 |
ast_log(LOG_WARNING, "Unable to allocate DSP!\n"); |
157 |
res = -1; |
158 |
} |
159 |
|
160 |
if (dsp) { |
161 |
if (!ignoretalk) |
162 |
; /* features |= DSP_FEATURE_SILENCE_SUPPRESS; */ |
163 |
if (!ignorefax) |
164 |
features |= DSP_FEATURE_FAX_DETECT; |
165 |
//if (!ignoredtmf) |
166 |
features |= DSP_FEATURE_DIGIT_DETECT; |
167 |
|
168 |
ast_dsp_set_threshold(dsp, 256); |
169 |
ast_dsp_set_features(dsp, features | DSP_DIGITMODE_RELAXDTMF); |
170 |
ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_DTMF); |
171 |
} |
172 |
|
173 |
if (!res) { |
174 |
ast_stopstream(chan); |
175 |
res = ast_streamfile(chan, tmp, chan->language); |
176 |
if (!res) { |
177 |
while(chan->stream) { |
178 |
res = ast_sched_wait(chan->sched); |
179 |
if ((res < 0) && !chan->timingfunc) { |
180 |
res = 0; |
181 |
break; |
182 |
} |
183 |
if (res < 0) |
184 |
res = 1000; |
185 |
res = ast_waitfor(chan, res); |
186 |
if (res < 0) { |
187 |
ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name); |
188 |
break; |
189 |
} else if (res > 0) { |
190 |
fr = ast_read(chan); |
191 |
if (!fr) { |
192 |
ast_log(LOG_DEBUG, "Got hangup\n"); |
193 |
res = -1; |
194 |
break; |
195 |
} |
196 |
|
197 |
fr2 = ast_dsp_process(chan, dsp, fr); |
198 |
if (!fr2) { |
199 |
ast_log(LOG_WARNING, "Bad DSP received (what happened!!)\n"); |
200 |
fr2 = fr; |
201 |
} |
202 |
|
203 |
if (fr2->frametype == AST_FRAME_DTMF) { |
204 |
if (fr2->subclass == 'f' && !ignorefax) { |
205 |
/* Fax tone -- Handle and return NULL */ |
206 |
ast_log(LOG_DEBUG, "Fax detected on %s\n", chan->name); |
207 |
if (strcmp(chan->exten, "fax")) { |
208 |
ast_log(LOG_NOTICE, "Redirecting %s to fax extension\n", chan->name); |
209 |
pbx_builtin_setvar_helper(chan, "FAX_DETECTED", "1"); |
210 |
pbx_builtin_setvar_helper(chan,"FAXEXTEN",chan->exten); |
211 |
if (ast_exists_extension(chan, chan->context, "fax", 1, chan->CALLERID_FIELD)) { |
212 |
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ |
213 |
strncpy(chan->exten, "fax", sizeof(chan->exten)-1); |
214 |
chan->priority = 0; |
215 |
} else |
216 |
ast_log(LOG_WARNING, "Fax detected, but no fax extension\n"); |
217 |
} else |
218 |
ast_log(LOG_WARNING, "Already in a fax extension, not redirecting\n"); |
219 |
|
220 |
res = 0; |
221 |
ast_frfree(fr); |
222 |
break; |
223 |
} else if (!ignoredtmf) { |
224 |
ast_log(LOG_DEBUG, "DTMF detected on %s\n", chan->name); |
225 |
char t[2]; |
226 |
t[0] = fr2->subclass; |
227 |
t[1] = '\0'; |
228 |
if (noextneeded || ast_canmatch_extension(chan, chan->context, t, 1, chan->CALLERID_FIELD)) { |
229 |
pbx_builtin_setvar_helper(chan, "DTMF_DETECTED", "1"); |
230 |
/* They entered a valid extension, or might be anyhow */ |
231 |
if (noextneeded) { |
232 |
ast_log(LOG_NOTICE, "DTMF received (not matching to exten)\n"); |
233 |
res = 0; |
234 |
} else { |
235 |
ast_log(LOG_NOTICE, "DTMF received (matching to exten)\n"); |
236 |
res = fr2->subclass; |
237 |
} |
238 |
ast_frfree(fr); |
239 |
break; |
240 |
} else |
241 |
ast_log(LOG_DEBUG, "Valid extension requested and DTMF did not match\n"); |
242 |
} |
243 |
} else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR) && !ignoretalk) { |
244 |
int totalsilence; |
245 |
int ms; |
246 |
res = ast_dsp_silence(dsp, fr, &totalsilence); |
247 |
if (res && (totalsilence > sildur)) { |
248 |
/* We've been quiet a little while */ |
249 |
if (notsilent) { |
250 |
/* We had heard some talking */ |
251 |
gettimeofday(&end, NULL); |
252 |
ms = (end.tv_sec - start.tv_sec) * 1000; |
253 |
ms += (end.tv_usec - start.tv_usec) / 1000; |
254 |
ms -= sildur; |
255 |
if (ms < 0) |
256 |
ms = 0; |
257 |
if ((ms > mindur) && ((maxdur < 0) || (ms < maxdur))) { |
258 |
char ms_str[10]; |
259 |
ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms); |
260 |
ast_log(LOG_NOTICE, "Redirecting %s to talk extension\n", chan->name); |
261 |
|
262 |
/* Save detected talk time (in milliseconds) */ |
263 |
sprintf(ms_str, "%d", ms); |
264 |
pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str); |
265 |
|
266 |
if (ast_exists_extension(chan, chan->context, "talk", 1, chan->CALLERID_FIELD)) { |
267 |
strncpy(chan->exten, "talk", sizeof(chan->exten) - 1); |
268 |
chan->priority = 0; |
269 |
} else |
270 |
ast_log(LOG_WARNING, "Talk detected, but no talk extension\n"); |
271 |
res = 0; |
272 |
ast_frfree(fr); |
273 |
break; |
274 |
} else |
275 |
ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms); |
276 |
notsilent = 0; |
277 |
} |
278 |
} else { |
279 |
if (!notsilent) { |
280 |
/* Heard some audio, mark the begining of the token */ |
281 |
gettimeofday(&start, NULL); |
282 |
ast_log(LOG_DEBUG, "Start of voice token!\n"); |
283 |
notsilent = 1; |
284 |
} |
285 |
} |
286 |
} |
287 |
ast_frfree(fr); |
288 |
} |
289 |
ast_sched_runq(chan->sched); |
290 |
} |
291 |
ast_stopstream(chan); |
292 |
} else { |
293 |
ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data); |
294 |
res = 0; |
295 |
} |
296 |
} else |
297 |
ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); |
298 |
|
299 |
if (res > -1) { |
300 |
if (origrformat && ast_set_read_format(chan, origrformat)) { |
301 |
ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", |
302 |
chan->name, ast_getformatname(origrformat)); |
303 |
} |
304 |
} |
305 |
|
306 |
if (dsp) |
307 |
ast_dsp_free(dsp); |
308 |
|
309 |
// LOCAL_USER_REMOVE(u); |
310 |
|
311 |
return res; |
312 |
} |
313 |
|
314 |
static int unload_module(void) |
315 |
{ |
316 |
// STANDARD_HANGUP_LOCALUSERS; |
317 |
return ast_unregister_application(app); |
318 |
} |
319 |
|
320 |
static int load_module(void) |
321 |
{ |
322 |
return ast_register_application(app, nv_background_detect_exec, synopsis, descrip); |
323 |
} |
324 |
|
325 |
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fax playing Background Music Detection Application"); |