From 5622df0a94a9d7762d07fa08d88c40fc2a566d28 Mon Sep 17 00:00:00 2001
From: "Joshua C. Colp" <jcolp@sangoma.com>
Date: Thu, 12 Dec 2019 00:03:46 +0000
Subject: [PATCH] confbridge: Add support for specifying maximum sample rate.

ConfBridge has the ability to move between different sample
rates for mixing the conference bridge. Up until now there has
only been the ability to set the conference bridge to mix at
a specific sample rate, or to let it move between sample rates
as necessary. This change adds the ability to configure a
conference bridge with a maximum sample rate so it can move
between sample rates but only up to the configured maximum.

ASTERISK-28658

Change-Id: Idff80896ccfb8a58a816e4ce9ac4ebde785963ee
---
 apps/app_confbridge.c                         |  2 ++
 apps/confbridge/conf_config_parser.c          | 17 +++++++++++++++
 apps/confbridge/include/confbridge.h          |  1 +
 bridges/bridge_softmix.c                      | 18 +++++++++++++++-
 configs/samples/confbridge.conf.sample        |  4 ++++
 .../app_confbridge_maximum_sample_rate.txt    |  5 +++++
 include/asterisk/bridge.h                     | 21 +++++++++++++++++++
 main/bridge.c                                 |  7 +++++++
 8 files changed, 74 insertions(+), 1 deletion(-)
 create mode 100644 doc/CHANGES-staging/app_confbridge_maximum_sample_rate.txt

diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index d0c704249a..0151e1c4fc 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -1706,6 +1706,8 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 
 		/* Set the internal sample rate on the bridge from the bridge profile */
 		ast_bridge_set_internal_sample_rate(conference->bridge, conference->b_profile.internal_sample_rate);
+		/* Set the maximum sample rate on the bridge from the bridge profile */
+		ast_bridge_set_maximum_sample_rate(conference->bridge, conference->b_profile.maximum_sample_rate);
 		/* Set the internal mixing interval on the bridge from the bridge profile */
 		ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval);
 		ast_bridge_set_binaural_active(conference->bridge, ast_test_flag(&conference->b_profile, BRIDGE_OPT_BINAURAL_ACTIVE));
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index f732c45402..b0c717ab22 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -274,6 +274,15 @@
 						will be used.
 					</para></description>
 				</configOption>
+				<configOption name="maximum_sample_rate">
+					<synopsis>Set the maximum native sample rate for mixing the conference</synopsis>
+					<description><para>
+						Sets the maximum native sample rate the
+						conference is mixed at. This is set to not have a
+						maximum by default. If a sample rate is specified,
+						though, the native sample rate will never exceed it.
+					</para></description>
+				</configOption>
 				<configOption name="language" default="en">
 					<synopsis>The language used for announcements to the conference.</synopsis>
 					<description><para>
@@ -1682,6 +1691,13 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
 	}
 	ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
 
