<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14328">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">codec_negotiation: Implement SDP answer preferences<br><br>* Added outgoing_call_answer_pref and incoming_call_answer_pref<br> to structures, endpoint config, and sample pjsip.conf.<br><br>* Added a new function ast_sip_session_create_joint_cap_from_stream<br> which takes a stream from pending topology and the capabilities<br> of the peer and creates the joint caps.<br><br>* Replaced the call to ast_format_get_compatible from<br> res_pjsip_sdp_rtp:set_caps with the new function just created.<br><br>* Slightly refactored the ast_sip_call_codec_str_to_pref function<br> in res_pjsip.<br><br>* Added a new function ast_sip_session_get_name which can be used<br> to get a meaningful name for error/warning/debug messages.<br> If a session->channel is not NULL, it returns the channel<br> name. Then it checks session->endpoint and if not NULL,<br> returns the endpoint's sorcery id. If all else fails, it<br> returns "unknown".<br><br>* Added a lot of debug messages to res_pjsip_session and<br> res_pjsip_sdp_rtp.<br><br>There is still more to come.<br><br>ASTERISK-28856<br><br>Change-Id: Iad188ae997bdcb5c28e2eb12c6bb2b732538ad45<br>---<br>M configs/samples/pjsip.conf.sample<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>9 files changed, 262 insertions(+), 33 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/28/14328/1</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 93fb701..a96bec0 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -839,6 +839,48 @@</span><br><span> ; at the end of the joint list.</span><br><span> ; remote_first - Include only the first codec in</span><br><span> ; the remote list.</span><br><span style="color: hsl(120, 100%, 40%);">+;outgoing_call_answer_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%);">+ ; SDP answer (remote), and those originally</span><br><span style="color: hsl(120, 100%, 40%);">+ ; sent in the SDP offer (local) is created and is</span><br><span style="color: hsl(120, 100%, 40%);">+ ; passed to the Asterisk core.</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_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%);">+;incoming_call_answer_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 (local), and those specified in</span><br><span style="color: hsl(120, 100%, 40%);">+ ; the original list sent to the core when the SDP</span><br><span style="color: hsl(120, 100%, 40%);">+ ; offer was received (local) is</span><br><span style="color: hsl(120, 100%, 40%);">+ ; created and is used to create the SDP</span><br><span style="color: hsl(120, 100%, 40%);">+ ; answer.</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> ;preferred_codec_only=no ; Respond to a SIP invite with the single most</span><br><span> ; preferred codec rather than advertising all joint</span><br><span> ; codec capabilities. This limits the other side's</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index fd80581..6def942 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -790,6 +790,10 @@</span><br><span> struct ast_flags incoming_call_offer_pref;</span><br><span> /*! Codec preference for an outgoing offer */</span><br><span> struct ast_flags outgoing_call_offer_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Codec preference for an outgoing answer */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags outgoing_call_answer_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Codec preference for an incoming answer */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags incoming_call_answer_pref;</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span>@@ -3260,12 +3264,12 @@</span><br><span> *</span><br><span> * \param pref A pointer to an ast_flags structure to receive the preference flags</span><br><span> * \param pref_str The call codec preference setting string</span><br><span style="color: hsl(0, 100%, 40%);">- * \param is_outgoing Is for outgoing calls?</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param allow_merge Allow the "local_merge" and "remote_merge" options?</span><br><span> *</span><br><span> * \retval 0 The string was parsed successfully</span><br><span> * \retval -1 The string option was invalid</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, int is_outgoing);</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, int allow_merge);</span><br><span> </span><br><span> /*!</span><br><span> * \brief Transport shutdown monitor callback.</span><br><span>diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h</span><br><span>index fd49a7b..5b1aa94 100644</span><br><span>--- a/include/asterisk/res_pjsip_session.h</span><br><span>+++ b/include/asterisk/res_pjsip_session.h</span><br><span>@@ -926,4 +926,13 @@</span><br><span> */</span><br><span> struct ast_sip_session_media *ast_sip_session_media_get_transport(struct ast_sip_session *session, struct ast_sip_session_media *session_media);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the channel or endpoint name associated with the session</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 session</span><br><span style="color: hsl(120, 100%, 40%);">+ * @retval Channel name or endpoint name or "unknown"</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_sip_session_get_name(struct ast_sip_session *session);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _RES_PJSIP_SESSION_H */</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 0d7020f..a7ec35b 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>@@ -38,7 +38,7 @@</span><br><span> * \note Returned object's reference must be released at some point,</span><br><span> */</span><br><span> struct ast_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span> struct ast_flags codec_pref);</span><br><span> </span><br><span> /*!</span><br><span>@@ -79,4 +79,24 @@</span><br><span> struct ast_format_cap *ast_sip_session_create_joint_call_cap(const struct ast_sip_session *session,</span><br><span> enum ast_media_type media_type, const struct ast_format_cap *remote);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create joint answer 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 stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * and the remote capabilities.</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 remote Capabilities received in an SDP answer from the peer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param stream Stream from pending topology that was sent on the offer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param media_type The media type</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_cap_from_stream(</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_sip_session *session, const struct ast_format_cap *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_stream *stream, enum ast_media_type media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</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 152d9e1..4acb6f3 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -952,11 +952,11 @@</span><br><span> </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>Preferences for selecting codecs for an incoming call.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Preferences for selecting codecs for an incoming call's SDP offer.</synopsis></span><br><span> <description></span><br><span> <para>Based on this setting, a joint list of preferred codecs between those</span><br><span> received in an incoming SDP offer (remote), and those specified in the</span><br><span style="color: hsl(0, 100%, 40%);">- endpoint's "allow" parameter (local) es created and is passed to the Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint's "allow" parameter (local) is created and is passed to the Asterisk</span><br><span> core. </para></span><br><span> <note><para>This list will consist of only those codecs found in both lists.</para></note></span><br><span> <enumlist></span><br><span>@@ -978,7 +978,7 @@</span><br><span> </description></span><br><span> </configOption></span><br><span> <configOption name="outgoing_call_offer_pref" default="local"></span><br><span style="color: hsl(0, 100%, 40%);">- <synopsis>Preferences for selecting codecs for an outgoing call.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Preferences for selecting codecs for an outgoing call's SDP offer.</synopsis></span><br><span> <description></span><br><span> <para>Based on this setting, a joint list of preferred codecs between</span><br><span> those received from the Asterisk core (remote), and those specified in</span><br><span>@@ -1012,6 +1012,65 @@</span><br><span> </enumlist></span><br><span> </description></span><br><span> </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="outgoing_call_answer_pref" default="local"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Preferences for selecting codecs for an outgoing call's SDP answer.</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 those</span><br><span style="color: hsl(120, 100%, 40%);">+ received in an incoming SDP answer (remote), and those originally send in</span><br><span style="color: hsl(120, 100%, 40%);">+ the SDP offer (local) is created and passed to the Asterisk 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 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_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 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_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="incoming_call_answer_pref" default="remote"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Preferences for selecting codecs for an incoming call's SDP answer.</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 (local), and those specified in</span><br><span style="color: hsl(120, 100%, 40%);">+ the original list send to the core when the SDP offer was received.</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 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> <configOption name="rtp_keepalive"></span><br><span> <synopsis>Number of seconds between RTP comfort noise keepalive packets.</synopsis></span><br><span> <description><para></span><br><span>@@ -5133,19 +5192,19 @@</span><br><span> return value;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, int is_outgoing)</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, int allow_merge)</span><br><span> {</span><br><span> pref->flags = 0;</span><br><span> </span><br><span> if (strcmp(pref_str, "local") == 0) {</span><br><span> 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(0, 100%, 40%);">- } else if (is_outgoing && strcmp(pref_str, "local_merge") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (allow_merge && strcmp(pref_str, "local_merge") == 0) {</span><br><span> 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> } else if (strcmp(pref_str, "local_first") == 0) {</span><br><span> 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> } else if (strcmp(pref_str, "remote") == 0) {</span><br><span> 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(0, 100%, 40%);">- } else if (is_outgoing && strcmp(pref_str, "remote_merge") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (allow_merge && strcmp(pref_str, "remote_merge") == 0) {</span><br><span> 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> } else if (strcmp(pref_str, "remote_first") == 0) {</span><br><span> 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>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index f95ee9e..e588a48 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -1128,6 +1128,7 @@</span><br><span> struct ast_flags pref = { 0, };</span><br><span> int outgoing = strcmp(var->name, "outgoing_call_offer_pref") == 0;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* The "local_merge" and "remote_merge" options are only allowed if outgoing */</span><br><span> int res = ast_sip_call_codec_str_to_pref(&pref, var->value, outgoing);</span><br><span> if (res != 0) {</span><br><span> return -1;</span><br><span>@@ -1142,6 +1143,28 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int call_answer_pref_handler(const struct aco_option *opt,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags pref = { 0, };</span><br><span style="color: hsl(120, 100%, 40%);">+ int incoming = strcmp(var->name, "incoming_call_answer_pref") == 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The "local_merge" and "remote_merge" options are only allowed if incoming */</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = ast_sip_call_codec_str_to_pref(&pref, var->value, incoming);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0) {</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%);">+ if (incoming) {</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint->media.incoming_call_answer_pref = pref;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint->media.outgoing_call_answer_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 style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</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>@@ -1166,6 +1189,30 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int outgoing_call_answer_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_answer_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 incoming_call_answer_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.incoming_call_answer_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> static void *sip_nat_hook_alloc(const char *name)</span><br><span> {</span><br><span> return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);</span><br><span>@@ -2025,6 +2072,10 @@</span><br><span> call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);</span><br><span> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_call_offer_pref", "remote",</span><br><span> call_offer_pref_handler, outgoing_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_answer_pref", "local",</span><br><span style="color: hsl(120, 100%, 40%);">+ call_answer_pref_handler, outgoing_call_answer_pref_to_str, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_call_answer_pref", "remote",</span><br><span style="color: hsl(120, 100%, 40%);">+ call_answer_pref_handler, incoming_call_answer_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 1bcb661..16c4bae 100644</span><br><span>--- a/res/res_pjsip_sdp_rtp.c</span><br><span>+++ b/res/res_pjsip_sdp_rtp.c</span><br><span>@@ -448,6 +448,7 @@</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(120, 100%, 40%);">+ ast_debug(1, "%s: Creating incoming call offer caps.\n", ast_sip_session_get_name(session));</span><br><span> incoming_call_offer_cap = ast_sip_session_create_joint_call_cap(</span><br><span> session, session_media->type, remote);</span><br><span> </span><br><span>@@ -490,8 +491,7 @@</span><br><span> int dsp_features = 0;</span><br><span> </span><br><span> if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||</span><br><span style="color: hsl(0, 100%, 40%);">- !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||</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(120, 100%, 40%);">+ !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {</span><br><span> ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",</span><br><span> ast_codec_media_type2str(session_media->type));</span><br><span> return -1;</span><br><span>@@ -499,8 +499,10 @@</span><br><span> </span><br><span> /* get the endpoint capabilities */</span><br><span> if (direct_media_enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Direct media enabled\n", ast_sip_session_get_name(session));</span><br><span> ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);</span><br><span> } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Direct media not enabled\n", ast_sip_session_get_name(session));</span><br><span> ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type);</span><br><span> }</span><br><span> </span><br><span>@@ -508,8 +510,9 @@</span><br><span> get_codecs(session, stream, &codecs, session_media);</span><br><span> ast_rtp_codecs_payload_formats(&codecs, peer, &fmts);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* get the joint capabilities between peer and endpoint */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_format_cap_get_compatible(caps, peer, joint);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Creating joint caps\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+ joint = ast_sip_session_create_joint_cap_from_stream(session, peer, asterisk_stream, session_media->type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (!ast_format_cap_count(joint)) {</span><br><span> struct ast_str *usbuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span> struct ast_str *thembuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span>@@ -541,11 +544,7 @@</span><br><span> AST_MEDIA_TYPE_UNKNOWN);</span><br><span> ast_format_cap_remove_by_type(caps, media_type);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (session->endpoint->preferred_codec_only){</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format *preferred_fmt = ast_format_cap_get_format(joint, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_format_cap_append(caps, preferred_fmt, 0);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(preferred_fmt, -1);</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (!session->endpoint->asymmetric_rtp_codec) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!session->endpoint->asymmetric_rtp_codec) {</span><br><span> struct ast_format *best;</span><br><span> /*</span><br><span> * If we don't allow the sending codec to be changed on our side</span><br><span>@@ -2078,6 +2077,7 @@</span><br><span> enable_rtcp(session, session_media, remote_stream);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Setting call caps.\n", ast_sip_session_get_name(session));</span><br><span> if (set_caps(session, session_media, session_media_transport, remote_stream, 0, asterisk_stream)) {</span><br><span> return -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 67899e4..1d9e3fd 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -186,6 +186,17 @@</span><br><span> ao2_callback_data(sdp_handlers, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, remove_handler, (void *)stream_type, handler);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_sip_session_get_name(struct ast_sip_session *session)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (session->channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_channel_name(session->channel);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (session->endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_sorcery_object_get_id(session->endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ return "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%);">+</span><br><span> static int media_stats_local_ssrc_cmp(</span><br><span> const struct ast_rtp_instance_stats *vec_elem, const struct ast_rtp_instance_stats *srch)</span><br><span> {</span><br><span>@@ -789,7 +800,8 @@</span><br><span> </span><br><span> /* If this stream is already declined mark it as such, or mark it as such if we've reached the limit */</span><br><span> if (!remote_stream->desc.port || is_stream_limitation_reached(type, session->endpoint, type_streams)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(type), i);</span><br><span> remove_stream_from_bundle(session_media, stream);</span><br><span> continue;</span><br><span>@@ -800,7 +812,8 @@</span><br><span> </span><br><span> if (session_media->handler) {</span><br><span> handler = session_media->handler;</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Negotiating incoming SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Negotiating incoming SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> session_media->handler->id);</span><br><span> res = handler->negotiate_incoming_sdp_stream(session, session_media, sdp, i, stream);</span><br><span>@@ -808,12 +821,14 @@</span><br><span> /* Catastrophic failure. Abort! */</span><br><span> return -1;</span><br><span> } else if (res == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(type), i);</span><br><span> remove_stream_from_bundle(session_media, stream);</span><br><span> continue;</span><br><span> } else if (res > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Media stream '%s' handled by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Media stream '%s' handled by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> session_media->handler->id);</span><br><span> /* Handled by this handler. Move to the next stream */</span><br><span>@@ -825,14 +840,17 @@</span><br><span> </span><br><span> handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);</span><br><span> if (!handler_list) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: No registered SDP handlers for media type '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ media);</span><br><span> continue;</span><br><span> }</span><br><span> AST_LIST_TRAVERSE(&handler_list->list, handler, next) {</span><br><span> if (handler == session_media->handler) {</span><br><span> continue;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Negotiating incoming SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Negotiating incoming SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> res = handler->negotiate_incoming_sdp_stream(session, session_media, sdp, i, stream);</span><br><span>@@ -840,12 +858,14 @@</span><br><span> /* Catastrophic failure. Abort! */</span><br><span> return -1;</span><br><span> } else if (res == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Declining incoming SDP media stream '%s' at position '%d'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(type), i);</span><br><span> remove_stream_from_bundle(session_media, stream);</span><br><span> continue;</span><br><span> } else if (res > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Media stream '%s' handled by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Media stream '%s' handled by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> /* Handled by this handler. Move to the next stream */</span><br><span>@@ -902,12 +922,14 @@</span><br><span> </span><br><span> handler = session_media->handler;</span><br><span> if (handler) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Applying negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> res = handler->apply_negotiated_sdp_stream(session, session_media, local, remote, index, asterisk_stream);</span><br><span> if (res >= 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Applied negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> return 0;</span><br><span>@@ -917,14 +939,17 @@</span><br><span> </span><br><span> handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);</span><br><span> if (!handler_list) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: No registered SDP handlers for media type '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ media);</span><br><span> return -1;</span><br><span> }</span><br><span> AST_LIST_TRAVERSE(&handler_list->list, handler, next) {</span><br><span> if (handler == session_media->handler) {</span><br><span> continue;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Applying negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> res = handler->apply_negotiated_sdp_stream(session, session_media, local, remote, index, asterisk_stream);</span><br><span>@@ -933,7 +958,8 @@</span><br><span> return -1;</span><br><span> }</span><br><span> if (res > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Applied negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type),</span><br><span> handler->id);</span><br><span> /* Handled by this handler. Move to the next stream */</span><br><span>@@ -943,7 +969,8 @@</span><br><span> }</span><br><span> </span><br><span> if (session_media->handler && session_media->handler->stream_stop) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(1, "Stopping SDP media stream '%s' as it is not currently negotiated\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Stopping SDP media stream '%s' as it is not currently negotiated\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session),</span><br><span> ast_codec_media_type2str(session_media->type));</span><br><span> session_media->handler->stream_stop(session_media);</span><br><span> }</span><br><span>@@ -2721,6 +2748,7 @@</span><br><span> continue;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "%s: Creating outgoing call offer caps.\n", ast_sip_session_get_name(session));</span><br><span> clone_stream = ast_sip_session_create_joint_call_stream(session, req_stream);</span><br><span> if (!clone_stream || ast_stream_get_format_count(clone_stream) == 0) {</span><br><span> ast_stream_free(clone_stream);</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 ca7ef44..dcb7e4a 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>@@ -63,7 +63,7 @@</span><br><span> }</span><br><span> </span><br><span> struct ast_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_format_cap *local, enum ast_media_type media_type,</span><br><span> struct ast_flags codec_pref)</span><br><span> {</span><br><span> struct ast_format_cap *joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span>@@ -153,3 +153,19 @@</span><br><span> </span><br><span> return joint;</span><br><span> }</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_cap_from_stream(</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_sip_session *session, const struct ast_format_cap *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_stream *stream, enum ast_media_type media_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_format_cap *local = ast_stream_get_formats(stream);</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%);">+ local, 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_answer_pref</span><br><span style="color: hsl(120, 100%, 40%);">+ : session->endpoint->media.incoming_call_answer_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, local, remote, joint);</span><br><span style="color: hsl(120, 100%, 40%);">+</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></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14328">change 14328</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/+/14328"/><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: Iad188ae997bdcb5c28e2eb12c6bb2b732538ad45 </div>
<div style="display:none"> Gerrit-Change-Number: 14328 </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>