res/res_rtp_asterisk: smoother can cause wrong timestamps if dtmf happen
[asterisk/asterisk.git] / res / res_rtp_asterisk.c
index c88c7b5..84b2088 100644 (file)
@@ -29,6 +29,7 @@
  */
 
 /*** MODULEINFO
+       <use type="external">openssl</use>
        <use type="external">pjproject</use>
        <support_level>core</support_level>
  ***/
 #include <signal.h>
 #include <fcntl.h>
 
-#ifdef HAVE_OPENSSL_SRTP
+#ifdef HAVE_OPENSSL
+#include <openssl/opensslconf.h>
+#include <openssl/opensslv.h>
+#if !defined(OPENSSL_NO_SRTP) && (OPENSSL_VERSION_NUMBER >= 0x10001000L)
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/bio.h>
+#if !defined(OPENSSL_NO_ECDH) && (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+#include <openssl/bn.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
+#endif
 #endif
 
 #ifdef HAVE_PJPROJECT
 #include "asterisk/module.h"
 #include "asterisk/rtp_engine.h"
 #include "asterisk/smoother.h"
+#include "asterisk/uuid.h"
 #include "asterisk/test.h"
+#include "asterisk/data_buffer.h"
+#ifdef HAVE_PJPROJECT
+#include "asterisk/res_pjproject.h"
+#endif
 
 #define MAX_TIMESTAMP_SKEW     640
 
 
 #define TURN_STATE_WAIT_TIME 2000
 
+#define DEFAULT_RTP_SEND_BUFFER_SIZE   250
+#define DEFAULT_RTP_RECV_BUFFER_SIZE   20
+
+/*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */
 #define RTCP_PT_FUR     192
+/*! Sender Report (From RFC3550) */
 #define RTCP_PT_SR      AST_RTP_RTCP_SR
+/*! Receiver Report (From RFC3550) */
 #define RTCP_PT_RR      AST_RTP_RTCP_RR
+/*! Source Description (From RFC3550) */
 #define RTCP_PT_SDES    202
+/*! Goodbye (To remove SSRC's from tables) (From RFC3550) */
 #define RTCP_PT_BYE     203
+/*! Application defined (From RFC3550) */
 #define RTCP_PT_APP     204
 /* VP8: RTCP Feedback */
-#define RTCP_PT_PSFB    206
+/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
+#define RTCP_PT_PSFB    AST_RTP_RTCP_PSFB
 
 #define RTP_MTU                1200
 #define DTMF_SAMPLE_RATE_MS    8 /*!< DTMF samples per millisecond */
 #define ZFONE_PROFILE_ID 0x505a
 
 #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4
+/*!
+ * \brief Calculate the min learning duration in ms.
+ *
+ * \details
+ * The min supported packet size represents 10 ms and we need to account
+ * for some jitter and fast clocks while learning.  Some messed up devices
+ * have very bad jitter for a small packet sample size.  Jitter can also
+ * be introduced by the network itself.
+ *
+ * So we'll allow packets to come in every 9ms on average for fast clocking
+ * with the last one coming in 5ms early for jitter.
+ */
+#define CALC_LEARNING_MIN_DURATION(count) (((count) - 1) * 9 - 5)
+#define DEFAULT_LEARNING_MIN_DURATION CALC_LEARNING_MIN_DURATION(DEFAULT_LEARNING_MIN_SEQUENTIAL)
 
 #define SRTP_MASTER_KEY_LEN 16
 #define SRTP_MASTER_SALT_LEN 14
 #define SRTP_MASTER_LEN (SRTP_MASTER_KEY_LEN + SRTP_MASTER_SALT_LEN)
 
+#define RTP_DTLS_ESTABLISHED -37
+
 enum strict_rtp_state {
        STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */
        STRICT_RTP_LEARN,    /*! Accept next packet as source */
        STRICT_RTP_CLOSED,   /*! Drop all RTP packets not coming from source that was learned */
 };
 
-#define DEFAULT_STRICT_RTP STRICT_RTP_CLOSED
+enum strict_rtp_mode {
+       STRICT_RTP_NO = 0,      /*! Don't adhere to any strict RTP rules */
+       STRICT_RTP_YES,         /*! Strict RTP that restricts packets based on time and sequence number */
+       STRICT_RTP_SEQNO,       /*! Strict RTP that restricts packets based on sequence number */
+};
+
+/*!
+ * \brief Strict RTP learning timeout time in milliseconds
+ *
+ * \note Set to 5 seconds to allow reinvite chains for direct media
+ * to settle before media actually starts to arrive.  There may be a
+ * reinvite collision involved on the other leg.
+ */
+#define STRICT_RTP_LEARN_TIMEOUT       5000
+
+#define DEFAULT_STRICT_RTP STRICT_RTP_YES      /*!< Enabled by default */
 #define DEFAULT_ICESUPPORT 1
 
 extern struct ast_srtp_res *res_srtp;
@@ -131,13 +188,14 @@ static int rtcpstats;                     /*!< Are we debugging RTCP? */
 static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */
 static struct ast_sockaddr rtpdebugaddr;       /*!< Debug packets to/from this host */
 static struct ast_sockaddr rtcpdebugaddr;      /*!< Debug RTCP packets to/from this host */
-static int rtpdebugport;               /*< Debug only RTP packets from IP or IP+Port if port is > 0 */
-static int rtcpdebugport;              /*< Debug only RTCP packets from IP or IP+Port if port is > 0 */
+static int rtpdebugport;               /*!< Debug only RTP packets from IP or IP+Port if port is > 0 */
+static int rtcpdebugport;              /*!< Debug only RTCP packets from IP or IP+Port if port is > 0 */
 #ifdef SO_NO_CHECK
 static int nochecksums;
 #endif
-static int strictrtp = DEFAULT_STRICT_RTP; /*< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */
-static int learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; /*< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */
+static int strictrtp = DEFAULT_STRICT_RTP; /*!< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */
+static int learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; /*!< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */
+static int learning_min_duration = DEFAULT_LEARNING_MIN_DURATION; /*!< Lowest acceptable timeout between the first and the last sequential RTP frame. */
 #ifdef HAVE_PJPROJECT
 static int icesupport = DEFAULT_ICESUPPORT;
 static struct sockaddr_in stunaddr;
@@ -145,9 +203,14 @@ static pj_str_t turnaddr;
 static int turnport = DEFAULT_TURN_PORT;
 static pj_str_t turnusername;
 static pj_str_t turnpassword;
+
 static struct ast_ha *ice_blacklist = NULL;    /*!< Blacklisted ICE networks */
 static ast_rwlock_t ice_blacklist_lock = AST_RWLOCK_INIT_VALUE;
 
+/*! Blacklisted networks for STUN requests */
+static struct ast_ha *stun_blacklist = NULL;
+static ast_rwlock_t stun_blacklist_lock = AST_RWLOCK_INIT_VALUE;
+
 
 /*! \brief Pool factory used by pjlib to allocate memory. */
 static pj_caching_pool cachingpool;
@@ -203,6 +266,7 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
 #define FLAG_NAT_INACTIVE_NOWARN        (1 << 1)
 #define FLAG_NEED_MARKER_BIT            (1 << 3)
 #define FLAG_DTMF_COMPENSATE            (1 << 4)
+#define FLAG_REQ_LOCAL_BRIDGE_BIT       (1 << 5)
 
 #define TRANSPORT_SOCKET_RTP 0
 #define TRANSPORT_SOCKET_RTCP 1
@@ -211,13 +275,17 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
 
 /*! \brief RTP learning mode tracking information */
 struct rtp_learning_info {
+       struct ast_sockaddr proposed_address;   /*!< Proposed remote address for strict RTP */
+       struct timeval start;   /*!< The time learning mode was started */
+       struct timeval received; /*!< The time of the first received packet */
        int max_seq;    /*!< The highest sequence number received */
        int packets;    /*!< The number of remaining packets before the source is accepted */
+       /*! Type of media stream carried by the RTP instance */
+       enum ast_media_type stream_type;
 };
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
 struct dtls_details {
-       ast_mutex_t lock; /*!< Lock for timeout timer synchronization */
        SSL *ssl;         /*!< SSL session */
        BIO *read_bio;    /*!< Memory buffer for reading */
        BIO *write_bio;   /*!< Memory buffer for writing */
@@ -227,6 +295,23 @@ struct dtls_details {
 };
 #endif
 
+#ifdef HAVE_PJPROJECT
+/*! An ao2 wrapper protecting the PJPROJECT ice structure with ref counting. */
+struct ice_wrap {
+       pj_ice_sess *real_ice;           /*!< ICE session */
+};
+#endif
+
+/*! \brief Structure used for mapping an incoming SSRC to an RTP instance */
+struct rtp_ssrc_mapping {
+       /*! \brief The received SSRC */
+       unsigned int ssrc;
+       /*! True if the SSRC is available.  Otherwise, this is a placeholder mapping until the SSRC is set. */
+       unsigned int ssrc_valid;
+       /*! \brief The RTP instance this SSRC belongs to*/
+       struct ast_rtp_instance *instance;
+};
+
 /*! \brief RTP session description */
 struct ast_rtp {
        int s;
@@ -234,16 +319,18 @@ struct ast_rtp {
        struct ast_frame f;
        unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
        unsigned int ssrc;              /*!< Synchronization source, RFC 3550, page 10. */
+       char cname[AST_UUID_STR_LEN]; /*!< Our local CNAME */
        unsigned int themssrc;          /*!< Their SSRC */
-       unsigned int rxssrc;
+       unsigned int themssrc_valid;    /*!< True if their SSRC is available. */
        unsigned int lastts;
-       unsigned int lastrxts;
        unsigned int lastividtimestamp;
        unsigned int lastovidtimestamp;
        unsigned int lastitexttimestamp;
        unsigned int lastotexttimestamp;
-       unsigned int lasteventseqn;
-       int lastrxseqno;                /*!< Last received sequence number */
+       int lastrxseqno;                /*!< Last received sequence number, from the network */
+       int expectedrxseqno;            /*!< Next expected sequence number, from the network */
+       AST_VECTOR(, int) missing_seqno; /*!< A vector of sequence numbers we never received */
+       int expectedseqno;              /*!< Next expected sequence number, from the core */
        unsigned short seedrxseqno;     /*!< What sequence number did they start with?*/
        unsigned int seedrxts;          /*!< What RTP timestamp did they start with? */
        unsigned int rxcount;           /*!< How many packets have we received? */
@@ -256,10 +343,6 @@ struct ast_rtp {
        struct ast_format *lasttxformat;
        struct ast_format *lastrxformat;
 
-       int rtptimeout;                 /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */
-       int rtpholdtimeout;             /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */
-       int rtpkeepalive;               /*!< Send RTP comfort noice packets for keepalive */
-
        /* DTMF Reception Variables */
        char resp;                        /*!< The current digit being processed */
        unsigned int last_seqno;          /*!< The last known sequence number for any DTMF packet */
@@ -278,17 +361,17 @@ struct ast_rtp {
        struct timeval rxcore;
        struct timeval txcore;
        double drxcore;                 /*!< The double representation of the first received packet */
-       struct timeval lastrx;          /*!< timeval when we last received a packet */
        struct timeval dtmfmute;
        struct ast_smoother *smoother;
-       int *ioid;
        unsigned short seqno;           /*!< Sequence number, RFC 3550, page 13. */
-       unsigned short rxseqno;
        struct ast_sched_context *sched;
-       struct io_context *io;
-       void *data;
        struct ast_rtcp *rtcp;
-       struct ast_rtp *bridged;        /*!< Who we are Packet bridged to */
+       unsigned int asymmetric_codec;  /*!< Indicate if asymmetric send/receive codecs are allowed */
+
+       struct ast_rtp_instance *bundled; /*!< The RTP instance we are bundled to */
+       int stream_num; /*!< Stream num for this RTP instance */
+       AST_VECTOR(, struct rtp_ssrc_mapping) ssrc_mapping; /*!< Mappings of SSRC to RTP instances */
+       struct ast_sockaddr bind_address; /*!< Requested bind address for the sockets */
 
        enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */
        struct ast_sockaddr strict_rtp_address;  /*!< Remote address information for strict RTP purposes */
@@ -298,15 +381,17 @@ struct ast_rtp {
         * but these are in place to keep learning mode sequence values sealed from their normal counterparts.
         */
        struct rtp_learning_info rtp_source_learn;      /* Learning mode track for the expected RTP source */
-       struct rtp_learning_info alt_source_learn;      /* Learning mode tracking for a new RTP source after one has been chosen */
 
        struct rtp_red *red;
 
-       ast_mutex_t lock;           /*!< Lock for synchronization purposes */
-       ast_cond_t cond;            /*!< Condition for signaling */
+       struct ast_data_buffer *send_buffer;            /*!< Buffer for storing sent packets for retransmission */
+       struct ast_data_buffer *recv_buffer;            /*!< Buffer for storing received packets for retransmission */
 
 #ifdef HAVE_PJPROJECT
-       pj_ice_sess *ice;           /*!< ICE session */
+       ast_cond_t cond;            /*!< ICE/TURN condition for signaling */
+
+       struct ice_wrap *ice;       /*!< ao2 wrapped ICE session */
+       enum ast_rtp_ice_role role; /*!< Our role in ICE negotiation */
        pj_turn_sock *turn_rtp;     /*!< RTP TURN relay */
        pj_turn_sock *turn_rtcp;    /*!< RTCP TURN relay */
        pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */
@@ -329,9 +414,10 @@ 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
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        SSL_CTX *ssl_ctx; /*!< SSL context */
        enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */
        enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
@@ -408,7 +494,7 @@ struct ast_rtcp {
        /* VP8: sequence number for the RTCP FIR FCI */
        int firseq;
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        struct dtls_details dtls; /*!< DTLS state information */
 #endif
 
@@ -417,6 +503,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 {
@@ -434,6 +523,12 @@ struct rtp_red {
        long int prev_ts;
 };
 
+/*! \brief Structure for storing RTP packets for retransmission */
+struct ast_rtp_rtcp_nack_payload {
+       size_t size;            /*!< The size of the payload */
+       unsigned char buf[0];   /*!< The payload data */
+};
+
 AST_LIST_HEAD_NOLOCK(frame_list, ast_frame);
 
 /* Forward Declarations */
@@ -460,15 +555,21 @@ static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_s
 static void ast_rtp_stop(struct ast_rtp_instance *instance);
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
-
-#ifdef HAVE_OPENSSL_SRTP
+static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance);
+static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance);
+static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc);
+static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num);
+static int ast_rtp_extension_enable(struct ast_rtp_instance *instance, enum ast_rtp_extension extension);
+static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);
+
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
 static int ast_rtp_activate(struct ast_rtp_instance *instance);
 static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
 static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
 static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
 #endif
 
-static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *via_ice, int use_srtp);
 
 #ifdef HAVE_PJPROJECT
 /*! \brief Helper function which clears the ICE host candidate mapping */
@@ -504,17 +605,20 @@ static void host_candidate_overrides_apply(unsigned int count, pj_sockaddr addrs
 }
 
 /*! \brief Helper function which updates an ast_sockaddr with the candidate used for the component */
-static void update_address_with_ice_candidate(struct ast_rtp *rtp, enum ast_rtp_ice_component_type component,
+static void update_address_with_ice_candidate(pj_ice_sess *ice, enum ast_rtp_ice_component_type component,
        struct ast_sockaddr *cand_address)
 {
        char address[PJ_INET6_ADDRSTRLEN];
 
-       if (!rtp->ice || (component < 1) || !rtp->ice->comp[component - 1].valid_check) {
+       if (component < 1 || !ice->comp[component - 1].valid_check) {
                return;
        }
 
-       ast_sockaddr_parse(cand_address, pj_sockaddr_print(&rtp->ice->comp[component - 1].valid_check->rcand->addr, address, sizeof(address), 0), 0);
-       ast_sockaddr_set_port(cand_address, pj_sockaddr_get_port(&rtp->ice->comp[component - 1].valid_check->rcand->addr));
+       ast_sockaddr_parse(cand_address,
+               pj_sockaddr_print(&ice->comp[component - 1].valid_check->rcand->addr, address,
+                       sizeof(address), 0), 0);
+       ast_sockaddr_set_port(cand_address,
+               pj_sockaddr_get_port(&ice->comp[component - 1].valid_check->rcand->addr));
 }
 
 /*! \brief Destructor for locally created ICE candidates */
@@ -531,6 +635,7 @@ static void ast_rtp_ice_candidate_destroy(void *obj)
        }
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_set_authentication(struct ast_rtp_instance *instance, const char *ufrag, const char *password)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -558,6 +663,7 @@ static int ice_candidate_cmp(void *obj, void *arg, int flags)
        return CMP_MATCH | CMP_STOP;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance, const struct ast_rtp_engine_ice_candidate *candidate)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -568,9 +674,12 @@ static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance,
                return;
        }
 
-       if (!rtp->ice_proposed_remote_candidates &&
-                       !(rtp->ice_proposed_remote_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) {
-               return;
+       if (!rtp->ice_proposed_remote_candidates) {
+               rtp->ice_proposed_remote_candidates = ao2_container_alloc_list(
+                       AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, ice_candidate_cmp);
+               if (!rtp->ice_proposed_remote_candidates) {
+                       return;
+               }
        }
 
        /* If this is going to exceed the maximum number of ICE candidates don't even add it */
@@ -622,40 +731,110 @@ static void pj_thread_register_check(void)
 static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *addr,
        int port, int replace);
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_stop(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ice_wrap *ice;
 
-       if (!rtp->ice) {
-               return;
+       ice = rtp->ice;
+       rtp->ice = NULL;
+       if (ice) {
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ao2_unlock(instance);
+               ao2_ref(ice, -1);
+               ao2_lock(instance);
        }
+}
 
-       pj_thread_register_check();
+/*!
+ * \brief ao2 ICE wrapper object destructor.
+ *
+ * \param vdoomed Object being destroyed.
+ *
+ * \note The associated struct ast_rtp_instance object must not
+ * be locked when unreffing the object.  Otherwise we could
+ * deadlock trying to destroy the PJPROJECT ICE structure.
+ */
+static void ice_wrap_dtor(void *vdoomed)
+{
+       struct ice_wrap *ice = vdoomed;
 
-       pj_ice_sess_destroy(rtp->ice);
-       rtp->ice = NULL;
+       if (ice->real_ice) {
+               pj_thread_register_check();
+
+               pj_ice_sess_destroy(ice->real_ice);
+       }
+}
+
+static void ast2pj_rtp_ice_role(enum ast_rtp_ice_role ast_role, enum pj_ice_sess_role *pj_role)
+{
+       switch (ast_role) {
+       case AST_RTP_ICE_ROLE_CONTROLLED:
+               *pj_role = PJ_ICE_SESS_ROLE_CONTROLLED;
+               break;
+       case AST_RTP_ICE_ROLE_CONTROLLING:
+               *pj_role = PJ_ICE_SESS_ROLE_CONTROLLING;
+               break;
+       }
+}
+
+static void pj2ast_rtp_ice_role(enum pj_ice_sess_role pj_role, enum ast_rtp_ice_role *ast_role)
+{
+       switch (pj_role) {
+       case PJ_ICE_SESS_ROLE_CONTROLLED:
+               *ast_role = AST_RTP_ICE_ROLE_CONTROLLED;
+               return;
+       case PJ_ICE_SESS_ROLE_CONTROLLING:
+               *ast_role = AST_RTP_ICE_ROLE_CONTROLLING;
+               return;
+       case PJ_ICE_SESS_ROLE_UNKNOWN:
+               /* Don't change anything */
+               return;
+       default:
+               /* If we aren't explicitly handling something, it's a bug */
+               ast_assert(0);
+               return;
+       }
 }
 
+/*! \pre instance is locked */
 static int ice_reset_session(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       pj_ice_sess_role role = rtp->ice->role;
        int res;
 
        ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
-       if (!rtp->ice->is_nominating && !rtp->ice->is_complete) {
+       if (!rtp->ice->real_ice->is_nominating && !rtp->ice->real_ice->is_complete) {
                ast_debug(3, "Nevermind. ICE isn't ready for a reset\n");
                return 0;
        }
 
-       ast_debug(3, "Stopping ICE for RTP instance '%p'\n", instance);
-       ast_rtp_ice_stop(instance);
-
        ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
        res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
        if (!res) {
-               /* Preserve the role that the old ICE session used */
-               pj_ice_sess_change_role(rtp->ice, role);
+               /* Use the current expected role for the ICE session */
+               enum pj_ice_sess_role role = PJ_ICE_SESS_ROLE_UNKNOWN;
+               ast2pj_rtp_ice_role(rtp->role, &role);
+               pj_ice_sess_change_role(rtp->ice->real_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, };
+
+               rtp->turn_state = PJ_TURN_STATE_NULL;
+
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ao2_unlock(instance);
+               pj_turn_sock_destroy(rtp->turn_rtcp);
+               ao2_lock(instance);
+               while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
+                       ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
+               }
        }
 
        return res;
@@ -688,6 +867,7 @@ static int ice_candidates_compare(struct ao2_container *left, struct ao2_contain
        return 0;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -707,6 +887,8 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
                ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
                ao2_cleanup(rtp->ice_proposed_remote_candidates);
                rtp->ice_proposed_remote_candidates = NULL;
+               /* If this ICE session is being preserved then go back to the role it currently is */
+               pj2ast_rtp_ice_role(rtp->ice->real_ice->role, &rtp->role);
                return;
        }
 
@@ -732,7 +914,8 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
                has_rtp |= candidate->id == AST_RTP_ICE_COMPONENT_RTP;
                has_rtcp |= candidate->id == AST_RTP_ICE_COMPONENT_RTCP;
 
-               pj_strdup2(rtp->ice->pool, &candidates[cand_cnt].foundation, candidate->foundation);
+               pj_strdup2(rtp->ice->real_ice->pool, &candidates[cand_cnt].foundation,
+                       candidate->foundation);
                candidates[cand_cnt].comp_id = candidate->id;
                candidates[cand_cnt].prio = candidate->priority;
 
@@ -752,10 +935,16 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 
                if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) {
                        ast_debug(3, "RTP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
+                       /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+                       ao2_unlock(instance);
                        pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
+                       ao2_lock(instance);
                } else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) {
                        ast_debug(3, "RTCP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
+                       /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+                       ao2_unlock(instance);
                        pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
+                       ao2_lock(instance);
                }
 
                cand_cnt++;
@@ -773,22 +962,33 @@ 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) {
-               pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
+       if (rtp->ice && has_rtp && (has_rtcp || rtp->ice_num_components == 1)) {
+               pj_status_t res;
                char reason[80];
+               struct ice_wrap *ice;
 
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ice = rtp->ice;
+               ao2_ref(ice, +1);
+               ao2_unlock(instance);
+               res = pj_ice_sess_create_check_list(ice->real_ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
                if (res == PJ_SUCCESS) {
                        ast_debug(3, "Successfully created ICE checklist (%p)\n", instance);
                        ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
-                       pj_ice_sess_start_check(rtp->ice);
+                       pj_ice_sess_start_check(ice->real_ice);
                        pj_timer_heap_poll(timer_heap, NULL);
+                       ao2_ref(ice, -1);
+                       ao2_lock(instance);
                        rtp->strict_rtp_state = STRICT_RTP_OPEN;
                        return;
                }
+               ao2_ref(ice, -1);
+               ao2_lock(instance);
 
                pj_strerror(res, reason, sizeof(reason));
                ast_log(LOG_WARNING, "Failed to create ICE session check list: %s (%p)\n", reason, instance);
@@ -802,9 +1002,12 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
           this function may be re-entered */
        ao2_ref(rtp->ice_active_remote_candidates, -1);
        rtp->ice_active_remote_candidates = NULL;
-       rtp->ice->rcand_cnt = rtp->ice->clist.count = 0;
+       if (rtp->ice) {
+               rtp->ice->real_ice->rcand_cnt = rtp->ice->real_ice->clist.count = 0;
+       }
 }
 
+/*! \pre instance is locked */
 static const char *ast_rtp_ice_get_ufrag(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -812,6 +1015,7 @@ static const char *ast_rtp_ice_get_ufrag(struct ast_rtp_instance *instance)
        return rtp->local_ufrag;
 }
 
+/*! \pre instance is locked */
 static const char *ast_rtp_ice_get_password(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -819,6 +1023,7 @@ static const char *ast_rtp_ice_get_password(struct ast_rtp_instance *instance)
        return rtp->local_passwd;
 }
 
+/*! \pre instance is locked */
 static struct ao2_container *ast_rtp_ice_get_local_candidates(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -830,6 +1035,7 @@ static struct ao2_container *ast_rtp_ice_get_local_candidates(struct ast_rtp_ins
        return rtp->ice_local_candidates;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_lite(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -840,9 +1046,10 @@ static void ast_rtp_ice_lite(struct ast_rtp_instance *instance)
 
        pj_thread_register_check();
 
-       pj_ice_sess_change_role(rtp->ice, PJ_ICE_SESS_ROLE_CONTROLLING);
+       pj_ice_sess_change_role(rtp->ice->real_ice, PJ_ICE_SESS_ROLE_CONTROLLING);
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp_ice_role role)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -855,25 +1062,44 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
                return;
        }
 
-       pj_thread_register_check();
+       rtp->role = role;
+
+       if (!rtp->ice->real_ice->is_nominating && !rtp->ice->real_ice->is_complete) {
+               pj_thread_register_check();
 
-       pj_ice_sess_change_role(rtp->ice, role == AST_RTP_ICE_ROLE_CONTROLLED ?
-               PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+               pj_ice_sess_change_role(rtp->ice->real_ice, role == AST_RTP_ICE_ROLE_CONTROLLED ?
+                       PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+       } else {
+               ast_debug(3, "Not setting ICE role because state is %s\n", rtp->ice->real_ice->is_nominating ? "nominating" : "complete" );
+       }
 }
 
-static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref,
-                                       const pj_sockaddr_t *addr, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len)
+/*! \pre instance is locked */
+static void ast_rtp_ice_add_cand(struct ast_rtp_instance *instance, struct ast_rtp *rtp,
+       unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref,
+       const pj_sockaddr_t *addr, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr,
+       int addr_len)
 {
        pj_str_t foundation;
        struct ast_rtp_engine_ice_candidate *candidate, *existing;
+       struct ice_wrap *ice;
        char address[PJ_INET6_ADDRSTRLEN];
+       pj_status_t status;
+
+       if (!rtp->ice) {
+               return;
+       }
 
        pj_thread_register_check();
 
-       pj_ice_calc_foundation(rtp->ice->pool, &foundation, type, addr);
+       pj_ice_calc_foundation(rtp->ice->real_ice->pool, &foundation, type, addr);
 
-       if (!rtp->ice_local_candidates && !(rtp->ice_local_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) {
-               return;
+       if (!rtp->ice_local_candidates) {
+               rtp->ice_local_candidates = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+                       NULL, ice_candidate_cmp);
+               if (!rtp->ice_local_candidates) {
+                       return;
+               }
        }
 
        if (!(candidate = ao2_alloc(sizeof(*candidate), ast_rtp_ice_candidate_destroy))) {
@@ -906,42 +1132,60 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned
                return;
        }
 
-       if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, base_addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) {
+       /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+       ice = rtp->ice;
+       ao2_ref(ice, +1);
+       ao2_unlock(instance);
+       status = pj_ice_sess_add_cand(ice->real_ice, comp_id, transport_id, type, local_pref,
+               &foundation, addr, base_addr, rel_addr, addr_len, NULL);
+       ao2_ref(ice, -1);
+       ao2_lock(instance);
+       if (!rtp->ice || status != PJ_SUCCESS) {
                ao2_ref(candidate, -1);
                return;
        }
 
        /* By placing the candidate into the ICE session it will have produced the priority, so update the local candidate with it */
-       candidate->priority = rtp->ice->lcand[rtp->ice->lcand_cnt - 1].prio;
+       candidate->priority = rtp->ice->real_ice->lcand[rtp->ice->real_ice->lcand_cnt - 1].prio;
 
        ao2_link(rtp->ice_local_candidates, candidate);
        ao2_ref(candidate, -1);
 }
 
+/* PJPROJECT TURN callback */
 static void ast_rtp_on_turn_rx_rtp_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len)
 {
        struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ice_wrap *ice;
        pj_status_t status;
 
-       status = pj_ice_sess_on_rx_pkt(rtp->ice, AST_RTP_ICE_COMPONENT_RTP, TRANSPORT_TURN_RTP, pkt, pkt_len, peer_addr,
-               addr_len);
-       if (status != PJ_SUCCESS) {
-               char buf[100];
+       ao2_lock(instance);
+       ice = ao2_bump(rtp->ice);
+       ao2_unlock(instance);
 
-               pj_strerror(status, buf, sizeof(buf));
-               ast_log(LOG_WARNING, "PJ ICE Rx error status code: %d '%s'.\n",
-                       (int)status, buf);
-               return;
-       }
-       if (!rtp->rtp_passthrough) {
-               return;
+       if (ice) {
+               status = pj_ice_sess_on_rx_pkt(ice->real_ice, AST_RTP_ICE_COMPONENT_RTP,
+                       TRANSPORT_TURN_RTP, pkt, pkt_len, peer_addr, addr_len);
+               ao2_ref(ice, -1);
+               if (status != PJ_SUCCESS) {
+                       char buf[100];
+
+                       pj_strerror(status, buf, sizeof(buf));
+                       ast_log(LOG_WARNING, "PJ ICE Rx error status code: %d '%s'.\n",
+                               (int)status, buf);
+                       return;
+               }
+               if (!rtp->rtp_passthrough) {
+                       return;
+               }
+               rtp->rtp_passthrough = 0;
        }
-       rtp->rtp_passthrough = 0;
 
        ast_sendto(rtp->s, pkt, pkt_len, 0, &rtp->rtp_loop);
 }
 
+/* PJPROJECT TURN callback */
 static void ast_rtp_on_turn_rtp_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state)
 {
        struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
@@ -954,8 +1198,9 @@ static void ast_rtp_on_turn_rtp_state(pj_turn_sock *turn_sock, pj_turn_state_t o
 
        rtp = ast_rtp_instance_get_data(instance);
 
+       ao2_lock(instance);
+
        /* We store the new state so the other thread can actually handle it */
-       ast_mutex_lock(&rtp->lock);
        rtp->turn_state = new_state;
        ast_cond_signal(&rtp->cond);
 
@@ -964,7 +1209,7 @@ static void ast_rtp_on_turn_rtp_state(pj_turn_sock *turn_sock, pj_turn_state_t o
                rtp->turn_rtp = NULL;
        }
 
-       ast_mutex_unlock(&rtp->lock);
+       ao2_unlock(instance);
 }
 
 /* RTP TURN Socket interface declaration */
@@ -973,34 +1218,44 @@ static pj_turn_sock_cb ast_rtp_turn_rtp_sock_cb = {
        .on_state = ast_rtp_on_turn_rtp_state,
 };
 
+/* PJPROJECT TURN callback */
 static void ast_rtp_on_turn_rx_rtcp_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len)
 {
        struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ice_wrap *ice;
        pj_status_t status;
 
-       status = pj_ice_sess_on_rx_pkt(rtp->ice, AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_TURN_RTCP, pkt, pkt_len, peer_addr,
-               addr_len);
-       if (status != PJ_SUCCESS) {
-               char buf[100];
+       ao2_lock(instance);
+       ice = ao2_bump(rtp->ice);
+       ao2_unlock(instance);
 
-               pj_strerror(status, buf, sizeof(buf));
-               ast_log(LOG_WARNING, "PJ ICE Rx error status code: %d '%s'.\n",
-                       (int)status, buf);
-               return;
-       }
-       if (!rtp->rtcp_passthrough) {
-               return;
+       if (ice) {
+               status = pj_ice_sess_on_rx_pkt(ice->real_ice, AST_RTP_ICE_COMPONENT_RTCP,
+                       TRANSPORT_TURN_RTCP, pkt, pkt_len, peer_addr, addr_len);
+               ao2_ref(ice, -1);
+               if (status != PJ_SUCCESS) {
+                       char buf[100];
+
+                       pj_strerror(status, buf, sizeof(buf));
+                       ast_log(LOG_WARNING, "PJ ICE Rx error status code: %d '%s'.\n",
+                               (int)status, buf);
+                       return;
+               }
+               if (!rtp->rtcp_passthrough) {
+                       return;
+               }
+               rtp->rtcp_passthrough = 0;
        }
-       rtp->rtcp_passthrough = 0;
 
        ast_sendto(rtp->rtcp->s, pkt, pkt_len, 0, &rtp->rtcp_loop);
 }
 
+/* PJPROJECT TURN callback */
 static void ast_rtp_on_turn_rtcp_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state)
 {
        struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
-       struct ast_rtp *rtp = NULL;
+       struct ast_rtp *rtp;
 
        /* If this is a leftover from an already destroyed RTP instance just ignore the state change */
        if (!instance) {
@@ -1009,8 +1264,9 @@ static void ast_rtp_on_turn_rtcp_state(pj_turn_sock *turn_sock, pj_turn_state_t
 
        rtp = ast_rtp_instance_get_data(instance);
 
+       ao2_lock(instance);
+
        /* We store the new state so the other thread can actually handle it */
-       ast_mutex_lock(&rtp->lock);
        rtp->turn_state = new_state;
        ast_cond_signal(&rtp->cond);
 
@@ -1019,7 +1275,7 @@ static void ast_rtp_on_turn_rtcp_state(pj_turn_sock *turn_sock, pj_turn_state_t
                rtp->turn_rtcp = NULL;
        }
 
-       ast_mutex_unlock(&rtp->lock);
+       ao2_unlock(instance);
 }
 
 /* RTCP TURN Socket interface declaration */
@@ -1053,7 +1309,16 @@ static void rtp_ioqueue_thread_destroy(struct ast_rtp_ioqueue_thread *ioqueue)
                pj_thread_destroy(ioqueue->thread);
        }
 
-       pj_pool_release(ioqueue->pool);
+       if (ioqueue->pool) {
+               /* This mimics the behavior of pj_pool_safe_release
+                * which was introduced in pjproject 2.6.
+                */
+               pj_pool_t *temp_pool = ioqueue->pool;
+
+               ioqueue->pool = NULL;
+               pj_pool_release(temp_pool);
+       }
+
        ast_free(ioqueue);
 }
 
@@ -1142,6 +1407,7 @@ end:
        return ioqueue;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_ice_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)
 {
@@ -1158,6 +1424,9 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
        struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
        pj_turn_session_info info;
        struct ast_sockaddr local, loop;
+       pj_status_t status;
+       pj_turn_sock_cfg turn_sock_cfg;
+       struct ice_wrap *ice;
 
        ast_rtp_instance_get_local_address(instance, &local);
        if (ast_sockaddr_is_ipv4(&local)) {
@@ -1192,18 +1461,27 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
 
        ast_sockaddr_parse(&addr, server, PARSE_PORT_FORBID);
 
-       ast_mutex_lock(&rtp->lock);
        if (*turn_sock) {
-               pj_turn_sock_destroy(*turn_sock);
                rtp->turn_state = PJ_TURN_STATE_NULL;
+
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ao2_unlock(instance);
+               pj_turn_sock_destroy(*turn_sock);
+               ao2_lock(instance);
                while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
-                       ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+                       ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
                }
        }
-       ast_mutex_unlock(&rtp->lock);
 
        if (component == AST_RTP_ICE_COMPONENT_RTP && !rtp->ioqueue) {
+               /*
+                * We cannot hold the instance lock because we could wait
+                * for the ioqueue thread to die and we might deadlock as
+                * a result.
+                */
+               ao2_unlock(instance);
                rtp->ioqueue = rtp_ioqueue_thread_get_or_create();
+               ao2_lock(instance);
                if (!rtp->ioqueue) {
                        return;
                }
@@ -1211,9 +1489,23 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
 
        pj_stun_config_init(&stun_config, &cachingpool.factory, 0, rtp->ioqueue->ioqueue, rtp->ioqueue->timerheap);
 
-       if (pj_turn_sock_create(&stun_config, ast_sockaddr_is_ipv4(&addr) ? pj_AF_INET() : pj_AF_INET6(), conn_type,
-               turn_cb, NULL, instance, turn_sock) != PJ_SUCCESS) {
+       /* Use ICE session group lock for TURN session to avoid deadlock */
+       pj_turn_sock_cfg_default(&turn_sock_cfg);
+       ice = rtp->ice;
+       if (ice) {
+               turn_sock_cfg.grp_lock = ice->real_ice->grp_lock;
+               ao2_ref(ice, +1);
+       }
+
+       /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+       ao2_unlock(instance);
+       status = pj_turn_sock_create(&stun_config,
+               ast_sockaddr_is_ipv4(&addr) ? pj_AF_INET() : pj_AF_INET6(), conn_type,
+               turn_cb, &turn_sock_cfg, instance, turn_sock);
+       ao2_cleanup(ice);
+       if (status != PJ_SUCCESS) {
                ast_log(LOG_WARNING, "Could not create a TURN client socket\n");
+               ao2_lock(instance);
                return;
        }
 
@@ -1222,13 +1514,16 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
        cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
        pj_strset2(&cred.data.static_cred.data, (char*)password);
 
-       /* Because the TURN socket is asynchronous but we are synchronous we need to wait until it is done */
-       ast_mutex_lock(&rtp->lock);
        pj_turn_sock_alloc(*turn_sock, pj_cstr(&turn_addr, server), port, NULL, &cred, NULL);
+       ao2_lock(instance);
+
+       /*
+        * Because the TURN socket is asynchronous and we are synchronous we need to
+        * wait until it is done
+        */
        while (rtp->turn_state < PJ_TURN_STATE_READY) {
-               ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+               ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
        }
-       ast_mutex_unlock(&rtp->lock);
 
        /* If a TURN session was allocated add it as a candidate */
        if (rtp->turn_state != PJ_TURN_STATE_READY) {
@@ -1237,8 +1532,9 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
 
        pj_turn_sock_get_info(*turn_sock, &info);
 
-       ast_rtp_ice_add_cand(rtp, component, conn_transport, PJ_ICE_CAND_TYPE_RELAYED, 65535, &info.relay_addr,
-               &info.relay_addr, &info.mapped_addr, pj_sockaddr_get_len(&info.relay_addr));
+       ast_rtp_ice_add_cand(instance, rtp, component, conn_transport,
+               PJ_ICE_CAND_TYPE_RELAYED, 65535, &info.relay_addr, &info.relay_addr,
+               &info.mapped_addr, pj_sockaddr_get_len(&info.relay_addr));
 
        if (component == AST_RTP_ICE_COMPONENT_RTP) {
                ast_sockaddr_copy(&rtp->rtp_loop, &loop);
@@ -1260,6 +1556,22 @@ static char *generate_random_string(char *buf, size_t size)
         return buf;
 }
 
+/*! \pre instance is locked */
+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,
@@ -1272,10 +1584,11 @@ 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
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
 static int dtls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
 {
        /* We don't want to actually verify the certificate so just accept what they have provided */
@@ -1313,8 +1626,6 @@ static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx,
        }
        dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
 
-       ast_mutex_init(&dtls->lock);
-
        return 0;
 
 error:
@@ -1346,45 +1657,32 @@ static int dtls_setup_rtcp(struct ast_rtp_instance *instance)
        return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);
 }
 
-static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+static const SSL_METHOD *get_dtls_method(void)
 {
-       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       int res;
-#ifdef HAVE_OPENSSL_EC
-       EC_KEY *ecdh;
-#endif
-
-       if (!dtls_cfg->enabled) {
-               return 0;
-       }
-
-       if (!ast_rtp_engine_srtp_is_registered()) {
-               ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
-               return -1;
-       }
-
-       if (rtp->ssl_ctx) {
-               return 0;
-       }
-
 #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
-       rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
+       return DTLSv1_method();
 #else
-       rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
+       return DTLS_method();
 #endif
-       if (!rtp->ssl_ctx) {
-               return -1;
-       }
+}
 
-       SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
+struct dtls_cert_info {
+       EVP_PKEY *private_key;
+       X509 *certificate;
+};
 
-#ifdef HAVE_OPENSSL_EC
+static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+#if !defined(OPENSSL_NO_ECDH) && (OPENSSL_VERSION_NUMBER >= 0x10000000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
+       EC_KEY *ecdh;
+#endif
 
+#ifndef OPENSSL_NO_DH
        if (!ast_strlen_zero(dtls_cfg->pvtfile)) {
                BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");
-               if (bio != NULL) {
+               if (bio) {
                        DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-                       if (dh != NULL) {
+                       if (dh) {
                                if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) {
                                        long options = SSL_OP_CIPHER_SERVER_PREFERENCE |
                                                SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE;
@@ -1396,9 +1694,12 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
                        BIO_free(bio);
                }
        }
+#endif /* !OPENSSL_NO_DH */
+
+#if !defined(OPENSSL_NO_ECDH) && (OPENSSL_VERSION_NUMBER >= 0x10000000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
        /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */
        ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
-       if (ecdh != NULL) {
+       if (ecdh) {
                if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {
                        #ifndef SSL_CTRL_SET_ECDH_AUTO
                                #define SSL_CTRL_SET_ECDH_AUTO 94
@@ -1412,115 +1713,349 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
                }
                EC_KEY_free(ecdh);
        }
+#endif /* !OPENSSL_NO_ECDH */
+}
 
-#endif /* #ifdef HAVE_OPENSSL_EC */
-
-       rtp->dtls_verify = dtls_cfg->verify;
+#if !defined(OPENSSL_NO_ECDH) && (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 
-       SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
-               SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, !(rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
-               dtls_verify_callback : NULL);
+static int create_ephemeral_ec_keypair(EVP_PKEY **keypair)
+{
+       EC_KEY *eckey = NULL;
+       EC_GROUP *group = NULL;
 
-       if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) {
-               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80");
-       } else if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_32) {
-               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32");
-       } else {
-               ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance);
-               return -1;
+       group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+       if (!group) {
+               goto error;
        }
 
-       rtp->local_hash = dtls_cfg->hash;
+       EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+       EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
 
-       if (!ast_strlen_zero(dtls_cfg->certfile)) {
-               char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
-               BIO *certbio;
-               X509 *cert = NULL;
-               const EVP_MD *type;
-               unsigned int size, i;
-               unsigned char fingerprint[EVP_MAX_MD_SIZE];
-               char *local_fingerprint = rtp->local_fingerprint;
+       eckey = EC_KEY_new();
+       if (!eckey) {
+               goto error;
+       }
 
-               if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {
-                       ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",
-                               dtls_cfg->certfile, instance);
-                       return -1;
-               }
+       if (!EC_KEY_set_group(eckey, group)) {
+               goto error;
+       }
 
-               if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||
-                   !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
-                       ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n",
-                               private, instance);
-                       return -1;
-               }
+       if (!EC_KEY_generate_key(eckey)) {
+               goto error;
+       }
 
-               if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA1) {
-                       type = EVP_sha1();
-               } else if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA256) {
-                       type = EVP_sha256();
-               } else {
-                       ast_log(LOG_ERROR, "Unsupported fingerprint hash type on RTP instance '%p'\n",
-                               instance);
-                       return -1;
-               }
+       *keypair = EVP_PKEY_new();
+       if (!*keypair) {
+               goto error;
+       }
 
-               if (!(certbio = BIO_new(BIO_s_file()))) {
-                       ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
-                               instance);
-                       return -1;
-               }
+       EVP_PKEY_assign_EC_KEY(*keypair, eckey);
+       EC_GROUP_free(group);
 
-               if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||
-                   !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||
-                   !X509_digest(cert, type, fingerprint, &size) ||
-                   !size) {
-                       ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
-                               dtls_cfg->certfile, instance);
-                       BIO_free_all(certbio);
-                       if (cert) {
-                               X509_free(cert);
-                       }
-                       return -1;
-               }
+       return 0;
 
-               for (i = 0; i < size; i++) {
-                       sprintf(local_fingerprint, "%02hhX:", fingerprint[i]);
-                       local_fingerprint += 3;
-               }
+error:
+       EC_KEY_free(eckey);
+       EC_GROUP_free(group);
 
-               *(local_fingerprint-1) = 0;
+       return -1;
+}
 
-               BIO_free_all(certbio);
-               X509_free(cert);
-       }
+/* From OpenSSL's x509 command */
+#define SERIAL_RAND_BITS 159
 
