ACN: res_pjsip endpoint options

This commit adds the endpoint options required to control
Advanced Codec Negotiation.

incoming_offer_codec_prefs
outgoing_offer_codec_prefs
incoming_answer_codec_prefs
outgoing_answer_codec_prefs

The documentation may need tweaking and some additional edits
added, especially for the "answer" prefs.  That'll be handled
when things finalize.

This commit is safe to merge as it doens't alter any existing
functionality nor does it alter the previous codec negotiation
work which may now be obsolete.

Change-Id: I920ba925d7dd36430dfd2ebd9d82d23f123d0e11
remotes/origin/18.0
George Joseph 4 years ago
parent 81b5e4a73f
commit 2d22e34206

@ -839,6 +839,54 @@
; 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
; specified on an incoming SDP offer (pending) are
; reconciled with the codecs specified on an endpoint
; (configured) before being sent to the Asterisk core.
; The string actually specifies 4 name:value pair
; parameters separated by commas. Whitespace is
; ignored and they may be specified in any order.
; prefer: <pending | configured>,
; operation: <intersect | only_preferred
; | only_nonpreferred>,
; keep: <first | all>,
; transcode: <allow | prevent>
;outgoing_offer_codec_prefs=; 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)
; when sending an SDP offer.
; The string actually specifies 4 name:value pair
; parameters separated by commas. Whitespace is
; ignored and they may be specified in any order.
; prefer: <pending | configured>,
; operation: <intersect | union
; | only_preferred | only_nonpreferred>,
; keep: <first | all>,
; transcode: <allow | prevent>
;incoming_answer_codec_prefs=; 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
; answer.
; The string actually specifies 4 name:value pair
; parameters separated by commas. Whitespace is
; ignored and they may be specified in any order.
; prefer: <pending | configured>,
; operation: <intersect | union
; | only_preferred | only_nonpreferred>,
; keep: <first | all>
;outgoing_answer_codec_prefs=; 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.
; The string actually specifies 4 name:value pair
; parameters separated by commas. Whitespace is
; ignored and they may be specified in any order.
; prefer: <pending | configured>,
; operation: <intersect | union
; | only_preferred | only_nonpreferred>,
; keep: <first | all>
;preferred_codec_only=no ; Respond to a SIP invite with the single most
; preferred codec rather than advertising all joint
; codec capabilities. This limits the other side's

@ -0,0 +1,29 @@
"""Add pjsip endpoint ACN options
Revision ID: b80485ff4dd0
Revises: fbb7766f17bc
Create Date: 2020-07-06 08:29:53.974820
"""
# revision identifiers, used by Alembic.
revision = 'b80485ff4dd0'
down_revision = '79290b511e4b'
from alembic import op
import sqlalchemy as sa
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)))
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')

