summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES12
-rw-r--r--addons/cdr_mysql.c8
-rw-r--r--apps/app_confbridge.c8
-rw-r--r--apps/app_originate.c12
-rw-r--r--apps/confbridge/conf_config_parser.c77
-rw-r--r--apps/confbridge/include/confbridge.h4
-rw-r--r--autoconf/ast_ext_tool_check.m42
-rw-r--r--autoconf/ast_pkgconfig.m42
-rw-r--r--bridges/bridge_softmix.c242
-rw-r--r--bridges/bridge_softmix/include/bridge_softmix_internal.h14
-rw-r--r--channels/chan_pjsip.c16
-rw-r--r--channels/chan_sip.c18
-rw-r--r--channels/pjsip/dialplan_functions.c8
-rw-r--r--configs/samples/confbridge.conf.sample8
-rwxr-xr-xconfigure297
-rw-r--r--configure.ac56
-rw-r--r--include/asterisk/bridge.h51
-rw-r--r--include/asterisk/res_pjsip.h133
-rw-r--r--include/asterisk/rtp_engine.h8
-rw-r--r--include/asterisk/stasis_bridges.h4
-rw-r--r--include/asterisk/utils.h13
-rw-r--r--main/bridge.c26
-rw-r--r--main/stasis_bridges.c3
-rwxr-xr-xmenuselect/configure4
-rw-r--r--res/res_musiconhold.c1
-rw-r--r--res/res_pjsip.c93
-rw-r--r--res/res_pjsip/config_system.c2
-rw-r--r--res/res_pjsip/config_transport.c2
-rw-r--r--res/res_pjsip/location.c5
-rw-r--r--res/res_pjsip/pjsip_configuration.c2
-rw-r--r--res/res_pjsip/pjsip_scheduler.c311
-rw-r--r--res/res_pjsip_header_funcs.c22
-rw-r--r--res/res_pjsip_history.c2
-rw-r--r--res/res_pjsip_notify.c230
-rw-r--r--res/res_pjsip_outbound_publish.c6
-rw-r--r--res/res_pjsip_outbound_registration.c7
-rw-r--r--res/res_pjsip_pubsub.c51
-rw-r--r--res/res_pjsip_refer.c18
-rw-r--r--res/res_pjsip_registrar.c34
-rw-r--r--res/res_pjsip_sdp_rtp.c3
-rw-r--r--res/res_pjsip_session.c2
-rw-r--r--res/res_pjsip_transport_websocket.c6
-rw-r--r--res/res_rtp_asterisk.c305
-rw-r--r--utils/Makefile3
44 files changed, 1553 insertions, 578 deletions
diff --git a/CHANGES b/CHANGES
index 09a7659d4..3a81ca7ba 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,13 @@ app_fax
* The app_fax module is now deprecated, users should migrate to the
replacement module res_fax.
+app_originate
+------------------
+ * An 'a' option has been added to the Originate dialplan application which
+ will execute the originate in an asynchronous fashion. If set then the
+ application will return immediately without waiting for the originated
+ channel to answer.
+
Build System
------------------
* MALLOC_DEBUG no longer has an effect on Asterisk's ABI. Asterisk built
@@ -115,6 +122,11 @@ res_pjproject
MALLOC_DEBUG. The cache gets in the way of determining if the pool contents
are used after free and who freed it.
+res_pjsip_notify
+------------------
+ * Extend the PJSIPNotify AMI command to send an in-dialog notify on a
+ channel.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 15.2.0 to Asterisk 15.3.0 ------------
------------------------------------------------------------------------------
diff --git a/addons/cdr_mysql.c b/addons/cdr_mysql.c
index 2fefe4ed1..97ebdf26f 100644
--- a/addons/cdr_mysql.c
+++ b/addons/cdr_mysql.c
@@ -58,6 +58,14 @@
#define DATE_FORMAT "%Y-%m-%d %T"
+#ifndef MYSQL_PORT
+# ifdef MARIADB_PORT
+# define MYSQL_PORT MARIADB_PORT
+# else
+# define MYSQL_PORT 3306
+# endif
+#endif
+
AST_THREADSTORAGE(sql1_buf);
AST_THREADSTORAGE(sql2_buf);
AST_THREADSTORAGE(escape_buf);
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 4f1108e6b..25cf2758f 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -1543,6 +1543,14 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
} else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
ast_bridge_set_sfu_video_mode(conference->bridge);
ast_bridge_set_video_update_discard(conference->bridge, conference->b_profile.video_update_discard);
+ ast_bridge_set_remb_send_interval(conference->bridge, conference->b_profile.remb_send_interval);
+ if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_LOWEST);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST);
+ }
}
/* Link it into the conference bridges container */
diff --git a/apps/app_originate.c b/apps/app_originate.c
index 30fa565be..107be846d 100644
--- a/apps/app_originate.c
+++ b/apps/app_originate.c
@@ -74,6 +74,10 @@ static const char app_originate[] = "Originate";
</parameter>
<parameter name="options" required="false">
<optionlist>
+ <option name="a">
+ <para>Originate asynchronously. In other words, continue in the dialplan
+ without waiting for the originated channel to answer.</para>
+ </option>
<option name="b" argsep="^">
<para>Before originating the outgoing call, Gosub to the specified
location using the newly created channel.</para>
@@ -123,6 +127,7 @@ static const char app_originate[] = "Originate";
enum {
OPT_PREDIAL_CALLEE = (1 << 0),
OPT_PREDIAL_CALLER = (1 << 1),
+ OPT_ASYNC = (1 << 2),
};
enum {
@@ -133,6 +138,7 @@ enum {
};
AST_APP_OPTIONS(originate_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION('a', OPT_ASYNC),
AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
END_OPTIONS );
@@ -250,7 +256,8 @@ static int originate_exec(struct ast_channel *chan, const char *data)
res = ast_pbx_outgoing_exten_predial(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, exten, priority, &outgoing_status,
- AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, 0, NULL,
+ ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
+ NULL, NULL, NULL, NULL, NULL, 0, NULL,
predial_callee);
} else {
ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
@@ -258,7 +265,8 @@ static int originate_exec(struct ast_channel *chan, const char *data)
res = ast_pbx_outgoing_app_predial(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, args.arg2, &outgoing_status,
- AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, NULL,
+ ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
+ NULL, NULL, NULL, NULL, NULL, NULL,
predial_callee);
}
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 71da80206..c143e39e2 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -458,6 +458,39 @@
video update requests from clients.
</para></description>
</configOption>
+ <configOption name="remb_send_interval" default="0">
+ <synopsis>Sets the interval in milliseconds that a combined REMB frame will be sent to video sources</synopsis>
+ <description><para>
+ Sets the interval in milliseconds that a combined REMB frame will be sent
+ to video sources. This is done by taking all REMB frames that have been
+ received since the last REMB frame was sent, making a combined value,
+ and sending it to the source. A REMB frame contains receiver estimated
+ maximum bitrate information. By creating a combined REMB frame the
+ sender of video can be influenced on the bitrate they choose, allowing
+ better quality for all receivers.
+ </para></description>
+ </configOption>
+ <configOption name="remb_behavior" default="average">
+ <synopsis>Sets how REMB reports are generated from multiple sources</synopsis>
+ <description><para>
+ Sets how REMB reports are combined from multiple sources to form one. A REMB report
+ consists of information about the receiver estimated maximum bitrate. As a source
+ stream may be forwarded to multiple receivers the reports must be combined into
+ a single one which is sent to the sender.</para>
+ <enumlist>
+ <enum name="average">
+ <para>The average of all estimated maximum bitrates is taken and sent
+ to the sender.</para>
+ </enum>
+ <enum name="lowest">
+ <para>The lowest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ <enum name="highest">
+ <para>The highest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
<configOption name="template">
<synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
</configOption>
@@ -1661,6 +1694,24 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
}
ast_cli(a->fd,"Video Update Discard: %u\n", b_profile.video_update_discard);
+ ast_cli(a->fd,"REMB Send Interval: %u\n", b_profile.remb_send_interval);
+
+ switch (b_profile.flags
+ & (BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
+ | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE:
+ ast_cli(a->fd, "REMB Behavior: average\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST:
+ ast_cli(a->fd, "REMB Behavior: lowest\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST:
+ ast_cli(a->fd, "REMB Behavior: highest\n");
+ break;
+ default:
+ ast_assert(0);
+ break;
+ }
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
@@ -1998,6 +2049,30 @@ static int video_mode_handler(const struct aco_option *opt, struct ast_variable
return 0;
}
+static int remb_behavior_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct bridge_profile *b_profile = obj;
+
+ if (strcasecmp(var->name, "remb_behavior")) {
+ return -1;
+ }
+
+ ast_clear_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE |
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST |
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+
+ if (!strcasecmp(var->value, "average")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE);
+ } else if (!strcasecmp(var->value, "lowest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST);
+ } else if (!strcasecmp(var->value, "highest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct user_profile *u_profile = obj;
@@ -2231,6 +2306,8 @@ int conf_load_config(void)
aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
aco_option_register_custom(&cfg_info, "sound_", ACO_PREFIX, bridge_types, NULL, sound_option_handler, 0);
aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
+ aco_option_register(&cfg_info, "remb_send_interval", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_send_interval));
+ aco_option_register_custom(&cfg_info, "remb_behavior", ACO_EXACT, bridge_types, "average", remb_behavior_handler, 0);
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 044ab4003..0a0a5713f 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -76,6 +76,9 @@ enum bridge_profile_flags {
BRIDGE_OPT_RECORD_FILE_TIMESTAMP = (1 << 5), /*!< Set if the record file should have a timestamp appended */
BRIDGE_OPT_BINAURAL_ACTIVE = (1 << 6), /*!< Set if binaural convolution is activated */
BRIDGE_OPT_VIDEO_SRC_SFU = (1 << 7), /*!< Selective forwarding unit */
+ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE = (1 << 8), /*!< The average of all REMB reports is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST = (1 << 9), /*!< The lowest estimated maximum bitrate is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST = (1 << 10), /*!< The highest estimated maximum bitrate is sent to the sender */
};
enum conf_menu_action_id {
@@ -222,6 +225,7 @@ struct bridge_profile {
struct bridge_profile_sounds *sounds;
char regcontext[AST_MAX_CONTEXT];
unsigned int video_update_discard; /*!< Amount of time after sending a video update request that subsequent requests should be discarded */
+ unsigned int remb_send_interval; /*!< Interval at which a combined REMB frame is sent to video sources */
};
/*! \brief The structure that represents a conference bridge */
diff --git a/autoconf/ast_ext_tool_check.m4 b/autoconf/ast_ext_tool_check.m4
index ef762eb87..cbe109e4a 100644
--- a/autoconf/ast_ext_tool_check.m4
+++ b/autoconf/ast_ext_tool_check.m4
@@ -11,7 +11,7 @@ AC_DEFUN([AST_EXT_TOOL_CHECK],
AC_PATH_TOOL(CONFIG_$1, $2, No, [${$1_DIR}/bin:$PATH])
if test ! "x${CONFIG_$1}" = xNo; then
$1_INCLUDE=$(${CONFIG_$1} m4_default([$3],[--cflags]))
- $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-I|-I${$1_DIR}|g")
+ $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-I|-I${$1_DIR}|g" -e "s|-std=c99||g")
$1_LIB=$(${CONFIG_$1} m4_default([$4],[--libs]))
$1_LIB=$(echo ${$1_LIB} | $SED -e "s|-L|-L${$1_DIR}|g")
diff --git a/autoconf/ast_pkgconfig.m4 b/autoconf/ast_pkgconfig.m4
index ae7bbc086..3415ed547 100644
--- a/autoconf/ast_pkgconfig.m4
+++ b/autoconf/ast_pkgconfig.m4
@@ -5,7 +5,7 @@ AC_DEFUN([AST_PKG_CONFIG_CHECK],
if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
PKG_CHECK_MODULES($1, $2, [
PBX_$1=1
- $1_INCLUDE="$$1_CFLAGS"
+ $1_INCLUDE=$(echo ${$1_CFLAGS} | $SED -e "s|-std=c99||g")
$1_LIB="$$1_LIBS"
AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 libraries.])
], [
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 16e1fb897..f0a3fb42d 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -69,6 +69,15 @@
#define SOFTBRIDGE_VIDEO_DEST_LEN strlen(SOFTBRIDGE_VIDEO_DEST_PREFIX)
#define SOFTBRIDGE_VIDEO_DEST_SEPARATOR '_'
+struct softmix_remb_collector {
+ /*! The frame which will be given to each source stream */
+ struct ast_frame frame;
+ /*! The REMB to send to the source which is collecting REMB reports */
+ struct ast_rtp_rtcp_feedback feedback;
+ /*! The maximum bitrate */
+ unsigned int bitrate;
+};
+
struct softmix_stats {
/*! Each index represents a sample rate used above the internal rate. */
unsigned int sample_rates[16];
@@ -768,6 +777,10 @@ static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_ch
ast_stream_topology_free(sc->topology);
+ ao2_cleanup(sc->remb_collector);
+
+ AST_VECTOR_FREE(&sc->video_sources);
+
/* Drop mutex lock */
ast_mutex_destroy(&sc->lock);
@@ -1160,6 +1173,39 @@ static int softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_br
/*!
* \internal
+ * \brief Determine what to do with an RTCP frame.
+ * \since 15.4.0
+ *
+ * \param bridge Which bridge is getting the frame
+ * \param bridge_channel Which channel is writing the frame.
+ * \param frame What is being written.
+ */
+static void softmix_bridge_write_rtcp(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+
+ /* We only care about REMB reports right now. In the future we may be able to use sender or
+ * receiver reports to further tweak things, but not yet.
+ */
+ if (frame->subclass.integer != AST_RTP_RTCP_PSFB || feedback->fmt != AST_RTP_RTCP_FMT_REMB ||
+ bridge->softmix.video_mode.mode != AST_BRIDGE_VIDEO_MODE_SFU ||
+ !bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ return;
+ }
+
+ /* REMB is the total estimated maximum bitrate across all streams within the session, so we store
+ * only the latest report and use it everywhere.
+ */
+ ast_mutex_lock(&sc->lock);
+ sc->remb = feedback->remb;
+ ast_mutex_unlock(&sc->lock);
+
+ return;
+}
+
+/*!
+ * \internal
* \brief Determine what to do with a frame written into the bridge.
* \since 12.0.0
*
@@ -1204,6 +1250,9 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
case AST_FRAME_CONTROL:
res = softmix_bridge_write_control(bridge, bridge_channel, frame);
break;
+ case AST_FRAME_RTCP:
+ softmix_bridge_write_rtcp(bridge, bridge_channel, frame);
+ break;
case AST_FRAME_BRIDGE_ACTION:
res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
break;
@@ -1219,6 +1268,104 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
return res;
}
+static void remb_collect_report(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ struct softmix_bridge_data *softmix_data, struct softmix_channel *sc)
+{
+ int i;
+ unsigned int bitrate;
+
+ /* If there are no video sources that we are a receiver of then we have noone to
+ * report REMB to.
+ */
+ if (!AST_VECTOR_SIZE(&sc->video_sources)) {
+ return;
+ }
+
+ /* We evenly divide the available maximum bitrate across the video sources
+ * to this receiver so each source gets an equal slice.
+ */
+ bitrate = (sc->remb.br_mantissa << sc->remb.br_exp) / AST_VECTOR_SIZE(&sc->video_sources);
+
+ /* If this receiver has no bitrate yet ignore it */
+ if (!bitrate) {
+ return;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&sc->video_sources); ++i) {
+ struct softmix_remb_collector *collector;
+
+ /* The collector will always exist if a video source is in our list */
+ collector = AST_VECTOR_GET(&softmix_data->remb_collectors, AST_VECTOR_GET(&sc->video_sources, i));
+
+ if (!collector->bitrate) {
+ collector->bitrate = bitrate;
+ continue;
+ }
+
+ switch (bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior) {
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE:
+ collector->bitrate = (collector->bitrate + bitrate) / 2;
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST:
+ if (bitrate < collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST:
+ if (bitrate > collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ }
+ }
+}
+
+static void remb_send_report(struct ast_bridge_channel *bridge_channel, struct softmix_channel *sc)
+{
+ int i;
+
+ if (!sc->remb_collector) {
+ return;
+ }
+
+ /* If we have a new bitrate then use it for the REMB, if not we use the previous
+ * one until we know otherwise. This way the bitrate doesn't drop to 0 all of a sudden.
+ */
+ if (sc->remb_collector->bitrate) {
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->bitrate;
+ sc->remb_collector->feedback.remb.br_exp = 0;
+
+ /* The mantissa only has 18 bits available, so while it exceeds them we bump
+ * up the exp.
+ */
+ while (sc->remb_collector->feedback.remb.br_mantissa > 0x3ffff) {
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->feedback.remb.br_mantissa >> 1;
+ sc->remb_collector->feedback.remb.br_exp++;
+ }
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&bridge_channel->stream_map.to_bridge); ++i) {
+ int bridge_num = AST_VECTOR_GET(&bridge_channel->stream_map.to_bridge, i);
+
+ /* If this stream is not being provided to the bridge there can be no receivers of it
+ * so therefore no REMB reports.
+ */
+ if (bridge_num == -1) {
+ continue;
+ }
+
+ /* We need to update the frame with this stream, or else it won't be
+ * properly routed. We don't use the actual channel stream identifier as
+ * the bridging core will do the translation from bridge stream identifier to
+ * channel stream identifier.
+ */
+ sc->remb_collector->frame.stream_num = bridge_num;
+ ast_bridge_channel_queue_frame(bridge_channel, &sc->remb_collector->frame);
+ }
+
+ sc->remb_collector->bitrate = 0;
+}
+
static void gather_softmix_stats(struct softmix_stats *stats,
const struct softmix_bridge_data *softmix_data,
struct ast_bridge_channel *bridge_channel)
@@ -1440,6 +1587,7 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
struct ast_format *cur_slin = ast_format_cache_get_slin_by_rate(softmix_data->internal_rate);
unsigned int softmix_samples = SOFTMIX_SAMPLES(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
unsigned int softmix_datalen = SOFTMIX_DATALEN(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
+ int remb_update = 0;
if (softmix_datalen > MAX_DATALEN) {
/* This should NEVER happen, but if it does we need to know about it. Almost
@@ -1478,6 +1626,14 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
check_binaural_position_change(bridge, softmix_data);
#endif
+ /* If we need to do a REMB update to all video sources then do so */
+ if (bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU &&
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval &&
+ ast_tvdiff_ms(ast_tvnow(), softmix_data->last_remb_update) > bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ remb_update = 1;
+ softmix_data->last_remb_update = ast_tvnow();
+ }
+
/* Go through pulling audio from each factory that has it available */
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
struct softmix_channel *sc = bridge_channel->tech_pvt;
@@ -1512,6 +1668,9 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
#endif
mixing_array.used_entries++;
}
+ if (remb_update) {
+ remb_collect_report(bridge, bridge_channel, softmix_data, sc);
+ }
ast_mutex_unlock(&sc->lock);
}
@@ -1562,6 +1721,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
/* A frame is now ready for the channel. */
ast_bridge_channel_queue_frame(bridge_channel, &sc->write_frame);
+
+ if (remb_update) {
+ remb_send_report(bridge_channel, sc);
+ }
}
update_all_rates = 0;
@@ -1688,6 +1851,8 @@ static void softmix_bridge_data_destroy(struct softmix_bridge_data *softmix_data
}
ast_mutex_destroy(&softmix_data->lock);
ast_cond_destroy(&softmix_data->cond);
+ AST_VECTOR_RESET(&softmix_data->remb_collectors, ao2_cleanup);
+ AST_VECTOR_FREE(&softmix_data->remb_collectors);
ast_free(softmix_data);
}
@@ -1718,6 +1883,8 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
softmix_data->internal_mixing_interval);
#endif
+ AST_VECTOR_INIT(&softmix_data->remb_collectors, 0);
+
bridge->tech_pvt = softmix_data;
/* Start the mixing thread. */
@@ -1814,7 +1981,10 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
stream = ast_stream_topology_get_stream(topology, i);
if (is_video_dest(stream, source_channel_name, source_stream_name)) {
+ struct softmix_channel *sc = participant->tech_pvt;
+
AST_VECTOR_REPLACE(&participant->stream_map.to_channel, bridge_stream_position, i);
+ AST_VECTOR_APPEND(&sc->video_sources, bridge_stream_position);
break;
}
}
@@ -1824,6 +1994,58 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
}
/*!
+ * \brief Allocate a REMB collector
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct softmix_remb_collector *remb_collector_alloc(void)
+{
+ struct softmix_remb_collector *collector;
+
+ collector = ao2_alloc_options(sizeof(*collector), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!collector) {
+ return NULL;
+ }
+
+ collector->frame.frametype = AST_FRAME_RTCP;
+ collector->frame.subclass.integer = AST_RTP_RTCP_PSFB;
+ collector->feedback.fmt = AST_RTP_RTCP_FMT_REMB;
+ collector->frame.data.ptr = &collector->feedback;
+ collector->frame.datalen = sizeof(collector->feedback);
+
+ return collector;
+}
+
+/*!
+ * \brief Setup REMB collection for a particular bridge stream and channel.
+ *
+ * \param bridge The bridge
+ * \param bridge_channel Channel that is collecting REMB information
+ * \param bridge_stream_position The slot in the bridge where source video comes from
+ */
+static void remb_enable_collection(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ size_t bridge_stream_position)
+{
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+
+ if (!sc->remb_collector) {
+ sc->remb_collector = remb_collector_alloc();
+ if (!sc->remb_collector) {
+ /* This is not fatal. Things will still continue to work but we won't
+ * produce a REMB report to the sender.
+ */
+ return;
+ }
+ }
+
+ if (AST_VECTOR_REPLACE(&softmix_data->remb_collectors, bridge_stream_position, ao2_bump(sc->remb_collector))) {
+ ao2_ref(sc->remb_collector, -1);
+ }
+}
+
+/*!
* \brief stream_topology_changed callback
*
* For most video modes, nothing beyond the ordinary is required.
@@ -1835,6 +2057,8 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
*/
static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+ struct softmix_channel *sc;
struct ast_bridge_channel *participant;
struct ast_vector_int media_types;
int nths[AST_MEDIA_TYPE_END] = {0};
@@ -1852,11 +2076,22 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
AST_VECTOR_INIT(&media_types, AST_MEDIA_TYPE_END);
+ /* The bridge stream identifiers may change, so reset the mapping for them.
+ * When channels end up getting added back in they'll reuse their existing
+ * collector and won't need to allocate a new one (unless they were just added).
+ */
+ AST_VECTOR_RESET(&softmix_data->remb_collectors, ao2_cleanup);
+
/* First traversal: re-initialize all of the participants' stream maps */
AST_LIST_TRAVERSE(&bridge->channels, participant, entry) {
ast_bridge_channel_lock(participant);
+
AST_VECTOR_RESET(&participant->stream_map.to_channel, AST_VECTOR_ELEM_CLEANUP_NOOP);
AST_VECTOR_RESET(&participant->stream_map.to_bridge, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
+ sc = participant->tech_pvt;
+ AST_VECTOR_RESET(&sc->video_sources, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
ast_bridge_channel_unlock(participant);
}
@@ -1897,7 +2132,12 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
if (is_video_source(stream)) {
AST_VECTOR_APPEND(&media_types, AST_MEDIA_TYPE_VIDEO);
AST_VECTOR_REPLACE(&participant->stream_map.to_bridge, i, AST_VECTOR_SIZE(&media_types) - 1);
- AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, -1);
+ /*
+ * There are cases where we need to bidirectionally send frames, such as for REMB reports
+ * so we also map back to the channel.
+ */
+ AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, i);
+ remb_enable_collection(bridge, participant, AST_VECTOR_SIZE(&media_types) - 1);
/*
* Unlock the channel and participant to prevent
* potential deadlock in map_source_to_destinations().
diff --git a/bridges/bridge_softmix/include/bridge_softmix_internal.h b/bridges/bridge_softmix/include/bridge_softmix_internal.h
index f842acb5e..3aa90915d 100644
--- a/bridges/bridge_softmix/include/bridge_softmix_internal.h
+++ b/bridges/bridge_softmix/include/bridge_softmix_internal.h
@@ -50,6 +50,8 @@
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/translate.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/vector.h"
#ifdef BINAURAL_RENDERING
#include <fftw3.h>
@@ -124,6 +126,8 @@ struct video_follow_talker_data {
int energy_average;
};
+struct softmix_remb_collector;
+
/*! \brief Structure which contains per-channel mixing information */
struct softmix_channel {
/*! Lock to protect this structure */
@@ -169,6 +173,12 @@ struct softmix_channel {
struct video_follow_talker_data video_talker;
/*! The ideal stream topology for the channel */
struct ast_stream_topology *topology;
+ /*! The latest REMB report from this participant */
+ struct ast_rtp_rtcp_feedback_remb remb;
+ /*! The REMB collector for this channel, collects REMB from all video receivers */
+ struct softmix_remb_collector *remb_collector;
+ /*! The bridge streams which are feeding us video sources */
+ AST_VECTOR(, int) video_sources;
};
struct softmix_bridge_data {
@@ -202,6 +212,10 @@ struct softmix_bridge_data {
unsigned int binaural_init;
/*! The last time a video update was sent into the bridge */
struct timeval last_video_update;
+ /*! The last time a REMB frame was sent to each source of video */
+ struct timeval last_remb_update;
+ /*! Per-bridge stream REMB collectors, which flow back to video source */
+ AST_VECTOR(, struct softmix_remb_collector *) remb_collectors;
};
struct softmix_mixing_array {
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 5cb52a5b2..dde7416c3 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -718,7 +718,7 @@ static int chan_pjsip_answer(struct ast_channel *ast)
can occur between this thread and bridging (specifically when native bridging
attempts to do direct media) */
ast_channel_unlock(ast);
- res = ast_sip_push_task_synchronous(session->serializer, answer, session);
+ res = ast_sip_push_task_wait_serializer(session->serializer, answer, session);
if (res) {
if (res == -1) {
ast_log(LOG_ERROR,"Cannot answer '%s': Unable to push answer task to the threadpool.\n",
@@ -966,6 +966,16 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru
case AST_FRAME_CNG:
break;
case AST_FRAME_RTCP:
+ /* We only support writing out feedback */
+ if (frame->subclass.integer != AST_RTP_RTCP_PSFB || !media) {
+ return 0;
+ } else if (media->type != AST_MEDIA_TYPE_VIDEO) {
+ ast_debug(3, "Channel %s stream %d is of type '%s', not video! Unable to write RTCP feedback.\n",
+ ast_channel_name(ast), stream_num, ast_codec_media_type2str(media->type));
+ return 0;
+ } else if (media->write_callback) {
+ res = media->write_callback(session, media, frame);
+ }
break;
default:
ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
@@ -2492,10 +2502,10 @@ static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *t
req_data.topology = topology;
req_data.dest = data;
- /* Default failure value in case ast_sip_push_task_synchronous() itself fails. */
+ /* Default failure value in case ast_sip_push_task_wait_servant() itself fails. */
req_data.cause = AST_CAUSE_FAILURE;
- if (ast_sip_push_task_synchronous(NULL, request, &req_data)) {
+ if (ast_sip_push_task_wait_servant(NULL, request, &req_data)) {
*cause = req_data.cause;
return NULL;
}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 138021e82..46f9ad699 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -25856,7 +25856,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan)
{
struct ast_channel *c;
- RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_bridge *bridge;
if (req->ignore) {
return 0;
@@ -25872,6 +25872,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
}
append_history(p, "Xfer", "INVITE/Replace received");
+ /* Get a ref to ensure the channel cannot go away on us. */
c = ast_channel_ref(p->owner);
/* Fake call progress */
@@ -25886,21 +25887,24 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
ast_raw_answer(c);
- ast_channel_lock(replaces_chan);
- bridge = ast_channel_get_bridge(replaces_chan);
- ast_channel_unlock(replaces_chan);
-
+ bridge = ast_bridge_transfer_acquire_bridge(replaces_chan);
if (bridge) {
+ /*
+ * We have two refs of the channel. One is held in c and the other
+ * is notionally represented by p->owner. The impart is "stealing"
+ * the p->owner ref on success so the bridging system can have
+ * control of when the channel is hung up.
+ */
if (ast_bridge_impart(bridge, c, replaces_chan, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
ast_hangup(c);
- ast_channel_unref(c);
}
+ ao2_ref(bridge, -1);
} else {
ast_channel_move(replaces_chan, c);
ast_hangup(c);
- ast_channel_unref(c);
}
+ ast_channel_unref(c);
sip_pvt_lock(p);
return 0;
}
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index aa376f892..ce347dcd9 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -897,7 +897,7 @@ int pjsip_acf_channel_read(struct ast_channel *chan, const char *cmd, char *data
func_args.field = args.field;
func_args.buf = buf;
func_args.len = len;
- if (ast_sip_push_task_synchronous(func_args.session->serializer, read_pjsip, &func_args)) {
+ if (ast_sip_push_task_wait_serializer(func_args.session->serializer, read_pjsip, &func_args)) {
ast_log(LOG_WARNING, "Unable to read properties of channel %s: failed to push task\n", ast_channel_name(chan));
ao2_ref(func_args.session, -1);
return -1;
@@ -1219,7 +1219,7 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
mdata.media_type = AST_MEDIA_TYPE_VIDEO;
}
- return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, media_offer_write_av, &mdata);
}
int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
@@ -1390,7 +1390,7 @@ int pjsip_acf_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *d
ast_channel_unlock(chan);
- return ast_sip_push_task_synchronous(channel->session->serializer, dtmf_mode_refresh_cb, &rdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, dtmf_mode_refresh_cb, &rdata);
}
static int refresh_write_cb(void *obj)
@@ -1438,5 +1438,5 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c
rdata.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
}
- return ast_sip_push_task_synchronous(channel->session->serializer, refresh_write_cb, &rdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata);
}
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index e2d8a620d..8b276cdb8 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -235,6 +235,14 @@ type=bridge
; the video stream. Since a full frame can be large limiting how often they occur can
; reduce bandwidth usage at the cost of increasing how long it may take a newly joined
; channel to receive the video stream.
+;remb_send_interval=1000 ; Interval (in milliseconds) at which a combined REMB frame will be sent to sources of video.
+ ; A REMB frame contains receiver estimated maximum bitrate information. By creating a combined
+ ; frame and sending it to the sources of video the sender can be influenced on what bitrate
+ ; they choose allowing a better experience for the receivers. This defaults to 0, or disabled.
+;remb_behavior=average ; How the combined REMB report for an SFU video bridge is constructed. If set to "average" then
+ ; the estimated maximum bitrate of each receiver is used to construct an average bitrate. If
+ ; set to "lowest" the lowest maximum bitrate is forwarded to the sender. If set to "highest"
+ ; the highest maximum bitrate is forwarded to the sender. This defaults to "average".
; All sounds in the conference are customizable using the bridge profile options below.
; Simply state the option followed by the filename or full path of the filename after
diff --git a/configure b/configure
index 3a8c5051a..a7657be94 100755
--- a/configure
+++ b/configure
@@ -7792,7 +7792,7 @@ if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
DOWNLOAD_TO_STDOUT="${WGET} -q -O-"
DOWNLOAD_TIMEOUT='--timeout=$1'
-else if test "${CURL}" != ":" ; then
+elif test "${CURL}" != ":" ; then
DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\""
DOWNLOAD_TO_STDOUT="${CURL} -Ls"
DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)'
@@ -7844,7 +7844,6 @@ fi
DOWNLOAD_TIMEOUT='--timeout=$(or $2,$1)'
fi
fi
-fi
@@ -13428,7 +13427,7 @@ else
$as_echo "yes" >&6; }
PBX_LIBEDIT=1
- LIBEDIT_INCLUDE="$LIBEDIT_CFLAGS"
+ LIBEDIT_INCLUDE=$(echo ${LIBEDIT_CFLAGS} | $SED -e "s|-std=c99||g")
LIBEDIT_LIB="$LIBEDIT_LIBS"
$as_echo "#define HAVE_LIBEDIT 1" >>confdefs.h
@@ -14102,7 +14101,7 @@ fi
if test ! "x${CONFIG_LIBXML2}" = xNo; then
LIBXML2_INCLUDE=$(${CONFIG_LIBXML2} --cflags)
- LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g")
+ LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g" -e "s|-std=c99||g")
LIBXML2_LIB=$(${CONFIG_LIBXML2} --libs)
LIBXML2_LIB=$(echo ${LIBXML2_LIB} | $SED -e "s|-L|-L${LIBXML2_DIR}|g")
@@ -19202,15 +19201,15 @@ if test $ac_cv_sizeof_int = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS int" >>confdefs.h
-else if test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS long" >>confdefs.h
-else if test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS long long" >>confdefs.h
-fi ; fi ; fi
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dladdr in dlfcn.h" >&5
$as_echo_n "checking for dladdr in dlfcn.h... " >&6; }
@@ -20374,15 +20373,13 @@ cat >>confdefs.h <<_ACEOF
_ACEOF
GSM_OK=1
- else
- if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+ elif test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
cat >>confdefs.h <<_ACEOF
#define HAVE_GSM_GSM_HEADER 1
_ACEOF
- GSM_OK=1
- fi
+ GSM_OK=1
fi
if test "${GSM_OK}" = "1" ; then
GSM_LIB="-lgsm"
@@ -20494,7 +20491,7 @@ else
$as_echo "yes" >&6; }
PBX_ILBC=1
- ILBC_INCLUDE="$ILBC_CFLAGS"
+ ILBC_INCLUDE=$(echo ${ILBC_CFLAGS} | $SED -e "s|-std=c99||g")
ILBC_LIB="$ILBC_LIBS"
$as_echo "#define HAVE_ILBC 1" >>confdefs.h
@@ -22632,7 +22629,7 @@ fi
if test ! "x${CONFIG_MYSQLCLIENT}" = xNo; then
MYSQLCLIENT_INCLUDE=$(${CONFIG_MYSQLCLIENT} --cflags)
- MYSQLCLIENT_INCLUDE=$(echo ${MYSQLCLIENT_INCLUDE} | $SED -e "s|-I|-I${MYSQLCLIENT_DIR}|g")
+ MYSQLCLIENT_INCLUDE=$(echo ${MYSQLCLIENT_INCLUDE} | $SED -e "s|-I|-I${MYSQLCLIENT_DIR}|g" -e "s|-std=c99||g")
MYSQLCLIENT_LIB=$(${CONFIG_MYSQLCLIENT} --libs)
MYSQLCLIENT_LIB=$(echo ${MYSQLCLIENT_LIB} | $SED -e "s|-L|-L${MYSQLCLIENT_DIR}|g")
@@ -22849,7 +22846,7 @@ fi
if test ! "x${CONFIG_NEON}" = xNo; then
NEON_INCLUDE=$(${CONFIG_NEON} --cflags)
- NEON_INCLUDE=$(echo ${NEON_INCLUDE} | $SED -e "s|-I|-I${NEON_DIR}|g")
+ NEON_INCLUDE=$(echo ${NEON_INCLUDE} | $SED -e "s|-I|-I${NEON_DIR}|g" -e "s|-std=c99||g")
NEON_LIB=$(${CONFIG_NEON} --libs)
NEON_LIB=$(echo ${NEON_LIB} | $SED -e "s|-L|-L${NEON_DIR}|g")
@@ -22969,7 +22966,7 @@ fi
if test ! "x${CONFIG_NEON29}" = xNo; then
NEON29_INCLUDE=$(${CONFIG_NEON29} --cflags)
- NEON29_INCLUDE=$(echo ${NEON29_INCLUDE} | $SED -e "s|-I|-I${NEON29_DIR}|g")
+ NEON29_INCLUDE=$(echo ${NEON29_INCLUDE} | $SED -e "s|-I|-I${NEON29_DIR}|g" -e "s|-std=c99||g")
NEON29_LIB=$(${CONFIG_NEON29} --libs)
NEON29_LIB=$(echo ${NEON29_LIB} | $SED -e "s|-L|-L${NEON29_DIR}|g")
@@ -23113,7 +23110,7 @@ fi
if test ! "x${CONFIG_NETSNMP}" = xNo; then
NETSNMP_INCLUDE=$(${CONFIG_NETSNMP} --cflags)
- NETSNMP_INCLUDE=$(echo ${NETSNMP_INCLUDE} | $SED -e "s|-I|-I${NETSNMP_DIR}|g")
+ NETSNMP_INCLUDE=$(echo ${NETSNMP_INCLUDE} | $SED -e "s|-I|-I${NETSNMP_DIR}|g" -e "s|-std=c99||g")
NETSNMP_LIB=$(${CONFIG_NETSNMP} --agent-libs)
NETSNMP_LIB=$(echo ${NETSNMP_LIB} | $SED -e "s|-L|-L${NETSNMP_DIR}|g")
@@ -24599,7 +24596,7 @@ else
$as_echo "yes" >&6; }
PBX_PJPROJECT=1
- PJPROJECT_INCLUDE="$PJPROJECT_CFLAGS"
+ PJPROJECT_INCLUDE=$(echo ${PJPROJECT_CFLAGS} | $SED -e "s|-std=c99||g")
PJPROJECT_LIB="$PJPROJECT_LIBS"
$as_echo "#define HAVE_PJPROJECT 1" >>confdefs.h
@@ -25653,7 +25650,7 @@ else
$as_echo "yes" >&6; }
PBX_PYTHONDEV=1
- PYTHONDEV_INCLUDE="$PYTHONDEV_CFLAGS"
+ PYTHONDEV_INCLUDE=$(echo ${PYTHONDEV_CFLAGS} | $SED -e "s|-std=c99||g")
PYTHONDEV_LIB="$PYTHONDEV_LIBS"
$as_echo "#define HAVE_PYTHONDEV 1" >>confdefs.h
@@ -25839,7 +25836,7 @@ else
$as_echo "yes" >&6; }
PBX_PORTAUDIO=1
- PORTAUDIO_INCLUDE="$PORTAUDIO_CFLAGS"
+ PORTAUDIO_INCLUDE=$(echo ${PORTAUDIO_CFLAGS} | $SED -e "s|-std=c99||g")
PORTAUDIO_LIB="$PORTAUDIO_LIBS"
$as_echo "#define HAVE_PORTAUDIO 1" >>confdefs.h
@@ -28898,6 +28895,7 @@ fi
+for ver in 5.3 5.2 5.1; do
if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
pbxlibdir=""
@@ -28912,117 +28910,14 @@ if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.3" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.3... " >&6; }
-if ${ac_cv_lib_lua5_3_luaL_newstate+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.3 ${pbxlibdir} -lm $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 luaL_newstate ();
-int
-main ()
-{
-return luaL_newstate ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_3_luaL_newstate=yes
-else
- ac_cv_lib_lua5_3_luaL_newstate=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_lua5_3_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_3_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_3_luaL_newstate" = xyes; then :
- AST_LUA_FOUND=yes
-else
- AST_LUA_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.3 -lm"
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- LUA_INCLUDE="-I${LUA_DIR}/include"
- fi
- LUA_INCLUDE="${LUA_INCLUDE} "
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.3/lua.h" "ac_cv_header_lua5_3_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_3_lua_h" = xyes; then :
- LUA_HEADER_FOUND=1
-else
- LUA_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${LUA_HEADER_FOUND}" = "x0" ; then
- LUA_LIB=""
- LUA_INCLUDE=""
- else
-
- PBX_LUA=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LUA 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.3"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.3"
- fi
-fi
-
-
-if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
- pbxlibdir=""
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- if test -d ${LUA_DIR}/lib; then
- pbxlibdir="-L${LUA_DIR}/lib"
- else
- pbxlibdir="-L${LUA_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.2" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.2... " >&6; }
-if ${ac_cv_lib_lua5_2_luaL_newstate+:} false; then :
+ as_ac_Lib=`$as_echo "ac_cv_lib_lua${ver}''_luaL_newstate" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua${ver}" >&5
+$as_echo_n "checking for luaL_newstate in -llua${ver}... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.2 ${pbxlibdir} -lm $LIBS"
+LIBS="-llua${ver} ${pbxlibdir} -lm $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -29042,121 +28937,18 @@ return luaL_newstate ();
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_2_luaL_newstate=yes
+ eval "$as_ac_Lib=yes"
else
- ac_cv_lib_lua5_2_luaL_newstate=no
+ eval "$as_ac_Lib=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_lua5_2_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_2_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_2_luaL_newstate" = xyes; then :
- AST_LUA_FOUND=yes
-else
- AST_LUA_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.2 -lm"
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- LUA_INCLUDE="-I${LUA_DIR}/include"
- fi
- LUA_INCLUDE="${LUA_INCLUDE} "
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.2/lua.h" "ac_cv_header_lua5_2_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_2_lua_h" = xyes; then :
- LUA_HEADER_FOUND=1
-else
- LUA_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${LUA_HEADER_FOUND}" = "x0" ; then
- LUA_LIB=""
- LUA_INCLUDE=""
- else
-
- PBX_LUA=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LUA 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.2"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.2"
- fi
-fi
-
-
-if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
- pbxlibdir=""
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- if test -d ${LUA_DIR}/lib; then
- pbxlibdir="-L${LUA_DIR}/lib"
- else
- pbxlibdir="-L${LUA_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.1" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.1... " >&6; }
-if ${ac_cv_lib_lua5_1_luaL_newstate+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.1 ${pbxlibdir} -lm $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 luaL_newstate ();
-int
-main ()
-{
-return luaL_newstate ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_1_luaL_newstate=yes
-else
- ac_cv_lib_lua5_1_luaL_newstate=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_lua5_1_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_1_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_1_luaL_newstate" = xyes; then :
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
AST_LUA_FOUND=yes
else
AST_LUA_FOUND=no
@@ -29167,7 +28959,7 @@ fi
# now check for the header.
if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.1 -lm"
+ LUA_LIB="${pbxlibdir} -llua${ver} -lm"
# if --with-LUA=DIR has been specified, use it.
if test "x${LUA_DIR}" != "x"; then
LUA_INCLUDE="-I${LUA_DIR}/include"
@@ -29177,8 +28969,9 @@ fi
# check for the header
ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.1/lua.h" "ac_cv_header_lua5_1_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_1_lua_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_lua${ver}/lua.h" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "lua${ver}/lua.h" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
LUA_HEADER_FOUND=1
else
LUA_HEADER_FOUND=0
@@ -29202,13 +28995,15 @@ _ACEOF
fi
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.1"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.1"
+ if test "x${PBX_LUA}" = "x1" ; then
+ if test x"${LUA_DIR}" = x; then
+ LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua${ver}"
+ else
+ LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua${ver}"
+ fi
+ break;
fi
-fi
+done
# Some distributions (like openSUSE and NetBSD) remove the 5.x suffix.
@@ -32345,7 +32140,7 @@ else
$as_echo "yes" >&6; }
PBX_GMIME=1
- GMIME_INCLUDE="$GMIME_CFLAGS"
+ GMIME_INCLUDE=$(echo ${GMIME_CFLAGS} | $SED -e "s|-std=c99||g")
GMIME_LIB="$GMIME_LIBS"
$as_echo "#define HAVE_GMIME 1" >>confdefs.h
@@ -33254,7 +33049,7 @@ fi
if test ! "x${CONFIG_SDL}" = xNo; then
SDL_INCLUDE=$(${CONFIG_SDL} --cflags)
- SDL_INCLUDE=$(echo ${SDL_INCLUDE} | $SED -e "s|-I|-I${SDL_DIR}|g")
+ SDL_INCLUDE=$(echo ${SDL_INCLUDE} | $SED -e "s|-I|-I${SDL_DIR}|g" -e "s|-std=c99||g")
SDL_LIB=$(${CONFIG_SDL} --libs)
SDL_LIB=$(echo ${SDL_LIB} | $SED -e "s|-L|-L${SDL_DIR}|g")
@@ -33782,7 +33577,7 @@ else
$as_echo "yes" >&6; }
PBX_GTK2=1
- GTK2_INCLUDE="$GTK2_CFLAGS"
+ GTK2_INCLUDE=$(echo ${GTK2_CFLAGS} | $SED -e "s|-std=c99||g")
GTK2_LIB="$GTK2_LIBS"
$as_echo "#define HAVE_GTK2 1" >>confdefs.h
@@ -33893,7 +33688,7 @@ else
$as_echo "yes" >&6; }
PBX_SYSTEMD=1
- SYSTEMD_INCLUDE="$SYSTEMD_CFLAGS"
+ SYSTEMD_INCLUDE=$(echo ${SYSTEMD_CFLAGS} | $SED -e "s|-std=c99||g")
SYSTEMD_LIB="$SYSTEMD_LIBS"
$as_echo "#define HAVE_SYSTEMD 1" >>confdefs.h
@@ -35772,9 +35567,9 @@ fi
${ac_cv_path_EGREP} 'CURSES|GTK2|OSARCH|NEWT' makeopts > makeopts.acbak2
if test "x${ac_cv_path_CMP}" = "x:"; then
( cd `pwd`/menuselect && ./configure )
-else if ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
+elif ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
( cd `pwd`/menuselect && ./configure )
-fi ; fi
+fi
rm makeopts.acbak makeopts.acbak2
diff --git a/configure.ac b/configure.ac
index 702ff4f97..128b0a0f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -293,7 +293,7 @@ if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
DOWNLOAD_TO_STDOUT="${WGET} -q -O-"
DOWNLOAD_TIMEOUT='--timeout=$1'
-else if test "${CURL}" != ":" ; then
+elif test "${CURL}" != ":" ; then
DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\""
DOWNLOAD_TO_STDOUT="${CURL} -Ls"
DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)'
@@ -305,7 +305,6 @@ else
DOWNLOAD_TIMEOUT='--timeout=$(or $2,$1)'
fi
fi
-fi
AC_SUBST(DOWNLOAD)
AC_SUBST(DOWNLOAD_TO_STDOUT)
AC_SUBST(DOWNLOAD_TIMEOUT)
@@ -1426,11 +1425,11 @@ AC_CHECK_SIZEOF(fd_set.fds_bits)
# correctly if the size is wrong.
if test $ac_cv_sizeof_int = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [int], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-else if test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [long], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-else if test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [long long], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-fi ; fi ; fi
+fi
AC_MSG_CHECKING(for dladdr in dlfcn.h)
PBX_DLADDR=0
@@ -1542,11 +1541,9 @@ if test "${USE_GSM}" != "no"; then
if test "${GSM_HEADER_FOUND}" = "1" ; then
AC_DEFINE_UNQUOTED([HAVE_GSM_HEADER], 1, [Define to indicate that gsm.h has no prefix for its location])
GSM_OK=1
- else
- if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
- AC_DEFINE_UNQUOTED([HAVE_GSM_GSM_HEADER], 1, [Define to indicate that gsm.h is in gsm/gsm.h])
- GSM_OK=1
- fi
+ elif test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+ AC_DEFINE_UNQUOTED([HAVE_GSM_GSM_HEADER], 1, [Define to indicate that gsm.h is in gsm/gsm.h])
+ GSM_OK=1
fi
if test "${GSM_OK}" = "1" ; then
GSM_LIB="-lgsm"
@@ -2357,32 +2354,17 @@ if test -z "$__opus_include" -o x"$__opus_include" = x" " ; then
fi
AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
-AST_EXT_LIB_CHECK([LUA], [lua5.3], [luaL_newstate], [lua5.3/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.3"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.3"
- fi
-fi
-
-AST_EXT_LIB_CHECK([LUA], [lua5.2], [luaL_newstate], [lua5.2/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.2"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.2"
- fi
-fi
-
-AST_EXT_LIB_CHECK([LUA], [lua5.1], [luaL_newstate], [lua5.1/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.1"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.1"
+for ver in 5.3 5.2 5.1; do
+ AST_EXT_LIB_CHECK([LUA], lua${ver}, [luaL_newstate], lua${ver}/lua.h, [-lm])
+ if test "x${PBX_LUA}" = "x1" ; then
+ if test x"${LUA_DIR}" = x; then
+ LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua${ver}"
+ else
+ LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua${ver}"
+ fi
+ break;
fi
-fi
+done
# Some distributions (like openSUSE and NetBSD) remove the 5.x suffix.
AST_EXT_LIB_CHECK([LUA], [lua], [luaL_newstate], [lua.h], [-lm])
@@ -2733,9 +2715,9 @@ AC_OUTPUT
${ac_cv_path_EGREP} 'CURSES|GTK2|OSARCH|NEWT' makeopts > makeopts.acbak2
if test "x${ac_cv_path_CMP}" = "x:"; then
( cd `pwd`/menuselect && ./configure )
-else if ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
+elif ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
( cd `pwd`/menuselect && ./configure )
-fi ; fi
+fi
rm makeopts.acbak makeopts.acbak2
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index 8d5c50211..3584085af 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -126,6 +126,24 @@ struct ast_bridge_video_talker_src_data {
struct ast_channel *chan_old_vsrc;
};
+/*! \brief REMB report behaviors */
+enum ast_bridge_video_sfu_remb_behavior {
+ /*! The average of all reports is sent to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE = 0,
+ /*! The lowest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST,
+ /*! The highest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST,
+};
+
+/*! \brief This is used for selective forwarding unit configuration */
+struct ast_bridge_video_sfu_data {
+ /*! The interval at which a REMB report is generated and sent */
+ unsigned int remb_send_interval;
+ /*! How the combined REMB report is generated */
+ enum ast_bridge_video_sfu_remb_behavior remb_behavior;
+};
+
/*! \brief Data structure that defines a video source mode */
struct ast_bridge_video_mode {
enum ast_bridge_video_mode_type mode;
@@ -133,7 +151,9 @@ struct ast_bridge_video_mode {
union {
struct ast_bridge_video_single_src_data single_src_data;
struct ast_bridge_video_talker_src_data talker_src_data;
+ struct ast_bridge_video_sfu_data sfu_data;
} mode_data;
+ /*! The minimum interval between video updates */
unsigned int video_update_discard;
};
@@ -912,6 +932,26 @@ void ast_bridge_set_sfu_video_mode(struct ast_bridge *bridge);
void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int video_update_discard);
/*!
+ * \brief Set the interval at which a combined REMB frame will be sent to video sources
+ *
+ * \param bridge Bridge to set the REMB send interval on
+ * \param remb_send_interval The REMB send interval
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
+ */
+void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval);
+
+/*!
+ * \brief Set the REMB report generation behavior on a bridge
+ *
+ * \param bridge Bridge to set the REMB behavior on
+ * \param behavior How REMB reports are generated
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
+ */
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior);
+
+/*!
* \brief Update information about talker energy for talker src video mode.
*/
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyfame);
@@ -945,6 +985,17 @@ void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *
*/
const char *ast_bridge_video_mode_to_string(enum ast_bridge_video_mode_type video_mode);
+/*!
+ * \brief Acquire the channel's bridge for transfer purposes.
+ * \since 13.21.0
+ *
+ * \param chan Channel involved in a transfer.
+ *
+ * \return The bridge the channel is in or NULL if it either isn't
+ * in a bridge or should not be considered to be in a bridge.
+ */
+struct ast_bridge *ast_bridge_transfer_acquire_bridge(struct ast_channel *chan);
+
enum ast_transfer_result {
/*! The transfer completed successfully */
AST_BRIDGE_TRANSFER_SUCCESS,
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 26439986b..d2ae39baf 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -258,6 +258,14 @@ struct ast_sip_contact {
AST_STRING_FIELD(user_agent);
/*! The name of the aor this contact belongs to */
AST_STRING_FIELD(aor);
+ /*! Asterisk Server name */
+ AST_STRING_FIELD(reg_server);
+ /*! IP-address of the Via header in REGISTER request */
+ AST_STRING_FIELD(via_addr);
+ /*! Content of the Call-ID header in REGISTER request */
+ AST_STRING_FIELD(call_id);
+ /*! The name of the endpoint that added the contact */
+ AST_STRING_FIELD(endpoint_name);
);
/*! Absolute time that this contact is no longer valid after */
struct timeval expiration_time;
@@ -269,16 +277,8 @@ struct ast_sip_contact {
double qualify_timeout;
/*! Endpoint that added the contact, only available in observers */
struct ast_sip_endpoint *endpoint;
- /*! Asterisk Server name */
- AST_STRING_FIELD_EXTENDED(reg_server);
- /*! IP-address of the Via header in REGISTER request */
- AST_STRING_FIELD_EXTENDED(via_addr);
- /* Port of the Via header in REGISTER request */
+ /*! Port of the Via header in REGISTER request */
int via_port;
- /*! Content of the Call-ID header in REGISTER request */
- AST_STRING_FIELD_EXTENDED(call_id);
- /*! The name of the endpoint that added the contact */
- AST_STRING_FIELD_EXTENDED(endpoint_name);
/*! If true delete the contact on Asterisk restart/boot */
int prune_on_boot;
};
@@ -751,6 +751,8 @@ struct ast_sip_endpoint {
AST_STRING_FIELD(message_context);
/*! Accountcode to auto-set on channels */
AST_STRING_FIELD(accountcode);
+ /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
+ AST_STRING_FIELD(incoming_mwi_mailbox);
);
/*! Configuration for extensions */
struct ast_sip_endpoint_extensions extensions;
@@ -812,8 +814,6 @@ struct ast_sip_endpoint {
unsigned int refer_blind_progress;
/*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
unsigned int notify_early_inuse_ringing;
- /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
- AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
};
/*! URI parameter for symmetric transport */
@@ -1407,7 +1407,7 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
* the next item on the SIP socket(s) can be serviced. On incoming messages,
* Asterisk automatically will push the request to a servant thread. When your
* module callback is called, processing will already be in a servant. However,
- * for other PSJIP events, such as transaction state changes due to timer
+ * for other PJSIP events, such as transaction state changes due to timer
* expirations, your module will be called into from a PJSIP thread. If you
* are called into from a PJSIP thread, then you should push whatever processing
* is needed to a servant as soon as possible. You can discern if you are currently
@@ -1543,28 +1543,92 @@ struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg);
int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
/*!
- * \brief Push a task to SIP servants and wait for it to complete
+ * \brief Push a task to SIP servants and wait for it to complete.
*
- * Like \ref ast_sip_push_task except that it blocks until the task completes.
+ * Like \ref ast_sip_push_task except that it blocks until the task
+ * completes. If the current thread is a SIP servant thread then the
+ * task executes immediately. Otherwise, the specified serializer
+ * executes the task and the current thread waits for it to complete.
*
- * \warning \b Never use this function in a SIP servant thread. This can potentially
- * cause a deadlock. If you are in a SIP servant thread, just call your function
- * in-line.
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
*
- * \warning \b Never hold locks that may be acquired by a SIP servant thread when
- * calling this function. Doing so may cause a deadlock if all SIP servant threads
- * are blocked waiting to acquire the lock while the thread holding the lock is
- * waiting for a free SIP servant thread.
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function. Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread.
*
- * \param serializer The SIP serializer to which the task belongs. May be NULL.
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea. You don't have control over which thread executes the
+ * destructor. Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task if the
+ * current thread is not a SIP servant. NULL if any of the default
+ * serializers can be used.
* \param sip_task The task to execute
* \param task_data The parameter to pass to the task when it executes
- * \retval 0 Success
- * \retval -1 Failure
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
+ * \brief Push a task to SIP servants and wait for it to complete.
+ * \deprecated Replaced with ast_sip_push_task_wait_servant().
*/
int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
/*!
+ * \brief Push a task to the serializer and wait for it to complete.
+ *
+ * Like \ref ast_sip_push_task except that it blocks until the task is
+ * completed by the specified serializer. If the specified serializer
+ * is the current thread then the task executes immediately.
+ *
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
+ *
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function. Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread for the serializer to execute in.
+ *
+ * \warning \b Never hold locks that may be acquired by the serializer
+ * when calling this function. Doing so will cause a deadlock.
+ *
+ * \warning \b Never use this function in the pjsip monitor thread (It
+ * is a SIP servant thread). This is likely to cause a deadlock.
+ *
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea. You don't have control over which thread executes the
+ * destructor. Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task. NULL if
+ * any of the default serializers can be used.
+ * \param sip_task The task to execute
+ * \param task_data The parameter to pass to the task when it executes
+ *
+ * \note It is generally better to call
+ * ast_sip_push_task_wait_servant() if you pass NULL for the
+ * serializer parameter.
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
* \brief Determine if the current thread is a SIP servant thread
*
* \retval 0 This is not a SIP servant thread
@@ -1588,13 +1652,13 @@ enum ast_sip_scheduler_task_flags {
/*!
* Run at a fixed interval.
- * Stop scheduling if the callback returns 0.
+ * Stop scheduling if the callback returns <= 0.
* Any other value is ignored.
*/
AST_SIP_SCHED_TASK_FIXED = (0 << 0),
/*!
* Run at a variable interval.
- * Stop scheduling if the callback returns 0.
+ * Stop scheduling if the callback returns <= 0.
* Any other return value is used as the new interval.
*/
AST_SIP_SCHED_TASK_VARIABLE = (1 << 0),
@@ -1620,16 +1684,23 @@ enum ast_sip_scheduler_task_flags {
*/
AST_SIP_SCHED_TASK_DATA_FREE = ( 1 << 3 ),
- /*! \brief AST_SIP_SCHED_TASK_PERIODIC
- * The task is scheduled at multiples of interval
+ /*!
+ * \brief The task is scheduled at multiples of interval
* \see Interval
*/
AST_SIP_SCHED_TASK_PERIODIC = (0 << 4),
- /*! \brief AST_SIP_SCHED_TASK_DELAY
- * The next invocation of the task is at last finish + interval
+ /*!
+ * \brief The next invocation of the task is at last finish + interval
* \see Interval
*/
AST_SIP_SCHED_TASK_DELAY = (1 << 4),
+ /*!
+ * \brief The scheduled task's events are tracked in the debug log.
+ * \details
+ * Schedule events such as scheduling, running, rescheduling, canceling,
+ * and destroying are logged about the task.
+ */
+ AST_SIP_SCHED_TASK_TRACK = (1 << 5),
};
/*!
@@ -1673,7 +1744,7 @@ struct ast_sip_sched_task;
*
*/
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
- int interval, ast_sip_task sip_task, char *name, void *task_data,
+ int interval, ast_sip_task sip_task, const char *name, void *task_data,
enum ast_sip_scheduler_task_flags flags);
/*!
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index b552948d2..3426b2a1e 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -292,10 +292,16 @@ struct ast_rtp_payload_type {
#define AST_RTP_RTCP_SR 200
/*! Receiver Report */
#define AST_RTP_RTCP_RR 201
+/*! Transport Layer Feed Back (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_RTPFB 205
/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
-#define AST_RTP_RTCP_PSFB 206
+#define AST_RTP_RTCP_PSFB 206
/* Common RTCP feedback message types */
+/*! Generic NACK (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_FMT_NACK 1
+/*! Picture loss indication (From RFC4585) */
+#define AST_RTP_RTCP_FMT_PLI 1
/*! Full INTRA-frame Request (From RFC5104) */
#define AST_RTP_RTCP_FMT_FIR 4
/*! REMB Information (From draft-alvestrand-rmcat-remb-03) */
diff --git a/include/asterisk/stasis_bridges.h b/include/asterisk/stasis_bridges.h
index 05d356cc2..a455a5b02 100644
--- a/include/asterisk/stasis_bridges.h
+++ b/include/asterisk/stasis_bridges.h
@@ -46,6 +46,8 @@ struct ast_bridge_snapshot {
AST_STRING_FIELD(creator);
/*! Name given to the bridge by its creator */
AST_STRING_FIELD(name);
+ /*! Unique ID of the channel providing video, if one exists */
+ AST_STRING_FIELD(video_source_id);
);
/*! AO2 container of bare channel uniqueid strings participating in the bridge.
* Allocated from ast_str_container_alloc() */
@@ -60,8 +62,6 @@ struct ast_bridge_snapshot {
unsigned int num_active;
/*! The video mode of the bridge */
enum ast_bridge_video_mode_type video_mode;
- /*! Unique ID of the channel providing video, if one exists */
- AST_STRING_FIELD_EXTENDED(video_source_id);
};
/*!
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 4da7fa465..b892cda9e 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -578,6 +578,13 @@ void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_
#ifdef AST_DEVMODE
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ _ast_assert(0, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ return __VA_ARGS__; \
+ }\
+})
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
if (__builtin_expect(!condition, 1)) {
@@ -586,6 +593,12 @@ static void force_inline _ast_assert(int condition, const char *condition_str, c
}
#else
#define ast_assert(a)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ return __VA_ARGS__; \
+ }\
+})
#endif
/*!
diff --git a/main/bridge.c b/main/bridge.c
index 1109c4b76..2b347fd3f 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -3850,6 +3850,24 @@ void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int
ast_bridge_unlock(bridge);
}
+void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval)
+{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
+ ast_bridge_lock(bridge);
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval = remb_send_interval;
+ ast_bridge_unlock(bridge);
+}
+
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior)
+{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
+ ast_bridge_lock(bridge);
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior = behavior;
+ ast_bridge_unlock(bridge);
+}
+
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyframe)
{
struct ast_bridge_video_talker_src_data *data;
@@ -4420,7 +4438,7 @@ static void set_transfer_variables_all(struct ast_channel *transferer, struct ao
ao2_iterator_destroy(&iter);
}
-static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
+struct ast_bridge *ast_bridge_transfer_acquire_bridge(struct ast_channel *chan)
{
struct ast_bridge *bridge;
@@ -4461,7 +4479,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
return AST_BRIDGE_TRANSFER_FAIL;
}
- bridge = acquire_bridge(transferer);
+ bridge = ast_bridge_transfer_acquire_bridge(transferer);
if (!bridge) {
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
@@ -4708,8 +4726,8 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
const char *app = NULL;
int hangup_target = 0;
- to_transferee_bridge = acquire_bridge(to_transferee);
- to_target_bridge = acquire_bridge(to_transfer_target);
+ to_transferee_bridge = ast_bridge_transfer_acquire_bridge(to_transferee);
+ to_target_bridge = ast_bridge_transfer_acquire_bridge(to_transfer_target);
transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
to_transfer_target, to_target_bridge, NULL, NULL);
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 4b68559de..59b9685ef 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -246,8 +246,7 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
return NULL;
}
- if (ast_string_field_init(snapshot, 128)
- || ast_string_field_init_extended(snapshot, video_source_id)) {
+ if (ast_string_field_init(snapshot, 128)) {
ao2_ref(snapshot, -1);
return NULL;
diff --git a/menuselect/configure b/menuselect/configure
index 6e5331edd..a0aa10928 100755
--- a/menuselect/configure
+++ b/menuselect/configure
@@ -4392,7 +4392,7 @@ fi
if test ! "x${CONFIG_LIBXML2}" = xNo; then
LIBXML2_INCLUDE=$(${CONFIG_LIBXML2} --cflags)
- LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g")
+ LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g" -e "s|-std=c99||g")
LIBXML2_LIB=$(${CONFIG_LIBXML2} --libs)
LIBXML2_LIB=$(echo ${LIBXML2_LIB} | $SED -e "s|-L|-L${LIBXML2_DIR}|g")
@@ -4633,7 +4633,7 @@ else
$as_echo "yes" >&6; }
PBX_GTK2=1
- GTK2_INCLUDE="$GTK2_CFLAGS"
+ GTK2_INCLUDE=$(echo ${GTK2_CFLAGS} | $SED -e "s|-std=c99||g")
GTK2_LIB="$GTK2_LIBS"
$as_echo "#define HAVE_GTK2 1" >>confdefs.h
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index 55b14c934..1c8728cf7 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -333,7 +333,6 @@ static int ast_moh_files_next(struct ast_channel *chan)
}
} else {
state->announcement = 0;
- state->samples = 0;
}
if (!state->class->total_files) {
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 7c9929740..19e6e1d13 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -2743,7 +2743,7 @@ static int register_service(void *data)
int ast_sip_register_service(pjsip_module *module)
{
- return ast_sip_push_task_synchronous(NULL, register_service, &module);
+ return ast_sip_push_task_wait_servant(NULL, register_service, &module);
}
static int unregister_service(void *data)
@@ -2759,7 +2759,7 @@ static int unregister_service(void *data)
void ast_sip_unregister_service(pjsip_module *module)
{
- ast_sip_push_task_synchronous(NULL, unregister_service, &module);
+ ast_sip_push_task_wait_servant(NULL, unregister_service, &module);
}
static struct ast_sip_authenticator *registered_authenticator;
@@ -3009,7 +3009,7 @@ static char *cli_dump_endpt(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_SHOWUSAGE;
}
- ast_sip_push_task_synchronous(NULL, do_cli_dump_endpt, a);
+ ast_sip_push_task_wait_servant(NULL, do_cli_dump_endpt, a);
return CLI_SUCCESS;
}
@@ -3801,8 +3801,6 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
{
const pjsip_method *pmethod = get_pjsip_method(method);
- ast_assert(endpoint != NULL);
-
if (!pmethod) {
ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);
return -1;
@@ -3811,6 +3809,7 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
if (dlg) {
return create_in_dialog_request(pmethod, dlg, tdata);
} else {
+ ast_assert(endpoint != NULL);
return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
}
}
@@ -4485,21 +4484,30 @@ static int serializer_pool_setup(void)
return 0;
}
+static struct ast_taskprocessor *serializer_pool_pick(void)
+{
+ struct ast_taskprocessor *serializer;
+
+ unsigned int pos;
+
+ /*
+ * Pick a serializer to use from the pool.
+ *
+ * Note: We don't care about any reentrancy behavior
+ * when incrementing serializer_pool_pos. If it gets
+ * incorrectly incremented it doesn't matter.
+ */
+ pos = serializer_pool_pos++;
+ pos %= SERIALIZER_POOL_SIZE;
+ serializer = serializer_pool[pos];
+
+ return serializer;
+}
+
int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
{
if (!serializer) {
- unsigned int pos;
-
- /*
- * Pick a serializer to use from the pool.
- *
- * Note: We don't care about any reentrancy behavior
- * when incrementing serializer_pool_pos. If it gets
- * incorrectly incremented it doesn't matter.
- */
- pos = serializer_pool_pos++;
- pos %= SERIALIZER_POOL_SIZE;
- serializer = serializer_pool[pos];
+ serializer = serializer_pool_pick();
}
return ast_taskprocessor_push(serializer, sip_task, task_data);
@@ -4523,9 +4531,8 @@ static int sync_task(void *data)
/*
* Once we unlock std->lock after signaling, we cannot access
- * std again. The thread waiting within
- * ast_sip_push_task_synchronous() is free to continue and
- * release its local variable (std).
+ * std again. The thread waiting within ast_sip_push_task_wait()
+ * is free to continue and release its local variable (std).
*/
ast_mutex_lock(&std->lock);
std->complete = 1;
@@ -4535,15 +4542,11 @@ static int sync_task(void *data)
return ret;
}
-int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+static int ast_sip_push_task_wait(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
{
/* This method is an onion */
struct sync_task_data std;
- if (ast_sip_thread_is_servant()) {
- return sip_task(task_data);
- }
-
memset(&std, 0, sizeof(std));
ast_mutex_init(&std.lock);
ast_cond_init(&std.cond, NULL);
@@ -4567,6 +4570,42 @@ int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*si
return std.fail;
}
+int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ if (ast_sip_thread_is_servant()) {
+ return sip_task(task_data);
+ }
+
+ return ast_sip_push_task_wait(serializer, sip_task, task_data);
+}
+
+int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ return ast_sip_push_task_wait_servant(serializer, sip_task, task_data);
+}
+
+int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ if (!serializer) {
+ /* Caller doesn't care which PJSIP serializer the task executes under. */
+ serializer = serializer_pool_pick();
+ if (!serializer) {
+ /* No serializer picked to execute the task */
+ return -1;
+ }
+ }
+ if (ast_taskprocessor_is_task(serializer)) {
+ /*
+ * We are the requested serializer so we must execute
+ * the task now or deadlock waiting on ourself to
+ * execute it.
+ */
+ return sip_task(task_data);
+ }
+
+ return ast_sip_push_task_wait(serializer, sip_task, task_data);
+}
+
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
{
size_t chars_to_copy = MIN(size - 1, pj_strlen(src));
@@ -5192,7 +5231,7 @@ static int reload_module(void)
* We must wait for the reload to complete so multiple
* reloads cannot happen at the same time.
*/
- if (ast_sip_push_task_synchronous(NULL, reload_configuration_task, NULL)) {
+ if (ast_sip_push_task_wait_servant(NULL, reload_configuration_task, NULL)) {
ast_log(LOG_WARNING, "Failed to reload PJSIP\n");
return -1;
}
@@ -5209,7 +5248,7 @@ static int unload_module(void)
/* The thread this is called from cannot call PJSIP/PJLIB functions,
* so we have to push the work to the threadpool to handle
*/
- ast_sip_push_task_synchronous(NULL, unload_pjsip, NULL);
+ ast_sip_push_task_wait_servant(NULL, unload_pjsip, NULL);
ast_sip_destroy_scheduler();
serializer_pool_shutdown();
ast_threadpool_shutdown(sip_threadpool);
diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c
index dfd92404b..ed2b5d232 100644
--- a/res/res_pjsip/config_system.c
+++ b/res/res_pjsip/config_system.c
@@ -282,5 +282,5 @@ static int system_create_resolver_and_set_nameservers(void *data)
void ast_sip_initialize_dns(void)
{
- ast_sip_push_task_synchronous(NULL, system_create_resolver_and_set_nameservers, NULL);
+ ast_sip_push_task_wait_servant(NULL, system_create_resolver_and_set_nameservers, NULL);
}
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 15c03769b..dd7c7049d 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -267,7 +267,7 @@ static void sip_transport_state_destroy(void *obj)
{
struct ast_sip_transport_state *state = obj;
- ast_sip_push_task_synchronous(NULL, destroy_sip_transport_state, state);
+ ast_sip_push_task_wait_servant(NULL, destroy_sip_transport_state, state);
}
/*! \brief Destructor for ast_sip_transport state information */
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 22da80577..6e79dc40b 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -133,11 +133,6 @@ static void *contact_alloc(const char *name)
return NULL;
}
- ast_string_field_init_extended(contact, endpoint_name);
- ast_string_field_init_extended(contact, reg_server);
- ast_string_field_init_extended(contact, via_addr);
- ast_string_field_init_extended(contact, call_id);
-
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
*aor_separator = '\0';
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 3094f248e..fb84a1f60 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2248,8 +2248,6 @@ void *ast_sip_endpoint_alloc(const char *name)
return NULL;
}
- ast_string_field_init_extended(endpoint, incoming_mwi_mailbox);
-
if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
ao2_cleanup(endpoint);
return NULL;
diff --git a/res/res_pjsip/pjsip_scheduler.c b/res/res_pjsip/pjsip_scheduler.c
index e4459da66..bbf666fd7 100644
--- a/res/res_pjsip/pjsip_scheduler.c
+++ b/res/res_pjsip/pjsip_scheduler.c
@@ -28,6 +28,7 @@
#include "asterisk/res_pjsip.h"
#include "include/res_pjsip_private.h"
#include "asterisk/res_pjsip_cli.h"
+#include "asterisk/taskprocessor.h"
#define TASK_BUCKETS 53
@@ -36,31 +37,31 @@ static struct ao2_container *tasks;
static int task_count;
struct ast_sip_sched_task {
- /*! ast_sip_sched task id */
- uint32_t task_id;
- /*! ast_sched scheudler id */
- int current_scheduler_id;
- /*! task is currently running */
- int is_running;
- /*! task */
- ast_sip_task task;
+ /*! The serializer to be used (if any) (Holds a ref) */
+ struct ast_taskprocessor *serializer;
/*! task data */
void *task_data;
- /*! reschedule interval in milliseconds */
- int interval;
- /*! the time the task was queued */
+ /*! task function */
+ ast_sip_task task;
+ /*! the time the task was originally scheduled/queued */
struct timeval when_queued;
/*! the last time the task was started */
struct timeval last_start;
/*! the last time the task was ended */
struct timeval last_end;
+ /*! When the periodic task is next expected to run */
+ struct timeval next_periodic;
+ /*! reschedule interval in milliseconds */
+ int interval;
+ /*! ast_sched scheudler id */
+ int current_scheduler_id;
+ /*! task is currently running */
+ int is_running;
/*! times run */
int run_count;
/*! the task reschedule, cleanup and policy flags */
enum ast_sip_scheduler_task_flags flags;
- /*! the serializer to be used (if any) */
- struct ast_taskprocessor *serializer;
- /* A name to be associated with the task */
+ /*! A name to be associated with the task */
char name[0];
};
@@ -76,14 +77,22 @@ static int push_to_serializer(const void *data);
*/
static int run_task(void *data)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, ao2_bump(data), ao2_cleanup);
+ RAII_VAR(struct ast_sip_sched_task *, schtd, data, ao2_cleanup);
int res;
int delay;
+ if (!schtd->interval) {
+ /* Task was cancelled while waiting to be executed by the serializer */
+ return -1;
+ }
+
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Running %s\n", schtd, schtd->name);
+ }
ao2_lock(schtd);
schtd->last_start = ast_tvnow();
schtd->is_running = 1;
- schtd->run_count++;
+ ++schtd->run_count;
ao2_unlock(schtd);
res = schtd->task(schtd->task_data);
@@ -93,10 +102,10 @@ static int run_task(void *data)
schtd->last_end = ast_tvnow();
/*
- * Don't restart if the task returned 0 or if the interval
+ * Don't restart if the task returned <= 0 or if the interval
* was set to 0 while the task was running
*/
- if (!res || !schtd->interval) {
+ if (res <= 0 || !schtd->interval) {
schtd->interval = 0;
ao2_unlock(schtd);
ao2_unlink(tasks, schtd);
@@ -110,18 +119,31 @@ static int run_task(void *data)
if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) {
delay = schtd->interval;
} else {
- delay = schtd->interval - (ast_tvdiff_ms(schtd->last_end, schtd->last_start) % schtd->interval);
+ int64_t diff;
+
+ /* Determine next periodic interval we need to expire. */
+ do {
+ schtd->next_periodic = ast_tvadd(schtd->next_periodic,
+ ast_samp2tv(schtd->interval, 1000));
+ diff = ast_tvdiff_ms(schtd->next_periodic, schtd->last_end);
+ } while (diff <= 0);
+ delay = diff;
}
- schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, (const void *)schtd);
+ schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, schtd);
if (schtd->current_scheduler_id < 0) {
schtd->interval = 0;
ao2_unlock(schtd);
+ ast_log(LOG_ERROR, "Sched %p: Failed to reschedule task %s\n", schtd, schtd->name);
ao2_unlink(tasks, schtd);
return -1;
}
ao2_unlock(schtd);
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Rescheduled %s for %d ms\n", schtd, schtd->name,
+ delay);
+ }
return 0;
}
@@ -133,9 +155,32 @@ static int run_task(void *data)
static int push_to_serializer(const void *data)
{
struct ast_sip_sched_task *schtd = (struct ast_sip_sched_task *)data;
+ int sched_id;
+ ao2_lock(schtd);
+ sched_id = schtd->current_scheduler_id;
+ schtd->current_scheduler_id = -1;
+ ao2_unlock(schtd);
+ if (sched_id < 0) {
+ /* Task was cancelled while waiting on the lock */
+ return 0;
+ }
+
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Ready to run %s\n", schtd, schtd->name);
+ }
+ ao2_t_ref(schtd, +1, "Give ref to run_task()");
if (ast_sip_push_task(schtd->serializer, run_task, schtd)) {
- ao2_ref(schtd, -1);
+ /*
+ * Oh my. Have to cancel the scheduled item because we
+ * unexpectedly cannot run it anymore.
+ */
+ ao2_unlink(tasks, schtd);
+ ao2_lock(schtd);
+ schtd->interval = 0;
+ ao2_unlock(schtd);
+
+ ao2_t_ref(schtd, -1, "Failed so release run_task() ref");
}
return 0;
@@ -144,48 +189,54 @@ static int push_to_serializer(const void *data)
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
{
int res;
+ int sched_id;
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
- if (schtd->current_scheduler_id < 0 || schtd->interval <= 0) {
- ao2_unlock_and_unref(schtd);
- return 0;
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Canceling %s\n", schtd, schtd->name);
}
+ /*
+ * Prevent any tasks in the serializer queue from
+ * running and restarting the scheduled item on us
+ * first.
+ */
+ ao2_lock(schtd);
schtd->interval = 0;
- ao2_unlock_and_unref(schtd);
+
+ sched_id = schtd->current_scheduler_id;
+ schtd->current_scheduler_id = -1;
+ ao2_unlock(schtd);
+ res = ast_sched_del(scheduler_context, sched_id);
+
ao2_unlink(tasks, schtd);
- res = ast_sched_del(scheduler_context, schtd->current_scheduler_id);
return res;
}
int ast_sip_sched_task_cancel_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int res;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_cancel(schtd);
+ res = ast_sip_sched_task_cancel(schtd);
+ ao2_ref(schtd, -1);
+ return res;
}
int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
struct timeval *queued, struct timeval *last_start, struct timeval *last_end)
{
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
+ ao2_lock(schtd);
if (queued) {
memcpy(queued, &schtd->when_queued, sizeof(struct timeval));
}
@@ -195,8 +246,7 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
if (last_end) {
memcpy(last_end, &schtd->last_end, sizeof(struct timeval));
}
-
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return 0;
}
@@ -204,18 +254,21 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
int ast_sip_sched_task_get_times_by_name(const char *name,
struct timeval *queued, struct timeval *last_start, struct timeval *last_end)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int res;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_get_times(schtd, queued, last_start, last_end);
+ res = ast_sip_sched_task_get_times(schtd, queued, last_start, last_end);
+ ao2_ref(schtd, -1);
+ return res;
}
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen)
@@ -224,13 +277,9 @@ int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, si
return -1;
}
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
+ ao2_lock(schtd);
ast_copy_string(name, schtd->name, maxlen);
-
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return 0;
}
@@ -241,9 +290,7 @@ int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
struct timeval since_when;
struct timeval now;
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
+ ao2_lock(schtd);
if (schtd->interval) {
delay = schtd->interval;
@@ -262,103 +309,136 @@ int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
delay = -1;
}
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return delay;
}
int ast_sip_sched_task_get_next_run_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int next_run;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_get_next_run(schtd);
+ next_run = ast_sip_sched_task_get_next_run(schtd);
+ ao2_ref(schtd, -1);
+ return next_run;
}
int ast_sip_sched_is_task_running(struct ast_sip_sched_task *schtd)
{
- if (!schtd) {
- return 0;
- }
-
- return schtd->is_running;
+ return schtd ? schtd->is_running : 0;
}
int ast_sip_sched_is_task_running_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int is_running;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return 0;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return 0;
}
- return schtd->is_running;
+ is_running = schtd->is_running;
+ ao2_ref(schtd, -1);
+ return is_running;
}
-static void schtd_destructor(void *data)
+static void schtd_dtor(void *data)
{
struct ast_sip_sched_task *schtd = data;
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Destructor %s\n", schtd, schtd->name);
+ }
if (schtd->flags & AST_SIP_SCHED_TASK_DATA_AO2) {
/* release our own ref, then release the callers if asked to do so */
ao2_ref(schtd->task_data, (schtd->flags & AST_SIP_SCHED_TASK_DATA_FREE) ? -2 : -1);
} else if (schtd->task_data && (schtd->flags & AST_SIP_SCHED_TASK_DATA_FREE)) {
ast_free(schtd->task_data);
}
+ ast_taskprocessor_unreference(schtd->serializer);
}
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
- int interval, ast_sip_task sip_task, char *name, void *task_data, enum ast_sip_scheduler_task_flags flags)
+ int interval, ast_sip_task sip_task, const char *name, void *task_data,
+ enum ast_sip_scheduler_task_flags flags)
{
#define ID_LEN 13 /* task_deadbeef */
struct ast_sip_sched_task *schtd;
int res;
- if (interval < 0) {
+ if (interval <= 0) {
return NULL;
}
- schtd = ao2_alloc((sizeof(*schtd) + (!ast_strlen_zero(name) ? strlen(name) : ID_LEN) + 1), schtd_destructor);
+ schtd = ao2_alloc((sizeof(*schtd) + (!ast_strlen_zero(name) ? strlen(name) : ID_LEN) + 1),
+ schtd_dtor);
if (!schtd) {
return NULL;
}
- schtd->task_id = ast_atomic_fetchadd_int(&task_count, 1);
- schtd->serializer = serializer;
+ schtd->serializer = ao2_bump(serializer);
+ schtd->task_data = task_data;
schtd->task = sip_task;
+ schtd->interval = interval;
+ schtd->flags = flags;
if (!ast_strlen_zero(name)) {
strcpy(schtd->name, name); /* Safe */
} else {
- sprintf(schtd->name, "task_%08x", schtd->task_id);
+ uint32_t task_id;
+
+ task_id = ast_atomic_fetchadd_int(&task_count, 1);
+ sprintf(schtd->name, "task_%08x", task_id);
+ }
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Scheduling %s for %d ms\n", schtd, schtd->name,
+ interval);
}
- schtd->task_data = task_data;
- schtd->flags = flags;
- schtd->interval = interval;
schtd->when_queued = ast_tvnow();
+ if (!(schtd->flags & AST_SIP_SCHED_TASK_DELAY)) {
+ schtd->next_periodic = ast_tvadd(schtd->when_queued,
+ ast_samp2tv(schtd->interval, 1000));
+ }
if (flags & AST_SIP_SCHED_TASK_DATA_AO2) {
ao2_ref(task_data, +1);
}
- res = ast_sched_add(scheduler_context, interval, push_to_serializer, (const void *)schtd);
+
+ /*
+ * We must put it in the 'tasks' container before scheduling
+ * the task because we don't want the push_to_serializer()
+ * sched task to "remove" it on failure before we even put
+ * it in. If this happens then nothing would remove it from
+ * the 'tasks' container.
+ */
+ ao2_link(tasks, schtd);
+
+ /*
+ * Lock so we are guaranteed to get the sched id set before
+ * the push_to_serializer() sched task can clear it.
+ */
+ ao2_lock(schtd);
+ res = ast_sched_add(scheduler_context, interval, push_to_serializer, schtd);
+ schtd->current_scheduler_id = res;
+ ao2_unlock(schtd);
if (res < 0) {
+ ao2_unlink(tasks, schtd);
ao2_ref(schtd, -1);
return NULL;
- } else {
- schtd->current_scheduler_id = res;
- ao2_link(tasks, schtd);
}
return schtd;
@@ -367,16 +447,17 @@ struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *seria
static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- struct ao2_iterator i;
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_tasks;
struct ast_sip_sched_task *schtd;
- const char *log_format = ast_logger_get_dateformat();
+ const char *log_format;
struct ast_tm tm;
char queued[32];
char last_start[32];
char next_start[32];
int datelen;
- struct timeval now = ast_tvnow();
- const char *separator = "======================================";
+ struct timeval now;
+ static const char separator[] = "=============================================";
switch (cmd) {
case CLI_INIT:
@@ -392,26 +473,47 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_SHOWUSAGE;
}
+ /* Get a sorted snapshot of the scheduled tasks */
+ sorted_tasks = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ ast_sip_sched_task_sort_fn, NULL);
+ if (!sorted_tasks) {
+ return CLI_SUCCESS;
+ }
+ if (ao2_container_dup(sorted_tasks, tasks, 0)) {
+ ao2_ref(sorted_tasks, -1);
+ return CLI_SUCCESS;
+ }
+
+ now = ast_tvnow();
+ log_format = ast_logger_get_dateformat();
+
ast_localtime(&now, &tm, NULL);
datelen = ast_strftime(queued, sizeof(queued), log_format, &tm);
ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n");
- ast_cli(a->fd, " %1$-24s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n",
+ ast_cli(a->fd, "%1$-45s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n",
"Task Name", "Interval", "Times Run", "State",
datelen, "Queued", "Last Started", "Next Start", "( secs)");
- ast_cli(a->fd, " %1$-24.24s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n",
+ ast_cli(a->fd, "%1$-45.45s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n",
separator, separator, separator, separator,
datelen, separator, separator, datelen + 8, separator);
+ iter = ao2_iterator_init(sorted_tasks, AO2_ITERATOR_UNLINK);
+ for (; (schtd = ao2_iterator_next(&iter)); ao2_ref(schtd, -1)) {
+ int next_run_sec;
+ struct timeval next;
+
+ ao2_lock(schtd);
- ao2_ref(tasks, +1);
- ao2_rdlock(tasks);
- i = ao2_iterator_init(tasks, 0);
- while ((schtd = ao2_iterator_next(&i))) {
- int next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000;
- struct timeval next = ast_tvadd(now, (struct timeval) {next_run_sec, 0});
+ next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000;
+ if (next_run_sec < 0) {
+ /* Scheduled task is now canceled */
+ ao2_unlock(schtd);
+ continue;
+ }
+ next = ast_tvadd(now, ast_tv(next_run_sec, 0));
ast_localtime(&schtd->when_queued, &tm, NULL);
ast_strftime(queued, sizeof(queued), log_format, &tm);
@@ -426,7 +528,7 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
ast_localtime(&next, &tm, NULL);
ast_strftime(next_start, sizeof(next_start), log_format, &tm);
- ast_cli(a->fd, " %1$-24.24s %2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n",
+ ast_cli(a->fd, "%1$-46.46s%2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n",
schtd->name,
schtd->interval / 1000.0,
schtd->run_count,
@@ -434,11 +536,10 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
datelen, queued, last_start,
next_start,
next_run_sec);
- ao2_cleanup(schtd);
+ ao2_unlock(schtd);
}
- ao2_iterator_destroy(&i);
- ao2_unlock(tasks);
- ao2_ref(tasks, -1);
+ ao2_iterator_destroy(&iter);
+ ao2_ref(sorted_tasks, -1);
ast_cli(a->fd, "\n");
return CLI_SUCCESS;
@@ -450,7 +551,8 @@ static struct ast_cli_entry cli_commands[] = {
int ast_sip_initialize_scheduler(void)
{
- if (!(scheduler_context = ast_sched_context_create())) {
+ scheduler_context = ast_sched_context_create();
+ if (!scheduler_context) {
ast_log(LOG_ERROR, "Failed to create scheduler. Aborting load\n");
return -1;
}
@@ -461,8 +563,9 @@ int ast_sip_initialize_scheduler(void)
return -1;
}
- tasks = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
- TASK_BUCKETS, ast_sip_sched_task_hash_fn, ast_sip_sched_task_sort_fn, ast_sip_sched_task_cmp_fn);
+ tasks = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK,
+ AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, TASK_BUCKETS, ast_sip_sched_task_hash_fn,
+ ast_sip_sched_task_sort_fn, ast_sip_sched_task_cmp_fn);
if (!tasks) {
ast_log(LOG_ERROR, "Failed to allocate task container. Aborting load\n");
ast_sched_context_destroy(scheduler_context);
@@ -479,7 +582,21 @@ int ast_sip_destroy_scheduler(void)
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
if (scheduler_context) {
+ if (tasks) {
+ struct ao2_iterator iter;
+ struct ast_sip_sched_task *schtd;
+
+ /* Cancel all scheduled tasks */
+ iter = ao2_iterator_init(tasks, 0);
+ while ((schtd = ao2_iterator_next(&iter))) {
+ ast_sip_sched_task_cancel(schtd);
+ ao2_ref(schtd, -1);
+ }
+ ao2_iterator_destroy(&iter);
+ }
+
ast_sched_context_destroy(scheduler_context);
+ scheduler_context = NULL;
}
ao2_cleanup(tasks);
diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c
index 6c0f9151d..798a1cde6 100644
--- a/res/res_pjsip_header_funcs.c
+++ b/res/res_pjsip_header_funcs.c
@@ -153,7 +153,7 @@ static const struct ast_datastore_info header_datastore = {
.type = "header_datastore",
};
-/*! \brief Data structure used for ast_sip_push_task_synchronous */
+/*! \brief Data structure used for ast_sip_push_task_wait_serializer */
struct header_data {
struct ast_sip_channel_pvt *channel;
char *header_name;
@@ -480,11 +480,11 @@ static int func_read_header(struct ast_channel *chan, const char *function, char
header_data.len = len;
if (!strcasecmp(args.action, "read")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, read_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ read_header, &header_data);
} else if (!strcasecmp(args.action, "remove")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, remove_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ remove_header, &header_data);
} else {
ast_log(AST_LOG_ERROR,
"Unknown action '%s' is not valid, must be 'read' or 'remove'.\n",
@@ -539,14 +539,14 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
header_data.len = 0;
if (!strcasecmp(args.action, "add")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, add_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ add_header, &header_data);
} else if (!strcasecmp(args.action, "update")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, update_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ update_header, &header_data);
} else if (!strcasecmp(args.action, "remove")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, remove_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ remove_header, &header_data);
} else {
ast_log(AST_LOG_ERROR,
"Unknown action '%s' is not valid, must be 'add', 'update', or 'remove'.\n",
diff --git a/res/res_pjsip_history.c b/res/res_pjsip_history.c
index ab035a296..eed06eed8 100644
--- a/res/res_pjsip_history.c
+++ b/res/res_pjsip_history.c
@@ -1385,7 +1385,7 @@ static int unload_module(void)
ast_cli_unregister_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
ast_sip_unregister_service(&logging_module);
- ast_sip_push_task_synchronous(NULL, clear_history_entries, NULL);
+ ast_sip_push_task_wait_servant(NULL, clear_history_entries, NULL);
AST_VECTOR_FREE(&vector_history);
ast_pjproject_caching_pool_destroy(&cachingpool);
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index 253cf9ac8..98a75c964 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -25,6 +25,7 @@
#include "asterisk.h"
#include <pjsip.h>
+#include <pjsip_ua.h>
#include "asterisk/cli.h"
#include "asterisk/config.h"
@@ -32,12 +33,13 @@
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
#include "asterisk/sorcery.h"
/*** DOCUMENTATION
<manager name="PJSIPNotify" language="en_US">
<synopsis>
- Send a NOTIFY to either an endpoint or an arbitrary URI.
+ Send a NOTIFY to either an endpoint, an arbitrary URI, or inside a SIP dialog.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
@@ -47,6 +49,9 @@
<parameter name="URI" required="false">
<para>Abritrary URI to which to send the NOTIFY.</para>
</parameter>
+ <parameter name="channel" required="false">
+ <para>Channel name to send the NOTIFY. Must be a PJSIP channel.</para>
+ </parameter>
<parameter name="Variable" required="true">
<para>Appends variables as headers/content to the NOTIFY. If the variable is
named <literal>Content</literal>, then the value will compose the body
@@ -55,14 +60,14 @@
</parameter>
</syntax>
<description>
- <para>Sends a NOTIFY to an endpoint or an arbitrary URI.</para>
+ <para>Sends a NOTIFY to an endpoint, an arbitrary URI, or inside a SIP dialog.</para>
<para>All parameters for this event must be specified in the body of this
request via multiple <literal>Variable: name=value</literal> sequences.</para>
- <note><para>One (and only one) of <literal>Endpoint</literal> or
- <literal>URI</literal> must be specified. If <literal>URI</literal> is used,
- the default outbound endpoint will be used to send the message. If the default
- outbound endpoint isn't configured, this command can not send to an arbitrary
- URI.</para></note>
+ <note><para>One (and only one) of <literal>Endpoint</literal>,
+ <literal>URI</literal>, or <literal>Channel</literal> must be specified.
+ If <literal>URI</literal> is used, the default outbound endpoint will be used
+ to send the message. If the default outbound endpoint isn't configured, this command
+ can not send to an arbitrary URI.</para></note>
</description>
</manager>
<configInfo name="res_pjsip_notify" language="en_US">
@@ -289,6 +294,16 @@ struct notify_uri_data {
void (*build_notify)(pjsip_tx_data *, void *);
};
+/*!
+ * \internal
+ * \brief Structure to hold task data for notifications (channel variant)
+ */
+struct notify_channel_data {
+ struct ast_sip_session *session;
+ void *info;
+ void (*build_notify)(pjsip_tx_data *, void *);
+};
+
static void notify_cli_uri_data_destroy(void *obj)
{
struct notify_uri_data *data = obj;
@@ -381,6 +396,19 @@ static void notify_ami_uri_data_destroy(void *obj)
ast_variables_destroy(info);
}
+/*!
+ * \internal
+ * \brief Destroy the notify AMI channel data releasing any resources.
+ */
+static void notify_ami_channel_data_destroy(void *obj)
+{
+ struct notify_channel_data *data = obj;
+ struct ast_variable *info = data->info;
+
+ ao2_cleanup(data->session);
+ ast_variables_destroy(info);
+}
+
static void build_ami_notify(pjsip_tx_data *tdata, void *info);
/*!
@@ -432,6 +460,28 @@ static struct notify_uri_data* notify_ami_uri_data_create(
/*!
* \internal
+ * \brief Construct a notify channel data object for AMI.
+ */
+static struct notify_channel_data *notify_ami_channel_data_create(
+ struct ast_sip_session *session, void *info)
+{
+ struct notify_channel_data *data;
+
+ data = ao2_alloc_options(sizeof(*data), notify_ami_channel_data_destroy,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!data) {
+ return NULL;
+ }
+
+ data->session = session;
+ data->info = info;
+ data->build_notify = build_ami_notify;
+
+ return data;
+}
+
+/*!
+ * \internal
* \brief Checks if the given header name is not allowed.
*
* \details Some headers are not allowed to be set by the user within the
@@ -672,9 +722,45 @@ static int notify_uri(void *obj)
return 0;
}
+/*!
+ * \internal
+ * \brief Send a notify request to a channel.
+ */
+static int notify_channel(void *obj)
+{
+ RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup);
+ pjsip_tx_data *tdata;
+ struct pjsip_dialog *dlg;
+
+ if (!data->session->channel
+ || !data->session->inv_session
+ || data->session->inv_session->state < PJSIP_INV_STATE_EARLY
+ || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ return -1;
+ }
+
+ ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));
+
+ dlg = data->session->inv_session->dlg;
+
+ if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
+ return -1;
+ }
+
+ ast_sip_add_header(tdata, "Subscription-State", "terminated");
+ data->build_notify(tdata, data->info);
+
+ if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {
+ return -1;
+ }
+
+ return 0;
+}
+
enum notify_result {
SUCCESS,
INVALID_ENDPOINT,
+ INVALID_CHANNEL,
ALLOC_ERROR,
TASK_PUSH_ERROR
};
@@ -684,6 +770,10 @@ typedef struct notify_data *(*task_data_create)(
typedef struct notify_uri_data *(*task_uri_data_create)(
const char *uri, void *info);
+
+typedef struct notify_channel_data *(*task_channel_data_create)(
+ struct ast_sip_session *session, void *info);
+
/*!
* \internal
* \brief Send a NOTIFY request to the endpoint within a threaded task.
@@ -734,6 +824,68 @@ static enum notify_result push_notify_uri(const char *uri, void *info,
/*!
* \internal
+ * \brief Send a NOTIFY request in a channel within an threaded task.
+ */
+static enum notify_result push_notify_channel(const char *channel_name, void *info,
+ task_channel_data_create data_create)
+{
+ struct notify_channel_data *data;
+ struct ast_channel *ch;
+ struct ast_sip_session *session;
+ struct ast_sip_channel_pvt *ch_pvt;
+
+ /* note: this increases the refcount of the channel */
+ ch = ast_channel_get_by_name(channel_name);
+ if (!ch) {
+ ast_debug(1, "No channel found with name %s", channel_name);
+ return INVALID_CHANNEL;
+ }
+
+ if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {
+ ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);
+ ast_channel_unref(ch);
+ return INVALID_CHANNEL;
+ }
+
+ ast_channel_lock(ch);
+ ch_pvt = ast_channel_tech_pvt(ch);
+ session = ch_pvt->session;
+
+ if (!session || !session->inv_session
+ || session->inv_session->state < PJSIP_INV_STATE_EARLY
+ || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ ast_debug(1, "No active session for channel %s\n", channel_name);
+ ast_channel_unlock(ch);
+ ast_channel_unref(ch);
+ return INVALID_CHANNEL;
+ }
+
+ ao2_ref(session, +1);
+ ast_channel_unlock(ch);
+
+ /* don't keep a reference to the channel, we've got a reference to the session */
+ ast_channel_unref(ch);
+
+ /*
+ * data_create will take ownership of the session,
+ * and take care of releasing the ref.
+ */
+ data = data_create(session, info);
+ if (!data) {
+ ao2_ref(session, -1);
+ return ALLOC_ERROR;
+ }
+
+ if (ast_sip_push_task(session->serializer, notify_channel, data)) {
+ ao2_ref(data, -1);
+ return TASK_PUSH_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/*!
+ * \internal
* \brief Do completion on the endpoint.
*/
static char *cli_complete_endpoint(const char *word, int state)
@@ -915,6 +1067,10 @@ static void manager_notify_endpoint(struct mansession *s,
}
switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
+ case INVALID_CHANNEL:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
case INVALID_ENDPOINT:
ast_variables_destroy(vars);
astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
@@ -944,6 +1100,10 @@ static void manager_notify_uri(struct mansession *s,
struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
+ case INVALID_CHANNEL:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
case INVALID_ENDPOINT:
/* Shouldn't be possible. */
ast_assert(0);
@@ -964,22 +1124,70 @@ static void manager_notify_uri(struct mansession *s,
/*!
* \internal
+ * \brief Completes SIPNotify AMI command in channel mode.
+ */
+static void manager_notify_channel(struct mansession *s,
+ const struct message *m, const char *channel)
+{
+ struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
+
+ switch (push_notify_channel(channel, vars, notify_ami_channel_data_create)) {
+ case INVALID_CHANNEL:
+ ast_variables_destroy(vars);
+ astman_send_error(s, m, "Channel not found");
+ break;
+ case INVALID_ENDPOINT:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
+ case ALLOC_ERROR:
+ ast_variables_destroy(vars);
+ astman_send_error(s, m, "Unable to allocate NOTIFY task data");
+ break;
+ case TASK_PUSH_ERROR:
+ /* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */
+ astman_send_error(s, m, "Unable to push Notify task");
+ break;
+ case SUCCESS:
+ astman_send_ack(s, m, "NOTIFY sent");
+ break;
+ }
+}
+
+/*!
+ * \internal
* \brief AMI entry point to send a SIP notify to an endpoint.
*/
static int manager_notify(struct mansession *s, const struct message *m)
{
const char *endpoint_name = astman_get_header(m, "Endpoint");
const char *uri = astman_get_header(m, "URI");
+ const char *channel = astman_get_header(m, "Channel");
+ int count = 0;
+
+ if (!ast_strlen_zero(endpoint_name)) {
+ ++count;
+ }
+ if (!ast_strlen_zero(uri)) {
+ ++count;
+ }
+ if (!ast_strlen_zero(channel)) {
+ ++count;
+ }
- if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {
- astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "
- "both 'URI' and 'Endpoint'. You must use only one of the two.\n");
+ if (1 < count) {
+ astman_send_error(s, m,
+ "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
+ "You must use only one of them.");
} else if (!ast_strlen_zero(endpoint_name)) {
manager_notify_endpoint(s, m, endpoint_name);
} else if (!ast_strlen_zero(uri)) {
manager_notify_uri(s, m, uri);
+ } else if (!ast_strlen_zero(channel)) {
+ manager_notify_channel(s, m, channel);
} else {
- astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");
+ astman_send_error(s, m,
+ "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
}
return 0;
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 8befbc1e8..4894e55d1 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -1070,7 +1070,7 @@ static struct sip_outbound_publisher *sip_outbound_publisher_alloc(
return NULL;
}
- if (ast_sip_push_task_synchronous(NULL, sip_outbound_publisher_init, publisher)) {
+ if (ast_sip_push_task_wait_servant(NULL, sip_outbound_publisher_init, publisher)) {
ast_log(LOG_ERROR, "Unable to create publisher for outbound publish '%s'\n",
ast_sorcery_object_get_id(client->publish));
ao2_ref(publisher, -1);
@@ -1514,8 +1514,8 @@ static int current_state_reusable(struct ast_sip_outbound_publish *publish,
*/
old_publish = current_state->client->publish;
current_state->client->publish = publish;
- if (ast_sip_push_task_synchronous(
- NULL, sip_outbound_publisher_reinit_all, current_state->client->publishers)) {
+ if (ast_sip_push_task_wait_servant(NULL, sip_outbound_publisher_reinit_all,
+ current_state->client->publishers)) {
/*
* If the state object fails to re-initialize then swap
* the old publish info back in.
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 2839ecbab..0d815ad39 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -834,6 +834,8 @@ static int reregister_immediately_cb(void *obj)
*
* \param obj What is needed to initiate a reregister attempt.
*
+ * \note Normally executed by the pjsip monitor thread.
+ *
* \return Nothing
*/
static void registration_transport_shutdown_cb(void *obj)
@@ -1480,7 +1482,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
return -1;
}
- if (ast_sip_push_task_synchronous(new_state->client_state->serializer,
+ if (ast_sip_push_task_wait_serializer(new_state->client_state->serializer,
sip_outbound_registration_regc_alloc, new_state)) {
return -1;
}
@@ -1850,8 +1852,7 @@ static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
struct sip_ami_outbound *ami = arg;
ami->registration = obj;
- return ast_sip_push_task_synchronous(
- NULL, ami_outbound_registration_task, ami);
+ return ast_sip_push_task_wait_servant(NULL, ami_outbound_registration_task, ami);
}
static int ami_show_outbound_registrations(struct mansession *s,
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 69c256dab..d98491495 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -560,15 +560,52 @@ static void *publication_resource_alloc(const char *name)
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_publication_resource), publication_resource_destroy);
}
-static void sub_tree_transport_cb(void *data) {
+static int sub_tree_subscription_terminate_cb(void *data)
+{
struct sip_subscription_tree *sub_tree = data;
- ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on restart: %d\n",
+ if (!sub_tree->evsub) {
+ /* Something else already terminated the subscription. */
+ ao2_ref(sub_tree, -1);
+ return 0;
+ }
+
+ ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on boot: %d\n",
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot);
sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+
+ ao2_ref(sub_tree, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we used as a subscription contact has shutdown.
+ *
+ * \param data What subscription needs to be terminated.
+ *
+ * \note Normally executed by the pjsip monitor thread.
+ *
+ * \return Nothing
+ */
+static void sub_tree_transport_cb(void *data)
+{
+ struct sip_subscription_tree *sub_tree = data;
+
+ /*
+ * Push off the subscription termination to the serializer to
+ * avoid deadlock. Another thread could be trying to send a
+ * message on the subscription that can deadlock with this
+ * thread.
+ */
+ ao2_ref(sub_tree, +1);
+ if (ast_sip_push_task(sub_tree->serializer, sub_tree_subscription_terminate_cb,
+ sub_tree)) {
+ ao2_ref(sub_tree, -1);
+ }
}
/*! \brief Destructor for subscription persistence */
@@ -621,7 +658,7 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
return;
}
- ast_debug(3, "Updating persistence for '%s->%s' prune on restart: %s\n",
+ ast_debug(3, "Updating persistence for '%s->%s' prune on boot: %s\n",
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot ? "yes" : "no");
@@ -645,7 +682,7 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
sub_tree->endpoint, rdata);
if (sub_tree->persistence->prune_on_boot) {
- ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on restart: %d\n",
+ ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
rdata->tp_info.transport->obj_name,
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot);
@@ -1318,7 +1355,8 @@ static void subscription_tree_destructor(void *obj)
destroy_subscriptions(sub_tree->root);
if (sub_tree->dlg) {
- ast_sip_push_task_synchronous(sub_tree->serializer, subscription_unreference_dialog, sub_tree);
+ ast_sip_push_task_wait_servant(sub_tree->serializer,
+ subscription_unreference_dialog, sub_tree);
}
ao2_cleanup(sub_tree->endpoint);
@@ -1665,7 +1703,8 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
}
recreate_data.persistence = persistence;
recreate_data.rdata = &rdata;
- if (ast_sip_push_task_synchronous(serializer, sub_persistence_recreate, &recreate_data)) {
+ if (ast_sip_push_task_wait_serializer(serializer, sub_persistence_recreate,
+ &recreate_data)) {
ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 7d892f653..1e6ca7f46 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -316,7 +316,15 @@ static void refer_progress_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
/* It's possible that a task is waiting to remove us already, so bump the refcount of progress so it doesn't get destroyed */
ao2_ref(progress, +1);
pjsip_dlg_dec_lock(progress->dlg);
- ast_sip_push_task_synchronous(progress->serializer, refer_progress_terminate, progress);
+ /*
+ * XXX We are always going to execute this inline rather than
+ * in the serializer because this function is a PJPROJECT
+ * callback and thus has to be a SIP servant thread.
+ *
+ * The likely remedy is to push most of this function into
+ * refer_progress_terminate() with ast_sip_push_task().
+ */
+ ast_sip_push_task_wait_servant(progress->serializer, refer_progress_terminate, progress);
pjsip_dlg_inc_lock(progress->dlg);
ao2_ref(progress, -1);
@@ -917,10 +925,7 @@ static int invite_replaces(void *data)
ast_channel_ref(invite->session->channel);
invite->channel = invite->session->channel;
- ast_channel_lock(invite->channel);
- invite->bridge = ast_channel_get_bridge(invite->channel);
- ast_channel_unlock(invite->channel);
-
+ invite->bridge = ast_bridge_transfer_acquire_bridge(invite->channel);
return 0;
}
@@ -963,7 +968,8 @@ static int refer_incoming_invite_request(struct ast_sip_session *session, struct
invite.session = other_session;
- if (ast_sip_push_task_synchronous(other_session->serializer, invite_replaces, &invite)) {
+ if (ast_sip_push_task_wait_serializer(other_session->serializer, invite_replaces,
+ &invite)) {
response = 481;
goto inv_replace_failed;
}
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index bdee91fb3..985933e2d 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -337,7 +337,7 @@ static int contact_transport_monitor_matcher(void *a, void *b)
&& strcmp(ma->contact_name, mb->contact_name) == 0;
}
-static void register_contact_transport_shutdown_cb(void *data)
+static int register_contact_transport_remove_cb(void *data)
{
struct contact_transport_monitor *monitor = data;
struct ast_sip_contact *contact;
@@ -345,7 +345,8 @@ static void register_contact_transport_shutdown_cb(void *data)
aor = ast_sip_location_retrieve_aor(monitor->aor_name);
if (!aor) {
- return;
+ ao2_ref(monitor, -1);
+ return 0;
}
ao2_lock(aor);
@@ -365,6 +366,35 @@ static void register_contact_transport_shutdown_cb(void *data)
}
ao2_unlock(aor);
ao2_ref(aor, -1);
+
+ ao2_ref(monitor, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we registered as a contact has shutdown.
+ *
+ * \param data What contact needs to be removed.
+ *
+ * \note Normally executed by the pjsip monitor thread.
+ *
+ * \return Nothing
+ */
+static void register_contact_transport_shutdown_cb(void *data)
+{
+ struct contact_transport_monitor *monitor = data;
+
+ /*
+ * Push off to a default serializer. This is in case sorcery
+ * does database accesses for contacts. Database accesses may
+ * not be on this machine. We don't want to tie up the pjsip
+ * monitor thread with potentially long access times.
+ */
+ ao2_ref(monitor, +1);
+ if (ast_sip_push_task(NULL, register_contact_transport_remove_cb, monitor)) {
+ ao2_ref(monitor, -1);
+ }
}
AST_VECTOR(excess_contact_vector, struct ast_sip_contact *);
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 03d37652f..14ed3b186 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -1096,6 +1096,9 @@ static void add_rtcp_fb_to_stream(struct ast_sip_session *session,
attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* goog-remb"));
pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+
+ attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* nack"));
+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
}
/*! \brief Function which negotiates an incoming media stream */
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index d13b372be..49ab87568 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -4153,7 +4153,7 @@ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_trans
if (ast_sip_transport_is_local(transport_state, &our_sdp_addr) || !transport_state->localnet) {
ast_debug(5, "Setting external media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
pj_strdup2(tdata->pool, &sdp->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
- pj_strdup2(tdata->pool, &sdp->origin.addr, transport->external_media_address);
+ pj_strassign(&sdp->origin.addr, &sdp->conn->addr);
}
}
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 974b15087..633594359 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -377,7 +377,7 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
create_data.ws_session = session;
- if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) {
+ if (ast_sip_push_task_wait_serializer(serializer, transport_create, &create_data)) {
ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
ast_taskprocessor_unreference(serializer);
ast_websocket_unref(session);
@@ -396,13 +396,13 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
}
if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
- ast_sip_push_task_synchronous(serializer, transport_read, &read_data);
+ ast_sip_push_task_wait_serializer(serializer, transport_read, &read_data);
} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
break;
}
}
- ast_sip_push_task_synchronous(serializer, transport_shutdown, transport);
+ ast_sip_push_task_wait_serializer(serializer, transport_shutdown, transport);
ast_taskprocessor_unreference(serializer);
ast_websocket_unref(session);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index b010f6c51..4ac20d551 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -71,6 +71,7 @@
#include "asterisk/smoother.h"
#include "asterisk/uuid.h"
#include "asterisk/test.h"
+#include "asterisk/data_buffer.h"
#ifdef HAVE_PJPROJECT
#include "asterisk/res_pjproject.h"
#endif
@@ -92,6 +93,8 @@
#define TURN_STATE_WAIT_TIME 2000
+#define DEFAULT_RTP_BUFFER_SIZE 250
+
/*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */
#define RTCP_PT_FUR 192
/*! Sender Report (From RFC3550) */
@@ -373,6 +376,8 @@ struct ast_rtp {
struct rtp_red *red;
+ struct ast_data_buffer *send_buffer; /*!< Buffer for storing sent packets for retransmission */
+
#ifdef HAVE_PJPROJECT
ast_cond_t cond; /*!< ICE/TURN condition for signaling */
@@ -509,6 +514,12 @@ struct rtp_red {
long int prev_ts;
};
+/*! \brief Structure for storing RTP packets for retransmission */
+struct ast_rtp_rtcp_nack_payload {
+ size_t size; /*!< The size of the payload */
+ unsigned char buf[0]; /*!< The payload data */
+};
+
AST_LIST_HEAD_NOLOCK(frame_list, ast_frame);
/* Forward Declarations */
@@ -3675,6 +3686,11 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
rtp->red = NULL;
}
+ /* Destroy the send buffer if it was being used */
+ if (rtp->send_buffer) {
+ ast_data_buffer_free(rtp->send_buffer);
+ }
+
ao2_cleanup(rtp->lasttxformat);
ao2_cleanup(rtp->lastrxformat);
ao2_cleanup(rtp->f.subclass.format);
@@ -4369,7 +4385,7 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
} else {
/* This is the first frame with sequence number we've seen, so start keeping track */
rtp->expectedseqno = frame->seqno + 1;
- }
+ }
} else {
rtp->expectedseqno = -1;
}
@@ -4383,13 +4399,27 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
/* If we know the remote address construct a packet and send it out */
if (!ast_sockaddr_isnull(&remote_address)) {
int hdrlen = 12, res, ice;
+ int packet_len = frame->datalen + hdrlen;
unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (seqno) | (mark << 23)));
put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
- if ((res = rtp_sendto(instance, (void *)rtpheader, frame->datalen + hdrlen, 0, &remote_address, &ice)) < 0) {
+ /* If retransmissions are enabled, we need to store this packet for future use */
+ if (rtp->send_buffer) {
+ struct ast_rtp_rtcp_nack_payload *payload;
+
+ payload = ast_malloc(sizeof(*payload) + packet_len);
+ if (payload) {
+ payload->size = packet_len;
+ memcpy(payload->buf, rtpheader, packet_len);
+ ast_data_buffer_put(rtp->send_buffer, rtp->seqno, payload);
+ }
+ }
+
+ res = rtp_sendto(instance, (void *)rtpheader, packet_len, 0, &remote_address, &ice);
+ if (res < 0) {
if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_debug(1, "RTP Transmission error of packet %d to %s: %s\n",
rtp->seqno,
@@ -4473,6 +4503,94 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red)
return &red->t140red;
}
+static void rtp_write_rtcp_fir(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *remote_address)
+{
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ int len = 20;
+ int ice;
+ int res;
+
+ if (!rtp || !rtp->rtcp) {
+ return;
+ }
+
+ if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+ /*
+ * RTCP was stopped.
+ */
+ return;
+ }
+
+ if (!rtp->themssrc_valid) {
+ /* We don't know their SSRC value so we don't know who to update. */
+ return;
+ }
+
+ /* Prepare RTCP FIR (PT=206, FMT=4) */
+ rtp->rtcp->firseq++;
+ if(rtp->rtcp->firseq == 256) {
+ rtp->rtcp->firseq = 0;
+ }
+
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(rtp->themssrc);
+ rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */
+ rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */
+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+ }
+}
+
+static void rtp_write_rtcp_psfb(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_frame *frame, struct ast_sockaddr *remote_address)
+{
+ struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ int len = 24;
+ int ice;
+ int res;
+
+ if (feedback->fmt != AST_RTP_RTCP_FMT_REMB) {
+ ast_debug(1, "Provided an RTCP feedback frame of format %d to write on RTP instance '%p' but only REMB is supported\n",
+ feedback->fmt, instance);
+ return;
+ }
+
+ if (!rtp || !rtp->rtcp) {
+ return;
+ }
+
+ /* If REMB support is not enabled don't send this RTCP packet */
+ if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+ ast_debug(1, "Provided an RTCP feedback REMB report to write on RTP instance '%p' but REMB support not enabled\n",
+ instance);
+ return;
+ }
+
+ if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+ /*
+ * RTCP was stopped.
+ */
+ return;
+ }
+
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (AST_RTP_RTCP_FMT_REMB << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(0); /* Per the draft this should always be 0 */
+ rtcpheader[3] = htonl(('R' << 24) | ('E' << 16) | ('M' << 8) | ('B')); /* Unique identifier 'R' 'E' 'M' 'B' */
+ rtcpheader[4] = htonl((1 << 24) | (feedback->remb.br_exp << 18) | (feedback->remb.br_mantissa)); /* Number of SSRCs / BR Exp / BR Mantissa */
+ rtcpheader[5] = htonl(rtp->ssrc); /* The SSRC this feedback message applies to */
+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP PSFB transmission error: %s\n", strerror(errno));
+ }
+}
+
/*! \pre instance is locked */
static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
{
@@ -4491,42 +4609,11 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
/* VP8: is this a request to send a RTCP FIR? */
if (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) {
- unsigned int *rtcpheader;
- char bdata[1024];
- int len = 20;
- int ice;
- int res;
-
- if (!rtp || !rtp->rtcp) {
- return 0;
- }
-
- if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
- /*
- * RTCP was stopped.
- */
- return 0;
- }
- if (!rtp->themssrc_valid) {
- /* We don't know their SSRC value so we don't know who to update. */
- return 0;
- }
-
- /* Prepare RTCP FIR (PT=206, FMT=4) */
- rtp->rtcp->firseq++;
- if(rtp->rtcp->firseq == 256) {
- rtp->rtcp->firseq = 0;
- }
-
- rtcpheader = (unsigned int *)bdata;
- rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
- rtcpheader[1] = htonl(rtp->ssrc);
- rtcpheader[2] = htonl(rtp->themssrc);
- rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */
- rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */
- res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? &remote_address : &rtp->rtcp->them, &ice);
- if (res < 0) {
- ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+ rtp_write_rtcp_fir(instance, rtp, &remote_address);
+ return 0;
+ } else if (frame->frametype == AST_FRAME_RTCP) {
+ if (frame->subclass.integer == AST_RTP_RTCP_PSFB) {
+ rtp_write_rtcp_psfb(instance, rtp, frame, &remote_address);
}
return 0;
}
@@ -5088,8 +5175,8 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
}
/*! \pre instance is locked */
-static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
- struct ast_rtp *rtp, unsigned int ssrc)
+static struct ast_rtp_instance *__rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc, int source)
{
int index;
@@ -5101,8 +5188,9 @@ static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instanc
/* Find the bundled child instance */
for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
+ unsigned int mapping_ssrc = source ? ast_rtp_get_ssrc(mapping->instance) : mapping->ssrc;
- if (mapping->ssrc_valid && mapping->ssrc == ssrc) {
+ if (mapping->ssrc_valid && mapping_ssrc == ssrc) {
return mapping->instance;
}
}
@@ -5114,6 +5202,20 @@ static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instanc
return NULL;
}
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_packet_source_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc)
+{
+ return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 0);
+}
+
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_media_source_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc)
+{
+ return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 1);
+}
+
static const char *rtcp_payload_type2str(unsigned int pt)
{
const char *str;
@@ -5146,6 +5248,69 @@ static const char *rtcp_payload_type2str(unsigned int pt)
return str;
}
+/*! \pre instance is locked */
+static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned int *nackdata, unsigned int position,
+ unsigned int length)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ int res = 0;
+ int blp_index;
+ int packet_index;
+ int ice;
+ struct ast_rtp_rtcp_nack_payload *payload;
+ unsigned int current_word;
+ unsigned int pid; /* Packet ID which refers to seqno of lost packet */
+ unsigned int blp; /* Bitmask of following lost packets */
+ struct ast_sockaddr remote_address = { {0,} };
+
+ if (!rtp->send_buffer) {
+ ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
+ return res;
+ }
+
+ ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+ /*
+ * We use index 3 because with feedback messages, the FCI (Feedback Control Information)
+ * does not begin until after the version, packet SSRC, and media SSRC words.
+ */
+ for (packet_index = 3; packet_index < length; packet_index++) {
+ current_word = ntohl(nackdata[position + packet_index]);
+ pid = current_word >> 16;
+ /* We know the remote end is missing this packet. Go ahead and send it if we still have it. */
+ payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, pid);
+ if (payload) {
+ res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
+ } else {
+ ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
+ }
+ /*
+ * The bitmask. Denoting the least significant bit as 1 and its most significant bit
+ * as 16, then bit i of the bitmask is set to 1 if the receiver has not received RTP
+ * packet (pid+i)(modulo 2^16). Otherwise, it is set to 0. We cannot assume bits set
+ * to 0 after a bit set to 1 have actually been received.
+ */
+ blp = current_word & 0xFF;
+ blp_index = 1;
+ while (blp) {
+ if (blp & 1) {
+ /* Packet (pid + i)(modulo 2^16) is missing too. */
+ unsigned int seqno = (pid + blp_index) % 65536;
+ payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno);
+ if (payload) {
+ res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
+ } else {
+ ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
+ }
+ }
+ blp >>= 1;
+ blp_index++;
+ }
+ }
+
+ return res;
+}
+
/*
* Unshifted RTCP header bit field masks
*/
@@ -5185,7 +5350,8 @@ 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
+#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 4
+#define RTCP_FB_NACK_BLOCK_WORD_LENGTH 2
static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
@@ -5262,6 +5428,8 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
unsigned int ssrc_valid;
unsigned int length;
unsigned int min_length;
+ /*! Always use packet source SSRC to find the rtp instance unless explicitly told not to. */
+ unsigned int use_packet_source = 1;
struct ast_json *message_blob;
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
@@ -5284,9 +5452,20 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
/* fall through */
case RTCP_PT_RR:
min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
+ use_packet_source = 0;
break;
case RTCP_PT_FUR:
break;
+ case AST_RTP_RTCP_RTPFB:
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_NACK:
+ min_length += RTCP_FB_NACK_BLOCK_WORD_LENGTH;
+ break;
+ default:
+ break;
+ }
+ use_packet_source = 0;
+ break;
case RTCP_PT_PSFB:
switch (rc) {
case AST_RTP_RTCP_FMT_REMB:
@@ -5335,13 +5514,16 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
}
rtcp_report->reception_report_count = rc;
- ssrc = ntohl(rtcpheader[i + 1]);
+ ssrc = ntohl(rtcpheader[i + 2]);
rtcp_report->ssrc = ssrc;
break;
case RTCP_PT_FUR:
case RTCP_PT_PSFB:
ssrc = ntohl(rtcpheader[i + 1]);
break;
+ case AST_RTP_RTCP_RTPFB:
+ ssrc = ntohl(rtcpheader[i + 2]);
+ break;
case RTCP_PT_SDES:
case RTCP_PT_BYE:
default:
@@ -5360,7 +5542,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
/* Determine the appropriate instance for this */
if (ssrc_valid) {
- child = rtp_find_instance_by_ssrc(transport, transport_rtp, ssrc);
+ /*
+ * Depending on the payload type, either the packet source or media source
+ * SSRC is used.
+ */
+ if (use_packet_source) {
+ child = rtp_find_instance_by_packet_source_ssrc(transport, transport_rtp, ssrc);
+ } else {
+ child = rtp_find_instance_by_media_source_ssrc(transport, transport_rtp, ssrc);
+ }
if (child && child != transport) {
/*
* It is safe to hold the child lock while holding the parent lock.
@@ -5381,7 +5571,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
}
if (ssrc_valid && rtp->themssrc_valid) {
- if (ssrc != rtp->themssrc) {
+ if (ssrc != rtp->themssrc && use_packet_source) {
/*
* Skip over this RTCP record as it does not contain the
* correct SSRC. We should not act upon RTCP records
@@ -5524,11 +5714,30 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
transport_rtp->f.src = "RTP";
f = &transport_rtp->f;
break;
+ case AST_RTP_RTCP_RTPFB:
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_NACK:
+ /* If retransmissions are not enabled ignore this message */
+ if (!rtp->send_buffer) {
+ break;
+ }
+
+ if (rtcp_debug_test_addr(addr)) {
+ ast_verbose("Received generic RTCP NACK message\n");
+ }
+
+ ast_rtp_rtcp_handle_nack(instance, rtcpheader, position, length);
+ break;
+ default:
+ break;
+ }
+ break;
case RTCP_PT_FUR:
- /* Handle RTCP FUR as FIR by setting the format to 4 */
+ /* Handle RTCP FUR as FIR by setting the format to 4 */
rc = AST_RTP_RTCP_FMT_FIR;
case RTCP_PT_PSFB:
switch (rc) {
+ case AST_RTP_RTCP_FMT_PLI:
case AST_RTP_RTCP_FMT_FIR:
if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an RTCP Fast Update Request\n");
@@ -5923,7 +6132,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
ssrc = ntohl(rtpheader[2]);
/* Determine the appropriate instance for this */
- child = rtp_find_instance_by_ssrc(instance, rtp, ssrc);
+ child = rtp_find_instance_by_packet_source_ssrc(instance, rtp, ssrc);
if (!child) {
/* Neither the bundled parent nor any child has this SSRC */
return &ast_null_frame;
@@ -6556,6 +6765,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
}
} else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
rtp->asymmetric_codec = value;
+ } else if (property == AST_RTP_PROPERTY_RETRANS_SEND) {
+ rtp->send_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_BUFFER_SIZE);
}
}
diff --git a/utils/Makefile b/utils/Makefile
index d62d45f4f..ae2af08e2 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -164,7 +164,7 @@ aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c
$(CMD_PREFIX) mv "$@.new" "$@"
aelparse.o: _ASTCFLAGS+=-I$(ASTTOPDIR)/res -Wno-unused
-aelparse: LIBS+=-lm
+aelparse: LIBS+=-lm $(AST_CLANG_BLOCKS_LIBS)
aelparse: aelparse.o aelbison.o pbx_ael.o hashtab.o lock.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o astmm.o
threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
@@ -174,6 +174,7 @@ threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
extconf.o: extconf.c
+conf2ael: LIBS+=$(AST_CLANG_BLOCKS_LIBS)
conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o hashtab.o lock.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o astmm.o
check_expr2: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h astmm.o