res/res_rtp_asterisk: smoother can cause wrong timestamps if dtmf happen
[asterisk/asterisk.git] / res / res_rtp_asterisk.c
index 01dfe76..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;
@@ -138,6 +195,7 @@ 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 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;
@@ -208,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
@@ -216,11 +275,16 @@ 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 {
        SSL *ssl;         /*!< SSL session */
        BIO *read_bio;    /*!< Memory buffer for reading */
@@ -238,6 +302,16 @@ struct ice_wrap {
 };
 #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;
@@ -245,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? */
@@ -267,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 */
@@ -289,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 */
@@ -309,10 +381,12 @@ 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;
 
+       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
        ast_cond_t cond;            /*!< ICE/TURN condition for signaling */
 
@@ -343,7 +417,7 @@ struct ast_rtp {
        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 */
@@ -420,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
 
@@ -449,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 */
@@ -477,8 +557,12 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos,
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 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);
 
-#ifdef HAVE_OPENSSL_SRTP
+#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);
@@ -590,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 */
@@ -680,6 +767,37 @@ static void ice_wrap_dtor(void *vdoomed)
        }
 }
 
+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)
 {
@@ -696,8 +814,9 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
        res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
        if (!res) {
                /* Use the current expected role for the ICE session */
-               pj_ice_sess_change_role(rtp->ice->real_ice, rtp->role == AST_RTP_ICE_ROLE_CONTROLLED ?
-                       PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+               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,
@@ -769,7 +888,7 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *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 */
-               rtp->role = rtp->ice->real_ice->role;
+               pj2ast_rtp_ice_role(rtp->ice->real_ice->role, &rtp->role);
                return;
        }
 
@@ -944,6 +1063,15 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
        }
 
        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->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" );
+       }
 }
 
 /*! \pre instance is locked */
@@ -966,8 +1094,12 @@ static void ast_rtp_ice_add_cand(struct ast_rtp_instance *instance, struct ast_r
 
        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))) {
@@ -1293,6 +1425,8 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
        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)) {
@@ -1355,11 +1489,20 @@ 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);
 
+       /* 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, NULL, instance, turn_sock);
+               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);
@@ -1445,7 +1588,7 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
 };
 #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 */
@@ -1514,46 +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);
 }
 
-/*! \pre instance is locked */
-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;
@@ -1565,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
@@ -1581,8 +1713,257 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
                }
                EC_KEY_free(ecdh);
        }
+#endif /* !OPENSSL_NO_ECDH */
+}
+
+#if !defined(OPENSSL_NO_ECDH) && (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+
+static int create_ephemeral_ec_keypair(EVP_PKEY **keypair)
+{
+       EC_KEY *eckey = NULL;
+       EC_GROUP *group = NULL;
+
+       group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+       if (!group) {
+               goto error;
+       }
+
+       EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+       EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
+
+       eckey = EC_KEY_new();
+       if (!eckey) {
+               goto error;
+       }
+
+       if (!EC_KEY_set_group(eckey, group)) {
+               goto error;
+       }
+
+       if (!EC_KEY_generate_key(eckey)) {
+               goto error;
+       }
+
+       *keypair = EVP_PKEY_new();
+       if (!*keypair) {
+               goto error;
+       }
+
+       EVP_PKEY_assign_EC_KEY(*keypair, eckey);
+       EC_GROUP_free(group);
+
+       return 0;
+
+error:
+       EC_KEY_free(eckey);
+       EC_GROUP_free(group);
+
+       return -1;
+}
+
+/* From OpenSSL's x509 command */
+#define SERIAL_RAND_BITS 159
+
+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 (!X509_set_version(cert, 2)) {
+               goto error;
+       }
+
+       /* Set the public key */
+       X509_set_pubkey(cert, keypair);
+
+       /* 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;
+       }
+
+       /*
+        * 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);
 
-#endif /* #ifdef HAVE_OPENSSL_EC */
+       configure_dhparams(rtp, dtls_cfg);
 
        rtp->dtls_verify = dtls_cfg->verify;
 
@@ -1601,25 +1982,22 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 
        rtp->local_hash = dtls_cfg->hash;
 
-       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;
+       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_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);
+               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_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);
+               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;
                }
 
@@ -1633,22 +2011,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
                        return -1;
                }
 
-               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;
-               }
-
-               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);
-                       }
+               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;
                }
 
@@ -1657,10 +2022,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
                        local_fingerprint += 3;
                }
 
-               *(local_fingerprint-1) = 0;
+               *(local_fingerprint - 1) = 0;
 
-               BIO_free_all(certbio);
-               X509_free(cert);
+               EVP_PKEY_free(cert_info.private_key);
+               X509_free(cert_info.certificate);
        }
 
        if (!ast_strlen_zero(dtls_cfg->cipher)) {
@@ -1901,15 +2266,19 @@ 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)
 {
@@ -1943,20 +2312,37 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl
 }
 #endif
 
