diff --git a/CHANGES b/CHANGES
index 03b2f78bf7..4e9df4fa58 100644
--- a/CHANGES
+++ b/CHANGES
@@ -140,6 +140,13 @@ res_pjsip_endpoint_identifier_ip
appropriate, as it now matches inbound requests on more than just IP
address.
+res_rtp_asterisk
+-----------------
+ * The RTP layer of Asterisk now has support for RFC 5761: "Multiplexing RTP
+ Data and Control Packets on a Single Port." So far, the only channel driver
+ that supports this feature is chan_pjsip. You can set "rtcp_mux = yes" on
+ a PJSIP endpoint in pjsip.conf to enable the feature.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 14.2.0 to Asterisk 14.3.0 ------------
------------------------------------------------------------------------------
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 569cc92227..2275580cab 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -23,9 +23,23 @@
=== UPGRADE-14.txt -- Upgrade info for 13 to 14
===========================================================
-Build System:
- - The LOW_MEMORY compile option no longer disables inline API. To disable
- inline API you must use the DISABLE_INLINE option.
+From 14.3.0 to 14.4.0:
+
+res_rtp_asterisk:
+ - The RTP layer of Asterisk now has support for RFC 5761: "Multiplexing RTP
+ Data and Control Packets on a Single Port." So far, the only channel driver
+ that supports this feature is chan_pjsip. You can set "rtcp_mux = yes" on
+ a PJSIP endpoint in pjsip.conf to enable the feature.
+
+New in 14.0.0
+
+ARI:
+ - The policy for when to send "Dial" events has changed. Previously, "Dial"
+ events were sent on the calling channel's topic. However, starting in Asterisk
+ 14, if there is no calling channel on which to send the event, the event is
+ instead sent on the called channel's topic. Note that for the ARI channels
+ resource's dial operation, this means that the "Dial" events will always be
+ sent on the called channel's topic.
Queue:
- When reloading the members of a queue, the members added dynamically (i.e.
diff --git a/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py b/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py
new file mode 100644
index 0000000000..50d3ee3389
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py
@@ -0,0 +1,30 @@
+"""empty message
+
+Revision ID: 15db7b91a97a
+Revises: 465e70e8c337
+Create Date: 2017-03-08 16:56:38.108162
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '15db7b91a97a'
+down_revision = '465e70e8c337'
+
+from alembic import op
+import sqlalchemy as sa
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+ ############################# Enums ##############################
+
+ # yesno_values have already been created, so use postgres enum object
+ # type to get around "already created" issue - works okay with mysql
+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+ op.add_column('ps_endpoints', sa.Column('rtcp_mux', yesno_values))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'rtcp_mux')
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 09ace0775b..fb04513070 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -676,6 +676,8 @@ struct ast_sip_endpoint_media_configuration {
unsigned int g726_non_standard;
/*! Bind the RTP instance to the media_address */
unsigned int bind_rtp_to_media_address;
+ /*! Use RTCP-MUX */
+ unsigned int rtcp_mux;
};
/*!
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 26dd451a79..d4d3f705af 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -85,6 +85,8 @@ struct ast_sip_session_media {
unsigned int remotely_held:1;
/*! \brief Stream is on hold by local side */
unsigned int locally_held:1;
+ /*! \brief Does remote support rtcp_mux */
+ unsigned int remote_rtcp_mux:1;
/*! \brief Stream type this session media handles */
char stream_type[1];
};
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index c0ae331557..6544e70a46 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -237,6 +237,15 @@ enum ast_rtp_instance_stat {
AST_RTP_INSTANCE_STAT_RXOCTETCOUNT,
};
+enum ast_rtp_instance_rtcp {
+ /*! RTCP should not be sent/received */
+ AST_RTP_INSTANCE_RTCP_DISABLED = 0,
+ /*! RTCP should be sent/received based on standard port rules */
+ AST_RTP_INSTANCE_RTCP_STANDARD,
+ /*! RTCP should be sent/received on the same port as RTP */
+ AST_RTP_INSTANCE_RTCP_MUX,
+};
+
/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */
/*! DTMF (RFC2833) */
#define AST_RTP_DTMF (1 << 0)
@@ -447,6 +456,8 @@ struct ast_rtp_engine_ice {
void (*turn_request)(struct ast_rtp_instance *instance, enum ast_rtp_ice_component_type component,
enum ast_transport transport, const char *server, unsigned int port,
const char *username, const char *password);
+ /*! Callback to alter the number of ICE components on a session */
+ void (*change_components)(struct ast_rtp_instance *instance, int num_components);
};
/*! \brief DTLS setup types */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index e1800e2dea..f4df498364 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -950,6 +950,16 @@
to the receiving one.
+
+ Enable RFC 5761 RTCP multiplexing on the RTP port
+
+ With this option enabled, Asterisk will attempt to negotiate the use of the "rtcp-mux"
+ attribute on all media streams. This will result in RTP and RTCP being sent and received
+ on the same port. This shifts the demultiplexing logic to the application rather than
+ the transport layer. This option is useful when interoperating with WebRTC endpoints
+ since they mandate this option's use.
+
+
Authentication type
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 1111664dc3..c8ff42708b 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1937,6 +1937,7 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "preferred_codec_only", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, preferred_codec_only));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtcp_mux));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index e32d2b65f8..9f39256e56 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -169,6 +169,23 @@ static int rtp_check_timeout(const void *data)
return 0;
}
+/*!
+ * \brief Enable RTCP on an RTP session.
+ */
+static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
+ const struct pjmedia_sdp_media *remote_media)
+{
+ enum ast_rtp_instance_rtcp rtcp_type;
+
+ if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux) {
+ rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
+ } else {
+ rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD;
+ }
+
+ ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type);
+}
+
/*! \brief Internal function which creates an RTP instance */
static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
{
@@ -186,7 +203,6 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
return -1;
}
- ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
@@ -569,6 +585,13 @@ static void process_ice_attributes(struct ast_sip_session *session, struct ast_s
continue;
}
+ if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux && candidate.id > 1) {
+ /* Remote side may have offered RTP and RTCP candidates. However, if we're using RTCP MUX,
+ * then we should ignore RTCP candidates.
+ */
+ continue;
+ }
+
candidate.foundation = foundation;
candidate.transport = transport;
@@ -865,6 +888,26 @@ static int setup_media_encryption(struct ast_sip_session *session,
return 0;
}
+static void set_ice_components(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
+{
+ struct ast_rtp_engine_ice *ice;
+
+ ast_assert(session_media->rtp != NULL);
+
+ ice = ast_rtp_instance_get_ice(session_media->rtp);
+ if (!session->endpoint->media.rtp.ice_support || !ice) {
+ return;
+ }
+
+ if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux) {
+ /* We both support RTCP mux. Only one ICE component necessary */
+ ice->change_components(session_media->rtp, 1);
+ } else {
+ /* They either don't support RTCP mux or we don't know if they do yet. */
+ ice->change_components(session_media->rtp, 2);
+ }
+}
+
/*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
@@ -909,6 +952,11 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
return -1;
}
+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);
+ set_ice_components(session, session_media);
+
+ enable_rtcp(session, session_media, stream);
+
res = setup_media_encryption(session, session_media, sdp, stream);
if (res) {
if (!session->endpoint->media.rtp.encryption_optimistic ||
@@ -1087,6 +1135,9 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
return -1;
}
+ set_ice_components(session, session_media);
+ enable_rtcp(session, session_media, NULL);
+
if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) ||
!(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
return -1;
@@ -1250,6 +1301,12 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
attr->name = !session_media->locally_held ? STR_SENDRECV : STR_SENDONLY;
media->attr[media->attr_count++] = attr;
+ /* If we've got rtcp-mux enabled, just unconditionally offer it in all SDPs */
+ if (session->endpoint->media.rtcp_mux) {
+ attr = pjmedia_sdp_attr_create(pool, "rtcp-mux", NULL);
+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+ }
+
/* Add the media stream to the SDP */
sdp->media[sdp->media_count++] = media;
@@ -1284,6 +1341,11 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL);
+ set_ice_components(session, session_media);
+
+ enable_rtcp(session, session_media, remote_stream);
+
res = setup_media_encryption(session, session_media, remote, remote_stream);
if (!session->endpoint->media.rtp.encryption_optimistic && res) {
/* If optimistic encryption is disabled and crypto should have been enabled but was not
@@ -1315,7 +1377,9 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0));
- ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
+ if (!session->endpoint->media.rtcp_mux || !session_media->remote_rtcp_mux) {
+ ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
+ }
/* If ICE support is enabled find all the needed attributes */
process_ice_attributes(session, session_media, remote, remote_stream);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 28ddeca420..a67bc8135b 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -329,6 +329,7 @@ struct ast_rtp {
struct ao2_container *ice_active_remote_candidates; /*!< The remote ICE candidates */
struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */
struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */
+ unsigned int ice_num_components; /*!< The number of ICE components */
#endif
#ifdef HAVE_OPENSSL_SRTP
@@ -417,6 +418,9 @@ struct ast_rtcp {
* own address every time
*/
char *local_addr_str;
+ enum ast_rtp_instance_rtcp type;
+ /* Buffer for frames created during RTCP interpretation */
+ unsigned char frame_buf[512 + AST_FRIENDLY_OFFSET];
};
struct rtp_red {
@@ -658,6 +662,22 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
pj_ice_sess_change_role(rtp->ice, role);
}
+ /* If we only have one component now, and we previously set up TURN for RTCP,
+ * we need to destroy that TURN socket.
+ */
+ if (rtp->ice_num_components == 1 && rtp->turn_rtcp) {
+ struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_STATE_WAIT_TIME, 1000));
+ struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
+
+ ast_mutex_lock(&rtp->lock);
+ pj_turn_sock_destroy(rtp->turn_rtcp);
+ rtp->turn_state = PJ_TURN_STATE_NULL;
+ while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
+ ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+ }
+ ast_mutex_unlock(&rtp->lock);
+ }
+
return res;
}
@@ -773,11 +793,12 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
}
- if (!has_rtcp) {
+ /* If we're only dealing with one ICE component, then we don't care about the lack of RTCP candidates */
+ if (!has_rtcp && rtp->ice_num_components > 1) {
ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
}
- if (has_rtp && has_rtcp) {
+ if (has_rtp && (has_rtcp || rtp->ice_num_components == 1)) {
pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
char reason[80];
@@ -1269,6 +1290,21 @@ static char *generate_random_string(char *buf, size_t size)
return buf;
}
+static void ast_rtp_ice_change_components(struct ast_rtp_instance *instance, int num_components)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+ /* Don't do anything if ICE is unsupported or if we're not changing the
+ * number of components
+ */
+ if (!icesupport || !rtp->ice || rtp->ice_num_components == num_components) {
+ return;
+ }
+
+ rtp->ice_num_components = num_components;
+ ice_reset_session(instance);
+}
+
/* ICE RTP Engine interface declaration */
static struct ast_rtp_engine_ice ast_rtp_ice = {
.set_authentication = ast_rtp_ice_set_authentication,
@@ -1281,6 +1317,7 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
.ice_lite = ast_rtp_ice_lite,
.set_role = ast_rtp_ice_set_role,
.turn_request = ast_rtp_ice_turn_request,
+ .change_components = ast_rtp_ice_change_components,
};
#endif
@@ -1540,6 +1577,7 @@ static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ int rtcp_dtls_unique = (rtp->dtls.ssl != rtp->rtcp->dtls.ssl);
dtls_srtp_stop_timeout_timer(instance, rtp, 0);
@@ -1557,7 +1595,7 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
if (rtp->rtcp) {
dtls_srtp_stop_timeout_timer(instance, rtp, 1);
- if (rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp->dtls.ssl && rtcp_dtls_unique) {
SSL_free(rtp->rtcp->dtls.ssl);
rtp->rtcp->dtls.ssl = NULL;
ast_mutex_destroy(&rtp->rtcp->dtls.lock);
@@ -1785,7 +1823,7 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
#ifdef HAVE_OPENSSL_SRTP
dtls_perform_handshake(instance, &rtp->dtls, 0);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}
#endif
@@ -2025,7 +2063,7 @@ static int dtls_srtp_renegotiate(const void *data)
SSL_do_handshake(rtp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 0);
- if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp && rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_renegotiate(rtp->rtcp->dtls.ssl);
SSL_do_handshake(rtp->rtcp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 1);
@@ -2616,7 +2654,7 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
passwd = pj_str(rtp->local_passwd);
/* Create an ICE session for ICE negotiation */
- if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2,
+ if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, rtp->ice_num_components,
&ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &rtp->ice) == PJ_SUCCESS) {
/* Make this available for the callbacks */
rtp->ice->user_data = instance;
@@ -2625,9 +2663,10 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP,
TRANSPORT_SOCKET_RTP);
- /* Only add the RTCP candidates to ICE when replacing the session. New sessions
+ /* Only add the RTCP candidates to ICE when replacing the session and if
+ * the ICE session contains more than just an RTP component. New sessions
* handle this in a separate part of the setup phase */
- if (replace && rtp->rtcp) {
+ if (replace && rtp->rtcp && rtp->ice_num_components > 1) {
rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP,
TRANSPORT_SOCKET_RTCP);
@@ -2712,6 +2751,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
#ifdef HAVE_PJPROJECT
/* Create an ICE session for ICE negotiation */
if (icesupport) {
+ rtp->ice_num_components = 2;
ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
if (ice_create(instance, addr, x, 0)) {
ast_log(LOG_NOTICE, "Failed to start ICE session\n");
@@ -2721,7 +2761,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
}
}
#endif
-
/* Record any information we may need */
rtp->sched = sched;
@@ -4150,63 +4189,21 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
}
-static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_sockaddr addr;
- unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
- unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
- int res, packetwords, position = 0;
+ unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
+ int packetwords, position = 0;
int report_counter = 0;
struct ast_rtp_rtcp_report_block *report_block;
struct ast_frame *f = &ast_null_frame;
- /* Read in RTCP data from the socket */
- if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET,
- sizeof(rtcpdata) - AST_FRIENDLY_OFFSET,
- 0, &addr)) < 0) {
- ast_assert(errno != EBADF);
- if (errno != EAGAIN) {
- ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
- (errno) ? strerror(errno) : "Unspecified");
- return NULL;
- }
- return &ast_null_frame;
- }
-
- /* If this was handled by the ICE session don't do anything further */
- if (!res) {
- return &ast_null_frame;
- }
-
- if (!*(rtcpdata + AST_FRIENDLY_OFFSET)) {
- struct sockaddr_in addr_tmp;
- struct ast_sockaddr addr_v4;
-
- if (ast_sockaddr_is_ipv4(&addr)) {
- ast_sockaddr_to_sin(&addr, &addr_tmp);
- } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
- ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
- ast_sockaddr_stringify(&addr));
- ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
- } else {
- ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
- ast_sockaddr_stringify(&addr));
- return &ast_null_frame;
- }
- if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, rtcpdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT)) {
- ast_sockaddr_from_sin(&addr, &addr_tmp);
- ast_sockaddr_copy(&rtp->rtcp->them, &addr);
- }
- return &ast_null_frame;
- }
-
- packetwords = res / 4;
+ packetwords = size / 4;
if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
/* Send to whoever sent to us */
- if (ast_sockaddr_cmp(&rtp->rtcp->them, &addr)) {
- ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+ if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
+ ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (rtpdebug) {
ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
@@ -4214,7 +4211,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
}
}
- ast_debug(1, "Got RTCP report of %d bytes\n", res);
+ ast_debug(1, "Got RTCP report of %zu bytes\n", size);
while (position < packetwords) {
int i, pt, rc;
@@ -4242,9 +4239,9 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
return &ast_null_frame;
}
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("\n\nGot RTCP from %s\n",
- ast_sockaddr_stringify(&addr));
+ ast_sockaddr_stringify(addr));
ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" :
(pt == RTCP_PT_RR) ? "Receiver Report" :
(pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");
@@ -4267,7 +4264,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
(unsigned int)ntohl(rtcpheader[i + 1]),
&rtcp_report->sender_information.ntp_timestamp);
rtcp_report->sender_information.rtp_timestamp = ntohl(rtcpheader[i + 2]);
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("NTP timestamp: %u.%06u\n",
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec,
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec);
@@ -4299,7 +4296,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
report_block->dlsr = ntohl(rtcpheader[i + 5]);
if (report_block->lsr
&& update_rtt_stats(rtp, report_block->lsr, report_block->dlsr)
- && rtcp_debug_test_addr(&addr)) {
+ && rtcp_debug_test_addr(addr)) {
struct timeval now;
unsigned int lsr_now, lsw, msw;
gettimeofday(&now, NULL);
@@ -4316,7 +4313,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
update_lost_stats(rtp, report_block->lost_count.packets);
rtp->rtcp->reported_jitter_count++;
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);
ast_verbose(" Packets lost so far: %u\n", report_block->lost_count.packets);
ast_verbose(" Highest sequence number: %u\n", report_block->highest_seq_no & 0x0000ffff);
@@ -4344,7 +4341,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
/* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
* object as a its data */
rtp->f.frametype = AST_FRAME_RTCP;
- rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
if (rc > 0) {
@@ -4367,7 +4364,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
case RTCP_PT_FUR:
/* Handle RTCP FIR as FUR */
case RTCP_PT_PSFB:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an RTCP Fast Update Request\n");
}
rtp->f.frametype = AST_FRAME_CONTROL;
@@ -4379,13 +4376,13 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
f = &rtp->f;
break;
case RTCP_PT_SDES:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an SDES from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
break;
case RTCP_PT_BYE:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received a BYE from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
@@ -4400,6 +4397,58 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
rtp->rtcp->rtcp_info = 1;
return f;
+
+}
+
+static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ struct ast_sockaddr addr;
+ unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned char *read_area = rtcpdata + AST_FRIENDLY_OFFSET;
+ size_t read_area_size = sizeof(rtcpdata) - AST_FRIENDLY_OFFSET;
+ int res;
+
+ /* Read in RTCP data from the socket */
+ if ((res = rtcp_recvfrom(instance, read_area, read_area_size,
+ 0, &addr)) < 0) {
+ ast_assert(errno != EBADF);
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
+ (errno) ? strerror(errno) : "Unspecified");
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ /* If this was handled by the ICE session don't do anything further */
+ if (!res) {
+ return &ast_null_frame;
+ }
+
+ if (!*(read_area)) {
+ struct sockaddr_in addr_tmp;
+ struct ast_sockaddr addr_v4;
+
+ if (ast_sockaddr_is_ipv4(&addr)) {
+ ast_sockaddr_to_sin(&addr, &addr_tmp);
+ } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
+ ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
+ ast_sockaddr_stringify(&addr));
+ ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
+ } else {
+ ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
+ ast_sockaddr_stringify(&addr));
+ return &ast_null_frame;
+ }
+ if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT)) {
+ ast_sockaddr_from_sin(&addr, &addr_tmp);
+ ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+ }
+ return &ast_null_frame;
+ }
+
+ return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
}
static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen)
@@ -4507,19 +4556,54 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
return 0;
}
+static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)
+{
+ uint8_t version;
+ uint8_t pt;
+ uint8_t m;
+
+ if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) {
+ return 0;
+ }
+
+ version = (packet[0] & 0XC0) >> 6;
+ if (version == 0) {
+ /* version 0 indicates this is a STUN packet and shouldn't
+ * be interpreted as a possible RTCP packet
+ */
+ return 0;
+ }
+
+ /* The second octet of a packet will be one of the following:
+ * For RTP: The marker bit (1 bit) and the RTP payload type (7 bits)
+ * For RTCP: The payload type (8)
+ *
+ * RTP has a forbidden range of payload types (64-95) since these
+ * will conflict with RTCP payload numbers if the marker bit is set.
+ */
+ m = packet[1] & 0x80;
+ pt = packet[1] & 0x7F;
+ if (m && pt >= 64 && pt <= 95) {
+ return 1;
+ }
+ return 0;
+}
+
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr addr;
int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno;
- unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp;
+ unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ size_t read_area_size = sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET;
+ unsigned int *rtpheader = (unsigned int*)(read_area), seqno, ssrc, timestamp;
RAII_VAR(struct ast_rtp_payload_type *, payload, NULL, ao2_cleanup);
struct ast_sockaddr remote_address = { {0,} };
struct frame_list frames;
/* If this is actually RTCP let's hop on over and handle it */
if (rtcp) {
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
return ast_rtcp_read(instance);
}
return &ast_null_frame;
@@ -4531,8 +4615,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
}
/* Actually read in the data from the socket */
- if ((res = rtp_recvfrom(instance, rtp->rawdata + AST_FRIENDLY_OFFSET,
- sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0,
+ if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0,
&addr)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
@@ -4548,12 +4631,17 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
return &ast_null_frame;
}
+ /* This could be a multiplexed RTCP packet. If so, be sure to interpret it correctly */
+ if (rtcp_mux(rtp, read_area)) {
+ return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
+ }
+
/* Make sure the data that was read in is actually enough to make up an RTP packet */
if (res < hdrlen) {
/* If this is a keepalive containing only nulls, don't bother with a warning */
int i;
for (i = 0; i < res; ++i) {
- if (rtp->rawdata[AST_FRIENDLY_OFFSET + i] != '\0') {
+ if (read_area[i] != '\0') {
ast_log(LOG_WARNING, "RTP Read too short\n");
return &ast_null_frame;
}
@@ -4580,7 +4668,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
ast_sockaddr_stringify(&addr));
return &ast_null_frame;
}
- if ((ast_stun_handle_packet(rtp->s, &addr_tmp, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) &&
+ if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) &&
ast_sockaddr_isnull(&remote_address)) {
ast_sockaddr_from_sin(&addr, &addr_tmp);
ast_rtp_instance_set_remote_address(instance, &addr);
@@ -4629,7 +4717,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* do not update the originally given address, but only the remote */
ast_rtp_instance_set_incoming_source_address(instance, &addr);
ast_sockaddr_copy(&remote_address, &addr);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(&addr) + 1);
}
@@ -4696,7 +4784,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* Remove any padding bytes that may be present */
if (padding) {
- res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1];
+ res -= read_area[res - 1];
}
/* Skip over any CSRC fields */
@@ -4770,11 +4858,11 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
* by passing the pointer to the frame list to it so that the method
* can append frames to the list as needed.
*/
- process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
+ process_dtmf_rfc2833(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
} else if (payload->rtp_code == AST_RTP_CISCO_DTMF) {
- f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
+ f = process_dtmf_cisco(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else if (payload->rtp_code == AST_RTP_CN) {
- f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
+ f = process_cn_rfc3389(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else {
ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n",
payloadtype,
@@ -4830,7 +4918,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
rtp->f.src = "RTP";
rtp->f.mallocd = 0;
rtp->f.datalen = res - hdrlen;
- rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.data.ptr = read_area + hdrlen;
rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
rtp->f.seqno = seqno;
@@ -4941,19 +5029,29 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
if (value) {
struct ast_sockaddr local_addr;
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == value) {
ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance);
return;
}
- /* Setup RTCP to be activated on the next RTP write */
- if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) {
- return;
+
+ if (!rtp->rtcp) {
+ rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp));
+ if (!rtp->rtcp) {
+ return;
+ }
+ rtp->rtcp->s = -1;
+ rtp->rtcp->dtls.timeout_timer = -1;
+ rtp->rtcp->schedid = -1;
}
+ rtp->rtcp->type = value;
+
/* Grab the IP address and port we are going to use */
ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us);
- ast_sockaddr_set_port(&rtp->rtcp->us,
- ast_sockaddr_port(&rtp->rtcp->us) + 1);
+ if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ ast_sockaddr_set_port(&rtp->rtcp->us,
+ ast_sockaddr_port(&rtp->rtcp->us) + 1);
+ }
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) {
@@ -4963,6 +5061,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
}
+ ast_free(rtp->rtcp->local_addr_str);
rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr));
if (!rtp->rtcp->local_addr_str) {
ast_free(rtp->rtcp);
@@ -4970,43 +5069,67 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
return;
}
- if ((rtp->rtcp->s =
- create_new_socket("RTCP",
- ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
- AF_INET :
- ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
- AF_INET6 : -1)) < 0) {
- ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
- ast_free(rtp->rtcp->local_addr_str);
- ast_free(rtp->rtcp);
- rtp->rtcp = NULL;
- return;
- }
+ if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ /* We're either setting up RTCP from scratch or
+ * switching from MUX. Either way, we won't have
+ * a socket set up, and we need to set it up
+ */
+ if ((rtp->rtcp->s =
+ create_new_socket("RTCP",
+ ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
+ AF_INET :
+ ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
+ AF_INET6 : -1)) < 0) {
+ ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
+ ast_free(rtp->rtcp->local_addr_str);
+ ast_free(rtp->rtcp);
+ rtp->rtcp = NULL;
+ return;
+ }
- /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
- if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
- ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
- close(rtp->rtcp->s);
- ast_free(rtp->rtcp->local_addr_str);
- ast_free(rtp->rtcp);
- rtp->rtcp = NULL;
- return;
+ /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
+ if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
+ ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp->local_addr_str);
+ ast_free(rtp->rtcp);
+ rtp->rtcp = NULL;
+ return;
+ }
+#ifdef HAVE_PJPROJECT
+ if (rtp->ice) {
+ rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
+ }
+#endif
+#ifdef HAVE_OPENSSL_SRTP
+ dtls_setup_rtcp(instance);
+#endif
+ } else {
+ struct ast_sockaddr addr;
+ /* RTCPMUX uses the same socket as RTP. If we were previously using standard RTCP
+ * then close the socket we previously created.
+ *
+ * It may seem as though there is a possible race condition here where we might try
+ * to close the RTCP socket while it is being used to send data. However, this is not
+ * a problem in practice since setting and adjusting of RTCP properties happens prior
+ * to activating RTP. It is not until RTP is activated that timers start for RTCP
+ * transmission
+ */
+ if (rtp->rtcp->s > -1) {
+ close(rtp->rtcp->s);
+ }
+ rtp->rtcp->s = rtp->s;
+ ast_rtp_instance_get_remote_address(instance, &addr);
+ ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+#ifdef HAVE_OPENSSL_SRTP
+ if (rtp->rtcp->dtls.ssl) {
+ SSL_free(rtp->rtcp->dtls.ssl);
+ }
+ rtp->rtcp->dtls.ssl = rtp->dtls.ssl;
+#endif
}
ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
- rtp->rtcp->schedid = -1;
-
-#ifdef HAVE_PJPROJECT
- if (rtp->ice) {
- rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
- }
-#endif
-
-#ifdef HAVE_OPENSSL_SRTP
- rtp->rtcp->dtls.timeout_timer = -1;
- dtls_setup_rtcp(instance);
-#endif
-
return;
} else {
if (rtp->rtcp) {
@@ -5021,9 +5144,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
}
rtp->rtcp->schedid = -1;
}
- close(rtp->rtcp->s);
+ if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
+ close(rtp->rtcp->s);
+ }
#ifdef HAVE_OPENSSL_SRTP
- if (rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_free(rtp->rtcp->dtls.ssl);
}
#endif
@@ -5065,10 +5190,12 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (!ast_sockaddr_isnull(addr)) {
- ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
+ if (rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
- /* Update the local RTCP address with what is being used */
- ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
+ /* Update the local RTCP address with what is being used */
+ ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
+ }
ast_sockaddr_copy(&rtp->rtcp->us, &local);
ast_free(rtp->rtcp->local_addr_str);
@@ -5356,7 +5483,7 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance)
dtls_perform_handshake(instance, &rtp->dtls, 0);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}