diff --git a/configure b/configure index e182dbcbe0181630f05b4a4e88a717362ad5091a..0d542bf7581760e52625b706c6c53bd2ea450b2f 100755 --- a/configure +++ b/configure @@ -9505,6 +9505,9 @@ $as_echo "#define HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE 1" >>confdefs.h $as_echo "#define HAVE_PJSIP_OAUTH_AUTHENTICATION 1" >>confdefs.h +$as_echo "#define HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK 1" >>confdefs.h + + @@ -16397,8 +16400,6 @@ main () if (*(data + i) != *(data3 + i)) return 14; close (fd); - free (data); - free (data3); return 0; } _ACEOF @@ -25850,6 +25851,43 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CPPFLAGS="${saved_cppflags}" fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjproject on_valid_pair callback" >&5 +$as_echo_n "checking for pjproject on_valid_pair callback... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pjsip.h> + #include <pjsip_ua.h> + #include <pjnath.h> + void on_valid_pair(pj_ice_sess *ice) {} + void on_ice_complete(pj_ice_sess *ice, pj_status_t status) {} + 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) {} + 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) {} +int +main () +{ +pj_ice_sess_cb ice_sess_cb = { + .on_valid_pair = on_valid_pair, + .on_ice_complete = on_ice_complete, + .on_rx_data = on_rx_data, + .on_tx_pkt = on_tx_pkt, + }; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK 1" >>confdefs.h + +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_exeext conftest.$ac_ext LIBS="${saved_libs}" CPPFLAGS="${saved_cppflags}" diff --git a/configure.ac b/configure.ac index ed26d699e817e2e41075cc0ddb78320e22018059..0a6852aec174d525d4af6adbca29d5ffa12c9bdb 100644 --- a/configure.ac +++ b/configure.ac @@ -2436,6 +2436,26 @@ if test "$USE_PJPROJECT" != "no" ; then AST_C_COMPILE_CHECK([PJSIP_ENDPOINT_COMPACT_FORM], [pjsip_cfg()->endpt.use_compact_form = PJ_TRUE;], [pjsip.h]) AST_C_COMPILE_CHECK([PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], [struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;], [pjsip.h]) AST_C_COMPILE_CHECK([PJSIP_OAUTH_AUTHENTICATION], [struct pjsip_oauth_credential credential;], [pjsip.h]) + AC_MSG_CHECKING(for pjproject on_valid_pair callback) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include <pjsip.h> + #include <pjsip_ua.h> + #include <pjnath.h> + void on_valid_pair(pj_ice_sess *ice) {} + void on_ice_complete(pj_ice_sess *ice, pj_status_t status) {} + 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) {} + 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) {}], + [pj_ice_sess_cb ice_sess_cb = { + .on_valid_pair = on_valid_pair, + .on_ice_complete = on_ice_complete, + .on_rx_data = on_rx_data, + .on_tx_pkt = on_tx_pkt, + };])], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK, 1, [Define to 1 if on_valid_pair callback is present.]), + AC_MSG_RESULT(no) + ) LIBS="${saved_libs}" CPPFLAGS="${saved_cppflags}" diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index 0da0770acbfa73773bd4989639217bdd5a0f5e5c..2c770d1433b588825e6f9f246b85fdb9b66b8998 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -630,6 +630,9 @@ /* Define if your system has PJPROJECT_BUNDLED */ #undef HAVE_PJPROJECT_BUNDLED +/* Define to 1 if on_valid_pair callback is present. */ +#undef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK + /* Define to 1 if PJPROJECT has the pjsip_auth_clt_deinit support feature. */ #undef HAVE_PJSIP_AUTH_CLT_DEINIT diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index c52ce2c1cf7a286698e9d983facb862f2d7abf58..969fc2d628308a120560a54c7f3fc40344ceb31a 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -447,6 +447,7 @@ struct ast_rtp { struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */ struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */ unsigned int ice_num_components; /*!< The number of ICE components */ + unsigned int ice_media_started:1; /*!< ICE media has started, either on a valid pair or on ICE completion */ #endif #if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP) @@ -968,6 +969,8 @@ static int ice_reset_session(struct ast_rtp_instance *instance) } } + rtp->ice_media_started = 0; + return res; } @@ -2527,13 +2530,14 @@ static void dtls_perform_setup(struct dtls_details *dtls) #ifdef HAVE_PJPROJECT static void rtp_learning_start(struct ast_rtp *rtp); -/* PJPROJECT ICE callback */ -static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) +/* Handles start of media during ICE negotiation or completion */ +static void ast_rtp_ice_start_media(pj_ice_sess *ice, pj_status_t status) { struct ast_rtp_instance *instance = ice->user_data; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); ao2_lock(instance); + if (status == PJ_SUCCESS) { struct ast_sockaddr remote_address; @@ -2552,6 +2556,11 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) } #if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP) + /* If we've already started media, no need to do all of this again */ + if (rtp->ice_media_started) { + ao2_unlock(instance); + return; + } ast_debug(3, "ast_rtp_on_ice_complete (%p) - perform DTLS\n", rtp); @@ -2574,6 +2583,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) } #endif + rtp->ice_media_started = 1; + if (!strictrtp) { ao2_unlock(instance); return; @@ -2584,6 +2595,20 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) ao2_unlock(instance); } +#ifdef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK +/* PJPROJECT ICE optional callback */ +static void ast_rtp_on_valid_pair(pj_ice_sess *ice) +{ + ast_rtp_ice_start_media(ice, PJ_SUCCESS); +} +#endif + +/* PJPROJECT ICE callback */ +static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) +{ + ast_rtp_ice_start_media(ice, status); +} + /* PJPROJECT ICE callback */ 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) { @@ -2640,6 +2665,9 @@ static pj_status_t ast_rtp_on_ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, uns /* ICE Session interface declaration */ static pj_ice_sess_cb ast_rtp_ice_sess_cb = { +#ifdef HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK + .on_valid_pair = ast_rtp_on_valid_pair, +#endif .on_ice_complete = ast_rtp_on_ice_complete, .on_rx_data = ast_rtp_on_ice_rx_data, .on_tx_pkt = ast_rtp_on_ice_tx_pkt, diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4 index bdbae319e819f281f6fec250440f3ec3209cd752..edf76ba6af636d7e0f1a24291e1aee27d31a0929 100644 --- a/third-party/pjproject/configure.m4 +++ b/third-party/pjproject/configure.m4 @@ -108,6 +108,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE], AC_DEFINE([HAVE_PJSIP_ENDPOINT_COMPACT_FORM], 1, [Define if your system has HAVE_PJSIP_ENDPOINT_COMPACT_FORM declared.]) AC_DEFINE([HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], 1, [Define if your system has HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE declared]) AC_DEFINE([HAVE_PJSIP_OAUTH_AUTHENTICATION], 1, [Define if your system has HAVE_PJSIP_OAUTH_AUTHENTICATION declared]) + AC_DEFINE([HAVE_PJPROJECT_ON_VALID_ICE_PAIR_CALLBACK], 1, [Define if your system has the on_valid_pair pjnath callback.]) AC_SUBST([PJPROJECT_BUNDLED]) AC_SUBST([PJPROJECT_DIR]) 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 new file mode 100644 index 0000000000000000000000000000000000000000..062e75e0254e45faac62ad51b7ae491ca94f8466 --- /dev/null +++ b/third-party/pjproject/patches/0040-ICE-Add-callback-for-finding-valid-pair.patch @@ -0,0 +1,84 @@ +From 8b8199180766e3eab6014feaa64ccaedcdc12816 Mon Sep 17 00:00:00 2001 +From: Ben Ford <bford@digium.com> +Date: Mon, 23 Dec 2019 11:11:13 -0600 +Subject: [PATCH] ICE: Add callback for finding valid pair. + +It's possible to start sending as soon as one valid pair is found during +ICE negotiation. The reason we would want to do this is because it is +possible for a delay to occur at the start of a call for up to 3 seconds +until ICE negotiation has actually completed. More information can be +found here: +https://bugs.chromium.org/p/chromium/issues/detail?id=1024096 + +This patch adds a callback once a valid pair is found that applications +can use to start sending to avoid this scenario. Since only one valid +pair is needed to start media, we only trigger the callback once. +--- + pjnath/include/pjnath/ice_session.h | 9 +++++++++ + pjnath/src/pjnath/ice_session.c | 16 ++++++++++++++++ + 2 files changed, 25 insertions(+) + +diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h +index 15f0d04..8971220 100644 +--- a/pjnath/include/pjnath/ice_session.h ++++ b/pjnath/include/pjnath/ice_session.h +@@ -468,6 +468,14 @@ typedef struct pj_ice_sess_cb + { + /** + * An optional callback that will be called by the ICE session when ++ * a valid pair has been found during ICE negotiation. ++ * ++ * @param ice The ICE session. ++ */ ++ void (*on_valid_pair)(pj_ice_sess *ice); ++ ++ /** ++ * An optional callback that will be called by the ICE session when + * ICE negotiation has completed, successfully or with failure. + * + * @param ice The ICE session. +@@ -625,6 +633,7 @@ struct pj_ice_sess + pj_bool_t is_nominating; /**< Nominating stage */ + pj_bool_t is_complete; /**< Complete? */ + pj_bool_t is_destroying; /**< Destroy is called */ ++ pj_bool_t valid_pair_found; /**< First pair found */ + pj_status_t ice_status; /**< Error status. */ + pj_timer_entry timer; /**< ICE timer. */ + pj_ice_sess_cb cb; /**< Callback. */ +diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c +index c51dba7..ed4138a 100644 +--- a/pjnath/src/pjnath/ice_session.c ++++ b/pjnath/src/pjnath/ice_session.c +@@ -418,6 +418,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, + + pj_list_init(&ice->early_check); + ++ ice->valid_pair_found = PJ_FALSE; ++ + /* Done */ + *p_ice = ice; + +@@ -1348,6 +1350,20 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, + GET_CHECK_ID(&ice->clist, check), + (check->nominated ? " and nominated" : ""))); + ++ { ++ /* On the first valid pair, we call the callback, if present */ ++ if (ice->valid_pair_found == PJ_FALSE) { ++ void (*on_valid_pair)(pj_ice_sess *ice); ++ ++ ice->valid_pair_found = PJ_TRUE; ++ on_valid_pair = ice->cb.on_valid_pair; ++ ++ if (on_valid_pair) { ++ (*on_valid_pair)(ice); ++ } ++ } ++ } ++ + } + + /* 8.2. Updating States +-- +2.7.4 +