<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>