-       if (!ast_strlen_zero(dtls_cfg->cipher)) {
-               if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) {
-                       ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n",
-                               dtls_cfg->cipher, instance);
-                       return -1;
-               }
+static int create_ephemeral_certificate(EVP_PKEY *keypair, X509 **certificate)
+{
+       X509 *cert = NULL;
+       BIGNUM *serial = NULL;
+       X509_NAME *name = NULL;
+
+       cert = X509_new();
+       if (!cert) {
+               goto error;
        }
 
-       if (!ast_strlen_zero(dtls_cfg->cafile) || !ast_strlen_zero(dtls_cfg->capath)) {
-               if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) {
-                       ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n",
-                               S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance);
-                       return -1;
-               }
+       if (!X509_set_version(cert, 2)) {
+               goto error;
        }
 
-       rtp->rekey = dtls_cfg->rekey;
-       rtp->suite = dtls_cfg->suite;
+       /* Set the public key */
+       X509_set_pubkey(cert, keypair);
 
-       res = dtls_details_initialize(&rtp->dtls, rtp->ssl_ctx, dtls_cfg->default_setup);
-       if (!res) {
-               dtls_setup_rtcp(instance);
+       /* Generate a random serial number */
+       if (!(serial = BN_new())
+          || !BN_rand(serial, SERIAL_RAND_BITS, -1, 0)
+          || !BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert))) {
+               goto error;
        }
 
-       return res;
+       /*
+        * Validity period - Current Chrome & Firefox make it 31 days starting
+        * with yesterday at the current time, so we will do the same.
+        */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+       if (!X509_time_adj_ex(X509_get_notBefore(cert), -1, 0, NULL)
+          || !X509_time_adj_ex(X509_get_notAfter(cert), 30, 0, NULL)) {
+               goto error;
+       }
+#else
+       if (!X509_time_adj_ex(X509_getm_notBefore(cert), -1, 0, NULL)
+          || !X509_time_adj_ex(X509_getm_notAfter(cert), 30, 0, NULL)) {
+               goto error;
+       }
+#endif
+
+       /* Set the name and issuer */
+       if (!(name = X509_get_subject_name(cert))
+          || !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
+                                                                         (unsigned char *) "asterisk", -1, -1, 0)
+          || !X509_set_issuer_name(cert, name)) {
+               goto error;
+       }
+
+       /* Sign it */
+       if (!X509_sign(cert, keypair, EVP_sha256())) {
+               goto error;
+       }
+
+       *certificate = cert;
+
+       return 0;
+
+error:
+       BN_free(serial);
+       X509_free(cert);
+
+       return -1;
+}
+
+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,
+                                                                               const struct ast_rtp_dtls_cfg *dtls_cfg,
+                                                                               struct dtls_cert_info *cert_info)
+{
+       /* Make sure these are initialized */
+       cert_info->private_key = NULL;
+       cert_info->certificate = NULL;
+
+       if (create_ephemeral_ec_keypair(&cert_info->private_key)) {
+               ast_log(LOG_ERROR, "Failed to create ephemeral ECDSA keypair\n");
+               goto error;
+       }
+
+       if (create_ephemeral_certificate(cert_info->private_key, &cert_info->certificate)) {
+               ast_log(LOG_ERROR, "Failed to create ephemeral X509 certificate\n");
+               goto error;
+       }
+
+       return 0;
+
+  error:
+       X509_free(cert_info->certificate);
+       EVP_PKEY_free(cert_info->private_key);
+
+       return -1;
+}
+
+#else
+
+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,
+                                                                               const struct ast_rtp_dtls_cfg *dtls_cfg,
+                                                                               struct dtls_cert_info *cert_info)
+{
+       ast_log(LOG_ERROR, "Your version of OpenSSL does not support ECDSA keys\n");
+       return -1;
+}
+
+#endif /* !OPENSSL_NO_ECDH */
+
+static int create_certificate_from_file(struct ast_rtp_instance *instance,
+                                                                               const struct ast_rtp_dtls_cfg *dtls_cfg,
+                                                                               struct dtls_cert_info *cert_info)
+{
+       FILE *fp;
+       BIO *certbio = NULL;
+       EVP_PKEY *private_key = NULL;
+       X509 *cert = NULL;
+       char *private_key_file = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
+
+       fp = fopen(private_key_file, "r");
+       if (!fp) {
+               ast_log(LOG_ERROR, "Failed to read private key from file '%s': %s\n", private_key_file, strerror(errno));
+               goto error;
+       }
+
+       if (!PEM_read_PrivateKey(fp, &private_key, NULL, NULL)) {
+               ast_log(LOG_ERROR, "Failed to read private key from PEM file '%s'\n", private_key_file);
+               fclose(fp);
+               goto error;
+       }
+
+       if (fclose(fp)) {
+               ast_log(LOG_ERROR, "Failed to close private key file '%s': %s\n", private_key_file, strerror(errno));
+               goto error;
+       }
+
+       certbio = BIO_new(BIO_s_file());
+       if (!certbio) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
+                               instance);
+               goto error;
+       }
+
+       if (!BIO_read_filename(certbio, dtls_cfg->certfile)
+          || !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
+               ast_log(LOG_ERROR, "Failed to read certificate from file '%s'\n", dtls_cfg->certfile);
+               goto error;
+       }
+
+       cert_info->private_key = private_key;
+       cert_info->certificate = cert;
+
+       BIO_free_all(certbio);
+
+       return 0;
+
+error:
+       X509_free(cert);
+       BIO_free_all(certbio);
+       EVP_PKEY_free(private_key);
+
+       return -1;
+}
+
+static int load_dtls_certificate(struct ast_rtp_instance *instance,
+                                                                const struct ast_rtp_dtls_cfg *dtls_cfg,
+                                                                struct dtls_cert_info *cert_info)
+{
+       if (dtls_cfg->ephemeral_cert) {
+               return create_certificate_ephemeral(instance, dtls_cfg, cert_info);
+       } else if (!ast_strlen_zero(dtls_cfg->certfile)) {
+               return create_certificate_from_file(instance, dtls_cfg, cert_info);
+       } else {
+               return -1;
+       }
+}
+
+/*! \pre instance is locked */
+static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct dtls_cert_info cert_info = { 0 };
+       int res;
+
+       if (!dtls_cfg->enabled) {
+               return 0;
+       }
+
+       if (!ast_rtp_engine_srtp_is_registered()) {
+               ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
+               return -1;
+       }
+
+       if (rtp->ssl_ctx) {
+               return 0;
+       }
+
+       rtp->ssl_ctx = SSL_CTX_new(get_dtls_method());
+       if (!rtp->ssl_ctx) {
+               return -1;
+       }
+
+       SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
+
+       configure_dhparams(rtp, dtls_cfg);
+
+       rtp->dtls_verify = dtls_cfg->verify;
+
+       SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
+               SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, !(rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
+               dtls_verify_callback : NULL);
+
+       if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) {
+               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80");
+       } else if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_32) {
+               SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32");
+       } else {
+               ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance);
+               return -1;
+       }
+
+       rtp->local_hash = dtls_cfg->hash;
+
+       if (!load_dtls_certificate(instance, dtls_cfg, &cert_info)) {
+               const EVP_MD *type;
+               unsigned int size, i;
+               unsigned char fingerprint[EVP_MAX_MD_SIZE];
+               char *local_fingerprint = rtp->local_fingerprint;
+
+               if (!SSL_CTX_use_certificate(rtp->ssl_ctx, cert_info.certificate)) {
+                       ast_log(LOG_ERROR, "Specified certificate for RTP instance '%p' could not be used\n",
+                                       instance);
+                       return -1;
+               }
+
+               if (!SSL_CTX_use_PrivateKey(rtp->ssl_ctx, cert_info.private_key)
+                   || !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
+                       ast_log(LOG_ERROR, "Specified private key for RTP instance '%p' could not be used\n",
+                                       instance);
+                       return -1;
+               }
+
+               if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA1) {
+                       type = EVP_sha1();
+               } else if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA256) {
+                       type = EVP_sha256();
+               } else {
+                       ast_log(LOG_ERROR, "Unsupported fingerprint hash type on RTP instance '%p'\n",
+                               instance);
+                       return -1;
+               }
+
+               if (!X509_digest(cert_info.certificate, type, fingerprint, &size) || !size) {
+                       ast_log(LOG_ERROR, "Could not produce fingerprint from certificate for RTP instance '%p'\n",
+                                       instance);
+                       return -1;
+               }
+
+               for (i = 0; i < size; i++) {
+                       sprintf(local_fingerprint, "%02hhX:", fingerprint[i]);
+                       local_fingerprint += 3;
+               }
+
+               *(local_fingerprint - 1) = 0;
+
+               EVP_PKEY_free(cert_info.private_key);
+               X509_free(cert_info.certificate);
+       }
+
+       if (!ast_strlen_zero(dtls_cfg->cipher)) {
+               if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) {
+                       ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n",
+                               dtls_cfg->cipher, instance);
+                       return -1;
+               }
+       }
+
+       if (!ast_strlen_zero(dtls_cfg->cafile) || !ast_strlen_zero(dtls_cfg->capath)) {
+               if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) {
+                       ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n",
+                               S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance);
+                       return -1;
+               }
+       }
+
+       rtp->rekey = dtls_cfg->rekey;
+       rtp->suite = dtls_cfg->suite;
+
+       res = dtls_details_initialize(&rtp->dtls, rtp->ssl_ctx, dtls_cfg->default_setup);
+       if (!res) {
+               dtls_setup_rtcp(instance);
+       }
+
+       return res;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1528,11 +2063,15 @@ static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
        return !rtp->ssl_ctx ? 0 : 1;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       SSL *ssl = rtp->dtls.ssl;
 
