]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
ACN: res_pjsip endpoint options
authorGeorge Joseph <gjoseph@digium.com>
Mon, 6 Jul 2020 14:56:44 +0000 (08:56 -0600)
committerGeorge Joseph <gjoseph@digium.com>
Wed, 8 Jul 2020 14:03:58 +0000 (09:03 -0500)
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
contrib/ast-db-manage/config/versions/b80485ff4dd0_add_pjsip_endpoint_acn_options.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
res/res_pjsip.c
res/res_pjsip/pjsip_configuration.c

index a559dfafaacb914f48168cb9fa1c6e39a64cebd8..cdb585d2a0be1866ad46deefa6019eab4d653dd1 100644 (file)
                            ; 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
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 (file)
index 0000000..241185a
--- /dev/null
@@ -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')
index 299310312bd82dc327abaedeccd700671b86f9cc..0ca29ff5d81a0d4ba16386d5604d6794a8ec274d 100644 (file)
@@ -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;
 };
 
 /*!
index 32907e307cdeaddcf56b0836e0b0030aacddf69c..847a14c886463c510da1fbeb43fc4e702f119b49 100644 (file)
                                <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>
index f95ee9e2409eff6bf9f2fc5dd4d0685fe922903d..e3eab8ad0ed515aa9b3a6b68d367e9c3c5072218 100644 (file)
@@ -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");