rtp_engine.c: Initial split of payload types into rx and tx mappings.
authorRichard Mudgett <rmudgett@digium.com>
Fri, 24 Jul 2015 00:24:04 +0000 (19:24 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Wed, 19 Aug 2015 22:09:58 +0000 (17:09 -0500)
There are numerous problems with the current implementation of the RTP
payload type mapping in Asterisk.  It uses only one mapping structure to
associate payload types to codecs.  The single mapping is overkill if all
of the payload type values are well known values.  Dynamic payload type
mappings do not work as well with the single mapping because RFC3264
allows each side of the link to negotiate different dynamic mappings for
what they want to receive.  Not only could you have the same codec mapped
for sending and receiving on different payload types you could wind up
with the same payload type mapped to different codecs for each direction.

1) An independent payload type mapping is needed for sending and
receiving.

2) The receive mapping needs to keep track of previous mappings because of
the slack to when negotiation happens and current packets in flight using
the old mapping arrive.

3) The transmit mapping only needs to keep track of the current negotiated
values since we are sending the packets and know when the switchover takes
place.

* Needed to create ast_rtp_codecs_payload_code_tx() and make some callers
use the new function because ast_rtp_codecs_payload_code() was used for
mappings in both directions.

* Needed to create ast_rtp_codecs_payloads_xover() for cases where we need
to pass preferred codec mappings to the peer channel for early media
bridging or when we need to prefer the offered mapping that RFC3264 says
we SHOULD use.

* ast_rtp_codecs_payloads_xover() and ast_rtp_codecs_payload_code_tx() are
the only new public functions created.  All the others were only used for
the tx or rx mapping direction so the function doxygen now reflects which
direction the function operates.

* chan_mgcp.c: Removed call to ast_rtp_codecs_payloads_clear() as doing
that makes no sense when processing an incoming SDP.  We would be wiping
out any mappings that we set for the possible outgoing SDP we sent
earlier.

ASTERISK-25166
Reported by: Kevin Harwell

ASTERISK-17410
Reported by: Boris Fox

Change-Id: Iaf6c227bca68cb7c414cf2fd4108a8ac98bd45ac

channels/chan_mgcp.c
channels/chan_unistim.c
include/asterisk/rtp_engine.h
main/rtp_engine.c
res/res_rtp_asterisk.c
res/res_rtp_multicast.c

index d9e182c..0210e8a 100644 (file)
@@ -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;
index a3c0ffd..874f6b2 100644 (file)
@@ -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) {
index d6a9be5..a52567a 100644 (file)
@@ -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
index 1375897..229af8f 100644 (file)
@@ -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;
        }
index cca1969..545e216 100644 (file)
@@ -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);
 
index 887eed2..192f3d1 100644 (file)
@@ -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;
        }