+       ao2_unlock(instance);
        dtls_srtp_stop_timeout_timer(instance, rtp, 0);
+       ao2_lock(instance);
 
        if (rtp->ssl_ctx) {
                SSL_CTX_free(rtp->ssl_ctx);
@@ -1542,20 +2081,23 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
        if (rtp->dtls.ssl) {
                SSL_free(rtp->dtls.ssl);
                rtp->dtls.ssl = NULL;
-               ast_mutex_destroy(&rtp->dtls.lock);
        }
 
        if (rtp->rtcp) {
+               ao2_unlock(instance);
                dtls_srtp_stop_timeout_timer(instance, rtp, 1);
+               ao2_lock(instance);
 
                if (rtp->rtcp->dtls.ssl) {
-                       SSL_free(rtp->rtcp->dtls.ssl);
+                       if (rtp->rtcp->dtls.ssl != ssl) {
+                               SSL_free(rtp->rtcp->dtls.ssl);
+                       }
                        rtp->rtcp->dtls.ssl = NULL;
-                       ast_mutex_destroy(&rtp->rtcp->dtls.lock);
                }
        }
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1571,6 +2113,7 @@ static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance)
        }
 }
 
+/*! \pre instance is locked */
 static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1578,6 +2121,7 @@ static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_i
        return rtp->dtls.connection;
 }
 
+/*! \pre instance is locked */
 static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1629,6 +2173,7 @@ static void dtls_set_setup(enum ast_rtp_dtls_setup *dtls_setup, enum ast_rtp_dtl
        }
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1642,6 +2187,7 @@ static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_r
        }
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint)
 {
        char *tmp = ast_strdupa(fingerprint), *value;
@@ -1659,6 +2205,7 @@ static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum
        }
 }
 
+/*! \pre instance is locked */
 static enum ast_rtp_dtls_hash ast_rtp_dtls_get_fingerprint_hash(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1666,6 +2213,7 @@ static enum ast_rtp_dtls_hash ast_rtp_dtls_get_fingerprint_hash(struct ast_rtp_i
        return rtp->local_hash;
 }
 
+/*! \pre instance is locked */
 static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1718,13 +2266,20 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
 #ifdef HAVE_PJPROJECT
        .ice = &ast_rtp_ice,
 #endif
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        .dtls = &ast_rtp_dtls,
        .activate = ast_rtp_activate,
 #endif
+       .ssrc_get = ast_rtp_get_ssrc,
+       .cname_get = ast_rtp_get_cname,
+       .set_remote_ssrc = ast_rtp_set_remote_ssrc,
+       .set_stream_num = ast_rtp_set_stream_num,
+       .extension_enable = ast_rtp_extension_enable,
+       .bundle = ast_rtp_bundle,
 };
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+/*! \pre instance is locked */
 static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtls_details *dtls, int rtcp)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1739,56 +2294,90 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl
 
        SSL_do_handshake(dtls->ssl);
 
-       /* Since the handshake is started in a thread outside of the channel thread it's possible
-        * for the response to be handled in the channel thread before we start the timeout timer.
-        * To ensure this doesn't actually happen we hold the DTLS lock. The channel thread will
-        * block until we're done at which point the timeout timer will be immediately stopped.
+       /*
+        * A race condition is prevented between this function and __rtp_recvfrom()
+        * because both functions have to get the instance lock before they can do
+        * anything.  Without holding the instance lock, this function could start
+        * the SSL handshake above in one thread and the __rtp_recvfrom() function
+        * called by the channel thread could read the response and stop the timeout
+        * timer before we have a chance to even start it.
         */
-       ast_mutex_lock(&dtls->lock);
-       dtls_srtp_check_pending(instance, rtp, rtcp);
        dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
-       ast_mutex_unlock(&dtls->lock);
+
+       /*
+        * We must call dtls_srtp_check_pending() after starting the timer.
+        * Otherwise we won't prevent the race condition.
+        */
+       dtls_srtp_check_pending(instance, rtp, rtcp);
+}
+#endif
+
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+static void dtls_perform_setup(struct dtls_details *dtls)
+{
+       if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {
+               return;
+       }
+
+       SSL_clear(dtls->ssl);
+       if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+               SSL_set_accept_state(dtls->ssl);
+       } else {
+               SSL_set_connect_state(dtls->ssl);
+       }
+       dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
 }
 #endif
 
 #ifdef HAVE_PJPROJECT
-static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
+static void rtp_learning_start(struct ast_rtp *rtp);
 
+/* PJPROJECT ICE callback */
 static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
 {
        struct ast_rtp_instance *instance = ice->user_data;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+       ao2_lock(instance);
        if (status == PJ_SUCCESS) {
                struct ast_sockaddr remote_address;
 
-               /* Symmetric RTP must be disabled for the remote address to not get overwritten */
-               ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 0);
+               ast_sockaddr_setnull(&remote_address);
+               update_address_with_ice_candidate(ice, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
+               if (!ast_sockaddr_isnull(&remote_address)) {
+                       /* Symmetric RTP must be disabled for the remote address to not get overwritten */
+                       ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 0);
 
-               update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
-               ast_rtp_instance_set_remote_address(instance, &remote_address);
+                       ast_rtp_instance_set_remote_address(instance, &remote_address);
+               }
 
                if (rtp->rtcp) {
-                       update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &rtp->rtcp->them);
+                       update_address_with_ice_candidate(ice, AST_RTP_ICE_COMPONENT_RTCP, &rtp->rtcp->them);
                }
        }
-#ifdef HAVE_OPENSSL_SRTP
+
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+
+       dtls_perform_setup(&rtp->dtls);
        dtls_perform_handshake(instance, &rtp->dtls, 0);
 
-       if (rtp->rtcp) {
+       if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+               dtls_perform_setup(&rtp->rtcp->dtls);
                dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
        }
 #endif
 
        if (!strictrtp) {
+               ao2_unlock(instance);
                return;
        }
 
-       rtp->strict_rtp_state = STRICT_RTP_LEARN;
-       rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
+       ast_verb(4, "%p -- Strict RTP learning after ICE completion\n", rtp);
+       rtp_learning_start(rtp);
+       ao2_unlock(instance);
 }
 
+/* PJPROJECT ICE callback */
 static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
 {
        struct ast_rtp_instance *instance = ice->user_data;
@@ -1805,6 +2394,7 @@ static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned
        }
 }
 
+/* PJPROJECT ICE callback */
 static pj_status_t ast_rtp_on_ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len)
 {
        struct ast_rtp_instance *instance = ice->user_data;
@@ -1900,7 +2490,8 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
        return 1;
 }
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+/*! \pre instance is locked */
 static int dtls_srtp_handle_timeout(struct ast_rtp_instance *instance, int rtcp)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -1919,13 +2510,15 @@ static int dtls_srtp_handle_timeout(struct ast_rtp_instance *instance, int rtcp)
        return dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
 }
 
+/* Scheduler callback */
 static int dtls_srtp_handle_rtp_timeout(const void *data)
 {
        struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
        int reschedule;
 
+       ao2_lock(instance);
        reschedule = dtls_srtp_handle_timeout(instance, 0);
-
+       ao2_unlock(instance);
        if (!reschedule) {
                ao2_ref(instance, -1);
        }
@@ -1933,13 +2526,15 @@ static int dtls_srtp_handle_rtp_timeout(const void *data)
        return reschedule;
 }
 
+/* Scheduler callback */
 static int dtls_srtp_handle_rtcp_timeout(const void *data)
 {
        struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
        int reschedule;
 
+       ao2_lock(instance);
        reschedule = dtls_srtp_handle_timeout(instance, 1);
-
+       ao2_unlock(instance);
        if (!reschedule) {
                ao2_ref(instance, -1);
        }
@@ -1967,6 +2562,7 @@ static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, str
        }
 }
 
+/*! \pre Must not be called with the instance locked. */
 static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
 {
        struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
@@ -1974,6 +2570,7 @@ static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, stru
        AST_SCHED_DEL_UNREF(rtp->sched, dtls->timeout_timer, ao2_ref(instance, -1));
 }
 
+/*! \pre instance is locked */
 static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
 {
        struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
@@ -2007,80 +2604,40 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as
        }
 }
 
+/* Scheduler callback */
 static int dtls_srtp_renegotiate(const void *data)
 {
        struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+       ao2_lock(instance);
+
        SSL_renegotiate(rtp->dtls.ssl);
        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);
        }
 
        rtp->rekeyid = -1;
+
+       ao2_unlock(instance);
        ao2_ref(instance, -1);
 
        return 0;
 }
 
-static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp)
+static int dtls_srtp_add_local_ssrc(struct ast_rtp *rtp, struct ast_rtp_instance *instance, int rtcp, unsigned int ssrc, int set_remote_policy)
 {
        unsigned char material[SRTP_MASTER_LEN * 2];
        unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
        struct ast_srtp_policy *local_policy, *remote_policy = NULL;
-       struct ast_rtp_instance_stats stats = { 0, };
        int res = -1;
        struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
 
-       /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
-       if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
-               X509 *certificate;
-
-               if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) {
-                       ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
-                       return -1;
-               }
-
-               /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
-               if (rtp->remote_fingerprint[0]) {
-                       const EVP_MD *type;
-                       unsigned char fingerprint[EVP_MAX_MD_SIZE];
-                       unsigned int size;
-
-                       if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA1) {
-                               type = EVP_sha1();
-                       } else if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA256) {
-                               type = EVP_sha256();
-                       } else {
-                               ast_log(LOG_WARNING, "Unsupported fingerprint hash type on RTP instance '%p'\n", instance);
-                               return -1;
-                       }
-
-                       if (!X509_digest(certificate, type, fingerprint, &size) ||
-                           !size ||
-                           memcmp(fingerprint, rtp->remote_fingerprint, size)) {
-                               X509_free(certificate);
-                               ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n",
-                                       instance);
-                               return -1;
-                       }
-               }
-
-               X509_free(certificate);
-       }
-
-       /* Ensure that certificate verification was successful */
-       if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(dtls->ssl) != X509_V_OK) {
-               ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",
-                       instance);
-               return -1;
-       }
-
        /* Produce key information and set up SRTP */
        if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
                ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",
@@ -2115,41 +2672,31 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
                goto error;
        }
 
-       if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) {
-               goto error;
-       }
+       res_srtp_policy->set_ssrc(local_policy, ssrc, 0);
 
-       res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0);
+       if (set_remote_policy) {
+               if (!(remote_policy = res_srtp_policy->alloc())) {
+                       goto error;
+               }
 
-       if (!(remote_policy = res_srtp_policy->alloc())) {
-               goto error;
-       }
+               if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {
+                       ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);
+                       goto error;
+               }
 
-       if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {
-               ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);
-               goto error;
-       }
+               if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {
+                       ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
+                       goto error;
+               }
 
-       if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {
-               ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
-               goto error;
+               res_srtp_policy->set_ssrc(remote_policy, 0, 1);
        }
 
-       res_srtp_policy->set_ssrc(remote_policy, 0, 1);
-
        if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) {
                ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);
                goto error;
        }
 
-       if (rtp->rekey) {
-               ao2_ref(instance, +1);
-               if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) {
-                       ao2_ref(instance, -1);
-                       goto error;
-               }
-       }
-
        res = 0;
 
 error:
@@ -2162,14 +2709,126 @@ error:
 
        return res;
 }
+
+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_rtp_instance *instance, int rtcp)
+{
+       struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
+       int index;
+
+       /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+       if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
+               X509 *certificate;
+
+               if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) {
+                       ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
+                       return -1;
+               }
+
+               /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+               if (rtp->remote_fingerprint[0]) {
+                       const EVP_MD *type;
+                       unsigned char fingerprint[EVP_MAX_MD_SIZE];
+                       unsigned int size;
+
+                       if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA1) {
+                               type = EVP_sha1();
+                       } else if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA256) {
+                               type = EVP_sha256();
+                       } else {
+                               ast_log(LOG_WARNING, "Unsupported fingerprint hash type on RTP instance '%p'\n", instance);
+                               return -1;
+                       }
+
+                       if (!X509_digest(certificate, type, fingerprint, &size) ||
+                           !size ||
+                           memcmp(fingerprint, rtp->remote_fingerprint, size)) {
+                               X509_free(certificate);
+                               ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n",
+                                       instance);
+                               return -1;
+                       }
+               }
+
+               X509_free(certificate);
+       }
+
+       if (dtls_srtp_add_local_ssrc(rtp, instance, rtcp, ast_rtp_instance_get_ssrc(instance), 1)) {
+               return -1;
+       }
+
+       for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
+               struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
+
+               if (dtls_srtp_add_local_ssrc(rtp, instance, rtcp, ast_rtp_instance_get_ssrc(mapping->instance), 0)) {
+                       return -1;
+               }
+       }
+
+       if (rtp->rekey) {
+               ao2_ref(instance, +1);
+               if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) {
+                       ao2_ref(instance, -1);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
 #endif
 
+/*! \brief Helper function to compare an elem in a vector by value */
+static int compare_by_value(int elem, int value)
+{
+       return elem - value;
+}
+
+/*! \brief Helper function to find an elem in a vector by value */
+static int find_by_value(int elem, int value)
+{
+       return elem == value;
+}
+
+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;
+}
+
+/*! \pre instance is locked */
 static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp)
 {
        int len;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        char *in = buf;
+#endif
 #ifdef HAVE_PJPROJECT
        struct ast_sockaddr *loop = rtcp ? &rtp->rtcp_loop : &rtp->rtp_loop;
 #endif
@@ -2178,7 +2837,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
           return len;
        }
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        /* If this is an SSL packet pass it to OpenSSL for processing. RFC section for first byte value:
         * https://tools.ietf.org/html/rfc5764#section-5.1.2 */
        if ((*in >= 20) && (*in <= 63)) {
@@ -2192,14 +2851,18 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
                        return -1;
                }
 
-               /* This mutex is locked so that this thread blocks until the dtls_perform_handshake function
-                * completes.
+               /*
+                * A race condition is prevented between dtls_perform_handshake()
+                * and this function because both functions have to get the
+                * instance lock before they can do anything.  The
+                * dtls_perform_handshake() function needs to start the timer
+                * before we stop it below.
                 */
-               ast_mutex_lock(&dtls->lock);
-               ast_mutex_unlock(&dtls->lock);
 
                /* Before we feed data into OpenSSL ensure that the timeout timer is either stopped or completed */
+               ao2_unlock(instance);
                dtls_srtp_stop_timeout_timer(instance, rtp, rtcp);
+               ao2_lock(instance);
 
                /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
                if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
@@ -2226,7 +2889,11 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
                        /* Any further connections will be existing since this is now established */
                        dtls->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
                        /* Use the keying material to set up key/salt information */
-                       res = dtls_srtp_setup(rtp, srtp, instance, rtcp);
+                       if ((res = dtls_srtp_setup(rtp, instance, rtcp))) {
+                               return res;
+                       }
+                       /* Notify that dtls has been established */
+                       res = RTP_DTLS_ESTABLISHED;
                } else {
                        /* Since we've sent additional traffic start the timeout timer for retransmission */
                        dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
@@ -2250,14 +2917,22 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
                pj_str_t combined = pj_str(ast_sockaddr_stringify(sa));
                pj_sockaddr address;
                pj_status_t status;
+               struct ice_wrap *ice;
 
                pj_thread_register_check();
 
                pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &combined, &address);
 
-               status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP,
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ice = rtp->ice;
+               ao2_ref(ice, +1);
+               ao2_unlock(instance);
+               status = pj_ice_sess_on_rx_pkt(ice->real_ice,
+                       rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP,
                        rtcp ? TRANSPORT_SOCKET_RTCP : TRANSPORT_SOCKET_RTP, buf, len, &address,
                        pj_sockaddr_get_len(&address));
+               ao2_ref(ice, -1);
+               ao2_lock(instance);
                if (status != PJ_SUCCESS) {
                        char buf[100];
 
@@ -2267,55 +2942,87 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
                        return -1;
                }
                if (!rtp->passthrough) {
+                       /* If a unidirectional ICE negotiation occurs then lock on to the source of the
+                        * ICE traffic and use it as the target. This will occur if the remote side only
+                        * wants to receive media but never send to us.
+                        */
+                       if (!rtp->ice_active_remote_candidates && !rtp->ice_proposed_remote_candidates) {
+                               if (rtcp) {
+                                       ast_sockaddr_copy(&rtp->rtcp->them, sa);
+                               } else {
+                                       ast_rtp_instance_set_remote_address(instance, sa);
+                               }
+                       }
                        return 0;
                }
                rtp->passthrough = 0;
        }
 #endif
 
-       if ((*in & 0xC0) && res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, rtcp) < 0) {
-          return -1;
-       }
-
        return len;
 }
 
+/*! \pre instance is locked */
 static int rtcp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa)
 {
        return __rtp_recvfrom(instance, buf, size, flags, sa, 1);
 }
 
+/*! \pre instance is locked */
 static int rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa)
 {
        return __rtp_recvfrom(instance, buf, size, flags, sa, 0);
 }
 
-static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp)
+/*! \pre instance is locked */
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *via_ice, int use_srtp)
 {
        int len = size;
        void *temp = buf;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
+       struct ast_rtp_instance *transport = rtp->bundled ? rtp->bundled : instance;
+       struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(transport);
+       struct ast_srtp *srtp = ast_rtp_instance_get_srtp(transport, rtcp);
        int res;
 
-       *ice = 0;
+       *via_ice = 0;
 
        if (use_srtp && res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
                return -1;
        }
 
 #ifdef HAVE_PJPROJECT
-       if (rtp->ice) {
+       if (transport_rtp->ice) {
+               enum ast_rtp_ice_component_type component = rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP;
+               pj_status_t status;
+               struct ice_wrap *ice;
+
+               /* If RTCP is sharing the same socket then use the same component */
+               if (rtcp && rtp->rtcp->s == rtp->s) {
+                       component = AST_RTP_ICE_COMPONENT_RTP;
+               }
+
                pj_thread_register_check();
 
-               if (pj_ice_sess_send_data(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len) == PJ_SUCCESS) {
-                       *ice = 1;
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ice = transport_rtp->ice;
+               ao2_ref(ice, +1);
+               if (instance == transport) {
+                       ao2_unlock(instance);
+               }
+               status = pj_ice_sess_send_data(ice->real_ice, component, temp, len);
+               ao2_ref(ice, -1);
+               if (instance == transport) {
+                       ao2_lock(instance);
+               }
+               if (status == PJ_SUCCESS) {
+                       *via_ice = 1;
                        return len;
                }
        }
 #endif
 
-       res = ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
+       res = ast_sendto(rtcp ? transport_rtp->rtcp->s : transport_rtp->s, temp, len, flags, sa);
        if (res > 0) {
                ast_rtp_instance_set_last_tx(instance, time(NULL));
        }
@@ -2323,11 +3030,13 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
        return res;
 }
 
+/*! \pre instance is locked */
 static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
        return __rtp_sendto(instance, buf, size, flags, sa, 1, ice, 1);
 }
 
+/*! \pre instance is locked */
 static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -2391,22 +3100,18 @@ static double stddev_compute(double stddev, double sample, double normdev, doubl
 
 static int create_new_socket(const char *type, int af)
 {
-       int sock = socket(af, SOCK_DGRAM, 0);
+       int sock = ast_socket_nonblock(af, SOCK_DGRAM, 0);
 
        if (sock < 0) {
-               if (!type) {
-                       type = "RTP/RTCP";
-               }
                ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno));
-       } else {
-               long flags = fcntl(sock, F_GETFL);
-               fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+               return sock;
+       }
+
 #ifdef SO_NO_CHECK
-               if (nochecksums) {
-                       setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
-               }
-#endif
+       if (nochecksums) {
+               setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
        }
+#endif
 
        return sock;
 }
@@ -2421,8 +3126,9 @@ static int create_new_socket(const char *type, int af)
  */
 static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
 {
-       info->max_seq = seq - 1;
+       info->max_seq = seq;
        info->packets = learning_min_sequential;
+       memset(&info->received, 0, sizeof(info->received));
 }
 
 /*!
@@ -2437,16 +3143,61 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
  */
 static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq)
 {
-       if (seq == info->max_seq + 1) {
+       if (seq == (uint16_t) (info->max_seq + 1)) {
                /* packet is in sequence */
                info->packets--;
        } else {
                /* Sequence discontinuity; reset */
                info->packets = learning_min_sequential - 1;
+               info->received = ast_tvnow();
+       }
+
+       /* Only check time if strictrtp is set to yes. Otherwise, we only needed to check seqno */
+       if (strictrtp == STRICT_RTP_YES) {
+               switch (info->stream_type) {
+               case AST_MEDIA_TYPE_UNKNOWN:
+               case AST_MEDIA_TYPE_AUDIO:
+                       /*
+                        * Protect against packet floods by checking that we
+                        * received the packet sequence in at least the minimum
+                        * allowed time.
+                        */
+                       if (ast_tvzero(info->received)) {
+                               info->received = ast_tvnow();
+                       } else if (!info->packets
+                               && ast_tvdiff_ms(ast_tvnow(), info->received) < learning_min_duration) {
+                               /* Packet flood; reset */
+                               info->packets = learning_min_sequential - 1;
+                               info->received = ast_tvnow();
+                       }
+                       break;
+               case AST_MEDIA_TYPE_VIDEO:
+               case AST_MEDIA_TYPE_IMAGE:
+               case AST_MEDIA_TYPE_TEXT:
+               case AST_MEDIA_TYPE_END:
+                       break;
+               }
        }
+
        info->max_seq = seq;
 
-       return (info->packets == 0);
+       return info->packets;
+}
+
+/*!
+ * \brief Start the strictrtp learning mode.
+ *
+ * \param rtp RTP session description
+ *
+ * \return Nothing
+ */
+static void rtp_learning_start(struct ast_rtp *rtp)
+{
+       rtp->strict_rtp_state = STRICT_RTP_LEARN;
+       memset(&rtp->rtp_source_learn.proposed_address, 0,
+               sizeof(rtp->rtp_source_learn.proposed_address));
+       rtp->rtp_source_learn.start = ast_tvnow();
+       rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t) rtp->lastrxseqno);
 }
 
 #ifdef HAVE_PJPROJECT
@@ -2476,10 +3227,37 @@ static int rtp_address_is_ice_blacklisted(const pj_sockaddr_t *address)
        return result;
 }
 
+/*!
+ * \internal
+ * \brief Checks an address against the STUN blacklist
+ * \since 13.16.0
+ *
+ * \note If there is no stun_blacklist list, always returns 0
+ *
+ * \param addr The address to consider
+ *
+ * \retval 0 if address is not STUN blacklisted
+ * \retval 1 if address is STUN blacklisted
+ */
+static int stun_address_is_blacklisted(const struct ast_sockaddr *addr)
+{
+       int result = 1;
+
+       ast_rwlock_rdlock(&stun_blacklist_lock);
+       if (!stun_blacklist
+               || ast_apply_ha(stun_blacklist, addr) == AST_SENSE_ALLOW) {
+               result = 0;
+       }
+       ast_rwlock_unlock(&stun_blacklist_lock);
+
+       return result;
+}
+
+/*! \pre instance is locked */
 static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *addr, int port, int component,
                                      int transport)
 {
-       pj_sockaddr address[16];
+       pj_sockaddr address[PJ_ICE_MAX_CAND];
        unsigned int count = PJ_ARRAY_SIZE(address), pos = 0;
        int basepos = -1;
 
@@ -2500,8 +3278,9 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
                                basepos = pos;
                        }
                        pj_sockaddr_set_port(&address[pos], port);
-                       ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
-                                    pj_sockaddr_get_len(&address[pos]));
+                       ast_rtp_ice_add_cand(instance, rtp, component, transport,
+                               PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
+                               pj_sockaddr_get_len(&address[pos]));
                }
        }
        if (basepos == -1) {
@@ -2510,31 +3289,55 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
        }
 
        /* If configured to use a STUN server to get our external mapped address do so */
