diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index bc625a287e..df52f665cd 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -274,7 +274,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t * */ 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 @@ -283,6 +282,59 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_ */ SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(char *originator_uuid, char *originatee_uuid); +/*! + \brief Signal a session to request direct media access to it's remote end + \param uuid the uuid of the session to request + \param flags flags to influence behaviour (SMF_REBRIDGE to rebridge the call in media mode) + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_media(char *uuid, switch_media_flag_t flags); + +/*! + \brief Signal a session to request indirect media allowing it to exchange media directly with another device + \param uuid the uuid of the session to request + \param flags flags to influence behaviour (SMF_REBRIDGE to rebridge the call in no_media mode) + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_nomedia(char *uuid, switch_media_flag_t flags); + +/*! + \brief Signal the session with a protocol specific hold message. + \param uuid the uuid of the session to hold + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_hold_uuid(char *uuid); + +/*! + \brief Signal the session with a protocol specific unhold message. + \param uuid the uuid of the session to hold + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_unhold_uuid(char *uuid); + +/*! + \brief Signal the session with a protocol specific hold message. + \param session the session to hold + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_hold(switch_core_session_t *session); + +/*! + \brief Signal the session with a protocol specific unhold message. + \param uuid the uuid of the session to unhold + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session); + +/*! + \brief Signal the session to broadcast audio + \param uuid the uuid of the session to broadcast on + \param path the path data of the broadcast "/path/to/file.wav []" or "speak:||" + \param flags flags to send to the request (SMF_ECHO_BRIDGED to send the broadcast to both sides of the call) + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, switch_media_flag_t flags); + /*! \brief Transfer variables from one session to another \param sessa the original session diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 37c79fdd06..33940c142c 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -60,6 +60,8 @@ struct switch_state_handler_table { switch_state_handler_t on_transmit; /*! executed when the state changes to hold*/ switch_state_handler_t on_hold; + /*! executed when the state changes to hibernate*/ + switch_state_handler_t on_hibernate; }; struct switch_stream_handle { diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 39c45936f2..510a913567 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -74,12 +74,25 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_R_SDP_VARIABLE "_switch_r_sdp_" #define SWITCH_L_SDP_VARIABLE "_switch_l_sdp_" -#define SWITCH_BRIDGE_VARIABLE "BRIDGETO" -#define SWITCH_SIGNAL_BRIDGE_VARIABLE "SIGNAL_BRIDGETO" +#define SWITCH_B_SDP_VARIABLE "_switch_m_sdp_" +#define SWITCH_BRIDGE_VARIABLE "_bridge_to_" +#define SWITCH_SIGNAL_BRIDGE_VARIABLE "_signal_bridge_to_" +#define SWITCH_ORIGINATOR_VARIABLE "_originator_" +#define SWITCH_LOCAL_MEDIA_IP_VARIABLE "_local_media_ip_" +#define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "_local_media_port_" +#define SWITCH_REMOTE_MEDIA_IP_VARIABLE "_remote_media_ip_" +#define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "_remote_media_port_" + #define SWITCH_BITS_PER_BYTE 8 typedef uint8_t switch_byte_t; +typedef enum { + SMF_NONE = 0, + SMF_REBRIDGE = (1 << 0), + SMF_ECHO_BRIDGED = (1 << 1) +} switch_media_flag_t; + typedef enum { SWITCH_BITPACK_MODE_RFC3551, SWITCH_BITPACK_MODE_AAL2 @@ -246,6 +259,10 @@ typedef enum { SWITCH_MESSAGE_INDICATE_BRIDGE - indicate a bridge starting SWITCH_MESSAGE_INDICATE_UNBRIDGE - indicate a bridge ending SWITCH_MESSAGE_INDICATE_TRANSFER - indicate a transfer is taking place + SWITCH_MESSAGE_INDICATE_MEDIA - indicate media is required + SWITCH_MESSAGE_INDICATE_NOMEDIA - indicate no-media is required + SWITCH_MESSAGE_INDICATE_HOLD - indicate hold + SWITCH_MESSAGE_INDICATE_UNHOLD - indicate unhold */ typedef enum { @@ -255,7 +272,11 @@ typedef enum { SWITCH_MESSAGE_INDICATE_BRIDGE, SWITCH_MESSAGE_INDICATE_UNBRIDGE, SWITCH_MESSAGE_INDICATE_TRANSFER, - SWITCH_MESSAGE_INDICATE_RINGING + SWITCH_MESSAGE_INDICATE_RINGING, + SWITCH_MESSAGE_INDICATE_MEDIA, + SWITCH_MESSAGE_INDICATE_NOMEDIA, + SWITCH_MESSAGE_INDICATE_HOLD, + SWITCH_MESSAGE_INDICATE_UNHOLD, } switch_core_session_message_types_t; @@ -376,6 +397,7 @@ CS_TRANSMIT - Channel is in a passive transmit state CS_EXECUTE - Channel is executing it's dialplan CS_LOOPBACK - Channel is in loopback CS_HOLD - Channel is on hold +CS_HIBERNATE - Channel is in a sleep state CS_HANGUP - Channel is flagged for hangup and ready to end CS_DONE - Channel is ready to be destroyed and out of the state machine @@ -388,6 +410,7 @@ typedef enum { CS_EXECUTE, CS_LOOPBACK, CS_HOLD, + CS_HIBERNATE, CS_HANGUP, CS_DONE } switch_channel_state_t; @@ -412,6 +435,8 @@ CF_TAGGED = (1 << 10) - Channel is tagged CF_WINNER = (1 << 11) - Channel is the winner CF_CONTROLLED = (1 << 12) - Channel is under control CF_NOMEDIA = (1 << 13) - Channel has no media +CF_SUSPEND = (1 << 14) - Suspend i/o +CF_EVENT_PARSE = (1 << 15) - Suspend control events */ @@ -429,7 +454,9 @@ typedef enum { CF_TAGGED = (1 << 10), CF_WINNER = (1 << 11), CF_CONTROLLED = (1 << 12), - CF_NOMEDIA = (1 << 13) + CF_NOMEDIA = (1 << 13), + CF_SUSPEND = (1 << 14), + CF_EVENT_PARSE = (1 << 15) } switch_channel_flag_t; diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index c51a13c2ab..4985501290 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -44,6 +44,9 @@ static switch_api_interface_t load_api_interface; static switch_api_interface_t reload_api_interface; static switch_api_interface_t kill_api_interface; static switch_api_interface_t originate_api_interface; +static switch_api_interface_t media_api_interface; +static switch_api_interface_t hold_api_interface; +static switch_api_interface_t broadcast_api_interface; static switch_status_t status_function(char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) { @@ -235,6 +238,97 @@ static switch_status_t transfer_function(char *cmd, switch_core_session_t *isess return SWITCH_STATUS_SUCCESS; } +static switch_status_t uuid_media_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (isession) { + return status; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 1) { + stream->write_function(stream, "USAGE: %s\n", media_api_interface.syntax); + } else { + if (!strcmp(argv[0], "off")) { + status = switch_ivr_nomedia(argv[1], SMF_REBRIDGE); + } else { + status = switch_ivr_media(argv[0], SMF_REBRIDGE); + } + } + + if (status == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "+OK Success\n"); + } else { + stream->write_function(stream, "-ERR Operation Failed\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t uuid_broadcast_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (isession) { + return status; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 2) { + stream->write_function(stream, "USAGE: %s\n", broadcast_api_interface.syntax); + } else { + switch_media_flag_t flags = SMF_NONE; + + if (argv[2] && !strcmp(argv[2], "both")) { + flags |= SMF_ECHO_BRIDGED; + } + + status = switch_ivr_broadcast(argv[0], argv[1], flags); + stream->write_function(stream, "+OK Message Sent\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t uuid_hold_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (isession) { + return status; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 1) { + stream->write_function(stream, "USAGE: %s\n", hold_api_interface.syntax); + } else { + if (!strcmp(argv[0], "off")) { + status = switch_ivr_unhold_uuid(argv[1]); + } else { + status = switch_ivr_hold_uuid(argv[0]); + } + } + + if (status == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "+OK Success\n"); + } else { + stream->write_function(stream, "-ERR Operation Failed\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t uuid_bridge_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { char *argv[4] = {0}; @@ -535,12 +629,36 @@ static switch_api_interface_t ctl_api_interface = { /*.next */ &help_api_interface }; +static switch_api_interface_t media_api_interface = { + /*.interface_name */ "media", + /*.desc */ "media", + /*.function */ uuid_media_function, + /*.syntax */ "", + /*.next */ &ctl_api_interface +}; + +static switch_api_interface_t hold_api_interface = { + /*.interface_name */ "hold", + /*.desc */ "hold", + /*.function */ uuid_hold_function, + /*.syntax */ "", + /*.next */ &media_api_interface +}; + +static switch_api_interface_t broadcast_api_interface = { + /*.interface_name */ "broadcast", + /*.desc */ "broadcast", + /*.function */ uuid_broadcast_function, + /*.syntax */ " [both]", + /*.next */ &hold_api_interface +}; + static switch_api_interface_t uuid_bridge_api_interface = { /*.interface_name */ "uuid_bridge", /*.desc */ "uuid_bridge", /*.function */ uuid_bridge_function, /*.syntax */ " ", - /*.next */ &ctl_api_interface + /*.next */ &broadcast_api_interface }; static switch_api_interface_t status_api_interface = { diff --git a/src/mod/applications/mod_playback/mod_playback.c b/src/mod/applications/mod_playback/mod_playback.c index 0fafa586b0..beae8295db 100644 --- a/src/mod/applications/mod_playback/mod_playback.c +++ b/src/mod/applications/mod_playback/mod_playback.c @@ -88,7 +88,16 @@ static void speak_function(switch_core_session_t *session, char *data) timer_name = argv[3]; if (!(engine && voice && text)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Params!\n"); + if (!engine) { + engine = "NULL"; + } + if (!voice) { + voice = "NULL"; + } + if (!text) { + text = "NULL"; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Params! [%s][%s][%s]\n", engine, voice, text); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } diff --git a/src/mod/endpoints/mod_sofia/Makefile b/src/mod/endpoints/mod_sofia/Makefile index 8e409148af..0eeccbbc51 100644 --- a/src/mod/endpoints/mod_sofia/Makefile +++ b/src/mod/endpoints/mod_sofia/Makefile @@ -1,6 +1,6 @@ OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/)) VERSION = sofia-sip-1.12 -TARBALL = sofia-sip-1.12.3.8.tar.gz +TARBALL = sofia-sip-1.12.3.9.tar.gz CFLAGS += -I. -I$(PREFIX)/include/$(VERSION) LDFLAGS += -lsofia-sip-ua LINKER=$(CC) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 450bfc2ab2..cde844672d 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -77,6 +77,10 @@ typedef struct private_object private_object_t; extern su_log_t tport_log[]; +static switch_frame_t silence_frame = {}; +static char silence_data[13] = ""; + + static char reg_sql[] = "CREATE TABLE sip_registrations (\n" " user VARCHAR(255),\n" @@ -142,7 +146,7 @@ typedef enum { typedef enum { TFLAG_IO = (1 << 0), - TFLAG_INBOUND = (1 << 1), + TFLAG_USEME = (1 << 1), TFLAG_OUTBOUND = (1 << 2), TFLAG_READING = (1 << 3), TFLAG_WRITING = (1 << 4), @@ -274,6 +278,8 @@ struct private_object { switch_port_t remote_sdp_audio_port; char *adv_sdp_audio_ip; switch_port_t adv_sdp_audio_port; + char *proxy_sdp_audio_ip; + switch_port_t proxy_sdp_audio_port; char *from_uri; char *to_uri; char *from_address; @@ -333,7 +339,7 @@ static switch_status_t activate_rtp(private_object_t *tech_pvt); static void deactivate_rtp(private_object_t *tech_pvt); -static void set_local_sdp(private_object_t *tech_pvt); +static void set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t port, char *sr, int force); static void tech_set_codecs(private_object_t *tech_pvt); @@ -648,30 +654,45 @@ static char *find_reg_url(sofia_profile_t *profile, char *user, char *host, char } -static void set_local_sdp(private_object_t *tech_pvt) +static void set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t port, char *sr, int force) { char buf[1024]; switch_time_t now = switch_time_now(); - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + if (!force && !ip && !sr && switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { return; } + if (!ip) { + if (!(ip = tech_pvt->adv_sdp_audio_ip)) { + ip = tech_pvt->proxy_sdp_audio_ip; + } + } + if (!port) { + if (!(port = tech_pvt->adv_sdp_audio_port)) { + port = tech_pvt->proxy_sdp_audio_port; + } + } + if (!sr) { + sr = "sendrecv"; + } + snprintf(buf, sizeof(buf), "v=0\n" "o=FreeSWITCH %d%"APR_TIME_T_FMT" %d%"APR_TIME_T_FMT" IN IP4 %s\n" "s=FreeSWITCH\n" "c=IN IP4 %s\n" "t=0 0\n" - "a=sendrecv\n" + "a=%s\n" "m=audio %d RTP/AVP", - tech_pvt->adv_sdp_audio_port, + port, now, - tech_pvt->adv_sdp_audio_port, + port, now, - tech_pvt->adv_sdp_audio_ip, - tech_pvt->adv_sdp_audio_ip, - tech_pvt->adv_sdp_audio_port + ip, + ip, + sr, + port ); if (tech_pvt->rm_encoding) { @@ -715,11 +736,7 @@ static void set_local_sdp(private_object_t *tech_pvt) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d telephone-event/8000\na=fmtp:%d 0-16\n", tech_pvt->te, tech_pvt->te); } - - - tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, buf); - } static void tech_set_codecs(private_object_t *tech_pvt) @@ -805,12 +822,17 @@ static void terminate_session(switch_core_session_t **session, switch_call_cause } } + static switch_status_t tech_choose_port(private_object_t *tech_pvt) { char *ip = tech_pvt->profile->rtpip; + switch_channel_t *channel; switch_port_t sdp_port; char *err; + char tmp[50]; + channel = switch_core_session_get_channel(tech_pvt->session); + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA) || tech_pvt->adv_sdp_audio_port) { return SWITCH_STATUS_SUCCESS; } @@ -846,6 +868,12 @@ static switch_status_t tech_choose_port(private_object_t *tech_pvt) tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, ip); tech_pvt->adv_sdp_audio_port = sdp_port; + + snprintf(tmp, sizeof(tmp), "%d", sdp_port); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); + + return SWITCH_STATUS_SUCCESS; } @@ -859,6 +887,7 @@ static void do_invite(switch_core_session_t *session) switch_caller_profile_t *caller_profile; char *cid_name, *cid_num; char *e_dest = NULL; + char *holdstr = ""; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -884,7 +913,7 @@ static void do_invite(switch_core_session_t *session) } tech_choose_port(tech_pvt); - set_local_sdp(tech_pvt); + set_local_sdp(tech_pvt, NULL, 0, NULL, 0); switch_set_flag_locked(tech_pvt, TFLAG_READY); @@ -934,14 +963,18 @@ static void do_invite(switch_core_session_t *session) } + holdstr = switch_test_flag(tech_pvt, TFLAG_SIP_HOLD) ? "*" : ""; nua_invite(tech_pvt->nh, TAG_IF(rpid, SIPTAG_HEADER_STR(rpid)), TAG_IF(alert_info, SIPTAG_HEADER_STR(alert_info)), + SIPTAG_CONTACT_STR(tech_pvt->profile->url), SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), + SOATAG_HOLD(holdstr), TAG_END()); + } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); } @@ -988,6 +1021,7 @@ static void do_xfer_invite(switch_core_session_t *session) nua_invite(tech_pvt->nh2, TAG_IF(rpid, SIPTAG_HEADER_STR(rpid)), + SIPTAG_CONTACT_STR(tech_pvt->profile->url), SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), @@ -999,6 +1033,33 @@ static void do_xfer_invite(switch_core_session_t *session) } +static void tech_absorb_sdp(private_object_t *tech_pvt) +{ + switch_channel_t *channel; + char *sdp_str; + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if ((sdp_str = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE))) { + sdp_parser_t *parser; + sdp_session_t *sdp; + sdp_media_t *m; + + if ((parser = sdp_parse(tech_pvt->home, sdp_str, (int)strlen(sdp_str), 0))) { + if ((sdp = sdp_session(parser))) { + for (m = sdp->sdp_media; m ; m = m->m_next) { + tech_pvt->proxy_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, (char *)sdp->sdp_connection->c_address); + tech_pvt->proxy_sdp_audio_port = (switch_port_t)m->m_port; + break; + } + } + sdp_parser_free(parser); + } + tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, sdp_str); + } +} + /* State methods they get called when the state changes to the specific state returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next @@ -1008,8 +1069,7 @@ static switch_status_t sofia_on_init(switch_core_session_t *session) { private_object_t *tech_pvt; switch_channel_t *channel = NULL; - char *sdp; - + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -1021,15 +1081,9 @@ static switch_status_t sofia_on_init(switch_core_session_t *session) switch_channel_set_variable(channel, "endpoint_disposition", "INIT"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA INIT\n"); - if (switch_channel_test_flag(channel, CF_NOMEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); - } - - if ((sdp = switch_channel_get_variable(channel, SWITCH_L_SDP_VARIABLE))) { - tech_pvt->local_sdp_str = switch_core_session_strdup(session, sdp); - switch_set_flag(tech_pvt, TFLAG_NOMEDIA); - switch_channel_set_flag(channel, CF_NOMEDIA); + tech_absorb_sdp(tech_pvt); } if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { @@ -1170,7 +1224,7 @@ static switch_status_t sofia_on_hangup(switch_core_session_t *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending BYE\n"); nua_bye(tech_pvt->nh, TAG_END()); } else { - if (switch_test_flag(tech_pvt, TFLAG_INBOUND)) { + if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Responding to INVITE with: %d\n", sip_cause); nua_respond(tech_pvt->nh, sip_cause, NULL, TAG_END()); } else { @@ -1297,7 +1351,7 @@ static switch_status_t activate_rtp(private_object_t *tech_pvt) const char *err = NULL; switch_rtp_flag_t flags; switch_status_t status; - + char tmp[50]; assert(tech_pvt != NULL); channel = switch_core_session_get_channel(tech_pvt->session); @@ -1333,7 +1387,10 @@ static switch_status_t activate_rtp(private_object_t *tech_pvt) tech_pvt->agreed_pt, tech_pvt->read_codec.implementation->microseconds_per_frame / 1000); - + snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); + if (tech_pvt->rtp_session && switch_test_flag(tech_pvt, TFLAG_REINVITE)) { switch_clear_flag_locked(tech_pvt, TFLAG_REINVITE); @@ -1390,7 +1447,6 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) { private_object_t *tech_pvt; switch_channel_t *channel = NULL; - char *sdp; assert(session != NULL); @@ -1400,19 +1456,17 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) tech_pvt = (private_object_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); - if ((sdp = switch_channel_get_variable(channel, SWITCH_L_SDP_VARIABLE))) { - tech_pvt->local_sdp_str = switch_core_session_strdup(session, sdp); - switch_set_flag(tech_pvt, TFLAG_NOMEDIA); - switch_channel_set_flag(channel, CF_NOMEDIA); + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); + tech_absorb_sdp(tech_pvt); } - if (!switch_test_flag(tech_pvt, TFLAG_ANS) && !switch_channel_test_flag(channel, CF_OUTBOUND)) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); tech_choose_port(tech_pvt); - set_local_sdp(tech_pvt); + set_local_sdp(tech_pvt, NULL, 0, NULL, 0); activate_rtp(tech_pvt); if (tech_pvt->nh) { @@ -1440,7 +1494,7 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f size_t bytes = 0, samples = 0, frames = 0, ms = 0; switch_channel_t *channel = NULL; int payload = 0; - + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -1451,6 +1505,15 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f return SWITCH_STATUS_FALSE; } + while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) { + if (switch_channel_ready(channel)) { + switch_yield(10000); + } else { + return SWITCH_STATUS_GENERR; + } + } + + tech_pvt->read_frame.datalen = 0; switch_set_flag_locked(tech_pvt, TFLAG_READING); @@ -1558,6 +1621,14 @@ static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_ tech_pvt = (private_object_t *) switch_core_session_get_private(session); assert(tech_pvt != NULL); + while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) { + if (switch_channel_ready(channel)) { + switch_yield(10000); + } else { + return SWITCH_STATUS_GENERR; + } + } + if (switch_test_flag(tech_pvt, TFLAG_HUP)) { return SWITCH_STATUS_FALSE; } @@ -1680,6 +1751,53 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_NOMEDIA: { + char *uuid; + switch_core_session_t *other_session; + switch_channel_t *other_channel; + char *ip = NULL, *port = NULL; + + switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); + tech_pvt->local_sdp_str = NULL; + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + ip = switch_channel_get_variable(other_channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE); + port = switch_channel_get_variable(other_channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE); + switch_core_session_rwunlock(other_session); + if (ip && port) { + set_local_sdp(tech_pvt, ip, atoi(port), NULL, 1); + } + } + if (!tech_pvt->local_sdp_str) { + tech_absorb_sdp(tech_pvt); + } + + do_invite(session); + } + break; + case SWITCH_MESSAGE_INDICATE_MEDIA: { + switch_clear_flag_locked(tech_pvt, TFLAG_NOMEDIA); + tech_pvt->local_sdp_str = NULL; + if (!switch_rtp_ready(tech_pvt->rtp_session)) { + tech_set_codecs(tech_pvt); + tech_choose_port(tech_pvt); + } + set_local_sdp(tech_pvt, NULL, 0, NULL, 1); + do_invite(session); + } + break; + + case SWITCH_MESSAGE_INDICATE_HOLD: { + switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + do_invite(session); + } + break; + + case SWITCH_MESSAGE_INDICATE_UNHOLD: { + switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + do_invite(session); + } + break; case SWITCH_MESSAGE_INDICATE_BRIDGE: if (switch_test_flag(tech_pvt, TFLAG_XFER)) { @@ -1704,7 +1822,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi switch_core_session_rwunlock(asession); } - + msg->pointer_arg = NULL; return SWITCH_STATUS_FALSE; } @@ -1726,7 +1844,6 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi case SWITCH_MESSAGE_INDICATE_PROGRESS: { struct private_object *tech_pvt; switch_channel_t *channel = NULL; - char *sdp; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -1734,27 +1851,35 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); - if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { + if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA) && !switch_test_flag(tech_pvt, TFLAG_ANS)) { switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Asked to send early media by %s\n", msg->from); - if ((sdp = switch_channel_get_variable(channel, SWITCH_L_SDP_VARIABLE))) { - tech_pvt->local_sdp_str = switch_core_session_strdup(session, sdp); - switch_set_flag(tech_pvt, TFLAG_NOMEDIA); - switch_channel_set_flag(channel, CF_NOMEDIA); + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + tech_absorb_sdp(tech_pvt); } - /* Transmit 183 Progress with SDP */ tech_choose_port(tech_pvt); - set_local_sdp(tech_pvt); + set_local_sdp(tech_pvt, NULL, 0, NULL, 0); activate_rtp(tech_pvt); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "183 SDP:\n%s\n", tech_pvt->local_sdp_str); - nua_respond(tech_pvt->nh, SIP_183_SESSION_PROGRESS, - SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - SOATAG_AUDIO_AUX("cn telephone-event"), - TAG_END()); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Ring SDP:\n%s\n", tech_pvt->local_sdp_str); + + + if (msg->message_id == SWITCH_MESSAGE_INDICATE_RINGING) { + nua_respond(tech_pvt->nh, + SIP_180_RINGING, + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_AUDIO_AUX("cn telephone-event"), + TAG_END()); + } else { + nua_respond(tech_pvt->nh, + SIP_183_SESSION_PROGRESS, + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_AUDIO_AUX("cn telephone-event"), + TAG_END()); + } } } break; @@ -1947,13 +2072,9 @@ static switch_status_t sofia_outgoing_channel(switch_core_session_t *session, sw *new_session = nsession; status = SWITCH_STATUS_SUCCESS; if (session) { - char *val; - switch_channel_t *channel = switch_core_session_get_channel(session); + //char *val; + //switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_transfer_variable(session, nsession, SOFIA_REPLACES_HEADER); - - if (switch_channel_test_flag(channel, CF_NOMEDIA) && (val = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE))) { - switch_channel_set_variable(nchannel, SWITCH_L_SDP_VARIABLE, val); - } } done: @@ -1967,10 +2088,12 @@ static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp) private_object_t *tech_pvt; sdp_media_t *m; sdp_attribute_t *a; - + switch_channel_t *channel; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); if ((tech_pvt->origin = switch_core_session_strdup(session, (char *) sdp->sdp_origin->o_username))) { if (strstr(tech_pvt->origin, "CiscoSystemsSIP-GW-UserAgent")) { @@ -2007,8 +2130,9 @@ static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp) } else { match = strcasecmp(map->rm_encoding, imp->iananame) ? 0 : 1; } - + if (match && (map->rm_rate == imp->samples_per_second)) { + char tmp[50]; tech_pvt->rm_encoding = switch_core_session_strdup(session, (char *)map->rm_encoding); tech_pvt->pt = (switch_payload_t)map->rm_pt; tech_pvt->rm_rate = map->rm_rate; @@ -2017,6 +2141,9 @@ static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp) tech_pvt->rm_fmtp = switch_core_session_strdup(session, (char *)map->rm_fmtp); tech_pvt->remote_sdp_audio_port = (switch_port_t)m->m_port; tech_pvt->agreed_pt = (switch_payload_t)map->rm_pt; + snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); + switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, tmp); break; } else { match = 0; @@ -2269,6 +2396,30 @@ static void sip_i_message(int status, } } +static void pass_sdp(switch_channel_t *channel, char *sdp) +{ + char *val; + switch_core_session_t *other_session; + switch_channel_t *other_channel; + + if ((val = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_VARIABLE)) && (other_session = switch_core_session_locate(val))) { + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, sdp); + } + if ( + switch_channel_test_flag(other_channel, CF_OUTBOUND) && + switch_channel_test_flag(other_channel, CF_NOMEDIA) && + switch_channel_test_flag(channel, CF_OUTBOUND) && + switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_ivr_nomedia(val, SMF_NONE); + } + + switch_core_session_rwunlock(other_session); + } +} + static void sip_i_state(int status, char const *phrase, @@ -2323,10 +2474,14 @@ static void sip_i_state(int status, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remote SDP:\n%s\n", r_sdp); tech_pvt->remote_sdp_str = switch_core_session_strdup(session, (char *)r_sdp); switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, (char *) r_sdp); + pass_sdp(channel, (char *) r_sdp); } } + if (status == 988) { + return; + } switch ((enum nua_callstate)ss_state) { case nua_callstate_init: @@ -2339,7 +2494,7 @@ static void sip_i_state(int status, if (channel) { if (status == 180) { if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { switch_core_session_message_t msg; msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING; msg.from = __FILE__; @@ -2362,7 +2517,7 @@ static void sip_i_state(int status, if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); switch_channel_set_flag(channel, CF_EARLY_MEDIA); - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { other_channel = switch_core_session_get_channel(other_session); switch_channel_pre_answer(other_channel); switch_core_session_rwunlock(other_session); @@ -2431,7 +2586,7 @@ static void sip_i_state(int status, switch_channel_set_variable(channel, "endpoint_disposition", "RECEIVED"); switch_channel_set_state(channel, CS_INIT); switch_set_flag_locked(tech_pvt, TFLAG_READY); - //sofia_answer_channel(session);//XXX TMP + switch_core_session_thread_launch(session); if (replaces_str && (replaces = sip_replaces_make(tech_pvt->home, replaces_str)) && (bnh = nua_handle_by_replaces(nua, replaces))) { @@ -2491,7 +2646,7 @@ static void sip_i_state(int status, } if (match) { tech_choose_port(tech_pvt); - set_local_sdp(tech_pvt); + set_local_sdp(tech_pvt, NULL, 0, NULL, 0); switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); activate_rtp(tech_pvt); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Processing Reinvite\n"); @@ -2519,7 +2674,7 @@ static void sip_i_state(int status, if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); switch_channel_set_flag(channel, CF_ANSWERED); - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { other_channel = switch_core_session_get_channel(other_session); switch_channel_answer(other_channel); switch_core_session_rwunlock(other_session); @@ -3842,7 +3997,6 @@ static void sip_i_invite(nua_t *nua, switch_safe_free(to_username); } - switch_set_flag_locked(tech_pvt, TFLAG_INBOUND); tech_pvt->sofia_private.session = session; nua_handle_bind(nh, &tech_pvt->sofia_private); } @@ -3943,7 +4097,7 @@ static void sip_r_register(int status, } if (!oreg) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Register handle to associate!\n"); + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Register handle to associate!\n"); return; } @@ -4249,7 +4403,7 @@ static void *SWITCH_THREAD_FUNC profile_thread_run(switch_thread_t *thread, void TAG_END()); /* Last tag should always finish the sequence */ nua_set_params(profile->nua, - NUTAG_EARLY_MEDIA(1), + //NUTAG_EARLY_MEDIA(1), NUTAG_AUTOANSWER(0), NUTAG_AUTOALERT(0), NUTAG_ALLOW("REGISTER"), @@ -5001,6 +5155,12 @@ static void pres_event_handler(switch_event_t *event) SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) { + silence_frame.data = silence_data; + silence_frame.datalen = sizeof(silence_data); + silence_frame.buflen = sizeof(silence_data); + silence_frame.flags = SFF_CNG; + + if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); return SWITCH_STATUS_TERM; diff --git a/src/switch_channel.c b/src/switch_channel.c index 4534299a81..832aed1d91 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -462,6 +462,7 @@ static const char *state_names[] = { "CS_EXECUTE", "CS_LOOPBACK", "CS_HOLD", + "CS_HIBERNATE", "CS_HANGUP", "CS_DONE", NULL @@ -543,6 +544,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_RING: case CS_EXECUTE: case CS_HOLD: + case CS_HIBERNATE: ok++; default: break; @@ -555,6 +557,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_RING: case CS_EXECUTE: case CS_HOLD: + case CS_HIBERNATE: ok++; default: break; @@ -567,6 +570,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_RING: case CS_EXECUTE: case CS_HOLD: + case CS_HIBERNATE: ok++; default: break; @@ -579,6 +583,19 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_RING: case CS_EXECUTE: case CS_TRANSMIT: + case CS_HIBERNATE: + ok++; + default: + break; + } + break; + case CS_HIBERNATE: + switch (state) { + case CS_LOOPBACK: + case CS_RING: + case CS_EXECUTE: + case CS_TRANSMIT: + case CS_HOLD: ok++; default: break; @@ -592,6 +609,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_EXECUTE: case CS_TRANSMIT: case CS_HOLD: + case CS_HIBERNATE: ok++; default: break; @@ -604,6 +622,7 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_c case CS_TRANSMIT: case CS_RING: case CS_HOLD: + case CS_HIBERNATE: ok++; default: break; diff --git a/src/switch_core.c b/src/switch_core.c index f6993db538..32b85b1640 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1367,6 +1367,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_outgoing_channel(switch_core switch_event_t *event; switch_channel_t *peer_channel = switch_core_session_get_channel(*new_session); + if (session && channel) { profile = switch_channel_get_caller_profile(channel); } @@ -1375,7 +1376,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_outgoing_channel(switch_core } if (channel && peer_channel) { - char *export_vars; + char *export_vars, *val; + + switch_channel_set_variable(peer_channel, SWITCH_ORIGINATOR_VARIABLE, switch_core_session_get_uuid(session)); + /* A comma (,) separated list of variable names that should ne propagated from originator to originatee */ if ((export_vars = switch_channel_get_variable(channel, "export_vars"))) { char *cptmp = switch_core_session_strdup(session, export_vars); @@ -1394,6 +1398,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_outgoing_channel(switch_core } } + if ((val = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE))) { + switch_channel_set_variable(peer_channel, SWITCH_B_SDP_VARIABLE, val); + } + + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_channel_set_flag(peer_channel, CF_NOMEDIA); + } + if (profile) { if ((cloned_profile = switch_caller_profile_clone(*new_session, profile)) != 0) { switch_channel_set_originator_caller_profile(peer_channel, cloned_profile); @@ -1579,7 +1591,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_queue_private_event(switch_c switch_status_t status = SWITCH_STATUS_FALSE; assert(session != NULL); - + if (!session->private_event_queue) { switch_queue_create(&session->private_event_queue, SWITCH_EVENT_QUEUE_LEN, session->pool); } @@ -1608,8 +1620,17 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_private_event(switch { switch_status_t status = SWITCH_STATUS_FALSE; void *pop; + switch_channel_t *channel; assert(session != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_channel_test_flag(channel, CF_EVENT_PARSE)) { + return status; + } + if (session->private_event_queue) { if ((status = (switch_status_t) switch_queue_trypop(session->private_event_queue, &pop)) == SWITCH_STATUS_SUCCESS) { @@ -1880,7 +1901,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess assert(session != NULL); assert(frame != NULL); - assert(frame->codec != NULL); + if (switch_channel_test_flag(session->channel, CF_HOLD)) { return SWITCH_STATUS_SUCCESS; @@ -1895,6 +1916,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess return SWITCH_STATUS_SUCCESS; } + assert(frame->codec != NULL); if ((session->write_codec && frame->codec && session->write_codec->implementation != frame->codec->implementation)) { need_codec = TRUE; @@ -2587,6 +2609,12 @@ static void switch_core_standard_on_hold(switch_core_session_t *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HOLD\n"); } +static void switch_core_standard_on_hibernate(switch_core_session_t *session) +{ + assert(session != NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HIBERNATE\n"); +} + SWITCH_DECLARE(void) switch_core_session_signal_state_change(switch_core_session_t *session) { @@ -2976,6 +3004,43 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) } } break; + case CS_HIBERNATE: /* wait in limbo */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State HIBERNATE\n", switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_hibernate || + (driver_state_handler->on_hibernate && + driver_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + + while((application_state_handler = switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hibernate || + (application_state_handler->on_hibernate && + application_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while(proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hibernate || + (application_state_handler->on_hibernate && + application_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_hibernate(session); + } + } + break; } if (midstate == CS_DONE) { diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 8a9b76a4b4..4aaab8b89f 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -89,11 +89,14 @@ static void switch_ivr_parse_event(switch_core_session_t *session, switch_event_ apr_ssize_t hlen = APR_HASH_KEY_STRING; unsigned long CMD_EXECUTE = apr_hashfunc_default("execute", &hlen); unsigned long CMD_HANGUP = apr_hashfunc_default("hangup", &hlen); - + unsigned long CMD_NOMEDIA = apr_hashfunc_default("nomedia", &hlen); + assert(channel != NULL); hlen = (switch_size_t) strlen(cmd); cmd_hash = apr_hashfunc_default(cmd, &hlen); + switch_channel_set_flag(channel, CF_EVENT_PARSE); + if (!switch_strlen_zero(cmd)) { if (cmd_hash == CMD_EXECUTE) { const switch_application_interface_t *application_interface; @@ -116,8 +119,14 @@ static void switch_ivr_parse_event(switch_core_session_t *session, switch_event_ } switch_channel_hangup(channel, cause); - } + } else if (cmd_hash == CMD_NOMEDIA) { + char *uuid = switch_event_get_header(event, "nomedia-uuid"); + switch_ivr_nomedia(uuid, SMF_REBRIDGE); + } } + + switch_channel_clear_flag(channel, CF_EVENT_PARSE); + } SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session) @@ -1459,13 +1468,15 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj) } if (switch_channel_test_flag(chan_a, CF_TRANSFER)) { + switch_channel_clear_flag(chan_a, CF_HOLD); + switch_channel_clear_flag(chan_a, CF_SUSPEND); break; } - + if (switch_core_session_dequeue_private_event(session_a, &event) == SWITCH_STATUS_SUCCESS) { - switch_channel_set_flag(chan_b, CF_HOLD); + switch_channel_set_flag(chan_b, CF_SUSPEND); switch_ivr_parse_event(session_a, event); - switch_channel_clear_flag(chan_b, CF_HOLD); + switch_channel_clear_flag(chan_b, CF_SUSPEND); switch_event_destroy(&event); } @@ -1474,7 +1485,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj) char dtmf[128]; switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); switch_core_session_send_dtmf(session_b, dtmf); - + if (input_callback) { if (input_callback(session_a, dtmf, SWITCH_INPUT_TYPE_DTMF, user_data, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", switch_channel_get_name(chan_a)); @@ -1516,6 +1527,12 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj) continue; } + + if (switch_channel_test_flag(chan_a, CF_SUSPEND) || switch_channel_test_flag(chan_b, CF_SUSPEND)) { + switch_yield(10000); + continue; + } + /* read audio from 1 channel and write it to the other */ status = switch_core_session_read_frame(session_a, &read_frame, -1, stream_id); @@ -1624,6 +1641,7 @@ static switch_status_t uuid_bridge_on_transmit(switch_core_session_t *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM TRANSMIT\n"); switch_channel_clear_state_handler(channel, NULL); + if (!switch_channel_test_flag(channel, CF_ORIGINATOR)) { switch_channel_set_flag(channel, CF_TAGGED); return SWITCH_STATUS_FALSE; @@ -1637,6 +1655,7 @@ static switch_status_t uuid_bridge_on_transmit(switch_core_session_t *session) uint8_t ready_a, ready_b; switch_caller_profile_t *profile, *new_profile; + switch_channel_clear_flag(channel, CF_TRANSFER); switch_channel_set_private(channel, "_uuid_bridge_", NULL); @@ -1646,8 +1665,9 @@ static switch_status_t uuid_bridge_on_transmit(switch_core_session_t *session) mystate = switch_channel_get_state(channel); } - switch_channel_clear_flag(other_channel, CF_TRANSFER|CF_TAGGED); - + switch_channel_clear_flag(other_channel, CF_TRANSFER); + switch_channel_clear_flag(other_channel, CF_TAGGED); + switch_core_session_reset(session); switch_core_session_reset(other_session); @@ -2175,7 +2195,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess notready: - if (!switch_channel_ready(caller_channel)) { + if (caller_channel && !switch_channel_ready(caller_channel)) { idx = IDX_CANCEL; } @@ -2214,11 +2234,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } if (caller_channel) { - char *val; - if (switch_channel_test_flag(peer_channel, CF_NOMEDIA) && (val = switch_channel_get_variable(peer_channel, SWITCH_R_SDP_VARIABLE))) { - switch_channel_set_variable(caller_channel, SWITCH_L_SDP_VARIABLE, val); - } - if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) { switch_channel_answer(caller_channel); } else if (switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { @@ -2294,6 +2309,221 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess return status; } +SWITCH_DECLARE(switch_status_t) switch_ivr_hold(switch_core_session_t *session) +{ + switch_core_session_message_t msg = {0}; + switch_channel_t *channel; + + msg.message_id = SWITCH_MESSAGE_INDICATE_HOLD; + msg.from = __FILE__; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_channel_set_flag(channel, CF_HOLD); + switch_channel_set_flag(channel, CF_SUSPEND); + + switch_core_session_receive_message(session, &msg); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_hold_uuid(char *uuid) +{ + switch_core_session_t *session; + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_hold(session); + switch_core_session_rwunlock(session); + } + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session) +{ + switch_core_session_message_t msg = {0}; + switch_channel_t *channel; + + msg.message_id = SWITCH_MESSAGE_INDICATE_UNHOLD; + msg.from = __FILE__; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_channel_clear_flag(channel, CF_HOLD); + switch_channel_clear_flag(channel, CF_SUSPEND); + + switch_core_session_receive_message(session, &msg); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_unhold_uuid(char *uuid) +{ + switch_core_session_t *session; + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_unhold(session); + switch_core_session_rwunlock(session); + } + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, switch_media_flag_t flags) +{ + switch_channel_t *channel; + uint8_t nomedia; + switch_core_session_t *session; + switch_event_t *event; + switch_core_session_t *other_session; + char *other_uuid = NULL; + + if ((session = switch_core_session_locate(uuid))) { + char *app; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if ((nomedia = switch_channel_test_flag(channel, CF_NOMEDIA))) { + switch_ivr_media(uuid, SMF_REBRIDGE); + } + + if (!strncasecmp(path, "speak:", 6)) { + path += 6; + app = "speak"; + } else { + app = "playback"; + } + + if ((flags & SMF_ECHO_BRIDGED) && (other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) + && (other_session = switch_core_session_locate(other_uuid))) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", app); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + switch_core_session_queue_private_event(other_session, &event); + } + switch_core_session_rwunlock(other_session); + other_session = NULL; + } + + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", app); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + switch_core_session_queue_private_event(session, &event); + } + + if (nomedia) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "nomedia"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "nomedia-uuid", "%s", uuid); + switch_core_session_queue_private_event(session, &event); + } + } + + switch_core_session_rwunlock(session); + } + return SWITCH_STATUS_SUCCESS; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_media(char *uuid, switch_media_flag_t flags) +{ + char *other_uuid = NULL; + switch_channel_t *channel, *other_channel = NULL; + switch_core_session_t *session, *other_session; + switch_core_session_message_t msg = {0}; + switch_status_t status = SWITCH_STATUS_GENERR; + + msg.message_id = SWITCH_MESSAGE_INDICATE_MEDIA; + msg.from = __FILE__; + + if ((session = switch_core_session_locate(uuid))) { + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + status = SWITCH_STATUS_SUCCESS; + switch_channel_clear_flag(channel, CF_NOMEDIA); + switch_core_session_receive_message(session, &msg); + if ((flags & SMF_REBRIDGE) && (other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) + && (other_session = switch_core_session_locate(other_uuid))) { + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + switch_core_session_receive_message(other_session, &msg); + switch_channel_clear_state_handler(other_channel, NULL); + switch_core_session_rwunlock(other_session); + } + if (other_channel) { + switch_channel_clear_state_handler(channel, NULL); + } + } + + switch_core_session_rwunlock(session); + + if (other_channel) { + switch_ivr_uuid_bridge(uuid, other_uuid); + } + } + + return status; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_nomedia(char *uuid, switch_media_flag_t flags) +{ + char *other_uuid; + switch_channel_t *channel, *other_channel = NULL; + switch_core_session_t *session, *other_session = NULL; + switch_core_session_message_t msg = {0}; + switch_status_t status = SWITCH_STATUS_GENERR; + + msg.message_id = SWITCH_MESSAGE_INDICATE_NOMEDIA; + msg.from = __FILE__; + + if ((session = switch_core_session_locate(uuid))) { + status = SWITCH_STATUS_SUCCESS; + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + if (!switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_channel_set_flag(channel, CF_NOMEDIA); + switch_core_session_receive_message(session, &msg); + if ((flags & SMF_REBRIDGE) && (other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && + (other_session = switch_core_session_locate(other_uuid))) { + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + switch_core_session_receive_message(other_session, &msg); + switch_channel_clear_state_handler(other_channel, NULL); + + } + if (other_channel) { + switch_channel_clear_state_handler(channel, NULL); + switch_ivr_signal_bridge(session, other_session); + switch_core_session_rwunlock(other_session); + } + } + switch_core_session_rwunlock(session); + } + + return status; +} + +static switch_status_t signal_bridge_on_hibernate(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + switch_channel_clear_flag(channel, CF_TRANSFER); + + switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)); + + return SWITCH_STATUS_SUCCESS; +} static switch_status_t signal_bridge_on_hangup(switch_core_session_t *session) { @@ -2314,7 +2544,7 @@ static switch_status_t signal_bridge_on_hangup(switch_core_session_t *session) } - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { switch_channel_t *other_channel = NULL; other_channel = switch_core_session_get_channel(other_session); @@ -2324,6 +2554,7 @@ static switch_status_t signal_bridge_on_hangup(switch_core_session_t *session) switch_core_session_rwunlock(other_session); } + return SWITCH_STATUS_SUCCESS; } @@ -2334,7 +2565,8 @@ static const switch_state_handler_table_t signal_bridge_state_handlers = { /*.on_hangup */ signal_bridge_on_hangup, /*.on_loopback */ NULL, /*.on_transmit */ NULL, - /*.on_hold */ NULL + /*.on_hold */ NULL, + /*.on_hibernate*/ signal_bridge_on_hibernate }; @@ -2352,9 +2584,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t * peer_channel = switch_core_session_get_channel(peer_session); assert(peer_channel != NULL); - switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session)); - switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); - switch_channel_clear_state_handler(caller_channel, NULL); switch_channel_clear_state_handler(peer_channel, NULL); @@ -2382,8 +2611,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t * switch_event_fire(&event); } - switch_channel_set_state(caller_channel, CS_TRANSMIT); - switch_channel_set_state(peer_channel, CS_TRANSMIT); + switch_channel_set_state_flag(caller_channel, CF_TRANSFER); + switch_channel_set_state_flag(peer_channel, CF_TRANSFER); + + + switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session)); + switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); + + switch_channel_set_state(caller_channel, CS_HIBERNATE); + switch_channel_set_state(peer_channel, CS_HIBERNATE); return SWITCH_STATUS_SUCCESS; } @@ -2483,7 +2719,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses switch_channel_event_set_data(caller_channel, event); switch_event_fire(&event); } - if (switch_channel_get_state(caller_channel) != CS_EXECUTE && !switch_channel_test_flag(caller_channel, CF_TRANSFER)) { switch_channel_hangup(caller_channel, SWITCH_CAUSE_NORMAL_CLEARING); } @@ -2555,7 +2790,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(char *originator_uuid, ch switch_channel_add_state_handler(originatee_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 sets flags you want to be set when the next state change happens */ switch_channel_set_state_flag(originator_channel, CF_TRANSFER); switch_channel_set_state_flag(originatee_channel, CF_TRANSFER); @@ -2585,6 +2820,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_ switch_channel_t *channel; switch_caller_profile_t *profile, *new_profile; switch_core_session_message_t msg = {0}; + switch_core_session_t *other_session; + char *uuid = NULL; assert(session != NULL); assert(extension != NULL); @@ -2608,6 +2845,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_ context = new_profile->context; } + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + switch_channel_t *other_channel = NULL; + + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + + switch_channel_set_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL); + switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL); + + switch_channel_hangup(other_channel, SWITCH_CAUSE_BLIND_TRANSFER); + switch_ivr_media(uuid, SMF_NONE); + + switch_core_session_rwunlock(other_session); + } + switch_channel_set_caller_profile(channel, new_profile); switch_channel_set_flag(channel, CF_TRANSFER); switch_channel_set_state(channel, CS_RING); diff --git a/src/switch_utils.c b/src/switch_utils.c index 14f54d33a6..965f4397dd 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -117,6 +117,8 @@ SWITCH_DECLARE(unsigned int) switch_separate_string(char *buf, char delim, char char *ptr; int quot = 0; char qc = '"'; + char *e; + int x; if (!buf || !array || !arraylen) { return 0; @@ -143,16 +145,19 @@ SWITCH_DECLARE(unsigned int) switch_separate_string(char *buf, char delim, char } if (*ptr) { - char *e; - if (*ptr == qc) { - ptr++; - } - if ((e = strchr(ptr, qc))) { - *e = '\0'; - } array[argc++] = ptr; } + /* strip quotes */ + for(x = 0; x < argc; x++) { + if (*(array[x]) == qc) { + (array[x])++; + if ((e = strchr(array[x], qc))) { + *e = '\0'; + } + } + } + return argc; }