+	if (b_profile.maximum_sample_rate) {
+		snprintf(tmp, sizeof(tmp), "%u", b_profile.maximum_sample_rate);
+	} else {
+		ast_copy_string(tmp, "none", sizeof(tmp));
+	}
+	ast_cli(a->fd,"Maximum Sample Rate: %s\n", tmp);
+
 	if (b_profile.mix_interval) {
 		ast_cli(a->fd,"Mixing Interval:      %u\n", b_profile.mix_interval);
 	} else {
@@ -2376,6 +2392,7 @@ int conf_load_config(void)
 	/* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
 	aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
 	aco_option_register(&cfg_info, "binaural_active", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_BINAURAL_ACTIVE);
+	aco_option_register(&cfg_info, "maximum_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, maximum_sample_rate), 0);
 	aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
 	aco_option_register(&cfg_info, "record_conference", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_CONFERENCE);
 	aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index f932da8587..82e709acdf 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -228,6 +228,7 @@ struct bridge_profile {
 	unsigned int flags;
 	unsigned int max_members;          /*!< The maximum number of participants allowed in the conference */
 	unsigned int internal_sample_rate; /*!< The internal sample rate of the bridge. 0 when set to auto adjust mode. */
+	unsigned int maximum_sample_rate; /*!< The maximum sample rate of the bridge. 0 when set to no maximum. */
 	unsigned int mix_interval;  /*!< The internal mixing interval used by the bridge. When set to 0 the bridgewill use a default interval. */
 	struct bridge_profile_sounds *sounds;
 	char regcontext[AST_MAX_CONTEXT];
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index d907084cf1..c24fa7a27f 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -88,12 +88,16 @@ struct softmix_stats {
 	unsigned int num_channels[16];
 	/*! The number of channels above the internal sample rate */
 	unsigned int num_above_internal_rate;
+	/*! The number of channels above the maximum sample rate */
+	unsigned int num_above_maximum_rate;
 	/*! The number of channels at the internal sample rate */
 	unsigned int num_at_internal_rate;
 	/*! The absolute highest sample rate preferred by any channel in the bridge */
 	unsigned int highest_supported_rate;
 	/*! Is the sample rate locked by the bridge, if so what is that rate.*/
 	unsigned int locked_rate;
+	/*! The maximum sample rate the bridge may use */
+	unsigned int maximum_rate;
 };
 
 struct softmix_translate_helper_entry {
@@ -1541,7 +1545,9 @@ static void gather_softmix_stats(struct softmix_stats *stats,
 	if (stats->highest_supported_rate < channel_native_rate) {
 		stats->highest_supported_rate = channel_native_rate;
 	}
-	if (softmix_data->internal_rate < channel_native_rate) {
+	if (stats->maximum_rate && stats->maximum_rate < channel_native_rate) {
+		stats->num_above_maximum_rate++;
+	} else if (softmix_data->internal_rate < channel_native_rate) {
 		int i;
 
 		for (i = 0; i < ARRAY_LEN(stats->sample_rates); i++) {
@@ -1593,6 +1599,15 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats,
 			softmix_data->internal_rate = stats->locked_rate;
 			return 1;
 		}
+	} else if (stats->num_above_maximum_rate) {
+		/* if the bridge has a maximum rate set and channels are above it only
+		 * update if it differs from the current rate we are using. */
+		if (softmix_data->internal_rate != stats->maximum_rate) {
+			ast_debug(1, "Locking at new maximum rate.  Bridge changed from %u to %u.\n",
+				softmix_data->internal_rate, stats->maximum_rate);
+			softmix_data->internal_rate = stats->maximum_rate;
+			return 1;
+		}
 	} else if (stats->num_above_internal_rate >= 2) {
 		/* the highest rate is just used as a starting point */
 		unsigned int best_rate = stats->highest_supported_rate;
@@ -1775,6 +1790,7 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
 		if (!stat_iteration_counter) {
 			memset(&stats, 0, sizeof(stats));
 			stats.locked_rate = bridge->softmix.internal_sample_rate;
+			stats.maximum_rate = bridge->softmix.maximum_sample_rate;
 		}
 
 		/* If the sample rate has changed, update the translator helper */
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index e1481818be..520b357f63 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -203,6 +203,10 @@ type=bridge
                                ; closest sample rate Asterisk does support to the one requested
                                ; will be used.
 
+;maximum_sample_rate=none      ; Sets the maximum sample rate the conference
+                               ; is mixed at. This is set to no maximum by default.
+                               ; Values can be anything from 8000-192000.
+
 ;mixing_interval=40     ; Sets the internal mixing interval in milliseconds for the bridge.  This
                         ; number reflects how tight or loose the mixing will be for the conference.
                         ; In order to improve performance a larger mixing interval such as 40ms may
diff --git a/doc/CHANGES-staging/app_confbridge_maximum_sample_rate.txt b/doc/CHANGES-staging/app_confbridge_maximum_sample_rate.txt
new file mode 100644
index 0000000000..1c584fa8f0
--- /dev/null
+++ b/doc/CHANGES-staging/app_confbridge_maximum_sample_rate.txt
@@ -0,0 +1,5 @@
+Subject: app_confbridge
+
+A new bridge profile option, maximum_sample_rate, has been added which sets
+a maximum sample rate that the bridge will be mixed at. This allows the bridge
+to move below the maximum sample rate as needed but caps it at the maximum.
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index 049ec980e6..a17aa00f27 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -301,6 +301,12 @@ struct ast_bridge_softmix {
 	 * the channel uniqueid.  Used for participant info correlation.
 	 */
 	unsigned int send_sdp_label;
+	/*!
+	 * \brief The maximum sample rate softmix uses to mix channels.
+	 *
+	 * \note If this value is 0, there is no maximum sample rate.
+	 */
+	unsigned int maximum_sample_rate;
 };
 
 AST_LIST_HEAD_NOLOCK(ast_bridge_channels_list, ast_bridge_channel);
@@ -905,6 +911,21 @@ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_
  */
 void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate);
 
+/*!
+ * \brief Adjust the maximum mixing sample rate of a bridge
+ * used during multimix mode.
+ * \since 13.31.0
+ * \since 16.8.0
+ * \since 17.2.0
+ *
+ * \param bridge Channel to change the sample rate on.
+ * \param sample_rate the maximum sample rate to use. If a
+ *        value of 0 is passed here, the bridge will be free to pick
+ *        what ever sample rate it chooses.
+ *
+ */
+void ast_bridge_set_maximum_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate);
+
 /*!
  * \brief Adjust the internal mixing interval of a bridge used
  * during multimix mode.
diff --git a/main/bridge.c b/main/bridge.c
index 9c53cfb5ac..bacf0be28a 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -3792,6 +3792,13 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
 	ast_bridge_unlock(bridge);
 }
 
+void ast_bridge_set_maximum_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
+{
+	ast_bridge_lock(bridge);
+	bridge->softmix.maximum_sample_rate = sample_rate;
+	ast_bridge_unlock(bridge);
+}
+
 static void cleanup_video_mode(struct ast_bridge *bridge)
 {
 	switch (bridge->softmix.video_mode.mode) {
-- 
GitLab