[Asterisk-code-review] codec_negotiation: Implement outgoing_call_offer_pref (asterisk[master])

George Joseph asteriskteam at digium.com
Mon Mar 16 16:06:30 CDT 2020


George Joseph has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/13945 )


Change subject: codec_negotiation: Implement outgoing_call_offer_pref
......................................................................

codec_negotiation: Implement outgoing_call_offer_pref

Based on this new endpoint setting, a joint list of preferred codecs
between those received from the Asterisk core (remote), and those
specified in the endpoint's "allow" parameter (local) is created and
is used to create the outgoing SDP offer.

* Add outgoing_call_offer_pref to pjsip_configuration (endpoint)

* Add "call_direction" to res_pjsip_session.

* Update pjsip_session_caps.c to make the functions more generic
  so they could be used for both incoming and outgoing and created
  ast_sip_session_join_call_offer_streams().

* Update ast_sip_session_create_outgoing to create the
  pending_media_state->topology with the results of
  ast_sip_session_join_call_offer_streams().

ASTERISK-28777

Change-Id: Id4ec0b4a906c2ae5885bf947f101c59059935437
---
M channels/chan_pjsip.c
M configs/samples/pjsip.conf.sample
A doc/CHANGES-staging/res_pjsip_call_offer_pref.txt
D doc/CHANGES-staging/res_pjsip_incoming_call_offer_pref.txt
M include/asterisk/res_pjsip.h
M include/asterisk/res_pjsip_session.h
M include/asterisk/res_pjsip_session_caps.h
M res/res_pjsip.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_sdp_rtp.c
M res/res_pjsip_session.c
M res/res_pjsip_session/pjsip_session_caps.c
12 files changed, 274 insertions(+), 244 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/45/13945/1

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 0ec1791..b88f565 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2691,7 +2691,6 @@
 	}
 
 	session = req_data.session;
-
 	if (!(session->channel = chan_pjsip_new(session, AST_STATE_DOWN, NULL, NULL, assignedids, requestor, NULL))) {
 		/* Session needs to be terminated prematurely */
 		return NULL;
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 695ba5d..52b64ba 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -798,16 +798,47 @@
                    ; "0" or not enabled)
 ;contact_user= ; On outgoing requests, force the user portion of the Contact
                ; header to this value (default: "")
-;incoming_call_offer_pref= ; Sets the preferred codecs, and order to use between
-                           ; those received in the offer, and those set in this
-                           ; configuration's allow line. Valid values include:
+;incoming_call_offer_pref= ; Based on this setting, a joint list of
+                           ; preferred codecs between those received in an
+                           ; incoming SDP offer (remote), and those specified
+                           ; in the endpoint's "allow" parameter (local)
+                           ; is created and is passed to the Asterisk core.
                            ;
-                           ; local - prefer and order by configuration (default).
-                           ; local_single - prefer and order by configuration,
-                           ;     but only choose 'top' most codec
-                           ; remote - prefer and order by incoming sdp.
-                           ; remote_single - prefer and order by incoming sdp,
-                           ;     but only choose 'top' most codec
+                           ; local - Include all codecs in the local list that
+                           ; are also in the remote list preserving the local
+                           ; order. (default).
+                           ; local_first - Include only the first codec in the
+                           ; local list that is also in the remote list.
+                           ; remote - Include all codecs in the remote list that
+                           ; are also in the local list preserving remote list
+                           ; order.
+                           ; remote_first - Include only the first codec in
+                           ; the remote list that is also in the local list.
+;outgoing_call_offer_pref= ; Based on this setting, a joint list of
+                           ; preferred codecs between those received from the
+                           ; Asterisk core (remote), and those specified in
+                           ; the endpoint's "allow" parameter (local) is
+                           ; created and is used to create the outgoing SDP
+                           ; offer.
+                           ;
+                           ; local - Include all codecs in the local list that
+                           ; are also in the remote list preserving the local
+                           ; order. (default).
+                           ; local_merge - Include all codecs in BOTH lists
+                           ; preserving the local list order.  Codes in the
+                           ; remote list not in the local list will be placed
+                           ; at the end of the joint list.
+                           ; local_first - Include only the first codec in the
+                           ; local list.
+                           ; remote - Include all codecs in the remote list that
+                           ; are also in the local list preserving remote list
+                           ; order.
+                           ; remote_merge - Include all codecs in BOTH lists
+                           ; preserving the remote list order.  Codes in the
+                           ; local list not in the remote list will be placed
+                           ; at the end of the joint list.
+                           ; remote_first - Include only the first codec in
+                           ; the remote list.
 ;preferred_codec_only=yes       ; Respond to a SIP invite with the single most preferred codec
                                 ; rather than advertising all joint codec capabilities. This
                                 ; limits the other side's codec choice to exactly what we prefer.