-       if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr) && count) {
+       if (count && stunaddr.sin_addr.s_addr && !stun_address_is_blacklisted(addr) &&
+               (ast_sockaddr_is_ipv4(addr) || ast_sockaddr_is_any(addr))) {
                struct sockaddr_in answer;
+               int rsp;
 
-               if (!ast_stun_request(component == AST_RTP_ICE_COMPONENT_RTCP ? rtp->rtcp->s : rtp->s, &stunaddr, NULL, &answer)) {
+               /*
+                * The instance should not be locked because we can block
+                * waiting for a STUN respone.
+                */
+               ao2_unlock(instance);
+               rsp = ast_stun_request(component == AST_RTP_ICE_COMPONENT_RTCP
+                       ? rtp->rtcp->s : rtp->s, &stunaddr, NULL, &answer);
+               ao2_lock(instance);
+               if (!rsp) {
                        pj_sockaddr base;
-                       pj_sockaddr ext;
-                       pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
-                       int srflx = 1;
 
-                       /* Use the first local host candidate as the base */
-                       pj_sockaddr_cp(&base, &address[basepos]);
-
-                       pj_sockaddr_init(pj_AF_INET(), &ext, &mapped, ntohs(answer.sin_port));
-
-                       /* If the returned address is the same as one of our host candidates, don't send the srflx */
-                       for (pos = 0; pos < count; pos++) {
-                               if ((pj_sockaddr_cmp(&address[pos], &ext) == 0) && !rtp_address_is_ice_blacklisted(&address[pos])) {
-                                       srflx = 0;
+                       /* Use the first local IPv4 host candidate as the base */
+                       for (pos = basepos; pos < count; pos++) {
+                               if (address[pos].addr.sa_family == PJ_AF_INET &&
+                                       !rtp_address_is_ice_blacklisted(&address[pos])) {
+                                       pj_sockaddr_cp(&base, &address[pos]);
                                        break;
                                }
                        }
 
-                       if (srflx) {
-                               ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &ext, &base,
-                                                        &base, pj_sockaddr_get_len(&ext));
+                       if (pos < count) {
+                               pj_sockaddr ext;
+                               pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
+                               int srflx = 1;
+
+                               pj_sockaddr_init(pj_AF_INET(), &ext, &mapped, ntohs(answer.sin_port));
+
+                               /*
+                                * If the returned address is the same as one of our host
+                                * candidates, don't send the srflx
+                                */
+                               for (pos = 0; pos < count; pos++) {
+                                       if (pj_sockaddr_cmp(&address[pos], &ext) == 0 &&
+                                               !rtp_address_is_ice_blacklisted(&address[pos])) {
+                                               srflx = 0;
+                                               break;
+                                       }
+                               }
+
+                               if (srflx) {
+                                       ast_rtp_ice_add_cand(instance, rtp, component, transport,
+                                               PJ_ICE_CAND_TYPE_SRFLX, 65535, &ext, &base, &base,
+                                               pj_sockaddr_get_len(&ext));
+                               }
                        }
                }
        }
@@ -2586,6 +3389,8 @@ static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
  * \param port port to use for adding RTP candidates to the ICE session
  * \param replace 0 when creating a new session, 1 when replacing a destroyed session
  *
+ * \pre instance is locked
+ *
  * \retval 0 on success
  * \retval -1 on failure
  */
@@ -2594,11 +3399,21 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
 {
        pj_stun_config stun_config;
        pj_str_t ufrag, passwd;
+       pj_status_t status;
+       struct ice_wrap *ice_old;
+       struct ice_wrap *ice;
+       pj_ice_sess *real_ice = NULL;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
        ao2_cleanup(rtp->ice_local_candidates);
        rtp->ice_local_candidates = NULL;
 
+       ice = ao2_alloc_options(sizeof(*ice), ice_wrap_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!ice) {
+               ast_rtp_ice_stop(instance);
+               return -1;
+       }
+
        pj_thread_register_check();
 
        pj_stun_config_init(&stun_config, &cachingpool.factory, 0, NULL, timer_heap);
@@ -2606,19 +3421,32 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
        ufrag = pj_str(rtp->local_ufrag);
        passwd = pj_str(rtp->local_passwd);
 
+       /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+       ao2_unlock(instance);
        /* Create an ICE session for ICE negotiation */
-       if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2,
-                       &ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &rtp->ice) == PJ_SUCCESS) {
-               /* Make this available for the callbacks */
-               rtp->ice->user_data = instance;
+       status = pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN,
+               rtp->ice_num_components, &ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &real_ice);
+       ao2_lock(instance);
+       if (status == PJ_SUCCESS) {
+               /* Safely complete linking the ICE session into the instance */
+               real_ice->user_data = instance;
+               ice->real_ice = real_ice;
+               ice_old = rtp->ice;
+               rtp->ice = ice;
+               if (ice_old) {
+                       ao2_unlock(instance);
+                       ao2_ref(ice_old, -1);
+                       ao2_lock(instance);
+               }
 
                /* Add all of the available candidates to the ICE session */
                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);
@@ -2627,43 +3455,30 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
                return 0;
        }
 
+       /*
+        * It is safe to unref this while instance is locked here.
+        * It was not initialized with a real_ice pointer.
+        */
+       ao2_ref(ice, -1);
+
+       ast_rtp_ice_stop(instance);
        return -1;
 
 }
 #endif
 
-static int ast_rtp_new(struct ast_rtp_instance *instance,
-                      struct ast_sched_context *sched, struct ast_sockaddr *addr,
-                      void *data)
+static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
 {
-       struct ast_rtp *rtp = NULL;
        int x, startplace;
 
-       /* Create a new RTP structure to hold all of our data */
-       if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {
-               return -1;
-       }
-
-       /* Initialize synchronization aspects */
-       ast_mutex_init(&rtp->lock);
-       ast_cond_init(&rtp->cond, NULL);
-
-       /* Set default parameters on the newly created RTP structure */
-       rtp->ssrc = ast_random();
-       rtp->seqno = ast_random() & 0x7fff;
-       rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);
-       if (strictrtp) {
-               rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
-               rtp_learning_seq_init(&rtp->alt_source_learn, (uint16_t)rtp->seqno);
-       }
+       rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
 
        /* Create a new socket for us to listen on and use */
        if ((rtp->s =
             create_new_socket("RTP",
-                              ast_sockaddr_is_ipv4(addr) ? AF_INET  :
-                              ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) {
+                              ast_sockaddr_is_ipv4(&rtp->bind_address) ? AF_INET  :
+                              ast_sockaddr_is_ipv6(&rtp->bind_address) ? AF_INET6 : -1)) < 0) {
                ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);
-               ast_free(rtp);
                return -1;
        }
 
@@ -2673,11 +3488,12 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
        startplace = x;
 
        for (;;) {
-               ast_sockaddr_set_port(addr, x);
+               ast_sockaddr_set_port(&rtp->bind_address, x);
                /* Try to bind, this will tell us whether the port is available or not */
-               if (!ast_bind(rtp->s, addr)) {
+               if (!ast_bind(rtp->s, &rtp->bind_address)) {
                        ast_debug(1, "Allocated port %d for RTP instance '%p'\n", x, instance);
-                       ast_rtp_instance_set_local_address(instance, addr);
+                       ast_rtp_instance_set_local_address(instance, &rtp->bind_address);
+                       ast_test_suite_event_notify("RTP_PORT_ALLOCATED", "Port: %d", x);
                        break;
                }
 
@@ -2690,134 +3506,241 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
                if (x == startplace || (errno != EADDRINUSE && errno != EACCES)) {
                        ast_log(LOG_ERROR, "Oh dear... we couldn't allocate a port for RTP instance '%p'\n", instance);
                        close(rtp->s);
-                       ast_free(rtp);
+                       rtp->s = -1;
                        return -1;
                }
        }
 
 #ifdef HAVE_PJPROJECT
+       /* Initialize synchronization aspects */
+       ast_cond_init(&rtp->cond, NULL);
+
        generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag));
        generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd));
-#endif
-       ast_rtp_instance_set_data(instance, rtp);
-#ifdef HAVE_PJPROJECT
+
        /* Create an ICE session for ICE negotiation */
        if (icesupport) {
-               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");
+               rtp->ice_num_components = 2;
+               ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->bind_address), x, instance);
+               if (ice_create(instance, &rtp->bind_address, x, 0)) {
+                       ast_log(LOG_NOTICE, "Failed to create ICE session\n");
                } else {
                        rtp->ice_port = x;
-                       ast_sockaddr_copy(&rtp->ice_original_rtp_addr, addr);
+                       ast_sockaddr_copy(&rtp->ice_original_rtp_addr, &rtp->bind_address);
                }
        }
 #endif
 
-       /* Record any information we may need */
-       rtp->sched = sched;
-
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        rtp->rekeyid = -1;
        rtp->dtls.timeout_timer = -1;
 #endif
 
-       rtp->f.subclass.format = ao2_bump(ast_format_none);
-       rtp->lastrxformat = ao2_bump(ast_format_none);
-       rtp->lasttxformat = ao2_bump(ast_format_none);
-
        return 0;
 }
 
-static int ast_rtp_destroy(struct ast_rtp_instance *instance)
+static void rtp_deallocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
 {
-       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       int saved_rtp_s = rtp->s;
 #ifdef HAVE_PJPROJECT
        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, };
 #endif
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
        ast_rtp_dtls_stop(instance);
 #endif
 
-       /* Destroy the smoother that was smoothing out audio if present */
-       if (rtp->smoother) {
-               ast_smoother_free(rtp->smoother);
-       }
-
        /* Close our own socket so we no longer get packets */
        if (rtp->s > -1) {
                close(rtp->s);
+               rtp->s = -1;
        }
 
        /* Destroy RTCP if it was being used */
-       if (rtp->rtcp) {
-               /*
-                * It is not possible for there to be an active RTCP scheduler
-                * entry at this point since it holds a reference to the
-                * RTP instance while it's active.
-                */
-               close(rtp->rtcp->s);
-               ast_free(rtp->rtcp->local_addr_str);
-               ast_free(rtp->rtcp);
-       }
-
-       /* Destroy RED if it was being used */
-       if (rtp->red) {
-               AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
-               ast_free(rtp->red);
-               rtp->red = NULL;
+       if (rtp->rtcp && rtp->rtcp->s > -1) {
+               if (saved_rtp_s != rtp->rtcp->s) {
+                       close(rtp->rtcp->s);
+               }
+               rtp->rtcp->s = -1;
        }
 
 #ifdef HAVE_PJPROJECT
        pj_thread_register_check();
 
-       /* Destroy the RTP TURN relay if being used */
-       ast_mutex_lock(&rtp->lock);
+       /*
+        * The instance lock is already held.
+        *
+        * Destroy the RTP TURN relay if being used
+        */
        if (rtp->turn_rtp) {
-               pj_turn_sock_destroy(rtp->turn_rtp);
                rtp->turn_state = PJ_TURN_STATE_NULL;
+
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ao2_unlock(instance);
+               pj_turn_sock_destroy(rtp->turn_rtp);
+               ao2_lock(instance);
                while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
-                       ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+                       ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
                }
+               rtp->turn_rtp = NULL;
        }
 
        /* Destroy the RTCP TURN relay if being used */
        if (rtp->turn_rtcp) {
-               pj_turn_sock_destroy(rtp->turn_rtcp);
                rtp->turn_state = PJ_TURN_STATE_NULL;
+
+               /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
+               ao2_unlock(instance);
+               pj_turn_sock_destroy(rtp->turn_rtcp);
+               ao2_lock(instance);
                while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
-                       ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+                       ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
                }
-       }
-       ast_mutex_unlock(&rtp->lock);
-
-       if (rtp->ioqueue) {
-               rtp_ioqueue_thread_remove(rtp->ioqueue);
+               rtp->turn_rtcp = NULL;
        }
 
-       /* Destroy the ICE session if being used */
-       if (rtp->ice) {
-               pj_ice_sess_destroy(rtp->ice);
-       }
+       /* Destroy any ICE session */
+       ast_rtp_ice_stop(instance);
 
        /* Destroy any candidates */
        if (rtp->ice_local_candidates) {
                ao2_ref(rtp->ice_local_candidates, -1);
+               rtp->ice_local_candidates = NULL;
        }
 
        if (rtp->ice_active_remote_candidates) {
                ao2_ref(rtp->ice_active_remote_candidates, -1);
+               rtp->ice_active_remote_candidates = NULL;
        }
-#endif
-
-       ao2_cleanup(rtp->lasttxformat);
-       ao2_cleanup(rtp->lastrxformat);
-       ao2_cleanup(rtp->f.subclass.format);
-
-       /* Destroy synchronization items */
-       ast_mutex_destroy(&rtp->lock);
-       ast_cond_destroy(&rtp->cond);
+
+       if (rtp->ioqueue) {
+               /*
+                * We cannot hold the instance lock because we could wait
+                * for the ioqueue thread to die and we might deadlock as
+                * a result.
+                */
+               ao2_unlock(instance);
+               rtp_ioqueue_thread_remove(rtp->ioqueue);
+               ao2_lock(instance);
+               rtp->ioqueue = NULL;
+       }
+#endif
+}
+
+/*! \pre instance is locked */
+static int ast_rtp_new(struct ast_rtp_instance *instance,
+                      struct ast_sched_context *sched, struct ast_sockaddr *addr,
+                      void *data)
+{
+       struct ast_rtp *rtp = NULL;
+
+       /* Create a new RTP structure to hold all of our data */
+       if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {
+               return -1;
+       }
+
+       /* Set default parameters on the newly created RTP structure */
+       rtp->ssrc = ast_random();
+       ast_uuid_generate_str(rtp->cname, sizeof(rtp->cname));
+       rtp->seqno = ast_random() & 0x7fff;
+       rtp->expectedrxseqno = -1;
+       rtp->expectedseqno = -1;
+       rtp->sched = sched;
+       ast_sockaddr_copy(&rtp->bind_address, addr);
+
+       /* Transport creation operations can grab the RTP data from the instance, so set it */
+       ast_rtp_instance_set_data(instance, rtp);
+
+       if (rtp_allocate_transport(instance, rtp)) {
+               return -1;
+       }
+
+       if (AST_VECTOR_INIT(&rtp->ssrc_mapping, 1)) {
+               return -1;
+       }
+
+       rtp->f.subclass.format = ao2_bump(ast_format_none);
+       rtp->lastrxformat = ao2_bump(ast_format_none);
+       rtp->lasttxformat = ao2_bump(ast_format_none);
+       rtp->stream_num = -1;
+
+       return 0;
+}
+
+/*!
+ * \brief SSRC mapping comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()
+ *
+ * \param elem Element to compare against
+ * \param value Value to compare with the vector element.
+ *
+ * \return 0 if element does not match.
+ * \return Non-zero if element matches.
+ */
+#define SSRC_MAPPING_ELEM_CMP(elem, value) ((elem).instance == (value))
+
+/*! \pre instance is locked */
+static int ast_rtp_destroy(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (rtp->bundled) {
+               struct ast_rtp *bundled_rtp;
+
+               /* We can't hold our instance lock while removing ourselves from the parent */
+               ao2_unlock(instance);
+
+               ao2_lock(rtp->bundled);
+               bundled_rtp = ast_rtp_instance_get_data(rtp->bundled);
+               AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, instance, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP);
+               ao2_unlock(rtp->bundled);
+
+               ao2_lock(instance);
+               ao2_ref(rtp->bundled, -1);
+       }
+
+       rtp_deallocate_transport(instance, rtp);
+
+       /* Destroy the smoother that was smoothing out audio if present */
+       if (rtp->smoother) {
+               ast_smoother_free(rtp->smoother);
+       }
+
+       /* Destroy RTCP if it was being used */
+       if (rtp->rtcp) {
+               /*
+                * It is not possible for there to be an active RTCP scheduler
+                * entry at this point since it holds a reference to the
+                * RTP instance while it's active.
+                */
+               ast_free(rtp->rtcp->local_addr_str);
+               ast_free(rtp->rtcp);
+       }
+
+       /* Destroy RED if it was being used */
+       if (rtp->red) {
+               ao2_unlock(instance);
+               AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
+               ao2_lock(instance);
+               ast_free(rtp->red);
+               rtp->red = NULL;
+       }
+
+       /* Destroy the send buffer if it was being used */
+       if (rtp->send_buffer) {
+               ast_data_buffer_free(rtp->send_buffer);
+       }
+
+       /* Destroy the recv buffer if it was being used */
+       if (rtp->recv_buffer) {
+               ast_data_buffer_free(rtp->recv_buffer);
+       }
+
+       ao2_cleanup(rtp->lasttxformat);
+       ao2_cleanup(rtp->lastrxformat);
+       ao2_cleanup(rtp->f.subclass.format);
+       AST_VECTOR_FREE(&rtp->ssrc_mapping);
+       AST_VECTOR_FREE(&rtp->missing_seqno);
 
        /* Finally destroy ourselves */
        ast_free(rtp);
@@ -2825,6 +3748,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
        return 0;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -2832,12 +3756,14 @@ static int ast_rtp_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp
        return 0;
 }
 
+/*! \pre instance is locked */
 static enum ast_rtp_dtmf_mode ast_rtp_dtmf_mode_get(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        return rtp->dtmfmode;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -2912,6 +3838,7 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
        return 0;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -2957,6 +3884,7 @@ static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance)
        return 0;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, char digit, unsigned int duration)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -3029,6 +3957,12 @@ static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, cha
 
        /* Oh and we can't forget to turn off the stuff that says we are sending DTMF */
        rtp->lastts += calc_txstamp(rtp, NULL) * DTMF_SAMPLE_RATE_MS;
+
+       /* Reset the smoother as the delivery time stored in it is now out of date */
+       if (rtp->smoother) {
+               ast_smoother_free(rtp->smoother);
+               rtp->smoother = NULL;
+       }
 cleanup:
        rtp->sending_digit = 0;
        rtp->send_digit = 0;
@@ -3036,11 +3970,13 @@ cleanup:
        return res;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit)
 {
        return ast_rtp_dtmf_end_with_duration(instance, digit, 0);
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_update_source(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -3052,6 +3988,7 @@ static void ast_rtp_update_source(struct ast_rtp_instance *instance)
        return;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_change_source(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -3059,26 +3996,26 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance)
        struct ast_srtp *rtcp_srtp = ast_rtp_instance_get_srtp(instance, 1);
        unsigned int ssrc = ast_random();
 
-       if (!rtp->lastts) {
-               ast_debug(3, "Not changing SSRC since we haven't sent any RTP yet\n");
-               return;
+       if (rtp->lastts) {
+               /* We simply set this bit so that the next packet sent will have the marker bit turned on */
+               ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
        }
 
-       /* We simply set this bit so that the next packet sent will have the marker bit turned on */
-       ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
-
        ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc);
 
        if (srtp) {
                ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);
                res_srtp->change_source(srtp, rtp->ssrc, ssrc);
                if (rtcp_srtp != srtp) {
-                       res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+                       res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc);
                }
        }
 
        rtp->ssrc = ssrc;
 
+       /* Since the source is changing, we don't know what sequence number to expect next */
+       rtp->expectedrxseqno = -1;
+
        return;
 }
 
@@ -3174,35 +4111,24 @@ static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
        rtp->rtcp->rxlost_count++;
 }
 
-/*! \brief Send RTCP SR or RR report */
-static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
+static int ast_rtcp_generate_report(struct ast_rtp_instance *instance, unsigned char *rtcpheader,
+               struct ast_rtp_rtcp_report *rtcp_report, int *sr)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);
-       int res;
        int len = 0;
        struct timeval now;
        unsigned int now_lsw;
        unsigned int now_msw;
-       unsigned int *rtcpheader;
        unsigned int lost_packets;
        int fraction_lost;
        struct timeval dlsr = { 0, };
-       char bdata[512];
-       int rate = rtp_get_rate(rtp->f.subclass.format);
-       int ice;
-       int header_offset = 0;
-       struct ast_sockaddr remote_address = { { 0, } };
        struct ast_rtp_rtcp_report_block *report_block = NULL;
-       RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
-                       ast_rtp_rtcp_report_alloc(rtp->themssrc ? 1 : 0),
-                       ao2_cleanup);
 
        if (!rtp || !rtp->rtcp) {
                return 0;
        }
 
-       if (ast_sockaddr_isnull(&rtp->rtcp->them)) {  /* This'll stop rtcp for this rtp session */
+       if (ast_sockaddr_isnull(&rtp->rtcp->them)) { /* This'll stop rtcp for this rtp session */
                /* RTCP was stopped. */
                return 0;
        }
@@ -3211,21 +4137,23 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
                return 1;
        }
 
+       *sr = rtp->txcount > rtp->rtcp->lastsrtxcount ? 1 : 0;
+
        /* Compute statistics */
        calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);
 
        gettimeofday(&now, NULL);
-       rtcp_report->reception_report_count = rtp->themssrc ? 1 : 0;
+       rtcp_report->reception_report_count = rtp->themssrc_valid ? 1 : 0;
        rtcp_report->ssrc = rtp->ssrc;
-       rtcp_report->type = sr ? RTCP_PT_SR : RTCP_PT_RR;
-       if (sr) {
+       rtcp_report->type = *sr ? RTCP_PT_SR : RTCP_PT_RR;
+       if (*sr) {
                rtcp_report->sender_information.ntp_timestamp = now;
                rtcp_report->sender_information.rtp_timestamp = rtp->lastts;
                rtcp_report->sender_information.packet_count = rtp->txcount;
                rtcp_report->sender_information.octet_count = rtp->txoctetcount;
        }
 
-       if (rtp->themssrc) {
+       if (rtp->themssrc_valid) {
                report_block = ast_calloc(1, sizeof(*report_block));
                if (!report_block) {
                        return 1;
@@ -3236,7 +4164,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
                report_block->lost_count.fraction = (fraction_lost & 0xff);
                report_block->lost_count.packets = (lost_packets & 0xffffff);
                report_block->highest_seq_no = (rtp->cycles | (rtp->lastrxseqno & 0xffff));
-               report_block->ia_jitter = (unsigned int)(rtp->rxjitter * rate);
+               report_block->ia_jitter = (unsigned int)(rtp->rxjitter * rtp_get_rate(rtp->f.subclass.format));
                report_block->lsr = rtp->rtcp->themrxlsr;
                /* If we haven't received an SR report, DLSR should be 0 */
                if (!ast_tvzero(rtp->rtcp->rxlsr)) {
@@ -3245,48 +4173,53 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
                }
        }
        timeval2ntp(rtcp_report->sender_information.ntp_timestamp, &now_msw, &now_lsw);
-       rtcpheader = (unsigned int *)bdata;
-       rtcpheader[1] = htonl(rtcp_report->ssrc);            /* Our SSRC */
+       put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc)); /* Our SSRC */
        len += 8;
-       if (sr) {
-               header_offset = 5;
-               rtcpheader[2] = htonl(now_msw);                 /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
-               rtcpheader[3] = htonl(now_lsw);                 /* now, LSW */
-               rtcpheader[4] = htonl(rtcp_report->sender_information.rtp_timestamp);
-               rtcpheader[5] = htonl(rtcp_report->sender_information.packet_count);
-               rtcpheader[6] = htonl(rtcp_report->sender_information.octet_count);
+       if (*sr) {
+               put_unaligned_uint32(rtcpheader + len, htonl(now_msw)); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970 */
+               put_unaligned_uint32(rtcpheader + len + 4, htonl(now_lsw)); /* now, LSW */
+               put_unaligned_uint32(rtcpheader + len + 8, htonl(rtcp_report->sender_information.rtp_timestamp));
+               put_unaligned_uint32(rtcpheader + len + 12, htonl(rtcp_report->sender_information.packet_count));
+               put_unaligned_uint32(rtcpheader + len + 16, htonl(rtcp_report->sender_information.octet_count));
                len += 20;
        }
        if (report_block) {
-               rtcpheader[2 + header_offset] = htonl(report_block->source_ssrc);     /* Their SSRC */
-               rtcpheader[3 + header_offset] = htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets);
-               rtcpheader[4 + header_offset] = htonl(report_block->highest_seq_no);
-               rtcpheader[5 + header_offset] = htonl(report_block->ia_jitter);
-               rtcpheader[6 + header_offset] = htonl(report_block->lsr);
-               rtcpheader[7 + header_offset] = htonl(report_block->dlsr);
+               put_unaligned_uint32(rtcpheader + len, htonl(report_block->source_ssrc)); /* Their SSRC */
+               put_unaligned_uint32(rtcpheader + len + 4, htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets));
+               put_unaligned_uint32(rtcpheader + len + 8, htonl(report_block->highest_seq_no));
+               put_unaligned_uint32(rtcpheader + len + 12, htonl(report_block->ia_jitter));
+               put_unaligned_uint32(rtcpheader + len + 16, htonl(report_block->lsr));
+               put_unaligned_uint32(rtcpheader + len + 20, htonl(report_block->dlsr));
                len += 24;
        }
-       rtcpheader[0] = htonl((2 << 30) | (rtcp_report->reception_report_count << 24)
-                                       | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1));
 
-       /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */
-       /* it can change mid call, and SDES can't) */
-       rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
-       rtcpheader[(len/4)+1] = htonl(rtcp_report->ssrc);
-       rtcpheader[(len/4)+2] = htonl(0x01 << 24);
-       len += 12;
+       put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (rtcp_report->reception_report_count << 24)
+                               | ((*sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1)));
 
-       ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
-       res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice);
-       if (res < 0) {
-               ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",
-                       sr ? "SR" : "RR",
-                       ast_sockaddr_stringify(&rtp->rtcp->them),
-                       strerror(errno));
+       return len;
+}
+
+static int ast_rtcp_calculate_sr_rr_statistics(struct ast_rtp_instance *instance,
+               struct ast_rtp_rtcp_report *rtcp_report, struct ast_sockaddr remote_address, int ice, int sr)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ast_rtp_rtcp_report_block *report_block = NULL;
+       RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);
+
+       if (!rtp || !rtp->rtcp) {
+               return 0;
+       }
+
+       if (ast_sockaddr_isnull(&rtp->rtcp->them)) {
                return 0;
        }
 
