diff options
-rw-r--r-- | bridges/bridge_softmix.c | 48 | ||||
-rw-r--r-- | channels/chan_pjsip.c | 2 | ||||
-rw-r--r-- | codecs/codec_speex.c | 5 | ||||
-rwxr-xr-x | configure | 299 | ||||
-rw-r--r-- | configure.ac | 7 | ||||
-rw-r--r-- | include/asterisk/data_buffer.h | 144 | ||||
-rw-r--r-- | include/asterisk/frame.h | 2 | ||||
-rw-r--r-- | include/asterisk/module.h | 11 | ||||
-rw-r--r-- | include/asterisk/rtp_engine.h | 26 | ||||
-rw-r--r-- | main/asterisk.c | 2 | ||||
-rw-r--r-- | main/bridge_channel.c | 3 | ||||
-rw-r--r-- | main/channel.c | 11 | ||||
-rw-r--r-- | main/data_buffer.c | 314 | ||||
-rw-r--r-- | main/indications.c | 33 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_transport_events.c | 55 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 70 | ||||
-rw-r--r-- | tests/test_data_buffer.c | 313 | ||||
-rw-r--r-- | third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch | 114 |
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; @@ -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 + |