From ccb32b0eb6eec5d2564584fae5c10d4272050b15 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 19 Sep 2006 02:18:24 +0000 Subject: [PATCH] add uuid_bridge api call and ivr function Usage: uuid_bridge *should* take 2 existing channels and bridge them git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2748 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_channel.h | 7 + src/include/switch_ivr.h | 9 ++ .../applications/mod_commands/mod_commands.c | 31 +++- src/switch_channel.c | 18 ++- src/switch_ivr.c | 134 ++++++++++++++++++ 5 files changed, 197 insertions(+), 2 deletions(-) diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index ab19fdd435..1f1d52fc97 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -247,6 +247,13 @@ SWITCH_DECLARE(int) switch_channel_test_flag(switch_channel_t *channel, switch_c */ SWITCH_DECLARE(void) switch_channel_set_flag(switch_channel_t *channel, switch_channel_flag_t flags); +/*! + \brief Set given flag(s) on a given channel to be applied on the next state change + \param channel channel on which to set flag(s) + \param flags or'd list of flags to set +*/ +SWITCH_DECLARE(void) switch_channel_set_state_flag(switch_channel_t *channel, switch_channel_flag_t flags); + /*! \brief Clear given flag(s) from a channel \param channel channel to clear flags from diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 3d9db37997..cde5758315 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -233,6 +233,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses */ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_t *session, char *extension, char *dialplan, char *context); + +/*! + \brief Bridge two existing sessions + \param originator_uuid the uuid of the originator + \param originatee_uuid the uuid of the originator + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(char *originator_uuid, char *originatee_uuid); + /** @} */ SWITCH_END_EXTERN_C diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 9c63a87647..19e8d92fd8 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -178,6 +178,27 @@ static switch_status_t transfer_function(char *cmd, switch_core_session_t *isess +static switch_status_t uuid_bridge_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + + if (isession) { + return SWITCH_STATUS_FALSE; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc != 2) { + stream->write_function(stream, "Invalid Parameters\nUsage: uuid_bridge \n"); + } else { + switch_ivr_uuid_bridge(argv[0], argv[1]); + } + + return SWITCH_STATUS_SUCCESS; +} + + static switch_status_t pause_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { @@ -415,11 +436,19 @@ static switch_status_t show_function(char *cmd, switch_core_session_t *session, } + +static switch_api_interface_t uuid_bridge_api_interface = { + /*.interface_name */ "uuid_bridge", + /*.desc */ "uuid_bridge", + /*.function */ uuid_bridge_function, + /*.next */ NULL +}; + static switch_api_interface_t status_api_interface = { /*.interface_name */ "status", /*.desc */ "status", /*.function */ status_function, - /*.next */ NULL + /*.next */ &uuid_bridge_api_interface }; static switch_api_interface_t show_api_interface = { diff --git a/src/switch_channel.c b/src/switch_channel.c index dd88e61938..c40c386b7a 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -98,6 +98,7 @@ struct switch_channel { switch_core_session_t *session; switch_channel_state_t state; uint32_t flags; + uint32_t state_flags; switch_caller_profile_t *caller_profile; switch_caller_profile_t *originator_caller_profile; switch_caller_profile_t *originatee_caller_profile; @@ -374,6 +375,15 @@ SWITCH_DECLARE(void) switch_channel_set_flag(switch_channel_t *channel, switch_c switch_set_flag_locked(channel, flags); } +SWITCH_DECLARE(void) switch_channel_set_state_flag(switch_channel_t *channel, switch_channel_flag_t flags) +{ + assert(channel != NULL); + + switch_mutex_lock(channel->flag_mutex); + channel->state_flags |= flags; + switch_mutex_unlock(channel->flag_mutex); +} + SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); @@ -395,7 +405,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_state(switch_channel_t SWITCH_DECLARE(unsigned int) switch_channel_ready(switch_channel_t *channel) { assert(channel != NULL); - return (channel->state > CS_RING && channel->state < CS_HANGUP) ? 1 : 0; + return (channel->state > CS_RING && channel->state < CS_HANGUP && !switch_test_flag(channel, CF_TRANSFER)) ? 1 : 0; } static const char *state_names[] = { @@ -616,6 +626,12 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c } } done: + + if (channel->state_flags) { + channel->flags |= channel->state_flags; + channel->state_flags = 0; + } + switch_mutex_unlock(channel->flag_mutex); return channel->state; } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 6228190a50..1d944c4b49 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -1396,6 +1396,98 @@ static const switch_state_handler_table_t audio_bridge_peer_state_handlers = { /*.on_hold */ audio_bridge_on_hold, }; + +static switch_status_t uuid_bridge_on_transmit(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + switch_core_session_t *other_session; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM TRANSMIT\n"); + + if ((other_session = switch_channel_get_private(channel, "_uuid_bridge_"))) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + switch_channel_state_t state = switch_channel_get_state(other_channel); + switch_event_t *event; + uint8_t ready_a, ready_b; + switch_caller_profile_t *profile, *new_profile; + + switch_channel_set_private(channel, "_uuid_bridge_", NULL); + while (state <= CS_HANGUP && state != CS_TRANSMIT) { + switch_yield(1000); + state = switch_channel_get_state(other_channel); + } + + switch_channel_clear_flag(channel, CF_TRANSFER); + switch_channel_clear_flag(other_channel, CF_TRANSFER); + + switch_core_session_reset(session); + switch_core_session_reset(other_session); + + ready_a = switch_channel_ready(channel); + ready_b = switch_channel_ready(other_channel); + + if (!ready_a || !ready_b) { + if (!ready_a) { + switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + if (!ready_b) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + return SWITCH_STATUS_FALSE; + } + + /* add another profile to both sessions for CDR's sake */ + if ((profile = switch_channel_get_caller_profile(channel))) { + new_profile = switch_caller_profile_clone(session, profile); + new_profile->destination_number = switch_core_session_strdup(session, switch_core_session_get_uuid(other_session)); + switch_channel_set_caller_profile(channel, new_profile); + } + + if ((profile = switch_channel_get_caller_profile(other_channel))) { + new_profile = switch_caller_profile_clone(other_session, profile); + new_profile->destination_number = switch_core_session_strdup(other_session, switch_core_session_get_uuid(session)); + switch_channel_set_caller_profile(other_channel, new_profile); + } + + /* fire events that will change the data table from "show channels" */ + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(other_session)); + switch_event_fire(&event); + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(other_channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(session)); + switch_event_fire(&event); + } + + switch_ivr_multi_threaded_bridge(session, other_session, NULL, NULL, NULL); + } else { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + + + return SWITCH_STATUS_FALSE; +} + +static const switch_state_handler_table_t uuid_bridge_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_loopback */ NULL, + /*.on_transmit */ uuid_bridge_on_transmit, + /*.on_hold */ NULL +}; + struct key_collect { char *key; char *file; @@ -2009,6 +2101,48 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses } +SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(char *originator_uuid, char *originatee_uuid) +{ + switch_core_session_t *originator_session, *originatee_session; + switch_channel_t *originator_channel, *originatee_channel; + switch_status_t status = SWITCH_STATUS_FALSE; + + if ((originator_session = switch_core_session_locate(originator_uuid))) { + if ((originatee_session = switch_core_session_locate(originatee_uuid))) { + originator_channel = switch_core_session_get_channel(originator_session); + originatee_channel = switch_core_session_get_channel(originatee_session); + + /* override transmit state for originator_channel to bridge to originatee_channel + * install pointer to originatee_session into originator_channel + * set CF_TRANSFER on both channels and change state to CS_TRANSMIT to + * inturrupt anything they are already doing. + * originatee_session will fall asleep and originator_session will bridge to it + */ + + switch_channel_add_state_handler(originator_channel, &uuid_bridge_state_handlers); + switch_channel_set_private(originator_channel, "_uuid_bridge_", originatee_session); + + /* switch_channel_set_state_flag sets flags you want to be set when the next stat change happens */ + switch_channel_set_state_flag(originator_channel, CF_TRANSFER); + switch_channel_set_state_flag(originatee_channel, CF_TRANSFER); + + /* release the read locks we have on the channels */ + switch_core_session_rwunlock(originator_session); + switch_core_session_rwunlock(originatee_session); + + /* change the states and let the chips fall where they may */ + switch_channel_set_state(originator_channel, CS_TRANSMIT); + switch_channel_set_state(originatee_channel, CS_TRANSMIT); + + } else { + switch_core_session_rwunlock(originator_session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no channel for uuid %s\n", originatee_uuid); + } + } + + return status; + +} SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_t *session, char *extension, char *dialplan, char *context) {