summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bridges/bridge_softmix.c48
-rwxr-xr-xconfigure203
-rw-r--r--configure.ac6
-rw-r--r--include/asterisk/data_buffer.h144
-rw-r--r--include/asterisk/module.h11
-rw-r--r--main/asterisk.c2
-rw-r--r--main/data_buffer.c314
-rw-r--r--res/res_pjsip/pjsip_transport_events.c55
-rw-r--r--tests/test_data_buffer.c313
9 files changed, 873 insertions, 223 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/configure b/configure
index 9fff2b273..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
@@ -26002,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
@@ -26195,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
@@ -26221,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 2ed4cd9cd..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], :)
@@ -2309,9 +2309,7 @@ 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/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/main/asterisk.c b/main/asterisk.c
index 36b1b54a1..2e80ffaf6 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -3963,7 +3963,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/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/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/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");