diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 72840a4a4e..381cad1b85 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -169,6 +169,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses \param session originating session \param bleg B leg session \param bridgeto the desired remote callstring + \param timelimit_sec timeout in seconds for outgoing call \param table optional state handler table to install on the channel \param cid_name_override override the caller id name \param cid_num_override override the caller id number @@ -177,6 +178,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *session, switch_core_session_t **bleg, char *bridgeto, + uint32_t timelimit_sec, const switch_state_handler_table_t *table, char *cid_name_override, char *cid_num_override); @@ -185,18 +187,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess \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 \param dtmf_callback code to execute if any dtmf is dialed during the bridge \param session_data data to pass to the DTMF callback for session \param peer_session_data data to pass to the DTMF callback for peer_session \return SWITCH_STATUS_SUCCESS if all is well */ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session, - switch_core_session_t *peer_session, - unsigned int timelimit, - switch_input_callback_function_t dtmf_callback, - void *session_data, - void *peer_session_data); + switch_core_session_t *peer_session, + switch_input_callback_function_t dtmf_callback, + void *session_data, + void *peer_session_data); /*! diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 46d5fc61d3..405656d1d2 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -355,6 +355,8 @@ CF_LOCK_THREAD = (1 << 6) - Prevent the channel thread from exiting while this CF_BRIDGED = (1 << 7) - Channel in a bridge CF_HOLD = (1 << 8) - Channel is on hold CF_SERVICE = (1 << 9) - Channel has a service thread +CF_TAGGED = (1 << 10) - Channel is tagged +CF_WINNER = (1 << 10) - Channel is the winner */ @@ -368,7 +370,9 @@ typedef enum { CF_LOCK_THREAD = (1 << 6), CF_BRIDGED = (1 << 7), CF_HOLD = (1 << 8), - CF_SERVICE = (1 << 9) + CF_SERVICE = (1 << 9), + CF_TAGGED = (1 << 10), + CF_WINNER = (1 << 11) } switch_channel_flag_t; @@ -636,7 +640,8 @@ typedef enum { SWITCH_CAUSE_PROTOCOL_ERROR = 111, SWITCH_CAUSE_INTERWORKING = 127, SWITCH_CAUSE_CRASH = 500, - SWITCH_CAUSE_SYSTEM_SHUTDOWN = 501 + SWITCH_CAUSE_SYSTEM_SHUTDOWN = 501, + SWITCH_CAUSE_LOSE_RACE = 502 } switch_call_cause_t; diff --git a/src/mod/applications/mod_bridgecall/mod_bridgecall.c b/src/mod/applications/mod_bridgecall/mod_bridgecall.c index c1e2f76a73..8192cce3a7 100644 --- a/src/mod/applications/mod_bridgecall/mod_bridgecall.c +++ b/src/mod/applications/mod_bridgecall/mod_bridgecall.c @@ -40,18 +40,23 @@ static void audio_bridge_function(switch_core_session_t *session, char *data) { switch_channel_t *caller_channel; switch_core_session_t *peer_session; - unsigned int timelimit = 60; /* probably a useful option to pass in when there's time */ + unsigned int timelimit = 60; + char *var; caller_channel = switch_core_session_get_channel(session); assert(caller_channel != NULL); - if (switch_ivr_originate(session, &peer_session, data, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + if ((var = switch_channel_get_variable(caller_channel, "call_timeout"))) { + timelimit = atoi(var); + } + + if (switch_ivr_originate(session, &peer_session, data, timelimit, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel!\n"); switch_channel_hangup(caller_channel, SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL); return; } else { /* peer channel is read locked now the bridge func will unlock it for us */ - switch_ivr_multi_threaded_bridge(session, peer_session, timelimit, NULL, NULL, NULL); + switch_ivr_multi_threaded_bridge(session, peer_session, NULL, NULL, NULL); } } diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index fec1d62191..cd7424fda1 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -209,6 +209,72 @@ static switch_status_t pause_function(char *cmd, switch_core_session_t *isession return SWITCH_STATUS_SUCCESS; } +static switch_status_t originate_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + switch_channel_t *caller_channel; + switch_core_session_t *caller_session; + char *argv[7] = {0}; + int x, argc = 0; + char *aleg, *exten, *dp, *context, *cid_name, *cid_num; + uint32_t timeout = 60; + + if (isession) { + stream->write_function(stream, "Illegal Usage\n"); + return SWITCH_STATUS_SUCCESS; + } + + if (switch_strlen_zero(cmd)) { + stream->write_function(stream, "Usage: originate [] [] [] [] []\n"); + return SWITCH_STATUS_SUCCESS; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + for(x = 0; x < argc; x++) { + if (!strcasecmp(argv[x], "undef")) { + argv[x] = NULL; + } + } + + aleg = argv[0]; + exten = argv[1]; + dp = argv[2]; + context = argv[3]; + cid_name = argv[4]; + cid_num = argv[5]; + + if (!dp) { + dp = "XML"; + } + + if (!context) { + context = "default"; + } + + if (argv[6]) { + timeout = atoi(argv[6]); + } + + if (!aleg && exten) { + stream->write_function(stream, "Invalid Arguements\n"); + return SWITCH_STATUS_SUCCESS; + } + + if (switch_ivr_originate(NULL, &caller_session, aleg, timeout, NULL, cid_name, cid_num) != SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "Cannot Create Outgoing Channel! [%s]\n", aleg); + return SWITCH_STATUS_SUCCESS; + } + + caller_channel = switch_core_session_get_channel(caller_session); + assert(caller_channel != NULL); + switch_channel_clear_state_handler(caller_channel, NULL); + switch_core_session_rwunlock(caller_session); + + switch_ivr_session_transfer(caller_session, exten, dp, context); + + return SWITCH_STATUS_SUCCESS;; +} + struct holder { switch_stream_handle_t *stream; char *http; @@ -349,7 +415,7 @@ static switch_api_interface_t transfer_api_interface = { static switch_api_interface_t load_api_interface = { /*.interface_name */ "load", - /*.desc */ "Load Modile", + /*.desc */ "Load Module", /*.function */ load_function, /*.next */ &transfer_api_interface }; @@ -369,6 +435,14 @@ static switch_api_interface_t commands_api_interface = { /*.next */ &reload_api_interface }; +static switch_api_interface_t originate_api_interface = { + /*.interface_name */ "originate", + /*.desc */ "Originate a Call", + /*.function */ originate_function, + /*.next */ &commands_api_interface +}; + + static const switch_loadable_module_interface_t mod_commands_module_interface = { /*.module_name */ modname, /*.endpoint_interface */ NULL, @@ -376,7 +450,7 @@ static const switch_loadable_module_interface_t mod_commands_module_interface = /*.dialplan_interface */ NULL, /*.codec_interface */ NULL, /*.application_interface */ NULL, - /*.api_interface */ &commands_api_interface + /*.api_interface */ &originate_api_interface }; diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index aeb0d74bd4..2648b3e480 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -192,7 +192,12 @@ static switch_status_t conference_say(conference_obj_t *conference, char *text, static void conference_list(conference_obj_t *conference, switch_stream_handle_t *stream, char *delim); static switch_status_t conf_function(char *buf, switch_core_session_t *session, switch_stream_handle_t *stream); static switch_status_t audio_bridge_on_ring(switch_core_session_t *session); -static switch_status_t conference_outcall(conference_obj_t *conference, switch_core_session_t *session, char *bridgeto, char *cid_name, char *cid_num); +static switch_status_t conference_outcall(conference_obj_t *conference, + switch_core_session_t *session, + char *bridgeto, + uint32_t timeout, + char *cid_name, + char *cid_num); static void conference_function(switch_core_session_t *session, char *data); static void launch_conference_thread(conference_obj_t *conference); static void *SWITCH_THREAD_FUNC input_thread_run(switch_thread_t *thread, void *obj); @@ -1387,7 +1392,7 @@ static switch_status_t conf_function(char *buf, switch_core_session_t *session, goto done; } else if (!strcasecmp(argv[1], "dial")) { if (argc > 2) { - conference_outcall(conference, NULL, argv[2], argv[3], argv[4]); + conference_outcall(conference, NULL, argv[2], 60, argv[3], argv[4]); stream->write_function(stream, "OK\n"); goto done; } else { @@ -1971,7 +1976,12 @@ static const switch_state_handler_table_t audio_bridge_peer_state_handlers = { /*.on_hold */ NULL, }; -static switch_status_t conference_outcall(conference_obj_t *conference, switch_core_session_t *session, char *bridgeto, char *cid_name, char *cid_num) +static switch_status_t conference_outcall(conference_obj_t *conference, + switch_core_session_t *session, + char *bridgeto, + uint32_t timeout, + char *cid_name, + char *cid_num) { switch_core_session_t *peer_session; switch_channel_t *peer_channel; @@ -1979,7 +1989,7 @@ static switch_status_t conference_outcall(conference_obj_t *conference, switch_c switch_channel_t *caller_channel = NULL; - if (switch_ivr_originate(session, &peer_session, bridgeto, &audio_bridge_peer_state_handlers, cid_name, cid_num) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_originate(session, &peer_session, bridgeto, timeout, &audio_bridge_peer_state_handlers, cid_name, cid_num) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel!\n"); if (session) { caller_channel = switch_core_session_get_channel(session); @@ -2172,7 +2182,11 @@ static void conference_function(switch_core_session_t *session, char *data) if (strlen(pin) < strlen(conference->pin)) { buf = pin + strlen(pin); - switch_ivr_collect_digits_count(session, buf, sizeof(pin) - (unsigned int)strlen(pin), (unsigned int)strlen(conference->pin) - (unsigned int)strlen(pin), "#", &term, 10000); + switch_ivr_collect_digits_count(session, + buf, + sizeof(pin) - (unsigned int)strlen(pin), + (unsigned int)strlen(conference->pin) - (unsigned int)strlen(pin), + "#", &term, 10000); } if (strcmp(pin, conference->pin)) { @@ -2196,7 +2210,7 @@ static void conference_function(switch_core_session_t *session, char *data) } if (bridgeto) { - if (conference_outcall(conference, session, bridgeto, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + if (conference_outcall(conference, session, bridgeto, 60, NULL, NULL) != SWITCH_STATUS_SUCCESS) { goto done; } } diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c index 4ebf9521dd..72acdea955 100644 --- a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -1903,7 +1903,7 @@ static JSBool js_bridge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, j return JS_FALSE; } - switch_ivr_multi_threaded_bridge(jss_a->session, jss_b->session, timelimit, NULL, NULL, NULL); + switch_ivr_multi_threaded_bridge(jss_a->session, jss_b->session, NULL, NULL, NULL); return JS_TRUE; } diff --git a/src/switch_channel.c b/src/switch_channel.c index 01d5d7c69f..374fff3f27 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -84,6 +84,7 @@ static struct switch_cause_table CAUSE_CHART[] = { { "INTERWORKING", SWITCH_CAUSE_INTERWORKING }, { "CRASH", SWITCH_CAUSE_CRASH }, { "SYSTEM_SHUTDOWN", SWITCH_CAUSE_SYSTEM_SHUTDOWN }, + { "LOSE_RACE", SWITCH_CAUSE_LOSE_RACE }, { NULL, 0 } }; @@ -798,17 +799,20 @@ SWITCH_DECLARE(void) switch_channel_clear_state_handler(switch_channel_t *channe assert(channel != NULL); - - for (index = 0; index < channel->state_handler_index; index++) { - if (channel->state_handlers[index] != state_handler) { - new_handlers[i++] = channel->state_handlers[index]; + if (state_handler) { + for (index = 0; index < channel->state_handler_index; index++) { + if (channel->state_handlers[index] != state_handler) { + new_handlers[i++] = channel->state_handlers[index]; + } } } for (index = 0; index < SWITCH_MAX_STATE_HANDLERS; index++) { channel->state_handlers[index] = NULL; } - for (index = 0; index < i; index++) { - channel->state_handlers[index] = new_handlers[i]; + if (state_handler) { + for (index = 0; index < i; index++) { + channel->state_handlers[index] = new_handlers[i]; + } } } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 39c8dc3ec4..33c5671ab0 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -541,7 +541,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess if (fh->speed > 2) { fh->speed = 2; - } else if(fh->speed < -2) { + } else if (fh->speed < -2) { fh->speed = -2; } @@ -1140,131 +1140,344 @@ static const switch_state_handler_table_t audio_bridge_peer_state_handlers = { /*.on_hold */ audio_bridge_on_hold, }; +struct key_collect { + char *key; + char *file; + switch_core_session_t *session; +}; +static void *SWITCH_THREAD_FUNC collect_thread_run(switch_thread_t *thread, void *obj) +{ + struct key_collect *collect = (struct key_collect *) obj; + switch_channel_t *channel = switch_core_session_get_channel(collect->session); + char buf[10] = ""; + char *p, term; + + while(switch_channel_ready(channel)) { + memset(buf, 0, sizeof(buf)); + + if (collect->file) { + switch_ivr_play_file(collect->session, NULL, collect->file, NULL, NULL, buf, sizeof(buf)); + } else { + switch_ivr_collect_digits_count(collect->session, buf, sizeof(buf), 1, "", &term, 0); + } + + for(p = buf; *p; p++) { + if (*collect->key == *p) { + switch_channel_set_flag(channel, CF_WINNER); + goto wbreak; + } + } + } + wbreak: + + return NULL; +} + +static void launch_collect_thread(struct key_collect *collect) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + switch_threadattr_create(&thd_attr, switch_core_session_get_pool(collect->session)); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, collect_thread_run, collect, switch_core_session_get_pool(collect->session)); +} + +static uint8_t check_channel_status(switch_channel_t **peer_channels, + switch_core_session_t **peer_sessions, + uint32_t len, + int32_t *idx, + char *file, + char *key) +{ + + int32_t i; + *idx = -1; + + if (len == 1) { + *idx = 0; + return (switch_channel_get_state(peer_channels[0]) < CS_HANGUP && + !switch_channel_test_flag(peer_channels[0], CF_ANSWERED) && + !switch_channel_test_flag(peer_channels[0], CF_EARLY_MEDIA)) ? 1 : 0; + } else { + int32_t hups = 0; + + for (i = 0; i < len; i++) { + if (!peer_channels[i]) { + continue; + } + if (switch_channel_get_state(peer_channels[i]) >= CS_HANGUP) { + hups++; + } else if (switch_channel_test_flag(peer_channels[i], CF_ANSWERED) && !switch_channel_test_flag(peer_channels[i], CF_TAGGED)) { + + if (key) { + struct key_collect *collect; + + if ((collect = switch_core_session_alloc(peer_sessions[i], sizeof(*collect)))) { + switch_channel_set_flag(peer_channels[i], CF_TAGGED); + collect->key = key; + if (file) { + collect->file = switch_core_session_strdup(peer_sessions[i], file); + } + collect->session = peer_sessions[i]; + launch_collect_thread(collect); + } + } else { + *idx = i; + return 0; + + } + } else if (switch_channel_test_flag(peer_channels[i], CF_WINNER)) { + *idx = i; + return 0; + } + } + + if (hups == len) { + return 0; + } else { + return 1; + } + } +} + +#define MAX_PEERS 256 SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *session, switch_core_session_t **bleg, char *bridgeto, + uint32_t timelimit_sec, const switch_state_handler_table_t *table, char *cid_name_override, char *cid_num_override) { - switch_core_session_t *peer_session; - switch_caller_profile_t *caller_profile, *caller_caller_profile; - char *chan_type, *chan_data; - unsigned int timelimit = 60; - switch_channel_t *peer_channel; + char *peer_names[MAX_PEERS]; + switch_core_session_t *peer_session, *peer_sessions[MAX_PEERS]; + switch_caller_profile_t *caller_profiles[MAX_PEERS], *caller_caller_profile; + char *chan_type = NULL, *chan_data; + switch_channel_t *peer_channel = NULL, *peer_channels[MAX_PEERS]; time_t start; switch_frame_t *read_frame = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_channel_t *caller_channel = NULL; + switch_memory_pool_t *pool; + char *data = NULL; + int i, argc; + int32_t idx = -1, ccount = 0; + switch_codec_t write_codec = {0}; + switch_frame_t write_frame = {0}; + uint8_t err = 0, fdata[1024]; + char *file = NULL, *key = NULL, *odata, *var; + write_frame.data = fdata; + *bleg = NULL; - chan_type = strdup(bridgeto); - - if ((chan_data = strchr(chan_type, '/')) != 0) { - *chan_data = '\0'; - chan_data++; + odata = strdup(bridgeto); + data = odata; + + if (!strncasecmp(data, "confirm=", 8)) { + data += 8; + file = data; + if ((data = strchr(file, ';'))) { + *data++ = '\0'; + if ((key = strchr(file, ':'))) { + *key++ = '\0'; + } else { + err++; + } + } else { + err++; + } + } + + if (err) { + status = SWITCH_STATUS_GENERR; + goto done; } if (session) { caller_channel = switch_core_session_get_channel(session); assert(caller_channel != NULL); - caller_caller_profile = switch_channel_get_caller_profile(caller_channel); - if (!cid_name_override) { - cid_name_override = caller_caller_profile->caller_id_name; - } - if (!cid_num_override) { - cid_num_override = caller_caller_profile->caller_id_number; - } - caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), - caller_caller_profile->username, - caller_caller_profile->dialplan, - cid_name_override, - cid_num_override, - caller_caller_profile->network_addr, - NULL, - NULL, - caller_caller_profile->rdnis, - caller_caller_profile->source, - caller_caller_profile->context, - chan_data); - } else { - if (!cid_name_override) { - cid_name_override = "FreeSWITCH"; + + if ((var = switch_channel_get_variable(caller_channel, "group_confirm_key"))) { + key = switch_core_session_strdup(session, var); + if ((var = switch_channel_get_variable(caller_channel, "group_confirm_file"))) { + file = switch_core_session_strdup(session, var); + } } - if (!cid_num_override) { - cid_num_override = "0000000000"; - } - caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), - NULL, - NULL, - cid_name_override, - cid_num_override, - NULL, - NULL, - NULL, - NULL, - __FILE__, - NULL, - chan_data); + } + + + + if (file && !strcmp(file, "undef")) { + file = NULL; } - - - if (switch_core_session_outgoing_channel(session, chan_type, caller_profile, &peer_session, NULL) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel!\n"); - status = SWITCH_STATUS_FALSE; - goto done; - } - - switch_core_session_read_lock(peer_session); - - peer_channel = switch_core_session_get_channel(peer_session); - assert(peer_channel != NULL); + argc = switch_separate_string(data, '&', peer_names, (sizeof(peer_names) / sizeof(peer_names[0]))); + + for (i = 0; i < argc; i++) { - if (!table) { - table = &audio_bridge_peer_state_handlers; - } + chan_type = peer_names[i]; + if ((chan_data = strchr(chan_type, '/')) != 0) { + *chan_data = '\0'; + chan_data++; + } + + if (session) { + if (!switch_channel_ready(caller_channel)) { + status = SWITCH_STATUS_FALSE; + goto done; + } - switch_channel_add_state_handler(peer_channel, table); + caller_caller_profile = switch_channel_get_caller_profile(caller_channel); - if (switch_core_session_runing(peer_session)) { - switch_channel_set_state(peer_channel, CS_RING); - } else { - switch_core_session_thread_launch(peer_session); + if (!cid_name_override) { + cid_name_override = caller_caller_profile->caller_id_name; + } + if (!cid_num_override) { + cid_num_override = caller_caller_profile->caller_id_number; + } + + caller_profiles[i] = switch_caller_profile_new(switch_core_session_get_pool(session), + caller_caller_profile->username, + caller_caller_profile->dialplan, + cid_name_override, + cid_num_override, + caller_caller_profile->network_addr, + NULL, + NULL, + caller_caller_profile->rdnis, + caller_caller_profile->source, + caller_caller_profile->context, + chan_data); + pool = NULL; + } else { + if (!cid_name_override) { + cid_name_override = "FreeSWITCH"; + } + if (!cid_num_override) { + cid_num_override = "0000000000"; + } + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + + caller_profiles[i] = switch_caller_profile_new(pool, + NULL, + NULL, + cid_name_override, + cid_num_override, + NULL, + NULL, + NULL, + NULL, + __FILE__, + NULL, + chan_data); + } + + + if (switch_core_session_outgoing_channel(session, chan_type, caller_profiles[i], &peer_sessions[i], pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel!\n"); + if (pool) { + switch_core_destroy_memory_pool(&pool); + } + caller_profiles[i] = NULL; + peer_channels[i] = NULL; + peer_sessions[i] = NULL; + continue; + } + pool = NULL; + + peer_channels[i] = switch_core_session_get_channel(peer_sessions[i]); + assert(peer_channels[i] != NULL); + + if (!table) { + table = &audio_bridge_peer_state_handlers; + } + + switch_channel_add_state_handler(peer_channels[i], table); + + if (switch_core_session_runing(peer_sessions[i])) { + switch_channel_set_state(peer_channels[i], CS_RING); + } else { + switch_core_session_thread_launch(peer_sessions[i]); + } } time(&start); + for (i = 0; i < argc; i++) { + if (peer_channels[i]) { + ccount++; + } + } + + if (ccount == 1) { + key = file = NULL; + } for (;;) { - int state = switch_channel_get_state(peer_channel); - if (state >= CS_RING) { - break; - } - - if (caller_channel && !switch_channel_ready(caller_channel)) { - break; - } - - if ((time(NULL) - start) > (time_t)timelimit) { - break; - } - switch_yield(1000); - } + for (i = 0; i < argc; i++) { + int state; - if (caller_channel) { + if (!peer_channels[i]) { + continue; + } + + state = switch_channel_get_state(peer_channels[i]); + + if (state >= CS_RING) { + goto endfor1; + } + + if (caller_channel && !switch_channel_ready(caller_channel)) { + break; + } + + if ((time(NULL) - start) > (time_t)timelimit_sec) { + break; + } + switch_yield(1000); + } + } + endfor1: + + if (session) { + switch_codec_t *read_codec = switch_core_session_get_read_codec(session); switch_channel_pre_answer(caller_channel); + + if (switch_core_codec_init(&write_codec, + "L16", + read_codec->implementation->samples_per_second, + read_codec->implementation->microseconds_per_frame / 1000, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, + pool) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", + read_codec->implementation->samples_per_second, + read_codec->implementation->microseconds_per_frame / 1000); + write_frame.codec = &write_codec; + write_frame.datalen = read_codec->implementation->bytes_per_frame; + write_frame.samples = write_frame.datalen / 2; + memset(write_frame.data, 255, write_frame.datalen); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec Error!"); + switch_channel_hangup(caller_channel, SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE); + } } - while ((!caller_channel || switch_channel_ready(caller_channel)) && - !switch_channel_test_flag(peer_channel, CF_ANSWERED) && - !switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) && - ((time(NULL) - start) < (time_t)timelimit)) { - + while ((!caller_channel || switch_channel_ready(caller_channel)) && + check_channel_status(peer_channels, peer_sessions, argc, &idx, file, key) && ((time(NULL) - start) < (time_t)timelimit_sec)) { + /* read from the channel while we wait if the audio is up on it */ if (session && (switch_channel_test_flag(caller_channel, CF_ANSWERED) || switch_channel_test_flag(caller_channel, CF_EARLY_MEDIA))) { switch_status_t status = switch_core_session_read_frame(session, &read_frame, 1000, 0); @@ -1273,7 +1486,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess break; } if (read_frame) { - if (switch_core_session_write_frame(session, read_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) { + if (switch_core_session_write_frame(session, &write_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) { break; } } @@ -1284,11 +1497,32 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } + switch_core_session_reset(session); + + for (i = 0; i < argc; i++) { + if (!peer_channels[i]) { + continue; + } + if (i != idx) { + switch_channel_hangup(peer_channels[i], SWITCH_CAUSE_LOSE_RACE); + } + } + + + if (idx > -1) { + peer_session = peer_sessions[idx]; + peer_channel = peer_channels[idx]; + } else { + status = SWITCH_STATUS_FALSE; + goto done; + } + if (caller_channel && 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_read_lock(peer_session); *bleg = peer_session; status = SWITCH_STATUS_SUCCESS; } else { @@ -1298,18 +1532,22 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess done: - free(chan_type); + if (odata) { + free(odata); + } + if (write_codec.implementation) { + switch_core_codec_destroy(&write_codec); + } return status; } SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session, - switch_core_session_t *peer_session, - unsigned int timelimit, - switch_input_callback_function_t input_callback, - void *session_data, - void *peer_session_data) - + switch_core_session_t *peer_session, + switch_input_callback_function_t input_callback, + void *session_data, + void *peer_session_data) + { diff --git a/src/switch_utils.c b/src/switch_utils.c index 3de793bae9..056189a308 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -75,8 +75,9 @@ SWITCH_DECLARE(unsigned char) switch_char_to_rfc2833(char key) SWITCH_DECLARE(unsigned int) switch_separate_string(char *buf, char delim, char **array, int arraylen) { int argc; - char *scan; - int paren = 0; + char *ptr; + int quot = 0; + char qc = '"'; if (!buf || !array || !arraylen) { return 0; @@ -84,25 +85,33 @@ SWITCH_DECLARE(unsigned int) switch_separate_string(char *buf, char delim, char memset(array, 0, arraylen * sizeof(*array)); - scan = buf; + ptr = buf; - for (argc = 0; *scan && (argc < arraylen - 1); argc++) { - array[argc] = scan; - for (; *scan; scan++) { - if (*scan == '(') - paren++; - else if (*scan == ')') { - if (paren) - paren--; - } else if ((*scan == delim) && !paren) { - *scan++ = '\0'; + for (argc = 0; *ptr && (argc < arraylen - 1); argc++) { + array[argc] = ptr; + for (; *ptr; ptr++) { + if (*ptr == qc) { + if (quot) { + quot--; + } else { + quot++; + } + } else if ((*ptr == delim) && !quot) { + *ptr++ = '\0'; break; } } } - if (*scan) { - array[argc++] = scan; + if (*ptr) { + char *e; + if (*ptr == qc) { + ptr++; + } + if ((e = strchr(ptr, qc))) { + *e = '\0'; + } + array[argc++] = ptr; } return argc;