<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13945">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><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.<br><br>* Update ast_sip_session_create_outgoing to create the<br> pending_media_state->topology with the results of<br> ast_sip_session_create_joint_call_stream().<br><br>* The endpoint "preferred_codec_only" option now automatically sets<br> AST_SIP_CALL_CODEC_PREF_FIRST in incoming_call_offer_pref.<br><br>* A helper function ast_stream_get_format_count() was added to<br> streams to return the current count of formats.<br><br>ASTERISK-28777<br><br>Change-Id: Id4ec0b4a906c2ae5885bf947f101c59059935437<br>---<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 include/asterisk/stream.h<br>M main/stream.c<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>13 files changed, 408 insertions(+), 297 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index 695ba5d..93fb701 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -798,20 +798,55 @@</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(0, 100%, 40%);">-;preferred_codec_only=yes ; Respond to a SIP invite with the single most preferred codec</span><br><span style="color: hsl(0, 100%, 40%);">- ; rather than advertising all joint codec capabilities. This</span><br><span style="color: hsl(0, 100%, 40%);">- ; limits the other side's codec choice to exactly what we prefer.</span><br><span style="color: hsl(0, 100%, 40%);">- ; default is no.</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.</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. (default)</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 style="color: hsl(120, 100%, 40%);">+;preferred_codec_only=no ; Respond to a SIP invite with the single most</span><br><span style="color: hsl(120, 100%, 40%);">+ ; preferred codec rather than advertising all joint</span><br><span style="color: hsl(120, 100%, 40%);">+ ; codec capabilities. This limits the other side's</span><br><span style="color: hsl(120, 100%, 40%);">+ ; codec choice to exactly what we prefer.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; default is no.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; NOTE: This option is deprecated in favor</span><br><span style="color: hsl(120, 100%, 40%);">+ ; of incoming_call_offer_pref. Setting both</span><br><span style="color: hsl(120, 100%, 40%);">+ ; options is unsupported.</span><br><span> ;asymmetric_rtp_codec= ; Allow the sending and receiving codec to differ and</span><br><span> ; not be automatically matched (default: "no")</span><br><span> ;refer_blind_progress= ; Whether to notifies all the progress details on blind</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..c1d5490 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -511,23 +511,41 @@</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%);">+ /*! Intersection 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%);">+ /*! Union 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> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Returns true if the preference is set in the parameter</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 param A ast_flags struct with one or more of enum ast_sip_call_codec_pref set</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param codec_pref The last component of one of the enum values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 if the enum value is set</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 if not</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_sip_call_codec_pref_test(__param, __codec_pref) (!!(ast_test_flag( &__param, AST_SIP_CALL_CODEC_PREF_ ## __codec_pref )))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Session timers options</span><br><span> */</span><br><span> struct ast_sip_timer_options {</span><br><span>@@ -769,7 +787,9 @@</span><br><span> /*! Enable webrtc settings and defaults */</span><br><span> unsigned int webrtc;</span><br><span> /*! Codec preference for an incoming offer */</span><br><span style="color: hsl(0, 100%, 40%);">- enum ast_sip_call_codec_pref incoming_call_offer_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags 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%);">+ struct ast_flags outgoing_call_offer_pref;</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span>@@ -3223,6 +3243,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 flags 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%);">+ * \note Don't try to free the string!</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(struct ast_flags pref);</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..fd49a7b 100644</span><br><span>--- a/include/asterisk/res_pjsip_session.h</span><br><span>+++ b/include/asterisk/res_pjsip_session.h</span><br><span>@@ -30,6 +30,9 @@</span><br><span> #include "asterisk/sdp_srtp.h"</span><br><span> /* Needed for ast_media_type */</span><br><span> #include "asterisk/codec.h"</span><br><span style="color: hsl(120, 100%, 40%);">+/* Needed for pjmedia_sdp_session and pjsip_inv_session */</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip_ua.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> /* Forward declarations */</span><br><span> struct ast_sip_endpoint;</span><br><span>@@ -80,8 +83,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 +158,12 @@</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%);">+/*! \brief Indicates the call direction respective to Asterisk */</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 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>@@ -222,8 +229,10 @@</span><br><span> enum ast_sip_dtmf_mode dtmf;</span><br><span> /*! Initial incoming INVITE Request-URI. NULL otherwise. */</span><br><span> pjsip_uri *request_uri;</span><br><span style="color: hsl(0, 100%, 40%);">- /* Media statistics for negotiated RTP streams */</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! 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 respective to Asterisk */</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..0d7020f 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>@@ -20,63 +20,63 @@</span><br><span> </span><br><span> struct ast_format_cap;</span><br><span> struct ast_sip_session;</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_sip_session_media;</span><br><span style="color: hsl(0, 100%, 40%);">-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 Create joint capabilities</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%);">+ * Creates a list of joint capabilities between the given remote capabilities, and local ones.</span><br><span style="color: hsl(120, 100%, 40%);">+ * "local" and "remote" reference the values in ast_sip_call_codec_pref.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param remote The "remote" capabilities</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param local The "local" capabilities</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 codec_prefs One or more of enum ast_sip_call_codec_pref</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A pointer to the joint capabilities (which may be empty).</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL will be returned only if no memory was available to allocate the structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Returned object's reference must be released at some point,</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_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags codec_pref);</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(120, 100%, 40%);">+ * \brief Create a new stream of joint capabilities</span><br><span> * \since 18.0.0</span><br><span> *</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(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%);">- * Creates and sets a list of joint capabilities between the given remote</span><br><span style="color: hsl(0, 100%, 40%);">- * capabilities, and pre-configured ones. The resulting joint list is then</span><br><span style="color: hsl(0, 100%, 40%);">- * stored, and 'owned' (reference held) by the session.</span><br><span style="color: hsl(0, 100%, 40%);">- *</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(0, 100%, 40%);">- * those. It will however, return a pointer to the current set.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \note Returned object's reference is not incremented.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Creates a new stream with capabilities between the given session's local capabilities,</span><br><span style="color: hsl(120, 100%, 40%);">+ * and the remote stream's. Codec selection is based on the session->endpoint's codecs, the</span><br><span style="color: hsl(120, 100%, 40%);">+ * session->endpoint's codec call preferences, and the stream passed by the core (for</span><br><span style="color: hsl(120, 100%, 40%);">+ * outgoing calls) or created by the incoming SDP (for incoming calls).</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 remote The remote stream</span><br><span> *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval A pointer to the incoming call offer capabilities</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A pointer to a new stream with the joint capabilities (which may be empty),</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL will be returned only if no memory was available to allocate the structure.</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_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *remote);</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%);">+ * \brief Create joint capabilities</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%);">+ * Creates a list of joint capabilities between the given session's local capabilities,</span><br><span style="color: hsl(120, 100%, 40%);">+ * and the remote capabilities. Codec selection is based on the session->endpoint's codecs, the</span><br><span style="color: hsl(120, 100%, 40%);">+ * session->endpoint's codec call preferences, and the "remote" capabilities passed by the core (for</span><br><span style="color: hsl(120, 100%, 40%);">+ * outgoing calls) or created by the incoming SDP (for incoming calls).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param session The session</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 style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A pointer to the joint capabilities (which may be empty).</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL will be returned only if no memory was available to allocate the structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Returned object's reference must be released at some point,</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_sip_session_create_joint_call_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/include/asterisk/stream.h b/include/asterisk/stream.h</span><br><span>index ade740d..17275c5 100644</span><br><span>--- a/include/asterisk/stream.h</span><br><span>+++ b/include/asterisk/stream.h</span><br><span>@@ -167,6 +167,17 @@</span><br><span> struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the count of the current negotiated formats of a stream</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param stream The media stream</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The count of negotiated formats</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_get_format_count(const struct ast_stream *stream);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Set the current negotiated formats of a stream</span><br><span> *</span><br><span> * \param stream The media stream</span><br><span>diff --git a/main/stream.c b/main/stream.c</span><br><span>index 626fa3a..41b7948 100644</span><br><span>--- a/main/stream.c</span><br><span>+++ b/main/stream.c</span><br><span>@@ -186,6 +186,13 @@</span><br><span> return stream->formats;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_get_format_count(const struct ast_stream *stream)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(stream != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return stream->formats ? ast_format_cap_count(stream->formats) : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps)</span><br><span> {</span><br><span> ast_assert(stream != NULL);</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 4d77a6d..12e41cc 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -923,25 +923,75 @@</span><br><span> </para></description></span><br><span> </configOption></span><br><span> <configOption name="preferred_codec_only" default="no"></span><br><span style="color: hsl(0, 100%, 40%);">- <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 style="color: hsl(120, 100%, 40%);">+ <synopsis>Respond to a SIP invite with the single most preferred codec (DEPRECATED)</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description><para>Respond to a SIP invite with the single most preferred codec</span><br><span style="color: hsl(120, 100%, 40%);">+ rather than advertising all joint codec capabilities. This limits the other side's codec</span><br><span style="color: hsl(120, 100%, 40%);">+ choice to exactly what we prefer.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <warning><para>This option has been deprecated in favor of</span><br><span style="color: hsl(120, 100%, 40%);">+ <literal>incoming_call_offer_pref</literal>. Setting both options is unsupported.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </warning></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="configOption">incoming_call_offer_pref</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></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.</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. (default)</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 +5094,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(struct ast_flags 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..5a4842b 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%);">+ struct ast_flags pref = { 0, };</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%);">+ ast_set_flag(&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%);">+ ast_set_flag(&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%);">+ ast_set_flag(&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%);">+ ast_set_flag(&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%);">+ ast_set_flag(&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%);">+ ast_set_flag(&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>@@ -1345,6 +1359,16 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (endpoint->preferred_codec_only) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (endpoint->media.incoming_call_offer_pref.flags != (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%);">+ ast_log(LOG_ERROR, "Setting both preferred_codec_only and incoming_call_offer_pref is not supported on endpoint '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_get_id(endpoint));</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%);">+ ast_clear_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_set_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_FIRST);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);</span><br><span> if (!endpoint->media.topology) {</span><br><span> return -1;</span><br><span>@@ -2009,7 +2033,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", "remote",</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 bc60d41..bb7c43e 100644</span><br><span>--- a/res/res_pjsip_sdp_rtp.c</span><br><span>+++ b/res/res_pjsip_sdp_rtp.c</span><br><span>@@ -399,13 +399,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>@@ -428,11 +428,11 @@</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>@@ -448,12 +448,13 @@</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_create_joint_call_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 style="color: hsl(0, 100%, 40%);">- if (!incoming_call_offer_cap) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!incoming_call_offer_cap || ast_format_cap_empty(incoming_call_offer_cap)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(incoming_call_offer_cap);</span><br><span> ast_rtp_codecs_payloads_destroy(&codecs);</span><br><span> return NULL;</span><br><span> }</span><br><span>@@ -1413,6 +1414,7 @@</span><br><span> struct ast_sip_session_media *session_media_transport;</span><br><span> enum ast_media_type media_type = session_media->type;</span><br><span> enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *joint;</span><br><span> int res;</span><br><span> </span><br><span> /* If no type formats have been configured reject this stream */</span><br><span>@@ -1504,8 +1506,10 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream,</span><br><span style="color: hsl(0, 100%, 40%);">- set_incoming_call_offer_cap(session, session_media, stream))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ joint = set_incoming_call_offer_cap(session, session_media, stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream, joint);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0) {</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 34d9c0b..3a7d562 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -468,8 +468,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>@@ -528,12 +526,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>@@ -2701,6 +2693,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>@@ -2709,8 +2703,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>@@ -2719,39 +2711,12 @@</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(120, 100%, 40%);">+ clone_stream = ast_sip_session_create_joint_call_stream(session, req_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!clone_stream || ast_stream_get_format_count(clone_stream) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_free(clone_stream);</span><br><span> continue;</span><br><span> }</span><br><span> </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%);">-</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%);">- 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%);">- 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(0, 100%, 40%);">-</span><br><span> if (!session->pending_media_state->topology) {</span><br><span> session->pending_media_state->topology = ast_stream_topology_alloc();</span><br><span> if (!session->pending_media_state->topology) {</span><br><span>@@ -3351,6 +3316,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..7aa4c1f 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,25 +24,24 @@</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(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <pjsip_ua.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stream.h"</span><br><span> #include "asterisk/res_pjsip.h"</span><br><span> #include "asterisk/res_pjsip_session.h"</span><br><span> #include "asterisk/res_pjsip_session_caps.h"</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-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(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static void log_caps(int level, const char *file, int line, const char *function,</span><br><span style="color: hsl(0, 100%, 40%);">- 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(0, 100%, 40%);">- const struct ast_format_cap *remote, const struct ast_format_cap *joint)</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_sip_session *session, enum ast_media_type media_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_format_cap *local, const struct ast_format_cap *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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%);">+ struct ast_flags 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,93 +51,57 @@</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' Caps for %s %s call with pref '%s' - remote: %s local: %s joint: %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(0, 100%, 40%);">- s2 ? " remote: " : "", s2 ? ast_format_cap_get_names(remote, &s2) : "",</span><br><span style="color: hsl(0, 100%, 40%);">- s3 ? " joint: " : "", s3 ? ast_format_cap_get_names(joint, &s3) : "");</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 style="color: hsl(120, 100%, 40%);">+ s2 ? ast_format_cap_get_names(remote, &s2) : "(NONE)",</span><br><span style="color: hsl(120, 100%, 40%);">+ s1 ? ast_format_cap_get_names(local, &s1) : "(NONE)",</span><br><span style="color: hsl(120, 100%, 40%);">+ s3 ? ast_format_cap_get_names(joint, &s3) : "(NONE)");</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_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags codec_pref)</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_format_cap *joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *local_filtered = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</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(0, 100%, 40%);">-}</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!joint || !local_filtered) {</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 style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(local_filtered);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</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 style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_from_cap(local_filtered, local, media_type);</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(120, 100%, 40%);">+ if (ast_sip_call_codec_pref_test(codec_pref, LOCAL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_get_compatible(local_filtered, 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_filtered, 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 style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_get_compatible(remote, local_filtered, 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_filtered, media_type); /* Then local */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </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(120, 100%, 40%);">+ ao2_ref(local_filtered, -1);</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(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- enum ast_sip_call_codec_pref pref;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format_cap *joint;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format_cap *local;</span><br><span style="color: hsl(0, 100%, 40%);">-</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(120, 100%, 40%);">+ if (ast_format_cap_empty(joint)) {</span><br><span> return joint;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">- local = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- 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(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_cleanup(joint);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_cleanup(local);</span><br><span style="color: hsl(0, 100%, 40%);">- return NULL;</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%);">- 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(0, 100%, 40%);">-</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(0, 100%, 40%);">- } 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(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- 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(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(joint, -1);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(local, -1);</span><br><span style="color: hsl(0, 100%, 40%);">- return NULL;</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 (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(0, 100%, 40%);">- session->endpoint->preferred_codec_only) {</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_sip_call_codec_pref_test(codec_pref, FIRST)) {</span><br><span> /*</span><br><span> * Save the most preferred one. Session capabilities are per stream and</span><br><span> * a stream only carries a single media type, so no reason to worry with</span><br><span>@@ -152,11 +115,41 @@</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%);">+ return joint;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(local, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *remote_stream)</span><br><span style="color: hsl(120, 100%, 40%);">+{</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> </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(120, 100%, 40%);">+ struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ session->endpoint->media.codecs, media_type,</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 style="color: hsl(120, 100%, 40%);">+</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%);">+ log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, 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 style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_sip_session_create_joint_call_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 style="color: hsl(120, 100%, 40%);">+ const struct ast_format_cap *remote)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ session->endpoint->media.codecs, media_type,</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);</span><br><span> </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: 14 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>