-       /* Update RTCP SR/RR statistics */
+       if (!rtcp_report) {
+               return -1;
+       }
+
+       report_block = rtcp_report->report_block[0];
+
        if (sr) {
                rtp->rtcp->txlsr = rtcp_report->sender_information.ntp_timestamp;
                rtp->rtcp->sr_count++;
@@ -3313,7 +4246,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
                        ast_verbose("    Fraction lost: %d\n", report_block->lost_count.fraction);
                        ast_verbose("    Cumulative loss: %u\n", report_block->lost_count.packets);
                        ast_verbose("    Highest seq no: %u\n", report_block->highest_seq_no);
-                       ast_verbose("    IA jitter: %.4f\n", (double)report_block->ia_jitter / rate);
+                       ast_verbose("    IA jitter: %.4f\n", (double)report_block->ia_jitter / rtp_get_rate(rtp->f.subclass.format));
                        ast_verbose("    Their last SR: %u\n", report_block->lsr);
                        ast_verbose("    DLSR: %4.4f (sec)\n\n", (double)(report_block->dlsr / 65536.0));
                }
@@ -3323,33 +4256,192 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
                        "to", ast_sockaddr_stringify(&remote_address),
                        "from", rtp->rtcp->local_addr_str);
        ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),
-                       rtcp_report,
-                       message_blob);
-       return res;
+                       rtcp_report, message_blob);
+
+       return 1;
+}
+
+static int ast_rtcp_generate_sdes(struct ast_rtp_instance *instance, unsigned char *rtcpheader,
+               struct ast_rtp_rtcp_report *rtcp_report)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       int len = 0;
+       uint16_t sdes_packet_len_bytes;
+       uint16_t sdes_packet_len_rounded;
+
+       if (!rtp || !rtp->rtcp) {
+               return 0;
+       }
+
+       if (ast_sockaddr_isnull(&rtp->rtcp->them)) {
+               return 0;
+       }
+
+       if (!rtcp_report) {
+               return -1;
+       }
+
+       sdes_packet_len_bytes =
+               4 + /* RTCP Header */
+               4 + /* SSRC */
+               1 + /* Type (CNAME) */
+               1 + /* Text Length */
+               AST_UUID_STR_LEN /* Text and NULL terminator */
+               ;
+
+       /* Round to 32 bit boundary */
+       sdes_packet_len_rounded = (sdes_packet_len_bytes + 3) & ~0x3;
+
+       put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | ((sdes_packet_len_rounded / 4) - 1)));
+       put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc));
+       rtcpheader[8] = 0x01; /* CNAME */
+       rtcpheader[9] = AST_UUID_STR_LEN - 1; /* Number of bytes of text */
+       memcpy(rtcpheader + 10, rtp->cname, AST_UUID_STR_LEN);
+       len += 10 + AST_UUID_STR_LEN;
+
+       /* Padding - Note that we don't set the padded bit on the packet. From
+        * RFC 3550 Section 6.5:
+        *
+        *   No length octet follows the null item type octet, but additional null
+        *   octets MUST be included if needd to pad until the next 32-bit
+        *   boundary. Note that this padding is separate from that indicated by
+        *   the P bit in the RTCP header.
+        *
+        * These bytes will already be zeroed out during array initialization.
+        */
+       len += (sdes_packet_len_rounded - sdes_packet_len_bytes);
+
+       return len;
+}
+
+static int ast_rtcp_generate_nack(struct ast_rtp_instance *instance, unsigned char *rtcpheader)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       int packet_len;
+       int blp_index;
+       int current_seqno;
+       int seqno;
+       unsigned int fci;
+
+       if (!rtp || !rtp->rtcp) {
+               return 0;
+       }
+
+       if (ast_sockaddr_isnull(&rtp->rtcp->them)) {
+               return 0;
+       }
+
+       current_seqno = rtp->expectedrxseqno;
+       seqno = rtp->lastrxseqno;
+       packet_len = 12; /* The header length is 12 (version line, packet source SSRC, media source SSRC) */
+
+       /* Get the missing sequence numbers for the FCI section of the NACK request */
+       for (blp_index = 0, fci = 0; current_seqno < seqno; current_seqno++, blp_index++) {
+               int *missing_seqno;
+
+               missing_seqno = AST_VECTOR_GET_CMP(&rtp->missing_seqno, current_seqno,
+                               find_by_value);
+
+               if (!missing_seqno) {
+                       continue;
+               }
+
+               /* We hit the max blp size, reset */
+               if (blp_index >= 17) {
+                       put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));
+                       fci = 0;
+                       blp_index = 0;
+                       packet_len += 4;
+               }
+
+               if (blp_index == 0) {
+                       fci |= (current_seqno << 16);
+               } else {
+                       fci |= (1 << (blp_index - 1));
+               }
+       }
+
+       put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));
+       packet_len += 4;
+
+       /* Length MUST be 2+n, where n is the number of NACKs. Same as length in words minus 1 */
+       put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (AST_RTP_RTCP_FMT_NACK << 24)
+                               | (AST_RTP_RTCP_RTPFB << 16) | ((packet_len / 4) - 1)));
+       put_unaligned_uint32(rtcpheader + 4, htonl(rtp->ssrc));
+       put_unaligned_uint32(rtcpheader + 8, htonl(rtp->themssrc));
+
+       return packet_len;
 }
 
-/*! \brief Write and RTCP packet to the far end
+/*!
+ * \brief Write a RTCP packet to the far end
+ *
  * \note Decide if we are going to send an SR (with Reception Block) or RR
- * RR is sent if we have not sent any rtp packets in the previous interval */
+ * RR is sent if we have not sent any rtp packets in the previous interval
+ *
+ * Scheduler callback
+ */
 static int ast_rtcp_write(const void *data)
 {
        struct ast_rtp_instance *instance = (struct ast_rtp_instance *) data;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        int res;
+       int sr = 0;
+       int packet_len = 0;
+       int ice;
+       struct ast_sockaddr remote_address = { { 0, } };
+       unsigned char *rtcpheader;
+       unsigned char bdata[AST_UUID_STR_LEN + 128] = ""; /* More than enough */
+       RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
+                       ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),
+                       ao2_cleanup);
 
        if (!rtp || !rtp->rtcp || rtp->rtcp->schedid == -1) {
                ao2_ref(instance, -1);
                return 0;
        }
 
-       if (rtp->txcount > rtp->rtcp->lastsrtxcount) {
-               /* Send an SR */
-               res = ast_rtcp_write_report(instance, 1);
+       ao2_lock(instance);
+       rtcpheader = bdata;
+
+       res = ast_rtcp_generate_report(instance, rtcpheader, rtcp_report, &sr);
+
+       if (res == 0 || res == 1) {
+               ast_debug(1, "Failed to add %s report to RTCP packet!\n", sr ? "SR" : "RR");
+               goto cleanup;
+       }
+
+       packet_len += res;
+
+       res = ast_rtcp_generate_sdes(instance, rtcpheader + packet_len, rtcp_report);
+
+       if (res == 0 || res == 1) {
+               ast_debug(1, "Failed to add SDES to RTCP packet!\n");
+               goto cleanup;
+       }
+
+       packet_len += res;
+
+       if (rtp->bundled) {
+               ast_rtp_instance_get_remote_address(instance, &remote_address);
+       } else {
+               ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
+       }
+
+       res = rtcp_sendto(instance, (unsigned int *)rtcpheader, packet_len, 0, &remote_address, &ice);
+       if (res < 0) {
+               ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",
+                               sr ? "SR" : "RR",
+                               ast_sockaddr_stringify(&rtp->rtcp->them),
+                               strerror(errno));
+               res = 0;
        } else {
-               /* Send an RR */
-               res = ast_rtcp_write_report(instance, 0);
+               ast_rtcp_calculate_sr_rr_statistics(instance, rtcp_report, remote_address, ice, sr);
        }
 
+cleanup:
+       ao2_unlock(instance);
+
        if (!res) {
                /*
                 * Not being rescheduled.
@@ -3361,13 +4453,29 @@ static int ast_rtcp_write(const void *data)
        return res;
 }
 
-static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec)
+static void put_unaligned_time24(void *p, uint32_t time_msw, uint32_t time_lsw)
+{
+       unsigned char *cp = p;
+       uint32_t datum;
+
+       /* Convert the time to 6.18 format */
+       datum = (time_msw << 18) & 0x00fc0000;
+       datum |= (time_lsw >> 14) & 0x0003ffff;
+
+       cp[0] = datum >> 16;
+       cp[1] = datum >> 8;
+       cp[2] = datum;
+}
+
+/*! \pre instance is locked */
+static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        int pred, mark = 0;
        unsigned int ms = calc_txstamp(rtp, &frame->delivery);
        struct ast_sockaddr remote_address = { {0,} };
        int rate = rtp_get_rate(frame->subclass.format) / 1000;
+       unsigned int seqno;
 
        if (ast_format_cmp(frame->subclass.format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) {
                frame->samples /= 2;
@@ -3434,6 +4542,40 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
                rtp->lastdigitts = rtp->lastts;
        }
 
+       /* Assume that the sequence number we expect to use is what will be used until proven otherwise */
+       seqno = rtp->seqno;
+
+       /* If the frame contains sequence number information use it to influence our sequence number */
+       if (ast_test_flag(frame, AST_FRFLAG_HAS_SEQUENCE_NUMBER)) {
+               if (rtp->expectedseqno != -1) {
+                       /* Determine where the frame from the core is in relation to where we expected */
+                       int difference = frame->seqno - rtp->expectedseqno;
+
+                       /* If there is a substantial difference then we've either got packets really out
+                        * of order, or the source is RTP and it has cycled. If this happens we resync
+                        * the sequence number adjustments to this frame. If we also have packet loss
+                        * things won't be reflected correctly but it will sort itself out after a bit.
+                        */
+                       if (abs(difference) > 100) {
+                               difference = 0;
+                       }
+
+                       /* Adjust the sequence number being used for this packet accordingly */
+                       seqno += difference;
+
+                       if (difference >= 0) {
+                               /* This frame is on time or in the future */
+                               rtp->expectedseqno = frame->seqno + 1;
+                               rtp->seqno += difference;
+                       }
+               } else {
+                       /* This is the first frame with sequence number we've seen, so start keeping track */
+                       rtp->expectedseqno = frame->seqno + 1;
+               }
+       } else {
+               rtp->expectedseqno = -1;
+       }
+
        if (ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) {
                rtp->lastts = frame->ts * rate;
        }
@@ -3442,14 +4584,60 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 
        /* If we know the remote address construct a packet and send it out */
        if (!ast_sockaddr_isnull(&remote_address)) {
-               int hdrlen = 12, res, ice;
-               unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
+               int hdrlen = 12;
+               int res;
+               int ice;
+               int ext = 0;
+               int abs_send_time_id;
+               int packet_len;
+               unsigned char *rtpheader;
+
+               /* If the abs-send-time extension has been negotiated determine how much space we need */
+               abs_send_time_id = ast_rtp_instance_extmap_get_id(instance, AST_RTP_EXTENSION_ABS_SEND_TIME);
+               if (abs_send_time_id != -1) {
+                       /* 4 bytes for the shared information, 1 byte for identifier, 3 bytes for abs-send-time */
+                       hdrlen += 8;
+                       ext = 1;
+               }
+
+               packet_len = frame->datalen + hdrlen;
+               rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
 
-               put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23)));
+               put_unaligned_uint32(rtpheader, htonl((2 << 30) | (ext << 28) | (codec << 16) | (seqno) | (mark << 23)));
                put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
                put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
 
-               if ((res = rtp_sendto(instance, (void *)rtpheader, frame->datalen + hdrlen, 0, &remote_address, &ice)) < 0) {
+               /* We assume right now that we will only ever have the abs-send-time extension in the packet
+                * which simplifies things a bit.
+                */
+               if (abs_send_time_id != -1) {
+                       unsigned int now_msw;
+                       unsigned int now_lsw;
+
+                       /* This happens before being placed into the retransmission buffer so that when we
+                        * retransmit we only have to update the timestamp, not everything else.
+                        */
+                       put_unaligned_uint32(rtpheader + 12, htonl((0xBEDE << 16) | 1));
+                       rtpheader[16] = (abs_send_time_id << 4) | 2;
+
+                       timeval2ntp(ast_tvnow(), &now_msw, &now_lsw);
+                       put_unaligned_time24(rtpheader + 17, now_msw, now_lsw);
+               }
+
+               /* If retransmissions are enabled, we need to store this packet for future use */
+               if (rtp->send_buffer) {
+                       struct ast_rtp_rtcp_nack_payload *payload;
+
+                       payload = ast_malloc(sizeof(*payload) + packet_len);
+                       if (payload) {
+                               payload->size = packet_len;
+                               memcpy(payload->buf, rtpheader, packet_len);
+                               ast_data_buffer_put(rtp->send_buffer, rtp->seqno, payload);
+                       }
+               }
+
+               res = rtp_sendto(instance, (void *)rtpheader, packet_len, 0, &remote_address, &ice);
+               if (res < 0) {
                        if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
                                ast_debug(1, "RTP Transmission error of packet %d to %s: %s\n",
                                          rtp->seqno,
@@ -3482,7 +4670,13 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
                }
        }
 
-       rtp->seqno++;
+       /* If the sequence number that has been used doesn't match what we expected then this is an out of
+        * order late packet, so we don't need to increment as we haven't yet gotten the expected frame from
+        * the core.
+        */
+       if (seqno == rtp->seqno) {
+               rtp->seqno++;
+       }
 
        return 0;
 }
@@ -3527,16 +4721,105 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red)
        return &red->t140red;
 }
 
-static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
+static void rtp_write_rtcp_fir(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *remote_address)
 {
-       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       struct ast_sockaddr remote_address = { {0,} };
-       struct ast_format *format;
-       int codec;
-
-       ast_rtp_instance_get_remote_address(instance, &remote_address);
+       unsigned int *rtcpheader;
+       char bdata[1024];
+       int len = 20;
+       int ice;
+       int res;
 
-       /* If we don't actually know the remote address don't even bother doing anything */
+       if (!rtp || !rtp->rtcp) {
+               return;
+       }
+
+       if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+               /*
+                * RTCP was stopped.
+                */
+               return;
+       }
+
+       if (!rtp->themssrc_valid) {
+               /* We don't know their SSRC value so we don't know who to update. */
+               return;
+       }
+
+       /* Prepare RTCP FIR (PT=206, FMT=4) */
+       rtp->rtcp->firseq++;
+       if(rtp->rtcp->firseq == 256) {
+               rtp->rtcp->firseq = 0;
+       }
+
+       rtcpheader = (unsigned int *)bdata;
+       rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+       rtcpheader[1] = htonl(rtp->ssrc);
+       rtcpheader[2] = htonl(rtp->themssrc);
+       rtcpheader[3] = htonl(rtp->themssrc);   /* FCI: SSRC */
+       rtcpheader[4] = htonl(rtp->rtcp->firseq << 24);                 /* FCI: Sequence number */
+       res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+       if (res < 0) {
+               ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+       }
+}
+
+static void rtp_write_rtcp_psfb(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_frame *frame, struct ast_sockaddr *remote_address)
+{
+       struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+       unsigned int *rtcpheader;
+       char bdata[1024];
+       int len = 24;
+       int ice;
+       int res;
+
+       if (feedback->fmt != AST_RTP_RTCP_FMT_REMB) {
+               ast_debug(1, "Provided an RTCP feedback frame of format %d to write on RTP instance '%p' but only REMB is supported\n",
+                       feedback->fmt, instance);
+               return;
+       }
+
+       if (!rtp || !rtp->rtcp) {
+               return;
+       }
+
+       /* If REMB support is not enabled don't send this RTCP packet */
+       if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+               ast_debug(1, "Provided an RTCP feedback REMB report to write on RTP instance '%p' but REMB support not enabled\n",
+                       instance);
+               return;
+       }
+
+       if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+               /*
+                * RTCP was stopped.
+                */
+               return;
+       }
+
+       rtcpheader = (unsigned int *)bdata;
+       rtcpheader[0] = htonl((2 << 30) | (AST_RTP_RTCP_FMT_REMB << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+       rtcpheader[1] = htonl(rtp->ssrc);
+       rtcpheader[2] = htonl(0); /* Per the draft this should always be 0 */
+       rtcpheader[3] = htonl(('R' << 24) | ('E' << 16) | ('M' << 8) | ('B')); /* Unique identifier 'R' 'E' 'M' 'B' */
+       rtcpheader[4] = htonl((1 << 24) | (feedback->remb.br_exp << 18) | (feedback->remb.br_mantissa)); /* Number of SSRCs / BR Exp / BR Mantissa */
+       rtcpheader[5] = htonl(rtp->ssrc); /* The SSRC this feedback message applies to */
+       res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+       if (res < 0) {
+               ast_log(LOG_ERROR, "RTCP PSFB transmission error: %s\n", strerror(errno));
+       }
+}
+
+/*! \pre instance is locked */
+static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ast_sockaddr remote_address = { {0,} };
+       struct ast_format *format;
+       int codec;
+
+       ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+       /* If we don't actually know the remote address don't even bother doing anything */
        if (ast_sockaddr_isnull(&remote_address)) {
                ast_debug(1, "No remote address on RTP instance '%p' so dropping frame\n", instance);
                return 0;
@@ -3544,39 +4827,11 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
 
        /* VP8: is this a request to send a RTCP FIR? */
        if (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) {
-               struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-               unsigned int *rtcpheader;
-               char bdata[1024];
-               int len = 20;
-               int ice;
-               int res;
-
-               if (!rtp || !rtp->rtcp) {
-                       return 0;
-               }
-
-               if (ast_sockaddr_isnull(&rtp->rtcp->them)) {
-                       /*
-                        * RTCP was stopped.
-                        */
-                       return 0;
-               }
-
-               /* Prepare RTCP FIR (PT=206, FMT=4) */
-               rtp->rtcp->firseq++;
-               if(rtp->rtcp->firseq == 256) {
-                       rtp->rtcp->firseq = 0;
-               }
-
-               rtcpheader = (unsigned int *)bdata;
-               rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
-               rtcpheader[1] = htonl(rtp->ssrc);
-               rtcpheader[2] = htonl(rtp->themssrc);
-               rtcpheader[3] = htonl(rtp->themssrc);   /* FCI: SSRC */
-               rtcpheader[4] = htonl(rtp->rtcp->firseq << 24);                 /* FCI: Sequence number */
-               res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &rtp->rtcp->them, &ice);
-               if (res < 0) {
-                       ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+               rtp_write_rtcp_fir(instance, rtp, &remote_address);
+               return 0;
+       } else if (frame->frametype == AST_FRAME_RTCP) {
+               if (frame->subclass.integer == AST_RTP_RTCP_PSFB) {
+                       rtp_write_rtcp_psfb(instance, rtp, frame, &remote_address);
                }
                return 0;
        }
@@ -3615,11 +4870,9 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
        format = frame->subclass.format;
        if (ast_format_cmp(rtp->lasttxformat, format) == AST_FORMAT_CMP_NOT_EQUAL) {
                /* Oh dear, if the format changed we will have to set up a new smoother */
-               if (option_debug > 0) {
-                       ast_debug(1, "Ooh, format changed from %s to %s\n",
-                               ast_format_get_name(rtp->lasttxformat),
-                               ast_format_get_name(frame->subclass.format));
-               }
+               ast_debug(1, "Ooh, format changed from %s to %s\n",
+                       ast_format_get_name(rtp->lasttxformat),
+                       ast_format_get_name(frame->subclass.format));
                ao2_replace(rtp->lasttxformat, format);
                if (rtp->smoother) {
                        ast_smoother_free(rtp->smoother);
@@ -3629,8 +4882,13 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
 
        /* If no smoother is present see if we have to set one up */
        if (!rtp->smoother && ast_format_can_be_smoothed(format)) {
+               unsigned int smoother_flags = ast_format_get_smoother_flags(format);
                unsigned int framing_ms = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(instance));
 
+               if (!framing_ms && (smoother_flags & AST_SMOOTHER_FLAG_FORCED)) {
+                       framing_ms = ast_format_get_default_ms(format);
+               }
+
                if (framing_ms) {
                        rtp->smoother = ast_smoother_new((framing_ms * ast_format_get_minimum_bytes(format)) / ast_format_get_minimum_ms(format));
                        if (!rtp->smoother) {
@@ -3638,6 +4896,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
                                        ast_format_get_name(format), framing_ms, ast_format_get_minimum_bytes(format));
                                return -1;
                        }
+                       ast_smoother_set_flags(rtp->smoother, smoother_flags);
                }
        }
 
@@ -3652,7 +4911,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
                }
 
                while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) {
-                               ast_rtp_raw_write(instance, f, codec);
+                               rtp_raw_write(instance, f, codec);
                }
        } else {
                int hdrlen = 12;
@@ -3664,7 +4923,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
                        f = frame;
                }
                if (f->data.ptr) {
-                       ast_rtp_raw_write(instance, f, codec);
+                       rtp_raw_write(instance, f, codec);
                }
                if (f != frame) {
                        ast_frfree(f);
@@ -3763,7 +5022,7 @@ static struct ast_frame *create_dtmf_frame(struct ast_rtp_instance *instance, en
        return &rtp->f;
 }
 
-static void process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct ast_sockaddr *addr, int payloadtype, int mark, struct frame_list *frames)
+static void process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, int payloadtype, int mark, struct frame_list *frames)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        struct ast_sockaddr remote_address = { {0,} };
@@ -3898,7 +5157,7 @@ static void process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned cha
        return;
 }
 
-static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct ast_sockaddr *addr, int payloadtype, int mark)
+static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, int payloadtype, int mark)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        unsigned int event, flags, power;
@@ -3978,7 +5237,7 @@ static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, u
        return f;
 }
 
-static struct ast_frame *process_cn_rfc3389(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct ast_sockaddr *addr, int payloadtype, int mark)
+static struct ast_frame *process_cn_rfc3389(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, int payloadtype, int mark)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
@@ -4133,109 +5392,467 @@ 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)
+/*! \pre instance is locked */
+static struct ast_rtp_instance *__rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
+       struct ast_rtp *rtp, unsigned int ssrc, int source)
 {
-       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;
-       int report_counter = 0;
-       struct ast_rtp_rtcp_report_block *report_block;
-       struct ast_frame *f = &ast_null_frame;
+       int index;
 
-       /* 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;
+       if (!AST_VECTOR_SIZE(&rtp->ssrc_mapping)) {
+               /* This instance is not bundled */
+               return instance;
+       }
+
+       /* Find the bundled child instance */
+       for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
+               struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
+               unsigned int mapping_ssrc = source ? ast_rtp_get_ssrc(mapping->instance) : mapping->ssrc;
+
+               if (mapping->ssrc_valid && mapping_ssrc == ssrc) {
+                       return mapping->instance;
                }
-               return &ast_null_frame;
        }
 
-       /* If this was handled by the ICE session don't do anything further */
-       if (!res) {
-               return &ast_null_frame;
+       /* Does the SSRC match the bundled parent? */
+       if (rtp->themssrc_valid && rtp->themssrc == ssrc) {
+               return instance;
        }
+       return NULL;
+}
 
-       if (!*(rtcpdata + AST_FRIENDLY_OFFSET)) {
-               struct sockaddr_in addr_tmp;
-               struct ast_sockaddr addr_v4;
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_packet_source_ssrc(struct ast_rtp_instance *instance,
+       struct ast_rtp *rtp, unsigned int ssrc)
+{
+       return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 0);
+}
 
-               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);
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_media_source_ssrc(struct ast_rtp_instance *instance,
+       struct ast_rtp *rtp, unsigned int ssrc)
+{
+       return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 1);
+}
+
+static const char *rtcp_payload_type2str(unsigned int pt)
+{
+       const char *str;
+
+       switch (pt) {
+       case RTCP_PT_SR:
+               str = "Sender Report";
+               break;
+       case RTCP_PT_RR:
+               str = "Receiver Report";
+               break;
+       case RTCP_PT_FUR:
+               /* Full INTRA-frame Request / Fast Update Request */
+               str = "H.261 FUR";
+               break;
+       case RTCP_PT_PSFB:
+               /* Payload Specific Feed Back */
+               str = "PSFB";
+               break;
+       case RTCP_PT_SDES:
+               str = "Source Description";
+               break;
+       case RTCP_PT_BYE:
+               str = "BYE";
+               break;
+       default:
+               str = "Unknown";
+               break;
+       }
+       return str;
+}
+
+/*! \pre instance is locked */
+static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned int *nackdata, unsigned int position,
+       unsigned int length)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       int res = 0;
+       int blp_index;
+       int packet_index;
+       int ice;
+       struct ast_rtp_rtcp_nack_payload *payload;
+       unsigned int current_word;
+       unsigned int pid;       /* Packet ID which refers to seqno of lost packet */
+       unsigned int blp;       /* Bitmask of following lost packets */
+       struct ast_sockaddr remote_address = { {0,} };
+       int abs_send_time_id;
+       unsigned int now_msw = 0;
+       unsigned int now_lsw = 0;
+
+       if (!rtp->send_buffer) {
+               ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
+               return res;
+       }
+
+       abs_send_time_id = ast_rtp_instance_extmap_get_id(instance, AST_RTP_EXTENSION_ABS_SEND_TIME);
+       if (abs_send_time_id != -1) {
+               timeval2ntp(ast_tvnow(), &now_msw, &now_lsw);
+       }
+
+       ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+       /*
+        * We use index 3 because with feedback messages, the FCI (Feedback Control Information)
+        * does not begin until after the version, packet SSRC, and media SSRC words.
+        */
+       for (packet_index = 3; packet_index < length; packet_index++) {
+               current_word = ntohl(nackdata[position + packet_index]);
+               pid = current_word >> 16;
+               /* We know the remote end is missing this packet. Go ahead and send it if we still have it. */
+               payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, pid);
+               if (payload) {
+                       if (abs_send_time_id != -1) {
+                               /* On retransmission we need to update the timestamp within the packet, as it
+                                * is supposed to contain when the packet was actually sent.
+                                */
+                               put_unaligned_time24(payload->buf + 17, now_msw, now_lsw);
+                       }
+                       res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
                } else {
-                       ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
-                                 ast_sockaddr_stringify(&addr));
-                       return &ast_null_frame;
+                       ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
                }
