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