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);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL);
}
}
<enum name="highest">
<para>The highest estimated maximum bitrate is forwarded to the sender.</para>
</enum>
+ <enum name="average_all">
+ <para>The average of all estimated maximum bitrates is taken from all
+ receivers in the bridge and a single value is sent to each sender.</para>
+ </enum>
+ <enum name="lowest_all">
+ <para>The lowest estimated maximum bitrate of all receivers in the bridge
+ is taken and sent to each sender.</para>
+ </enum>
+ <enum name="highest_all">
+ <para>The highest estimated maximum bitrate of all receivers in the bridge
+ is taken and sent to each sender.</para>
+ </enum>
</enumlist>
</description>
</configOption>
switch (b_profile.flags
& (BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
- | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST | BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL
+ | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL)) {
case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE:
ast_cli(a->fd, "REMB Behavior: average\n");
break;
case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST:
ast_cli(a->fd, "REMB Behavior: highest\n");
break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL:
+ ast_cli(a->fd, "REMB Behavior: average_all\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL:
+ ast_cli(a->fd, "REMB Behavior: lowest_all\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL:
+ ast_cli(a->fd, "REMB Behavior: highest_all\n");
+ break;
default:
ast_assert(0);
break;
ast_clear_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE |
BRIDGE_OPT_REMB_BEHAVIOR_LOWEST |
- BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST |
+ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL |
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL |
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL);
if (!strcasecmp(var->value, "average")) {
ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE);
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 if (!strcasecmp(var->value, "average_all")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL);
+ } else if (!strcasecmp(var->value, "lowest_all")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL);
+ } else if (!strcasecmp(var->value, "highest_all")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL);
} else {
return -1;
}
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 */
BRIDGE_OPT_ENABLE_EVENTS = (1 << 11), /*!< Enable sending events to participants */
+ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL = (1 << 12), /*!< The average of all REMB reports in the entire bridge is sent to each sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL = (1 << 13), /*!< The lowest estimated maximum bitrate from all receivers is sent to each sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL = (1 << 14), /*!< The highest estimated maximum bitrate from all receivers is sent to each sender */
};
enum conf_menu_action_id {
return res;
}
+static void remb_collect_report_all(struct ast_bridge *bridge, struct softmix_bridge_data *softmix_data,
+ float bitrate)
+{
+ if (!softmix_data->bitrate) {
+ softmix_data->bitrate = bitrate;
+ return;
+ }
+
+ switch (bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior) {
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL:
+ softmix_data->bitrate = (softmix_data->bitrate + bitrate) / 2;
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL:
+ if (bitrate < softmix_data->bitrate) {
+ softmix_data->bitrate = bitrate;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL:
+ if (bitrate > softmix_data->bitrate) {
+ softmix_data->bitrate = bitrate;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE:
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST:
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST:
+ /* These will never actually get hit due to being handled by remb_collect_report below */
+ break;
+ }
+}
+
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)
{
return;
}
+ /* If we are using the "all" variants then we should use the bridge bitrate to store information */
+ if (bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior == AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL ||
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior == AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL ||
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior == AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL) {
+ remb_collect_report_all(bridge, softmix_data, bitrate);
+ return;
+ }
+
for (i = 0; i < AST_VECTOR_SIZE(&sc->video_sources); ++i) {
struct softmix_remb_collector *collector;
collector->bitrate = bitrate;
}
break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL:
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL:
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL:
+ /* These will never actually get hit due to being handled by remb_collect_report_all above */
+ break;
}
}
sc->remb.br_exp = 0;
}
-static void remb_send_report(struct ast_bridge_channel *bridge_channel, struct softmix_channel *sc)
+static void remb_send_report(struct ast_bridge_channel *bridge_channel, struct softmix_bridge_data *softmix_data,
+ struct softmix_channel *sc)
{
+ float bitrate = softmix_data->bitrate;
int i;
int exp;
return;
}
+ /* If there is no bridge level bitrate fall back to collector level */
+ if (!bitrate) {
+ bitrate = sc->remb_collector->bitrate;
+ sc->remb_collector->bitrate = 0;
+ }
+
/* We always do this calculation as even when the bitrate is zero the browser
* still prefers it to be accurate instead of lying.
*
* Precision on the "lower" end is lost due to zeros being shifted in. This loss is
* both expected and acceptable.
*/
- frexp(sc->remb_collector->bitrate, &exp);
+ frexp(bitrate, &exp);
exp = exp > 18 ? exp - 18 : 0;
- sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->bitrate / (1 << exp);
+ sc->remb_collector->feedback.remb.br_mantissa = bitrate / (1 << exp);
sc->remb_collector->feedback.remb.br_exp = exp;
for (i = 0; i < AST_VECTOR_SIZE(&bridge_channel->stream_map.to_bridge); ++i) {
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,
ast_bridge_channel_queue_frame(bridge_channel, &sc->write_frame);
if (remb_update) {
- remb_send_report(bridge_channel, sc);
+ remb_send_report(bridge_channel, softmix_data, sc);
}
}
+ if (remb_update) {
+ /* In case we are doing bridge level REMB reset the bitrate so we start fresh */
+ softmix_data->bitrate = 0;
+ }
+
update_all_rates = 0;
if (!stat_iteration_counter) {
update_all_rates = analyse_softmix_stats(&stats, softmix_data,
struct timeval last_remb_update;
/*! Per-bridge stream REMB collectors, which flow back to video source */
AST_VECTOR(, struct softmix_remb_collector *) remb_collectors;
+ /*! Per-bridge REMB bitrate */
+ float bitrate;
};
struct softmix_mixing_array {
;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".
+ ; the highest maximum bitrate is forwarded to the sender. If set to "average_all" a single average
+ ; is generated from every receiver and the same value is sent to every sender. If set to
+ ; "lowest_all" the lowest maximum bitrate of all receivers is sent to every sender. If set to
+ ; "highest_all" the highest maximum bitrate of all receivers is sent to every sender. This
+ ; defaults to "average".
;enable_events=no ; If enabled, recipients who joined the bridge via a channel driver
; that supports Enhanced Messaging (currently only chan_pjsip) will
--- /dev/null
+Subject: ConfBridge
+
+Add "average_all", "highest_all", and "lowest_all" values for
+the remb_behavior option. These values operate on a bridge
+level instead of a per-source level. This means that a single
+REMB value is calculated and sent to every sender, instead of
+a REMB value that is unique for the specific sender..
AST_BRIDGE_VIDEO_SFU_REMB_LOWEST,
/*! The highest reported bitrate is forwarded to the sender */
AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST,
+ /*! The average of all reports WITHIN the bridge is sent to each sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL,
+ /*! The lowest reported bitrate from all channels in the bridge is forwarded to each sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL,
+ /*! The highest reported bitrate from all channels in the bridge is forwarded to each sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL,
};
/*! \brief This is used for selective forwarding unit configuration */