-               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);
+               /*
+                * The bitmask. Denoting the least significant bit as 1 and its most significant bit
+                * as 16, then bit i of the bitmask is set to 1 if the receiver has not received RTP
+                * packet (pid+i)(modulo 2^16). Otherwise, it is set to 0. We cannot assume bits set
+                * to 0 after a bit set to 1 have actually been received.
+                */
+               blp = current_word & 0xFF;
+               blp_index = 1;
+               while (blp) {
+                       if (blp & 1) {
+                               /* Packet (pid + i)(modulo 2^16) is missing too. */
+                               unsigned int seqno = (pid + blp_index) % 65536;
+                               payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno);
+                               if (payload) {
+                                       if (abs_send_time_id != -1) {
+                                               put_unaligned_time24(payload->buf + 17, now_msw, now_lsw);
+                                       }
+                                       res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
+                               } else {
+                                       ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
+                               }
+                       }
+                       blp >>= 1;
+                       blp_index++;
                }
-               return &ast_null_frame;
        }
 
-       packetwords = res / 4;
+       return res;
+}
 
-       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 (rtpdebug) {
-                               ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
-                                         ast_sockaddr_stringify(&rtp->rtcp->them));
-                       }
+/*
+ * Unshifted RTCP header bit field masks
+ */
+#define RTCP_LENGTH_MASK                       0xFFFF
+#define RTCP_PAYLOAD_TYPE_MASK         0xFF
+#define RTCP_REPORT_COUNT_MASK         0x1F
+#define RTCP_PADDING_MASK                      0x01
+#define RTCP_VERSION_MASK                      0x03
+
+/*
+ * RTCP header bit field shift offsets
+ */
+#define RTCP_LENGTH_SHIFT                      0
+#define RTCP_PAYLOAD_TYPE_SHIFT                16
+#define RTCP_REPORT_COUNT_SHIFT                24
+#define RTCP_PADDING_SHIFT                     29
+#define RTCP_VERSION_SHIFT                     30
+
+#define RTCP_VERSION                           2U
+#define RTCP_VERSION_SHIFTED           (RTCP_VERSION << RTCP_VERSION_SHIFT)
+#define RTCP_VERSION_MASK_SHIFTED      (RTCP_VERSION_MASK << RTCP_VERSION_SHIFT)
+
+/*
+ * RTCP first packet record validity header mask and value.
+ *
+ * RFC3550 intentionally defines the encoding of RTCP_PT_SR and RTCP_PT_RR
+ * such that they differ in the least significant bit.  Either of these two
+ * payload types MUST be the first RTCP packet record in a compound packet.
+ *
+ * RFC3550 checks the padding bit in the algorithm they use to check the
+ * RTCP packet for validity.  However, we aren't masking the padding bit
+ * to check since we don't know if it is a compound RTCP packet or not.
+ */
+#define RTCP_VALID_MASK (RTCP_VERSION_MASK_SHIFTED | (((RTCP_PAYLOAD_TYPE_MASK & ~0x1)) << RTCP_PAYLOAD_TYPE_SHIFT))
+#define RTCP_VALID_VALUE (RTCP_VERSION_SHIFTED | (RTCP_PT_SR << RTCP_PAYLOAD_TYPE_SHIFT))
+
+#define RTCP_SR_BLOCK_WORD_LENGTH 5
+#define RTCP_RR_BLOCK_WORD_LENGTH 6
+#define RTCP_HEADER_SSRC_LENGTH   2
+#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 4
+#define RTCP_FB_NACK_BLOCK_WORD_LENGTH 2
+
+static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, struct ast_srtp *srtp,
+       const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
+{
+       struct ast_rtp_instance *transport = instance;
+       struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(instance);
+       int len = size;
+       unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
+       unsigned int packetwords;
+       unsigned int position;
+       unsigned int first_word;
+       /*! True if we have seen an acceptable SSRC to learn the remote RTCP address */
+       unsigned int ssrc_seen;
+       struct ast_rtp_rtcp_report_block *report_block;
+       struct ast_frame *f = &ast_null_frame;
+
+       /* If this is encrypted then decrypt the payload */
+       if ((*rtcpheader & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
+                   srtp, rtcpheader, &len, 1) < 0) {
+          return &ast_null_frame;
+       }
+
+       packetwords = len / 4;
+
+       ast_debug(1, "Got RTCP report of %d bytes from %s\n",
+               len, ast_sockaddr_stringify(addr));
+
+       /*
+        * Validate the RTCP packet according to an adapted and slightly
+        * modified RFC3550 validation algorithm.
+        */
+       if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
+               ast_debug(1, "%p -- RTCP from %s: Frame size (%u words) is too short\n",
+                       transport_rtp, ast_sockaddr_stringify(addr), packetwords);
+               return &ast_null_frame;
+       }
+       position = 0;
+       first_word = ntohl(rtcpheader[position]);
+       if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
+               ast_debug(1, "%p -- RTCP from %s: Failed first packet validity check\n",
+                       transport_rtp, ast_sockaddr_stringify(addr));
+               return &ast_null_frame;
+       }
+       do {
+               position += ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
+               if (packetwords <= position) {
+                       break;
                }
+               first_word = ntohl(rtcpheader[position]);
+       } while ((first_word & RTCP_VERSION_MASK_SHIFTED) == RTCP_VERSION_SHIFTED);
+       if (position != packetwords) {
+               ast_debug(1, "%p -- RTCP from %s: Failed packet version or length check\n",
+                       transport_rtp, ast_sockaddr_stringify(addr));
+               return &ast_null_frame;
        }
 
-       ast_debug(1, "Got RTCP report of %d bytes\n", res);
+       /*
+        * Note: RFC3605 points out that true NAT (vs NAPT) can cause RTCP
+        * to have a different IP address and port than RTP.  Otherwise, when
+        * strictrtp is enabled we could reject RTCP packets not coming from
+        * the learned RTP IP address if it is available.
+        */
+
+       /*
+        * strictrtp safety needs SSRC to match before we use the
+        * sender's address for symmetrical RTP to send our RTCP
+        * reports.
+        *
+        * If strictrtp is not enabled then claim to have already seen
+        * a matching SSRC so we'll accept this packet's address for
+        * symmetrical RTP.
+        */
+       ssrc_seen = transport_rtp->strict_rtp_state == STRICT_RTP_OPEN;
 
+       position = 0;
        while (position < packetwords) {
-               int i, pt, rc;
+               unsigned int i;
+               unsigned int pt;
+               unsigned int rc;
+               unsigned int ssrc;
+               /*! True if the ssrc value we have is valid and not garbage because it doesn't exist. */
+               unsigned int ssrc_valid;
                unsigned int length;
+               unsigned int min_length;
+               /*! Always use packet source SSRC to find the rtp instance unless explicitly told not to. */
+               unsigned int use_packet_source = 1;
+
                struct ast_json *message_blob;
                RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
+               struct ast_rtp_instance *child;
+               struct ast_rtp *rtp;
+               struct ast_rtp_rtcp_feedback *feedback;
 
                i = position;
-               length = ntohl(rtcpheader[i]);
-               pt = (length & 0xff0000) >> 16;
-               rc = (length & 0x1f000000) >> 24;
-               length &= 0xffff;
-
-               rtcp_report = ast_rtp_rtcp_report_alloc(rc);
-               if (!rtcp_report) {
+               first_word = ntohl(rtcpheader[i]);
+               pt = (first_word >> RTCP_PAYLOAD_TYPE_SHIFT) & RTCP_PAYLOAD_TYPE_MASK;
+               rc = (first_word >> RTCP_REPORT_COUNT_SHIFT) & RTCP_REPORT_COUNT_MASK;
+               /* RFC3550 says 'length' is the number of words in the packet - 1 */
+               length = ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
+
+               /* Check expected RTCP packet record length */
+               min_length = RTCP_HEADER_SSRC_LENGTH;
+               switch (pt) {
+               case RTCP_PT_SR:
+                       min_length += RTCP_SR_BLOCK_WORD_LENGTH;
+                       /* fall through */
+               case RTCP_PT_RR:
+                       min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
+                       use_packet_source = 0;
+                       break;
+               case RTCP_PT_FUR:
+                       break;
+               case AST_RTP_RTCP_RTPFB:
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_NACK:
+                               min_length += RTCP_FB_NACK_BLOCK_WORD_LENGTH;
+                               break;
+                       default:
+                               break;
+                       }
+                       use_packet_source = 0;
+                       break;
+               case RTCP_PT_PSFB:
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_REMB:
+                               min_length += RTCP_FB_REMB_BLOCK_WORD_LENGTH;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case RTCP_PT_SDES:
+               case RTCP_PT_BYE:
+                       /*
+                        * There may not be a SSRC/CSRC present.  The packet is
+                        * useless but still valid if it isn't present.
+                        *
+                        * We don't know what min_length should be so disable the check
+                        */
+                       min_length = length;
+                       break;
+               default:
+                       ast_debug(1, "%p -- RTCP from %s: %u(%s) skipping record\n",
+                               transport_rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
+                       if (rtcp_debug_test_addr(addr)) {
+                               ast_verbose("\n");
+                               ast_verbose("RTCP from %s: %u(%s) skipping record\n",
+                                       ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt));
+                       }
+                       position += length;
+                       continue;
+               }
+               if (length < min_length) {
+                       ast_debug(1, "%p -- RTCP from %s: %u(%s) length field less than expected minimum.  Min:%u Got:%u\n",
+                               transport_rtp, ast_sockaddr_stringify(addr), pt, rtcp_payload_type2str(pt),
+                               min_length - 1, length - 1);
                        return &ast_null_frame;
                }
-               rtcp_report->reception_report_count = rc;
-               rtcp_report->ssrc = ntohl(rtcpheader[i + 1]);
 
-               if ((i + length) > packetwords) {
-                       if (rtpdebug) {
-                               ast_debug(1, "RTCP Read too short\n");
+               /* Get the RTCP record SSRC if defined for the record */
+               ssrc_valid = 1;
+               switch (pt) {
+               case RTCP_PT_SR:
+               case RTCP_PT_RR:
+                       rtcp_report = ast_rtp_rtcp_report_alloc(rc);
+                       if (!rtcp_report) {
+                               return &ast_null_frame;
                        }
-                       return &ast_null_frame;
+                       rtcp_report->reception_report_count = rc;
+
+                       ssrc = ntohl(rtcpheader[i + 2]);
+                       rtcp_report->ssrc = ssrc;
+                       break;
+               case RTCP_PT_FUR:
+               case RTCP_PT_PSFB:
+                       ssrc = ntohl(rtcpheader[i + 1]);
+                       break;
+               case AST_RTP_RTCP_RTPFB:
+                       ssrc = ntohl(rtcpheader[i + 2]);
+                       break;
+               case RTCP_PT_SDES:
+               case RTCP_PT_BYE:
+               default:
+                       ssrc = 0;
+                       ssrc_valid = 0;
+                       break;
+               }
+
+               if (rtcp_debug_test_addr(addr)) {
+                       ast_verbose("\n");
+                       ast_verbose("RTCP from %s\n", ast_sockaddr_stringify(addr));
+                       ast_verbose("PT: %u(%s)\n", pt, rtcp_payload_type2str(pt));
+                       ast_verbose("Reception reports: %u\n", rc);
+                       ast_verbose("SSRC of sender: %u\n", ssrc);
+               }
+
+               /* Determine the appropriate instance for this */
+               if (ssrc_valid) {
+                       /*
+                        * Depending on the payload type, either the packet source or media source
+                        * SSRC is used.
+                        */
+                       if (use_packet_source) {
+                               child = rtp_find_instance_by_packet_source_ssrc(transport, transport_rtp, ssrc);
+                       } else {
+                               child = rtp_find_instance_by_media_source_ssrc(transport, transport_rtp, ssrc);
+                       }
+                       if (child && child != transport) {
+                               /*
+                                * It is safe to hold the child lock while holding the parent lock.
+                                * We guarantee that the locking order is always parent->child or
+                                * that the child lock is not held when acquiring the parent lock.
+                                */
+                               ao2_lock(child);
+                               instance = child;
+                               rtp = ast_rtp_instance_get_data(instance);
+                       } else {
+                               /* The child is the parent! We don't need to unlock it. */
+                               child = NULL;
+                               rtp = transport_rtp;
+                       }
+               } else {
+                       child = NULL;
+                       rtp = transport_rtp;
+               }
+
+               if (ssrc_valid && rtp->themssrc_valid) {
+                       /*
+                        * If the SSRC is 1, we still need to handle RTCP since this could be a
+                        * special case. For example, if we have a unidirectional video stream, the
+                        * SSRC may be set to 1 by the browser (in the case of chromium), and requests
+                        * will still need to be processed so that video can flow as expected. This
+                        * should only be done for PLI and FUR, since there is not a way to get the
+                        * appropriate rtp instance when the SSRC is 1.
+                        */
+                       int exception = (ssrc == 1 && !((pt == RTCP_PT_PSFB && rc == AST_RTP_RTCP_FMT_PLI) || pt == RTCP_PT_FUR));
+                       if ((ssrc != rtp->themssrc && use_packet_source && ssrc != 1)
+                                       || exception) {
+                               /*
+                                * Skip over this RTCP record as it does not contain the
+                                * correct SSRC.  We should not act upon RTCP records
+                                * for a different stream.
+                                */
+                               position += length;
+                               ast_debug(1, "%p -- RTCP from %s: Skipping record, received SSRC '%u' != expected '%u'\n",
+                                       rtp, ast_sockaddr_stringify(addr), ssrc, rtp->themssrc);
+                               if (child) {
+                                       ao2_unlock(child);
+                               }
+                               continue;
+                       }
+                       ssrc_seen = 1;
                }
 
-               if (rtcp_debug_test_addr(&addr)) {
-                       ast_verbose("\n\nGot RTCP from %s\n",
-                                   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");
-                       ast_verbose("Reception reports: %d\n", rc);
-                       ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc);
+               if (ssrc_seen && 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 (rtpdebug) {
+                                       ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
+                                               ast_sockaddr_stringify(addr));
+                               }
+                       }
                }
 
-               i += 2; /* Advance past header and ssrc */
+               i += RTCP_HEADER_SSRC_LENGTH; /* Advance past header and ssrc */
                switch (pt) {
                case RTCP_PT_SR:
                        gettimeofday(&rtp->rtcp->rxlsr, NULL);
@@ -4250,7 +5867,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);
@@ -4259,7 +5876,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                                                rtcp_report->sender_information.packet_count,
                                                rtcp_report->sender_information.octet_count);
                        }
-                       i += 5;
+                       i += RTCP_SR_BLOCK_WORD_LENGTH;
                        /* Intentional fall through */
                case RTCP_PT_RR:
                        if (rtcp_report->type != RTCP_PT_SR) {
@@ -4270,9 +5887,12 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                                /* Don't handle multiple reception reports (rc > 1) yet */
                                report_block = ast_calloc(1, sizeof(*report_block));
                                if (!report_block) {
+                                       if (child) {
+                                               ao2_unlock(child);
+                                       }
                                        return &ast_null_frame;
                                }
-                               rtcp_report->report_block[report_counter] = report_block;
+                               rtcp_report->report_block[0] = report_block;
                                report_block->source_ssrc = ntohl(rtcpheader[i]);
                                report_block->lost_count.packets = ntohl(rtcpheader[i + 1]) & 0x00ffffff;
                                report_block->lost_count.fraction = ((ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24);
@@ -4282,7 +5902,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);
@@ -4299,7 +5919,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);
@@ -4309,63 +5929,205 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                                        ast_verbose("  DLSR: %4.4f (sec)\n",(double)report_block->dlsr / 65536.0);
                                        ast_verbose("  RTT: %4.4f(sec)\n", rtp->rtcp->rtt);
                                }
-                               report_counter++;
                        }
                        /* If and when we handle more than one report block, this should occur outside
                         * this loop.
                         */
 
                        message_blob = ast_json_pack("{s: s, s: s, s: f}",
-                                       "from", ast_sockaddr_stringify(&rtp->rtcp->them),
-                                       "to", rtp->rtcp->local_addr_str,
-                                       "rtt", rtp->rtcp->rtt);
+                               "from", ast_sockaddr_stringify(addr),
+                               "to", transport_rtp->rtcp->local_addr_str,
+                               "rtt", rtp->rtcp->rtt);
                        ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
                                        rtcp_report,
                                        message_blob);
                        ast_json_unref(message_blob);
-                       break;
-               case RTCP_PT_FUR:
-               /* Handle RTCP FIR as FUR */
+
+                       /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
+                        * object as a its data */
+                       transport_rtp->f.frametype = AST_FRAME_RTCP;
+                       transport_rtp->f.subclass.integer = pt;
+                       transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
+                       memcpy(transport_rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
+                       transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
+                       if (rc > 0) {
+                               /* There's always a single report block stored, here */
+                               struct ast_rtp_rtcp_report *rtcp_report2;
+                               report_block = transport_rtp->f.data.ptr + transport_rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *);
+                               memcpy(report_block, rtcp_report->report_block[0], sizeof(struct ast_rtp_rtcp_report_block));
+                               rtcp_report2 = (struct ast_rtp_rtcp_report *)transport_rtp->f.data.ptr;
+                               rtcp_report2->report_block[0] = report_block;
+                               transport_rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block);
+                       }
+                       transport_rtp->f.offset = AST_FRIENDLY_OFFSET;
+                       transport_rtp->f.samples = 0;
+                       transport_rtp->f.mallocd = 0;
+                       transport_rtp->f.delivery.tv_sec = 0;
+                       transport_rtp->f.delivery.tv_usec = 0;
+                       transport_rtp->f.src = "RTP";
+                       f = &transport_rtp->f;
+                       break;
+               case AST_RTP_RTCP_RTPFB:
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_NACK:
+                               /* If retransmissions are not enabled ignore this message */
+                               if (!rtp->send_buffer) {
+                                       break;
+                               }
+
+                               if (rtcp_debug_test_addr(addr)) {
+                                       ast_verbose("Received generic RTCP NACK message\n");
+                               }
+
+                               ast_rtp_rtcp_handle_nack(instance, rtcpheader, position, length);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case RTCP_PT_FUR:
+                       /* Handle RTCP FUR as FIR by setting the format to 4 */
+                       rc = AST_RTP_RTCP_FMT_FIR;
                case RTCP_PT_PSFB:
-                       if (rtcp_debug_test_addr(&addr)) {
-                               ast_verbose("Received an RTCP Fast Update Request\n");
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_PLI:
+                       case AST_RTP_RTCP_FMT_FIR:
+                               if (rtcp_debug_test_addr(addr)) {
+                                       ast_verbose("Received an RTCP Fast Update Request\n");
+                               }
+                               transport_rtp->f.frametype = AST_FRAME_CONTROL;
+                               transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
+                               transport_rtp->f.datalen = 0;
+                               transport_rtp->f.samples = 0;
+                               transport_rtp->f.mallocd = 0;
+                               transport_rtp->f.src = "RTP";
+                               f = &transport_rtp->f;
+                               break;
+                       case AST_RTP_RTCP_FMT_REMB:
+                               /* If REMB support is not enabled ignore this message */
+                               if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+                                       break;
+                               }
+
+                               if (rtcp_debug_test_addr(addr)) {
+                                       ast_verbose("Received REMB report\n");
+                               }
+                               transport_rtp->f.frametype = AST_FRAME_RTCP;
+                               transport_rtp->f.subclass.integer = pt;
+                               transport_rtp->f.stream_num = rtp->stream_num;
+                               transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
+                               feedback = transport_rtp->f.data.ptr;
+                               feedback->fmt = rc;
+
+                               /* We don't actually care about the SSRC information in the feedback message */
+                               first_word = ntohl(rtcpheader[i + 2]);
+                               feedback->remb.br_exp = (first_word >> 18) & ((1 << 6) - 1);
+                               feedback->remb.br_mantissa = first_word & ((1 << 18) - 1);
+
+                               transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_feedback);
+                               transport_rtp->f.offset = AST_FRIENDLY_OFFSET;
+                               transport_rtp->f.samples = 0;
+                               transport_rtp->f.mallocd = 0;
+                               transport_rtp->f.delivery.tv_sec = 0;
+                               transport_rtp->f.delivery.tv_usec = 0;
+                               transport_rtp->f.src = "RTP";
+                               f = &transport_rtp->f;
+                               break;
+                       default:
+                               break;
                        }
-                       rtp->f.frametype = AST_FRAME_CONTROL;
-                       rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
-                       rtp->f.datalen = 0;
-                       rtp->f.samples = 0;
-                       rtp->f.mallocd = 0;
-                       rtp->f.src = "RTP";
-                       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));
+                                       ast_sockaddr_stringify(addr));
                        }
                        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));
+                                       ast_sockaddr_stringify(addr));
                        }
                        break;
                default:
-                       ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n",
-                                 pt, ast_sockaddr_stringify(&rtp->rtcp->them));
                        break;
                }
-               position += (length + 1);
+               position += length;
+               rtp->rtcp->rtcp_info = 1;
+
+               if (child) {
+                       ao2_unlock(child);
+               }
        }
-       rtp->rtcp->rtcp_info = 1;
 
        return f;
 }
 
-static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen)
+/*! \pre instance is locked */
+static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, 1);
+       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) {
+               if (res == RTP_DTLS_ESTABLISHED) {
+                       rtp->f.frametype = AST_FRAME_CONTROL;
+                       rtp->f.subclass.integer = AST_CONTROL_SRCCHANGE;
+                       return &rtp->f;
+               }
+
+               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, srtp, read_area, res, &addr);
+}
+
+/*! \pre instance is locked */
+static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
+       struct ast_rtp_instance *instance1, unsigned int *rtpheader, int len, int hdrlen)
 {
-       struct ast_rtp_instance *instance1 = ast_rtp_instance_get_bridged(instance);
-       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance), *bridged = ast_rtp_instance_get_data(instance1);
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct ast_rtp *bridged;
        int res = 0, payload = 0, bridged_payload = 0, mark;
        RAII_VAR(struct ast_rtp_payload_type *, payload_type, NULL, ao2_cleanup);
        int reconstruct = ntohl(rtpheader[0]);
@@ -4375,7 +6137,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
 
        /* Get fields from packet */
        payload = (reconstruct & 0x7f0000) >> 16;
-       mark = (((reconstruct & 0x800000) >> 23) != 0);
+       mark = (reconstruct & 0x800000) >> 23;
 
        /* Check what the payload value should be */
        payload_type = ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payload);
@@ -4392,21 +6154,12 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
                return -1;
        }
 
-       rtp->rxcount++;
-       rtp->rxoctetcount += (len - hdrlen);
-
        /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */
        if (ast_rtp_codecs_find_payload_code(ast_rtp_instance_get_codecs(instance1), bridged_payload) == -1) {
                ast_debug(1, "Unsupported payload type received \n");
                return -1;
        }
 
-       /* If bridged peer is in dtmf, feed all packets to core until it finishes to avoid infinite dtmf */
-       if (bridged->sending_digit) {
-               ast_debug(1, "Feeding packets to core until DTMF finishes\n");
-               return -1;
-       }
-
        /*
         * Even if we are no longer in dtmf, we could still be receiving
         * re-transmissions of the last dtmf end still.  Feed those to the
@@ -4417,25 +6170,86 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
                return -1;
        }
 
-       /* If the marker bit has been explicitly set turn it on */
-       if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) {
-               mark = 1;
-               ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT);
+       if (payload_type->asterisk_format) {
+               ao2_replace(rtp->lastrxformat, payload_type->format);
        }
 
-       /* Reconstruct part of the packet */
-       reconstruct &= 0xFF80FFFF;
-       reconstruct |= (bridged_payload << 16);
-       reconstruct |= (mark << 23);
-       rtpheader[0] = htonl(reconstruct);
+       /*
+        * We have now determined that we need to send the RTP packet
+        * out the bridged instance to do local bridging so we must unlock
+        * the receiving instance to prevent deadlock with the bridged
+        * instance.
+        *
+        * Technically we should grab a ref to instance1 so it won't go
+        * away on us.  However, we should be safe because the bridged
+        * instance won't change without both channels involved being
+        * locked and we currently have the channel lock for the receiving
+        * instance.
+        */
+       ao2_unlock(instance);
+       ao2_lock(instance1);
+
+       /*
+        * Get the peer rtp pointer now to emphasize that using it
+        * must happen while instance1 is locked.
+        */
+       bridged = ast_rtp_instance_get_data(instance1);
+
+
+       /* If bridged peer is in dtmf, feed all packets to core until it finishes to avoid infinite dtmf */
+       if (bridged->sending_digit) {
+               ast_debug(1, "Feeding packet to core until DTMF finishes\n");
+               ao2_unlock(instance1);
+               ao2_lock(instance);
+               return -1;
+       }
+
+       if (payload_type->asterisk_format) {
+               /*
+                * If bridged peer has already received rtp, perform the asymmetric codec check
+                * if that feature has been activated
+                */
+               if (!bridged->asymmetric_codec
+                       && bridged->lastrxformat != ast_format_none
+                       && ast_format_cmp(payload_type->format, bridged->lastrxformat) == AST_FORMAT_CMP_NOT_EQUAL) {
+                       ast_debug(1, "Asymmetric RTP codecs detected (TX: %s, RX: %s) sending frame to core\n",
+                               ast_format_get_name(payload_type->format),
+                               ast_format_get_name(bridged->lastrxformat));
+                       ao2_unlock(instance1);
+                       ao2_lock(instance);
+                       return -1;
+               }
+
+               ao2_replace(bridged->lasttxformat, payload_type->format);
+       }
 
        ast_rtp_instance_get_remote_address(instance1, &remote_address);
 
        if (ast_sockaddr_isnull(&remote_address)) {
                ast_debug(5, "Remote address is null, most likely RTP has been stopped\n");
+               ao2_unlock(instance1);
+               ao2_lock(instance);
                return 0;
        }
 