-#ifdef HAVE_PJPROJECT
-static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
-
-/* PJPROJECT ICE callback */
-static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
+static void dtls_perform_setup(struct dtls_details *dtls)
 {
-       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;
+       if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {
+               return;
+       }
 
-               ast_sockaddr_setnull(&remote_address);
+       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_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;
+
+               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 */
@@ -1970,10 +2356,13 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
                }
        }
 
-#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 && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+               dtls_perform_setup(&rtp->rtcp->dtls);
                dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
        }
 #endif
@@ -1983,8 +2372,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
                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);
 }
 
@@ -2101,7 +2490,7 @@ 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)
 {
@@ -2241,59 +2630,14 @@ static int dtls_srtp_renegotiate(const void *data)
        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",
@@ -2328,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:
@@ -2375,8 +2709,85 @@ 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;
@@ -2415,8 +2826,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 {
        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
@@ -2425,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)) {
@@ -2477,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);
@@ -2543,11 +2959,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
        }
 #endif
 
-       if ((*in & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
-                   srtp, buf, &len, rtcp || rtcp_mux(rtp, buf)) < 0) {
-          return -1;
-       }
-
        return len;
 }
 
@@ -2569,7 +2980,9 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
        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;
 
        *via_ice = 0;
@@ -2579,20 +2992,29 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
        }
 
 #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();
 
                /* Release the instance lock to avoid deadlock with PJPROJECT group lock */
-               ice = rtp->ice;
+               ice = transport_rtp->ice;
                ao2_ref(ice, +1);
-               ao2_unlock(instance);
-               status = pj_ice_sess_send_data(ice->real_ice,
-                       rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len);
+               if (instance == transport) {
+                       ao2_unlock(instance);
+               }
+               status = pj_ice_sess_send_data(ice->real_ice, component, temp, len);
                ao2_ref(ice, -1);
-               ao2_lock(instance);
+               if (instance == transport) {
+                       ao2_lock(instance);
+               }
                if (status == PJ_SUCCESS) {
                        *via_ice = 1;
                        return len;
@@ -2600,7 +3022,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
        }
 #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));
        }
@@ -2678,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;
 }
@@ -2708,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));
 }
 
 /*!
@@ -2724,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
@@ -2825,8 +3289,8 @@ 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 && count && ast_sockaddr_is_ipv4(addr)
-               && !stun_address_is_blacklisted(addr)) {
+       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;
 
@@ -2840,27 +3304,40 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
                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(instance, 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));
+                               }
                        }
                }
        }
@@ -2990,35 +3467,18 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
 }
 #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)
+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;
-       }
-
-       /* 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;
        }
 
@@ -3028,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;
                }
 
@@ -3045,7 +3506,7 @@ 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;
                }
        }
@@ -3056,78 +3517,52 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 
        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) {
                rtp->ice_num_components = 2;
-               ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
-               if (ice_create(instance, addr, x, 0)) {
+               ast_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;
 }
 
-/*! \pre instance is locked */
-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) {
-               ao2_unlock(instance);
-               AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
-               ao2_lock(instance);
-               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
@@ -3148,6 +3583,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
                while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
                        ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
                }
+               rtp->turn_rtp = NULL;
        }
 
        /* Destroy the RTCP TURN relay if being used */
@@ -3161,6 +3597,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
                while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
                        ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);
                }
+               rtp->turn_rtcp = NULL;
        }
 
        /* Destroy any ICE session */
@@ -3169,10 +3606,12 @@ static int ast_rtp_destroy(struct ast_rtp_instance *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;
        }
 
        if (rtp->ioqueue) {
@@ -3184,17 +3623,124 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
                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);
-
-#ifdef HAVE_PJPROJECT
-       /* Destroy synchronization items */
-       ast_cond_destroy(&rtp->cond);
-#endif
+       AST_VECTOR_FREE(&rtp->ssrc_mapping);
+       AST_VECTOR_FREE(&rtp->missing_seqno);
 
        /* Finally destroy ourselves */
        ast_free(rtp);
@@ -3411,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;
@@ -3444,14 +3996,11 @@ 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) {
@@ -3464,6 +4013,9 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance)
 
        rtp->ssrc = ssrc;
 
+       /* Since the source is changing, we don't know what sequence number to expect next */
+       rtp->expectedrxseqno = -1;
+
        return;
 }
 
@@ -3559,39 +4111,24 @@ static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
        rtp->rtcp->rxlost_count++;
 }
 
-/*!
- * \brief Send RTCP SR or RR report
- *
- * \pre instance is locked
- */
-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;
        }
