diff --git a/UPGRADE.txt b/UPGRADE.txt
index 0940bbd267ea0a93b409f8e433ef86b2f867bce6..ce548d083b090517249cd33ffcde286f1ae1c598 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -26,6 +26,23 @@ From 11.6-cert2 to 11.6-cert3:
   * MixMonitorMute - call or system
   * StopMixMonitor - call or system
 
+* Added a 'force_avp' option for chan_sip. When enabled this option will
+  cause the media transport in the offer or answer SDP to be 'RTP/AVP',
+  'RTP/AVPF', 'RTP/SAVP', or 'RTP/SAVPF' even if a DTLS stream has been
+  configured. This option can be set to improve interoperability with WebRTC
+  clients that don't use the RFC defined transport for DTLS.
+
+* The 'dtlsverify' option in chan_sip now has additional values besides
+  'yes' and 'no'. If 'yes' is specified both the certificate and fingerprint
+  will be verified. If 'no' is specified then neither the certificate or
+  fingerprint is verified. If 'certificate' is specified then only the
+  certificate is verified. If 'fingerprint' is specified then only the
+  fingerprint is verified.
+
+* A 'dtlsfingerprint' option has been added to chan_sip which allows the
+  hash to be specified for the DTLS fingerprint placed in SDP. Supported
+  values are 'sha-1' and 'sha-256' with 'sha-256' being the default.
+
 * Added http.conf session_inactivity timer option to close HTTP connections
   that aren't doing anything.
 
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 5a71388d5bb5d5b8e523617c9a9703af48d0ae8f..4c1cb6f694612d3ddeb51b3941f5ade337223b84 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1401,7 +1401,7 @@ static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_c
 static int process_sdp_a_image(const char *a, struct sip_pvt *p);
 static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
 static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
-static void start_ice(struct ast_rtp_instance *instance);
+static void start_ice(struct ast_rtp_instance *instance, int offer);
 static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
 			     struct ast_str **m_buf, struct ast_str **a_buf,
 			     int debug, int *min_packet_size);
@@ -10040,12 +10040,21 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 			if (process_sdp_a_dtls(value, p, p->rtp)) {
 				processed = TRUE;
+				if (p->srtp) {
+					ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+				}
 			}
 			if (process_sdp_a_dtls(value, p, p->vrtp)) {
 				processed = TRUE;
+				if (p->vsrtp) {
+					ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+				}
 			}
 			if (process_sdp_a_dtls(value, p, p->trtp)) {
 				processed = TRUE;
+				if (p->tsrtp) {
+					ast_set_flag(p->tsrtp, SRTP_CRYPTO_OFFER_OK);
+				}
 			}
 
 			break;
@@ -10412,7 +10421,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 					if (process_sdp_a_ice(value, p, p->rtp)) {
 						processed = TRUE;
 					} else if (process_sdp_a_dtls(value, p, p->rtp)) {
+						processed_crypto = TRUE;
 						processed = TRUE;
+						if (p->srtp) {
+							ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+						}
 					} else if (process_sdp_a_sendonly(value, &sendonly)) {
 						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
@@ -10427,7 +10440,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 					if (process_sdp_a_ice(value, p, p->vrtp)) {
 						processed = TRUE;
 					} else if (process_sdp_a_dtls(value, p, p->vrtp)) {
+						processed_crypto = TRUE;
 						processed = TRUE;
+						if (p->vsrtp) {
+							ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+						}
 					} else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
 						processed_crypto = TRUE;
 						processed = TRUE;
@@ -10578,7 +10595,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	/* Setup audio address and port */
 	if (p->rtp) {
 		if (sa && portno > 0) {
-			start_ice(p->rtp);
+			start_ice(p->rtp, (req->method != SIP_RESPONSE) ? 0 : 1);
 			ast_sockaddr_set_port(sa, portno);
 			ast_rtp_instance_set_remote_address(p->rtp, sa);
 			if (debug) {
@@ -10626,7 +10643,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	/* Setup video address and port */
 	if (p->vrtp) {
 		if (vsa && vportno > 0) {
-			start_ice(p->vrtp);
+			start_ice(p->vrtp, (req->method != SIP_RESPONSE) ? 0 : 1);
 			ast_sockaddr_set_port(vsa, vportno);
 			ast_rtp_instance_set_remote_address(p->vrtp, vsa);
 			if (debug) {
@@ -10644,7 +10661,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	/* Setup text address and port */
 	if (p->trtp) {
 		if (tsa && tportno > 0) {
-			start_ice(p->trtp);
+			start_ice(p->trtp, (req->method != SIP_RESPONSE) ? 0 : 1);
 			ast_sockaddr_set_port(tsa, tportno);
 			ast_rtp_instance_set_remote_address(p->trtp, tsa);
 			if (debug) {
@@ -10971,7 +10988,7 @@ static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_i
 {
 	struct ast_rtp_engine_dtls *dtls;
 	int found = FALSE;
-	char value[256], hash[6];
+	char value[256], hash[32];
 
 	if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) {
 		return found;
@@ -11003,11 +11020,13 @@ static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_i
 			ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n",
 				value, p->callid);
 		}
-	} else if (sscanf(a, "fingerprint: %5s %255s", hash, value) == 2) {
+	} else if (sscanf(a, "fingerprint: %31s %255s", hash, value) == 2) {
 		found = TRUE;
 
 		if (!strcasecmp(hash, "sha-1")) {
 			dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value);
+		} else if (!strcasecmp(hash, "sha-256")) {
+			dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA256, value);
 		} else {
 			ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n",
 				hash, p->callid);
@@ -12594,7 +12613,13 @@ static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a
 	while ((candidate = ao2_iterator_next(&i))) {
 		ast_str_append(a_buf, 0, "a=candidate:%s %d %s %d ", candidate->foundation, candidate->id, candidate->transport, candidate->priority);
 		ast_str_append(a_buf, 0, "%s ", ast_sockaddr_stringify_host(&candidate->address));
-		ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));
+
+		if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX
+			&& candidate->id == AST_RTP_ICE_COMPONENT_RTCP) {
+			ast_str_append(a_buf, 0, "%d typ ", ast_sockaddr_port(&candidate->address) + 1);
+		} else {
+			ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));
+		}
 
 		if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
 			ast_str_append(a_buf, 0, "host");
@@ -12619,7 +12644,7 @@ static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a
 }
 
 /*! \brief Start ICE negotiation on an RTP instance */
-static void start_ice(struct ast_rtp_instance *instance)
+static void start_ice(struct ast_rtp_instance *instance, int offer)
 {
 	struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance);
 
@@ -12627,6 +12652,8 @@ static void start_ice(struct ast_rtp_instance *instance)
 		return;
 	}
 
+	/* If we are the offerer then we are the controlling agent, otherwise they are */
+	ice->set_role(instance, offer ? AST_RTP_ICE_ROLE_CONTROLLING : AST_RTP_ICE_ROLE_CONTROLLED);
 	ice->start(instance);
 }
 
@@ -12634,6 +12661,7 @@ static void start_ice(struct ast_rtp_instance *instance)
 static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
 {
 	struct ast_rtp_engine_dtls *dtls;
+	enum ast_rtp_dtls_hash hash;
 	const char *fingerprint;
 
 	if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) {
@@ -12668,8 +12696,11 @@ static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **
 		break;
 	}
 
-	if ((fingerprint = dtls->get_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1))) {
-		ast_str_append(a_buf, 0, "a=fingerprint:SHA-1 %s\r\n", fingerprint);
+	hash = dtls->get_fingerprint_hash(instance);
+	fingerprint = dtls->get_fingerprint(instance);
+	if (fingerprint && (hash == AST_RTP_DTLS_HASH_SHA1 || hash == AST_RTP_DTLS_HASH_SHA256)) {
+		ast_str_append(a_buf, 0, "a=fingerprint:%s %s\r\n", hash == AST_RTP_DTLS_HASH_SHA1 ? "SHA-1" : "SHA-256",
+			fingerprint);
 	}
 }
 
@@ -12977,7 +13008,11 @@ static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, s
 	struct ast_rtp_engine_dtls *dtls;
 
 	if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
-		return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
+		if (ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)) {
+			return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "RTP/SAVPF" : "RTP/SAVP";
+		} else {
+			return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
+		}
 	} else {
 		if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 			return secure ? "RTP/SAVPF" : "RTP/AVPF";
@@ -30925,6 +30960,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
 			} else if (!strcasecmp(v->name, "icesupport")) {
 				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
+			} else if (!strcasecmp(v->name, "force_avp")) {
+				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_FORCE_AVP);
 			} else {
 				ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value);
 			}
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 073f5f4d6ad45c871482877f11811b4c6125d090..69304927ffeacdfd976e475f4a15a2512ff6a025 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -374,10 +374,11 @@
 #define SIP_PAGE3_DIRECT_MEDIA_OUTGOING  (1 << 4)  /*!< DP: Only send direct media reinvites on outgoing calls */
 #define SIP_PAGE3_USE_AVPF               (1 << 5)  /*!< DGP: Support a minimal AVPF-compatible profile */
 #define SIP_PAGE3_ICE_SUPPORT            (1 << 6)  /*!< DGP: Enable ICE support */
+#define SIP_PAGE3_FORCE_AVP              (1 << 7)  /*!< DGP: Force 'RTP/AVP' for all streams, even DTLS */
 
 #define SIP_PAGE3_FLAGS_TO_COPY \
 	(SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32 | SIP_PAGE3_NAT_AUTO_RPORT | SIP_PAGE3_NAT_AUTO_COMEDIA | \
-	 SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_USE_AVPF | SIP_PAGE3_ICE_SUPPORT)
+	 SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_USE_AVPF | SIP_PAGE3_ICE_SUPPORT | SIP_PAGE3_FORCE_AVP)
 
 #define CHECK_AUTH_BUF_INITLEN   256
 
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 702f6e5f37b9652f46474902f5eeb07674fadc1f..554d3ea69d72d6a4fede1c0cdf6961893f1297f8 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -1007,6 +1007,8 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;avpf=yes                       ; Enable inter-operability with media streams using the AVPF RTP profile.
 				; This will cause all offers and answers to use AVPF (or SAVPF). This
 				; option may be specified at the global or peer scope.
+;force_avp=yes			; Force 'RTP/AVP', 'RTP/AVPF', 'RTP/SAVP', and 'RTP/SAVPF' to be used for
+				; media streams when appropriate, even if a DTLS stream is present.
 ;----------------------------------------- REALTIME SUPPORT ------------------------
 ; For additional information on ARA, the Asterisk Realtime Architecture,
 ; please read https://wiki.asterisk.org/wiki/display/AST/Realtime+Database+Configuration
@@ -1261,6 +1263,7 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; dtlscafile
 ; dtlscapath
 ; dtlssetup
+; dtlsfingerprint
 ;
 
 ;------------------------------------------------------------------------------
@@ -1269,7 +1272,11 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; DTLS-SRTP support is available if the underlying RTP engine in use supports it.
 ;
 ; dtlsenable = yes                   ; Enable or disable DTLS-SRTP support
-; dtlsverify = yes                   ; Verify that the provided peer certificate is valid
+; dtlsverify = yes                   ; Verify that provided peer certificate and fingerprint are valid
+;				     ; A value of 'yes' will perform both certificate and fingerprint verification
+;				     ; A value of 'no' will perform no certificate or fingerprint verification
+;				     ; A value of 'fingerprint' will perform ONLY fingerprint verification
+;				     ; A value of 'certificate' will perform ONLY certficiate verification
 ; dtlsrekey = 60                     ; Interval at which to renegotiate the TLS session and rekey the SRTP session
 ;                                    ; If this is not set or the value provided is 0 rekeying will be disabled
 ; dtlscertfile = file                ; Path to certificate file to present
@@ -1284,6 +1291,7 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;                                    ; accept connections only), and actpass (we will do both). This value will be used in
 ;                                    ; the outgoing SDP when offering and for incoming SDP offers when the remote party sends
 ;                                    ; actpass
+; dtlsfingerprint = sha-1            ; The hash to use for the fingerprint in SDP (valid options are sha-1 and sha-256)
 
 ;[sip_proxy]
 ; For incoming calls only. Example: FWD (Free World Dialup)
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index 51455e92ac321bd257da30dc7a31dee872660b8c..3d7bd8fce3c88dcc28808676f0bd2ac13465e9fe 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -322,10 +322,22 @@ enum ast_rtp_ice_candidate_type {
 	AST_RTP_ICE_CANDIDATE_TYPE_RELAYED, /*!< ICE relayed candidate, which represents the address allocated in TURN server. */
 };
 
