From aa4816659ce4c04c3bae368a0ced9d0ef9e156bf Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 20 Apr 2010 16:15:37 -0500 Subject: [PATCH] add rtcp seperate on audio and video and add passthru --- conf/sip_profiles/internal.xml | 5 +- src/include/switch_rtp.h | 2 +- src/include/switch_types.h | 4 +- src/mod/endpoints/mod_sofia/mod_sofia.h | 3 +- src/mod/endpoints/mod_sofia/sofia.c | 12 ++- src/mod/endpoints/mod_sofia/sofia_glue.c | 53 ++++++++++-- src/switch_rtp.c | 105 +++++++++++++++++++---- 7 files changed, 154 insertions(+), 30 deletions(-) diff --git a/conf/sip_profiles/internal.xml b/conf/sip_profiles/internal.xml index dcce589013..2f5636e5a6 100644 --- a/conf/sip_profiles/internal.xml +++ b/conf/sip_profiles/internal.xml @@ -208,8 +208,9 @@ - - + + + diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index 4ad264ae8e..c4bab48190 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -219,7 +219,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_ice(switch_rtp_t *rtp_sessio \param send_rate interval in milliseconds to send at \return SWITCH_STATUS_SUCCESS */ -SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_session, int send_rate); +SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_session, int send_rate, switch_port_t remote_port); /*! \brief Acvite a jitter buffer on an RTP session diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 52f8ac1c24..2589e2b8c3 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -535,7 +535,9 @@ typedef enum { SWITCH_RTP_FLAG_DEBUG_RTP_READ = (1 << 27), SWITCH_RTP_FLAG_DEBUG_RTP_WRITE = (1 << 28), SWITCH_RTP_FLAG_VIDEO = (1 << 29), - SWITCH_RTP_FLAG_ENABLE_RTCP = (1 << 30) + SWITCH_RTP_FLAG_ENABLE_RTCP = (1 << 30), + SWITCH_RTP_FLAG_RTCP_PASSTHRU = (1 << 31) + /* don't add any more 31 is the limit! gotta chnge to an array to add more */ } switch_rtp_flag_enum_t; typedef uint32_t switch_rtp_flag_t; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index ebf669028f..e1b1e61d53 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -466,7 +466,8 @@ struct sofia_profile { char *record_path; char *presence_hosts; char *challenge_realm; - char *rtcp_interval_msec; + char *rtcp_audio_interval_msec; + char *rtcp_video_interval_msec; sofia_cid_type_t cid_type; sofia_dtmf_t dtmf_type; int auto_restart; diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 71faa4e782..89940a8cd7 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -2459,8 +2459,10 @@ switch_status_t reconfig_sofia(sofia_profile_t *profile) profile->hold_music = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "outbound-proxy")) { profile->outbound_proxy = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "rtcp-interval-msec")) { - profile->rtcp_interval_msec = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rtcp-audio-interval-msec")) { + profile->rtcp_audio_interval_msec = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rtcp-video-interval-msec")) { + profile->rtcp_video_interval_msec = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "session-timeout")) { int v_session_timeout = atoi(val); if (v_session_timeout >= 0) { @@ -2998,8 +3000,10 @@ switch_status_t config_sofia(int reload, char *profile_name) profile->hold_music = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "outbound-proxy")) { profile->outbound_proxy = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "rtcp-interval-msec")) { - profile->rtcp_interval_msec = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rtcp-audio-interval-msec")) { + profile->rtcp_audio_interval_msec = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rtcp-video-interval-msec")) { + profile->rtcp_video_interval_msec = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "session-timeout")) { int v_session_timeout = atoi(val); if (v_session_timeout >= 0) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 1143fda5f0..49b302b7ff 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -691,7 +691,7 @@ const char *sofia_glue_get_unknown_header(sip_t const *sip, const char *name) switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int force) { char *lookup_rtpip = tech_pvt->profile->rtpip; /* Pointer to externally looked up address */ - switch_port_t sdp_port; /* The external port to be sent in the SDP */ + switch_port_t sdp_port, rtcp_port; /* The external port to be sent in the SDP */ const char *use_ip = NULL; /* The external IP to be sent in the SDP */ /* Don't do anything if we're in proxy mode or if a (remote) port already has been found */ @@ -734,6 +734,7 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int forc if (!zstr(tech_pvt->remote_ip) && sofia_glue_check_nat(tech_pvt->profile, tech_pvt->remote_ip)) { /* Yes, map the port through switch_nat */ switch_nat_add_mapping(tech_pvt->local_sdp_audio_port, SWITCH_NAT_UDP, &sdp_port, SWITCH_FALSE); + switch_nat_add_mapping(tech_pvt->local_sdp_audio_port + 1, SWITCH_NAT_UDP, &rtcp_port, SWITCH_FALSE); } else { /* No NAT detected */ use_ip = tech_pvt->profile->rtpip; @@ -2726,12 +2727,23 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f (tech_pvt->stun_flags & STUN_FLAG_FUNNY) ? 1 : 0); } - if ((val = switch_channel_get_variable(tech_pvt->channel, "rtcp_interval_msec")) || (val = tech_pvt->profile->rtcp_interval_msec)) { - int interval = atoi(val); - if (interval < 100 || interval > 5000) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Invalid rtcp interval spec [%d] must be between 100 and 5000\n", interval); + printf("WTF [%s][%s]\n", switch_channel_get_variable(tech_pvt->channel, "rtcp_audio_interval_msec"), tech_pvt->profile->rtcp_audio_interval_msec); + + if ((val = switch_channel_get_variable(tech_pvt->channel, "rtcp_audio_interval_msec")) || (val = tech_pvt->profile->rtcp_audio_interval_msec)) { + const char *rport = switch_channel_get_variable(tech_pvt->channel, "sip_remote_audio_rtcp_port"); + switch_port_t remote_port = 0; + if (rport) { + remote_port = atoi(rport); + } + if (!strcasecmp(val, "passthru")) { + switch_rtp_activate_rtcp(tech_pvt->rtp_session, -1, remote_port); } else { - switch_rtp_activate_rtcp(tech_pvt->rtp_session, interval); + int interval = atoi(val); + if (interval < 100 || interval > 5000) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Invalid rtcp interval spec [%d] must be between 100 and 5000\n", interval); + } else { + switch_rtp_activate_rtcp(tech_pvt->rtp_session, interval, remote_port); + } } } @@ -2949,6 +2961,27 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f switch_channel_set_variable_printf(tech_pvt->channel, "sip_use_video_pt", "%d", tech_pvt->video_agreed_pt); tech_pvt->video_ssrc = switch_rtp_get_ssrc(tech_pvt->rtp_session); switch_channel_set_variable_printf(tech_pvt->channel, "rtp_use_video_ssrc", "%u", tech_pvt->ssrc); + + + if ((val = switch_channel_get_variable(tech_pvt->channel, "rtcp_audio_interval_msec")) || (val = tech_pvt->profile->rtcp_audio_interval_msec)) { + const char *rport = switch_channel_get_variable(tech_pvt->channel, "sip_remote_video_rtcp_port"); + switch_port_t remote_port = 0; + if (rport) { + remote_port = atoi(rport); + } + if (!strcasecmp(val, "passthru")) { + switch_rtp_activate_rtcp(tech_pvt->rtp_session, -1, remote_port); + } else { + int interval = atoi(val); + if (interval < 100 || interval > 5000) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Invalid rtcp interval spec [%d] must be between 100 and 5000\n", interval); + } else { + switch_rtp_activate_rtcp(tech_pvt->rtp_session, interval, remote_port); + } + } + } + + } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err)); switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); @@ -3378,6 +3411,11 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * } else if (m->m_type == sdp_media_audio && m->m_port && !got_audio) { sdp_rtpmap_t *map; for (attr = m->m_attributes; attr; attr = attr->a_next) { + + if (!strcasecmp(attr->a_name, "rtcp") && attr->a_value) { + switch_channel_set_variable(tech_pvt->channel, "sip_remote_audio_rtcp_port", attr->a_value); + } + if (!strcasecmp(attr->a_name, "ptime") && attr->a_value) { ptime = atoi(attr->a_value); } else if (!strcasecmp(attr->a_name, "maxptime") && attr->a_value) { @@ -3708,6 +3746,9 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * if (!strcasecmp(attr->a_name, "framerate") && attr->a_value) { framerate = atoi(attr->a_value); } + if (!strcasecmp(attr->a_name, "rtcp") && attr->a_value) { + switch_channel_set_variable(tech_pvt->channel, "sip_remote_video_rtcp_port", attr->a_value); + } } if (!(rm_encoding = map->rm_encoding)) { rm_encoding = ""; diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 0cacc3bded..c2b2b457d7 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -195,6 +195,7 @@ struct switch_rtp { uint32_t ms_per_packet; switch_port_t local_port; switch_port_t remote_port; + switch_port_t remote_rtcp_port; uint32_t stuncount; uint32_t funny_stun; uint32_t default_stuncount; @@ -773,10 +774,8 @@ static switch_status_t enable_remote_rtcp_socket(switch_rtp_t *rtp_session, cons if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { - rtp_session->rtcp_remote_addr = rtp_session->remote_addr; - if (switch_sockaddr_info_get(&rtp_session->rtcp_remote_addr, rtp_session->remote_host_str, SWITCH_UNSPEC, - rtp_session->remote_port + 1, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS || !rtp_session->rtcp_remote_addr) { + rtp_session->remote_rtcp_port, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS || !rtp_session->rtcp_remote_addr) { *err = "RTCP Remote Address Error!"; return SWITCH_STATUS_FALSE; } @@ -1248,6 +1247,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session { switch_rtp_t *rtp_session = NULL; switch_core_session_t *session = switch_core_memory_pool_get_data(pool, "__session"); + switch_channel_t *channel = NULL; + + if (session) channel = switch_core_session_get_channel(session); *new_rtp_session = NULL; @@ -1303,13 +1305,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session rtp_session->payload = payload; - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { - rtp_session->rtcp_send_msg.header.version = 2; - rtp_session->rtcp_send_msg.header.p = 0; - rtp_session->rtcp_send_msg.header.type = 200; - rtp_session->rtcp_send_msg.header.count = 0; - rtp_session->rtcp_send_msg.header.length = htons(6); - } + + rtp_session->rtcp_send_msg.header.version = 2; + rtp_session->rtcp_send_msg.header.p = 0; + rtp_session->rtcp_send_msg.header.type = 200; + rtp_session->rtcp_send_msg.header.count = 0; + rtp_session->rtcp_send_msg.header.length = htons(6); + switch_rtp_set_interval(rtp_session, ms_per_packet, samples_per_interval); rtp_session->conf_samples_per_interval = samples_per_interval; @@ -1341,12 +1343,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session switch_clear_flag_locked(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); } + switch_channel_set_private(channel, "__rtcp_audio_rtp_session", rtp_session); + #ifdef ENABLE_ZRTP if (zrtp_on) { switch_rtp_t *master_rtp_session = NULL; int initiator = 0; - switch_channel_t *channel = switch_core_session_get_channel(session); const char *zrtp_enabled = switch_channel_get_variable(channel, "zrtp_secure_media"); const char *srtp_enabled = switch_channel_get_variable(channel, "sip_secure_media"); @@ -1531,14 +1534,24 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t * return SWITCH_STATUS_SUCCESS; } -SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_session, int send_rate) +SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_session, int send_rate, switch_port_t remote_port) { const char *err = NULL; switch_set_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTCP send rate is: %d and packet rate is: %d\n", send_rate, rtp_session->ms_per_packet); - rtp_session->rtcp_interval = send_rate/(rtp_session->ms_per_packet/1000); + if (!(rtp_session->remote_rtcp_port = remote_port)) { + rtp_session->remote_rtcp_port = rtp_session->remote_port + 1; + } + + if (send_rate == -1) { + switch_set_flag(rtp_session, SWITCH_RTP_FLAG_RTCP_PASSTHRU); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "RTCP passthru enabled. Remote Port: %d\n", rtp_session->remote_rtcp_port); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "RTCP send rate is: %d and packet rate is: %d Remote Port: %d\n", + send_rate, rtp_session->ms_per_packet, rtp_session->remote_rtcp_port); + rtp_session->rtcp_interval = send_rate/(rtp_session->ms_per_packet/1000); + } return enable_local_rtcp_socket(rtp_session, &err) || enable_remote_rtcp_socket(rtp_session, &err); @@ -2236,6 +2249,68 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (rtcp_poll_status == SWITCH_STATUS_SUCCESS) { rtcp_status = read_rtcp_packet(rtp_session, &rtcp_bytes, flags); + + if (rtcp_status == SWITCH_STATUS_SUCCESS && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_RTCP_PASSTHRU)) { + switch_core_session_t *session = switch_core_memory_pool_get_data(rtp_session->pool, "__session"); + switch_channel_t *channel = switch_core_session_get_channel(session); + + const char *uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + if (uuid) { + switch_core_session_t *other_session; + switch_rtp_t *other_rtp_session = NULL; + + if ((other_session = switch_core_session_locate(uuid))) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + if ((other_rtp_session = switch_channel_get_private(other_channel, "__rtcp_audio_rtp_session")) && + switch_test_flag(other_rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + *other_rtp_session->rtcp_send_msg.body = *rtp_session->rtcp_recv_msg.body; + + if (switch_test_flag(other_rtp_session, SWITCH_RTP_FLAG_SECURE_SEND)) { + int sbytes = (int) rtcp_bytes; + int stat = srtp_protect_rtcp(other_rtp_session->send_ctx, &other_rtp_session->rtcp_send_msg.header, &sbytes); + if (stat) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: SRTP RTCP protection failed with code %d\n", stat); + } + rtcp_bytes = sbytes; + } + +#ifdef ENABLE_ZRTP + /* ZRTP Send */ + if (1) { + unsigned int sbytes = (int) bytes; + zrtp_status_t stat = zrtp_status_fail; + + stat = zrtp_process_rtcp(other_rtp_session->zrtp_stream, (void *) &other_rtp_session->rtcp_send_msg, &sbytes); + + switch (stat) { + case zrtp_status_ok: + break; + case zrtp_status_drop: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection drop with code %d\n", stat); + ret = (int) bytes; + goto end; + break; + case zrtp_status_fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection fail with code %d\n", stat); + break; + default: + break; + } + + bytes = sbytes; + } +#endif + if (switch_socket_sendto(other_rtp_session->rtcp_sock_output, other_rtp_session->rtcp_remote_addr, 0, + (const char*)&other_rtp_session->rtcp_send_msg, &rtcp_bytes ) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"RTCP packet not written\n"); + } + + + } + } + } + + } } } @@ -3244,7 +3319,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_session->last_write_ts = this_ts; - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP) && + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP) && !switch_test_flag(rtp_session, SWITCH_RTP_FLAG_RTCP_PASSTHRU) && rtp_session->rtcp_interval && (rtp_session->stats.outbound.packet_count % rtp_session->rtcp_interval) == 0) { struct switch_rtcp_senderinfo* sr = (struct switch_rtcp_senderinfo*)rtp_session->rtcp_send_msg.body;