@@ -3600,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;
@@ -3625,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)) {
@@ -3634,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++;
@@ -3702,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));
                }
@@ -3712,9 +4256,121 @@ 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;
 }
 
 /*!
@@ -3730,6 +4386,15 @@ 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);
@@ -3737,13 +4402,44 @@ static int ast_rtcp_write(const void *data)
        }
 
        ao2_lock(instance);
-       if (rtp->txcount > rtp->rtcp->lastsrtxcount) {
-               /* Send an SR */
-               res = ast_rtcp_write_report(instance, 1);
+       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) {
@@ -3757,6 +4453,20 @@ static int ast_rtcp_write(const void *data)
        return res;
 }
 
+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)
 {
@@ -3765,6 +4475,7 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
        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;
@@ -3831,6 +4542,40 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
                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;
        }
@@ -3839,14 +4584,60 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
 
        /* 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,
@@ -3879,7 +4670,13 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
                }
        }
 
-       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;
 }
@@ -3924,15 +4721,103 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red)
        return &red->t140red;
 }
 
-/*! \pre instance is locked */
-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;
+       unsigned int *rtcpheader;
+       char bdata[1024];
+       int len = 20;
+       int ice;
+       int res;
 
-       ast_rtp_instance_get_remote_address(instance, &remote_address);
+       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)) {
@@ -3942,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) || rtp->rtcp->schedid < 0) {
-                       /*
-                        * 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;
        }
@@ -4013,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);
@@ -4167,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,} };
@@ -4302,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;
@@ -4382,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);
 
@@ -4537,67 +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_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
+/*! \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)
+{
+       int index;
+
+       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;
+               }
+       }
+
+       /* Does the SSRC match the bundled parent? */
+       if (rtp->themssrc_valid && rtp->themssrc == ssrc) {
+               return instance;
+       }
+       return NULL;
+}
+
+/*! \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);
+}
+
+/*! \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, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
+               }
+               /*
+                * 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 res;
+}
+
+/*
+ * 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);
-       int packetwords, position = 0;
-       int report_counter = 0;
+       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;
 
-       packetwords = size / 4;
+       /* 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;
+       }
 
-       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));
-                       }
+       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 %zu bytes\n", size);
+       /*
+        * 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\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);
+                       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 (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);
@@ -4621,7 +5876,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                                                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) {
@@ -4632,9 +5887,12 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                                /* 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);
@@ -4671,16 +5929,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                                        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);
@@ -4688,70 +5945,129 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
 
                        /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
                         * object as a its data */
-                       rtp->f.frametype = AST_FRAME_RTCP;
-                       rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
-                       memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
-                       rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
+                       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 = rtp->f.data.ptr + rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *);
-                               memcpy(report_block, rtcp_report->report_block[report_counter-1], sizeof(struct ast_rtp_rtcp_report_block));
-                               rtcp_report2 = (struct ast_rtp_rtcp_report *)rtp->f.data.ptr;
-                               rtcp_report2->report_block[report_counter-1] = report_block;
-                               rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block);
+                               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;
                        }
-                       rtp->f.offset = AST_FRIENDLY_OFFSET;
-                       rtp->f.samples = 0;
-                       rtp->f.mallocd = 0;
-                       rtp->f.delivery.tv_sec = 0;
-                       rtp->f.delivery.tv_usec = 0;
-                       rtp->f.src = "RTP";
-                       f = &rtp->f;
                        break;
                case RTCP_PT_FUR:
-               /* Handle RTCP FIR as 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)) {
                                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)) {
                                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;
-
 }
 
 /*! \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;
@@ -4761,8 +6077,14 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
        /* Read in RTCP data from the socket */
        if ((res = rtcp_recvfrom(instance, read_area, read_area_size,
                                0, &addr)) < 0) {
-               ast_assert(errno != EBADF);
-               if (errno != EAGAIN) {
+               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;
@@ -4797,7 +6119,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                return &ast_null_frame;
        }
 
-       return ast_rtcp_interpret(instance, read_area, res, &addr);
+       return ast_rtcp_interpret(instance, srtp, read_area, res, &addr);
 }
 
 /*! \pre instance is locked */
