From 24084adf77f6b9fff43e0b58f411490188a7eaaf Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 1 Oct 2014 01:28:10 +0500 Subject: [PATCH] %FEATURE Add new feature to filter the SDP on bypass_media calls to remove or limit codecs. VARIABLE: bypass_media_sdp_filter Can be set globally or per leg on the inbound side of a bypass_media bridge. VALID FILTERS: remove(): Removes the specified codec if it exists in the SDP. only(): Removes all codecs besides the one specified (providing that it exists in the sdp) (will not remove telephone-event)) EXAMPLE 1 (remove everything leaving only g729): EXAMPLE 2 (remove everything leaving only g729 and also remove dtmf): EXAMPLE 3 (remove alaw and speex): --- src/include/switch_channel.h | 3 +- src/include/switch_core_media.h | 2 +- src/include/switch_utils.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 21 +-- src/mod/endpoints/mod_verto/mod_verto.c | 3 +- src/switch_channel.c | 59 +++++++ src/switch_core_media.c | 214 ++++++++++++++++++++++++ src/switch_core_session.c | 2 +- src/switch_utils.c | 19 +++ 9 files changed, 306 insertions(+), 18 deletions(-) diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index a305c96a45..3c08d6a22d 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -678,7 +678,8 @@ SWITCH_DECLARE(void) switch_channel_release_device_record(switch_device_record_t SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_device_state_function_t function, void *user_data); SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function); SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state); - +SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_channel, switch_channel_t *to_channel, const char *sdp); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h index 59e1b02b34..e956905e60 100644 --- a/src/include/switch_core_media.h +++ b/src/include/switch_core_media.h @@ -275,7 +275,7 @@ SWITCH_DECLARE(payload_map_t *) switch_core_media_add_payload_map(switch_core_se SWITCH_DECLARE(switch_rtp_crypto_key_type_t) switch_core_media_crypto_str2type(const char *str); SWITCH_DECLARE(const char *) switch_core_media_crypto_type2str(switch_rtp_crypto_key_type_t type); SWITCH_DECLARE(int) switch_core_media_crypto_keylen(switch_rtp_crypto_key_type_t type); - +SWITCH_DECLARE(char *) switch_core_media_filter_sdp(const char *sdp, const char *cmd, const char *arg); SWITCH_END_EXTERN_C #endif diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index d7c07e7171..e8b6843d14 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -418,6 +418,7 @@ SWITCH_DECLARE(switch_status_t) switch_frame_alloc(switch_frame_t **frame, switc SWITCH_DECLARE(switch_status_t) switch_frame_dup(switch_frame_t *orig, switch_frame_t **clone); SWITCH_DECLARE(switch_status_t) switch_frame_free(switch_frame_t **frame); SWITCH_DECLARE(switch_bool_t) switch_is_number(const char *str); +SWITCH_DECLARE(switch_bool_t) switch_is_leading_number(const char *str); SWITCH_DECLARE(char *) switch_find_parameter(const char *str, const char *param, switch_memory_pool_t *pool); /*! diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 6cf16e5912..5550e78d9e 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -6567,9 +6567,8 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { other_channel = switch_core_session_get_channel(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } + switch_channel_pass_sdp(channel, other_channel, r_sdp); + //switch_channel_pre_answer(other_channel); switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_PROGRESS); switch_core_session_rwunlock(other_session); @@ -6626,9 +6625,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, other_channel = switch_core_session_get_channel(other_session); //other_tech_pvt = switch_core_session_get_private(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } + switch_channel_pass_sdp(channel, other_channel, r_sdp); switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); switch_core_session_rwunlock(other_session); } @@ -7114,10 +7111,8 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { other_channel = switch_core_session_get_channel(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } - + switch_channel_pass_sdp(channel, other_channel, r_sdp); + if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n"); sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); @@ -7218,10 +7213,8 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { other_channel = switch_core_session_get_channel(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } - + switch_channel_pass_sdp(channel, other_channel, r_sdp); + //switch_channel_answer(other_channel); switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c index ce5dcd0d4b..8f24ab9d39 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.c +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -2401,7 +2401,8 @@ static void pass_sdp(verto_pvt_t *tech_pvt) if (switch_core_session_get_partner(tech_pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) { other_channel = switch_core_session_get_channel(other_session); - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, tech_pvt->r_sdp); + switch_channel_pass_sdp(tech_pvt->channel, other_channel, tech_pvt->r_sdp); + switch_channel_set_flag(other_channel, CF_PROXY_MODE); switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); switch_core_session_rwunlock(other_session); diff --git a/src/switch_channel.c b/src/switch_channel.c index bfd20abc91..0a34ab2a61 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -5220,6 +5220,65 @@ SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switc return status; } +SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_channel, switch_channel_t *to_channel, const char *sdp) +{ + char *use_sdp = (char *) sdp; + char *patched_sdp = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (!switch_channel_get_variable(to_channel, SWITCH_B_SDP_VARIABLE)) { + const char *var; + + if ((var = switch_channel_get_variable(from_channel, "bypass_media_sdp_filter"))) { + char *cmd = switch_core_session_strdup(from_channel->session, var); + int argc = 0; + char *argv[50]; + int x = 0; + + argc = switch_split(cmd, '|', argv); + + for (x = 0; x < argc; x++) { + char *command = argv[x]; + char *arg = strchr(command, '('); + + if (arg) { + char *e = switch_find_end_paren(arg, '(', ')'); + *arg++ = '\0'; + if (e) *e = '\0'; + } + + if (zstr(command) || zstr(arg)) { + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(from_channel), SWITCH_LOG_WARNING, "%s SDP FILTER PARSE ERROR\n", from_channel->name); + } else { + char *tmp_sdp = NULL; + + if (patched_sdp) { + tmp_sdp = switch_core_media_filter_sdp(patched_sdp, command, arg); + } else { + tmp_sdp = switch_core_media_filter_sdp(use_sdp, command, arg); + } + + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(from_channel), SWITCH_LOG_DEBUG, + "Filter command %s(%s)\nFROM:\n==========\n%s\nTO:\n==========\n%s\n\n", + command, arg, patched_sdp ? patched_sdp : use_sdp, tmp_sdp); + + + if (tmp_sdp) { + switch_safe_free(patched_sdp); + patched_sdp = use_sdp = tmp_sdp; + } + } + } + } + + switch_channel_set_variable(to_channel, SWITCH_B_SDP_VARIABLE, use_sdp); + } + + switch_safe_free(patched_sdp); + + return status; +} /* For Emacs: * Local Variables: diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 18735a957c..e4e810b6dd 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -8854,6 +8854,220 @@ SWITCH_DECLARE(void) switch_core_media_deinit(void) } +static int payload_number(const char *name) +{ + if (!strcasecmp(name, "pcmu")) { + return 0; + } + + if (!strcasecmp(name, "pcma")) { + return 8; + } + + if (!strcasecmp(name, "gsm")) { + return 3; + } + + if (!strcasecmp(name, "g722")) { + return 9; + } + + if (!strcasecmp(name, "g729")) { + return 18; + } + + if (!strcasecmp(name, "dvi4")) { + return 5; + } + + if (!strcasecmp(name, "h261")) { + return 31; + } + + if (!strcasecmp(name, "h263")) { + return 34; + } + + return -1; +} + +static int find_pt(const char *sdp, const char *name) +{ + const char *p; + + if ((p = switch_stristr(name, sdp))) { + if (p < end_of_p(sdp) && *(p+strlen(name)) == '/' && *(p-1) == ' ') { + p -= 2; + + while(*p > 47 && *p < 58) { + p--; + } + p++; + + if (p) { + return atoi(p); + } + } + } + + return -1; +} + + +SWITCH_DECLARE(char *) switch_core_media_filter_sdp(const char *sdp_str, const char *cmd, const char *arg) +{ + char *new_sdp = NULL; + int pt = -1, te = -1; + switch_size_t len; + const char *i; + char *o; + int in_m = 0, m_tally = 0, slash = 0; + int number = 0, skip = 0; + int remove = !strcasecmp(cmd, "remove"); + int only = !strcasecmp(cmd, "only"); + char *end = end_of_p((char *)sdp_str); + int tst; + end++; + + + if (remove || only) { + pt = payload_number(arg); + + if (pt < 0) { + pt = find_pt(sdp_str, arg); + } + } else { + return NULL; + } + + if (only) { + te = find_pt(sdp_str, "telephone-event"); + } + + + len = strlen(sdp_str); + new_sdp = malloc(len); + o = new_sdp; + i = sdp_str; + + + while(i && *i && i < end) { + + if (*i == 'm' && *(i+1) == '=') { + in_m = 1; + m_tally++; + } + + if (in_m) { + if (*i == '\r' || *i == '\n') { + in_m = 0; + slash = 0; + } else { + if (*i == '/') { + slash++; + while(*i != ' ' && i < end) { + *o++ = *i++; + } + + *o++ = *i++; + } + + if (slash && switch_is_leading_number(i)) { + + + number = atoi(i); + + while(i < end && ((*i > 47 && *i < 58) || *i == ' ')) { + + if (remove) { + tst = (number != pt); + } else { + tst = (number == pt || number == te); + } + + if (tst) { + *o++ = *i; + } + i++; + + if (*i == ' ') { + break; + } + + } + + if (remove) { + tst = (number == pt); + } else { + tst = (number != pt && number != te); + } + + if (tst) { + skip++; + } + } + } + } + + while (i < end && !strncasecmp(i, "a=rtpmap:", 9)) { + const char *t = i + 9; + + number = atoi(t); + + if (remove) { + tst = (number == pt); + } else { + tst = (number != pt && number != te); + } + + while(i < end && (*i != '\r' && *i != '\n')) { + if (!tst) *o++ = *i; + i++; + } + + while(i < end && (*i == '\r' || *i == '\n')) { + if (!tst) *o++ = *i; + i++; + } + } + + while (i < end && !strncasecmp(i, "a=fmtp:", 7)) { + const char *t = i + 7; + + number = atoi(t); + + if (remove) { + tst = (number == pt); + } else { + tst = (number != pt && number != te); + } + + while(i < end && (*i != '\r' && *i != '\n')) { + if (!tst) *o++ = *i; + i++; + } + + while(i < end && (*i == '\r' || *i == '\n')) { + if (!tst) *o++ = *i; + i++; + } + } + + if (!skip) { + *o++ = *i; + } + + skip = 0; + + i++; + } + + *o = '\0'; + + return new_sdp; +} + + /* For Emacs: * Local Variables: diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 853d106e68..c5f1eaa718 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -654,7 +654,7 @@ SWITCH_DECLARE(switch_call_cause_t) switch_core_session_outgoing_channel(switch_ } if ((val = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE))) { - switch_channel_set_variable(peer_channel, SWITCH_B_SDP_VARIABLE, val); + switch_channel_pass_sdp(channel, peer_channel, val); } if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { diff --git a/src/switch_utils.c b/src/switch_utils.c index f57ea00c4e..3cde97a127 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -1231,6 +1231,25 @@ SWITCH_DECLARE(switch_bool_t) switch_is_number(const char *str) return r; } +SWITCH_DECLARE(switch_bool_t) switch_is_leading_number(const char *str) +{ + const char *p; + switch_bool_t r = SWITCH_FALSE; + + if (*str == '-' || *str == '+') { + str++; + } + + for (p = str; p && *p; p++) { + if ((*p == '.' || (*p > 47 && *p < 58))) { + r = SWITCH_TRUE; + break; + } + } + + return r; +} + SWITCH_DECLARE(const char *) switch_stristr(const char *instr, const char *str) { /*