+       /* If the marker bit has been explicitly set turn it on */
+       if (ast_test_flag(bridged, FLAG_NEED_MARKER_BIT)) {
+               mark = 1;
+               ast_clear_flag(bridged, FLAG_NEED_MARKER_BIT);
+       }
+
+       /* Set the marker bit for the first local bridged packet which has the first bridged peer's SSRC. */
+       if (ast_test_flag(bridged, FLAG_REQ_LOCAL_BRIDGE_BIT)) {
+               mark = 1;
+               ast_clear_flag(bridged, FLAG_REQ_LOCAL_BRIDGE_BIT);
+       }
+
+       /* Reconstruct part of the packet */
+       reconstruct &= 0xFF80FFFF;
+       reconstruct |= (bridged_payload << 16);
+       reconstruct |= (mark << 23);
+       rtpheader[0] = htonl(reconstruct);
+
        /* Send the packet back out */
        res = rtp_sendto(instance1, (void *)rtpheader, len, 0, &remote_address, &ice);
        if (res < 0) {
@@ -4445,7 +6259,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
                                ast_sockaddr_stringify(&remote_address),
                                strerror(errno));
                } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) {
-                       if (option_debug || rtpdebug) {
+                       if (rtpdebug || DEBUG_ATLEAST(1)) {
                                ast_log(LOG_WARNING,
                                        "RTP NAT: Can't write RTP to private "
                                        "address %s, waiting for other end to "
@@ -4454,6 +6268,8 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
                        }
                        ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
                }
+               ao2_unlock(instance1);
+               ao2_lock(instance);
                return 0;
        }
 
@@ -4464,24 +6280,31 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
                            bridged_payload, len - hdrlen);
        }
 
+       ao2_unlock(instance1);
+       ao2_lock(instance);
        return 0;
 }
 
-static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
+static void rtp_instance_unlock(struct ast_rtp_instance *instance)
+{
+       if (instance) {
+               ao2_unlock(instance);
+       }
+}
+
+static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, struct ast_srtp *srtp,
+       const struct ast_sockaddr *remote_address, unsigned char *read_area, int length, int prev_seqno)
 {
+       unsigned int *rtpheader = (unsigned int*)(read_area);
        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;
+       struct ast_rtp_instance *instance1;
+       int res = length, hdrlen = 12, seqno, timestamp, payloadtype, padding, mark, ext, cc;
        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) {
-                       return ast_rtcp_read(instance);
-               }
+       /* If this payload is encrypted then decrypt it using the given SRTP instance */
+       if ((*read_area & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
+                   srtp, read_area, &res, 0) < 0) {
                return &ast_null_frame;
        }
 
@@ -4490,128 +6313,8 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                ast_rtp_dtmf_continuation(instance);
        }
 
-       /* 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,
-                               &addr)) < 0) {
-               ast_assert(errno != EBADF);
-               if (errno != EAGAIN) {
-                       ast_log(LOG_WARNING, "RTP 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 */
-       if (!res) {
-               return &ast_null_frame;
-       }
-
-       /* 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') {
-                               ast_log(LOG_WARNING, "RTP Read too short\n");
-                               return &ast_null_frame;
-                       }
-               }
-               return &ast_null_frame;
-       }
-
-       /* Get fields and verify this is an RTP packet */
-       seqno = ntohl(rtpheader[0]);
-
-       ast_rtp_instance_get_remote_address(instance, &remote_address);
-
-       if (!(version = (seqno & 0xC0000000) >> 30)) {
-               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->s, &addr_tmp, rtp->rawdata + AST_FRIENDLY_OFFSET, 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);
-               }
-               return &ast_null_frame;
-       }
-
-       /* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
-       if (rtp->strict_rtp_state == STRICT_RTP_LEARN) {
-               ast_debug(1, "%p -- Probation learning mode pass with source address %s\n", rtp, ast_sockaddr_stringify(&addr));
-               /* For now, we always copy the address. */
-               ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
-
-               /* Send the rtp and the seqno from header to rtp_learning_rtp_seq_update to see whether we can exit or not*/
-               if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
-                       ast_debug(1, "%p -- Probation at seq %d with %d to go; discarding frame\n",
-                               rtp, rtp->rtp_source_learn.max_seq, rtp->rtp_source_learn.packets);
-                       return &ast_null_frame;
-               }
-
-               ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
-               rtp->strict_rtp_state = STRICT_RTP_CLOSED;
-       }
-       if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) {
-               if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
-                       /* Always reset the alternate learning source */
-                       rtp_learning_seq_init(&rtp->alt_source_learn, seqno);
-               } else {
-                       /* Start trying to learn from the new address. If we pass a probationary period with
-                        * it, that means we've stopped getting RTP from the original source and we should
-                        * switch to it.
-                        */
-                       if (rtp_learning_rtp_seq_update(&rtp->alt_source_learn, seqno)) {
-                               ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets\n",
-                                               rtp, ast_sockaddr_stringify(&addr), rtp->alt_source_learn.packets);
-                               return &ast_null_frame;
-                       }
-                       ast_verb(4, "%p -- Switching RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
-                       ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
-               }
-       }
-
-       /* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
-       if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
-               if (ast_sockaddr_cmp(&remote_address, &addr)) {
-                       /* 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) {
-                               ast_sockaddr_copy(&rtp->rtcp->them, &addr);
-                               ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(&addr) + 1);
-                       }
-                       rtp->rxseqno = 0;
-                       ast_set_flag(rtp, FLAG_NAT_ACTIVE);
-                       if (rtpdebug)
-                               ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s\n",
-                                         ast_sockaddr_stringify(&remote_address));
-               }
-       }
-
-       /* If we are directly bridged to another instance send the audio directly out */
-       if (ast_rtp_instance_get_bridged(instance) && !bridge_p2p_rtp_write(instance, rtpheader, res, hdrlen)) {
-               return &ast_null_frame;
-       }
-
-       /* If the version is not what we expected by this point then just drop the packet */
-       if (version != 2) {
-               return &ast_null_frame;
-       }
-
        /* Pull out the various other fields we will need */
+       seqno = ntohl(rtpheader[0]);
        payloadtype = (seqno & 0x7f0000) >> 16;
        padding = seqno & (1 << 29);
        mark = seqno & (1 << 23);
@@ -4619,44 +6322,12 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
        cc = (seqno & 0xF000000) >> 24;
        seqno &= 0xffff;
        timestamp = ntohl(rtpheader[1]);
-       ssrc = ntohl(rtpheader[2]);
 
        AST_LIST_HEAD_INIT_NOLOCK(&frames);
-       /* Force a marker bit and change SSRC if the SSRC changes */
-       if (rtp->rxssrc && rtp->rxssrc != ssrc) {
-               struct ast_frame *f, srcupdate = {
-                       AST_FRAME_CONTROL,
-                       .subclass.integer = AST_CONTROL_SRCCHANGE,
-               };
-
-               if (!mark) {
-                       if (rtpdebug) {
-                               ast_debug(1, "Forcing Marker bit, because SSRC has changed\n");
-                       }
-                       mark = 1;
-               }
-
-               f = ast_frisolate(&srcupdate);
-               AST_LIST_INSERT_TAIL(&frames, f, frame_list);
-
-               rtp->seedrxseqno = 0;
-               rtp->rxcount = 0;
-               rtp->rxoctetcount = 0;
-               rtp->cycles = 0;
-               rtp->lastrxseqno = 0;
-               rtp->last_seqno = 0;
-               rtp->last_end_timestamp = 0;
-               if (rtp->rtcp) {
-                       rtp->rtcp->expected_prior = 0;
-                       rtp->rtcp->received_prior = 0;
-               }
-       }
-
-       rtp->rxssrc = ssrc;
 
        /* 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 */
@@ -4668,13 +6339,15 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
        if (ext) {
                hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
                hdrlen += 4;
-               if (option_debug) {
+               if (DEBUG_ATLEAST(1)) {
                        unsigned int profile;
                        profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
-                       if (profile == 0x505a)
-                               ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n");
-                       else
-                               ast_debug(1, "Found unknown RTP Extensions %x\n", profile);
+                       if (profile == 0x505a) {
+                               ast_log(LOG_DEBUG, "Found Zfone extension in RTP stream - zrtp - not supported.\n");
+                       } else if (profile != 0xbede) {
+                               /* SDP negotiated RTP extensions can not currently be output in logging */
+                               ast_log(LOG_DEBUG, "Found unknown RTP Extensions %x\n", profile);
+                       }
                }
        }
 
@@ -4703,17 +6376,25 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
        if ((int)rtp->lastrxseqno - (int)seqno  > 100) /* if so it would indicate that the sender cycled; allow for misordering */
                rtp->cycles += RTP_SEQ_MOD;
 
-       prev_seqno = rtp->lastrxseqno;
-       rtp->lastrxseqno = seqno;
+       /* If we are directly bridged to another instance send the audio directly out,
+        * but only after updating core information about the received traffic so that
+        * outgoing RTCP reflects it.
+        */
+       instance1 = ast_rtp_instance_get_bridged(instance);
+       if (instance1
+               && !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {
+               struct timeval rxtime;
+               struct ast_frame *f;
 
-       if (!rtp->themssrc) {
-               rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
-       }
+               /* Update statistics for jitter so they are correct in RTCP */
+               calc_rxstamp(&rxtime, rtp, timestamp, mark);
 
-       if (rtp_debug_test_addr(&addr)) {
-               ast_verbose("Got  RTP packet from    %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
-                           ast_sockaddr_stringify(&addr),
-                           payloadtype, seqno, timestamp,res - hdrlen);
+               /* When doing P2P we don't need to raise any frames about SSRC change to the core */
+               while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list)) != NULL) {
+                       ast_frfree(f);
+               }
+
+               return &ast_null_frame;
        }
 
        payload = ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payloadtype);
@@ -4730,15 +6411,15 @@ 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, 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, 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, payloadtype, mark);
                } else {
                        ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n",
                                payloadtype,
-                               ast_sockaddr_stringify(&remote_address));
+                               ast_sockaddr_stringify(remote_address));
                }
 
                if (f) {
@@ -4769,7 +6450,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                        ast_codec_media_type2str(ast_format_get_type(rtp->f.subclass.format)));
                return &ast_null_frame;
        }
-       rtp->rxseqno = seqno;
 
        if (rtp->dtmf_timeout && rtp->dtmf_timeout < timestamp) {
                rtp->dtmf_timeout = 0;
@@ -4785,14 +6465,14 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                }
        }
 
-       rtp->lastrxts = timestamp;
-
        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;
+       ast_set_flag(&rtp->f, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
        rtp->f.seqno = seqno;
+       rtp->f.stream_num = rtp->stream_num;
 
        if ((ast_format_cmp(rtp->f.subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL)
                && ((int)seqno - (prev_seqno + 1) > 0)
@@ -4886,13 +6566,612 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
        } else {
                ast_log(LOG_WARNING, "Unknown or unsupported media type: %s\n",
                        ast_codec_media_type2str(ast_format_get_type(rtp->f.subclass.format)));
-               return &ast_null_frame;;
+               return &ast_null_frame;
+       }
+
+       AST_LIST_INSERT_TAIL(&frames, &rtp->f, frame_list);
+       return AST_LIST_FIRST(&frames);
+}
+
+/*! \pre instance is locked */
+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_srtp *srtp;
+       RAII_VAR(struct ast_rtp_instance *, child, NULL, rtp_instance_unlock);
+       struct ast_sockaddr addr;
+       int res, hdrlen = 12, version, payloadtype, mark;
+       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, prev_seqno;
+       struct ast_sockaddr remote_address = { {0,} };
+       struct frame_list frames;
+       struct ast_frame *frame;
+
+       /* If this is actually RTCP let's hop on over and handle it */
+       if (rtcp) {
+               if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+                       return ast_rtcp_read(instance);
+               }
+               return &ast_null_frame;
+       }
+
+       /* Actually read in the data from the socket */
+       if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0,
+                               &addr)) < 0) {
+               if (res == RTP_DTLS_ESTABLISHED) {
+                       rtp->f.frametype = AST_FRAME_CONTROL;
+                       rtp->f.subclass.integer = AST_CONTROL_SRCCHANGE;
+                       return &rtp->f;
+               }
+
+               ast_assert(errno != EBADF);
+               if (errno != EAGAIN) {
+                       ast_log(LOG_WARNING, "RTP 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 */
+       if (!res) {
+               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, ast_rtp_instance_get_srtp(instance, 1), read_area, res, &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 (read_area[i] != '\0') {
+                               ast_log(LOG_WARNING, "RTP Read too short\n");
+                               return &ast_null_frame;
+                       }
+               }
+               return &ast_null_frame;
+       }
+
+       /* Get fields and verify this is an RTP packet */
+       seqno = ntohl(rtpheader[0]);
+
+       ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+       if (!(version = (seqno & 0xC0000000) >> 30)) {
+               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->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);
+               }
+               return &ast_null_frame;
+       }
+
+       /* If the version is not what we expected by this point then just drop the packet */
+       if (version != 2) {
+               return &ast_null_frame;
+       }
+
+       /* We use the SSRC to determine what RTP instance this packet is actually for */
+       ssrc = ntohl(rtpheader[2]);
+
+       /* We use the SRTP data from the provided instance that it came in on, not the child */
+       srtp = ast_rtp_instance_get_srtp(instance, 0);
+
+       /* Determine the appropriate instance for this */
+       child = rtp_find_instance_by_packet_source_ssrc(instance, rtp, ssrc);
+       if (!child) {
+               /* Neither the bundled parent nor any child has this SSRC */
+               return &ast_null_frame;
+       }
+       if (child != instance) {
+               /* It is safe to hold the child lock while holding the parent lock, we guarantee that the locking order
+                * is always parent->child or that the child lock is not held when acquiring the parent lock.
+                */
+               ao2_lock(child);
+               instance = child;
+               rtp = ast_rtp_instance_get_data(instance);
+       } else {
+               /* The child is the parent! We don't need to unlock it. */
+               child = NULL;
+       }
+
+       /* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
+       switch (rtp->strict_rtp_state) {
+       case STRICT_RTP_LEARN:
+               /*
+                * Scenario setup:
+                * PartyA -- Ast1 -- Ast2 -- PartyB
+                *
+                * The learning timeout is necessary for Ast1 to handle the above
+                * setup where PartyA calls PartyB and Ast2 initiates direct media
+                * between Ast1 and PartyB.  Ast1 may lock onto the Ast2 stream and
+                * never learn the PartyB stream when it starts.  The timeout makes
+                * Ast1 stay in the learning state long enough to see and learn the
+                * RTP stream from PartyB.
+                *
+                * To mitigate against attack, the learning state cannot switch
+                * streams while there are competing streams.  The competing streams
+                * interfere with each other's qualification.  Once we accept a
+                * stream and reach the timeout, an attacker cannot interfere
+                * anymore.
+                *
+                * Here are a few scenarios and each one assumes that the streams
+                * are continuous:
+                *
+                * 1) We already have a known stream source address and the known
+                * stream wants to change to a new source address.  An attacking
+                * stream will block learning the new stream source.  After the
+                * timeout we re-lock onto the original stream source address which
+                * likely went away.  The result is one way audio.
+                *
+                * 2) We already have a known stream source address and the known
+                * stream doesn't want to change source addresses.  An attacking
+                * stream will not be able to replace the known stream.  After the
+                * timeout we re-lock onto the known stream.  The call is not
+                * affected.
+                *
+                * 3) We don't have a known stream source address.  This presumably
+                * is the start of a call.  Competing streams will result in staying
+                * in learning mode until a stream becomes the victor and we reach
+                * the timeout.  We cannot exit learning if we have no known stream
+                * to lock onto.  The result is one way audio until there is a victor.
+                *
+                * If we learn a stream source address before the timeout we will be
+                * in scenario 1) or 2) when a competing stream starts.
+                */
+               if (!ast_sockaddr_isnull(&rtp->strict_rtp_address)
+                       && STRICT_RTP_LEARN_TIMEOUT < ast_tvdiff_ms(ast_tvnow(), rtp->rtp_source_learn.start)) {
+                       ast_verb(4, "%p -- Strict RTP learning complete - Locking on source address %s\n",
+                               rtp, ast_sockaddr_stringify(&rtp->strict_rtp_address));
+                       ast_test_suite_event_notify("STRICT_RTP_LEARN", "Source: %s",
+                               ast_sockaddr_stringify(&rtp->strict_rtp_address));
+                       rtp->strict_rtp_state = STRICT_RTP_CLOSED;
+               } else {
+                       struct ast_sockaddr target_address;
+
+                       if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+                               /*
+                                * We are open to learning a new address but have received
+                                * traffic from the current address, accept it and reset
+                                * the learning counts for a new source.  When no more
+                                * current source packets arrive a new source can take over
+                                * once sufficient traffic is received.
+                                */
+                               rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+                               break;
+                       }
+
+                       /*
+                        * We give preferential treatment to the requested target address
+                        * (negotiated SDP address) where we are to send our RTP.  However,
+                        * the other end has no obligation to send from that address even
+                        * though it is practically a requirement when NAT is involved.
+                        */
+                       ast_rtp_instance_get_requested_target_address(instance, &target_address);
+                       if (!ast_sockaddr_cmp(&target_address, &addr)) {
+                               /* Accept the negotiated target RTP stream as the source */
+                               ast_verb(4, "%p -- Strict RTP switching to RTP target address %s as source\n",
+                                       rtp, ast_sockaddr_stringify(&addr));
+                               ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+                               rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+                               break;
+                       }
+
+                       /*
+                        * Trying to learn a new address.  If we pass a probationary period
+                        * with it, that means we've stopped getting RTP from the original
+                        * source and we should switch to it.
+                        */
+                       if (!ast_sockaddr_cmp(&rtp->rtp_source_learn.proposed_address, &addr)) {
+                               if (rtp->rtp_source_learn.stream_type == AST_MEDIA_TYPE_UNKNOWN) {
+                                       struct ast_rtp_codecs *codecs;
+
+                                       codecs = ast_rtp_instance_get_codecs(instance);
+                                       rtp->rtp_source_learn.stream_type =
+                                               ast_rtp_codecs_get_stream_type(codecs);
+                                       ast_verb(4, "%p -- Strict RTP qualifying stream type: %s\n",
+                                               rtp, ast_codec_media_type2str(rtp->rtp_source_learn.stream_type));
+                               }
+                               if (!rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
+                                       /* Accept the new RTP stream */
+                                       ast_verb(4, "%p -- Strict RTP switching source address to %s\n",
+                                               rtp, ast_sockaddr_stringify(&addr));
+                                       ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+                                       rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+                                       break;
+                               }
+                               /* Not ready to accept the RTP stream candidate */
+                               ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets.\n",
+                                       rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
+                       } else {
+                               /*
+                                * This is either an attacking stream or
+                                * the start of the expected new stream.
+                                */
+                               ast_sockaddr_copy(&rtp->rtp_source_learn.proposed_address, &addr);
+                               rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
+                               ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Qualifying new stream.\n",
+                                       rtp, ast_sockaddr_stringify(&addr));
+                       }
+                       return &ast_null_frame;
+               }
+               /* Fall through */
+       case STRICT_RTP_CLOSED:
+               /*
+                * We should not allow a stream address change if the SSRC matches
+                * once strictrtp learning is closed.  Any kind of address change
+                * like this should have happened while we were in the learning
+                * state.  We do not want to allow the possibility of an attacker
+                * interfering with the RTP stream after the learning period.
+                * An attacker could manage to get an RTCP packet redirected to
+                * them which can contain the SSRC value.
+                */
+               if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+                       break;
+               }
+               ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
+                       rtp, ast_sockaddr_stringify(&addr));
+#ifdef TEST_FRAMEWORK
+       {
+               static int strict_rtp_test_event = 1;
+               if (strict_rtp_test_event) {
+                       ast_test_suite_event_notify("STRICT_RTP_CLOSED", "Source: %s",
+                               ast_sockaddr_stringify(&addr));
+                       strict_rtp_test_event = 0; /* Only run this event once to prevent possible spam */
+               }
+       }
+#endif
+               return &ast_null_frame;
+       case STRICT_RTP_OPEN:
+               break;
+       }
+
+       /* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
+       if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
+               if (ast_sockaddr_cmp(&remote_address, &addr)) {
+                       /* 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 && 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);
+                       }
+                       ast_set_flag(rtp, FLAG_NAT_ACTIVE);
+                       if (rtpdebug)
+                               ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s\n",
+                                         ast_sockaddr_stringify(&remote_address));
+               }
+       }
+
+       /* Pull out the various other fields we will need */
+       payloadtype = (seqno & 0x7f0000) >> 16;
+       mark = seqno & (1 << 23);
+       seqno &= 0xffff;
+       timestamp = ntohl(rtpheader[1]);
+
+       if (rtp_debug_test_addr(&addr)) {
+               ast_verbose("Got  RTP packet from    %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
+                           ast_sockaddr_stringify(&addr),
+                           payloadtype, seqno, timestamp, res - hdrlen);
+       }
+
+       AST_LIST_HEAD_INIT_NOLOCK(&frames);
+
+       /* Only non-bundled instances can change/learn the remote's SSRC implicitly. */
+       if (!child && !AST_VECTOR_SIZE(&rtp->ssrc_mapping)) {
+               /* Force a marker bit and change SSRC if the SSRC changes */
+               if (rtp->themssrc_valid && rtp->themssrc != ssrc) {
+                       struct ast_frame *f, srcupdate = {
+                               AST_FRAME_CONTROL,
+                               .subclass.integer = AST_CONTROL_SRCCHANGE,
+                       };
+
+                       if (!mark) {
+                               if (rtpdebug) {
+                                       ast_debug(1, "Forcing Marker bit, because SSRC has changed\n");
+                               }
+                               mark = 1;
+                       }
+
+                       f = ast_frisolate(&srcupdate);
+                       AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+
+                       rtp->seedrxseqno = 0;
+                       rtp->rxcount = 0;
+                       rtp->rxoctetcount = 0;
+                       rtp->cycles = 0;
+                       rtp->lastrxseqno = 0;
+                       rtp->last_seqno = 0;
+                       rtp->last_end_timestamp = 0;
+                       if (rtp->rtcp) {
+                               rtp->rtcp->expected_prior = 0;
+                               rtp->rtcp->received_prior = 0;
+                       }
+               }
+
+               rtp->themssrc = ssrc; /* Record their SSRC to put in future RR */
+               rtp->themssrc_valid = 1;
+       }
+
+       prev_seqno = rtp->lastrxseqno;
+       rtp->lastrxseqno = seqno;
+
+       if (!rtp->recv_buffer) {
+               /* If there is no receive buffer then we can pass back the frame directly */
+               return ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
+       } else if (rtp->expectedrxseqno == -1 || seqno == rtp->expectedrxseqno) {
+               rtp->expectedrxseqno = seqno + 1;
+
+               /* If there are no buffered packets that will be placed after this frame then we can
+                * return it directly without duplicating it.
+                */
+               if (!ast_data_buffer_count(rtp->recv_buffer)) {
+                       return ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
+               }
+
+               if (!AST_VECTOR_REMOVE_CMP_ORDERED(&rtp->missing_seqno, seqno, find_by_value,
+                       AST_VECTOR_ELEM_CLEANUP_NOOP)) {
+                       ast_debug(2, "Packet with sequence number '%d' on RTP instance '%p' is no longer missing\n",
+                               seqno, instance);
+               }
+
+               /* If we don't have the next packet after this we can directly return the frame, as there is no
+                * chance it will be overwritten.
+                */
+               if (!ast_data_buffer_get(rtp->recv_buffer, seqno + 1)) {
+                       return ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
+               }
+
+               /* Otherwise we need to dupe the frame so that the potential processing of frames placed after
+                * it do not overwrite the data. You may be thinking that we could just add the current packet
+                * to the head of the frames list and avoid having to duplicate it but this would result in out
+                * of order packet processing by libsrtp which we are trying to avoid.
+                */
+               frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, seqno - 1));
+               if (frame) {
+                       AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+               }
+
+               /* Add any additional packets that we have buffered and that are available */
+               while (ast_data_buffer_count(rtp->recv_buffer)) {
+                       struct ast_rtp_rtcp_nack_payload *payload;
+
+                       payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_remove(rtp->recv_buffer, rtp->expectedrxseqno);
+                       if (!payload) {
+                               break;
+                       }
+
+                       frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, rtp->expectedrxseqno - 1));
+                       ast_free(payload);
+
+                       if (!frame) {
+                               /* If this packet can't be interpeted due to being out of memory we return what we have and assume
+                                * that we will determine it is a missing packet later and NACK for it.
+                                */
+                               return AST_LIST_FIRST(&frames);
+                       }
+
+                       ast_debug(2, "Pulled buffered packet with sequence number '%d' to additionally return on RTP instance '%p'\n",
+                               frame->seqno, instance);
+                       AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+                       rtp->expectedrxseqno++;
+               }
+
+               return AST_LIST_FIRST(&frames);
+       } else if ((abs(seqno - rtp->expectedrxseqno) > 100) ||
+               ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer)) {
+               int inserted = 0;
+
+               /* We have a large number of outstanding buffered packets or we've jumped far ahead in time.
+                * To compensate we dump what we have in the buffer and place the current packet in a logical
+                * spot. In the case of video we also require a full frame to give the decoding side a fighting
+                * chance.
+                */
+
+               if (rtp->rtp_source_learn.stream_type == AST_MEDIA_TYPE_VIDEO) {
+                       ast_debug(2, "Source on RTP instance '%p' has wild gap or packet loss, sending FIR\n",
+                               instance);
+                       rtp_write_rtcp_fir(instance, rtp, &remote_address);
+               }
+
+               while (ast_data_buffer_count(rtp->recv_buffer)) {
+                       struct ast_rtp_rtcp_nack_payload *payload;
+
+                       payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_remove_head(rtp->recv_buffer);
+                       if (!payload) {
+                               continue;
+                       }
+
+                       /* Even when dumping the receive buffer we do our best to order things, so we ensure that the
+                        * packet we just received is processed in the correct order, so see if we need to insert it now.
+                        */
+                       if (!inserted) {
+                               int buffer_seqno;
+
+                               buffer_seqno = ntohl(payload->buf[0]) & 0xffff;
+                               if (seqno < buffer_seqno) {
+                                       frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno));
+                                       if (frame) {
+                                               AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+                                               rtp->expectedrxseqno = seqno + 1;
+                                               prev_seqno = seqno;
+                                               ast_debug(2, "Inserted just received packet with sequence number '%d' in correct order on RTP instance '%p'\n",
+                                                       seqno, instance);
+                                       }
+                                       inserted = 1;
+                               }
+                       }
+
+                       frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, prev_seqno));
+                       if (frame) {
+                               AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+                               prev_seqno = frame->seqno;
+                               ast_debug(2, "Emptying queue and returning packet with sequence number '%d' from RTP instance '%p'\n",
+                                       frame->seqno, instance);
+                       }
+
+                       ast_free(payload);
+               }
+
+               if (!inserted) {
+                       /* This current packet goes after them, and we assume that packets going forward will follow
+                        * that new sequence number increment. It is okay for this to not be duplicated as it is guaranteed
+                        * to be the last packet processed right now and it is also guaranteed that it will always return
+                        * non-NULL.
+                        */
+                       frame = ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
+                       AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+                       rtp->expectedrxseqno = seqno + 1;
+
+                       ast_debug(2, "Adding just received packet with sequence number '%d' to end of dumped queue on RTP instance '%p'\n",
+                               seqno, instance);
+               }
+
+               /* As there is such a large gap we don't want to flood the order side with missing packets, so we
+                * give up and start anew.
+                */
+               AST_VECTOR_RESET(&rtp->missing_seqno, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
+               return AST_LIST_FIRST(&frames);
+       } else if (seqno < rtp->expectedrxseqno) {
+               /* If this is a packet from the past then we have received a duplicate packet, so just drop it */
+               ast_debug(2, "Received an old packet with sequence number '%d' on RTP instance '%p', dropping it\n",
+                       seqno, instance);
+               return &ast_null_frame;
+       } else if (ast_data_buffer_get(rtp->recv_buffer, seqno)) {
+               /* If this is a packet we already have buffered then it is a duplicate, so just drop it */
+               ast_debug(2, "Received a duplicate transmission of packet with sequence number '%d' on RTP instance '%p', dropping it\n",
+                       seqno, instance);
+               return &ast_null_frame;
+       } else {
+               /* This is an out of order packet from the future */
+               struct ast_rtp_rtcp_nack_payload *payload;
+               int difference;
+
+               ast_debug(2, "Received an out of order packet with sequence number '%d' from the future on RTP instance '%p'\n",
+                       seqno, instance);
+
+               payload = ast_malloc(sizeof(*payload) + res);
+               if (!payload) {
+                       /* If the payload can't be allocated then we can't defer this packet right now.
+                        * Instead of dumping what we have we pretend we lost this packet. It will then
+                        * get NACKed later or the existing buffer will be returned entirely. Well, we may
+                        * try since we're seemingly out of memory. It's a bad situation all around and
+                        * packets are likely to get lost anyway.
+                        */
+                       return &ast_null_frame;
+               }
+
+               payload->size = res;
+               memcpy(payload->buf, rtpheader, res);
+               ast_data_buffer_put(rtp->recv_buffer, seqno, payload);
+               AST_VECTOR_REMOVE_CMP_ORDERED(&rtp->missing_seqno, seqno, find_by_value,
+                       AST_VECTOR_ELEM_CLEANUP_NOOP);
+
+               difference = seqno - (prev_seqno + 1);
+               while (difference > 0) {
+                       /* We don't want missing sequence number duplicates. If, for some reason,
+                        * packets are really out of order, we could end up in this scenario:
+                        *
+                        * We are expecting sequence number 100
+                        * We receive sequence number 105
+                        * Sequence numbers 100 through 104 get added to the vector
+                        * We receive sequence number 101 (this section is skipped)
+                        * We receive sequence number 103
+                        * Sequence number 102 is added to the vector
+                        *
+                        * This will prevent the duplicate from being added.
+                        */
+                       if (AST_VECTOR_GET_CMP(&rtp->missing_seqno, seqno - difference,
+                                               find_by_value)) {
+                               difference--;
+                               continue;
+                       }
+
+                       ast_debug(2, "Added missing sequence number '%d' to RTP instance '%p'\n",
+                               seqno - difference, instance);
+                       AST_VECTOR_ADD_SORTED(&rtp->missing_seqno, seqno - difference,
+                                       compare_by_value);
+                       difference--;
+               }
+
+               /* When our data buffer is half full we assume that the packets aren't just out of order but
+                * have actually been lost. To get them back we construct and send a NACK causing the sender to
+                * retransmit them.
+                */
+               if (ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer) / 2) {
+                       int packet_len = 0;
+                       int res = 0;
+                       int ice;
+                       int sr;
+                       size_t data_size = AST_UUID_STR_LEN + 128 + (seqno - rtp->expectedrxseqno) / 17;
+                       RAII_VAR(unsigned char *, rtcpheader, NULL, ast_free_ptr);
+                       RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
+                                       ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),
+                                       ao2_cleanup);
+
+                       rtcpheader = ast_malloc(sizeof(*rtcpheader) + data_size);
+                       if (!rtcpheader) {
+                               ast_debug(1, "Failed to allocate memory for NACK\n");
+                               return &ast_null_frame;
+                       }
+
+                       memset(rtcpheader, 0, data_size);
+
+                       res = ast_rtcp_generate_report(instance, rtcpheader, rtcp_report, &sr);
+
+                       if (res == 0 || res == 1) {
+                               ast_debug(1, "Failed to add %s report to NACK, stopping here\n", sr ? "SR" : "RR");
+                               return &ast_null_frame;
+                       }
+
+                       packet_len += res;
+
+                       res = ast_rtcp_generate_nack(instance, rtcpheader + packet_len);
+
+                       if (res == 0) {
+                               ast_debug(1, "Failed to construct NACK, stopping here\n");
+                               return &ast_null_frame;
+                       }
+
+                       packet_len += res;
+
+                       res = rtcp_sendto(instance, rtcpheader, packet_len, 0, &remote_address, &ice);
+                       if (res < 0) {
+                               ast_debug(1, "Failed to send NACK request out\n");
+                       } else {
+                               /* Update RTCP SR/RR statistics */
+                               ast_rtcp_calculate_sr_rr_statistics(instance, rtcp_report, remote_address, ice, sr);
+                       }
+
+                       ast_debug(2, "Sending a NACK request on RTP instance '%p' to get missing packets\n", instance);
+               }
+
+               return &ast_null_frame;
        }
 