@@ -4805,7 +6127,7 @@ 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 *rtp = ast_rtp_instance_get_data(instance);
-       struct ast_rtp *bridged = ast_rtp_instance_get_data(instance1);
+       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]);
@@ -4815,7 +6137,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
 
        /* 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);
@@ -4832,21 +6154,12 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
                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
@@ -4857,18 +6170,10 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
                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
@@ -4884,6 +6189,40 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *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)) {
@@ -4893,6 +6232,24 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *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) {
@@ -4902,7 +6259,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
                                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 "
@@ -4928,19 +6285,308 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
        return 0;
 }
 
+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_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 frame_list frames;
+
+       /* 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;
+       }
+
+       /* If we are currently sending DTMF to the remote party send a continuation packet */
+       if (rtp->sending_digit) {
+               ast_rtp_dtmf_continuation(instance);
+       }
+
+       /* 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);
+       ext = seqno & (1 << 28);
+       cc = (seqno & 0xF000000) >> 24;
+       seqno &= 0xffff;
+       timestamp = ntohl(rtpheader[1]);
+
+       AST_LIST_HEAD_INIT_NOLOCK(&frames);
+
+       /* Remove any padding bytes that may be present */
+       if (padding) {
+               res -= read_area[res - 1];
+       }
+
+       /* Skip over any CSRC fields */
+       if (cc) {
+               hdrlen += cc * 4;
+       }
+
+       /* Look for any RTP extensions, currently we do not support any */
+       if (ext) {
+               hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
+               hdrlen += 4;
+               if (DEBUG_ATLEAST(1)) {
+                       unsigned int profile;
+                       profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
+                       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);
+                       }
+               }
+       }
+
+       /* Make sure after we potentially mucked with the header length that it is once again valid */
+       if (res < hdrlen) {
+               ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d\n", res, hdrlen);
+               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+       }
+
+       rtp->rxcount++;
+       rtp->rxoctetcount += (res - hdrlen);
+       if (rtp->rxcount == 1) {
+               rtp->seedrxseqno = seqno;
+       }
+
+       /* Do not schedule RR if RTCP isn't run */
+       if (rtp->rtcp && !ast_sockaddr_isnull(&rtp->rtcp->them) && rtp->rtcp->schedid < 0) {
+               /* Schedule transmission of Receiver Report */
+               ao2_ref(instance, +1);
+               rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
+               if (rtp->rtcp->schedid < 0) {
+                       ao2_ref(instance, -1);
+                       ast_log(LOG_WARNING, "scheduling RTCP transmission failed.\n");
+               }
+       }
+       if ((int)rtp->lastrxseqno - (int)seqno  > 100) /* if so it would indicate that the sender cycled; allow for misordering */
+               rtp->cycles += RTP_SEQ_MOD;
+
+       /* 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;
+
+               /* Update statistics for jitter so they are correct in RTCP */
+               calc_rxstamp(&rxtime, rtp, timestamp, mark);
+
+               /* 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);
+       if (!payload) {
+               /* Unknown payload type. */
+               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+       }
+
+       /* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */
+       if (!payload->asterisk_format) {
+               struct ast_frame *f = NULL;
+               if (payload->rtp_code == AST_RTP_DTMF) {
+                       /* process_dtmf_rfc2833 may need to return multiple frames. We do this
+                        * 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, read_area + hdrlen, res - hdrlen, seqno, timestamp, payloadtype, mark, &frames);
+               } else if (payload->rtp_code == AST_RTP_CISCO_DTMF) {
+                       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, 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));
+               }
+
+               if (f) {
+                       AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+               }
+               /* Even if no frame was returned by one of the above methods,
+                * we may have a frame to return in our frame list
+                */
+               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+       }
+
+       ao2_replace(rtp->lastrxformat, payload->format);
+       ao2_replace(rtp->f.subclass.format, payload->format);
+       switch (ast_format_get_type(rtp->f.subclass.format)) {
+       case AST_MEDIA_TYPE_AUDIO:
+               rtp->f.frametype = AST_FRAME_VOICE;
+               break;
+       case AST_MEDIA_TYPE_VIDEO:
+               rtp->f.frametype = AST_FRAME_VIDEO;
+               break;
+       case AST_MEDIA_TYPE_TEXT:
+               rtp->f.frametype = AST_FRAME_TEXT;
+               break;
+       case AST_MEDIA_TYPE_IMAGE:
+               /* Fall through */
+       default:
+               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;
+       }
+
+       if (rtp->dtmf_timeout && rtp->dtmf_timeout < timestamp) {
+               rtp->dtmf_timeout = 0;
+
+               if (rtp->resp) {
+                       struct ast_frame *f;
+                       f = create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0);
+                       f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, rtp_get_rate(f->subclass.format)), ast_tv(0, 0));
+                       rtp->resp = 0;
+                       rtp->dtmf_timeout = rtp->dtmf_duration = 0;
+                       AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+                       return AST_LIST_FIRST(&frames);
+               }
+       }
+
+       rtp->f.src = "RTP";
+       rtp->f.mallocd = 0;
+       rtp->f.datalen = res - hdrlen;
+       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)
+               && ((int)seqno - (prev_seqno + 1) < 10)) {
+               unsigned char *data = rtp->f.data.ptr;
+
+               memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen);
+               rtp->f.datalen +=3;
+               *data++ = 0xEF;
+               *data++ = 0xBF;
+               *data = 0xBD;
+       }
+
+       if (ast_format_cmp(rtp->f.subclass.format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) {
+               unsigned char *data = rtp->f.data.ptr;
+               unsigned char *header_end;
+               int num_generations;
+               int header_length;
+               int len;
+               int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/
+               int x;
+
+               ao2_replace(rtp->f.subclass.format, ast_format_t140);
+               header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen);
+               if (header_end == NULL) {
+                       return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+               }
+               header_end++;
+
+               header_length = header_end - data;
+               num_generations = header_length / 4;
+               len = header_length;
+
+               if (!diff) {
+                       for (x = 0; x < num_generations; x++)
+                               len += data[x * 4 + 3];
+
+                       if (!(rtp->f.datalen - len))
+                               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+
+                       rtp->f.data.ptr += len;
+                       rtp->f.datalen -= len;
+               } else if (diff > num_generations && diff < 10) {
+                       len -= 3;
+                       rtp->f.data.ptr += len;
+                       rtp->f.datalen -= len;
+
+                       data = rtp->f.data.ptr;
+                       *data++ = 0xEF;
+                       *data++ = 0xBF;
+                       *data = 0xBD;
+               } else {
+                       for ( x = 0; x < num_generations - diff; x++)
+                               len += data[x * 4 + 3];
+
+                       rtp->f.data.ptr += len;
+                       rtp->f.datalen -= len;
+               }
+       }
+
+       if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_AUDIO) {
+               rtp->f.samples = ast_codec_samples_count(&rtp->f);
+               if (ast_format_cache_is_slinear(rtp->f.subclass.format)) {
+                       ast_frame_byteswap_be(&rtp->f);
+               }
+               calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
+               /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
+               ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
+               rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000);
+               rtp->f.len = rtp->f.samples / ((ast_format_get_sample_rate(rtp->f.subclass.format) / 1000));
+       } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_VIDEO) {
+               /* Video -- samples is # of samples vs. 90000 */
+               if (!rtp->lastividtimestamp)
+                       rtp->lastividtimestamp = timestamp;
+               ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
+               rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000);
+               rtp->f.samples = timestamp - rtp->lastividtimestamp;
+               rtp->lastividtimestamp = timestamp;
+               rtp->f.delivery.tv_sec = 0;
+               rtp->f.delivery.tv_usec = 0;
+               /* Pass the RTP marker bit as bit */
+               rtp->f.subclass.frame_ending = mark ? 1 : 0;
+       } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_TEXT) {
+               /* TEXT -- samples is # of samples vs. 1000 */
+               if (!rtp->lastitexttimestamp)
+                       rtp->lastitexttimestamp = timestamp;
+               rtp->f.samples = timestamp - rtp->lastitexttimestamp;
+               rtp->lastitexttimestamp = timestamp;
+               rtp->f.delivery.tv_sec = 0;
+               rtp->f.delivery.tv_usec = 0;
+       } 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;
+       }
+
+       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_rtp_instance *instance1;
+       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, padding, mark, ext, cc, prev_seqno;
+       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;
-       RAII_VAR(struct ast_rtp_payload_type *, payload, NULL, ao2_cleanup);
+       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) {
@@ -4950,14 +6596,15 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                return &ast_null_frame;
        }
 