diff --git a/doc/CHANGES-staging/res_pjsip_call_offer_pref.txt b/doc/CHANGES-staging/res_pjsip_call_offer_pref.txt
new file mode 100644
index 0000000..c8b8747
--- /dev/null
+++ b/doc/CHANGES-staging/res_pjsip_call_offer_pref.txt
@@ -0,0 +1,8 @@
+Subject: res_pjsip
+Subject: res_pjsip_session
+Master-Only: True
+
+Two new options, incoming_call_offer_pref and outgoing_call_offer_pref
+have been added to res_pjsip endpoints that specify the preferred order
+of codecs to use between those received/sent in an SDP offer and those
+set in the endpoint configuration.
diff --git a/doc/CHANGES-staging/res_pjsip_incoming_call_offer_pref.txt b/doc/CHANGES-staging/res_pjsip_incoming_call_offer_pref.txt
deleted file mode 100644
index 5a12052..0000000
--- a/doc/CHANGES-staging/res_pjsip_incoming_call_offer_pref.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-Subject: res_pjsip
-Subject: res_pjsip_session
-Master-Only: True
-
-A new option, incoming_call_offer_pref, was added to res_pjsip endpoints that
-specifies the preferred order of codecs to use between those received in the
-offer, and those set in the configuration.
-
-Valid values include:
-  local - prefer and order by configuration (default).
-  local_single - prefer and order by configuration, but only choose 'top'
-                 most codec
-  remote - prefer and order by incoming sdp.
-  remote_single - prefer and order by incoming sdp, but only choose 'top' most
-                  most codec
-
-Example A:
-  [alice]
-  type=endpoint
-  incoming_call_offer_pref=local
-  allow=!all,opus,alaw,ulaw
-
-  Alice's incoming sdp=g722,ulaw,alaw
-  RESULT: alaw,ulaw
-
-Example B:
-  [alice]
-  type=endpoint
-  incoming_call_offer_pref=local_single
-  allow=!all,opus,alaw,ulaw
-
-  Alice's incoming sdp=g722,ulaw,alaw
-  RESULT: alaw
-
-Example C:
-  [alice]
-  type=endpoint
-  incoming_call_offer_pref=remote
-  allow=!all,opus,alaw,ulaw
-
-  Alice's incoming sdp=g722,ulaw,alaw
-  RESULT: ulaw,alaw
-
-Example D:
-  [alice]
-  type=endpoint
-  incoming_call_offer_pref=remote_single
-  allow=!all,opus,alaw,ulaw
-
-  Alice's incoming sdp=g722,ulaw,alaw
-  RESULT: ulaw
-
-
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 816e614..cda6337 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -511,22 +511,31 @@
 
 /*!
  * \brief Incoming/Outgoing call offer/answer joint codec preference.
+ *
+ * The default is INTERSECT ALL LOCAL.
  */
 enum ast_sip_call_codec_pref {
+	/*! Two bits for merge */
+	/*! Union of local and remote */
+	AST_SIP_CALL_CODEC_PREF_INTERSECT =	1 << 0,
+	/*! Intersection of local and remote */
+	AST_SIP_CALL_CODEC_PREF_UNION =		1 << 1,
+
+	/*! Two bits for filter */
+	/*! No filter */
+	AST_SIP_CALL_CODEC_PREF_ALL =	 	1 << 2,
+	/*! Only the first */
+	AST_SIP_CALL_CODEC_PREF_FIRST = 	1 << 3,
+
+	/*! Two bits for preference and sort   */
 	/*! Prefer, and order by local values */
-	AST_SIP_CALL_CODEC_PREF_LOCAL,
-	/*! Prefer, and order by local values (intersection) */
-	AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT,
-	/*! Prefer, and order by local values (top/first only) */
-	AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE,
+	AST_SIP_CALL_CODEC_PREF_LOCAL = 	1 << 4,
 	/*! Prefer, and order by remote values */
-	AST_SIP_CALL_CODEC_PREF_REMOTE,
-	/*! Prefer, and order by remote values (intersection) */
-	AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT,
-	/*! Prefer, and order by remote values (top/first only) */
-	AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE,
+	AST_SIP_CALL_CODEC_PREF_REMOTE = 	1 << 5,
 };
 
+#define ast_sip_call_codec_pref_test(pref, codec_pref) (!!(pref & AST_SIP_CALL_CODEC_PREF_ ## codec_pref ))
+
 /*!
  * \brief Session timers options
  */
@@ -770,6 +779,8 @@
 	unsigned int webrtc;
 	/*! Codec preference for an incoming offer */
 	enum ast_sip_call_codec_pref incoming_call_offer_pref;
+	/*! Codec preference for an outgoing offer */
+	enum ast_sip_call_codec_pref outgoing_call_offer_pref;
 };
 
 /*!
@@ -3223,6 +3234,18 @@
 int ast_sip_str_to_dtmf(const char *dtmf_mode);
 
 /*!
+ * \brief Convert the call codec preference enum to a string
+ * \since 18.0.0
+ *
+ * \param pref the call codec preference setting
+ *
+ * \returns a constant string with either the setting value or 'unknown'
+ *
+ */
+const char *ast_sip_call_codec_pref_to_str(enum ast_sip_call_codec_pref pref);
+
+
+/*!
  * \brief Transport shutdown monitor callback.
  * \since 13.18.0
  *
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index a5ae6f1..7879c75 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -80,8 +80,6 @@
 	struct ast_sip_session_sdp_handler *handler;
 	/*! \brief Holds SRTP information */
 	struct ast_sdp_srtp *srtp;
-	/*! \brief Media format capabilities */
-	struct ast_sip_session_caps *caps;
 	/*! \brief What type of encryption is in use on this stream */
 	enum ast_sip_session_media_encryption encryption;
 	/*! \brief The media transport in use for this stream */
@@ -157,6 +155,10 @@
 /*! \brief Opaque struct controlling the suspension of the session's serializer. */
 struct ast_sip_session_suspender;
 
+enum ast_sip_session_call_direction {
+	AST_SIP_SESSION_INCOMING_CALL = 0,
+	AST_SIP_SESSION_OUTGOING_CALL,
+};
 /*!
  * \brief A structure describing a SIP session
  *
@@ -224,6 +226,8 @@
 	pjsip_uri *request_uri;
 	/* Media statistics for negotiated RTP streams */
 	AST_VECTOR(, struct ast_rtp_instance_stats *) media_stats;
+	/* The direction of the call */
+	enum ast_sip_session_call_direction call_direction;
 };
 
 typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
diff --git a/include/asterisk/res_pjsip_session_caps.h b/include/asterisk/res_pjsip_session_caps.h
index 810a1e6..2518f96 100644
--- a/include/asterisk/res_pjsip_session_caps.h
+++ b/include/asterisk/res_pjsip_session_caps.h
@@ -24,59 +24,37 @@
 struct ast_sip_session_caps;
 
 /*!
- * \brief Allocate a SIP session capabilities object.
+ * \brief Get a new stream with joint capabilities between request and endpoint
  * \since 18.0.0
  *
- * \retval An ao2 allocated SIP session capabilities object, or NULL on error
+ * \param session The session
+ * \param request_stream The requested stream from the remote or core
+ *
+ * \retval A pointer to a new stream with the joint capabilities
  */
-struct ast_sip_session_caps *ast_sip_session_caps_alloc(void);
+struct ast_stream *ast_sip_session_join_call_offer_streams(const struct ast_sip_session *session,
+	struct ast_stream *request_stream);
 
 /*!
- * \brief Set the incoming call offer capabilities for a session.
- * \since 18.0.0
- *
- * This will replace any capabilities already present.
- *
- * \param caps A session's capabilities object
- * \param cap The capabilities to set it to
- */
-void ast_sip_session_set_incoming_call_offer_cap(struct ast_sip_session_caps *caps,
-	struct ast_format_cap *cap);
-
-/*!
- * \brief Get the incoming call offer capabilities.
- * \since 18.0.0
- *
- * \note Returned objects reference is not incremented.
- *
- * \param caps A session's capabilities object
- *
- * \retval An incoming call offer capabilities object
- */
-const struct ast_format_cap *ast_sip_session_get_incoming_call_offer_cap(
-	const struct ast_sip_session_caps *caps);
-
-/*!
- * \brief Make the incoming call offer capabilities for a session.
+ * \brief Make the call offer capabilities for a session.
  * \since 18.0.0
  *
  * Creates and sets a list of joint capabilities between the given remote
  * capabilities, and pre-configured ones. The resulting joint list is then
  * stored, and 'owned' (reference held) by the session.
  *
- * If the incoming capabilities have been set elsewhere, this will not replace
+ * If the capabilities have been set elsewhere, this will not replace
  * those. It will however, return a pointer to the current set.
  *
  * \note Returned object's reference is not incremented.
  *
  * \param session The session
- * \param session_media An associated media session
- * \param remote Capabilities of a device
+ * \param media_type The media type
+ * \param remote Capabilities received in an SDP offer or from the core
  *
  * \retval A pointer to the incoming call offer capabilities
  */
-const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
-	const struct ast_sip_session *session, const struct ast_sip_session_media *session_media,
-	const struct ast_format_cap *remote);
+struct ast_format_cap *ast_sip_session_join_call_offer_cap(const struct ast_sip_session *session,
+	enum ast_media_type media_type, const struct ast_format_cap *remote);
 
 #endif /* RES_PJSIP_SESSION_CAPS_H */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 4d77a6d..b918828 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -926,22 +926,62 @@
 					<synopsis>Respond to a SIP invite with the single most preferred codec rather than advertising all joint codec capabilities. This limits the other side's codec choice to exactly what we prefer.</synopsis>
 				</configOption>
 				<configOption name="incoming_call_offer_pref" default="local">
-					<synopsis>After receiving an incoming offer create a list of preferred codecs between
-					those received in the SDP offer, and those specified in endpoint configuration.</synopsis>
+					<synopsis>Preferences for selecting codecs for an incoming call.</synopsis>
 					<description>
-						<note><para>This list will consist of only those codecs found in both.</para></note>
+						<para>Based on this setting, a joint list of preferred codecs between those
+						received in an incoming SDP offer (remote), and those specified in the
+						endpoint's "allow" parameter (local) es created and is passed to the Asterisk
+						core. </para>
+						<note><para>This list will consist of only those codecs found in both lists.</para></note>
 						<enumlist>
 							<enum name="local"><para>
-								Order by the endpoint configuration allow line (default)
+								Include all codecs in the local list that are also in the remote list
+								preserving the local order.  (default).
 							</para></enum>
-							<enum name="local_single"><para>
-								Order by the endpoint configuration allow line, but the list will only contain the first, or 'top' item
+							<enum name="local_first"><para>
+								Include only the first codec in the local list that is also in the remote list.
 							</para></enum>
 							<enum name="remote"><para>
-								Order by what is received in the SDP offer
+								Include all codecs in the remote list that are also in the local list
+								preserving the remote order.
 							</para></enum>
-							<enum name="remote_single"><para>
-								Order by what is received in the SDP offer, but the list will only contain the first, or 'top' item
+							<enum name="remote_first"><para>
+								Include only the first codec in the remote list that is also in the local list.
+							</para></enum>
+						</enumlist>
+					</description>
+				</configOption>
+				<configOption name="outgoing_call_offer_pref" default="local">
+					<synopsis>Preferences for selecting codecs for an outgoing call.</synopsis>
+					<description>
+						<para>Based on this setting, a joint list of preferred codecs between
+						those received from the Asterisk core (remote), and those specified in
+						the endpoint's "allow" parameter (local) is created and is used to create
+						the outgoing SDP offer.</para>
+						<enumlist>
+							<enum name="local"><para>
+								Include all codecs in the local list that are also in the remote list
+								preserving the local order. (default).
+							</para></enum>
+							<enum name="local_merge"><para>
+								Include all codecs in BOTH lists preserving the local order.
+								Remote codecs not in the local list will be placed at the end
+								of the joint list.
+							</para></enum>
+							<enum name="local_first"><para>
+								Include only the first codec in the local list.
+							</para></enum>
+							<enum name="remote"><para>
+								Include all codecs in the remote list that are also in the local list
+								preserving the remote order.
+							</para></enum>
+							<enum name="remote_merge"><para>
+								Include all codecs in BOTH lists preserving the remote order.
+								Local codecs not in the remote list will be placed at the end
+								of the joint list.
+							</para></enum>
+							<enum name="remote_first"><para>
+								Include only the first codec in the remote list.
 							</para></enum>
 						</enumlist>
 					</description>
@@ -5044,6 +5084,29 @@
 	return result;
 }
 
+const char *ast_sip_call_codec_pref_to_str(enum ast_sip_call_codec_pref pref)
+{
+	const char *value;
+
+	if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
+		value = "local";
+	} else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
+		value = "local_merge";
+	} else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) {
+		value = "local_first";
+	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
+		value = "remote";
+	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
+		value = "remote_merge";
+	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, FIRST)) {
+		value = "remote_first";
+	} else {
+		value = "unknown";
+	}
+
+	return value;
+}
+
 /*!
  * \brief Set name and number information on an identity header.
  *
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 1d61558..280aeef 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1121,43 +1121,57 @@
 	return 0;
 }
 
-static const char *sip_call_codec_pref_strings[] = {
-	[AST_SIP_CALL_CODEC_PREF_LOCAL] = "local",
-	[AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT] = "local_limit",
-	[AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE] = "local_single",
-	[AST_SIP_CALL_CODEC_PREF_REMOTE] = "remote",
-	[AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT] = "remote_limit",
-	[AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE] = "remote_single",
-};
-
-static int incoming_call_offer_pref_handler(const struct aco_option *opt,
+static int call_offer_pref_handler(const struct aco_option *opt,
 	struct ast_variable *var, void *obj)
 {
 	struct ast_sip_endpoint *endpoint = obj;
-	unsigned int i;
+	enum ast_sip_call_codec_pref pref;
+	int outgoing = strcmp(var->name, "outgoing_call_offer_pref") == 0;
 
-	for (i = 0; i < ARRAY_LEN(sip_call_codec_pref_strings); ++i) {
-		if (!strcmp(var->value, sip_call_codec_pref_strings[i])) {
-			/* Local and remote limit are not available values for this option */
-			if (i == AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT ||
-				i == AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT) {
-				return -1;
-			}
-
-			endpoint->media.incoming_call_offer_pref = i;
-			return 0;
-		}
+	if (strcmp(var->value, "local") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL;
+	} else if (outgoing && strcmp(var->value, "local_merge") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL;
+	} else if (strcmp(var->value, "local_first") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST;
+	} else if (strcmp(var->value, "remote") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL;
+	} else if (outgoing && strcmp(var->value, "remote_merge") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL;
+	} else if (strcmp(var->value, "remote_first") == 0) {
+		pref = AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_FIRST;
+	} else {
+		return -1;
 	}
 
-	return -1;
+	if (outgoing) {
+		endpoint->media.outgoing_call_offer_pref = pref;
+	} else {
+		endpoint->media.incoming_call_offer_pref = pref;
+	}
+
+	return 0;
 }
 
 static int incoming_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)
 {
 	const struct ast_sip_endpoint *endpoint = obj;
 
-	if (ARRAY_IN_BOUNDS(endpoint->media.incoming_call_offer_pref, sip_call_codec_pref_strings)) {
-		*buf = ast_strdup(sip_call_codec_pref_strings[endpoint->media.incoming_call_offer_pref]);
+	*buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.incoming_call_offer_pref));
+	if (!(*buf)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int outgoing_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_endpoint *endpoint = obj;
+
+	*buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.outgoing_call_offer_pref));
+	if (!(*buf)) {
+		return -1;
 	}
 
 	return 0;
@@ -2009,7 +2023,9 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "suppress_q850_reason_headers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, suppress_q850_reason_headers));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ignore_183_without_sdp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ignore_183_without_sdp));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_call_offer_pref", "local",
-		incoming_call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);
+		call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_call_offer_pref", "local",
+		call_offer_pref_handler, outgoing_call_offer_pref_to_str, NULL, 0, 0);
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 4bbbe92..5b1d1ef 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -376,13 +376,13 @@
 
 static int apply_cap_to_bundled(struct ast_sip_session_media *session_media,
 	struct ast_sip_session_media *session_media_transport,
-	struct ast_stream *asterisk_stream, const struct ast_format_cap *joint)
+	struct ast_stream *asterisk_stream, struct ast_format_cap *joint)
 {
 	if (!joint) {
 		return -1;
 	}
 
-	ast_stream_set_formats(asterisk_stream, (struct ast_format_cap *)joint);
+	ast_stream_set_formats(asterisk_stream, joint);
 
 	/* If this is a bundled stream then apply the payloads to RTP instance acting as transport to prevent conflicts */
 	if (session_media_transport != session_media && session_media->bundled) {
@@ -401,15 +401,16 @@
 			ao2_ref(format, -1);
 		}
 	}
+	ao2_ref(joint, -1);
 
 	return 0;
 }
 
-static const struct ast_format_cap *set_incoming_call_offer_cap(
+static struct ast_format_cap *set_incoming_call_offer_cap(
 	struct ast_sip_session *session, struct ast_sip_session_media *session_media,
 	const struct pjmedia_sdp_media *stream)
 {
-	const struct ast_format_cap *incoming_call_offer_cap;
+	struct ast_format_cap *incoming_call_offer_cap;
 	struct ast_format_cap *remote;
 	struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT;
 	int fmts = 0;
@@ -425,8 +426,8 @@
 	get_codecs(session, stream, &codecs, session_media);
 	ast_rtp_codecs_payload_formats(&codecs, remote, &fmts);
 
-	incoming_call_offer_cap = ast_sip_session_join_incoming_call_offer_cap(
-		session, session_media, remote);
+	incoming_call_offer_cap = ast_sip_session_join_call_offer_cap(
+		session, session_media->type, remote);
 
 	ao2_ref(remote, -1);
 
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 0c752b8..853add2 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -467,8 +467,6 @@
 
 	ast_free(session_media->mid);
 	ast_free(session_media->remote_mslabel);
-
-	ao2_cleanup(session_media->caps);
 }
 
 struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,
@@ -527,12 +525,6 @@
 		} else {
 			session_media->bundle_group = -1;
 		}
-
-		session_media->caps = ast_sip_session_caps_alloc();
-		if (!session_media->caps) {
-			ao2_ref(session_media, -1);
-			return NULL;
-		}
 	}
 
 	if (AST_VECTOR_REPLACE(&media_state->sessions, position, session_media)) {
@@ -2699,6 +2691,8 @@
 		return NULL;
 	}
 	session->aor = ao2_bump(found_aor);
+	session->call_direction = AST_SIP_SESSION_OUTGOING_CALL;
+
 	ast_party_id_copy(&session->id, &endpoint->id.self);
 
 	if (ast_stream_topology_get_count(req_topology) > 0) {
@@ -2707,8 +2701,6 @@
 
 		for (i = 0; i < ast_stream_topology_get_count(req_topology); ++i) {
 			struct ast_stream *req_stream;
-			struct ast_format_cap *req_cap;
-			struct ast_format_cap *joint_cap;
 			struct ast_stream *clone_stream;
 
 			req_stream = ast_stream_topology_get_stream(req_topology, i);
@@ -2717,37 +2709,7 @@
 				continue;
 			}
 
-			req_cap = ast_stream_get_formats(req_stream);
-
-			joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-			if (!joint_cap) {
-				continue;
-			}
-
-			ast_format_cap_get_compatible(req_cap, endpoint->media.codecs, joint_cap);
-			if (!ast_format_cap_count(joint_cap)) {
-				ao2_ref(joint_cap, -1);
-				continue;
-			}
-
-			clone_stream = ast_stream_clone(req_stream, NULL);
-			if (!clone_stream) {
-				ao2_ref(joint_cap, -1);
-				continue;
-			}
-
-			if (ast_stream_get_type(req_stream) == AST_MEDIA_TYPE_AUDIO) {
-				/*
-				 * By appending codecs from the endpoint after compatible ones this
-				 * guarantees that priority is given to those while also allowing
-				 * translation to occur for non-compatible.
-				 */
-				ast_format_cap_append_from_cap(joint_cap,
-					endpoint->media.codecs, AST_MEDIA_TYPE_AUDIO);
-			}
-
-			ast_stream_set_formats(clone_stream, joint_cap);
-			ao2_ref(joint_cap, -1);
+			clone_stream = ast_sip_session_join_call_offer_streams(session, req_stream);
 
 			if (!session->pending_media_state->topology) {
 				session->pending_media_state->topology = ast_stream_topology_alloc();
@@ -3283,6 +3245,7 @@
 #endif
 		return;
 	}
+	session->call_direction = AST_SIP_SESSION_INCOMING_CALL;
 
 	/*
 	 * The current thread is supposed be the session serializer to prevent
diff --git a/res/res_pjsip_session/pjsip_session_caps.c b/res/res_pjsip_session/pjsip_session_caps.c
index e131200..03a452c 100644
--- a/res/res_pjsip_session/pjsip_session_caps.c
+++ b/res/res_pjsip_session/pjsip_session_caps.c
@@ -24,6 +24,7 @@
 #include "asterisk/format_cap.h"
 #include "asterisk/logger.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/stream.h"
 
 #include <pjsip_ua.h>
 
@@ -32,17 +33,22 @@
 #include "asterisk/res_pjsip_session_caps.h"
 
 struct ast_sip_session_caps {
-	struct ast_format_cap *incoming_call_offer_cap;
+	struct ast_format_cap *call_offer_cap;
 };
 
 static void log_caps(int level, const char *file, int line, const char *function,
 	const char *msg, const struct ast_sip_session *session,
-	const struct ast_sip_session_media *session_media, const struct ast_format_cap *local,
+	enum ast_media_type media_type, const struct ast_format_cap *local,
 	const struct ast_format_cap *remote, const struct ast_format_cap *joint)
 {
 	struct ast_str *s1;
 	struct ast_str *s2;
 	struct ast_str *s3;
+	int outgoing = session->call_direction == AST_SIP_SESSION_OUTGOING_CALL;
+	enum ast_sip_call_codec_pref pref =
+		outgoing
+		? session->endpoint->media.outgoing_call_offer_pref
+		: session->endpoint->media.incoming_call_offer_pref;
 
 	if (level == __LOG_DEBUG && !DEBUG_ATLEAST(3)) {
 		return;
@@ -52,91 +58,84 @@
 	s2 = remote ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;
 	s3 = joint ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;
 
-	ast_log(level, file, line, function, "'%s' %s '%s' capabilities -%s%s%s%s%s%s\n",
+	ast_log(level, file, line, function, "'%s' %s %s '%s' caps for pref '%s' -%s%s%s%s%s%s\n",
 		session->channel ? ast_channel_name(session->channel) :
 			ast_sorcery_object_get_id(session->endpoint),
-		msg ? msg : "-", ast_codec_media_type2str(session_media->type),
-		s1 ? " local: " : "", s1 ? ast_format_cap_get_names(local, &s1) : "",
+		msg ? msg : "-",
+		outgoing? "outgoing" : "incoming",
+		ast_codec_media_type2str(media_type),
+		ast_sip_call_codec_pref_to_str(pref),
 		s2 ? " remote: " : "", s2 ? ast_format_cap_get_names(remote, &s2) : "",
+		s1 ? " local: " : "", s1 ? ast_format_cap_get_names(local, &s1) : "",
 		s3 ? " joint: " : "", s3 ? ast_format_cap_get_names(joint, &s3) : "");
 }
 
-static void sip_session_caps_destroy(void *obj)
+struct ast_stream *ast_sip_session_join_call_offer_streams(const struct ast_sip_session *session,
+	struct ast_stream *remote_stream)
 {
-	struct ast_sip_session_caps *caps = obj;
+	struct ast_stream *joint_stream = ast_stream_clone(remote_stream, NULL);
+	struct ast_format_cap *remote = ast_stream_get_formats(remote_stream);
+	enum ast_media_type media_type = ast_stream_get_type(remote_stream);
+	struct ast_format_cap *joint = ast_sip_session_join_call_offer_cap(session, media_type, remote);
 
-	ao2_cleanup(caps->incoming_call_offer_cap);
+	ast_stream_set_formats(joint_stream, joint);
+	ao2_cleanup(joint);
+
+	return joint_stream;
 }
 
-struct ast_sip_session_caps *ast_sip_session_caps_alloc(void)
-{
-	return ao2_alloc_options(sizeof(struct ast_sip_session_caps),
-		sip_session_caps_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
-}
 
-void ast_sip_session_set_incoming_call_offer_cap(struct ast_sip_session_caps *caps,
-	struct ast_format_cap *cap)
-{
-	ao2_cleanup(caps->incoming_call_offer_cap);
-	caps->incoming_call_offer_cap = ao2_bump(cap);
-}
-
-const struct ast_format_cap *ast_sip_session_get_incoming_call_offer_cap(
-	const struct ast_sip_session_caps *caps)
-{
-	return caps->incoming_call_offer_cap;
-}
-
-const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
-	const struct ast_sip_session *session, const struct ast_sip_session_media *session_media,
+struct ast_format_cap *ast_sip_session_join_call_offer_cap(
+	const struct ast_sip_session *session, enum ast_media_type media_type,
 	const struct ast_format_cap *remote)
 {
-	enum ast_sip_call_codec_pref pref;
 	struct ast_format_cap *joint;
 	struct ast_format_cap *local;
+	enum ast_sip_call_codec_pref call_offer_pref =
+		session->call_direction == AST_SIP_SESSION_OUTGOING_CALL
+		? session->endpoint->media.outgoing_call_offer_pref
+		: session->endpoint->media.incoming_call_offer_pref;
 
-	joint = session_media->caps->incoming_call_offer_cap;
-
-	if (joint) {
-		/*
-		 * If the incoming call offer capabilities have been set elsewhere, e.g. dialplan
-		 * then those take precedence.
-		 */
-		return joint;
-	}
 
 	joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	local = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 
 	if (!joint || !local) {
-		ast_log(LOG_ERROR, "Failed to allocate %s incoming call offer capabilities\n",
-				ast_codec_media_type2str(session_media->type));
+		ast_log(LOG_ERROR, "Failed to allocate %s call offer capabilities\n",
+				ast_codec_media_type2str(media_type));
 
 		ao2_cleanup(joint);
 		ao2_cleanup(local);
 		return NULL;
 	}
 
-	pref = session->endpoint->media.incoming_call_offer_pref;
-	ast_format_cap_append_from_cap(local, session->endpoint->media.codecs,
-		session_media->type);
+	ast_format_cap_append_from_cap(local, session->endpoint->media.codecs, media_type);
 
-	if (pref < AST_SIP_CALL_CODEC_PREF_REMOTE) {
-		ast_format_cap_get_compatible(local, remote, joint); /* Prefer local */
+	if (ast_sip_call_codec_pref_test(call_offer_pref, LOCAL)) {
+		if (ast_sip_call_codec_pref_test(call_offer_pref, INTERSECT)) {
+			ast_format_cap_get_compatible(local, remote, joint); /* Get common, prefer local */
+		} else {
+			ast_format_cap_append_from_cap(joint, local, media_type); /* Add local */
+			ast_format_cap_append_from_cap(joint, remote, media_type); /* Then remote */
+		}
 	} else {
-		ast_format_cap_get_compatible(remote, local, joint); /* Prefer remote */
+		if (ast_sip_call_codec_pref_test(call_offer_pref, INTERSECT)) {
+			ast_format_cap_get_compatible(remote, local, joint); /* Get common, prefer remote */
+		} else {
+			ast_format_cap_append_from_cap(joint, remote, media_type); /* Add remote */
+			ast_format_cap_append_from_cap(joint, local, media_type); /* Then local */
+		}
 	}
 
 	if (ast_format_cap_empty(joint)) {
-		log_caps(LOG_NOTICE, "No joint incoming", session, session_media, local, remote, NULL);
+		log_caps(LOG_NOTICE, "No joint", session, media_type, local, remote, NULL);
 
 		ao2_ref(joint, -1);
 		ao2_ref(local, -1);
 		return NULL;
 	}
 
-	if (pref == AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE ||
-		pref == AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE ||
+	if (ast_sip_call_codec_pref_test(call_offer_pref, FIRST) ||
 		session->endpoint->preferred_codec_only) {
 
 		/*
@@ -152,11 +151,9 @@
 		ao2_ref(single, -1);
 	}
 
-	log_caps(LOG_DEBUG, "Joint incoming", session, session_media, local, remote, joint);
+	log_caps(LOG_DEBUG, "Joint", session, media_type, local, remote, joint);
 
 	ao2_ref(local, -1);
 
-	ast_sip_session_set_incoming_call_offer_cap(session_media->caps, joint);
-
 	return joint;
 }

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/13945
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: Id4ec0b4a906c2ae5885bf947f101c59059935437
Gerrit-Change-Number: 13945
Gerrit-PatchSet: 1
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20200316/659f8dbd/attachment-0001.html>


More information about the asterisk-code-review mailing list