-       AST_LIST_INSERT_TAIL(&frames, &rtp->f, frame_list);
-       return AST_LIST_FIRST(&frames);
+       return &ast_null_frame;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -4901,19 +7180,31 @@ 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;
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+                               rtp->rtcp->dtls.timeout_timer = -1;
+#endif
+                               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)) {
@@ -4923,6 +7214,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);
@@ -4930,60 +7222,92 @@ 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);
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_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 && rtp->rtcp->s != rtp->s) {
+                                       close(rtp->rtcp->s);
+                               }
+                               rtp->rtcp->s = rtp->s;
+                               ast_rtp_instance_get_remote_address(instance, &addr);
+                               ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+                               if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
+                                       SSL_free(rtp->rtcp->dtls.ssl);
+                               }
+                               rtp->rtcp->dtls.ssl = rtp->dtls.ssl;
 #endif
+                       }
 
-                       return;
+                       ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
                } else {
                        if (rtp->rtcp) {
                                if (rtp->rtcp->schedid > -1) {
+                                       ao2_unlock(instance);
                                        if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
                                                /* Successfully cancelled scheduler entry. */
                                                ao2_ref(instance, -1);
                                        } else {
                                                /* Unable to cancel scheduler entry */
                                                ast_debug(1, "Failed to tear down RTCP on RTP instance '%p'\n", instance);
+                                               ao2_lock(instance);
                                                return;
                                        }
+                                       ao2_lock(instance);
                                        rtp->rtcp->schedid = -1;
                                }
-                               close(rtp->rtcp->s);
-#ifdef HAVE_OPENSSL_SRTP
-                               if (rtp->rtcp->dtls.ssl) {
+                               if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
+                                       close(rtp->rtcp->s);
+                               }
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+                               ao2_unlock(instance);
+                               dtls_srtp_stop_timeout_timer(instance, rtp, 1);
+                               ao2_lock(instance);
+
+                               if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
                                        SSL_free(rtp->rtcp->dtls.ssl);
                                }
 #endif
@@ -4991,13 +7315,18 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                ast_free(rtp->rtcp);
                                rtp->rtcp = NULL;
                        }
-                       return;
                }
+       } else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
+               rtp->asymmetric_codec = value;
+       } else if (property == AST_RTP_PROPERTY_RETRANS_SEND) {
+               rtp->send_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_SEND_BUFFER_SIZE);
+       } else if (property == AST_RTP_PROPERTY_RETRANS_RECV) {
+               rtp->recv_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_RECV_BUFFER_SIZE);
+               AST_VECTOR_INIT(&rtp->missing_seqno, 0);
        }
-
-       return;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -5005,10 +7334,12 @@ static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp)
        return rtcp ? (rtp->rtcp ? rtp->rtcp->s : -1) : rtp->s;
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_sockaddr *addr)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        struct ast_sockaddr local;
+       int index;
 
        ast_rtp_instance_get_local_address(instance, &local);
        if (!ast_sockaddr_isnull(addr)) {
@@ -5021,42 +7352,66 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
                }
        }
 
-       if (rtp->rtcp) {
+       if (rtp->rtcp && !ast_sockaddr_isnull(addr)) {
                ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
                ast_sockaddr_copy(&rtp->rtcp->them, addr);
-               if (!ast_sockaddr_isnull(addr)) {
+
+               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);
-                       ast_sockaddr_copy(&rtp->rtcp->us, &local);
-
-                       ast_free(rtp->rtcp->local_addr_str);
-                       rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local));
                }
+               ast_sockaddr_copy(&rtp->rtcp->us, &local);
+
+               ast_free(rtp->rtcp->local_addr_str);
+               rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local));
+       }
+
+       /* Update any bundled RTP instances */
+       for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
+               struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
+
+               ast_rtp_instance_set_remote_address(mapping->instance, addr);
        }
 
-       rtp->rxseqno = 0;
+       /* Need to reset the DTMF last sequence number and the timestamp of the last END packet */
+       rtp->last_seqno = 0;
+       rtp->last_end_timestamp = 0;
 
-       if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN) {
-               rtp->strict_rtp_state = STRICT_RTP_LEARN;
-               rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
+       if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN
+               && !ast_sockaddr_isnull(addr) && ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
+               /* We only need to learn a new strict source address if we've been told the source is
+                * changing to something different.
+                */
+               ast_verb(4, "%p -- Strict RTP learning after remote address set to: %s\n",
+                       rtp, ast_sockaddr_stringify(addr));
+               rtp_learning_start(rtp);
        }
 }
 
-/*! \brief Write t140 redundacy frame
+/*!
+ * \brief Write t140 redundacy frame
+ *
  * \param data primary data to be buffered
+ *
+ * Scheduler callback
  */
 static int red_write(const void *data)
 {
        struct ast_rtp_instance *instance = (struct ast_rtp_instance*) data;
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-       ast_rtp_write(instance, &rtp->red->t140);
+       ao2_lock(instance);
+       if (rtp->red->t140.datalen > 0) {
+               ast_rtp_write(instance, &rtp->red->t140);
+       }
+       ao2_unlock(instance);
 
        return 1;
 }
 
+/*! \pre instance is locked */
 static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -5089,12 +7444,33 @@ static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int
        return 0;
 }
 
+/*! \pre instance is locked */
 static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       struct rtp_red *red = rtp->red;
+
+       if (!red) {
+               return 0;
+       }
+
+       if (frame->datalen > 0) {
+               if (red->t140.datalen > 0) {
+                       const unsigned char *primary = red->buf_data;
+
+                       /* There is something already in the T.140 buffer */
+                       if (primary[0] == 0x08 || primary[0] == 0x0a || primary[0] == 0x0d) {
+                               /* Flush the previous T.140 packet if it is a command */
+                               ast_rtp_write(instance, &rtp->red->t140);
+                       } else {
+                               primary = frame->data.ptr;
+                               if (primary[0] == 0x08 || primary[0] == 0x0a || primary[0] == 0x0d) {
+                                       /* Flush the previous T.140 packet if we are buffering a command now */
+                                       ast_rtp_write(instance, &rtp->red->t140);
+                               }
+                       }
+               }
 
-       if (frame->datalen > -1) {
-               struct rtp_red *red = rtp->red;
                memcpy(&red->buf_data[red->t140.datalen], frame->data.ptr, frame->datalen);
                red->t140.datalen += frame->datalen;
                red->t140.ts = frame->ts;
@@ -5103,15 +7479,23 @@ static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *f
        return 0;
 }
 
+/*! \pre Neither instance0 nor instance1 are locked */
 static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance0);
 
-       ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
+       ao2_lock(instance0);
+       ast_set_flag(rtp, FLAG_NEED_MARKER_BIT | FLAG_REQ_LOCAL_BRIDGE_BIT);
+       if (rtp->smoother) {
+               ast_smoother_free(rtp->smoother);
+               rtp->smoother = NULL;
+       }
+       ao2_unlock(instance0);
 
        return 0;
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -5163,6 +7547,7 @@ static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_in
        return 0;
 }
 
+/*! \pre Neither instance0 nor instance1 are locked */
 static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1)
 {
        /* If both sides are not using the same method of DTMF transmission
@@ -5179,52 +7564,62 @@ static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_ins
                 (!ast_channel_tech(chan0)->send_digit_begin != !ast_channel_tech(chan1)->send_digit_begin)) ? 0 : 1);
 }
 
+/*! \pre instance is NOT locked */
 static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        struct sockaddr_in suggestion_tmp;
 
+       /*
+        * The instance should not be locked because we can block
+        * waiting for a STUN respone.
+        */
        ast_sockaddr_to_sin(suggestion, &suggestion_tmp);
        ast_stun_request(rtp->s, &suggestion_tmp, username, NULL);
        ast_sockaddr_from_sin(suggestion, &suggestion_tmp);
 }
 
+/*! \pre instance is locked */
 static void ast_rtp_stop(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
        struct ast_sockaddr addr = { {0,} };
 
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+       ao2_unlock(instance);
        AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
 
        dtls_srtp_stop_timeout_timer(instance, rtp, 0);
        if (rtp->rtcp) {
                dtls_srtp_stop_timeout_timer(instance, rtp, 1);
        }
+       ao2_lock(instance);
 #endif
 
        if (rtp->rtcp && rtp->rtcp->schedid > -1) {
+               ao2_unlock(instance);
                if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
                        /* successfully cancelled scheduler entry. */
                        ao2_ref(instance, -1);
                }
+               ao2_lock(instance);
                rtp->rtcp->schedid = -1;
        }
 
        if (rtp->red) {
+               ao2_unlock(instance);
                AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
+               ao2_lock(instance);
                ast_free(rtp->red);
                rtp->red = NULL;
        }
 
        ast_rtp_instance_set_remote_address(instance, &addr);
-       if (rtp->rtcp) {
-               ast_sockaddr_setnull(&rtp->rtcp->them);
-       }
 
        ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
 }
 
+/*! \pre instance is locked */
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char *desc)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -5232,7 +7627,11 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos,
        return ast_set_qos(rtp->s, tos, cos, desc);
 }
 
-/*! \brief generate comfort noice (CNG) */
+/*!
+ * \brief generate comfort noice (CNG)
+ *
+ * \pre instance is locked
+ */
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
 {
        unsigned int *rtpheader;
@@ -5281,31 +7680,157 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
        return res;
 }
 
-#ifdef HAVE_OPENSSL_SRTP
-static void dtls_perform_setup(struct dtls_details *dtls)
+/*! \pre instance is locked */
+static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance)
 {
-       if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return rtp->ssrc;
+}
+
+/*! \pre instance is locked */
+static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return rtp->cname;
+}
+
+/*! \pre instance is locked */
+static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       if (rtp->themssrc_valid && rtp->themssrc == ssrc) {
                return;
        }
 
-       SSL_clear(dtls->ssl);
-       if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
-               SSL_set_accept_state(dtls->ssl);
-       } else {
-               SSL_set_connect_state(dtls->ssl);
+       rtp->themssrc = ssrc;
+       rtp->themssrc_valid = 1;
+
+       /* If this is bundled we need to update the SSRC mapping */
+       if (rtp->bundled) {
+               struct ast_rtp *bundled_rtp;
+               int index;
+
+               ao2_unlock(instance);
+
+               /* The child lock can't be held while accessing the parent */
+               ao2_lock(rtp->bundled);
+               bundled_rtp = ast_rtp_instance_get_data(rtp->bundled);
+
+               for (index = 0; index < AST_VECTOR_SIZE(&bundled_rtp->ssrc_mapping); ++index) {
+                       struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&bundled_rtp->ssrc_mapping, index);
+
+                       if (mapping->instance == instance) {
+                               mapping->ssrc = ssrc;
+                               mapping->ssrc_valid = 1;
+                               break;
+                       }
+               }
+
+               ao2_unlock(rtp->bundled);
+
+               ao2_lock(instance);
        }
-       dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
 }
 
-static int ast_rtp_activate(struct ast_rtp_instance *instance)
+static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-       dtls_perform_setup(&rtp->dtls);
+       rtp->stream_num = stream_num;
+}
 
-       if (rtp->rtcp) {
-               dtls_perform_setup(&rtp->rtcp->dtls);
+static int ast_rtp_extension_enable(struct ast_rtp_instance *instance, enum ast_rtp_extension extension)
+{
+       switch (extension) {
+       case AST_RTP_EXTENSION_ABS_SEND_TIME:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/*! \pre child is locked */
+static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent)
+{
+       struct ast_rtp *child_rtp = ast_rtp_instance_get_data(child);
+       struct ast_rtp *parent_rtp;
+       struct rtp_ssrc_mapping mapping;
+       struct ast_sockaddr them = { { 0, } };
+
+       if (child_rtp->bundled == parent) {
+               return 0;
+       }
+
+       /* If this instance was already bundled then remove the SSRC mapping */
+       if (child_rtp->bundled) {
+               struct ast_rtp *bundled_rtp;
+
+               ao2_unlock(child);
+
+               /* The child lock can't be held while accessing the parent */
+               ao2_lock(child_rtp->bundled);
+               bundled_rtp = ast_rtp_instance_get_data(child_rtp->bundled);
+               AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, child, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP);
+               ao2_unlock(child_rtp->bundled);
+
+               ao2_lock(child);
+               ao2_ref(child_rtp->bundled, -1);
+               child_rtp->bundled = NULL;
+       }
+
+       if (!parent) {
+               /* We transitioned away from bundle so we need our own transport resources once again */
+               rtp_allocate_transport(child, child_rtp);
+               return 0;
+       }
+
+       parent_rtp = ast_rtp_instance_get_data(parent);
+
+       /* We no longer need any transport related resources as we will use our parent RTP instance instead */
+       rtp_deallocate_transport(child, child_rtp);
+
+       /* Children maintain a reference to the parent to guarantee that the transport doesn't go away on them */
+       child_rtp->bundled = ao2_bump(parent);
+
+       mapping.ssrc = child_rtp->themssrc;
+       mapping.ssrc_valid = child_rtp->themssrc_valid;
+       mapping.instance = child;
+
+       ao2_unlock(child);
+
+       ao2_lock(parent);
+
+       AST_VECTOR_APPEND(&parent_rtp->ssrc_mapping, mapping);
+
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+       /* If DTLS-SRTP is already in use then add the local SSRC to it, otherwise it will get added once DTLS
+        * negotiation has been completed.
+        */
+       if (parent_rtp->dtls.connection == AST_RTP_DTLS_CONNECTION_EXISTING) {
+               dtls_srtp_add_local_ssrc(parent_rtp, parent, 0, child_rtp->ssrc, 0);
        }
+#endif
+
+       /* Bundle requires that RTCP-MUX be in use so only the main remote address needs to match */
+       ast_rtp_instance_get_remote_address(parent, &them);
+
+       ao2_unlock(parent);
+
+       ao2_lock(child);
+
+       ast_rtp_instance_set_remote_address(child, &them);
+
+       return 0;
+}
+
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+/*! \pre instance is locked */
+static int ast_rtp_activate(struct ast_rtp_instance *instance)
+{
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
        /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */
 #ifdef HAVE_PJPROJECT
@@ -5314,9 +7839,11 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance)
        }
 #endif
 
+       dtls_perform_setup(&rtp->dtls);
        dtls_perform_handshake(instance, &rtp->dtls, 0);
 
-       if (rtp->rtcp) {
+       if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+               dtls_perform_setup(&rtp->rtcp->dtls);
                dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
        }
 
@@ -5457,6 +7984,66 @@ static struct ast_cli_entry cli_rtp[] = {
        AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"),
 };
 
+#ifdef HAVE_PJPROJECT
+/*!
+ * \internal
+ * \brief Clear the configured blacklist.
+ * \since 13.16.0
+ *
+ * \param lock R/W lock protecting the blacklist
+ * \param blacklist List to clear
+ *
+ * \return Nothing
+ */
+static void blacklist_clear(ast_rwlock_t *lock, struct ast_ha **blacklist)
+{
+       ast_rwlock_wrlock(lock);
+       ast_free_ha(*blacklist);
+       *blacklist = NULL;
+       ast_rwlock_unlock(lock);
+}
+
+/*!
+ * \internal
+ * \brief Load the blacklist configuration.
+ * \since 13.16.0
+ *
+ * \param cfg Raw config file options.
+ * \param option_name Blacklist option name
+ * \param lock R/W lock protecting the blacklist
+ * \param blacklist List to load
+ *
+ * \return Nothing
+ */
+static void blacklist_config_load(struct ast_config *cfg, const char *option_name,
+       ast_rwlock_t *lock, struct ast_ha **blacklist)
+{
+       struct ast_variable *var;
+
+       ast_rwlock_wrlock(lock);
+       for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+               if (!strcasecmp(var->name, option_name)) {
+                       struct ast_ha *na;
+                       int ha_error = 0;
+
+                       na = ast_append_ha("d", var->value, *blacklist, &ha_error);
+                       if (!na) {
+                               ast_log(LOG_WARNING, "Invalid %s value: %s\n",
+                                       option_name, var->value);
+                       } else {
+                               *blacklist = na;
+                       }
+                       if (ha_error) {
+                               ast_log(LOG_ERROR,
+                                       "Bad %s configuration value line %d: %s\n",
+                                       option_name, var->lineno, var->value);
+                       }
+               }
+       }
+       ast_rwlock_unlock(lock);
+}
+#endif
+
 static int rtp_reload(int reload)
 {
        struct ast_config *cfg;
@@ -5469,15 +8056,21 @@ static int rtp_reload(int reload)
 #endif
 
        cfg = ast_config_load2("rtp.conf", "rtp", config_flags);
-       if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+       if (!cfg || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
                return 0;
        }
 
+#ifdef SO_NO_CHECK
+       nochecksums = 0;
+#endif
+
        rtpstart = DEFAULT_RTP_START;
        rtpend = DEFAULT_RTP_END;
+       rtcpinterval = RTCP_DEFAULT_INTERVALMS;
        dtmftimeout = DEFAULT_DTMF_TIMEOUT;
        strictrtp = DEFAULT_STRICT_RTP;
        learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL;
+       learning_min_duration = DEFAULT_LEARNING_MIN_DURATION;
 
        /** This resource is not "reloaded" so much as unloaded and loaded again.
         * In the case of the TURN related variables, the memory referenced by a
@@ -5495,141 +8088,134 @@ static int rtp_reload(int reload)
        turnusername = pj_str(NULL);
        turnpassword = pj_str(NULL);
        host_candidate_overrides_clear();
-       ast_rwlock_wrlock(&ice_blacklist_lock);
-       ast_free_ha(ice_blacklist);
-       ice_blacklist = NULL;
-       ast_rwlock_unlock(&ice_blacklist_lock);
+       blacklist_clear(&ice_blacklist_lock, &ice_blacklist);
+       blacklist_clear(&stun_blacklist_lock, &stun_blacklist);
 #endif
 
-       if (cfg) {
-               if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
-                       rtpstart = atoi(s);
-                       if (rtpstart < MINIMUM_RTP_PORT)
-                               rtpstart = MINIMUM_RTP_PORT;
-                       if (rtpstart > MAXIMUM_RTP_PORT)
-                               rtpstart = MAXIMUM_RTP_PORT;
-               }
-               if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
-                       rtpend = atoi(s);
-                       if (rtpend < MINIMUM_RTP_PORT)
-                               rtpend = MINIMUM_RTP_PORT;
-                       if (rtpend > MAXIMUM_RTP_PORT)
-                               rtpend = MAXIMUM_RTP_PORT;
-               }
-               if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
-                       rtcpinterval = atoi(s);
-                       if (rtcpinterval == 0)
-                               rtcpinterval = 0; /* Just so we're clear... it's zero */
-                       if (rtcpinterval < RTCP_MIN_INTERVALMS)
-                               rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
-                       if (rtcpinterval > RTCP_MAX_INTERVALMS)
-                               rtcpinterval = RTCP_MAX_INTERVALMS;
-               }
-               if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
+       if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
+               rtpstart = atoi(s);
+               if (rtpstart < MINIMUM_RTP_PORT)
+                       rtpstart = MINIMUM_RTP_PORT;
+               if (rtpstart > MAXIMUM_RTP_PORT)
+                       rtpstart = MAXIMUM_RTP_PORT;
+       }
+       if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
+               rtpend = atoi(s);
+               if (rtpend < MINIMUM_RTP_PORT)
+                       rtpend = MINIMUM_RTP_PORT;
+               if (rtpend > MAXIMUM_RTP_PORT)
+                       rtpend = MAXIMUM_RTP_PORT;
+       }
+       if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
+               rtcpinterval = atoi(s);
+               if (rtcpinterval == 0)
+                       rtcpinterval = 0; /* Just so we're clear... it's zero */
+               if (rtcpinterval < RTCP_MIN_INTERVALMS)
+                       rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
+               if (rtcpinterval > RTCP_MAX_INTERVALMS)
+                       rtcpinterval = RTCP_MAX_INTERVALMS;
+       }
+       if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
 #ifdef SO_NO_CHECK
-                       nochecksums = ast_false(s) ? 1 : 0;
+               nochecksums =&nbs