<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14637">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">ACN: Advanced Codec Negotiation for chan_pjsip<br><br>This commit is the second in a series that implements<br>Advanced Codec Negotiation and does not represent a final<br>implementation.  Many existing features either do not yet work<br>or have yet to be tested including...<br><br> * Direct media<br> * 100rel/early media<br> * Re-invites<br> * Fax<br> * Multi-stream<br> * Deferred SDP<br> * ARI channel operations<br> * Operation with other channel technologies<br><br>There are also several refactors that still need to happen to remove<br>duplicated code, re-organize internal functions, consolidate<br>activities, etc.<br><br>Summary of functional changes by module:<br><br>app_dial<br> * Capture resolved topology when a callee channel answers and pass<br>   it to the bridging code in features.c.<br><br>features<br> * In pre_bridge_setup (which answers the caller channel), use<br>   the new ast_raw_answer_with_stream_topology() API to pass<br>   the callee channel's topology to the caller channel.<br><br>channel<br> * Added a new callback "answer_with_stream_topology" to the<br>   ast_channel_tech structure.  Channels implementing it will<br>   receive the callee channel's topology if available.<br> * Added peer_topology to the ast_bridge_config structure which<br>   is used to pass the callee channel's topology to pre_bridge_setup.<br> * Added a new API "ast_raw_answer_with_stream_topology" which<br>   calls the channel's answer_with_stream_topology callback.<br><br>stream<br> * Fixed a leak in ast_stream_topology_create_resolved.<br><br>res_pjsip<br> * Updated documentation.<br> * Removed obsolete code.<br><br>res_pjsip/pjsip_configuration<br> * Updated endpoint configuration parsing.<br> * Removed obsolete code.<br><br>res_pjsip_refer<br> * Use ast_raw_answer_with_stream_topology instead of ast_raw_answer.<br><br>res_pjsip_sdp_rtp<br> * Refactored get_codecs to return an ast_format_caps.<br> * Removed obsolete code in set_incoming_call_offer_cap.<br> * Removed obsolete code in set_caps.<br> * Removed obsolete code in negotiate_incoming_sdp_stream.<br> * Removed obsolete code in create_outgoing_sdp_stream.<br><br>res_pjsip_session<br> * Preserve the pending topology in handle_negotiated_sdp so<br>   chan_pjsip can do a topology resolution.<br> * Removed obsolete code in ast_sip_session_create_outgoing.<br> * Removed obsolete code in new_invite.<br> * Updated session_inv_on_tsx_state_changed to call handlers with<br>   the proper BEFORE/AFTER MEDIA flag.<br> * Removed obsolete code in create_local_sdp.<br><br>res_pjsip_session/pjsip_session_caps<br>tests/test_res_pjsip_session_caps<br> * Removed.<br><br>chan_pjsip<br> * Implement chan_pjsip_answer_with_stream_topology<br> * chan_pjsip_incoming_request now resolves the topology on an<br>   incoming invite with that of the endpoint.<br> * chan_pjsip_request_with_stream_topology now resolves the<br>   topology passed from the core on an outgoing call with that<br>   of the endpoint.<br> * chan_pjsip_incoming_response now resolves the topology received<br>   on a 200 OK with that which was sent on the original invite.<br> * chan_pjsip_answer_with_stream_topology now resolves the topology<br>   passed from the core with that which was originally passed to<br>   the core.<br><br>ASTERISK-28856<br><br>Change-Id: Iad188ae997bdcb5c28e2eb12c6bb2b732538ad45<br>---<br>M apps/app_dial.c<br>M channels/chan_pjsip.c<br>M configs/samples/pjsip.conf.sample<br>M contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py<br>M include/asterisk/channel.h<br>M include/asterisk/res_pjsip.h<br>M include/asterisk/res_pjsip_session.h<br>D include/asterisk/res_pjsip_session_caps.h<br>M main/bridge_channel.c<br>M main/channel.c<br>M main/features.c<br>M main/stream.c<br>M res/Makefile<br>M res/res_pjsip.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip_refer.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>D res/res_pjsip_session/pjsip_session_caps.c<br>D tests/test_res_pjsip_session_caps.c<br>20 files changed, 894 insertions(+), 1,221 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/37/14637/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_dial.c b/apps/app_dial.c</span><br><span>index 95f36d7..1a09cb9 100644</span><br><span>--- a/apps/app_dial.c</span><br><span>+++ b/apps/app_dial.c</span><br><span>@@ -1204,7 +1204,8 @@</span><br><span>    struct privacy_args *pa,</span><br><span>     const struct cause_args *num_in, int *result, char *dtmf_progress,</span><br><span>   const int ignore_cc,</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_party_id *forced_clid, struct ast_party_id *stored_clid)</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_party_id *forced_clid, struct ast_party_id *stored_clid,</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_bridge_config *config)</span><br><span> {</span><br><span>       struct cause_args num = *num_in;</span><br><span>     int prestart = num.busy + num.congestion + num.nochan;</span><br><span>@@ -1418,6 +1419,13 @@</span><br><span>                                                      }</span><br><span>                                            }</span><br><span>                                            peer = c;</span><br><span style="color: hsl(120, 100%, 40%);">+                                             if (f->data.ptr != NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 config->peer_topology = *((struct ast_stream_topology **)f->data.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  ast_trace(2, "%s Found topology in frame: %p %p %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              ast_channel_name(peer), f->data.ptr, config->peer_topology,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                             ast_str_tmp(256, ast_stream_topology_to_str(config->peer_topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+                                                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                                          /* Inform everyone else that they've been canceled.</span><br><span>                                               * The dial end event for the peer will be sent out after</span><br><span>                                             * other Dial options have been handled.</span><br><span>@@ -2838,7 +2846,7 @@</span><br><span>     }</span><br><span> </span><br><span>        peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,</span><br><span style="color: hsl(0, 100%, 40%);">-              dtmf_progress, ignore_cc, &forced_clid, &stored_clid);</span><br><span style="color: hsl(120, 100%, 40%);">+                dtmf_progress, ignore_cc, &forced_clid, &stored_clid, &config);</span><br><span> </span><br><span>      if (!peer) {</span><br><span>                 if (result) {</span><br><span>@@ -3267,6 +3275,7 @@</span><br><span>                                ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);</span><br><span>                   }</span><br><span>                    setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                     res = ast_bridge_call(chan, peer, &config);</span><br><span>              }</span><br><span>    }</span><br><span>@@ -3304,6 +3313,13 @@</span><br><span>   }</span><br><span> </span><br><span> done:</span><br><span style="color: hsl(120, 100%, 40%);">+        if (config.peer_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_trace(2, "%s Cleaning up topology: %p %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_channel_name(peer), &config.peer_topology,</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_str_tmp(256, ast_stream_topology_to_str(config.peer_topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_stream_topology_free(config.peer_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span>    if (config.warning_sound) {</span><br><span>          ast_free((char *)config.warning_sound);</span><br><span>      }</span><br><span>diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c</span><br><span>index e99ec31..9f12e52 100644</span><br><span>--- a/channels/chan_pjsip.c</span><br><span>+++ b/channels/chan_pjsip.c</span><br><span>@@ -95,6 +95,7 @@</span><br><span> static int chan_pjsip_call(struct ast_channel *ast, const char *dest, int timeout);</span><br><span> static int chan_pjsip_hangup(struct ast_channel *ast);</span><br><span> static int chan_pjsip_answer(struct ast_channel *ast);</span><br><span style="color: hsl(120, 100%, 40%);">+static int chan_pjsip_answer_with_stream_topology(struct ast_channel *ast, struct ast_stream_topology *topology);</span><br><span> static struct ast_frame *chan_pjsip_read_stream(struct ast_channel *ast);</span><br><span> static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *f);</span><br><span> static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, struct ast_frame *f);</span><br><span>@@ -118,6 +119,7 @@</span><br><span>   .call = chan_pjsip_call,</span><br><span>     .hangup = chan_pjsip_hangup,</span><br><span>         .answer = chan_pjsip_answer,</span><br><span style="color: hsl(120, 100%, 40%);">+  .answer_with_stream_topology = chan_pjsip_answer_with_stream_topology,</span><br><span>       .read_stream = chan_pjsip_read_stream,</span><br><span>       .write = chan_pjsip_write,</span><br><span>   .write_stream = chan_pjsip_write_stream,</span><br><span>@@ -132,11 +134,16 @@</span><br><span>     .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER | AST_CHAN_TP_SEND_TEXT_DATA</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_stream_topology *resolve_topology(struct ast_stream_topology *topology,</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_endpoint *endpoint, struct ast_stream_codec_negotiation_prefs *prefs, int *cause);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief SIP session interaction functions */</span><br><span> static void chan_pjsip_session_begin(struct ast_sip_session *session);</span><br><span> static void chan_pjsip_session_end(struct ast_sip_session *session);</span><br><span> static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata);</span><br><span style="color: hsl(0, 100%, 40%);">-static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_incoming_response_before_media(struct ast_sip_session *session, struct pjsip_rx_data *rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_incoming_response_after_media(struct ast_sip_session *session, struct pjsip_rx_data *rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_outgoing_response(struct ast_sip_session *session, struct pjsip_tx_data *tdata);</span><br><span> </span><br><span> /*! \brief SIP session supplement structure */</span><br><span> static struct ast_sip_session_supplement chan_pjsip_supplement = {</span><br><span>@@ -145,16 +152,25 @@</span><br><span>     .session_begin = chan_pjsip_session_begin,</span><br><span>   .session_end = chan_pjsip_session_end,</span><br><span>       .incoming_request = chan_pjsip_incoming_request,</span><br><span style="color: hsl(120, 100%, 40%);">+      .outgoing_response = chan_pjsip_outgoing_response,</span><br><span>   /* It is important that this supplement runs after media has been negotiated */</span><br><span>      .response_priority = AST_SIP_SESSION_AFTER_MEDIA,</span><br><span> };</span><br><span> </span><br><span> /*! \brief SIP session supplement structure just for responses */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_sip_session_supplement chan_pjsip_supplement_response = {</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_session_supplement chan_pjsip_supplement_response_before_media = {</span><br><span>        .method = "INVITE",</span><br><span>        .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,</span><br><span style="color: hsl(0, 100%, 40%);">-        .incoming_response = chan_pjsip_incoming_response,</span><br><span style="color: hsl(0, 100%, 40%);">-      .response_priority = AST_SIP_SESSION_BEFORE_MEDIA | AST_SIP_SESSION_AFTER_MEDIA,</span><br><span style="color: hsl(120, 100%, 40%);">+      .incoming_response = chan_pjsip_incoming_response_before_media,</span><br><span style="color: hsl(120, 100%, 40%);">+       .response_priority = AST_SIP_SESSION_BEFORE_MEDIA,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief SIP session supplement structure just for responses */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_session_supplement chan_pjsip_supplement_response_after_media = {</span><br><span style="color: hsl(120, 100%, 40%);">+  .method = "INVITE",</span><br><span style="color: hsl(120, 100%, 40%);">+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,</span><br><span style="color: hsl(120, 100%, 40%);">+      .incoming_response = chan_pjsip_incoming_response_after_media,</span><br><span style="color: hsl(120, 100%, 40%);">+        .response_priority = AST_SIP_SESSION_AFTER_MEDIA,</span><br><span> };</span><br><span> </span><br><span> static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata);</span><br><span>@@ -243,6 +259,7 @@</span><br><span> {</span><br><span>  SCOPE_ENTER(1, "%s Native formats %s\n", ast_channel_name(chan),</span><br><span>           ast_str_tmp(AST_FORMAT_CAP_NAMES_LEN, ast_format_cap_get_names(ast_channel_nativeformats(chan), &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN);</span><br><span>     SCOPE_EXIT_RTN();</span><br><span> }</span><br><span>@@ -506,43 +523,10 @@</span><br><span>       }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Determine if a topology is compatible with format capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This will return true if ANY formats in the topology are compatible with the format</span><br><span style="color: hsl(0, 100%, 40%);">- * capabilities.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * XXX When supporting true multistream, we will need to be sure to mark which streams from</span><br><span style="color: hsl(0, 100%, 40%);">- * top1 are compatible with which streams from top2. Then the ones that are not compatible</span><br><span style="color: hsl(0, 100%, 40%);">- * will need to be marked as "removed" so that they are negotiated as expected.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param top Topology</span><br><span style="color: hsl(0, 100%, 40%);">- * \param cap Format capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval 1 The topology has at least one compatible format</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval 0 The topology has no compatible formats or an error occurred.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static int compatible_formats_exist(struct ast_stream_topology *top, struct ast_format_cap *cap)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_format_cap *cap_from_top;</span><br><span style="color: hsl(0, 100%, 40%);">-    int res;</span><br><span style="color: hsl(0, 100%, 40%);">-        SCOPE_ENTER(1, "Topology: %s Formats: %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_str_tmp(AST_FORMAT_CAP_NAMES_LEN, ast_stream_topology_to_str(top, &STR_TMP)),</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_str_tmp(AST_FORMAT_CAP_NAMES_LEN, ast_format_cap_get_names(cap, &STR_TMP)));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    cap_from_top = ast_stream_topology_get_formats(top);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!cap_from_top) {</span><br><span style="color: hsl(0, 100%, 40%);">-            SCOPE_EXIT_RTN_VALUE(0, "Topology had no formats\n");</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       res = ast_format_cap_iscompatible(cap_from_top, cap);</span><br><span style="color: hsl(0, 100%, 40%);">-   ao2_ref(cap_from_top, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      SCOPE_EXIT_RTN_VALUE(res, "Compatible? %s\n", res ? "yes" : "no");</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! \brief Function called to create a new PJSIP Asterisk channel */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int state, const char *exten, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *cid_name)</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int state, const char *exten,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *cid_name, struct ast_stream_topology *resolved_topology)</span><br><span> {</span><br><span>    struct ast_channel *chan;</span><br><span>    struct ast_format_cap *caps;</span><br><span>@@ -550,10 +534,11 @@</span><br><span>         struct ast_sip_channel_pvt *channel;</span><br><span>         struct ast_variable *var;</span><br><span>    struct ast_stream_topology *topology;</span><br><span style="color: hsl(0, 100%, 40%);">-   SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+  SCOPE_ENTER(1, "%s Topology: %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_str_tmp(256, ast_stream_topology_to_str(resolved_topology, &STR_TMP)));</span><br><span> </span><br><span>  if (!(pvt = ao2_alloc_options(sizeof(*pvt), chan_pjsip_pvt_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {</span><br><span style="color: hsl(0, 100%, 40%);">-         SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create pvt\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(NULL, "No pvt\n");</span><br><span>    }</span><br><span> </span><br><span>        chan = ast_channel_alloc_with_endpoint(1, state,</span><br><span>@@ -579,24 +564,16 @@</span><br><span> </span><br><span>         ast_channel_tech_pvt_set(chan, channel);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if (!ast_stream_topology_get_count(session->pending_media_state->topology) ||</span><br><span style="color: hsl(0, 100%, 40%);">-             !compatible_formats_exist(session->pending_media_state->topology, session->endpoint->media.codecs)) {</span><br><span style="color: hsl(0, 100%, 40%);">-               caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">-               if (!caps) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_channel_unlock(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_hangup(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-                       SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create caps\n");</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">-            topology = ast_stream_topology_clone(session->endpoint->media.topology);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                caps = ast_stream_topology_get_formats(session->pending_media_state->topology);</span><br><span style="color: hsl(0, 100%, 40%);">-           topology = ast_stream_topology_clone(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_stream_topology_get_active_count(resolved_topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_hangup(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+             SCOPE_EXIT_RTN_VALUE(NULL, "Resolved topology empty\n");</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!topology || !caps) {</span><br><span style="color: hsl(120, 100%, 40%);">+     caps = ast_stream_topology_get_formats(resolved_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+    topology = ast_stream_topology_clone(resolved_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!caps || !topology) {</span><br><span>            ao2_cleanup(caps);</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_stream_topology_free(topology);</span><br><span>          ast_channel_unlock(chan);</span><br><span>            ast_hangup(chan);</span><br><span>            SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't get caps or clone topology\n");</span><br><span>@@ -605,25 +582,20 @@</span><br><span>   ast_channel_stage_snapshot(chan);</span><br><span> </span><br><span>        ast_channel_nativeformats_set(chan, caps);</span><br><span style="color: hsl(0, 100%, 40%);">-      ast_channel_set_stream_topology(chan, topology);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     if (!ast_format_cap_empty(caps)) {</span><br><span>           struct ast_format *fmt;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-             fmt = ast_format_cap_get_best_by_type(caps, AST_MEDIA_TYPE_AUDIO);</span><br><span style="color: hsl(0, 100%, 40%);">-              if (!fmt) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     /* Since our capabilities aren't empty, this will succeed */</span><br><span style="color: hsl(0, 100%, 40%);">-                        fmt = ast_format_cap_get_format(caps, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(120, 100%, 40%);">+             fmt = ast_format_cap_get_format(caps, 0);</span><br><span>            ast_channel_set_writeformat(chan, fmt);</span><br><span>              ast_channel_set_rawwriteformat(chan, fmt);</span><br><span>           ast_channel_set_readformat(chan, fmt);</span><br><span>               ast_channel_set_rawreadformat(chan, fmt);</span><br><span>            ao2_ref(fmt, -1);</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    ao2_ref(caps, -1);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        ast_channel_set_stream_topology(chan, topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   if (state == AST_STATE_RING) {</span><br><span>               ast_channel_rings_set(chan, 1);</span><br><span>      }</span><br><span>@@ -674,6 +646,8 @@</span><br><span> </span><br><span> struct answer_data {</span><br><span>  struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_stream_topology *topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ int cause;</span><br><span>   unsigned long indent;</span><br><span> };</span><br><span> </span><br><span>@@ -683,9 +657,15 @@</span><br><span>       pj_status_t status = PJ_SUCCESS;</span><br><span>     pjsip_tx_data *packet = NULL;</span><br><span>        struct ast_sip_session *session = ans_data->session;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_format_cap *active_caps;</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct pjmedia_sdp_session *local;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_stream_topology *resolved_topology;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_str *error_message = ast_str_alloca(128);</span><br><span>         SCOPE_ENTER_TASK(1, ans_data->indent, "%s\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+        ans_data->cause = AST_CAUSE_NORMAL_CLEARING;</span><br><span> </span><br><span>  if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ans_data->cause = AST_CAUSE_INVALID_CALL_REFERENCE;</span><br><span>               ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",</span><br><span>                      session->inv_session->cause,</span><br><span>                   pjsip_get_status_text(session->inv_session->cause)->ptr);</span><br><span>@@ -696,11 +676,76 @@</span><br><span>   }</span><br><span> </span><br><span>        pjsip_dlg_inc_lock(session->inv_session->dlg);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (session->inv_session->invite_tsx) {</span><br><span style="color: hsl(0, 100%, 40%);">-           status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet);</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!session->inv_session->invite_tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ans_data->cause = AST_CAUSE_INVALID_CALL_REFERENCE;</span><br><span>               ast_log(LOG_ERROR,"Cannot answer '%s' because there is no associated SIP transaction\n",</span><br><span>                   ast_channel_name(session->channel));</span><br><span style="color: hsl(120, 100%, 40%);">+               pjsip_dlg_dec_lock(session->inv_session->dlg);</span><br><span style="color: hsl(120, 100%, 40%);">+          SCOPE_EXIT_RTN_VALUE(0, "No associated sip transaction\n");</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 (ans_data->topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+          resolved_topology = ast_stream_topology_create_resolved(ans_data->topology,</span><br><span style="color: hsl(120, 100%, 40%);">+                        session->pending_media_state->topology, &session->endpoint->media.outgoing_answer_codec_prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+                        &error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!resolved_topology || ast_stream_topology_get_active_count(resolved_topology) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (session->endpoint->media.outgoing_answer_codec_prefs.transcode != CODEC_NEGOTIATION_TRANSCODE_ALLOW) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_trace(1, "%s transcoding prevented.  Bail\n", ast_str_buffer(error_message));</span><br><span style="color: hsl(120, 100%, 40%);">+                           packet = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (pjsip_inv_end_session(session->inv_session, 500, NULL, &packet) == PJ_SUCCESS && packet) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                 ast_sip_session_send_response(session, packet);</span><br><span style="color: hsl(120, 100%, 40%);">+                               }</span><br><span style="color: hsl(120, 100%, 40%);">+                             goto end;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_trace(1, "No topology in common but transcoding allowed.  Continue\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* In this case, we're going to use the existing pending topology */</span><br><span style="color: hsl(120, 100%, 40%);">+              } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(1, "Transcoding not needed.  Continue\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_stream_topology_free(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+                       /* Transfer resolved_topology's reference to pending */</span><br><span style="color: hsl(120, 100%, 40%);">+                   session->pending_media_state->topology = resolved_topology;</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%);">+           ast_trace(1, "Setting channel topology to %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)));</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%);">+   active_caps = ast_stream_topology_get_formats(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_channel_nativeformats_set(session->channel, active_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!ast_format_cap_empty(active_caps)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct ast_format *fmt;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             fmt = ast_format_cap_get_format(active_caps, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_channel_set_writeformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_channel_set_rawwriteformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_channel_set_readformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_channel_set_rawreadformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(fmt, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(active_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_channel_set_stream_topology(session->channel, ao2_bump(session->pending_media_state->topology));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       local = ast_sip_session_create_local_sdp(session->inv_session, session, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!local) {</span><br><span style="color: hsl(120, 100%, 40%);">+         packet = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                if (pjsip_inv_end_session(session->inv_session, 500, NULL, &packet) == PJ_SUCCESS && packet) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_sip_session_send_response(session, packet);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             ans_data->cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+               goto end;</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%);">+   pjmedia_sdp_neg_set_prefer_remote_codec_order(session->inv_session->neg, PJ_FALSE);</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!session->endpoint->preferred_codec_only) {</span><br><span style="color: hsl(120, 100%, 40%);">+         pjmedia_sdp_neg_set_answer_multiple_codecs(session->inv_session->neg, PJ_TRUE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+             SCOPE_ENTER(2, "pjsip_inv_answer\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               status = pjsip_inv_answer(session->inv_session, 200, NULL, local, &packet);</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT("pjsip_inv_answer\n");</span><br><span>  }</span><br><span>    pjsip_dlg_dec_lock(session->inv_session->dlg);</span><br><span> </span><br><span>@@ -708,12 +753,14 @@</span><br><span>             ast_sip_session_send_response(session, packet);</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+end:</span><br><span> #ifdef HAVE_PJSIP_INV_SESSION_REF</span><br><span>     pjsip_inv_dec_ref(session->inv_session);</span><br><span> #endif</span><br><span> </span><br><span>    if (status != PJ_SUCCESS) {</span><br><span>          char err[PJ_ERR_MSG_SIZE];</span><br><span style="color: hsl(120, 100%, 40%);">+            ans_data->cause = AST_CAUSE_FAILURE;</span><br><span> </span><br><span>          pj_strerror(status, err, sizeof(err));</span><br><span>               ast_log(LOG_WARNING,"Cannot answer '%s': %s\n",</span><br><span>@@ -724,21 +771,23 @@</span><br><span>             */</span><br><span>          SCOPE_EXIT_RTN_VALUE(-2, "pjproject failure\n");</span><br><span>   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  SCOPE_EXIT_RTN_VALUE(0);</span><br><span> }</span><br><span> </span><br><span> /*! \brief Function called by core when we should answer a PJSIP session */</span><br><span style="color: hsl(0, 100%, 40%);">-static int chan_pjsip_answer(struct ast_channel *ast)</span><br><span style="color: hsl(120, 100%, 40%);">+static int chan_pjsip_answer_with_stream_topology(struct ast_channel *ast,</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_stream_topology *topology)</span><br><span> {</span><br><span>   struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);</span><br><span>     struct ast_sip_session *session;</span><br><span>     struct answer_data ans_data = { 0, };</span><br><span>        int res;</span><br><span style="color: hsl(0, 100%, 40%);">-        SCOPE_ENTER(1, "%s\n", ast_channel_name(ast));</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_ENTER(1, "%s  Topology: %s\n", ast_channel_name(ast),</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_str_tmp(256, ast_stream_topology_to_str(topology, &STR_TMP)));</span><br><span> </span><br><span>   if (ast_channel_state(ast) == AST_STATE_UP) {</span><br><span>                SCOPE_EXIT_RTN_VALUE(0, "Already up\n");</span><br><span style="color: hsl(0, 100%, 40%);">-              return 0;</span><br><span>    }</span><br><span> </span><br><span>        ast_setstate(ast, AST_STATE_UP);</span><br><span>@@ -757,6 +806,7 @@</span><br><span>          attempts to do direct media) */</span><br><span>   ast_channel_unlock(ast);</span><br><span>     ans_data.session = session;</span><br><span style="color: hsl(120, 100%, 40%);">+   ans_data.topology = topology;</span><br><span>        ans_data.indent = ast_trace_get_indent();</span><br><span>    res = ast_sip_push_task_wait_serializer(session->serializer, answer, &ans_data);</span><br><span>      if (res) {</span><br><span>@@ -774,7 +824,12 @@</span><br><span>    ao2_ref(session, -1);</span><br><span>        ast_channel_lock(ast);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      SCOPE_EXIT_RTN_VALUE(0);</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_EXIT_RTN_VALUE(0, "Cause: %d %s\n", ans_data.cause, ast_cause2str(ans_data.cause));</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 chan_pjsip_answer(struct ast_channel *ast)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    return chan_pjsip_answer_with_stream_topology(ast, NULL);</span><br><span> }</span><br><span> </span><br><span> /*! \brief Internal helper function called when CNG tone is detected */</span><br><span>@@ -1620,9 +1675,9 @@</span><br><span> static int handle_topology_request_change(struct ast_sip_session *session,</span><br><span>  const struct ast_stream_topology *proposed)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        struct topology_change_refresh_data *refresh_data;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct topology_change_refresh_data *refresh_data = { 0, };</span><br><span>  int res;</span><br><span style="color: hsl(0, 100%, 40%);">-        SCOPE_ENTER(1);</span><br><span style="color: hsl(120, 100%, 40%);">+       SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(refresh_data->session));</span><br><span> </span><br><span>    refresh_data = topology_change_refresh_data_alloc(session, proposed);</span><br><span>        if (!refresh_data) {</span><br><span>@@ -1649,7 +1704,7 @@</span><br><span>         const struct ast_stream_topology *topology;</span><br><span>  struct ast_frame f = { .frametype = AST_FRAME_CONTROL, .subclass = { .integer = condition } };</span><br><span>       char subclass[40] = "";</span><br><span style="color: hsl(0, 100%, 40%);">-       SCOPE_ENTER(1, "%s Handling %s\n", ast_channel_name(ast),</span><br><span style="color: hsl(120, 100%, 40%);">+   SCOPE_ENTER(1, "%s Handling %d %s\n", ast_channel_name(ast), condition,</span><br><span>            ast_frame_subclass2str(&f, subclass, sizeof(subclass), NULL, 0));</span><br><span> </span><br><span>    switch (condition) {</span><br><span>@@ -2433,7 +2488,6 @@</span><br><span>                 ast_str_tmp(256, ast_stream_topology_to_str(channel->session->pending_media_state->topology, &STR_TMP))</span><br><span>                 );</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       res = ast_sip_session_create_invite(session, &tdata);</span><br><span> </span><br><span>        if (res) {</span><br><span>@@ -2444,6 +2498,7 @@</span><br><span>           update_initial_connected_line(session);</span><br><span>              ast_sip_session_send_request(session, tdata);</span><br><span>        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  ao2_ref(channel, -1);</span><br><span>        SCOPE_EXIT_RTN_VALUE(res, "RC: %d\n", res);</span><br><span> }</span><br><span>@@ -2456,6 +2511,8 @@</span><br><span>           ast_str_tmp(256, ast_stream_topology_to_str(channel->session->pending_media_state->topology, &STR_TMP)));</span><br><span> </span><br><span>   ao2_ref(channel, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(2, "%s Push call task\n", ast_sip_session_get_name(channel->session));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      if (ast_sip_push_task(channel->session->serializer, call, channel)) {</span><br><span>          ast_log(LOG_WARNING, "Error attempting to place outbound call to '%s'\n", dest);</span><br><span>           ao2_cleanup(channel);</span><br><span>@@ -2591,7 +2648,7 @@</span><br><span>                SCOPE_EXIT_RTN_VALUE(-1, "No channel or session\n");</span><br><span>       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));</span><br><span style="color: hsl(120, 100%, 40%);">+   cause = hangup_cause2sip(ast_channel_hangupcause(ast));</span><br><span>      h_data = hangup_data_alloc(cause, ast);</span><br><span> </span><br><span>  if (!h_data) {</span><br><span>@@ -2616,142 +2673,262 @@</span><br><span>   SCOPE_EXIT_RTN_VALUE(-1, "Cause: %d\n", cause);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_endpoint *determine_endpoint(char *destination, char **request_user,</span><br><span style="color: hsl(120, 100%, 40%);">+        int *cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_endpoint *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *endpoint_name;</span><br><span style="color: hsl(120, 100%, 40%);">+  SCOPE_ENTER(1, "%s\n", destination);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ast_sip_get_disable_multi_domain()) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* If a request user has been specified extract it from the endpoint name portion */</span><br><span style="color: hsl(120, 100%, 40%);">+          if ((endpoint_name = strchr(destination, '@'))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     *request_user = destination;</span><br><span style="color: hsl(120, 100%, 40%);">+                  *endpoint_name++ = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+              } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      endpoint_name = ast_strdupa(destination);</span><br><span style="color: hsl(120, 100%, 40%);">+                     *request_user = NULL;</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 (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (*request_user) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                   *request_user);</span><br><span style="color: hsl(120, 100%, 40%);">+                       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_RTN_VALUE(NULL, "Empty endpoint name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                        *cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_RTN_VALUE(NULL, "Endpoint not found\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             SCOPE_EXIT_RTN_VALUE(endpoint, "Endpoint: %s\n", endpoint_name);</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%);">+   /* Multi-domain is possible */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* First try to find an exact endpoint match, for single (user) or multi-domain (user@domain) */</span><br><span style="color: hsl(120, 100%, 40%);">+      endpoint_name = ast_strdupa(destination);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_RTN_VALUE(NULL, "Empty endpoint name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* It seems it's not a multi-domain endpoint or single endpoint exact match,</span><br><span style="color: hsl(120, 100%, 40%);">+               * it's possible that it's a SIP trunk with a specified user (user@trunkname),</span><br><span style="color: hsl(120, 100%, 40%);">+                 * so extract the user before @ sign.</span><br><span style="color: hsl(120, 100%, 40%);">+          */</span><br><span style="color: hsl(120, 100%, 40%);">+           endpoint_name = strchr(destination, '@');</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!endpoint_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /*</span><br><span style="color: hsl(120, 100%, 40%);">+                     * Couldn't find an '@' so it had to be an endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+                        * name that doesn't exist.</span><br><span style="color: hsl(120, 100%, 40%);">+                        */</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                destination);</span><br><span style="color: hsl(120, 100%, 40%);">+                 *cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_RTN_VALUE(NULL, "Endpoint '%s' not found\n", destination);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             *request_user = destination;</span><br><span style="color: hsl(120, 100%, 40%);">+          *endpoint_name++ = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                           *request_user);</span><br><span style="color: hsl(120, 100%, 40%);">+                       *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_RTN_VALUE(NULL, "Empty endpoint\n");</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%);">+           endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                        *cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(120, 100%, 40%);">+                      SCOPE_EXIT_RTN_VALUE(NULL, "Endpoint '%s' not found\n", destination);</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 style="color: hsl(120, 100%, 40%);">+   SCOPE_EXIT_RTN_VALUE(endpoint, "Endpoint: %s\n", endpoint_name);</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 style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Bounce a topology off an endpoint's configured topology</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param topology The pending topology</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param endpoint The endpoint to get the configured topology from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param prefs The codec negotiation prefs to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param [out]cause A pointer to a cause in which to set the resulting cause code</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval Non-null topology which may be empty</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL is there was a system error like a failure to create an object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This function will always return a topology unless system issues prevent it.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The topology MAY be empty if there were no codecs in common but transcoding was</span><br><span style="color: hsl(120, 100%, 40%);">+ * allowed.</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 style="color: hsl(120, 100%, 40%);">+static struct ast_stream_topology *resolve_topology(struct ast_stream_topology *topology,</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_endpoint *endpoint, struct ast_stream_codec_negotiation_prefs *prefs, int *cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_stream_topology *joint_topology;</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *endpoint_name = ast_sorcery_object_get_id(endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+      int valid_stream_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_str *error_message;</span><br><span style="color: hsl(120, 100%, 40%);">+        SCOPE_ENTER(2, "Endpoint: %s ReqTopology: %s ConfigTopology: %s Prefs: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+               endpoint_name,</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_str_tmp(256, ast_stream_topology_to_str(topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_str_tmp(256, ast_stream_topology_to_str(endpoint->media.topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_str_tmp(256, ast_stream_codec_prefs_to_str(prefs, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!endpoint->media.topology || !ast_stream_topology_get_active_count(endpoint->media.topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Endpoint '%s': No configured topology\n", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+               SCOPE_EXIT_RTN_VALUE(NULL, "No configured topology\n");</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 style="color: hsl(120, 100%, 40%);">+     * Use the endpoint's topology if none was specified.  This can occur if the</span><br><span style="color: hsl(120, 100%, 40%);">+       * caller's channel tech doesn't support streams.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!topology || !ast_stream_topology_get_count(topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          joint_topology = ast_stream_topology_clone(endpoint->media.topology);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!joint_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_log(LOG_ERROR, "Endpoint '%s': Couldn't clone endpoint's topology\n", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+                   SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't clone endpoint's topology\n");</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%);">+           /* No point in doing any comparisons since the joint topology == endpoint's */</span><br><span style="color: hsl(120, 100%, 40%);">+            *cause = AST_CAUSE_NORMAL_CLEARING;</span><br><span style="color: hsl(120, 100%, 40%);">+           SCOPE_EXIT_RTN_VALUE(joint_topology, "No request topology. Using endpoint's: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_str_tmp(256, ast_stream_topology_to_str(joint_topology, &STR_TMP)));</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%);">+   error_message = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!error_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+         *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Endpoint '%s': Couldn't create error_message\n", endpoint_name);</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create error_message\n");</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%);">+   *cause = AST_CAUSE_NORMAL_CLEARING;</span><br><span style="color: hsl(120, 100%, 40%);">+   joint_topology = ast_stream_topology_create_resolved(topology, endpoint->media.topology,</span><br><span style="color: hsl(120, 100%, 40%);">+           prefs, &error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!joint_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+                *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Endpoint '%s': Couldn't create even an empty joint topology %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  endpoint_name, error_message ? ast_str_buffer(error_message): "Not enough memory");</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_free(error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create even an empty joint topology\n");</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%);">+   valid_stream_count = ast_stream_topology_get_active_count(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+    /* TODO: Do this on a stream by stream basis */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (valid_stream_count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (prefs->transcode == CODEC_NEGOTIATION_TRANSCODE_ALLOW) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ao2_cleanup(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+                  joint_topology = ast_stream_topology_clone(endpoint->media.topology);</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (!joint_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_log(LOG_ERROR, "Endpoint '%s': Fatal error %s\n", endpoint_name, error_message ? ast_str_buffer(error_message): "Not enough memory");</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_free(error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+                              *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+                           SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't clone endpoint's topology\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     *cause = AST_CAUSE_NORMAL_CLEARING;</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_trace(2, "No codecs in common but transcoding allowed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_trace(2, "No codecs in common and transcoding not allowed: %s\n", error_message ? ast_str_buffer(error_message): "Not enough memory");</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_log(LOG_ERROR, "Endpoint '%s': %s\n", endpoint_name, error_message ? ast_str_buffer(error_message): "Not enough memory");</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 style="color: hsl(120, 100%, 40%);">+   ast_free(error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_EXIT_RTN_VALUE(joint_topology, "Cause: %d %s Joint topology: %s\n", *cause, ast_cause2str(*cause),</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_str_tmp(256, ast_stream_topology_to_str(joint_topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct request_data {</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_endpoint *endpoint;</span><br><span>   struct ast_stream_topology *topology;</span><br><span style="color: hsl(0, 100%, 40%);">-   const char *dest;</span><br><span style="color: hsl(0, 100%, 40%);">-       int cause;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_session *session;</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *aor;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *request_user;</span><br><span style="color: hsl(120, 100%, 40%);">+   int *cause;</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned long indent;</span><br><span> };</span><br><span> </span><br><span> static int request(void *obj)</span><br><span> {</span><br><span>        struct request_data *req_data = obj;</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_sip_session *session = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">- char *tmp = ast_strdupa(req_data->dest), *endpoint_name = NULL, *request_user = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_sip_endpoint *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *endpoint_name = ast_sorcery_object_get_id(req_data->endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER_TASK(1, req_data->indent, "%s\n", endpoint_name);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(0, 100%, 40%);">-              AST_APP_ARG(endpoint);</span><br><span style="color: hsl(0, 100%, 40%);">-          AST_APP_ARG(aor);</span><br><span style="color: hsl(0, 100%, 40%);">-       );</span><br><span style="color: hsl(0, 100%, 40%);">-      SCOPE_ENTER(1, "%s\n",tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ req_data->session = ast_sip_session_create_outgoing(req_data->endpoint, NULL, req_data->aor,</span><br><span style="color: hsl(120, 100%, 40%);">+         req_data->request_user, req_data->topology);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (ast_strlen_zero(tmp)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty destination\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(0, 100%, 40%);">-            SCOPE_EXIT_RTN_VALUE(-1, "Empty destination\n");</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       AST_NONSTANDARD_APP_ARGS(args, tmp, '/');</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_sip_get_disable_multi_domain()) {</span><br><span style="color: hsl(0, 100%, 40%);">-               /* If a request user has been specified extract it from the endpoint name portion */</span><br><span style="color: hsl(0, 100%, 40%);">-            if ((endpoint_name = strchr(args.endpoint, '@'))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     request_user = args.endpoint;</span><br><span style="color: hsl(0, 100%, 40%);">-                   *endpoint_name++ = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-                } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        endpoint_name = args.endpoint;</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (request_user) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                     request_user);</span><br><span style="color: hsl(0, 100%, 40%);">-                  } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                      }</span><br><span style="color: hsl(0, 100%, 40%);">-                       req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(0, 100%, 40%);">-                    SCOPE_EXIT_RTN_VALUE(-1, "Empty endpoint name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-               endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",</span><br><span style="color: hsl(0, 100%, 40%);">-                      endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-         if (!endpoint) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-                  req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(0, 100%, 40%);">-                    SCOPE_EXIT_RTN_VALUE(-1, "Endpoint not found\n");</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* First try to find an exact endpoint match, for single (user) or multi-domain (user@domain) */</span><br><span style="color: hsl(0, 100%, 40%);">-                endpoint_name = args.endpoint;</span><br><span style="color: hsl(0, 100%, 40%);">-          if (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                      req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(0, 100%, 40%);">-                    SCOPE_EXIT_RTN_VALUE(-1, "Empty endpoint name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-               endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",</span><br><span style="color: hsl(0, 100%, 40%);">-                      endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-         if (!endpoint) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* It seems it's not a multi-domain endpoint or single endpoint exact match,</span><br><span style="color: hsl(0, 100%, 40%);">-                         * it's possible that it's a SIP trunk with a specified user (user@trunkname),</span><br><span style="color: hsl(0, 100%, 40%);">-                   * so extract the user before @ sign.</span><br><span style="color: hsl(0, 100%, 40%);">-                    */</span><br><span style="color: hsl(0, 100%, 40%);">-                     endpoint_name = strchr(args.endpoint, '@');</span><br><span style="color: hsl(0, 100%, 40%);">-                     if (!endpoint_name) {</span><br><span style="color: hsl(0, 100%, 40%);">-                           /*</span><br><span style="color: hsl(0, 100%, 40%);">-                               * Couldn't find an '@' so it had to be an endpoint</span><br><span style="color: hsl(0, 100%, 40%);">-                          * name that doesn't exist.</span><br><span style="color: hsl(0, 100%, 40%);">-                          */</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                  args.endpoint);</span><br><span style="color: hsl(0, 100%, 40%);">-                         req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(0, 100%, 40%);">-                            SCOPE_EXIT_RTN_VALUE(-1, "Endpoint not found\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                     }</span><br><span style="color: hsl(0, 100%, 40%);">-                       request_user = args.endpoint;</span><br><span style="color: hsl(0, 100%, 40%);">-                   *endpoint_name++ = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (ast_strlen_zero(endpoint_name)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                           ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                     request_user);</span><br><span style="color: hsl(0, 100%, 40%);">-                          req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;</span><br><span style="color: hsl(0, 100%, 40%);">-                            SCOPE_EXIT_RTN_VALUE(-1, "Empty endpoint name\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",</span><br><span style="color: hsl(0, 100%, 40%);">-                              endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (!endpoint) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-                          req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(0, 100%, 40%);">-                            SCOPE_EXIT_RTN_VALUE(-1, "Endpoint not found\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                     }</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user,</span><br><span style="color: hsl(0, 100%, 40%);">-               req_data->topology);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(endpoint, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!session) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!req_data->session) {</span><br><span>                 ast_log(LOG_ERROR, "Failed to create outgoing session to endpoint '%s'\n", endpoint_name);</span><br><span style="color: hsl(0, 100%, 40%);">-            req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(0, 100%, 40%);">-            SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create session\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          *req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;</span><br><span style="color: hsl(120, 100%, 40%);">+         SCOPE_EXIT_RTN_VALUE(-1, "No session\n");</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   req_data->session = session;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      SCOPE_EXIT_RTN_VALUE(0);</span><br><span> }</span><br><span> </span><br><span> /*! \brief Function called by core to create a new outgoing PJSIP session */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *type,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_stream_topology *req_topology, const struct ast_assigned_ids *assignedids,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_channel *requestor, const char *data, int *cause)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct request_data req_data;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct request_data req_data = { 0, };</span><br><span style="color: hsl(120, 100%, 40%);">+        RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct ast_stream_topology *, resolved_topology, NULL, ao2_cleanup);</span><br><span>        RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       char *tmp_args = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_APP_ARG(endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_APP_ARG(aor);</span><br><span style="color: hsl(120, 100%, 40%);">+     );</span><br><span>   SCOPE_ENTER(1, "%s Topology: %s\n", data,</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_str_tmp(256, ast_stream_topology_to_str(topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_str_tmp(256, ast_stream_topology_to_str(resolved_topology, &STR_TMP)));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     req_data.topology = topology;</span><br><span style="color: hsl(0, 100%, 40%);">-   req_data.dest = data;</span><br><span style="color: hsl(0, 100%, 40%);">-   /* Default failure value in case ast_sip_push_task_wait_servant() itself fails. */</span><br><span style="color: hsl(0, 100%, 40%);">-      req_data.cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_NONSTANDARD_APP_ARGS(args, tmp_args, '/');</span><br><span style="color: hsl(120, 100%, 40%);">+        endpoint = determine_endpoint(args.endpoint, &req_data.request_user, cause);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_RTN_VALUE(NULL, "No endpoint\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ resolved_topology = resolve_topology(req_topology, endpoint, &endpoint->media.outgoing_offer_codec_prefs, cause);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!resolved_topology || *cause != AST_CAUSE_NORMAL_CLEARING</span><br><span style="color: hsl(120, 100%, 40%);">+         || ast_stream_topology_get_active_count(resolved_topology) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            SCOPE_EXIT_RTN_VALUE(NULL, "No resolved_topology\n");</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%);">+   req_data.endpoint = endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+ req_data.topology = resolved_topology;</span><br><span style="color: hsl(120, 100%, 40%);">+        req_data.cause = cause;</span><br><span style="color: hsl(120, 100%, 40%);">+       req_data.aor = args.aor;</span><br><span style="color: hsl(120, 100%, 40%);">+      req_data.indent = ast_trace_get_indent();</span><br><span>    if (ast_sip_push_task_wait_servant(NULL, request, &req_data)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             *cause = req_data.cause;</span><br><span style="color: hsl(120, 100%, 40%);">+              *cause = *req_data.cause;</span><br><span>            SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't push task\n");</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!req_data.session) {</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_RTN_VALUE(NULL, "No session\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span>    session = req_data.session;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (!(session->channel = chan_pjsip_new(session, AST_STATE_DOWN, NULL, NULL, assignedids, requestor, NULL))) {</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Session needs to be terminated prematurely */</span><br><span style="color: hsl(120, 100%, 40%);">+      session->channel = chan_pjsip_new(session, AST_STATE_DOWN, NULL, NULL, assignedids, requestor, NULL, resolved_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!session->channel) {</span><br><span>          SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create channel\n");</span><br><span>       }</span><br><span> </span><br><span>@@ -3077,8 +3254,10 @@</span><br><span> static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span> {</span><br><span>  RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_stream_topology *, resolved_topology, NULL, ao2_cleanup);</span><br><span>        struct transport_info_data *transport_data;</span><br><span>  pjsip_tx_data *packet = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int cause;</span><br><span>   SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span>     if (session->channel) {</span><br><span>@@ -3115,7 +3294,18 @@</span><br><span>  datastore->data = transport_data;</span><br><span>         ast_sip_session_add_datastore(session, datastore);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (!(session->channel = chan_pjsip_new(session, AST_STATE_RING, session->exten, NULL, NULL, NULL, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+    resolved_topology = resolve_topology(session->pending_media_state->topology, session->endpoint,</span><br><span style="color: hsl(120, 100%, 40%);">+              &session->endpoint->media.incoming_offer_codec_prefs, &cause);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!resolved_topology || cause != AST_CAUSE_NORMAL_CLEARING</span><br><span style="color: hsl(120, 100%, 40%);">+          || ast_stream_topology_get_active_count(resolved_topology) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (pjsip_inv_end_session(session->inv_session, 488, NULL, &packet) == PJ_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+                      && packet) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_sip_session_send_response(session, packet);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             SCOPE_EXIT_RTN_VALUE(-1, "No resolved topology\n");</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 (!(session->channel = chan_pjsip_new(session, AST_STATE_RING, session->exten, NULL, NULL, NULL, NULL, resolved_topology))) {</span><br><span>                if (pjsip_inv_end_session(session->inv_session, 503, NULL, &packet) == PJ_SUCCESS</span><br><span>                     && packet) {</span><br><span>                         ast_sip_session_send_response(session, packet);</span><br><span>@@ -3124,8 +3314,10 @@</span><br><span>             ast_log(LOG_ERROR, "Failed to allocate new PJSIP channel on incoming SIP INVITE\n");</span><br><span>               SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create channel\n");</span><br><span>         }</span><br><span style="color: hsl(0, 100%, 40%);">-       /* channel gets created on incoming request, but we wait to call start</span><br><span style="color: hsl(0, 100%, 40%);">-           so other supplements have a chance to run */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_stream_topology_free(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+       session->pending_media_state->topology = ao2_bump(resolved_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        SCOPE_EXIT_RTN_VALUE(0);</span><br><span> }</span><br><span> </span><br><span>@@ -3210,6 +3402,11 @@</span><br><span> </span><br><span>       ast_debug(3, "Started PBX on new PJSIP channel %s\n", ast_channel_name(session->channel));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   ast_trace(1, "Channel: %p %d  ChanTopo: %p %d  SessionTopo: %p %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+               session->channel, ao2_ref(session->channel, 0), ast_channel_get_stream_topology(session->channel),</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_ref(ast_channel_get_stream_topology(session->channel), 0), session->pending_media_state->topology,</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(session->pending_media_state->topology, 0));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>         SCOPE_EXIT_RTN_VALUE((res == AST_PBX_SUCCESS) ? 0 : -1, "RC: %d\n", res);</span><br><span> }</span><br><span> </span><br><span>@@ -3220,7 +3417,8 @@</span><br><span> };</span><br><span> </span><br><span> /*! \brief Function called when a response is received on the session */</span><br><span style="color: hsl(0, 100%, 40%);">-static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_incoming_response(enum ast_sip_session_response_priority priority,</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span> {</span><br><span>   struct pjsip_status_line status = rdata->msg_info.msg->line.status;</span><br><span>    struct ast_control_pvt_cause_code *cause_code;</span><br><span>@@ -3273,10 +3471,32 @@</span><br><span>             }</span><br><span>            break;</span><br><span>       case 200:</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_trace(1, "%s Method: %.*s Status: %d  Queueing ANSWER\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(0, 100%, 40%);">-                      (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (priority == AST_SIP_SESSION_AFTER_MEDIA) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        struct ast_str *error_message = ast_str_alloca(128);</span><br><span style="color: hsl(120, 100%, 40%);">+                  struct ast_stream_topology *resolved_topology = ast_stream_topology_create_resolved(</span><br><span style="color: hsl(120, 100%, 40%);">+                          session->active_media_state->topology, session->pending_media_state->topology,</span><br><span style="color: hsl(120, 100%, 40%);">+                            &session->endpoint->media.incoming_answer_codec_prefs, &error_message);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-               ast_queue_control(session->channel, AST_CONTROL_ANSWER);</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!resolved_topology || ast_stream_topology_get_active_count(resolved_topology) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_log(LOG_ERROR, "%s %s\n", ast_sip_session_get_name(session), ast_str_buffer(error_message));</span><br><span style="color: hsl(120, 100%, 40%);">+                            ao2_cleanup(resolved_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_queue_hangup_with_cause(session->channel, AST_CAUSE_NO_ROUTE_DESTINATION);</span><br><span style="color: hsl(120, 100%, 40%);">+                             SCOPE_EXIT_RTN("%s\n", ast_str_buffer(error_message));</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%);">+                   ast_trace(1, "%s Method: %.*s Status: %d  Queueing ANSWER with topology %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_sip_session_get_name(session), (int)rdata->msg_info.cseq->method.name.slen,</span><br><span style="color: hsl(120, 100%, 40%);">+                         rdata->msg_info.cseq->method.name.ptr, status.code,</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_str_tmp(256, ast_stream_topology_to_str(resolved_topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_queue_control_data(session->channel, AST_CONTROL_ANSWER,</span><br><span style="color: hsl(120, 100%, 40%);">+                               &resolved_topology, sizeof(resolved_topology));</span><br><span style="color: hsl(120, 100%, 40%);">+           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_trace(1, "%s Method: %.*s Status: %d  Deferring ANSWER until after media\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+                         (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span>            break;</span><br><span>       default:</span><br><span>             ast_trace(1, "%s Method: %.*s Status: %d  Ignored\n", ast_sip_session_get_name(session),</span><br><span>@@ -3287,6 +3507,55 @@</span><br><span>  SCOPE_EXIT_RTN();</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when a response is received on the session before media */</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_incoming_response_before_media(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_ENTER(1, "%s Method: %.*s Status: %d  Before Media\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+             (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr,</span><br><span style="color: hsl(120, 100%, 40%);">+               rdata->msg_info.msg->line.status.code);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       chan_pjsip_incoming_response(AST_SIP_SESSION_BEFORE_MEDIA, session, rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+   SCOPE_EXIT_RTN();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when a response is received on the session after media */</span><br><span style="color: hsl(120, 100%, 40%);">+static void chan_pjsip_incoming_response_after_media(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_format_cap *active_caps;</span><br><span style="color: hsl(120, 100%, 40%);">+   SCOPE_ENTER(1, "%s Method: %.*s Status: %d  After Media  Active Topology: %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+         (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr,</span><br><span style="color: hsl(120, 100%, 40%);">+               rdata->msg_info.msg->line.status.code,</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP))</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%);">+  chan_pjsip_incoming_response(AST_SIP_SESSION_AFTER_MEDIA, session, rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  active_caps = ast_stream_topology_get_formats(session->active_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_nativeformats_set(session->channel, active_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!ast_format_cap_empty(active_caps)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct ast_format *fmt;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             fmt = ast_format_cap_get_format(active_caps, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_channel_set_writeformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_channel_set_rawwriteformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_channel_set_readformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_channel_set_rawreadformat(session->channel, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(fmt, -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%);">+   ast_channel_set_stream_topology(session->channel, ao2_bump(session->active_media_state->topology));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_cleanup(active_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_EXIT_RTN();</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 void chan_pjsip_outgoing_response(struct ast_sip_session *session, struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_ENTER(1, "%s Status: %d\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+                (int)tdata->msg->line.status.code);</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_EXIT_RTN();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span> {</span><br><span>      SCOPE_ENTER(1, "%s Method: %.*s Status: %d  After Media\n", ast_sip_session_get_name(session),</span><br><span>@@ -3403,7 +3672,8 @@</span><br><span>     ast_sip_register_service(&refer_callback_module);</span><br><span> </span><br><span>    ast_sip_session_register_supplement(&chan_pjsip_supplement);</span><br><span style="color: hsl(0, 100%, 40%);">-        ast_sip_session_register_supplement(&chan_pjsip_supplement_response);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sip_session_register_supplement(&chan_pjsip_supplement_response_before_media);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_sip_session_register_supplement(&chan_pjsip_supplement_response_after_media);</span><br><span> </span><br><span>    if (!(pjsip_uids_onhold = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK,</span><br><span>                        AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, 37, uid_hold_hash_fn,</span><br><span>@@ -3435,7 +3705,8 @@</span><br><span>   pjsip_uids_onhold = NULL;</span><br><span>    ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);</span><br><span>       ast_sip_session_unregister_supplement(&pbx_start_supplement);</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response_after_media);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response_before_media);</span><br><span>     ast_sip_session_unregister_supplement(&chan_pjsip_supplement);</span><br><span>   ast_sip_session_unregister_supplement(&call_pickup_supplement);</span><br><span>  ast_sip_unregister_service(&refer_callback_module);</span><br><span>@@ -3459,7 +3730,8 @@</span><br><span> </span><br><span>  pjsip_channel_cli_unregister();</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response_after_media);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response_before_media);</span><br><span>     ast_sip_session_unregister_supplement(&chan_pjsip_supplement);</span><br><span>   ast_sip_session_unregister_supplement(&pbx_start_supplement);</span><br><span>    ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);</span><br><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index 2636f63..894f287 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -802,48 +802,7 @@</span><br><span>                    ; "0" or not enabled)</span><br><span> ;contact_user= ; On outgoing requests, force the user portion of the Contact</span><br><span>                ; header to this value (default: "")</span><br><span style="color: hsl(0, 100%, 40%);">-;incoming_call_offer_pref= ; Based on this setting, a joint list of</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; preferred codecs between those received in an</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; incoming SDP offer (remote), and those specified</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; in the endpoint's "allow" parameter (local)</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; is created and is passed to the Asterisk core.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ;</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local - Include all codecs in the local list that</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; are also in the remote list preserving the local</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; order. (default).</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local_first - Include only the first codec in the</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local list that is also in the remote list.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote - Include all codecs in the remote list that</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; are also in the local list preserving remote list</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; order.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote_first - Include only the first codec in</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; the remote list that is also in the local list.</span><br><span style="color: hsl(0, 100%, 40%);">-;outgoing_call_offer_pref= ; Based on this setting, a joint list of</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; preferred codecs between those received from the</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; Asterisk core (remote), and those specified in</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; the endpoint's "allow" parameter (local) is</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; created and is used to create the outgoing SDP</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; offer.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ;</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local - Include all codecs in the local list that</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; are also in the remote list preserving the local</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; order.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local_merge - Include all codecs in BOTH lists</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; preserving the local list order.  Codes in the</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote list not in the local list will be placed</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; at the end of the joint list.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local_first - Include only the first codec in the</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local list.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote - Include all codecs in the remote list that</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; are also in the local list preserving remote list</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; order. (default)</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote_merge - Include all codecs in BOTH lists</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; preserving the remote list order.  Codes in the</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; local list not in the remote list will be placed</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; at the end of the joint list.</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; remote_first - Include only the first codec in</span><br><span style="color: hsl(0, 100%, 40%);">-                           ; the remote list.</span><br><span style="color: hsl(0, 100%, 40%);">-;incoming_offer_codec_prefs=; This is a string that describes how the codecs</span><br><span style="color: hsl(120, 100%, 40%);">+;codec_prefs_incoming_offer=; This is a string that describes how the codecs</span><br><span>                             ; specified on an incoming SDP offer (pending) are</span><br><span>                             ; reconciled with the codecs specified on an endpoint</span><br><span>                             ; (configured) before being sent to the Asterisk core.</span><br><span>@@ -855,7 +814,7 @@</span><br><span>                             ;    | only_nonpreferred>,</span><br><span>                             ; keep: <first | all>,</span><br><span>                             ; transcode: <allow | prevent></span><br><span style="color: hsl(0, 100%, 40%);">-;outgoing_offer_codec_prefs=; This is a string that describes how the codecs</span><br><span style="color: hsl(120, 100%, 40%);">+;codec_prefs_outgoing_offer=; This is a string that describes how the codecs</span><br><span>                             ; specified in the topology that comes from the</span><br><span>                             ; Asterisk core (pending) are reconciled with the</span><br><span>                             ; codecs specified on an endpoint (configured)</span><br><span>@@ -868,7 +827,7 @@</span><br><span>                             ;    | only_preferred | only_nonpreferred>,</span><br><span>                             ; keep: <first | all>,</span><br><span>                             ; transcode: <allow | prevent></span><br><span style="color: hsl(0, 100%, 40%);">-;incoming_answer_codec_prefs=; This is a string that describes how the codecs</span><br><span style="color: hsl(120, 100%, 40%);">+;codec_prefs_incoming_answer=; This is a string that describes how the codecs</span><br><span>                              ; specified in an incoming SDP answer (pending)</span><br><span>                              ; are reconciled with the codecs specified on an</span><br><span>                              ; endpoint (configured) when receiving an SDP</span><br><span>@@ -880,7 +839,7 @@</span><br><span>                              ; operation: <intersect | union</span><br><span>                              ;    | only_preferred | only_nonpreferred>,</span><br><span>                              ; keep: <first | all></span><br><span style="color: hsl(0, 100%, 40%);">-;outgoing_answer_codec_prefs=; This is a string that describes how the codecs</span><br><span style="color: hsl(120, 100%, 40%);">+;codec_prefs_outgoing_answer=; This is a string that describes how the codecs</span><br><span>                              ; that come from the core (pending) are reconciled</span><br><span>                              ; with the codecs specified on an endpoint</span><br><span>                              ; (configured) when sending an SDP answer.</span><br><span>diff --git a/contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py b/contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py</span><br><span>index 241185a..87b770c 100644</span><br><span>--- a/contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py</span><br><span>+++ b/contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py</span><br><span>@@ -8,7 +8,7 @@</span><br><span> </span><br><span> # revision identifiers, used by Alembic.</span><br><span> revision = 'b80485ff4dd0'</span><br><span style="color: hsl(0, 100%, 40%);">-down_revision = '79290b511e4b'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '61797b9fced6'</span><br><span> </span><br><span> from alembic import op</span><br><span> import sqlalchemy as sa</span><br><span>@@ -16,14 +16,14 @@</span><br><span> max_value_length = 128</span><br><span> </span><br><span> def upgrade():</span><br><span style="color: hsl(0, 100%, 40%);">-    op.add_column('ps_endpoints', sa.Column('incoming_offer_codec_prefs', sa.String(max_value_length)))</span><br><span style="color: hsl(0, 100%, 40%);">-    op.add_column('ps_endpoints', sa.Column('outgoing_offer_codec_prefs', sa.String(max_value_length)))</span><br><span style="color: hsl(0, 100%, 40%);">-    op.add_column('ps_endpoints', sa.Column('incoming_answer_codec_prefs', sa.String(max_value_length)))</span><br><span style="color: hsl(0, 100%, 40%);">-    op.add_column('ps_endpoints', sa.Column('outgoing_answer_codec_prefs', sa.String(max_value_length)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('codec_prefs_incoming_offer', sa.String(max_value_length)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('codec_prefs_outgoing_offer', sa.String(max_value_length)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('codec_prefs_incoming_answer', sa.String(max_value_length)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('codec_prefs_outgoing_answer', sa.String(max_value_length)))</span><br><span> </span><br><span> </span><br><span> def downgrade():</span><br><span style="color: hsl(0, 100%, 40%);">-    op.drop_column('ps_endpoints', 'incoming_offer_codecs_prefs')</span><br><span style="color: hsl(0, 100%, 40%);">-    op.drop_column('ps_endpoints', 'outgoing_offer_codecs_prefs')</span><br><span style="color: hsl(0, 100%, 40%);">-    op.drop_column('ps_endpoints', 'incoming_answer_codecs_prefs')</span><br><span style="color: hsl(0, 100%, 40%);">-    op.drop_column('ps_endpoints', 'outgoing_answer_codecs_prefs')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'codec_prefs_incoming_offer')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'codec_prefs_outgoing_offer')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'codec_prefs_incoming_answer')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'codec_prefs_outgoing_answer')</span><br><span>diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h</span><br><span>index cc90c83..20a159c 100644</span><br><span>--- a/include/asterisk/channel.h</span><br><span>+++ b/include/asterisk/channel.h</span><br><span>@@ -708,6 +708,19 @@</span><br><span>     int (* const answer)(struct ast_channel *chan);</span><br><span> </span><br><span>  /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * \brief Answer the channel with topology</span><br><span style="color: hsl(120, 100%, 40%);">+     * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+   *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \param chan The channel to answer</span><br><span style="color: hsl(120, 100%, 40%);">+   * \param topology The topology to use, probably the peer's.</span><br><span style="color: hsl(120, 100%, 40%);">+       *</span><br><span style="color: hsl(120, 100%, 40%);">+     * \note The topology may be NULL when the peer doesn't support streams</span><br><span style="color: hsl(120, 100%, 40%);">+    * or, in the case where transcoding is in effect, when this channel should use</span><br><span style="color: hsl(120, 100%, 40%);">+        * its existing topology.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+   int (* const answer_with_stream_topology)(struct ast_channel *chan, struct ast_stream_topology *topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*!</span><br><span>   * \brief Read a frame (or chain of frames from the same stream), in standard format (see frame.h)</span><br><span>    *</span><br><span>    * \param chan channel to read frames from</span><br><span>@@ -1081,6 +1094,12 @@</span><br><span>   * exist when the end_bridge_callback is called, then it needs to be fixed up properly</span><br><span>        */</span><br><span>  void (*end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator);</span><br><span style="color: hsl(120, 100%, 40%);">+    /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * When the peer queues an ANSWER control frame, it can indicate it's resolved topology.</span><br><span style="color: hsl(120, 100%, 40%);">+   * If the calling channel implements the answer_with_stream_topology callback, the</span><br><span style="color: hsl(120, 100%, 40%);">+     * peer's topology will be passed to the calling channel.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_stream_topology *peer_topology;</span><br><span> };</span><br><span> </span><br><span> struct chanmon;</span><br><span>@@ -1802,6 +1821,31 @@</span><br><span> int ast_raw_answer(struct ast_channel *chan);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Answer a channel passing in a stream topology</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel to answer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param topology the peer's stream topology</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function answers a channel and handles all necessary call</span><br><span style="color: hsl(120, 100%, 40%);">+ * setup functions.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The channel passed does not need to be locked, but is locked</span><br><span style="color: hsl(120, 100%, 40%);">+ * by the function when needed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Unlike ast_answer(), this function will not wait for media</span><br><span style="color: hsl(120, 100%, 40%);">+ * flow to begin. The caller should be careful before sending media</span><br><span style="color: hsl(120, 100%, 40%);">+ * to the channel before incoming media arrives, as the outgoing</span><br><span style="color: hsl(120, 100%, 40%);">+ * media may be lost.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The topology is usually that of the peer channel and may be NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_raw_answer_with_stream_topology(struct ast_channel *chan, struct ast_stream_topology *topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Answer a channel, with a selectable delay before returning</span><br><span>  *</span><br><span>  * \param chan channel to answer</span><br><span>@@ -5054,4 +5098,18 @@</span><br><span>  */</span><br><span> void *ast_channel_get_stream_topology_change_source(struct ast_channel *chan);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Checks if a channel's technology implements a particular callback function</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan The channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param function The function to look for</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 if the channel has a technology set and it implements the function</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 if the channel doesn't have a technology set or it doesn't implement the function</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_channel_has_tech_function(chan, function) \</span><br><span style="color: hsl(120, 100%, 40%);">+   (ast_channel_tech(chan) ? ast_channel_tech(chan)->function != NULL : 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _ASTERISK_CHANNEL_H */</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index eaa9b21..d072638 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -523,42 +523,6 @@</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Incoming/Outgoing call offer/answer joint codec preference.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * The default is INTERSECT ALL LOCAL.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-enum ast_sip_call_codec_pref {</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Two bits for merge */</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! Intersection of local and remote */</span><br><span style="color: hsl(0, 100%, 40%);">- AST_SIP_CALL_CODEC_PREF_INTERSECT =     1 << 0,</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Union of local and remote */</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_SIP_CALL_CODEC_PREF_UNION =         1 << 1,</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Two bits for filter */</span><br><span style="color: hsl(0, 100%, 40%);">-      /*! No filter */</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_SIP_CALL_CODEC_PREF_ALL =           1 << 2,</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Only the first */</span><br><span style="color: hsl(0, 100%, 40%);">-   AST_SIP_CALL_CODEC_PREF_FIRST =         1 << 3,</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Two bits for preference and sort   */</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! Prefer, and order by local values */</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_SIP_CALL_CODEC_PREF_LOCAL =         1 << 4,</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! Prefer, and order by remote values */</span><br><span style="color: hsl(0, 100%, 40%);">-       AST_SIP_CALL_CODEC_PREF_REMOTE =        1 << 5,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Returns true if the preference is set in the parameter</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 18.0.0</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param param A ast_flags struct with one or more of enum ast_sip_call_codec_pref set</span><br><span style="color: hsl(0, 100%, 40%);">- * \param codec_pref The last component of one of the enum values</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval 1 if the enum value is set</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval 0 if not</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define ast_sip_call_codec_pref_test(__param, __codec_pref) (!!(ast_test_flag( &__param, AST_SIP_CALL_CODEC_PREF_ ## __codec_pref )))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span>  * \brief Session timers options</span><br><span>  */</span><br><span> struct ast_sip_timer_options {</span><br><span>@@ -799,10 +763,6 @@</span><br><span>      unsigned int bundle;</span><br><span>         /*! Enable webrtc settings and defaults */</span><br><span>   unsigned int webrtc;</span><br><span style="color: hsl(0, 100%, 40%);">-    /*! Codec preference for an incoming offer */</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_flags incoming_call_offer_pref;</span><br><span style="color: hsl(0, 100%, 40%);">-      /*! Codec preference for an outgoing offer */</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_flags outgoing_call_offer_pref;</span><br><span>   /*! Codec negotiation prefs for incoming offers */</span><br><span>   struct ast_stream_codec_negotiation_prefs incoming_offer_codec_prefs;</span><br><span>        /*! Codec negotiation prefs for outgoing offers */</span><br><span>@@ -3296,12 +3256,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 9db68a8..1422036 100644</span><br><span>--- a/include/asterisk/res_pjsip_session.h</span><br><span>+++ b/include/asterisk/res_pjsip_session.h</span><br><span>@@ -935,4 +935,7 @@</span><br><span>  */</span><br><span> const char *ast_sip_session_get_name(const struct ast_sip_session *session);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct pjmedia_sdp_session *ast_sip_session_create_local_sdp(pjsip_inv_session *inv,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_session *session, const pjmedia_sdp_session *offer);</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>deleted file mode 100644</span><br><span>index 0d7020f..0000000</span><br><span>--- a/include/asterisk/res_pjsip_session_caps.h</span><br><span>+++ /dev/null</span><br><span>@@ -1,82 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/*</span><br><span style="color: hsl(0, 100%, 40%);">- * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Copyright (C) 2020, Sangoma Technologies Corporation</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Kevin Harwell <kharwell@sangoma.com></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(0, 100%, 40%);">- * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(0, 100%, 40%);">- * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(0, 100%, 40%);">- * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(0, 100%, 40%);">- * channels for your use.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is free software, distributed under the terms of</span><br><span style="color: hsl(0, 100%, 40%);">- * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(0, 100%, 40%);">- * at the top of the source tree.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#ifndef RES_PJSIP_SESSION_CAPS_H</span><br><span style="color: hsl(0, 100%, 40%);">-#define RES_PJSIP_SESSION_CAPS_H</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_format_cap;</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_sip_session;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Create joint capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 18.0.0</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Creates a list of joint capabilities between the given remote capabilities, and local ones.</span><br><span style="color: hsl(0, 100%, 40%);">- * "local" and "remote" reference the values in ast_sip_call_codec_pref.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param remote The "remote" capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \param local The "local" capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \param media_type The media type</span><br><span style="color: hsl(0, 100%, 40%);">- * \param codec_prefs One or more of enum ast_sip_call_codec_pref</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval A pointer to the joint capabilities (which may be empty).</span><br><span style="color: hsl(0, 100%, 40%);">- *         NULL will be returned only if no memory was available to allocate the structure.</span><br><span style="color: hsl(0, 100%, 40%);">- * \note Returned object's reference must be released at some point,</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-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(0, 100%, 40%);">-   struct ast_flags codec_pref);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Create a new stream of joint capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 18.0.0</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Creates a new stream with capabilities between the given session's local capabilities,</span><br><span style="color: hsl(0, 100%, 40%);">- * and the remote stream's.  Codec selection is based on the session->endpoint's codecs, the</span><br><span style="color: hsl(0, 100%, 40%);">- * session->endpoint's codec call preferences, and the stream passed by the core (for</span><br><span style="color: hsl(0, 100%, 40%);">- * outgoing calls) or created by the incoming SDP (for incoming calls).</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param session The session</span><br><span style="color: hsl(0, 100%, 40%);">- * \param remote The remote stream</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval A pointer to a new stream with the joint capabilities (which may be empty),</span><br><span style="color: hsl(0, 100%, 40%);">- *         NULL will be returned only if no memory was available to allocate the structure.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_stream *remote);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Create joint capabilities</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 18.0.0</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Creates a list of joint capabilities between the given session's local capabilities,</span><br><span style="color: hsl(0, 100%, 40%);">- * and the remote capabilities. Codec selection is based on the session->endpoint's codecs, the</span><br><span style="color: hsl(0, 100%, 40%);">- * session->endpoint's codec call preferences, and the "remote" capabilities passed by the core (for</span><br><span style="color: hsl(0, 100%, 40%);">- * outgoing calls) or created by the incoming SDP (for incoming calls).</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param session The session</span><br><span style="color: hsl(0, 100%, 40%);">- * \param media_type The media type</span><br><span style="color: hsl(0, 100%, 40%);">- * \param remote Capabilities received in an SDP offer or from the core</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval A pointer to the joint capabilities (which may be empty).</span><br><span style="color: hsl(0, 100%, 40%);">- *         NULL will be returned only if no memory was available to allocate the structure.</span><br><span style="color: hsl(0, 100%, 40%);">- * \note Returned object's reference must be released at some point,</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_format_cap *ast_sip_session_create_joint_call_cap(const struct ast_sip_session *session,</span><br><span style="color: hsl(0, 100%, 40%);">-        enum ast_media_type media_type, const struct ast_format_cap *remote);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#endif /* RES_PJSIP_SESSION_CAPS_H */</span><br><span>diff --git a/main/bridge_channel.c b/main/bridge_channel.c</span><br><span>index 251dea7..f9db06c 100644</span><br><span>--- a/main/bridge_channel.c</span><br><span>+++ b/main/bridge_channel.c</span><br><span>@@ -2307,11 +2307,10 @@</span><br><span>  */</span><br><span> static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel *chan;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_channel *chan = bridge_channel->chan;</span><br><span>  struct ast_option_header *aoh;</span><br><span>       int is_caller;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      chan = bridge_channel->chan;</span><br><span>      switch (fr->subclass.integer) {</span><br><span>   case AST_CONTROL_REDIRECTING:</span><br><span>                is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);</span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 8dd008d..8b2099e 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -2619,7 +2619,8 @@</span><br><span>         }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int ast_raw_answer(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_raw_answer_with_stream_topology(struct ast_channel *chan, struct ast_stream_topology *topology)</span><br><span> {</span><br><span>       int res = 0;</span><br><span>         SCOPE_TRACE(1, "%s\n", ast_channel_name(chan));</span><br><span>@@ -2650,7 +2651,10 @@</span><br><span>   case AST_STATE_RINGING:</span><br><span>      case AST_STATE_RING:</span><br><span>                 ast_channel_lock(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-         if (ast_channel_tech(chan)->answer) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ast_channel_tech(chan)->answer_with_stream_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 res = ast_channel_tech(chan)->answer_with_stream_topology(chan, topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (ast_channel_tech(chan)->answer) {</span><br><span>                      res = ast_channel_tech(chan)->answer(chan);</span><br><span>               }</span><br><span>            ast_setstate(chan, AST_STATE_UP);</span><br><span>@@ -2667,6 +2671,11 @@</span><br><span>   return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_raw_answer(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_raw_answer_with_stream_topology(chan, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int __ast_answer(struct ast_channel *chan, unsigned int delay)</span><br><span> {</span><br><span>  int res = 0;</span><br><span>diff --git a/main/features.c b/main/features.c</span><br><span>index 51cc3ed..20ed8f4 100644</span><br><span>--- a/main/features.c</span><br><span>+++ b/main/features.c</span><br><span>@@ -76,6 +76,7 @@</span><br><span> #include "asterisk/stasis_channels.h"</span><br><span> #include "asterisk/features_config.h"</span><br><span> #include "asterisk/max_forwards.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stream.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span>    <application name="Bridge" language="en_US"></span><br><span>@@ -558,12 +559,17 @@</span><br><span>       set_config_flags(chan, config);</span><br><span> </span><br><span>  /* Answer if need be */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   if (ast_channel_state(chan) != AST_STATE_UP) {</span><br><span style="color: hsl(0, 100%, 40%);">-          if (ast_raw_answer(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           res = ast_raw_answer_with_stream_topology(chan, config->peer_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (res != 0) {</span><br><span>                      return -1;</span><br><span>           }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #ifdef FOR_DEBUG</span><br><span>  /* show the two channels and cdrs involved in the bridge for debug & devel purposes */</span><br><span>   ast_channel_log("Pre-bridge CHAN Channel info", chan);</span><br><span>diff --git a/main/stream.c b/main/stream.c</span><br><span>index a21177d..01b07ca 100644</span><br><span>--- a/main/stream.c</span><br><span>+++ b/main/stream.c</span><br><span>@@ -602,6 +602,16 @@</span><br><span>                     ast_format_cap_append(joint_caps, single, 0);</span><br><span>                        ao2_ref(single, -1);</span><br><span>                 }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (error_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_str_append(error_message, 0, "No common formats available for media type '%s' ",</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_codec_media_type2str(pending_stream->type));</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_format_cap_append_names(preferred_caps, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_str_append(error_message, 0, "<>");</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_format_cap_append_names(nonpreferred_caps, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_str_append(error_message, 0, " with prefs: ");</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_stream_codec_prefs_to_str(prefs, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span>    }</span><br><span> </span><br><span>        joint_stream = ast_stream_clone(pending_stream, NULL);</span><br><span>@@ -613,7 +623,7 @@</span><br><span>         /* ref to joint_caps will be transferred to the stream */</span><br><span>    ast_stream_set_formats(joint_stream, joint_caps);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (TRACE_ATLEAST(1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (TRACE_ATLEAST(3)) {</span><br><span>              struct ast_str *buf = ast_str_create((AST_FORMAT_CAP_NAMES_LEN * 3) + AST_STREAM_MAX_CODEC_PREFS_LENGTH);</span><br><span>            if (buf) {</span><br><span>                   ast_str_set(&buf, 0, "Resolved '%s' stream ", ast_codec_media_type2str(pending_stream->type));</span><br><span>@@ -1040,7 +1050,10 @@</span><br><span>                     ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);</span><br><span>                } else {</span><br><span>                     joint_stream = ast_stream_create_resolved(pending_stream, configured_stream, prefs, error_message);</span><br><span style="color: hsl(0, 100%, 40%);">-                     if (ast_stream_get_format_count(joint_stream) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!joint_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ao2_cleanup(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+                          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                  } else if (ast_stream_get_format_count(joint_stream) == 0) {</span><br><span>                                 ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);</span><br><span>                        }</span><br><span>            }</span><br><span>diff --git a/res/Makefile b/res/Makefile</span><br><span>index fc48611..564c7a1 100644</span><br><span>--- a/res/Makefile</span><br><span>+++ b/res/Makefile</span><br><span>@@ -66,7 +66,6 @@</span><br><span> $(call MOD_ADD_C,res_snmp,snmp/agent.c)</span><br><span> $(call MOD_ADD_C,res_parking,$(wildcard parking/*.c))</span><br><span> $(call MOD_ADD_C,res_pjsip,$(wildcard res_pjsip/*.c))</span><br><span style="color: hsl(0, 100%, 40%);">-$(call MOD_ADD_C,res_pjsip_session,$(wildcard res_pjsip_session/*.c))</span><br><span> $(call MOD_ADD_C,res_prometheus,$(wildcard prometheus/*.c))</span><br><span> $(call MOD_ADD_C,res_ari,ari/cli.c ari/config.c ari/ari_websockets.c)</span><br><span> $(call MOD_ADD_C,res_ari_model,ari/ari_model_validators.c)</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index bb77e54..061508a 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -102,7 +102,7 @@</span><br><span>                            <configOption name="allow"></span><br><span>                                  <synopsis>Media Codec(s) to allow</synopsis></span><br><span>                             </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <configOption name="incoming_offer_codec_prefs"></span><br><span style="color: hsl(120, 100%, 40%);">+                              <configOption name="codec_prefs_incoming_offer"></span><br><span>                                     <synopsis>Codec negotiation prefs for incoming offers.</synopsis></span><br><span>                                        <description></span><br><span>                                          <para></span><br><span>@@ -154,7 +154,7 @@</span><br><span>                                           <para></span><br><span>                                                 </para></span><br><span>                                                <example></span><br><span style="color: hsl(0, 100%, 40%);">-                                                 incoming_offer_codec_prefs = prefer: pending, operation: intersect, keep: all, transcode: allow</span><br><span style="color: hsl(120, 100%, 40%);">+                                                       codec_prefs_incoming_offer = prefer: pending, operation: intersect, keep: all, transcode: allow</span><br><span>                                              </example></span><br><span>                                             <para></span><br><span>                                                         Prefer the codecs coming from the caller.  Use only the ones that are common.</span><br><span>@@ -162,7 +162,7 @@</span><br><span>                                          </para></span><br><span>                                        </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <configOption name="outgoing_offer_codec_prefs"></span><br><span style="color: hsl(120, 100%, 40%);">+                              <configOption name="codec_prefs_outgoing_offer"></span><br><span>                                     <synopsis>Codec negotiation prefs for outgoing offers.</synopsis></span><br><span>                                        <description></span><br><span>                                          <para></span><br><span>@@ -215,7 +215,7 @@</span><br><span>                                           <para></span><br><span>                                                 </para></span><br><span>                                                <example></span><br><span style="color: hsl(0, 100%, 40%);">-                                         outgoing_offer_codec_prefs = prefer: configured, operation: union, keep: first, transcode: prevent</span><br><span style="color: hsl(120, 100%, 40%);">+                                            codec_prefs_outgoing_offer = prefer: configured, operation: union, keep: first, transcode: prevent</span><br><span>                                           </example></span><br><span>                                             <para></span><br><span>                                                 Prefer the codecs coming from the endpoint.  Merge them with the codecs from the core</span><br><span>@@ -223,7 +223,7 @@</span><br><span>                                          </para></span><br><span>                                        </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <configOption name="incoming_answer_codec_prefs"></span><br><span style="color: hsl(120, 100%, 40%);">+                             <configOption name="codec_prefs_incoming_answer"></span><br><span>                                    <synopsis>Codec negotiation prefs for incoming answers.</synopsis></span><br><span>                                       <description></span><br><span>                                          <para></span><br><span>@@ -272,14 +272,14 @@</span><br><span>                                                 <para></span><br><span>                                                 </para></span><br><span>                                                <example></span><br><span style="color: hsl(0, 100%, 40%);">-                                         incoming_answer_codec_prefs = keep: first</span><br><span style="color: hsl(120, 100%, 40%);">+                                             codec_prefs_incoming_answer = keep: first</span><br><span>                                            </example></span><br><span>                                             <para></span><br><span>                                                 Use the defaults but keep oinly the first codec.</span><br><span>                                             </para></span><br><span>                                        </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <configOption name="outgoing_answer_codec_prefs"></span><br><span style="color: hsl(120, 100%, 40%);">+                             <configOption name="codec_prefs_outgoing_answer"></span><br><span>                                    <synopsis>Codec negotiation prefs for outgoing answers.</synopsis></span><br><span>                                       <description></span><br><span>                                          <para></span><br><span>@@ -328,7 +328,7 @@</span><br><span>                                           <para></span><br><span>                                                 </para></span><br><span>                                                <example></span><br><span style="color: hsl(0, 100%, 40%);">-                                         incoming_answer_codec_prefs = keep: first</span><br><span style="color: hsl(120, 100%, 40%);">+                                             codec_prefs_incoming_answer = keep: first</span><br><span>                                            </example></span><br><span>                                             <para></span><br><span>                                                 Use the defaults but keep oinly the first codec.</span><br><span>@@ -1180,70 +1180,6 @@</span><br><span>                                    <literal>incoming_call_offer_pref</literal>.  Setting both options is unsupported.</para></span><br><span>                                  </warning></span><br><span>                                     </description></span><br><span style="color: hsl(0, 100%, 40%);">-                                    <see-also></span><br><span style="color: hsl(0, 100%, 40%);">-                                                <ref type="configOption">incoming_call_offer_pref</ref></span><br><span style="color: hsl(0, 100%, 40%);">-                                   </see-also></span><br><span style="color: hsl(0, 100%, 40%);">-                               </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <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(0, 100%, 40%);">-                                 <description></span><br><span style="color: hsl(0, 100%, 40%);">-                                             <para>Based on this setting, a joint list of preferred codecs between those</span><br><span style="color: hsl(0, 100%, 40%);">-                                               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(0, 100%, 40%);">-                                             core. </para></span><br><span style="color: hsl(0, 100%, 40%);">-                                             <note><para>This list will consist of only those codecs found in both lists.</para></note></span><br><span style="color: hsl(0, 100%, 40%);">-                                              <enumlist></span><br><span style="color: hsl(0, 100%, 40%);">-                                                        <enum name="local"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                         Include all codecs in the local list that are also in the remote list</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           preserving the local order.  (default).</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="local_first"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                           Include only the first codec in the local list that is also in the remote list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="remote"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                                Include all codecs in the remote list that are also in the local list</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           preserving the remote order.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="remote_first"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                          Include only the first codec in the remote list that is also in the local list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                              </enumlist></span><br><span style="color: hsl(0, 100%, 40%);">-                                       </description></span><br><span style="color: hsl(0, 100%, 40%);">-                            </configOption></span><br><span style="color: hsl(0, 100%, 40%);">-                           <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(0, 100%, 40%);">-                                 <description></span><br><span style="color: hsl(0, 100%, 40%);">-                                             <para>Based on this setting, a joint list of preferred codecs between</span><br><span style="color: hsl(0, 100%, 40%);">-                                             those received from the Asterisk core (remote), and those specified in</span><br><span style="color: hsl(0, 100%, 40%);">-                                          the endpoint's "allow" parameter (local) is created and is used to create</span><br><span style="color: hsl(0, 100%, 40%);">-                                         the outgoing SDP offer.</para></span><br><span style="color: hsl(0, 100%, 40%);">-                                            <enumlist></span><br><span style="color: hsl(0, 100%, 40%);">-                                                        <enum name="local"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                         Include all codecs in the local list that are also in the remote list</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           preserving the local order.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="local_merge"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                           Include all codecs in BOTH lists preserving the local order.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                            Remote codecs not in the local list will be placed at the end</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           of the joint list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="local_first"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                           Include only the first codec in the local list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="remote"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                                Include all codecs in the remote list that are also in the local list</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           preserving the remote order. (default)</span><br><span style="color: hsl(0, 100%, 40%);">-                                                  </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="remote_merge"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                          Include all codecs in BOTH lists preserving the remote order.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           Local codecs not in the remote list will be placed at the end</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           of the joint list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                                      <enum name="remote_first"><para></span><br><span style="color: hsl(0, 100%, 40%);">-                                                          Include only the first codec in the remote list.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        </para></enum></span><br><span style="color: hsl(0, 100%, 40%);">-                                              </enumlist></span><br><span style="color: hsl(0, 100%, 40%);">-                                       </description></span><br><span>                                 </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>@@ -5378,52 +5314,6 @@</span><br><span>     return result;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       const char *value;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-           value = "local";</span><br><span style="color: hsl(0, 100%, 40%);">-      } else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                value = "local_merge";</span><br><span style="color: hsl(0, 100%, 40%);">-        } else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          value = "local_first";</span><br><span style="color: hsl(0, 100%, 40%);">-        } else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-           value = "remote";</span><br><span style="color: hsl(0, 100%, 40%);">-     } else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-               value = "remote_merge";</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, FIRST)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             value = "remote_first";</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                value = "unknown";</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return value;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-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(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        pref->flags = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     if (strcmp(pref_str, "local") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-         ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(0, 100%, 40%);">-    } else if (is_outgoing && strcmp(pref_str, "local_merge") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(0, 100%, 40%);">-        } else if (strcmp(pref_str, "local_first") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (strcmp(pref_str, "remote") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-         ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(0, 100%, 40%);">-   } else if (is_outgoing && strcmp(pref_str, "remote_merge") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if (strcmp(pref_str, "remote_first") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_FIRST);</span><br><span style="color: hsl(0, 100%, 40%);">-     } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*!</span><br><span>  * \brief Set name and number information on an identity header.</span><br><span>  *</span><br><span>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index 89ae3e1..70dc77d 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -1121,51 +1121,6 @@</span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int call_offer_pref_handler(const struct aco_option *opt,</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_variable *var, void *obj)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_flags pref = { 0, };</span><br><span style="color: hsl(0, 100%, 40%);">- int outgoing = strcmp(var->name, "outgoing_call_offer_pref") == 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- int res = ast_sip_call_codec_str_to_pref(&pref, var->value, outgoing);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (res != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-         return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (outgoing) {</span><br><span style="color: hsl(0, 100%, 40%);">-         endpoint->media.outgoing_call_offer_pref = pref;</span><br><span style="color: hsl(0, 100%, 40%);">-     } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                endpoint->media.incoming_call_offer_pref = pref;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int incoming_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        const struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  *buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.incoming_call_offer_pref));</span><br><span style="color: hsl(0, 100%, 40%);">- if (!(*buf)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int outgoing_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        const struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  *buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.outgoing_call_offer_pref));</span><br><span style="color: hsl(0, 100%, 40%);">- if (!(*buf)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static int codec_prefs_handler(const struct aco_option *opt,</span><br><span>   struct ast_variable *var, void *obj)</span><br><span> {</span><br><span>@@ -1185,7 +1140,7 @@</span><br><span>    }</span><br><span>    ast_free(error_message);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if (strcmp(var->name, "incoming_offer_codec_prefs") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (strcmp(var->name, "codec_prefs_incoming_offer") == 0) {</span><br><span>             if (prefs.operation == CODEC_NEGOTIATION_OPERATION_UNION) {</span><br><span>                  ast_log(LOG_ERROR, "Endpoint '%s': Codec preference '%s' has invalid value '%s' for option: '%s'",</span><br><span>                                 ast_sorcery_object_get_id(endpoint),</span><br><span>@@ -1194,19 +1149,15 @@</span><br><span>                               var->name);</span><br><span>                       return -1;</span><br><span>           }</span><br><span style="color: hsl(0, 100%, 40%);">-               endpoint->media.incoming_offer_codec_prefs = prefs;</span><br><span>               default_prefer = CODEC_NEGOTIATION_PREFER_PENDING;</span><br><span>           default_operation = CODEC_NEGOTIATION_OPERATION_INTERSECT;</span><br><span style="color: hsl(0, 100%, 40%);">-      } else if (strcmp(var->name, "outgoing_offer_codec_prefs") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-         endpoint->media.outgoing_offer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (strcmp(var->name, "codec_prefs_outgoing_offer") == 0) {</span><br><span>              default_prefer = CODEC_NEGOTIATION_PREFER_PENDING;</span><br><span>           default_operation = CODEC_NEGOTIATION_OPERATION_UNION;</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (strcmp(var->name, "incoming_answer_codec_prefs") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                endpoint->media.incoming_answer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (strcmp(var->name, "codec_prefs_incoming_answer") == 0) {</span><br><span>             default_prefer = CODEC_NEGOTIATION_PREFER_PENDING;</span><br><span>           default_operation = CODEC_NEGOTIATION_OPERATION_INTERSECT;</span><br><span style="color: hsl(0, 100%, 40%);">-      } else if (strcmp(var->name, "outgoing_answer_codec_prefs") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                endpoint->media.outgoing_answer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (strcmp(var->name, "codec_prefs_outgoing_answer") == 0) {</span><br><span>             default_prefer = CODEC_NEGOTIATION_PREFER_PENDING;</span><br><span>           default_operation = CODEC_NEGOTIATION_OPERATION_INTERSECT;</span><br><span>   }</span><br><span>@@ -1227,6 +1178,16 @@</span><br><span>           prefs.transcode = CODEC_NEGOTIATION_TRANSCODE_ALLOW;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(var->name, "codec_prefs_incoming_offer") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              endpoint->media.incoming_offer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (strcmp(var->name, "codec_prefs_outgoing_offer") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               endpoint->media.outgoing_offer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (strcmp(var->name, "codec_prefs_incoming_answer") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              endpoint->media.incoming_answer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (strcmp(var->name, "codec_prefs_outgoing_answer") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              endpoint->media.outgoing_answer_codec_prefs = prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -1451,14 +1412,15 @@</span><br><span>                 return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (endpoint->preferred_codec_only) {</span><br><span style="color: hsl(0, 100%, 40%);">-                if (endpoint->media.incoming_call_offer_pref.flags != (AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   ast_log(LOG_ERROR, "Setting both preferred_codec_only and incoming_call_offer_pref is not supported on endpoint '%s'\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                              ast_sorcery_object_get_id(endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">-                   return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_clear_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_ALL);</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_set_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_FIRST);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (endpoint->preferred_codec_only</span><br><span style="color: hsl(120, 100%, 40%);">+         && endpoint->media.incoming_offer_codec_prefs.keep != CODEC_NEGOTIATION_KEEP_FIRST) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "The preferred_codec_only parameter is deprecated. "</span><br><span style="color: hsl(120, 100%, 40%);">+                   "Overriding value of incoming_offer_codec_prefs.keep from '%s' to '%s' for endpoint '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_stream_codec_keep_to_str(endpoint->media.incoming_offer_codec_prefs.keep),</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_stream_codec_keep_to_str(CODEC_NEGOTIATION_KEEP_FIRST),</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_sorcery_object_get_id(endpoint));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               endpoint->media.incoming_offer_codec_prefs.keep = CODEC_NEGOTIATION_KEEP_FIRST;</span><br><span>   }</span><br><span> </span><br><span>        endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);</span><br><span>@@ -2124,21 +2086,17 @@</span><br><span>       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accept_multiple_sdp_answers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.accept_multiple_sdp_answers));</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "suppress_q850_reason_headers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, suppress_q850_reason_headers));</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ignore_183_without_sdp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ignore_183_without_sdp));</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_call_offer_pref", "local",</span><br><span style="color: hsl(0, 100%, 40%);">-            call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_call_offer_pref", "remote",</span><br><span style="color: hsl(0, 100%, 40%);">-           call_offer_pref_handler, outgoing_call_offer_pref_to_str, NULL, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_offer_codec_prefs",</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "codec_prefs_incoming_offer",</span><br><span>          "prefer: pending, operation: intersect, keep: all, transcode: allow",</span><br><span>              codec_prefs_handler, incoming_offer_codec_prefs_to_str, NULL, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-    ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_offer_codec_prefs",</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "codec_prefs_outgoing_offer",</span><br><span>          "prefer: pending, operation: union, keep: all, transcode: allow",</span><br><span>          codec_prefs_handler, outgoing_offer_codec_prefs_to_str, NULL, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-    ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_answer_codec_prefs",</span><br><span style="color: hsl(0, 100%, 40%);">-            "prefer: pending, operation: intersect, keep: all",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "codec_prefs_incoming_answer",</span><br><span style="color: hsl(120, 100%, 40%);">+          "prefer: pending, operation: intersect, keep: all, transcode: allow",</span><br><span>              codec_prefs_handler, incoming_answer_codec_prefs_to_str, NULL, 0, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-   ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_answer_codec_prefs",</span><br><span style="color: hsl(0, 100%, 40%);">-            "prefer: pending, operation: intersect, keep: all",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "codec_prefs_outgoing_answer",</span><br><span style="color: hsl(120, 100%, 40%);">+          "prefer: pending, operation: intersect, keep: all, transcode: allow",</span><br><span>              codec_prefs_handler, outgoing_answer_codec_prefs_to_str, NULL, 0, 0);</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, stir_shaken));</span><br><span> </span><br><span>diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c</span><br><span>index 06ea0b6..6a496dc 100644</span><br><span>--- a/res/res_pjsip_refer.c</span><br><span>+++ b/res/res_pjsip_refer.c</span><br><span>@@ -983,7 +983,7 @@</span><br><span>       ast_channel_lock(session->channel);</span><br><span>       ast_setstate(session->channel, AST_STATE_RING);</span><br><span>   ast_channel_unlock(session->channel);</span><br><span style="color: hsl(0, 100%, 40%);">-        ast_raw_answer(session->channel);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_raw_answer_with_stream_topology(session->channel, ast_channel_get_stream_topology(other_session->channel));</span><br><span> </span><br><span>    ast_debug(3, "INVITE with Replaces being attempted.  '%s' --> '%s'\n",</span><br><span>          ast_channel_name(session->channel), ast_channel_name(invite.channel));</span><br><span>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c</span><br><span>index eacae22..d323e44 100644</span><br><span>--- a/res/res_pjsip_sdp_rtp.c</span><br><span>+++ b/res/res_pjsip_sdp_rtp.c</span><br><span>@@ -56,7 +56,6 @@</span><br><span> </span><br><span> #include "asterisk/res_pjsip.h"</span><br><span> #include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session_caps.h"</span><br><span> </span><br><span> /*! \brief Scheduler for RTCP purposes */</span><br><span> static struct ast_sched_context *sched;</span><br><span>@@ -311,71 +310,144 @@</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs,</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_format_cap *get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs,</span><br><span>   struct ast_sip_session_media *session_media)</span><br><span> {</span><br><span>    pjmedia_sdp_attr *attr;</span><br><span>      pjmedia_sdp_rtpmap *rtpmap;</span><br><span>  pjmedia_sdp_fmtp fmtp;</span><br><span>       struct ast_format *format;</span><br><span style="color: hsl(0, 100%, 40%);">-      int i, num = 0, tel_event = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int tel_event = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    int res;</span><br><span style="color: hsl(120, 100%, 40%);">+      int framing;</span><br><span>         char name[256];</span><br><span>      char media[20];</span><br><span>      char fmt_param[256];</span><br><span>         enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?</span><br><span>            AST_RTP_OPT_G726_NONSTANDARD : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_format_cap *caps;</span><br><span>         SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        ast_rtp_codecs_payloads_initialize(codecs);</span><br><span style="color: hsl(120, 100%, 40%);">+   res = ast_rtp_codecs_payloads_initialize(codecs);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (res != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "%s: Failed to initialize codecs\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+         SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't initialize codecs\n");</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%);">+   caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!caps) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "%s: Failed to allocate %s capabilities\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_rtp_codecs_payloads_destroy(codecs);</span><br><span style="color: hsl(120, 100%, 40%);">+              SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't allocate caps\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span>        /* Iterate through provided formats */</span><br><span>       for (i = 0; i < stream->desc.fmt_count; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+          const pj_str_t *payload_str = &stream->desc.fmt[i];</span><br><span style="color: hsl(120, 100%, 40%);">+            int payload = pj_strtoul(payload_str);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>             /* The payload is kept as a string for things like t38 but for video it is always numerical */</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_rtp_codecs_payloads_set_m_type(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]));</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_rtp_codecs_payloads_set_m_type(codecs, NULL, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                 /* Look for the optional rtpmap attribute */</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!(attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", &stream->desc.fmt[i]))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Interpret the attribute as an rtpmap */</span><br><span style="color: hsl(0, 100%, 40%);">-              if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_prov, attr, &rtpmap)) != PJ_SUCCESS) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name));</span><br><span style="color: hsl(0, 100%, 40%);">-          if (strcmp(name, "telephone-event") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   tel_event++;</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media));</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL,</span><br><span style="color: hsl(0, 100%, 40%);">-                      pj_strtoul(&stream->desc.fmt[i]), media, name, options, rtpmap->clock_rate);</span><br><span style="color: hsl(0, 100%, 40%);">-          /* Look for an optional associated fmtp attribute */</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS) {</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_copy_pj_str(fmt_param, &fmtp.fmt, sizeof(fmt_param));</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (sscanf(fmt_param, "%30d", &num) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+             name[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+               attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", payload_str);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (attr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* Interpret the attribute as an rtpmap */</span><br><span style="color: hsl(120, 100%, 40%);">+                    res = pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_prov, attr, &rtpmap);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (res != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* We only get format here for the warning message */</span><br><span style="color: hsl(120, 100%, 40%);">+                         format = ast_rtp_codecs_get_payload_format(codecs, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_log(LOG_WARNING, "%s: Format %s (%d) skipped due to unparseable rtpmap attribute\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_sip_session_get_name(session), format ? ast_format_get_name(format) : "?", payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                            ast_rtp_codecs_payloads_unset(codecs, NULL, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                         ao2_cleanup(format);</span><br><span>                                 continue;</span><br><span>                    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-                   if ((format = ast_rtp_codecs_get_payload_format(codecs, num))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                struct ast_format *format_parsed;</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name));</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (strcmp(name, "telephone-event") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         tel_event++;</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-                           ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param));</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media));</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, payload,</span><br><span style="color: hsl(120, 100%, 40%);">+                           media, name, options, rtpmap->clock_rate);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-                               format_parsed = ast_format_parse_sdp_fmtp(format, fmt_param);</span><br><span style="color: hsl(0, 100%, 40%);">-                           if (format_parsed) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                    ast_rtp_codecs_payload_replace_format(codecs, num, format_parsed);</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ao2_ref(format_parsed, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-                             }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * We have to wait until an attempt is made to find an rtpmap attribute</span><br><span style="color: hsl(120, 100%, 40%);">+                * to get the format.  For dynamic payloads, it's the call to</span><br><span style="color: hsl(120, 100%, 40%);">+              * set_rtpmap_type_rate that creates its format.</span><br><span style="color: hsl(120, 100%, 40%);">+               */</span><br><span style="color: hsl(120, 100%, 40%);">+           format = ast_rtp_codecs_get_payload_format(codecs, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_debug(1, "%s: Media type: %s  Index: %d  Format code: %d  Format name: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_sip_session_get_name(session), media, i, payload, format ? ast_format_get_name(format) : "?");</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%);">+             * Dynamic payloads MUST have an rtpmap so at this point,</span><br><span style="color: hsl(120, 100%, 40%);">+              * all static and dynamic payloads should have a format except</span><br><span style="color: hsl(120, 100%, 40%);">+                 * those like telephone event for which we don't add a format to format caps.</span><br><span style="color: hsl(120, 100%, 40%);">+              */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!format) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        continue;</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 style="color: hsl(120, 100%, 40%);">+             * Look for an optional fmtp attribute that mnatches the payload code</span><br><span style="color: hsl(120, 100%, 40%);">+          * on the "m" attribute.</span><br><span style="color: hsl(120, 100%, 40%);">+             * FYI... Technically, for a static payload, you CAN have an fmtp attribute</span><br><span style="color: hsl(120, 100%, 40%);">+            * without a corresponding rtpmap attribute.</span><br><span style="color: hsl(120, 100%, 40%);">+           */</span><br><span style="color: hsl(120, 100%, 40%);">+           attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", payload_str);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (attr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   struct ast_format *format_parsed;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   res = pjmedia_sdp_attr_get_fmtp(attr, &fmtp);</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* If we can't parse the fmtp attribute, we're just going to skip the format */</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (res != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_WARNING, "%s: Format %s (%d) skipped due to unparseable fmtp attribute\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                      ast_sip_session_get_name(session), ast_format_get_name(format), payload);</span><br><span>                            ao2_ref(format, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_rtp_codecs_payloads_unset(codecs, NULL, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                         continue;</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%);">+                   ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param));</span><br><span style="color: hsl(120, 100%, 40%);">+                   format_parsed = ast_format_parse_sdp_fmtp(format, fmt_param);</span><br><span style="color: hsl(120, 100%, 40%);">+                 /*</span><br><span style="color: hsl(120, 100%, 40%);">+                     * format_parsed can be one of 3 things...</span><br><span style="color: hsl(120, 100%, 40%);">+                     * * ao2_bump(format) if the format doesn't have an attribute parser.</span><br><span style="color: hsl(120, 100%, 40%);">+                      *   In this case we have no need to replace the format in the codecs list.</span><br><span style="color: hsl(120, 100%, 40%);">+                    * * NULL if it has a parser but it wasn't able to clone format.</span><br><span style="color: hsl(120, 100%, 40%);">+                   *   Again, no need to replace the format in the codecs list.</span><br><span style="color: hsl(120, 100%, 40%);">+                  * * A cloned format with the attributes filled out.</span><br><span style="color: hsl(120, 100%, 40%);">+                   *   Replace the format.</span><br><span style="color: hsl(120, 100%, 40%);">+                       *</span><br><span style="color: hsl(120, 100%, 40%);">+                     * We need to keep careful track of reference counting whatever happens.</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 (format_parsed && format_parsed != format) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_rtp_codecs_payload_replace_format(codecs, payload, format_parsed);</span><br><span style="color: hsl(120, 100%, 40%);">+                                ao2_ref(format, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                          format = format_parsed;</span><br><span>                      }</span><br><span>            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           res = ast_format_cap_append(caps, format, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (res != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_log(LOG_WARNING, "%s: Format %s (%d) skipped due to inability to append it to the format caps\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_sip_session_get_name(session), ast_format_get_name(format), payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_rtp_codecs_payloads_unset(codecs, NULL, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_ref(format, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!tel_event && (session->dtmf == AST_SIP_DTMF_AUTO)) {</span><br><span>                 ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);</span><br><span>     }</span><br><span>@@ -388,16 +460,16 @@</span><br><span>            }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        /* Get the packetization, if it exists */</span><br><span>    if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) {</span><br><span style="color: hsl(0, 100%, 40%);">-           unsigned long framing = pj_strtoul(pj_strltrim(&attr->value));</span><br><span style="color: hsl(120, 100%, 40%);">+         framing = pj_strtoul(pj_strltrim(&attr->value));</span><br><span>              if (framing && session->endpoint->media.rtp.use_ptime) {</span><br><span>                       ast_rtp_codecs_set_framing(codecs, framing);</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_format_cap_set_framing(caps, framing);</span><br><span>           }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   SCOPE_EXIT_RTN();</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_EXIT_RTN_VALUE(caps);</span><br><span> }</span><br><span> </span><br><span> static int apply_cap_to_bundled(struct ast_sip_session_media *session_media,</span><br><span>@@ -435,34 +507,12 @@</span><br><span>         struct ast_sip_session *session, struct ast_sip_session_media *session_media,</span><br><span>        const struct pjmedia_sdp_media *stream)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_format_cap *incoming_call_offer_cap;</span><br><span>      struct ast_format_cap *remote;</span><br><span>       struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT;</span><br><span style="color: hsl(0, 100%, 40%);">-        int fmts = 0;</span><br><span>        SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        remote = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!remote) {</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_log(LOG_ERROR, "Failed to allocate %s incoming remote capabilities\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                            ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(0, 100%, 40%);">-              SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't allocate caps\n");</span><br><span style="color: hsl(0, 100%, 40%);">-   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* Get the peer's capabilities*/</span><br><span style="color: hsl(0, 100%, 40%);">-    get_codecs(session, stream, &codecs, session_media);</span><br><span style="color: hsl(0, 100%, 40%);">-        ast_rtp_codecs_payload_formats(&codecs, remote, &fmts);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- incoming_call_offer_cap = ast_sip_session_create_joint_call_cap(</span><br><span style="color: hsl(0, 100%, 40%);">-                session, session_media->type, remote);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ao2_ref(remote, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!incoming_call_offer_cap || ast_format_cap_empty(incoming_call_offer_cap)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                ao2_cleanup(incoming_call_offer_cap);</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_rtp_codecs_payloads_destroy(&codecs);</span><br><span style="color: hsl(0, 100%, 40%);">-           SCOPE_EXIT_RTN_VALUE(NULL, "No incoming call offer caps\n");</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(120, 100%, 40%);">+     remote = get_codecs(session, stream, &codecs, session_media);</span><br><span> </span><br><span>        /*</span><br><span>    * Setup rx payload type mapping to prefer the mapping</span><br><span>@@ -475,7 +525,7 @@</span><br><span> </span><br><span>     ast_rtp_codecs_payloads_destroy(&codecs);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       SCOPE_EXIT_RTN_VALUE(incoming_call_offer_cap);</span><br><span style="color: hsl(120, 100%, 40%);">+        SCOPE_EXIT_RTN_VALUE(remote);</span><br><span> }</span><br><span> </span><br><span> static int set_caps(struct ast_sip_session *session,</span><br><span>@@ -484,52 +534,20 @@</span><br><span>       const struct pjmedia_sdp_media *stream,</span><br><span>      int is_offer, struct ast_stream *asterisk_stream)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);</span><br><span>  RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-     RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-    enum ast_media_type media_type = session_media->type;</span><br><span>     struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT;</span><br><span style="color: hsl(0, 100%, 40%);">-        int fmts = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-   int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_format_cap_count(session->direct_media_cap);</span><br><span>  int dsp_features = 0;</span><br><span>        SCOPE_ENTER(1, "%s %s\n", ast_sip_session_get_name(session), is_offer ? "OFFER" : "ANSWER");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  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(0, 100%, 40%);">-             ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(0, 100%, 40%);">-              SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create %s capabilities\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* get the endpoint capabilities */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (direct_media_enabled) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* get the capabilities on the peer */</span><br><span style="color: hsl(0, 100%, 40%);">-  get_codecs(session, stream, &codecs,  session_media);</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_rtp_codecs_payload_formats(&codecs, peer, &fmts);</span><br><span style="color: hsl(120, 100%, 40%);">+ peer = get_codecs(session, stream, &codecs,  session_media);</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(0, 100%, 40%);">-       if (!ast_format_cap_count(joint)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             struct ast_str *usbuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span style="color: hsl(0, 100%, 40%);">-               struct ast_str *thembuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_format_cap_count(peer)) {</span><br><span>           ast_rtp_codecs_payloads_destroy(&codecs);</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_codec_media_type2str(session_media->type),</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_format_cap_get_names(caps, &usbuf),</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_format_cap_get_names(peer, &thembuf));</span><br><span style="color: hsl(0, 100%, 40%);">-          SCOPE_EXIT_RTN_VALUE(-1, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                  ast_codec_media_type2str(session_media->type),</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_format_cap_get_names(caps, &usbuf),</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_format_cap_get_names(peer, &thembuf));</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_NOTICE, "No joint capabilities for media stream between our configuration(%s) and incoming SDP(%s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_stream_topology_to_str(session->pending_media_state->topology, NULL),</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_format_cap_get_names(peer, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+                SCOPE_EXIT_RTN_VALUE(-1, "Peer responded with no formats\n");</span><br><span>      }</span><br><span> </span><br><span>        if (is_offer) {</span><br><span>@@ -539,49 +557,14 @@</span><br><span>               */</span><br><span>          ast_rtp_codecs_payloads_xover(&codecs, &codecs, NULL);</span><br><span>       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp),</span><br><span>                session_media->rtp);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream, joint);</span><br><span style="color: hsl(120, 100%, 40%);">+ apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream, peer);</span><br><span> </span><br><span>     if (session->channel && ast_sip_session_is_pending_stream_default(session, asterisk_stream)) {</span><br><span>            ast_channel_lock(session->channel);</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel),</span><br><span style="color: hsl(0, 100%, 40%);">-                    AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_format_cap_remove_by_type(caps, media_type);</span><br><span style="color: hsl(0, 100%, 40%);">-</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(0, 100%, 40%);">-                    struct ast_format *best;</span><br><span style="color: hsl(0, 100%, 40%);">-                        /*</span><br><span style="color: hsl(0, 100%, 40%);">-                       * If we don't allow the sending codec to be changed on our side</span><br><span style="color: hsl(0, 100%, 40%);">-                     * then get the best codec from the joint capabilities of the media</span><br><span style="color: hsl(0, 100%, 40%);">-                      * type and use only that. This ensures the core won't start sending</span><br><span style="color: hsl(0, 100%, 40%);">-                         * out a format that we aren't currently sending.</span><br><span style="color: hsl(0, 100%, 40%);">-                    */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                     best = ast_format_cap_get_best_by_type(joint, media_type);</span><br><span style="color: hsl(0, 100%, 40%);">-                      if (best) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_format_cap_append(caps, best, ast_format_cap_get_framing(joint));</span><br><span style="color: hsl(0, 100%, 40%);">-                           ao2_ref(best, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-                      }</span><br><span style="color: hsl(0, 100%, 40%);">-               } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_format_cap_append_from_cap(caps, joint, media_type);</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               /*</span><br><span style="color: hsl(0, 100%, 40%);">-               * Apply the new formats to the channel, potentially changing</span><br><span style="color: hsl(0, 100%, 40%);">-            * raw read/write formats and translation path while doing so.</span><br><span style="color: hsl(0, 100%, 40%);">-           */</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_channel_nativeformats_set(session->channel, caps);</span><br><span style="color: hsl(0, 100%, 40%);">-               if (media_type == AST_MEDIA_TYPE_AUDIO) {</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_set_read_format(session->channel, ast_channel_readformat(session->channel));</span><br><span style="color: hsl(0, 100%, 40%);">-                  ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span> </span><br><span>                if ( ((session->dtmf == AST_SIP_DTMF_AUTO) || (session->dtmf == AST_SIP_DTMF_AUTO_INFO) )</span><br><span>                  && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)</span><br><span>@@ -604,7 +587,8 @@</span><br><span>    }</span><br><span> </span><br><span>        ast_rtp_codecs_payloads_destroy(&codecs);</span><br><span style="color: hsl(0, 100%, 40%);">-   SCOPE_EXIT_RTN_VALUE(0);</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_EXIT_RTN_VALUE(0, "Formats: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_str_tmp(128, ast_format_cap_get_names(peer, &STR_TMP)));</span><br><span> }</span><br><span> </span><br><span> static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, pjmedia_sdp_media *media, pj_pool_t *pool,</span><br><span>@@ -1428,13 +1412,6 @@</span><br><span>      int res;</span><br><span>     SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        /* If no type formats have been configured reject this stream */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(0, 100%, 40%);">-              SCOPE_EXIT_RTN_VALUE(0, "Endpoint has no codecs\n");</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* Ensure incoming transport is compatible with the endpoint's configuration */</span><br><span>  if (!session->endpoint->media.rtp.use_received_transport) {</span><br><span>            encryption = check_endpoint_media_transport(session->endpoint, stream);</span><br><span>@@ -1651,8 +1628,10 @@</span><br><span> }</span><br><span> </span><br><span> /*! \brief Function which creates an outgoing stream */</span><br><span style="color: hsl(0, 100%, 40%);">-static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,</span><br><span style="color: hsl(0, 100%, 40%);">-                                    struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream)</span><br><span style="color: hsl(120, 100%, 40%);">+static int create_outgoing_sdp_stream(struct ast_sip_session *session,</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_session_media *session_media,</span><br><span style="color: hsl(120, 100%, 40%);">+  struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_stream *stream)</span><br><span> {</span><br><span>      pj_pool_t *pool = session->inv_session->pool_prov;</span><br><span>     static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };</span><br><span>@@ -1673,7 +1652,7 @@</span><br><span>      int noncodec = (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0;</span><br><span>        int min_packet_size = 0, max_packet_size = 0;</span><br><span>        int rtp_code;</span><br><span style="color: hsl(0, 100%, 40%);">-   RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_format_cap *caps = NULL;</span><br><span>  enum ast_media_type media_type = session_media->type;</span><br><span>     struct ast_sip_session_media *session_media_transport;</span><br><span>       pj_sockaddr ip;</span><br><span>@@ -1713,6 +1692,8 @@</span><br><span> </span><br><span>          sdp->media[sdp->media_count++] = media;</span><br><span>                ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_trace(2, "%s stream removed.  Caps: %s\n", ast_codec_media_type2str(ast_stream_get_type(stream)),</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_stream_to_str(stream, NULL));</span><br><span> </span><br><span>                SCOPE_EXIT_RTN_VALUE(1, "Stream removed or no formats\n");</span><br><span>         }</span><br><span>@@ -1818,19 +1799,16 @@</span><br><span>          enable_rtcp(session, session_media, NULL);</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_codec_media_type2str(session_media->type));</span><br><span style="color: hsl(0, 100%, 40%);">-              SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create caps\n");</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    if (direct_media_enabled) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);</span><br><span style="color: hsl(120, 100%, 40%);">+//           ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);</span><br><span>    } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_format_cap_append_from_cap(caps, ast_stream_get_formats(stream), media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+             caps = (struct ast_format_cap *)ast_stream_get_formats(stream);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!caps) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  SCOPE_EXIT_RTN_VALUE(-1, "Couldn't get stream caps\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   for (index = 0; index < ast_format_cap_count(caps); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+     for (index = 0; caps && index < ast_format_cap_count(caps); ++index) {</span><br><span>            struct ast_format *format = ast_format_cap_get_format(caps, index);</span><br><span> </span><br><span>              if (ast_format_get_type(format) != media_type) {</span><br><span>@@ -1905,7 +1883,6 @@</span><br><span>             }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        /* If no formats were actually added to the media stream don't add it to the SDP */</span><br><span>      if (!media->desc.fmt_count) {</span><br><span>             SCOPE_EXIT_RTN_VALUE(1, "No formats added to stream\n");</span><br><span>@@ -2022,8 +1999,11 @@</span><br><span>  char host[NI_MAXHOST];</span><br><span>       int res;</span><br><span>     struct ast_sip_session_media *session_media_transport;</span><br><span style="color: hsl(0, 100%, 40%);">-  SCOPE_ENTER(1, "%s Stream: %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+  SCOPE_ENTER(1, "%s Stream: %s %s %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP))</span><br><span style="color: hsl(120, 100%, 40%);">+       );</span><br><span> </span><br><span>       if (!session->channel) {</span><br><span>          SCOPE_EXIT_RTN_VALUE(1, "No channel\n");</span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 33bfd21..542d10c 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -31,7 +31,6 @@</span><br><span> </span><br><span> #include "asterisk/res_pjsip.h"</span><br><span> #include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session_caps.h"</span><br><span> #include "asterisk/callerid.h"</span><br><span> #include "asterisk/datastore.h"</span><br><span> #include "asterisk/module.h"</span><br><span>@@ -101,8 +100,6 @@</span><br><span>         char stream_type[1];</span><br><span> };</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static int sdp_handler_list_hash(const void *obj, int flags)</span><br><span> {</span><br><span>   const struct sdp_handler_list *handler_list = obj;</span><br><span>@@ -812,7 +809,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>@@ -823,7 +821,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>@@ -831,12 +830,14 @@</span><br><span>                             /* Catastrophic failure. Abort! */</span><br><span>                           SCOPE_EXIT_RTN_VALUE(-1, "Couldn't negotiate incoming sdp stream\n");</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>@@ -848,27 +849,32 @@</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>                     if (res < 0) {</span><br><span>                            /* Catastrophic failure. Abort! */</span><br><span style="color: hsl(0, 100%, 40%);">-                              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                            SCOPE_EXIT_RTN_VALUE(-1, "Couldn't negotiate incoming sdp stream\n");</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>@@ -929,12 +935,17 @@</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_trace(2, "Calling apply_negotiated_sdp_stream\n");</span><br><span>             res = handler->apply_negotiated_sdp_stream(session, session_media, local, remote, index, asterisk_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+</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>                     SCOPE_EXIT_RTN_VALUE(0,  "%s: Applied negotiated SDP media stream '%s' using %s SDP handler\n",</span><br><span>@@ -956,17 +967,22 @@</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 style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_trace(2, "Calling apply_negotiated_sdp_stream\n");</span><br><span>             res = handler->apply_negotiated_sdp_stream(session, session_media, local, remote, index, asterisk_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+         res = 1;</span><br><span>             if (res < 0) {</span><br><span>                    /* Catastrophic failure. Abort! */</span><br><span>                   SCOPE_EXIT_RTN_VALUE(-1, "%s: Handler '%s' returned %d\n",</span><br><span>                                 ast_sip_session_get_name(session), handler->id, res);</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>@@ -989,12 +1005,16 @@</span><br><span>              res ? "not negotiated.  Stopped" : "handled");</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote)</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local,</span><br><span style="color: hsl(120, 100%, 40%);">+        const pjmedia_sdp_session *remote, pjsip_inv_state inv_state)</span><br><span> {</span><br><span>   int i;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct ast_stream_topology *topology;</span><br><span>        unsigned int changed = 0; /* 0 = unchanged, 1 = new source, 2 = new topology */</span><br><span style="color: hsl(0, 100%, 40%);">- SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_stream_topology *cloned_pending;</span><br><span style="color: hsl(120, 100%, 40%);">+   SCOPE_ENTER(1, "%s  Pending topology: %s  Active topology %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%);">+            ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span> </span><br><span>        if (!session->pending_media_state->topology) {</span><br><span>                 if (session->active_media_state->topology) {</span><br><span>@@ -1035,6 +1055,15 @@</span><br><span>          SCOPE_EXIT_RTN_VALUE(-1, "Media stream count mismatch\n");</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * This process updates the pending stream topology so we need to save a copy first.</span><br><span style="color: hsl(120, 100%, 40%);">+   * This shoule be done better.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   cloned_pending = ast_stream_topology_clone(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!cloned_pending) {</span><br><span style="color: hsl(120, 100%, 40%);">+                SCOPE_EXIT_RTN_VALUE(-1, "Couldn't clone pending topology\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  for (i = 0; i < local->media_count; ++i) {</span><br><span>             struct ast_sip_session_media *session_media;</span><br><span>                 struct ast_stream *stream;</span><br><span>@@ -1102,21 +1131,17 @@</span><br><span>                 }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Update the topology on the channel to match the accepted one */</span><br><span style="color: hsl(0, 100%, 40%);">-      topology = ast_stream_topology_clone(session->pending_media_state->topology);</span><br><span style="color: hsl(0, 100%, 40%);">-     if (topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-         ast_channel_set_stream_topology(session->channel, topology);</span><br><span style="color: hsl(0, 100%, 40%);">-         /* If this is a remotely done renegotiation that has changed the stream topology notify what is</span><br><span style="color: hsl(0, 100%, 40%);">-          * currently handling this channel. Note that fax uses its own process, so if we are transitioning</span><br><span style="color: hsl(0, 100%, 40%);">-               * between audio and fax or vice versa we don't notify.</span><br><span style="color: hsl(0, 100%, 40%);">-              */</span><br><span style="color: hsl(0, 100%, 40%);">-             if (pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE &&</span><br><span style="color: hsl(0, 100%, 40%);">-                   session->active_media_state && session->active_media_state->topology &&</span><br><span style="color: hsl(0, 100%, 40%);">-                        !ast_stream_topology_equal(session->active_media_state->topology, topology) &&</span><br><span style="color: hsl(0, 100%, 40%);">-                    !session->active_media_state->default_session[AST_MEDIA_TYPE_IMAGE] &&</span><br><span style="color: hsl(0, 100%, 40%);">-                    !session->pending_media_state->default_session[AST_MEDIA_TYPE_IMAGE]) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   changed = 2;</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If this is a remotely done renegotiation that has changed the stream topology notify what is</span><br><span style="color: hsl(120, 100%, 40%);">+        * currently handling this channel. Note that fax uses its own process, so if we are transitioning</span><br><span style="color: hsl(120, 100%, 40%);">+     * between audio and fax or vice versa we don't notify.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE &&</span><br><span style="color: hsl(120, 100%, 40%);">+         session->active_media_state && session->active_media_state->topology &&</span><br><span style="color: hsl(120, 100%, 40%);">+              !ast_stream_topology_equal(session->active_media_state->topology,</span><br><span style="color: hsl(120, 100%, 40%);">+                       session->pending_media_state->topology) &&</span><br><span style="color: hsl(120, 100%, 40%);">+              !session->active_media_state->default_session[AST_MEDIA_TYPE_IMAGE] &&</span><br><span style="color: hsl(120, 100%, 40%);">+          !session->pending_media_state->default_session[AST_MEDIA_TYPE_IMAGE]) {</span><br><span style="color: hsl(120, 100%, 40%);">+         changed = 2;</span><br><span>         }</span><br><span> </span><br><span>        /* Remove all current file descriptors from the channel */</span><br><span>@@ -1135,7 +1160,7 @@</span><br><span>   /* Active and pending flip flop as needed */</span><br><span>         ast_sip_session_media_stats_save(session, session->active_media_state);</span><br><span>   SWAP(session->active_media_state, session->pending_media_state);</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+   session->pending_media_state->topology = cloned_pending;</span><br><span> </span><br><span>   ast_channel_unlock(session->channel);</span><br><span> </span><br><span>@@ -1149,7 +1174,10 @@</span><br><span>                ast_queue_frame(session->channel, &ast_null_frame);</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   SCOPE_EXIT_RTN_VALUE(0);</span><br><span style="color: hsl(120, 100%, 40%);">+      SCOPE_EXIT_RTN_VALUE(0, "%s  Pending topology: %s  Active topology %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%);">+            ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span> }</span><br><span> </span><br><span> #define DATASTORE_BUCKETS 53</span><br><span>@@ -1539,7 +1567,7 @@</span><br><span>                   pjmedia_sdp_neg_get_active_local(inv_session->neg, &previous_sdp);</span><br><span>            }</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-       SCOPE_EXIT_RTN_VALUE(create_local_sdp(inv_session, session, previous_sdp));</span><br><span style="color: hsl(120, 100%, 40%);">+   SCOPE_EXIT_RTN_VALUE(ast_sip_session_create_local_sdp(inv_session, session, previous_sdp));</span><br><span> }</span><br><span> </span><br><span> static void set_from_header(struct ast_sip_session *session)</span><br><span>@@ -1927,7 +1955,7 @@</span><br><span>                 pjmedia_sdp_neg_set_remote_offer(inv_session->pool, inv_session->neg, previous_offer);</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   new_answer = create_local_sdp(inv_session, session, previous_offer);</span><br><span style="color: hsl(120, 100%, 40%);">+  new_answer = ast_sip_session_create_local_sdp(inv_session, session, previous_offer);</span><br><span>         if (!new_answer) {</span><br><span>           ast_log(LOG_WARNING, "Could not create a new local SDP answer for channel '%s'\n",</span><br><span>                         ast_channel_name(session->channel));</span><br><span>@@ -1947,9 +1975,10 @@</span><br><span> </span><br><span> void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+        SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span>         handle_outgoing_response(session, tdata);</span><br><span>    pjsip_inv_send_msg(session->inv_session, tdata);</span><br><span style="color: hsl(0, 100%, 40%);">-     return;</span><br><span style="color: hsl(120, 100%, 40%);">+       SCOPE_EXIT_RTN();</span><br><span> }</span><br><span> </span><br><span> static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata);</span><br><span>@@ -2265,7 +2294,7 @@</span><br><span>  pjmedia_sdp_session *offer;</span><br><span>  SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (!(offer = create_local_sdp(session->inv_session, session, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!(offer = ast_sip_session_create_local_sdp(session->inv_session, session, NULL))) {</span><br><span>           pjsip_inv_terminate(session->inv_session, 500, PJ_FALSE);</span><br><span>                 SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create offer\n");</span><br><span>   }</span><br><span>@@ -2709,6 +2738,12 @@</span><br><span>   SCOPE_ENTER(1, "%s %s Topology: %s\n", ast_sorcery_object_get_id(endpoint), request_user,</span><br><span>          ast_str_tmp(256, ast_stream_topology_to_str(req_topology, &STR_TMP)));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        if (!req_topology || ast_stream_topology_get_count(req_topology) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Endpoint '%s': No, or empty, topology specified\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_sorcery_object_get_id(endpoint));</span><br><span style="color: hsl(120, 100%, 40%);">+         SCOPE_EXIT_RTN_VALUE(NULL, "No or empty topology\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* If no location has been provided use the AOR list from the endpoint itself */</span><br><span>     if (location || !contact) {</span><br><span>          location = S_OR(location, endpoint->aors);</span><br><span>@@ -2757,57 +2792,19 @@</span><br><span>              inv_session, NULL);</span><br><span>  if (!session) {</span><br><span>              pjsip_inv_terminate(inv_session, 500, PJ_FALSE);</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create session\n");</span><br><span>       }</span><br><span>    session->aor = ao2_bump(found_aor);</span><br><span>       session->call_direction = AST_SIP_SESSION_OUTGOING_CALL;</span><br><span> </span><br><span>      ast_party_id_copy(&session->id, &endpoint->id.self);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (ast_stream_topology_get_count(req_topology) > 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-               /* get joint caps between req_topology and endpoint topology */</span><br><span style="color: hsl(0, 100%, 40%);">-         int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-          for (i = 0; i < ast_stream_topology_get_count(req_topology); ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  struct ast_stream *req_stream;</span><br><span style="color: hsl(0, 100%, 40%);">-                  struct ast_stream *clone_stream;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                        req_stream = ast_stream_topology_get_stream(req_topology, i);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (ast_stream_get_state(req_stream) == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       clone_stream = ast_sip_session_create_joint_call_stream(session, req_stream);</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (!clone_stream || ast_stream_get_format_count(clone_stream) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                          ast_stream_free(clone_stream);</span><br><span style="color: hsl(0, 100%, 40%);">-                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!session->pending_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            session->pending_media_state->topology = ast_stream_topology_alloc();</span><br><span style="color: hsl(0, 100%, 40%);">-                             if (!session->pending_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                    pjsip_inv_terminate(inv_session, 500, PJ_FALSE);</span><br><span style="color: hsl(0, 100%, 40%);">-                                        ao2_ref(session, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-                                   SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create topology\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                         }</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (ast_stream_topology_append_stream(session->pending_media_state->topology, clone_stream) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_stream_free(clone_stream);</span><br><span style="color: hsl(0, 100%, 40%);">-                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(120, 100%, 40%);">+     session->pending_media_state->topology = ast_stream_topology_clone(req_topology);</span><br><span> </span><br><span>  if (!session->pending_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-            /* Use the configured topology on the endpoint as the pending one */</span><br><span style="color: hsl(0, 100%, 40%);">-            session->pending_media_state->topology = ast_stream_topology_clone(endpoint->media.topology);</span><br><span style="color: hsl(0, 100%, 40%);">-          if (!session->pending_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    pjsip_inv_terminate(inv_session, 500, PJ_FALSE);</span><br><span style="color: hsl(0, 100%, 40%);">-                        ao2_ref(session, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-                   SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't clone topology\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(120, 100%, 40%);">+             pjsip_inv_terminate(inv_session, 500, PJ_FALSE);</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(session, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+         SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't clone topology\n");</span><br><span>       }</span><br><span> </span><br><span>        if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {</span><br><span>@@ -3215,14 +3212,9 @@</span><br><span>    pjsip_tx_data *tdata = NULL;</span><br><span>         pjsip_timer_setting timer;</span><br><span>   pjsip_rdata_sdp_info *sdp_info;</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_session *local = NULL;</span><br><span>   char buffer[AST_SOCKADDR_BUFLEN];</span><br><span>    SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(invite->session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     /* From this point on, any calls to pjsip_inv_terminate have the last argument as PJ_TRUE</span><br><span style="color: hsl(0, 100%, 40%);">-        * so that we will be notified so we can destroy the session properly</span><br><span style="color: hsl(0, 100%, 40%);">-    */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>  if (invite->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {</span><br><span>          ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",</span><br><span>                      invite->session->inv_session->cause,</span><br><span>@@ -3309,31 +3301,8 @@</span><br><span>                       }</span><br><span>                    goto end;</span><br><span>            }</span><br><span style="color: hsl(0, 100%, 40%);">-               /* We are creating a local SDP which is an answer to their offer */</span><br><span style="color: hsl(0, 100%, 40%);">-             local = create_local_sdp(invite->session->inv_session, invite->session, sdp_info->sdp);</span><br><span style="color: hsl(0, 100%, 40%);">-     } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* We are creating a local SDP which is an offer */</span><br><span style="color: hsl(0, 100%, 40%);">-             local = create_local_sdp(invite->session->inv_session, invite->session, NULL);</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* If we were unable to create a local SDP terminate the session early, it won't go anywhere */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!local) {</span><br><span style="color: hsl(0, 100%, 40%);">-           tdata = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-           if (pjsip_inv_end_session(invite->session->inv_session, 500, NULL, &tdata) == PJ_SUCCESS</span><br><span style="color: hsl(0, 100%, 40%);">-                      && tdata) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_sip_session_send_response(invite->session, tdata);</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-               goto end;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       pjsip_inv_set_local_sdp(invite->session->inv_session, local);</span><br><span style="color: hsl(0, 100%, 40%);">-     pjmedia_sdp_neg_set_prefer_remote_codec_order(invite->session->inv_session->neg, PJ_FALSE);</span><br><span style="color: hsl(0, 100%, 40%);">-#ifdef PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!invite->session->endpoint->preferred_codec_only) {</span><br><span style="color: hsl(0, 100%, 40%);">-                pjmedia_sdp_neg_set_answer_multiple_codecs(invite->session->inv_session->neg, PJ_TRUE);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      handle_incoming_request(invite->session, invite->rdata);</span><br><span> </span><br><span> end:</span><br><span>@@ -3518,8 +3487,7 @@</span><br><span>           break;</span><br><span>       default:</span><br><span>             /* Handle other in-dialog methods if their supplements have been registered */</span><br><span style="color: hsl(0, 100%, 40%);">-          handled = dlg && (inv_session = pjsip_dlg_get_inv_session(dlg)) &&</span><br><span style="color: hsl(0, 100%, 40%);">-                      has_supplement(inv_session->mod_data[session_module.id], rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+           handled = (session && has_supplement(session, rdata));</span><br><span>               break;</span><br><span>       }</span><br><span> </span><br><span>@@ -3642,10 +3610,11 @@</span><br><span>      SCOPE_ENTER(1, "%s Method: %.*s\n", ast_sip_session_get_name(session),</span><br><span>             (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    ast_debug(3, "Method is %.*s\n", (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name));</span><br><span>  AST_LIST_TRAVERSE(&session->supplements, supplement, next) {</span><br><span>          if (supplement->incoming_request && does_method_match(&req.method.name, supplement->method)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (supplement->incoming_request(session, rdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                   rc = supplement->incoming_request(session, rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (rc) {</span><br><span>                            break;</span><br><span>                       }</span><br><span>            }</span><br><span>@@ -3692,15 +3661,11 @@</span><br><span>          enum ast_sip_session_response_priority response_priority)</span><br><span> {</span><br><span>       struct ast_sip_session_supplement *supplement;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct pjsip_status_line status = rdata->msg_info.msg->line.status;</span><br><span>    SCOPE_ENTER(1, "%s Method: %.*s Status: %d  Priority %s\n", ast_sip_session_get_name(session),</span><br><span>             (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr,</span><br><span>              rdata->msg_info.msg->line.status.code,</span><br><span>                 response_priority == AST_SIP_SESSION_AFTER_MEDIA ? "after" : "before");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Response is %d %.*s\n", status.code, (int) pj_strlen(&status.reason),</span><br><span style="color: hsl(0, 100%, 40%);">-                       pj_strbuf(&status.reason));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      AST_LIST_TRAVERSE(&session->supplements, supplement, next) {</span><br><span>          if (!(supplement->response_priority & response_priority)) {</span><br><span>                   continue;</span><br><span>@@ -3717,6 +3682,7 @@</span><br><span>            enum ast_sip_session_response_priority response_priority)</span><br><span> {</span><br><span>       SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       ast_debug(3, "Received %s\n", rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ?</span><br><span>                       "request" : "response");</span><br><span> </span><br><span>@@ -3764,11 +3730,6 @@</span><br><span>            ast_log(LOG_ERROR, "Cannot send response due to missing sequence header");</span><br><span>                 SCOPE_EXIT_RTN("Missing cseq\n");</span><br><span>  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_debug(3, "Method is %.*s, Response is %d %.*s\n", (int) pj_strlen(&cseq->method.name),</span><br><span style="color: hsl(0, 100%, 40%);">-             pj_strbuf(&cseq->method.name), status.code, (int) pj_strlen(&status.reason),</span><br><span style="color: hsl(0, 100%, 40%);">-         pj_strbuf(&status.reason));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      ast_sip_message_apply_transport(session->endpoint->transport, tdata);</span><br><span> </span><br><span>      AST_LIST_TRAVERSE(&session->supplements, supplement, next) {</span><br><span>@@ -3999,8 +3960,6 @@</span><br><span>          SCOPE_EXIT_RTN("Shutting down\n");</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   session = inv->mod_data[id];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      print_debug_details(inv, tsx, e);</span><br><span>    if (!session) {</span><br><span>              /* The session has ended.  Ignore the transaction change. */</span><br><span>@@ -4036,7 +3995,9 @@</span><br><span>                 if ((e->body.tsx_state.src.rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) ||</span><br><span>                       (tsx->method.id != PJSIP_BYE_METHOD)) {</span><br><span>                   handle_incoming(session, e->body.tsx_state.src.rdata,</span><br><span style="color: hsl(0, 100%, 40%);">-                                AST_SIP_SESSION_AFTER_MEDIA);</span><br><span style="color: hsl(120, 100%, 40%);">+                         (session->inv_session->state == PJSIP_INV_STATE_CALLING ||</span><br><span style="color: hsl(120, 100%, 40%);">+                              session->inv_session->state == PJSIP_INV_STATE_EARLY)</span><br><span style="color: hsl(120, 100%, 40%);">+                           ? AST_SIP_SESSION_BEFORE_MEDIA : AST_SIP_SESSION_AFTER_MEDIA);</span><br><span>               }</span><br><span>            if (tsx->method.id == PJSIP_INVITE_METHOD) {</span><br><span>                      if (tsx->role == PJSIP_ROLE_UAC) {</span><br><span>@@ -4222,8 +4183,10 @@</span><br><span>               ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));</span><br><span> </span><br><span>      if (handler) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_trace(2, "Handler '%s' already exists\n", handler->id);</span><br><span>             /* if an already assigned handler reports a catastrophic error, fail */</span><br><span style="color: hsl(0, 100%, 40%);">-         res = handler->create_outgoing_sdp_stream(session, session_media, answer, remote, stream);</span><br><span style="color: hsl(120, 100%, 40%);">+         res = handler->create_outgoing_sdp_stream(session,</span><br><span style="color: hsl(120, 100%, 40%);">+                 session_media, answer, remote, stream);</span><br><span>              if (res < 0) {</span><br><span>                    SCOPE_EXIT_RTN_VALUE(-1, "Coudn't create sdp stream\n");</span><br><span>               }</span><br><span>@@ -4238,9 +4201,15 @@</span><br><span>   /* no handler for this stream type and we have a list to search */</span><br><span>   AST_LIST_TRAVERSE(&handler_list->list, handler, next) {</span><br><span>               if (handler == session_media->handler) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_trace(2, "Skipping handler '%s'\n", handler->id);</span><br><span>                   continue;</span><br><span>            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_trace(2, "Calling handler '%s' with stream %s\n", handler->id,</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));</span><br><span>          res = handler->create_outgoing_sdp_stream(session, session_media, answer, remote, stream);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_trace(2, "Handler '%s' RC: %d stream %s\n", handler->id, res,</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));</span><br><span>          if (res < 0) {</span><br><span>                    /* catastrophic error */</span><br><span>                     SCOPE_EXIT_RTN_VALUE(-1, "Coudn't create sdp stream\n");</span><br><span>@@ -4334,7 +4303,8 @@</span><br><span>       return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer)</span><br><span style="color: hsl(120, 100%, 40%);">+struct pjmedia_sdp_session *ast_sip_session_create_local_sdp(pjsip_inv_session *inv,</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_session *session, const pjmedia_sdp_session *offer)</span><br><span> {</span><br><span>      static const pj_str_t STR_IN = { "IN", 2 };</span><br><span>        static const pj_str_t STR_IP4 = { "IP4", 3 };</span><br><span>@@ -4364,26 +4334,11 @@</span><br><span>    pj_strdup2(inv->pool_prov, &local->origin.user, session->endpoint->media.sdpowner);</span><br><span>  pj_strdup2(inv->pool_prov, &local->name, session->endpoint->media.sdpsession);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      if (!session->pending_media_state->topology || !ast_stream_topology_get_count(session->pending_media_state->topology)) {</span><br><span style="color: hsl(0, 100%, 40%);">-            /* We've encountered a situation where we have been told to create a local SDP but noone has given us any indication</span><br><span style="color: hsl(0, 100%, 40%);">-                 * of what kind of stream topology they would like. We try to not alter the current state of the SDP negotiation</span><br><span style="color: hsl(0, 100%, 40%);">-                 * by using what is currently negotiated. If this is unavailable we fall back to what is configured on the endpoint.</span><br><span style="color: hsl(0, 100%, 40%);">-             */</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_stream_topology_free(session->pending_media_state->topology);</span><br><span style="color: hsl(0, 100%, 40%);">-         if (session->active_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      session->pending_media_state->topology = ast_stream_topology_clone(session->active_media_state->topology);</span><br><span style="color: hsl(0, 100%, 40%);">-          } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        session->pending_media_state->topology = ast_stream_topology_clone(session->endpoint->media.topology);</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-               if (!session->pending_media_state->topology) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    SCOPE_EXIT_RTN_VALUE(NULL, "No pending topology\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    for (i = 0; i < ast_stream_topology_get_count(session->pending_media_state->topology); ++i) {</span><br><span>               struct ast_sip_session_media *session_media;</span><br><span>                 struct ast_stream *stream;</span><br><span>           unsigned int streams = local->media_count;</span><br><span style="color: hsl(120, 100%, 40%);">+         int res = 0;</span><br><span> </span><br><span>             /* This code does not enforce any maximum stream count limitations as that is done on either</span><br><span>                  * the handling of an incoming SDP offer or on the handling of a session refresh.</span><br><span>@@ -4396,7 +4351,10 @@</span><br><span>                   SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't add state\n");</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (add_sdp_streams(session_media, session, local, offer, stream)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_trace(2, "Adding stream %s\n", ast_stream_to_str(stream, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+                res = add_sdp_streams(session_media, session, local, offer, stream);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_trace(2, "Added stream %s  RC: %d\n", ast_stream_to_str(stream, NULL), res);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (res) {</span><br><span>                   SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't add stream\n");</span><br><span>           }</span><br><span> </span><br><span>@@ -4477,13 +4435,12 @@</span><br><span>              SCOPE_EXIT_RTN("Shutting down\n");</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   session = inv->mod_data[session_module.id];</span><br><span>       if (handle_incoming_sdp(session, offer)) {</span><br><span>           ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span>          SCOPE_EXIT_RTN("Couldn't handle sdp\n");</span><br><span>       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if ((answer = create_local_sdp(inv, session, offer))) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if ((answer = ast_sip_session_create_local_sdp(inv, session, offer))) {</span><br><span>              pjsip_inv_set_sdp_answer(inv, answer);</span><br><span>       }</span><br><span> </span><br><span>@@ -4501,14 +4458,15 @@</span><br><span> {</span><br><span>         const pjmedia_sdp_session *local, *remote;</span><br><span>   struct ast_sip_session *session = inv->mod_data[session_module.id];</span><br><span style="color: hsl(0, 100%, 40%);">-  SCOPE_ENTER(1, "%s  Inv State: %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(0, 100%, 40%);">-              pjsip_inv_state_name(inv->state));</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(1, "%s  Inv State: %s  Pending topology: %s  Active topology %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_sip_session_get_name(session), pjsip_inv_state_name(inv->state),</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span> </span><br><span>        if (ast_shutdown_final()) {</span><br><span>          SCOPE_EXIT_RTN("Shutting down\n");</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   session = inv->mod_data[session_module.id];</span><br><span>       if (!session || !session->channel) {</span><br><span>              /*</span><br><span>            * If we don't have a session or channel then we really</span><br><span>@@ -4562,11 +4520,14 @@</span><br><span>                SCOPE_EXIT_RTN("Couldn't get active local\n");return;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (handle_negotiated_sdp(session, local, remote)) {</span><br><span style="color: hsl(120, 100%, 40%);">+  if (handle_negotiated_sdp(session, local, remote, inv->state)) {</span><br><span>          ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   SCOPE_EXIT_RTN();</span><br><span style="color: hsl(120, 100%, 40%);">+     SCOPE_EXIT_RTN("%s  Inv State: %s  Pending topology: %s  Active topology %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_sip_session_get_name(session), pjsip_inv_state_name(inv->state),</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span> }</span><br><span> </span><br><span> static pjsip_redirect_op session_inv_on_redirected(pjsip_inv_session *inv, const pjsip_uri *target, const pjsip_event *e)</span><br><span>@@ -4708,12 +4669,15 @@</span><br><span>            return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span>    endpt = ast_sip_get_pjsip_endpoint();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      pjsip_inv_usage_init(endpt, &inv_callback);</span><br><span>      pjsip_100rel_init_module(endpt);</span><br><span>     pjsip_timer_init_module(endpt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    if (ast_sip_register_service(&session_module)) {</span><br><span>                 return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  ast_sip_register_service(&session_reinvite_module);</span><br><span>      ast_sip_register_service(&outbound_invite_auth_module);</span><br><span> </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>deleted file mode 100644</span><br><span>index ca7ef44..0000000</span><br><span>--- a/res/res_pjsip_session/pjsip_session_caps.c</span><br><span>+++ /dev/null</span><br><span>@@ -1,155 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/*</span><br><span style="color: hsl(0, 100%, 40%);">- * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Copyright (C) 2020, Sangoma Technologies Corporation</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Kevin Harwell <kharwell@digium.com></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(0, 100%, 40%);">- * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(0, 100%, 40%);">- * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(0, 100%, 40%);">- * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(0, 100%, 40%);">- * channels for your use.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is free software, distributed under the terms of</span><br><span style="color: hsl(0, 100%, 40%);">- * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(0, 100%, 40%);">- * at the top of the source tree.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk.h"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/astobj2.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/channel.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/format.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/format_cap.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/logger.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/sorcery.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/stream.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session_caps.h"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void log_caps(int level, const char *file, int line, const char *function,</span><br><span style="color: hsl(0, 100%, 40%);">- const struct ast_sip_session *session, enum ast_media_type media_type,</span><br><span style="color: hsl(0, 100%, 40%);">-  const struct ast_format_cap *local, const struct ast_format_cap *remote,</span><br><span style="color: hsl(0, 100%, 40%);">-        const struct ast_format_cap *joint)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_str *s1;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_str *s2;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_str *s3;</span><br><span style="color: hsl(0, 100%, 40%);">-     int outgoing = session->call_direction == AST_SIP_SESSION_OUTGOING_CALL;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_flags pref =</span><br><span style="color: hsl(0, 100%, 40%);">-         outgoing</span><br><span style="color: hsl(0, 100%, 40%);">-                ? session->endpoint->media.outgoing_call_offer_pref</span><br><span style="color: hsl(0, 100%, 40%);">-               : session->endpoint->media.incoming_call_offer_pref;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      if (level == __LOG_DEBUG && !DEBUG_ATLEAST(3)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       s1 = local ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-   s2 = remote ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-  s3 = joint ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   ast_log(level, file, line, function, "'%s' Caps for %s %s call with pref '%s' - remote: %s local: %s joint: %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-            session->channel ? ast_channel_name(session->channel) :</span><br><span style="color: hsl(0, 100%, 40%);">-                   ast_sorcery_object_get_id(session->endpoint),</span><br><span style="color: hsl(0, 100%, 40%);">-                outgoing? "outgoing" : "incoming",</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_codec_media_type2str(media_type),</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_sip_call_codec_pref_to_str(pref),</span><br><span style="color: hsl(0, 100%, 40%);">-           s2 ? ast_format_cap_get_names(remote, &s2) : "(NONE)",</span><br><span style="color: hsl(0, 100%, 40%);">-            s1 ? ast_format_cap_get_names(local, &s1) : "(NONE)",</span><br><span style="color: hsl(0, 100%, 40%);">-             s3 ? ast_format_cap_get_names(joint, &s3) : "(NONE)");</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-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(0, 100%, 40%);">-   struct ast_flags codec_pref)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_format_cap *joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">-       struct ast_format_cap *local_filtered = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!joint || !local_filtered) {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_log(LOG_ERROR, "Failed to allocate %s call offer capabilities\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                         ast_codec_media_type2str(media_type));</span><br><span style="color: hsl(0, 100%, 40%);">-          ao2_cleanup(joint);</span><br><span style="color: hsl(0, 100%, 40%);">-             ao2_cleanup(local_filtered);</span><br><span style="color: hsl(0, 100%, 40%);">-            return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_format_cap_append_from_cap(local_filtered, local, media_type);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      if (ast_sip_call_codec_pref_test(codec_pref, LOCAL)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      ast_format_cap_get_compatible(local_filtered, remote, joint); /* Get common, prefer local */</span><br><span style="color: hsl(0, 100%, 40%);">-            } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_format_cap_append_from_cap(joint, local_filtered, media_type); /* Add local */</span><br><span style="color: hsl(0, 100%, 40%);">-                      ast_format_cap_append_from_cap(joint, remote, media_type); /* Then remote */</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      ast_format_cap_get_compatible(remote, local_filtered, joint); /* Get common, prefer remote */</span><br><span style="color: hsl(0, 100%, 40%);">-           } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_format_cap_append_from_cap(joint, remote, media_type); /* Add remote */</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_format_cap_append_from_cap(joint, local_filtered, media_type); /* Then local */</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ao2_ref(local_filtered, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if (ast_format_cap_empty(joint)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              return joint;</span><br><span style="color: hsl(0, 100%, 40%);">-   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_sip_call_codec_pref_test(codec_pref, FIRST)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          /*</span><br><span style="color: hsl(0, 100%, 40%);">-               * Save the most preferred one. Session capabilities are per stream and</span><br><span style="color: hsl(0, 100%, 40%);">-          * a stream only carries a single media type, so no reason to worry with</span><br><span style="color: hsl(0, 100%, 40%);">-                 * the type here (i.e different or multiple types)</span><br><span style="color: hsl(0, 100%, 40%);">-               */</span><br><span style="color: hsl(0, 100%, 40%);">-             struct ast_format *single = ast_format_cap_get_format(joint, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-                /* Remove all formats */</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_format_cap_remove_by_type(joint, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">-           /* Put the most preferred one back */</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_format_cap_append(joint, single, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-                ao2_ref(single, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return joint;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_stream *remote_stream)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_stream *joint_stream = ast_stream_clone(remote_stream, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-        const struct ast_format_cap *remote = ast_stream_get_formats(remote_stream);</span><br><span style="color: hsl(0, 100%, 40%);">-    enum ast_media_type media_type = ast_stream_get_type(remote_stream);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,</span><br><span style="color: hsl(0, 100%, 40%);">-            session->endpoint->media.codecs, media_type,</span><br><span style="color: hsl(0, 100%, 40%);">-              session->call_direction == AST_SIP_SESSION_OUTGOING_CALL</span><br><span style="color: hsl(0, 100%, 40%);">-                             ? session->endpoint->media.outgoing_call_offer_pref</span><br><span style="color: hsl(0, 100%, 40%);">-                               : session->endpoint->media.incoming_call_offer_pref);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     ast_stream_set_formats(joint_stream, joint);</span><br><span style="color: hsl(0, 100%, 40%);">-    ao2_cleanup(joint);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return joint_stream;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_format_cap *ast_sip_session_create_joint_call_cap(</span><br><span style="color: hsl(0, 100%, 40%);">-      const struct ast_sip_session *session, enum ast_media_type media_type,</span><br><span style="color: hsl(0, 100%, 40%);">-  const struct ast_format_cap *remote)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,</span><br><span style="color: hsl(0, 100%, 40%);">-            session->endpoint->media.codecs, media_type,</span><br><span style="color: hsl(0, 100%, 40%);">-              session->call_direction == AST_SIP_SESSION_OUTGOING_CALL</span><br><span style="color: hsl(0, 100%, 40%);">-                             ? session->endpoint->media.outgoing_call_offer_pref</span><br><span style="color: hsl(0, 100%, 40%);">-                               : session->endpoint->media.incoming_call_offer_pref);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return joint;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span>diff --git a/tests/test_res_pjsip_session_caps.c b/tests/test_res_pjsip_session_caps.c</span><br><span>deleted file mode 100644</span><br><span>index 54438f5..0000000</span><br><span>--- a/tests/test_res_pjsip_session_caps.c</span><br><span>+++ /dev/null</span><br><span>@@ -1,176 +0,0 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/*</span><br><span style="color: hsl(0, 100%, 40%);">- * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Copyright (C) 2020, Sangoma Technologies Corporation</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * George Joseph <gjoseph@sangoma.com></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(0, 100%, 40%);">- * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(0, 100%, 40%);">- * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(0, 100%, 40%);">- * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(0, 100%, 40%);">- * channels for your use.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This program is free software, distributed under the terms of</span><br><span style="color: hsl(0, 100%, 40%);">- * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(0, 100%, 40%);">- * at the top of the source tree.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \file</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief res_pjsip_session format caps tests</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \author George Joseph <gjoseph@sangoma.com></span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*** MODULEINFO</span><br><span style="color: hsl(0, 100%, 40%);">-      <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(0, 100%, 40%);">-     <depend>pjproject</depend></span><br><span style="color: hsl(0, 100%, 40%);">-  <depend>res_pjsip</depend></span><br><span style="color: hsl(0, 100%, 40%);">-  <depend>res_pjsip_session</depend></span><br><span style="color: hsl(0, 100%, 40%);">-  <support_level>core</support_level></span><br><span style="color: hsl(0, 100%, 40%);">- ***/</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk.h"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/test.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/module.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/utils.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/format.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/format_cap.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/res_pjsip_session_caps.h"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static enum ast_test_result_state test_create_joint(struct ast_test *test, const char *local_string,</span><br><span style="color: hsl(0, 100%, 40%);">-  const char *remote_string, const char *pref_string, int is_outgoing, const char *expected_string,</span><br><span style="color: hsl(0, 100%, 40%);">-       enum ast_test_result_state expected_result)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    RAII_VAR(struct ast_format_cap *, local, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-       RAII_VAR(struct ast_format_cap *, remote, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-      RAII_VAR(struct ast_format_cap *, joint, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-       struct ast_str *joint_str = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span style="color: hsl(0, 100%, 40%);">-   const char *joint_string;</span><br><span style="color: hsl(0, 100%, 40%);">-       char *stripped_joint;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_flags codec_prefs;</span><br><span style="color: hsl(0, 100%, 40%);">-   int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_test_status_update(test, "Testing local: (%s), remote: (%s), pref: (%-12s), outgoing: (%s), expected: (%s) expected result: (%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-              local_string, remote_string, pref_string, is_outgoing ? "yes" : "no ", expected_string,</span><br><span style="color: hsl(0, 100%, 40%);">-             expected_result == AST_TEST_PASS ? "PASS" : "FAIL");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        ast_test_validate(test, local != NULL && remote != NULL && joint != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = ast_format_cap_update_by_allow_disallow(local, local_string, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (rc != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_test_status_update(test, "    %sxpected Failure: Coulldn't parse local codecs (%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                        expected_result == AST_TEST_FAIL ? "E" : "Une", local_string);</span><br><span style="color: hsl(0, 100%, 40%);">-              return expected_result == AST_TEST_FAIL ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = ast_format_cap_update_by_allow_disallow(remote, remote_string, 1);</span><br><span style="color: hsl(0, 100%, 40%);">- if (rc != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_test_status_update(test, "    %sxpected Failure: Coulldn't parse remote codecs (%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                       expected_result == AST_TEST_FAIL ? "E" : "Une", remote_string);</span><br><span style="color: hsl(0, 100%, 40%);">-             return expected_result == AST_TEST_FAIL ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = ast_sip_call_codec_str_to_pref(&codec_prefs, pref_string, is_outgoing);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (rc != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_test_status_update(test, "    %sxpected Failure: Invalid preference string incoming/outgoing combination.\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     expected_result == AST_TEST_FAIL ? "E" : "Une");</span><br><span style="color: hsl(0, 100%, 40%);">-            return expected_result == AST_TEST_FAIL ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       joint = ast_sip_create_joint_call_cap(remote, local, AST_MEDIA_TYPE_AUDIO, codec_prefs);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (joint == NULL) {</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_test_status_update(test, "    %sxpected Failure: No joint caps.\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                       expected_result == AST_TEST_FAIL ? "E" : "Une");</span><br><span style="color: hsl(0, 100%, 40%);">-            return expected_result == AST_TEST_FAIL ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       joint_string = ast_format_cap_get_names(joint, &joint_str);</span><br><span style="color: hsl(0, 100%, 40%);">- stripped_joint = ast_str_truncate(joint_str, ast_str_strlen(joint_str) - 1) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-        for(i = 0; i <= strlen(stripped_joint); i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-               if(stripped_joint[i] == '|') {</span><br><span style="color: hsl(0, 100%, 40%);">-                  stripped_joint[i] = ',';</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!joint_string || strcmp(stripped_joint, expected_string) != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_test_status_update(test, "    %sxpected Failure: Expected: (%s) Actual: (%s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                  expected_result == AST_TEST_FAIL ? "E" : "Une", expected_string, stripped_joint);</span><br><span style="color: hsl(0, 100%, 40%);">-           return expected_result == AST_TEST_FAIL ? AST_TEST_PASS : AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return AST_TEST_PASS;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define RUN_CREATE_JOINT(local, remote, pref, outgoing, expected, result) \</span><br><span style="color: hsl(0, 100%, 40%);">-do { \</span><br><span style="color: hsl(0, 100%, 40%);">- if (test_create_joint(test, local, remote, pref, outgoing, expected, result) != AST_TEST_PASS) { \</span><br><span style="color: hsl(0, 100%, 40%);">-              rc += 1; \</span><br><span style="color: hsl(0, 100%, 40%);">-      } \</span><br><span style="color: hsl(0, 100%, 40%);">-} while (0)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-AST_TEST_DEFINE(low_level)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       int rc = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     switch (cmd) {</span><br><span style="color: hsl(0, 100%, 40%);">-  case TEST_INIT:</span><br><span style="color: hsl(0, 100%, 40%);">-         info->name = __func__;</span><br><span style="color: hsl(0, 100%, 40%);">-               info->category = "/res/res_pjsip_session/caps/";</span><br><span style="color: hsl(0, 100%, 40%);">-           info->summary = "Test res_pjsip_session_caps";</span><br><span style="color: hsl(0, 100%, 40%);">-             info->description = "Test res_pjsip_session_caps";</span><br><span style="color: hsl(0, 100%, 40%);">-         return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(0, 100%, 40%);">-        case TEST_EXECUTE:</span><br><span style="color: hsl(0, 100%, 40%);">-              break;</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Incoming */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_test_status_update(test, "Testing incoming expected pass\n");</span><br><span style="color: hsl(0, 100%, 40%);">-     RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "local",              0,      "alaw,g722",  AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "local_first",        0,      "alaw",                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("slin",                      "all",                                "local",              0,      "slin",                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "remote",             0,      "g722,alaw",  AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "remote_first",       0,      "g722",                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("all",                               "slin",                               "remote_first",       0,      "slin",                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ast_test_status_update(test, "Testing incoming expected fail\n");</span><br><span style="color: hsl(0, 100%, 40%);">-     RUN_CREATE_JOINT("ulaw,alaw,g722",    "g729",                               "local",              0,      "",                           AST_TEST_FAIL);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "local_merge",        0,      "",                           AST_TEST_FAIL);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,alaw,g729",     "remote_merge",       0,      "",                           AST_TEST_FAIL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ast_test_status_update(test, "Testing outgoing expected pass\n");</span><br><span style="color: hsl(0, 100%, 40%);">-     RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "local",              1,      "alaw,g722",                  AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "local_first",        1,      "alaw",                                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "local_merge",        1,      "ulaw,alaw,g722,g729",        AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "remote",             1,      "g722,alaw",                  AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "remote_first",       1,      "g722",                                       AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("ulaw,alaw,g722",    "g722,g729,alaw",     "remote_merge",       1,      "g722,g729,alaw,ulaw",        AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">- RUN_CREATE_JOINT("!all",                      "g722,g729,alaw",     "remote_merge",       1,      "g722,g729,alaw",             AST_TEST_PASS);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return rc >= 1 ? AST_TEST_FAIL : AST_TEST_PASS;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int load_module(void)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_TEST_REGISTER(low_level);</span><br><span style="color: hsl(0, 100%, 40%);">-   return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int unload_module(void)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- AST_TEST_UNREGISTER(low_level);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "res_pjsip_session caps test module",</span><br><span style="color: hsl(0, 100%, 40%);">-        .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(0, 100%, 40%);">-       .load = load_module,</span><br><span style="color: hsl(0, 100%, 40%);">-    .unload = unload_module,</span><br><span style="color: hsl(0, 100%, 40%);">-        .requires = "res_pjsip_session",</span><br><span style="color: hsl(0, 100%, 40%);">-);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14637">change 14637</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/+/14637"/><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: 14637 </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>