summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bridges/bridge_softmix.c48
-rw-r--r--channels/chan_pjsip.c2
-rw-r--r--codecs/codec_speex.c5
-rwxr-xr-xconfigure299
-rw-r--r--configure.ac7
-rw-r--r--include/asterisk/data_buffer.h144
-rw-r--r--include/asterisk/frame.h2
-rw-r--r--include/asterisk/module.h11
-rw-r--r--include/asterisk/rtp_engine.h26
-rw-r--r--main/asterisk.c2
-rw-r--r--main/bridge_channel.c3
-rw-r--r--main/channel.c11
-rw-r--r--main/data_buffer.c314
-rw-r--r--main/indications.c33
-rw-r--r--res/res_pjsip/pjsip_transport_events.c55
-rw-r--r--res/res_rtp_asterisk.c70
-rw-r--r--tests/test_data_buffer.c313
-rw-r--r--third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch114
18 files changed, 1107 insertions, 352 deletions
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 9986d9c73..16e1fb897 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -956,6 +956,30 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri
/*!
* \internal
+ * \brief Clear talking flag, stop contributing to mixing and notify handlers.
+ * \since 13.21.0, 15.4.0
+ *
+ * \param bridge_channel Which channel's talking to clear
+ *
+ * \return Nothing
+ */
+static void clear_talking(struct ast_bridge_channel *bridge_channel)
+{
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+
+ if (sc->talking) {
+ ast_mutex_lock(&sc->lock);
+ ast_slinfactory_flush(&sc->factory);
+ sc->talking = 0;
+ ast_mutex_unlock(&sc->lock);
+
+ /* Notify that we are no longer talking. */
+ ast_bridge_channel_notify_talking(bridge_channel, 0);
+ }
+}
+
+/*!
+ * \internal
* \brief Check for voice status updates.
* \since 13.20.0
*
@@ -966,23 +990,14 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri
*/
static void softmix_bridge_check_voice(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
- struct softmix_channel *sc = bridge_channel->tech_pvt;
-
- if (sc->talking
- && bridge_channel->features->mute) {
+ if (bridge_channel->features->mute) {
/*
* We were muted while we were talking.
*
* Immediately stop contributing to mixing
* and report no longer talking.
*/
- ast_mutex_lock(&sc->lock);
- ast_slinfactory_flush(&sc->factory);
- sc->talking = 0;
- ast_mutex_unlock(&sc->lock);
-
- /* Notify that we are no longer talking. */
- ast_bridge_channel_notify_talking(bridge_channel, 0);
+ clear_talking(bridge_channel);
}
}
@@ -1113,6 +1128,17 @@ static int softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_br
*/
switch (frame->subclass.integer) {
+ case AST_CONTROL_HOLD:
+ /*
+ * Doing anything for holds in a conference bridge could be considered a bit
+ * odd. That being said, in most cases one would probably want the talking
+ * flag cleared when 'hold' is pressed by the remote endpoint, so go ahead
+ * and do that here. However, that is all we'll do. Meaning if for some reason
+ * the endpoint continues to send audio frames despite pressing 'hold' talking
+ * will once again be detected for that channel.
+ */
+ clear_talking(bridge_channel);
+ break;
case AST_CONTROL_VIDUPDATE:
if (!bridge->softmix.video_mode.video_update_discard ||
ast_tvdiff_ms(ast_tvnow(), softmix_data->last_video_update) > bridge->softmix.video_mode.video_update_discard) {
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 2c111feeb..5cb52a5b2 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -965,6 +965,8 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru
break;
case AST_FRAME_CNG:
break;
+ case AST_FRAME_RTCP:
+ break;
default:
ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
break;
diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c
index 0a9359620..591fce9de 100644
--- a/codecs/codec_speex.c
+++ b/codecs/codec_speex.c
@@ -372,6 +372,11 @@ static void lintospeex_feedback(struct ast_trans_pvt *pvt, struct ast_frame *fee
if(!exp_rtcp_fb)
return;
+ /* We only accept feedback information in the form of SR and RR reports */
+ if (feedback->subclass.integer != AST_RTP_RTCP_SR && feedback->subclass.integer != AST_RTP_RTCP_RR) {
+ return;
+ }
+
rtcp_report = (struct ast_rtp_rtcp_report *)feedback->data.ptr;
if (rtcp_report->reception_report_count == 0)
return;
diff --git a/configure b/configure
index 4e07f0e1b..86ffdea88 100755
--- a/configure
+++ b/configure
@@ -6820,10 +6820,8 @@ $as_echo "no" >&6; }
fi
-for ac_prog in python2 python2.7
-do
- # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
+# Extract the first word of "python", so it can be a program name with args.
+set dummy python; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PYTHON+:} false; then :
@@ -6849,6 +6847,7 @@ done
done
IFS=$as_save_IFS
+ test -z "$ac_cv_path_PYTHON" && ac_cv_path_PYTHON=":"
;;
esac
fi
@@ -6862,10 +6861,6 @@ $as_echo "no" >&6; }
fi
- test -n "$PYTHON" && break
-done
-test -n "$PYTHON" || PYTHON=":"
-
# Extract the first word of "find", so it can be a program name with args.
set dummy find; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -25891,102 +25886,6 @@ fi
-if test "x${PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "x1" -a "${USE_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "no"; then
- pbxlibdir=""
- # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it.
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then
- if test -d ${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib; then
- pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib"
- else
- pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_evsub_set_uas_timeout in -lpjsip" >&5
-$as_echo_n "checking for pjsip_evsub_set_uas_timeout in -lpjsip... " >&6; }
-if ${ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pjsip_evsub_set_uas_timeout ();
-int
-main ()
-{
-return pjsip_evsub_set_uas_timeout ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=yes
-else
- ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&5
-$as_echo "$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&6; }
-if test "x$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" = xyes; then :
- AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=yes
-else
- AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND}" = "yes"; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
- # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it.
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="-I${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/include"
- fi
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE} $PJPROJECT_CFLAGS"
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
-if test "x$ac_cv_header_pjsip_h" = xyes; then :
- PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=1
-else
- PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND}" = "x0" ; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB=""
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE=""
- else
-
- PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-
if test "x${PBX_PJSIP_TSX_LAYER_FIND_TSX2}" != "x1" -a "${USE_PJSIP_TSX_LAYER_FIND_TSX2}" != "no"; then
pbxlibdir=""
# if --with-PJSIP_TSX_LAYER_FIND_TSX2=DIR has been specified, use it.
@@ -26098,188 +25997,12 @@ if test -n "$PYTHONDEV_CFLAGS"; then
pkg_cv_PYTHONDEV_CFLAGS="$PYTHONDEV_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python >= 3\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python >= 3") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_CFLAGS=`$PKG_CONFIG --cflags "python >= 3" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-if test -n "$PYTHONDEV_LIBS"; then
- pkg_cv_PYTHONDEV_LIBS="$PYTHONDEV_LIBS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python >= 3\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python >= 3") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_LIBS=`$PKG_CONFIG --libs "python >= 3" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
- _pkg_short_errors_supported=yes
-else
- _pkg_short_errors_supported=no
-fi
- if test $_pkg_short_errors_supported = yes; then
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "python >= 3" 2>&1`
- else
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "python >= 3" 2>&1`
- fi
- # Put the nasty error message in config.log where it belongs
- echo "$PYTHONDEV_PKG_ERRORS" >&5
-
-
- PBX_PYTHONDEV=0
-
-
-elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
- PBX_PYTHONDEV=0
-
-
-else
- PYTHONDEV_CFLAGS=$pkg_cv_PYTHONDEV_CFLAGS
- PYTHONDEV_LIBS=$pkg_cv_PYTHONDEV_LIBS
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
- PBX_PYTHONDEV=1
- PYTHONDEV_INCLUDE="$PYTHONDEV_CFLAGS"
- PYTHONDEV_LIB="$PYTHONDEV_LIBS"
-
-$as_echo "#define HAVE_PYTHONDEV 1" >>confdefs.h
-
-
-fi
- fi
-
-
- if test "x${PBX_PYTHONDEV}" != "x1" -a "${USE_PYTHONDEV}" != "no"; then
-
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PYTHONDEV" >&5
-$as_echo_n "checking for PYTHONDEV... " >&6; }
-
-if test -n "$PYTHONDEV_CFLAGS"; then
- pkg_cv_PYTHONDEV_CFLAGS="$PYTHONDEV_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python3\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python3") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_CFLAGS=`$PKG_CONFIG --cflags "python3" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-if test -n "$PYTHONDEV_LIBS"; then
- pkg_cv_PYTHONDEV_LIBS="$PYTHONDEV_LIBS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python3\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python3") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_LIBS=`$PKG_CONFIG --libs "python3" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
- _pkg_short_errors_supported=yes
-else
- _pkg_short_errors_supported=no
-fi
- if test $_pkg_short_errors_supported = yes; then
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "python3" 2>&1`
- else
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "python3" 2>&1`
- fi
- # Put the nasty error message in config.log where it belongs
- echo "$PYTHONDEV_PKG_ERRORS" >&5
-
-
- PBX_PYTHONDEV=0
-
-
-elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
- PBX_PYTHONDEV=0
-
-
-else
- PYTHONDEV_CFLAGS=$pkg_cv_PYTHONDEV_CFLAGS
- PYTHONDEV_LIBS=$pkg_cv_PYTHONDEV_LIBS
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
- PBX_PYTHONDEV=1
- PYTHONDEV_INCLUDE="$PYTHONDEV_CFLAGS"
- PYTHONDEV_LIB="$PYTHONDEV_LIBS"
-
-$as_echo "#define HAVE_PYTHONDEV 1" >>confdefs.h
-
-
-fi
- fi
-
-
- if test "x${PBX_PYTHONDEV}" != "x1" -a "${USE_PYTHONDEV}" != "no"; then
-
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PYTHONDEV" >&5
-$as_echo_n "checking for PYTHONDEV... " >&6; }
-
-if test -n "$PYTHONDEV_CFLAGS"; then
- pkg_cv_PYTHONDEV_CFLAGS="$PYTHONDEV_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python-3.6\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python-3.6") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "python") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_CFLAGS=`$PKG_CONFIG --cflags "python-3.6" 2>/dev/null`
+ pkg_cv_PYTHONDEV_CFLAGS=`$PKG_CONFIG --cflags "python" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -26291,12 +26014,12 @@ if test -n "$PYTHONDEV_LIBS"; then
pkg_cv_PYTHONDEV_LIBS="$PYTHONDEV_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python-3.6\""; } >&5
- ($PKG_CONFIG --exists --print-errors "python-3.6") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "python") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_PYTHONDEV_LIBS=`$PKG_CONFIG --libs "python-3.6" 2>/dev/null`
+ pkg_cv_PYTHONDEV_LIBS=`$PKG_CONFIG --libs "python" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -26317,9 +26040,9 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "python-3.6" 2>&1`
+ PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "python" 2>&1`
else
- PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "python-3.6" 2>&1`
+ PYTHONDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "python" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$PYTHONDEV_PKG_ERRORS" >&5
diff --git a/configure.ac b/configure.ac
index de34c4206..e2af23493 100644
--- a/configure.ac
+++ b/configure.ac
@@ -261,7 +261,7 @@ AC_PATH_PROG([CAT], [cat], :)
AC_PATH_PROG([CUT], [cut], :)
AC_PATH_PROG([FLEX], [flex], :)
AC_PATH_PROG([GREP], [grep], :)
-AC_PATH_PROGS([PYTHON], [python2 python2.7], :)
+AC_PATH_PROG([PYTHON], [python], :)
AC_PATH_PROG([FIND], [find], :)
AC_PATH_PROG([COMPRESS], [compress], :)
AC_PATH_PROG([BASENAME], [basename], :)
@@ -2303,16 +2303,13 @@ if test "$USE_PJPROJECT" != "no" ; then
AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
- AST_EXT_LIB_CHECK([PJSIP_EVSUB_SET_UAS_TIMEOUT], [pjsip], [pjsip_evsub_set_uas_timeout], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip], [pjsip_tsx_layer_find_tsx2], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
fi
fi
AC_SUBST([PYTHONDEV_LIB])
AC_SUBST([PYTHONDEV_INCLUDE])
-AST_PKG_CONFIG_CHECK([PYTHONDEV], [python >= 3])
-AST_PKG_CONFIG_CHECK([PYTHONDEV], [python3])
-AST_PKG_CONFIG_CHECK([PYTHONDEV], [python-3.6])
+AST_PKG_CONFIG_CHECK([PYTHONDEV], [python])
AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
diff --git a/include/asterisk/data_buffer.h b/include/asterisk/data_buffer.h
new file mode 100644
index 000000000..dacbaa5e4
--- /dev/null
+++ b/include/asterisk/data_buffer.h
@@ -0,0 +1,144 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Data Buffer API
+ *
+ * A data buffer acts as a ring buffer of data. It is given a fixed
+ * number of data packets to store (which may be dynamically changed).
+ * Given a number it will store a data packet at that position relative
+ * to the others. Given a number it will retrieve the given data packet
+ * if it is present. This is purposely a storage of arbitrary things so
+ * that it can be used for multiple things.
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Ben Ford <bford@digium.com>
+ */
+
+#ifndef _AST_DATA_BUFFER_H_
+#define _AST_DATA_BUFFER_H_
+
+/*!
+ * \brief A buffer of data payloads.
+ */
+struct ast_data_buffer;
+
+/*!
+ * \brief A callback function to free a data payload in a data buffer
+ *
+ * \param The data payload
+ */
+typedef void (*ast_data_buffer_free_callback)(void *data);
+
+/*!
+ * \brief Allocate a data buffer
+ *
+ * \param free_fn Callback function to free a data payload
+ * \param size The maximum number of data payloads to contain in the data buffer
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note free_fn can be NULL. It is up to the consumer of this API to ensure that memory is
+ * managed appropriately.
+ *
+ * \since 15.4.0
+ */
+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size);
+
+/*!
+ * \brief Resize a data buffer
+ *
+ * \param buffer The data buffer
+ * \param size The new maximum size of the data buffer
+ *
+ * \note If the data buffer is shrunk any old data payloads will be freed using the configured callback.
+ * The data buffer is flexible and can be used for multiple purposes. Therefore it is up to the
+ * caller of the function to know whether or not a buffer should have its size changed. Increasing
+ * the size of the buffer may make sense in some scenarios, but shrinking should always be handled
+ * with caution since data can be lost.
+ *
+ * \since 15.4.0
+ */
+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size);
+
+/*!
+ * \brief Place a data payload at a position in the data buffer
+ *
+ * \param buffer The data buffer
+ * \param pos The position of the data payload
+ * \param payload The data payload
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note It is up to the consumer of this API to ensure proper memory management of data payloads
+ *
+ * \since 15.4.0
+ */
+int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload);
+
+/*!
+ * \brief Retrieve a data payload from the data buffer
+ *
+ * \param buffer The data buffer
+ * \param pos The position of the data payload
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This does not remove the data payload from the data buffer. It will be removed when it is displaced.
+ *
+ * \since 15.4.0
+ */
+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos);
+
+/*!
+ * \brief Free a data buffer (and all held data payloads)
+ *
+ * \param buffer The data buffer
+ *
+ * \since 15.4.0
+ */
+void ast_data_buffer_free(struct ast_data_buffer *buffer);
+
+/*!
+ * \brief Return the number of payloads in a data buffer
+ *
+ * \param buffer The data buffer
+ *
+ * \retval the number of data payloads
+ *
+ * \since 15.4.0
+ */
+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer);
+
+/*!
+ * \brief Return the maximum number of payloads a data buffer can hold
+ *
+ * \param buffer The data buffer
+ *
+ * \retval the maximum number of data payloads
+ *
+ * \since 15.4.0
+ */
+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer);
+
+#endif /* _AST_DATA_BUFFER_H */
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index eb6a6479a..c3c0f8817 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -127,7 +127,7 @@ enum ast_frame_type {
* directly into bridges.
*/
AST_FRAME_BRIDGE_ACTION_SYNC,
- /*! RTCP feedback */
+ /*! RTCP feedback (the subclass will contain the payload type) */
AST_FRAME_RTCP,
};
#define AST_FRAME_DTMF AST_FRAME_DTMF_END
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index faa4f7f67..08b4c4317 100644
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -376,6 +376,13 @@ struct ast_module_info {
*/
const char *enhances;
+ /*! These reserved fields should be NULL, they exist to allow addition to this
+ * structure in a non-breaking way. */
+ void *reserved1;
+ void *reserved2;
+ void *reserved3;
+ void *reserved4;
+
/*! The support level for the given module */
enum ast_module_support_level support_level;
};
@@ -448,6 +455,10 @@ void __ast_module_unref(struct ast_module *mod, const char *file, int line, cons
NULL, \
NULL, \
NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
support_level, \
}; \
static void __attribute__((constructor)) __reg_module(void) \
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index 4e32d6b32..b552948d2 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -292,6 +292,14 @@ struct ast_rtp_payload_type {
#define AST_RTP_RTCP_SR 200
/*! Receiver Report */
#define AST_RTP_RTCP_RR 201
+/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_PSFB 206
+
+/* Common RTCP feedback message types */
+/*! Full INTRA-frame Request (From RFC5104) */
+#define AST_RTP_RTCP_FMT_FIR 4
+/*! REMB Information (From draft-alvestrand-rmcat-remb-03) */
+#define AST_RTP_RTCP_FMT_REMB 15
/*!
* \since 12
@@ -327,6 +335,24 @@ struct ast_rtp_rtcp_report {
struct ast_rtp_rtcp_report_block *report_block[0];
};
+/*!
+ * \since 15.4.0
+ * \brief A REMB feedback message (see draft-alvestrand-rmcat-remb-03 for details) */
+struct ast_rtp_rtcp_feedback_remb {
+ unsigned int br_exp; /*!< Exponential scaling of the mantissa for the maximum total media bit rate value */
+ unsigned int br_mantissa; /*!< The mantissa of the maximum total media bit rate */
+};
+
+/*!
+ * \since 15.4.0
+ * \brief An object that represents data received in a feedback report */
+struct ast_rtp_rtcp_feedback {
+ unsigned int fmt; /*!< The feedback message type */
+ union {
+ struct ast_rtp_rtcp_feedback_remb remb; /*!< REMB feedback information */
+ };
+};
+
/*! Structure that represents statistics from an RTP instance */
struct ast_rtp_instance_stats {
/*! Number of packets transmitted */
diff --git a/main/asterisk.c b/main/asterisk.c
index fa1b852bb..3e5785076 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -3551,7 +3551,7 @@ int main(int argc, char *argv[])
*
* \todo Document these options
*/
- optind = 0;
+ optind = 1;
while ((c = getopt(argc, argv, getopt_settings)) != -1) {
/*!\note Please keep the ordering here to alphabetical, capital letters
* first. This will make it easier in the future to select unused
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 89e55713f..3aac5eb25 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -653,7 +653,8 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
case AST_FRAME_VIDEO:
case AST_FRAME_TEXT:
case AST_FRAME_IMAGE:
- /* Media frames need to be mapped to an appropriate write stream */
+ case AST_FRAME_RTCP:
+ /* These frames need to be mapped to an appropriate write stream */
if (frame->stream_num < 0) {
/* Map to default stream */
frame->stream_num = -1;
diff --git a/main/channel.c b/main/channel.c
index 869b29f5e..815d5dbfe 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4123,8 +4123,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int
if (ast_channel_writetrans(chan)) {
ast_translate(ast_channel_writetrans(chan), f, 0);
}
- ast_frfree(f);
- f = &ast_null_frame;
+ break;
default:
/* Just pass it on! */
break;
@@ -5267,6 +5266,14 @@ int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame
/* Ignore these */
res = 0;
break;
+ case AST_FRAME_RTCP:
+ /* RTCP information is on a per-stream basis and only available on multistream capable channels */
+ if (ast_channel_tech(chan)->write_stream && stream) {
+ res = ast_channel_tech(chan)->write_stream(chan, ast_stream_get_position(stream), fr);
+ } else {
+ res = 0;
+ }
+ break;
default:
/* At this point, fr is the incoming frame and f is NULL. Channels do
* not expect to get NULL as a frame pointer and will segfault. Hence,
diff --git a/main/data_buffer.c b/main/data_buffer.c
new file mode 100644
index 000000000..ccbffd22d
--- /dev/null
+++ b/main/data_buffer.c
@@ -0,0 +1,314 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Data Buffer API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Ben Ford <bford@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/logger.h"
+#include "asterisk/strings.h"
+#include "asterisk/data_buffer.h"
+#include "asterisk/linkedlists.h"
+
+/*!
+ * \brief The number of payloads to increment the cache by
+ */
+#define CACHED_PAYLOAD_MAX 5
+
+/*!
+ * \brief Payload entry placed inside of the data buffer list
+ */
+struct data_buffer_payload_entry {
+ /*! \brief The payload for this position */
+ void *payload;
+ /*! \brief The provided position for this */
+ size_t pos;
+ /*! \brief Linked list information */
+ AST_LIST_ENTRY(data_buffer_payload_entry) list;
+};
+
+/*!
+ * \brief Data buffer containing fixed number of data payloads
+ */
+struct ast_data_buffer {
+ /*! \brief Callback function to free a data payload */
+ ast_data_buffer_free_callback free_fn;
+ /*! \brief A linked list of data payloads */
+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) payloads;
+ /*! \brief A linked list of unused cached data payloads */
+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) cached_payloads;
+ /*! \brief The current number of data payloads in the buffer */
+ size_t count;
+ /*! \brief Maximum number of data payloads in the buffer */
+ size_t max;
+ /*! \brief The current number of data payloads in the cache */
+ size_t cache_count;
+};
+
+static void free_fn_do_nothing(void *data)
+{
+ return;
+}
+
+/*!
+ * \brief Helper function to allocate a data payload
+ */
+static struct data_buffer_payload_entry *data_buffer_payload_alloc(void *payload, size_t pos)
+{
+ struct data_buffer_payload_entry *data_payload;
+
+ data_payload = ast_calloc(1, sizeof(*data_payload));
+ if (!data_payload) {
+ return NULL;
+ }
+
+ data_payload->payload = payload;
+ data_payload->pos = pos;
+
+ return data_payload;
+}
+
+/*!
+ * \brief Helper function that sets the cache to its maximum number of payloads
+ */
+static void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer)
+{
+ int buffer_space;
+
+ ast_assert(buffer != NULL);
+
+ buffer_space = buffer->max - buffer->count;
+
+ if (buffer->cache_count == buffer_space) {
+ return;
+ }
+
+ if (buffer->cache_count < buffer_space) {
+ /* Add payloads to the cache, if able */
+ while (buffer->cache_count < CACHED_PAYLOAD_MAX && buffer->cache_count < buffer_space) {
+ struct data_buffer_payload_entry *buffer_payload;
+
+ buffer_payload = data_buffer_payload_alloc(NULL, -1);
+ if (buffer_payload) {
+ AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);
+ buffer->cache_count++;
+ continue;
+ }
+
+ ast_log(LOG_ERROR, "Failed to allocate memory to the cache.");
+ break;
+ }
+ } else if (buffer->cache_count > buffer_space) {
+ /* Remove payloads from the cache */
+ while (buffer->cache_count > buffer_space) {
+ struct data_buffer_payload_entry *buffer_payload;
+
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
+ if (buffer_payload) {
+ ast_free(buffer_payload);
+ buffer->cache_count--;
+ continue;
+ }
+
+ ast_log(LOG_ERROR, "Failed to remove memory from the cache.");
+ break;
+ }
+ }
+}
+
+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size)
+{
+ struct ast_data_buffer *buffer;
+
+ ast_assert(size != 0);
+
+ buffer = ast_calloc(1, sizeof(*buffer));
+ if (!buffer) {
+ return NULL;
+ }
+
+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->payloads);
+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->cached_payloads);
+
+ /* If free_fn is NULL, just use free_fn_do_nothing as a default */
+ buffer->free_fn = free_fn ? free_fn : free_fn_do_nothing;
+ buffer->max = size;
+
+ ast_data_buffer_cache_adjust(buffer);
+
+ return buffer;
+}
+
+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)
+{
+ struct data_buffer_payload_entry *existing_payload;
+
+ ast_assert(buffer != NULL);
+
+ /* The buffer must have at least a size of 1 */
+ ast_assert(size > 0);
+
+ if (buffer->max == size) {
+ return;
+ }
+
+ /* If the size is decreasing, some payloads will need to be freed */
+ if (buffer->max > size) {
+ int remove = buffer->max - size;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
+ if (remove) {
+ AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
+ buffer->free_fn(existing_payload->payload);
+ ast_free(existing_payload);
+ buffer->count--;
+ remove--;
+ continue;
+ }
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ buffer->max = size;
+ ast_data_buffer_cache_adjust(buffer);
+}
+
+int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload)
+{
+ struct data_buffer_payload_entry *buffer_payload = NULL;
+ struct data_buffer_payload_entry *existing_payload;
+ int inserted = 0;
+
+ ast_assert(buffer != NULL);
+ ast_assert(payload != NULL);
+
+ /* If the data buffer has reached its maximum size then the head goes away and
+ * we will reuse its buffer payload
+ */
+ if (buffer->count == buffer->max) {
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
+ buffer->free_fn(buffer_payload->payload);
+ buffer->count--;
+
+ /* Update this buffer payload with its new information */
+ buffer_payload->payload = payload;
+ buffer_payload->pos = pos;
+ }
+ if (!buffer_payload) {
+ if (!buffer->cache_count) {
+ ast_data_buffer_cache_adjust(buffer);
+ }
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
+ buffer->cache_count--;
+
+ /* Update the payload from the cache with its new information */
+ buffer_payload->payload = payload;
+ buffer_payload->pos = pos;
+ }
+ if (!buffer_payload) {
+ return -1;
+ }
+
+ /* Given the position find its ideal spot within the buffer */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
+ /* If it's already in the buffer, drop it */
+ if (existing_payload->pos == pos) {
+ ast_debug(3, "Packet with position %zu is already in buffer. Not inserting.\n", pos);
+ inserted = -1;
+ break;
+ }
+
+ if (existing_payload->pos > pos) {
+ AST_LIST_INSERT_BEFORE_CURRENT(buffer_payload, list);
+ inserted = 1;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (inserted == -1) {
+ return 0;
+ }
+
+ if (!inserted) {
+ AST_LIST_INSERT_TAIL(&buffer->payloads, buffer_payload, list);
+ }
+
+ buffer->count++;
+
+ return 0;
+}
+
+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos)
+{
+ struct data_buffer_payload_entry *buffer_payload;
+
+ ast_assert(buffer != NULL);
+
+ AST_LIST_TRAVERSE(&buffer->payloads, buffer_payload, list) {
+ if (buffer_payload->pos == pos) {
+ return buffer_payload->payload;
+ }
+ }
+
+ return NULL;
+}
+
+void ast_data_buffer_free(struct ast_data_buffer *buffer)
+{
+ struct data_buffer_payload_entry *buffer_payload;
+
+ ast_assert(buffer != NULL);
+
+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list))) {
+ buffer->free_fn(buffer_payload->payload);
+ ast_free(buffer_payload);
+ }
+
+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list))) {
+ ast_free(buffer_payload);
+ }
+
+ ast_free(buffer);
+}
+
+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)
+{
+ ast_assert(buffer != NULL);
+
+ return buffer->count;
+}
+
+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)
+{
+ ast_assert(buffer != NULL);
+
+ return buffer->max;
+}
diff --git a/main/indications.c b/main/indications.c
index 9b0976809..c9f02416f 100644
--- a/main/indications.c
+++ b/main/indications.c
@@ -632,9 +632,7 @@ static struct ast_tone_zone *ast_tone_zone_alloc(void)
static char *complete_country(struct ast_cli_args *a)
{
- char *res = NULL;
struct ao2_iterator i;
- int which = 0;
size_t wordlen;
struct ast_tone_zone *tz;
@@ -642,17 +640,17 @@ static char *complete_country(struct ast_cli_args *a)
i = ao2_iterator_init(ast_tone_zones, 0);
while ((tz = ao2_iterator_next(&i))) {
- if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
- res = ast_strdup(tz->country);
- }
- tz = ast_tone_zone_unref(tz);
- if (res) {
- break;
+ if (!strncasecmp(a->word, tz->country, wordlen)) {
+ if (ast_cli_completion_add(ast_strdup(tz->country))) {
+ ast_tone_zone_unref(tz);
+ break;
+ }
}
+ ast_tone_zone_unref(tz);
}
ao2_iterator_destroy(&i);
- return res;
+ return NULL;
}
static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -718,17 +716,17 @@ static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct
static char *complete_indications(struct ast_cli_args *a)
{
- char *res = NULL;
- int which = 0;
size_t wordlen;
struct ast_tone_zone_sound *ts;
- struct ast_tone_zone *tz, tmp_tz = {
+ struct ast_tone_zone *tz;
+ struct ast_tone_zone tmp_tz = {
.nrringcadence = 0,
};
ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
- if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
+ tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER);
+ if (!tz) {
return NULL;
}
@@ -736,16 +734,17 @@ static char *complete_indications(struct ast_cli_args *a)
ast_tone_zone_lock(tz);
AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
- if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
- res = ast_strdup(ts->name);
- break;
+ if (!strncasecmp(a->word, ts->name, wordlen)) {
+ if (ast_cli_completion_add(ast_strdup(ts->name))) {
+ break;
+ }
}
}
ast_tone_zone_unlock(tz);
tz = ast_tone_zone_unref(tz);
- return res;
+ return NULL;
}
static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c
index c701b8411..cc7b7c077 100644
--- a/res/res_pjsip/pjsip_transport_events.c
+++ b/res/res_pjsip/pjsip_transport_events.c
@@ -114,6 +114,36 @@ static void transport_monitor_dtor(void *vdoomed)
AST_VECTOR_FREE(&monitored->monitors);
}
+/*!
+ * \internal
+ * \brief Do registered callbacks for the transport.
+ * \since 13.21.0
+ *
+ * \param transports Active transports container
+ * \param transport Which transport to do callbacks for.
+ *
+ * \return Nothing
+ */
+static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
+{
+ struct transport_monitor *monitored;
+
+ monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (monitored) {
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ ast_debug(3, "running callback %p(%p) for transport %s\n",
+ notifier->cb, notifier->data, transport->obj_name);
+ notifier->cb(notifier->data);
+ }
+ ao2_ref(monitored, -1);
+ }
+}
+
/*! \brief Callback invoked when transport state changes occur */
static void transport_state_callback(pjsip_transport *transport,
pjsip_transport_state state, const pjsip_transport_state_info *info)
@@ -147,6 +177,7 @@ static void transport_state_callback(pjsip_transport *transport,
if (!transport->is_shutdown) {
pjsip_transport_shutdown(transport);
}
+ transport_state_do_reg_callbacks(transports, transport);
break;
case PJSIP_TP_STATE_SHUTDOWN:
/*
@@ -157,23 +188,17 @@ static void transport_state_callback(pjsip_transport *transport,
*/
transport->is_shutdown = PJ_TRUE;
- monitored = ao2_find(transports, transport->obj_name,
- OBJ_SEARCH_KEY | OBJ_UNLINK);
- if (monitored) {
- int idx;
-
- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
- struct transport_monitor_notifier *notifier;
-
- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- ast_debug(3, "running callback %p(%p) for transport %s\n",
- notifier->cb, notifier->data, transport->obj_name);
- notifier->cb(notifier->data);
- }
- ao2_ref(monitored, -1);
- }
+ transport_state_do_reg_callbacks(transports, transport);
+ break;
+ case PJSIP_TP_STATE_DESTROY:
+ transport_state_do_reg_callbacks(transports, transport);
break;
default:
+ /*
+ * We have to have a default case because the enum is
+ * defined by a third-party library.
+ */
+ ast_assert(0);
break;
}
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index d0e482405..b010f6c51 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -106,7 +106,7 @@
#define RTCP_PT_APP 204
/* VP8: RTCP Feedback */
/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
-#define RTCP_PT_PSFB 206
+#define RTCP_PT_PSFB AST_RTP_RTCP_PSFB
#define RTP_MTU 1200
#define DTMF_SAMPLE_RATE_MS 8 /*!< DTMF samples per millisecond */
@@ -5185,6 +5185,7 @@ static const char *rtcp_payload_type2str(unsigned int pt)
#define RTCP_SR_BLOCK_WORD_LENGTH 5
#define RTCP_RR_BLOCK_WORD_LENGTH 6
#define RTCP_HEADER_SSRC_LENGTH 2
+#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 5
static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
@@ -5266,6 +5267,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
struct ast_rtp_instance *child;
struct ast_rtp *rtp;
+ struct ast_rtp_rtcp_feedback *feedback;
i = position;
first_word = ntohl(rtcpheader[i]);
@@ -5284,7 +5286,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
break;
case RTCP_PT_FUR:
+ break;
case RTCP_PT_PSFB:
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_REMB:
+ min_length += RTCP_FB_REMB_BLOCK_WORD_LENGTH;
+ break;
+ default:
+ break;
+ }
break;
case RTCP_PT_SDES:
case RTCP_PT_BYE:
@@ -5493,6 +5503,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
/* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
* object as a its data */
transport_rtp->f.frametype = AST_FRAME_RTCP;
+ transport_rtp->f.subclass.integer = pt;
transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
memcpy(transport_rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
@@ -5514,18 +5525,55 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
f = &transport_rtp->f;
break;
case RTCP_PT_FUR:
- /* Handle RTCP FIR as FUR */
+ /* Handle RTCP FUR as FIR by setting the format to 4 */
+ rc = AST_RTP_RTCP_FMT_FIR;
case RTCP_PT_PSFB:
- if (rtcp_debug_test_addr(addr)) {
- ast_verbose("Received an RTCP Fast Update Request\n");
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_FIR:
+ if (rtcp_debug_test_addr(addr)) {
+ ast_verbose("Received an RTCP Fast Update Request\n");
+ }
+ transport_rtp->f.frametype = AST_FRAME_CONTROL;
+ transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
+ transport_rtp->f.datalen = 0;
+ transport_rtp->f.samples = 0;
+ transport_rtp->f.mallocd = 0;
+ transport_rtp->f.src = "RTP";
+ f = &transport_rtp->f;
+ break;
+ case AST_RTP_RTCP_FMT_REMB:
+ /* If REMB support is not enabled ignore this message */
+ if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+ break;
+ }
+
+ if (rtcp_debug_test_addr(addr)) {
+ ast_verbose("Received REMB report\n");
+ }
+ transport_rtp->f.frametype = AST_FRAME_RTCP;
+ transport_rtp->f.subclass.integer = pt;
+ transport_rtp->f.stream_num = rtp->stream_num;
+ transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
+ feedback = transport_rtp->f.data.ptr;
+ feedback->fmt = rc;
+
+ /* We don't actually care about the SSRC information in the feedback message */
+ first_word = ntohl(rtcpheader[i + 2]);
+ feedback->remb.br_exp = (first_word >> 18) & ((1 << 6) - 1);
+ feedback->remb.br_mantissa = first_word & ((1 << 18) - 1);
+
+ transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_feedback);
+ transport_rtp->f.offset = AST_FRIENDLY_OFFSET;
+ transport_rtp->f.samples = 0;
+ transport_rtp->f.mallocd = 0;
+ transport_rtp->f.delivery.tv_sec = 0;
+ transport_rtp->f.delivery.tv_usec = 0;
+ transport_rtp->f.src = "RTP";
+ f = &transport_rtp->f;
+ break;
+ default:
+ break;
}
- transport_rtp->f.frametype = AST_FRAME_CONTROL;
- transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
- transport_rtp->f.datalen = 0;
- transport_rtp->f.samples = 0;
- transport_rtp->f.mallocd = 0;
- transport_rtp->f.src = "RTP";
- f = &transport_rtp->f;
break;
case RTCP_PT_SDES:
if (rtcp_debug_test_addr(addr)) {
diff --git a/tests/test_data_buffer.c b/tests/test_data_buffer.c
new file mode 100644
index 000000000..11fdc7b6f
--- /dev/null
+++ b/tests/test_data_buffer.c
@@ -0,0 +1,313 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Media Stream API Unit Tests
+ *
+ * \author Ben Ford <bford@digium.com>
+ *
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/data_buffer.h"
+
+#define BUFFER_MAX_NOMINAL 10
+
+struct mock_payload
+{
+ int id;
+};
+
+/* Ensures that RAII_VAR will not trip ast_assert(buffer != NULL) in the callback */
+static void ast_data_buffer_free_wrapper(struct ast_data_buffer *buffer)
+{
+ if (!buffer) {
+ return;
+ }
+
+ ast_data_buffer_free(buffer);
+}
+
+AST_TEST_DEFINE(buffer_create)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_create";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer create unit test";
+ info->description =
+ "Test that creating a data buffer results in a buffer with the expected values";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 0,
+ "Newly created buffer does not have the expected payload count");
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL,
+ "Newly created buffer does not have the expected max size");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_put)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+ struct mock_payload *payload;
+ struct mock_payload *fetched_payload;
+ int ret;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_put";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer put unit test";
+ info->description =
+ "Test that putting payloads in the buffer yields the expected results";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, 2);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 0,
+ "Newly created buffer is not empty");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for first payload");
+
+ payload->id = 2;
+ ret = ast_data_buffer_put(buffer, 2, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Adding a payload to an empty buffer did not return the expected value");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 1,
+ "Adding a payload to an empty buffer did not update count to the expected value");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get only payload from buffer given valid arguments");
+
+ ast_data_buffer_put(buffer, 2, payload);
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 1,
+ "Adding a payload that is already in the buffer should not do anything");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for second payload");
+
+ payload->id = 1;
+ ast_data_buffer_put(buffer, 1, payload);
+ fetched_payload = ast_data_buffer_get(buffer, 1);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer given valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 2,
+ "Buffer does not have the expected count after removing a payload");
+ ast_test_validate(test, fetched_payload->id == 1,
+ "Did not get the expected payload from the buffer");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for third payload");
+
+ payload->id = 3;
+ ret = ast_data_buffer_put(buffer, 3, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to replace a payload in the buffer");
+ ast_test_validate(test, ast_data_buffer_count(buffer) <= 2,
+ "Buffer count exceeded the max");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 3);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer at position 3 given valid arguments");
+ ast_test_validate(test, fetched_payload->id == 3,
+ "Did not get the expected payload at position 3 from the buffer");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer at position 2 given valid arguments");
+ ast_test_validate(test, fetched_payload->id == 2,
+ "Did not get the expected payload at position 2 from the buffer");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_resize)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_resize";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer resize unit test";
+ info->description =
+ "Tests resizing a data buffer to make sure it has the expected outcome";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+
+ ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL,
+ "Trying to resize buffer to same size should not change its max size");
+
+ ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL + 2);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL + 2,
+ "Increasing buffer size did not return the expected max");
+
+ ast_data_buffer_resize(buffer, 1);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == 1,
+ "Decreasing buffer size did not return the expected max");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_nominal)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+ struct mock_payload *payload;
+ struct mock_payload *fetched_payload;
+ int ret;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_nominal";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer nominal unit test";
+ info->description =
+ "Tests the normal usage of a data buffer to ensure the expected payloads "
+ "are present after multiple insertions";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for payload %d", i);
+
+ ret = ast_data_buffer_put(buffer, i, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to add payload %d to buffer", i);
+ }
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL,
+ "Buffer does not have the expected count after adding payloads");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get payload at position %d during first loop", i);
+ }
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for payload %d", i + BUFFER_MAX_NOMINAL);
+
+ ret = ast_data_buffer_put(buffer, i + BUFFER_MAX_NOMINAL, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to add payload %d to buffer", i + BUFFER_MAX_NOMINAL);
+ }
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL,
+ "Buffer does not have the expected count after replacing payloads");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i);
+
+ ast_test_validate(test, fetched_payload == NULL,
+ "Got an unexpected payload at position %d", i);
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i + BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get payload at position %d during second loop", i + BUFFER_MAX_NOMINAL);
+ }
+
+ return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(buffer_create);
+ AST_TEST_UNREGISTER(buffer_put);
+ AST_TEST_UNREGISTER(buffer_resize);
+ AST_TEST_UNREGISTER(buffer_nominal);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(buffer_create);
+ AST_TEST_REGISTER(buffer_put);
+ AST_TEST_REGISTER(buffer_resize);
+ AST_TEST_REGISTER(buffer_nominal);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Data buffer API test module");
diff --git a/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch b/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch
new file mode 100644
index 000000000..3aafd69e8
--- /dev/null
+++ b/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch
@@ -0,0 +1,114 @@
+From 67485f3a6c711f67a40ff46288cb6be1658023bd Mon Sep 17 00:00:00 2001
+From: nanang <nanang@localhost>
+Date: Mon, 26 Mar 2018 10:33:50 +0000
+Subject: [PATCH] Close #2101: - set atomic's mutex to NULL in atomic destroy
+ - added few sanity checks to the atomic functions.
+
+---
+ pjlib/src/pj/os_core_unix.c | 20 ++++++++++++++++++--
+ pjlib/src/pj/os_core_win32.c | 4 ++++
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
+index ebfe84348..c17ad4ef0 100644
+--- a/pjlib/src/pj/os_core_unix.c
++++ b/pjlib/src/pj/os_core_unix.c
+@@ -879,9 +879,16 @@ PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ */
+ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+ {
++ pj_status_t status;
++
+ PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
++
+ #if PJ_HAS_THREADS
+- return pj_mutex_destroy( atomic_var->mutex );
++ status = pj_mutex_destroy( atomic_var->mutex );
++ if (status == PJ_SUCCESS) {
++ atomic_var->mutex = NULL;
++ }
++ return status;
+ #else
+ return 0;
+ #endif
+@@ -892,10 +899,16 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+ */
+ PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
+ {
++ pj_status_t status;
++
+ PJ_CHECK_STACK();
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+
+ #if PJ_HAS_THREADS
+- pj_mutex_lock( atomic_var->mutex );
++ status = pj_mutex_lock( atomic_var->mutex );
++ if (status != PJ_SUCCESS) {
++ return;
++ }
+ #endif
+ atomic_var->value = value;
+ #if PJ_HAS_THREADS
+@@ -946,6 +959,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_inc_and_get(atomic_var);
+ }
+
+@@ -974,6 +988,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_dec_and_get(atomic_var);
+ }
+
+@@ -1005,6 +1020,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
+ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value )
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_add_and_get(atomic_var, value);
+ }
+
+diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
+index 1cb6004d3..8c934b34d 100644
+--- a/pjlib/src/pj/os_core_win32.c
++++ b/pjlib/src/pj/os_core_win32.c
+@@ -750,6 +750,7 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+ PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
+ {
+ PJ_CHECK_STACK();
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+
+ InterlockedExchange(&atomic_var->value, value);
+ }
+@@ -784,6 +785,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_inc_and_get(atomic_var);
+ }
+
+@@ -806,6 +808,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_dec_and_get(atomic_var);
+ }
+
+@@ -815,6 +818,7 @@ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value )
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+ InterlockedExchangeAdd( &atomic_var->value, value );
+ #else
+--
+2.14.3
+