<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13688">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">RTP/ICE: Send on first valid pair.<br><br>When handling ICE negotiations, it's possible that there can be a delay<br>between STUN binding requests which in turn will cause a delay in ICE<br>completion, preventing media from flowing. It should be possible to send<br>media when there is at least one valid pair, preventing this scenario<br>from occurring.<br><br>A change was added to PJPROJECT that adds an optional callback<br>(on_valid_pair) that will be called when the first valid pair is found<br>during ICE negotiation. Asterisk uses this to start the DTLS handshake,<br>allowing media to flow. It will only be called once, either on the first<br>valid pair, or when ICE negotiation is complete.<br><br>ASTERISK-28716<br><br>Change-Id: Ia7b68c34f06d2a1d91c5ed51627b66fd0363d867<br>---<br>M configure<br>M configure.ac<br>M include/asterisk/autoconfig.h.in<br>M res/res_rtp_asterisk.c<br>M third-party/pjproject/configure.m4<br>A third-party/pjproject/patches/0040-ICE-Add-callback-for-finding-valid-pair.patch<br>6 files changed, 178 insertions(+), 4 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure b/configure</span><br><span>index e182dbc..0d542bf 100755</span><br><span>--- a/configure</span><br><span>+++ b/configure</span><br><span>@@ -9505,6 +9505,9 @@</span><br><span> $as_echo "#define HAVE_PJSIP_OAUTH_AUTHENTICATION 1" >>confdefs.h</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> </span><br><span> </span><br><span>@@ -16397,8 +16400,6 @@</span><br><span> if (*(data + i) != *(data3 + i))</span><br><span> return 14;</span><br><span> close (fd);</span><br><span style="color: hsl(0, 100%, 40%);">- free (data);</span><br><span style="color: hsl(0, 100%, 40%);">- free (data3);</span><br><span> return 0;</span><br><span> }</span><br><span> _ACEOF</span><br><span>@@ -25850,6 +25851,43 @@</span><br><span> CPPFLAGS="${saved_cppflags}"</span><br><span> fi</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjproject on_valid_pair callback" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo_n "checking for pjproject on_valid_pair callback... " >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext</span><br><span style="color: hsl(120, 100%, 40%);">+/* end confdefs.h. */</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjsip_ua.h></span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjnath.h></span><br><span style="color: hsl(120, 100%, 40%);">+ void on_valid_pair(pj_ice_sess *ice) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ void on_ice_complete(pj_ice_sess *ice, pj_status_t status) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ void on_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t on_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) {}</span><br><span style="color: hsl(120, 100%, 40%);">+int</span><br><span style="color: hsl(120, 100%, 40%);">+main ()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+pj_ice_sess_cb ice_sess_cb = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_valid_pair = on_valid_pair,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_ice_complete = on_ice_complete,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_rx_data = on_rx_data,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_tx_pkt = on_tx_pkt,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ ;</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+_ACEOF</span><br><span style="color: hsl(120, 100%, 40%);">+if ac_fn_c_try_link "$LINENO"; then :</span><br><span style="color: hsl(120, 100%, 40%);">+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "yes" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+else</span><br><span style="color: hsl(120, 100%, 40%);">+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "no" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+fi</span><br><span style="color: hsl(120, 100%, 40%);">+rm -f core conftest.err conftest.$ac_objext \</span><br><span style="color: hsl(120, 100%, 40%);">+ conftest$ac_exeext conftest.$ac_ext</span><br><span> LIBS="${saved_libs}"</span><br><span> CPPFLAGS="${saved_cppflags}"</span><br><span> </span><br><span>diff --git a/configure.ac b/configure.ac</span><br><span>index ed26d69..0a6852a 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -2436,6 +2436,26 @@</span><br><span> AST_C_COMPILE_CHECK([PJSIP_ENDPOINT_COMPACT_FORM], [pjsip_cfg()->endpt.use_compact_form = PJ_TRUE;], [pjsip.h])</span><br><span> AST_C_COMPILE_CHECK([PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], [struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;], [pjsip.h])</span><br><span> AST_C_COMPILE_CHECK([PJSIP_OAUTH_AUTHENTICATION], [struct pjsip_oauth_credential credential;], [pjsip.h])</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_MSG_CHECKING(for pjproject on_valid_pair callback)</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_LINK_IFELSE(</span><br><span style="color: hsl(120, 100%, 40%);">+ [AC_LANG_PROGRAM(</span><br><span style="color: hsl(120, 100%, 40%);">+ [#include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjsip_ua.h></span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjnath.h></span><br><span style="color: hsl(120, 100%, 40%);">+ void on_valid_pair(pj_ice_sess *ice) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ void on_ice_complete(pj_ice_sess *ice, pj_status_t status) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ void on_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) {}</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t on_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) {}],</span><br><span style="color: hsl(120, 100%, 40%);">+ [pj_ice_sess_cb ice_sess_cb = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_valid_pair = on_valid_pair,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_ice_complete = on_ice_complete,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_rx_data = on_rx_data,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_tx_pkt = on_tx_pkt,</span><br><span style="color: hsl(120, 100%, 40%);">+ };])],</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_MSG_RESULT(yes)</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_DEFINE(HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK, 1, [Define to 1 if on_valid_pair callback is present.]),</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_MSG_RESULT(no)</span><br><span style="color: hsl(120, 100%, 40%);">+ )</span><br><span> LIBS="${saved_libs}"</span><br><span> CPPFLAGS="${saved_cppflags}"</span><br><span> </span><br><span>diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in</span><br><span>index 0da0770..2c770d1 100644</span><br><span>--- a/include/asterisk/autoconfig.h.in</span><br><span>+++ b/include/asterisk/autoconfig.h.in</span><br><span>@@ -630,6 +630,9 @@</span><br><span> /* Define if your system has PJPROJECT_BUNDLED */</span><br><span> #undef HAVE_PJPROJECT_BUNDLED</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Define to 1 if on_valid_pair callback is present. */</span><br><span style="color: hsl(120, 100%, 40%);">+#undef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Define to 1 if PJPROJECT has the pjsip_auth_clt_deinit support feature. */</span><br><span> #undef HAVE_PJSIP_AUTH_CLT_DEINIT</span><br><span> </span><br><span>diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c</span><br><span>index c52ce2c..969fc2d 100644</span><br><span>--- a/res/res_rtp_asterisk.c</span><br><span>+++ b/res/res_rtp_asterisk.c</span><br><span>@@ -447,6 +447,7 @@</span><br><span> struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */</span><br><span> struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */</span><br><span> unsigned int ice_num_components; /*!< The number of ICE components */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int ice_media_started:1; /*!< ICE media has started, either on a valid pair or on ICE completion */</span><br><span> #endif</span><br><span> </span><br><span> #if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)</span><br><span>@@ -968,6 +969,8 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ rtp->ice_media_started = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span>@@ -2527,13 +2530,14 @@</span><br><span> #ifdef HAVE_PJPROJECT</span><br><span> static void rtp_learning_start(struct ast_rtp *rtp);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* PJPROJECT ICE callback */</span><br><span style="color: hsl(0, 100%, 40%);">-static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)</span><br><span style="color: hsl(120, 100%, 40%);">+/* Handles start of media during ICE negotiation or completion */</span><br><span style="color: hsl(120, 100%, 40%);">+static void ast_rtp_ice_start_media(pj_ice_sess *ice, pj_status_t status)</span><br><span> {</span><br><span> struct ast_rtp_instance *instance = ice->user_data;</span><br><span> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);</span><br><span> </span><br><span> ao2_lock(instance);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (status == PJ_SUCCESS) {</span><br><span> struct ast_sockaddr remote_address;</span><br><span> </span><br><span>@@ -2552,6 +2556,11 @@</span><br><span> }</span><br><span> </span><br><span> #if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If we've already started media, no need to do all of this again */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rtp->ice_media_started) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(instance);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span> ast_debug(3, "ast_rtp_on_ice_complete (%p) - perform DTLS\n", rtp);</span><br><span> </span><br><span>@@ -2574,6 +2583,8 @@</span><br><span> }</span><br><span> #endif</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ rtp->ice_media_started = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (!strictrtp) {</span><br><span> ao2_unlock(instance);</span><br><span> return;</span><br><span>@@ -2584,6 +2595,20 @@</span><br><span> ao2_unlock(instance);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK</span><br><span style="color: hsl(120, 100%, 40%);">+/* PJPROJECT ICE optional callback */</span><br><span style="color: hsl(120, 100%, 40%);">+static void ast_rtp_on_valid_pair(pj_ice_sess *ice)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_rtp_ice_start_media(ice, PJ_SUCCESS);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* PJPROJECT ICE callback */</span><br><span style="color: hsl(120, 100%, 40%);">+static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_rtp_ice_start_media(ice, status);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* PJPROJECT ICE callback */</span><br><span> static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)</span><br><span> {</span><br><span>@@ -2640,6 +2665,9 @@</span><br><span> </span><br><span> /* ICE Session interface declaration */</span><br><span> static pj_ice_sess_cb ast_rtp_ice_sess_cb = {</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_valid_pair = ast_rtp_on_valid_pair,</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> .on_ice_complete = ast_rtp_on_ice_complete,</span><br><span> .on_rx_data = ast_rtp_on_ice_rx_data,</span><br><span> .on_tx_pkt = ast_rtp_on_ice_tx_pkt,</span><br><span>diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4</span><br><span>index bdbae31..edf76ba 100644</span><br><span>--- a/third-party/pjproject/configure.m4</span><br><span>+++ b/third-party/pjproject/configure.m4</span><br><span>@@ -108,6 +108,7 @@</span><br><span> AC_DEFINE([HAVE_PJSIP_ENDPOINT_COMPACT_FORM], 1, [Define if your system has HAVE_PJSIP_ENDPOINT_COMPACT_FORM declared.])</span><br><span> AC_DEFINE([HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], 1, [Define if your system has HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE declared])</span><br><span> AC_DEFINE([HAVE_PJSIP_OAUTH_AUTHENTICATION], 1, [Define if your system has HAVE_PJSIP_OAUTH_AUTHENTICATION declared])</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_DEFINE([HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK], 1, [Define if your system has the on_valid_pair pjnath callback.])</span><br><span> </span><br><span> AC_SUBST([PJPROJECT_BUNDLED])</span><br><span> AC_SUBST([PJPROJECT_DIR])</span><br><span>diff --git a/third-party/pjproject/patches/0040-ICE-Add-callback-for-finding-valid-pair.patch b/third-party/pjproject/patches/0040-ICE-Add-callback-for-finding-valid-pair.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..062e75e</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0040-ICE-Add-callback-for-finding-valid-pair.patch</span><br><span>@@ -0,0 +1,84 @@</span><br><span style="color: hsl(120, 100%, 40%);">+From 8b8199180766e3eab6014feaa64ccaedcdc12816 Mon Sep 17 00:00:00 2001</span><br><span style="color: hsl(120, 100%, 40%);">+From: Ben Ford <bford@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+Date: Mon, 23 Dec 2019 11:11:13 -0600</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: [PATCH] ICE: Add callback for finding valid pair.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+It's possible to start sending as soon as one valid pair is found during</span><br><span style="color: hsl(120, 100%, 40%);">+ICE negotiation. The reason we would want to do this is because it is</span><br><span style="color: hsl(120, 100%, 40%);">+possible for a delay to occur at the start of a call for up to 3 seconds</span><br><span style="color: hsl(120, 100%, 40%);">+until ICE negotiation has actually completed. More information can be</span><br><span style="color: hsl(120, 100%, 40%);">+found here:</span><br><span style="color: hsl(120, 100%, 40%);">+https://bugs.chromium.org/p/chromium/issues/detail?id=1024096</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This patch adds a callback once a valid pair is found that applications</span><br><span style="color: hsl(120, 100%, 40%);">+can use to start sending to avoid this scenario. Since only one valid</span><br><span style="color: hsl(120, 100%, 40%);">+pair is needed to start media, we only trigger the callback once.</span><br><span style="color: hsl(120, 100%, 40%);">+---</span><br><span style="color: hsl(120, 100%, 40%);">+ pjnath/include/pjnath/ice_session.h | 9 +++++++++</span><br><span style="color: hsl(120, 100%, 40%);">+ pjnath/src/pjnath/ice_session.c | 16 ++++++++++++++++</span><br><span style="color: hsl(120, 100%, 40%);">+ 2 files changed, 25 insertions(+)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h</span><br><span style="color: hsl(120, 100%, 40%);">+index 15f0d04..8971220 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjnath/include/pjnath/ice_session.h</span><br><span>++++ b/pjnath/include/pjnath/ice_session.h</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -468,6 +468,14 @@ typedef struct pj_ice_sess_cb</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ /**</span><br><span style="color: hsl(120, 100%, 40%);">+ * An optional callback that will be called by the ICE session when</span><br><span style="color: hsl(120, 100%, 40%);">++ * a valid pair has been found during ICE negotiation.</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * @param ice The ICE session.</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++ void (*on_valid_pair)(pj_ice_sess *ice);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ /**</span><br><span style="color: hsl(120, 100%, 40%);">++ * An optional callback that will be called by the ICE session when</span><br><span style="color: hsl(120, 100%, 40%);">+ * ICE negotiation has completed, successfully or with failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * @param ice The ICE session.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -625,6 +633,7 @@ struct pj_ice_sess</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_bool_t is_nominating; /**< Nominating stage */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_bool_t is_complete; /**< Complete? */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_bool_t is_destroying; /**< Destroy is called */</span><br><span style="color: hsl(120, 100%, 40%);">++ pj_bool_t valid_pair_found; /**< First pair found */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t ice_status; /**< Error status. */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_timer_entry timer; /**< ICE timer. */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_ice_sess_cb cb; /**< Callback. */</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c</span><br><span style="color: hsl(120, 100%, 40%);">+index c51dba7..ed4138a 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjnath/src/pjnath/ice_session.c</span><br><span>++++ b/pjnath/src/pjnath/ice_session.c</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -418,6 +418,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ pj_list_init(&ice->early_check);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++ ice->valid_pair_found = PJ_FALSE;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Done */</span><br><span style="color: hsl(120, 100%, 40%);">+ *p_ice = ice;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1348,6 +1350,20 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,</span><br><span style="color: hsl(120, 100%, 40%);">+ GET_CHECK_ID(&ice->clist, check),</span><br><span style="color: hsl(120, 100%, 40%);">+ (check->nominated ? " and nominated" : "")));</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++ {</span><br><span style="color: hsl(120, 100%, 40%);">++ /* On the first valid pair, we call the callback, if present */</span><br><span style="color: hsl(120, 100%, 40%);">++ if (ice->valid_pair_found == PJ_FALSE) {</span><br><span style="color: hsl(120, 100%, 40%);">++ void (*on_valid_pair)(pj_ice_sess *ice);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ ice->valid_pair_found = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">++ on_valid_pair = ice->cb.on_valid_pair;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ if (on_valid_pair) {</span><br><span style="color: hsl(120, 100%, 40%);">++ (*on_valid_pair)(ice);</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* 8.2. Updating States</span><br><span style="color: hsl(120, 100%, 40%);">+-- </span><br><span style="color: hsl(120, 100%, 40%);">+2.7.4</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/13688">change 13688</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/13688"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 17 </div>
<div style="display:none"> Gerrit-Change-Id: Ia7b68c34f06d2a1d91c5ed51627b66fd0363d867 </div>
<div style="display:none"> Gerrit-Change-Number: 13688 </div>
<div style="display:none"> Gerrit-PatchSet: 8 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>