diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 347b5ef436..1b97dd5f76 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -258,6 +258,22 @@ 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's bridge partner + \param channel channel to derive the partner channel to set flag(s) on + \param flags or'd list of flags to set + \return true if the flag was set +*/ +SWITCH_DECLARE(switch_bool_t) switch_channel_set_flag_partner(switch_channel_t *channel, switch_channel_flag_t flags); + +/*! + \brief Clears given flag(s) on a given channel's bridge partner + \param channel channel to derive the partner channel to clear flag(s) from + \param flags the flags to clear + \return true if the flag was cleared +*/ +SWITCH_DECLARE(switch_bool_t) switch_channel_clear_flag_partner(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) diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 3465d60eb7..5b31745428 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -96,6 +96,7 @@ SWITCH_BEGIN_EXTERN_C #endif #define SWITCH_URL_SEPARATOR "://" #define SWITCH_ENDPOINT_DISPOSITION_VARIABLE "endpoint_disposition" +#define SWITCH_HOLD_MUSIC_VARIABLE "hold_music" #define SWITCH_EXPORT_VARS_VARIABLE "export_vars" #define SWITCH_R_SDP_VARIABLE "switch_r_sdp" #define SWITCH_L_SDP_VARIABLE "switch_l_sdp" @@ -168,6 +169,7 @@ typedef enum { SMF_ECHO_ALEG = (1 << 1), SMF_ECHO_BLEG = (1 << 2), SMF_FORCE = (1 << 3), + SMF_LOOP = (1 << 4) } switch_media_flag_t; typedef enum { @@ -529,6 +531,8 @@ CF_EVENT_PARSE = (1 << 15) - Suspend control events CF_REPEAT_STATE = (1 << 16) - Tell the state machine to repeat a state CF_GEN_RINGBACK = (1 << 17) - Channel is generating it's own ringback CF_RING_READY = (1 << 18) - Channel is ready to send ringback +CF_BREAK = (1 << 19) - Channel should stop what it's doing +CF_BROADCAST = (1 << 20) - Channel is broadcasting */ @@ -551,7 +555,9 @@ typedef enum { CF_EVENT_PARSE = (1 << 15), CF_REPEAT_STATE = (1 << 16), CF_GEN_RINGBACK = (1 << 17), - CF_RING_READY = (1 << 18) + CF_RING_READY = (1 << 18), + CF_BREAK = (1 << 19), + CF_BROADCAST = (1 << 20) } switch_channel_flag_t; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index b992c35de5..853f90eaa7 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -90,7 +90,10 @@ static char reg_sql[] = "CREATE TABLE sip_registrations (\n" " user VARCHAR(255),\n" " host VARCHAR(255),\n" - " contact VARCHAR(1024),\n" " status VARCHAR(255),\n" " rpid VARCHAR(255),\n" " expires INTEGER(8)" ");\n"; + " contact VARCHAR(1024),\n" + " status VARCHAR(255),\n" + " rpid VARCHAR(255),\n" + " expires INTEGER(8)" ");\n"; static char sub_sql[] = @@ -102,13 +105,19 @@ static char sub_sql[] = " sub_to_host VARCHAR(255),\n" " event VARCHAR(255),\n" " contact VARCHAR(1024),\n" - " call_id VARCHAR(255),\n" " full_from VARCHAR(255),\n" " full_via VARCHAR(255),\n" " expires INTEGER(8)" ");\n"; + " call_id VARCHAR(255),\n" + " full_from VARCHAR(255),\n" + " full_via VARCHAR(255),\n" + " expires INTEGER(8)" ");\n"; static char auth_sql[] = "CREATE TABLE sip_authentication (\n" " user VARCHAR(255),\n" - " host VARCHAR(255),\n" " passwd VARCHAR(255),\n" " nonce VARCHAR(255),\n" " expires INTEGER(8)" + " host VARCHAR(255),\n" + " passwd VARCHAR(255),\n" + " nonce VARCHAR(255),\n" + " expires INTEGER(8)" ");\n"; static const char modname[] = "mod_sofia"; @@ -231,6 +240,7 @@ struct sofia_profile { char *bindurl; char *sipdomain; char *timer_name; + char *hold_music; int sip_port; char *codec_string; int running; @@ -1024,7 +1034,7 @@ static switch_status_t do_invite(switch_core_session_t *session) return SWITCH_STATUS_FALSE; } - + if ((alertbuf = switch_channel_get_variable(channel, "alert_info"))) { @@ -2418,10 +2428,29 @@ static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp } for (a = sdp->sdp_attributes; a; a = a->a_next) { + if (switch_strlen_zero(a->a_name)) { + continue; + } if (!strcasecmp(a->a_name, "sendonly")) { - switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + if (!switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { + char *stream; + + switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + switch_channel_set_flag(channel, CF_HOLD); + if (!(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { + stream = tech_pvt->profile->hold_music; + } + if (stream) { + switch_ivr_broadcast(switch_core_session_get_uuid(tech_pvt->session), stream, SMF_ECHO_BLEG | SMF_LOOP); + } + } } else if (!strcasecmp(a->a_name, "sendrecv")) { - switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + if (switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { + switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + switch_channel_clear_flag(channel, CF_HOLD); + switch_channel_clear_flag_partner(channel, CF_BROADCAST); + switch_channel_set_flag_partner(channel, CF_BREAK); + } } else if (!strcasecmp(a->a_name, "ptime")) { dptime = atoi(a->a_value); } @@ -5135,6 +5164,8 @@ static switch_status_t config_sofia(int reload) profile->sipdomain = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "rtp-timer-name")) { profile->timer_name = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "hold-music")) { + profile->hold_music = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "manage-presence")) { if (switch_true(val)) { profile->pflags |= PFLAG_PRESENCE; diff --git a/src/switch_channel.c b/src/switch_channel.c index a0bcf9d504..4bba831110 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -396,6 +396,42 @@ SWITCH_DECLARE(int) switch_channel_test_flag(switch_channel_t *channel, switch_c return switch_test_flag(channel, flags) ? 1 : 0; } +SWITCH_DECLARE(switch_bool_t) switch_channel_set_flag_partner(switch_channel_t *channel, switch_channel_flag_t flags) +{ + char *uuid; + + assert(channel != NULL); + + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE))) { + switch_core_session_t *session; + if ((session = switch_core_session_locate(uuid))) { + switch_channel_set_flag(switch_core_session_get_channel(session), flags); + switch_core_session_rwunlock(session); + return SWITCH_TRUE; + } + } + + return SWITCH_FALSE; +} + +SWITCH_DECLARE(switch_bool_t) switch_channel_clear_flag_partner(switch_channel_t *channel, switch_channel_flag_t flags) +{ + char *uuid; + + assert(channel != NULL); + + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE))) { + switch_core_session_t *session; + if ((session = switch_core_session_locate(uuid))) { + switch_channel_clear_flag(switch_core_session_get_channel(session), flags); + switch_core_session_rwunlock(session); + return SWITCH_TRUE; + } + } + + return SWITCH_FALSE; +} + SWITCH_DECLARE(void) switch_channel_set_flag(switch_channel_t *channel, switch_channel_flag_t flags) { assert(channel != NULL); @@ -431,9 +467,20 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_state(switch_channel_t SWITCH_DECLARE(uint8_t) switch_channel_ready(switch_channel_t *channel) { - assert(channel != NULL); + uint8_t ret = 0; - return (!channel->hangup_cause && channel->state > CS_RING && channel->state < CS_HANGUP && !switch_test_flag(channel, CF_TRANSFER)) ? 1 : 0; + assert(channel != NULL); + + if (!channel->hangup_cause && channel->state > CS_RING && channel->state < CS_HANGUP && !switch_test_flag(channel, CF_TRANSFER)) { + ret++; + } + + if (switch_test_flag(channel, CF_BREAK)) { + switch_clear_flag_locked(channel, CF_BREAK); + ret = (uint8_t) 0; + } + + return ret; } static const char *state_names[] = { diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 5e5962592e..401a8fed3f 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -599,6 +599,7 @@ SWITCH_DECLARE(void) switch_core_session_reset(switch_core_session_t *session) switch_channel_dequeue_dtmf(channel, buf, sizeof(buf)); } + switch_channel_clear_flag(channel, CF_BREAK); } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 9d30ecc5bc..bed242e8ff 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -108,11 +108,25 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_event(switch_core_session_t *se const switch_application_interface_t *application_interface; char *app_name = switch_event_get_header(event, "execute-app-name"); char *app_arg = switch_event_get_header(event, "execute-app-arg"); + char *loop_h = switch_event_get_header(event, "loops"); + int loops = 0; + + if (loop_h) { + loops = atoi(loop_h); + } if (app_name && app_arg) { if ((application_interface = switch_loadable_module_get_application_interface(app_name))) { if (application_interface->application_function) { - application_interface->application_function(session, app_arg); + int x; + switch_channel_set_flag(channel, CF_BROADCAST); + for (x = 0; x < loops || loops < 0; x++) { + application_interface->application_function(session, app_arg); + if (!switch_channel_test_flag(channel, CF_BROADCAST)) { + break; + } + } + switch_channel_clear_flag(channel, CF_BROADCAST); } } } diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 0acf0f40f0..32cf328acc 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -737,8 +737,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, swi switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "%s", app); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + if ((flags & SMF_LOOP)) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "loops", "%d", -1); + } + switch_core_session_queue_private_event(other_session, &event); } + switch_core_session_rwunlock(other_session); master = other_session; other_session = NULL; @@ -749,6 +754,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, swi switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "%s", app); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + if ((flags & SMF_LOOP)) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "loops", "%d", -1); + } switch_core_session_queue_private_event(session, &event); } master = session; diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index 227ae28a14..47279353b9 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -81,11 +81,22 @@ static void *audio_bridge_thread(switch_thread_t * thread, void *obj) switch_channel_set_flag(chan_a, CF_BRIDGED); - while (switch_channel_ready(chan_a) && data->running > 0 && his_thread->running > 0) { - switch_channel_state_t b_state = switch_channel_get_state(chan_b); + for (;;) { + switch_channel_state_t b_state; switch_status_t status; switch_event_t *event; + if (!(data->running > 0 && his_thread->running > 0)) { + break; + } + + /* if you really want to make sure it's not ready, test it twice because it might be just a break */ + if (!switch_channel_ready(chan_a) && !switch_channel_ready(chan_a)) { + break; + } + + b_state = switch_channel_get_state(chan_b); + switch (b_state) { case CS_HANGUP: case CS_DONE: