diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index d9e182cc79be83777d0a9057b7018ff0451f690f..0210e8ab2c8efd51a95328fa55177bff2a114556 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -2018,7 +2018,6 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req)
 	ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
 	ast_debug(3, "Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 	/* Scan through the RTP payload types specified in a "m=" line: */
-	ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp);
 	codecs = ast_strdupa(m + len);
 	while (!ast_strlen_zero(codecs)) {
 		if (sscanf(codecs, "%30d%n", &codec, &len) != 1) {
@@ -4487,7 +4486,8 @@ static enum ast_rtp_glue_result mgcp_get_rtp_peer(struct ast_channel *chan, stru
 	if (!(sub = ast_channel_tech_pvt(chan)) || !(sub->rtp))
 		return AST_RTP_GLUE_RESULT_FORBID;
 
-	*instance = sub->rtp ? ao2_ref(sub->rtp, +1), sub->rtp : NULL;
+	ao2_ref(sub->rtp, +1);
+	*instance = sub->rtp;
 
 	if (sub->parent->directmedia)
 		return AST_RTP_GLUE_RESULT_REMOTE;
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index a3c0ffd5c13a5d43b927d0801bb8e49a42aaaeee..874f6b2dccc5f36b94ecdf4b02dda147f83832e0 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -2709,7 +2709,8 @@ static void send_start_rtp(struct unistim_subchannel *sub)
 	}
 
 	pte = sub->parent->parent->session;
-	codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, ast_channel_readformat(sub->owner), 0);
+	codec = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(sub->rtp),
+		1, ast_channel_readformat(sub->owner), 0);
 	if ((ast_format_cmp(ast_channel_readformat(sub->owner), ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) ||
 		(ast_format_cmp(ast_channel_readformat(sub->owner), ast_format_alaw) == AST_FORMAT_CMP_EQUAL)) {
 		if (unistimdebug) {
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index d6a9be57401d7db0039c4438d3cc714d5dbf186f..a52567a139637c7eccaccf6b96ab09d8d6a7c12c 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -251,6 +251,8 @@ struct ast_rtp_payload_type {
 	int rtp_code;
 	/*! Actual payload number */
 	int payload;
+	/*! TRUE if this is the primary mapping to the format. */
+	unsigned int primary_mapping:1;
 };
 
 /* Common RTCP report types */
@@ -577,16 +579,18 @@ struct ast_rtp_engine {
 
 /*! Structure that represents codec and packetization information */
 struct ast_rtp_codecs {
-	/*! Payloads present */
-	AST_VECTOR(, struct ast_rtp_payload_type *) payloads;
-	/*! The framing for this media session */
-	unsigned int framing;
 	/*! RW lock that protects elements in this structure */
 	ast_rwlock_t codecs_lock;
+	/*! Rx payload type mapping exceptions */
+	AST_VECTOR(, struct ast_rtp_payload_type *) payload_mapping_rx;
+	/*! Tx payload type mapping */
+	AST_VECTOR(, struct ast_rtp_payload_type *) payload_mapping_tx;
+	/*! The framing for this media session */
+	unsigned int framing;
 };
 
 #define AST_RTP_CODECS_NULL_INIT \
-    { .payloads = { 0, }, .framing = 0, .codecs_lock = AST_RWLOCK_INIT_VALUE, }
+    { .codecs_lock = AST_RWLOCK_INIT_VALUE, .payload_mapping_rx = { 0, }, .payload_mapping_tx = { 0, }, .framing = 0, }
 
 /*! Structure that represents the glue that binds an RTP instance to a channel */
 struct ast_rtp_glue {
@@ -1192,7 +1196,7 @@ int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs);
 void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs);
 
 /*!
- * \brief Clear payload information from an RTP instance
+ * \brief Clear rx and tx payload mapping information from an RTP instance
  *
  * \param codecs The codecs structure that payloads will be cleared from
  * \param instance Optionally the instance that the codecs structure belongs to
@@ -1230,7 +1234,19 @@ void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp
 void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance);
 
 /*!
- * \brief Record payload information that was seen in an m= SDP line
+ * \brief Crossover copy the tx payload mapping of src to the rx payload mapping of dest.
+ * \since 14.0.0
+ *
+ * \param src The source codecs structure
+ * \param dest The destination codecs structure that the values from src will be copied to
+ * \param instance Optionally the instance that the dst codecs structure belongs to
+ *
+ * \return Nothing
+ */
+void ast_rtp_codecs_payloads_xover(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Record tx payload type information that was seen in an m= SDP line
  *
  * \param codecs The codecs structure to muck with
  * \param instance Optionally the instance that the codecs structure belongs to
@@ -1249,7 +1265,7 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod
 void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload);
 
 /*!
- * \brief Record payload information that was seen in an a=rtpmap: SDP line
+ * \brief Record tx payload type information that was seen in an a=rtpmap: SDP line
  *
  * \param codecs The codecs structure to muck with
  * \param instance Optionally the instance that the codecs structure belongs to
@@ -1275,7 +1291,7 @@ void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct as
 int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options);
 
 /*!
- * \brief Set payload type to a known MIME media type for a codec with a specific sample rate
+ * \brief Set tx payload type to a known MIME media type for a codec with a specific sample rate
  *
  * \param codecs RTP structure to modify
  * \param instance Optionally the instance that the codecs structure belongs to
@@ -1300,7 +1316,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 				  unsigned int sample_rate);
 
 /*!
- * \brief Remove payload information
+ * \brief Remove tx payload type mapped information
  *
  * \param codecs The codecs structure to muck with
  * \param instance Optionally the instance that the codecs structure belongs to
@@ -1319,7 +1335,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload);
 
 /*!
- * \brief Retrieve payload information by payload
+ * \brief Retrieve rx payload mapped information by payload type
  *
  * \param codecs Codecs structure to look in
  * \param payload Numerical payload to look up
@@ -1342,10 +1358,10 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp
 struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *codecs, int payload);
 
 /*!
- * \brief Update the format associated with a payload in a codecs structure
+ * \brief Update the format associated with a tx payload type in a codecs structure
  *
  * \param codecs Codecs structure to operate on
- * \param payload Numerical payload to look up
+ * \param payload Numerical payload type to look up
  * \param format The format to replace the existing one
  *
  * \retval 0 success
@@ -1356,10 +1372,10 @@ struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *c
 int ast_rtp_codecs_payload_replace_format(struct ast_rtp_codecs *codecs, int payload, struct ast_format *format);
 
 /*!
- * \brief Retrieve the actual ast_format stored on the codecs structure for a specific payload
+ * \brief Retrieve the actual ast_format stored on the codecs structure for a specific tx payload type
  *
  * \param codecs Codecs structure to look in
- * \param payload Numerical payload to look up
+ * \param payload Numerical payload type to look up
  *
  * \retval pointer to format structure on success
  * \retval NULL on failure
@@ -1428,14 +1444,15 @@ unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format
 void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats);
 
 /*!
- * \brief Retrieve a payload based on whether it is an Asterisk format and the code
+ * \brief Retrieve a rx mapped payload type based on whether it is an Asterisk format and the code
  *
  * \param codecs Codecs structure to look in
  * \param asterisk_format Non-zero if the given Asterisk format is present
  * \param format Asterisk format to look for
  * \param code The format to look for
  *
- * \retval Numerical payload
+ * \retval Numerical payload type
+ * \retval -1 if not found.
  *
  * Example usage:
  *
@@ -1450,12 +1467,26 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo
 int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code);
 
 /*!
- * \brief Search for a payload code in the ast_rtp_codecs structure
+ * \brief Retrieve a tx mapped payload type based on whether it is an Asterisk format and the code
+ * \since 14.0.0
  *
  * \param codecs Codecs structure to look in
+ * \param asterisk_format Non-zero if the given Asterisk format is present
+ * \param format Asterisk format to look for
  * \param code The format to look for
  *
- * \retval Numerical payload or -1 if unable to find payload in codecs
+ * \retval Numerical payload type
+ * \retval -1 if not found.
+ */
+int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code);
+
+/*!
+ * \brief Search for the tx payload type in the ast_rtp_codecs structure
+ *
+ * \param codecs Codecs structure to look in
+ * \param payload The payload type format to look for
+ *
+ * \retval Numerical payload type or -1 if unable to find payload in codecs
  *
  * Example usage:
  *
@@ -1464,9 +1495,8 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
  * \endcode
  *
  * This looks for the numerical payload for ULAW in the codecs structure.
- *
  */
-int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int code);
+int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int payload);
 
 /*!
  * \brief Retrieve mime subtype information on a payload
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 1375897d67f320a97cc4aaf0fda70f115c7fb6bc..229af8fc9e1abc5cb945dc6bdbdbfdee1f9d536d 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -580,22 +580,32 @@ int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs)
 
 	codecs->framing = 0;
 	ast_rwlock_init(&codecs->codecs_lock);
-	res = AST_VECTOR_INIT(&codecs->payloads, AST_RTP_MAX_PT);
+	res = AST_VECTOR_INIT(&codecs->payload_mapping_rx, AST_RTP_MAX_PT);
+	res |= AST_VECTOR_INIT(&codecs->payload_mapping_tx, AST_RTP_MAX_PT);
+	if (res) {
+		AST_VECTOR_FREE(&codecs->payload_mapping_rx);
+		AST_VECTOR_FREE(&codecs->payload_mapping_tx);
+	}
 
 	return res;
 }
 
 void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs)
 {
-	int i;
+	int idx;
+	struct ast_rtp_payload_type *type;
 
-	for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) {
-		struct ast_rtp_payload_type *type;
+	for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); ++idx) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_rx, idx);
+		ao2_t_cleanup(type, "destroying ast_rtp_codec rx mapping");
+	}
+	AST_VECTOR_FREE(&codecs->payload_mapping_rx);
 
-		type = AST_VECTOR_GET(&codecs->payloads, i);
-		ao2_t_cleanup(type, "destroying ast_rtp_codec");
+	for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx);
+		ao2_t_cleanup(type, "destroying ast_rtp_codec tx mapping");
 	}
-	AST_VECTOR_FREE(&codecs->payloads);
+	AST_VECTOR_FREE(&codecs->payload_mapping_tx);
 
 	ast_rwlock_destroy(&codecs->codecs_lock);
 }
@@ -613,34 +623,254 @@ void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp
 	}
 }
 
-void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
+/*!
+ * \internal
+ * \brief Clear the rx primary mapping flag on all other matching mappings.
+ * \since 14.0.0
+ *
+ * \param codecs Codecs that need rx clearing.
+ * \param to_match Payload type object to compare against.
+ *
+ * \note It is assumed that codecs is write locked before calling.
+ *
+ * \return Nothing
+ */
+static void payload_mapping_rx_clear_primary(struct ast_rtp_codecs *codecs, struct ast_rtp_payload_type *to_match)
 {
-	int i;
+	int idx;
+	struct ast_rtp_payload_type *current;
+	struct ast_rtp_payload_type *new_type;
+
+	if (!to_match->primary_mapping) {
+		return;
+	}
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); ++idx) {
+		current = AST_VECTOR_GET(&codecs->payload_mapping_rx, idx);
+
+		if (!current || current == to_match || !current->primary_mapping) {
+			continue;
+		}
+		if (current->asterisk_format && to_match->asterisk_format) {
+			if (ast_format_cmp(current->format, to_match->format) == AST_FORMAT_CMP_NOT_EQUAL) {
+				continue;
+			}
+		} else if (!current->asterisk_format && !to_match->asterisk_format) {
+			if (current->rtp_code != to_match->rtp_code) {
+				continue;
+			}
+		} else {
+			continue;
+		}
+
+		/* Replace current with non-primary marked version */
+		new_type = ast_rtp_engine_alloc_payload_type();
+		if (!new_type) {
+			continue;
+		}
+		*new_type = *current;
+		new_type->primary_mapping = 0;
+		ao2_bump(new_type->format);
+		AST_VECTOR_REPLACE(&codecs->payload_mapping_rx, idx, new_type);
+		ao2_ref(current, -1);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Copy the rx payload type mapping to the destination.
+ * \since 14.0.0
+ *
+ * \param src The source codecs structure
+ * \param dest The destination codecs structure that the values from src will be copied to
+ * \param instance Optionally the instance that the dst codecs structure belongs to
+ *
+ * \note It is assumed that src is at least read locked before calling.
+ * \note It is assumed that dest is write locked before calling.
+ *
+ * \return Nothing
+ */
+static void rtp_codecs_payloads_copy_rx(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
+{
+	int idx;
+	struct ast_rtp_payload_type *type;
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&src->payload_mapping_rx); ++idx) {
+		type = AST_VECTOR_GET(&src->payload_mapping_rx, idx);
+		if (!type) {
+			continue;
+		}
+
+		ast_debug(2, "Copying rx payload mapping %d (%p) from %p to %p\n",
+			idx, type, src, dest);
+		ao2_ref(type, +1);
+		if (idx < AST_VECTOR_SIZE(&dest->payload_mapping_rx)) {
+			ao2_t_cleanup(AST_VECTOR_GET(&dest->payload_mapping_rx, idx),
+				"cleaning up rx mapping vector element about to be replaced");
+		}
+		AST_VECTOR_REPLACE(&dest->payload_mapping_rx, idx, type);
+
+		payload_mapping_rx_clear_primary(dest, type);
+
+		if (instance && instance->engine && instance->engine->payload_set) {
+			instance->engine->payload_set(instance, idx, type->asterisk_format, type->format, type->rtp_code);
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief Remove other matching payload mappings.
+ * \since 14.0.0
+ *
+ * \param codecs Codecs that need tx mappings removed.
+ * \param instance RTP instance to notify of any payloads removed.
+ * \param to_match Payload type object to compare against.
+ *
+ * \note It is assumed that codecs is write locked before calling.
+ *
+ * \return Nothing
+ */
+static void payload_mapping_tx_remove_other_mappings(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_rtp_payload_type *to_match)
+{
+	int idx;
+	struct ast_rtp_payload_type *current;
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) {
+		current = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx);
+
+		if (!current || current == to_match) {
+			continue;
+		}
+		if (current->asterisk_format && to_match->asterisk_format) {
+			if (ast_format_cmp(current->format, to_match->format) == AST_FORMAT_CMP_NOT_EQUAL) {
+				continue;
+			}
+		} else if (!current->asterisk_format && !to_match->asterisk_format) {
+			if (current->rtp_code != to_match->rtp_code) {
+				continue;
+			}
+		} else {
+			continue;
+		}
+
+		/* Remove other mapping */
+		AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, idx, NULL);
+		ao2_ref(current, -1);
+		if (instance && instance->engine && instance->engine->payload_set) {
+			instance->engine->payload_set(instance, idx, 0, NULL, 0);
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief Copy the tx payload type mapping to the destination.
+ * \since 14.0.0
+ *
+ * \param src The source codecs structure
+ * \param dest The destination codecs structure that the values from src will be copied to
+ * \param instance Optionally the instance that the dst codecs structure belongs to
+ *
+ * \note It is assumed that src is at least read locked before calling.
+ * \note It is assumed that dest is write locked before calling.
+ *
+ * \return Nothing
+ */
+static void rtp_codecs_payloads_copy_tx(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
+{
+	int idx;
+	struct ast_rtp_payload_type *type;
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&src->payload_mapping_tx); ++idx) {
+		type = AST_VECTOR_GET(&src->payload_mapping_tx, idx);
+		if (!type) {
+			continue;
+		}
+
+		ast_debug(2, "Copying tx payload mapping %d (%p) from %p to %p\n",
+			idx, type, src, dest);
+		ao2_ref(type, +1);
+		if (idx < AST_VECTOR_SIZE(&dest->payload_mapping_tx)) {
+			ao2_t_cleanup(AST_VECTOR_GET(&dest->payload_mapping_tx, idx),
+				"cleaning up tx mapping vector element about to be replaced");
+		}
+		AST_VECTOR_REPLACE(&dest->payload_mapping_tx, idx, type);
+
+		if (instance && instance->engine && instance->engine->payload_set) {
+			instance->engine->payload_set(instance, idx, type->asterisk_format, type->format, type->rtp_code);
+		}
+
+		payload_mapping_tx_remove_other_mappings(dest, instance, type);
+	}
+}
 
-	ast_rwlock_rdlock(&src->codecs_lock);
+void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
+{
 	ast_rwlock_wrlock(&dest->codecs_lock);
 
-	for (i = 0; i < AST_VECTOR_SIZE(&src->payloads); i++) {
-		struct ast_rtp_payload_type *type;
+	/* Deadlock avoidance because of held write lock. */
+	while (ast_rwlock_tryrdlock(&src->codecs_lock)) {
+		ast_rwlock_unlock(&dest->codecs_lock);
+		sched_yield();
+		ast_rwlock_wrlock(&dest->codecs_lock);
+	}
 
-		type = AST_VECTOR_GET(&src->payloads, i);
+	rtp_codecs_payloads_copy_rx(src, dest, instance);
+	rtp_codecs_payloads_copy_tx(src, dest, instance);
+	dest->framing = src->framing;
+
+	ast_rwlock_unlock(&src->codecs_lock);
+	ast_rwlock_unlock(&dest->codecs_lock);
+}
+
+void ast_rtp_codecs_payloads_xover(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
+{
+	int idx;
+	struct ast_rtp_payload_type *type;
+
+	ast_rwlock_wrlock(&dest->codecs_lock);
+	if (src != dest) {
+		/* Deadlock avoidance because of held write lock. */
+		while (ast_rwlock_tryrdlock(&src->codecs_lock)) {
+			ast_rwlock_unlock(&dest->codecs_lock);
+			sched_yield();
+			ast_rwlock_wrlock(&dest->codecs_lock);
+		}
+	}
+
+	/* Crossover copy payload type tx mapping to rx mapping. */
+	for (idx = 0; idx < AST_VECTOR_SIZE(&src->payload_mapping_tx); ++idx) {
+		type = AST_VECTOR_GET(&src->payload_mapping_tx, idx);
 		if (!type) {
 			continue;
 		}
-		if (i < AST_VECTOR_SIZE(&dest->payloads)) {
-			ao2_t_cleanup(AST_VECTOR_GET(&dest->payloads, i), "cleaning up vector element about to be replaced");
+
+		/* All tx mapping elements should have the primary flag set. */
+		ast_assert(type->primary_mapping);
+
+		ast_debug(2, "Crossover copying tx to rx payload mapping %d (%p) from %p to %p\n",
+			idx, type, src, dest);
+		ao2_ref(type, +1);
+		if (idx < AST_VECTOR_SIZE(&dest->payload_mapping_rx)) {
+			ao2_t_cleanup(AST_VECTOR_GET(&dest->payload_mapping_rx, idx),
+				"cleaning up rx mapping vector element about to be replaced");
 		}
-		ast_debug(2, "Copying payload %d (%p) from %p to %p\n", i, type, src, dest);
-		ao2_bump(type);
-		AST_VECTOR_REPLACE(&dest->payloads, i, type);
+		AST_VECTOR_REPLACE(&dest->payload_mapping_rx, idx, type);
+
+		payload_mapping_rx_clear_primary(dest, type);
 
 		if (instance && instance->engine && instance->engine->payload_set) {
-			instance->engine->payload_set(instance, i, type->asterisk_format, type->format, type->rtp_code);
+			instance->engine->payload_set(instance, idx, type->asterisk_format, type->format, type->rtp_code);
 		}
 	}
+
 	dest->framing = src->framing;
+
+	if (src != dest) {
+		ast_rwlock_unlock(&src->codecs_lock);
+	}
 	ast_rwlock_unlock(&dest->codecs_lock);
-	ast_rwlock_unlock(&src->codecs_lock);
 }
 
 void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload)
@@ -665,15 +895,18 @@ void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct as
 
 	ast_rwlock_wrlock(&codecs->codecs_lock);
 
-	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		ao2_t_cleanup(AST_VECTOR_GET(&codecs->payloads, payload), "cleaning up replaced payload type");
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+		ao2_t_cleanup(AST_VECTOR_GET(&codecs->payload_mapping_tx, payload),
+			"cleaning up replaced tx payload type");
 	}
-	AST_VECTOR_REPLACE(&codecs->payloads, payload, new_type);
+	AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, payload, new_type);
 
 	if (instance && instance->engine && instance->engine->payload_set) {
 		instance->engine->payload_set(instance, payload, new_type->asterisk_format, new_type->format, new_type->rtp_code);
 	}
 
+	payload_mapping_tx_remove_other_mappings(codecs, instance, new_type);
+
 	ast_rwlock_unlock(&codecs->codecs_lock);
 }
 
@@ -682,7 +915,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 				 enum ast_rtp_options options,
 				 unsigned int sample_rate)
 {
-	unsigned int i;
+	unsigned int idx;
 	int found = 0;
 
 	if (pt < 0 || pt >= AST_RTP_MAX_PT) {
@@ -691,8 +924,9 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 
 	ast_rwlock_rdlock(&mime_types_lock);
 	ast_rwlock_wrlock(&codecs->codecs_lock);
-	for (i = 0; i < mime_types_len; ++i) {
-		const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i];
+
+	for (idx = 0; idx < mime_types_len; ++idx) {
+		const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[idx];
 		struct ast_rtp_payload_type *new_type;
 
 		if (strcasecmp(mimesubtype, t->subtype)) {
@@ -718,27 +952,32 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 			continue;
 		}
 
-		if (pt < AST_VECTOR_SIZE(&codecs->payloads)) {
-			ao2_t_cleanup(AST_VECTOR_GET(&codecs->payloads, pt), "cleaning up replaced payload type");
-		}
-
-		new_type->payload = pt;
 		new_type->asterisk_format = t->payload_type.asterisk_format;
 		new_type->rtp_code = t->payload_type.rtp_code;
-		if ((ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) &&
-				t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) {
+		new_type->payload = pt;
+		new_type->primary_mapping = 1;
+		if (t->payload_type.asterisk_format
+			&& ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL
+			&& (options & AST_RTP_OPT_G726_NONSTANDARD)) {
 			new_type->format = ao2_bump(ast_format_g726_aal2);
 		} else {
 			new_type->format = ao2_bump(t->payload_type.format);
 		}
-		AST_VECTOR_REPLACE(&codecs->payloads, pt, new_type);
+
+		if (pt < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+			ao2_t_cleanup(AST_VECTOR_GET(&codecs->payload_mapping_tx, pt),
+				"cleaning up replaced tx payload type");
+		}
+		AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, pt, new_type);
 
 		if (instance && instance->engine && instance->engine->payload_set) {
 			instance->engine->payload_set(instance, pt, new_type->asterisk_format, new_type->format, new_type->rtp_code);
 		}
 
+		payload_mapping_tx_remove_other_mappings(codecs, instance, new_type);
 		break;
 	}
+
 	ast_rwlock_unlock(&codecs->codecs_lock);
 	ast_rwlock_unlock(&mime_types_lock);
 
@@ -761,10 +1000,11 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp
 	ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs);
 
 	ast_rwlock_wrlock(&codecs->codecs_lock);
-	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		type = AST_VECTOR_GET(&codecs->payloads, payload);
+
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_tx, payload);
 		ao2_cleanup(type);
-		AST_VECTOR_REPLACE(&codecs->payloads, payload, NULL);
+		AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, payload, NULL);
 	}
 
 	if (instance && instance->engine && instance->engine->payload_set) {
@@ -783,8 +1023,8 @@ struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *c
 	}
 
 	ast_rwlock_rdlock(&codecs->codecs_lock);
-	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		type = AST_VECTOR_GET(&codecs->payloads, payload);
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_rx)) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_rx, payload);
 		ao2_bump(type);
 	}
 	ast_rwlock_unlock(&codecs->codecs_lock);
@@ -814,12 +1054,14 @@ int ast_rtp_codecs_payload_replace_format(struct ast_rtp_codecs *codecs, int pay
 	type->format = format;
 	type->asterisk_format = 1;
 	type->payload = payload;
+	type->primary_mapping = 1;
 
 	ast_rwlock_wrlock(&codecs->codecs_lock);
-	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		ao2_cleanup(AST_VECTOR_GET(&codecs->payloads, payload));
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+		ao2_cleanup(AST_VECTOR_GET(&codecs->payload_mapping_tx, payload));
 	}
-	AST_VECTOR_REPLACE(&codecs->payloads, payload, type);
+	AST_VECTOR_REPLACE(&codecs->payload_mapping_tx, payload, type);
+	payload_mapping_tx_remove_other_mappings(codecs, NULL, type);
 	ast_rwlock_unlock(&codecs->codecs_lock);
 
 	return 0;