@ -49,6 +49,7 @@
/* Needed for ast_sip_for_each_channel_snapshot struct */
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_endpoints.h"
#include "asterisk/stream.h"
#define PJSIP_MINVERSION(m,n,p) (((m << 24) | (n << 16) | (p << 8)) >= PJ_VERSION_NUM)
@ -802,6 +803,14 @@ struct ast_sip_endpoint_media_configuration {
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 */
struct ast_stream_codec_negotiation_prefs outgoing_offer_codec_prefs;
/*! Codec negotiation prefs for incoming answers */
struct ast_stream_codec_negotiation_prefs incoming_answer_codec_prefs;
/*! Codec negotiation prefs for outgoing answers */
struct ast_stream_codec_negotiation_prefs outgoing_answer_codec_prefs;
};
/*!

@ -102,6 +102,239 @@
<configOption name="allow">
<synopsis>Media Codec(s) to allow</synopsis>
</configOption>
<configOption name="incoming_offer_codec_prefs">
<synopsis>Codec negotiation prefs for incoming offers.</synopsis>
<description>
<para>
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.
The string actually specifies 4 <literal>name:value</literal> pair parameters
separated by commas. Whitespace is ignored and they may be specified in any order.
</para>
<para>
Parameters:
</para>
<enumlist>
<enum name="prefer: &lt; pending | configured &gt;">
<para>
</para>
<enumlist>
<enum name="pending"><para>The codec list from the caller. (default)</para></enum>
<enum name="configured"><para>The codec list from the endpoint.</para></enum>
</enumlist>
</enum>
<enum name="operation : &lt; intersect | only_preferred | only_nonpreferred &gt;">
<para>
</para>
<enumlist>
<enum name="intersect"><para>Only common codecs with the preferred codecs first. (default)</para></enum>
<enum name="only_preferred"><para>Use only the preferred codecs.</para></enum>
<enum name="only_nonpreferred"><para>Use only the non-preferred codecs.</para></enum>
</enumlist>
</enum>
<enum name="keep : &lt; all | first &gt;">
<para>
</para>
<enumlist>
<enum name="all"><para>After the operation, keep all codecs. (default)</para></enum>
<enum name="first"><para>After the operation, keep only the first codec.</para></enum>
</enumlist>
</enum>
<enum name="transcode : &lt; allow | prevent &gt;">
<para>
</para>
<enumlist>
<enum name="allow"><para>Allow transcoding. (default)</para></enum>
<enum name="prevent"><para>Prevent transcoding.</para></enum>
</enumlist>
</enum>
</enumlist>
<para>
</para>
<example>
incoming_offer_codec_prefs = prefer: pending, operation: intersect, keep: all, transcode: allow
</example>
<para>
Prefer the codecs coming from the caller. Use only the ones that are common.
keeping the order of the preferred list. Keep all codecs in the result. Allow transcoding.
</para>
</description>
</configOption>
<configOption name="outgoing_offer_codec_prefs">
<synopsis>Codec negotiation prefs for outgoing offers.</synopsis>
<description>
<para>
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) when sending an SDP offer.
The string actually specifies 4 <literal>name:value</literal> pair parameters
separated by commas. Whitespace is ignored and they may be specified in any order.
</para>
<para>
Parameters:
</para>
<enumlist>
<enum name="prefer: &lt; pending | configured &gt;">
<para>
</para>
<enumlist>
<enum name="pending"><para>The codec list from the core. (default)</para></enum>
<enum name="configured"><para>The codec list from the endpoint.</para></enum>
</enumlist>
</enum>
<enum name="operation : &lt; union | intersect | only_preferred | only_nonpreferred &gt;">
<para>
</para>
<enumlist>
<enum name="union"><para>Merge the lists with the preferred codecs first. (default)</para></enum>
<enum name="intersect"><para>Only common codecs with the preferred codecs first. (default)</para></enum>
<enum name="only_preferred"><para>Use only the preferred codecs.</para></enum>
<enum name="only_nonpreferred"><para>Use only the non-preferred codecs.</para></enum>
</enumlist>
</enum>
<enum name="keep : &lt; all | first &gt;">
<para>
</para>
<enumlist>
<enum name="all"><para>After the operation, keep all codecs. (default)</para></enum>
<enum name="first"><para>After the operation, keep only the first codec.</para></enum>
</enumlist>
</enum>
<enum name="transcode : &lt; allow | prevent &gt;">
<para>
</para>
<enumlist>
<enum name="allow"><para>Allow transcoding. (default)</para></enum>
<enum name="prevent"><para>Prevent transcoding.</para></enum>
</enumlist>
</enum>
</enumlist>
<para>
</para>
<example>
outgoing_offer_codec_prefs = 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
keeping the order of the preferred list. Keep only the first one. No transcoding allowed.
</para>
</description>
</configOption>
<configOption name="incoming_answer_codec_prefs">
<synopsis>Codec negotiation prefs for incoming answers.</synopsis>
<description>
<para>
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 answer.
The string actually specifies 4 <literal>name:value</literal> pair parameters
separated by commas. Whitespace is ignored and they may be specified in any order.
</para>
<para>
Parameters:
</para>
<enumlist>
<enum name="prefer: &lt; pending | configured &gt;">
<para>
</para>
<enumlist>
<enum name="pending"><para>The codec list in the received SDP answer. (default)</para></enum>
<enum name="configured"><para>The codec list from the endpoint.</para></enum>
</enumlist>
</enum>
<enum name="operation : &lt; union | intersect | only_preferred | only_nonpreferred &gt;">
<para>
</para>
<enumlist>
<enum name="union"><para>Merge the lists with the preferred codecs first.</para></enum>
<enum name="intersect"><para>Only common codecs with the preferred codecs first. (default)</para></enum>
<enum name="only_preferred"><para>Use only the preferred codecs.</para></enum>
<enum name="only_nonpreferred"><para>Use only the non-preferred codecs.</para></enum>
</enumlist>
</enum>
<enum name="keep : &lt; all | first &gt;">
<para>
</para>
<enumlist>
<enum name="all"><para>After the operation, keep all codecs. (default)</para></enum>
<enum name="first"><para>After the operation, keep only the first codec.</para></enum>
</enumlist>
</enum>
<enum name="transcode : &lt; allow | prevent &gt;">
<para>
The transcode parameter is ignored when processing answers.
</para>
</enum>
</enumlist>
<para>
</para>
<example>
incoming_answer_codec_prefs = keep: first
</example>
<para>
Use the defaults but keep oinly the first codec.
</para>
</description>
</configOption>
<configOption name="outgoing_answer_codec_prefs">
<synopsis>Codec negotiation prefs for outgoing answers.</synopsis>
<description>
<para>
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.
The string actually specifies 4 <literal>name:value</literal> pair parameters
separated by commas. Whitespace is ignored and they may be specified in any order.
</para>
<para>
Parameters:
</para>
<enumlist>
<enum name="prefer: &lt; pending | configured &gt;">
<para>
</para>
<enumlist>
<enum name="pending"><para>The codec list that came from the core. (default)</para></enum>
<enum name="configured"><para>The codec list from the endpoint.</para></enum>
</enumlist>
</enum>
<enum name="operation : &lt; union | intersect | only_preferred | only_nonpreferred &gt;">
<para>
</para>
<enumlist>
<enum name="union"><para>Merge the lists with the preferred codecs first.</para></enum>
<enum name="intersect"><para>Only common codecs with the preferred codecs first. (default)</para></enum>
<enum name="only_preferred"><para>Use only the preferred codecs.</para></enum>
<enum name="only_nonpreferred"><para>Use only the non-preferred codecs.</para></enum>
</enumlist>
</enum>
<enum name="keep : &lt; all | first &gt;">
<para>
</para>
<enumlist>
<enum name="all"><para>After the operation, keep all codecs. (default)</para></enum>
<enum name="first"><para>After the operation, keep only the first codec.</para></enum>
</enumlist>
</enum>
<enum name="transcode : &lt; allow | prevent &gt;">
<para>
The transcode parameter is ignored when processing answers.
</para>
</enum>
</enumlist>
<para>
</para>
<example>
incoming_answer_codec_prefs = keep: first
</example>
<para>
Use the defaults but keep oinly the first codec.
</para>
</description>
</configOption>
<configOption name="allow_overlap" default="yes">
<synopsis>Enable RFC3578 overlap dialing support.</synopsis>
</configOption>

@ -1166,6 +1166,109 @@ static int outgoing_call_offer_pref_to_str(const void *obj, const intptr_t *args
return 0;
}
static int codec_prefs_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
struct ast_stream_codec_negotiation_prefs prefs;
struct ast_str *error_message = ast_str_create(128);
enum ast_stream_codec_negotiation_prefs_prefer_values default_prefer;
enum ast_stream_codec_negotiation_prefs_operation_values default_operation;
int res = 0;
res = ast_stream_codec_prefs_parse(var->value, &prefs, &error_message);
if (res < 0) {
ast_log(LOG_ERROR, "Endpoint '%s': %s for option '%s'\n",
ast_sorcery_object_get_id(endpoint), ast_str_buffer(error_message), var->name);
ast_free(error_message);
return -1;
}
ast_free(error_message);
if (strcmp(var->name, "incoming_offer_codec_prefs") == 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),
ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_OPERATION),
ast_stream_codec_operation_to_str(CODEC_NEGOTIATION_OPERATION_UNION),
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;
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;
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;
default_prefer = CODEC_NEGOTIATION_PREFER_PENDING;
default_operation = CODEC_NEGOTIATION_OPERATION_INTERSECT;
}
if (prefs.prefer == CODEC_NEGOTIATION_PREFER_UNSPECIFIED) {
prefs.prefer = default_prefer;
}
if (prefs.operation == CODEC_NEGOTIATION_OPERATION_UNSPECIFIED) {
prefs.operation = default_operation;
}
if (prefs.keep == CODEC_NEGOTIATION_KEEP_UNSPECIFIED) {
prefs.keep = CODEC_NEGOTIATION_KEEP_ALL;
}
if (prefs.transcode == CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED) {
prefs.transcode = CODEC_NEGOTIATION_TRANSCODE_ALLOW;
}
return 0;
}
static int codec_prefs_to_str(const struct ast_stream_codec_negotiation_prefs *prefs,
const void *obj, const intptr_t *args, char **buf)
{
struct ast_str *codecs = ast_str_create(AST_STREAM_MAX_CODEC_PREFS_LENGTH);
if (!codecs) {
return -1;
}
*buf = ast_strdup(ast_stream_codec_prefs_to_str(prefs, &codecs));
ast_free(codecs);
return 0;
}
static int incoming_offer_codec_prefs_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
return codec_prefs_to_str(&endpoint->media.incoming_offer_codec_prefs, obj, args, buf);
}
static int outgoing_offer_codec_prefs_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
return codec_prefs_to_str(&endpoint->media.outgoing_offer_codec_prefs, obj, args, buf);
}
static int incoming_answer_codec_prefs_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
return codec_prefs_to_str(&endpoint->media.incoming_answer_codec_prefs, obj, args, buf);
}
static int outgoing_answer_codec_prefs_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
return codec_prefs_to_str(&endpoint->media.outgoing_answer_codec_prefs, obj, args, buf);
}
static void *sip_nat_hook_alloc(const char *name)
{
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
@ -2025,6 +2128,18 @@ int ast_res_pjsip_initialize_configuration(void)
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",
"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",
"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",
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",
codec_prefs_handler, outgoing_answer_codec_prefs_to_str, NULL, 0, 0);
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");

Loading…
Cancel
Save