Add rtcp-mux support
authorMark Michelson <mmichelson@digium.com>
Tue, 7 Mar 2017 20:13:02 +0000 (14:13 -0600)
committerMark Michelson <mmichelson@digium.com>
Wed, 15 Mar 2017 21:34:13 +0000 (16:34 -0500)
This commit adds support for RFC 5761: Multiplexing RTP Data and Control
Packets on a Single Port. Specifically, it enables the feature when
using chan_pjsip.

A new option, "rtcp_mux" has been added to endpoint configuration in
pjsip.conf. If set, then Asterisk will attempt to use rtcp-mux with
whatever it communicates with. Asterisk follows the rules set forth in
RFC 5761 with regards to falling back to standard RTCP behavior if the
far end does not indicate support for rtcp-mux.

The lion's share of the changes in this commit are in
res_rtp_asterisk.c. This is because it was pretty much hard wired to
have an RTP and an RTCP transport. The strategy used here is that when
rtcp-mux is enabled, the current RTCP transport and its trappings (such
as DTLS SSL session) are freed, and the RTCP session instead just
mooches off the RTP session. This leads to a lot of specialized if
statements throughout.

ASTERISK-26732 #close
Reported by Dan Jenkins

Change-Id: If46a93ba1282418d2803e3fd7869374da8b77ab5

CHANGES
UPGRADE.txt
contrib/ast-db-manage/config/versions/15db7b91a97a_add_rtcp_mux.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
include/asterisk/rtp_engine.h
res/res_pjsip.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_sdp_rtp.c
res/res_rtp_asterisk.c

diff --git a/CHANGES b/CHANGES
index 03b2f78..4e9df4f 100644 (file)
--- 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 ------------
 ------------------------------------------------------------------------------
index 569cc92..2275580 100644 (file)
 === 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 (file)
index 0000000..50d3ee3
--- /dev/null
@@ -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')
index 09ace07..fb04513 100644 (file)
@@ -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;
 };
 
 /*!
index 26dd451..d4d3f70 100644 (file)
@@ -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];
 };
index c0ae331..6544e70 100644 (file)
@@ -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 */
index e1800e2..f4df498 100644 (file)
                                                to the receiving one.
                                        </para></description>
                                </configOption>
+                               <configOption name="rtcp_mux" default="no">
+                                       <synopsis>Enable RFC 5761 RTCP multiplexing on the RTP port</synopsis>
+                                       <description><para>
+                                               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.
+                                       </para></description>
+                               </configOption>
                        </configObject>
                        <configObject name="auth">
                                <synopsis>Authentication type</synopsis>
index 1111664..c8ff427 100644 (file)
@@ -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");
index e32d2b6..9f39256 100644 (file)
@@ -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);
index 28ddeca..a67bc81 100644 (file)
@@ -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;
-                       }
-
-                       /* 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;
-                       }
-
-                       ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
-                       rtp->rtcp->schedid = -1;
+                       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;
+                               }
 #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);
-                       }
+                               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);
+                               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);
                        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);
        }