From 2d22e342060d4541576f82f47d80b7a1f88c29e8 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Mon, 6 Jul 2020 08:56:44 -0600 Subject: [PATCH] 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 --- configs/samples/pjsip.conf.sample | 48 ++++ ...85ff4dd0_add_pjsip_endpoint_acn_options.py | 29 +++ include/asterisk/res_pjsip.h | 9 + res/res_pjsip.c | 233 ++++++++++++++++++ res/res_pjsip/pjsip_configuration.c | 115 +++++++++ 5 files changed, 434 insertions(+) create mode 100644 contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index a559dfafaa..cdb585d2a0 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -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: , + ; operation: , + ; keep: , + ; transcode: +;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: , + ; operation: , + ; keep: , + ; transcode: +;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: , + ; operation: , + ; keep: +;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: , + ; operation: , + ; keep: ;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 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 new file mode 100644 index 0000000000..241185a6a8 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py @@ -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') diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 299310312b..0ca29ff5d8 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -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; }; /*! diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 32907e307c..847a14c886 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -102,6 +102,239 @@ Media Codec(s) to allow + + Codec negotiation prefs for incoming offers. + + + 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. + + + + Parameters: + + + + + + + The codec list from the caller. (default) + The codec list from the endpoint. + + + + + + + Only common codecs with the preferred codecs first. (default) + Use only the preferred codecs. + Use only the non-preferred codecs. + + + + + + + After the operation, keep all codecs. (default) + After the operation, keep only the first codec. + + + + + + + Allow transcoding. (default) + Prevent transcoding. + + + + + + + incoming_offer_codec_prefs = prefer: pending, operation: intersect, keep: all, transcode: allow + + + 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. + + + + + Codec negotiation prefs for outgoing offers. + + + 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. + + + + Parameters: + + + + + + + The codec list from the core. (default) + The codec list from the endpoint. + + + + + + + Merge the lists with the preferred codecs first. (default) + Only common codecs with the preferred codecs first. (default) + Use only the preferred codecs. + Use only the non-preferred codecs. + + + + + + + After the operation, keep all codecs. (default) + After the operation, keep only the first codec. + + + + + + + Allow transcoding. (default) + Prevent transcoding. + + + + + + + outgoing_offer_codec_prefs = prefer: configured, operation: union, keep: first, transcode: prevent + + + 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. + + + + + Codec negotiation prefs for incoming answers. + + + 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. + + + Parameters: + + + + + + + The codec list in the received SDP answer. (default) + The codec list from the endpoint. + + + + + + + Merge the lists with the preferred codecs first. + Only common codecs with the preferred codecs first. (default) + Use only the preferred codecs. + Use only the non-preferred codecs. + + + + + + + After the operation, keep all codecs. (default) + After the operation, keep only the first codec. + + + + + The transcode parameter is ignored when processing answers. + + + + + + + incoming_answer_codec_prefs = keep: first + + + Use the defaults but keep oinly the first codec. + + + + + Codec negotiation prefs for outgoing answers. + + + 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. + + + Parameters: + + + + + + + The codec list that came from the core. (default) + The codec list from the endpoint. + + + + + + + Merge the lists with the preferred codecs first. + Only common codecs with the preferred codecs first. (default) + Use only the preferred codecs. + Use only the non-preferred codecs. + + + + + + + After the operation, keep all codecs. (default) + After the operation, keep only the first codec. + + + + + The transcode parameter is ignored when processing answers. + + + + + + + incoming_answer_codec_prefs = keep: first + + + Use the defaults but keep oinly the first codec. + + + Enable RFC3578 overlap dialing support. diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index f95ee9e240..e3eab8ad0e 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -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"); -- 2.47.2