@@ -835,8 +1077,8 @@ struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *code
 	}
 
 	ast_rwlock_rdlock(&codecs->codecs_lock);
-	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		type = AST_VECTOR_GET(&codecs->payloads, payload);
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_tx, payload);
 		if (type && type->asterisk_format) {
 			format = ao2_bump(type->format);
 		}
@@ -870,16 +1112,17 @@ unsigned int ast_rtp_codecs_get_framing(struct ast_rtp_codecs *codecs)
 
 void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats)
 {
-	int i;
+	int idx;
 
 	ast_format_cap_remove_by_type(astformats, AST_MEDIA_TYPE_UNKNOWN);
 	*nonastformats = 0;
 
 	ast_rwlock_rdlock(&codecs->codecs_lock);
-	for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) {
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) {
 		struct ast_rtp_payload_type *type;
 
-		type = AST_VECTOR_GET(&codecs->payloads, i);
+		type = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx);
 		if (!type) {
 			continue;
 		}
@@ -890,7 +1133,6 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo
 			*nonastformats |= type->rtp_code;
 		}
 	}
-
 	if (codecs->framing) {
 		ast_format_cap_set_framing(astformats, codecs->framing);
 	}
@@ -898,59 +1140,161 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo
 	ast_rwlock_unlock(&codecs->codecs_lock);
 }
 
+/*!
+ * \internal
+ * \brief Find the static payload type mapping for the format.
+ * \since 14.0.0
+ *
+ * \param asterisk_format Non-zero if the given Asterisk format is present
+ * \param format Asterisk format to look for
+ * \param code The non-Asterisk format code to look for
+ *
+ * \retval Numerical payload type
+ * \retval -1 if not found.
+ */
+static int find_static_payload_type(int asterisk_format, const struct ast_format *format, int code)
+{
+	int idx;
+	int payload = -1;
+
+	if (!asterisk_format) {
+		ast_rwlock_rdlock(&static_RTP_PT_lock);
+		for (idx = 0; idx < AST_RTP_MAX_PT; ++idx) {
+			if (static_RTP_PT[idx]
+				&& !static_RTP_PT[idx]->asterisk_format
+				&& static_RTP_PT[idx]->rtp_code == code) {
+				payload = idx;
+				break;
+			}
+		}
+		ast_rwlock_unlock(&static_RTP_PT_lock);
+	} else if (format) {
+		ast_rwlock_rdlock(&static_RTP_PT_lock);
+		for (idx = 0; idx < AST_RTP_MAX_PT; ++idx) {
+			if (static_RTP_PT[idx]
+				&& static_RTP_PT[idx]->asterisk_format
+				&& ast_format_cmp(format, static_RTP_PT[idx]->format)
+					!= AST_FORMAT_CMP_NOT_EQUAL) {
+				payload = idx;
+				break;
+			}
+		}
+		ast_rwlock_unlock(&static_RTP_PT_lock);
+	}
+
+	return payload;
+}
+
 int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
 {
 	struct ast_rtp_payload_type *type;
-	int i;
+	int idx;
 	int payload = -1;
 
-	ast_rwlock_rdlock(&codecs->codecs_lock);
-	for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) {
-		type = AST_VECTOR_GET(&codecs->payloads, i);
-		if (!type) {
-			continue;
+	if (!asterisk_format) {
+		ast_rwlock_rdlock(&codecs->codecs_lock);
+		for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); ++idx) {
+			type = AST_VECTOR_GET(&codecs->payload_mapping_rx, idx);
+			if (!type) {
+				continue;
+			}
+
+			if (!type->asterisk_format
+				&& type->rtp_code == code) {
+				if (type->primary_mapping) {
+					payload = idx;
+					break;
+				}
+				if (payload == -1) {
+					payload = idx;
+				}
+			}
 		}
+		ast_rwlock_unlock(&codecs->codecs_lock);
+	} else if (format) {
+		ast_rwlock_rdlock(&codecs->codecs_lock);
+		for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_rx); ++idx) {
+			type = AST_VECTOR_GET(&codecs->payload_mapping_rx, idx);
+			if (!type) {
+				continue;
+			}
 
-		if ((asterisk_format && format && ast_format_cmp(format, type->format) == AST_FORMAT_CMP_EQUAL)
-			|| (!asterisk_format && type->rtp_code == code)) {
-			payload = i;
-			break;
+			if (type->asterisk_format
+				&& ast_format_cmp(format, type->format) == AST_FORMAT_CMP_EQUAL) {
+				if (type->primary_mapping) {
+					payload = idx;
+					break;
+				}
+				if (payload == -1) {
+					payload = idx;
+				}
+			}
 		}
+		ast_rwlock_unlock(&codecs->codecs_lock);
 	}
-	ast_rwlock_unlock(&codecs->codecs_lock);
 
 	if (payload < 0) {
-		ast_rwlock_rdlock(&static_RTP_PT_lock);
-		for (i = 0; i < AST_RTP_MAX_PT; i++) {
-			if (!static_RTP_PT[i]) {
+		payload = find_static_payload_type(asterisk_format, format, code);
+	}
+
+	return payload;
+}
+
+int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)
+{
+	struct ast_rtp_payload_type *type;
+	int idx;
+	int payload = -1;
+
+	if (!asterisk_format) {
+		ast_rwlock_rdlock(&codecs->codecs_lock);
+		for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) {
+			type = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx);
+			if (!type) {
 				continue;
 			}
-			if (static_RTP_PT[i]->asterisk_format && asterisk_format && format &&
-				(ast_format_cmp(format, static_RTP_PT[i]->format) != AST_FORMAT_CMP_NOT_EQUAL)) {
-				payload = i;
+
+			if (!type->asterisk_format
+				&& type->rtp_code == code) {
+				payload = idx;
 				break;
-			} else if (!static_RTP_PT[i]->asterisk_format && !asterisk_format &&
-				(static_RTP_PT[i]->rtp_code == code)) {
-				payload = i;
+			}
+		}
+		ast_rwlock_unlock(&codecs->codecs_lock);
+	} else if (format) {
+		ast_rwlock_rdlock(&codecs->codecs_lock);
+		for (idx = 0; idx < AST_VECTOR_SIZE(&codecs->payload_mapping_tx); ++idx) {
+			type = AST_VECTOR_GET(&codecs->payload_mapping_tx, idx);
+			if (!type) {
+				continue;
+			}
+
+			if (type->asterisk_format
+				&& ast_format_cmp(format, type->format) == AST_FORMAT_CMP_EQUAL) {
+				payload = idx;
 				break;
 			}
 		}
-		ast_rwlock_unlock(&static_RTP_PT_lock);
+		ast_rwlock_unlock(&codecs->codecs_lock);
+	}
+
+	if (payload < 0) {
+		payload = find_static_payload_type(asterisk_format, format, code);
 	}
 
 	return payload;
 }
 
