detection as the ConfbridgeTalking event, so bridges must be configured
with "talk_detection_events=yes" for this flag to have meaning.
+res_pjsip
+------------------
+ * Two new options have been added to the system and endpoint objects to
+ control whether, on outbound calls, Asterisk will accept updated SDP answers
+ during the initial INVITE transaction when 100rel is not in effect.
+ This usually happens when the INVITE is forked to multiple UASs and more
+ than one sends an SDP answer or when a single UAS needs to change a media
+ port to switch from custom ringback to the actual media destination.
+
+ The 'follow_early_media_forked' option sets whether Asterisk will accept
+ the updated SDP when the To tag on the subsequent response is different than
+ that on the the previous response. This usually occurs in the forked INVITE
+ scenario. The default value is "yes" which is the current behavior.
+
+ The 'accept_multiple_sdp_answers' flag sets whether Asterisk will accept the
+ updated SDP when the To tag on the subsequent response is the same as that
+ on the previous response. This can occur when a UAS needs to switch media
+ ports from custom ringback to the final media path. The default value is
+ "no" which is the current behavior.
+
+ These options have to be enabled system-wide in the system config section
+ of pjsip.conf as well as on individual endpoints that require the
+ functionality.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.20.0 to Asterisk 13.21.0 ----------
------------------------------------------------------------------------------
; this mailbox will be used when notifying other modules
; of MWI status changes. If not set, incoming MWI
; NOTIFYs are ignored.
+;follow_early_media_fork = ; On outgoing calls, if the UAS responds with
+ ; different SDP attributes on subsequent 18X or 2XX
+ ; responses (such as a port update) AND the To tag
+ ; on the subsequent response is different than that
+ ; on the previous one, follow it. This usually
+ ; happens when the INVITE is forked to multiple UASs
+ ; and more than 1 sends an SDP answer.
+ ; This option must also be enabled in the system
+ ; section.
+ ; (default: yes)
+;accept_multiple_sdp_answers =
+ ; On outgoing calls, if the UAS responds with
+ ; different SDP attributes on non-100rel 18X or 2XX
+ ; responses (such as a port update) AND the To tag on
+ ; the subsequent response is the same as that on the
+ ; previous one, process it. This can happen when the
+ ; UAS needs to change ports for some reason such as
+ ; using a separate port for custom ringback.
+ ; This option must also be enabled in the system
+ ; section.
+ ; (default: no)
;==========================AUTH SECTION OPTIONS=========================
;[auth]
; Disabling this option has been known to cause interoperability
; issues, so disable at your own risk.
; (default: "yes")
+;follow_early_media_fork = ; On outgoing calls, if the UAS responds with
+ ; different SDP attributes on subsequent 18X or 2XX
+ ; responses (such as a port update) AND the To tag
+ ; on the subsequent response is different than that
+ ; on the previous one, follow it. This usually
+ ; happens when the INVITE is forked to multiple UASs
+ ; and more than 1 sends an SDP answer.
+ ; This option must also be enabled on endpoints that
+ ; require this functionality.
+ ; (default: yes)
+;accept_multiple_sdp_answers =
+ ; On outgoing calls, if the UAS responds with
+ ; different SDP attributes on non-100rel 18X or 2XX
+ ; responses (such as a port update) AND the To tag on
+ ; the subsequent response is the same as that on the
+ ; previous one, process it. This can happen when the
+ ; UAS needs to change ports for some reason such as
+ ; using a separate port for custom ringback.
+ ; This option must also be enabled on endpoints that
+ ; require this functionality.
+ ; (default: no)
;type= ; Must be of type system (default: "")
;==========================GLOBAL SECTION OPTIONS=========================
POPT_DIR
POPT_INCLUDE
POPT_LIB
+PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_LIB
PBX_PJSIP_TSX_LAYER_FIND_TSX2
PJSIP_TSX_LAYER_FIND_TSX2_DIR
PJSIP_TSX_LAYER_FIND_TSX2_INCLUDE
$as_echo "#define HAVE_PJSIP_TSX_LAYER_FIND_TSX2 1" >>confdefs.h
+$as_echo "#define HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS 1" >>confdefs.h
+
+
+
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DESCRIP="PJSIP INVITE Accept Multiple SDP Answers"
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_OPTION=pjsip
+PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS=0
+
+
+
+
+
+
fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+ if test "x${PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS}" != "x1" -a "${USE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS}" != "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if \"pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;\" compiles using pjsip.h" >&5
+$as_echo_n "checking if \"pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;\" compiles using pjsip.h... " >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR}" != "x"; then
+ PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE="-I${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS_INCLUDE}"
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ #include <pjsip.h>
+int
+main ()
+{
+ pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ PBX_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS=1
+
+$as_echo "#define HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS 1" >>confdefs.h
+
+
+
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_SESSION_REF], [PJSIP INVITE Session Reference Count support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_AUTH_CLT_DEINIT], [pjsip_auth_clt_deinit support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip_tsx_layer_find_tsx2 support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [PJSIP INVITE Accept Multiple SDP Answers], [PJPROJECT], [pjsip])
fi
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
LIBS="${LIBS} ${PJPROJECT_LIB}"
AST_C_COMPILE_CHECK([PJSIP_TLS_TRANSPORT_PROTO], [struct pjsip_tls_setting setting; int proto; proto = setting.proto;], [pjsip.h])
+ AST_C_COMPILE_CHECK([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;], [pjsip.h])
LIBS="${saved_libs}"
CPPFLAGS="${saved_cppflags}"
--- /dev/null
+"""Add early media options
+
+Revision ID: 0be05c3a8225
+Revises: d3e4284f8707
+Create Date: 2018-06-18 17:26:16.737692
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '0be05c3a8225'
+down_revision = 'd3e4284f8707'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+ op.add_column('ps_systems', sa.Column('follow_early_media_fork', yesno_values))
+ op.add_column('ps_systems', sa.Column('accept_multiple_sdp_answers', yesno_values))
+ op.add_column('ps_endpoints', sa.Column('follow_early_media_fork', yesno_values))
+ op.add_column('ps_endpoints', sa.Column('accept_multiple_sdp_answers', yesno_values))
+
+def downgrade():
+ if op.get_context().bind.dialect.name == 'mssql':
+ op.drop_constraint('ck_ps_systems_follow_early_media_fork_yesno_values','ps_systems')
+ op.drop_constraint('ck_ps_systems_accept_multiple_sdp_answers_yesno_values','ps_systems')
+ op.drop_constraint('ck_ps_endpoints_follow_early_media_fork_yesno_values','ps_endpoints')
+ op.drop_constraint('ck_ps_endpoints_accept_multiple_sdp_answers_yesno_values','ps_endpoints')
+ op.drop_column('ps_systems', 'follow_early_media_fork')
+ op.drop_column('ps_systems', 'accept_multiple_sdp_answers')
+ op.drop_column('ps_endpoints', 'follow_early_media_fork')
+ op.drop_column('ps_endpoints', 'accept_multiple_sdp_answers')
/* Define to 1 if PJPROJECT has the pjsip_get_dest_info support feature. */
#undef HAVE_PJSIP_GET_DEST_INFO
+/* Define if your system has the PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+ headers. */
+#undef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+
/* Define to 1 if PJPROJECT has the PJSIP INVITE Session Reference Count
support feature. */
#undef HAVE_PJSIP_INV_SESSION_REF
unsigned int notify_early_inuse_ringing;
/*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
+ /*! Follow forked media with a different To tag */
+ unsigned int follow_early_media_fork;
+ /*! Accept updated SDPs on non-100rel 18X and 2XX responses with the same To tag */
+ unsigned int accept_multiple_sdp_answers;
};
/*! URI parameter for symmetric transport */
changes. If not set, incoming MWI NOTIFYs are ignored.
</para></description>
</configOption>
+ <configOption name="follow_early_media_fork">
+ <synopsis>Follow SDP forked media when To tag is different</synopsis>
+ <description><para>
+ On outgoing calls, if the UAS responds with different SDP attributes
+ on subsequent 18X or 2XX responses (such as a port update) AND the
+ To tag on the subsequent response is different than that on the previous
+ one, follow it. This usually happens when the INVITE is forked to multiple
+ UASs and more than one sends an SDP answer.
+ </para>
+ <note><para>
+ This option must also be enabled in the <literal>system</literal>
+ section for it to take effect here.
+ </para></note>
+ </description>
+ </configOption>
+ <configOption name="accept_multiple_sdp_answers" default="no">
+ <synopsis>Accept multiple SDP answers on non-100rel responses</synopsis>
+ <description><para>
+ On outgoing calls, if the UAS responds with different SDP attributes
+ on non-100rel 18X or 2XX responses (such as a port update) AND the
+ To tag on the subsequent response is the same as that on the previous one,
+ process the updated SDP. This can happen when the UAS needs to change ports
+ for some reason such as using a separate port for custom ringback.
+ </para>
+ <note><para>
+ This option must also be enabled in the <literal>system</literal>
+ section for it to take effect here.
+ </para></note>
+ </description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
request is too large. See RFC 3261 section 18.1.1.
</para></description>
</configOption>
+ <configOption name="follow_early_media_fork">
+ <synopsis>Follow SDP forked media when To tag is different</synopsis>
+ <description><para>
+ On outgoing calls, if the UAS responds with different SDP attributes
+ on subsequent 18X or 2XX responses (such as a port update) AND the
+ To tag on the subsequent response is different than that on the previous
+ one, follow it.
+ </para>
+ <note><para>
+ This option must also be enabled on endpoints that require
+ this functionality.
+ </para></note>
+ </description>
+ </configOption>
+ <configOption name="accept_multiple_sdp_answers">
+ <synopsis>Follow SDP forked media when To tag is the same</synopsis>
+ <description><para>
+ On outgoing calls, if the UAS responds with different SDP attributes
+ on non-100rel 18X or 2XX responses (such as a port update) AND the
+ To tag on the subsequent response is the same as that on the previous one,
+ process the updated SDP.
+ </para>
+ <note><para>
+ This option must also be enabled on endpoints that require
+ this functionality.
+ </para></note>
+ </description>
+ </configOption>
<configOption name="type">
<synopsis>Must be of type 'system'.</synopsis>
</configOption>
} threadpool;
/*! Nonzero to disable switching from UDP to TCP transport */
unsigned int disable_tcp_switch;
+ /*!
+ * Although early media is enabled in pjproject by default, it's only
+ * enabled when the To tags are different. These options allow turning
+ * on or off the feature for different tags and same tags.
+ */
+ unsigned int follow_early_media_fork;
+ unsigned int accept_multiple_sdp_answers;
};
static struct ast_threadpool_options sip_threadpool_options = {
pjsip_cfg()->tsx.t1 = system->timert1;
pjsip_cfg()->tsx.td = system->timerb;
+ pjsip_cfg()->endpt.follow_early_media_fork = system->follow_early_media_fork;
+#ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+ pjsip_cfg()->endpt.accept_multiple_sdp_answers = system->accept_multiple_sdp_answers;
+#else
+ if (system->accept_multiple_sdp_answers) {
+ ast_log(LOG_WARNING,
+ "The accept_multiple_sdp_answers flag is not supported in this version of pjproject. Ignoring\n");
+ }
+#endif
+
if (system->compactheaders) {
extern pj_bool_t pjsip_use_compact_form;
OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "yes",
OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
+ ast_sorcery_object_field_register(system_sorcery, "system", "follow_early_media_fork", "yes",
+ OPT_BOOL_T, 1, FLDSET(struct system_config, follow_early_media_fork));
+ ast_sorcery_object_field_register(system_sorcery, "system", "accept_multiple_sdp_answers", "no",
+ OPT_BOOL_T, 1, FLDSET(struct system_config, accept_multiple_sdp_answers));
ast_sorcery_load(system_sorcery);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "refer_blind_progress", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, refer_blind_progress));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "incoming_mwi_mailbox", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, incoming_mwi_mailbox));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "follow_early_media_fork", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, follow_early_media_fork));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accept_multiple_sdp_answers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, accept_multiple_sdp_answers));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
return;
}
+ if (session->endpoint) {
+ int bail = 0;
+
+ /*
+ * If following_fork is set, then this is probably the result of a
+ * forked INVITE and SDP asnwers coming from the different fork UAS
+ * destinations. In this case updated_sdp_answer will also be set.
+ *
+ * If only updated_sdp_answer is set, then this is the non-forking
+ * scenario where the same UAS just needs to change something like
+ * the media port.
+ */
+
+ if (inv->following_fork) {
+ if (session->endpoint->follow_early_media_fork) {
+ ast_debug(3, "Following early media fork with different To tags\n");
+ } else {
+ ast_debug(3, "Not following early media fork with different To tags\n");
+ bail = 1;
+ }
+ }
+#ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
+ else if (inv->updated_sdp_answer) {
+ if (session->endpoint->accept_multiple_sdp_answers) {
+ ast_debug(3, "Accepting updated SDP with same To tag\n");
+ } else {
+ ast_debug(3, "Ignoring updated SDP answer with same To tag\n");
+ bail = 1;
+ }
+ }
+#endif
+ if (bail) {
+ return;
+ }
+ }
+
if ((status != PJ_SUCCESS) || (pjmedia_sdp_neg_get_active_local(inv->neg, &local) != PJ_SUCCESS) ||
(pjmedia_sdp_neg_get_active_remote(inv->neg, &remote) != PJ_SUCCESS)) {
ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
AC_DEFINE([HAVE_PJSIP_INV_SESSION_REF], 1, [Define if your system has PJSIP_INV_SESSION_REF])
AC_DEFINE([HAVE_PJSIP_AUTH_CLT_DEINIT], 1, [Define if your system has pjsip_auth_clt_deinit declared.])
AC_DEFINE([HAVE_PJSIP_TSX_LAYER_FIND_TSX2], 1, [Define if your system has pjsip_tsx_layer_find_tsx2 declared.])
+ AC_DEFINE([HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], 1, [Define if your system has HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS declared.])
AC_SUBST([PJPROJECT_BUNDLED])
AC_SUBST([PJPROJECT_DIR])
--- /dev/null
+From 13e20772cd3c8735a6b78e30391a33f3eba4c023 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Fri, 22 Jun 2018 09:33:34 -0600
+Subject: [PATCH] sip_inv: Add option to accept updated SDP on same To tag
+
+Currently, setting pjsip_cfg()->endpt.follow_early_media_fork allows
+sip_inv to process media updates when the To tag is different. There
+are some cases where media updates need to be processed when the tags
+are the same. Since removing the requirement for different tags would
+change default behavior, a new option "accept_multiple_sdp_answers"
+has been added along with a new pjsip_inv_session flag
+"updated_sdp_answer" to indicate under which condition we're
+updating.
+
+The logic was also updated to more closely follow RFC6337 in that
+if 100rel is efffect, do not accept updated SDPs.
+
+See
+https://tools.ietf.org/html/rfc6337#section-3.1.1
+for more information.
+---
+ pjsip/include/pjsip-ua/sip_inv.h | 2 ++
+ pjsip/include/pjsip/sip_config.h | 25 ++++++++++++++++
+ pjsip/src/pjsip-ua/sip_inv.c | 62 +++++++++++++++++++++++++---------------
+ pjsip/src/pjsip/sip_config.c | 3 +-
+ 4 files changed, 68 insertions(+), 24 deletions(-)
+
+diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
+index 1bb7b8adc..77ef070c3 100644
+--- a/pjsip/include/pjsip-ua/sip_inv.h
++++ b/pjsip/include/pjsip-ua/sip_inv.h
+@@ -442,6 +442,8 @@ struct pjsip_inv_session
+ pj_bool_t following_fork; /**< Internal, following
+ forked media? */
+ pj_atomic_t *ref_cnt; /**< Reference counter. */
++ pj_bool_t updated_sdp_answer; /**< SDP answer just been
++ updated? */
+ };
+
+
+diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
+index b3a9468e2..b7cf6feed 100644
+--- a/pjsip/include/pjsip/sip_config.h
++++ b/pjsip/include/pjsip/sip_config.h
+@@ -157,6 +157,17 @@ typedef struct pjsip_cfg_t
+ */
+ pj_bool_t disable_secure_dlg_check;
+
++ /**
++ * Accept multiple SDP answers on non-reliable 18X responses and the 2XX
++ * response when they are all received from the same source (same To tag).
++ *
++ * See also:
++ * https://tools.ietf.org/html/rfc6337#section-3.1.1
++ *
++ * Default is PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS.
++ */
++ pj_bool_t accept_multiple_sdp_answers;
++
+ } endpt;
+
+ /** Transaction layer settings. */
+@@ -402,6 +413,20 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
+ #endif
+
+
++/**
++ * Accept multiple SDP answers on non-reliable 18X responses and the 2XX
++ * response when they are all received from the same source (same To tag).
++ *
++ * This option can also be controlled at run-time by the
++ * \a accept_multiple_sdp_answers setting in pjsip_cfg_t.
++ *
++ * Default is PJ_FALSE.
++ */
++#ifndef PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS
++# define PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS PJ_TRUE
++#endif
++
++
+ /**
+ * Specify whether "alias" param should be added to the Via header
+ * in any outgoing request with connection oriented transport.
+diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
+index c9686a088..c22726bad 100644
+--- a/pjsip/src/pjsip-ua/sip_inv.c
++++ b/pjsip/src/pjsip-ua/sip_inv.c
+@@ -162,6 +162,7 @@ struct tsx_inv_data
+ pj_bool_t retrying; /* Resend (e.g. due to 401/407) */
+ pj_str_t done_tag; /* To tag in RX response with answer */
+ pj_bool_t done_early;/* Negotiation was done for early med? */
++ pj_bool_t done_early_rel;/* Early med was realiable? */
+ pj_bool_t has_sdp; /* Message with SDP? */
+ };
+
+@@ -2000,18 +2001,20 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+
+ /* Initialize info that we are following forked media */
+ inv->following_fork = PJ_FALSE;
++ inv->updated_sdp_answer = PJ_FALSE;
+
+ /* MUST NOT do multiple SDP offer/answer in a single transaction,
+- * EXCEPT if:
+- * - this is an initial UAC INVITE transaction (i.e. not re-INVITE), and
+- * - the previous negotiation was done on an early media (18x) and
+- * this response is a final/2xx response, and
+- * - the 2xx response has different To tag than the 18x response
+- * (i.e. the request has forked).
++ * EXCEPT previous nego was in 18x (early media) and any of the following
++ * condition is met:
++ * - Non-forking scenario:
++ * - 'accept_multiple_sdp_answers' is set, and
++ * - previous early response was not reliable (rfc6337 section 3.1.1).
++ * - Forking scenario:
++ * - This response has different To tag than the previous response, and
++ * - This response is 18x/2xx (early or final). If this is 18x,
++ * only do multiple SDP nego if 'follow_early_media_fork' is set.
+ *
+- * The exception above is to add a rudimentary support for early media
+- * forking (sample case: custom ringback). See this ticket for more
+- * info: http://trac.pjsip.org/repos/ticket/657
++ * See also ticket #657, #1644, #1764 for more info.
+ */
+ if (tsx_inv_data->sdp_done) {
+ pj_str_t res_tag;
+@@ -2020,21 +2023,29 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+ res_tag = rdata->msg_info.to->tag;
+ st_code = rdata->msg_info.msg->line.status.code;
+
+- /* Allow final/early response after SDP has been negotiated in early
+- * media, IF this response is a final/early response with different
+- * tag.
+- * See ticket #1644 and #1764 for forked early media case.
+- */
+- if (tsx->role == PJSIP_ROLE_UAC &&
+- (st_code/100 == 2 ||
+- (st_code/10 == 18 /* st_code == 18x */
+- && pjsip_cfg()->endpt.follow_early_media_fork)) &&
+- tsx_inv_data->done_early &&
+- pj_stricmp(&tsx_inv_data->done_tag, &res_tag))
++ if (tsx->role == PJSIP_ROLE_UAC && tsx_inv_data->done_early &&
++ (
++ /* Non-forking scenario */
++ (
++ !tsx_inv_data->done_early_rel &&
++ (st_code/100 == 2 || st_code/10 == 18) &&
++ pjsip_cfg()->endpt.accept_multiple_sdp_answers &&
++ !pj_stricmp(&tsx_inv_data->done_tag, &res_tag)
++ )
++ ||
++ /* Forking scenario */
++ (
++ (st_code/100 == 2 ||
++ (st_code/10 == 18 &&
++ pjsip_cfg()->endpt.follow_early_media_fork)) &&
++ pj_stricmp(&tsx_inv_data->done_tag, &res_tag)
++ )
++ )
++ )
+ {
+ const pjmedia_sdp_session *reoffer_sdp = NULL;
+
+- PJ_LOG(4,(inv->obj_name, "Received forked %s response "
++ PJ_LOG(4,(inv->obj_name, "Received %s response "
+ "after SDP negotiation has been done in early "
+ "media. Renegotiating SDP..",
+ (st_code/10==18? "early" : "final" )));
+@@ -2054,7 +2065,9 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+ return status;
+ }
+
+- inv->following_fork = PJ_TRUE;
++ inv->following_fork = !!pj_stricmp(&tsx_inv_data->done_tag,
++ &res_tag);
++ inv->updated_sdp_answer = PJ_TRUE;
+
+ } else {
+
+@@ -2135,6 +2148,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+ PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
+ {
+ int status_code;
++ pjsip_msg *msg = rdata->msg_info.msg;
+
+ /* This is an answer.
+ * Process and negotiate remote answer.
+@@ -2161,8 +2175,10 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+ */
+
+ tsx_inv_data->sdp_done = 1;
+- status_code = rdata->msg_info.msg->line.status.code;
++ status_code = msg->line.status.code;
+ tsx_inv_data->done_early = (status_code/100==1);
++ tsx_inv_data->done_early_rel = tsx_inv_data->done_early &&
++ pjsip_100rel_is_reliable(rdata);
+ pj_strdup(tsx->pool, &tsx_inv_data->done_tag,
+ &rdata->msg_info.to->tag);
+
+diff --git a/pjsip/src/pjsip/sip_config.c b/pjsip/src/pjsip/sip_config.c
+index 3576f351e..316824a00 100644
+--- a/pjsip/src/pjsip/sip_config.c
++++ b/pjsip/src/pjsip/sip_config.c
+@@ -34,7 +34,8 @@ pjsip_cfg_t pjsip_sip_cfg_var =
+ PJSIP_FOLLOW_EARLY_MEDIA_FORK,
+ PJSIP_REQ_HAS_VIA_ALIAS,
+ PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE,
+- 0
++ 0,
++ PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS
+ },
+
+ /* Transaction settings */
+--
+2.14.4
+