-       /* If we are currently sending DTMF to the remote party send a continuation packet */
-       if (rtp->sending_digit) {
-               ast_rtp_dtmf_continuation(instance);
-       }
-
        /* 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",
@@ -4974,7 +6621,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 
        /* This could be a multiplexed RTCP packet. If so, be sure to interpret it correctly */
        if (rtcp_mux(rtp, read_area)) {
-               return ast_rtcp_interpret(instance, read_area, res, &addr);
+               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 */
@@ -5017,40 +6664,185 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                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;
-               }
+       /* 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);
 
-               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;
+       /* 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 (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);
+       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 {
-                       /* 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.
+                       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.
                         */
-                       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_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;
                        }
-                       ast_verb(4, "%p -- Switching RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
-                       ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+
+                       /*
+                        * 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)) {
@@ -5062,7 +6854,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                                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",
@@ -5070,298 +6861,314 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
                }
        }
 
-       /* If we are directly bridged to another instance send the audio directly out */
-       instance1 = ast_rtp_instance_get_bridged(instance);
-       if (instance1
-               && !bridge_p2p_rtp_write(instance, instance1, 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 */
        payloadtype = (seqno & 0x7f0000) >> 16;
-       padding = seqno & (1 << 29);
        mark = seqno & (1 << 23);
-       ext = seqno & (1 << 28);
-       cc = (seqno & 0xF000000) >> 24;
        seqno &= 0xffff;
        timestamp = ntohl(rtpheader[1]);
-       ssrc = ntohl(rtpheader[2]);
+
+       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);
-       /* 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");
+       /* 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;
                        }
-                       mark = 1;
-               }
 
-               f = ast_frisolate(&srcupdate);
-               AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+                       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->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;
        }
 
-       rtp->rxssrc = ssrc;
+       prev_seqno = rtp->lastrxseqno;
+       rtp->lastrxseqno = seqno;
 
-       /* Remove any padding bytes that may be present */
-       if (padding) {
-               res -= read_area[res - 1];
-       }
+       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;
 
-       /* Skip over any CSRC fields */
-       if (cc) {
-               hdrlen += cc * 4;
-       }
+               /* 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);
+               }
 
-       /* Look for any RTP extensions, currently we do not support any */
-       if (ext) {
-               hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
-               hdrlen += 4;
-               if (option_debug) {
-                       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 (!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);
                }
-       }
 
-       /* Make sure after we potentially mucked with the header length that it is once again valid */
-       if (res < hdrlen) {
-               ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d\n", res, hdrlen);
-               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
-       }
+               /* 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);
+               }
 
-       rtp->rxcount++;
-       rtp->rxoctetcount += (res - hdrlen);
-       if (rtp->rxcount == 1) {
-               rtp->seedrxseqno = 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);
+               }
 
-       /* Do not schedule RR if RTCP isn't run */
-       if (rtp->rtcp && !ast_sockaddr_isnull(&rtp->rtcp->them) && rtp->rtcp->schedid < 0) {
-               /* Schedule transmission of Receiver Report */
-               ao2_ref(instance, +1);
-               rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
-               if (rtp->rtcp->schedid < 0) {
-                       ao2_ref(instance, -1);
-                       ast_log(LOG_WARNING, "scheduling RTCP transmission failed.\n");
+               /* 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++;
                }
-       }
-       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;
+               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;
 
-       if (!rtp->themssrc) {
-               rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
-       }
+               /* 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_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);
-       }
+               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);
+               }
 
-       payload = ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payloadtype);
-       if (!payload) {
-               /* Unknown payload type. */
-               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
-       }
+               while (ast_data_buffer_count(rtp->recv_buffer)) {
+                       struct ast_rtp_rtcp_nack_payload *payload;
 
-       /* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */
-       if (!payload->asterisk_format) {
-               struct ast_frame *f = NULL;
-               if (payload->rtp_code == AST_RTP_DTMF) {
-                       /* process_dtmf_rfc2833 may need to return multiple frames. We do this
-                        * 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, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
-               } else if (payload->rtp_code == AST_RTP_CISCO_DTMF) {
-                       f = process_dtmf_cisco(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
-               } else if (payload->rtp_code == AST_RTP_CN) {
-                       f = process_cn_rfc3389(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
-               } else {
-                       ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n",
-                               payloadtype,
-                               ast_sockaddr_stringify(&remote_address));
+                       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 (f) {
-                       AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+               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);
                }
-               /* Even if no frame was returned by one of the above methods,
-                * we may have a frame to return in our frame list
+
+               /* 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.
                 */
-               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
-       }
+               AST_VECTOR_RESET(&rtp->missing_seqno, AST_VECTOR_ELEM_CLEANUP_NOOP);
 
-       ao2_replace(rtp->lastrxformat, payload->format);
-       ao2_replace(rtp->f.subclass.format, payload->format);
-       switch (ast_format_get_type(rtp->f.subclass.format)) {
-       case AST_MEDIA_TYPE_AUDIO:
-               rtp->f.frametype = AST_FRAME_VOICE;
-               break;
-       case AST_MEDIA_TYPE_VIDEO:
-               rtp->f.frametype = AST_FRAME_VIDEO;
-               break;
-       case AST_MEDIA_TYPE_TEXT:
-               rtp->f.frametype = AST_FRAME_TEXT;
-               break;
-       case AST_MEDIA_TYPE_IMAGE:
-               /* Fall through */
-       default:
-               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_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;
-       }
-       rtp->rxseqno = seqno;
-
-       if (rtp->dtmf_timeout && rtp->dtmf_timeout < timestamp) {
-               rtp->dtmf_timeout = 0;
-
-               if (rtp->resp) {
-                       struct ast_frame *f;
-                       f = create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0);
-                       f->len = ast_tvdiff_ms(ast_samp2tv(rtp->dtmf_duration, rtp_get_rate(f->subclass.format)), ast_tv(0, 0));
-                       rtp->resp = 0;
-                       rtp->dtmf_timeout = rtp->dtmf_duration = 0;
-                       AST_LIST_INSERT_TAIL(&frames, f, frame_list);
-                       return AST_LIST_FIRST(&frames);
+       } 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;
                }
-       }
 
-       rtp->lastrxts = timestamp;
+               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;
+                       }
 
-       rtp->f.src = "RTP";
-       rtp->f.mallocd = 0;
-       rtp->f.datalen = res - hdrlen;
-       rtp->f.data.ptr = read_area + hdrlen;
-       rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
-       rtp->f.seqno = seqno;
+                       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--;
+               }
 
-       if ((ast_format_cmp(rtp->f.subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL)
-               && ((int)seqno - (prev_seqno + 1) > 0)
-               && ((int)seqno - (prev_seqno + 1) < 10)) {
-               unsigned char *data = rtp->f.data.ptr;
+               /* 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;
+                       }
 
-               memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen);
-               rtp->f.datalen +=3;
-               *data++ = 0xEF;
-               *data++ = 0xBF;
-               *data = 0xBD;
-       }
+                       memset(rtcpheader, 0, data_size);
 
-       if (ast_format_cmp(rtp->f.subclass.format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) {
-               unsigned char *data = rtp->f.data.ptr;
-               unsigned char *header_end;
-               int num_generations;
-               int header_length;
-               int len;
-               int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/
-               int x;
+                       res = ast_rtcp_generate_report(instance, rtcpheader, rtcp_report, &sr);
 
-               ao2_replace(rtp->f.subclass.format, ast_format_t140);
-               header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen);
-               if (header_end == NULL) {
-                       return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
-               }
-               header_end++;
+                       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;
+                       }
 
-               header_length = header_end - data;
-               num_generations = header_length / 4;
-               len = header_length;
+                       packet_len += res;
 
-               if (!diff) {
-                       for (x = 0; x < num_generations; x++)
-                               len += data[x * 4 + 3];
+                       res = ast_rtcp_generate_nack(instance, rtcpheader + packet_len);
 
-                       if (!(rtp->f.datalen - len))
-                               return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+                       if (res == 0) {
+                               ast_debug(1, "Failed to construct NACK, stopping here\n");
+                               return &ast_null_frame;
+                       }
 
-                       rtp->f.data.ptr += len;
-                       rtp->f.datalen -= len;
-               } else if (diff > num_generations && diff < 10) {
-                       len -= 3;
-                       rtp->f.data.ptr += len;
-                       rtp->f.datalen -= len;
+                       packet_len += res;
 
-                       data = rtp->f.data.ptr;
-                       *data++ = 0xEF;
-                       *data++ = 0xBF;
-                       *data = 0xBD;
-               } else {
-                       for ( x = 0; x < num_generations - diff; x++)
-                               len += data[x * 4 + 3];
+                       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);
+                       }
 
-                       rtp->f.data.ptr += len;
-                       rtp->f.datalen -= len;
+                       ast_debug(2, "Sending a NACK request on RTP instance '%p' to get missing packets\n", instance);
                }
-       }
 
-       if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_AUDIO) {
-               rtp->f.samples = ast_codec_samples_count(&rtp->f);
-               if (ast_format_cache_is_slinear(rtp->f.subclass.format)) {
-                       ast_frame_byteswap_be(&rtp->f);
-               }
-               calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
-               /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
-               ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
-               rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000);
-               rtp->f.len = rtp->f.samples / ((ast_format_get_sample_rate(rtp->f.subclass.format) / 1000));
-       } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_VIDEO) {
-               /* Video -- samples is # of samples vs. 90000 */
-               if (!rtp->lastividtimestamp)
-                       rtp->lastividtimestamp = timestamp;
-               ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
-               rtp->f.ts = timestamp / (rtp_get_rate(rtp->f.subclass.format) / 1000);
-               rtp->f.samples = timestamp - rtp->lastividtimestamp;
-               rtp->lastividtimestamp = timestamp;
-               rtp->f.delivery.tv_sec = 0;
-               rtp->f.delivery.tv_usec = 0;
-               /* Pass the RTP marker bit as bit */
-               rtp->f.subclass.frame_ending = mark ? 1 : 0;
-       } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_TEXT) {
-               /* TEXT -- samples is # of samples vs. 1000 */
-               if (!rtp->lastitexttimestamp)
-                       rtp->lastitexttimestamp = timestamp;
-               rtp->f.samples = timestamp - rtp->lastitexttimestamp;
-               rtp->lastitexttimestamp = timestamp;
-               rtp->f.delivery.tv_sec = 0;
-               rtp->f.delivery.tv_usec = 0;
-       } 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);
+       return &ast_null_frame;
 }
 
 /*! \pre instance is locked */
