codec_negotiation: Implement outgoing_call_offer_pref

Based on this new endpoint 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.

* Add outgoing_call_offer_pref to pjsip_configuration (endpoint)

* Add "call_direction" to res_pjsip_session.

* Update pjsip_session_caps.c to make the functions more generic
  so they could be used for both incoming and outgoing.

* Update ast_sip_session_create_outgoing to create the
  pending_media_state->topology with the results of
  ast_sip_session_create_joint_call_stream().

* The endpoint "preferred_codec_only" option now automatically sets
  AST_SIP_CALL_CODEC_PREF_FIRST in incoming_call_offer_pref.

* A helper function ast_stream_get_format_count() was added to
  streams to return the current count of formats.

ASTERISK-28777

Change-Id: Id4ec0b4a906c2ae5885bf947f101c59059935437
This commit is contained in:
George Joseph
2020-03-13 13:40:46 -06:00
committed by Friendly Automation
parent 57a457c26c
commit 2ee455958e
13 changed files with 408 additions and 297 deletions

View File

@@ -923,25 +923,75 @@
</para></description>
</configOption>
<configOption name="preferred_codec_only" default="no">
<synopsis>Respond to a SIP invite with the single most preferred codec rather than advertising all joint codec capabilities. This limits the other side's codec choice to exactly what we prefer.</synopsis>
<synopsis>Respond to a SIP invite with the single most preferred codec (DEPRECATED)</synopsis>
<description><para>Respond to a SIP invite with the single most preferred codec
rather than advertising all joint codec capabilities. This limits the other side's codec
choice to exactly what we prefer.</para>
<warning><para>This option has been deprecated in favor of
<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>After receiving an incoming offer create a list of preferred codecs between
those received in the SDP offer, and those specified in endpoint configuration.</synopsis>
<synopsis>Preferences for selecting codecs for an incoming call.</synopsis>
<description>
<note><para>This list will consist of only those codecs found in both.</para></note>
<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>
Order by the endpoint configuration allow line (default)
Include all codecs in the local list that are also in the remote list
preserving the local order. (default).
</para></enum>
<enum name="local_single"><para>
Order by the endpoint configuration allow line, but the list will only contain the first, or 'top' item
<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>
Order by what is received in the SDP offer
Include all codecs in the remote list that are also in the local list
preserving the remote order.
</para></enum>
<enum name="remote_single"><para>
Order by what is received in the SDP offer, but the list will only contain the first, or 'top' item
<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>
@@ -5044,6 +5094,29 @@ int ast_sip_str_to_dtmf(const char * dtmf_mode)
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;
}
/*!
* \brief Set name and number information on an identity header.
*

View File

@@ -1121,43 +1121,57 @@ static int contact_user_to_str(const void *obj, const intptr_t *args, char **buf
return 0;
}
static const char *sip_call_codec_pref_strings[] = {
[AST_SIP_CALL_CODEC_PREF_LOCAL] = "local",
[AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT] = "local_limit",
[AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE] = "local_single",
[AST_SIP_CALL_CODEC_PREF_REMOTE] = "remote",
[AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT] = "remote_limit",
[AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE] = "remote_single",
};
static int incoming_call_offer_pref_handler(const struct aco_option *opt,
static int call_offer_pref_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
unsigned int i;
struct ast_flags pref = { 0, };
int outgoing = strcmp(var->name, "outgoing_call_offer_pref") == 0;
for (i = 0; i < ARRAY_LEN(sip_call_codec_pref_strings); ++i) {
if (!strcmp(var->value, sip_call_codec_pref_strings[i])) {
/* Local and remote limit are not available values for this option */
if (i == AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT ||
i == AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT) {
return -1;
}
endpoint->media.incoming_call_offer_pref = i;
return 0;
}
if (strcmp(var->value, "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 (outgoing && strcmp(var->value, "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(var->value, "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(var->value, "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 (outgoing && strcmp(var->value, "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(var->value, "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 -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;
if (ARRAY_IN_BOUNDS(endpoint->media.incoming_call_offer_pref, sip_call_codec_pref_strings)) {
*buf = ast_strdup(sip_call_codec_pref_strings[endpoint->media.incoming_call_offer_pref]);
*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;
@@ -1345,6 +1359,16 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
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);
}
endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);
if (!endpoint->media.topology) {
return -1;
@@ -2009,7 +2033,9 @@ int ast_res_pjsip_initialize_configuration(void)
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",
incoming_call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);
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);
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");

View File

@@ -399,13 +399,13 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
static int apply_cap_to_bundled(struct ast_sip_session_media *session_media,
struct ast_sip_session_media *session_media_transport,
struct ast_stream *asterisk_stream, const struct ast_format_cap *joint)
struct ast_stream *asterisk_stream, struct ast_format_cap *joint)
{
if (!joint) {
return -1;
}
ast_stream_set_formats(asterisk_stream, (struct ast_format_cap *)joint);
ast_stream_set_formats(asterisk_stream, joint);
/* If this is a bundled stream then apply the payloads to RTP instance acting as transport to prevent conflicts */
if (session_media_transport != session_media && session_media->bundled) {
@@ -428,11 +428,11 @@ static int apply_cap_to_bundled(struct ast_sip_session_media *session_media,
return 0;
}
static const struct ast_format_cap *set_incoming_call_offer_cap(
static struct ast_format_cap *set_incoming_call_offer_cap(
struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_media *stream)
{
const struct ast_format_cap *incoming_call_offer_cap;
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;
@@ -448,12 +448,13 @@ static const struct ast_format_cap *set_incoming_call_offer_cap(
get_codecs(session, stream, &codecs, session_media);
ast_rtp_codecs_payload_formats(&codecs, remote, &fmts);
incoming_call_offer_cap = ast_sip_session_join_incoming_call_offer_cap(
session, session_media, remote);
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) {
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);
return NULL;
}
@@ -1413,6 +1414,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
struct ast_sip_session_media *session_media_transport;
enum ast_media_type media_type = session_media->type;
enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
struct ast_format_cap *joint;
int res;
/* If no type formats have been configured reject this stream */
@@ -1504,8 +1506,10 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
}
}
if (apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream,
set_incoming_call_offer_cap(session, session_media, stream))) {
joint = set_incoming_call_offer_cap(session, session_media, stream);
res = apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream, joint);
ao2_cleanup(joint);
if (res != 0) {
return 0;
}

View File

@@ -468,8 +468,6 @@ static void session_media_dtor(void *obj)
ast_free(session_media->mid);
ast_free(session_media->remote_mslabel);
ao2_cleanup(session_media->caps);
}
struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,
@@ -528,12 +526,6 @@ struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_ses
} else {
session_media->bundle_group = -1;
}
session_media->caps = ast_sip_session_caps_alloc();
if (!session_media->caps) {
ao2_ref(session_media, -1);
return NULL;
}
}
if (AST_VECTOR_REPLACE(&media_state->sessions, position, session_media)) {
@@ -2701,6 +2693,8 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
return NULL;
}
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) {
@@ -2709,8 +2703,6 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
for (i = 0; i < ast_stream_topology_get_count(req_topology); ++i) {
struct ast_stream *req_stream;
struct ast_format_cap *req_cap;
struct ast_format_cap *joint_cap;
struct ast_stream *clone_stream;
req_stream = ast_stream_topology_get_stream(req_topology, i);
@@ -2719,39 +2711,12 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
continue;
}
req_cap = ast_stream_get_formats(req_stream);
joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!joint_cap) {
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;
}
ast_format_cap_get_compatible(req_cap, endpoint->media.codecs, joint_cap);
if (ast_stream_get_type(req_stream) == AST_MEDIA_TYPE_AUDIO) {
/*
* By appending codecs from the endpoint after compatible ones this
* guarantees that priority is given to those while also allowing
* translation to occur for non-compatible.
*/
ast_format_cap_append_from_cap(joint_cap,
endpoint->media.codecs, AST_MEDIA_TYPE_AUDIO);
}
if (!ast_format_cap_count(joint_cap)) {
ao2_ref(joint_cap, -1);
continue;
}
clone_stream = ast_stream_clone(req_stream, NULL);
if (!clone_stream) {
ao2_ref(joint_cap, -1);
continue;
}
ast_stream_set_formats(clone_stream, joint_cap);
ao2_ref(joint_cap, -1);
if (!session->pending_media_state->topology) {
session->pending_media_state->topology = ast_stream_topology_alloc();
if (!session->pending_media_state->topology) {
@@ -3351,6 +3316,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
#endif
return;
}
session->call_direction = AST_SIP_SESSION_INCOMING_CALL;
/*
* The current thread is supposed be the session serializer to prevent

View File

@@ -24,25 +24,24 @@
#include "asterisk/format_cap.h"
#include "asterisk/logger.h"
#include "asterisk/sorcery.h"
#include <pjsip_ua.h>
#include "asterisk/stream.h"
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/res_pjsip_session_caps.h"
struct ast_sip_session_caps {
struct ast_format_cap *incoming_call_offer_cap;
};
static void log_caps(int level, const char *file, int line, const char *function,
const char *msg, const struct ast_sip_session *session,
const struct ast_sip_session_media *session_media, const struct ast_format_cap *local,
const struct ast_format_cap *remote, const struct ast_format_cap *joint)
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;
@@ -52,93 +51,57 @@ static void log_caps(int level, const char *file, int line, const char *function
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' %s '%s' capabilities -%s%s%s%s%s%s\n",
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),
msg ? msg : "-", ast_codec_media_type2str(session_media->type),
s1 ? " local: " : "", s1 ? ast_format_cap_get_names(local, &s1) : "",
s2 ? " remote: " : "", s2 ? ast_format_cap_get_names(remote, &s2) : "",
s3 ? " joint: " : "", s3 ? ast_format_cap_get_names(joint, &s3) : "");
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)");
}
static void sip_session_caps_destroy(void *obj)
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_sip_session_caps *caps = obj;
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);
ao2_cleanup(caps->incoming_call_offer_cap);
}
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;
}
struct ast_sip_session_caps *ast_sip_session_caps_alloc(void)
{
return ao2_alloc_options(sizeof(struct ast_sip_session_caps),
sip_session_caps_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
}
ast_format_cap_append_from_cap(local_filtered, local, media_type);
void ast_sip_session_set_incoming_call_offer_cap(struct ast_sip_session_caps *caps,
struct ast_format_cap *cap)
{
ao2_cleanup(caps->incoming_call_offer_cap);
caps->incoming_call_offer_cap = ao2_bump(cap);
}
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 */
}
}
const struct ast_format_cap *ast_sip_session_get_incoming_call_offer_cap(
const struct ast_sip_session_caps *caps)
{
return caps->incoming_call_offer_cap;
}
ao2_ref(local_filtered, -1);
const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
const struct ast_sip_session *session, const struct ast_sip_session_media *session_media,
const struct ast_format_cap *remote)
{
enum ast_sip_call_codec_pref pref;
struct ast_format_cap *joint;
struct ast_format_cap *local;
joint = session_media->caps->incoming_call_offer_cap;
if (joint) {
/*
* If the incoming call offer capabilities have been set elsewhere, e.g. dialplan
* then those take precedence.
*/
if (ast_format_cap_empty(joint)) {
return joint;
}
joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
local = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!joint || !local) {
ast_log(LOG_ERROR, "Failed to allocate %s incoming call offer capabilities\n",
ast_codec_media_type2str(session_media->type));
ao2_cleanup(joint);
ao2_cleanup(local);
return NULL;
}
pref = session->endpoint->media.incoming_call_offer_pref;
ast_format_cap_append_from_cap(local, session->endpoint->media.codecs,
session_media->type);
if (pref < AST_SIP_CALL_CODEC_PREF_REMOTE) {
ast_format_cap_get_compatible(local, remote, joint); /* Prefer local */
} else {
ast_format_cap_get_compatible(remote, local, joint); /* Prefer remote */
}
if (ast_format_cap_empty(joint)) {
log_caps(LOG_NOTICE, "No joint incoming", session, session_media, local, remote, NULL);
ao2_ref(joint, -1);
ao2_ref(local, -1);
return NULL;
}
if (pref == AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE ||
pref == AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE ||
session->endpoint->preferred_codec_only) {
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
@@ -152,11 +115,41 @@ const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
ao2_ref(single, -1);
}
log_caps(LOG_DEBUG, "Joint incoming", session, session_media, local, remote, joint);
return joint;
}
ao2_ref(local, -1);
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);
struct ast_format_cap *remote = ast_stream_get_formats(remote_stream);
enum ast_media_type media_type = ast_stream_get_type(remote_stream);
ast_sip_session_set_incoming_call_offer_cap(session_media->caps, joint);
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;
}