From 70d73cafb757281b85661e7295adbd4802a3abe8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 19 Apr 2010 19:07:23 -0500 Subject: [PATCH] FSRTP-14 --- src/include/switch.h | 1 + src/include/switch_rtcp_frame.h | 74 ++++ src/include/switch_rtp.h | 17 + src/include/switch_types.h | 35 +- src/mod/endpoints/mod_sofia/mod_sofia.c | 45 +++ src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 4 + src/mod/endpoints/mod_sofia/sofia_glue.c | 9 + src/switch_event.c | 1 + src/switch_rtp.c | 454 +++++++++++++++++++++-- 10 files changed, 600 insertions(+), 41 deletions(-) create mode 100644 src/include/switch_rtcp_frame.h diff --git a/src/include/switch.h b/src/include/switch.h index 7e93646e29..2b007d2425 100644 --- a/src/include/switch.h +++ b/src/include/switch.h @@ -116,6 +116,7 @@ #include "switch_utils.h" #include "switch_caller.h" #include "switch_frame.h" +#include "switch_rtcp_frame.h" #include "switch_module_interfaces.h" #include "switch_channel.h" #include "switch_buffer.h" diff --git a/src/include/switch_rtcp_frame.h b/src/include/switch_rtcp_frame.h new file mode 100644 index 0000000000..1c718a089f --- /dev/null +++ b/src/include/switch_rtcp_frame.h @@ -0,0 +1,74 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2009, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Sherwin Sim + * + * + * switch_rtcp_frame.h -- RTCP Frame Structure + * + */ +/*! \file switch_rtcp_frame.h + \brief RTCP Frame Structure +*/ + +#ifndef SWITCH_RTCP_FRAME_H +#define SWITCH_RTCP_FRAME_H + +#include + +SWITCH_BEGIN_EXTERN_C +/*! \brief An abstraction of a rtcp frame */ + struct switch_rtcp_frame { + + uint16_t report_count; + + uint16_t packet_type; + + uint32_t ssrc; + + uint32_t ntp_msw; + + uint32_t ntp_lsw; + + uint32_t timestamp; + + uint32_t packet_count; + + uint32_t octect_count; + +}; + +SWITCH_END_EXTERN_C +#endif +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index e3098d0035..4ad264ae8e 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -41,6 +41,7 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_RTP_MAX_BUF_LEN 16384 +#define SWITCH_RTCP_MAX_BUF_LEN 16384 #define SWITCH_RTP_MAX_CRYPTO_LEN 64 #define SWITCH_RTP_KEY_LEN 30 #define SWITCH_RTP_CRYPTO_KEY_32 "AES_CM_128_HMAC_SHA1_32" @@ -213,6 +214,13 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session); */ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_ice(switch_rtp_t *rtp_session, char *login, char *rlogin); +/*! + \brief Activate sending RTCP Sender Reports (SR's) + \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); + /*! \brief Acvite a jitter buffer on an RTP session \param rtp_session the rtp session @@ -347,6 +355,15 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read(switch_rtp_t *rtp_sessi */ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp_session, switch_frame_t *frame, switch_io_flag_t io_flags); + +/*! + \brief Read RTCP data from a given RTP session without copying + \param rtp_session the RTP session to read from + \param frame an RTCP frame to populate with information + \return the number of bytes read +*/ +SWITCH_DECLARE(switch_status_t) switch_rtcp_zerocopy_read_frame(switch_rtp_t *rtp_session, switch_rtcp_frame_t *frame); + SWITCH_DECLARE(void) rtp_flush_read_buffer(switch_rtp_t *rtp_session, switch_rtp_flush_t flush); /*! diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 3c624e922e..52f8ac1c24 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -534,7 +534,8 @@ typedef enum { SWITCH_ZRTP_FLAG_SECURE_MITM_RECV = (1 << 26), 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_VIDEO = (1 << 29), + SWITCH_RTP_FLAG_ENABLE_RTCP = (1 << 30) } switch_rtp_flag_enum_t; typedef uint32_t switch_rtp_flag_t; @@ -607,6 +608,35 @@ typedef struct { #pragma pack(pop, r1) #endif +#ifdef _MSC_VER +#pragma pack(push, r1, 1) +#endif + +#if SWITCH_BYTE_ORDER == __BIG_ENDIAN +typedef struct { + unsigned version:2; /* protocol version */ + unsigned p:1; /* padding flag */ + unsigned count:5; /* number of reception report blocks */ + unsigned type:8; /* packet type */ + unsigned length:16; /* length in 32-bit words - 1 */ +} switch_rtcp_hdr_t; + +#else /* BIG_ENDIAN */ + +typedef struct { + unsigned count:5; /* number of reception report blocks */ + unsigned p:1; /* padding flag */ + unsigned version:2; /* protocol version */ + unsigned type:8; /* packet type */ + unsigned length:16; /* length in 32-bit words - 1 */ +} switch_rtcp_hdr_t; + +#endif + +#ifdef _MSC_VER +#pragma pack(pop, r1) +#endif + /*! \enum switch_priority_t \brief Priority Indication @@ -1352,6 +1382,7 @@ typedef enum { SWITCH_EVENT_SERVER_DISCONNECTED, SWITCH_EVENT_SEND_INFO, SWITCH_EVENT_RECV_INFO, + SWITCH_EVENT_RECV_RTCP_MESSAGE, SWITCH_EVENT_CALL_SECURE, SWITCH_EVENT_NAT, SWITCH_EVENT_RECORD_START, @@ -1472,6 +1503,7 @@ typedef uint16_t switch_port_t; typedef uint8_t switch_payload_t; typedef struct switch_app_log switch_app_log_t; typedef struct switch_rtp switch_rtp_t; +typedef struct switch_rtcp switch_rtcp_t; typedef struct switch_core_session_message switch_core_session_message_t; typedef struct switch_event_header switch_event_header_t; typedef struct switch_event switch_event_t; @@ -1479,6 +1511,7 @@ typedef struct switch_event_subclass switch_event_subclass_t; typedef struct switch_event_node switch_event_node_t; typedef struct switch_loadable_module switch_loadable_module_t; typedef struct switch_frame switch_frame_t; +typedef struct switch_rtcp_frame switch_rtcp_frame_t; typedef struct switch_channel switch_channel_t; typedef struct switch_file_handle switch_file_handle_t; typedef struct switch_core_session switch_core_session_t; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 32512b4544..77510d4570 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -802,6 +802,7 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f switch_channel_t *channel = switch_core_session_get_channel(session); int payload = 0; uint32_t sanity = 1000; + switch_rtcp_frame_t rtcp_frame; switch_assert(tech_pvt != NULL); @@ -859,6 +860,50 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f } return status; } + + /* Try to read an RTCP frame, if successful raise an event */ + if (switch_rtcp_zerocopy_read_frame(tech_pvt->rtp_session, &rtcp_frame) == SWITCH_STATUS_SUCCESS) { + switch_event_t *event; + + if (switch_event_create(&event, SWITCH_EVENT_RECV_RTCP_MESSAGE) == SWITCH_STATUS_SUCCESS) { + char buf[30]; + + char* uuid = switch_core_session_get_uuid(session); + if (uuid) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session)); + } + + snprintf(buf, sizeof(buf), "%.8x", rtcp_frame.ssrc); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SSRC", buf); + + snprintf(buf, sizeof(buf), "%u", rtcp_frame.ntp_msw); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Most-Significant-Word", buf); + + snprintf(buf, sizeof(buf), "%u", rtcp_frame.ntp_lsw); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Least-Significant-Word", buf); + + snprintf(buf, sizeof(buf), "%u", rtcp_frame.timestamp); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "RTP-Timestamp", buf); + + snprintf(buf, sizeof(buf), "%u", rtcp_frame.packet_count); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sender-Packet-Count", buf); + + snprintf(buf, sizeof(buf), "%u", rtcp_frame.octect_count); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Octect-Packet-Count", buf); + + snprintf(buf, sizeof(buf), "%lu", tech_pvt->read_frame.timestamp); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-RTP-Timestamp", buf); + + snprintf(buf, sizeof(buf), "%u", tech_pvt->read_frame.rate); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "RTP-Rate", buf); + + snprintf(buf, sizeof(buf), "%lu", switch_time_now()); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Capture-Time", buf); + + switch_event_fire(&event); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Dispatched RTCP event\n"); + } + } /* Fast PASS! */ if (switch_test_flag((&tech_pvt->read_frame), SFF_PROXY_PACKET)) { diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index c03da5f648..ebf669028f 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -466,6 +466,7 @@ struct sofia_profile { char *record_path; char *presence_hosts; char *challenge_realm; + char *rtcp_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 6d21074516..71faa4e782 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -2459,6 +2459,8 @@ 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, "session-timeout")) { int v_session_timeout = atoi(val); if (v_session_timeout >= 0) { @@ -2996,6 +2998,8 @@ 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, "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 782e62dd90..1143fda5f0 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -2725,6 +2725,15 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f switch_rtp_activate_stun_ping(tech_pvt->rtp_session, tech_pvt->stun_ip, tech_pvt->stun_port, stun_ping, (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); + } else { + switch_rtp_activate_rtcp(tech_pvt->rtp_session, interval); + } + } if ((val = switch_channel_get_variable(tech_pvt->channel, "jitterbuffer_msec"))) { int len = atoi(val); diff --git a/src/switch_event.c b/src/switch_event.c index b65c7b1371..00a08fba1b 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -180,6 +180,7 @@ static char *EVENT_NAMES[] = { "SERVER_DISCONNECTED", "SEND_INFO", "RECV_INFO", + "RECV_RTCP_MESSAGE", "CALL_SECURE", "NAT", "RECORD_START", diff --git a/src/switch_rtp.c b/src/switch_rtp.c index fc929e8f74..101c721484 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -89,6 +89,11 @@ typedef struct { char body[SWITCH_RTP_MAX_BUF_LEN]; } rtp_msg_t; +typedef struct { + switch_rtcp_hdr_t header; + char body[SWITCH_RTCP_MAX_BUF_LEN]; +} rtcp_msg_t; + struct switch_rtp_vad_data { switch_core_session_t *session; switch_codec_t vad_codec; @@ -137,16 +142,17 @@ struct switch_rtp { * families are equal, sock_input == sock_output and only one socket is * used. */ - switch_socket_t *sock_input, *sock_output; - switch_pollfd_t *read_pollfd; + switch_socket_t *sock_input, *sock_output, *rtcp_sock_input, *rtcp_sock_output; + switch_pollfd_t *read_pollfd, *rtcp_read_pollfd; switch_pollfd_t *jb_pollfd; - switch_sockaddr_t *local_addr; + switch_sockaddr_t *local_addr, *rtcp_local_addr; rtp_msg_t send_msg; + rtcp_msg_t rtcp_send_msg; - switch_sockaddr_t *remote_addr; + switch_sockaddr_t *remote_addr, *rtcp_remote_addr; rtp_msg_t recv_msg; - + rtcp_msg_t rtcp_recv_msg; switch_sockaddr_t *remote_stun_addr; @@ -173,12 +179,13 @@ struct switch_rtp { switch_time_t last_write_timestamp; uint32_t flags; switch_memory_pool_t *pool; - switch_sockaddr_t *from_addr; + switch_sockaddr_t *from_addr, *rtcp_from_addr; char *rx_host; switch_port_t rx_port; char *ice_user; char *user_ice; char *timer_name; + char *local_host_str; char *remote_host_str; switch_time_t last_stun; uint32_t samples_per_interval; @@ -186,6 +193,7 @@ struct switch_rtp { uint32_t conf_samples_per_interval; uint32_t rsamples_per_interval; uint32_t ms_per_packet; + switch_port_t local_port; switch_port_t remote_port; uint32_t stuncount; uint32_t funny_stun; @@ -216,6 +224,8 @@ struct switch_rtp { switch_rtp_stats_t stats; uint32_t hot_hits; uint32_t sync_packets; + int rtcp_interval; + switch_bool_t rtcp_fresh_frame; #ifdef ENABLE_ZRTP zrtp_session_t *zrtp_session; @@ -225,10 +235,16 @@ struct switch_rtp { int zinit; #endif -#ifdef RTP_DEBUG_WRITE_DELTA switch_time_t send_time; -#endif +}; +struct switch_rtcp_senderinfo { + unsigned ssrc:32; + unsigned ntp_msw:32; + unsigned ntp_lsw:32; + unsigned ts:32; + unsigned pc:32; + unsigned oc:32; }; static int global_init = 0; @@ -284,7 +300,7 @@ static switch_status_t do_stun_ping(switch_rtp_t *rtp_session) switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_stun_addr, 0, (void *) packet, &bytes); rtp_session->stuncount = rtp_session->default_stuncount; - end: + end: WRITE_DEC(rtp_session); return status; @@ -324,7 +340,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session) switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) packet, &bytes); rtp_session->stuncount = rtp_session->default_stuncount; - end: + end: WRITE_DEC(rtp_session); return status; @@ -410,7 +426,7 @@ static void handle_ice(switch_rtp_t *rtp_session, void *data, switch_size_t len) switch_socket_sendto(rtp_session->sock_output, rtp_session->from_addr, 0, (void *) rpacket, &bytes); } - end: + end: READ_DEC(rtp_session); WRITE_DEC(rtp_session); @@ -750,6 +766,104 @@ SWITCH_DECLARE(void) switch_rtp_intentional_bugs(switch_rtp_t *rtp_session, swit rtp_session->rtp_bugs = bugs; } + +static switch_status_t enable_remote_rtcp_socket(switch_rtp_t *rtp_session, const char **err) { + + switch_status_t status = SWITCH_STATUS_SUCCESS; + + 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) { + *err = "RTCP Remote Address Error!"; + return SWITCH_STATUS_FALSE; + } + + if (rtp_session->rtcp_sock_input && switch_sockaddr_get_family(rtp_session->rtcp_remote_addr) == + switch_sockaddr_get_family(rtp_session->rtcp_local_addr)) { + rtp_session->rtcp_sock_output = rtp_session->rtcp_sock_input; + } else { + if (rtp_session->rtcp_sock_output && rtp_session->rtcp_sock_output != rtp_session->rtcp_sock_input) { + switch_socket_close(rtp_session->rtcp_sock_output); + } + if ((status = switch_socket_create(&rtp_session->rtcp_sock_output, + switch_sockaddr_get_family(rtp_session->rtcp_remote_addr), + SOCK_DGRAM, 0, rtp_session->pool)) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP Socket Error!"; + } + } + } else { + *err = "RTCP NOT ACTIVE!"; + } + + return status; + +} + +static switch_status_t enable_local_rtcp_socket(switch_rtp_t *rtp_session, const char **err) { + + const char *host = rtp_session->local_host_str; + switch_port_t port = rtp_session->local_port; + switch_socket_t *rtcp_new_sock = NULL, *rtcp_old_sock = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + char bufa[30]; + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + if (switch_sockaddr_info_get(&rtp_session->rtcp_local_addr, host, SWITCH_UNSPEC, port+1, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP Local Address Error!"; + goto done; + } + + if (switch_socket_create(&rtcp_new_sock, switch_sockaddr_get_family(rtp_session->rtcp_local_addr), SOCK_DGRAM, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP Socket Error!"; + goto done; + } + + if (switch_socket_opt_set(rtcp_new_sock, SWITCH_SO_REUSEADDR, 1) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP Socket Error!"; + goto done; + } + + if (switch_socket_bind(rtcp_new_sock, rtp_session->rtcp_local_addr) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP Bind Error!"; + goto done; + } + + if (switch_sockaddr_info_get(&rtp_session->rtcp_from_addr, switch_get_addr(bufa, sizeof(bufa), rtp_session->from_addr), + SWITCH_UNSPEC, switch_sockaddr_get_port(rtp_session->from_addr) + 1, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { + *err = "RTCP From Address Error!"; + goto done; + } + + rtcp_old_sock = rtp_session->rtcp_sock_input; + rtp_session->rtcp_sock_input = rtcp_new_sock; + rtcp_new_sock = NULL; + + switch_socket_create_pollset(&rtp_session->rtcp_read_pollfd, rtp_session->rtcp_sock_input, SWITCH_POLLIN | SWITCH_POLLERR, rtp_session->pool); + + done: + + if (*err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating rtcp [%s]\n", *err); + status = SWITCH_STATUS_FALSE; + } + + if (rtcp_new_sock) { + switch_socket_close(rtcp_new_sock); + } + + if (rtcp_old_sock) { + switch_socket_close(rtcp_old_sock); + } + } else { + status = SWITCH_STATUS_FALSE; + } + + return status; +} + SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_session, const char *host, switch_port_t port, const char **err) { switch_socket_t *new_sock = NULL, *old_sock = NULL; @@ -781,11 +895,17 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s goto done; } + + rtp_session->local_host_str = switch_core_strdup(rtp_session->pool, host); + rtp_session->local_port = port; + + if (switch_sockaddr_info_get(&rtp_session->local_addr, host, SWITCH_UNSPEC, port, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { *err = "Local Address Error!"; goto done; } + if (rtp_session->sock_input) { switch_rtp_kill_socket(rtp_session); } @@ -799,11 +919,12 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s *err = "Socket Error!"; goto done; } - + if (switch_socket_bind(new_sock, rtp_session->local_addr) != SWITCH_STATUS_SUCCESS) { *err = "Bind Error!"; goto done; } + #ifndef WIN32 len = sizeof(i); switch_socket_opt_set(new_sock, SWITCH_SO_NONBLOCK, TRUE); @@ -827,12 +948,15 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s } switch_socket_opt_set(new_sock, SWITCH_SO_NONBLOCK, FALSE); -#endif old_sock = rtp_session->sock_input; rtp_session->sock_input = new_sock; new_sock = NULL; + +#endif + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK)) { switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, TRUE); switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); @@ -840,11 +964,18 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s switch_socket_create_pollset(&rtp_session->read_pollfd, rtp_session->sock_input, SWITCH_POLLIN | SWITCH_POLLERR, rtp_session->pool); - status = SWITCH_STATUS_SUCCESS; - *err = "Success"; + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + if ((status = enable_local_rtcp_socket(rtp_session, err)) == SWITCH_STATUS_SUCCESS) { + *err = "Success"; + } + } else { + status = SWITCH_STATUS_SUCCESS; + *err = "Success"; + } + switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_IO); - done: + done: if (new_sock) { switch_socket_close(new_sock); @@ -854,6 +985,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s switch_socket_close(old_sock); } + if (rtp_session->ready != 1) { WRITE_DEC(rtp_session); READ_DEC(rtp_session); @@ -901,6 +1033,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ return SWITCH_STATUS_FALSE; } + switch_mutex_lock(rtp_session->write_mutex); rtp_session->remote_addr = remote_addr; @@ -923,6 +1056,10 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ } } + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + status = enable_remote_rtcp_socket(rtp_session, err); + } + switch_mutex_unlock(rtp_session->write_mutex); return status; @@ -985,8 +1122,11 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess policy->next = NULL; policy->key = (uint8_t *) crypto_key->key; - crypto_policy_set_rtcp_default(&policy->rtcp); - policy->rtcp.sec_serv = sec_serv_none; + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + crypto_policy_set_rtcp_default(&policy->rtcp); + policy->rtcp.sec_serv = sec_serv_none; + } policy->rtp.sec_serv = sec_serv_conf_and_auth; switch (direction) { @@ -1136,8 +1276,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session /* for from address on recvfrom calls */ switch_sockaddr_info_get(&rtp_session->from_addr, NULL, SWITCH_UNSPEC, 0, 0, pool); - - + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + switch_sockaddr_info_get(&rtp_session->rtcp_from_addr, NULL, SWITCH_UNSPEC, 0, 0, pool); + } rtp_session->seq = (uint16_t) rand(); rtp_session->ssrc = (uint32_t) ((intptr_t) rtp_session + (uint32_t) switch_epoch_time_now(NULL)); @@ -1162,6 +1303,14 @@ 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); + } + switch_rtp_set_interval(rtp_session, ms_per_packet, samples_per_interval); rtp_session->conf_samples_per_interval = samples_per_interval; @@ -1257,7 +1406,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session } } - end: + end: #endif @@ -1316,7 +1465,7 @@ SWITCH_DECLARE(switch_rtp_t *) switch_rtp_new(const char *rx_host, goto end; } - end: + end: if (rtp_session) { switch_mutex_unlock(rtp_session->flag_mutex); @@ -1382,6 +1531,19 @@ 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) +{ + 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); + + return enable_local_rtcp_socket(rtp_session, &err) || enable_remote_rtcp_socket(rtp_session, &err); + +} + SWITCH_DECLARE(switch_status_t) switch_rtp_activate_ice(switch_rtp_t *rtp_session, char *login, char *rlogin) { char ice_user[80]; @@ -1407,6 +1569,10 @@ static void ping_socket(switch_rtp_t *rtp_session) uint32_t o = UINT_MAX; switch_size_t len = sizeof(o); switch_socket_sendto(rtp_session->sock_input, rtp_session->local_addr, 0, (void *) &o, &len); + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP) && rtp_session->rtcp_sock_input) { + switch_socket_sendto(rtp_session->rtcp_sock_input, rtp_session->rtcp_local_addr, 0, (void *) &o, &len); + } } SWITCH_DECLARE(void) switch_rtp_break(switch_rtp_t *rtp_session) @@ -1443,6 +1609,16 @@ SWITCH_DECLARE(void) switch_rtp_kill_socket(switch_rtp_t *rtp_session) if (rtp_session->sock_output && rtp_session->sock_output != rtp_session->sock_input) { switch_socket_shutdown(rtp_session->sock_output, SWITCH_SHUTDOWN_READWRITE); } + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + if (rtp_session->rtcp_sock_input) { + ping_socket(rtp_session); + switch_socket_shutdown(rtp_session->rtcp_sock_input, SWITCH_SHUTDOWN_READWRITE); + } + if (rtp_session->rtcp_sock_output && rtp_session->rtcp_sock_output != rtp_session->rtcp_sock_input) { + switch_socket_shutdown(rtp_session->rtcp_sock_output, SWITCH_SHUTDOWN_READWRITE); + } + } } switch_mutex_unlock(rtp_session->flag_mutex); } @@ -1746,7 +1922,7 @@ static void do_flush(switch_rtp_t *rtp_session) } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CONSOLE, "%s FLUSH\n", switch_channel_get_name(switch_core_session_get_channel(session)) - ); + ); } } @@ -1841,18 +2017,126 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t return status; } +static switch_status_t read_rtcp_packet(switch_rtp_t *rtp_session, switch_size_t *bytes, switch_frame_flag_t *flags) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (!switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + return SWITCH_STATUS_FALSE; + } + + switch_assert(bytes); + + *bytes = sizeof(rtcp_msg_t); + if ((status = switch_socket_recvfrom(rtp_session->rtcp_from_addr, rtp_session->rtcp_sock_input, 0, (void *) &rtp_session->rtcp_recv_msg, bytes)) + != SWITCH_STATUS_SUCCESS) { + *bytes = 0; + } + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_RECV)) { + int sbytes = (int) *bytes; + err_status_t stat = 0; + + stat = srtp_unprotect_rtcp(rtp_session->recv_ctx, &rtp_session->rtcp_recv_msg.header, &sbytes); + + if (stat) { + if (++rtp_session->srtp_errs >= MAX_SRTP_ERRS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Error: SRTP RTCP unprotect failed with code %d%s\n", stat, + stat == err_status_replay_fail ? " (replay check failed)" : stat == + err_status_auth_fail ? " (auth check failed)" : ""); + return SWITCH_STATUS_FALSE; + } else { + sbytes = 0; + } + } else { + rtp_session->srtp_errs = 0; + } + + *bytes = sbytes; + + } + + +#ifdef ENABLE_ZRTP + /* ZRTP Recv */ + if (bytes) { + unsigned int sbytes = (int) bytes; + zrtp_status_t stat = 0; + + stat = zrtp_process_srtcp(rtp_session->zrtp_stream, (void *) &rtp_session->rtcp_recv_msg, &sbytes); + + switch (stat) { + case zrtp_status_ok: + bytes = sbytes; + break; + case zrtp_status_drop: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection drop with code %d\n", stat); + bytes = 0; + goto do_continue; + case zrtp_status_fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection fail with code %d\n", stat); + ret = -1; + goto end; + default: + break; + } + } +#endif + + + if (*bytes) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10,"Received an RTCP packet of length %lu bytes\n", *bytes); + if (rtp_session->rtcp_recv_msg.header.version == 2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10,"RTCP packet type is %d\n", rtp_session->rtcp_recv_msg.header.type); + if (rtp_session->rtcp_recv_msg.header.type == 200) { + struct switch_rtcp_senderinfo* sr = (struct switch_rtcp_senderinfo*)rtp_session->rtcp_recv_msg.body; + + rtp_session->rtcp_fresh_frame = 1; + + /* sender report */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10,"Received a SR with %d report blocks, " \ + "length in words = %d, " \ + "SSRC = 0x%X, " \ + "NTP MSW = %u, " \ + "NTP LSW = %u, " \ + "RTP timestamp = %u, " \ + "Sender Packet Count = %u, " \ + "Sender Octet Count = %u\n", + rtp_session->rtcp_recv_msg.header.count, + ntohs(rtp_session->rtcp_recv_msg.header.length), + ntohl(sr->ssrc), + ntohl(sr->ntp_msw), + ntohl(sr->ntp_lsw), + ntohl(sr->ts), + ntohl(sr->pc), + ntohl(sr->oc)); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received an unsupported RTCP packet version %d\nn", rtp_session->rtcp_recv_msg.header.version); + } + + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_type, switch_frame_flag_t *flags, switch_io_flag_t io_flags) { switch_core_session_t *session = switch_core_memory_pool_get_data(rtp_session->pool, "__session"); switch_channel_t *channel = NULL; switch_size_t bytes = 0; + switch_size_t rtcp_bytes = 0; switch_status_t status = SWITCH_STATUS_SUCCESS, poll_status = SWITCH_STATUS_SUCCESS; + switch_status_t rtcp_status = SWITCH_STATUS_SUCCESS, rtcp_poll_status = SWITCH_STATUS_SUCCESS; int check = 0; int ret = -1; int sleep_mss = 1000; int poll_sec = 5; int poll_loop = 0; int fdr = 0; + int rtcp_fdr = 0; int hot_socket = 0; if (session) { @@ -1904,7 +2188,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ } } - recvfrom: + recvfrom: bytes = 0; if (!switch_rtp_ready(rtp_session)) { @@ -1946,6 +2230,14 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ return_cng_frame(); } } + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP) && rtp_session->rtcp_read_pollfd) { + rtcp_poll_status = switch_poll(rtp_session->rtcp_read_pollfd, 1, &rtcp_fdr, 0); + + if (rtcp_poll_status == SWITCH_STATUS_SUCCESS) { + rtcp_status = read_rtcp_packet(rtp_session, &rtcp_bytes, flags); + } + } if (bytes < 0) { ret = (int) bytes; @@ -2210,7 +2502,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ We know the real rules here, but if we enforce them, it's an interop nightmare so, we put up with as much as we can so we don't have to deal with being punished for doing it right. Nice guys finish last! - */ + */ if (bytes && !switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) && !switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PASS_RFC2833) && rtp_session->recv_msg.header.pt == rtp_session->recv_te) { switch_size_t len = bytes - rtp_header_len; @@ -2324,7 +2616,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ return_cng_frame(); } - timer_check: + timer_check: if (do_cng) { uint8_t *data = (uint8_t *) rtp_session->recv_msg.body; @@ -2371,7 +2663,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ break; - do_continue: + do_continue: if (!bytes && !rtp_session->timer.interval) { switch_yield(sleep_mss); @@ -2391,7 +2683,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; } - end: + end: READ_DEC(rtp_session); @@ -2513,6 +2805,33 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_read(switch_rtp_t *rtp_session, void return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status_t) switch_rtcp_zerocopy_read_frame(switch_rtp_t *rtp_session, switch_rtcp_frame_t *frame) +{ + + if (!switch_test_flag(rtp_session, SWITCH_RTP_FLAG_ENABLE_RTCP)) { + return SWITCH_STATUS_FALSE; + } + + /* A fresh frame has been found! */ + if (rtp_session->rtcp_fresh_frame) { + struct switch_rtcp_senderinfo* sr = (struct switch_rtcp_senderinfo*)rtp_session->rtcp_recv_msg.body; + /* turn the flag off! */ + rtp_session->rtcp_fresh_frame = 0; + + frame->ssrc = ntohl(sr->ssrc); + frame->packet_type = rtp_session->rtcp_recv_msg.header.type; + frame->ntp_msw = ntohl(sr->ntp_msw); + frame->ntp_lsw = ntohl(sr->ntp_lsw); + frame->timestamp = ntohl(sr->ts); + frame->packet_count = ntohl(sr->pc); + frame->octect_count = ntohl(sr->oc); + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_TIMEOUT; +} + SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp_session, switch_frame_t *frame, switch_io_flag_t io_flags) { int bytes = 0; @@ -2625,10 +2944,11 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read(switch_rtp_t *rtp_sessi static int rtp_common_write(switch_rtp_t *rtp_session, rtp_msg_t *send_msg, void *data, uint32_t datalen, switch_payload_t payload, uint32_t timestamp, switch_frame_flag_t *flags) { - switch_size_t bytes; + switch_size_t bytes, rtcp_bytes; uint8_t send = 1; uint32_t this_ts = 0; int ret; + switch_time_t now; if (!switch_rtp_ready(rtp_session)) { return SWITCH_STATUS_FALSE; @@ -2860,15 +3180,14 @@ static int rtp_common_write(switch_rtp_t *rtp_session, } #endif + now = switch_time_now(); #ifdef RTP_DEBUG_WRITE_DELTA { - switch_time_t now = switch_time_now(); int delta = (int) (now - rtp_session->send_time) / 1000; printf("WRITE %d delta %d\n", (int) bytes, delta); - rtp_session->send_time = now; } #endif - + rtp_session->send_time = now; if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_DEBUG_RTP_WRITE)) { switch_core_session_t *session = switch_core_memory_pool_get_data(rtp_session->pool, "__session"); @@ -2924,6 +3243,61 @@ 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) && + 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; + + sr->ssrc = send_msg->header.ssrc; + sr->ntp_msw = htonl(rtp_session->send_time / 1000000 + 2208988800UL); + sr->ntp_lsw = htonl(rtp_session->send_time % 1000000 * ((UINT_MAX * 1.0)/ 1000000.0)); + sr->ts = send_msg->header.ts; + sr->pc = htonl(rtp_session->stats.outbound.packet_count); + sr->oc = htonl((rtp_session->stats.outbound.raw_bytes - rtp_session->stats.outbound.packet_count * sizeof(srtp_hdr_t))); + + rtcp_bytes = sizeof(switch_rtcp_hdr_t) + sizeof(struct switch_rtcp_senderinfo); + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_SEND)) { + int sbytes = (int) rtcp_bytes; + int stat = srtp_protect_rtcp(rtp_session->send_ctx, &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(rtp_session->zrtp_stream, (void *) &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(rtp_session->rtcp_sock_output, rtp_session->rtcp_remote_addr, 0, + (const char*)&rtp_session->rtcp_send_msg, &rtcp_bytes ) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"RTCP packet not written\n"); + } + } } if (rtp_session->remote_stun_addr) { @@ -2939,7 +3313,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, ret = (int) bytes; - end: + end: WRITE_DEC(rtp_session); @@ -3031,9 +3405,9 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra send_msg = frame->packet; /* - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { - send_msg->header.pt = rtp_session->payload; - } + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { + send_msg->header.pt = rtp_session->payload; + } */ if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, frame->packet, &bytes) != SWITCH_STATUS_SUCCESS) { @@ -3131,9 +3505,9 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra } /* - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { - send_msg->header.pt = rtp_session->payload; - } + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { + send_msg->header.pt = rtp_session->payload; + } */ return rtp_common_write(rtp_session, send_msg, data, len, payload, ts, &frame->flags); @@ -3234,7 +3608,7 @@ SWITCH_DECLARE(int) switch_rtp_write_manual(switch_rtp_t *rtp_session, ret = (int) bytes; - end: + end: WRITE_DEC(rtp_session);