+/*! \brief ICE component types */
+enum ast_rtp_ice_component_type {
+	AST_RTP_ICE_COMPONENT_RTP = 1,
+	AST_RTP_ICE_COMPONENT_RTCP = 2,
+};
+
+/*! \brief ICE role during negotiation */
+enum ast_rtp_ice_role {
+	AST_RTP_ICE_ROLE_CONTROLLED,
+	AST_RTP_ICE_ROLE_CONTROLLING,
+};
+
 /*! \brief Structure for an ICE candidate */
 struct ast_rtp_engine_ice_candidate {
 	char *foundation;                     /*!< Foundation identifier */
-	unsigned int id;                      /*!< Component identifier */
+	enum ast_rtp_ice_component_type id;   /*!< Component identifier */
 	char *transport;                      /*!< Transport for the media */
 	int priority;                         /*!< Priority which is used if multiple candidates can be used */
 	struct ast_sockaddr address;          /*!< Address of the candidate */
@@ -351,6 +363,8 @@ struct ast_rtp_engine_ice {
 	struct ao2_container *(*get_local_candidates)(struct ast_rtp_instance *instance);
 	/*! Callback for telling the ICE support that it is talking to an ice-lite implementation */
 	void (*ice_lite)(struct ast_rtp_instance *instance);
+	/*! Callback for changing our role in negotiation */
+	void (*set_role)(struct ast_rtp_instance *instance, enum ast_rtp_ice_role role);
 };
 
 /*! \brief DTLS setup types */
@@ -363,22 +377,31 @@ enum ast_rtp_dtls_setup {
 
 /*! \brief DTLS connection states */
 enum ast_rtp_dtls_connection {
-        AST_RTP_DTLS_CONNECTION_NEW,      /*!< Endpoint wants to use a new connection */
+	AST_RTP_DTLS_CONNECTION_NEW,      /*!< Endpoint wants to use a new connection */
 	AST_RTP_DTLS_CONNECTION_EXISTING, /*!< Endpoint wishes to use existing connection */
 };
 
 /*! \brief DTLS fingerprint hashes */
 enum ast_rtp_dtls_hash {
-	AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */
+	AST_RTP_DTLS_HASH_SHA256, /*!< SHA-256 fingerprint hash */
+	AST_RTP_DTLS_HASH_SHA1,   /*!< SHA-1 fingerprint hash */
+};
+
+/*! \brief DTLS verification settings */
+enum ast_rtp_dtls_verify {
+	AST_RTP_DTLS_VERIFY_NONE = 0,               /*!< Don't verify anything */
+	AST_RTP_DTLS_VERIFY_FINGERPRINT = (1 << 0), /*!< Verify the fingerprint */
+	AST_RTP_DTLS_VERIFY_CERTIFICATE = (1 << 1), /*!< Verify the certificate */
 };
 
 /*! \brief DTLS configuration structure */
 struct ast_rtp_dtls_cfg {
 	unsigned int enabled:1;                /*!< Whether DTLS support is enabled or not */
-	unsigned int verify:1;                 /*!< Whether to request and verify a client certificate when acting as server */
 	unsigned int rekey;                    /*!< Interval at which to renegotiate and rekey - defaults to 0 (off) */
 	enum ast_rtp_dtls_setup default_setup; /*!< Default setup type to use for outgoing */
 	enum ast_srtp_suite suite;             /*!< Crypto suite in use */
+	enum ast_rtp_dtls_hash hash;		   /*!< Hash to use for fingerprint */
+	enum ast_rtp_dtls_verify verify;	   /*!< What should be verified */
 	char *certfile;                        /*!< Certificate file */
 	char *pvtfile;                         /*!< Private key file */
 	char *cipher;                          /*!< Cipher to use */
@@ -404,8 +427,10 @@ struct ast_rtp_engine_dtls {
 	void (*set_setup)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup);
 	/*! Set the remote fingerprint */
 	void (*set_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint);
+	/*! Get the local fingerprint hash type */
+	enum ast_rtp_dtls_hash (*get_fingerprint_hash)(struct ast_rtp_instance *instance);
 	/*! Get the local fingerprint */
-	const char *(*get_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash);
+	const char *(*get_fingerprint)(struct ast_rtp_instance *instance);
 };
 
 /*! Structure that represents an RTP stack (engine) */
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index dc2983637c26e7d1f2187f32f44248e7ffca0bef..01c1cea8a8085246009d2e430b1b98ca11906cc0 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2089,7 +2089,17 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 	if (!strcasecmp(name, "dtlsenable")) {
 		dtls_cfg->enabled = ast_true(value) ? 1 : 0;
 	} else if (!strcasecmp(name, "dtlsverify")) {
-		dtls_cfg->verify = ast_true(value) ? 1 : 0;
+		if (!strcasecmp(value, "yes")) {
+			dtls_cfg->verify = AST_RTP_DTLS_VERIFY_FINGERPRINT | AST_RTP_DTLS_VERIFY_CERTIFICATE;
+		} else if (!strcasecmp(value, "fingerprint")) {
+			dtls_cfg->verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;
+		} else if (!strcasecmp(value, "certificate")) {
+			dtls_cfg->verify = AST_RTP_DTLS_VERIFY_CERTIFICATE;
+		} else if (!strcasecmp(value, "no")) {
+			dtls_cfg->verify = AST_RTP_DTLS_VERIFY_NONE;
+		} else {
+			return -1;
+		}
 	} else if (!strcasecmp(name, "dtlsrekey")) {
 		if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {
 			return -1;
@@ -2117,6 +2127,12 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 		} else if (!strcasecmp(value, "actpass")) {
 			dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
 		}
+	} else if (!strcasecmp(name, "dtlsfingerprint")) {
+		if (!strcasecmp(value, "sha-256")) {
+			dtls_cfg->hash = AST_RTP_DTLS_HASH_SHA256;
+		} else if (!strcasecmp(value, "sha-1")) {
+			dtls_cfg->hash = AST_RTP_DTLS_HASH_SHA1;
+		}
 	} else {
 		return -1;
 	}
@@ -2130,6 +2146,7 @@ void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rt
 	dst_cfg->verify = src_cfg->verify;
 	dst_cfg->rekey = src_cfg->rekey;
 	dst_cfg->suite = src_cfg->suite;
+	dst_cfg->hash = src_cfg->hash;
 	dst_cfg->certfile = ast_strdup(src_cfg->certfile);
 	dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
 	dst_cfg->cipher = ast_strdup(src_cfg->cipher);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 78c1ea9eaebb0bee04aaa5c670b7866de302308e..97e68c939a42dc7e9e682ed0c38ba4e9f483ff51 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -70,6 +70,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/unaligned.h"
 #include "asterisk/module.h"
 #include "asterisk/rtp_engine.h"
+#include "asterisk/test.h"
 
 #define MAX_TIMESTAMP_SKEW	640
 
@@ -176,15 +177,22 @@ static int worker_terminate;
 #define TRANSPORT_TURN_RTP 3
 #define TRANSPORT_TURN_RTCP 4
 
-#define COMPONENT_RTP 1
-#define COMPONENT_RTCP 2
-
 /*! \brief RTP learning mode tracking information */
 struct rtp_learning_info {
 	int max_seq;	/*!< The highest sequence number received */
 	int packets;	/*!< The number of remaining packets before the source is accepted */
 };
 
+#ifdef HAVE_OPENSSL_SRTP
+struct dtls_details {
+	SSL *ssl;         /*!< SSL session */
+	BIO *read_bio;    /*!< Memory buffer for reading */
+	BIO *write_bio;   /*!< Memory buffer for writing */
+	enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
+	enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */
+};
+#endif
+
 /*! \brief RTP session description */
 struct ast_rtp {
 	int s;
@@ -268,7 +276,7 @@ struct ast_rtp {
 	pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */
 	ast_cond_t cond;            /*!< Condition for signaling */
 	unsigned int passthrough:1; /*!< Bit to indicate that the received packet should be passed through */
-	unsigned int ice_started:1; /*!< Bit to indicate ICE connectivity checks have started */
+	unsigned int ice_port;      /*!< Port that ICE was started with if it was previously started */
 
 	char remote_ufrag[256];  /*!< The remote ICE username */
 	char remote_passwd[256]; /*!< The remote ICE password */
@@ -276,23 +284,25 @@ struct ast_rtp {
 	char local_ufrag[256];  /*!< The local ICE username */
 	char local_passwd[256]; /*!< The local ICE password */
 
-	struct ao2_container *local_candidates;   /*!< The local ICE candidates */
-	struct ao2_container *remote_candidates;  /*!< The remote ICE candidates */
+	struct ao2_container *ice_local_candidates;           /*!< The local ICE candidates */
+	struct ao2_container *ice_active_remote_candidates;   /*!< The remote ICE candidates */
+	struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */
+	struct ast_sockaddr ice_original_rtp_addr;            /*!< rtp address that ICE started on first session */
 #endif
 
 #ifdef HAVE_OPENSSL_SRTP
 	SSL_CTX *ssl_ctx; /*!< SSL context */
-	SSL *ssl;         /*!< SSL session */
-	BIO *read_bio;    /*!< Memory buffer for reading */
-	BIO *write_bio;   /*!< Memory buffer for writing */
-	enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
+	ast_mutex_t dtls_timer_lock;           /*!< Lock for synchronization purposes */
+	enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */
 	enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
+	enum ast_rtp_dtls_hash local_hash; /*!< Local hash used for the fingerprint */
 	char local_fingerprint[160]; /*!< Fingerprint of our certificate */
+	enum ast_rtp_dtls_hash remote_hash; /*!< Remote hash used for the fingerprint */
 	unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */
-	enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */
-	unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */
 	unsigned int rekey; /*!< Interval at which to renegotiate and rekey */
 	int rekeyid; /*!< Scheduled item id for rekeying */
+	int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */
+	struct dtls_details dtls; /*!< DTLS state information */
 #endif
 };
 
@@ -355,6 +365,10 @@ struct ast_rtcp {
 	double normdevrtt;
 	double stdevrtt;
 	unsigned int rtt_count;
+
+#ifdef HAVE_OPENSSL_SRTP
+	struct dtls_details dtls; /*!< DTLS state information */
+#endif
 };
 
 struct rtp_red {
@@ -402,6 +416,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 
 #ifdef HAVE_OPENSSL_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);
 #endif
 
 static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
@@ -434,17 +449,32 @@ static void ast_rtp_ice_set_authentication(struct ast_rtp_instance *instance, co
 	}
 }
 
+static int ice_candidate_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_rtp_engine_ice_candidate *candidate1 = obj, *candidate2 = arg;
+
+	if (strcmp(candidate1->foundation, candidate2->foundation) ||
+			candidate1->id != candidate2->id ||
+			ast_sockaddr_cmp(&candidate1->address, &candidate2->address) ||
+			candidate1->type != candidate1->type) {
+		return 0;
+	}
+
+	return CMP_MATCH | CMP_STOP;
+}
+
 static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance, const struct ast_rtp_engine_ice_candidate *candidate)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	struct ast_rtp_engine_ice_candidate *remote_candidate;
 
-	if (!rtp->remote_candidates && !(rtp->remote_candidates = ao2_container_alloc(1, NULL, NULL))) {
+	if (!rtp->ice_proposed_remote_candidates &&
+			!(rtp->ice_proposed_remote_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) {
 		return;
 	}
 
 	/* If this is going to exceed the maximum number of ICE candidates don't even add it */
-	if (ao2_container_count(rtp->remote_candidates) == PJ_ICE_MAX_CAND) {
+	if (ao2_container_count(rtp->ice_proposed_remote_candidates) == PJ_ICE_MAX_CAND) {
 		return;
 	}
 
@@ -460,7 +490,7 @@ static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance,
 	ast_sockaddr_copy(&remote_candidate->relay_address, &candidate->relay_address);
 	remote_candidate->type = candidate->type;
 
-	ao2_link(rtp->remote_candidates, remote_candidate);
+	ao2_link(rtp->ice_proposed_remote_candidates, remote_candidate);
 	ao2_ref(remote_candidate, -1);
 }
 
@@ -502,6 +532,67 @@ static void update_address_with_ice_candidate(struct ast_rtp *rtp, int component
 	ast_sockaddr_set_port(cand_address, pj_sockaddr_get_port(&rtp->ice->comp[component - 1].valid_check->rcand->addr));
 }
 
+static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *addr,
+	int port, int replace);
+
+static void ast_rtp_ice_stop(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!rtp->ice) {
+		return;
+	}
+
+	pj_thread_register_check();
+
+	pj_ice_sess_destroy(rtp->ice);
+	rtp->ice = NULL;
+}
+
+static int ice_reset_session(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	pj_ice_sess_role role = rtp->ice->role;
+	int res;
+
+	ast_rtp_ice_stop(instance);
+
+	res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
+	if (!res) {
+		/* Preserve the role that the old ICE session used */
+		pj_ice_sess_change_role(rtp->ice, role);
+	}
+
+	return res;
+}
+
+static int ice_candidates_compare(struct ao2_container *left, struct ao2_container *right)
+{
+	struct ao2_iterator i;
+	struct ast_rtp_engine_ice_candidate *right_candidate;
+
+	if (ao2_container_count(left) != ao2_container_count(right)) {
+		return -1;
+	}
+
+	i = ao2_iterator_init(right, 0);
+	while ((right_candidate = ao2_iterator_next(&i))) {
+		struct ast_rtp_engine_ice_candidate *left_candidate = ao2_find(left, right_candidate, OBJ_POINTER);
+
+		if (!left_candidate) {
+			ao2_ref(right_candidate, -1);
+			ao2_iterator_destroy(&i);
+			return -1;
+		}
+
+		ao2_ref(left_candidate, -1);
+		ao2_ref(right_candidate, -1);
+	}
+	ao2_iterator_destroy(&i);
+
+	return 0;
+}
+
 static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -511,13 +602,32 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 	struct ast_rtp_engine_ice_candidate *candidate;
 	int cand_cnt = 0;
 
-	if (!rtp->ice || !rtp->remote_candidates || rtp->ice_started) {
+	if (!rtp->ice || !rtp->ice_proposed_remote_candidates) {
+		return;
+	}
+
+	/* Check for equivalence in the lists */
+	if (rtp->ice_active_remote_candidates &&
+			!ice_candidates_compare(rtp->ice_proposed_remote_candidates, rtp->ice_active_remote_candidates)) {
+		ao2_cleanup(rtp->ice_proposed_remote_candidates);
+		rtp->ice_proposed_remote_candidates = NULL;
+		return;
+	}
+
+	/* Out with the old, in with the new */
+	ao2_cleanup(rtp->ice_active_remote_candidates);
+	rtp->ice_active_remote_candidates = rtp->ice_proposed_remote_candidates;
+	rtp->ice_proposed_remote_candidates = NULL;
+
+	/* Reset the ICE session. Is this going to work? */
+	if (ice_reset_session(instance)) {
+		ast_log(LOG_NOTICE, "Failed to create replacement ICE session\n");
 		return;
 	}
 
 	pj_thread_register_check();
 
-	i = ao2_iterator_init(rtp->remote_candidates, 0);
+	i = ao2_iterator_init(rtp->ice_active_remote_candidates, 0);
 
 	while ((candidate = ao2_iterator_next(&i)) && (cand_cnt < PJ_ICE_MAX_CAND)) {
 		pj_str_t address;
@@ -540,37 +650,36 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 			candidates[cand_cnt].type = PJ_ICE_CAND_TYPE_RELAYED;
 		}
 
-		if (candidate->id == COMPONENT_RTP && rtp->turn_rtp) {
+		if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) {
 			pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
-		} else if (candidate->id == COMPONENT_RTCP && rtp->turn_rtcp) {
+		} else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) {
 			pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
 		}
 
 		cand_cnt++;
+		ao2_ref(candidate, -1);
 	}
 
 	ao2_iterator_destroy(&i);
 
-	if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->remote_candidates), &candidates[0]) == PJ_SUCCESS) {
+	if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) {
+		ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
 		pj_ice_sess_start_check(rtp->ice);
 		pj_timer_heap_poll(timerheap, NULL);
-		rtp->ice_started = 1;
 		rtp->strict_rtp_state = STRICT_RTP_OPEN;
-	}
-}
-
-static void ast_rtp_ice_stop(struct ast_rtp_instance *instance)
-{
-	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-
-	if (!rtp->ice) {
 		return;
 	}
 
-	pj_thread_register_check();
+	ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: FAILURE");
 
-	pj_ice_sess_destroy(rtp->ice);
-	rtp->ice = NULL;
+	/* even though create check list failed don't stop ice as
+	   it might still work */
+	ast_debug(1, "Failed to create ICE session check list\n");
+	/* however we do need to reset remote candidates since
+	   this function may be re-entered */
+	ao2_ref(rtp->ice_active_remote_candidates, -1);
+	rtp->ice_active_remote_candidates = NULL;
+	rtp->ice->rcand_cnt = rtp->ice->clist.count = 0;
 }
 
 static const char *ast_rtp_ice_get_ufrag(struct ast_rtp_instance *instance)
@@ -591,11 +700,11 @@ static struct ao2_container *ast_rtp_ice_get_local_candidates(struct ast_rtp_ins
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if (rtp->local_candidates) {
-		ao2_ref(rtp->local_candidates, +1);
+	if (rtp->ice_local_candidates) {
+		ao2_ref(rtp->ice_local_candidates, +1);
 	}
 
-	return rtp->local_candidates;
+	return rtp->ice_local_candidates;
 }
 
 static void ast_rtp_ice_lite(struct ast_rtp_instance *instance)
@@ -611,18 +720,18 @@ static void ast_rtp_ice_lite(struct ast_rtp_instance *instance)
 	pj_ice_sess_change_role(rtp->ice, PJ_ICE_SESS_ROLE_CONTROLLING);
 }
 
-static int ice_candidate_cmp(void *obj, void *arg, int flags)
+static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp_ice_role role)
 {
-	struct ast_rtp_engine_ice_candidate *candidate1 = obj, *candidate2 = arg;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if ((strcmp(candidate1->foundation, candidate2->foundation)) ||
-	    (candidate1->id != candidate2->id) ||
-	    (ast_sockaddr_cmp(&candidate1->address, &candidate2->address)) ||
-	    (candidate1->type != candidate1->type)) {
-		return 0;
+	if (!rtp->ice) {
+		return;
 	}
 
-	return CMP_MATCH | CMP_STOP;
+	pj_thread_register_check();
+
+	pj_ice_sess_change_role(rtp->ice, role == AST_RTP_ICE_ROLE_CONTROLLED ?
+		PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
 }
 
 static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref,
@@ -636,7 +745,7 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned
 
 	pj_ice_calc_foundation(rtp->ice->pool, &foundation, type, addr);
 
-	if (!rtp->local_candidates && !(rtp->local_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) {
+	if (!rtp->ice_local_candidates && !(rtp->ice_local_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) {
 		return;
 	}
 
@@ -664,13 +773,13 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned
 		candidate->type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
 	}
 
-	if ((existing = ao2_find(rtp->local_candidates, candidate, OBJ_POINTER))) {
+	if ((existing = ao2_find(rtp->ice_local_candidates, candidate, OBJ_POINTER))) {
 		ao2_ref(existing, -1);
 		ao2_ref(candidate, -1);
 		return;
 	}
 
-	if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) {
+	if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, base_addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) {
 		ao2_ref(candidate, -1);
 		return;
 	}
@@ -678,7 +787,7 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned
 	/* By placing the candidate into the ICE session it will have produced the priority, so update the local candidate with it */
 	candidate->priority = rtp->ice->lcand[rtp->ice->lcand_cnt - 1].prio;
 
-	ao2_link(rtp->local_candidates, candidate);
+	ao2_link(rtp->ice_local_candidates, candidate);
 	ao2_ref(candidate, -1);
 }
 
@@ -704,25 +813,83 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
 	.get_password = ast_rtp_ice_get_password,
 	.get_local_candidates = ast_rtp_ice_get_local_candidates,
 	.ice_lite = ast_rtp_ice_lite,
+	.set_role = ast_rtp_ice_set_role,
 };
 #endif
 
 #ifdef HAVE_OPENSSL_SRTP
-static void dtls_info_callback(const SSL *ssl, int where, int ret)
+static int dtls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
 {
-	struct ast_rtp *rtp = SSL_get_ex_data(ssl, 0);
+	/* We don't want to actually verify the certificate so just accept what they have provided */
+	return 1;
+}
 
-	/* We only care about alerts */
-	if (!(where & SSL_CB_ALERT)) {
-		return;
+static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx,
+	enum ast_rtp_dtls_setup setup)
+{
+	dtls->dtls_setup = setup;
+
+	if (!(dtls->ssl = SSL_new(ssl_ctx))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for SSL\n");
+		goto error;
+	}
+
+	if (!(dtls->read_bio = BIO_new(BIO_s_mem()))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic\n");
+		goto error;
+	}
+	BIO_set_mem_eof_return(dtls->read_bio, -1);
+
+	if (!(dtls->write_bio = BIO_new(BIO_s_mem()))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic\n");
+		goto error;
+	}
+	BIO_set_mem_eof_return(dtls->write_bio, -1);
+
+	SSL_set_bio(dtls->ssl, dtls->read_bio, dtls->write_bio);
+
+	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;
+
+	return 0;
+
+error:
+	if (dtls->read_bio) {
+		BIO_free(dtls->read_bio);
+		dtls->read_bio = NULL;
+	}
+
+	if (dtls->write_bio) {
+		BIO_free(dtls->write_bio);
+		dtls->write_bio = NULL;
+	}
+
+	if (dtls->ssl) {
+		SSL_free(dtls->ssl);
+		dtls->ssl = NULL;
+	}
+	return -1;
+}
+
+static int dtls_setup_rtcp(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!rtp->ssl_ctx || !rtp->rtcp) {
+		return 0;
 	}
 
-	rtp->dtls_failure = 1;
+	return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);
 }
 
 static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	int res;
 
 	if (!dtls_cfg->enabled) {
 		return 0;
@@ -736,7 +903,11 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		return -1;
 	}
 
-	SSL_CTX_set_verify(rtp->ssl_ctx, dtls_cfg->verify ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, NULL);
+	rtp->dtls_verify = dtls_cfg->verify;
+
+	SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
+		SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, !(rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
+		dtls_verify_callback : NULL);
 
 	if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) {
 		SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80");
@@ -744,13 +915,16 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32");
 	} else {
 		ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance);
-		goto error;
+		return -1;
 	}
 
+	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;
+		const EVP_MD *type;
 		unsigned int size, i;
 		unsigned char fingerprint[EVP_MAX_MD_SIZE];
 		char *local_fingerprint = rtp->local_fingerprint;
@@ -758,30 +932,40 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		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);
-			goto error;
+			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);
-			goto error;
+			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);
-			goto error;
+			return -1;
+		}
+
+		if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA1) {
+			type = EVP_sha1();
+		} else if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA256) {
+			type = EVP_sha256();
+		} else {
+			ast_log(LOG_ERROR, "Unsupported fingerprint hash type on RTP instance '%p'\n",
+				instance);
+			return -1;
 		}
 
 		if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||
 		    !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||
-		    !X509_digest(cert, EVP_sha1(), fingerprint, &size) ||
+		    !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);
-			goto error;
+			return -1;
 		}
 
 		for (i = 0; i < size; i++) {
@@ -798,7 +982,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) {
 			ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n",
 				dtls_cfg->cipher, instance);
-			goto error;
+			return -1;
 		}
 	}
 
@@ -806,69 +990,19 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) {
 			ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n",
 				S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance);
-			goto error;
+			return -1;
 		}
 	}
 
 	rtp->rekey = dtls_cfg->rekey;
-	rtp->dtls_setup = dtls_cfg->default_setup;
 	rtp->suite = dtls_cfg->suite;
 
-	if (!(rtp->ssl = SSL_new(rtp->ssl_ctx))) {
-		ast_log(LOG_ERROR, "Failed to allocate memory for SSL context on RTP instance '%p'\n",
-			instance);
-		goto error;
-	}
-
-	SSL_set_ex_data(rtp->ssl, 0, rtp);
-	SSL_set_info_callback(rtp->ssl, dtls_info_callback);
-
-	if (!(rtp->read_bio = BIO_new(BIO_s_mem()))) {
-		ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic on RTP instance '%p'\n",
-			instance);
-		goto error;
-	}
-	BIO_set_mem_eof_return(rtp->read_bio, -1);
-
-	if (!(rtp->write_bio = BIO_new(BIO_s_mem()))) {
-		ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic on RTP instance '%p'\n",
-			instance);
-		goto error;
-	}
-	BIO_set_mem_eof_return(rtp->write_bio, -1);
-
-	SSL_set_bio(rtp->ssl, rtp->read_bio, rtp->write_bio);
-
-	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
-		SSL_set_accept_state(rtp->ssl);
-	} else {
-		SSL_set_connect_state(rtp->ssl);
-	}
-
-	rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
-
-	return 0;
-
-error:
-	if (rtp->read_bio) {
-		BIO_free(rtp->read_bio);
-		rtp->read_bio = NULL;
-	}
-
-	if (rtp->write_bio) {
-		BIO_free(rtp->write_bio);
-		rtp->write_bio = NULL;
-	}
-
-	if (rtp->ssl) {
-		SSL_free(rtp->ssl);
-		rtp->ssl = NULL;
+	res = dtls_details_initialize(&rtp->dtls, rtp->ssl_ctx, dtls_cfg->default_setup);
+	if (!res) {
+		dtls_setup_rtcp(instance);
 	}
 
-	SSL_CTX_free(rtp->ssl_ctx);
-	rtp->ssl_ctx = NULL;
-
-	return -1;
+	return res;
 }
 
 static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
@@ -887,9 +1021,14 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
 		rtp->ssl_ctx = NULL;
 	}
 
-	if (rtp->ssl) {
-		SSL_free(rtp->ssl);
-		rtp->ssl = NULL;
+	if (rtp->dtls.ssl) {
+		SSL_free(rtp->dtls.ssl);
+		rtp->dtls.ssl = NULL;
+	}
+
+	if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
+		SSL_free(rtp->rtcp->dtls.ssl);
+		rtp->rtcp->dtls.ssl = NULL;
 	}
 }
 
@@ -897,49 +1036,50 @@ static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	/* If the SSL session is not yet finalized don't bother resetting */
-	if (!SSL_is_init_finished(rtp->ssl)) {
-		return;
+	if (SSL_is_init_finished(rtp->dtls.ssl)) {
+		SSL_shutdown(rtp->dtls.ssl);
+		rtp->dtls.connection = AST_RTP_DTLS_CONNECTION_NEW;
 	}
 
-	SSL_shutdown(rtp->ssl);
-	rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
+	if (rtp->rtcp && SSL_is_init_finished(rtp->rtcp->dtls.ssl)) {
+		SSL_shutdown(rtp->rtcp->dtls.ssl);
+		rtp->rtcp->dtls.connection = AST_RTP_DTLS_CONNECTION_NEW;
+	}
 }
 
 static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	return rtp->connection;
+	return rtp->dtls.connection;
 }
 
 static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	return rtp->dtls_setup;
+	return rtp->dtls.dtls_setup;
 }
 
-static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup)
+static void dtls_set_setup(enum ast_rtp_dtls_setup *dtls_setup, enum ast_rtp_dtls_setup setup, SSL *ssl)
 {
-	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-	enum ast_rtp_dtls_setup old = rtp->dtls_setup;
+	enum ast_rtp_dtls_setup old = *dtls_setup;
 
 	switch (setup) {
 	case AST_RTP_DTLS_SETUP_ACTIVE:
-		rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+		*dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
 		break;
 	case AST_RTP_DTLS_SETUP_PASSIVE:
-		rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+		*dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
 		break;
 	case AST_RTP_DTLS_SETUP_ACTPASS:
 		/* We can't respond to an actpass setup with actpass ourselves... so respond with active, as we can initiate connections */
-		if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
-			rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+		if (*dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+			*dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
 		}
 		break;
 	case AST_RTP_DTLS_SETUP_HOLDCONN:
-		rtp->dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN;
+		*dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN;
 		break;
 	default:
 		/* This should never occur... if it does exit early as we don't know what state things are in */
@@ -947,46 +1087,64 @@ static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_r
 	}
 
 	/* If the setup state did not change we go on as if nothing happened */
-	if (old == rtp->dtls_setup) {
+	if (old == *dtls_setup) {
 		return;
 	}
 
 	/* If they don't want us to establish a connection wait until later */
-	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) {
+	if (*dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) {
 		return;
 	}
 
-	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
-		SSL_set_connect_state(rtp->ssl);
-	} else if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
-		SSL_set_accept_state(rtp->ssl);
+	if (*dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+		SSL_set_connect_state(ssl);
+	} else if (*dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+		SSL_set_accept_state(ssl);
 	} else {
 		return;
 	}
 }
 
+static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (rtp->dtls.ssl) {
+		dtls_set_setup(&rtp->dtls.dtls_setup, setup, rtp->dtls.ssl);
+	}
+
+	if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
+		dtls_set_setup(&rtp->rtcp->dtls.dtls_setup, setup, rtp->rtcp->dtls.ssl);
+	}
+}
+
 static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint)
 {
 	char *tmp = ast_strdupa(fingerprint), *value;
 	int pos = 0;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if (hash != AST_RTP_DTLS_HASH_SHA1) {
+	if (hash != AST_RTP_DTLS_HASH_SHA1 && hash != AST_RTP_DTLS_HASH_SHA256) {
 		return;
 	}
 
+	rtp->remote_hash = hash;
+
 	while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) {
 		sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]);
 	}
 }
 
-static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash)
+static enum ast_rtp_dtls_hash ast_rtp_dtls_get_fingerprint_hash(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if (hash != AST_RTP_DTLS_HASH_SHA1) {
-		return NULL;
-	}
+	return rtp->local_hash;
+}
+
+static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
 	return rtp->local_fingerprint;
 }
@@ -1001,6 +1159,7 @@ static struct ast_rtp_engine_dtls ast_rtp_dtls = {
 	.get_setup = ast_rtp_dtls_get_setup,
 	.set_setup = ast_rtp_dtls_set_setup,
 	.set_fingerprint = ast_rtp_dtls_set_fingerprint,
+	.get_fingerprint_hash = ast_rtp_dtls_get_fingerprint_hash,
 	.get_fingerprint = ast_rtp_dtls_get_fingerprint,
 };
 
@@ -1044,10 +1203,42 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
 
 static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
 
+#ifdef HAVE_OPENSSL_SRTP
+static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtls_details *dtls, int rtcp)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!dtls->ssl) {
+		return;
+	}
+
+	if (SSL_is_init_finished(dtls->ssl)) {
+		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;
+	}
+	SSL_do_handshake(dtls->ssl);
+	dtls_srtp_check_pending(instance, rtp, rtcp);
+}
+#endif
+
 #ifdef USE_PJPROJECT
 static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
 {
-	struct ast_rtp *rtp = ice->user_data;
+	struct ast_rtp_instance *instance = ice->user_data;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+#ifdef HAVE_OPENSSL_SRTP
+	dtls_perform_handshake(instance, &rtp->dtls, 0);
+
+	if (rtp->rtcp) {
+		dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
+	}
+#endif
 
 	if (!strictrtp) {
 		return;
@@ -1059,7 +1250,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
 
 static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
 {
-	struct ast_rtp *rtp = ice->user_data;
+	struct ast_rtp_instance *instance = ice->user_data;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
 	/* Instead of handling the packet here (which really doesn't work with our architecture) we set a bit to indicate that it should be handled after pj_ice_sess_on_rx_pkt
 	 * returns */
@@ -1068,7 +1260,8 @@ static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned
 
 static pj_status_t ast_rtp_on_ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len)
 {
-	struct ast_rtp *rtp = ice->user_data;
+	struct ast_rtp_instance *instance = ice->user_data;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	pj_status_t status = PJ_EINVALIDOP;
 	pj_ssize_t _size = (pj_ssize_t)size;
 
@@ -1247,9 +1440,54 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
 }
 
 #ifdef HAVE_OPENSSL_SRTP
-static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
+
+static int dtls_srtp_handle_timeout(const void *data)
 {
-	size_t pending = BIO_ctrl_pending(rtp->write_bio);
+	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!rtp)
+	{
+		return 0;
+	}
+
+	ast_mutex_lock(&rtp->dtls_timer_lock);
+	if (rtp->dtlstimerid == -1)
+	{
+		ast_mutex_unlock(&rtp->dtls_timer_lock);
+		ao2_ref(instance, -1);
+		return 0;
+	}
+
+	rtp->dtlstimerid = -1;
+	ast_mutex_unlock(&rtp->dtls_timer_lock);
+
+	if (rtp->dtls.ssl && !SSL_is_init_finished(rtp->dtls.ssl)) {
+		DTLSv1_handle_timeout(rtp->dtls.ssl);
+	}
+	dtls_srtp_check_pending(instance, rtp, 0);
+
+	if (rtp->rtcp && rtp->rtcp->dtls.ssl && !SSL_is_init_finished(rtp->rtcp->dtls.ssl)) {
+		DTLSv1_handle_timeout(rtp->rtcp->dtls.ssl);
+	}
+	dtls_srtp_check_pending(instance, rtp, 1);
+
+	ao2_ref(instance, -1);
+
+	return 0;
+}
+
+static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
+{
+	struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
+	size_t pending;
+	struct timeval dtls_timeout; /* timeout on DTLS  */
+
+	if (!dtls->ssl || !dtls->write_bio) {
+		return;
+	}
+
+	pending = BIO_ctrl_pending(dtls->write_bio);
 
 	if (pending > 0) {
 		char outgoing[pending];
@@ -1257,16 +1495,37 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as
 		struct ast_sockaddr remote_address = { {0, } };
 		int ice;
 
-		ast_rtp_instance_get_remote_address(instance, &remote_address);
+		if (!rtcp) {
+			ast_rtp_instance_get_remote_address(instance, &remote_address);
+		} else {
+			ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
+		}
 
 		/* If we do not yet know an address to send this to defer it until we do */
 		if (ast_sockaddr_isnull(&remote_address)) {
 			return;
 		}
 
-		out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing));
+		out = BIO_read(dtls->write_bio, outgoing, sizeof(outgoing));
+
+		/* Stop existing DTLS timer if running */
+		ast_mutex_lock(&rtp->dtls_timer_lock);
+		if (rtp->dtlstimerid > -1) {
+			AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
+			rtp->dtlstimerid = -1;
+		}
+
+		if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) {
+			int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
+			ao2_ref(instance, +1);
+			if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) {
+				ao2_ref(instance, -1);
+				ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance);
+			}
+		}
+		ast_mutex_unlock(&rtp->dtls_timer_lock);
 
-		__rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0);
+		__rtp_sendto(instance, outgoing, out, 0, &remote_address, rtcp, &ice, 0);
 	}
 }
 
@@ -1275,9 +1534,15 @@ static int dtls_srtp_renegotiate(const void *data)
 	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	SSL_renegotiate(rtp->ssl);
-	SSL_do_handshake(rtp->ssl);
-	dtls_srtp_check_pending(instance, rtp);
+	SSL_renegotiate(rtp->dtls.ssl);
+	SSL_do_handshake(rtp->dtls.ssl);
+	dtls_srtp_check_pending(instance, rtp, 0);
+
+	if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
+		SSL_renegotiate(rtp->rtcp->dtls.ssl);
+		SSL_do_handshake(rtp->rtcp->dtls.ssl);
+		dtls_srtp_check_pending(instance, rtp, 1);
+	}
 
 	rtp->rekeyid = -1;
 	ao2_ref(instance, -1);
@@ -1293,20 +1558,30 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
 	struct ast_rtp_instance_stats stats = { 0, };
 
 	/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
-	if (SSL_CTX_get_verify_mode(rtp->ssl_ctx) != SSL_VERIFY_NONE) {
+	if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
 		X509 *certificate;
 
-		if (!(certificate = SSL_get_peer_certificate(rtp->ssl))) {
+		if (!(certificate = SSL_get_peer_certificate(rtp->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 (!X509_digest(certificate, EVP_sha1(), fingerprint, &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);
@@ -1320,21 +1595,21 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
 	}
 
 	/* Ensure that certificate verification was successful */
-	if (SSL_get_verify_result(rtp->ssl) != X509_V_OK) {
+	if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(rtp->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(rtp->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
+	if (!SSL_export_keying_material(rtp->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",
 			instance);
 		return -1;
 	}
 
 	/* Whether we are acting as a server or client determines where the keys/salts are */
-	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+	if (rtp->dtls.dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
 		local_key = material;
 		remote_key = local_key + SRTP_MASTER_KEY_LEN;
 		local_salt = remote_key + SRTP_MASTER_KEY_LEN;
@@ -1420,50 +1695,51 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 	}
 
 #ifdef HAVE_OPENSSL_SRTP
-	if (!rtcp) {
-		dtls_srtp_check_pending(instance, rtp);
+	dtls_srtp_check_pending(instance, rtp, rtcp);
 
-		/* If this is an SSL packet pass it to OpenSSL for processing */
-		if ((*in >= 20) && (*in <= 64)) {
-			int res = 0;
+	/* If this is an SSL packet pass it to OpenSSL for processing */
+	if ((*in >= 20) && (*in <= 64)) {
+		struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
+		int res = 0;
 
-			/* If no SSL session actually exists terminate things */
-			if (!rtp->ssl) {
-				ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n",
-					instance);
-				return -1;
-			}
-
-			/* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
-			if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
-				rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
-				SSL_set_accept_state(rtp->ssl);
-			}
+		/* If no SSL session actually exists terminate things */
+		if (!dtls->ssl) {
+			ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n",
+				instance);
+			return -1;
+		}
 
-			dtls_srtp_check_pending(instance, rtp);
+		/* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
+		if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+			dtls->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+			SSL_set_accept_state(dtls->ssl);
+		}
 
-			BIO_write(rtp->read_bio, buf, len);
+		dtls_srtp_check_pending(instance, rtp, rtcp);
 
-			len = SSL_read(rtp->ssl, buf, len);
+		BIO_write(dtls->read_bio, buf, len);
 
-			dtls_srtp_check_pending(instance, rtp);
+		len = SSL_read(dtls->ssl, buf, len);
 
-			if (rtp->dtls_failure) {
-				ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p', terminating\n",
-					instance);
-				return -1;
-			}
+		if ((len < 0) && (SSL_get_error(dtls->ssl, len) == SSL_ERROR_SSL)) {
+			unsigned long error = ERR_get_error();
+			ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p' due to reason '%s', terminating\n",
+				instance, ERR_reason_error_string(error));
+			return -1;
+		}
 
-			if (SSL_is_init_finished(rtp->ssl)) {
-				/* Any further connections will be existing since this is now established */
-				rtp->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
+		dtls_srtp_check_pending(instance, rtp, rtcp);
 
+		if (SSL_is_init_finished(dtls->ssl)) {
+			/* Any further connections will be existing since this is now established */
+			dtls->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
+			if (!rtcp) {
 				/* Use the keying material to set up key/salt information */
 				res = dtls_srtp_setup(rtp, srtp, instance);
 			}
-
-			return res;
 		}
+
+		return res;
 	}
 #endif
 
@@ -1477,7 +1753,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 
 		pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &combined, &address);
 
-		status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP,
+		status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP,
 			rtcp ? TRANSPORT_SOCKET_RTCP : TRANSPORT_SOCKET_RTP, buf, len, &address,
 			pj_sockaddr_get_len(&address));
 		if (status != PJ_SUCCESS) {
@@ -1529,9 +1805,9 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 	if (rtp->ice) {
 		pj_thread_register_check();
 
-		if (pj_ice_sess_send_data(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, temp, len) == PJ_SUCCESS) {
+		if (pj_ice_sess_send_data(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len) == PJ_SUCCESS) {
 			*ice = 1;
-			return 0;
+			return len;
 		}
 	}
 #endif
@@ -1669,15 +1945,19 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
 	}
 
 	/* If configured to use a STUN server to get our external mapped address do so */
-	if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr)) {
+	if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr) && count) {
 		struct sockaddr_in answer;
 
-		if (!ast_stun_request(rtp->s, &stunaddr, NULL, &answer)) {
+		if (!ast_stun_request(component == AST_RTP_ICE_COMPONENT_RTCP ? rtp->rtcp->s : rtp->s, &stunaddr, NULL, &answer)) {
+			pj_sockaddr base;
 			pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
 
+			/* Use the first local host candidate as the base */
+			pj_sockaddr_cp(&base, &address[0]);
+
 			pj_sockaddr_init(pj_AF_INET(), &address[0], &mapped, ntohs(answer.sin_port));
 
-			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &address[0],
+			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &base,
 					     NULL, pj_sockaddr_get_len(&address[0]));
 		}
 	}
@@ -1719,16 +1999,68 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
 }
 #endif
 
+#ifdef USE_PJPROJECT
+/*!
+ * \internal
+ * \brief Creates an ICE session. Can be used to replace a destroyed ICE session.
+ *
+ * \param instance RTP instance for which the ICE session is being replaced
+ * \param addr ast_sockaddr to use for adding RTP candidates to the ICE session
+ * \param port port to use for adding RTP candidates to the ICE session
+ * \param replace 0 when creating a new session, 1 when replacing a destroyed session
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *addr,
+	int port, int replace)
+{
+	pj_stun_config stun_config;
+	pj_str_t ufrag, passwd;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	ao2_cleanup(rtp->ice_local_candidates);
+	rtp->ice_local_candidates = NULL;
+
+	pj_thread_register_check();
+
+	pj_stun_config_init(&stun_config, &cachingpool.factory, 0, ioqueue, timerheap);
+
+	ufrag = pj_str(rtp->local_ufrag);
+	passwd = pj_str(rtp->local_passwd);
+
+	/* Create an ICE session for ICE negotiation */
+	if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2,
+			&ast_rtp_ice_sess_cb, &ufrag, &passwd, &rtp->ice) == PJ_SUCCESS) {
+		/* Make this available for the callbacks */
+		rtp->ice->user_data = instance;
+
+		/* Add all of the available candidates to the ICE session */
+		rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP,
+			TRANSPORT_SOCKET_RTP, &ast_rtp_turn_rtp_sock_cb, &rtp->turn_rtp);
+
+		/* Only add the RTCP candidates to ICE when replacing the session. New sessions
+		 * handle this in a separate part of the setup phase */
+		if (replace && rtp->rtcp) {
+			rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us,
+				ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP,
+				TRANSPORT_SOCKET_RTCP, &ast_rtp_turn_rtcp_sock_cb, &rtp->turn_rtcp);
+		}
+
+		return 0;
+	}
+
+	return -1;
+
+}
+#endif
+
 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;
 	int x, startplace;
