more ivr goodies
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@705 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
12094ad7a0
commit
dec2ae05a5
|
@ -80,7 +80,9 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio
|
||||||
unsigned int buflen,
|
unsigned int buflen,
|
||||||
unsigned int maxdigits,
|
unsigned int maxdigits,
|
||||||
const char *terminators,
|
const char *terminators,
|
||||||
char *terminator);
|
char *terminator,
|
||||||
|
unsigned int timeout,
|
||||||
|
unsigned int poll_channel);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief play a file from the disk to the session
|
\brief play a file from the disk to the session
|
||||||
|
@ -121,6 +123,18 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio
|
||||||
unsigned int buflen);
|
unsigned int buflen);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Speak given text with given tts engine
|
||||||
|
\param session the session to speak on
|
||||||
|
\param voice_name the desired voice
|
||||||
|
\param timer_name optional timer to use for async behaviour
|
||||||
|
\param rate the sample rate
|
||||||
|
\param dtmf_callback code to execute if any dtmf is dialed during the audio
|
||||||
|
\param text the text to speak
|
||||||
|
\param buf an option data pointer to pass to the callback or a string to put encountered digits in
|
||||||
|
\param buflen the len of buf
|
||||||
|
\return SWITCH_STATUS_SUCCESS if all is well
|
||||||
|
*/
|
||||||
SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session,
|
SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session,
|
||||||
char *tts_name,
|
char *tts_name,
|
||||||
char *voice_name,
|
char *voice_name,
|
||||||
|
@ -131,6 +145,17 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session
|
||||||
void *buf,
|
void *buf,
|
||||||
unsigned int buflen);
|
unsigned int buflen);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Bridge Audio from one session to another
|
||||||
|
\param session one session
|
||||||
|
\param peer_session the other session
|
||||||
|
\param timelimit maximum number of seconds to wait for both channels to be answered
|
||||||
|
\return SWITCH_STATUS_SUCCESS if all is well
|
||||||
|
*/
|
||||||
|
SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_session *session,
|
||||||
|
switch_core_session *peer_session,
|
||||||
|
unsigned int timelimit);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -36,159 +36,13 @@
|
||||||
|
|
||||||
static const char modname[] = "mod_bridgecall";
|
static const char modname[] = "mod_bridgecall";
|
||||||
|
|
||||||
struct audio_bridge_data {
|
|
||||||
switch_core_session *session_a;
|
|
||||||
switch_core_session *session_b;
|
|
||||||
int running;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *audio_bridge_thread(switch_thread *thread, void *obj)
|
|
||||||
{
|
|
||||||
struct switch_core_thread_session *data = obj;
|
|
||||||
int *stream_id_p;
|
|
||||||
int stream_id = 0, ans_a = 0, ans_b = 0;
|
|
||||||
|
|
||||||
switch_channel *chan_a, *chan_b;
|
|
||||||
switch_frame *read_frame;
|
|
||||||
switch_core_session *session_a, *session_b;
|
|
||||||
|
|
||||||
session_a = data->objs[0];
|
|
||||||
session_b = data->objs[1];
|
|
||||||
|
|
||||||
stream_id_p = data->objs[2];
|
|
||||||
if (stream_id_p) {
|
|
||||||
stream_id = *stream_id_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
chan_a = switch_core_session_get_channel(session_a);
|
|
||||||
chan_b = switch_core_session_get_channel(session_b);
|
|
||||||
|
|
||||||
|
|
||||||
ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED);
|
|
||||||
ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED);
|
|
||||||
|
|
||||||
|
|
||||||
while (data->running > 0) {
|
|
||||||
switch_channel_state b_state = switch_channel_get_state(chan_b);
|
|
||||||
|
|
||||||
switch (b_state) {
|
|
||||||
case CS_HANGUP:
|
|
||||||
data->running = -1;
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this call is running on early media and it answers for real, pass it along... */
|
|
||||||
if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) {
|
|
||||||
if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) {
|
|
||||||
switch_channel_answer(chan_b);
|
|
||||||
}
|
|
||||||
ans_b++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) {
|
|
||||||
if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) {
|
|
||||||
switch_channel_answer(chan_a);
|
|
||||||
}
|
|
||||||
ans_a++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if 1 channel has DTMF pass it to the other */
|
|
||||||
if (switch_channel_has_dtmf(chan_a)) {
|
|
||||||
char dtmf[128];
|
|
||||||
switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf));
|
|
||||||
switch_core_session_send_dtmf(session_b, dtmf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read audio from 1 channel and write it to the other */
|
|
||||||
if (switch_core_session_read_frame(session_a, &read_frame, -1, stream_id) == SWITCH_STATUS_SUCCESS
|
|
||||||
&& read_frame->datalen) {
|
|
||||||
if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
|
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "write: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_b));
|
|
||||||
data->running = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "read: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_a));
|
|
||||||
data->running = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
switch_channel_hangup(chan_b);
|
|
||||||
data->running = 0;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static switch_status audio_bridge_on_hangup(switch_core_session *session)
|
|
||||||
{
|
|
||||||
switch_core_session *other_session;
|
|
||||||
switch_channel *channel = NULL, *other_channel = NULL;
|
|
||||||
|
|
||||||
channel = switch_core_session_get_channel(session);
|
|
||||||
assert(channel != NULL);
|
|
||||||
|
|
||||||
other_session = switch_channel_get_private(channel);
|
|
||||||
assert(other_session != NULL);
|
|
||||||
|
|
||||||
other_channel = switch_core_session_get_channel(other_session);
|
|
||||||
assert(other_channel != NULL);
|
|
||||||
|
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM HANGUP %s kill %s\n", switch_channel_get_name(channel),
|
|
||||||
switch_channel_get_name(other_channel));
|
|
||||||
|
|
||||||
switch_core_session_kill_channel(other_session, SWITCH_SIG_KILL);
|
|
||||||
switch_core_session_kill_channel(session, SWITCH_SIG_KILL);
|
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static switch_status audio_bridge_on_ring(switch_core_session *session)
|
|
||||||
{
|
|
||||||
switch_channel *channel = NULL;
|
|
||||||
|
|
||||||
channel = switch_core_session_get_channel(session);
|
|
||||||
assert(channel != NULL);
|
|
||||||
|
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM RING\n");
|
|
||||||
|
|
||||||
/* put the channel in a passive state so we can loop audio to it */
|
|
||||||
if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
|
|
||||||
switch_channel_set_state(channel, CS_TRANSMIT);
|
|
||||||
return SWITCH_STATUS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const switch_state_handler_table audio_bridge_peer_state_handlers = {
|
|
||||||
/*.on_init */ NULL,
|
|
||||||
/*.on_ring */ audio_bridge_on_ring,
|
|
||||||
/*.on_execute */ NULL,
|
|
||||||
/*.on_hangup */ audio_bridge_on_hangup,
|
|
||||||
/*.on_loopback */ NULL,
|
|
||||||
/*.on_transmit */ NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const switch_state_handler_table audio_bridge_caller_state_handlers = {
|
|
||||||
/*.on_init */ NULL,
|
|
||||||
/*.on_ring */ NULL,
|
|
||||||
/*.on_execute */ NULL,
|
|
||||||
/*.on_hangup */ audio_bridge_on_hangup,
|
|
||||||
/*.on_loopback */ NULL,
|
|
||||||
/*.on_transmit */ NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static void audio_bridge_function(switch_core_session *session, char *data)
|
static void audio_bridge_function(switch_core_session *session, char *data)
|
||||||
{
|
{
|
||||||
switch_channel *caller_channel, *peer_channel;
|
switch_channel *caller_channel;
|
||||||
switch_core_session *peer_session;
|
switch_core_session *peer_session;
|
||||||
switch_caller_profile *caller_profile, *caller_caller_profile;
|
switch_caller_profile *caller_profile, *caller_caller_profile;
|
||||||
char chan_type[128] = { '\0' }, *chan_data;
|
char chan_type[128] = { '\0' }, *chan_data;
|
||||||
int timelimit = 60; /* probably a useful option to pass in when there's time */
|
unsigned int timelimit = 60; /* probably a useful option to pass in when there's time */
|
||||||
caller_channel = switch_core_session_get_channel(session);
|
caller_channel = switch_core_session_get_channel(session);
|
||||||
assert(caller_channel != NULL);
|
assert(caller_channel != NULL);
|
||||||
|
|
||||||
|
@ -211,76 +65,12 @@ static void audio_bridge_function(switch_core_session *session, char *data)
|
||||||
|
|
||||||
if (switch_core_session_outgoing_channel(session, chan_type, caller_profile, &peer_session) !=
|
if (switch_core_session_outgoing_channel(session, chan_type, caller_profile, &peer_session) !=
|
||||||
SWITCH_STATUS_SUCCESS) {
|
SWITCH_STATUS_SUCCESS) {
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "DOH!\n");
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create Outgoing Channel!\n");
|
||||||
switch_channel_hangup(caller_channel);
|
switch_channel_hangup(caller_channel);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
struct switch_core_thread_session this_audio_thread, other_audio_thread;
|
switch_ivr_multi_threaded_bridge(session, peer_session, timelimit);
|
||||||
time_t start;
|
|
||||||
int stream_id = 0;
|
|
||||||
|
|
||||||
peer_channel = switch_core_session_get_channel(peer_session);
|
|
||||||
memset(&other_audio_thread, 0, sizeof(other_audio_thread));
|
|
||||||
memset(&this_audio_thread, 0, sizeof(this_audio_thread));
|
|
||||||
other_audio_thread.objs[0] = session;
|
|
||||||
other_audio_thread.objs[1] = peer_session;
|
|
||||||
other_audio_thread.objs[2] = &stream_id;
|
|
||||||
other_audio_thread.running = 5;
|
|
||||||
|
|
||||||
this_audio_thread.objs[0] = peer_session;
|
|
||||||
this_audio_thread.objs[1] = session;
|
|
||||||
this_audio_thread.objs[2] = &stream_id;
|
|
||||||
this_audio_thread.running = 2;
|
|
||||||
|
|
||||||
|
|
||||||
switch_channel_set_private(caller_channel, peer_session);
|
|
||||||
switch_channel_set_private(peer_channel, session);
|
|
||||||
switch_channel_add_state_handler(caller_channel, &audio_bridge_caller_state_handlers);
|
|
||||||
switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers);
|
|
||||||
switch_core_session_thread_launch(peer_session);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int state = switch_channel_get_state(peer_channel);
|
|
||||||
if (state > CS_RING) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch_yield(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
time(&start);
|
|
||||||
while (switch_channel_get_state(caller_channel) == CS_EXECUTE &&
|
|
||||||
switch_channel_get_state(peer_channel) == CS_TRANSMIT &&
|
|
||||||
!switch_channel_test_flag(peer_channel, CF_ANSWERED) &&
|
|
||||||
!switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) &&
|
|
||||||
((time(NULL) - start) < timelimit)) {
|
|
||||||
switch_yield(20000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) {
|
|
||||||
switch_channel_answer(caller_channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switch_channel_test_flag(peer_channel, CF_ANSWERED) ||
|
|
||||||
switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) {
|
|
||||||
|
|
||||||
switch_core_session_launch_thread(session, audio_bridge_thread, (void *) &other_audio_thread);
|
|
||||||
audio_bridge_thread(NULL, (void *) &this_audio_thread);
|
|
||||||
|
|
||||||
switch_channel_hangup(peer_channel);
|
|
||||||
if (other_audio_thread.running > 0) {
|
|
||||||
other_audio_thread.running = -1;
|
|
||||||
/* wait for the other audio thread */
|
|
||||||
while (other_audio_thread.running) {
|
|
||||||
switch_yield(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_channel_hangup(caller_channel);
|
|
||||||
switch_channel_hangup(peer_channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ static void ivrtest_function(switch_core_session *session, char *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switch_ivr_collect_digits_count(session, buf, sizeof(buf), 10, "#*", &term) != SWITCH_STATUS_SUCCESS) {
|
if (switch_ivr_collect_digits_count(session, buf, sizeof(buf), 10, "#*", &term, 10000, 1) != SWITCH_STATUS_SUCCESS) {
|
||||||
switch_channel_hangup(channel);
|
switch_channel_hangup(channel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -857,7 +857,9 @@ SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
|
||||||
int refresh;
|
int refresh;
|
||||||
struct iax_event *iaxevent = NULL;
|
struct iax_event *iaxevent = NULL;
|
||||||
switch_event *s_event;
|
switch_event *s_event;
|
||||||
load_config();
|
if (load_config() != SWITCH_STATUS_SUCCESS) {
|
||||||
|
return SWITCH_STATUS_TERM;
|
||||||
|
}
|
||||||
|
|
||||||
if (globals.debug) {
|
if (globals.debug) {
|
||||||
iax_enable_debug();
|
iax_enable_debug();
|
||||||
|
|
|
@ -69,6 +69,7 @@ all: depends $(MODNAME).so
|
||||||
|
|
||||||
depends:
|
depends:
|
||||||
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) mozilla --prefix=$(PREFIX)
|
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) mozilla --prefix=$(PREFIX)
|
||||||
|
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install curl-7.15.2.tar.gz --prefix=$(PREFIX)
|
||||||
|
|
||||||
$(MODNAME).so: $(MODNAME).c
|
$(MODNAME).so: $(MODNAME).c
|
||||||
$(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o
|
$(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
* mod_spidermonkey.c -- Javascript Module
|
* mod_spidermonkey.c -- Javascript Module
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#define HAVE_CURL
|
||||||
|
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
#include "jsarena.h"
|
#include "jsarena.h"
|
||||||
#include "jsutil.h"
|
#include "jsutil.h"
|
||||||
|
@ -47,6 +49,9 @@
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
#include <libteletone.h>
|
#include <libteletone.h>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#ifdef HAVE_CURL
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(disable: 4311)
|
#pragma warning(disable: 4311)
|
||||||
|
@ -68,7 +73,6 @@ static struct {
|
||||||
} globals;
|
} globals;
|
||||||
|
|
||||||
|
|
||||||
//extern JSClass global_class;
|
|
||||||
static JSClass global_class = {
|
static JSClass global_class = {
|
||||||
"Global", JSCLASS_HAS_PRIVATE,
|
"Global", JSCLASS_HAS_PRIVATE,
|
||||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||||
|
@ -303,11 +307,12 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg
|
||||||
char *tts_name = NULL;
|
char *tts_name = NULL;
|
||||||
char *voice_name = NULL;
|
char *voice_name = NULL;
|
||||||
char *text = NULL;
|
char *text = NULL;
|
||||||
|
char *dtmf_callback = NULL;
|
||||||
switch_codec *codec;
|
switch_codec *codec;
|
||||||
char buf[10] = "";
|
|
||||||
void *bp = NULL;
|
void *bp = NULL;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
//switch_dtmf_callback_function dtmf_func = NULL;
|
struct dtmf_callback_state cb_state = {0};
|
||||||
|
switch_dtmf_callback_function dtmf_func = NULL;
|
||||||
|
|
||||||
channel = switch_core_session_get_channel(jss->session);
|
channel = switch_core_session_get_channel(jss->session);
|
||||||
assert(channel != NULL);
|
assert(channel != NULL);
|
||||||
|
@ -322,8 +327,18 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg
|
||||||
text = JS_GetStringBytes(JS_ValueToString(cx, argv[2]));
|
text = JS_GetStringBytes(JS_ValueToString(cx, argv[2]));
|
||||||
}
|
}
|
||||||
if (argc > 3) {
|
if (argc > 3) {
|
||||||
bp = buf;
|
dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[3]));
|
||||||
len = sizeof(buf);
|
if (switch_strlen_zero(dtmf_callback)) {
|
||||||
|
dtmf_callback = NULL;
|
||||||
|
} else {
|
||||||
|
memset(&cb_state, 0, sizeof(cb_state));
|
||||||
|
switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer));
|
||||||
|
cb_state.code_buffer_len = strlen(cb_state.code_buffer);
|
||||||
|
cb_state.session_state = jss;
|
||||||
|
dtmf_func = js_dtmf_callback;
|
||||||
|
bp = &cb_state;
|
||||||
|
len = sizeof(cb_state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tts_name && text) {
|
if (!tts_name && text) {
|
||||||
|
@ -336,15 +351,14 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg
|
||||||
voice_name && strlen(voice_name) ? voice_name : NULL,
|
voice_name && strlen(voice_name) ? voice_name : NULL,
|
||||||
NULL,
|
NULL,
|
||||||
codec->implementation->samples_per_second,
|
codec->implementation->samples_per_second,
|
||||||
NULL,
|
dtmf_func,
|
||||||
text,
|
text,
|
||||||
bp,
|
bp,
|
||||||
len);
|
len);
|
||||||
if(len) {
|
|
||||||
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
|
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer));
|
||||||
}
|
|
||||||
return JS_TRUE;
|
return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
@ -353,15 +367,24 @@ static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval
|
||||||
char *terminators = NULL;
|
char *terminators = NULL;
|
||||||
char *buf;
|
char *buf;
|
||||||
int digits;
|
int digits;
|
||||||
|
int32 timeout = 5000;
|
||||||
|
int32 poll_chan = 1;
|
||||||
|
|
||||||
|
|
||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
char term;
|
char term;
|
||||||
digits = atoi(JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
|
digits = atoi(JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
terminators = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
|
terminators = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
|
||||||
}
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
JS_ValueToInt32(cx, argv[2], &timeout);
|
||||||
|
}
|
||||||
|
if (argc > 3) {
|
||||||
|
JS_ValueToInt32(cx, argv[3], &poll_chan);
|
||||||
|
}
|
||||||
buf = switch_core_session_alloc(jss->session, digits);
|
buf = switch_core_session_alloc(jss->session, digits);
|
||||||
switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term);
|
switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term, timeout, poll_chan ? SWITCH_TRUE : SWITCH_FALSE);
|
||||||
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
|
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -381,6 +404,95 @@ static JSBool session_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *ar
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_CURL
|
||||||
|
|
||||||
|
struct config_data {
|
||||||
|
JSContext *cx;
|
||||||
|
JSObject *obj;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t realtime_callback(void *ptr, size_t size, size_t nmemb, void *data)
|
||||||
|
{
|
||||||
|
register int realsize = size * nmemb;
|
||||||
|
char *line, lineb[2048], *nextline = NULL, *val = NULL, *p = NULL;
|
||||||
|
jsval rval;
|
||||||
|
struct config_data *config_data = data;
|
||||||
|
char code[256];
|
||||||
|
|
||||||
|
if (config_data->name) {
|
||||||
|
switch_copy_string(lineb, (char *) ptr, sizeof(lineb));
|
||||||
|
line = lineb;
|
||||||
|
while (line) {
|
||||||
|
if ((nextline = strchr(line, '\n'))) {
|
||||||
|
*nextline = '\0';
|
||||||
|
nextline++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((val = strchr(line, '='))) {
|
||||||
|
*val = '\0';
|
||||||
|
val++;
|
||||||
|
if (val[0] == '>') {
|
||||||
|
*val = '\0';
|
||||||
|
val++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = line; p && *p == ' '; p++);
|
||||||
|
line = p;
|
||||||
|
for (p=line+strlen(line)-1;*p == ' '; p--)
|
||||||
|
*p = '\0';
|
||||||
|
for (p = val; p && *p == ' '; p++);
|
||||||
|
val = p;
|
||||||
|
for (p=val+strlen(val)-1;*p == ' '; p--)
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
snprintf(code, sizeof(code), "~%s[\"%s\"] = \"%s\"", config_data->name, line, val);
|
||||||
|
eval_some_js(code, config_data->cx, config_data->obj, &rval);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
line = nextline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realsize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static JSBool js_fetchurl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
char *url = NULL, *name = NULL;
|
||||||
|
CURL *curl_handle = NULL;
|
||||||
|
struct config_data config_data;
|
||||||
|
|
||||||
|
if ( argc > 0 && (url = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
|
||||||
|
if (argc > 1)
|
||||||
|
name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
|
||||||
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
curl_handle = curl_easy_init();
|
||||||
|
if (!strncasecmp(url, "https", 5)) {
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
}
|
||||||
|
config_data.cx = cx;
|
||||||
|
config_data.obj = obj;
|
||||||
|
if (name)
|
||||||
|
config_data.name = name;
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, realtime_callback);
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&config_data);
|
||||||
|
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "asterisk-js/1.0");
|
||||||
|
curl_easy_perform(curl_handle);
|
||||||
|
curl_easy_cleanup(curl_handle);
|
||||||
|
} else {
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error!\n");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Session Object */
|
/* Session Object */
|
||||||
/*********************************************************************************/
|
/*********************************************************************************/
|
||||||
enum session_tinyid {
|
enum session_tinyid {
|
||||||
|
@ -643,7 +755,6 @@ static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval
|
||||||
|
|
||||||
write_frame.samples = write_frame.datalen / 2;
|
write_frame.samples = write_frame.datalen / 2;
|
||||||
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
|
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
|
||||||
|
|
||||||
if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
|
if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
|
||||||
break;
|
break;
|
||||||
|
@ -651,7 +762,9 @@ static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_core_thread_session_end(&thread_session);
|
if (tto->timer) {
|
||||||
|
switch_core_thread_session_end(&thread_session);
|
||||||
|
}
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,9 +817,158 @@ static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsva
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSBool js_include(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
char *code;
|
||||||
|
if ( argc > 0 && (code = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
|
||||||
|
eval_some_js(code, cx, obj, rval);
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Arguements\n");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define B64BUFFLEN 1024
|
||||||
|
static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
static int write_buf(int fd, char *buf) {
|
||||||
|
|
||||||
|
size_t len = strlen(buf);
|
||||||
|
if (fd && write(fd, buf, len) != len) {
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool js_email(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
char *to = NULL, *from = NULL, *headers, *body = NULL, *file = NULL;
|
||||||
|
char *bound = "XXXX_boundary_XXXX";
|
||||||
|
char filename[80], buf[B64BUFFLEN];
|
||||||
|
int fd = 0, ifd = 0;
|
||||||
|
int x=0, y=0, bytes=0, ilen=0;
|
||||||
|
unsigned int b=0, l=0;
|
||||||
|
unsigned char in[B64BUFFLEN];
|
||||||
|
unsigned char out[B64BUFFLEN+512];
|
||||||
|
char *path = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
argc > 3 &&
|
||||||
|
(from = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))) &&
|
||||||
|
(to = JS_GetStringBytes(JS_ValueToString(cx, argv[1]))) &&
|
||||||
|
(headers = JS_GetStringBytes(JS_ValueToString(cx, argv[2]))) &&
|
||||||
|
(body = JS_GetStringBytes(JS_ValueToString(cx, argv[3])))
|
||||||
|
) {
|
||||||
|
if ( argc > 4)
|
||||||
|
file = JS_GetStringBytes(JS_ValueToString(cx, argv[4]));
|
||||||
|
|
||||||
|
|
||||||
|
snprintf(filename, 80, "/tmp/mail.%ld", switch_time_now());
|
||||||
|
|
||||||
|
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
|
||||||
|
if (file) {
|
||||||
|
path = file;
|
||||||
|
if ((ifd = open(path, O_RDONLY)) < 1) {
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, B64BUFFLEN, "MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"%s\"\n", bound);
|
||||||
|
if (!write_buf(fd, buf)) {
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!write_buf(fd, headers))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
if (!write_buf(fd, "\n\n"))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
snprintf(buf, B64BUFFLEN, "--%s\nContent-Type: text/plain\n\n", bound);
|
||||||
|
if (!write_buf(fd, buf))
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!write_buf(fd, body))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\nContent-Transfer-Encoding: base64\nContent-Description: Sound attachment.\nContent-Disposition: attachment; filename=\"%s\"\n\n", bound, file);
|
||||||
|
if (!write_buf(fd, buf))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
while((ilen=read(ifd, in, B64BUFFLEN))) {
|
||||||
|
for (x=0;x<ilen;x++) {
|
||||||
|
b = (b<<8) + in[x];
|
||||||
|
l += 8;
|
||||||
|
while (l >= 6) {
|
||||||
|
out[bytes++] = c64[(b>>(l-=6))%64];
|
||||||
|
if (++y!=72)
|
||||||
|
continue;
|
||||||
|
out[bytes++] = '\n';
|
||||||
|
y=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (write(fd, &out, bytes) != bytes) {
|
||||||
|
return -1;
|
||||||
|
} else
|
||||||
|
bytes=0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l > 0)
|
||||||
|
out[bytes++] = c64[((b%16)<<(6-l))%64];
|
||||||
|
if (l != 0) while (l < 6)
|
||||||
|
out[bytes++] = '=', l += 2;
|
||||||
|
|
||||||
|
if (write(fd, &out, bytes) != bytes) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
snprintf(buf, B64BUFFLEN, "\n\n--%s--\n.\n", bound);
|
||||||
|
if (!write_buf(fd, buf))
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd)
|
||||||
|
close(fd);
|
||||||
|
if (ifd)
|
||||||
|
close(ifd);
|
||||||
|
|
||||||
|
snprintf(buf, B64BUFFLEN, "/bin/cat %s | /usr/sbin/sendmail -tf \"%s\" %s", filename, from, to);
|
||||||
|
system(buf);
|
||||||
|
unlink(filename);
|
||||||
|
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed file [%s] to [%s]\n", filename, to);
|
||||||
|
} else {
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed data to [%s]\n", to);
|
||||||
|
}
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static JSFunctionSpec fs_functions[] = {
|
static JSFunctionSpec fs_functions[] = {
|
||||||
{"console_log", js_log, 2},
|
{"console_log", js_log, 2},
|
||||||
|
{"include", js_include, 1},
|
||||||
|
{"email", js_email, 2},
|
||||||
|
#ifdef HAVE_CURL
|
||||||
|
{"fetchURL", js_fetchurl, 1},
|
||||||
|
#endif
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
261
src/switch_ivr.c
261
src/switch_ivr.c
|
@ -77,27 +77,44 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio
|
||||||
unsigned int buflen,
|
unsigned int buflen,
|
||||||
unsigned int maxdigits,
|
unsigned int maxdigits,
|
||||||
const char *terminators,
|
const char *terminators,
|
||||||
char *terminator)
|
char *terminator,
|
||||||
|
unsigned int timeout,
|
||||||
|
unsigned int poll_channel
|
||||||
|
)
|
||||||
{
|
{
|
||||||
unsigned int i = 0, x = (unsigned int) strlen(buf);
|
unsigned int i = 0, x = (unsigned int) strlen(buf);
|
||||||
switch_channel *channel;
|
switch_channel *channel;
|
||||||
switch_status status = SWITCH_STATUS_SUCCESS;
|
switch_status status = SWITCH_STATUS_SUCCESS;
|
||||||
|
switch_time_t started = 0;
|
||||||
|
unsigned int elapsed;
|
||||||
|
|
||||||
channel = switch_core_session_get_channel(session);
|
channel = switch_core_session_get_channel(session);
|
||||||
assert(channel != NULL);
|
assert(channel != NULL);
|
||||||
|
|
||||||
*terminator = '\0';
|
*terminator = '\0';
|
||||||
|
|
||||||
for (i = 0 ; i < x; i++) {
|
if (terminators) {
|
||||||
if (strchr(terminators, buf[i])) {
|
for (i = 0 ; i < x; i++) {
|
||||||
*terminator = buf[i];
|
if (strchr(terminators, buf[i])) {
|
||||||
return SWITCH_STATUS_SUCCESS;
|
*terminator = buf[i];
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
started = switch_time_now();
|
||||||
|
}
|
||||||
while (switch_channel_get_state(channel) == CS_EXECUTE) {
|
while (switch_channel_get_state(channel) == CS_EXECUTE) {
|
||||||
switch_frame *read_frame;
|
switch_frame *read_frame;
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
elapsed = (switch_time_now() - started) / 1000;
|
||||||
|
if (elapsed >= timeout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (switch_channel_has_dtmf(channel)) {
|
if (switch_channel_has_dtmf(channel)) {
|
||||||
char dtmf[128];
|
char dtmf[128];
|
||||||
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
|
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
|
||||||
|
@ -116,9 +133,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (poll_channel) {
|
||||||
if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) {
|
if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch_yield(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +659,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n");
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done speaking text\n");
|
||||||
switch_core_codec_destroy(&codec);
|
switch_core_codec_destroy(&codec);
|
||||||
flags = 0;
|
flags = 0;
|
||||||
switch_core_codec_destroy(&codec);
|
switch_core_codec_destroy(&codec);
|
||||||
|
@ -654,4 +674,225 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Bridge Related Stuff*/
|
||||||
|
/*********************************************************************************/
|
||||||
|
struct audio_bridge_data {
|
||||||
|
switch_core_session *session_a;
|
||||||
|
switch_core_session *session_b;
|
||||||
|
int running;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *audio_bridge_thread(switch_thread *thread, void *obj)
|
||||||
|
{
|
||||||
|
struct switch_core_thread_session *data = obj;
|
||||||
|
int *stream_id_p;
|
||||||
|
int stream_id = 0, ans_a = 0, ans_b = 0;
|
||||||
|
|
||||||
|
switch_channel *chan_a, *chan_b;
|
||||||
|
switch_frame *read_frame;
|
||||||
|
switch_core_session *session_a, *session_b;
|
||||||
|
|
||||||
|
session_a = data->objs[0];
|
||||||
|
session_b = data->objs[1];
|
||||||
|
|
||||||
|
stream_id_p = data->objs[2];
|
||||||
|
if (stream_id_p) {
|
||||||
|
stream_id = *stream_id_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
chan_a = switch_core_session_get_channel(session_a);
|
||||||
|
chan_b = switch_core_session_get_channel(session_b);
|
||||||
|
|
||||||
|
|
||||||
|
ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED);
|
||||||
|
ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED);
|
||||||
|
|
||||||
|
|
||||||
|
while (data->running > 0) {
|
||||||
|
switch_channel_state b_state = switch_channel_get_state(chan_b);
|
||||||
|
|
||||||
|
switch (b_state) {
|
||||||
|
case CS_HANGUP:
|
||||||
|
data->running = -1;
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this call is running on early media and it answers for real, pass it along... */
|
||||||
|
if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) {
|
||||||
|
if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) {
|
||||||
|
switch_channel_answer(chan_b);
|
||||||
|
}
|
||||||
|
ans_b++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) {
|
||||||
|
if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) {
|
||||||
|
switch_channel_answer(chan_a);
|
||||||
|
}
|
||||||
|
ans_a++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if 1 channel has DTMF pass it to the other */
|
||||||
|
if (switch_channel_has_dtmf(chan_a)) {
|
||||||
|
char dtmf[128];
|
||||||
|
switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf));
|
||||||
|
switch_core_session_send_dtmf(session_b, dtmf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read audio from 1 channel and write it to the other */
|
||||||
|
if (switch_core_session_read_frame(session_a, &read_frame, -1, stream_id) == SWITCH_STATUS_SUCCESS
|
||||||
|
&& read_frame->datalen) {
|
||||||
|
if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "write: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_b));
|
||||||
|
data->running = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "read: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_a));
|
||||||
|
data->running = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
switch_channel_hangup(chan_b);
|
||||||
|
data->running = 0;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static switch_status audio_bridge_on_hangup(switch_core_session *session)
|
||||||
|
{
|
||||||
|
switch_core_session *other_session;
|
||||||
|
switch_channel *channel = NULL, *other_channel = NULL;
|
||||||
|
|
||||||
|
channel = switch_core_session_get_channel(session);
|
||||||
|
assert(channel != NULL);
|
||||||
|
|
||||||
|
other_session = switch_channel_get_private(channel);
|
||||||
|
assert(other_session != NULL);
|
||||||
|
|
||||||
|
other_channel = switch_core_session_get_channel(other_session);
|
||||||
|
assert(other_channel != NULL);
|
||||||
|
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM HANGUP %s kill %s\n", switch_channel_get_name(channel),
|
||||||
|
switch_channel_get_name(other_channel));
|
||||||
|
|
||||||
|
switch_core_session_kill_channel(other_session, SWITCH_SIG_KILL);
|
||||||
|
switch_core_session_kill_channel(session, SWITCH_SIG_KILL);
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static switch_status audio_bridge_on_ring(switch_core_session *session)
|
||||||
|
{
|
||||||
|
switch_channel *channel = NULL;
|
||||||
|
|
||||||
|
channel = switch_core_session_get_channel(session);
|
||||||
|
assert(channel != NULL);
|
||||||
|
|
||||||
|
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM RING\n");
|
||||||
|
|
||||||
|
/* put the channel in a passive state so we can loop audio to it */
|
||||||
|
if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
|
||||||
|
switch_channel_set_state(channel, CS_TRANSMIT);
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const switch_state_handler_table audio_bridge_peer_state_handlers = {
|
||||||
|
/*.on_init */ NULL,
|
||||||
|
/*.on_ring */ audio_bridge_on_ring,
|
||||||
|
/*.on_execute */ NULL,
|
||||||
|
/*.on_hangup */ audio_bridge_on_hangup,
|
||||||
|
/*.on_loopback */ NULL,
|
||||||
|
/*.on_transmit */ NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const switch_state_handler_table audio_bridge_caller_state_handlers = {
|
||||||
|
/*.on_init */ NULL,
|
||||||
|
/*.on_ring */ NULL,
|
||||||
|
/*.on_execute */ NULL,
|
||||||
|
/*.on_hangup */ audio_bridge_on_hangup,
|
||||||
|
/*.on_loopback */ NULL,
|
||||||
|
/*.on_transmit */ NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_session *session,
|
||||||
|
switch_core_session *peer_session,
|
||||||
|
unsigned int timelimit)
|
||||||
|
{
|
||||||
|
struct switch_core_thread_session this_audio_thread, other_audio_thread;
|
||||||
|
switch_channel *caller_channel, *peer_channel;
|
||||||
|
time_t start;
|
||||||
|
int stream_id = 0;
|
||||||
|
|
||||||
|
caller_channel = switch_core_session_get_channel(session);
|
||||||
|
assert(caller_channel != NULL);
|
||||||
|
|
||||||
|
peer_channel = switch_core_session_get_channel(peer_session);
|
||||||
|
assert(peer_channel != NULL);
|
||||||
|
|
||||||
|
memset(&other_audio_thread, 0, sizeof(other_audio_thread));
|
||||||
|
memset(&this_audio_thread, 0, sizeof(this_audio_thread));
|
||||||
|
other_audio_thread.objs[0] = session;
|
||||||
|
other_audio_thread.objs[1] = peer_session;
|
||||||
|
other_audio_thread.objs[2] = &stream_id;
|
||||||
|
other_audio_thread.running = 5;
|
||||||
|
|
||||||
|
this_audio_thread.objs[0] = peer_session;
|
||||||
|
this_audio_thread.objs[1] = session;
|
||||||
|
this_audio_thread.objs[2] = &stream_id;
|
||||||
|
this_audio_thread.running = 2;
|
||||||
|
|
||||||
|
|
||||||
|
switch_channel_set_private(caller_channel, peer_session);
|
||||||
|
switch_channel_set_private(peer_channel, session);
|
||||||
|
switch_channel_add_state_handler(caller_channel, &audio_bridge_caller_state_handlers);
|
||||||
|
switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers);
|
||||||
|
switch_core_session_thread_launch(peer_session);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int state = switch_channel_get_state(peer_channel);
|
||||||
|
if (state > CS_RING) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch_yield(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
time(&start);
|
||||||
|
while (switch_channel_get_state(caller_channel) == CS_EXECUTE &&
|
||||||
|
switch_channel_get_state(peer_channel) == CS_TRANSMIT &&
|
||||||
|
!switch_channel_test_flag(peer_channel, CF_ANSWERED) &&
|
||||||
|
!switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) &&
|
||||||
|
((time(NULL) - start) < timelimit)) {
|
||||||
|
switch_yield(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) {
|
||||||
|
switch_channel_answer(caller_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switch_channel_test_flag(peer_channel, CF_ANSWERED) ||
|
||||||
|
switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) {
|
||||||
|
|
||||||
|
switch_core_session_launch_thread(session, audio_bridge_thread, (void *) &other_audio_thread);
|
||||||
|
audio_bridge_thread(NULL, (void *) &this_audio_thread);
|
||||||
|
|
||||||
|
switch_channel_hangup(peer_channel);
|
||||||
|
if (other_audio_thread.running > 0) {
|
||||||
|
other_audio_thread.running = -1;
|
||||||
|
/* wait for the other audio thread */
|
||||||
|
while (other_audio_thread.running) {
|
||||||
|
switch_yield(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue