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