-#ifdef USE_PJPROJECT
-	pj_stun_config stun_config;
-	pj_str_t ufrag, passwd;
-#endif
 
 	/* Create a new RTP structure to hold all of our data */
 	if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {
@@ -1791,22 +2123,17 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 	ast_rtp_instance_set_data(instance, rtp);
 
 #ifdef USE_PJPROJECT
-	pj_thread_register_check();
-
-	pj_stun_config_init(&stun_config, &cachingpool.factory, 0, ioqueue, timerheap);
-
 	generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag));
-	ufrag = pj_str(rtp->local_ufrag);
 	generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd));
-	passwd = pj_str(rtp->local_passwd);
 
 	/* Create an ICE session for ICE negotiation */
-	if (icesupport && pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2, &ast_rtp_ice_sess_cb, &ufrag, &passwd, &rtp->ice) == PJ_SUCCESS) {
-		/* Make this available for the callbacks */
-		rtp->ice->user_data = rtp;
-
-		/* Add all of the available candidates to the ICE session */
-		rtp_add_candidates_to_ice(instance, rtp, addr, x, COMPONENT_RTP, TRANSPORT_SOCKET_RTP, &ast_rtp_turn_rtp_sock_cb, &rtp->turn_rtp);
+	if (icesupport) {
+		if (ice_create(instance, addr, x, 0)) {
+			ast_log(LOG_NOTICE, "Failed to start ICE session\n");
+		} else {
+			rtp->ice_port = x;
+			ast_sockaddr_copy(&rtp->ice_original_rtp_addr, addr);
+		}
 	}
 #endif
 
@@ -1815,6 +2142,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 
 #ifdef HAVE_OPENSSL_SRTP
 	rtp->rekeyid = -1;
+	rtp->dtlstimerid = -1;
 #endif
 
 	return 0;
@@ -1842,6 +2170,11 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 		 * RTP instance while it's active.
 		 */
 		close(rtp->rtcp->s);
+#ifdef HAVE_OPENSSL_SRTP
+		if (rtp->rtcp->dtls.ssl) {
+			SSL_free(rtp->rtcp->dtls.ssl);
+		}
+#endif
 		ast_free(rtp->rtcp);
 	}
 
@@ -1872,12 +2205,12 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 	}
 
 	/* Destroy any candidates */
-	if (rtp->local_candidates) {
-		ao2_ref(rtp->local_candidates, -1);
+	if (rtp->ice_local_candidates) {
+		ao2_ref(rtp->ice_local_candidates, -1);
 	}
 
-	if (rtp->remote_candidates) {
-		ao2_ref(rtp->remote_candidates, -1);
+	if (rtp->ice_active_remote_candidates) {
+		ao2_ref(rtp->ice_active_remote_candidates, -1);
 	}
 
 	/* Destroy synchronization items */
@@ -1892,8 +2225,8 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 	}
 
 	/* Destroy the SSL session if present */
-	if (rtp->ssl) {
-		SSL_free(rtp->ssl);
+	if (rtp->dtls.ssl) {
+		SSL_free(rtp->dtls.ssl);
 	}
 #endif
 
@@ -1971,7 +2304,7 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
 				strerror(errno));
 		}
 #ifdef USE_PJPROJECT
-		update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+		update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 		if (rtp_debug_test_addr(&remote_address)) {
 			ast_verbose("Sent RTP DTMF packet to %s%s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
@@ -2023,7 +2356,7 @@ static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance)
 	}
 
 #ifdef USE_PJPROJECT
-	update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+	update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 
 	if (rtp_debug_test_addr(&remote_address)) {
@@ -2100,7 +2433,7 @@ static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, cha
 		}
 
 #ifdef USE_PJPROJECT
-		update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+		update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 
 		if (rtp_debug_test_addr(&remote_address)) {
@@ -2285,7 +2618,7 @@ static int ast_rtcp_write_rr(struct ast_rtp_instance *instance)
 	rtp->rtcp->rr_count++;
 
 #ifdef USE_PJPROJECT
-	update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address);
+	update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &remote_address);
 #endif
 
 	if (rtcp_debug_test_addr(&remote_address)) {
@@ -2398,7 +2731,7 @@ static int ast_rtcp_write_sr(struct ast_rtp_instance *instance)
 	rtp->rtcp->lastsrtxcount = rtp->txcount;
 
 #ifdef USE_PJPROJECT
-	update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address);
+	update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &remote_address);
 #endif
 
 	if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
@@ -2589,7 +2922,7 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 		}
 
 #ifdef USE_PJPROJECT
-		update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+		update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 
 		if (rtp_debug_test_addr(&remote_address)) {
@@ -3470,7 +3803,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
 	}
 
 #ifdef USE_PJPROJECT
-	update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+	update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 
 	if (rtp_debug_test_addr(&remote_address)) {
@@ -3930,11 +4263,15 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 
 #ifdef USE_PJPROJECT
 			if (rtp->ice) {
-				rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP,
+				rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP,
 							  &ast_rtp_turn_rtcp_sock_cb, &rtp->turn_rtcp);
 			}
 #endif
 
+#ifdef HAVE_OPENSSL_SRTP
+			dtls_setup_rtcp(instance);
+#endif
+
 			return;
 		} else {
 			if (rtp->rtcp) {
@@ -3950,6 +4287,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 					rtp->rtcp->schedid = -1;
 				}
 				close(rtp->rtcp->s);
+#ifdef HAVE_OPENSSL_SRTP
+				if (rtp->rtcp->dtls.ssl) {
+					SSL_free(rtp->rtcp->dtls.ssl);
+				}
+#endif
 				ast_free(rtp->rtcp);
 				rtp->rtcp = NULL;
 			}
@@ -4154,6 +4496,9 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 
 #ifdef HAVE_OPENSSL_SRTP
 	AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
+	ast_mutex_lock(&rtp->dtls_timer_lock);
+	AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
+	ast_mutex_unlock(&rtp->dtls_timer_lock);
 #endif
 
 	if (rtp->rtcp && rtp->rtcp->schedid > 0) {
@@ -4223,7 +4568,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
 	}
 
 #ifdef USE_PJPROJECT
-	update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address);
+	update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address);
 #endif
 
 	if (rtp_debug_test_addr(&remote_address)) {
@@ -4243,13 +4588,18 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if (!rtp->ssl) {
+	/* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */
+#ifdef USE_PJPROJECT
+	if (rtp->ice) {
 		return 0;
 	}
+#endif
 
-	SSL_do_handshake(rtp->ssl);
+	dtls_perform_handshake(instance, &rtp->dtls, 0);
 
-	dtls_srtp_check_pending(instance, rtp);
+	if (rtp->rtcp) {
+		dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
+	}
 
 	return 0;
 }