@@ -5384,7 +7191,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                        return;
                                }
                                rtp->rtcp->s = -1;
-#ifdef HAVE_OPENSSL_SRTP
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
                                rtp->rtcp->dtls.timeout_timer = -1;
 #endif
                                rtp->rtcp->schedid = -1;
@@ -5447,7 +7254,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                        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
+#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
                                dtls_setup_rtcp(instance);
 #endif
                        } else {
@@ -5467,7 +7274,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                rtp->rtcp->s = rtp->s;
                                ast_rtp_instance_get_remote_address(instance, &addr);
                                ast_sockaddr_copy(&rtp->rtcp->them, &addr);
-#ifdef HAVE_OPENSSL_SRTP
+#if 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);
                                }
@@ -5495,7 +7302,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
                                        close(rtp->rtcp->s);
                                }
-#ifdef HAVE_OPENSSL_SRTP
+#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);
@@ -5509,6 +7316,13 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
                                rtp->rtcp = NULL;
                        }
                }
+       } 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);
        }
 }
 
@@ -5525,6 +7339,7 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
 {
        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)) {
@@ -5553,11 +7368,25 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
                rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local));
        }
 
-       rtp->rxseqno = 0;
+       /* 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);
+       }
+
+       /* 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);
        }
 }
 
@@ -5574,7 +7403,9 @@ static int red_write(const void *data)
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
        ao2_lock(instance);
-       ast_rtp_write(instance, &rtp->red->t140);
+       if (rtp->red->t140.datalen > 0) {
+               ast_rtp_write(instance, &rtp->red->t140);
+       }
        ao2_unlock(instance);
 
        return 1;
@@ -5617,9 +7448,29 @@ static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int
 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;
@@ -5634,7 +7485,11 @@ static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_r
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance0);
 
        ao2_lock(instance0);
-       ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
+       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;
@@ -5730,7 +7585,7 @@ 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));
 
@@ -5836,43 +7691,147 @@ static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance)
 /*! \pre instance is locked */
 static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance)
 {
-       /* XXX
-        *
-        * Asterisk currently puts a zero-length CNAME value in RTCP SDES items,
-        * meaning our CNAME will always be an empty string. In future, should
-        * Asterisk actually start using meaningful CNAMEs, this function will
-        * need to return that instead of an empty string
-        */
-       return "";
+       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+       return rtp->cname;
 }
 
