File: | src/switch_ivr_async.c |
Location: | line 3945, column 10 |
Description: | Dereference of null pointer (loaded from variable 'result') |
1 | /* | |||
2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application | |||
3 | * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> | |||
4 | * | |||
5 | * Version: MPL 1.1 | |||
6 | * | |||
7 | * The contents of this file are subject to the Mozilla Public License Version | |||
8 | * 1.1 (the "License"); you may not use this file except in compliance with | |||
9 | * the License. You may obtain a copy of the License at | |||
10 | * http://www.mozilla.org/MPL/ | |||
11 | * | |||
12 | * Software distributed under the License is distributed on an "AS IS" basis, | |||
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
14 | * for the specific language governing rights and limitations under the | |||
15 | * License. | |||
16 | * | |||
17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application | |||
18 | * | |||
19 | * The Initial Developer of the Original Code is | |||
20 | * Anthony Minessale II <anthm@freeswitch.org> | |||
21 | * Portions created by the Initial Developer are Copyright (C) | |||
22 | * the Initial Developer. All Rights Reserved. | |||
23 | * | |||
24 | * Contributor(s): | |||
25 | * | |||
26 | * Anthony Minessale II <anthm@freeswitch.org> | |||
27 | * Michael Jerris <mike@jerris.com> | |||
28 | * Bret McDanel <bret AT 0xdecafbad dot com> | |||
29 | * Luke Dashjr <luke@openmethods.com> (OpenMethods, LLC) | |||
30 | * Christopher M. Rienzo <chris@rienzo.com> | |||
31 | * | |||
32 | * switch_ivr_async.c -- IVR Library (async operations) | |||
33 | * | |||
34 | */ | |||
35 | ||||
36 | #include <switch.h> | |||
37 | #include <speex/speex_preprocess.h> | |||
38 | #include <speex/speex_echo.h> | |||
39 | ||||
40 | struct switch_ivr_dmachine_binding { | |||
41 | char *digits; | |||
42 | int32_t key; | |||
43 | uint8_t rmatch; | |||
44 | switch_ivr_dmachine_callback_t callback; | |||
45 | switch_byte_t is_regex; | |||
46 | void *user_data; | |||
47 | struct switch_ivr_dmachine_binding *next; | |||
48 | }; | |||
49 | typedef struct switch_ivr_dmachine_binding switch_ivr_dmachine_binding_t; | |||
50 | ||||
51 | typedef struct { | |||
52 | switch_ivr_dmachine_binding_t *binding_list; | |||
53 | switch_ivr_dmachine_binding_t *tail; | |||
54 | char *name; | |||
55 | char *terminators; | |||
56 | } dm_binding_head_t; | |||
57 | ||||
58 | struct switch_ivr_dmachine { | |||
59 | switch_memory_pool_t *pool; | |||
60 | switch_byte_t my_pool; | |||
61 | char *name; | |||
62 | uint32_t digit_timeout_ms; | |||
63 | uint32_t input_timeout_ms; | |||
64 | switch_hash_t *binding_hash; | |||
65 | switch_ivr_dmachine_match_t match; | |||
66 | switch_digit_action_target_t target; | |||
67 | char digits[DMACHINE_MAX_DIGIT_LEN512]; | |||
68 | char last_matching_digits[DMACHINE_MAX_DIGIT_LEN512]; | |||
69 | char last_failed_digits[DMACHINE_MAX_DIGIT_LEN512]; | |||
70 | uint32_t cur_digit_len; | |||
71 | uint32_t max_digit_len; | |||
72 | switch_time_t last_digit_time; | |||
73 | switch_byte_t is_match; | |||
74 | switch_ivr_dmachine_callback_t match_callback; | |||
75 | switch_ivr_dmachine_callback_t nonmatch_callback; | |||
76 | dm_binding_head_t *realm; | |||
77 | switch_ivr_dmachine_binding_t *last_matching_binding; | |||
78 | void *user_data; | |||
79 | switch_mutex_t *mutex; | |||
80 | switch_status_t last_return; | |||
81 | }; | |||
82 | ||||
83 | ||||
84 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_last_ping(switch_ivr_dmachine_t *dmachine) | |||
85 | { | |||
86 | return dmachine->last_return; | |||
87 | } | |||
88 | ||||
89 | SWITCH_DECLARE(switch_digit_action_target_t)__attribute__((visibility("default"))) switch_digit_action_target_t switch_ivr_dmachine_get_target(switch_ivr_dmachine_t *dmachine) | |||
90 | { | |||
91 | switch_assert(dmachine)((dmachine) ? (void) (0) : __assert_fail ("dmachine", "src/switch_ivr_async.c" , 91, __PRETTY_FUNCTION__)); | |||
92 | return dmachine->target; | |||
93 | } | |||
94 | ||||
95 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_set_target(switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target) | |||
96 | { | |||
97 | switch_assert(dmachine)((dmachine) ? (void) (0) : __assert_fail ("dmachine", "src/switch_ivr_async.c" , 97, __PRETTY_FUNCTION__)); | |||
98 | dmachine->target = target; | |||
99 | } | |||
100 | ||||
101 | ||||
102 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_set_match_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t match_callback) | |||
103 | { | |||
104 | ||||
105 | switch_assert(dmachine)((dmachine) ? (void) (0) : __assert_fail ("dmachine", "src/switch_ivr_async.c" , 105, __PRETTY_FUNCTION__)); | |||
106 | dmachine->match_callback = match_callback; | |||
107 | ||||
108 | } | |||
109 | ||||
110 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_set_nonmatch_callback(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_callback_t nonmatch_callback) | |||
111 | { | |||
112 | ||||
113 | switch_assert(dmachine)((dmachine) ? (void) (0) : __assert_fail ("dmachine", "src/switch_ivr_async.c" , 113, __PRETTY_FUNCTION__)); | |||
114 | dmachine->nonmatch_callback = nonmatch_callback; | |||
115 | ||||
116 | } | |||
117 | ||||
118 | SWITCH_DECLARE(const char *)__attribute__((visibility("default"))) const char * switch_ivr_dmachine_get_name(switch_ivr_dmachine_t *dmachine) | |||
119 | { | |||
120 | return (const char *) dmachine->name; | |||
121 | } | |||
122 | ||||
123 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_create(switch_ivr_dmachine_t **dmachine_p, | |||
124 | const char *name, | |||
125 | switch_memory_pool_t *pool, | |||
126 | uint32_t digit_timeout_ms, | |||
127 | uint32_t input_timeout_ms, | |||
128 | switch_ivr_dmachine_callback_t match_callback, | |||
129 | switch_ivr_dmachine_callback_t nonmatch_callback, | |||
130 | void *user_data) | |||
131 | { | |||
132 | switch_byte_t my_pool = 0; | |||
133 | switch_ivr_dmachine_t *dmachine; | |||
134 | ||||
135 | if (!pool) { | |||
136 | switch_core_new_memory_pool(&pool)switch_core_perform_new_memory_pool(&pool, "src/switch_ivr_async.c" , (const char *)__func__, 136); | |||
137 | my_pool = 1; | |||
138 | } | |||
139 | ||||
140 | dmachine = switch_core_alloc(pool, sizeof(*dmachine))switch_core_perform_alloc(pool, sizeof(*dmachine), "src/switch_ivr_async.c" , (const char *)__func__, 140); | |||
141 | dmachine->pool = pool; | |||
142 | dmachine->my_pool = my_pool; | |||
143 | dmachine->digit_timeout_ms = digit_timeout_ms; | |||
144 | dmachine->input_timeout_ms = input_timeout_ms; | |||
145 | dmachine->match.dmachine = dmachine; | |||
146 | dmachine->name = switch_core_strdup(dmachine->pool, name)switch_core_perform_strdup(dmachine->pool, name, "src/switch_ivr_async.c" , (const char *)__func__, 146); | |||
147 | switch_mutex_init(&dmachine->mutex, SWITCH_MUTEX_NESTED0x1, dmachine->pool); | |||
148 | ||||
149 | switch_core_hash_init(&dmachine->binding_hash)switch_core_hash_init_case(&dmachine->binding_hash, SWITCH_TRUE ); | |||
150 | ||||
151 | if (match_callback) { | |||
152 | dmachine->match_callback = match_callback; | |||
153 | } | |||
154 | ||||
155 | if (nonmatch_callback) { | |||
156 | dmachine->nonmatch_callback = nonmatch_callback; | |||
157 | } | |||
158 | ||||
159 | dmachine->user_data = user_data; | |||
160 | ||||
161 | *dmachine_p = dmachine; | |||
162 | ||||
163 | return SWITCH_STATUS_SUCCESS; | |||
164 | } | |||
165 | ||||
166 | ||||
167 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_set_digit_timeout_ms(switch_ivr_dmachine_t *dmachine, uint32_t digit_timeout_ms) | |||
168 | { | |||
169 | dmachine->digit_timeout_ms = digit_timeout_ms; | |||
170 | } | |||
171 | ||||
172 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_set_input_timeout_ms(switch_ivr_dmachine_t *dmachine, uint32_t input_timeout_ms) | |||
173 | { | |||
174 | dmachine->input_timeout_ms = input_timeout_ms; | |||
175 | } | |||
176 | ||||
177 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_dmachine_destroy(switch_ivr_dmachine_t **dmachine) | |||
178 | { | |||
179 | switch_memory_pool_t *pool; | |||
180 | ||||
181 | if (!(dmachine && *dmachine)) return; | |||
182 | ||||
183 | pool = (*dmachine)->pool; | |||
184 | ||||
185 | switch_core_hash_destroy(&(*dmachine)->binding_hash); | |||
186 | ||||
187 | if ((*dmachine)->my_pool) { | |||
188 | switch_core_destroy_memory_pool(&pool)switch_core_perform_destroy_memory_pool(&pool, "src/switch_ivr_async.c" , (const char *)__func__, 188); | |||
189 | } | |||
190 | } | |||
191 | ||||
192 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_set_terminators(switch_ivr_dmachine_t *dmachine, const char *terminators) | |||
193 | { | |||
194 | if (!dmachine->realm) { | |||
195 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 195, ((void*)0), SWITCH_LOG_ERROR, "No realm selected.\n"); | |||
196 | return SWITCH_STATUS_FALSE; | |||
197 | } | |||
198 | ||||
199 | ||||
200 | dmachine->realm->terminators = switch_core_strdup(dmachine->pool, terminators)switch_core_perform_strdup(dmachine->pool, terminators, "src/switch_ivr_async.c" , (const char *)__func__, 200); | |||
201 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 201, ((void*)0), SWITCH_LOG_INFO, "Digit parser %s: Setting terminators for realm '%s' to '%s'\n", | |||
202 | dmachine->name, dmachine->realm->name, terminators); | |||
203 | ||||
204 | return SWITCH_STATUS_SUCCESS; | |||
205 | } | |||
206 | ||||
207 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_set_realm(switch_ivr_dmachine_t *dmachine, const char *realm) | |||
208 | { | |||
209 | dm_binding_head_t *headp = switch_core_hash_find(dmachine->binding_hash, realm); | |||
210 | ||||
211 | if (headp) { | |||
212 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 212, ((void*)0), SWITCH_LOG_INFO, "Digit parser %s: Setting realm to '%s'\n", dmachine->name, realm); | |||
213 | dmachine->realm = headp; | |||
214 | return SWITCH_STATUS_SUCCESS; | |||
215 | } | |||
216 | ||||
217 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 217, ((void*)0), SWITCH_LOG_ERROR, "Digit parser %s: Error Setting realm to '%s'\n", dmachine->name, realm); | |||
218 | ||||
219 | return SWITCH_STATUS_FALSE; | |||
220 | } | |||
221 | ||||
222 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_clear_realm(switch_ivr_dmachine_t *dmachine, const char *realm) | |||
223 | { | |||
224 | dm_binding_head_t *headp; | |||
225 | ||||
226 | if (zstr(realm)_zstr(realm)) { | |||
227 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 227, ((void*)0), SWITCH_LOG_ERROR, "Digit parser %s: Error unknown realm: '%s'\n", dmachine->name, realm); | |||
228 | return SWITCH_STATUS_FALSE; | |||
229 | } | |||
230 | ||||
231 | headp = switch_core_hash_find(dmachine->binding_hash, realm); | |||
232 | ||||
233 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 233, ((void*)0), SWITCH_LOG_INFO, "Digit parser %s: Clearing realm '%s'\n", dmachine->name, realm); | |||
234 | ||||
235 | if (headp == dmachine->realm) { | |||
236 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 236, ((void*)0), SWITCH_LOG_WARNING, | |||
237 | "Digit parser %s: '%s' was the active realm, no realm currently selected.\n", dmachine->name, realm); | |||
238 | dmachine->realm = NULL((void*)0); | |||
239 | } | |||
240 | ||||
241 | /* pool alloc'd just ditch it and it will give back the memory when we destroy ourselves */ | |||
242 | switch_core_hash_delete(dmachine->binding_hash, realm); | |||
243 | return SWITCH_STATUS_SUCCESS; | |||
244 | } | |||
245 | ||||
246 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_bind(switch_ivr_dmachine_t *dmachine, | |||
247 | const char *realm, | |||
248 | const char *digits, | |||
249 | int32_t key, | |||
250 | switch_ivr_dmachine_callback_t callback, | |||
251 | void *user_data) | |||
252 | { | |||
253 | switch_ivr_dmachine_binding_t *binding = NULL((void*)0), *ptr; | |||
254 | switch_size_t len; | |||
255 | dm_binding_head_t *headp; | |||
256 | const char *msg = ""; | |||
257 | ||||
258 | if (strlen(digits) > DMACHINE_MAX_DIGIT_LEN512 -1) { | |||
259 | return SWITCH_STATUS_FALSE; | |||
260 | } | |||
261 | ||||
262 | if (zstr(realm)_zstr(realm)) { | |||
263 | realm = "default"; | |||
264 | } | |||
265 | ||||
266 | if (!(headp = switch_core_hash_find(dmachine->binding_hash, realm))) { | |||
267 | headp = switch_core_alloc(dmachine->pool, sizeof(*headp))switch_core_perform_alloc(dmachine->pool, sizeof(*headp), "src/switch_ivr_async.c" , (const char *)__func__, 267); | |||
268 | headp->name = switch_core_strdup(dmachine->pool, realm)switch_core_perform_strdup(dmachine->pool, realm, "src/switch_ivr_async.c" , (const char *)__func__, 268); | |||
269 | switch_core_hash_insert(dmachine->binding_hash, realm, headp)switch_core_hash_insert_destructor(dmachine->binding_hash, realm, headp, ((void*)0)); | |||
270 | } | |||
271 | ||||
272 | for(ptr = headp->binding_list; ptr; ptr = ptr->next) { | |||
273 | if ((ptr->is_regex && !strcmp(ptr->digits, digits+1)__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (ptr->digits) && __builtin_constant_p (digits+1) && (__s1_len = __builtin_strlen (ptr->digits), __s2_len = __builtin_strlen (digits+1), (!((size_t)(const void *)((ptr->digits) + 1) - (size_t)(const void *)(ptr->digits) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((digits+1) + 1) - (size_t )(const void *)(digits+1) == 1) || __s2_len >= 4)) ? __builtin_strcmp (ptr->digits, digits+1) : (__builtin_constant_p (ptr-> digits) && ((size_t)(const void *)((ptr->digits) + 1) - (size_t)(const void *)(ptr->digits) == 1) && (__s1_len = __builtin_strlen (ptr->digits), __s1_len < 4) ? (__builtin_constant_p (digits+1) && ((size_t)(const void *)((digits+1) + 1) - (size_t)(const void *)(digits+1) == 1) ? __builtin_strcmp (ptr->digits, digits+1) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (digits+1); int __result = (((const unsigned char *) (const char *) (ptr->digits))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (ptr->digits))[1] - __s2[1]); if ( __s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (ptr->digits))[2] - __s2[ 2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (ptr->digits))[3 ] - __s2[3]); } } __result; }))) : (__builtin_constant_p (digits +1) && ((size_t)(const void *)((digits+1) + 1) - (size_t )(const void *)(digits+1) == 1) && (__s2_len = __builtin_strlen (digits+1), __s2_len < 4) ? (__builtin_constant_p (ptr-> digits) && ((size_t)(const void *)((ptr->digits) + 1) - (size_t)(const void *)(ptr->digits) == 1) ? __builtin_strcmp (ptr->digits, digits+1) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (ptr-> digits); int __result = (((const unsigned char *) (const char *) (digits+1))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (digits+1))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (digits+1))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (digits+1))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (ptr->digits, digits+1)))); })) || !strcmp(ptr->digits, digits)__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (ptr->digits) && __builtin_constant_p (digits) && (__s1_len = __builtin_strlen (ptr->digits), __s2_len = __builtin_strlen (digits), (!((size_t)(const void *)((ptr->digits) + 1) - ( size_t)(const void *)(ptr->digits) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((digits) + 1) - (size_t )(const void *)(digits) == 1) || __s2_len >= 4)) ? __builtin_strcmp (ptr->digits, digits) : (__builtin_constant_p (ptr->digits ) && ((size_t)(const void *)((ptr->digits) + 1) - ( size_t)(const void *)(ptr->digits) == 1) && (__s1_len = __builtin_strlen (ptr->digits), __s1_len < 4) ? (__builtin_constant_p (digits) && ((size_t)(const void *)((digits) + 1) - ( size_t)(const void *)(digits) == 1) ? __builtin_strcmp (ptr-> digits, digits) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (digits); int __result = (((const unsigned char *) (const char *) (ptr->digits)) [0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (ptr-> digits))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ( ptr->digits))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (ptr->digits))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (digits) && ((size_t)(const void *)((digits) + 1) - ( size_t)(const void *)(digits) == 1) && (__s2_len = __builtin_strlen (digits), __s2_len < 4) ? (__builtin_constant_p (ptr-> digits) && ((size_t)(const void *)((ptr->digits) + 1) - (size_t)(const void *)(ptr->digits) == 1) ? __builtin_strcmp (ptr->digits, digits) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (ptr-> digits); int __result = (((const unsigned char *) (const char *) (digits))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) ( digits))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ( digits))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (digits ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (ptr ->digits, digits)))); })) { | |||
274 | msg = "Reuse Existing "; | |||
275 | binding = ptr; | |||
276 | binding->callback = callback; | |||
277 | binding->user_data = user_data; | |||
278 | goto done; | |||
279 | } | |||
280 | } | |||
281 | ||||
282 | ||||
283 | binding = switch_core_alloc(dmachine->pool, sizeof(*binding))switch_core_perform_alloc(dmachine->pool, sizeof(*binding) , "src/switch_ivr_async.c", (const char *)__func__, 283); | |||
284 | ||||
285 | if (*digits == '~') { | |||
286 | binding->is_regex = 1; | |||
287 | digits++; | |||
288 | } | |||
289 | ||||
290 | binding->key = key; | |||
291 | binding->digits = switch_core_strdup(dmachine->pool, digits)switch_core_perform_strdup(dmachine->pool, digits, "src/switch_ivr_async.c" , (const char *)__func__, 291); | |||
292 | binding->callback = callback; | |||
293 | binding->user_data = user_data; | |||
294 | ||||
295 | if (headp->tail) { | |||
296 | headp->tail->next = binding; | |||
297 | } else { | |||
298 | headp->binding_list = binding; | |||
299 | } | |||
300 | ||||
301 | headp->tail = binding; | |||
302 | ||||
303 | len = strlen(digits); | |||
304 | ||||
305 | if (dmachine->realm != headp) { | |||
306 | switch_ivr_dmachine_set_realm(dmachine, realm); | |||
307 | } | |||
308 | ||||
309 | if (binding->is_regex && dmachine->max_digit_len != DMACHINE_MAX_DIGIT_LEN512 -1) { | |||
310 | dmachine->max_digit_len = DMACHINE_MAX_DIGIT_LEN512 -1; | |||
311 | } else if (len > dmachine->max_digit_len) { | |||
312 | dmachine->max_digit_len = (uint32_t) len; | |||
313 | } | |||
314 | ||||
315 | done: | |||
316 | ||||
317 | if (binding->is_regex) { | |||
318 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 318, ((void*)0), SWITCH_LOG_DEBUG, "%sDigit parser %s: binding %s/%s/%d callback: %p data: %p\n", | |||
319 | msg, dmachine->name, digits, realm, key, (void *)(intptr_t) callback, user_data); | |||
320 | } else { | |||
321 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 321, ((void*)0), SWITCH_LOG_DEBUG, "%sDigit parser %s: binding %s/%s/%d callback: %p data: %p\n", | |||
322 | msg, dmachine->name, digits, realm, key, (void *)(intptr_t) callback, user_data); | |||
323 | } | |||
324 | ||||
325 | return SWITCH_STATUS_SUCCESS; | |||
326 | } | |||
327 | ||||
328 | typedef enum { | |||
329 | DM_MATCH_NONE, | |||
330 | DM_MATCH_EXACT, | |||
331 | DM_MATCH_PARTIAL, | |||
332 | DM_MATCH_BOTH, | |||
333 | DM_MATCH_NEVER | |||
334 | } dm_match_t; | |||
335 | ||||
336 | ||||
337 | static dm_match_t switch_ivr_dmachine_check_match(switch_ivr_dmachine_t *dmachine, switch_bool_t is_timeout) | |||
338 | { | |||
339 | dm_match_t best = DM_MATCH_NONE; | |||
340 | switch_ivr_dmachine_binding_t *bp, *exact_bp = NULL((void*)0), *partial_bp = NULL((void*)0), *both_bp = NULL((void*)0), *r_bp = NULL((void*)0); | |||
341 | int pmatches = 0, ematches = 0, rmatches = 0; | |||
342 | ||||
343 | if (!dmachine->cur_digit_len || !dmachine->realm) goto end; | |||
344 | ||||
345 | for(bp = dmachine->realm->binding_list; bp; bp = bp->next) { | |||
346 | if (bp->is_regex) { | |||
347 | switch_status_t r_status = switch_regex_match(dmachine->digits, bp->digits); | |||
348 | ||||
349 | if (r_status == SWITCH_STATUS_SUCCESS) { | |||
350 | bp->rmatch++; | |||
351 | } else { | |||
352 | bp->rmatch = 0; | |||
353 | } | |||
354 | ||||
355 | rmatches++; | |||
356 | pmatches++; | |||
357 | ||||
358 | } else { | |||
359 | if (!strncmp(dmachine->digits, bp->digits, strlen(dmachine->digits))(__extension__ (__builtin_constant_p (strlen(dmachine->digits )) && ((__builtin_constant_p (dmachine->digits) && strlen (dmachine->digits) < ((size_t) (strlen(dmachine ->digits)))) || (__builtin_constant_p (bp->digits) && strlen (bp->digits) < ((size_t) (strlen(dmachine->digits ))))) ? __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (dmachine->digits) && __builtin_constant_p (bp-> digits) && (__s1_len = __builtin_strlen (dmachine-> digits), __s2_len = __builtin_strlen (bp->digits), (!((size_t )(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((bp->digits) + 1) - (size_t)(const void *)(bp->digits) == 1) || __s2_len >= 4)) ? __builtin_strcmp (dmachine->digits, bp->digits) : (__builtin_constant_p (dmachine->digits) && ((size_t)(const void *)((dmachine ->digits) + 1) - (size_t)(const void *)(dmachine->digits ) == 1) && (__s1_len = __builtin_strlen (dmachine-> digits), __s1_len < 4) ? (__builtin_constant_p (bp->digits ) && ((size_t)(const void *)((bp->digits) + 1) - ( size_t)(const void *)(bp->digits) == 1) ? __builtin_strcmp (dmachine->digits, bp->digits) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (bp->digits); int __result = (((const unsigned char *) (const char *) (dmachine->digits))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[1] - __s2[1]); if ( __s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[2] - __s2 [2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (dmachine->digits ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ( bp->digits) && ((size_t)(const void *)((bp->digits ) + 1) - (size_t)(const void *)(bp->digits) == 1) && (__s2_len = __builtin_strlen (bp->digits), __s2_len < 4 ) ? (__builtin_constant_p (dmachine->digits) && (( size_t)(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) ? __builtin_strcmp (dmachine ->digits, bp->digits) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (dmachine ->digits); int __result = (((const unsigned char *) (const char *) (bp->digits))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (bp->digits))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (dmachine->digits, bp->digits)))); }) : strncmp (dmachine ->digits, bp->digits, strlen(dmachine->digits))))) { | |||
360 | pmatches++; | |||
361 | ematches = 1; | |||
362 | } | |||
363 | } | |||
364 | } | |||
365 | ||||
366 | if (!zstr(dmachine->realm->terminators)_zstr(dmachine->realm->terminators)) { | |||
367 | char *p = dmachine->realm->terminators; | |||
368 | char *q; | |||
369 | ||||
370 | while(p && *p) { | |||
371 | if ((q=strrchr(dmachine->digits, *p))) { | |||
372 | *q = '\0'; | |||
373 | is_timeout = 1; | |||
374 | break; | |||
375 | } | |||
376 | p++; | |||
377 | } | |||
378 | } | |||
379 | ||||
380 | for(bp = dmachine->realm->binding_list; bp; bp = bp->next) { | |||
381 | if (bp->is_regex) { | |||
382 | if (bp->rmatch) { | |||
383 | if (is_timeout || (bp == dmachine->realm->binding_list && !bp->next)) { | |||
384 | best = DM_MATCH_EXACT; | |||
385 | exact_bp = bp; | |||
386 | break; | |||
387 | } | |||
388 | best = DM_MATCH_PARTIAL; | |||
389 | } | |||
390 | } else { | |||
391 | int pmatch = !strncmp(dmachine->digits, bp->digits, strlen(dmachine->digits))(__extension__ (__builtin_constant_p (strlen(dmachine->digits )) && ((__builtin_constant_p (dmachine->digits) && strlen (dmachine->digits) < ((size_t) (strlen(dmachine ->digits)))) || (__builtin_constant_p (bp->digits) && strlen (bp->digits) < ((size_t) (strlen(dmachine->digits ))))) ? __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (dmachine->digits) && __builtin_constant_p (bp-> digits) && (__s1_len = __builtin_strlen (dmachine-> digits), __s2_len = __builtin_strlen (bp->digits), (!((size_t )(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((bp->digits) + 1) - (size_t)(const void *)(bp->digits) == 1) || __s2_len >= 4)) ? __builtin_strcmp (dmachine->digits, bp->digits) : (__builtin_constant_p (dmachine->digits) && ((size_t)(const void *)((dmachine ->digits) + 1) - (size_t)(const void *)(dmachine->digits ) == 1) && (__s1_len = __builtin_strlen (dmachine-> digits), __s1_len < 4) ? (__builtin_constant_p (bp->digits ) && ((size_t)(const void *)((bp->digits) + 1) - ( size_t)(const void *)(bp->digits) == 1) ? __builtin_strcmp (dmachine->digits, bp->digits) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (bp->digits); int __result = (((const unsigned char *) (const char *) (dmachine->digits))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[1] - __s2[1]); if ( __s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[2] - __s2 [2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (dmachine->digits ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ( bp->digits) && ((size_t)(const void *)((bp->digits ) + 1) - (size_t)(const void *)(bp->digits) == 1) && (__s2_len = __builtin_strlen (bp->digits), __s2_len < 4 ) ? (__builtin_constant_p (dmachine->digits) && (( size_t)(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) ? __builtin_strcmp (dmachine ->digits, bp->digits) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (dmachine ->digits); int __result = (((const unsigned char *) (const char *) (bp->digits))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (bp->digits))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (dmachine->digits, bp->digits)))); }) : strncmp (dmachine ->digits, bp->digits, strlen(dmachine->digits)))); | |||
392 | ||||
393 | if (!exact_bp && pmatch && (((pmatches == 1 || ematches == 1) && !rmatches) || is_timeout) && !strcmp(bp->digits, dmachine->digits)__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (bp->digits) && __builtin_constant_p (dmachine-> digits) && (__s1_len = __builtin_strlen (bp->digits ), __s2_len = __builtin_strlen (dmachine->digits), (!((size_t )(const void *)((bp->digits) + 1) - (size_t)(const void *) (bp->digits) == 1) || __s1_len >= 4) && (!((size_t )(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) || __s2_len >= 4)) ? __builtin_strcmp (bp->digits, dmachine->digits) : (__builtin_constant_p (bp->digits) && ((size_t)(const void *)((bp->digits ) + 1) - (size_t)(const void *)(bp->digits) == 1) && (__s1_len = __builtin_strlen (bp->digits), __s1_len < 4 ) ? (__builtin_constant_p (dmachine->digits) && (( size_t)(const void *)((dmachine->digits) + 1) - (size_t)(const void *)(dmachine->digits) == 1) ? __builtin_strcmp (bp-> digits, dmachine->digits) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (dmachine ->digits); int __result = (((const unsigned char *) (const char *) (bp->digits))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (bp->digits))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (bp->digits))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (dmachine->digits) && ((size_t)(const void *)((dmachine ->digits) + 1) - (size_t)(const void *)(dmachine->digits ) == 1) && (__s2_len = __builtin_strlen (dmachine-> digits), __s2_len < 4) ? (__builtin_constant_p (bp->digits ) && ((size_t)(const void *)((bp->digits) + 1) - ( size_t)(const void *)(bp->digits) == 1) ? __builtin_strcmp (bp->digits, dmachine->digits) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (bp->digits); int __result = (((const unsigned char *) (const char *) (dmachine->digits))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[1] - __s2[1]); if ( __s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (dmachine->digits))[2] - __s2 [2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (dmachine->digits ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (bp-> digits, dmachine->digits)))); })) { | |||
394 | best = DM_MATCH_EXACT; | |||
395 | exact_bp = bp; | |||
396 | if (dmachine->cur_digit_len == dmachine->max_digit_len) break; | |||
397 | } | |||
398 | ||||
399 | if (!(both_bp && partial_bp) && strlen(bp->digits) != strlen(dmachine->digits) && pmatch) { | |||
400 | ||||
401 | if (exact_bp) { | |||
402 | best = DM_MATCH_BOTH; | |||
403 | both_bp = bp; | |||
404 | } else { | |||
405 | best = DM_MATCH_PARTIAL; | |||
406 | partial_bp = bp; | |||
407 | } | |||
408 | } | |||
409 | ||||
410 | if (both_bp && exact_bp && partial_bp) break; | |||
411 | } | |||
412 | } | |||
413 | ||||
414 | if (!pmatches) { | |||
415 | best = DM_MATCH_NEVER; | |||
416 | } | |||
417 | ||||
418 | ||||
419 | end: | |||
420 | ||||
421 | if (is_timeout) { | |||
422 | if (both_bp) { | |||
423 | r_bp = exact_bp ? exact_bp : both_bp; | |||
424 | } | |||
425 | } | |||
426 | ||||
427 | if (best == DM_MATCH_EXACT && exact_bp) { | |||
428 | r_bp = exact_bp; | |||
429 | } | |||
430 | ||||
431 | ||||
432 | if (r_bp) { | |||
433 | dmachine->last_matching_binding = r_bp; | |||
434 | switch_set_string(dmachine->last_matching_digits, dmachine->digits)switch_copy_string(dmachine->last_matching_digits, dmachine ->digits, sizeof(dmachine->last_matching_digits)); | |||
435 | best = DM_MATCH_EXACT; | |||
436 | } | |||
437 | ||||
438 | return best; | |||
439 | ||||
440 | } | |||
441 | ||||
442 | static switch_bool_t switch_ivr_dmachine_check_timeout(switch_ivr_dmachine_t *dmachine) | |||
443 | { | |||
444 | switch_time_t now = switch_time_now(); | |||
445 | uint32_t timeout = dmachine->cur_digit_len ? dmachine->digit_timeout_ms : dmachine->input_timeout_ms; | |||
446 | ||||
447 | if (!dmachine->last_digit_time) dmachine->last_digit_time = now; | |||
448 | ||||
449 | if (timeout) { | |||
450 | if ((uint32_t)((now - dmachine->last_digit_time) / 1000) > timeout) { | |||
451 | return SWITCH_TRUE; | |||
452 | } | |||
453 | } | |||
454 | ||||
455 | return SWITCH_FALSE; | |||
456 | } | |||
457 | ||||
458 | SWITCH_DECLARE(switch_ivr_dmachine_match_t *)__attribute__((visibility("default"))) switch_ivr_dmachine_match_t * switch_ivr_dmachine_get_match(switch_ivr_dmachine_t *dmachine) | |||
459 | { | |||
460 | if (dmachine->is_match) { | |||
461 | dmachine->is_match = 0; | |||
462 | return &dmachine->match; | |||
463 | } | |||
464 | ||||
465 | return NULL((void*)0); | |||
466 | } | |||
467 | ||||
468 | SWITCH_DECLARE(const char *)__attribute__((visibility("default"))) const char * switch_ivr_dmachine_get_failed_digits(switch_ivr_dmachine_t *dmachine) | |||
469 | { | |||
470 | ||||
471 | return dmachine->last_failed_digits; | |||
472 | } | |||
473 | ||||
474 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_ping(switch_ivr_dmachine_t *dmachine, switch_ivr_dmachine_match_t **match_p) | |||
475 | { | |||
476 | switch_bool_t is_timeout = switch_ivr_dmachine_check_timeout(dmachine); | |||
477 | dm_match_t is_match = switch_ivr_dmachine_check_match(dmachine, is_timeout); | |||
478 | switch_status_t r, s; | |||
479 | int clear = 0; | |||
480 | ||||
481 | if (is_match == DM_MATCH_NEVER) { | |||
482 | is_timeout++; | |||
483 | } | |||
484 | ||||
485 | if (switch_mutex_trylock(dmachine->mutex) != SWITCH_STATUS_SUCCESS) { | |||
486 | return SWITCH_STATUS_SUCCESS; | |||
487 | } | |||
488 | ||||
489 | if (zstr(dmachine->digits)_zstr(dmachine->digits) && !is_timeout) { | |||
490 | r = SWITCH_STATUS_SUCCESS; | |||
491 | } else if (dmachine->cur_digit_len > dmachine->max_digit_len) { | |||
492 | r = SWITCH_STATUS_FALSE; | |||
493 | } else if (is_match == DM_MATCH_EXACT || (is_match == DM_MATCH_BOTH && is_timeout)) { | |||
494 | r = SWITCH_STATUS_FOUND; | |||
495 | ||||
496 | dmachine->match.match_digits = dmachine->last_matching_digits; | |||
497 | dmachine->match.match_key = dmachine->last_matching_binding->key; | |||
498 | dmachine->match.user_data = dmachine->last_matching_binding->user_data; | |||
499 | ||||
500 | if (match_p) { | |||
501 | *match_p = &dmachine->match; | |||
502 | } | |||
503 | ||||
504 | dmachine->is_match = 1; | |||
505 | ||||
506 | dmachine->match.type = DM_MATCH_POSITIVE; | |||
507 | ||||
508 | if (dmachine->last_matching_binding->callback) { | |||
509 | s = dmachine->last_matching_binding->callback(&dmachine->match); | |||
510 | ||||
511 | switch(s) { | |||
512 | case SWITCH_STATUS_CONTINUE: | |||
513 | r = SWITCH_STATUS_SUCCESS; | |||
514 | break; | |||
515 | case SWITCH_STATUS_SUCCESS: | |||
516 | break; | |||
517 | default: | |||
518 | r = SWITCH_STATUS_BREAK; | |||
519 | break; | |||
520 | } | |||
521 | } | |||
522 | ||||
523 | if (dmachine->match_callback) { | |||
524 | dmachine->match.user_data = dmachine->user_data; | |||
525 | s = dmachine->match_callback(&dmachine->match); | |||
526 | ||||
527 | switch(s) { | |||
528 | case SWITCH_STATUS_CONTINUE: | |||
529 | r = SWITCH_STATUS_SUCCESS; | |||
530 | break; | |||
531 | case SWITCH_STATUS_SUCCESS: | |||
532 | break; | |||
533 | default: | |||
534 | r = SWITCH_STATUS_BREAK; | |||
535 | break; | |||
536 | } | |||
537 | ||||
538 | } | |||
539 | ||||
540 | clear++; | |||
541 | } else if (is_timeout) { | |||
542 | r = SWITCH_STATUS_TIMEOUT; | |||
543 | } else if (is_match == DM_MATCH_NONE && dmachine->cur_digit_len == dmachine->max_digit_len) { | |||
544 | r = SWITCH_STATUS_NOTFOUND; | |||
545 | } else { | |||
546 | r = SWITCH_STATUS_SUCCESS; | |||
547 | } | |||
548 | ||||
549 | if (r != SWITCH_STATUS_FOUND && r != SWITCH_STATUS_SUCCESS && r != SWITCH_STATUS_BREAK) { | |||
550 | switch_set_string(dmachine->last_failed_digits, dmachine->digits)switch_copy_string(dmachine->last_failed_digits, dmachine-> digits, sizeof(dmachine->last_failed_digits)); | |||
551 | dmachine->match.match_digits = dmachine->last_failed_digits; | |||
552 | ||||
553 | dmachine->match.type = DM_MATCH_NEGATIVE; | |||
554 | ||||
555 | if (dmachine->nonmatch_callback) { | |||
556 | dmachine->match.user_data = dmachine->user_data; | |||
557 | s = dmachine->nonmatch_callback(&dmachine->match); | |||
558 | ||||
559 | switch(s) { | |||
560 | case SWITCH_STATUS_CONTINUE: | |||
561 | r = SWITCH_STATUS_SUCCESS; | |||
562 | break; | |||
563 | case SWITCH_STATUS_SUCCESS: | |||
564 | break; | |||
565 | default: | |||
566 | r = SWITCH_STATUS_BREAK; | |||
567 | break; | |||
568 | } | |||
569 | ||||
570 | } | |||
571 | ||||
572 | clear++; | |||
573 | } | |||
574 | ||||
575 | if (clear) { | |||
576 | switch_ivr_dmachine_clear(dmachine); | |||
577 | } | |||
578 | ||||
579 | dmachine->last_return = r; | |||
580 | ||||
581 | switch_mutex_unlock(dmachine->mutex); | |||
582 | ||||
583 | return r; | |||
584 | } | |||
585 | ||||
586 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_feed(switch_ivr_dmachine_t *dmachine, const char *digits, switch_ivr_dmachine_match_t **match) | |||
587 | { | |||
588 | const char *p; | |||
589 | switch_status_t status = SWITCH_STATUS_BREAK; | |||
590 | ||||
591 | if (!zstr(digits)_zstr(digits)) { | |||
592 | status = SWITCH_STATUS_SUCCESS; | |||
593 | } | |||
594 | ||||
595 | for (p = digits; p && *p; p++) { | |||
596 | switch_mutex_lock(dmachine->mutex); | |||
597 | if (dmachine->cur_digit_len < dmachine->max_digit_len) { | |||
598 | switch_status_t istatus; | |||
599 | char *e = dmachine->digits + strlen(dmachine->digits); | |||
600 | ||||
601 | *e++ = *p; | |||
602 | *e = '\0'; | |||
603 | dmachine->cur_digit_len++; | |||
604 | switch_mutex_unlock(dmachine->mutex); | |||
605 | dmachine->last_digit_time = switch_time_now(); | |||
606 | if (status == SWITCH_STATUS_SUCCESS && (istatus = switch_ivr_dmachine_ping(dmachine, match)) != SWITCH_STATUS_SUCCESS) { | |||
607 | status = istatus; | |||
608 | } | |||
609 | } else { | |||
610 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 610, ((void*)0), SWITCH_LOG_ERROR, "dmachine overflow error!\n"); | |||
611 | status = SWITCH_STATUS_FALSE; | |||
612 | } | |||
613 | } | |||
614 | ||||
615 | return status; | |||
616 | } | |||
617 | ||||
618 | SWITCH_DECLARE(switch_bool_t)__attribute__((visibility("default"))) switch_bool_t switch_ivr_dmachine_is_parsing(switch_ivr_dmachine_t *dmachine) | |||
619 | { | |||
620 | return !!dmachine->cur_digit_len; | |||
621 | } | |||
622 | ||||
623 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_dmachine_clear(switch_ivr_dmachine_t *dmachine) | |||
624 | { | |||
625 | ||||
626 | memset(dmachine->digits, 0, sizeof(dmachine->digits)); | |||
627 | dmachine->cur_digit_len = 0; | |||
628 | dmachine->last_digit_time = 0; | |||
629 | return SWITCH_STATUS_SUCCESS; | |||
630 | } | |||
631 | ||||
632 | ||||
633 | ||||
634 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_session_echo(switch_core_session_t *session, switch_input_args_t *args) | |||
635 | { | |||
636 | switch_status_t status; | |||
637 | switch_frame_t *read_frame; | |||
638 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
639 | int orig_vid = switch_channel_test_flag(channel, CF_VIDEO); | |||
640 | ||||
641 | if (switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 641) != SWITCH_STATUS_SUCCESS) { | |||
642 | return SWITCH_STATUS_FALSE; | |||
643 | } | |||
644 | ||||
645 | arg_recursion_check_start(args)if (args) { if (args->loops >= 25) { switch_log_printf( SWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 645, ((void*)0), SWITCH_LOG_ERROR, "RECURSION ERROR! It's not the best idea to call things that collect input recursively from an input callback.\n" ); return SWITCH_STATUS_GENERR; } else {args->loops++;} }; | |||
646 | ||||
647 | restart: | |||
648 | ||||
649 | while (switch_channel_ready(channel)switch_channel_test_ready(channel, SWITCH_TRUE, SWITCH_FALSE)) { | |||
650 | status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); | |||
651 | if (!SWITCH_READ_ACCEPTABLE(status)(status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK || status == SWITCH_STATUS_INUSE)) { | |||
652 | break; | |||
653 | } | |||
654 | ||||
655 | if (!orig_vid && switch_channel_test_flag(channel, CF_VIDEO)) { | |||
656 | orig_vid = 1; | |||
657 | goto restart; | |||
658 | } | |||
659 | ||||
660 | ||||
661 | switch_ivr_parse_all_events(session); | |||
662 | ||||
663 | if (args && (args->input_callback || args->buf || args->buflen)) { | |||
664 | switch_dtmf_t dtmf = {0}; | |||
665 | ||||
666 | /* | |||
667 | dtmf handler function you can hook up to be executed when a digit is dialed during playback | |||
668 | if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. | |||
669 | */ | |||
670 | if (switch_channel_has_dtmf(channel)) { | |||
671 | if (!args->input_callback && !args->buf) { | |||
672 | status = SWITCH_STATUS_BREAK; | |||
673 | break; | |||
674 | } | |||
675 | switch_channel_dequeue_dtmf(channel, &dtmf); | |||
676 | if (args->input_callback) { | |||
677 | status = args->input_callback(session, (void *) &dtmf, SWITCH_INPUT_TYPE_DTMF, args->buf, args->buflen); | |||
678 | } else { | |||
679 | *((char *) args->buf) = dtmf.digit; | |||
680 | status = SWITCH_STATUS_BREAK; | |||
681 | } | |||
682 | } | |||
683 | ||||
684 | if (args->input_callback) { | |||
685 | switch_event_t *event = NULL((void*)0); | |||
686 | ||||
687 | if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { | |||
688 | status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); | |||
689 | switch_event_destroy(&event); | |||
690 | } | |||
691 | } | |||
692 | ||||
693 | if (status != SWITCH_STATUS_SUCCESS) { | |||
694 | break; | |||
695 | } | |||
696 | } | |||
697 | ||||
698 | ||||
699 | switch_core_session_write_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); | |||
700 | ||||
701 | #ifndef SWITCH_VIDEO_IN_THREADS | |||
702 | status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); | |||
703 | ||||
704 | if (!SWITCH_READ_ACCEPTABLE(status)(status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK || status == SWITCH_STATUS_INUSE)) { | |||
705 | break; | |||
706 | } | |||
707 | ||||
708 | if (switch_test_flag(read_frame, SFF_CNG)((read_frame)->flags & SFF_CNG)) { | |||
709 | continue; | |||
710 | } | |||
711 | ||||
712 | switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); | |||
713 | #endif | |||
714 | ||||
715 | if (switch_channel_test_flag(channel, CF_BREAK)) { | |||
716 | switch_channel_clear_flag(channel, CF_BREAK); | |||
717 | break; | |||
718 | } | |||
719 | } | |||
720 | ||||
721 | return SWITCH_STATUS_SUCCESS; | |||
722 | } | |||
723 | ||||
724 | typedef struct { | |||
725 | switch_file_handle_t fh; | |||
726 | int mux; | |||
727 | int loop; | |||
728 | char *file; | |||
729 | } displace_helper_t; | |||
730 | ||||
731 | static switch_bool_t write_displace_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
732 | { | |||
733 | displace_helper_t *dh = (displace_helper_t *) user_data; | |||
734 | ||||
735 | switch (type) { | |||
736 | case SWITCH_ABC_TYPE_INIT: | |||
737 | break; | |||
738 | case SWITCH_ABC_TYPE_CLOSE: | |||
739 | if (dh) { | |||
740 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
741 | switch_channel_t *channel; | |||
742 | ||||
743 | switch_core_file_close(&dh->fh); | |||
744 | ||||
745 | if (session && (channel = switch_core_session_get_channel(session))) { | |||
746 | switch_channel_set_private(channel, dh->file, NULL((void*)0)); | |||
747 | } | |||
748 | } | |||
749 | break; | |||
750 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
751 | { | |||
752 | switch_frame_t *rframe = switch_core_media_bug_get_read_replace_frame(bug); | |||
753 | if (dh && !dh->mux) { | |||
754 | memset(rframe->data, 255, rframe->datalen); | |||
755 | } | |||
756 | switch_core_media_bug_set_read_replace_frame(bug, rframe); | |||
757 | } | |||
758 | break; | |||
759 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
760 | if (dh) { | |||
761 | switch_frame_t *rframe = NULL((void*)0); | |||
762 | switch_size_t len; | |||
763 | switch_status_t st; | |||
764 | ||||
765 | rframe = switch_core_media_bug_get_write_replace_frame(bug); | |||
766 | len = rframe->samples; | |||
767 | ||||
768 | if (dh->mux) { | |||
769 | int16_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
770 | int16_t *fp = rframe->data; | |||
771 | uint32_t x; | |||
772 | ||||
773 | st = switch_core_file_read(&dh->fh, buf, &len); | |||
774 | ||||
775 | for (x = 0; x < (uint32_t) len * dh->fh.channels; x++) { | |||
776 | int32_t mixed = fp[x] + buf[x]; | |||
777 | switch_normalize_to_16bit(mixed)if (mixed > 32767) mixed = 32767; else if (mixed < -32768 ) mixed = -32768;; | |||
778 | fp[x] = (int16_t) mixed; | |||
779 | } | |||
780 | } else { | |||
781 | st = switch_core_file_read(&dh->fh, rframe->data, &len); | |||
782 | if (len < rframe->samples) { | |||
783 | memset((char *)rframe->data + len * 2 * dh->fh.channels, 0, (rframe->datalen - len) * 2 * dh->fh.channels); | |||
784 | } | |||
785 | } | |||
786 | ||||
787 | rframe->datalen = rframe->samples * 2 * dh->fh.channels; | |||
788 | ||||
789 | if (st != SWITCH_STATUS_SUCCESS || len == 0) { | |||
790 | if (dh->loop) { | |||
791 | uint32_t pos = 0; | |||
792 | switch_core_file_seek(&dh->fh, &pos, 0, SEEK_SET0); | |||
793 | } else { | |||
794 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
795 | switch_channel_t *channel; | |||
796 | ||||
797 | if (session && (channel = switch_core_session_get_channel(session))) { | |||
798 | switch_channel_set_private(channel, dh->file, NULL((void*)0)); | |||
799 | } | |||
800 | return SWITCH_FALSE; | |||
801 | } | |||
802 | } | |||
803 | ||||
804 | switch_core_media_bug_set_write_replace_frame(bug, rframe); | |||
805 | } | |||
806 | break; | |||
807 | case SWITCH_ABC_TYPE_WRITE: | |||
808 | default: | |||
809 | break; | |||
810 | } | |||
811 | ||||
812 | return SWITCH_TRUE; | |||
813 | } | |||
814 | ||||
815 | static switch_bool_t read_displace_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
816 | { | |||
817 | displace_helper_t *dh = (displace_helper_t *) user_data; | |||
818 | ||||
819 | switch (type) { | |||
820 | case SWITCH_ABC_TYPE_INIT: | |||
821 | break; | |||
822 | case SWITCH_ABC_TYPE_CLOSE: | |||
823 | if (dh) { | |||
824 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
825 | switch_channel_t *channel; | |||
826 | ||||
827 | switch_core_file_close(&dh->fh); | |||
828 | ||||
829 | if (session && (channel = switch_core_session_get_channel(session))) { | |||
830 | switch_channel_set_private(channel, dh->file, NULL((void*)0)); | |||
831 | } | |||
832 | } | |||
833 | break; | |||
834 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
835 | { | |||
836 | switch_frame_t *rframe = switch_core_media_bug_get_write_replace_frame(bug); | |||
837 | if (dh && !dh->mux) { | |||
838 | memset(rframe->data, 255, rframe->datalen); | |||
839 | } | |||
840 | switch_core_media_bug_set_write_replace_frame(bug, rframe); | |||
841 | } | |||
842 | break; | |||
843 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
844 | if (dh) { | |||
845 | switch_frame_t *rframe = NULL((void*)0); | |||
846 | switch_size_t len; | |||
847 | switch_status_t st; | |||
848 | rframe = switch_core_media_bug_get_read_replace_frame(bug); | |||
849 | len = rframe->samples; | |||
850 | ||||
851 | if (dh->mux) { | |||
852 | int16_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
853 | int16_t *fp = rframe->data; | |||
854 | uint32_t x; | |||
855 | ||||
856 | st = switch_core_file_read(&dh->fh, buf, &len); | |||
857 | ||||
858 | for (x = 0; x < (uint32_t) len * dh->fh.channels; x++) { | |||
859 | int32_t mixed = fp[x] + buf[x]; | |||
860 | switch_normalize_to_16bit(mixed)if (mixed > 32767) mixed = 32767; else if (mixed < -32768 ) mixed = -32768;; | |||
861 | fp[x] = (int16_t) mixed; | |||
862 | } | |||
863 | ||||
864 | } else { | |||
865 | st = switch_core_file_read(&dh->fh, rframe->data, &len); | |||
866 | rframe->samples = (uint32_t) len; | |||
867 | } | |||
868 | ||||
869 | rframe->datalen = rframe->samples * 2 * dh->fh.channels; | |||
870 | ||||
871 | ||||
872 | if (st != SWITCH_STATUS_SUCCESS || len == 0) { | |||
873 | if (dh->loop) { | |||
874 | uint32_t pos = 0; | |||
875 | switch_core_file_seek(&dh->fh, &pos, 0, SEEK_SET0); | |||
876 | } else { | |||
877 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
878 | switch_channel_t *channel; | |||
879 | ||||
880 | if (session && (channel = switch_core_session_get_channel(session))) { | |||
881 | switch_channel_set_private(channel, dh->file, NULL((void*)0)); | |||
882 | } | |||
883 | return SWITCH_FALSE; | |||
884 | } | |||
885 | } | |||
886 | ||||
887 | switch_core_media_bug_set_read_replace_frame(bug, rframe); | |||
888 | } | |||
889 | break; | |||
890 | case SWITCH_ABC_TYPE_WRITE: | |||
891 | default: | |||
892 | break; | |||
893 | } | |||
894 | ||||
895 | return SWITCH_TRUE; | |||
896 | } | |||
897 | ||||
898 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_displace_session(switch_core_session_t *session, const char *file) | |||
899 | { | |||
900 | switch_media_bug_t *bug; | |||
901 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
902 | ||||
903 | if ((bug = switch_channel_get_private(channel, file))) { | |||
904 | switch_channel_set_private(channel, file, NULL((void*)0)); | |||
905 | switch_core_media_bug_remove(session, &bug); | |||
906 | return SWITCH_STATUS_SUCCESS; | |||
907 | } | |||
908 | ||||
909 | return SWITCH_STATUS_FALSE; | |||
910 | } | |||
911 | ||||
912 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_displace_session(switch_core_session_t *session, const char *file, uint32_t limit, const char *flags) | |||
913 | { | |||
914 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
915 | switch_media_bug_t *bug; | |||
916 | switch_status_t status; | |||
917 | time_t to = 0; | |||
918 | char *ext; | |||
919 | const char *prefix; | |||
920 | displace_helper_t *dh; | |||
921 | const char *p; | |||
922 | switch_bool_t hangup_on_error = SWITCH_FALSE; | |||
923 | switch_codec_implementation_t read_impl = { 0 }; | |||
924 | switch_core_session_get_read_impl(session, &read_impl); | |||
925 | ||||
926 | if ((p = switch_channel_get_variable(channel, "DISPLACE_HANGUP_ON_ERROR")switch_channel_get_variable_dup(channel, "DISPLACE_HANGUP_ON_ERROR" , SWITCH_TRUE, -1))) { | |||
927 | hangup_on_error = switch_true(p); | |||
928 | } | |||
929 | ||||
930 | if (zstr(file)_zstr(file)) { | |||
931 | return SWITCH_STATUS_FALSE; | |||
932 | } | |||
933 | ||||
934 | if ((status = switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 934)) != SWITCH_STATUS_SUCCESS) { | |||
935 | return SWITCH_STATUS_FALSE; | |||
936 | } | |||
937 | ||||
938 | if (!switch_channel_media_up(channel)(switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag (channel, CF_EARLY_MEDIA)) || !switch_core_session_get_read_codec(session)) { | |||
939 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 939, (const char*)(session), SWITCH_LOG_ERROR, "Can not displace session. Media not enabled on channel\n"); | |||
940 | return SWITCH_STATUS_FALSE; | |||
941 | } | |||
942 | ||||
943 | if ((bug = switch_channel_get_private(channel, file))) { | |||
944 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 944, (const char*)(session), SWITCH_LOG_ERROR, "Only 1 of the same file per channel please!\n"); | |||
945 | return SWITCH_STATUS_FALSE; | |||
946 | } | |||
947 | ||||
948 | if (!(dh = switch_core_session_alloc(session, sizeof(*dh))switch_core_perform_session_alloc(session, sizeof(*dh), "src/switch_ivr_async.c" , (const char *)__func__, 948))) { | |||
949 | return SWITCH_STATUS_MEMERR; | |||
950 | } | |||
951 | ||||
952 | if (!(prefix = switch_channel_get_variable(channel, "sound_prefix")switch_channel_get_variable_dup(channel, "sound_prefix", SWITCH_TRUE , -1))) { | |||
953 | prefix = SWITCH_GLOBAL_dirs.base_dir; | |||
954 | } | |||
955 | ||||
956 | if (!strstr(file, SWITCH_URL_SEPARATOR"://")) { | |||
957 | if (!switch_is_file_path(file)) { | |||
958 | char *tfile = NULL((void*)0); | |||
959 | char *e; | |||
960 | ||||
961 | if (*file == '[') { | |||
962 | tfile = switch_core_session_strdup(session, file)switch_core_perform_session_strdup(session, file, "src/switch_ivr_async.c" , (const char *)__func__, 962); | |||
963 | if ((e = switch_find_end_paren(tfile, '[', ']'))) { | |||
964 | *e = '\0'; | |||
965 | file = e + 1; | |||
966 | } else { | |||
967 | tfile = NULL((void*)0); | |||
968 | } | |||
969 | } | |||
970 | ||||
971 | file = switch_core_session_sprintf(session, "%s%s%s%s%s", switch_str_nil(tfile)(tfile ? tfile : ""), tfile ? "]" : "", prefix, SWITCH_PATH_SEPARATOR"/", file); | |||
972 | } | |||
973 | if ((ext = strrchr(file, '.'))) { | |||
974 | ext++; | |||
975 | } else { | |||
976 | ext = read_impl.iananame; | |||
977 | file = switch_core_session_sprintf(session, "%s.%s", file, ext); | |||
978 | } | |||
979 | } | |||
980 | ||||
981 | dh->fh.channels = read_impl.number_of_channels; | |||
982 | dh->fh.samplerate = read_impl.actual_samples_per_second; | |||
983 | dh->file = switch_core_session_strdup(session, file)switch_core_perform_session_strdup(session, file, "src/switch_ivr_async.c" , (const char *)__func__, 983); | |||
984 | ||||
985 | if (switch_core_file_open(&dh->fh,switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 988, &dh->fh, file, read_impl.number_of_channels , read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, ((void*)0)) | |||
986 | file,switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 988, &dh->fh, file, read_impl.number_of_channels , read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, ((void*)0)) | |||
987 | read_impl.number_of_channels,switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 988, &dh->fh, file, read_impl.number_of_channels , read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, ((void*)0)) | |||
988 | read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL)switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 988, &dh->fh, file, read_impl.number_of_channels , read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, ((void*)0)) != SWITCH_STATUS_SUCCESS) { | |||
989 | if (hangup_on_error) { | |||
990 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 990, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
991 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
992 | } | |||
993 | return SWITCH_STATUS_GENERR; | |||
994 | } | |||
995 | ||||
996 | if (limit) { | |||
997 | to = switch_epoch_time_now(NULL((void*)0)) + limit; | |||
998 | } | |||
999 | ||||
1000 | if (flags && strchr(flags, 'm')(__extension__ (__builtin_constant_p ('m') && !__builtin_constant_p (flags) && ('m') == '\0' ? (char *) __rawmemchr (flags , 'm') : __builtin_strchr (flags, 'm')))) { | |||
1001 | dh->mux++; | |||
1002 | } | |||
1003 | ||||
1004 | if (flags && strchr(flags, 'l')(__extension__ (__builtin_constant_p ('l') && !__builtin_constant_p (flags) && ('l') == '\0' ? (char *) __rawmemchr (flags , 'l') : __builtin_strchr (flags, 'l')))) { | |||
1005 | dh->loop++; | |||
1006 | } | |||
1007 | ||||
1008 | if (flags && strchr(flags, 'r')(__extension__ (__builtin_constant_p ('r') && !__builtin_constant_p (flags) && ('r') == '\0' ? (char *) __rawmemchr (flags , 'r') : __builtin_strchr (flags, 'r')))) { | |||
1009 | status = switch_core_media_bug_add(session, "displace", file, | |||
1010 | read_displace_callback, dh, to, SMBF_WRITE_REPLACE | SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug); | |||
1011 | } else { | |||
1012 | status = switch_core_media_bug_add(session, "displace", file, | |||
1013 | write_displace_callback, dh, to, SMBF_WRITE_REPLACE | SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug); | |||
1014 | } | |||
1015 | ||||
1016 | if (status != SWITCH_STATUS_SUCCESS) { | |||
1017 | switch_core_file_close(&dh->fh); | |||
1018 | return status; | |||
1019 | } | |||
1020 | ||||
1021 | switch_channel_set_private(channel, file, bug); | |||
1022 | ||||
1023 | return SWITCH_STATUS_SUCCESS; | |||
1024 | } | |||
1025 | ||||
1026 | ||||
1027 | struct record_helper { | |||
1028 | char *file; | |||
1029 | switch_file_handle_t *fh; | |||
1030 | switch_file_handle_t in_fh; | |||
1031 | switch_file_handle_t out_fh; | |||
1032 | int native; | |||
1033 | uint32_t packet_len; | |||
1034 | int min_sec; | |||
1035 | int final_timeout_ms; | |||
1036 | int initial_timeout_ms; | |||
1037 | int silence_threshold; | |||
1038 | int silence_timeout_ms; | |||
1039 | switch_time_t silence_time; | |||
1040 | int rready; | |||
1041 | int wready; | |||
1042 | switch_time_t last_read_time; | |||
1043 | switch_time_t last_write_time; | |||
1044 | switch_bool_t hangup_on_error; | |||
1045 | switch_codec_implementation_t read_impl; | |||
1046 | switch_bool_t speech_detected; | |||
1047 | switch_buffer_t *thread_buffer; | |||
1048 | switch_thread_t *thread; | |||
1049 | switch_mutex_t *buffer_mutex; | |||
1050 | int thread_ready; | |||
1051 | const char *completion_cause; | |||
1052 | }; | |||
1053 | ||||
1054 | /** | |||
1055 | * Set the recording completion cause. The cause can only be set once, to minimize the logic in the record_callback. | |||
1056 | * [The completion_cause strings are essentially those of an MRCP Recorder resource.] | |||
1057 | */ | |||
1058 | static void set_completion_cause(struct record_helper *rh, const char *completion_cause) | |||
1059 | { | |||
1060 | if (!rh->completion_cause) { | |||
1061 | rh->completion_cause = completion_cause; | |||
1062 | } | |||
1063 | } | |||
1064 | ||||
1065 | static switch_bool_t is_silence_frame(switch_frame_t *frame, int silence_threshold, switch_codec_implementation_t *codec_impl) | |||
1066 | { | |||
1067 | int16_t *fdata = (int16_t *) frame->data; | |||
1068 | uint32_t samples = frame->datalen / sizeof(*fdata); | |||
1069 | switch_bool_t is_silence = SWITCH_TRUE; | |||
1070 | uint32_t channel_num = 0; | |||
1071 | ||||
1072 | int divisor = 0; | |||
1073 | if (!(divisor = codec_impl->samples_per_second / 8000)) { | |||
1074 | divisor = 1; | |||
1075 | } | |||
1076 | ||||
1077 | /* is silence only if every channel is silent */ | |||
1078 | for (channel_num = 0; channel_num < codec_impl->number_of_channels && is_silence; channel_num++) { | |||
1079 | uint32_t count = 0, j = channel_num; | |||
1080 | double energy = 0; | |||
1081 | for (count = 0; count < samples; count++) { | |||
1082 | energy += abs(fdata[j]); | |||
1083 | j += codec_impl->number_of_channels; | |||
1084 | } | |||
1085 | is_silence &= (uint32_t) ((energy / (samples / divisor)) < silence_threshold); | |||
1086 | } | |||
1087 | ||||
1088 | return is_silence; | |||
1089 | } | |||
1090 | ||||
1091 | static void send_record_stop_event(switch_channel_t *channel, switch_codec_implementation_t *read_impl, struct record_helper *rh) | |||
1092 | { | |||
1093 | switch_event_t *event; | |||
1094 | ||||
1095 | if (rh->fh) { | |||
1096 | switch_channel_set_variable_printf(channel, "record_samples", "%d", rh->fh->samples_out); | |||
1097 | if (read_impl->actual_samples_per_second) { | |||
1098 | switch_channel_set_variable_printf(channel, "record_seconds", "%d", rh->fh->samples_out / read_impl->actual_samples_per_second); | |||
1099 | switch_channel_set_variable_printf(channel, "record_ms", "%d", rh->fh->samples_out / (read_impl->actual_samples_per_second / 1000)); | |||
1100 | } | |||
1101 | } | |||
1102 | ||||
1103 | if (!zstr(rh->completion_cause)_zstr(rh->completion_cause)) { | |||
1104 | switch_channel_set_variable_printf(channel, "record_completion_cause", "%s", rh->completion_cause); | |||
1105 | } | |||
1106 | ||||
1107 | if (switch_event_create(&event, SWITCH_EVENT_RECORD_STOP)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 1107, &event, SWITCH_EVENT_RECORD_STOP , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
1108 | switch_channel_event_set_data(channel, event); | |||
1109 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", rh->file); | |||
1110 | if (!zstr(rh->completion_cause)_zstr(rh->completion_cause)) { | |||
1111 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-Completion-Cause", rh->completion_cause); | |||
1112 | } | |||
1113 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 1113, &event, ((void*)0)); | |||
1114 | } | |||
1115 | } | |||
1116 | ||||
1117 | static void *SWITCH_THREAD_FUNC recording_thread(switch_thread_t *thread, void *obj) | |||
1118 | { | |||
1119 | switch_media_bug_t *bug = (switch_media_bug_t *) obj; | |||
1120 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
1121 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1122 | struct record_helper *rh; | |||
1123 | switch_size_t bsize = SWITCH_RECOMMENDED_BUFFER_SIZE8192, samples = 0, inuse = 0; | |||
1124 | unsigned char *data = switch_core_session_alloc(session, bsize)switch_core_perform_session_alloc(session, bsize, "src/switch_ivr_async.c" , (const char *)__func__, 1124); | |||
1125 | int channels = switch_core_media_bug_test_flag(bug, SMBF_STEREO) ? 2 : 1; | |||
1126 | ||||
1127 | if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { | |||
1128 | return NULL((void*)0); | |||
1129 | } | |||
1130 | ||||
1131 | rh = switch_core_media_bug_get_user_data(bug); | |||
1132 | switch_buffer_create_dynamic(&rh->thread_buffer, 1024 * 512, 1024 * 64, 0); | |||
1133 | rh->thread_ready = 1; | |||
1134 | ||||
1135 | while(switch_test_flag(rh->fh, SWITCH_FILE_OPEN)((rh->fh)->flags & SWITCH_FILE_OPEN)) { | |||
1136 | switch_mutex_lock(rh->buffer_mutex); | |||
1137 | inuse = switch_buffer_inuse(rh->thread_buffer); | |||
1138 | ||||
1139 | if (rh->thread_ready && switch_channel_up_nosig(channel)(switch_channel_get_state(channel) < CS_HANGUP) && inuse < bsize) { | |||
1140 | switch_mutex_unlock(rh->buffer_mutex); | |||
1141 | switch_yield(20000)switch_sleep(20000);; | |||
1142 | continue; | |||
1143 | } else if ((!rh->thread_ready || switch_channel_down_nosig(channel)(switch_channel_get_state(channel) >= CS_HANGUP)) && !inuse) { | |||
1144 | break; | |||
1145 | } | |||
1146 | ||||
1147 | samples = switch_buffer_read(rh->thread_buffer, data, bsize) / 2 / channels; | |||
1148 | switch_mutex_unlock(rh->buffer_mutex); | |||
1149 | ||||
1150 | if (switch_core_file_write(rh->fh, data, &samples) != SWITCH_STATUS_SUCCESS) { | |||
1151 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1151, (const char*)(session), SWITCH_LOG_ERROR, "Error writing %s\n", rh->file); | |||
1152 | /* File write failed */ | |||
1153 | set_completion_cause(rh, "uri-failure"); | |||
1154 | if (rh->hangup_on_error) { | |||
1155 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 1155, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
1156 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
1157 | } | |||
1158 | } | |||
1159 | } | |||
1160 | ||||
1161 | switch_core_session_rwunlock(session); | |||
1162 | ||||
1163 | return NULL((void*)0); | |||
1164 | } | |||
1165 | ||||
1166 | static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
1167 | { | |||
1168 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
1169 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1170 | struct record_helper *rh = (struct record_helper *) user_data; | |||
1171 | switch_event_t *event; | |||
1172 | switch_frame_t *nframe; | |||
1173 | switch_size_t len = 0; | |||
1174 | int mask = switch_core_media_bug_test_flag(bug, SMBF_MASK); | |||
1175 | unsigned char null_data[SWITCH_RECOMMENDED_BUFFER_SIZE8192] = {0}; | |||
1176 | ||||
1177 | switch (type) { | |||
1178 | case SWITCH_ABC_TYPE_INIT: | |||
1179 | { | |||
1180 | const char *var = switch_channel_get_variable(channel, "RECORD_USE_THREAD")switch_channel_get_variable_dup(channel, "RECORD_USE_THREAD", SWITCH_TRUE, -1); | |||
1181 | ||||
1182 | if (zstr(var)_zstr(var) || switch_true(var)) { | |||
1183 | switch_threadattr_t *thd_attr = NULL((void*)0); | |||
1184 | switch_memory_pool_t *pool = switch_core_session_get_pool(session); | |||
1185 | int sanity = 200; | |||
1186 | ||||
1187 | ||||
1188 | switch_core_session_get_read_impl(session, &rh->read_impl); | |||
1189 | switch_mutex_init(&rh->buffer_mutex, SWITCH_MUTEX_NESTED0x1, pool); | |||
1190 | switch_threadattr_create(&thd_attr, pool); | |||
1191 | switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE240 * 1024); | |||
1192 | switch_thread_create(&rh->thread, thd_attr, recording_thread, bug, pool); | |||
1193 | ||||
1194 | while(--sanity > 0 && !rh->thread_ready) { | |||
1195 | switch_yield(10000)switch_sleep(10000);; | |||
1196 | } | |||
1197 | } | |||
1198 | ||||
1199 | ||||
1200 | if (switch_event_create(&event, SWITCH_EVENT_RECORD_START)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 1200, &event, SWITCH_EVENT_RECORD_START , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
1201 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", rh->file); | |||
1202 | switch_channel_event_set_data(channel, event); | |||
1203 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 1203, &event, ((void*)0)); | |||
1204 | } | |||
1205 | ||||
1206 | rh->silence_time = switch_micro_time_now(); | |||
1207 | rh->silence_timeout_ms = rh->initial_timeout_ms; | |||
1208 | rh->speech_detected = SWITCH_FALSE; | |||
1209 | rh->completion_cause = NULL((void*)0); | |||
1210 | ||||
1211 | switch_core_session_get_read_impl(session, &rh->read_impl); | |||
1212 | } | |||
1213 | break; | |||
1214 | case SWITCH_ABC_TYPE_TAP_NATIVE_READ: | |||
1215 | { | |||
1216 | switch_time_t now = switch_micro_time_now(); | |||
1217 | switch_time_t diff; | |||
1218 | ||||
1219 | rh->rready = 1; | |||
1220 | ||||
1221 | nframe = switch_core_media_bug_get_native_read_frame(bug); | |||
1222 | len = nframe->datalen; | |||
1223 | ||||
1224 | if (!rh->wready) { | |||
1225 | unsigned char fill_data[SWITCH_RECOMMENDED_BUFFER_SIZE8192] = {0}; | |||
1226 | switch_size_t fill_len = len; | |||
1227 | ||||
1228 | switch_core_gen_encoded_silence(fill_data, &rh->read_impl, len); | |||
1229 | switch_core_file_write(&rh->out_fh, fill_data, &fill_len); | |||
1230 | } | |||
1231 | ||||
1232 | ||||
1233 | if (rh->last_read_time && rh->last_read_time < now) { | |||
1234 | diff = ((now - rh->last_read_time) + 3000 ) / rh->read_impl.microseconds_per_packet; | |||
1235 | ||||
1236 | if (diff > 1) { | |||
1237 | unsigned char fill_data[SWITCH_RECOMMENDED_BUFFER_SIZE8192] = {0}; | |||
1238 | switch_core_gen_encoded_silence(fill_data, &rh->read_impl, len); | |||
1239 | ||||
1240 | while(diff > 1) { | |||
1241 | switch_size_t fill_len = len; | |||
1242 | switch_core_file_write(&rh->in_fh, fill_data, &fill_len); | |||
1243 | diff--; | |||
1244 | } | |||
1245 | } | |||
1246 | } | |||
1247 | ||||
1248 | switch_core_file_write(&rh->in_fh, mask ? null_data : nframe->data, &len); | |||
1249 | rh->last_read_time = now; | |||
1250 | ||||
1251 | } | |||
1252 | break; | |||
1253 | case SWITCH_ABC_TYPE_TAP_NATIVE_WRITE: | |||
1254 | { | |||
1255 | switch_time_t now = switch_micro_time_now(); | |||
1256 | switch_time_t diff; | |||
1257 | rh->wready = 1; | |||
1258 | ||||
1259 | nframe = switch_core_media_bug_get_native_write_frame(bug); | |||
1260 | len = nframe->datalen; | |||
1261 | ||||
1262 | if (!rh->rready) { | |||
1263 | unsigned char fill_data[SWITCH_RECOMMENDED_BUFFER_SIZE8192] = {0}; | |||
1264 | switch_size_t fill_len = len; | |||
1265 | switch_core_gen_encoded_silence(fill_data, &rh->read_impl, len); | |||
1266 | switch_core_file_write(&rh->in_fh, fill_data, &fill_len); | |||
1267 | } | |||
1268 | ||||
1269 | ||||
1270 | ||||
1271 | ||||
1272 | if (rh->last_write_time && rh->last_write_time < now) { | |||
1273 | diff = ((now - rh->last_write_time) + 3000 ) / rh->read_impl.microseconds_per_packet; | |||
1274 | ||||
1275 | if (diff > 1) { | |||
1276 | unsigned char fill_data[SWITCH_RECOMMENDED_BUFFER_SIZE8192] = {0}; | |||
1277 | switch_core_gen_encoded_silence(fill_data, &rh->read_impl, len); | |||
1278 | ||||
1279 | while(diff > 1) { | |||
1280 | switch_size_t fill_len = len; | |||
1281 | switch_core_file_write(&rh->out_fh, fill_data, &fill_len); | |||
1282 | diff--; | |||
1283 | } | |||
1284 | } | |||
1285 | } | |||
1286 | ||||
1287 | switch_core_file_write(&rh->out_fh, mask ? null_data : nframe->data, &len); | |||
1288 | rh->last_write_time = now; | |||
1289 | ||||
1290 | } | |||
1291 | break; | |||
1292 | case SWITCH_ABC_TYPE_CLOSE: | |||
1293 | { | |||
1294 | const char *var; | |||
1295 | ||||
1296 | switch_codec_implementation_t read_impl = { 0 }; | |||
1297 | switch_core_session_get_read_impl(session, &read_impl); | |||
1298 | ||||
1299 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1299, (const char*)(session), SWITCH_LOG_DEBUG, "Stop recording file %s\n", rh->file); | |||
1300 | switch_channel_set_private(channel, rh->file, NULL((void*)0)); | |||
1301 | ||||
1302 | if (rh->native) { | |||
1303 | switch_core_file_close(&rh->in_fh); | |||
1304 | switch_core_file_close(&rh->out_fh); | |||
1305 | } else if (rh->fh) { | |||
1306 | switch_size_t len; | |||
1307 | uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
1308 | switch_frame_t frame = { 0 }; | |||
1309 | ||||
1310 | if (rh->thread_ready) { | |||
1311 | switch_status_t st; | |||
1312 | ||||
1313 | rh->thread_ready = 0; | |||
1314 | switch_thread_join(&st, rh->thread); | |||
1315 | } | |||
1316 | ||||
1317 | if (rh->thread_buffer) { | |||
1318 | switch_buffer_destroy(&rh->thread_buffer); | |||
1319 | } | |||
1320 | ||||
1321 | ||||
1322 | frame.data = data; | |||
1323 | frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE8192; | |||
1324 | ||||
1325 | while (switch_core_media_bug_read(bug, &frame, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) { | |||
1326 | len = (switch_size_t) frame.datalen / 2; | |||
1327 | ||||
1328 | if (len && switch_core_file_write(rh->fh, mask ? null_data : data, &len) != SWITCH_STATUS_SUCCESS) { | |||
1329 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1329, (const char*)(session), SWITCH_LOG_ERROR, "Error writing %s\n", rh->file); | |||
1330 | /* File write failed */ | |||
1331 | set_completion_cause(rh, "uri-failure"); | |||
1332 | if (rh->hangup_on_error) { | |||
1333 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 1333, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
1334 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
1335 | } | |||
1336 | send_record_stop_event(channel, &read_impl, rh); | |||
1337 | return SWITCH_FALSE; | |||
1338 | } | |||
1339 | } | |||
1340 | ||||
1341 | ||||
1342 | switch_core_file_close(rh->fh); | |||
1343 | if (rh->fh->samples_out < rh->fh->samplerate * rh->min_sec) { | |||
1344 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1344, (const char*)(session), SWITCH_LOG_DEBUG, "Discarding short file %s\n", rh->file); | |||
1345 | switch_channel_set_variable(channel, "RECORD_DISCARDED", "true")switch_channel_set_variable_var_check(channel, "RECORD_DISCARDED" , "true", SWITCH_TRUE); | |||
1346 | switch_file_remove(rh->file, switch_core_session_get_pool(session)); | |||
1347 | set_completion_cause(rh, "input-too-short"); | |||
1348 | } | |||
1349 | ||||
1350 | if (switch_channel_down_nosig(channel)(switch_channel_get_state(channel) >= CS_HANGUP)) { | |||
1351 | /* We got hung up */ | |||
1352 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1352, (const char*)(session), SWITCH_LOG_DEBUG, "Channel is hung up\n"); | |||
1353 | if (rh->speech_detected) { | |||
1354 | /* Treat it as equivalent with final-silence */ | |||
1355 | set_completion_cause(rh, "success-silence"); | |||
1356 | } else { | |||
1357 | /* Treat it as equivalent with inital-silence timeout */ | |||
1358 | set_completion_cause(rh, "no-input-timeout"); | |||
1359 | } | |||
1360 | } else { | |||
1361 | /* Set the completion_cause to maxtime reached, unless it's already set */ | |||
1362 | set_completion_cause(rh, "success-maxtime"); | |||
1363 | } | |||
1364 | } | |||
1365 | ||||
1366 | send_record_stop_event(channel, &read_impl, rh); | |||
1367 | ||||
1368 | switch_channel_execute_on(channel, SWITCH_RECORD_POST_PROCESS_EXEC_APP_VARIABLE"record_post_process_exec_app"); | |||
1369 | ||||
1370 | if ((var = switch_channel_get_variable(channel, SWITCH_RECORD_POST_PROCESS_EXEC_API_VARIABLE)switch_channel_get_variable_dup(channel, "record_post_process_exec_api" , SWITCH_TRUE, -1))) { | |||
1371 | char *cmd = switch_core_session_strdup(session, var)switch_core_perform_session_strdup(session, var, "src/switch_ivr_async.c" , (const char *)__func__, 1371); | |||
1372 | char *data, *expanded = NULL((void*)0); | |||
1373 | switch_stream_handle_t stream = { 0 }; | |||
1374 | ||||
1375 | SWITCH_STANDARD_STREAM(stream)memset(&stream, 0, sizeof(stream)); stream.data = malloc( 1024); ((stream.data) ? (void) (0) : __assert_fail ("stream.data" , "src/switch_ivr_async.c", 1375, __PRETTY_FUNCTION__)); memset (stream.data, 0, 1024); stream.end = stream.data; stream.data_size = 1024; stream.write_function = switch_console_stream_write; stream.raw_write_function = switch_console_stream_raw_write; stream.alloc_len = 1024; stream.alloc_chunk = 1024; | |||
1376 | ||||
1377 | if ((data = strchr(cmd, ':')(__extension__ (__builtin_constant_p (':') && !__builtin_constant_p (cmd) && (':') == '\0' ? (char *) __rawmemchr (cmd, ':' ) : __builtin_strchr (cmd, ':'))))) { | |||
1378 | *data++ = '\0'; | |||
1379 | expanded = switch_channel_expand_variables(channel, data)switch_channel_expand_variables_check(channel, data, ((void*) 0), ((void*)0), 0); | |||
1380 | } | |||
1381 | ||||
1382 | switch_api_execute(cmd, expanded, session, &stream); | |||
1383 | ||||
1384 | if (expanded && expanded != data) { | |||
1385 | free(expanded); | |||
1386 | } | |||
1387 | ||||
1388 | switch_safe_free(stream.data)if (stream.data) {free(stream.data);stream.data=((void*)0);}; | |||
1389 | ||||
1390 | } | |||
1391 | ||||
1392 | ||||
1393 | } | |||
1394 | ||||
1395 | break; | |||
1396 | case SWITCH_ABC_TYPE_READ_PING: | |||
1397 | ||||
1398 | if (rh->fh) { | |||
1399 | switch_size_t len; | |||
1400 | uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
1401 | switch_frame_t frame = { 0 }; | |||
1402 | switch_status_t status; | |||
1403 | int i = 0; | |||
1404 | ||||
1405 | frame.data = data; | |||
1406 | frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE8192; | |||
1407 | ||||
1408 | for (;;) { | |||
1409 | status = switch_core_media_bug_read(bug, &frame, i++ == 0 ? SWITCH_FALSE : SWITCH_TRUE); | |||
1410 | ||||
1411 | if (status != SWITCH_STATUS_SUCCESS || !frame.datalen) { | |||
1412 | break; | |||
1413 | } else { | |||
1414 | len = (switch_size_t) frame.datalen / 2 / frame.channels; | |||
1415 | ||||
1416 | if (rh->thread_buffer) { | |||
1417 | switch_mutex_lock(rh->buffer_mutex); | |||
1418 | switch_buffer_write(rh->thread_buffer, mask ? null_data : data, frame.datalen); | |||
1419 | switch_mutex_unlock(rh->buffer_mutex); | |||
1420 | } else if (switch_core_file_write(rh->fh, mask ? null_data : data, &len) != SWITCH_STATUS_SUCCESS) { | |||
1421 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1421, (const char*)(session), SWITCH_LOG_ERROR, "Error writing %s\n", rh->file); | |||
1422 | /* File write failed */ | |||
1423 | set_completion_cause(rh, "uri-failure"); | |||
1424 | if (rh->hangup_on_error) { | |||
1425 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 1425, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
1426 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
1427 | } | |||
1428 | return SWITCH_FALSE; | |||
1429 | } | |||
1430 | ||||
1431 | /* check for silence timeout */ | |||
1432 | if (rh->silence_threshold) { | |||
1433 | switch_codec_implementation_t read_impl = { 0 }; | |||
1434 | switch_core_session_get_read_impl(session, &read_impl); | |||
1435 | if (is_silence_frame(&frame, rh->silence_threshold, &read_impl)) { | |||
1436 | if (!rh->silence_time) { | |||
1437 | /* start of silence */ | |||
1438 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1438, (const char*)(session), SWITCH_LOG_DEBUG, "Start of silence detected\n"); | |||
1439 | rh->silence_time = switch_micro_time_now(); | |||
1440 | } else { | |||
1441 | /* continuing silence */ | |||
1442 | int duration_ms = (int)((switch_micro_time_now() - rh->silence_time) / 1000); | |||
1443 | if (rh->silence_timeout_ms > 0 && duration_ms >= rh->silence_timeout_ms) { | |||
1444 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1444, (const char*)(session), SWITCH_LOG_DEBUG, "Recording file %s timeout: %i >= %i\n", rh->file, duration_ms, rh->silence_timeout_ms); | |||
1445 | switch_core_media_bug_set_flag(bug, SMBF_PRUNE); | |||
1446 | if (rh->speech_detected) { | |||
1447 | /* Reached final silence timeout */ | |||
1448 | set_completion_cause(rh, "success-silence"); | |||
1449 | } else { | |||
1450 | /* Reached initial silence timeout */ | |||
1451 | set_completion_cause(rh, "no-input-timeout"); | |||
1452 | /* Discard the silent file? */ | |||
1453 | } | |||
1454 | } | |||
1455 | } | |||
1456 | } else { /* not silence */ | |||
1457 | if (rh->silence_time) { | |||
1458 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1458, (const char*)(session), SWITCH_LOG_DEBUG, "Start of speech detected\n"); | |||
1459 | rh->speech_detected = SWITCH_TRUE; | |||
1460 | /* end of silence */ | |||
1461 | rh->silence_time = 0; | |||
1462 | /* switch from initial timeout to final timeout */ | |||
1463 | rh->silence_timeout_ms = rh->final_timeout_ms; | |||
1464 | } | |||
1465 | } | |||
1466 | } else { | |||
1467 | /* no silence detection */ | |||
1468 | if (!rh->speech_detected) { | |||
1469 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1469, (const char*)(session), SWITCH_LOG_DEBUG, "No silence detection configured; assuming start of speech\n"); | |||
1470 | rh->speech_detected = SWITCH_TRUE; | |||
1471 | } | |||
1472 | } | |||
1473 | } | |||
1474 | } | |||
1475 | } | |||
1476 | break; | |||
1477 | case SWITCH_ABC_TYPE_WRITE: | |||
1478 | default: | |||
1479 | break; | |||
1480 | } | |||
1481 | ||||
1482 | return SWITCH_TRUE; | |||
1483 | } | |||
1484 | ||||
1485 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_record_session_mask(switch_core_session_t *session, const char *file, switch_bool_t on) | |||
1486 | { | |||
1487 | switch_media_bug_t *bug; | |||
1488 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1489 | ||||
1490 | if ((bug = switch_channel_get_private(channel, file))) { | |||
1491 | if (on) { | |||
1492 | switch_core_media_bug_set_flag(bug, SMBF_MASK); | |||
1493 | } else { | |||
1494 | switch_core_media_bug_clear_flag(bug, SMBF_MASK); | |||
1495 | } | |||
1496 | return SWITCH_STATUS_SUCCESS; | |||
1497 | } | |||
1498 | return SWITCH_STATUS_FALSE; | |||
1499 | } | |||
1500 | ||||
1501 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_record_session(switch_core_session_t *session, const char *file) | |||
1502 | { | |||
1503 | switch_media_bug_t *bug; | |||
1504 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1505 | ||||
1506 | if (!strcasecmp(file, "all")) { | |||
1507 | return switch_core_media_bug_remove_callback(session, record_callback); | |||
1508 | } else if ((bug = switch_channel_get_private(channel, file))) { | |||
1509 | switch_core_media_bug_remove(session, &bug); | |||
1510 | return SWITCH_STATUS_SUCCESS; | |||
1511 | } | |||
1512 | return SWITCH_STATUS_FALSE; | |||
1513 | } | |||
1514 | ||||
1515 | static void* switch_ivr_record_user_data_dup(switch_core_session_t *session, void *user_data) | |||
1516 | { | |||
1517 | struct record_helper *rh = (struct record_helper *) user_data, *dup = NULL((void*)0); | |||
1518 | ||||
1519 | dup = switch_core_session_alloc(session, sizeof(*dup))switch_core_perform_session_alloc(session, sizeof(*dup), "src/switch_ivr_async.c" , (const char *)__func__, 1519); | |||
1520 | memcpy(dup, rh, sizeof(*rh)); | |||
1521 | dup->file = switch_core_session_strdup(session, rh->file)switch_core_perform_session_strdup(session, rh->file, "src/switch_ivr_async.c" , (const char *)__func__, 1521); | |||
1522 | dup->fh = switch_core_session_alloc(session, sizeof(switch_file_handle_t))switch_core_perform_session_alloc(session, sizeof(switch_file_handle_t ), "src/switch_ivr_async.c", (const char *)__func__, 1522); | |||
1523 | memcpy(dup->fh, rh->fh, sizeof(switch_file_handle_t)); | |||
1524 | ||||
1525 | return dup; | |||
1526 | } | |||
1527 | ||||
1528 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_transfer_recordings(switch_core_session_t *orig_session, switch_core_session_t *new_session) | |||
1529 | { | |||
1530 | const char *var = NULL((void*)0); | |||
1531 | switch_channel_t *orig_channel = switch_core_session_get_channel(orig_session); | |||
1532 | switch_channel_t *new_channel = switch_core_session_get_channel(new_session); | |||
1533 | ||||
1534 | if ((var = switch_channel_get_variable(orig_channel, SWITCH_RECORD_POST_PROCESS_EXEC_API_VARIABLE)switch_channel_get_variable_dup(orig_channel, "record_post_process_exec_api" , SWITCH_TRUE, -1))) { | |||
1535 | switch_channel_set_variable(new_channel, SWITCH_RECORD_POST_PROCESS_EXEC_API_VARIABLE, var)switch_channel_set_variable_var_check(new_channel, "record_post_process_exec_api" , var, SWITCH_TRUE); | |||
1536 | } | |||
1537 | switch_channel_transfer_variable_prefix(orig_channel, new_channel, SWITCH_RECORD_POST_PROCESS_EXEC_APP_VARIABLE"record_post_process_exec_app"); | |||
1538 | ||||
1539 | return switch_core_media_bug_transfer_callback(orig_session, new_session, record_callback, switch_ivr_record_user_data_dup); | |||
1540 | } | |||
1541 | ||||
1542 | struct eavesdrop_pvt { | |||
1543 | switch_buffer_t *buffer; | |||
1544 | switch_mutex_t *mutex; | |||
1545 | switch_buffer_t *r_buffer; | |||
1546 | switch_mutex_t *r_mutex; | |||
1547 | switch_buffer_t *w_buffer; | |||
1548 | switch_mutex_t *w_mutex; | |||
1549 | switch_core_session_t *eavesdropper; | |||
1550 | uint32_t flags; | |||
1551 | switch_frame_t demux_frame; | |||
1552 | uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
1553 | }; | |||
1554 | ||||
1555 | ||||
1556 | ||||
1557 | static switch_bool_t eavesdrop_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
1558 | { | |||
1559 | struct eavesdrop_pvt *ep = (struct eavesdrop_pvt *) user_data; | |||
1560 | uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
1561 | switch_frame_t frame = { 0 }; | |||
1562 | ||||
1563 | frame.data = data; | |||
1564 | frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE8192; | |||
1565 | ||||
1566 | switch (type) { | |||
1567 | case SWITCH_ABC_TYPE_INIT: | |||
1568 | break; | |||
1569 | case SWITCH_ABC_TYPE_CLOSE: | |||
1570 | break; | |||
1571 | case SWITCH_ABC_TYPE_WRITE: | |||
1572 | break; | |||
1573 | case SWITCH_ABC_TYPE_READ_PING: | |||
1574 | if (ep->buffer) { | |||
1575 | if (switch_core_media_bug_read(bug, &frame, SWITCH_FALSE) != SWITCH_STATUS_FALSE) { | |||
1576 | switch_buffer_lock(ep->buffer); | |||
1577 | switch_buffer_zwrite(ep->buffer, frame.data, frame.datalen); | |||
1578 | switch_buffer_unlock(ep->buffer); | |||
1579 | } | |||
1580 | } else { | |||
1581 | return SWITCH_FALSE; | |||
1582 | } | |||
1583 | break; | |||
1584 | case SWITCH_ABC_TYPE_READ: | |||
1585 | break; | |||
1586 | ||||
1587 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
1588 | { | |||
1589 | ||||
1590 | if (switch_test_flag(ep, ED_MUX_READ)((ep)->flags & ED_MUX_READ)) { | |||
1591 | switch_frame_t *rframe = switch_core_media_bug_get_read_replace_frame(bug); | |||
1592 | ||||
1593 | if (switch_buffer_inuse(ep->r_buffer) >= rframe->datalen) { | |||
1594 | uint32_t bytes; | |||
1595 | switch_buffer_lock(ep->r_buffer); | |||
1596 | bytes = (uint32_t) switch_buffer_read(ep->r_buffer, ep->data, rframe->datalen); | |||
1597 | ||||
1598 | rframe->datalen = switch_merge_sln(rframe->data, rframe->samples, (int16_t *) ep->data, bytes / 2) * 2; | |||
1599 | rframe->samples = rframe->datalen / 2; | |||
1600 | ||||
1601 | ep->demux_frame.data = ep->data; | |||
1602 | ep->demux_frame.datalen = bytes; | |||
1603 | ep->demux_frame.samples = bytes / 2; | |||
1604 | ||||
1605 | switch_buffer_unlock(ep->r_buffer); | |||
1606 | switch_core_media_bug_set_read_replace_frame(bug, rframe); | |||
1607 | switch_core_media_bug_set_read_demux_frame(bug, &ep->demux_frame); | |||
1608 | } | |||
1609 | } | |||
1610 | } | |||
1611 | break; | |||
1612 | ||||
1613 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
1614 | { | |||
1615 | if (switch_test_flag(ep, ED_MUX_WRITE)((ep)->flags & ED_MUX_WRITE)) { | |||
1616 | switch_frame_t *rframe = switch_core_media_bug_get_write_replace_frame(bug); | |||
1617 | ||||
1618 | if (switch_buffer_inuse(ep->w_buffer) >= rframe->datalen) { | |||
1619 | uint32_t bytes; | |||
1620 | switch_buffer_lock(ep->w_buffer); | |||
1621 | bytes = (uint32_t) switch_buffer_read(ep->w_buffer, data, rframe->datalen); | |||
1622 | ||||
1623 | rframe->datalen = switch_merge_sln(rframe->data, rframe->samples, (int16_t *) data, bytes / 2) * 2; | |||
1624 | rframe->samples = rframe->datalen / 2; | |||
1625 | ||||
1626 | switch_buffer_unlock(ep->w_buffer); | |||
1627 | switch_core_media_bug_set_write_replace_frame(bug, rframe); | |||
1628 | } | |||
1629 | } | |||
1630 | } | |||
1631 | break; | |||
1632 | ||||
1633 | default: | |||
1634 | break; | |||
1635 | } | |||
1636 | ||||
1637 | return SWITCH_TRUE; | |||
1638 | } | |||
1639 | ||||
1640 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_eavesdrop_pop_eavesdropper(switch_core_session_t *session, switch_core_session_t **sessionp) | |||
1641 | { | |||
1642 | switch_media_bug_t *bug; | |||
1643 | switch_status_t status = SWITCH_STATUS_FALSE; | |||
1644 | ||||
1645 | if (switch_core_media_bug_pop(session, "eavesdrop", &bug) == SWITCH_STATUS_SUCCESS) { | |||
1646 | struct eavesdrop_pvt *ep = (struct eavesdrop_pvt *) switch_core_media_bug_get_user_data(bug); | |||
1647 | ||||
1648 | if (ep && ep->eavesdropper && ep->eavesdropper != session) { | |||
1649 | switch_core_session_read_lock(ep->eavesdropper); | |||
1650 | *sessionp = ep->eavesdropper; | |||
1651 | switch_core_media_bug_set_flag(bug, SMBF_PRUNE); | |||
1652 | status = SWITCH_STATUS_SUCCESS; | |||
1653 | } | |||
1654 | } | |||
1655 | ||||
1656 | ||||
1657 | return status; | |||
1658 | } | |||
1659 | ||||
1660 | struct exec_cb_data { | |||
1661 | switch_core_session_t *caller; | |||
1662 | char *var; | |||
1663 | char *val; | |||
1664 | }; | |||
1665 | ||||
1666 | static void exec_cb(switch_media_bug_t *bug, void *user_data) | |||
1667 | { | |||
1668 | struct exec_cb_data *data = (struct exec_cb_data *) user_data; | |||
1669 | struct eavesdrop_pvt *ep = (struct eavesdrop_pvt *) switch_core_media_bug_get_user_data(bug); | |||
1670 | ||||
1671 | if (ep && ep->eavesdropper && ep->eavesdropper != data->caller) { | |||
1672 | switch_channel_t *a = switch_core_session_get_channel(ep->eavesdropper); | |||
1673 | switch_channel_t *b = switch_core_session_get_channel(data->caller); | |||
1674 | ||||
1675 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 1675, ((void*)0), SWITCH_LOG_DEBUG, "%s telling %s to exec %s:%s\n", | |||
1676 | switch_channel_get_name(b), switch_channel_get_name(a), data->var, data->val); | |||
1677 | ||||
1678 | switch_core_session_execute_application(ep->eavesdropper, data->var, data->val)switch_core_session_execute_application_get_flags(ep->eavesdropper , data->var, data->val, ((void*)0)); | |||
1679 | } | |||
1680 | } | |||
1681 | ||||
1682 | static void display_exec_cb(switch_media_bug_t *bug, void *user_data) | |||
1683 | { | |||
1684 | struct exec_cb_data *data = (struct exec_cb_data *) user_data; | |||
1685 | struct eavesdrop_pvt *ep = (struct eavesdrop_pvt *) switch_core_media_bug_get_user_data(bug); | |||
1686 | ||||
1687 | if (ep && ep->eavesdropper && ep->eavesdropper != data->caller) { | |||
1688 | switch_core_session_message_t msg = { 0 }; | |||
1689 | ||||
1690 | msg.from = __FILE__"src/switch_ivr_async.c"; | |||
1691 | msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY; | |||
1692 | msg.string_array_arg[0] = data->var; | |||
1693 | msg.string_array_arg[1] = data->val; | |||
1694 | ||||
1695 | switch_core_session_receive_message(ep->eavesdropper, &msg)switch_core_session_perform_receive_message(ep->eavesdropper , &msg, "src/switch_ivr_async.c", (const char *)__func__, 1695); | |||
1696 | } | |||
1697 | } | |||
1698 | ||||
1699 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_eavesdrop_exec_all(switch_core_session_t *session, const char *app, const char *arg) | |||
1700 | { | |||
1701 | struct exec_cb_data *data = NULL((void*)0); | |||
1702 | ||||
1703 | data = switch_core_session_alloc(session, sizeof(*data))switch_core_perform_session_alloc(session, sizeof(*data), "src/switch_ivr_async.c" , (const char *)__func__, 1703); | |||
1704 | data->var = switch_core_session_strdup(session, app)switch_core_perform_session_strdup(session, app, "src/switch_ivr_async.c" , (const char *)__func__, 1704); | |||
1705 | data->val = switch_core_session_strdup(session, arg)switch_core_perform_session_strdup(session, arg, "src/switch_ivr_async.c" , (const char *)__func__, 1705); | |||
1706 | data->caller = session; | |||
1707 | ||||
1708 | return switch_core_media_bug_exec_all(session, "eavesdrop", exec_cb, data); | |||
1709 | } | |||
1710 | ||||
1711 | ||||
1712 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_eavesdrop_update_display(switch_core_session_t *session, const char *name, const char *number) | |||
1713 | { | |||
1714 | struct exec_cb_data *data = NULL((void*)0); | |||
1715 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1716 | switch_status_t status = SWITCH_STATUS_FALSE; | |||
1717 | ||||
1718 | data = switch_core_session_alloc(session, sizeof(*data))switch_core_perform_session_alloc(session, sizeof(*data), "src/switch_ivr_async.c" , (const char *)__func__, 1718); | |||
1719 | data->var = switch_core_session_strdup(session, name)switch_core_perform_session_strdup(session, name, "src/switch_ivr_async.c" , (const char *)__func__, 1719); | |||
1720 | data->val = switch_core_session_strdup(session, number)switch_core_perform_session_strdup(session, number, "src/switch_ivr_async.c" , (const char *)__func__, 1720); | |||
1721 | data->caller = session; | |||
1722 | ||||
1723 | if (!switch_channel_test_app_flag_key("EAVESDROP", channel, 1)) { | |||
1724 | switch_channel_set_app_flag_key("EAVESDROP", channel, 1); | |||
1725 | status = switch_core_media_bug_exec_all(session, "eavesdrop", display_exec_cb, data); | |||
1726 | switch_channel_clear_app_flag_key("EAVESDROP", channel, 1); | |||
1727 | } | |||
1728 | ||||
1729 | return status; | |||
1730 | } | |||
1731 | ||||
1732 | ||||
1733 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_eavesdrop_session(switch_core_session_t *session, | |||
1734 | const char *uuid, const char *require_group, switch_eavesdrop_flag_t flags) | |||
1735 | { | |||
1736 | switch_core_session_t *tsession; | |||
1737 | switch_status_t status = SWITCH_STATUS_FALSE; | |||
1738 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
1739 | int codec_initialized = 0; | |||
1740 | const char *name, *num; | |||
1741 | ||||
1742 | if ((tsession = switch_core_session_locate(uuid)switch_core_session_perform_locate(uuid, "src/switch_ivr_async.c" , (const char *)__func__, 1742))) { | |||
1743 | struct eavesdrop_pvt *ep = NULL((void*)0); | |||
1744 | switch_media_bug_t *bug = NULL((void*)0); | |||
1745 | switch_channel_t *tchannel = switch_core_session_get_channel(tsession); | |||
1746 | switch_frame_t *read_frame, write_frame = { 0 }; | |||
1747 | switch_codec_t codec = { 0 }; | |||
1748 | int16_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE8192 / 2]; | |||
1749 | uint32_t tlen; | |||
1750 | const char *macro_name = "eavesdrop_announce"; | |||
1751 | const char *id_name = NULL((void*)0); | |||
1752 | switch_codec_implementation_t tread_impl = { 0 }, read_impl = { 0 }; | |||
1753 | switch_core_session_message_t msg = { 0 }; | |||
1754 | char cid_buf[1024] = ""; | |||
1755 | switch_caller_profile_t *cp = NULL((void*)0); | |||
1756 | uint32_t sanity = 600; | |||
1757 | ||||
1758 | if (!switch_channel_media_up(channel)(switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag (channel, CF_EARLY_MEDIA))) { | |||
1759 | goto end; | |||
1760 | } | |||
1761 | ||||
1762 | while(switch_channel_state_change_pending(tchannel) || !switch_channel_media_up(tchannel)(switch_channel_test_flag(tchannel, CF_ANSWERED) || switch_channel_test_flag (tchannel, CF_EARLY_MEDIA))) { | |||
1763 | switch_yield(10000)switch_sleep(10000);; | |||
1764 | if (!--sanity) break; | |||
1765 | } | |||
1766 | ||||
1767 | if (!switch_channel_media_up(tchannel)(switch_channel_test_flag(tchannel, CF_ANSWERED) || switch_channel_test_flag (tchannel, CF_EARLY_MEDIA))) { | |||
1768 | goto end; | |||
1769 | } | |||
1770 | ||||
1771 | switch_core_session_get_read_impl(tsession, &tread_impl); | |||
1772 | switch_core_session_get_read_impl(session, &read_impl); | |||
1773 | ||||
1774 | if ((id_name = switch_channel_get_variable(tchannel, "eavesdrop_announce_id")switch_channel_get_variable_dup(tchannel, "eavesdrop_announce_id" , SWITCH_TRUE, -1))) { | |||
1775 | const char *tmp = switch_channel_get_variable(tchannel, "eavesdrop_annnounce_macro")switch_channel_get_variable_dup(tchannel, "eavesdrop_annnounce_macro" , SWITCH_TRUE, -1); | |||
1776 | if (tmp) { | |||
1777 | macro_name = tmp; | |||
1778 | } | |||
1779 | ||||
1780 | switch_ivr_phrase_macro(session, macro_name, id_name, NULL, NULL)switch_ivr_phrase_macro_event(session, macro_name, id_name, ( (void*)0), ((void*)0), ((void*)0)); | |||
1781 | } | |||
1782 | ||||
1783 | ||||
1784 | if (!zstr(require_group)_zstr(require_group)) { | |||
1785 | int argc, i; | |||
1786 | int ok = 0; | |||
1787 | char *argv[10] = { 0 }; | |||
1788 | char *data; | |||
1789 | ||||
1790 | const char *group_name = switch_channel_get_variable(tchannel, "eavesdrop_group")switch_channel_get_variable_dup(tchannel, "eavesdrop_group", SWITCH_TRUE , -1); | |||
1791 | /* If we don't have a group, then return */ | |||
1792 | if (!group_name) { | |||
1793 | status = SWITCH_STATUS_BREAK; | |||
1794 | goto end; | |||
1795 | } | |||
1796 | /* Separate the group */ | |||
1797 | data = strdup(group_name)(__extension__ (__builtin_constant_p (group_name) && ( (size_t)(const void *)((group_name) + 1) - (size_t)(const void *)(group_name) == 1) ? (((const char *) (group_name))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (group_name) + 1; char *__retval = (char *) malloc ( __len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, group_name, __len); __retval; })) : __strdup (group_name ))); | |||
1798 | if ((argc = switch_separate_string(data, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) { | |||
1799 | for (i = 0; i < argc; i++) { | |||
1800 | /* If one of the group matches, then ok */ | |||
1801 | if (argv[i] && !strcmp(argv[i], require_group)__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (argv[i]) && __builtin_constant_p (require_group) && (__s1_len = __builtin_strlen (argv[i]), __s2_len = __builtin_strlen (require_group), (!((size_t)(const void *)((argv[i]) + 1) - ( size_t)(const void *)(argv[i]) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((require_group) + 1) - (size_t)(const void *)(require_group) == 1) || __s2_len >= 4)) ? __builtin_strcmp (argv[i], require_group) : (__builtin_constant_p (argv[i]) && ((size_t)(const void *)((argv[i]) + 1) - (size_t)(const void *)(argv[i]) == 1) && (__s1_len = __builtin_strlen (argv [i]), __s1_len < 4) ? (__builtin_constant_p (require_group ) && ((size_t)(const void *)((require_group) + 1) - ( size_t)(const void *)(require_group) == 1) ? __builtin_strcmp (argv[i], require_group) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (require_group ); int __result = (((const unsigned char *) (const char *) (argv [i]))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (argv [i]))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (argv [i]))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (argv [i]))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (require_group) && ((size_t)(const void *)((require_group ) + 1) - (size_t)(const void *)(require_group) == 1) && (__s2_len = __builtin_strlen (require_group), __s2_len < 4 ) ? (__builtin_constant_p (argv[i]) && ((size_t)(const void *)((argv[i]) + 1) - (size_t)(const void *)(argv[i]) == 1 ) ? __builtin_strcmp (argv[i], require_group) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (argv[i]); int __result = (((const unsigned char *) ( const char *) (require_group))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (require_group))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (require_group))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (require_group))[3] - __s2[3]); } } __result ; })))) : __builtin_strcmp (argv[i], require_group)))); })) { | |||
1802 | ok = 1; | |||
1803 | } | |||
1804 | } | |||
1805 | } | |||
1806 | switch_safe_free(data)if (data) {free(data);data=((void*)0);}; | |||
1807 | /* If we didn't find any match, then end */ | |||
1808 | if (!ok) { | |||
1809 | status = SWITCH_STATUS_BREAK; | |||
1810 | goto end; | |||
1811 | } | |||
1812 | } | |||
1813 | ||||
1814 | ||||
1815 | ep = switch_core_session_alloc(session, sizeof(*ep))switch_core_perform_session_alloc(session, sizeof(*ep), "src/switch_ivr_async.c" , (const char *)__func__, 1815); | |||
1816 | ||||
1817 | tlen = tread_impl.decoded_bytes_per_packet; | |||
1818 | ||||
1819 | ||||
1820 | if (switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 1820) != SWITCH_STATUS_SUCCESS) { | |||
1821 | goto end; | |||
1822 | } | |||
1823 | ||||
1824 | ||||
1825 | if (switch_core_codec_init(&codec,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1826 | "L16",switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1827 | NULL,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1828 | tread_impl.actual_samples_per_second,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1829 | tread_impl.microseconds_per_packet / 1000,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1830 | tread_impl.number_of_channels,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1831 | SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
1832 | NULL, switch_core_session_get_pool(session))switch_core_codec_init_with_bitrate(&codec, "L16", ((void *)0), tread_impl.actual_samples_per_second, tread_impl.microseconds_per_packet / 1000, tread_impl.number_of_channels, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) != SWITCH_STATUS_SUCCESS) { | |||
1833 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1833, (const char*)(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); | |||
1834 | switch_core_session_rwunlock(tsession); | |||
1835 | goto end; | |||
1836 | } | |||
1837 | ||||
1838 | codec_initialized = 1; | |||
1839 | ||||
1840 | switch_core_session_set_read_codec(session, &codec); | |||
1841 | write_frame.codec = &codec; | |||
1842 | write_frame.data = buf; | |||
1843 | write_frame.buflen = sizeof(buf); | |||
1844 | write_frame.rate = codec.implementation->actual_samples_per_second; | |||
1845 | ||||
1846 | ep->eavesdropper = session; | |||
1847 | ep->flags = flags; | |||
1848 | switch_mutex_init(&ep->mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(tsession)); | |||
1849 | switch_buffer_create_dynamic(&ep->buffer, 2048, 2048, 8192); | |||
1850 | switch_buffer_add_mutex(ep->buffer, ep->mutex); | |||
1851 | ||||
1852 | switch_mutex_init(&ep->w_mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(tsession)); | |||
1853 | switch_buffer_create_dynamic(&ep->w_buffer, 2048, 2048, 8192); | |||
1854 | switch_buffer_add_mutex(ep->w_buffer, ep->w_mutex); | |||
1855 | ||||
1856 | switch_mutex_init(&ep->r_mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(tsession)); | |||
1857 | switch_buffer_create_dynamic(&ep->r_buffer, 2048, 2048, 8192); | |||
1858 | switch_buffer_add_mutex(ep->r_buffer, ep->r_mutex); | |||
1859 | ||||
1860 | ||||
1861 | if (switch_core_media_bug_add(tsession, "eavesdrop", uuid, | |||
1862 | eavesdrop_callback, ep, 0, | |||
1863 | SMBF_READ_STREAM | SMBF_WRITE_STREAM | SMBF_READ_REPLACE | SMBF_WRITE_REPLACE | | |||
1864 | SMBF_READ_PING | SMBF_THREAD_LOCK | SMBF_NO_PAUSE, | |||
1865 | &bug) != SWITCH_STATUS_SUCCESS) { | |||
1866 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 1866, (const char*)(session), SWITCH_LOG_ERROR, "Cannot attach bug\n"); | |||
1867 | goto end; | |||
1868 | } | |||
1869 | ||||
1870 | ||||
1871 | msg.from = __FILE__"src/switch_ivr_async.c"; | |||
1872 | ||||
1873 | /* Tell the channel we are going to be in a bridge */ | |||
1874 | msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; | |||
1875 | switch_core_session_receive_message(session, &msg)switch_core_session_perform_receive_message(session, &msg , "src/switch_ivr_async.c", (const char *)__func__, 1875); | |||
1876 | cp = switch_channel_get_caller_profile(tchannel); | |||
1877 | ||||
1878 | name = cp->caller_id_name; | |||
1879 | num = cp->caller_id_number; | |||
1880 | ||||
1881 | if (flags & ED_COPY_DISPLAY) { | |||
1882 | if (switch_channel_test_flag(tchannel, CF_BRIDGE_ORIGINATOR) || !switch_channel_test_flag(tchannel, CF_BRIDGED)) { | |||
1883 | name = cp->callee_id_name; | |||
1884 | num = cp->callee_id_number; | |||
1885 | } else { | |||
1886 | name = cp->caller_id_name; | |||
1887 | num = cp->caller_id_number; | |||
1888 | } | |||
1889 | } | |||
1890 | ||||
1891 | sanity = 300; | |||
1892 | while(switch_channel_up(channel)(switch_channel_check_signal(channel, SWITCH_TRUE) || switch_channel_get_state (channel) < CS_HANGUP) && !switch_channel_media_ack(channel)(!switch_channel_test_cap(channel, CC_MEDIA_ACK) || switch_channel_test_flag (channel, CF_MEDIA_ACK)) && --sanity) { | |||
1893 | switch_yield(10000)switch_sleep(10000);; | |||
1894 | } | |||
1895 | ||||
1896 | ||||
1897 | switch_snprintf(cid_buf, sizeof(cid_buf), "%s|%s", name, num); | |||
1898 | msg.string_arg = cid_buf; | |||
1899 | msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY; | |||
1900 | switch_core_session_receive_message(session, &msg)switch_core_session_perform_receive_message(session, &msg , "src/switch_ivr_async.c", (const char *)__func__, 1900); | |||
1901 | ||||
1902 | while (switch_channel_up_nosig(tchannel)(switch_channel_get_state(tchannel) < CS_HANGUP) && switch_channel_ready(channel)switch_channel_test_ready(channel, SWITCH_TRUE, SWITCH_FALSE)) { | |||
1903 | uint32_t len = sizeof(buf); | |||
1904 | switch_event_t *event = NULL((void*)0); | |||
1905 | char *fcommand = NULL((void*)0); | |||
1906 | char db[2] = ""; | |||
1907 | ||||
1908 | status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); | |||
1909 | ||||
1910 | if (!SWITCH_READ_ACCEPTABLE(status)(status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK || status == SWITCH_STATUS_INUSE)) { | |||
1911 | goto end_loop; | |||
1912 | } | |||
1913 | ||||
1914 | if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { | |||
1915 | char *command = switch_event_get_header(event, "eavesdrop-command")switch_event_get_header_idx(event, "eavesdrop-command", -1); | |||
1916 | if (command) { | |||
1917 | fcommand = switch_core_session_strdup(session, command)switch_core_perform_session_strdup(session, command, "src/switch_ivr_async.c" , (const char *)__func__, 1917); | |||
1918 | } | |||
1919 | switch_event_destroy(&event); | |||
1920 | } | |||
1921 | ||||
1922 | if ((flags & ED_DTMF) && switch_channel_has_dtmf(channel)) { | |||
1923 | switch_dtmf_t dtmf = { 0 }; | |||
1924 | switch_channel_dequeue_dtmf(channel, &dtmf); | |||
1925 | db[0] = dtmf.digit; | |||
1926 | fcommand = db; | |||
1927 | } | |||
1928 | ||||
1929 | if (fcommand) { | |||
1930 | char *d; | |||
1931 | for (d = fcommand; *d; d++) { | |||
1932 | int z = 1; | |||
1933 | ||||
1934 | switch (*d) { | |||
1935 | case '1': | |||
1936 | switch_set_flag(ep, ED_MUX_READ)(ep)->flags |= (ED_MUX_READ); | |||
1937 | switch_clear_flag(ep, ED_MUX_WRITE)(ep)->flags &= ~(ED_MUX_WRITE); | |||
1938 | break; | |||
1939 | case '2': | |||
1940 | switch_set_flag(ep, ED_MUX_WRITE)(ep)->flags |= (ED_MUX_WRITE); | |||
1941 | switch_clear_flag(ep, ED_MUX_READ)(ep)->flags &= ~(ED_MUX_READ); | |||
1942 | break; | |||
1943 | case '3': | |||
1944 | switch_set_flag(ep, ED_MUX_READ)(ep)->flags |= (ED_MUX_READ); | |||
1945 | switch_set_flag(ep, ED_MUX_WRITE)(ep)->flags |= (ED_MUX_WRITE); | |||
1946 | break; | |||
1947 | case '0': | |||
1948 | switch_clear_flag(ep, ED_MUX_READ)(ep)->flags &= ~(ED_MUX_READ); | |||
1949 | switch_clear_flag(ep, ED_MUX_WRITE)(ep)->flags &= ~(ED_MUX_WRITE); | |||
1950 | break; | |||
1951 | case '*': | |||
1952 | goto end_loop; | |||
1953 | default: | |||
1954 | z = 0; | |||
1955 | break; | |||
1956 | ||||
1957 | } | |||
1958 | ||||
1959 | if (z) { | |||
1960 | if (ep->r_buffer) { | |||
1961 | switch_buffer_lock(ep->r_buffer); | |||
1962 | switch_buffer_zero(ep->r_buffer); | |||
1963 | switch_buffer_unlock(ep->r_buffer); | |||
1964 | } | |||
1965 | ||||
1966 | if (ep->w_buffer) { | |||
1967 | switch_buffer_lock(ep->w_buffer); | |||
1968 | switch_buffer_zero(ep->w_buffer); | |||
1969 | switch_buffer_unlock(ep->w_buffer); | |||
1970 | } | |||
1971 | } | |||
1972 | } | |||
1973 | } | |||
1974 | ||||
1975 | if (!switch_test_flag(read_frame, SFF_CNG)((read_frame)->flags & SFF_CNG)) { | |||
1976 | switch_buffer_lock(ep->r_buffer); | |||
1977 | switch_buffer_zwrite(ep->r_buffer, read_frame->data, read_frame->datalen); | |||
1978 | switch_buffer_unlock(ep->r_buffer); | |||
1979 | ||||
1980 | switch_buffer_lock(ep->w_buffer); | |||
1981 | switch_buffer_zwrite(ep->w_buffer, read_frame->data, read_frame->datalen); | |||
1982 | switch_buffer_unlock(ep->w_buffer); | |||
1983 | } | |||
1984 | ||||
1985 | if (len > tlen) { | |||
1986 | len = tlen; | |||
1987 | } | |||
1988 | ||||
1989 | if (switch_buffer_inuse(ep->buffer) >= len) { | |||
1990 | switch_buffer_lock(ep->buffer); | |||
1991 | while (switch_buffer_inuse(ep->buffer) >= len) { | |||
1992 | write_frame.datalen = (uint32_t) switch_buffer_read(ep->buffer, buf, len); | |||
1993 | write_frame.samples = write_frame.datalen / 2; | |||
1994 | ||||
1995 | if ((status = switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0)) != SWITCH_STATUS_SUCCESS) { | |||
1996 | break; | |||
1997 | } | |||
1998 | } | |||
1999 | switch_buffer_unlock(ep->buffer); | |||
2000 | } | |||
2001 | ||||
2002 | } | |||
2003 | ||||
2004 | end_loop: | |||
2005 | ||||
2006 | /* Tell the channel we are no longer going to be in a bridge */ | |||
2007 | msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; | |||
2008 | switch_core_session_receive_message(session, &msg)switch_core_session_perform_receive_message(session, &msg , "src/switch_ivr_async.c", (const char *)__func__, 2008); | |||
2009 | ||||
2010 | ||||
2011 | ||||
2012 | end: | |||
2013 | ||||
2014 | if (codec_initialized) | |||
2015 | switch_core_codec_destroy(&codec); | |||
2016 | ||||
2017 | if (bug) { | |||
2018 | switch_core_media_bug_remove(tsession, &bug); | |||
2019 | } | |||
2020 | ||||
2021 | if (ep) { | |||
2022 | if (ep->buffer) { | |||
2023 | switch_buffer_destroy(&ep->buffer); | |||
2024 | } | |||
2025 | ||||
2026 | if (ep->r_buffer) { | |||
2027 | switch_buffer_destroy(&ep->r_buffer); | |||
2028 | } | |||
2029 | ||||
2030 | if (ep->w_buffer) { | |||
2031 | switch_buffer_destroy(&ep->w_buffer); | |||
2032 | } | |||
2033 | } | |||
2034 | ||||
2035 | switch_core_session_rwunlock(tsession); | |||
2036 | status = SWITCH_STATUS_SUCCESS; | |||
2037 | ||||
2038 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
2039 | } | |||
2040 | ||||
2041 | return status; | |||
2042 | } | |||
2043 | ||||
2044 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_record_session(switch_core_session_t *session, char *file, uint32_t limit, switch_file_handle_t *fh) | |||
2045 | { | |||
2046 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2047 | const char *p; | |||
2048 | const char *vval; | |||
2049 | switch_media_bug_t *bug; | |||
2050 | switch_status_t status; | |||
2051 | time_t to = 0; | |||
2052 | switch_media_bug_flag_t flags = SMBF_READ_STREAM | SMBF_WRITE_STREAM | SMBF_READ_PING; | |||
2053 | uint8_t channels; | |||
2054 | switch_codec_implementation_t read_impl = { 0 }; | |||
2055 | struct record_helper *rh = NULL((void*)0); | |||
2056 | int file_flags = SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT; | |||
2057 | switch_bool_t hangup_on_error = SWITCH_FALSE; | |||
2058 | char *file_path = NULL((void*)0); | |||
2059 | char *ext; | |||
2060 | char *in_file = NULL((void*)0), *out_file = NULL((void*)0); | |||
2061 | ||||
2062 | if ((p = switch_channel_get_variable(channel, "RECORD_HANGUP_ON_ERROR")switch_channel_get_variable_dup(channel, "RECORD_HANGUP_ON_ERROR" , SWITCH_TRUE, -1))) { | |||
2063 | hangup_on_error = switch_true(p); | |||
2064 | } | |||
2065 | ||||
2066 | if ((status = switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2066)) != SWITCH_STATUS_SUCCESS) { | |||
2067 | return SWITCH_STATUS_FALSE; | |||
2068 | } | |||
2069 | ||||
2070 | if (!switch_channel_media_up(channel)(switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag (channel, CF_EARLY_MEDIA)) || !switch_core_session_get_read_codec(session)) { | |||
2071 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2071, (const char*)(session), SWITCH_LOG_ERROR, "Can not record session. Media not enabled on channel\n"); | |||
2072 | return SWITCH_STATUS_FALSE; | |||
2073 | } | |||
2074 | ||||
2075 | switch_core_session_get_read_impl(session, &read_impl); | |||
2076 | channels = read_impl.number_of_channels; | |||
2077 | ||||
2078 | if ((bug = switch_channel_get_private(channel, file))) { | |||
2079 | if (switch_true(switch_channel_get_variable(channel, "RECORD_TOGGLE_ON_REPEAT")switch_channel_get_variable_dup(channel, "RECORD_TOGGLE_ON_REPEAT" , SWITCH_TRUE, -1))) { | |||
2080 | return switch_ivr_stop_record_session(session, file); | |||
2081 | } | |||
2082 | ||||
2083 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2083, (const char*)(session), SWITCH_LOG_WARNING, "Already recording [%s]\n", file); | |||
2084 | return SWITCH_STATUS_SUCCESS; | |||
2085 | } | |||
2086 | ||||
2087 | ||||
2088 | if ((p = switch_channel_get_variable(channel, "RECORD_CHECK_BRIDGE")switch_channel_get_variable_dup(channel, "RECORD_CHECK_BRIDGE" , SWITCH_TRUE, -1)) && switch_true(p)) { | |||
2089 | switch_core_session_t *other_session; | |||
2090 | int exist = 0; | |||
2091 | switch_status_t rstatus = SWITCH_STATUS_SUCCESS; | |||
2092 | ||||
2093 | if (switch_core_session_get_partner(session, &other_session)switch_core_session_perform_get_partner(session, &other_session , "src/switch_ivr_async.c", (const char *)__func__, 2093) == SWITCH_STATUS_SUCCESS) { | |||
2094 | switch_channel_t *other_channel = switch_core_session_get_channel(other_session); | |||
2095 | if ((bug = switch_channel_get_private(other_channel, file))) { | |||
2096 | if (switch_true(switch_channel_get_variable(other_channel, "RECORD_TOGGLE_ON_REPEAT")switch_channel_get_variable_dup(other_channel, "RECORD_TOGGLE_ON_REPEAT" , SWITCH_TRUE, -1))) { | |||
2097 | rstatus = switch_ivr_stop_record_session(other_session, file); | |||
2098 | } else { | |||
2099 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(other_session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2099, (const char*)(other_session), SWITCH_LOG_WARNING, "Already recording [%s]\n", file); | |||
2100 | } | |||
2101 | exist = 1; | |||
2102 | } | |||
2103 | switch_core_session_rwunlock(other_session); | |||
2104 | } | |||
2105 | ||||
2106 | if (exist) { | |||
2107 | return rstatus; | |||
2108 | } | |||
2109 | } | |||
2110 | ||||
2111 | if (!fh) { | |||
2112 | if (!(fh = switch_core_session_alloc(session, sizeof(*fh))switch_core_perform_session_alloc(session, sizeof(*fh), "src/switch_ivr_async.c" , (const char *)__func__, 2112))) { | |||
2113 | return SWITCH_STATUS_MEMERR; | |||
2114 | } | |||
2115 | } | |||
2116 | ||||
2117 | if ((p = switch_channel_get_variable(channel, "RECORD_WRITE_ONLY")switch_channel_get_variable_dup(channel, "RECORD_WRITE_ONLY", SWITCH_TRUE, -1)) && switch_true(p)) { | |||
2118 | flags &= ~SMBF_READ_STREAM; | |||
2119 | flags |= SMBF_WRITE_STREAM; | |||
2120 | } | |||
2121 | ||||
2122 | if ((p = switch_channel_get_variable(channel, "RECORD_READ_ONLY")switch_channel_get_variable_dup(channel, "RECORD_READ_ONLY", SWITCH_TRUE , -1)) && switch_true(p)) { | |||
2123 | flags &= ~SMBF_WRITE_STREAM; | |||
2124 | flags |= SMBF_READ_STREAM; | |||
2125 | } | |||
2126 | ||||
2127 | if ((p = switch_channel_get_variable(channel, "RECORD_STEREO")switch_channel_get_variable_dup(channel, "RECORD_STEREO", SWITCH_TRUE , -1)) && switch_true(p)) { | |||
2128 | flags |= SMBF_STEREO; | |||
2129 | flags &= ~SMBF_STEREO_SWAP; | |||
2130 | channels = 2; | |||
2131 | } | |||
2132 | ||||
2133 | if ((p = switch_channel_get_variable(channel, "RECORD_STEREO_SWAP")switch_channel_get_variable_dup(channel, "RECORD_STEREO_SWAP" , SWITCH_TRUE, -1)) && switch_true(p)) { | |||
2134 | flags |= SMBF_STEREO; | |||
2135 | flags |= SMBF_STEREO_SWAP; | |||
2136 | channels = 2; | |||
2137 | } | |||
2138 | ||||
2139 | if ((p = switch_channel_get_variable(channel, "RECORD_ANSWER_REQ")switch_channel_get_variable_dup(channel, "RECORD_ANSWER_REQ", SWITCH_TRUE, -1)) && switch_true(p)) { | |||
2140 | flags |= SMBF_ANSWER_REQ; | |||
2141 | } | |||
2142 | ||||
2143 | if ((p = switch_channel_get_variable(channel, "RECORD_BRIDGE_REQ")switch_channel_get_variable_dup(channel, "RECORD_BRIDGE_REQ", SWITCH_TRUE, -1)) && switch_true(p)) { | |||
2144 | flags |= SMBF_BRIDGE_REQ; | |||
2145 | } | |||
2146 | ||||
2147 | if ((p = switch_channel_get_variable(channel, "RECORD_APPEND")switch_channel_get_variable_dup(channel, "RECORD_APPEND", SWITCH_TRUE , -1)) && switch_true(p)) { | |||
2148 | file_flags |= SWITCH_FILE_WRITE_APPEND; | |||
2149 | } | |||
2150 | ||||
2151 | ||||
2152 | fh->samplerate = 0; | |||
2153 | if ((vval = switch_channel_get_variable(channel, "record_sample_rate")switch_channel_get_variable_dup(channel, "record_sample_rate" , SWITCH_TRUE, -1))) { | |||
2154 | int tmp = 0; | |||
2155 | ||||
2156 | tmp = atoi(vval); | |||
2157 | ||||
2158 | if (switch_is_valid_rate(tmp)(tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 11025 || tmp == 22050 || tmp == 44100 || tmp == 48000)) { | |||
2159 | fh->samplerate = tmp; | |||
2160 | } | |||
2161 | } | |||
2162 | ||||
2163 | if (!fh->samplerate) { | |||
2164 | fh->samplerate = read_impl.actual_samples_per_second; | |||
2165 | } | |||
2166 | ||||
2167 | fh->channels = channels; | |||
2168 | ||||
2169 | if ((vval = switch_channel_get_variable(channel, "enable_file_write_buffering")switch_channel_get_variable_dup(channel, "enable_file_write_buffering" , SWITCH_TRUE, -1))) { | |||
2170 | int tmp = atoi(vval); | |||
2171 | ||||
2172 | if (tmp > 0) { | |||
2173 | fh->pre_buffer_datalen = tmp; | |||
2174 | } else if (switch_true(vval)) { | |||
2175 | fh->pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN65536; | |||
2176 | } | |||
2177 | ||||
2178 | } else { | |||
2179 | fh->pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN65536; | |||
2180 | } | |||
2181 | ||||
2182 | ||||
2183 | if (!switch_is_file_path(file)) { | |||
2184 | char *tfile = NULL((void*)0); | |||
2185 | char *e; | |||
2186 | const char *prefix; | |||
2187 | ||||
2188 | prefix = switch_channel_get_variable(channel, "sound_prefix")switch_channel_get_variable_dup(channel, "sound_prefix", SWITCH_TRUE , -1); | |||
2189 | ||||
2190 | if (!prefix) { | |||
2191 | prefix = SWITCH_GLOBAL_dirs.base_dir; | |||
2192 | } | |||
2193 | ||||
2194 | if (*file == '[') { | |||
2195 | tfile = switch_core_session_strdup(session, file)switch_core_perform_session_strdup(session, file, "src/switch_ivr_async.c" , (const char *)__func__, 2195); | |||
2196 | if ((e = switch_find_end_paren(tfile, '[', ']'))) { | |||
2197 | *e = '\0'; | |||
2198 | file = e + 1; | |||
2199 | } else { | |||
2200 | tfile = NULL((void*)0); | |||
2201 | } | |||
2202 | } else { | |||
2203 | file_path = switch_core_session_sprintf(session, "%s%s%s", prefix, SWITCH_PATH_SEPARATOR"/", file); | |||
2204 | } | |||
2205 | ||||
2206 | file = switch_core_session_sprintf(session, "%s%s%s%s%s", switch_str_nil(tfile)(tfile ? tfile : ""), tfile ? "]" : "", prefix, SWITCH_PATH_SEPARATOR"/", file); | |||
2207 | } else { | |||
2208 | file_path = switch_core_session_strdup(session, file)switch_core_perform_session_strdup(session, file, "src/switch_ivr_async.c" , (const char *)__func__, 2208); | |||
2209 | } | |||
2210 | ||||
2211 | if (file_path && !strstr(file_path, SWITCH_URL_SEPARATOR"://")) { | |||
2212 | char *p; | |||
2213 | char *path = switch_core_session_strdup(session, file_path)switch_core_perform_session_strdup(session, file_path, "src/switch_ivr_async.c" , (const char *)__func__, 2213); | |||
2214 | ||||
2215 | if ((p = strrchr(path, *SWITCH_PATH_SEPARATOR"/"))) { | |||
2216 | *p = '\0'; | |||
2217 | if (switch_dir_make_recursive(path, SWITCH_DEFAULT_DIR_PERMS0x0400 | 0x0200 | 0x0100 | 0x0040 | 0x0010, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { | |||
2218 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2218, (const char*)(session), SWITCH_LOG_ERROR, "Error creating %s\n", path); | |||
2219 | return SWITCH_STATUS_GENERR; | |||
2220 | } | |||
2221 | ||||
2222 | } else { | |||
2223 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2223, (const char*)(session), SWITCH_LOG_ERROR, "Error finding the folder path section in '%s'\n", path); | |||
2224 | path = NULL((void*)0); | |||
2225 | } | |||
2226 | } | |||
2227 | ||||
2228 | rh = switch_core_session_alloc(session, sizeof(*rh))switch_core_perform_session_alloc(session, sizeof(*rh), "src/switch_ivr_async.c" , (const char *)__func__, 2228); | |||
2229 | ||||
2230 | if ((ext = strrchr(file, '.'))) { | |||
2231 | ext++; | |||
2232 | if (switch_core_file_open(fh, file, channels, read_impl.actual_samples_per_second, file_flags, NULL)switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 2232, fh, file, channels, read_impl.actual_samples_per_second , file_flags, ((void*)0)) != SWITCH_STATUS_SUCCESS) { | |||
2233 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2233, (const char*)(session), SWITCH_LOG_ERROR, "Error opening %s\n", file); | |||
2234 | if (hangup_on_error) { | |||
2235 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2235, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
2236 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
2237 | } | |||
2238 | return SWITCH_STATUS_GENERR; | |||
2239 | } | |||
2240 | } else { | |||
2241 | int tflags = 0; | |||
2242 | ||||
2243 | ext = read_impl.iananame; | |||
2244 | ||||
2245 | in_file = switch_core_session_sprintf(session, "%s-in.%s", file, ext); | |||
2246 | out_file = switch_core_session_sprintf(session, "%s-out.%s", file, ext); | |||
2247 | ||||
2248 | ||||
2249 | if (switch_core_file_open(&rh->in_fh, in_file, channels, read_impl.actual_samples_per_second, file_flags, NULL)switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 2249, &rh->in_fh, in_file, channels, read_impl.actual_samples_per_second, file_flags, ((void*)0)) != SWITCH_STATUS_SUCCESS) { | |||
2250 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2250, (const char*)(session), SWITCH_LOG_ERROR, "Error opening %s\n", in_file); | |||
2251 | if (hangup_on_error) { | |||
2252 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2252, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
2253 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
2254 | } | |||
2255 | return SWITCH_STATUS_GENERR; | |||
2256 | } | |||
2257 | ||||
2258 | if (switch_core_file_open(&rh->out_fh, out_file, channels, read_impl.actual_samples_per_second, file_flags, NULL)switch_core_perform_file_open("src/switch_ivr_async.c", (const char *)__func__, 2258, &rh->out_fh, out_file, channels , read_impl.actual_samples_per_second, file_flags, ((void*)0) ) != SWITCH_STATUS_SUCCESS) { | |||
2259 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2259, (const char*)(session), SWITCH_LOG_ERROR, "Error opening %s\n", out_file); | |||
2260 | switch_core_file_close(&rh->in_fh); | |||
2261 | if (hangup_on_error) { | |||
2262 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2262, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
2263 | switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); | |||
2264 | } | |||
2265 | return SWITCH_STATUS_GENERR; | |||
2266 | } | |||
2267 | ||||
2268 | rh->native = 1; | |||
2269 | fh = NULL((void*)0); | |||
2270 | ||||
2271 | if ((flags & SMBF_WRITE_STREAM)) { | |||
2272 | tflags |= SMBF_TAP_NATIVE_WRITE; | |||
2273 | } | |||
2274 | ||||
2275 | if ((flags & SMBF_READ_STREAM)) { | |||
2276 | tflags |= SMBF_TAP_NATIVE_READ; | |||
2277 | } | |||
2278 | ||||
2279 | flags = tflags; | |||
2280 | } | |||
2281 | ||||
2282 | ||||
2283 | ||||
2284 | if ((p = switch_channel_get_variable(channel, "RECORD_TITLE")switch_channel_get_variable_dup(channel, "RECORD_TITLE", SWITCH_TRUE , -1))) { | |||
2285 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2285); | |||
2286 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_TITLE, vval); | |||
2287 | switch_channel_set_variable(channel, "RECORD_TITLE", NULL)switch_channel_set_variable_var_check(channel, "RECORD_TITLE" , ((void*)0), SWITCH_TRUE); | |||
2288 | } | |||
2289 | ||||
2290 | if ((p = switch_channel_get_variable(channel, "RECORD_COPYRIGHT")switch_channel_get_variable_dup(channel, "RECORD_COPYRIGHT", SWITCH_TRUE , -1))) { | |||
2291 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2291); | |||
2292 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, vval); | |||
2293 | switch_channel_set_variable(channel, "RECORD_COPYRIGHT", NULL)switch_channel_set_variable_var_check(channel, "RECORD_COPYRIGHT" , ((void*)0), SWITCH_TRUE); | |||
2294 | } | |||
2295 | ||||
2296 | if ((p = switch_channel_get_variable(channel, "RECORD_SOFTWARE")switch_channel_get_variable_dup(channel, "RECORD_SOFTWARE", SWITCH_TRUE , -1))) { | |||
2297 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2297); | |||
2298 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, vval); | |||
2299 | switch_channel_set_variable(channel, "RECORD_SOFTWARE", NULL)switch_channel_set_variable_var_check(channel, "RECORD_SOFTWARE" , ((void*)0), SWITCH_TRUE); | |||
2300 | } | |||
2301 | ||||
2302 | if ((p = switch_channel_get_variable(channel, "RECORD_ARTIST")switch_channel_get_variable_dup(channel, "RECORD_ARTIST", SWITCH_TRUE , -1))) { | |||
2303 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2303); | |||
2304 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, vval); | |||
2305 | switch_channel_set_variable(channel, "RECORD_ARTIST", NULL)switch_channel_set_variable_var_check(channel, "RECORD_ARTIST" , ((void*)0), SWITCH_TRUE); | |||
2306 | } | |||
2307 | ||||
2308 | if ((p = switch_channel_get_variable(channel, "RECORD_COMMENT")switch_channel_get_variable_dup(channel, "RECORD_COMMENT", SWITCH_TRUE , -1))) { | |||
2309 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2309); | |||
2310 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, vval); | |||
2311 | switch_channel_set_variable(channel, "RECORD_COMMENT", NULL)switch_channel_set_variable_var_check(channel, "RECORD_COMMENT" , ((void*)0), SWITCH_TRUE); | |||
2312 | } | |||
2313 | ||||
2314 | if ((p = switch_channel_get_variable(channel, "RECORD_DATE")switch_channel_get_variable_dup(channel, "RECORD_DATE", SWITCH_TRUE , -1))) { | |||
2315 | vval = (const char *) switch_core_session_strdup(session, p)switch_core_perform_session_strdup(session, p, "src/switch_ivr_async.c" , (const char *)__func__, 2315); | |||
2316 | if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval); | |||
2317 | switch_channel_set_variable(channel, "RECORD_DATE", NULL)switch_channel_set_variable_var_check(channel, "RECORD_DATE", ((void*)0), SWITCH_TRUE); | |||
2318 | } | |||
2319 | ||||
2320 | if (limit) { | |||
2321 | to = switch_epoch_time_now(NULL((void*)0)) + limit; | |||
2322 | } | |||
2323 | ||||
2324 | rh->fh = fh; | |||
2325 | rh->file = switch_core_session_strdup(session, file)switch_core_perform_session_strdup(session, file, "src/switch_ivr_async.c" , (const char *)__func__, 2325); | |||
2326 | rh->packet_len = read_impl.decoded_bytes_per_packet; | |||
2327 | ||||
2328 | if (file_flags & SWITCH_FILE_WRITE_APPEND) { | |||
2329 | rh->min_sec = 3; | |||
2330 | } | |||
2331 | ||||
2332 | if ((p = switch_channel_get_variable(channel, "RECORD_MIN_SEC")switch_channel_get_variable_dup(channel, "RECORD_MIN_SEC", SWITCH_TRUE , -1))) { | |||
2333 | int tmp = atoi(p); | |||
2334 | if (tmp >= 0) { | |||
2335 | rh->min_sec = tmp; | |||
2336 | } | |||
2337 | } | |||
2338 | ||||
2339 | if ((p = switch_channel_get_variable(channel, "RECORD_INITIAL_TIMEOUT_MS")switch_channel_get_variable_dup(channel, "RECORD_INITIAL_TIMEOUT_MS" , SWITCH_TRUE, -1))) { | |||
2340 | int tmp = atoi(p); | |||
2341 | if (tmp >= 0) { | |||
2342 | rh->initial_timeout_ms = tmp; | |||
2343 | rh->silence_threshold = 200; | |||
2344 | } | |||
2345 | } | |||
2346 | ||||
2347 | if ((p = switch_channel_get_variable(channel, "RECORD_FINAL_TIMEOUT_MS")switch_channel_get_variable_dup(channel, "RECORD_FINAL_TIMEOUT_MS" , SWITCH_TRUE, -1))) { | |||
2348 | int tmp = atoi(p); | |||
2349 | if (tmp >= 0) { | |||
2350 | rh->final_timeout_ms = tmp; | |||
2351 | rh->silence_threshold = 200; | |||
2352 | } | |||
2353 | } | |||
2354 | ||||
2355 | if ((p = switch_channel_get_variable(channel, "RECORD_SILENCE_THRESHOLD")switch_channel_get_variable_dup(channel, "RECORD_SILENCE_THRESHOLD" , SWITCH_TRUE, -1))) { | |||
2356 | int tmp = atoi(p); | |||
2357 | if (tmp >= 0) { | |||
2358 | rh->silence_threshold = tmp; | |||
2359 | } | |||
2360 | } | |||
2361 | ||||
2362 | rh->hangup_on_error = hangup_on_error; | |||
2363 | ||||
2364 | if ((status = switch_core_media_bug_add(session, "session_record", file, | |||
2365 | record_callback, rh, to, flags, &bug)) != SWITCH_STATUS_SUCCESS) { | |||
2366 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2366, (const char*)(session), SWITCH_LOG_ERROR, "Error adding media bug for file %s\n", file); | |||
2367 | if (rh->native) { | |||
2368 | switch_core_file_close(&rh->in_fh); | |||
2369 | switch_core_file_close(&rh->out_fh); | |||
2370 | } else { | |||
2371 | switch_core_file_close(fh); | |||
2372 | } | |||
2373 | return status; | |||
2374 | } | |||
2375 | ||||
2376 | if ((p = switch_channel_get_variable(channel, "RECORD_PRE_BUFFER_FRAMES")switch_channel_get_variable_dup(channel, "RECORD_PRE_BUFFER_FRAMES" , SWITCH_TRUE, -1))) { | |||
2377 | int tmp = atoi(p); | |||
2378 | ||||
2379 | if (tmp > 0) { | |||
2380 | switch_core_media_bug_set_pre_buffer_framecount(bug, tmp); | |||
2381 | } | |||
2382 | } else { | |||
2383 | switch_core_media_bug_set_pre_buffer_framecount(bug, 25); | |||
2384 | } | |||
2385 | ||||
2386 | switch_channel_set_private(channel, file, bug); | |||
2387 | ||||
2388 | return SWITCH_STATUS_SUCCESS; | |||
2389 | } | |||
2390 | ||||
2391 | ||||
2392 | typedef struct { | |||
2393 | SpeexPreprocessState *read_st; | |||
2394 | SpeexPreprocessState *write_st; | |||
2395 | SpeexEchoState *read_ec; | |||
2396 | SpeexEchoState *write_ec; | |||
2397 | switch_byte_t read_data[2048]; | |||
2398 | switch_byte_t write_data[2048]; | |||
2399 | switch_byte_t read_out[2048]; | |||
2400 | switch_byte_t write_out[2048]; | |||
2401 | switch_mutex_t *read_mutex; | |||
2402 | switch_mutex_t *write_mutex; | |||
2403 | int done; | |||
2404 | } pp_cb_t; | |||
2405 | ||||
2406 | static switch_bool_t preprocess_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
2407 | { | |||
2408 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
2409 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2410 | pp_cb_t *cb = (pp_cb_t *) user_data; | |||
2411 | switch_codec_implementation_t read_impl = { 0 }; | |||
2412 | switch_frame_t *frame = NULL((void*)0); | |||
2413 | ||||
2414 | switch_core_session_get_read_impl(session, &read_impl); | |||
2415 | ||||
2416 | switch (type) { | |||
2417 | case SWITCH_ABC_TYPE_INIT: | |||
2418 | { | |||
2419 | switch_mutex_init(&cb->read_mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(session)); | |||
2420 | switch_mutex_init(&cb->write_mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(session)); | |||
2421 | } | |||
2422 | break; | |||
2423 | case SWITCH_ABC_TYPE_CLOSE: | |||
2424 | { | |||
2425 | if (cb->read_st) { | |||
2426 | speex_preprocess_state_destroy(cb->read_st); | |||
2427 | } | |||
2428 | ||||
2429 | if (cb->write_st) { | |||
2430 | speex_preprocess_state_destroy(cb->write_st); | |||
2431 | } | |||
2432 | ||||
2433 | if (cb->read_ec) { | |||
2434 | speex_echo_state_destroy(cb->read_ec); | |||
2435 | } | |||
2436 | ||||
2437 | if (cb->write_ec) { | |||
2438 | speex_echo_state_destroy(cb->write_ec); | |||
2439 | } | |||
2440 | ||||
2441 | switch_channel_set_private(channel, "_preprocess", NULL((void*)0)); | |||
2442 | } | |||
2443 | break; | |||
2444 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
2445 | { | |||
2446 | if (cb->done) | |||
2447 | return SWITCH_FALSE; | |||
2448 | frame = switch_core_media_bug_get_read_replace_frame(bug); | |||
2449 | ||||
2450 | if (cb->read_st) { | |||
2451 | ||||
2452 | if (cb->read_ec) { | |||
2453 | speex_echo_cancellation(cb->read_ec, (int16_t *) frame->data, (int16_t *) cb->write_data, (int16_t *) cb->read_out); | |||
2454 | memcpy(frame->data, cb->read_out, frame->datalen); | |||
2455 | } | |||
2456 | ||||
2457 | speex_preprocess_run(cb->read_st, frame->data); | |||
2458 | } | |||
2459 | ||||
2460 | if (cb->write_ec) { | |||
2461 | memcpy(cb->read_data, frame->data, frame->datalen); | |||
2462 | } | |||
2463 | } | |||
2464 | break; | |||
2465 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
2466 | { | |||
2467 | if (cb->done) | |||
2468 | return SWITCH_FALSE; | |||
2469 | frame = switch_core_media_bug_get_write_replace_frame(bug); | |||
2470 | ||||
2471 | if (cb->write_st) { | |||
2472 | ||||
2473 | if (cb->write_ec) { | |||
2474 | speex_echo_cancellation(cb->write_ec, (int16_t *) frame->data, (int16_t *) cb->read_data, (int16_t *) cb->write_out); | |||
2475 | memcpy(frame->data, cb->write_out, frame->datalen); | |||
2476 | } | |||
2477 | ||||
2478 | speex_preprocess_run(cb->write_st, frame->data); | |||
2479 | } | |||
2480 | ||||
2481 | if (cb->read_ec) { | |||
2482 | memcpy(cb->write_data, frame->data, frame->datalen); | |||
2483 | } | |||
2484 | } | |||
2485 | break; | |||
2486 | default: | |||
2487 | break; | |||
2488 | } | |||
2489 | ||||
2490 | return SWITCH_TRUE; | |||
2491 | } | |||
2492 | ||||
2493 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_preprocess_session(switch_core_session_t *session, const char *cmds) | |||
2494 | { | |||
2495 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2496 | switch_media_bug_t *bug; | |||
2497 | switch_status_t status; | |||
2498 | time_t to = 0; | |||
2499 | switch_media_bug_flag_t flags = SMBF_NO_PAUSE; | |||
2500 | switch_codec_implementation_t read_impl = { 0 }; | |||
2501 | pp_cb_t *cb; | |||
2502 | int update = 0; | |||
2503 | int argc; | |||
2504 | char *mydata = NULL((void*)0), *argv[5]; | |||
2505 | int i = 0; | |||
2506 | ||||
2507 | switch_core_session_get_read_impl(session, &read_impl); | |||
2508 | ||||
2509 | if ((cb = switch_channel_get_private(channel, "_preprocess"))) { | |||
2510 | update = 1; | |||
2511 | } else { | |||
2512 | cb = switch_core_session_alloc(session, sizeof(*cb))switch_core_perform_session_alloc(session, sizeof(*cb), "src/switch_ivr_async.c" , (const char *)__func__, 2512); | |||
2513 | } | |||
2514 | ||||
2515 | ||||
2516 | if (update) { | |||
2517 | if (!strcasecmp(cmds, "stop")) { | |||
2518 | cb->done = 1; | |||
2519 | return SWITCH_STATUS_SUCCESS; | |||
2520 | } | |||
2521 | } | |||
2522 | ||||
2523 | mydata = strdup(cmds)(__extension__ (__builtin_constant_p (cmds) && ((size_t )(const void *)((cmds) + 1) - (size_t)(const void *)(cmds) == 1) ? (((const char *) (cmds))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (cmds) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, cmds, __len) ; __retval; })) : __strdup (cmds))); | |||
2524 | argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); | |||
2525 | ||||
2526 | for (i = 0; i < argc; i++) { | |||
2527 | char *var = argv[i]; | |||
2528 | char *val = NULL((void*)0); | |||
2529 | char rw; | |||
2530 | int tr; | |||
2531 | int err = 1; | |||
2532 | SpeexPreprocessState *st = NULL((void*)0); | |||
2533 | SpeexEchoState *ec = NULL((void*)0); | |||
2534 | switch_mutex_t *mutex = NULL((void*)0); | |||
2535 | int r = 0; | |||
2536 | ||||
2537 | if (var) { | |||
2538 | if ((val = strchr(var, '=')(__extension__ (__builtin_constant_p ('=') && !__builtin_constant_p (var) && ('=') == '\0' ? (char *) __rawmemchr (var, '=' ) : __builtin_strchr (var, '='))))) { | |||
2539 | *val++ = '\0'; | |||
2540 | ||||
2541 | rw = *var++; | |||
2542 | while (*var == '.' || *var == '_') { | |||
2543 | var++; | |||
2544 | } | |||
2545 | ||||
2546 | if (rw == 'r') { | |||
2547 | if (!cb->read_st) { | |||
2548 | cb->read_st = speex_preprocess_state_init(read_impl.samples_per_packet, read_impl.samples_per_second); | |||
2549 | flags |= SMBF_READ_REPLACE; | |||
2550 | } | |||
2551 | st = cb->read_st; | |||
2552 | ec = cb->read_ec; | |||
2553 | mutex = cb->read_mutex; | |||
2554 | } else if (rw == 'w') { | |||
2555 | if (!cb->write_st) { | |||
2556 | cb->write_st = speex_preprocess_state_init(read_impl.samples_per_packet, read_impl.samples_per_second); | |||
2557 | flags |= SMBF_WRITE_REPLACE; | |||
2558 | } | |||
2559 | st = cb->write_st; | |||
2560 | ec = cb->write_ec; | |||
2561 | mutex = cb->write_mutex; | |||
2562 | } | |||
2563 | ||||
2564 | if (mutex) | |||
2565 | switch_mutex_lock(mutex); | |||
2566 | ||||
2567 | if (st) { | |||
2568 | err = 0; | |||
2569 | tr = switch_true(val); | |||
2570 | if (!strcasecmp(var, "agc")) { | |||
2571 | int l = read_impl.samples_per_second; | |||
2572 | int tmp = atoi(val); | |||
2573 | ||||
2574 | if (!tr) { | |||
2575 | l = tmp; | |||
2576 | } | |||
2577 | ||||
2578 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2578, (const char*)(session), SWITCH_LOG_DEBUG, "Setting AGC on %c to %d\n", rw, tr); | |||
2579 | speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC2, &tr); | |||
2580 | speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL6, &l); | |||
2581 | ||||
2582 | } else if (!strcasecmp(var, "noise_suppress")) { | |||
2583 | int db = atoi(val); | |||
2584 | if (db < 0) { | |||
2585 | r = speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS18, &db); | |||
2586 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2586, (const char*)(session), SWITCH_LOG_DEBUG, "Setting NOISE_SUPPRESS on %c to %d [%d]\n", rw, db, | |||
2587 | r); | |||
2588 | } else { | |||
2589 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2589, (const char*)(session), SWITCH_LOG_ERROR, "Syntax error noise_suppress should be in -db\n"); | |||
2590 | } | |||
2591 | } else if (!strcasecmp(var, "echo_cancel")) { | |||
2592 | int tail = 1024; | |||
2593 | int tmp = atoi(val); | |||
2594 | ||||
2595 | if (!tr && tmp > 0) { | |||
2596 | tail = tmp; | |||
2597 | } else if (!tr) { | |||
2598 | if (ec) { | |||
2599 | if (rw == 'r') { | |||
2600 | speex_echo_state_destroy(cb->read_ec); | |||
2601 | cb->read_ec = NULL((void*)0); | |||
2602 | } else { | |||
2603 | speex_echo_state_destroy(cb->write_ec); | |||
2604 | cb->write_ec = NULL((void*)0); | |||
2605 | } | |||
2606 | } | |||
2607 | ||||
2608 | ec = NULL((void*)0); | |||
2609 | } | |||
2610 | ||||
2611 | if (!ec) { | |||
2612 | if (rw == 'r') { | |||
2613 | ec = cb->read_ec = speex_echo_state_init(read_impl.samples_per_packet, tail); | |||
2614 | speex_echo_ctl(ec, SPEEX_ECHO_SET_SAMPLING_RATE24, &read_impl.samples_per_second); | |||
2615 | flags |= SMBF_WRITE_REPLACE; | |||
2616 | } else { | |||
2617 | ec = cb->write_ec = speex_echo_state_init(read_impl.samples_per_packet, tail); | |||
2618 | speex_echo_ctl(ec, SPEEX_ECHO_SET_SAMPLING_RATE24, &read_impl.samples_per_second); | |||
2619 | flags |= SMBF_READ_REPLACE; | |||
2620 | } | |||
2621 | speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_STATE24, ec); | |||
2622 | } | |||
2623 | ||||
2624 | ||||
2625 | } else if (!strcasecmp(var, "echo_suppress")) { | |||
2626 | int db = atoi(val); | |||
2627 | if (db < 0) { | |||
2628 | speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS20, &db); | |||
2629 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2629, (const char*)(session), SWITCH_LOG_DEBUG, "Setting ECHO_SUPPRESS on %c to %d [%d]\n", rw, db, | |||
2630 | r); | |||
2631 | } else { | |||
2632 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2632, (const char*)(session), SWITCH_LOG_ERROR, "Syntax error echo_suppress should be in -db\n"); | |||
2633 | } | |||
2634 | } else { | |||
2635 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2635, (const char*)(session), SWITCH_LOG_WARNING, "Warning unknown parameter [%s] \n", var); | |||
2636 | } | |||
2637 | } | |||
2638 | } | |||
2639 | ||||
2640 | if (mutex) | |||
2641 | switch_mutex_unlock(mutex); | |||
2642 | ||||
2643 | if (err) { | |||
2644 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2644, (const char*)(session), SWITCH_LOG_ERROR, "Syntax error parsing preprocessor commands\n"); | |||
2645 | } | |||
2646 | ||||
2647 | } else { | |||
2648 | break; | |||
2649 | } | |||
2650 | } | |||
2651 | ||||
2652 | ||||
2653 | switch_safe_free(mydata)if (mydata) {free(mydata);mydata=((void*)0);}; | |||
2654 | ||||
2655 | if (update) { | |||
2656 | return SWITCH_STATUS_SUCCESS; | |||
2657 | } | |||
2658 | ||||
2659 | ||||
2660 | if ((status = switch_core_media_bug_add(session, "preprocess", NULL((void*)0), | |||
2661 | preprocess_callback, cb, to, flags, &bug)) != SWITCH_STATUS_SUCCESS) { | |||
2662 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2662, (const char*)(session), SWITCH_LOG_ERROR, "Error adding media bug.\n"); | |||
2663 | if (cb->read_st) { | |||
2664 | speex_preprocess_state_destroy(cb->read_st); | |||
2665 | } | |||
2666 | ||||
2667 | if (cb->write_st) { | |||
2668 | speex_preprocess_state_destroy(cb->write_st); | |||
2669 | } | |||
2670 | ||||
2671 | if (cb->read_ec) { | |||
2672 | speex_echo_state_destroy(cb->read_ec); | |||
2673 | } | |||
2674 | ||||
2675 | if (cb->write_ec) { | |||
2676 | speex_echo_state_destroy(cb->write_ec); | |||
2677 | } | |||
2678 | ||||
2679 | return status; | |||
2680 | } | |||
2681 | ||||
2682 | switch_channel_set_private(channel, "_preprocess", cb); | |||
2683 | ||||
2684 | return SWITCH_STATUS_SUCCESS; | |||
2685 | } | |||
2686 | ||||
2687 | ||||
2688 | typedef struct { | |||
2689 | switch_core_session_t *session; | |||
2690 | int mute; | |||
2691 | int read_level; | |||
2692 | int write_level; | |||
2693 | int read_mute; | |||
2694 | int write_mute; | |||
2695 | } switch_session_audio_t; | |||
2696 | ||||
2697 | static switch_bool_t session_audio_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
2698 | { | |||
2699 | switch_session_audio_t *pvt = (switch_session_audio_t *) user_data; | |||
2700 | switch_frame_t *frame = NULL((void*)0); | |||
2701 | int level = 0, mute = 0; | |||
2702 | switch_core_session_t *session = switch_core_media_bug_get_session(bug); | |||
2703 | switch_codec_implementation_t read_impl = { 0 }; | |||
2704 | ||||
2705 | switch_core_session_get_read_impl(session, &read_impl); | |||
2706 | ||||
2707 | ||||
2708 | if (type == SWITCH_ABC_TYPE_READ_REPLACE || type == SWITCH_ABC_TYPE_WRITE_REPLACE) { | |||
2709 | if (!(pvt->read_level || pvt->write_level || pvt->read_mute || pvt->write_mute)) { | |||
2710 | switch_channel_set_private(switch_core_session_get_channel(pvt->session), "__audio", NULL((void*)0)); | |||
2711 | return SWITCH_FALSE; | |||
2712 | } | |||
2713 | } | |||
2714 | ||||
2715 | if (type == SWITCH_ABC_TYPE_READ_REPLACE) { | |||
2716 | level = pvt->read_level; | |||
2717 | mute = pvt->read_mute; | |||
2718 | frame = switch_core_media_bug_get_read_replace_frame(bug); | |||
2719 | } else if (type == SWITCH_ABC_TYPE_WRITE_REPLACE) { | |||
2720 | level = pvt->write_level; | |||
2721 | mute = pvt->write_mute; | |||
2722 | frame = switch_core_media_bug_get_write_replace_frame(bug); | |||
2723 | } | |||
2724 | ||||
2725 | if (frame) { | |||
2726 | if (mute) { | |||
2727 | if (mute > 1) { | |||
2728 | switch_generate_sln_silence(frame->data, frame->datalen / 2, read_impl.number_of_channels, mute); | |||
2729 | } else { | |||
2730 | memset(frame->data, 0, frame->datalen); | |||
2731 | } | |||
2732 | } else if (level) { | |||
2733 | switch_change_sln_volume(frame->data, frame->datalen / 2, level); | |||
2734 | } | |||
2735 | ||||
2736 | if (type == SWITCH_ABC_TYPE_READ_REPLACE) { | |||
2737 | switch_core_media_bug_set_read_replace_frame(bug, frame); | |||
2738 | } else if (type == SWITCH_ABC_TYPE_WRITE_REPLACE) { | |||
2739 | switch_core_media_bug_set_write_replace_frame(bug, frame); | |||
2740 | } | |||
2741 | } | |||
2742 | ||||
2743 | return SWITCH_TRUE; | |||
2744 | } | |||
2745 | ||||
2746 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_session_audio(switch_core_session_t *session) | |||
2747 | { | |||
2748 | switch_media_bug_t *bug; | |||
2749 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2750 | ||||
2751 | if ((bug = switch_channel_get_private(channel, "__audio"))) { | |||
2752 | switch_channel_set_private(channel, "__audio", NULL((void*)0)); | |||
2753 | switch_core_media_bug_remove(session, &bug); | |||
2754 | return SWITCH_STATUS_SUCCESS; | |||
2755 | } | |||
2756 | return SWITCH_STATUS_FALSE; | |||
2757 | } | |||
2758 | ||||
2759 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_session_audio(switch_core_session_t *session, const char *cmd, const char *direction, int level) | |||
2760 | { | |||
2761 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2762 | switch_media_bug_t *bug; | |||
2763 | switch_status_t status; | |||
2764 | switch_session_audio_t *pvt; | |||
2765 | switch_codec_implementation_t read_impl = { 0 }; | |||
2766 | int existing = 0, c_read = 0, c_write = 0, flags = SMBF_NO_PAUSE; | |||
2767 | ||||
2768 | if (switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2768) != SWITCH_STATUS_SUCCESS) { | |||
2769 | return SWITCH_STATUS_FALSE; | |||
2770 | } | |||
2771 | ||||
2772 | switch_core_session_get_read_impl(session, &read_impl); | |||
2773 | ||||
2774 | ||||
2775 | if ((bug = switch_channel_get_private(channel, "__audio"))) { | |||
2776 | pvt = switch_core_media_bug_get_user_data(bug); | |||
2777 | existing = 1; | |||
2778 | } else { | |||
2779 | if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt))switch_core_perform_session_alloc(session, sizeof(*pvt), "src/switch_ivr_async.c" , (const char *)__func__, 2779))) { | |||
2780 | return SWITCH_STATUS_MEMERR; | |||
2781 | } | |||
2782 | ||||
2783 | pvt->session = session; | |||
2784 | } | |||
2785 | ||||
2786 | ||||
2787 | if (!strcasecmp(direction, "write")) { | |||
2788 | flags = SMBF_WRITE_REPLACE; | |||
2789 | c_write = 1; | |||
2790 | } else if (!strcasecmp(direction, "read")) { | |||
2791 | flags = SMBF_READ_REPLACE; | |||
2792 | c_read = 1; | |||
2793 | } else if (!strcasecmp(direction, "both")) { | |||
2794 | flags = SMBF_READ_REPLACE | SMBF_WRITE_REPLACE; | |||
2795 | c_read = c_write = 1; | |||
2796 | } | |||
2797 | ||||
2798 | ||||
2799 | if (!strcasecmp(cmd, "mute")) { | |||
2800 | if (c_read) { | |||
2801 | pvt->read_mute = level; | |||
2802 | pvt->read_level = 0; | |||
2803 | } | |||
2804 | if (c_write) { | |||
2805 | pvt->write_mute = level; | |||
2806 | pvt->write_level = 0; | |||
2807 | } | |||
2808 | } else if (!strcasecmp(cmd, "level")) { | |||
2809 | if (level < 5 && level > -5) { | |||
2810 | if (c_read) { | |||
2811 | pvt->read_level = level; | |||
2812 | } | |||
2813 | if (c_write) { | |||
2814 | pvt->write_level = level; | |||
2815 | } | |||
2816 | } | |||
2817 | } | |||
2818 | ||||
2819 | if (existing) { | |||
2820 | switch_core_media_bug_set_flag(bug, flags); | |||
2821 | } else { | |||
2822 | if ((status = switch_core_media_bug_add(session, "audio", cmd, | |||
2823 | session_audio_callback, pvt, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) { | |||
2824 | return status; | |||
2825 | } | |||
2826 | ||||
2827 | switch_channel_set_private(channel, "__audio", bug); | |||
2828 | } | |||
2829 | ||||
2830 | ||||
2831 | return SWITCH_STATUS_SUCCESS; | |||
2832 | } | |||
2833 | ||||
2834 | ||||
2835 | typedef struct { | |||
2836 | switch_core_session_t *session; | |||
2837 | teletone_dtmf_detect_state_t dtmf_detect; | |||
2838 | } switch_inband_dtmf_t; | |||
2839 | ||||
2840 | static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
2841 | { | |||
2842 | switch_inband_dtmf_t *pvt = (switch_inband_dtmf_t *) user_data; | |||
2843 | switch_frame_t *frame = NULL((void*)0); | |||
2844 | switch_channel_t *channel = switch_core_session_get_channel(pvt->session); | |||
2845 | teletone_hit_type_t hit; | |||
2846 | ||||
2847 | switch (type) { | |||
2848 | case SWITCH_ABC_TYPE_INIT: | |||
2849 | break; | |||
2850 | case SWITCH_ABC_TYPE_CLOSE: | |||
2851 | break; | |||
2852 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
2853 | if ((frame = switch_core_media_bug_get_read_replace_frame(bug))) { | |||
2854 | if ((hit = teletone_dtmf_detect(&pvt->dtmf_detect, frame->data, frame->samples)) == TT_HIT_END) { | |||
2855 | switch_dtmf_t dtmf = {0}; | |||
2856 | ||||
2857 | teletone_dtmf_get(&pvt->dtmf_detect, &dtmf.digit, &dtmf.duration); | |||
2858 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 2858, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_DEBUG, "DTMF DETECTED: [%c][%d]\n", | |||
2859 | dtmf.digit, dtmf.duration); | |||
2860 | dtmf.source = SWITCH_DTMF_INBAND_AUDIO; | |||
2861 | switch_channel_queue_dtmf(channel, &dtmf); | |||
2862 | } | |||
2863 | switch_core_media_bug_set_read_replace_frame(bug, frame); | |||
2864 | } | |||
2865 | break; | |||
2866 | case SWITCH_ABC_TYPE_WRITE: | |||
2867 | default: | |||
2868 | break; | |||
2869 | } | |||
2870 | ||||
2871 | return SWITCH_TRUE; | |||
2872 | } | |||
2873 | ||||
2874 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_inband_dtmf_session(switch_core_session_t *session) | |||
2875 | { | |||
2876 | switch_media_bug_t *bug; | |||
2877 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2878 | ||||
2879 | if ((bug = switch_channel_get_private(channel, "dtmf"))) { | |||
2880 | switch_channel_set_private(channel, "dtmf", NULL((void*)0)); | |||
2881 | switch_core_media_bug_remove(session, &bug); | |||
2882 | return SWITCH_STATUS_SUCCESS; | |||
2883 | } | |||
2884 | return SWITCH_STATUS_FALSE; | |||
2885 | } | |||
2886 | ||||
2887 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_inband_dtmf_session(switch_core_session_t *session) | |||
2888 | { | |||
2889 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2890 | switch_media_bug_t *bug; | |||
2891 | switch_status_t status; | |||
2892 | switch_inband_dtmf_t *pvt; | |||
2893 | switch_codec_implementation_t read_impl = { 0 }; | |||
2894 | ||||
2895 | switch_core_session_get_read_impl(session, &read_impl); | |||
2896 | ||||
2897 | if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt))switch_core_perform_session_alloc(session, sizeof(*pvt), "src/switch_ivr_async.c" , (const char *)__func__, 2897))) { | |||
2898 | return SWITCH_STATUS_MEMERR; | |||
2899 | } | |||
2900 | ||||
2901 | teletone_dtmf_detect_init(&pvt->dtmf_detect, read_impl.actual_samples_per_second); | |||
2902 | ||||
2903 | pvt->session = session; | |||
2904 | ||||
2905 | ||||
2906 | if (switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 2906) != SWITCH_STATUS_SUCCESS) { | |||
2907 | return SWITCH_STATUS_FALSE; | |||
2908 | } | |||
2909 | ||||
2910 | if ((status = switch_core_media_bug_add(session, "inband_dtmf", NULL((void*)0), | |||
2911 | inband_dtmf_callback, pvt, 0, SMBF_READ_REPLACE | SMBF_NO_PAUSE | SMBF_ONE_ONLY, &bug)) != SWITCH_STATUS_SUCCESS) { | |||
2912 | return status; | |||
2913 | } | |||
2914 | ||||
2915 | switch_channel_set_private(channel, "dtmf", bug); | |||
2916 | ||||
2917 | return SWITCH_STATUS_SUCCESS; | |||
2918 | } | |||
2919 | ||||
2920 | typedef struct { | |||
2921 | switch_core_session_t *session; | |||
2922 | teletone_generation_session_t ts; | |||
2923 | switch_queue_t *digit_queue; | |||
2924 | switch_buffer_t *audio_buffer; | |||
2925 | switch_mutex_t *mutex; | |||
2926 | int read; | |||
2927 | int ready; | |||
2928 | int skip; | |||
2929 | } switch_inband_dtmf_generate_t; | |||
2930 | ||||
2931 | static int teletone_dtmf_generate_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) | |||
2932 | { | |||
2933 | switch_buffer_t *audio_buffer = ts->user_data; | |||
2934 | int wrote; | |||
2935 | ||||
2936 | if (!audio_buffer) { | |||
2937 | return -1; | |||
2938 | } | |||
2939 | ||||
2940 | wrote = teletone_mux_tones(ts, map); | |||
2941 | switch_buffer_write(audio_buffer, ts->buffer, wrote * 2); | |||
2942 | ||||
2943 | return 0; | |||
2944 | } | |||
2945 | ||||
2946 | static switch_status_t generate_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) | |||
2947 | { | |||
2948 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
2949 | switch_media_bug_t *bug = switch_channel_get_private(channel, "dtmf_generate"); | |||
2950 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
2951 | ||||
2952 | if (bug) { | |||
2953 | switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) switch_core_media_bug_get_user_data(bug); | |||
2954 | ||||
2955 | if (pvt) { | |||
2956 | switch_mutex_lock(pvt->mutex); | |||
2957 | ||||
2958 | if (pvt->ready) { | |||
2959 | switch_dtmf_t *dt = NULL((void*)0); | |||
2960 | switch_zmalloc(dt, sizeof(*dt))(void)((((dt = calloc(1, (sizeof(*dt))))) ? (void) (0) : __assert_fail ("(dt = calloc(1, (sizeof(*dt))))", "src/switch_ivr_async.c" , 2960, __PRETTY_FUNCTION__)),dt); | |||
2961 | *dt = *dtmf; | |||
2962 | if (!switch_buffer_inuse(pvt->audio_buffer)) { | |||
2963 | pvt->skip = 10; | |||
2964 | } | |||
2965 | if (switch_queue_trypush(pvt->digit_queue, dt) == SWITCH_STATUS_SUCCESS) { | |||
2966 | switch_event_t *event; | |||
2967 | ||||
2968 | if (switch_event_create(&event, SWITCH_EVENT_DTMF)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 2968, &event, SWITCH_EVENT_DTMF , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
2969 | switch_channel_event_set_data(channel, event); | |||
2970 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Digit", "%c", dtmf->digit); | |||
2971 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Duration", "%u", dtmf->duration); | |||
2972 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "DTMF-Conversion", "native:inband"); | |||
2973 | if (switch_channel_test_flag(channel, CF_DIVERT_EVENTS)) { | |||
2974 | switch_core_session_queue_event(session, &event); | |||
2975 | } else { | |||
2976 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 2976, &event, ((void*)0)); | |||
2977 | } | |||
2978 | } | |||
2979 | ||||
2980 | dt = NULL((void*)0); | |||
2981 | /* | |||
2982 | SWITCH_STATUS_FALSE indicates pretend there never was a DTMF | |||
2983 | since we will be generating it inband now. | |||
2984 | */ | |||
2985 | status = SWITCH_STATUS_FALSE; | |||
2986 | } else { | |||
2987 | free(dt); | |||
2988 | } | |||
2989 | } | |||
2990 | switch_mutex_unlock(pvt->mutex); | |||
2991 | } | |||
2992 | } | |||
2993 | ||||
2994 | return status; | |||
2995 | } | |||
2996 | ||||
2997 | ||||
2998 | static switch_bool_t inband_dtmf_generate_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
2999 | { | |||
3000 | switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) user_data; | |||
3001 | switch_frame_t *frame; | |||
3002 | switch_codec_implementation_t read_impl = { 0 }; | |||
3003 | switch_core_session_get_read_impl(pvt->session, &read_impl); | |||
3004 | ||||
3005 | switch (type) { | |||
3006 | case SWITCH_ABC_TYPE_INIT: | |||
3007 | { | |||
3008 | switch_queue_create(&pvt->digit_queue, 100, switch_core_session_get_pool(pvt->session)); | |||
3009 | switch_buffer_create_dynamic(&pvt->audio_buffer, 512, 1024, 0); | |||
3010 | teletone_init_session(&pvt->ts, 0, teletone_dtmf_generate_handler, pvt->audio_buffer); | |||
3011 | pvt->ts.rate = read_impl.actual_samples_per_second; | |||
3012 | pvt->ts.channels = 1; | |||
3013 | switch_mutex_init(&pvt->mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(pvt->session)); | |||
3014 | if (pvt->read) { | |||
3015 | switch_core_event_hook_add_recv_dtmf(pvt->session, generate_on_dtmf); | |||
3016 | } else { | |||
3017 | switch_core_event_hook_add_send_dtmf(pvt->session, generate_on_dtmf); | |||
3018 | } | |||
3019 | switch_mutex_lock(pvt->mutex); | |||
3020 | pvt->ready = 1; | |||
3021 | switch_mutex_unlock(pvt->mutex); | |||
3022 | } | |||
3023 | break; | |||
3024 | case SWITCH_ABC_TYPE_CLOSE: | |||
3025 | { | |||
3026 | switch_mutex_lock(pvt->mutex); | |||
3027 | pvt->ready = 0; | |||
3028 | switch_core_event_hook_remove_recv_dtmf(pvt->session, generate_on_dtmf); | |||
3029 | switch_buffer_destroy(&pvt->audio_buffer); | |||
3030 | teletone_destroy_session(&pvt->ts); | |||
3031 | switch_mutex_unlock(pvt->mutex); | |||
3032 | } | |||
3033 | break; | |||
3034 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
3035 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
3036 | { | |||
3037 | switch_size_t bytes; | |||
3038 | void *pop; | |||
3039 | ||||
3040 | if (pvt->skip) { | |||
3041 | pvt->skip--; | |||
3042 | return SWITCH_TRUE; | |||
3043 | } | |||
3044 | ||||
3045 | ||||
3046 | switch_mutex_lock(pvt->mutex); | |||
3047 | ||||
3048 | if (!pvt->ready) { | |||
3049 | switch_mutex_unlock(pvt->mutex); | |||
3050 | return SWITCH_FALSE; | |||
3051 | } | |||
3052 | ||||
3053 | if (pvt->read) { | |||
3054 | frame = switch_core_media_bug_get_read_replace_frame(bug); | |||
3055 | } else { | |||
3056 | frame = switch_core_media_bug_get_write_replace_frame(bug); | |||
3057 | } | |||
3058 | ||||
3059 | if (!switch_buffer_inuse(pvt->audio_buffer)) { | |||
3060 | if (switch_queue_trypop(pvt->digit_queue, &pop) == SWITCH_STATUS_SUCCESS) { | |||
3061 | switch_dtmf_t *dtmf = (switch_dtmf_t *) pop; | |||
3062 | ||||
3063 | ||||
3064 | if (dtmf->source != SWITCH_DTMF_INBAND_AUDIO) { | |||
3065 | char buf[2] = ""; | |||
3066 | int duration = dtmf->duration; | |||
3067 | ||||
3068 | buf[0] = dtmf->digit; | |||
3069 | if (duration > (int)switch_core_max_dtmf_duration(0)) { | |||
3070 | duration = switch_core_default_dtmf_duration(0); | |||
3071 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3071, (const char*)(switch_core_media_bug_get_session (bug)), | |||
3072 | SWITCH_LOG_WARNING, "%s Truncating DTMF duration %d ms to %d ms\n", | |||
3073 | switch_channel_get_name(switch_core_session_get_channel(pvt->session)), dtmf->duration / 8, duration); | |||
3074 | } | |||
3075 | ||||
3076 | ||||
3077 | pvt->ts.duration = duration; | |||
3078 | teletone_run(&pvt->ts, buf); | |||
3079 | } | |||
3080 | free(pop); | |||
3081 | } | |||
3082 | } | |||
3083 | ||||
3084 | if (switch_buffer_inuse(pvt->audio_buffer) && (bytes = switch_buffer_read(pvt->audio_buffer, frame->data, frame->datalen))) { | |||
3085 | if (bytes < frame->datalen) { | |||
3086 | switch_byte_t *dp = frame->data; | |||
3087 | memset(dp + bytes, 0, frame->datalen - bytes); | |||
3088 | } | |||
3089 | } | |||
3090 | ||||
3091 | if (pvt->read) { | |||
3092 | switch_core_media_bug_set_read_replace_frame(bug, frame); | |||
3093 | } else { | |||
3094 | switch_core_media_bug_set_write_replace_frame(bug, frame); | |||
3095 | } | |||
3096 | ||||
3097 | switch_mutex_unlock(pvt->mutex); | |||
3098 | } | |||
3099 | break; | |||
3100 | default: | |||
3101 | break; | |||
3102 | } | |||
3103 | ||||
3104 | return SWITCH_TRUE; | |||
3105 | } | |||
3106 | ||||
3107 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_inband_dtmf_generate_session(switch_core_session_t *session) | |||
3108 | { | |||
3109 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3110 | switch_media_bug_t *bug = switch_channel_get_private(channel, "dtmf_generate"); | |||
3111 | ||||
3112 | if (bug) { | |||
3113 | switch_channel_set_private(channel, "dtmf_generate", NULL((void*)0)); | |||
3114 | switch_core_media_bug_remove(session, &bug); | |||
3115 | return SWITCH_STATUS_SUCCESS; | |||
3116 | } | |||
3117 | ||||
3118 | return SWITCH_STATUS_FALSE; | |||
3119 | ||||
3120 | } | |||
3121 | ||||
3122 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_inband_dtmf_generate_session(switch_core_session_t *session, switch_bool_t read_stream) | |||
3123 | { | |||
3124 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3125 | switch_media_bug_t *bug; | |||
3126 | switch_status_t status; | |||
3127 | switch_inband_dtmf_generate_t *pvt; | |||
3128 | ||||
3129 | if ((status = switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 3129)) != SWITCH_STATUS_SUCCESS) { | |||
3130 | return SWITCH_STATUS_FALSE; | |||
3131 | } | |||
3132 | ||||
3133 | if (!switch_channel_media_up(channel)(switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag (channel, CF_EARLY_MEDIA)) || !switch_core_session_get_read_codec(session)) { | |||
3134 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3134, (const char*)(session), SWITCH_LOG_ERROR, "Can not install inband dtmf generate. Media not enabled on channel\n"); | |||
3135 | return status; | |||
3136 | } | |||
3137 | ||||
3138 | if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt))switch_core_perform_session_alloc(session, sizeof(*pvt), "src/switch_ivr_async.c" , (const char *)__func__, 3138))) { | |||
3139 | return SWITCH_STATUS_MEMERR; | |||
3140 | } | |||
3141 | ||||
3142 | pvt->session = session; | |||
3143 | pvt->read = !!read_stream; | |||
3144 | ||||
3145 | if ((status = switch_core_media_bug_add(session, "inband_dtmf_generate", NULL((void*)0), | |||
3146 | inband_dtmf_generate_callback, pvt, 0, | |||
3147 | SMBF_NO_PAUSE | (pvt->read ? SMBF_READ_REPLACE : SMBF_WRITE_REPLACE) , &bug)) != SWITCH_STATUS_SUCCESS) { | |||
3148 | return status; | |||
3149 | } | |||
3150 | ||||
3151 | switch_channel_set_private(channel, "dtmf_generate", bug); | |||
3152 | ||||
3153 | return SWITCH_STATUS_SUCCESS; | |||
3154 | } | |||
3155 | ||||
3156 | #define MAX_TONES16 16 | |||
3157 | typedef struct { | |||
3158 | teletone_multi_tone_t mt; | |||
3159 | char *app; | |||
3160 | char *data; | |||
3161 | char *key; | |||
3162 | teletone_tone_map_t map; | |||
3163 | int up; | |||
3164 | int total_hits; | |||
3165 | int hits; | |||
3166 | int sleep; | |||
3167 | int expires; | |||
3168 | int default_sleep; | |||
3169 | int default_expires; | |||
3170 | int once; | |||
3171 | switch_time_t start_time; | |||
3172 | switch_tone_detect_callback_t callback; | |||
3173 | } switch_tone_detect_t; | |||
3174 | ||||
3175 | typedef struct { | |||
3176 | switch_tone_detect_t list[MAX_TONES16 + 1]; | |||
3177 | int index; | |||
3178 | switch_media_bug_t *bug; | |||
3179 | switch_core_session_t *session; | |||
3180 | int bug_running; | |||
3181 | int detect_fax; | |||
3182 | } switch_tone_container_t; | |||
3183 | ||||
3184 | ||||
3185 | static void tone_detect_set_total_time(switch_tone_container_t *cont, int index) | |||
3186 | { | |||
3187 | char *total_time = switch_mprintf("%d", (int)(switch_micro_time_now() - cont->list[index].start_time) / 1000); | |||
3188 | ||||
3189 | switch_channel_set_variable_name_printf(switch_core_session_get_channel(cont->session), total_time, "tone_detect_%s_total_time", | |||
3190 | cont->list[index].key); | |||
3191 | switch_safe_free(total_time)if (total_time) {free(total_time);total_time=((void*)0);}; | |||
3192 | } | |||
3193 | ||||
3194 | static switch_status_t tone_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) | |||
3195 | { | |||
3196 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3197 | switch_tone_container_t *cont = switch_channel_get_private(channel, "_tone_detect_"); | |||
3198 | int i; | |||
3199 | ||||
3200 | if (!cont || !cont->detect_fax || dtmf->digit != 'f') { | |||
3201 | return SWITCH_STATUS_SUCCESS; | |||
3202 | } | |||
3203 | ||||
3204 | i = cont->detect_fax; | |||
3205 | ||||
3206 | tone_detect_set_total_time(cont, i); | |||
3207 | if (cont->list[i].callback) { | |||
3208 | cont->list[i].callback(cont->session, cont->list[i].app, cont->list[i].data); | |||
3209 | } else { | |||
3210 | switch_channel_execute_on(switch_core_session_get_channel(cont->session), SWITCH_CHANNEL_EXECUTE_ON_TONE_DETECT_VARIABLE"execute_on_tone_detect"); | |||
3211 | switch_channel_api_on(switch_core_session_get_channel(cont->session), SWITCH_CHANNEL_API_ON_TONE_DETECT_VARIABLE"api_on_tone_detect"); | |||
3212 | ||||
3213 | if (cont->list[i].app) { | |||
3214 | switch_core_session_execute_application_async(cont->session, cont->list[i].app, cont->list[i].data); | |||
3215 | } | |||
3216 | } | |||
3217 | ||||
3218 | return SWITCH_STATUS_SUCCESS; | |||
3219 | ||||
3220 | } | |||
3221 | ||||
3222 | static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
3223 | { | |||
3224 | switch_tone_container_t *cont = (switch_tone_container_t *) user_data; | |||
3225 | switch_frame_t *frame = NULL((void*)0); | |||
3226 | int i = 0; | |||
3227 | switch_bool_t rval = SWITCH_TRUE; | |||
3228 | ||||
3229 | switch (type) { | |||
3230 | case SWITCH_ABC_TYPE_INIT: | |||
3231 | if (cont) { | |||
3232 | cont->bug_running = 1; | |||
3233 | } | |||
3234 | break; | |||
3235 | case SWITCH_ABC_TYPE_CLOSE: | |||
3236 | break; | |||
3237 | case SWITCH_ABC_TYPE_READ_REPLACE: | |||
3238 | case SWITCH_ABC_TYPE_WRITE_REPLACE: | |||
3239 | { | |||
3240 | ||||
3241 | if (type == SWITCH_ABC_TYPE_READ_REPLACE) { | |||
3242 | frame = switch_core_media_bug_get_read_replace_frame(bug); | |||
3243 | } else { | |||
3244 | frame = switch_core_media_bug_get_write_replace_frame(bug); | |||
3245 | } | |||
3246 | ||||
3247 | for (i = 0; i < cont->index; i++) { | |||
3248 | int skip = 0; | |||
3249 | ||||
3250 | if (cont->list[i].sleep) { | |||
3251 | cont->list[i].sleep--; | |||
3252 | if (cont->list[i].sleep) { | |||
3253 | skip = 1; | |||
3254 | } | |||
3255 | } | |||
3256 | ||||
3257 | if (cont->list[i].expires) { | |||
3258 | cont->list[i].expires--; | |||
3259 | if (!cont->list[i].expires) { | |||
3260 | cont->list[i].hits = 0; | |||
3261 | cont->list[i].sleep = 0; | |||
3262 | cont->list[i].expires = 0; | |||
3263 | } | |||
3264 | } | |||
3265 | ||||
3266 | if (!cont->list[i].up) | |||
3267 | skip = 1; | |||
3268 | ||||
3269 | if (skip) | |||
3270 | continue; | |||
3271 | ||||
3272 | if (teletone_multi_tone_detect(&cont->list[i].mt, frame->data, frame->samples)) { | |||
3273 | switch_event_t *event; | |||
3274 | cont->list[i].hits++; | |||
3275 | ||||
3276 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3276, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_DEBUG, "TONE %s HIT %d/%d\n", | |||
3277 | cont->list[i].key, cont->list[i].hits, cont->list[i].total_hits); | |||
3278 | cont->list[i].sleep = cont->list[i].default_sleep; | |||
3279 | cont->list[i].expires = cont->list[i].default_expires; | |||
3280 | ||||
3281 | if (cont->list[i].hits >= cont->list[i].total_hits) { | |||
3282 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3282, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_DEBUG, "TONE %s DETECTED\n", | |||
3283 | cont->list[i].key); | |||
3284 | tone_detect_set_total_time(cont, i); | |||
3285 | cont->list[i].up = 0; | |||
3286 | ||||
3287 | if (cont->list[i].callback) { | |||
3288 | if ((rval = cont->list[i].callback(cont->session, cont->list[i].app, cont->list[i].data)) == SWITCH_TRUE) { | |||
3289 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3289, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_DEBUG, "Re-enabling %s\n", | |||
3290 | cont->list[i].key); | |||
3291 | cont->list[i].up = 1; | |||
3292 | cont->list[i].hits = 0; | |||
3293 | cont->list[i].sleep = 0; | |||
3294 | cont->list[i].expires = 0; | |||
3295 | } | |||
3296 | } else { | |||
3297 | switch_channel_execute_on(switch_core_session_get_channel(cont->session), SWITCH_CHANNEL_EXECUTE_ON_TONE_DETECT_VARIABLE"execute_on_tone_detect"); | |||
3298 | if (cont->list[i].app) { | |||
3299 | switch_core_session_execute_application_async(cont->session, cont->list[i].app, cont->list[i].data); | |||
3300 | } | |||
3301 | } | |||
3302 | ||||
3303 | if (cont->list[i].once) { | |||
3304 | rval = SWITCH_FALSE; | |||
3305 | } | |||
3306 | ||||
3307 | if (switch_event_create(&event, SWITCH_EVENT_DETECTED_TONE)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 3307, &event, SWITCH_EVENT_DETECTED_TONE , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
3308 | switch_event_t *dup; | |||
3309 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Detected-Tone", cont->list[i].key); | |||
3310 | ||||
3311 | if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { | |||
3312 | switch_event_fire(&dup)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 3312, &dup, ((void*)0)); | |||
3313 | } | |||
3314 | ||||
3315 | if (switch_core_session_queue_event(cont->session, &event) != SWITCH_STATUS_SUCCESS) { | |||
3316 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3316, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_ERROR, | |||
3317 | "Event queue failed!\n"); | |||
3318 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); | |||
3319 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 3319, &event, ((void*)0)); | |||
3320 | } | |||
3321 | } | |||
3322 | } | |||
3323 | } | |||
3324 | } | |||
3325 | } | |||
3326 | break; | |||
3327 | case SWITCH_ABC_TYPE_WRITE: | |||
3328 | default: | |||
3329 | break; | |||
3330 | } | |||
3331 | ||||
3332 | if (rval == SWITCH_FALSE) { | |||
3333 | cont->bug_running = 0; | |||
3334 | } | |||
3335 | ||||
3336 | return rval; | |||
3337 | } | |||
3338 | ||||
3339 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_tone_detect_session(switch_core_session_t *session) | |||
3340 | { | |||
3341 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3342 | switch_tone_container_t *cont = switch_channel_get_private(channel, "_tone_detect_"); | |||
3343 | int i = 0; | |||
3344 | ||||
3345 | if (cont) { | |||
3346 | switch_channel_set_private(channel, "_tone_detect_", NULL((void*)0)); | |||
3347 | for (i = 0; i < cont->index; i++) { | |||
3348 | cont->list[i].up = 0; | |||
3349 | } | |||
3350 | switch_core_media_bug_remove(session, &cont->bug); | |||
3351 | if (cont->detect_fax) { | |||
3352 | cont->detect_fax = 0; | |||
3353 | } | |||
3354 | return SWITCH_STATUS_SUCCESS; | |||
3355 | } | |||
3356 | return SWITCH_STATUS_FALSE; | |||
3357 | } | |||
3358 | ||||
3359 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_tone_detect_session(switch_core_session_t *session, | |||
3360 | const char *key, const char *tone_spec, | |||
3361 | const char *flags, time_t timeout, | |||
3362 | int hits, const char *app, const char *data, switch_tone_detect_callback_t callback) | |||
3363 | { | |||
3364 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3365 | switch_status_t status; | |||
3366 | switch_tone_container_t *cont = switch_channel_get_private(channel, "_tone_detect_"); | |||
3367 | char *p, *next; | |||
3368 | int i = 0, ok = 0, detect_fax = 0; | |||
3369 | switch_media_bug_flag_t bflags = 0; | |||
3370 | const char *var; | |||
3371 | switch_codec_implementation_t read_impl = { 0 }; | |||
3372 | switch_core_session_get_read_impl(session, &read_impl); | |||
3373 | ||||
3374 | ||||
3375 | if (zstr(key)_zstr(key)) { | |||
3376 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3376, (const char*)(session), SWITCH_LOG_ERROR, "No Key Specified!\n"); | |||
3377 | return SWITCH_STATUS_FALSE; | |||
3378 | } | |||
3379 | ||||
3380 | if (cont) { | |||
3381 | if (cont->index >= MAX_TONES16) { | |||
3382 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3382, (const char*)(session), SWITCH_LOG_ERROR, "Max Tones Reached!\n"); | |||
3383 | return SWITCH_STATUS_FALSE; | |||
3384 | } | |||
3385 | ||||
3386 | for (i = 0; i < cont->index; i++) { | |||
3387 | if (!zstr(cont->list[i].key)_zstr(cont->list[i].key) && !strcasecmp(key, cont->list[i].key)) { | |||
3388 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3388, (const char*)(session), SWITCH_LOG_DEBUG, "Re-enabling %s\n", key); | |||
3389 | cont->list[i].up = 1; | |||
3390 | cont->list[i].hits = 0; | |||
3391 | cont->list[i].sleep = 0; | |||
3392 | cont->list[i].expires = 0; | |||
3393 | return SWITCH_STATUS_SUCCESS; | |||
3394 | } | |||
3395 | } | |||
3396 | } | |||
3397 | ||||
3398 | if (zstr(tone_spec)_zstr(tone_spec)) { | |||
3399 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3399, (const char*)(session), SWITCH_LOG_ERROR, "No Spec Specified!\n"); | |||
3400 | return SWITCH_STATUS_FALSE; | |||
3401 | } | |||
3402 | ||||
3403 | if (!cont && !(cont = switch_core_session_alloc(session, sizeof(*cont))switch_core_perform_session_alloc(session, sizeof(*cont), "src/switch_ivr_async.c" , (const char *)__func__, 3403))) { | |||
3404 | return SWITCH_STATUS_MEMERR; | |||
3405 | } | |||
3406 | ||||
3407 | if ((var = switch_channel_get_variable(channel, "tone_detect_hits")switch_channel_get_variable_dup(channel, "tone_detect_hits", SWITCH_TRUE , -1))) { | |||
3408 | int tmp = atoi(var); | |||
3409 | if (tmp > 0) { | |||
3410 | hits = tmp; | |||
3411 | } | |||
3412 | } | |||
3413 | ||||
3414 | if (!hits) hits = 1; | |||
3415 | ||||
3416 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3416, (const char*)(session), SWITCH_LOG_DEBUG, "Adding tone spec %s index %d hits %d\n", tone_spec, cont->index, hits); | |||
3417 | ||||
3418 | i = 0; | |||
3419 | p = (char *) tone_spec; | |||
3420 | ||||
3421 | do { | |||
3422 | teletone_process_t this; | |||
3423 | next = strchr(p, ',')(__extension__ (__builtin_constant_p (',') && !__builtin_constant_p (p) && (',') == '\0' ? (char *) __rawmemchr (p, ',') : __builtin_strchr (p, ','))); | |||
3424 | while (*p == ' ') | |||
3425 | p++; | |||
3426 | if ((this = (teletone_process_t) atof(p))) { | |||
3427 | ok++; | |||
3428 | cont->list[cont->index].map.freqs[i++] = this; | |||
3429 | } | |||
3430 | if (!strncasecmp(p, "1100", 4)) { | |||
3431 | detect_fax = cont->index; | |||
3432 | } | |||
3433 | ||||
3434 | if (next) { | |||
3435 | p = next + 1; | |||
3436 | } | |||
3437 | } while (next); | |||
3438 | cont->list[cont->index].map.freqs[i++] = 0; | |||
3439 | ||||
3440 | if (!ok) { | |||
3441 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3441, (const char*)(session), SWITCH_LOG_ERROR, "Invalid tone spec!\n"); | |||
3442 | return SWITCH_STATUS_FALSE; | |||
3443 | } | |||
3444 | ||||
3445 | cont->detect_fax = detect_fax; | |||
3446 | ||||
3447 | cont->list[cont->index].key = switch_core_session_strdup(session, key)switch_core_perform_session_strdup(session, key, "src/switch_ivr_async.c" , (const char *)__func__, 3447); | |||
3448 | ||||
3449 | if (app) { | |||
3450 | cont->list[cont->index].app = switch_core_session_strdup(session, app)switch_core_perform_session_strdup(session, app, "src/switch_ivr_async.c" , (const char *)__func__, 3450); | |||
3451 | } | |||
3452 | ||||
3453 | if (data) { | |||
3454 | cont->list[cont->index].data = switch_core_session_strdup(session, data)switch_core_perform_session_strdup(session, data, "src/switch_ivr_async.c" , (const char *)__func__, 3454); | |||
3455 | } | |||
3456 | ||||
3457 | cont->list[cont->index].callback = callback; | |||
3458 | ||||
3459 | if (!hits) | |||
3460 | hits = 1; | |||
3461 | ||||
3462 | cont->list[cont->index].hits = 0; | |||
3463 | cont->list[cont->index].total_hits = hits; | |||
3464 | cont->list[cont->index].start_time = switch_micro_time_now(); | |||
3465 | ||||
3466 | cont->list[cont->index].up = 1; | |||
3467 | memset(&cont->list[cont->index].mt, 0, sizeof(cont->list[cont->index].mt)); | |||
3468 | cont->list[cont->index].mt.sample_rate = read_impl.actual_samples_per_second; | |||
3469 | teletone_multi_tone_init(&cont->list[cont->index].mt, &cont->list[cont->index].map); | |||
3470 | cont->session = session; | |||
3471 | ||||
3472 | if (switch_channel_pre_answer(channel)switch_channel_perform_pre_answer(channel, "src/switch_ivr_async.c" , (const char *)__func__, 3472) != SWITCH_STATUS_SUCCESS) { | |||
3473 | return SWITCH_STATUS_FALSE; | |||
3474 | } | |||
3475 | ||||
3476 | cont->list[cont->index].default_sleep = 25; | |||
3477 | cont->list[cont->index].default_expires = 250; | |||
3478 | ||||
3479 | if ((var = switch_channel_get_variable(channel, "tone_detect_sleep")switch_channel_get_variable_dup(channel, "tone_detect_sleep", SWITCH_TRUE, -1))) { | |||
3480 | int tmp = atoi(var); | |||
3481 | if (tmp > 0) { | |||
3482 | cont->list[cont->index].default_sleep = tmp; | |||
3483 | } | |||
3484 | } | |||
3485 | ||||
3486 | if ((var = switch_channel_get_variable(channel, "tone_detect_expires")switch_channel_get_variable_dup(channel, "tone_detect_expires" , SWITCH_TRUE, -1))) { | |||
3487 | int tmp = atoi(var); | |||
3488 | if (tmp > 0) { | |||
3489 | cont->list[cont->index].default_expires = tmp; | |||
3490 | } | |||
3491 | } | |||
3492 | ||||
3493 | ||||
3494 | if (zstr(flags)_zstr(flags)) { | |||
3495 | bflags = SMBF_READ_REPLACE; | |||
3496 | } else { | |||
3497 | if (strchr(flags, 'o')(__extension__ (__builtin_constant_p ('o') && !__builtin_constant_p (flags) && ('o') == '\0' ? (char *) __rawmemchr (flags , 'o') : __builtin_strchr (flags, 'o')))) { | |||
3498 | cont->list[cont->index].once = 1; | |||
3499 | } | |||
3500 | ||||
3501 | if (strchr(flags, 'r')(__extension__ (__builtin_constant_p ('r') && !__builtin_constant_p (flags) && ('r') == '\0' ? (char *) __rawmemchr (flags , 'r') : __builtin_strchr (flags, 'r')))) { | |||
3502 | bflags |= SMBF_READ_REPLACE; | |||
3503 | } else if (strchr(flags, 'w')(__extension__ (__builtin_constant_p ('w') && !__builtin_constant_p (flags) && ('w') == '\0' ? (char *) __rawmemchr (flags , 'w') : __builtin_strchr (flags, 'w')))) { | |||
3504 | bflags |= SMBF_WRITE_REPLACE; | |||
3505 | } | |||
3506 | } | |||
3507 | ||||
3508 | bflags |= SMBF_NO_PAUSE; | |||
3509 | ||||
3510 | if (cont->bug_running) { | |||
3511 | status = SWITCH_STATUS_SUCCESS; | |||
3512 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3512, (const char*)(session), SWITCH_LOG_DEBUG, "%s bug already running\n", switch_channel_get_name(channel)); | |||
3513 | } else { | |||
3514 | cont->bug_running = 1; | |||
3515 | if (cont->detect_fax) { | |||
3516 | switch_core_event_hook_add_send_dtmf(session, tone_on_dtmf); | |||
3517 | switch_core_event_hook_add_recv_dtmf(session, tone_on_dtmf); | |||
3518 | } | |||
3519 | ||||
3520 | if ((status = switch_core_media_bug_add(session, "tone_detect", key, | |||
3521 | tone_detect_callback, cont, timeout, bflags, &cont->bug)) != SWITCH_STATUS_SUCCESS) { | |||
3522 | cont->bug_running = 0; | |||
3523 | return status; | |||
3524 | } | |||
3525 | switch_channel_set_private(channel, "_tone_detect_", cont); | |||
3526 | } | |||
3527 | ||||
3528 | cont->index++; | |||
3529 | ||||
3530 | return SWITCH_STATUS_SUCCESS; | |||
3531 | } | |||
3532 | ||||
3533 | typedef struct { | |||
3534 | const char *app; | |||
3535 | uint32_t flags; | |||
3536 | switch_bind_flag_t bind_flags; | |||
3537 | } dtmf_meta_app_t; | |||
3538 | ||||
3539 | typedef struct { | |||
3540 | dtmf_meta_app_t map[14]; | |||
3541 | time_t last_digit; | |||
3542 | switch_bool_t meta_on; | |||
3543 | char meta; | |||
3544 | int up; | |||
3545 | } dtmf_meta_settings_t; | |||
3546 | ||||
3547 | typedef struct { | |||
3548 | dtmf_meta_settings_t sr[3]; | |||
3549 | } dtmf_meta_data_t; | |||
3550 | ||||
3551 | #define SWITCH_META_VAR_KEY"__dtmf_meta" "__dtmf_meta" | |||
3552 | #define SWITCH_BLOCK_DTMF_KEY"__dtmf_block" "__dtmf_block" | |||
3553 | ||||
3554 | typedef struct { | |||
3555 | switch_core_session_t *session; | |||
3556 | const char *app; | |||
3557 | int flags; | |||
3558 | } bch_t; | |||
3559 | ||||
3560 | static void *SWITCH_THREAD_FUNC bcast_thread(switch_thread_t *thread, void *obj) | |||
3561 | { | |||
3562 | bch_t *bch = (bch_t *) obj; | |||
3563 | ||||
3564 | if (!bch->session) { | |||
3565 | return NULL((void*)0); | |||
3566 | } | |||
3567 | ||||
3568 | switch_core_session_read_lock(bch->session); | |||
3569 | switch_ivr_broadcast(switch_core_session_get_uuid(bch->session), bch->app, bch->flags); | |||
3570 | switch_core_session_rwunlock(bch->session); | |||
3571 | ||||
3572 | return NULL((void*)0); | |||
3573 | ||||
3574 | } | |||
3575 | SWITCH_DECLARE(void)__attribute__((visibility("default"))) void switch_ivr_broadcast_in_thread(switch_core_session_t *session, const char *app, int flags) | |||
3576 | { | |||
3577 | switch_thread_t *thread; | |||
3578 | switch_threadattr_t *thd_attr = NULL((void*)0); | |||
3579 | switch_memory_pool_t *pool; | |||
3580 | bch_t *bch; | |||
3581 | ||||
3582 | switch_assert(session)((session) ? (void) (0) : __assert_fail ("session", "src/switch_ivr_async.c" , 3582, __PRETTY_FUNCTION__)); | |||
3583 | ||||
3584 | pool = switch_core_session_get_pool(session); | |||
3585 | ||||
3586 | bch = switch_core_session_alloc(session, sizeof(*bch))switch_core_perform_session_alloc(session, sizeof(*bch), "src/switch_ivr_async.c" , (const char *)__func__, 3586); | |||
3587 | bch->session = session; | |||
3588 | bch->app = app; | |||
3589 | bch->flags = flags; | |||
3590 | ||||
3591 | ||||
3592 | switch_threadattr_create(&thd_attr, pool); | |||
3593 | switch_threadattr_detach_set(thd_attr, 1); | |||
3594 | switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE240 * 1024); | |||
3595 | switch_thread_create(&thread, thd_attr, bcast_thread, bch, pool); | |||
3596 | } | |||
3597 | ||||
3598 | static switch_status_t meta_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) | |||
3599 | { | |||
3600 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3601 | dtmf_meta_data_t *md = switch_channel_get_private(channel, SWITCH_META_VAR_KEY"__dtmf_meta"); | |||
3602 | time_t now = switch_epoch_time_now(NULL((void*)0)); | |||
3603 | char digit[2] = ""; | |||
3604 | int dval; | |||
3605 | ||||
3606 | if (!md || switch_channel_test_flag(channel, CF_INNER_BRIDGE)) { | |||
3607 | return SWITCH_STATUS_SUCCESS; | |||
3608 | } | |||
3609 | ||||
3610 | if (direction == SWITCH_DTMF_RECV && !md->sr[SWITCH_DTMF_RECV].up) { | |||
3611 | return SWITCH_STATUS_SUCCESS; | |||
3612 | } | |||
3613 | ||||
3614 | if (direction == SWITCH_DTMF_SEND && !md->sr[SWITCH_DTMF_SEND].up) { | |||
3615 | return SWITCH_STATUS_SUCCESS; | |||
3616 | } | |||
3617 | ||||
3618 | if (md->sr[direction].meta_on && now - md->sr[direction].last_digit > 5) { | |||
3619 | md->sr[direction].meta_on = SWITCH_FALSE; | |||
3620 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3620, (const char*)(session), SWITCH_LOG_ERROR, "%s Meta digit timeout parsing %c\n", switch_channel_get_name(channel), | |||
3621 | dtmf->digit); | |||
3622 | return SWITCH_STATUS_SUCCESS; | |||
3623 | } | |||
3624 | ||||
3625 | md->sr[direction].last_digit = now; | |||
3626 | ||||
3627 | if (dtmf->digit == md->sr[direction].meta) { | |||
3628 | if (md->sr[direction].meta_on) { | |||
3629 | md->sr[direction].meta_on = SWITCH_FALSE; | |||
3630 | return SWITCH_STATUS_SUCCESS; | |||
3631 | } else { | |||
3632 | md->sr[direction].meta_on = SWITCH_TRUE; | |||
3633 | return SWITCH_STATUS_FALSE; | |||
3634 | } | |||
3635 | } | |||
3636 | ||||
3637 | if (md->sr[direction].meta_on) { | |||
3638 | if (is_dtmf(dtmf->digit)((dtmf->digit > 47 && dtmf->digit < 58) || (dtmf->digit > 64 && dtmf->digit < 69) || (dtmf->digit > 96 && dtmf->digit < 101) || dtmf->digit == 35 || dtmf->digit == 42 || dtmf->digit == 87 || dtmf->digit == 119 || dtmf->digit == 70 || dtmf ->digit == 102)) { | |||
3639 | int ok = 0; | |||
3640 | *digit = dtmf->digit; | |||
3641 | dval = switch_dtmftoi(digit); | |||
3642 | ||||
3643 | if (direction == SWITCH_DTMF_RECV && (md->sr[direction].map[dval].bind_flags & SBF_DIAL_ALEG)) { | |||
3644 | ok = 1; | |||
3645 | } else if (direction == SWITCH_DTMF_SEND && (md->sr[direction].map[dval].bind_flags & SBF_DIAL_BLEG)) { | |||
3646 | ok = 1; | |||
3647 | } | |||
3648 | ||||
3649 | if (ok && md->sr[direction].map[dval].app) { | |||
3650 | uint32_t flags = md->sr[direction].map[dval].flags; | |||
3651 | ||||
3652 | if ((md->sr[direction].map[dval].bind_flags & SBF_EXEC_OPPOSITE)) { | |||
3653 | if (direction == SWITCH_DTMF_SEND) { | |||
3654 | flags |= SMF_ECHO_ALEG; | |||
3655 | } else { | |||
3656 | flags |= SMF_ECHO_BLEG; | |||
3657 | } | |||
3658 | } else if ((md->sr[direction].map[dval].bind_flags & SBF_EXEC_SAME)) { | |||
3659 | if (direction == SWITCH_DTMF_SEND) { | |||
3660 | flags |= SMF_ECHO_BLEG; | |||
3661 | } else { | |||
3662 | flags |= SMF_ECHO_ALEG; | |||
3663 | } | |||
3664 | } else if ((md->sr[direction].map[dval].bind_flags & SBF_EXEC_ALEG)) { | |||
3665 | flags |= SMF_ECHO_ALEG; | |||
3666 | } else if ((md->sr[direction].map[dval].bind_flags & SBF_EXEC_BLEG)) { | |||
3667 | flags |= SMF_ECHO_BLEG; | |||
3668 | } else { | |||
3669 | flags |= SMF_ECHO_ALEG; | |||
3670 | } | |||
3671 | ||||
3672 | if ((md->sr[direction].map[dval].bind_flags & SBF_EXEC_INLINE)) { | |||
3673 | flags |= SMF_EXEC_INLINE; | |||
3674 | } | |||
3675 | ||||
3676 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3676, (const char*)(session), SWITCH_LOG_DEBUG, "%s Processing meta digit '%c' [%s]\n", | |||
3677 | switch_channel_get_name(channel), dtmf->digit, md->sr[direction].map[dval].app); | |||
3678 | ||||
3679 | if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { | |||
3680 | switch_ivr_broadcast_in_thread(session, md->sr[direction].map[dval].app, flags | SMF_REBRIDGE); | |||
3681 | } else { | |||
3682 | switch_ivr_broadcast(switch_core_session_get_uuid(session), md->sr[direction].map[dval].app, flags); | |||
3683 | } | |||
3684 | ||||
3685 | if ((md->sr[direction].map[dval].bind_flags & SBF_ONCE)) { | |||
3686 | memset(&md->sr[direction].map[dval], 0, sizeof(md->sr[direction].map[dval])); | |||
3687 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3687, (const char*)(session), SWITCH_LOG_DEBUG, "%s Unbinding meta digit '%c'\n", | |||
3688 | switch_channel_get_name(channel), dtmf->digit); | |||
3689 | } | |||
3690 | ||||
3691 | } else { | |||
3692 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3692, (const char*)(session), SWITCH_LOG_WARNING, "%s Ignoring meta digit '%c' not mapped\n", | |||
3693 | switch_channel_get_name(channel), dtmf->digit); | |||
3694 | ||||
3695 | } | |||
3696 | } | |||
3697 | md->sr[direction].meta_on = SWITCH_FALSE; | |||
3698 | return SWITCH_STATUS_FALSE; | |||
3699 | } | |||
3700 | ||||
3701 | return SWITCH_STATUS_SUCCESS; | |||
3702 | } | |||
3703 | ||||
3704 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_unbind_dtmf_meta_session(switch_core_session_t *session, uint32_t key) | |||
3705 | { | |||
3706 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3707 | ||||
3708 | if (key) { | |||
3709 | dtmf_meta_data_t *md = switch_channel_get_private(channel, SWITCH_META_VAR_KEY"__dtmf_meta"); | |||
3710 | ||||
3711 | if (!md || key > 9) { | |||
3712 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3712, (const char*)(session), SWITCH_LOG_ERROR, "Invalid key %u\n", key); | |||
3713 | return SWITCH_STATUS_FALSE; | |||
3714 | } | |||
3715 | ||||
3716 | memset(&md->sr[SWITCH_DTMF_RECV].map[key], 0, sizeof(md->sr[SWITCH_DTMF_RECV].map[key])); | |||
3717 | memset(&md->sr[SWITCH_DTMF_SEND].map[key], 0, sizeof(md->sr[SWITCH_DTMF_SEND].map[key])); | |||
3718 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3718, (const char*)(session), SWITCH_LOG_INFO, "UnBound A-Leg: %d\n", key); | |||
3719 | ||||
3720 | } else { | |||
3721 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3721, (const char*)(session), SWITCH_LOG_INFO, "UnBound A-Leg: ALL\n"); | |||
3722 | switch_channel_set_private(channel, SWITCH_META_VAR_KEY"__dtmf_meta", NULL((void*)0)); | |||
3723 | } | |||
3724 | ||||
3725 | return SWITCH_STATUS_SUCCESS; | |||
3726 | } | |||
3727 | ||||
3728 | static switch_status_t block_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) | |||
3729 | { | |||
3730 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3731 | uint8_t enabled = (uint8_t)(intptr_t)switch_channel_get_private(channel, SWITCH_BLOCK_DTMF_KEY"__dtmf_block"); | |||
3732 | ||||
3733 | if (!enabled || switch_channel_test_flag(channel, CF_INNER_BRIDGE)) { | |||
3734 | return SWITCH_STATUS_SUCCESS; | |||
3735 | } | |||
3736 | ||||
3737 | return SWITCH_STATUS_FALSE; | |||
3738 | } | |||
3739 | ||||
3740 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_unblock_dtmf_session(switch_core_session_t *session) | |||
3741 | { | |||
3742 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3743 | uint8_t enabled = (uint8_t)(intptr_t)switch_channel_get_private(channel, SWITCH_BLOCK_DTMF_KEY"__dtmf_block"); | |||
3744 | ||||
3745 | if (enabled) { | |||
3746 | switch_channel_set_private(channel, SWITCH_BLOCK_DTMF_KEY"__dtmf_block", NULL((void*)0)); | |||
3747 | } | |||
3748 | ||||
3749 | return SWITCH_STATUS_SUCCESS; | |||
3750 | } | |||
3751 | ||||
3752 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_block_dtmf_session(switch_core_session_t *session) | |||
3753 | { | |||
3754 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3755 | uint8_t enabled = (uint8_t)(intptr_t)switch_channel_get_private(channel, SWITCH_BLOCK_DTMF_KEY"__dtmf_block"); | |||
3756 | ||||
3757 | if (!enabled) { | |||
3758 | switch_channel_set_private(channel, SWITCH_BLOCK_DTMF_KEY"__dtmf_block", (void *)(intptr_t)1); | |||
3759 | switch_core_event_hook_add_send_dtmf(session, block_on_dtmf); | |||
3760 | switch_core_event_hook_add_recv_dtmf(session, block_on_dtmf); | |||
3761 | } | |||
3762 | ||||
3763 | return SWITCH_STATUS_SUCCESS; | |||
3764 | } | |||
3765 | ||||
3766 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_bind_dtmf_meta_session(switch_core_session_t *session, uint32_t key, | |||
3767 | switch_bind_flag_t bind_flags, const char *app) | |||
3768 | { | |||
3769 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3770 | dtmf_meta_data_t *md = switch_channel_get_private(channel, SWITCH_META_VAR_KEY"__dtmf_meta"); | |||
3771 | const char *meta_var = switch_channel_get_variable(channel, "bind_meta_key")switch_channel_get_variable_dup(channel, "bind_meta_key", SWITCH_TRUE , -1); | |||
3772 | char meta = '*'; | |||
3773 | char str[2] = ""; | |||
3774 | ||||
3775 | if (meta_var) { | |||
3776 | char t_meta = *meta_var; | |||
3777 | if (is_dtmf(t_meta)((t_meta > 47 && t_meta < 58) || (t_meta > 64 && t_meta < 69) || (t_meta > 96 && t_meta < 101) || t_meta == 35 || t_meta == 42 || t_meta == 87 || t_meta == 119 || t_meta == 70 || t_meta == 102)) { | |||
3778 | meta = t_meta; | |||
3779 | } else { | |||
3780 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3780, (const char*)(session), SWITCH_LOG_WARNING, "Invalid META KEY %c\n", t_meta); | |||
3781 | } | |||
3782 | } | |||
3783 | ||||
3784 | if (meta != '*' && meta != '#') { | |||
3785 | str[0] = meta; | |||
3786 | ||||
3787 | if (switch_dtmftoi(str) == (char)key) { | |||
3788 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3788, (const char*)(session), SWITCH_LOG_ERROR, "Invalid key %u, same as META CHAR\n", key); | |||
3789 | return SWITCH_STATUS_FALSE; | |||
3790 | } | |||
3791 | } | |||
3792 | ||||
3793 | ||||
3794 | if (key > 13) { | |||
3795 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3795, (const char*)(session), SWITCH_LOG_ERROR, "Invalid key %u\n", key); | |||
3796 | return SWITCH_STATUS_FALSE; | |||
3797 | } | |||
3798 | ||||
3799 | if (!md) { | |||
3800 | md = switch_core_session_alloc(session, sizeof(*md))switch_core_perform_session_alloc(session, sizeof(*md), "src/switch_ivr_async.c" , (const char *)__func__, 3800); | |||
3801 | switch_channel_set_private(channel, SWITCH_META_VAR_KEY"__dtmf_meta", md); | |||
3802 | switch_core_event_hook_add_send_dtmf(session, meta_on_dtmf); | |||
3803 | switch_core_event_hook_add_recv_dtmf(session, meta_on_dtmf); | |||
3804 | } | |||
3805 | ||||
3806 | if (!zstr(app)_zstr(app)) { | |||
3807 | if ((bind_flags & SBF_DIAL_ALEG)) { | |||
3808 | md->sr[SWITCH_DTMF_RECV].meta = meta; | |||
3809 | md->sr[SWITCH_DTMF_RECV].up = 1; | |||
3810 | md->sr[SWITCH_DTMF_RECV].map[key].app = switch_core_session_strdup(session, app)switch_core_perform_session_strdup(session, app, "src/switch_ivr_async.c" , (const char *)__func__, 3810); | |||
3811 | md->sr[SWITCH_DTMF_RECV].map[key].flags |= SMF_HOLD_BLEG; | |||
3812 | md->sr[SWITCH_DTMF_RECV].map[key].bind_flags = bind_flags; | |||
3813 | ||||
3814 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3814, (const char*)(session), SWITCH_LOG_INFO, "Bound A-Leg: %c%c %s\n", meta, switch_itodtmf((char)key), app); | |||
3815 | } | |||
3816 | if ((bind_flags & SBF_DIAL_BLEG)) { | |||
3817 | md->sr[SWITCH_DTMF_SEND].meta = meta; | |||
3818 | md->sr[SWITCH_DTMF_SEND].up = 1; | |||
3819 | md->sr[SWITCH_DTMF_SEND].map[key].app = switch_core_session_strdup(session, app)switch_core_perform_session_strdup(session, app, "src/switch_ivr_async.c" , (const char *)__func__, 3819); | |||
3820 | md->sr[SWITCH_DTMF_SEND].map[key].flags |= SMF_HOLD_BLEG; | |||
3821 | md->sr[SWITCH_DTMF_SEND].map[key].bind_flags = bind_flags; | |||
3822 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3822, (const char*)(session), SWITCH_LOG_INFO, "Bound B-Leg: %c%c %s\n", meta, switch_itodtmf((char)key), app); | |||
3823 | } | |||
3824 | ||||
3825 | } else { | |||
3826 | if ((bind_flags & SBF_DIAL_ALEG)) { | |||
3827 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3827, (const char*)(session), SWITCH_LOG_INFO, "UnBound A-Leg: %c%c\n", meta, switch_itodtmf((char)key)); | |||
3828 | md->sr[SWITCH_DTMF_SEND].map[key].app = NULL((void*)0); | |||
3829 | } else { | |||
3830 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3830, (const char*)(session), SWITCH_LOG_INFO, "UnBound: B-Leg %c%d\n", meta, key); | |||
3831 | md->sr[SWITCH_DTMF_SEND].map[key].app = NULL((void*)0); | |||
3832 | } | |||
3833 | } | |||
3834 | ||||
3835 | return SWITCH_STATUS_SUCCESS; | |||
3836 | } | |||
3837 | ||||
3838 | typedef struct { | |||
3839 | int done; | |||
3840 | char *result; | |||
3841 | } play_and_detect_speech_state_t; | |||
3842 | ||||
3843 | static switch_status_t play_and_detect_input_callback(switch_core_session_t *session, void *input, switch_input_type_t input_type, void *data, unsigned int len) | |||
3844 | { | |||
3845 | play_and_detect_speech_state_t *state = (play_and_detect_speech_state_t *)data; | |||
3846 | switch_event_t *event; | |||
3847 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3848 | if (input_type == SWITCH_INPUT_TYPE_EVENT) { | |||
3849 | event = (switch_event_t *)input; | |||
3850 | if (event->event_id == SWITCH_EVENT_DETECTED_SPEECH && !state->done) { | |||
3851 | const char *speech_type = switch_event_get_header(event, "Speech-Type")switch_event_get_header_idx(event, "Speech-Type", -1); | |||
3852 | if (!zstr(speech_type)_zstr(speech_type)) { | |||
3853 | if (!strcasecmp(speech_type, "detected-speech")) { | |||
3854 | const char *result; | |||
3855 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3855, (const char*)(session), SWITCH_LOG_INFO, "(%s) DETECTED SPEECH\n", switch_channel_get_name(channel)); | |||
3856 | result = switch_event_get_body(event); | |||
3857 | if (!zstr(result)_zstr(result)) { | |||
3858 | state->result = switch_core_session_strdup(session, result)switch_core_perform_session_strdup(session, result, "src/switch_ivr_async.c" , (const char *)__func__, 3858); | |||
3859 | } else { | |||
3860 | state->result = ""; | |||
3861 | } | |||
3862 | state->done = 1; | |||
3863 | return SWITCH_STATUS_BREAK; | |||
3864 | } else if (!strcasecmp(speech_type, "begin-speaking")) { | |||
3865 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3865, (const char*)(session), SWITCH_LOG_INFO, "(%s) START OF SPEECH\n", switch_channel_get_name(channel)); | |||
3866 | return SWITCH_STATUS_BREAK; | |||
3867 | } | |||
3868 | } | |||
3869 | } | |||
3870 | } | |||
3871 | return SWITCH_STATUS_SUCCESS; | |||
3872 | } | |||
3873 | ||||
3874 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_play_and_detect_speech(switch_core_session_t *session, | |||
3875 | const char *file, | |||
3876 | const char *mod_name, | |||
3877 | const char *grammar, | |||
3878 | char **result, | |||
3879 | uint32_t input_timeout, | |||
3880 | switch_input_args_t *args) | |||
3881 | { | |||
3882 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
3883 | int recognizing = 0; | |||
3884 | switch_input_args_t myargs = { 0 }; | |||
3885 | play_and_detect_speech_state_t state = { 0, "" }; | |||
3886 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
3887 | ||||
3888 | arg_recursion_check_start(args)if (args) { if (args->loops >= 25) { switch_log_printf( SWITCH_CHANNEL_ID_LOG, "src/switch_ivr_async.c", (const char * )__func__, 3888, ((void*)0), SWITCH_LOG_ERROR, "RECURSION ERROR! It's not the best idea to call things that collect input recursively from an input callback.\n" ); return SWITCH_STATUS_GENERR; } else {args->loops++;} }; | |||
3889 | ||||
3890 | if (result == NULL((void*)0)) { | |||
| ||||
3891 | goto done; | |||
3892 | } | |||
3893 | ||||
3894 | if (!input_timeout) input_timeout = 5000; | |||
3895 | ||||
3896 | if (!args) { | |||
3897 | args = &myargs; | |||
3898 | } | |||
3899 | ||||
3900 | /* start speech detection */ | |||
3901 | if (switch_ivr_detect_speech(session, mod_name, grammar, "", NULL((void*)0), NULL((void*)0)) != SWITCH_STATUS_SUCCESS) { | |||
3902 | goto done; | |||
3903 | } | |||
3904 | recognizing = 1; | |||
3905 | ||||
3906 | /* play the prompt, looking for detection result */ | |||
3907 | args->input_callback = play_and_detect_input_callback; | |||
3908 | args->buf = &state; | |||
3909 | args->buflen = sizeof(state); | |||
3910 | status = switch_ivr_play_file(session, NULL((void*)0), file, args); | |||
3911 | ||||
3912 | if (args->dmachine && switch_ivr_dmachine_last_ping(args->dmachine) != SWITCH_STATUS_SUCCESS) { | |||
3913 | state.done = 1; | |||
3914 | goto done; | |||
3915 | } | |||
3916 | ||||
3917 | if (status != SWITCH_STATUS_BREAK && status != SWITCH_STATUS_SUCCESS) { | |||
3918 | goto done; | |||
3919 | } | |||
3920 | ||||
3921 | /* wait for result if not done */ | |||
3922 | if (!state.done) { | |||
3923 | switch_ivr_detect_speech_start_input_timers(session); | |||
3924 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 3924, (const char*)(session), SWITCH_LOG_INFO, "(%s) WAITING FOR RESULT\n", switch_channel_get_name(channel)); | |||
3925 | while (!state.done && switch_channel_ready(channel)switch_channel_test_ready(channel, SWITCH_TRUE, SWITCH_FALSE)) { | |||
3926 | status = switch_ivr_sleep(session, input_timeout, SWITCH_FALSE, args); | |||
3927 | ||||
3928 | if (args->dmachine && switch_ivr_dmachine_last_ping(args->dmachine) != SWITCH_STATUS_SUCCESS) { | |||
3929 | state.done = 1; | |||
3930 | goto done; | |||
3931 | } | |||
3932 | ||||
3933 | if (status != SWITCH_STATUS_BREAK && status != SWITCH_STATUS_SUCCESS) { | |||
3934 | goto done; | |||
3935 | } | |||
3936 | } | |||
3937 | } | |||
3938 | recognizing = !state.done; | |||
3939 | ||||
3940 | done: | |||
3941 | if (recognizing) { | |||
3942 | switch_ivr_pause_detect_speech(session); | |||
3943 | } | |||
3944 | ||||
3945 | *result = state.result; | |||
| ||||
3946 | ||||
3947 | if (!state.done) { | |||
3948 | status = SWITCH_STATUS_FALSE; | |||
3949 | } | |||
3950 | ||||
3951 | arg_recursion_check_stop(args)if (args) args->loops--; | |||
3952 | ||||
3953 | return status;; | |||
3954 | } | |||
3955 | ||||
3956 | struct speech_thread_handle { | |||
3957 | switch_core_session_t *session; | |||
3958 | switch_asr_handle_t *ah; | |||
3959 | switch_media_bug_t *bug; | |||
3960 | switch_mutex_t *mutex; | |||
3961 | switch_thread_cond_t *cond; | |||
3962 | switch_memory_pool_t *pool; | |||
3963 | int ready; | |||
3964 | }; | |||
3965 | ||||
3966 | static void *SWITCH_THREAD_FUNC speech_thread(switch_thread_t *thread, void *obj) | |||
3967 | { | |||
3968 | struct speech_thread_handle *sth = (struct speech_thread_handle *) obj; | |||
3969 | switch_channel_t *channel = switch_core_session_get_channel(sth->session); | |||
3970 | switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; | |||
3971 | switch_status_t status; | |||
3972 | switch_event_t *event; | |||
3973 | ||||
3974 | switch_thread_cond_create(&sth->cond, sth->pool); | |||
3975 | switch_mutex_init(&sth->mutex, SWITCH_MUTEX_NESTED0x1, sth->pool); | |||
3976 | ||||
3977 | if (switch_core_session_read_lock(sth->session) != SWITCH_STATUS_SUCCESS) { | |||
3978 | sth->ready = 0; | |||
3979 | return NULL((void*)0); | |||
3980 | } | |||
3981 | ||||
3982 | switch_mutex_lock(sth->mutex); | |||
3983 | ||||
3984 | sth->ready = 1; | |||
3985 | ||||
3986 | while (switch_channel_up_nosig(channel)(switch_channel_get_state(channel) < CS_HANGUP) && !switch_test_flag(sth->ah, SWITCH_ASR_FLAG_CLOSED)((sth->ah)->flags & SWITCH_ASR_FLAG_CLOSED)) { | |||
3987 | char *xmlstr = NULL((void*)0); | |||
3988 | switch_event_t *headers = NULL((void*)0); | |||
3989 | ||||
3990 | switch_thread_cond_wait(sth->cond, sth->mutex); | |||
3991 | ||||
3992 | if (switch_channel_down_nosig(channel)(switch_channel_get_state(channel) >= CS_HANGUP) || switch_test_flag(sth->ah, SWITCH_ASR_FLAG_CLOSED)((sth->ah)->flags & SWITCH_ASR_FLAG_CLOSED)) { | |||
3993 | break; | |||
3994 | } | |||
3995 | ||||
3996 | if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { | |||
3997 | ||||
3998 | status = switch_core_asr_get_results(sth->ah, &xmlstr, &flags); | |||
3999 | ||||
4000 | if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { | |||
4001 | goto done; | |||
4002 | } else if (status == SWITCH_STATUS_SUCCESS) { | |||
4003 | /* Try to fetch extra information for this result, the return value doesn't really matter here - it's just optional data. */ | |||
4004 | switch_core_asr_get_result_headers(sth->ah, &headers, &flags); | |||
4005 | } | |||
4006 | ||||
4007 | if (status == SWITCH_STATUS_SUCCESS && switch_true(switch_channel_get_variable(channel, "asr_intercept_dtmf")switch_channel_get_variable_dup(channel, "asr_intercept_dtmf" , SWITCH_TRUE, -1))) { | |||
4008 | const char *p; | |||
4009 | ||||
4010 | if ((p = switch_stristr("<input>", xmlstr))) { | |||
4011 | p += 7; | |||
4012 | } | |||
4013 | ||||
4014 | while (p && *p) { | |||
4015 | char c; | |||
4016 | ||||
4017 | if (*p == '<') { | |||
4018 | break; | |||
4019 | } | |||
4020 | ||||
4021 | if (!strncasecmp(p, "pound", 5)) { | |||
4022 | c = '#'; | |||
4023 | p += 5; | |||
4024 | } else if (!strncasecmp(p, "hash", 4)) { | |||
4025 | c = '#'; | |||
4026 | p += 4; | |||
4027 | } else if (!strncasecmp(p, "star", 4)) { | |||
4028 | c = '*'; | |||
4029 | p += 4; | |||
4030 | } else if (!strncasecmp(p, "asterisk", 8)) { | |||
4031 | c = '*'; | |||
4032 | p += 8; | |||
4033 | } else { | |||
4034 | c = *p; | |||
4035 | p++; | |||
4036 | } | |||
4037 | ||||
4038 | if (is_dtmf(c)((c > 47 && c < 58) || (c > 64 && c < 69) || (c > 96 && c < 101) || c == 35 || c == 42 || c == 87 || c == 119 || c == 70 || c == 102)) { | |||
4039 | switch_dtmf_t dtmf = {0}; | |||
4040 | dtmf.digit = c; | |||
4041 | dtmf.duration = switch_core_default_dtmf_duration(0); | |||
4042 | dtmf.source = SWITCH_DTMF_INBAND_AUDIO; | |||
4043 | switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4043, (const char*)switch_channel_get_session(channel ), SWITCH_LOG_DEBUG, "Queue speech detected dtmf %c\n", c); | |||
4044 | switch_channel_queue_dtmf(channel, &dtmf); | |||
4045 | } | |||
4046 | ||||
4047 | } | |||
4048 | switch_ivr_resume_detect_speech(sth->session); | |||
4049 | } | |||
4050 | ||||
4051 | if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 4051, &event, SWITCH_EVENT_DETECTED_SPEECH , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
4052 | if (status == SWITCH_STATUS_SUCCESS) { | |||
4053 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "detected-speech"); | |||
4054 | ||||
4055 | if (headers) { | |||
4056 | switch_event_merge(event, headers); | |||
4057 | } | |||
4058 | ||||
4059 | switch_event_add_body(event, "%s", xmlstr); | |||
4060 | } else { | |||
4061 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "begin-speaking"); | |||
4062 | } | |||
4063 | ||||
4064 | if (switch_test_flag(sth->ah, SWITCH_ASR_FLAG_FIRE_EVENTS)((sth->ah)->flags & SWITCH_ASR_FLAG_FIRE_EVENTS)) { | |||
4065 | switch_event_t *dup; | |||
4066 | ||||
4067 | if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { | |||
4068 | switch_channel_event_set_data(channel, dup); | |||
4069 | switch_event_fire(&dup)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 4069, &dup, ((void*)0)); | |||
4070 | } | |||
4071 | ||||
4072 | } | |||
4073 | ||||
4074 | if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) { | |||
4075 | switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4075, (const char*)switch_channel_get_session(channel ), SWITCH_LOG_ERROR, "Event queue failed!\n"); | |||
4076 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); | |||
4077 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 4077, &event, ((void*)0)); | |||
4078 | } | |||
4079 | } | |||
4080 | ||||
4081 | switch_safe_free(xmlstr)if (xmlstr) {free(xmlstr);xmlstr=((void*)0);}; | |||
4082 | ||||
4083 | if (headers) { | |||
4084 | switch_event_destroy(&headers); | |||
4085 | } | |||
4086 | } | |||
4087 | } | |||
4088 | done: | |||
4089 | ||||
4090 | if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 4090, &event, SWITCH_EVENT_DETECTED_SPEECH , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
4091 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "closed"); | |||
4092 | if (switch_test_flag(sth->ah, SWITCH_ASR_FLAG_FIRE_EVENTS)((sth->ah)->flags & SWITCH_ASR_FLAG_FIRE_EVENTS)) { | |||
4093 | switch_event_t *dup; | |||
4094 | ||||
4095 | if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { | |||
4096 | switch_channel_event_set_data(channel, dup); | |||
4097 | switch_event_fire(&dup)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 4097, &dup, ((void*)0)); | |||
4098 | } | |||
4099 | ||||
4100 | } | |||
4101 | ||||
4102 | if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) { | |||
4103 | switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4103, (const char*)switch_channel_get_session(channel ), SWITCH_LOG_ERROR, "Event queue failed!\n"); | |||
4104 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); | |||
4105 | switch_event_fire(&event)switch_event_fire_detailed("src/switch_ivr_async.c", (const char * )(const char *)__func__, 4105, &event, ((void*)0)); | |||
4106 | } | |||
4107 | } | |||
4108 | ||||
4109 | switch_mutex_unlock(sth->mutex); | |||
4110 | switch_core_session_rwunlock(sth->session); | |||
4111 | ||||
4112 | return NULL((void*)0); | |||
4113 | } | |||
4114 | ||||
4115 | static switch_bool_t speech_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) | |||
4116 | { | |||
4117 | struct speech_thread_handle *sth = (struct speech_thread_handle *) user_data; | |||
4118 | uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
4119 | switch_frame_t frame = { 0 }; | |||
4120 | switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; | |||
4121 | ||||
4122 | frame.data = data; | |||
4123 | frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE8192; | |||
4124 | ||||
4125 | switch (type) { | |||
4126 | case SWITCH_ABC_TYPE_INIT:{ | |||
4127 | switch_thread_t *thread; | |||
4128 | switch_threadattr_t *thd_attr = NULL((void*)0); | |||
4129 | ||||
4130 | switch_threadattr_create(&thd_attr, sth->pool); | |||
4131 | switch_threadattr_detach_set(thd_attr, 1); | |||
4132 | switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE240 * 1024); | |||
4133 | switch_thread_create(&thread, thd_attr, speech_thread, sth, sth->pool); | |||
4134 | } | |||
4135 | break; | |||
4136 | case SWITCH_ABC_TYPE_CLOSE:{ | |||
4137 | switch_core_asr_close(sth->ah, &flags); | |||
4138 | if (sth->mutex && sth->cond && sth->ready) { | |||
4139 | switch_mutex_lock(sth->mutex); | |||
4140 | switch_thread_cond_signal(sth->cond); | |||
4141 | switch_mutex_unlock(sth->mutex); | |||
4142 | } | |||
4143 | } | |||
4144 | break; | |||
4145 | case SWITCH_ABC_TYPE_READ: | |||
4146 | if (sth->ah) { | |||
4147 | if (switch_core_media_bug_read(bug, &frame, SWITCH_FALSE) != SWITCH_STATUS_FALSE) { | |||
4148 | if (switch_core_asr_feed(sth->ah, frame.data, frame.datalen, &flags) != SWITCH_STATUS_SUCCESS) { | |||
4149 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug))SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4149, (const char*)(switch_core_media_bug_get_session (bug)), SWITCH_LOG_DEBUG, "Error Feeding Data\n"); | |||
4150 | return SWITCH_FALSE; | |||
4151 | } | |||
4152 | if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { | |||
4153 | if (sth->mutex && sth->cond && sth->ready) { | |||
4154 | switch_mutex_lock(sth->mutex); | |||
4155 | switch_thread_cond_signal(sth->cond); | |||
4156 | switch_mutex_unlock(sth->mutex); | |||
4157 | } | |||
4158 | } | |||
4159 | } | |||
4160 | } | |||
4161 | break; | |||
4162 | case SWITCH_ABC_TYPE_WRITE: | |||
4163 | default: | |||
4164 | break; | |||
4165 | } | |||
4166 | ||||
4167 | return SWITCH_TRUE; | |||
4168 | } | |||
4169 | ||||
4170 | static switch_status_t speech_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) | |||
4171 | { | |||
4172 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4173 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4174 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
4175 | switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; | |||
4176 | ||||
4177 | if (sth) { | |||
4178 | if (switch_core_asr_feed_dtmf(sth->ah, dtmf, &flags) != SWITCH_STATUS_SUCCESS) { | |||
4179 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4179, (const char*)(session), SWITCH_LOG_ERROR, "Error Feeding DTMF\n"); | |||
4180 | } | |||
4181 | } | |||
4182 | ||||
4183 | return status; | |||
4184 | } | |||
4185 | ||||
4186 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_stop_detect_speech(switch_core_session_t *session) | |||
4187 | { | |||
4188 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4189 | struct speech_thread_handle *sth; | |||
4190 | ||||
4191 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "src/switch_ivr_async.c", 4191, __PRETTY_FUNCTION__)); | |||
4192 | if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"))) { | |||
4193 | switch_channel_set_private(channel, SWITCH_SPEECH_KEY"speech", NULL((void*)0)); | |||
4194 | switch_core_event_hook_remove_recv_dtmf(session, speech_on_dtmf); | |||
4195 | switch_core_media_bug_remove(session, &sth->bug); | |||
4196 | return SWITCH_STATUS_SUCCESS; | |||
4197 | } | |||
4198 | ||||
4199 | return SWITCH_STATUS_FALSE; | |||
4200 | } | |||
4201 | ||||
4202 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_pause_detect_speech(switch_core_session_t *session) | |||
4203 | { | |||
4204 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4205 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4206 | ||||
4207 | if (sth) { | |||
4208 | switch_core_asr_pause(sth->ah); | |||
4209 | return SWITCH_STATUS_SUCCESS; | |||
4210 | } | |||
4211 | return SWITCH_STATUS_FALSE; | |||
4212 | } | |||
4213 | ||||
4214 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_resume_detect_speech(switch_core_session_t *session) | |||
4215 | { | |||
4216 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4217 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4218 | ||||
4219 | if (sth) { | |||
4220 | switch_core_asr_resume(sth->ah); | |||
4221 | return SWITCH_STATUS_SUCCESS; | |||
4222 | } | |||
4223 | return SWITCH_STATUS_FALSE; | |||
4224 | } | |||
4225 | ||||
4226 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, const char *grammar, const char *name) | |||
4227 | { | |||
4228 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4229 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4230 | switch_status_t status; | |||
4231 | ||||
4232 | if (sth) { | |||
4233 | if ((status = switch_core_asr_load_grammar(sth->ah, grammar, name)) != SWITCH_STATUS_SUCCESS) { | |||
4234 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4234, (const char*)(session), SWITCH_LOG_DEBUG, "Error loading Grammar\n"); | |||
4235 | switch_ivr_stop_detect_speech(session); | |||
4236 | } | |||
4237 | return status; | |||
4238 | } | |||
4239 | return SWITCH_STATUS_FALSE; | |||
4240 | } | |||
4241 | ||||
4242 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_set_param_detect_speech(switch_core_session_t *session, const char *name, const char *val) | |||
4243 | { | |||
4244 | struct speech_thread_handle *sth = switch_channel_get_private(switch_core_session_get_channel(session), SWITCH_SPEECH_KEY"speech"); | |||
4245 | switch_status_t status = SWITCH_STATUS_FALSE; | |||
4246 | ||||
4247 | if (sth && sth->ah && name && val) { | |||
4248 | switch_core_asr_text_param(sth->ah, (char *) name, val); | |||
4249 | status = SWITCH_STATUS_SUCCESS; | |||
4250 | } | |||
4251 | ||||
4252 | return status; | |||
4253 | } | |||
4254 | ||||
4255 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_start_input_timers(switch_core_session_t *session) | |||
4256 | { | |||
4257 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4258 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4259 | ||||
4260 | if (sth) { | |||
4261 | switch_core_asr_start_input_timers(sth->ah); | |||
4262 | return SWITCH_STATUS_SUCCESS; | |||
4263 | } | |||
4264 | return SWITCH_STATUS_FALSE; | |||
4265 | } | |||
4266 | ||||
4267 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, const char *name) | |||
4268 | { | |||
4269 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4270 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4271 | switch_status_t status; | |||
4272 | ||||
4273 | if (sth) { | |||
4274 | if ((status = switch_core_asr_unload_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { | |||
4275 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4275, (const char*)(session), SWITCH_LOG_DEBUG, "Error unloading Grammar\n"); | |||
4276 | switch_ivr_stop_detect_speech(session); | |||
4277 | } | |||
4278 | return status; | |||
4279 | } | |||
4280 | return SWITCH_STATUS_FALSE; | |||
4281 | } | |||
4282 | ||||
4283 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_enable_grammar(switch_core_session_t *session, const char *name) | |||
4284 | { | |||
4285 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4286 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4287 | switch_status_t status; | |||
4288 | ||||
4289 | if (sth) { | |||
4290 | if ((status = switch_core_asr_enable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { | |||
4291 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4291, (const char*)(session), SWITCH_LOG_DEBUG, "Error enabling Grammar\n"); | |||
4292 | switch_ivr_stop_detect_speech(session); | |||
4293 | } | |||
4294 | return status; | |||
4295 | } | |||
4296 | return SWITCH_STATUS_FALSE; | |||
4297 | } | |||
4298 | ||||
4299 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_disable_grammar(switch_core_session_t *session, const char *name) | |||
4300 | { | |||
4301 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4302 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4303 | switch_status_t status; | |||
4304 | ||||
4305 | if (sth) { | |||
4306 | if ((status = switch_core_asr_disable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { | |||
4307 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4307, (const char*)(session), SWITCH_LOG_DEBUG, "Error disabling Grammar\n"); | |||
4308 | switch_ivr_stop_detect_speech(session); | |||
4309 | } | |||
4310 | return status; | |||
4311 | } | |||
4312 | return SWITCH_STATUS_FALSE; | |||
4313 | } | |||
4314 | ||||
4315 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_disable_all_grammars(switch_core_session_t *session) | |||
4316 | { | |||
4317 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4318 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4319 | switch_status_t status; | |||
4320 | ||||
4321 | if (sth) { | |||
4322 | if ((status = switch_core_asr_disable_all_grammars(sth->ah)) != SWITCH_STATUS_SUCCESS) { | |||
4323 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4323, (const char*)(session), SWITCH_LOG_DEBUG, "Error disabling all Grammars\n"); | |||
4324 | switch_ivr_stop_detect_speech(session); | |||
4325 | } | |||
4326 | return status; | |||
4327 | } | |||
4328 | return SWITCH_STATUS_FALSE; | |||
4329 | } | |||
4330 | ||||
4331 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech_init(switch_core_session_t *session, const char *mod_name, | |||
4332 | const char *dest, switch_asr_handle_t *ah) | |||
4333 | { | |||
4334 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4335 | switch_status_t status; | |||
4336 | switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; | |||
4337 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4338 | switch_codec_implementation_t read_impl = { 0 }; | |||
4339 | const char *p; | |||
4340 | char key[512] = ""; | |||
4341 | ||||
4342 | if (sth) { | |||
4343 | /* Already initialized */ | |||
4344 | return SWITCH_STATUS_SUCCESS; | |||
4345 | } | |||
4346 | ||||
4347 | if (!ah) { | |||
4348 | if (!(ah = switch_core_session_alloc(session, sizeof(*ah))switch_core_perform_session_alloc(session, sizeof(*ah), "src/switch_ivr_async.c" , (const char *)__func__, 4348))) { | |||
4349 | return SWITCH_STATUS_MEMERR; | |||
4350 | } | |||
4351 | } | |||
4352 | ||||
4353 | switch_core_session_get_read_impl(session, &read_impl); | |||
4354 | ||||
4355 | if ((status = switch_core_asr_open(ah, | |||
4356 | mod_name, | |||
4357 | "L16", | |||
4358 | read_impl.actual_samples_per_second, dest, &flags, | |||
4359 | switch_core_session_get_pool(session))) != SWITCH_STATUS_SUCCESS) { | |||
4360 | return status; | |||
4361 | } | |||
4362 | ||||
4363 | sth = switch_core_session_alloc(session, sizeof(*sth))switch_core_perform_session_alloc(session, sizeof(*sth), "src/switch_ivr_async.c" , (const char *)__func__, 4363); | |||
4364 | sth->pool = switch_core_session_get_pool(session); | |||
4365 | sth->session = session; | |||
4366 | sth->ah = ah; | |||
4367 | ||||
4368 | if ((p = switch_channel_get_variable(channel, "fire_asr_events")switch_channel_get_variable_dup(channel, "fire_asr_events", SWITCH_TRUE , -1)) && switch_true(p)) { | |||
4369 | switch_set_flag(ah, SWITCH_ASR_FLAG_FIRE_EVENTS)(ah)->flags |= (SWITCH_ASR_FLAG_FIRE_EVENTS); | |||
4370 | } | |||
4371 | ||||
4372 | switch_snprintf(key, sizeof(key), "%s/%s/%s/%s", mod_name, NULL((void*)0), NULL((void*)0), dest); | |||
4373 | ||||
4374 | if ((status = switch_core_media_bug_add(session, "detect_speech", key, | |||
4375 | speech_callback, sth, 0, SMBF_READ_STREAM | SMBF_NO_PAUSE, &sth->bug)) != SWITCH_STATUS_SUCCESS) { | |||
4376 | switch_core_asr_close(ah, &flags); | |||
4377 | return status; | |||
4378 | } | |||
4379 | ||||
4380 | if ((status = switch_core_event_hook_add_recv_dtmf(session, speech_on_dtmf)) != SWITCH_STATUS_SUCCESS) { | |||
4381 | switch_ivr_stop_detect_speech(session); | |||
4382 | return status; | |||
4383 | } | |||
4384 | ||||
4385 | switch_channel_set_private(channel, SWITCH_SPEECH_KEY"speech", sth); | |||
4386 | ||||
4387 | return SWITCH_STATUS_SUCCESS; | |||
4388 | } | |||
4389 | ||||
4390 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_detect_speech(switch_core_session_t *session, | |||
4391 | const char *mod_name, | |||
4392 | const char *grammar, const char *name, const char *dest, switch_asr_handle_t *ah) | |||
4393 | { | |||
4394 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4395 | switch_status_t status; | |||
4396 | struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"); | |||
4397 | const char *p; | |||
4398 | ||||
4399 | if (!sth) { | |||
4400 | /* No speech thread handle available yet, init speech detection first. */ | |||
4401 | if ((status = switch_ivr_detect_speech_init(session, mod_name, dest, ah)) != SWITCH_STATUS_SUCCESS) { | |||
4402 | return status; | |||
4403 | } | |||
4404 | ||||
4405 | /* Fetch the new speech thread handle */ | |||
4406 | if (!(sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY"speech"))) { | |||
4407 | return SWITCH_STATUS_FALSE; | |||
4408 | } | |||
4409 | } | |||
4410 | ||||
4411 | if (switch_core_asr_load_grammar(sth->ah, grammar, name) != SWITCH_STATUS_SUCCESS) { | |||
4412 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4412, (const char*)(session), SWITCH_LOG_DEBUG, "Error loading Grammar\n"); | |||
4413 | switch_ivr_stop_detect_speech(session); | |||
4414 | return SWITCH_STATUS_FALSE; | |||
4415 | } | |||
4416 | ||||
4417 | if ((p = switch_channel_get_variable(channel, "fire_asr_events")switch_channel_get_variable_dup(channel, "fire_asr_events", SWITCH_TRUE , -1)) && switch_true(p)) { | |||
4418 | switch_set_flag(sth->ah, SWITCH_ASR_FLAG_FIRE_EVENTS)(sth->ah)->flags |= (SWITCH_ASR_FLAG_FIRE_EVENTS); | |||
4419 | } | |||
4420 | ||||
4421 | return SWITCH_STATUS_SUCCESS; | |||
4422 | } | |||
4423 | ||||
4424 | struct hangup_helper { | |||
4425 | char uuid_str[SWITCH_UUID_FORMATTED_LENGTH256 + 1]; | |||
4426 | switch_bool_t bleg; | |||
4427 | switch_call_cause_t cause; | |||
4428 | }; | |||
4429 | ||||
4430 | SWITCH_STANDARD_SCHED_FUNC(sch_hangup_callback)static void sch_hangup_callback (switch_scheduler_task_t *task ) | |||
4431 | { | |||
4432 | struct hangup_helper *helper; | |||
4433 | switch_core_session_t *session, *other_session; | |||
4434 | const char *other_uuid; | |||
4435 | ||||
4436 | switch_assert(task)((task) ? (void) (0) : __assert_fail ("task", "src/switch_ivr_async.c" , 4436, __PRETTY_FUNCTION__)); | |||
4437 | ||||
4438 | helper = (struct hangup_helper *) task->cmd_arg; | |||
4439 | ||||
4440 | if ((session = switch_core_session_locate(helper->uuid_str)switch_core_session_perform_locate(helper->uuid_str, "src/switch_ivr_async.c" , (const char *)__func__, 4440))) { | |||
4441 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
4442 | ||||
4443 | if (helper->bleg) { | |||
4444 | if ((other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)switch_channel_get_variable_dup(channel, "bridge_to", SWITCH_TRUE , -1)) && (other_session = switch_core_session_locate(other_uuid)switch_core_session_perform_locate(other_uuid, "src/switch_ivr_async.c" , (const char *)__func__, 4444))) { | |||
4445 | switch_channel_t *other_channel = switch_core_session_get_channel(other_session); | |||
4446 | switch_channel_hangup(other_channel, helper->cause)switch_channel_perform_hangup(other_channel, "src/switch_ivr_async.c" , (const char *)__func__, 4446, helper->cause); | |||
4447 | switch_core_session_rwunlock(other_session); | |||
4448 | } else { | |||
4449 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "src/switch_ivr_async.c", (const char *)__func__, 4449, (const char*)(session), SWITCH_LOG_WARNING, "No channel to hangup\n"); | |||
4450 | } | |||
4451 | } else { | |||
4452 | switch_channel_hangup(channel, helper->cause)switch_channel_perform_hangup(channel, "src/switch_ivr_async.c" , (const char *)__func__, 4452, helper->cause); | |||
4453 | } | |||
4454 | ||||
4455 | switch_core_session_rwunlock(session); | |||
4456 | } | |||
4457 | } | |||
4458 | ||||
4459 | SWITCH_DECLARE(uint32_t)__attribute__((visibility("default"))) uint32_t switch_ivr_schedule_hangup(time_t runtime, const char *uuid, switch_call_cause_t cause, switch_bool_t bleg) | |||
4460 | { | |||
4461 | struct hangup_helper *helper; | |||
4462 | size_t len = sizeof(*helper); | |||
4463 | ||||
4464 | switch_zmalloc(helper, len)(void)((((helper = calloc(1, (len)))) ? (void) (0) : __assert_fail ("(helper = calloc(1, (len)))", "src/switch_ivr_async.c", 4464 , __PRETTY_FUNCTION__)),helper); | |||
4465 | ||||
4466 | switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); | |||
4467 | helper->cause = cause; | |||
4468 | helper->bleg = bleg; | |||
4469 | ||||
4470 | return switch_scheduler_add_task(runtime, sch_hangup_callback, (char *) __SWITCH_FUNC__(const char *)__func__, uuid, 0, helper, SSHF_FREE_ARG); | |||
4471 | } | |||
4472 | ||||
4473 | struct transfer_helper { | |||
4474 | char uuid_str[SWITCH_UUID_FORMATTED_LENGTH256 + 1]; | |||
4475 | char *extension; | |||
4476 | char *dialplan; | |||
4477 | char *context; | |||
4478 | }; | |||
4479 | ||||
4480 | SWITCH_STANDARD_SCHED_FUNC(sch_transfer_callback)static void sch_transfer_callback (switch_scheduler_task_t *task ) | |||
4481 | { | |||
4482 | struct transfer_helper *helper; | |||
4483 | switch_core_session_t *session; | |||
4484 | ||||
4485 | switch_assert(task)((task) ? (void) (0) : __assert_fail ("task", "src/switch_ivr_async.c" , 4485, __PRETTY_FUNCTION__)); | |||
4486 | ||||
4487 | helper = (struct transfer_helper *) task->cmd_arg; | |||
4488 | ||||
4489 | if ((session = switch_core_session_locate(helper->uuid_str)switch_core_session_perform_locate(helper->uuid_str, "src/switch_ivr_async.c" , (const char *)__func__, 4489))) { | |||
4490 | switch_ivr_session_transfer(session, helper->extension, helper->dialplan, helper->context); | |||
4491 | switch_core_session_rwunlock(session); | |||
4492 | } | |||
4493 | ||||
4494 | } | |||
4495 | ||||
4496 | SWITCH_DECLARE(uint32_t)__attribute__((visibility("default"))) uint32_t switch_ivr_schedule_transfer(time_t runtime, const char *uuid, char *extension, char *dialplan, char *context) | |||
4497 | { | |||
4498 | struct transfer_helper *helper; | |||
4499 | size_t len = sizeof(*helper); | |||
4500 | char *cur = NULL((void*)0); | |||
4501 | ||||
4502 | if (extension) { | |||
4503 | len += strlen(extension) + 1; | |||
4504 | } | |||
4505 | ||||
4506 | if (dialplan) { | |||
4507 | len += strlen(dialplan) + 1; | |||
4508 | } | |||
4509 | ||||
4510 | if (context) { | |||
4511 | len += strlen(context) + 1; | |||
4512 | } | |||
4513 | ||||
4514 | switch_zmalloc(cur, len)(void)((((cur = calloc(1, (len)))) ? (void) (0) : __assert_fail ("(cur = calloc(1, (len)))", "src/switch_ivr_async.c", 4514, __PRETTY_FUNCTION__)),cur); | |||
4515 | helper = (struct transfer_helper *) cur; | |||
4516 | ||||
4517 | switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); | |||
4518 | ||||
4519 | cur += sizeof(*helper); | |||
4520 | ||||
4521 | if (extension) { | |||
4522 | switch_copy_string(cur, extension, strlen(extension) + 1); | |||
4523 | helper->extension = cur; | |||
4524 | cur += strlen(helper->extension) + 1; | |||
4525 | } | |||
4526 | ||||
4527 | if (dialplan) { | |||
4528 | switch_copy_string(cur, dialplan, strlen(dialplan) + 1); | |||
4529 | helper->dialplan = cur; | |||
4530 | cur += strlen(helper->dialplan) + 1; | |||
4531 | } | |||
4532 | ||||
4533 | if (context) { | |||
4534 | switch_copy_string(cur, context, strlen(context) + 1); | |||
4535 | helper->context = cur; | |||
4536 | } | |||
4537 | ||||
4538 | return switch_scheduler_add_task(runtime, sch_transfer_callback, (char *) __SWITCH_FUNC__(const char *)__func__, uuid, 0, helper, SSHF_FREE_ARG); | |||
4539 | } | |||
4540 | ||||
4541 | struct broadcast_helper { | |||
4542 | char uuid_str[SWITCH_UUID_FORMATTED_LENGTH256 + 1]; | |||
4543 | char *path; | |||
4544 | switch_media_flag_t flags; | |||
4545 | }; | |||
4546 | ||||
4547 | SWITCH_STANDARD_SCHED_FUNC(sch_broadcast_callback)static void sch_broadcast_callback (switch_scheduler_task_t * task) | |||
4548 | { | |||
4549 | struct broadcast_helper *helper; | |||
4550 | switch_assert(task)((task) ? (void) (0) : __assert_fail ("task", "src/switch_ivr_async.c" , 4550, __PRETTY_FUNCTION__)); | |||
4551 | ||||
4552 | helper = (struct broadcast_helper *) task->cmd_arg; | |||
4553 | switch_ivr_broadcast(helper->uuid_str, helper->path, helper->flags); | |||
4554 | } | |||
4555 | ||||
4556 | SWITCH_DECLARE(uint32_t)__attribute__((visibility("default"))) uint32_t switch_ivr_schedule_broadcast(time_t runtime, const char *uuid, const char *path, switch_media_flag_t flags) | |||
4557 | { | |||
4558 | struct broadcast_helper *helper; | |||
4559 | size_t len = sizeof(*helper) + strlen(path) + 1; | |||
4560 | char *cur = NULL((void*)0); | |||
4561 | ||||
4562 | switch_zmalloc(cur, len)(void)((((cur = calloc(1, (len)))) ? (void) (0) : __assert_fail ("(cur = calloc(1, (len)))", "src/switch_ivr_async.c", 4562, __PRETTY_FUNCTION__)),cur); | |||
4563 | helper = (struct broadcast_helper *) cur; | |||
4564 | ||||
4565 | cur += sizeof(*helper); | |||
4566 | switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); | |||
4567 | helper->flags = flags; | |||
4568 | ||||
4569 | switch_copy_string(cur, path, len - sizeof(helper)); | |||
4570 | helper->path = cur; | |||
4571 | ||||
4572 | return switch_scheduler_add_task(runtime, sch_broadcast_callback, (char *) __SWITCH_FUNC__(const char *)__func__, uuid, 0, helper, SSHF_FREE_ARG); | |||
4573 | } | |||
4574 | ||||
4575 | SWITCH_DECLARE(switch_status_t)__attribute__((visibility("default"))) switch_status_t switch_ivr_broadcast(const char *uuid, const char *path, switch_media_flag_t flags) | |||
4576 | { | |||
4577 | switch_channel_t *channel; | |||
4578 | switch_core_session_t *session, *master; | |||
4579 | switch_event_t *event; | |||
4580 | switch_core_session_t *other_session = NULL((void*)0); | |||
4581 | const char *other_uuid = NULL((void*)0); | |||
4582 | char *app = "playback"; | |||
4583 | char *cause = NULL((void*)0); | |||
4584 | char *mypath; | |||
4585 | char *p; | |||
4586 | int app_flags = 0, nomedia = 0; | |||
4587 | ||||
4588 | switch_assert(path)((path) ? (void) (0) : __assert_fail ("path", "src/switch_ivr_async.c" , 4588, __PRETTY_FUNCTION__)); | |||
4589 | ||||
4590 | if (!(master = session = switch_core_session_locate(uuid)switch_core_session_perform_locate(uuid, "src/switch_ivr_async.c" , (const char *)__func__, 4590))) { | |||
4591 | return SWITCH_STATUS_FALSE; | |||
4592 | } | |||
4593 | ||||
4594 | channel = switch_core_session_get_channel(session); | |||
4595 | ||||
4596 | mypath = strdup(path)(__extension__ (__builtin_constant_p (path) && ((size_t )(const void *)((path) + 1) - (size_t)(const void *)(path) == 1) ? (((const char *) (path))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (path) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, path, __len) ; __retval; })) : __strdup (path))); | |||
4597 | assert(mypath)((mypath) ? (void) (0) : __assert_fail ("mypath", "src/switch_ivr_async.c" , 4597, __PRETTY_FUNCTION__)); | |||
4598 | ||||
4599 | if ((p = strchr(mypath, ':')(__extension__ (__builtin_constant_p (':') && !__builtin_constant_p (mypath) && (':') == '\0' ? (char *) __rawmemchr (mypath , ':') : __builtin_strchr (mypath, ':')))) && *(p + 1) == ':') { | |||
4600 | app = mypath; | |||
4601 | *p++ = '\0'; | |||
4602 | *p++ = '\0'; | |||
4603 | path = p; | |||
4604 | } | |||
4605 | ||||
4606 | if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { | |||
4607 | nomedia = 1; | |||
4608 | switch_ivr_media(uuid, SMF_REBRIDGE); | |||
4609 | } | |||
4610 | ||||
4611 | if ((cause = strchr(app, '!')(__extension__ (__builtin_constant_p ('!') && !__builtin_constant_p (app) && ('!') == '\0' ? (char *) __rawmemchr (app, '!' ) : __builtin_strchr (app, '!'))))) { | |||
4612 | *cause++ = '\0'; | |||
4613 | if (!cause) { | |||
4614 | cause = "normal_clearing"; | |||
4615 | } | |||
4616 | } | |||
4617 | ||||
4618 | if ((flags & SMF_ECHO_BLEG) && (other_uuid = switch_channel_get_partner_uuid(channel)) | |||
4619 | && (other_session = switch_core_session_locate(other_uuid)switch_core_session_perform_locate(other_uuid, "src/switch_ivr_async.c" , (const char *)__func__, 4619))) { | |||
4620 | if ((flags & SMF_EXEC_INLINE)) { | |||
4621 | switch_core_session_execute_application_get_flags(other_session, app, path, &app_flags); | |||
4622 | nomedia = 0; | |||
4623 | } else { | |||
4624 | switch_core_session_get_app_flags(app, &app_flags); | |||
4625 | if (switch_event_create(&event, SWITCH_EVENT_COMMAND)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 4625, &event, SWITCH_EVENT_COMMAND , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
4626 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); | |||
4627 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-name", app); | |||
4628 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-arg", path); | |||
4629 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, (flags & SMF_PRIORITY) ? "event-lock-pri" : "event-lock", "true"); | |||
4630 | ||||
4631 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "lead-frames", "%d", 5); | |||
4632 | ||||
4633 | if ((flags & SMF_LOOP)) { | |||
4634 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "loops", "%d", -1); | |||
4635 | } | |||
4636 | ||||
4637 | if ((flags & SMF_HOLD_BLEG)) { | |||
4638 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hold-bleg", "true"); | |||
4639 | } | |||
4640 | ||||
4641 | switch_core_session_queue_private_event(other_session, &event, (flags & SMF_PRIORITY)); | |||
4642 | } | |||
4643 | } | |||
4644 | ||||
4645 | switch_core_session_rwunlock(other_session); | |||
4646 | master = other_session; | |||
4647 | other_session = NULL((void*)0); | |||
4648 | } | |||
4649 | ||||
4650 | if ((app_flags & SAF_MEDIA_TAP)) { | |||
4651 | nomedia = 0; | |||
4652 | } | |||
4653 | ||||
4654 | if ((flags & SMF_ECHO_ALEG)) { | |||
4655 | if ((flags & SMF_EXEC_INLINE)) { | |||
4656 | nomedia = 0; | |||
4657 | switch_core_session_execute_application(session, app, path)switch_core_session_execute_application_get_flags(session, app , path, ((void*)0)); | |||
4658 | } else { | |||
4659 | if (switch_event_create(&event, SWITCH_EVENT_COMMAND)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 4659, &event, SWITCH_EVENT_COMMAND , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
4660 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); | |||
4661 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-name", app); | |||
4662 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-arg", path); | |||
4663 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, (flags & SMF_PRIORITY) ? "event-lock-pri" : "event-lock", "true"); | |||
4664 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "lead-frames", "%d", 5); | |||
4665 | ||||
4666 | if ((flags & SMF_LOOP)) { | |||
4667 | switch_event_add_header(event, SWITCH_STACK_BOTTOM, "loops", "%d", -1); | |||
4668 | } | |||
4669 | if ((flags & SMF_HOLD_BLEG)) { | |||
4670 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hold-bleg", "true"); | |||
4671 | } | |||
4672 | ||||
4673 | switch_core_session_queue_private_event(session, &event, (flags & SMF_PRIORITY)); | |||
4674 | ||||
4675 | if (nomedia) | |||
4676 | switch_channel_set_flag(channel, CF_BROADCAST_DROP_MEDIA)switch_channel_set_flag_value(channel, CF_BROADCAST_DROP_MEDIA , 1); | |||
4677 | } | |||
4678 | } | |||
4679 | master = session; | |||
4680 | } | |||
4681 | ||||
4682 | if (cause) { | |||
4683 | if (switch_event_create(&event, SWITCH_EVENT_COMMAND)switch_event_create_subclass_detailed("src/switch_ivr_async.c" , (const char * )(const char *)__func__, 4683, &event, SWITCH_EVENT_COMMAND , ((void*)0)) == SWITCH_STATUS_SUCCESS) { | |||
4684 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); | |||
4685 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-name", "hangup"); | |||
4686 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute-app-arg", cause); | |||
4687 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, (flags & SMF_PRIORITY) ? "event-lock-pri" : "event-lock", "true"); | |||
4688 | switch_core_session_queue_private_event(session, &event, (flags & SMF_PRIORITY)); | |||
4689 | } | |||
4690 | } | |||
4691 | ||||
4692 | switch_core_session_rwunlock(session); | |||
4693 | switch_safe_free(mypath)if (mypath) {free(mypath);mypath=((void*)0);}; | |||
4694 | ||||
4695 | return SWITCH_STATUS_SUCCESS; | |||
4696 | } | |||
4697 | ||||
4698 | /* For Emacs: | |||
4699 | * Local Variables: | |||
4700 | * mode:c | |||
4701 | * indent-tabs-mode:t | |||
4702 | * tab-width:4 | |||
4703 | * c-basic-offset:4 | |||
4704 | * End: | |||
4705 | * For VIM: | |||
4706 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: | |||
4707 | */ |