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