-#ifdef HAVE_OPENSSL_SRTP
-static void dtls_perform_setup(struct dtls_details *dtls)
+/*! \pre instance is locked */
+static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc)
 {
-       if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {
+       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;
 }
 
-/*! \pre instance is locked */
-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
        if (rtp->ice) {
@@ -5880,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 && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+               dtls_perform_setup(&rtp->rtcp->dtls);
                dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
        }
 
@@ -6099,11 +8060,17 @@ static int rtp_reload(int reload)
                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
@@ -6165,13 +8132,21 @@ static int rtp_reload(int reload)
                };
        }
        if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) {
-               strictrtp = ast_true(s);
+               if (ast_true(s)) {
+                       strictrtp = STRICT_RTP_YES;
+               } else if (!strcasecmp(s, "seqno")) {
+                       strictrtp = STRICT_RTP_SEQNO;
+               } else {
+                       strictrtp = STRICT_RTP_NO;
+               }
        }
        if ((s = ast_variable_retrieve(cfg, "general", "probation"))) {
-               if ((sscanf(s, "%d", &learning_min_sequential) <= 0) || learning_min_sequential <= 0) {
+               if ((sscanf(s, "%d", &learning_min_sequential) != 1) || learning_min_sequential <= 1) {
                        ast_log(LOG_WARNING, "Value for 'probation' could not be read, using default of '%d' instead\n",
                                DEFAULT_LEARNING_MIN_SEQUENTIAL);
+                       learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL;
                }
+               learning_min_duration = CALC_LEARNING_MIN_DURATION(learning_min_sequential);
        }
 #ifdef HAVE_PJPROJECT
        if ((s = ast_variable_retrieve(cfg, "general", "icesupport"))) {
@@ -6267,7 +8242,7 @@ static void rtp_terminate_pjproject(void)
                pj_thread_destroy(timer_thread);
        }
 
-       pj_caching_pool_destroy(&cachingpool);
+       ast_pjproject_caching_pool_destroy(&cachingpool);
        pj_shutdown();
 }
 #endif
@@ -6292,7 +8267,7 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
-       pj_caching_pool_init(&cachingpool, &pj_pool_factory_default_policy, 0);
+       ast_pjproject_caching_pool_init(&cachingpool, &pj_pool_factory_default_policy, 0);
 
        pool = pj_pool_create(&cachingpool.factory, "timer", 512, 512, NULL);
 
@@ -6355,4 +8330,7 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk RTP Stack",
        .unload = unload_module,
        .reload = reload_module,
        .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+#ifdef HAVE_PJPROJECT
+       .requires = "res_pjproject",
+#endif
 );