-int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int code)
+int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int payload)
 {
 	struct ast_rtp_payload_type *type;
 	int res = -1;
 
 	ast_rwlock_rdlock(&codecs->codecs_lock);
-	if (code < AST_VECTOR_SIZE(&codecs->payloads)) {
-		type = AST_VECTOR_GET(&codecs->payloads, code);
+	if (payload < AST_VECTOR_SIZE(&codecs->payload_mapping_tx)) {
+		type = AST_VECTOR_GET(&codecs->payload_mapping_tx, payload);
 		if (type) {
-			res = type->payload;
+			res = payload;
 		}
 	}
 	ast_rwlock_unlock(&codecs->codecs_lock);
@@ -1192,13 +1536,13 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c_dst, st
 		goto done;
 	}
 
-	ast_rtp_codecs_payloads_copy(&instance_src->codecs, &instance_dst->codecs, instance_dst);
+	ast_rtp_codecs_payloads_xover(&instance_src->codecs, &instance_dst->codecs, instance_dst);
 
 	if (vinstance_dst && vinstance_src) {
-		ast_rtp_codecs_payloads_copy(&vinstance_src->codecs, &vinstance_dst->codecs, vinstance_dst);
+		ast_rtp_codecs_payloads_xover(&vinstance_src->codecs, &vinstance_dst->codecs, vinstance_dst);
 	}
 	if (tinstance_dst && tinstance_src) {
-		ast_rtp_codecs_payloads_copy(&tinstance_src->codecs, &tinstance_dst->codecs, tinstance_dst);
+		ast_rtp_codecs_payloads_xover(&tinstance_src->codecs, &tinstance_dst->codecs, tinstance_dst);
 	}
 
 	if (glue_dst->update_peer(c_dst, instance_src, vinstance_src, tinstance_src, cap_src, 0)) {
@@ -1772,6 +2116,7 @@ static void add_static_payload(int map, struct ast_format *format, int rtp_code)
 			type->rtp_code = rtp_code;
 		}
 		type->payload = map;
+		type->primary_mapping = 1;
 		ao2_cleanup(static_RTP_PT[map]);
 		static_RTP_PT[map] = type;
 	}
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index cca19698c8cc7166193b5fdd299001894c9a0005..545e21634042a195cc2be6f2c6cba011625806de 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -2690,7 +2690,7 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit)
 	}
 
 	/* Grab the payload that they expect the RFC2833 packet to be received in */
-	payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_DTMF);
+	payload = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_DTMF);
 
 	rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
 	rtp->send_duration = 160;
@@ -3417,10 +3417,8 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
 	}
 
 	/* Grab the subclass and look up the payload we are going to use */
-	codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance),
-	                                    1,
-	                                    frame->subclass.format,
-	                                    0);
+	codec = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance),
+		1, frame->subclass.format, 0);
 	if (codec < 0) {
 		ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n",
 			ast_format_get_name(frame->subclass.format));
@@ -4206,7 +4204,8 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
 	}
 
 	/* Otherwise adjust bridged payload to match */
-	bridged_payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance1), payload_type->asterisk_format, payload_type->format, payload_type->rtp_code);
+	bridged_payload = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance1),
+		payload_type->asterisk_format, payload_type->format, payload_type->rtp_code);
 
 	/* If no codec could be matched between instance and instance1, then somehow things were made incompatible while we were still bridged.  Bail. */
 	if (bridged_payload < 0) {
@@ -5012,7 +5011,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
 		return -1;
 	}
 
-	payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_CN);
+	payload = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_CN);
 
 	level = 127 - (level & 0x7f);
 
diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c
index 887eed229864f258be51e18d9d88b431eb1a9e75..192f3d13735f0ab92206a0e59296071565717bf2 100644
--- a/res/res_rtp_multicast.c
+++ b/res/res_rtp_multicast.c
@@ -247,7 +247,9 @@ static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_fra
 	}
 
 	/* Grab the actual payload number for when we create the RTP packet */
-	if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass.format, 0)) < 0) {
+	codec = ast_rtp_codecs_payload_code_tx(ast_rtp_instance_get_codecs(instance),
+		1, frame->subclass.format, 0);
+	if (codec < 0) {
 		return -1;
 	}