summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/congestion_controller/goog_cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/congestion_controller/goog_cc')
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/BUILD.gn369
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc70
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h52
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc92
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h85
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_unittest.cc136
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc111
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.h76
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build225
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_unittest.cc206
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.cc166
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.h62
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc81
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h48
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller_unittest.cc105
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.cc305
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.h133
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build234
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc309
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc529
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h189
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h43
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build238
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build233
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.cc725
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.h147
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc934
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.cc140
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.h90
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.cc77
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.h38
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build221
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc260
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h97
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build226
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc1080
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h203
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build232
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc1526
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc201
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.h58
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator_unittest.cc228
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.cc558
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.h196
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build225
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_unittest.cc1131
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build225
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc189
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.h50
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc427
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc695
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h210
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc206
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build233
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc200
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.h75
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc332
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.h125
-rw-r--r--third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator_unittest.cc151
59 files changed, 15808 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/BUILD.gn b/third_party/libwebrtc/modules/congestion_controller/goog_cc/BUILD.gn
new file mode 100644
index 0000000000..150201e1bd
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/BUILD.gn
@@ -0,0 +1,369 @@
+# Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../../webrtc.gni")
+
+config("bwe_test_logging") {
+ if (rtc_enable_bwe_test_logging) {
+ defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=1" ]
+ } else {
+ defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0" ]
+ }
+}
+
+rtc_library("goog_cc") {
+ configs += [ ":bwe_test_logging" ]
+ sources = [
+ "goog_cc_network_control.cc",
+ "goog_cc_network_control.h",
+ ]
+
+ deps = [
+ ":alr_detector",
+ ":delay_based_bwe",
+ ":estimators",
+ ":loss_based_bwe_v2",
+ ":probe_controller",
+ ":pushback_controller",
+ ":send_side_bwe",
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:field_trial_based_config",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:data_size",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:rtc_event_bwe",
+ "../../../logging:rtc_event_pacing",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base/experiments:alr_experiment",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../../rtc_base/experiments:rate_control_settings",
+ "../../../system_wrappers",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("link_capacity_estimator") {
+ sources = [
+ "link_capacity_estimator.cc",
+ "link_capacity_estimator.h",
+ ]
+ deps = [
+ "../../../api/units:data_rate",
+ "../../../rtc_base:safe_minmax",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+
+rtc_library("pushback_controller") {
+ sources = [
+ "congestion_window_pushback_controller.cc",
+ "congestion_window_pushback_controller.h",
+ ]
+ deps = [
+ "../../../api:field_trials_view",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_size",
+ "../../../rtc_base:checks",
+ "../../../rtc_base/experiments:rate_control_settings",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("alr_detector") {
+ sources = [
+ "alr_detector.cc",
+ "alr_detector.h",
+ ]
+ deps = [
+ "../../../api:field_trials_view",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:field_trial_based_config",
+ "../../../logging:rtc_event_pacing",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:safe_conversions",
+ "../../../rtc_base:timeutils",
+ "../../../rtc_base/experiments:alr_experiment",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../pacing:interval_budget",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+rtc_library("estimators") {
+ configs += [ ":bwe_test_logging" ]
+ sources = [
+ "acknowledged_bitrate_estimator.cc",
+ "acknowledged_bitrate_estimator.h",
+ "acknowledged_bitrate_estimator_interface.cc",
+ "acknowledged_bitrate_estimator_interface.h",
+ "bitrate_estimator.cc",
+ "bitrate_estimator.h",
+ "delay_increase_detector_interface.h",
+ "probe_bitrate_estimator.cc",
+ "probe_bitrate_estimator.h",
+ "robust_throughput_estimator.cc",
+ "robust_throughput_estimator.h",
+ "trendline_estimator.cc",
+ "trendline_estimator.h",
+ ]
+
+ deps = [
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:data_size",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:rtc_event_bwe",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base:macromagic",
+ "../../../rtc_base:rtc_numerics",
+ "../../../rtc_base:safe_conversions",
+ "../../../rtc_base:safe_minmax",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("loss_based_bwe_v2") {
+ sources = [
+ "loss_based_bwe_v2.cc",
+ "loss_based_bwe_v2.h",
+ ]
+ deps = [
+ "../../../api:array_view",
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:data_size",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../rtc_base:logging",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("loss_based_bwe_v1") {
+ configs += [ ":bwe_test_logging" ]
+ sources = [
+ "loss_based_bandwidth_estimation.cc",
+ "loss_based_bandwidth_estimation.h",
+ ]
+ deps = [
+ "../../../api:field_trials_view",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../rtc_base:checks",
+ "../../../rtc_base/experiments:field_trial_parser",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+}
+
+rtc_library("send_side_bwe") {
+ configs += [ ":bwe_test_logging" ]
+ sources = [
+ "send_side_bandwidth_estimation.cc",
+ "send_side_bandwidth_estimation.h",
+ ]
+ deps = [
+ ":loss_based_bwe_v1",
+ ":loss_based_bwe_v2",
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:rtc_event_bwe",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../../system_wrappers:field_trial",
+ "../../../system_wrappers:metrics",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("delay_based_bwe") {
+ configs += [ ":bwe_test_logging" ]
+ sources = [
+ "delay_based_bwe.cc",
+ "delay_based_bwe.h",
+ "inter_arrival_delta.cc",
+ "inter_arrival_delta.h",
+ ]
+
+ deps = [
+ ":estimators",
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:network_control",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:rtc_event_bwe",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base:race_checker",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../../system_wrappers:metrics",
+ "../../pacing",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("probe_controller") {
+ sources = [
+ "probe_controller.cc",
+ "probe_controller.h",
+ ]
+
+ deps = [
+ "../../../api:field_trials_view",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:data_size",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:rtc_event_bwe",
+ "../../../logging:rtc_event_pacing",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base:macromagic",
+ "../../../rtc_base:safe_conversions",
+ "../../../rtc_base/experiments:field_trial_parser",
+ "../../../system_wrappers:metrics",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("test_goog_cc_printer") {
+ testonly = true
+ sources = [
+ "test/goog_cc_printer.cc",
+ "test/goog_cc_printer.h",
+ ]
+ deps = [
+ ":alr_detector",
+ ":delay_based_bwe",
+ ":estimators",
+ ":goog_cc",
+ "../../../api/rtc_event_log",
+ "../../../api/transport:goog_cc",
+ "../../../api/transport:network_control",
+ "../../../api/units:timestamp",
+ "../../../rtc_base:checks",
+ "../../../test/logging:log_writer",
+ "../../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+ }
+ if (!build_with_chromium) {
+ rtc_library("goog_cc_unittests") {
+ testonly = true
+
+ sources = [
+ "acknowledged_bitrate_estimator_unittest.cc",
+ "alr_detector_unittest.cc",
+ "congestion_window_pushback_controller_unittest.cc",
+ "delay_based_bwe_unittest.cc",
+ "delay_based_bwe_unittest_helper.cc",
+ "delay_based_bwe_unittest_helper.h",
+ "goog_cc_network_control_unittest.cc",
+ "loss_based_bwe_v2_test.cc",
+ "probe_bitrate_estimator_unittest.cc",
+ "probe_controller_unittest.cc",
+ "robust_throughput_estimator_unittest.cc",
+ "send_side_bandwidth_estimation_unittest.cc",
+ "trendline_estimator_unittest.cc",
+ ]
+ deps = [
+ ":alr_detector",
+ ":delay_based_bwe",
+ ":estimators",
+ ":goog_cc",
+ ":loss_based_bwe_v2",
+ ":probe_controller",
+ ":pushback_controller",
+ ":send_side_bwe",
+ "../../../api:field_trials_view",
+ "../../../api:network_state_predictor_api",
+ "../../../api/rtc_event_log",
+ "../../../api/test/network_emulation",
+ "../../../api/test/network_emulation:create_cross_traffic",
+ "../../../api/transport:field_trial_based_config",
+ "../../../api/transport:goog_cc",
+ "../../../api/transport:network_control",
+ "../../../api/units:data_rate",
+ "../../../api/units:data_size",
+ "../../../api/units:time_delta",
+ "../../../api/units:timestamp",
+ "../../../logging:mocks",
+ "../../../logging:rtc_event_bwe",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:logging",
+ "../../../rtc_base:random",
+ "../../../rtc_base:rtc_base_tests_utils",
+ "../../../rtc_base:stringutils",
+ "../../../rtc_base/experiments:alr_experiment",
+ "../../../system_wrappers",
+ "../../../test:explicit_key_value_config",
+ "../../../test:field_trial",
+ "../../../test:test_support",
+ "../../../test/scenario",
+ "../../pacing",
+ "//testing/gmock",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ]
+ }
+ }
+}
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc
new file mode 100644
index 0000000000..08b42a8168
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+
+namespace webrtc {
+
+AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
+ const FieldTrialsView* key_value_config)
+ : AcknowledgedBitrateEstimator(
+ key_value_config,
+ std::make_unique<BitrateEstimator>(key_value_config)) {}
+
+AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
+
+AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
+ const FieldTrialsView* key_value_config,
+ std::unique_ptr<BitrateEstimator> bitrate_estimator)
+ : in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {}
+
+void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector(
+ const std::vector<PacketResult>& packet_feedback_vector) {
+ RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
+ packet_feedback_vector.end(),
+ PacketResult::ReceiveTimeOrder()));
+ for (const auto& packet : packet_feedback_vector) {
+ if (alr_ended_time_ && packet.sent_packet.send_time > *alr_ended_time_) {
+ bitrate_estimator_->ExpectFastRateChange();
+ alr_ended_time_.reset();
+ }
+ DataSize acknowledged_estimate = packet.sent_packet.size;
+ acknowledged_estimate += packet.sent_packet.prior_unacked_data;
+ bitrate_estimator_->Update(packet.receive_time, acknowledged_estimate,
+ in_alr_);
+ }
+}
+
+absl::optional<DataRate> AcknowledgedBitrateEstimator::bitrate() const {
+ return bitrate_estimator_->bitrate();
+}
+
+absl::optional<DataRate> AcknowledgedBitrateEstimator::PeekRate() const {
+ return bitrate_estimator_->PeekRate();
+}
+
+void AcknowledgedBitrateEstimator::SetAlrEndedTime(Timestamp alr_ended_time) {
+ alr_ended_time_.emplace(alr_ended_time);
+}
+
+void AcknowledgedBitrateEstimator::SetAlr(bool in_alr) {
+ in_alr_ = in_alr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h
new file mode 100644
index 0000000000..d10846ab3a
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
+#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
+
+namespace webrtc {
+
+class AcknowledgedBitrateEstimator
+ : public AcknowledgedBitrateEstimatorInterface {
+ public:
+ AcknowledgedBitrateEstimator(
+ const FieldTrialsView* key_value_config,
+ std::unique_ptr<BitrateEstimator> bitrate_estimator);
+
+ explicit AcknowledgedBitrateEstimator(
+ const FieldTrialsView* key_value_config);
+ ~AcknowledgedBitrateEstimator() override;
+
+ void IncomingPacketFeedbackVector(
+ const std::vector<PacketResult>& packet_feedback_vector) override;
+ absl::optional<DataRate> bitrate() const override;
+ absl::optional<DataRate> PeekRate() const override;
+ void SetAlr(bool in_alr) override;
+ void SetAlrEndedTime(Timestamp alr_ended_time) override;
+
+ private:
+ absl::optional<Timestamp> alr_ended_time_;
+ bool in_alr_;
+ std::unique_ptr<BitrateEstimator> bitrate_estimator_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc
new file mode 100644
index 0000000000..c043353a7a
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
+
+#include <algorithm>
+
+#include "api/units/time_delta.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
+#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+constexpr char RobustThroughputEstimatorSettings::kKey[];
+
+RobustThroughputEstimatorSettings::RobustThroughputEstimatorSettings(
+ const FieldTrialsView* key_value_config) {
+ Parser()->Parse(
+ key_value_config->Lookup(RobustThroughputEstimatorSettings::kKey));
+ if (window_packets < 10 || 1000 < window_packets) {
+ RTC_LOG(LS_WARNING) << "Window size must be between 10 and 1000 packets";
+ window_packets = 20;
+ }
+ if (max_window_packets < 10 || 1000 < max_window_packets) {
+ RTC_LOG(LS_WARNING)
+ << "Max window size must be between 10 and 1000 packets";
+ max_window_packets = 500;
+ }
+ max_window_packets = std::max(max_window_packets, window_packets);
+
+ if (required_packets < 10 || 1000 < required_packets) {
+ RTC_LOG(LS_WARNING) << "Required number of initial packets must be between "
+ "10 and 1000 packets";
+ required_packets = 10;
+ }
+ required_packets = std::min(required_packets, window_packets);
+
+ if (min_window_duration < TimeDelta::Millis(100) ||
+ TimeDelta::Millis(3000) < min_window_duration) {
+ RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 3000 ms";
+ min_window_duration = TimeDelta::Millis(750);
+ }
+ if (max_window_duration < TimeDelta::Seconds(1) ||
+ TimeDelta::Seconds(15) < max_window_duration) {
+ RTC_LOG(LS_WARNING) << "Max window duration must be between 1 and 15 s";
+ max_window_duration = TimeDelta::Seconds(5);
+ }
+ min_window_duration = std::min(min_window_duration, max_window_duration);
+
+ if (unacked_weight < 0.0 || 1.0 < unacked_weight) {
+ RTC_LOG(LS_WARNING)
+ << "Weight for prior unacked size must be between 0 and 1.";
+ unacked_weight = 1.0;
+ }
+}
+
+std::unique_ptr<StructParametersParser>
+RobustThroughputEstimatorSettings::Parser() {
+ return StructParametersParser::Create(
+ "enabled", &enabled, //
+ "window_packets", &window_packets, //
+ "max_window_packets", &max_window_packets, //
+ "window_duration", &min_window_duration, //
+ "max_window_duration", &max_window_duration, //
+ "required_packets", &required_packets, //
+ "unacked_weight", &unacked_weight);
+}
+
+AcknowledgedBitrateEstimatorInterface::
+ ~AcknowledgedBitrateEstimatorInterface() {}
+
+std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
+AcknowledgedBitrateEstimatorInterface::Create(
+ const FieldTrialsView* key_value_config) {
+ RobustThroughputEstimatorSettings simplified_estimator_settings(
+ key_value_config);
+ if (simplified_estimator_settings.enabled) {
+ return std::make_unique<RobustThroughputEstimator>(
+ simplified_estimator_settings);
+ }
+ return std::make_unique<AcknowledgedBitrateEstimator>(key_value_config);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h
new file mode 100644
index 0000000000..515af1efc9
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
+
+namespace webrtc {
+
+struct RobustThroughputEstimatorSettings {
+ static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings";
+
+ RobustThroughputEstimatorSettings() = delete;
+ explicit RobustThroughputEstimatorSettings(
+ const FieldTrialsView* key_value_config);
+
+ bool enabled = false; // Set to true to use RobustThroughputEstimator.
+
+ // The estimator keeps the smallest window containing at least
+ // `window_packets` and at least the packets received during the last
+ // `min_window_duration` milliseconds.
+ // (This means that it may store more than `window_packets` at high bitrates,
+ // and a longer duration than `min_window_duration` at low bitrates.)
+ // However, if will never store more than kMaxPackets (for performance
+ // reasons), and never longer than max_window_duration (to avoid very old
+ // packets influencing the estimate for example when sending is paused).
+ unsigned window_packets = 20;
+ unsigned max_window_packets = 500;
+ TimeDelta min_window_duration = TimeDelta::Seconds(1);
+ TimeDelta max_window_duration = TimeDelta::Seconds(5);
+
+ // The estimator window requires at least `required_packets` packets
+ // to produce an estimate.
+ unsigned required_packets = 10;
+
+ // If audio packets aren't included in allocation (i.e. the
+ // estimated available bandwidth is divided only among the video
+ // streams), then `unacked_weight` should be set to 0.
+ // If audio packets are included in allocation, but not in bandwidth
+ // estimation (i.e. they don't have transport-wide sequence numbers,
+ // but we nevertheless divide the estimated available bandwidth among
+ // both audio and video streams), then `unacked_weight` should be set to 1.
+ // If all packets have transport-wide sequence numbers, then the value
+ // of `unacked_weight` doesn't matter.
+ double unacked_weight = 1.0;
+
+ std::unique_ptr<StructParametersParser> Parser();
+};
+
+class AcknowledgedBitrateEstimatorInterface {
+ public:
+ static std::unique_ptr<AcknowledgedBitrateEstimatorInterface> Create(
+ const FieldTrialsView* key_value_config);
+ virtual ~AcknowledgedBitrateEstimatorInterface();
+
+ virtual void IncomingPacketFeedbackVector(
+ const std::vector<PacketResult>& packet_feedback_vector) = 0;
+ virtual absl::optional<DataRate> bitrate() const = 0;
+ virtual absl::optional<DataRate> PeekRate() const = 0;
+ virtual void SetAlr(bool in_alr) = 0;
+ virtual void SetAlrEndedTime(Timestamp alr_ended_time) = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_unittest.cc
new file mode 100644
index 0000000000..e5b733b119
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_unittest.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
+
+#include <memory>
+#include <utility>
+
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/fake_clock.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+namespace webrtc {
+
+namespace {
+
+constexpr int64_t kFirstArrivalTimeMs = 10;
+constexpr int64_t kFirstSendTimeMs = 10;
+constexpr uint16_t kSequenceNumber = 1;
+constexpr size_t kPayloadSize = 10;
+
+class MockBitrateEstimator : public BitrateEstimator {
+ public:
+ using BitrateEstimator::BitrateEstimator;
+ MOCK_METHOD(void,
+ Update,
+ (Timestamp at_time, DataSize data_size, bool in_alr),
+ (override));
+ MOCK_METHOD(absl::optional<DataRate>, bitrate, (), (const, override));
+ MOCK_METHOD(void, ExpectFastRateChange, (), (override));
+};
+
+struct AcknowledgedBitrateEstimatorTestStates {
+ FieldTrialBasedConfig field_trial_config;
+ std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator;
+ MockBitrateEstimator* mock_bitrate_estimator;
+};
+
+AcknowledgedBitrateEstimatorTestStates CreateTestStates() {
+ AcknowledgedBitrateEstimatorTestStates states;
+ auto mock_bitrate_estimator =
+ std::make_unique<MockBitrateEstimator>(&states.field_trial_config);
+ states.mock_bitrate_estimator = mock_bitrate_estimator.get();
+ states.acknowledged_bitrate_estimator =
+ std::make_unique<AcknowledgedBitrateEstimator>(
+ &states.field_trial_config, std::move(mock_bitrate_estimator));
+ return states;
+}
+
+std::vector<PacketResult> CreateFeedbackVector() {
+ std::vector<PacketResult> packet_feedback_vector(2);
+ packet_feedback_vector[0].receive_time =
+ Timestamp::Millis(kFirstArrivalTimeMs);
+ packet_feedback_vector[0].sent_packet.send_time =
+ Timestamp::Millis(kFirstSendTimeMs);
+ packet_feedback_vector[0].sent_packet.sequence_number = kSequenceNumber;
+ packet_feedback_vector[0].sent_packet.size = DataSize::Bytes(kPayloadSize);
+ packet_feedback_vector[1].receive_time =
+ Timestamp::Millis(kFirstArrivalTimeMs + 10);
+ packet_feedback_vector[1].sent_packet.send_time =
+ Timestamp::Millis(kFirstSendTimeMs + 10);
+ packet_feedback_vector[1].sent_packet.sequence_number = kSequenceNumber;
+ packet_feedback_vector[1].sent_packet.size =
+ DataSize::Bytes(kPayloadSize + 10);
+ return packet_feedback_vector;
+}
+
+} // anonymous namespace
+
+TEST(TestAcknowledgedBitrateEstimator, UpdateBandwidth) {
+ auto states = CreateTestStates();
+ auto packet_feedback_vector = CreateFeedbackVector();
+ {
+ InSequence dummy;
+ EXPECT_CALL(*states.mock_bitrate_estimator,
+ Update(packet_feedback_vector[0].receive_time,
+ packet_feedback_vector[0].sent_packet.size,
+ /*in_alr*/ false))
+ .Times(1);
+ EXPECT_CALL(*states.mock_bitrate_estimator,
+ Update(packet_feedback_vector[1].receive_time,
+ packet_feedback_vector[1].sent_packet.size,
+ /*in_alr*/ false))
+ .Times(1);
+ }
+ states.acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
+ packet_feedback_vector);
+}
+
+TEST(TestAcknowledgedBitrateEstimator, ExpectFastRateChangeWhenLeftAlr) {
+ auto states = CreateTestStates();
+ auto packet_feedback_vector = CreateFeedbackVector();
+ {
+ InSequence dummy;
+ EXPECT_CALL(*states.mock_bitrate_estimator,
+ Update(packet_feedback_vector[0].receive_time,
+ packet_feedback_vector[0].sent_packet.size,
+ /*in_alr*/ false))
+ .Times(1);
+ EXPECT_CALL(*states.mock_bitrate_estimator, ExpectFastRateChange())
+ .Times(1);
+ EXPECT_CALL(*states.mock_bitrate_estimator,
+ Update(packet_feedback_vector[1].receive_time,
+ packet_feedback_vector[1].sent_packet.size,
+ /*in_alr*/ false))
+ .Times(1);
+ }
+ states.acknowledged_bitrate_estimator->SetAlrEndedTime(
+ Timestamp::Millis(kFirstArrivalTimeMs + 1));
+ states.acknowledged_bitrate_estimator->IncomingPacketFeedbackVector(
+ packet_feedback_vector);
+}
+
+TEST(TestAcknowledgedBitrateEstimator, ReturnBitrate) {
+ auto states = CreateTestStates();
+ absl::optional<DataRate> return_value = DataRate::KilobitsPerSec(42);
+ EXPECT_CALL(*states.mock_bitrate_estimator, bitrate())
+ .Times(1)
+ .WillOnce(Return(return_value));
+ EXPECT_EQ(return_value, states.acknowledged_bitrate_estimator->bitrate());
+}
+
+} // namespace webrtc*/
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc
new file mode 100644
index 0000000000..f1e649b7cd
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/alr_detector.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <memory>
+
+#include "api/rtc_event_log/rtc_event.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "logging/rtc_event_log/events/rtc_event_alr_state.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+AlrDetectorConfig GetConfigFromTrials(const FieldTrialsView* key_value_config) {
+ RTC_CHECK(AlrExperimentSettings::MaxOneFieldTrialEnabled(*key_value_config));
+ absl::optional<AlrExperimentSettings> experiment_settings =
+ AlrExperimentSettings::CreateFromFieldTrial(
+ *key_value_config,
+ AlrExperimentSettings::kScreenshareProbingBweExperimentName);
+ if (!experiment_settings) {
+ experiment_settings = AlrExperimentSettings::CreateFromFieldTrial(
+ *key_value_config,
+ AlrExperimentSettings::kStrictPacingAndProbingExperimentName);
+ }
+ AlrDetectorConfig conf;
+ if (experiment_settings) {
+ conf.bandwidth_usage_ratio =
+ experiment_settings->alr_bandwidth_usage_percent / 100.0;
+ conf.start_budget_level_ratio =
+ experiment_settings->alr_start_budget_level_percent / 100.0;
+ conf.stop_budget_level_ratio =
+ experiment_settings->alr_stop_budget_level_percent / 100.0;
+ }
+ conf.Parser()->Parse(
+ key_value_config->Lookup("WebRTC-AlrDetectorParameters"));
+ return conf;
+}
+} // namespace
+
+std::unique_ptr<StructParametersParser> AlrDetectorConfig::Parser() {
+ return StructParametersParser::Create( //
+ "bw_usage", &bandwidth_usage_ratio, //
+ "start", &start_budget_level_ratio, //
+ "stop", &stop_budget_level_ratio);
+}
+
+AlrDetector::AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log)
+ : conf_(config), alr_budget_(0, true), event_log_(event_log) {}
+
+AlrDetector::AlrDetector(const FieldTrialsView* key_value_config)
+ : AlrDetector(GetConfigFromTrials(key_value_config), nullptr) {}
+
+AlrDetector::AlrDetector(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log)
+ : AlrDetector(GetConfigFromTrials(key_value_config), event_log) {}
+AlrDetector::~AlrDetector() {}
+
+void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) {
+ if (!last_send_time_ms_.has_value()) {
+ last_send_time_ms_ = send_time_ms;
+ // Since the duration for sending the bytes is unknwon, return without
+ // updating alr state.
+ return;
+ }
+ int64_t delta_time_ms = send_time_ms - *last_send_time_ms_;
+ last_send_time_ms_ = send_time_ms;
+
+ alr_budget_.UseBudget(bytes_sent);
+ alr_budget_.IncreaseBudget(delta_time_ms);
+ bool state_changed = false;
+ if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio &&
+ !alr_started_time_ms_) {
+ alr_started_time_ms_.emplace(rtc::TimeMillis());
+ state_changed = true;
+ } else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio &&
+ alr_started_time_ms_) {
+ state_changed = true;
+ alr_started_time_ms_.reset();
+ }
+ if (event_log_ && state_changed) {
+ event_log_->Log(
+ std::make_unique<RtcEventAlrState>(alr_started_time_ms_.has_value()));
+ }
+}
+
+void AlrDetector::SetEstimatedBitrate(int bitrate_bps) {
+ RTC_DCHECK(bitrate_bps);
+ int target_rate_kbps =
+ static_cast<double>(bitrate_bps) * conf_.bandwidth_usage_ratio / 1000;
+ alr_budget_.set_target_rate_kbps(target_rate_kbps);
+}
+
+absl::optional<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
+ const {
+ return alr_started_time_ms_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.h
new file mode 100644
index 0000000000..5e7a3e1075
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "modules/pacing/interval_budget.h"
+#include "rtc_base/experiments/alr_experiment.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
+
+namespace webrtc {
+
+class RtcEventLog;
+
+struct AlrDetectorConfig {
+ // Sent traffic ratio as a function of network capacity used to determine
+ // application-limited region. ALR region start when bandwidth usage drops
+ // below kAlrStartUsageRatio and ends when it raises above
+ // kAlrEndUsageRatio. NOTE: This is intentionally conservative at the moment
+ // until BW adjustments of application limited region is fine tuned.
+ double bandwidth_usage_ratio = 0.65;
+ double start_budget_level_ratio = 0.80;
+ double stop_budget_level_ratio = 0.50;
+ std::unique_ptr<StructParametersParser> Parser();
+};
+// Application limited region detector is a class that utilizes signals of
+// elapsed time and bytes sent to estimate whether network traffic is
+// currently limited by the application's ability to generate traffic.
+//
+// AlrDetector provides a signal that can be utilized to adjust
+// estimate bandwidth.
+// Note: This class is not thread-safe.
+class AlrDetector {
+ public:
+ AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log);
+ explicit AlrDetector(const FieldTrialsView* key_value_config);
+ AlrDetector(const FieldTrialsView* key_value_config, RtcEventLog* event_log);
+ ~AlrDetector();
+
+ void OnBytesSent(size_t bytes_sent, int64_t send_time_ms);
+
+ // Set current estimated bandwidth.
+ void SetEstimatedBitrate(int bitrate_bps);
+
+ // Returns time in milliseconds when the current application-limited region
+ // started or empty result if the sender is currently not application-limited.
+ absl::optional<int64_t> GetApplicationLimitedRegionStartTime() const;
+
+ private:
+ friend class GoogCcStatePrinter;
+ const AlrDetectorConfig conf_;
+
+ absl::optional<int64_t> last_send_time_ms_;
+
+ IntervalBudget alr_budget_;
+ absl::optional<int64_t> alr_started_time_ms_;
+
+ RtcEventLog* event_log_;
+};
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build
new file mode 100644
index 0000000000..f6520bf358
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_gn/moz.build
@@ -0,0 +1,225 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("alr_detector_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_unittest.cc
new file mode 100644
index 0000000000..eac19d0081
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/alr_detector_unittest.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/alr_detector.h"
+
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/experiments/alr_experiment.h"
+#include "test/field_trial.h"
+#include "test/gtest.h"
+
+namespace {
+
+constexpr int kEstimatedBitrateBps = 300000;
+
+} // namespace
+
+namespace webrtc {
+namespace {
+class SimulateOutgoingTrafficIn {
+ public:
+ explicit SimulateOutgoingTrafficIn(AlrDetector* alr_detector,
+ int64_t* timestamp_ms)
+ : alr_detector_(alr_detector), timestamp_ms_(timestamp_ms) {
+ RTC_CHECK(alr_detector_);
+ }
+
+ SimulateOutgoingTrafficIn& ForTimeMs(int time_ms) {
+ interval_ms_ = time_ms;
+ ProduceTraffic();
+ return *this;
+ }
+
+ SimulateOutgoingTrafficIn& AtPercentOfEstimatedBitrate(int usage_percentage) {
+ usage_percentage_.emplace(usage_percentage);
+ ProduceTraffic();
+ return *this;
+ }
+
+ private:
+ void ProduceTraffic() {
+ if (!interval_ms_ || !usage_percentage_)
+ return;
+ const int kTimeStepMs = 10;
+ for (int t = 0; t < *interval_ms_; t += kTimeStepMs) {
+ *timestamp_ms_ += kTimeStepMs;
+ alr_detector_->OnBytesSent(kEstimatedBitrateBps * *usage_percentage_ *
+ kTimeStepMs / (8 * 100 * 1000),
+ *timestamp_ms_);
+ }
+ int remainder_ms = *interval_ms_ % kTimeStepMs;
+ if (remainder_ms > 0) {
+ *timestamp_ms_ += kTimeStepMs;
+ alr_detector_->OnBytesSent(kEstimatedBitrateBps * *usage_percentage_ *
+ remainder_ms / (8 * 100 * 1000),
+ *timestamp_ms_);
+ }
+ }
+ AlrDetector* const alr_detector_;
+ int64_t* timestamp_ms_;
+ absl::optional<int> interval_ms_;
+ absl::optional<int> usage_percentage_;
+};
+} // namespace
+
+TEST(AlrDetectorTest, AlrDetection) {
+ FieldTrialBasedConfig field_trials;
+ int64_t timestamp_ms = 1000;
+ AlrDetector alr_detector(&field_trials);
+ alr_detector.SetEstimatedBitrate(kEstimatedBitrateBps);
+
+ // Start in non-ALR state.
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // Stay in non-ALR state when usage is close to 100%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1000)
+ .AtPercentOfEstimatedBitrate(90);
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // Verify that we ALR starts when bitrate drops below 20%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1500)
+ .AtPercentOfEstimatedBitrate(20);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // Verify that ALR ends when usage is above 65%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(4000)
+ .AtPercentOfEstimatedBitrate(100);
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+}
+
+TEST(AlrDetectorTest, ShortSpike) {
+ FieldTrialBasedConfig field_trials;
+ int64_t timestamp_ms = 1000;
+ AlrDetector alr_detector(&field_trials);
+ alr_detector.SetEstimatedBitrate(kEstimatedBitrateBps);
+ // Start in non-ALR state.
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // Verify that we ALR starts when bitrate drops below 20%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1000)
+ .AtPercentOfEstimatedBitrate(20);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // Verify that we stay in ALR region even after a short bitrate spike.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(100)
+ .AtPercentOfEstimatedBitrate(150);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // ALR ends when usage is above 65%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(3000)
+ .AtPercentOfEstimatedBitrate(100);
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+}
+
+TEST(AlrDetectorTest, BandwidthEstimateChanges) {
+ FieldTrialBasedConfig field_trials;
+ int64_t timestamp_ms = 1000;
+ AlrDetector alr_detector(&field_trials);
+ alr_detector.SetEstimatedBitrate(kEstimatedBitrateBps);
+
+ // Start in non-ALR state.
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // ALR starts when bitrate drops below 20%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1000)
+ .AtPercentOfEstimatedBitrate(20);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // When bandwidth estimate drops the detector should stay in ALR mode and quit
+ // it shortly afterwards as the sender continues sending the same amount of
+ // traffic. This is necessary to ensure that ProbeController can still react
+ // to the BWE drop by initiating a new probe.
+ alr_detector.SetEstimatedBitrate(kEstimatedBitrateBps / 5);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1000)
+ .AtPercentOfEstimatedBitrate(50);
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+}
+
+TEST(AlrDetectorTest, ParseControlFieldTrial) {
+ webrtc::test::ScopedFieldTrials scoped_field_trial(
+ "WebRTC-ProbingScreenshareBwe/Control/");
+ absl::optional<AlrExperimentSettings> parsed_params =
+ AlrExperimentSettings::CreateFromFieldTrial(
+ FieldTrialBasedConfig(), "WebRTC-ProbingScreenshareBwe");
+ EXPECT_FALSE(static_cast<bool>(parsed_params));
+}
+
+TEST(AlrDetectorTest, ParseActiveFieldTrial) {
+ webrtc::test::ScopedFieldTrials scoped_field_trial(
+ "WebRTC-ProbingScreenshareBwe/1.1,2875,85,20,-20,1/");
+ absl::optional<AlrExperimentSettings> parsed_params =
+ AlrExperimentSettings::CreateFromFieldTrial(
+ FieldTrialBasedConfig(), "WebRTC-ProbingScreenshareBwe");
+ ASSERT_TRUE(static_cast<bool>(parsed_params));
+ EXPECT_EQ(1.1f, parsed_params->pacing_factor);
+ EXPECT_EQ(2875, parsed_params->max_paced_queue_time);
+ EXPECT_EQ(85, parsed_params->alr_bandwidth_usage_percent);
+ EXPECT_EQ(20, parsed_params->alr_start_budget_level_percent);
+ EXPECT_EQ(-20, parsed_params->alr_stop_budget_level_percent);
+ EXPECT_EQ(1, parsed_params->group_id);
+}
+
+TEST(AlrDetectorTest, ParseAlrSpecificFieldTrial) {
+ webrtc::test::ScopedFieldTrials scoped_field_trial(
+ "WebRTC-AlrDetectorParameters/"
+ "bw_usage:90%,start:0%,stop:-10%/");
+ FieldTrialBasedConfig field_trials;
+ AlrDetector alr_detector(&field_trials);
+ int64_t timestamp_ms = 1000;
+ alr_detector.SetEstimatedBitrate(kEstimatedBitrateBps);
+
+ // Start in non-ALR state.
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // ALR does not start at 100% utilization.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(1000)
+ .AtPercentOfEstimatedBitrate(100);
+ EXPECT_FALSE(alr_detector.GetApplicationLimitedRegionStartTime());
+
+ // ALR does start at 85% utilization.
+ // Overused 10% above so it should take about 2s to reach a budget level of
+ // 0%.
+ SimulateOutgoingTrafficIn(&alr_detector, &timestamp_ms)
+ .ForTimeMs(2100)
+ .AtPercentOfEstimatedBitrate(85);
+ EXPECT_TRUE(alr_detector.GetApplicationLimitedRegionStartTime());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.cc
new file mode 100644
index 0000000000..9c68e48886
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+
+#include "api/units/data_rate.h"
+#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+constexpr int kInitialRateWindowMs = 500;
+constexpr int kRateWindowMs = 150;
+constexpr int kMinRateWindowMs = 150;
+constexpr int kMaxRateWindowMs = 1000;
+
+const char kBweThroughputWindowConfig[] = "WebRTC-BweThroughputWindowConfig";
+
+} // namespace
+
+BitrateEstimator::BitrateEstimator(const FieldTrialsView* key_value_config)
+ : sum_(0),
+ initial_window_ms_("initial_window_ms",
+ kInitialRateWindowMs,
+ kMinRateWindowMs,
+ kMaxRateWindowMs),
+ noninitial_window_ms_("window_ms",
+ kRateWindowMs,
+ kMinRateWindowMs,
+ kMaxRateWindowMs),
+ uncertainty_scale_("scale", 10.0),
+ uncertainty_scale_in_alr_("scale_alr", uncertainty_scale_),
+ small_sample_uncertainty_scale_("scale_small", uncertainty_scale_),
+ small_sample_threshold_("small_thresh", DataSize::Zero()),
+ uncertainty_symmetry_cap_("symmetry_cap", DataRate::Zero()),
+ estimate_floor_("floor", DataRate::Zero()),
+ current_window_ms_(0),
+ prev_time_ms_(-1),
+ bitrate_estimate_kbps_(-1.0f),
+ bitrate_estimate_var_(50.0f) {
+ // E.g WebRTC-BweThroughputWindowConfig/initial_window_ms:350,window_ms:250/
+ ParseFieldTrial(
+ {&initial_window_ms_, &noninitial_window_ms_, &uncertainty_scale_,
+ &uncertainty_scale_in_alr_, &small_sample_uncertainty_scale_,
+ &small_sample_threshold_, &uncertainty_symmetry_cap_, &estimate_floor_},
+ key_value_config->Lookup(kBweThroughputWindowConfig));
+}
+
+BitrateEstimator::~BitrateEstimator() = default;
+
+void BitrateEstimator::Update(Timestamp at_time, DataSize amount, bool in_alr) {
+ int rate_window_ms = noninitial_window_ms_.Get();
+ // We use a larger window at the beginning to get a more stable sample that
+ // we can use to initialize the estimate.
+ if (bitrate_estimate_kbps_ < 0.f)
+ rate_window_ms = initial_window_ms_.Get();
+ bool is_small_sample = false;
+ float bitrate_sample_kbps = UpdateWindow(at_time.ms(), amount.bytes(),
+ rate_window_ms, &is_small_sample);
+ if (bitrate_sample_kbps < 0.0f)
+ return;
+ if (bitrate_estimate_kbps_ < 0.0f) {
+ // This is the very first sample we get. Use it to initialize the estimate.
+ bitrate_estimate_kbps_ = bitrate_sample_kbps;
+ return;
+ }
+ // Optionally use higher uncertainty for very small samples to avoid dropping
+ // estimate and for samples obtained in ALR.
+ float scale = uncertainty_scale_;
+ if (is_small_sample && bitrate_sample_kbps < bitrate_estimate_kbps_) {
+ scale = small_sample_uncertainty_scale_;
+ } else if (in_alr && bitrate_sample_kbps < bitrate_estimate_kbps_) {
+ // Optionally use higher uncertainty for samples obtained during ALR.
+ scale = uncertainty_scale_in_alr_;
+ }
+ // Define the sample uncertainty as a function of how far away it is from the
+ // current estimate. With low values of uncertainty_symmetry_cap_ we add more
+ // uncertainty to increases than to decreases. For higher values we approach
+ // symmetry.
+ float sample_uncertainty =
+ scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) /
+ (bitrate_estimate_kbps_ +
+ std::min(bitrate_sample_kbps,
+ uncertainty_symmetry_cap_.Get().kbps<float>()));
+
+ float sample_var = sample_uncertainty * sample_uncertainty;
+ // Update a bayesian estimate of the rate, weighting it lower if the sample
+ // uncertainty is large.
+ // The bitrate estimate uncertainty is increased with each update to model
+ // that the bitrate changes over time.
+ float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f;
+ bitrate_estimate_kbps_ = (sample_var * bitrate_estimate_kbps_ +
+ pred_bitrate_estimate_var * bitrate_sample_kbps) /
+ (sample_var + pred_bitrate_estimate_var);
+ bitrate_estimate_kbps_ =
+ std::max(bitrate_estimate_kbps_, estimate_floor_.Get().kbps<float>());
+ bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
+ (sample_var + pred_bitrate_estimate_var);
+ BWE_TEST_LOGGING_PLOT(1, "acknowledged_bitrate", at_time.ms(),
+ bitrate_estimate_kbps_ * 1000);
+}
+
+float BitrateEstimator::UpdateWindow(int64_t now_ms,
+ int bytes,
+ int rate_window_ms,
+ bool* is_small_sample) {
+ RTC_DCHECK(is_small_sample != nullptr);
+ // Reset if time moves backwards.
+ if (now_ms < prev_time_ms_) {
+ prev_time_ms_ = -1;
+ sum_ = 0;
+ current_window_ms_ = 0;
+ }
+ if (prev_time_ms_ >= 0) {
+ current_window_ms_ += now_ms - prev_time_ms_;
+ // Reset if nothing has been received for more than a full window.
+ if (now_ms - prev_time_ms_ > rate_window_ms) {
+ sum_ = 0;
+ current_window_ms_ %= rate_window_ms;
+ }
+ }
+ prev_time_ms_ = now_ms;
+ float bitrate_sample = -1.0f;
+ if (current_window_ms_ >= rate_window_ms) {
+ *is_small_sample = sum_ < small_sample_threshold_->bytes();
+ bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
+ current_window_ms_ -= rate_window_ms;
+ sum_ = 0;
+ }
+ sum_ += bytes;
+ return bitrate_sample;
+}
+
+absl::optional<DataRate> BitrateEstimator::bitrate() const {
+ if (bitrate_estimate_kbps_ < 0.f)
+ return absl::nullopt;
+ return DataRate::KilobitsPerSec(bitrate_estimate_kbps_);
+}
+
+absl::optional<DataRate> BitrateEstimator::PeekRate() const {
+ if (current_window_ms_ > 0)
+ return DataSize::Bytes(sum_) / TimeDelta::Millis(current_window_ms_);
+ return absl::nullopt;
+}
+
+void BitrateEstimator::ExpectFastRateChange() {
+ // By setting the bitrate-estimate variance to a higher value we allow the
+ // bitrate to change fast for the next few samples.
+ bitrate_estimate_var_ += 200;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.h
new file mode 100644
index 0000000000..a6f985800e
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
+
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/units/data_rate.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+// Computes a bayesian estimate of the throughput given acks containing
+// the arrival time and payload size. Samples which are far from the current
+// estimate or are based on few packets are given a smaller weight, as they
+// are considered to be more likely to have been caused by, e.g., delay spikes
+// unrelated to congestion.
+class BitrateEstimator {
+ public:
+ explicit BitrateEstimator(const FieldTrialsView* key_value_config);
+ virtual ~BitrateEstimator();
+ virtual void Update(Timestamp at_time, DataSize amount, bool in_alr);
+
+ virtual absl::optional<DataRate> bitrate() const;
+ absl::optional<DataRate> PeekRate() const;
+
+ virtual void ExpectFastRateChange();
+
+ private:
+ float UpdateWindow(int64_t now_ms,
+ int bytes,
+ int rate_window_ms,
+ bool* is_small_sample);
+ int sum_;
+ FieldTrialConstrained<int> initial_window_ms_;
+ FieldTrialConstrained<int> noninitial_window_ms_;
+ FieldTrialParameter<double> uncertainty_scale_;
+ FieldTrialParameter<double> uncertainty_scale_in_alr_;
+ FieldTrialParameter<double> small_sample_uncertainty_scale_;
+ FieldTrialParameter<DataSize> small_sample_threshold_;
+ FieldTrialParameter<DataRate> uncertainty_symmetry_cap_;
+ FieldTrialParameter<DataRate> estimate_floor_;
+ int64_t current_window_ms_;
+ int64_t prev_time_ms_;
+ float bitrate_estimate_kbps_;
+ float bitrate_estimate_var_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc
new file mode 100644
index 0000000000..2f188f30ca
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/experiments/rate_control_settings.h"
+
+namespace webrtc {
+
+CongestionWindowPushbackController::CongestionWindowPushbackController(
+ const FieldTrialsView* key_value_config)
+ : add_pacing_(
+ absl::StartsWith(key_value_config->Lookup(
+ "WebRTC-AddPacingToCongestionWindowPushback"),
+ "Enabled")),
+ min_pushback_target_bitrate_bps_(
+ RateControlSettings::ParseFromKeyValueConfig(key_value_config)
+ .CongestionWindowMinPushbackTargetBitrateBps()),
+ current_data_window_(
+ RateControlSettings::ParseFromKeyValueConfig(key_value_config)
+ .CongestionWindowInitialDataWindow()) {}
+
+void CongestionWindowPushbackController::UpdateOutstandingData(
+ int64_t outstanding_bytes) {
+ outstanding_bytes_ = outstanding_bytes;
+}
+void CongestionWindowPushbackController::UpdatePacingQueue(
+ int64_t pacing_bytes) {
+ pacing_bytes_ = pacing_bytes;
+}
+
+void CongestionWindowPushbackController::SetDataWindow(DataSize data_window) {
+ current_data_window_ = data_window;
+}
+
+uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
+ uint32_t bitrate_bps) {
+ if (!current_data_window_ || current_data_window_->IsZero())
+ return bitrate_bps;
+ int64_t total_bytes = outstanding_bytes_;
+ if (add_pacing_)
+ total_bytes += pacing_bytes_;
+ double fill_ratio =
+ total_bytes / static_cast<double>(current_data_window_->bytes());
+ if (fill_ratio > 1.5) {
+ encoding_rate_ratio_ *= 0.9;
+ } else if (fill_ratio > 1) {
+ encoding_rate_ratio_ *= 0.95;
+ } else if (fill_ratio < 0.1) {
+ encoding_rate_ratio_ = 1.0;
+ } else {
+ encoding_rate_ratio_ *= 1.05;
+ encoding_rate_ratio_ = std::min(encoding_rate_ratio_, 1.0);
+ }
+ uint32_t adjusted_target_bitrate_bps =
+ static_cast<uint32_t>(bitrate_bps * encoding_rate_ratio_);
+
+ // Do not adjust below the minimum pushback bitrate but do obey if the
+ // original estimate is below it.
+ bitrate_bps = adjusted_target_bitrate_bps < min_pushback_target_bitrate_bps_
+ ? std::min(bitrate_bps, min_pushback_target_bitrate_bps_)
+ : adjusted_target_bitrate_bps;
+ return bitrate_bps;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h
new file mode 100644
index 0000000000..ea9ed97c3d
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/units/data_size.h"
+
+namespace webrtc {
+
+// This class enables pushback from congestion window directly to video encoder.
+// When the congestion window is filling up, the video encoder target bitrate
+// will be reduced accordingly to accommodate the network changes. To avoid
+// pausing video too frequently, a minimum encoder target bitrate threshold is
+// used to prevent video pause due to a full congestion window.
+class CongestionWindowPushbackController {
+ public:
+ explicit CongestionWindowPushbackController(
+ const FieldTrialsView* key_value_config);
+ void UpdateOutstandingData(int64_t outstanding_bytes);
+ void UpdatePacingQueue(int64_t pacing_bytes);
+ uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
+ void SetDataWindow(DataSize data_window);
+
+ private:
+ const bool add_pacing_;
+ const uint32_t min_pushback_target_bitrate_bps_;
+ absl::optional<DataSize> current_data_window_;
+ int64_t outstanding_bytes_ = 0;
+ int64_t pacing_bytes_ = 0;
+ double encoding_rate_ratio_ = 1.0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller_unittest.cc
new file mode 100644
index 0000000000..62dde02323
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller_unittest.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
+
+#include <memory>
+
+#include "api/transport/field_trial_based_config.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::_;
+
+namespace webrtc {
+namespace test {
+
+class CongestionWindowPushbackControllerTest : public ::testing::Test {
+ public:
+ CongestionWindowPushbackControllerTest() {
+ cwnd_controller_.reset(
+ new CongestionWindowPushbackController(&field_trial_config_));
+ }
+
+ protected:
+ FieldTrialBasedConfig field_trial_config_;
+
+ std::unique_ptr<CongestionWindowPushbackController> cwnd_controller_;
+};
+
+TEST_F(CongestionWindowPushbackControllerTest, FullCongestionWindow) {
+ cwnd_controller_->UpdateOutstandingData(100000);
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(50000));
+
+ uint32_t bitrate_bps = 80000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(72000u, bitrate_bps);
+
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(50000));
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(static_cast<uint32_t>(72000 * 0.9 * 0.9), bitrate_bps);
+}
+
+TEST_F(CongestionWindowPushbackControllerTest, NormalCongestionWindow) {
+ cwnd_controller_->UpdateOutstandingData(199999);
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(200000));
+
+ uint32_t bitrate_bps = 80000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(80000u, bitrate_bps);
+}
+
+TEST_F(CongestionWindowPushbackControllerTest, LowBitrate) {
+ cwnd_controller_->UpdateOutstandingData(100000);
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(50000));
+
+ uint32_t bitrate_bps = 35000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(static_cast<uint32_t>(35000 * 0.9), bitrate_bps);
+
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(20000));
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(30000u, bitrate_bps);
+}
+
+TEST_F(CongestionWindowPushbackControllerTest, NoPushbackOnDataWindowUnset) {
+ cwnd_controller_->UpdateOutstandingData(1e8); // Large number
+
+ uint32_t bitrate_bps = 80000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_EQ(80000u, bitrate_bps);
+}
+
+TEST_F(CongestionWindowPushbackControllerTest, PushbackOnInititialDataWindow) {
+ test::ScopedFieldTrials trials("WebRTC-CongestionWindow/InitWin:100000/");
+ cwnd_controller_.reset(
+ new CongestionWindowPushbackController(&field_trial_config_));
+ cwnd_controller_->UpdateOutstandingData(1e8); // Large number
+
+ uint32_t bitrate_bps = 80000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_GT(80000u, bitrate_bps);
+}
+
+TEST_F(CongestionWindowPushbackControllerTest, PushbackDropFrame) {
+ test::ScopedFieldTrials trials("WebRTC-CongestionWindow/DropFrame:true/");
+ cwnd_controller_.reset(
+ new CongestionWindowPushbackController(&field_trial_config_));
+ cwnd_controller_->UpdateOutstandingData(1e8); // Large number
+ cwnd_controller_->SetDataWindow(DataSize::Bytes(50000));
+
+ uint32_t bitrate_bps = 80000;
+ bitrate_bps = cwnd_controller_->UpdateTargetBitrate(bitrate_bps);
+ EXPECT_GT(80000u, bitrate_bps);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.cc
new file mode 100644
index 0000000000..07ac599148
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.cc
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/strings/match.h"
+#include "api/rtc_event_log/rtc_event.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/units/time_delta.h"
+#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
+#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
+#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
+#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+namespace {
+constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
+constexpr TimeDelta kSendTimeGroupLength = TimeDelta::Millis(5);
+
+// This ssrc is used to fulfill the current API but will be removed
+// after the API has been changed.
+constexpr uint32_t kFixedSsrc = 0;
+} // namespace
+
+constexpr char BweSeparateAudioPacketsSettings::kKey[];
+
+BweSeparateAudioPacketsSettings::BweSeparateAudioPacketsSettings(
+ const FieldTrialsView* key_value_config) {
+ Parser()->Parse(
+ key_value_config->Lookup(BweSeparateAudioPacketsSettings::kKey));
+}
+
+std::unique_ptr<StructParametersParser>
+BweSeparateAudioPacketsSettings::Parser() {
+ return StructParametersParser::Create( //
+ "enabled", &enabled, //
+ "packet_threshold", &packet_threshold, //
+ "time_threshold", &time_threshold);
+}
+
+DelayBasedBwe::Result::Result()
+ : updated(false),
+ probe(false),
+ target_bitrate(DataRate::Zero()),
+ recovered_from_overuse(false) {}
+
+DelayBasedBwe::DelayBasedBwe(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log,
+ NetworkStatePredictor* network_state_predictor)
+ : event_log_(event_log),
+ key_value_config_(key_value_config),
+ separate_audio_(key_value_config),
+ audio_packets_since_last_video_(0),
+ last_video_packet_recv_time_(Timestamp::MinusInfinity()),
+ network_state_predictor_(network_state_predictor),
+ video_delay_detector_(
+ new TrendlineEstimator(key_value_config_, network_state_predictor_)),
+ audio_delay_detector_(
+ new TrendlineEstimator(key_value_config_, network_state_predictor_)),
+ active_delay_detector_(video_delay_detector_.get()),
+ last_seen_packet_(Timestamp::MinusInfinity()),
+ uma_recorded_(false),
+ rate_control_(key_value_config, /*send_side=*/true),
+ prev_bitrate_(DataRate::Zero()),
+ prev_state_(BandwidthUsage::kBwNormal) {
+ RTC_LOG(LS_INFO)
+ << "Initialized DelayBasedBwe with separate audio overuse detection"
+ << separate_audio_.Parser()->Encode();
+}
+
+DelayBasedBwe::~DelayBasedBwe() {}
+
+DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
+ const TransportPacketsFeedback& msg,
+ absl::optional<DataRate> acked_bitrate,
+ absl::optional<DataRate> probe_bitrate,
+ absl::optional<NetworkStateEstimate> network_estimate,
+ bool in_alr) {
+ RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
+
+ auto packet_feedback_vector = msg.SortedByReceiveTime();
+ // TODO(holmer): An empty feedback vector here likely means that
+ // all acks were too late and that the send time history had
+ // timed out. We should reduce the rate when this occurs.
+ if (packet_feedback_vector.empty()) {
+ RTC_LOG(LS_WARNING) << "Very late feedback received.";
+ return DelayBasedBwe::Result();
+ }
+
+ if (!uma_recorded_) {
+ RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
+ BweNames::kSendSideTransportSeqNum,
+ BweNames::kBweNamesMax);
+ uma_recorded_ = true;
+ }
+ bool delayed_feedback = true;
+ bool recovered_from_overuse = false;
+ BandwidthUsage prev_detector_state = active_delay_detector_->State();
+ for (const auto& packet_feedback : packet_feedback_vector) {
+ delayed_feedback = false;
+ IncomingPacketFeedback(packet_feedback, msg.feedback_time);
+ if (prev_detector_state == BandwidthUsage::kBwUnderusing &&
+ active_delay_detector_->State() == BandwidthUsage::kBwNormal) {
+ recovered_from_overuse = true;
+ }
+ prev_detector_state = active_delay_detector_->State();
+ }
+
+ if (delayed_feedback) {
+ // TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
+ // against building very large network queues.
+ return Result();
+ }
+ rate_control_.SetInApplicationLimitedRegion(in_alr);
+ rate_control_.SetNetworkStateEstimate(network_estimate);
+ return MaybeUpdateEstimate(acked_bitrate, probe_bitrate,
+ std::move(network_estimate),
+ recovered_from_overuse, in_alr, msg.feedback_time);
+}
+
+void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
+ Timestamp at_time) {
+ // Reset if the stream has timed out.
+ if (last_seen_packet_.IsInfinite() ||
+ at_time - last_seen_packet_ > kStreamTimeOut) {
+ video_inter_arrival_delta_ =
+ std::make_unique<InterArrivalDelta>(kSendTimeGroupLength);
+ audio_inter_arrival_delta_ =
+ std::make_unique<InterArrivalDelta>(kSendTimeGroupLength);
+
+ video_delay_detector_.reset(
+ new TrendlineEstimator(key_value_config_, network_state_predictor_));
+ audio_delay_detector_.reset(
+ new TrendlineEstimator(key_value_config_, network_state_predictor_));
+ active_delay_detector_ = video_delay_detector_.get();
+ }
+ last_seen_packet_ = at_time;
+
+ // As an alternative to ignoring small packets, we can separate audio and
+ // video packets for overuse detection.
+ DelayIncreaseDetectorInterface* delay_detector_for_packet =
+ video_delay_detector_.get();
+ if (separate_audio_.enabled) {
+ if (packet_feedback.sent_packet.audio) {
+ delay_detector_for_packet = audio_delay_detector_.get();
+ audio_packets_since_last_video_++;
+ if (audio_packets_since_last_video_ > separate_audio_.packet_threshold &&
+ packet_feedback.receive_time - last_video_packet_recv_time_ >
+ separate_audio_.time_threshold) {
+ active_delay_detector_ = audio_delay_detector_.get();
+ }
+ } else {
+ audio_packets_since_last_video_ = 0;
+ last_video_packet_recv_time_ =
+ std::max(last_video_packet_recv_time_, packet_feedback.receive_time);
+ active_delay_detector_ = video_delay_detector_.get();
+ }
+ }
+ DataSize packet_size = packet_feedback.sent_packet.size;
+
+ TimeDelta send_delta = TimeDelta::Zero();
+ TimeDelta recv_delta = TimeDelta::Zero();
+ int size_delta = 0;
+
+ InterArrivalDelta* inter_arrival_for_packet =
+ (separate_audio_.enabled && packet_feedback.sent_packet.audio)
+ ? audio_inter_arrival_delta_.get()
+ : video_inter_arrival_delta_.get();
+ bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas(
+ packet_feedback.sent_packet.send_time, packet_feedback.receive_time,
+ at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta);
+
+ delay_detector_for_packet->Update(recv_delta.ms<double>(),
+ send_delta.ms<double>(),
+ packet_feedback.sent_packet.send_time.ms(),
+ packet_feedback.receive_time.ms(),
+ packet_size.bytes(), calculated_deltas);
+}
+
+DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,
+ absl::optional<DataRate> link_capacity) {
+ RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity);
+ return rate_control_.Update(&input, at_time);
+}
+
+DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
+ absl::optional<DataRate> acked_bitrate,
+ absl::optional<DataRate> probe_bitrate,
+ absl::optional<NetworkStateEstimate> state_estimate,
+ bool recovered_from_overuse,
+ bool in_alr,
+ Timestamp at_time) {
+ Result result;
+
+ // Currently overusing the bandwidth.
+ if (active_delay_detector_->State() == BandwidthUsage::kBwOverusing) {
+ if (acked_bitrate &&
+ rate_control_.TimeToReduceFurther(at_time, *acked_bitrate)) {
+ result.updated =
+ UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
+ } else if (!acked_bitrate && rate_control_.ValidEstimate() &&
+ rate_control_.InitialTimeToReduceFurther(at_time)) {
+ // Overusing before we have a measured acknowledged bitrate. Reduce send
+ // rate by 50% every 200 ms.
+ // TODO(tschumim): Improve this and/or the acknowledged bitrate estimator
+ // so that we (almost) always have a bitrate estimate.
+ rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2, at_time);
+ result.updated = true;
+ result.probe = false;
+ result.target_bitrate = rate_control_.LatestEstimate();
+ }
+ } else {
+ if (probe_bitrate) {
+ result.probe = true;
+ result.updated = true;
+ rate_control_.SetEstimate(*probe_bitrate, at_time);
+ result.target_bitrate = rate_control_.LatestEstimate();
+ } else {
+ result.updated =
+ UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
+ result.recovered_from_overuse = recovered_from_overuse;
+ }
+ }
+ BandwidthUsage detector_state = active_delay_detector_->State();
+ if ((result.updated && prev_bitrate_ != result.target_bitrate) ||
+ detector_state != prev_state_) {
+ DataRate bitrate = result.updated ? result.target_bitrate : prev_bitrate_;
+
+ BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", at_time.ms(), bitrate.bps());
+
+ if (event_log_) {
+ event_log_->Log(std::make_unique<RtcEventBweUpdateDelayBased>(
+ bitrate.bps(), detector_state));
+ }
+
+ prev_bitrate_ = bitrate;
+ prev_state_ = detector_state;
+ }
+
+ result.delay_detector_state = detector_state;
+ return result;
+}
+
+bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
+ absl::optional<DataRate> acked_bitrate,
+ DataRate* target_rate) {
+ const RateControlInput input(active_delay_detector_->State(), acked_bitrate);
+ *target_rate = rate_control_.Update(&input, at_time);
+ return rate_control_.ValidEstimate();
+}
+
+void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) {
+ rate_control_.SetRtt(avg_rtt);
+}
+
+bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
+ DataRate* bitrate) const {
+ // Currently accessed from both the process thread (see
+ // ModuleRtpRtcpImpl::Process()) and the configuration thread (see
+ // Call::GetStats()). Should in the future only be accessed from a single
+ // thread.
+ RTC_DCHECK(ssrcs);
+ RTC_DCHECK(bitrate);
+ if (!rate_control_.ValidEstimate())
+ return false;
+
+ *ssrcs = {kFixedSsrc};
+ *bitrate = rate_control_.LatestEstimate();
+ return true;
+}
+
+void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) {
+ RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: "
+ << ToString(start_bitrate);
+ rate_control_.SetStartBitrate(start_bitrate);
+}
+
+void DelayBasedBwe::SetMinBitrate(DataRate min_bitrate) {
+ // Called from both the configuration thread and the network thread. Shouldn't
+ // be called from the network thread in the future.
+ rate_control_.SetMinBitrate(min_bitrate);
+}
+
+TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const {
+ return rate_control_.GetExpectedBandwidthPeriod();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.h
new file mode 100644
index 0000000000..e91a1dff54
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/transport/network_types.h"
+#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
+#include "modules/congestion_controller/goog_cc/inter_arrival_delta.h"
+#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
+#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
+#include "modules/remote_bitrate_estimator/inter_arrival.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
+#include "rtc_base/race_checker.h"
+
+namespace webrtc {
+class RtcEventLog;
+
+struct BweSeparateAudioPacketsSettings {
+ static constexpr char kKey[] = "WebRTC-Bwe-SeparateAudioPackets";
+
+ BweSeparateAudioPacketsSettings() = default;
+ explicit BweSeparateAudioPacketsSettings(
+ const FieldTrialsView* key_value_config);
+
+ bool enabled = false;
+ int packet_threshold = 10;
+ TimeDelta time_threshold = TimeDelta::Seconds(1);
+
+ std::unique_ptr<StructParametersParser> Parser();
+};
+
+class DelayBasedBwe {
+ public:
+ struct Result {
+ Result();
+ ~Result() = default;
+ bool updated;
+ bool probe;
+ DataRate target_bitrate = DataRate::Zero();
+ bool recovered_from_overuse;
+ BandwidthUsage delay_detector_state;
+ };
+
+ explicit DelayBasedBwe(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log,
+ NetworkStatePredictor* network_state_predictor);
+
+ DelayBasedBwe() = delete;
+ DelayBasedBwe(const DelayBasedBwe&) = delete;
+ DelayBasedBwe& operator=(const DelayBasedBwe&) = delete;
+
+ virtual ~DelayBasedBwe();
+
+ Result IncomingPacketFeedbackVector(
+ const TransportPacketsFeedback& msg,
+ absl::optional<DataRate> acked_bitrate,
+ absl::optional<DataRate> probe_bitrate,
+ absl::optional<NetworkStateEstimate> network_estimate,
+ bool in_alr);
+ void OnRttUpdate(TimeDelta avg_rtt);
+ bool LatestEstimate(std::vector<uint32_t>* ssrcs, DataRate* bitrate) const;
+ void SetStartBitrate(DataRate start_bitrate);
+ void SetMinBitrate(DataRate min_bitrate);
+ TimeDelta GetExpectedBwePeriod() const;
+ DataRate TriggerOveruse(Timestamp at_time,
+ absl::optional<DataRate> link_capacity);
+ DataRate last_estimate() const { return prev_bitrate_; }
+ BandwidthUsage last_state() const { return prev_state_; }
+
+ private:
+ friend class GoogCcStatePrinter;
+ void IncomingPacketFeedback(const PacketResult& packet_feedback,
+ Timestamp at_time);
+ Result MaybeUpdateEstimate(
+ absl::optional<DataRate> acked_bitrate,
+ absl::optional<DataRate> probe_bitrate,
+ absl::optional<NetworkStateEstimate> state_estimate,
+ bool recovered_from_overuse,
+ bool in_alr,
+ Timestamp at_time);
+ // Updates the current remote rate estimate and returns true if a valid
+ // estimate exists.
+ bool UpdateEstimate(Timestamp at_time,
+ absl::optional<DataRate> acked_bitrate,
+ DataRate* target_rate);
+
+ rtc::RaceChecker network_race_;
+ RtcEventLog* const event_log_;
+ const FieldTrialsView* const key_value_config_;
+
+ // Alternatively, run two separate overuse detectors for audio and video,
+ // and fall back to the audio one if we haven't seen a video packet in a
+ // while.
+ BweSeparateAudioPacketsSettings separate_audio_;
+ int64_t audio_packets_since_last_video_;
+ Timestamp last_video_packet_recv_time_;
+
+ NetworkStatePredictor* network_state_predictor_;
+ std::unique_ptr<InterArrival> video_inter_arrival_;
+ std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_;
+ std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_;
+ std::unique_ptr<InterArrival> audio_inter_arrival_;
+ std::unique_ptr<InterArrivalDelta> audio_inter_arrival_delta_;
+ std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_;
+ DelayIncreaseDetectorInterface* active_delay_detector_;
+
+ Timestamp last_seen_packet_;
+ bool uma_recorded_;
+ AimdRateControl rate_control_;
+ DataRate prev_bitrate_;
+ BandwidthUsage prev_state_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build
new file mode 100644
index 0000000000..15f5a583ef
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_gn/moz.build
@@ -0,0 +1,234 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("delay_based_bwe_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc
new file mode 100644
index 0000000000..b7dc6aae47
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+
+#include <string>
+
+#include "api/transport/network_types.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
+#include "modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+constexpr int kNumProbesCluster0 = 5;
+constexpr int kNumProbesCluster1 = 8;
+const PacedPacketInfo kPacingInfo0(0, kNumProbesCluster0, 2000);
+const PacedPacketInfo kPacingInfo1(1, kNumProbesCluster1, 4000);
+constexpr float kTargetUtilizationFraction = 0.95f;
+} // namespace
+
+TEST_F(DelayBasedBweTest, ProbeDetection) {
+ int64_t now_ms = clock_.TimeInMilliseconds();
+
+ // First burst sent at 8 * 1000 / 10 = 800 kbps.
+ for (int i = 0; i < kNumProbesCluster0; ++i) {
+ clock_.AdvanceTimeMilliseconds(10);
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo0);
+ }
+ EXPECT_TRUE(bitrate_observer_.updated());
+
+ // Second burst sent at 8 * 1000 / 5 = 1600 kbps.
+ for (int i = 0; i < kNumProbesCluster1; ++i) {
+ clock_.AdvanceTimeMilliseconds(5);
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo1);
+ }
+
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_GT(bitrate_observer_.latest_bitrate(), 1500000u);
+}
+
+TEST_F(DelayBasedBweTest, ProbeDetectionNonPacedPackets) {
+ int64_t now_ms = clock_.TimeInMilliseconds();
+ // First burst sent at 8 * 1000 / 10 = 800 kbps, but with every other packet
+ // not being paced which could mess things up.
+ for (int i = 0; i < kNumProbesCluster0; ++i) {
+ clock_.AdvanceTimeMilliseconds(5);
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo0);
+ // Non-paced packet, arriving 5 ms after.
+ clock_.AdvanceTimeMilliseconds(5);
+ IncomingFeedback(now_ms, now_ms, 100, PacedPacketInfo());
+ }
+
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_GT(bitrate_observer_.latest_bitrate(), 800000u);
+}
+
+TEST_F(DelayBasedBweTest, ProbeDetectionFasterArrival) {
+ int64_t now_ms = clock_.TimeInMilliseconds();
+ // First burst sent at 8 * 1000 / 10 = 800 kbps.
+ // Arriving at 8 * 1000 / 5 = 1600 kbps.
+ int64_t send_time_ms = 0;
+ for (int i = 0; i < kNumProbesCluster0; ++i) {
+ clock_.AdvanceTimeMilliseconds(1);
+ send_time_ms += 10;
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo0);
+ }
+
+ EXPECT_FALSE(bitrate_observer_.updated());
+}
+
+TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrival) {
+ int64_t now_ms = clock_.TimeInMilliseconds();
+ // First burst sent at 8 * 1000 / 5 = 1600 kbps.
+ // Arriving at 8 * 1000 / 7 = 1142 kbps.
+ // Since the receive rate is significantly below the send rate, we expect to
+ // use 95% of the estimated capacity.
+ int64_t send_time_ms = 0;
+ for (int i = 0; i < kNumProbesCluster1; ++i) {
+ clock_.AdvanceTimeMilliseconds(7);
+ send_time_ms += 5;
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo1);
+ }
+
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
+ kTargetUtilizationFraction * 1140000u, 10000u);
+}
+
+TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) {
+ int64_t now_ms = clock_.TimeInMilliseconds();
+ // Burst sent at 8 * 1000 / 1 = 8000 kbps.
+ // Arriving at 8 * 1000 / 2 = 4000 kbps.
+ // Since the receive rate is significantly below the send rate, we expect to
+ // use 95% of the estimated capacity.
+ int64_t send_time_ms = 0;
+ for (int i = 0; i < kNumProbesCluster1; ++i) {
+ clock_.AdvanceTimeMilliseconds(2);
+ send_time_ms += 1;
+ now_ms = clock_.TimeInMilliseconds();
+ IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo1);
+ }
+
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
+ kTargetUtilizationFraction * 4000000u, 10000u);
+}
+
+TEST_F(DelayBasedBweTest, GetExpectedBwePeriodMs) {
+ auto default_interval = bitrate_estimator_->GetExpectedBwePeriod();
+ EXPECT_GT(default_interval.ms(), 0);
+ CapacityDropTestHelper(1, true, 333, 0);
+ auto interval = bitrate_estimator_->GetExpectedBwePeriod();
+ EXPECT_GT(interval.ms(), 0);
+ EXPECT_NE(interval.ms(), default_interval.ms());
+}
+
+TEST_F(DelayBasedBweTest, InitialBehavior) {
+ InitialBehaviorTestHelper(730000);
+}
+
+TEST_F(DelayBasedBweTest, RateIncreaseReordering) {
+ RateIncreaseReorderingTestHelper(730000);
+}
+TEST_F(DelayBasedBweTest, RateIncreaseRtpTimestamps) {
+ RateIncreaseRtpTimestampsTestHelper(622);
+}
+
+TEST_F(DelayBasedBweTest, CapacityDropOneStream) {
+ CapacityDropTestHelper(1, false, 300, 0);
+}
+
+TEST_F(DelayBasedBweTest, CapacityDropPosOffsetChange) {
+ CapacityDropTestHelper(1, false, 867, 30000);
+}
+
+TEST_F(DelayBasedBweTest, CapacityDropNegOffsetChange) {
+ CapacityDropTestHelper(1, false, 933, -30000);
+}
+
+TEST_F(DelayBasedBweTest, CapacityDropOneStreamWrap) {
+ CapacityDropTestHelper(1, true, 333, 0);
+}
+
+TEST_F(DelayBasedBweTest, TestTimestampGrouping) {
+ TestTimestampGroupingTestHelper();
+}
+
+TEST_F(DelayBasedBweTest, TestShortTimeoutAndWrap) {
+ // Simulate a client leaving and rejoining the call after 35 seconds. This
+ // will make abs send time wrap, so if streams aren't timed out properly
+ // the next 30 seconds of packets will be out of order.
+ TestWrappingHelper(35);
+}
+
+TEST_F(DelayBasedBweTest, TestLongTimeoutAndWrap) {
+ // Simulate a client leaving and rejoining the call after some multiple of
+ // 64 seconds later. This will cause a zero difference in abs send times due
+ // to the wrap, but a big difference in arrival time, if streams aren't
+ // properly timed out.
+ TestWrappingHelper(10 * 64);
+}
+
+TEST_F(DelayBasedBweTest, TestInitialOveruse) {
+ const DataRate kStartBitrate = DataRate::KilobitsPerSec(300);
+ const DataRate kInitialCapacity = DataRate::KilobitsPerSec(200);
+ const uint32_t kDummySsrc = 0;
+ // High FPS to ensure that we send a lot of packets in a short time.
+ const int kFps = 90;
+
+ stream_generator_->AddStream(new test::RtpStream(kFps, kStartBitrate.bps()));
+ stream_generator_->set_capacity_bps(kInitialCapacity.bps());
+
+ // Needed to initialize the AimdRateControl.
+ bitrate_estimator_->SetStartBitrate(kStartBitrate);
+
+ // Produce 30 frames (in 1/3 second) and give them to the estimator.
+ int64_t bitrate_bps = kStartBitrate.bps();
+ bool seen_overuse = false;
+ for (int i = 0; i < 30; ++i) {
+ bool overuse = GenerateAndProcessFrame(kDummySsrc, bitrate_bps);
+ // The purpose of this test is to ensure that we back down even if we don't
+ // have any acknowledged bitrate estimate yet. Hence, if the test works
+ // as expected, we should not have a measured bitrate yet.
+ EXPECT_FALSE(acknowledged_bitrate_estimator_->bitrate().has_value());
+ if (overuse) {
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(bitrate_observer_.latest_bitrate(), kStartBitrate.bps() / 2,
+ 15000);
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ seen_overuse = true;
+ break;
+ } else if (bitrate_observer_.updated()) {
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_observer_.Reset();
+ }
+ }
+ EXPECT_TRUE(seen_overuse);
+ EXPECT_NEAR(bitrate_observer_.latest_bitrate(), kStartBitrate.bps() / 2,
+ 15000);
+}
+
+TEST_F(DelayBasedBweTest, TestTimestampPrecisionHandling) {
+ // This test does some basic checks to make sure that timestamps with higher
+ // than millisecond precision are handled properly and do not cause any
+ // problems in the estimator. Specifically, previously reported in
+ // webrtc:14023 and described in more details there, the rounding to the
+ // nearest milliseconds caused discrepancy in the accumulated delay. This lead
+ // to false-positive overuse detection.
+ // Technical details of the test:
+ // Send times(ms): 0.000, 9.725, 20.000, 29.725, 40.000, 49.725, ...
+ // Recv times(ms): 0.500, 10.000, 20.500, 30.000, 40.500, 50.000, ...
+ // Send deltas(ms): 9.750, 10.250, 9.750, 10.250, 9.750, ...
+ // Recv deltas(ms): 9.500, 10.500, 9.500, 10.500, 9.500, ...
+ // There is no delay building up between the send times and the receive times,
+ // therefore this case should never lead to an overuse detection. However, if
+ // the time deltas were accidentally rounded to the nearest milliseconds, then
+ // all the send deltas would be equal to 10ms while some recv deltas would
+ // round up to 11ms which would lead in a false illusion of delay build up.
+ uint32_t last_bitrate = bitrate_observer_.latest_bitrate();
+ for (int i = 0; i < 1000; ++i) {
+ clock_.AdvanceTimeMicroseconds(500);
+ IncomingFeedback(clock_.CurrentTime(),
+ clock_.CurrentTime() - TimeDelta::Micros(500), 1000,
+ PacedPacketInfo());
+ clock_.AdvanceTimeMicroseconds(9500);
+ IncomingFeedback(clock_.CurrentTime(),
+ clock_.CurrentTime() - TimeDelta::Micros(250), 1000,
+ PacedPacketInfo());
+ clock_.AdvanceTimeMicroseconds(10000);
+
+ // The bitrate should never decrease in this test.
+ EXPECT_LE(last_bitrate, bitrate_observer_.latest_bitrate());
+ last_bitrate = bitrate_observer_.latest_bitrate();
+ }
+}
+
+class DelayBasedBweTestWithBackoffTimeoutExperiment : public DelayBasedBweTest {
+ public:
+ DelayBasedBweTestWithBackoffTimeoutExperiment()
+ : DelayBasedBweTest(
+ "WebRTC-BweAimdRateControlConfig/initial_backoff_interval:200ms/") {
+ }
+};
+
+// This test subsumes and improves DelayBasedBweTest.TestInitialOveruse above.
+TEST_F(DelayBasedBweTestWithBackoffTimeoutExperiment, TestInitialOveruse) {
+ const DataRate kStartBitrate = DataRate::KilobitsPerSec(300);
+ const DataRate kInitialCapacity = DataRate::KilobitsPerSec(200);
+ const uint32_t kDummySsrc = 0;
+ // High FPS to ensure that we send a lot of packets in a short time.
+ const int kFps = 90;
+
+ stream_generator_->AddStream(new test::RtpStream(kFps, kStartBitrate.bps()));
+ stream_generator_->set_capacity_bps(kInitialCapacity.bps());
+
+ // Needed to initialize the AimdRateControl.
+ bitrate_estimator_->SetStartBitrate(kStartBitrate);
+
+ // Produce 30 frames (in 1/3 second) and give them to the estimator.
+ int64_t bitrate_bps = kStartBitrate.bps();
+ bool seen_overuse = false;
+ for (int frames = 0; frames < 30 && !seen_overuse; ++frames) {
+ bool overuse = GenerateAndProcessFrame(kDummySsrc, bitrate_bps);
+ // The purpose of this test is to ensure that we back down even if we don't
+ // have any acknowledged bitrate estimate yet. Hence, if the test works
+ // as expected, we should not have a measured bitrate yet.
+ EXPECT_FALSE(acknowledged_bitrate_estimator_->bitrate().has_value());
+ if (overuse) {
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(bitrate_observer_.latest_bitrate(), kStartBitrate.bps() / 2,
+ 15000);
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ seen_overuse = true;
+ } else if (bitrate_observer_.updated()) {
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_observer_.Reset();
+ }
+ }
+ EXPECT_TRUE(seen_overuse);
+ // Continue generating an additional 15 frames (equivalent to 167 ms) and
+ // verify that we don't back down further.
+ for (int frames = 0; frames < 15 && seen_overuse; ++frames) {
+ bool overuse = GenerateAndProcessFrame(kDummySsrc, bitrate_bps);
+ EXPECT_FALSE(overuse);
+ if (bitrate_observer_.updated()) {
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ EXPECT_GE(bitrate_bps, kStartBitrate.bps() / 2 - 15000);
+ EXPECT_LE(bitrate_bps, kInitialCapacity.bps() + 15000);
+ bitrate_observer_.Reset();
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc
new file mode 100644
index 0000000000..8618a7814e
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+constexpr size_t kMtu = 1200;
+constexpr uint32_t kAcceptedBitrateErrorBps = 50000;
+
+// Number of packets needed before we have a valid estimate.
+constexpr int kNumInitialPackets = 2;
+
+constexpr int kInitialProbingPackets = 5;
+
+namespace test {
+
+void TestBitrateObserver::OnReceiveBitrateChanged(uint32_t bitrate) {
+ latest_bitrate_ = bitrate;
+ updated_ = true;
+}
+
+RtpStream::RtpStream(int fps, int bitrate_bps)
+ : fps_(fps), bitrate_bps_(bitrate_bps), next_rtp_time_(0) {
+ RTC_CHECK_GT(fps_, 0);
+}
+
+// Generates a new frame for this stream. If called too soon after the
+// previous frame, no frame will be generated. The frame is split into
+// packets.
+int64_t RtpStream::GenerateFrame(int64_t time_now_us,
+ std::vector<PacketResult>* packets) {
+ if (time_now_us < next_rtp_time_) {
+ return next_rtp_time_;
+ }
+ RTC_CHECK(packets != NULL);
+ size_t bits_per_frame = (bitrate_bps_ + fps_ / 2) / fps_;
+ size_t n_packets =
+ std::max<size_t>((bits_per_frame + 4 * kMtu) / (8 * kMtu), 1u);
+ size_t payload_size = (bits_per_frame + 4 * n_packets) / (8 * n_packets);
+ for (size_t i = 0; i < n_packets; ++i) {
+ PacketResult packet;
+ packet.sent_packet.send_time =
+ Timestamp::Micros(time_now_us + kSendSideOffsetUs);
+ packet.sent_packet.size = DataSize::Bytes(payload_size);
+ packets->push_back(packet);
+ }
+ next_rtp_time_ = time_now_us + (1000000 + fps_ / 2) / fps_;
+ return next_rtp_time_;
+}
+
+// The send-side time when the next frame can be generated.
+int64_t RtpStream::next_rtp_time() const {
+ return next_rtp_time_;
+}
+
+void RtpStream::set_bitrate_bps(int bitrate_bps) {
+ ASSERT_GE(bitrate_bps, 0);
+ bitrate_bps_ = bitrate_bps;
+}
+
+int RtpStream::bitrate_bps() const {
+ return bitrate_bps_;
+}
+
+bool RtpStream::Compare(const std::unique_ptr<RtpStream>& lhs,
+ const std::unique_ptr<RtpStream>& rhs) {
+ return lhs->next_rtp_time_ < rhs->next_rtp_time_;
+}
+
+StreamGenerator::StreamGenerator(int capacity, int64_t time_now)
+ : capacity_(capacity), prev_arrival_time_us_(time_now) {}
+
+StreamGenerator::~StreamGenerator() = default;
+
+// Add a new stream.
+void StreamGenerator::AddStream(RtpStream* stream) {
+ streams_.push_back(std::unique_ptr<RtpStream>(stream));
+}
+
+// Set the link capacity.
+void StreamGenerator::set_capacity_bps(int capacity_bps) {
+ ASSERT_GT(capacity_bps, 0);
+ capacity_ = capacity_bps;
+}
+
+// Divides `bitrate_bps` among all streams. The allocated bitrate per stream
+// is decided by the current allocation ratios.
+void StreamGenerator::SetBitrateBps(int bitrate_bps) {
+ ASSERT_GE(streams_.size(), 0u);
+ int total_bitrate_before = 0;
+ for (const auto& stream : streams_) {
+ total_bitrate_before += stream->bitrate_bps();
+ }
+ int64_t bitrate_before = 0;
+ int total_bitrate_after = 0;
+ for (const auto& stream : streams_) {
+ bitrate_before += stream->bitrate_bps();
+ int64_t bitrate_after =
+ (bitrate_before * bitrate_bps + total_bitrate_before / 2) /
+ total_bitrate_before;
+ stream->set_bitrate_bps(bitrate_after - total_bitrate_after);
+ total_bitrate_after += stream->bitrate_bps();
+ }
+ ASSERT_EQ(bitrate_before, total_bitrate_before);
+ EXPECT_EQ(total_bitrate_after, bitrate_bps);
+}
+
+// TODO(holmer): Break out the channel simulation part from this class to make
+// it possible to simulate different types of channels.
+int64_t StreamGenerator::GenerateFrame(std::vector<PacketResult>* packets,
+ int64_t time_now_us) {
+ RTC_CHECK(packets != NULL);
+ RTC_CHECK(packets->empty());
+ RTC_CHECK_GT(capacity_, 0);
+ auto it =
+ std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
+ (*it)->GenerateFrame(time_now_us, packets);
+ for (PacketResult& packet : *packets) {
+ int capacity_bpus = capacity_ / 1000;
+ int64_t required_network_time_us =
+ (8 * 1000 * packet.sent_packet.size.bytes() + capacity_bpus / 2) /
+ capacity_bpus;
+ prev_arrival_time_us_ =
+ std::max(time_now_us + required_network_time_us,
+ prev_arrival_time_us_ + required_network_time_us);
+ packet.receive_time = Timestamp::Micros(prev_arrival_time_us_);
+ }
+ it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
+ return std::max((*it)->next_rtp_time(), time_now_us);
+}
+} // namespace test
+
+DelayBasedBweTest::DelayBasedBweTest() : DelayBasedBweTest("") {}
+
+DelayBasedBweTest::DelayBasedBweTest(absl::string_view field_trial_string)
+ : field_trial(
+ std::make_unique<test::ScopedFieldTrials>(field_trial_string)),
+ clock_(100000000),
+ acknowledged_bitrate_estimator_(
+ AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)),
+ probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
+ bitrate_estimator_(
+ new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
+ stream_generator_(new test::StreamGenerator(1e6, // Capacity.
+ clock_.TimeInMicroseconds())),
+ arrival_time_offset_ms_(0),
+ first_update_(true) {}
+
+DelayBasedBweTest::~DelayBasedBweTest() {}
+
+void DelayBasedBweTest::AddDefaultStream() {
+ stream_generator_->AddStream(new test::RtpStream(30, 3e5));
+}
+
+const uint32_t DelayBasedBweTest::kDefaultSsrc = 0;
+
+void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
+ int64_t send_time_ms,
+ size_t payload_size) {
+ IncomingFeedback(arrival_time_ms, send_time_ms, payload_size,
+ PacedPacketInfo());
+}
+
+void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
+ int64_t send_time_ms,
+ size_t payload_size,
+ const PacedPacketInfo& pacing_info) {
+ RTC_CHECK_GE(arrival_time_ms + arrival_time_offset_ms_, 0);
+ IncomingFeedback(Timestamp::Millis(arrival_time_ms + arrival_time_offset_ms_),
+ Timestamp::Millis(send_time_ms), payload_size, pacing_info);
+}
+
+void DelayBasedBweTest::IncomingFeedback(Timestamp receive_time,
+ Timestamp send_time,
+ size_t payload_size,
+ const PacedPacketInfo& pacing_info) {
+ PacketResult packet;
+ packet.receive_time = receive_time;
+ packet.sent_packet.send_time = send_time;
+ packet.sent_packet.size = DataSize::Bytes(payload_size);
+ packet.sent_packet.pacing_info = pacing_info;
+ if (packet.sent_packet.pacing_info.probe_cluster_id !=
+ PacedPacketInfo::kNotAProbe)
+ probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(packet);
+
+ TransportPacketsFeedback msg;
+ msg.feedback_time = Timestamp::Millis(clock_.TimeInMilliseconds());
+ msg.packet_feedbacks.push_back(packet);
+ acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
+ msg.SortedByReceiveTime());
+ DelayBasedBwe::Result result =
+ bitrate_estimator_->IncomingPacketFeedbackVector(
+ msg, acknowledged_bitrate_estimator_->bitrate(),
+ probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(),
+ /*network_estimate*/ absl::nullopt, /*in_alr*/ false);
+ if (result.updated) {
+ bitrate_observer_.OnReceiveBitrateChanged(result.target_bitrate.bps());
+ }
+}
+
+// Generates a frame of packets belonging to a stream at a given bitrate and
+// with a given ssrc. The stream is pushed through a very simple simulated
+// network, and is then given to the receive-side bandwidth estimator.
+// Returns true if an over-use was seen, false otherwise.
+// The StreamGenerator::updated() should be used to check for any changes in
+// target bitrate after the call to this function.
+bool DelayBasedBweTest::GenerateAndProcessFrame(uint32_t ssrc,
+ uint32_t bitrate_bps) {
+ stream_generator_->SetBitrateBps(bitrate_bps);
+ std::vector<PacketResult> packets;
+
+ int64_t next_time_us =
+ stream_generator_->GenerateFrame(&packets, clock_.TimeInMicroseconds());
+ if (packets.empty())
+ return false;
+
+ bool overuse = false;
+ bitrate_observer_.Reset();
+ clock_.AdvanceTimeMicroseconds(packets.back().receive_time.us() -
+ clock_.TimeInMicroseconds());
+ for (auto& packet : packets) {
+ RTC_CHECK_GE(packet.receive_time.ms() + arrival_time_offset_ms_, 0);
+ packet.receive_time += TimeDelta::Millis(arrival_time_offset_ms_);
+
+ if (packet.sent_packet.pacing_info.probe_cluster_id !=
+ PacedPacketInfo::kNotAProbe)
+ probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(packet);
+ }
+
+ acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(packets);
+ TransportPacketsFeedback msg;
+ msg.packet_feedbacks = packets;
+ msg.feedback_time = Timestamp::Millis(clock_.TimeInMilliseconds());
+
+ DelayBasedBwe::Result result =
+ bitrate_estimator_->IncomingPacketFeedbackVector(
+ msg, acknowledged_bitrate_estimator_->bitrate(),
+ probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(),
+ /*network_estimate*/ absl::nullopt, /*in_alr*/ false);
+ if (result.updated) {
+ bitrate_observer_.OnReceiveBitrateChanged(result.target_bitrate.bps());
+ if (!first_update_ && result.target_bitrate.bps() < bitrate_bps)
+ overuse = true;
+ first_update_ = false;
+ }
+
+ clock_.AdvanceTimeMicroseconds(next_time_us - clock_.TimeInMicroseconds());
+ return overuse;
+}
+
+// Run the bandwidth estimator with a stream of `number_of_frames` frames, or
+// until it reaches `target_bitrate`.
+// Can for instance be used to run the estimator for some time to get it
+// into a steady state.
+uint32_t DelayBasedBweTest::SteadyStateRun(uint32_t ssrc,
+ int max_number_of_frames,
+ uint32_t start_bitrate,
+ uint32_t min_bitrate,
+ uint32_t max_bitrate,
+ uint32_t target_bitrate) {
+ uint32_t bitrate_bps = start_bitrate;
+ bool bitrate_update_seen = false;
+ // Produce `number_of_frames` frames and give them to the estimator.
+ for (int i = 0; i < max_number_of_frames; ++i) {
+ bool overuse = GenerateAndProcessFrame(ssrc, bitrate_bps);
+ if (overuse) {
+ EXPECT_LT(bitrate_observer_.latest_bitrate(), max_bitrate);
+ EXPECT_GT(bitrate_observer_.latest_bitrate(), min_bitrate);
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_update_seen = true;
+ } else if (bitrate_observer_.updated()) {
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_observer_.Reset();
+ }
+ if (bitrate_update_seen && bitrate_bps > target_bitrate) {
+ break;
+ }
+ }
+ EXPECT_TRUE(bitrate_update_seen);
+ return bitrate_bps;
+}
+
+void DelayBasedBweTest::InitialBehaviorTestHelper(
+ uint32_t expected_converge_bitrate) {
+ const int kFramerate = 50; // 50 fps to avoid rounding errors.
+ const int kFrameIntervalMs = 1000 / kFramerate;
+ const PacedPacketInfo kPacingInfo(0, 5, 5000);
+ DataRate bitrate = DataRate::Zero();
+ int64_t send_time_ms = 0;
+ std::vector<uint32_t> ssrcs;
+ EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
+ EXPECT_EQ(0u, ssrcs.size());
+ clock_.AdvanceTimeMilliseconds(1000);
+ EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
+ EXPECT_FALSE(bitrate_observer_.updated());
+ bitrate_observer_.Reset();
+ clock_.AdvanceTimeMilliseconds(1000);
+ // Inserting packets for 5 seconds to get a valid estimate.
+ for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
+ // NOTE!!! If the following line is moved under the if case then this test
+ // wont work on windows realease bots.
+ PacedPacketInfo pacing_info =
+ i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
+
+ if (i == kNumInitialPackets) {
+ EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
+ EXPECT_EQ(0u, ssrcs.size());
+ EXPECT_FALSE(bitrate_observer_.updated());
+ bitrate_observer_.Reset();
+ }
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, kMtu,
+ pacing_info);
+ clock_.AdvanceTimeMilliseconds(1000 / kFramerate);
+ send_time_ms += kFrameIntervalMs;
+ }
+ EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
+ ASSERT_EQ(1u, ssrcs.size());
+ EXPECT_EQ(kDefaultSsrc, ssrcs.front());
+ EXPECT_NEAR(expected_converge_bitrate, bitrate.bps(),
+ kAcceptedBitrateErrorBps);
+ EXPECT_TRUE(bitrate_observer_.updated());
+ bitrate_observer_.Reset();
+ EXPECT_EQ(bitrate_observer_.latest_bitrate(), bitrate.bps());
+}
+
+void DelayBasedBweTest::RateIncreaseReorderingTestHelper(
+ uint32_t expected_bitrate_bps) {
+ const int kFramerate = 50; // 50 fps to avoid rounding errors.
+ const int kFrameIntervalMs = 1000 / kFramerate;
+ const PacedPacketInfo kPacingInfo(0, 5, 5000);
+ int64_t send_time_ms = 0;
+ // Inserting packets for five seconds to get a valid estimate.
+ for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
+ // NOTE!!! If the following line is moved under the if case then this test
+ // wont work on windows realease bots.
+ PacedPacketInfo pacing_info =
+ i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
+
+ // TODO(sprang): Remove this hack once the single stream estimator is gone,
+ // as it doesn't do anything in Process().
+ if (i == kNumInitialPackets) {
+ // Process after we have enough frames to get a valid input rate estimate.
+
+ EXPECT_FALSE(bitrate_observer_.updated()); // No valid estimate.
+ }
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, kMtu,
+ pacing_info);
+ clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
+ send_time_ms += kFrameIntervalMs;
+ }
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
+ kAcceptedBitrateErrorBps);
+ for (int i = 0; i < 10; ++i) {
+ clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
+ send_time_ms += 2 * kFrameIntervalMs;
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
+ IncomingFeedback(clock_.TimeInMilliseconds(),
+ send_time_ms - kFrameIntervalMs, 1000);
+ }
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
+ kAcceptedBitrateErrorBps);
+}
+
+// Make sure we initially increase the bitrate as expected.
+void DelayBasedBweTest::RateIncreaseRtpTimestampsTestHelper(
+ int expected_iterations) {
+ // This threshold corresponds approximately to increasing linearly with
+ // bitrate(i) = 1.04 * bitrate(i-1) + 1000
+ // until bitrate(i) > 500000, with bitrate(1) ~= 30000.
+ uint32_t bitrate_bps = 30000;
+ int iterations = 0;
+ AddDefaultStream();
+ // Feed the estimator with a stream of packets and verify that it reaches
+ // 500 kbps at the expected time.
+ while (bitrate_bps < 5e5) {
+ bool overuse = GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
+ if (overuse) {
+ EXPECT_GT(bitrate_observer_.latest_bitrate(), bitrate_bps);
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_observer_.Reset();
+ } else if (bitrate_observer_.updated()) {
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ bitrate_observer_.Reset();
+ }
+ ++iterations;
+ }
+ ASSERT_EQ(expected_iterations, iterations);
+}
+
+void DelayBasedBweTest::CapacityDropTestHelper(
+ int number_of_streams,
+ bool wrap_time_stamp,
+ uint32_t expected_bitrate_drop_delta,
+ int64_t receiver_clock_offset_change_ms) {
+ const int kFramerate = 30;
+ const int kStartBitrate = 900e3;
+ const int kMinExpectedBitrate = 800e3;
+ const int kMaxExpectedBitrate = 1100e3;
+ const uint32_t kInitialCapacityBps = 1000e3;
+ const uint32_t kReducedCapacityBps = 500e3;
+
+ int steady_state_time = 0;
+ if (number_of_streams <= 1) {
+ steady_state_time = 10;
+ AddDefaultStream();
+ } else {
+ steady_state_time = 10 * number_of_streams;
+ int bitrate_sum = 0;
+ int kBitrateDenom = number_of_streams * (number_of_streams - 1);
+ for (int i = 0; i < number_of_streams; i++) {
+ // First stream gets half available bitrate, while the rest share the
+ // remaining half i.e.: 1/2 = Sum[n/(N*(N-1))] for n=1..N-1 (rounded up)
+ int bitrate = kStartBitrate / 2;
+ if (i > 0) {
+ bitrate = (kStartBitrate * i + kBitrateDenom / 2) / kBitrateDenom;
+ }
+ stream_generator_->AddStream(new test::RtpStream(kFramerate, bitrate));
+ bitrate_sum += bitrate;
+ }
+ ASSERT_EQ(bitrate_sum, kStartBitrate);
+ }
+
+ // Run in steady state to make the estimator converge.
+ stream_generator_->set_capacity_bps(kInitialCapacityBps);
+ uint32_t bitrate_bps = SteadyStateRun(
+ kDefaultSsrc, steady_state_time * kFramerate, kStartBitrate,
+ kMinExpectedBitrate, kMaxExpectedBitrate, kInitialCapacityBps);
+ EXPECT_NEAR(kInitialCapacityBps, bitrate_bps, 180000u);
+ bitrate_observer_.Reset();
+
+ // Add an offset to make sure the BWE can handle it.
+ arrival_time_offset_ms_ += receiver_clock_offset_change_ms;
+
+ // Reduce the capacity and verify the decrease time.
+ stream_generator_->set_capacity_bps(kReducedCapacityBps);
+ int64_t overuse_start_time = clock_.TimeInMilliseconds();
+ int64_t bitrate_drop_time = -1;
+ for (int i = 0; i < 100 * number_of_streams; ++i) {
+ GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
+ if (bitrate_drop_time == -1 &&
+ bitrate_observer_.latest_bitrate() <= kReducedCapacityBps) {
+ bitrate_drop_time = clock_.TimeInMilliseconds();
+ }
+ if (bitrate_observer_.updated())
+ bitrate_bps = bitrate_observer_.latest_bitrate();
+ }
+
+ EXPECT_NEAR(expected_bitrate_drop_delta,
+ bitrate_drop_time - overuse_start_time, 33);
+}
+
+void DelayBasedBweTest::TestTimestampGroupingTestHelper() {
+ const int kFramerate = 50; // 50 fps to avoid rounding errors.
+ const int kFrameIntervalMs = 1000 / kFramerate;
+ int64_t send_time_ms = 0;
+ // Initial set of frames to increase the bitrate. 6 seconds to have enough
+ // time for the first estimate to be generated and for Process() to be called.
+ for (int i = 0; i <= 6 * kFramerate; ++i) {
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
+
+ clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
+ send_time_ms += kFrameIntervalMs;
+ }
+ EXPECT_TRUE(bitrate_observer_.updated());
+ EXPECT_GE(bitrate_observer_.latest_bitrate(), 400000u);
+
+ // Insert batches of frames which were sent very close in time. Also simulate
+ // capacity over-use to see that we back off correctly.
+ const int kTimestampGroupLength = 15;
+ for (int i = 0; i < 100; ++i) {
+ for (int j = 0; j < kTimestampGroupLength; ++j) {
+ // Insert `kTimestampGroupLength` frames with just 1 timestamp ticks in
+ // between. Should be treated as part of the same group by the estimator.
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 100);
+ clock_.AdvanceTimeMilliseconds(kFrameIntervalMs / kTimestampGroupLength);
+ send_time_ms += 1;
+ }
+ // Increase time until next batch to simulate over-use.
+ clock_.AdvanceTimeMilliseconds(10);
+ send_time_ms += kFrameIntervalMs - kTimestampGroupLength;
+ }
+ EXPECT_TRUE(bitrate_observer_.updated());
+ // Should have reduced the estimate.
+ EXPECT_LT(bitrate_observer_.latest_bitrate(), 400000u);
+}
+
+void DelayBasedBweTest::TestWrappingHelper(int silence_time_s) {
+ const int kFramerate = 100;
+ const int kFrameIntervalMs = 1000 / kFramerate;
+ int64_t send_time_ms = 0;
+
+ for (size_t i = 0; i < 3000; ++i) {
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
+ clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
+ send_time_ms += kFrameIntervalMs;
+ }
+ DataRate bitrate_before = DataRate::Zero();
+ std::vector<uint32_t> ssrcs;
+ bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before);
+
+ clock_.AdvanceTimeMilliseconds(silence_time_s * 1000);
+ send_time_ms += silence_time_s * 1000;
+
+ for (size_t i = 0; i < 24; ++i) {
+ IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
+ clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
+ send_time_ms += kFrameIntervalMs;
+ }
+ DataRate bitrate_after = DataRate::Zero();
+ bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after);
+ EXPECT_LT(bitrate_after, bitrate_before);
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h
new file mode 100644
index 0000000000..d56fe892d5
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "api/transport/field_trial_based_config.h"
+#include "api/transport/network_types.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+#include "system_wrappers/include/clock.h"
+#include "test/field_trial.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+class TestBitrateObserver {
+ public:
+ TestBitrateObserver() : updated_(false), latest_bitrate_(0) {}
+ ~TestBitrateObserver() {}
+
+ void OnReceiveBitrateChanged(uint32_t bitrate);
+
+ void Reset() { updated_ = false; }
+
+ bool updated() const { return updated_; }
+
+ uint32_t latest_bitrate() const { return latest_bitrate_; }
+
+ private:
+ bool updated_;
+ uint32_t latest_bitrate_;
+};
+
+class RtpStream {
+ public:
+ enum { kSendSideOffsetUs = 1000000 };
+
+ RtpStream(int fps, int bitrate_bps);
+
+ RtpStream(const RtpStream&) = delete;
+ RtpStream& operator=(const RtpStream&) = delete;
+
+ // Generates a new frame for this stream. If called too soon after the
+ // previous frame, no frame will be generated. The frame is split into
+ // packets.
+ int64_t GenerateFrame(int64_t time_now_us,
+ std::vector<PacketResult>* packets);
+
+ // The send-side time when the next frame can be generated.
+ int64_t next_rtp_time() const;
+
+ void set_bitrate_bps(int bitrate_bps);
+
+ int bitrate_bps() const;
+
+ static bool Compare(const std::unique_ptr<RtpStream>& lhs,
+ const std::unique_ptr<RtpStream>& rhs);
+
+ private:
+ int fps_;
+ int bitrate_bps_;
+ int64_t next_rtp_time_;
+};
+
+class StreamGenerator {
+ public:
+ StreamGenerator(int capacity, int64_t time_now);
+ ~StreamGenerator();
+
+ StreamGenerator(const StreamGenerator&) = delete;
+ StreamGenerator& operator=(const StreamGenerator&) = delete;
+
+ // Add a new stream.
+ void AddStream(RtpStream* stream);
+
+ // Set the link capacity.
+ void set_capacity_bps(int capacity_bps);
+
+ // Divides `bitrate_bps` among all streams. The allocated bitrate per stream
+ // is decided by the initial allocation ratios.
+ void SetBitrateBps(int bitrate_bps);
+
+ // Set the RTP timestamp offset for the stream identified by `ssrc`.
+ void set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset);
+
+ // TODO(holmer): Break out the channel simulation part from this class to make
+ // it possible to simulate different types of channels.
+ int64_t GenerateFrame(std::vector<PacketResult>* packets,
+ int64_t time_now_us);
+
+ private:
+ // Capacity of the simulated channel in bits per second.
+ int capacity_;
+ // The time when the last packet arrived.
+ int64_t prev_arrival_time_us_;
+ // All streams being transmitted on this simulated channel.
+ std::vector<std::unique_ptr<RtpStream>> streams_;
+};
+} // namespace test
+
+class DelayBasedBweTest : public ::testing::Test {
+ public:
+ DelayBasedBweTest();
+ explicit DelayBasedBweTest(absl::string_view field_trial_string);
+ ~DelayBasedBweTest() override;
+
+ protected:
+ void AddDefaultStream();
+
+ // Helpers to insert a single packet into the delay-based BWE.
+ void IncomingFeedback(int64_t arrival_time_ms,
+ int64_t send_time_ms,
+ size_t payload_size);
+ void IncomingFeedback(int64_t arrival_time_ms,
+ int64_t send_time_ms,
+ size_t payload_size,
+ const PacedPacketInfo& pacing_info);
+ void IncomingFeedback(Timestamp receive_time,
+ Timestamp send_time,
+ size_t payload_size,
+ const PacedPacketInfo& pacing_info);
+
+ // Generates a frame of packets belonging to a stream at a given bitrate and
+ // with a given ssrc. The stream is pushed through a very simple simulated
+ // network, and is then given to the receive-side bandwidth estimator.
+ // Returns true if an over-use was seen, false otherwise.
+ // The StreamGenerator::updated() should be used to check for any changes in
+ // target bitrate after the call to this function.
+ bool GenerateAndProcessFrame(uint32_t ssrc, uint32_t bitrate_bps);
+
+ // Run the bandwidth estimator with a stream of `number_of_frames` frames, or
+ // until it reaches `target_bitrate`.
+ // Can for instance be used to run the estimator for some time to get it
+ // into a steady state.
+ uint32_t SteadyStateRun(uint32_t ssrc,
+ int number_of_frames,
+ uint32_t start_bitrate,
+ uint32_t min_bitrate,
+ uint32_t max_bitrate,
+ uint32_t target_bitrate);
+
+ void TestTimestampGroupingTestHelper();
+
+ void TestWrappingHelper(int silence_time_s);
+
+ void InitialBehaviorTestHelper(uint32_t expected_converge_bitrate);
+ void RateIncreaseReorderingTestHelper(uint32_t expected_bitrate);
+ void RateIncreaseRtpTimestampsTestHelper(int expected_iterations);
+ void CapacityDropTestHelper(int number_of_streams,
+ bool wrap_time_stamp,
+ uint32_t expected_bitrate_drop_delta,
+ int64_t receiver_clock_offset_change_ms);
+
+ static const uint32_t kDefaultSsrc;
+ FieldTrialBasedConfig field_trial_config_;
+
+ std::unique_ptr<test::ScopedFieldTrials>
+ field_trial; // Must be initialized first.
+ SimulatedClock clock_; // Time at the receiver.
+ test::TestBitrateObserver bitrate_observer_;
+ std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
+ acknowledged_bitrate_estimator_;
+ const std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
+ std::unique_ptr<DelayBasedBwe> bitrate_estimator_;
+ std::unique_ptr<test::StreamGenerator> stream_generator_;
+ int64_t arrival_time_offset_ms_;
+ bool first_update_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h
new file mode 100644
index 0000000000..fc12cff7d5
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/delay_increase_detector_interface.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
+
+#include <stdint.h>
+
+#include "api/network_state_predictor.h"
+
+namespace webrtc {
+
+class DelayIncreaseDetectorInterface {
+ public:
+ DelayIncreaseDetectorInterface() {}
+ virtual ~DelayIncreaseDetectorInterface() {}
+
+ DelayIncreaseDetectorInterface(const DelayIncreaseDetectorInterface&) =
+ delete;
+ DelayIncreaseDetectorInterface& operator=(
+ const DelayIncreaseDetectorInterface&) = delete;
+
+ // Update the detector with a new sample. The deltas should represent deltas
+ // between timestamp groups as defined by the InterArrival class.
+ virtual void Update(double recv_delta_ms,
+ double send_delta_ms,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ size_t packet_size,
+ bool calculated_deltas) = 0;
+
+ virtual BandwidthUsage State() const = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build
new file mode 100644
index 0000000000..b15062af41
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/estimators_gn/moz.build
@@ -0,0 +1,238 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/bitrate_estimator.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc",
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("estimators_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build
new file mode 100644
index 0000000000..973d4bf32f
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_gn/moz.build
@@ -0,0 +1,233 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("goog_cc_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.cc
new file mode 100644
index 0000000000..3a9de8c4dc
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.cc
@@ -0,0 +1,725 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/goog_cc_network_control.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "api/network_state_predictor.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h"
+#include "modules/congestion_controller/goog_cc/alr_detector.h"
+#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
+#include "modules/congestion_controller/goog_cc/probe_controller.h"
+#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
+#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+// From RTCPSender video report interval.
+constexpr TimeDelta kLossUpdateInterval = TimeDelta::Millis(1000);
+
+// Pacing-rate relative to our target send rate.
+// Multiplicative factor that is applied to the target bitrate to calculate
+// the number of bytes that can be transmitted per interval.
+// Increasing this factor will result in lower delays in cases of bitrate
+// overshoots from the encoder.
+constexpr float kDefaultPaceMultiplier = 2.5f;
+
+// If the probe result is far below the current throughput estimate
+// it's unlikely that the probe is accurate, so we don't want to drop too far.
+// However, if we actually are overusing, we want to drop to something slightly
+// below the current throughput estimate to drain the network queues.
+constexpr double kProbeDropThroughputFraction = 0.85;
+
+bool IsEnabled(const FieldTrialsView* config, absl::string_view key) {
+ return absl::StartsWith(config->Lookup(key), "Enabled");
+}
+
+bool IsNotDisabled(const FieldTrialsView* config, absl::string_view key) {
+ return !absl::StartsWith(config->Lookup(key), "Disabled");
+}
+
+BandwidthLimitedCause GetBandwidthLimitedCause(
+ LossBasedState loss_based_state,
+ BandwidthUsage bandwidth_usage,
+ bool not_probe_if_delay_increased) {
+ if (not_probe_if_delay_increased &&
+ (bandwidth_usage == BandwidthUsage::kBwOverusing ||
+ bandwidth_usage == BandwidthUsage::kBwUnderusing)) {
+ return BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased;
+ }
+ switch (loss_based_state) {
+ case LossBasedState::kDecreasing:
+ return BandwidthLimitedCause::kLossLimitedBweDecreasing;
+ case LossBasedState::kIncreasing:
+ return BandwidthLimitedCause::kLossLimitedBweIncreasing;
+ default:
+ return BandwidthLimitedCause::kDelayBasedLimited;
+ }
+}
+
+} // namespace
+
+GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config,
+ GoogCcConfig goog_cc_config)
+ : key_value_config_(config.key_value_config ? config.key_value_config
+ : &trial_based_config_),
+ event_log_(config.event_log),
+ packet_feedback_only_(goog_cc_config.feedback_only),
+ safe_reset_on_route_change_("Enabled"),
+ safe_reset_acknowledged_rate_("ack"),
+ use_min_allocatable_as_lower_bound_(
+ IsNotDisabled(key_value_config_, "WebRTC-Bwe-MinAllocAsLowerBound")),
+ ignore_probes_lower_than_network_estimate_(IsNotDisabled(
+ key_value_config_,
+ "WebRTC-Bwe-IgnoreProbesLowerThanNetworkStateEstimate")),
+ limit_probes_lower_than_throughput_estimate_(
+ IsEnabled(key_value_config_,
+ "WebRTC-Bwe-LimitProbesLowerThanThroughputEstimate")),
+ rate_control_settings_(
+ RateControlSettings::ParseFromKeyValueConfig(key_value_config_)),
+ pace_at_max_of_bwe_and_lower_link_capacity_(
+ IsEnabled(key_value_config_,
+ "WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity")),
+ probe_controller_(
+ new ProbeController(key_value_config_, config.event_log)),
+ congestion_window_pushback_controller_(
+ rate_control_settings_.UseCongestionWindowPushback()
+ ? std::make_unique<CongestionWindowPushbackController>(
+ key_value_config_)
+ : nullptr),
+ bandwidth_estimation_(
+ std::make_unique<SendSideBandwidthEstimation>(key_value_config_,
+ event_log_)),
+ alr_detector_(
+ std::make_unique<AlrDetector>(key_value_config_, config.event_log)),
+ probe_bitrate_estimator_(new ProbeBitrateEstimator(config.event_log)),
+ network_estimator_(std::move(goog_cc_config.network_state_estimator)),
+ network_state_predictor_(
+ std::move(goog_cc_config.network_state_predictor)),
+ delay_based_bwe_(new DelayBasedBwe(key_value_config_,
+ event_log_,
+ network_state_predictor_.get())),
+ acknowledged_bitrate_estimator_(
+ AcknowledgedBitrateEstimatorInterface::Create(key_value_config_)),
+ initial_config_(config),
+ last_loss_based_target_rate_(*config.constraints.starting_rate),
+ last_pushback_target_rate_(last_loss_based_target_rate_),
+ last_stable_target_rate_(last_loss_based_target_rate_),
+ pacing_factor_(config.stream_based_config.pacing_factor.value_or(
+ kDefaultPaceMultiplier)),
+ min_total_allocated_bitrate_(
+ config.stream_based_config.min_total_allocated_bitrate.value_or(
+ DataRate::Zero())),
+ max_padding_rate_(config.stream_based_config.max_padding_rate.value_or(
+ DataRate::Zero())) {
+ RTC_DCHECK(config.constraints.at_time.IsFinite());
+ ParseFieldTrial(
+ {&safe_reset_on_route_change_, &safe_reset_acknowledged_rate_},
+ key_value_config_->Lookup("WebRTC-Bwe-SafeResetOnRouteChange"));
+ if (delay_based_bwe_)
+ delay_based_bwe_->SetMinBitrate(kCongestionControllerMinBitrate);
+}
+
+GoogCcNetworkController::~GoogCcNetworkController() {}
+
+NetworkControlUpdate GoogCcNetworkController::OnNetworkAvailability(
+ NetworkAvailability msg) {
+ NetworkControlUpdate update;
+ update.probe_cluster_configs = probe_controller_->OnNetworkAvailability(msg);
+ return update;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange(
+ NetworkRouteChange msg) {
+ if (safe_reset_on_route_change_) {
+ absl::optional<DataRate> estimated_bitrate;
+ if (safe_reset_acknowledged_rate_) {
+ estimated_bitrate = acknowledged_bitrate_estimator_->bitrate();
+ if (!estimated_bitrate)
+ estimated_bitrate = acknowledged_bitrate_estimator_->PeekRate();
+ } else {
+ estimated_bitrate = bandwidth_estimation_->target_rate();
+ }
+ if (estimated_bitrate) {
+ if (msg.constraints.starting_rate) {
+ msg.constraints.starting_rate =
+ std::min(*msg.constraints.starting_rate, *estimated_bitrate);
+ } else {
+ msg.constraints.starting_rate = estimated_bitrate;
+ }
+ }
+ }
+
+ acknowledged_bitrate_estimator_ =
+ AcknowledgedBitrateEstimatorInterface::Create(key_value_config_);
+ probe_bitrate_estimator_.reset(new ProbeBitrateEstimator(event_log_));
+ if (network_estimator_)
+ network_estimator_->OnRouteChange(msg);
+ delay_based_bwe_.reset(new DelayBasedBwe(key_value_config_, event_log_,
+ network_state_predictor_.get()));
+ bandwidth_estimation_->OnRouteChange();
+ probe_controller_->Reset(msg.at_time);
+ NetworkControlUpdate update;
+ update.probe_cluster_configs = ResetConstraints(msg.constraints);
+ MaybeTriggerOnNetworkChanged(&update, msg.at_time);
+ return update;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnProcessInterval(
+ ProcessInterval msg) {
+ NetworkControlUpdate update;
+ if (initial_config_) {
+ update.probe_cluster_configs =
+ ResetConstraints(initial_config_->constraints);
+ update.pacer_config = GetPacingRates(msg.at_time);
+
+ if (initial_config_->stream_based_config.requests_alr_probing) {
+ probe_controller_->EnablePeriodicAlrProbing(
+ *initial_config_->stream_based_config.requests_alr_probing);
+ }
+ absl::optional<DataRate> total_bitrate =
+ initial_config_->stream_based_config.max_total_allocated_bitrate;
+ if (total_bitrate) {
+ auto probes = probe_controller_->OnMaxTotalAllocatedBitrate(
+ *total_bitrate, msg.at_time);
+ update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
+ probes.begin(), probes.end());
+ }
+ initial_config_.reset();
+ }
+ if (congestion_window_pushback_controller_ && msg.pacer_queue) {
+ congestion_window_pushback_controller_->UpdatePacingQueue(
+ msg.pacer_queue->bytes());
+ }
+ bandwidth_estimation_->UpdateEstimate(msg.at_time);
+ absl::optional<int64_t> start_time_ms =
+ alr_detector_->GetApplicationLimitedRegionStartTime();
+ probe_controller_->SetAlrStartTimeMs(start_time_ms);
+
+ auto probes = probe_controller_->Process(msg.at_time);
+ update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
+ probes.begin(), probes.end());
+
+ if (rate_control_settings_.UseCongestionWindow() &&
+ last_packet_received_time_.IsFinite() && !feedback_max_rtts_.empty()) {
+ UpdateCongestionWindowSize();
+ }
+ if (congestion_window_pushback_controller_ && current_data_window_) {
+ congestion_window_pushback_controller_->SetDataWindow(
+ *current_data_window_);
+ } else {
+ update.congestion_window = current_data_window_;
+ }
+ MaybeTriggerOnNetworkChanged(&update, msg.at_time);
+ return update;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnRemoteBitrateReport(
+ RemoteBitrateReport msg) {
+ if (packet_feedback_only_) {
+ RTC_LOG(LS_ERROR) << "Received REMB for packet feedback only GoogCC";
+ return NetworkControlUpdate();
+ }
+ bandwidth_estimation_->UpdateReceiverEstimate(msg.receive_time,
+ msg.bandwidth);
+ BWE_TEST_LOGGING_PLOT(1, "REMB_kbps", msg.receive_time.ms(),
+ msg.bandwidth.bps() / 1000);
+ return NetworkControlUpdate();
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnRoundTripTimeUpdate(
+ RoundTripTimeUpdate msg) {
+ if (packet_feedback_only_ || msg.smoothed)
+ return NetworkControlUpdate();
+ RTC_DCHECK(!msg.round_trip_time.IsZero());
+ if (delay_based_bwe_)
+ delay_based_bwe_->OnRttUpdate(msg.round_trip_time);
+ bandwidth_estimation_->UpdateRtt(msg.round_trip_time, msg.receive_time);
+ return NetworkControlUpdate();
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnSentPacket(
+ SentPacket sent_packet) {
+ alr_detector_->OnBytesSent(sent_packet.size.bytes(),
+ sent_packet.send_time.ms());
+ acknowledged_bitrate_estimator_->SetAlr(
+ alr_detector_->GetApplicationLimitedRegionStartTime().has_value());
+
+ if (!first_packet_sent_) {
+ first_packet_sent_ = true;
+ // Initialize feedback time to send time to allow estimation of RTT until
+ // first feedback is received.
+ bandwidth_estimation_->UpdatePropagationRtt(sent_packet.send_time,
+ TimeDelta::Zero());
+ }
+ bandwidth_estimation_->OnSentPacket(sent_packet);
+
+ if (congestion_window_pushback_controller_) {
+ congestion_window_pushback_controller_->UpdateOutstandingData(
+ sent_packet.data_in_flight.bytes());
+ NetworkControlUpdate update;
+ MaybeTriggerOnNetworkChanged(&update, sent_packet.send_time);
+ return update;
+ } else {
+ return NetworkControlUpdate();
+ }
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnReceivedPacket(
+ ReceivedPacket received_packet) {
+ last_packet_received_time_ = received_packet.receive_time;
+ return NetworkControlUpdate();
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnStreamsConfig(
+ StreamsConfig msg) {
+ NetworkControlUpdate update;
+ if (msg.requests_alr_probing) {
+ probe_controller_->EnablePeriodicAlrProbing(*msg.requests_alr_probing);
+ }
+ if (msg.max_total_allocated_bitrate) {
+ update.probe_cluster_configs =
+ probe_controller_->OnMaxTotalAllocatedBitrate(
+ *msg.max_total_allocated_bitrate, msg.at_time);
+ }
+
+ bool pacing_changed = false;
+ if (msg.pacing_factor && *msg.pacing_factor != pacing_factor_) {
+ pacing_factor_ = *msg.pacing_factor;
+ pacing_changed = true;
+ }
+ if (msg.min_total_allocated_bitrate &&
+ *msg.min_total_allocated_bitrate != min_total_allocated_bitrate_) {
+ min_total_allocated_bitrate_ = *msg.min_total_allocated_bitrate;
+ pacing_changed = true;
+
+ if (use_min_allocatable_as_lower_bound_) {
+ ClampConstraints();
+ delay_based_bwe_->SetMinBitrate(min_data_rate_);
+ bandwidth_estimation_->SetMinMaxBitrate(min_data_rate_, max_data_rate_);
+ }
+ }
+ if (msg.max_padding_rate && *msg.max_padding_rate != max_padding_rate_) {
+ max_padding_rate_ = *msg.max_padding_rate;
+ pacing_changed = true;
+ }
+
+ if (pacing_changed)
+ update.pacer_config = GetPacingRates(msg.at_time);
+ return update;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnTargetRateConstraints(
+ TargetRateConstraints constraints) {
+ NetworkControlUpdate update;
+ update.probe_cluster_configs = ResetConstraints(constraints);
+ MaybeTriggerOnNetworkChanged(&update, constraints.at_time);
+ return update;
+}
+
+void GoogCcNetworkController::ClampConstraints() {
+ // TODO(holmer): We should make sure the default bitrates are set to 10 kbps,
+ // and that we don't try to set the min bitrate to 0 from any applications.
+ // The congestion controller should allow a min bitrate of 0.
+ min_data_rate_ = std::max(min_target_rate_, kCongestionControllerMinBitrate);
+ if (use_min_allocatable_as_lower_bound_) {
+ min_data_rate_ = std::max(min_data_rate_, min_total_allocated_bitrate_);
+ }
+ if (max_data_rate_ < min_data_rate_) {
+ RTC_LOG(LS_WARNING) << "max bitrate smaller than min bitrate";
+ max_data_rate_ = min_data_rate_;
+ }
+ if (starting_rate_ && starting_rate_ < min_data_rate_) {
+ RTC_LOG(LS_WARNING) << "start bitrate smaller than min bitrate";
+ starting_rate_ = min_data_rate_;
+ }
+}
+
+std::vector<ProbeClusterConfig> GoogCcNetworkController::ResetConstraints(
+ TargetRateConstraints new_constraints) {
+ min_target_rate_ = new_constraints.min_data_rate.value_or(DataRate::Zero());
+ max_data_rate_ =
+ new_constraints.max_data_rate.value_or(DataRate::PlusInfinity());
+ starting_rate_ = new_constraints.starting_rate;
+ ClampConstraints();
+
+ bandwidth_estimation_->SetBitrates(starting_rate_, min_data_rate_,
+ max_data_rate_, new_constraints.at_time);
+
+ if (starting_rate_)
+ delay_based_bwe_->SetStartBitrate(*starting_rate_);
+ delay_based_bwe_->SetMinBitrate(min_data_rate_);
+
+ return probe_controller_->SetBitrates(
+ min_data_rate_, starting_rate_.value_or(DataRate::Zero()), max_data_rate_,
+ new_constraints.at_time);
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnTransportLossReport(
+ TransportLossReport msg) {
+ if (packet_feedback_only_)
+ return NetworkControlUpdate();
+ int64_t total_packets_delta =
+ msg.packets_received_delta + msg.packets_lost_delta;
+ bandwidth_estimation_->UpdatePacketsLost(
+ msg.packets_lost_delta, total_packets_delta, msg.receive_time);
+ return NetworkControlUpdate();
+}
+
+void GoogCcNetworkController::UpdateCongestionWindowSize() {
+ TimeDelta min_feedback_max_rtt = TimeDelta::Millis(
+ *std::min_element(feedback_max_rtts_.begin(), feedback_max_rtts_.end()));
+
+ const DataSize kMinCwnd = DataSize::Bytes(2 * 1500);
+ TimeDelta time_window =
+ min_feedback_max_rtt +
+ TimeDelta::Millis(
+ rate_control_settings_.GetCongestionWindowAdditionalTimeMs());
+
+ DataSize data_window = last_loss_based_target_rate_ * time_window;
+ if (current_data_window_) {
+ data_window =
+ std::max(kMinCwnd, (data_window + current_data_window_.value()) / 2);
+ } else {
+ data_window = std::max(kMinCwnd, data_window);
+ }
+ current_data_window_ = data_window;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(
+ TransportPacketsFeedback report) {
+ if (report.packet_feedbacks.empty()) {
+ // TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
+ // against building very large network queues.
+ return NetworkControlUpdate();
+ }
+
+ if (congestion_window_pushback_controller_) {
+ congestion_window_pushback_controller_->UpdateOutstandingData(
+ report.data_in_flight.bytes());
+ }
+ TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
+ TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
+ Timestamp max_recv_time = Timestamp::MinusInfinity();
+
+ std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
+ for (const auto& feedback : feedbacks)
+ max_recv_time = std::max(max_recv_time, feedback.receive_time);
+
+ for (const auto& feedback : feedbacks) {
+ TimeDelta feedback_rtt =
+ report.feedback_time - feedback.sent_packet.send_time;
+ TimeDelta min_pending_time = max_recv_time - feedback.receive_time;
+ TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
+ max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
+ min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
+ }
+
+ if (max_feedback_rtt.IsFinite()) {
+ feedback_max_rtts_.push_back(max_feedback_rtt.ms());
+ const size_t kMaxFeedbackRttWindow = 32;
+ if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
+ feedback_max_rtts_.pop_front();
+ // TODO(srte): Use time since last unacknowledged packet.
+ bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time,
+ min_propagation_rtt);
+ }
+ if (packet_feedback_only_) {
+ if (!feedback_max_rtts_.empty()) {
+ int64_t sum_rtt_ms =
+ std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
+ static_cast<int64_t>(0));
+ int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
+ if (delay_based_bwe_)
+ delay_based_bwe_->OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
+ }
+
+ TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
+ for (const auto& packet_feedback : feedbacks) {
+ TimeDelta pending_time = packet_feedback.receive_time - max_recv_time;
+ TimeDelta rtt = report.feedback_time -
+ packet_feedback.sent_packet.send_time - pending_time;
+ // Value used for predicting NACK round trip time in FEC controller.
+ feedback_min_rtt = std::min(rtt, feedback_min_rtt);
+ }
+ if (feedback_min_rtt.IsFinite()) {
+ bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
+ }
+
+ expected_packets_since_last_loss_update_ +=
+ report.PacketsWithFeedback().size();
+ for (const auto& packet_feedback : report.PacketsWithFeedback()) {
+ if (!packet_feedback.IsReceived())
+ lost_packets_since_last_loss_update_ += 1;
+ }
+ if (report.feedback_time > next_loss_update_) {
+ next_loss_update_ = report.feedback_time + kLossUpdateInterval;
+ bandwidth_estimation_->UpdatePacketsLost(
+ lost_packets_since_last_loss_update_,
+ expected_packets_since_last_loss_update_, report.feedback_time);
+ expected_packets_since_last_loss_update_ = 0;
+ lost_packets_since_last_loss_update_ = 0;
+ }
+ }
+ absl::optional<int64_t> alr_start_time =
+ alr_detector_->GetApplicationLimitedRegionStartTime();
+
+ if (previously_in_alr_ && !alr_start_time.has_value()) {
+ int64_t now_ms = report.feedback_time.ms();
+ acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
+ probe_controller_->SetAlrEndedTimeMs(now_ms);
+ }
+ previously_in_alr_ = alr_start_time.has_value();
+ acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
+ report.SortedByReceiveTime());
+ auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate();
+ bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate,
+ report.feedback_time);
+ for (const auto& feedback : report.SortedByReceiveTime()) {
+ if (feedback.sent_packet.pacing_info.probe_cluster_id !=
+ PacedPacketInfo::kNotAProbe) {
+ probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback);
+ }
+ }
+
+ if (network_estimator_) {
+ network_estimator_->OnTransportPacketsFeedback(report);
+ auto prev_estimate = estimate_;
+ estimate_ = network_estimator_->GetCurrentEstimate();
+ // TODO(srte): Make OnTransportPacketsFeedback signal whether the state
+ // changed to avoid the need for this check.
+ if (estimate_ && (!prev_estimate || estimate_->last_feed_time !=
+ prev_estimate->last_feed_time)) {
+ event_log_->Log(std::make_unique<RtcEventRemoteEstimate>(
+ estimate_->link_capacity_lower, estimate_->link_capacity_upper));
+ probe_controller_->SetNetworkStateEstimate(*estimate_);
+ }
+ }
+ absl::optional<DataRate> probe_bitrate =
+ probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
+ if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
+ estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
+ *probe_bitrate < estimate_->link_capacity_lower) {
+ probe_bitrate.reset();
+ }
+ if (limit_probes_lower_than_throughput_estimate_ && probe_bitrate &&
+ acknowledged_bitrate) {
+ // Limit the backoff to something slightly below the acknowledged
+ // bitrate. ("Slightly below" because we want to drain the queues
+ // if we are actually overusing.)
+ // The acknowledged bitrate shouldn't normally be higher than the delay
+ // based estimate, but it could happen e.g. due to packet bursts or
+ // encoder overshoot. We use std::min to ensure that a probe result
+ // below the current BWE never causes an increase.
+ DataRate limit =
+ std::min(delay_based_bwe_->last_estimate(),
+ *acknowledged_bitrate * kProbeDropThroughputFraction);
+ probe_bitrate = std::max(*probe_bitrate, limit);
+ }
+
+ NetworkControlUpdate update;
+ bool recovered_from_overuse = false;
+
+ DelayBasedBwe::Result result;
+ result = delay_based_bwe_->IncomingPacketFeedbackVector(
+ report, acknowledged_bitrate, probe_bitrate, estimate_,
+ alr_start_time.has_value());
+
+ if (result.updated) {
+ if (result.probe) {
+ bandwidth_estimation_->SetSendBitrate(result.target_bitrate,
+ report.feedback_time);
+ }
+ // Since SetSendBitrate now resets the delay-based estimate, we have to
+ // call UpdateDelayBasedEstimate after SetSendBitrate.
+ bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time,
+ result.target_bitrate);
+ }
+ bandwidth_estimation_->UpdateLossBasedEstimator(
+ report, result.delay_detector_state, probe_bitrate,
+ estimate_ ? estimate_->link_capacity_upper : DataRate::PlusInfinity());
+ if (result.updated) {
+ // Update the estimate in the ProbeController, in case we want to probe.
+ MaybeTriggerOnNetworkChanged(&update, report.feedback_time);
+ }
+
+ recovered_from_overuse = result.recovered_from_overuse;
+
+ if (recovered_from_overuse) {
+ probe_controller_->SetAlrStartTimeMs(alr_start_time);
+ auto probes = probe_controller_->RequestProbe(report.feedback_time);
+ update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
+ probes.begin(), probes.end());
+ }
+
+ // No valid RTT could be because send-side BWE isn't used, in which case
+ // we don't try to limit the outstanding packets.
+ if (rate_control_settings_.UseCongestionWindow() &&
+ max_feedback_rtt.IsFinite()) {
+ UpdateCongestionWindowSize();
+ }
+ if (congestion_window_pushback_controller_ && current_data_window_) {
+ congestion_window_pushback_controller_->SetDataWindow(
+ *current_data_window_);
+ } else {
+ update.congestion_window = current_data_window_;
+ }
+
+ return update;
+}
+
+NetworkControlUpdate GoogCcNetworkController::OnNetworkStateEstimate(
+ NetworkStateEstimate msg) {
+ estimate_ = msg;
+ return NetworkControlUpdate();
+}
+
+NetworkControlUpdate GoogCcNetworkController::GetNetworkState(
+ Timestamp at_time) const {
+ NetworkControlUpdate update;
+ update.target_rate = TargetTransferRate();
+ update.target_rate->network_estimate.at_time = at_time;
+ update.target_rate->network_estimate.loss_rate_ratio =
+ last_estimated_fraction_loss_.value_or(0) / 255.0;
+ update.target_rate->network_estimate.round_trip_time =
+ last_estimated_round_trip_time_;
+ update.target_rate->network_estimate.bwe_period =
+ delay_based_bwe_->GetExpectedBwePeriod();
+
+ update.target_rate->at_time = at_time;
+ update.target_rate->target_rate = last_pushback_target_rate_;
+ update.target_rate->stable_target_rate =
+ bandwidth_estimation_->GetEstimatedLinkCapacity();
+ update.pacer_config = GetPacingRates(at_time);
+ update.congestion_window = current_data_window_;
+ return update;
+}
+
+void GoogCcNetworkController::MaybeTriggerOnNetworkChanged(
+ NetworkControlUpdate* update,
+ Timestamp at_time) {
+ uint8_t fraction_loss = bandwidth_estimation_->fraction_loss();
+ TimeDelta round_trip_time = bandwidth_estimation_->round_trip_time();
+ DataRate loss_based_target_rate = bandwidth_estimation_->target_rate();
+ DataRate pushback_target_rate = loss_based_target_rate;
+
+ BWE_TEST_LOGGING_PLOT(1, "fraction_loss_%", at_time.ms(),
+ (fraction_loss * 100) / 256);
+ BWE_TEST_LOGGING_PLOT(1, "rtt_ms", at_time.ms(), round_trip_time.ms());
+ BWE_TEST_LOGGING_PLOT(1, "Target_bitrate_kbps", at_time.ms(),
+ loss_based_target_rate.kbps());
+
+ double cwnd_reduce_ratio = 0.0;
+ if (congestion_window_pushback_controller_) {
+ int64_t pushback_rate =
+ congestion_window_pushback_controller_->UpdateTargetBitrate(
+ loss_based_target_rate.bps());
+ pushback_rate = std::max<int64_t>(bandwidth_estimation_->GetMinBitrate(),
+ pushback_rate);
+ pushback_target_rate = DataRate::BitsPerSec(pushback_rate);
+ if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {
+ cwnd_reduce_ratio = static_cast<double>(loss_based_target_rate.bps() -
+ pushback_target_rate.bps()) /
+ loss_based_target_rate.bps();
+ }
+ }
+ DataRate stable_target_rate =
+ bandwidth_estimation_->GetEstimatedLinkCapacity();
+ stable_target_rate = std::min(stable_target_rate, pushback_target_rate);
+
+ if ((loss_based_target_rate != last_loss_based_target_rate_) ||
+ (fraction_loss != last_estimated_fraction_loss_) ||
+ (round_trip_time != last_estimated_round_trip_time_) ||
+ (pushback_target_rate != last_pushback_target_rate_) ||
+ (stable_target_rate != last_stable_target_rate_)) {
+ last_loss_based_target_rate_ = loss_based_target_rate;
+ last_pushback_target_rate_ = pushback_target_rate;
+ last_estimated_fraction_loss_ = fraction_loss;
+ last_estimated_round_trip_time_ = round_trip_time;
+ last_stable_target_rate_ = stable_target_rate;
+
+ alr_detector_->SetEstimatedBitrate(loss_based_target_rate.bps());
+
+ TimeDelta bwe_period = delay_based_bwe_->GetExpectedBwePeriod();
+
+ TargetTransferRate target_rate_msg;
+ target_rate_msg.at_time = at_time;
+ if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {
+ target_rate_msg.target_rate = loss_based_target_rate;
+ target_rate_msg.cwnd_reduce_ratio = cwnd_reduce_ratio;
+ } else {
+ target_rate_msg.target_rate = pushback_target_rate;
+ }
+ target_rate_msg.stable_target_rate = stable_target_rate;
+ target_rate_msg.network_estimate.at_time = at_time;
+ target_rate_msg.network_estimate.round_trip_time = round_trip_time;
+ target_rate_msg.network_estimate.loss_rate_ratio = fraction_loss / 255.0f;
+ target_rate_msg.network_estimate.bwe_period = bwe_period;
+
+ update->target_rate = target_rate_msg;
+
+ auto probes = probe_controller_->SetEstimatedBitrate(
+ loss_based_target_rate,
+ GetBandwidthLimitedCause(
+ bandwidth_estimation_->loss_based_state(),
+ delay_based_bwe_->last_state(),
+ probe_controller_->DontProbeIfDelayIncreased()),
+ at_time);
+ update->probe_cluster_configs.insert(update->probe_cluster_configs.end(),
+ probes.begin(), probes.end());
+ update->pacer_config = GetPacingRates(at_time);
+ RTC_LOG(LS_VERBOSE) << "bwe " << at_time.ms() << " pushback_target_bps="
+ << last_pushback_target_rate_.bps()
+ << " estimate_bps=" << loss_based_target_rate.bps();
+ }
+}
+
+PacerConfig GoogCcNetworkController::GetPacingRates(Timestamp at_time) const {
+ // Pacing rate is based on target rate before congestion window pushback,
+ // because we don't want to build queues in the pacer when pushback occurs.
+ DataRate pacing_rate = DataRate::Zero();
+ if (pace_at_max_of_bwe_and_lower_link_capacity_ && estimate_) {
+ pacing_rate =
+ std::max({min_total_allocated_bitrate_, estimate_->link_capacity_lower,
+ last_loss_based_target_rate_}) *
+ pacing_factor_;
+ } else {
+ pacing_rate =
+ std::max(min_total_allocated_bitrate_, last_loss_based_target_rate_) *
+ pacing_factor_;
+ }
+ DataRate padding_rate =
+ std::min(max_padding_rate_, last_pushback_target_rate_);
+ PacerConfig msg;
+ msg.at_time = at_time;
+ msg.time_window = TimeDelta::Seconds(1);
+ msg.data_window = pacing_rate * msg.time_window;
+ msg.pad_window = padding_rate * msg.time_window;
+ return msg;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.h
new file mode 100644
index 0000000000..37a064e37c
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
+
+#include <stdint.h>
+
+#include <deque>
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/transport/field_trial_based_config.h"
+#include "api/transport/network_control.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/timestamp.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
+#include "modules/congestion_controller/goog_cc/alr_detector.h"
+#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+#include "modules/congestion_controller/goog_cc/probe_controller.h"
+#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+#include "rtc_base/experiments/rate_control_settings.h"
+
+namespace webrtc {
+struct GoogCcConfig {
+ std::unique_ptr<NetworkStateEstimator> network_state_estimator = nullptr;
+ std::unique_ptr<NetworkStatePredictor> network_state_predictor = nullptr;
+ bool feedback_only = false;
+};
+
+class GoogCcNetworkController : public NetworkControllerInterface {
+ public:
+ GoogCcNetworkController(NetworkControllerConfig config,
+ GoogCcConfig goog_cc_config);
+
+ GoogCcNetworkController() = delete;
+ GoogCcNetworkController(const GoogCcNetworkController&) = delete;
+ GoogCcNetworkController& operator=(const GoogCcNetworkController&) = delete;
+
+ ~GoogCcNetworkController() override;
+
+ // NetworkControllerInterface
+ NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override;
+ NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override;
+ NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
+ NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override;
+ NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
+ NetworkControlUpdate OnSentPacket(SentPacket msg) override;
+ NetworkControlUpdate OnReceivedPacket(ReceivedPacket msg) override;
+ NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override;
+ NetworkControlUpdate OnTargetRateConstraints(
+ TargetRateConstraints msg) override;
+ NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override;
+ NetworkControlUpdate OnTransportPacketsFeedback(
+ TransportPacketsFeedback msg) override;
+ NetworkControlUpdate OnNetworkStateEstimate(
+ NetworkStateEstimate msg) override;
+
+ NetworkControlUpdate GetNetworkState(Timestamp at_time) const;
+
+ private:
+ friend class GoogCcStatePrinter;
+ std::vector<ProbeClusterConfig> ResetConstraints(
+ TargetRateConstraints new_constraints);
+ void ClampConstraints();
+ void MaybeTriggerOnNetworkChanged(NetworkControlUpdate* update,
+ Timestamp at_time);
+ void UpdateCongestionWindowSize();
+ PacerConfig GetPacingRates(Timestamp at_time) const;
+ const FieldTrialBasedConfig trial_based_config_;
+
+ const FieldTrialsView* const key_value_config_;
+ RtcEventLog* const event_log_;
+ const bool packet_feedback_only_;
+ FieldTrialFlag safe_reset_on_route_change_;
+ FieldTrialFlag safe_reset_acknowledged_rate_;
+ const bool use_min_allocatable_as_lower_bound_;
+ const bool ignore_probes_lower_than_network_estimate_;
+ const bool limit_probes_lower_than_throughput_estimate_;
+ const RateControlSettings rate_control_settings_;
+ const bool pace_at_max_of_bwe_and_lower_link_capacity_;
+
+ const std::unique_ptr<ProbeController> probe_controller_;
+ const std::unique_ptr<CongestionWindowPushbackController>
+ congestion_window_pushback_controller_;
+
+ std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
+ std::unique_ptr<AlrDetector> alr_detector_;
+ std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
+ std::unique_ptr<NetworkStateEstimator> network_estimator_;
+ std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
+ std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
+ std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
+ acknowledged_bitrate_estimator_;
+
+ absl::optional<NetworkControllerConfig> initial_config_;
+
+ DataRate min_target_rate_ = DataRate::Zero();
+ DataRate min_data_rate_ = DataRate::Zero();
+ DataRate max_data_rate_ = DataRate::PlusInfinity();
+ absl::optional<DataRate> starting_rate_;
+
+ bool first_packet_sent_ = false;
+
+ absl::optional<NetworkStateEstimate> estimate_;
+
+ Timestamp next_loss_update_ = Timestamp::MinusInfinity();
+ int lost_packets_since_last_loss_update_ = 0;
+ int expected_packets_since_last_loss_update_ = 0;
+
+ std::deque<int64_t> feedback_max_rtts_;
+
+ DataRate last_loss_based_target_rate_;
+ DataRate last_pushback_target_rate_;
+ DataRate last_stable_target_rate_;
+
+ absl::optional<uint8_t> last_estimated_fraction_loss_ = 0;
+ TimeDelta last_estimated_round_trip_time_ = TimeDelta::PlusInfinity();
+ Timestamp last_packet_received_time_ = Timestamp::MinusInfinity();
+
+ double pacing_factor_;
+ DataRate min_total_allocated_bitrate_;
+ DataRate max_padding_rate_;
+
+ bool previously_in_alr_ = false;
+
+ absl::optional<DataSize> current_data_window_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc
new file mode 100644
index 0000000000..7e051f505b
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <queue>
+
+#include "absl/strings/string_view.h"
+#include "api/test/network_emulation/create_cross_traffic.h"
+#include "api/test/network_emulation/cross_traffic.h"
+#include "api/transport/goog_cc_factory.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "test/field_trial.h"
+#include "test/gtest.h"
+#include "test/scenario/scenario.h"
+
+using ::testing::NiceMock;
+
+namespace webrtc {
+namespace test {
+namespace {
+// Count dips from a constant high bandwidth level within a short window.
+int CountBandwidthDips(std::queue<DataRate> bandwidth_history,
+ DataRate threshold) {
+ if (bandwidth_history.empty())
+ return true;
+ DataRate first = bandwidth_history.front();
+ bandwidth_history.pop();
+
+ int dips = 0;
+ bool state_high = true;
+ while (!bandwidth_history.empty()) {
+ if (bandwidth_history.front() + threshold < first && state_high) {
+ ++dips;
+ state_high = false;
+ } else if (bandwidth_history.front() == first) {
+ state_high = true;
+ } else if (bandwidth_history.front() > first) {
+ // If this is toggling we will catch it later when front becomes first.
+ state_high = false;
+ }
+ bandwidth_history.pop();
+ }
+ return dips;
+}
+GoogCcNetworkControllerFactory CreateFeedbackOnlyFactory() {
+ GoogCcFactoryConfig config;
+ config.feedback_only = true;
+ return GoogCcNetworkControllerFactory(std::move(config));
+}
+
+const uint32_t kInitialBitrateKbps = 60;
+const DataRate kInitialBitrate = DataRate::KilobitsPerSec(kInitialBitrateKbps);
+const float kDefaultPacingRate = 2.5f;
+
+CallClient* CreateVideoSendingClient(
+ Scenario* s,
+ CallClientConfig config,
+ std::vector<EmulatedNetworkNode*> send_link,
+ std::vector<EmulatedNetworkNode*> return_link) {
+ auto* client = s->CreateClient("send", std::move(config));
+ auto* route = s->CreateRoutes(client, send_link,
+ s->CreateClient("return", CallClientConfig()),
+ return_link);
+ s->CreateVideoStream(route->forward(), VideoStreamConfig());
+ return client;
+}
+
+NetworkRouteChange CreateRouteChange(
+ Timestamp time,
+ absl::optional<DataRate> start_rate = absl::nullopt,
+ absl::optional<DataRate> min_rate = absl::nullopt,
+ absl::optional<DataRate> max_rate = absl::nullopt) {
+ NetworkRouteChange route_change;
+ route_change.at_time = time;
+ route_change.constraints.at_time = time;
+ route_change.constraints.min_data_rate = min_rate;
+ route_change.constraints.max_data_rate = max_rate;
+ route_change.constraints.starting_rate = start_rate;
+ return route_change;
+}
+
+PacketResult CreatePacketResult(Timestamp arrival_time,
+ Timestamp send_time,
+ size_t payload_size,
+ PacedPacketInfo pacing_info) {
+ PacketResult packet_result;
+ packet_result.sent_packet = SentPacket();
+ packet_result.sent_packet.send_time = send_time;
+ packet_result.sent_packet.size = DataSize::Bytes(payload_size);
+ packet_result.sent_packet.pacing_info = pacing_info;
+ packet_result.receive_time = arrival_time;
+ return packet_result;
+}
+
+// Simulate sending packets and receiving transport feedback during
+// `runtime_ms`.
+absl::optional<DataRate> PacketTransmissionAndFeedbackBlock(
+ NetworkControllerInterface* controller,
+ int64_t runtime_ms,
+ int64_t delay,
+ Timestamp& current_time) {
+ NetworkControlUpdate update;
+ absl::optional<DataRate> target_bitrate;
+ int64_t delay_buildup = 0;
+ int64_t start_time_ms = current_time.ms();
+ while (current_time.ms() - start_time_ms < runtime_ms) {
+ constexpr size_t kPayloadSize = 1000;
+ PacketResult packet =
+ CreatePacketResult(current_time + TimeDelta::Millis(delay_buildup),
+ current_time, kPayloadSize, PacedPacketInfo());
+ delay_buildup += delay;
+ update = controller->OnSentPacket(packet.sent_packet);
+ if (update.target_rate) {
+ target_bitrate = update.target_rate->target_rate;
+ }
+ TransportPacketsFeedback feedback;
+ feedback.feedback_time = packet.receive_time;
+ feedback.packet_feedbacks.push_back(packet);
+ update = controller->OnTransportPacketsFeedback(feedback);
+ if (update.target_rate) {
+ target_bitrate = update.target_rate->target_rate;
+ }
+ current_time += TimeDelta::Millis(50);
+ update = controller->OnProcessInterval({.at_time = current_time});
+ if (update.target_rate) {
+ target_bitrate = update.target_rate->target_rate;
+ }
+ }
+ return target_bitrate;
+}
+
+// Scenarios:
+
+void UpdatesTargetRateBasedOnLinkCapacity(absl::string_view test_name = "") {
+ auto factory = CreateFeedbackOnlyFactory();
+ Scenario s("googcc_unit/target_capacity" + std::string(test_name), false);
+ CallClientConfig config;
+ config.transport.cc_factory = &factory;
+ config.transport.rates.min_rate = DataRate::KilobitsPerSec(10);
+ config.transport.rates.max_rate = DataRate::KilobitsPerSec(1500);
+ config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
+ auto send_net = s.CreateMutableSimulationNode([](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(500);
+ c->delay = TimeDelta::Millis(100);
+ c->loss_rate = 0.0;
+ });
+ auto ret_net = s.CreateMutableSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(100); });
+ StatesPrinter* truth = s.CreatePrinter(
+ "send.truth.txt", TimeDelta::PlusInfinity(), {send_net->ConfigPrinter()});
+
+ auto* client = CreateVideoSendingClient(&s, config, {send_net->node()},
+ {ret_net->node()});
+
+ truth->PrintRow();
+ s.RunFor(TimeDelta::Seconds(25));
+ truth->PrintRow();
+ EXPECT_NEAR(client->target_rate().kbps(), 450, 100);
+
+ send_net->UpdateConfig([](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(800);
+ c->delay = TimeDelta::Millis(100);
+ });
+
+ truth->PrintRow();
+ s.RunFor(TimeDelta::Seconds(20));
+ truth->PrintRow();
+ EXPECT_NEAR(client->target_rate().kbps(), 750, 150);
+
+ send_net->UpdateConfig([](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(100);
+ c->delay = TimeDelta::Millis(200);
+ });
+ ret_net->UpdateConfig(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(200); });
+
+ truth->PrintRow();
+ s.RunFor(TimeDelta::Seconds(50));
+ truth->PrintRow();
+ EXPECT_NEAR(client->target_rate().kbps(), 90, 25);
+}
+
+DataRate RunRembDipScenario(absl::string_view test_name) {
+ Scenario s(test_name);
+ NetworkSimulationConfig net_conf;
+ net_conf.bandwidth = DataRate::KilobitsPerSec(2000);
+ net_conf.delay = TimeDelta::Millis(50);
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = DataRate::KilobitsPerSec(1000);
+ });
+ auto send_net = {s.CreateSimulationNode(net_conf)};
+ auto ret_net = {s.CreateSimulationNode(net_conf)};
+ auto* route = s.CreateRoutes(
+ client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+
+ s.RunFor(TimeDelta::Seconds(10));
+ EXPECT_GT(client->send_bandwidth().kbps(), 1500);
+
+ DataRate RembLimit = DataRate::KilobitsPerSec(250);
+ client->SetRemoteBitrate(RembLimit);
+ s.RunFor(TimeDelta::Seconds(1));
+ EXPECT_EQ(client->send_bandwidth(), RembLimit);
+
+ DataRate RembLimitLifted = DataRate::KilobitsPerSec(10000);
+ client->SetRemoteBitrate(RembLimitLifted);
+ s.RunFor(TimeDelta::Seconds(10));
+
+ return client->send_bandwidth();
+}
+
+} // namespace
+
+class NetworkControllerTestFixture {
+ public:
+ NetworkControllerTestFixture() : factory_() {}
+
+ std::unique_ptr<NetworkControllerInterface> CreateController() {
+ NetworkControllerConfig config = InitialConfig();
+ std::unique_ptr<NetworkControllerInterface> controller =
+ factory_.Create(config);
+ return controller;
+ }
+
+ private:
+ NetworkControllerConfig InitialConfig(
+ int starting_bandwidth_kbps = kInitialBitrateKbps,
+ int min_data_rate_kbps = 0,
+ int max_data_rate_kbps = 5 * kInitialBitrateKbps) {
+ NetworkControllerConfig config;
+ config.constraints.at_time = Timestamp::Zero();
+ config.constraints.min_data_rate =
+ DataRate::KilobitsPerSec(min_data_rate_kbps);
+ config.constraints.max_data_rate =
+ DataRate::KilobitsPerSec(max_data_rate_kbps);
+ config.constraints.starting_rate =
+ DataRate::KilobitsPerSec(starting_bandwidth_kbps);
+ config.event_log = &event_log_;
+ return config;
+ }
+
+ NiceMock<MockRtcEventLog> event_log_;
+ GoogCcNetworkControllerFactory factory_;
+};
+
+TEST(GoogCcNetworkControllerTest, InitializeTargetRateOnFirstProcessInterval) {
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+
+ NetworkControlUpdate update =
+ controller->OnProcessInterval({.at_time = Timestamp::Millis(123456)});
+
+ EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate);
+ EXPECT_EQ(update.pacer_config->data_rate(),
+ kInitialBitrate * kDefaultPacingRate);
+ EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate,
+ kInitialBitrate * 3);
+ EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate,
+ kInitialBitrate * 5);
+}
+
+TEST(GoogCcNetworkControllerTest, ReactsToChangedNetworkConditions) {
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+ Timestamp current_time = Timestamp::Millis(123);
+ NetworkControlUpdate update =
+ controller->OnProcessInterval({.at_time = current_time});
+ update = controller->OnRemoteBitrateReport(
+ {.receive_time = current_time, .bandwidth = kInitialBitrate * 2});
+
+ current_time += TimeDelta::Millis(25);
+ update = controller->OnProcessInterval({.at_time = current_time});
+ EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate * 2);
+ EXPECT_EQ(update.pacer_config->data_rate(),
+ kInitialBitrate * 2 * kDefaultPacingRate);
+
+ update = controller->OnRemoteBitrateReport(
+ {.receive_time = current_time, .bandwidth = kInitialBitrate});
+ current_time += TimeDelta::Millis(25);
+ update = controller->OnProcessInterval({.at_time = current_time});
+ EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate);
+ EXPECT_EQ(update.pacer_config->data_rate(),
+ kInitialBitrate * kDefaultPacingRate);
+}
+
+TEST(GoogCcNetworkControllerTest, OnNetworkRouteChanged) {
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+ Timestamp current_time = Timestamp::Millis(123);
+ DataRate new_bitrate = DataRate::BitsPerSec(200000);
+ NetworkControlUpdate update = controller->OnNetworkRouteChange(
+ CreateRouteChange(current_time, new_bitrate));
+ EXPECT_EQ(update.target_rate->target_rate, new_bitrate);
+ EXPECT_EQ(update.pacer_config->data_rate(), new_bitrate * kDefaultPacingRate);
+ EXPECT_EQ(update.probe_cluster_configs.size(), 2u);
+
+ // If the bitrate is reset to -1, the new starting bitrate will be
+ // the minimum default bitrate.
+ const DataRate kDefaultMinBitrate = DataRate::KilobitsPerSec(5);
+ update = controller->OnNetworkRouteChange(CreateRouteChange(current_time));
+ EXPECT_EQ(update.target_rate->target_rate, kDefaultMinBitrate);
+ EXPECT_NEAR(update.pacer_config->data_rate().bps<double>(),
+ kDefaultMinBitrate.bps<double>() * kDefaultPacingRate, 10);
+ EXPECT_EQ(update.probe_cluster_configs.size(), 2u);
+}
+
+TEST(GoogCcNetworkControllerTest, ProbeOnRouteChange) {
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+ Timestamp current_time = Timestamp::Millis(123);
+ NetworkControlUpdate update = controller->OnNetworkRouteChange(
+ CreateRouteChange(current_time, 2 * kInitialBitrate, DataRate::Zero(),
+ 20 * kInitialBitrate));
+
+ EXPECT_TRUE(update.pacer_config.has_value());
+ EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate * 2);
+ EXPECT_EQ(update.probe_cluster_configs.size(), 2u);
+ EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate,
+ kInitialBitrate * 6);
+ EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate,
+ kInitialBitrate * 12);
+
+ update = controller->OnProcessInterval({.at_time = current_time});
+}
+
+// Bandwidth estimation is updated when feedbacks are received.
+// Feedbacks which show an increasing delay cause the estimation to be reduced.
+TEST(GoogCcNetworkControllerTest, UpdatesDelayBasedEstimate) {
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+ const int64_t kRunTimeMs = 6000;
+ Timestamp current_time = Timestamp::Millis(123);
+
+ // The test must run and insert packets/feedback long enough that the
+ // BWE computes a valid estimate. This is first done in an environment which
+ // simulates no bandwidth limitation, and therefore not built-up delay.
+ absl::optional<DataRate> target_bitrate_before_delay =
+ PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 0,
+ current_time);
+ ASSERT_TRUE(target_bitrate_before_delay.has_value());
+
+ // Repeat, but this time with a building delay, and make sure that the
+ // estimation is adjusted downwards.
+ absl::optional<DataRate> target_bitrate_after_delay =
+ PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 50,
+ current_time);
+ EXPECT_LT(*target_bitrate_after_delay, *target_bitrate_before_delay);
+}
+
+TEST(GoogCcNetworkControllerTest, PaceAtMaxOfLowerLinkCapacityAndBwe) {
+ ScopedFieldTrials trial(
+ "WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity/Enabled/");
+ NetworkControllerTestFixture fixture;
+ std::unique_ptr<NetworkControllerInterface> controller =
+ fixture.CreateController();
+ Timestamp current_time = Timestamp::Millis(123);
+ NetworkControlUpdate update =
+ controller->OnProcessInterval({.at_time = current_time});
+ current_time += TimeDelta::Millis(100);
+ NetworkStateEstimate network_estimate = {.link_capacity_lower =
+ 10 * kInitialBitrate};
+ update = controller->OnNetworkStateEstimate(network_estimate);
+ // OnNetworkStateEstimate does not trigger processing a new estimate. So add a
+ // dummy loss report to trigger a BWE update in the next process interval.
+ TransportLossReport loss_report;
+ loss_report.start_time = current_time;
+ loss_report.end_time = current_time;
+ loss_report.receive_time = current_time;
+ loss_report.packets_received_delta = 50;
+ loss_report.packets_lost_delta = 1;
+ update = controller->OnTransportLossReport(loss_report);
+ update = controller->OnProcessInterval({.at_time = current_time});
+ ASSERT_TRUE(update.pacer_config);
+ ASSERT_TRUE(update.target_rate);
+ ASSERT_LT(update.target_rate->target_rate,
+ network_estimate.link_capacity_lower);
+ EXPECT_EQ(update.pacer_config->data_rate().kbps(),
+ network_estimate.link_capacity_lower.kbps() * kDefaultPacingRate);
+
+ current_time += TimeDelta::Millis(100);
+ // Set a low link capacity estimate and verify that pacing rate is set
+ // relative to loss based/delay based estimate.
+ network_estimate = {.link_capacity_lower = 0.5 * kInitialBitrate};
+ update = controller->OnNetworkStateEstimate(network_estimate);
+ // Again, we need to inject a dummy loss report to trigger an update of the
+ // BWE in the next process interval.
+ loss_report.start_time = current_time;
+ loss_report.end_time = current_time;
+ loss_report.receive_time = current_time;
+ loss_report.packets_received_delta = 50;
+ loss_report.packets_lost_delta = 0;
+ update = controller->OnTransportLossReport(loss_report);
+ update = controller->OnProcessInterval({.at_time = current_time});
+ ASSERT_TRUE(update.target_rate);
+ ASSERT_GT(update.target_rate->target_rate,
+ network_estimate.link_capacity_lower);
+ EXPECT_EQ(update.pacer_config->data_rate().kbps(),
+ update.target_rate->target_rate.kbps() * kDefaultPacingRate);
+}
+
+// Test congestion window pushback on network delay happens.
+TEST(GoogCcScenario, CongestionWindowPushbackOnNetworkDelay) {
+ auto factory = CreateFeedbackOnlyFactory();
+ ScopedFieldTrials trial(
+ "WebRTC-CongestionWindow/QueueSize:800,MinBitrate:30000/");
+ Scenario s("googcc_unit/cwnd_on_delay", false);
+ auto send_net =
+ s.CreateMutableSimulationNode([=](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(1000);
+ c->delay = TimeDelta::Millis(100);
+ });
+ auto ret_net = s.CreateSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(100); });
+ CallClientConfig config;
+ config.transport.cc_factory = &factory;
+ // Start high so bandwidth drop has max effect.
+ config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
+ config.transport.rates.max_rate = DataRate::KilobitsPerSec(2000);
+ config.transport.rates.min_rate = DataRate::KilobitsPerSec(10);
+
+ auto* client = CreateVideoSendingClient(&s, std::move(config),
+ {send_net->node()}, {ret_net});
+
+ s.RunFor(TimeDelta::Seconds(10));
+ send_net->PauseTransmissionUntil(s.Now() + TimeDelta::Seconds(10));
+ s.RunFor(TimeDelta::Seconds(3));
+
+ // After 3 seconds without feedback from any sent packets, we expect that the
+ // target rate is reduced to the minimum pushback threshold
+ // kDefaultMinPushbackTargetBitrateBps, which is defined as 30 kbps in
+ // congestion_window_pushback_controller.
+ EXPECT_LT(client->target_rate().kbps(), 40);
+}
+
+// Test congestion window pushback on network delay happens.
+TEST(GoogCcScenario, CongestionWindowPushbackDropFrameOnNetworkDelay) {
+ auto factory = CreateFeedbackOnlyFactory();
+ ScopedFieldTrials trial(
+ "WebRTC-CongestionWindow/QueueSize:800,MinBitrate:30000,DropFrame:true/");
+ Scenario s("googcc_unit/cwnd_on_delay", false);
+ auto send_net =
+ s.CreateMutableSimulationNode([=](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(1000);
+ c->delay = TimeDelta::Millis(100);
+ });
+ auto ret_net = s.CreateSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(100); });
+ CallClientConfig config;
+ config.transport.cc_factory = &factory;
+ // Start high so bandwidth drop has max effect.
+ config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
+ config.transport.rates.max_rate = DataRate::KilobitsPerSec(2000);
+ config.transport.rates.min_rate = DataRate::KilobitsPerSec(10);
+
+ auto* client = CreateVideoSendingClient(&s, std::move(config),
+ {send_net->node()}, {ret_net});
+
+ s.RunFor(TimeDelta::Seconds(10));
+ send_net->PauseTransmissionUntil(s.Now() + TimeDelta::Seconds(10));
+ s.RunFor(TimeDelta::Seconds(3));
+
+ // As the dropframe is set, after 3 seconds without feedback from any sent
+ // packets, we expect that the target rate is not reduced by congestion
+ // window.
+ EXPECT_GT(client->target_rate().kbps(), 300);
+}
+
+TEST(GoogCcScenario, PaddingRateLimitedByCongestionWindowInTrial) {
+ ScopedFieldTrials trial(
+ "WebRTC-CongestionWindow/QueueSize:200,MinBitrate:30000/");
+
+ Scenario s("googcc_unit/padding_limited", false);
+ auto send_net =
+ s.CreateMutableSimulationNode([=](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(1000);
+ c->delay = TimeDelta::Millis(100);
+ });
+ auto ret_net = s.CreateSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(100); });
+ CallClientConfig config;
+ // Start high so bandwidth drop has max effect.
+ config.transport.rates.start_rate = DataRate::KilobitsPerSec(1000);
+ config.transport.rates.max_rate = DataRate::KilobitsPerSec(2000);
+ auto* client = s.CreateClient("send", config);
+ auto* route =
+ s.CreateRoutes(client, {send_net->node()},
+ s.CreateClient("return", CallClientConfig()), {ret_net});
+ VideoStreamConfig video;
+ video.stream.pad_to_rate = config.transport.rates.max_rate;
+ s.CreateVideoStream(route->forward(), video);
+
+ // Run for a few seconds to allow the controller to stabilize.
+ s.RunFor(TimeDelta::Seconds(10));
+
+ // Check that padding rate matches target rate.
+ EXPECT_NEAR(client->padding_rate().kbps(), client->target_rate().kbps(), 1);
+
+ // Check this is also the case when congestion window pushback kicks in.
+ send_net->PauseTransmissionUntil(s.Now() + TimeDelta::Seconds(1));
+ EXPECT_NEAR(client->padding_rate().kbps(), client->target_rate().kbps(), 1);
+}
+
+TEST(GoogCcScenario, LimitsToFloorIfRttIsHighInTrial) {
+ // The field trial limits maximum RTT to 2 seconds, higher RTT means that the
+ // controller backs off until it reaches the minimum configured bitrate. This
+ // allows the RTT to recover faster than the regular control mechanism would
+ // achieve.
+ const DataRate kBandwidthFloor = DataRate::KilobitsPerSec(50);
+ ScopedFieldTrials trial("WebRTC-Bwe-MaxRttLimit/limit:2s,floor:" +
+ std::to_string(kBandwidthFloor.kbps()) + "kbps/");
+ // In the test case, we limit the capacity and add a cross traffic packet
+ // burst that blocks media from being sent. This causes the RTT to quickly
+ // increase above the threshold in the trial.
+ const DataRate kLinkCapacity = DataRate::KilobitsPerSec(100);
+ const TimeDelta kBufferBloatDuration = TimeDelta::Seconds(10);
+ Scenario s("googcc_unit/limit_trial", false);
+ auto send_net = s.CreateSimulationNode([=](NetworkSimulationConfig* c) {
+ c->bandwidth = kLinkCapacity;
+ c->delay = TimeDelta::Millis(100);
+ });
+ auto ret_net = s.CreateSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(100); });
+ CallClientConfig config;
+ config.transport.rates.start_rate = kLinkCapacity;
+
+ auto* client = CreateVideoSendingClient(&s, config, {send_net}, {ret_net});
+ // Run for a few seconds to allow the controller to stabilize.
+ s.RunFor(TimeDelta::Seconds(10));
+ const DataSize kBloatPacketSize = DataSize::Bytes(1000);
+ const int kBloatPacketCount =
+ static_cast<int>(kBufferBloatDuration * kLinkCapacity / kBloatPacketSize);
+ // This will cause the RTT to be large for a while.
+ s.TriggerPacketBurst({send_net}, kBloatPacketCount, kBloatPacketSize.bytes());
+ // Wait to allow the high RTT to be detected and acted upon.
+ s.RunFor(TimeDelta::Seconds(6));
+ // By now the target rate should have dropped to the minimum configured rate.
+ EXPECT_NEAR(client->target_rate().kbps(), kBandwidthFloor.kbps(), 5);
+}
+
+TEST(GoogCcScenario, UpdatesTargetRateBasedOnLinkCapacity) {
+ UpdatesTargetRateBasedOnLinkCapacity();
+}
+
+TEST(GoogCcScenario, StableEstimateDoesNotVaryInSteadyState) {
+ auto factory = CreateFeedbackOnlyFactory();
+ Scenario s("googcc_unit/stable_target", false);
+ CallClientConfig config;
+ config.transport.cc_factory = &factory;
+ NetworkSimulationConfig net_conf;
+ net_conf.bandwidth = DataRate::KilobitsPerSec(500);
+ net_conf.delay = TimeDelta::Millis(100);
+ auto send_net = s.CreateSimulationNode(net_conf);
+ auto ret_net = s.CreateSimulationNode(net_conf);
+
+ auto* client = CreateVideoSendingClient(&s, config, {send_net}, {ret_net});
+ // Run for a while to allow the estimate to stabilize.
+ s.RunFor(TimeDelta::Seconds(30));
+ DataRate min_stable_target = DataRate::PlusInfinity();
+ DataRate max_stable_target = DataRate::MinusInfinity();
+ DataRate min_target = DataRate::PlusInfinity();
+ DataRate max_target = DataRate::MinusInfinity();
+
+ // Measure variation in steady state.
+ for (int i = 0; i < 20; ++i) {
+ auto stable_target_rate = client->stable_target_rate();
+ auto target_rate = client->target_rate();
+ EXPECT_LE(stable_target_rate, target_rate);
+
+ min_stable_target = std::min(min_stable_target, stable_target_rate);
+ max_stable_target = std::max(max_stable_target, stable_target_rate);
+ min_target = std::min(min_target, target_rate);
+ max_target = std::max(max_target, target_rate);
+ s.RunFor(TimeDelta::Seconds(1));
+ }
+ // We should expect drops by at least 15% (default backoff.)
+ EXPECT_LT(min_target / max_target, 0.85);
+ // We should expect the stable target to be more stable than the immediate one
+ EXPECT_GE(min_stable_target / max_stable_target, min_target / max_target);
+}
+
+TEST(GoogCcScenario, LossBasedControlUpdatesTargetRateBasedOnLinkCapacity) {
+ ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/");
+ // TODO(srte): Should the behavior be unaffected at low loss rates?
+ UpdatesTargetRateBasedOnLinkCapacity("_loss_based");
+}
+
+TEST(GoogCcScenario, LossBasedControlDoesModestBackoffToHighLoss) {
+ ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/");
+ Scenario s("googcc_unit/high_loss_channel", false);
+ CallClientConfig config;
+ config.transport.rates.min_rate = DataRate::KilobitsPerSec(10);
+ config.transport.rates.max_rate = DataRate::KilobitsPerSec(1500);
+ config.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
+ auto send_net = s.CreateSimulationNode([](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(2000);
+ c->delay = TimeDelta::Millis(200);
+ c->loss_rate = 0.1;
+ });
+ auto ret_net = s.CreateSimulationNode(
+ [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(200); });
+
+ auto* client = CreateVideoSendingClient(&s, config, {send_net}, {ret_net});
+
+ s.RunFor(TimeDelta::Seconds(120));
+ // Without LossBasedControl trial, bandwidth drops to ~10 kbps.
+ EXPECT_GT(client->target_rate().kbps(), 100);
+}
+
+DataRate AverageBitrateAfterCrossInducedLoss(absl::string_view name) {
+ Scenario s(name, false);
+ NetworkSimulationConfig net_conf;
+ net_conf.bandwidth = DataRate::KilobitsPerSec(1000);
+ net_conf.delay = TimeDelta::Millis(100);
+ // Short queue length means that we'll induce loss when sudden TCP traffic
+ // spikes are induced. This corresponds to ca 200 ms for a packet size of 1000
+ // bytes. Such limited buffers are common on for instance wifi routers.
+ net_conf.packet_queue_length_limit = 25;
+
+ auto send_net = {s.CreateSimulationNode(net_conf)};
+ auto ret_net = {s.CreateSimulationNode(net_conf)};
+
+ auto* client = s.CreateClient("send", CallClientConfig());
+ auto* callee = s.CreateClient("return", CallClientConfig());
+ auto* route = s.CreateRoutes(client, send_net, callee, ret_net);
+ // TODO(srte): Make this work with RTX enabled or remove it.
+ auto* video = s.CreateVideoStream(route->forward(), [](VideoStreamConfig* c) {
+ c->stream.use_rtx = false;
+ });
+ s.RunFor(TimeDelta::Seconds(10));
+ for (int i = 0; i < 4; ++i) {
+ // Sends TCP cross traffic inducing loss.
+ auto* tcp_traffic = s.net()->StartCrossTraffic(CreateFakeTcpCrossTraffic(
+ s.net()->CreateRoute(send_net), s.net()->CreateRoute(ret_net),
+ FakeTcpConfig()));
+ s.RunFor(TimeDelta::Seconds(2));
+ // Allow the ccongestion controller to recover.
+ s.net()->StopCrossTraffic(tcp_traffic);
+ s.RunFor(TimeDelta::Seconds(20));
+ }
+
+ // Querying the video stats from within the expected runtime environment
+ // (i.e. the TQ that belongs to the CallClient, not the Scenario TQ that
+ // we're currently on).
+ VideoReceiveStreamInterface::Stats video_receive_stats;
+ auto* video_stream = video->receive();
+ callee->SendTask([&video_stream, &video_receive_stats]() {
+ video_receive_stats = video_stream->GetStats();
+ });
+ return DataSize::Bytes(
+ video_receive_stats.rtp_stats.packet_counter.TotalBytes()) /
+ s.TimeSinceStart();
+}
+
+TEST(GoogCcScenario, MaintainsLowRateInSafeResetTrial) {
+ const DataRate kLinkCapacity = DataRate::KilobitsPerSec(200);
+ const DataRate kStartRate = DataRate::KilobitsPerSec(300);
+
+ ScopedFieldTrials trial("WebRTC-Bwe-SafeResetOnRouteChange/Enabled/");
+ Scenario s("googcc_unit/safe_reset_low");
+ auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kLinkCapacity;
+ c->delay = TimeDelta::Millis(10);
+ });
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = kStartRate;
+ });
+ auto* route = s.CreateRoutes(
+ client, {send_net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow the controller to stabilize.
+ s.RunFor(TimeDelta::Millis(500));
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kLinkCapacity.kbps(), 50);
+ s.ChangeRoute(route->forward(), {send_net});
+ // Allow new settings to propagate.
+ s.RunFor(TimeDelta::Millis(100));
+ // Under the trial, the target should be unchanged for low rates.
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kLinkCapacity.kbps(), 50);
+}
+
+TEST(GoogCcScenario, CutsHighRateInSafeResetTrial) {
+ const DataRate kLinkCapacity = DataRate::KilobitsPerSec(1000);
+ const DataRate kStartRate = DataRate::KilobitsPerSec(300);
+
+ ScopedFieldTrials trial("WebRTC-Bwe-SafeResetOnRouteChange/Enabled/");
+ Scenario s("googcc_unit/safe_reset_high_cut");
+ auto send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kLinkCapacity;
+ c->delay = TimeDelta::Millis(50);
+ });
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = kStartRate;
+ });
+ auto* route = s.CreateRoutes(
+ client, {send_net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow the controller to stabilize.
+ s.RunFor(TimeDelta::Millis(500));
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kLinkCapacity.kbps(), 300);
+ s.ChangeRoute(route->forward(), {send_net});
+ // Allow new settings to propagate.
+ s.RunFor(TimeDelta::Millis(50));
+ // Under the trial, the target should be reset from high values.
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kStartRate.kbps(), 30);
+}
+
+TEST(GoogCcScenario, DetectsHighRateInSafeResetTrial) {
+ ScopedFieldTrials trial("WebRTC-Bwe-SafeResetOnRouteChange/Enabled,ack/");
+ const DataRate kInitialLinkCapacity = DataRate::KilobitsPerSec(200);
+ const DataRate kNewLinkCapacity = DataRate::KilobitsPerSec(800);
+ const DataRate kStartRate = DataRate::KilobitsPerSec(300);
+
+ Scenario s("googcc_unit/safe_reset_high_detect");
+ auto* initial_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kInitialLinkCapacity;
+ c->delay = TimeDelta::Millis(50);
+ });
+ auto* new_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kNewLinkCapacity;
+ c->delay = TimeDelta::Millis(50);
+ });
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = kStartRate;
+ });
+ auto* route = s.CreateRoutes(
+ client, {initial_net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow the controller to stabilize.
+ s.RunFor(TimeDelta::Millis(2000));
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kInitialLinkCapacity.kbps(), 50);
+ s.ChangeRoute(route->forward(), {new_net});
+ // Allow new settings to propagate, but not probes to be received.
+ s.RunFor(TimeDelta::Millis(50));
+ // Under the field trial, the target rate should be unchanged since it's lower
+ // than the starting rate.
+ EXPECT_NEAR(client->send_bandwidth().kbps(), kInitialLinkCapacity.kbps(), 50);
+ // However, probing should have made us detect the higher rate.
+ // NOTE: This test causes high loss rate, and the loss-based estimator reduces
+ // the bitrate, making the test fail if we wait longer than one second here.
+ s.RunFor(TimeDelta::Millis(1000));
+ EXPECT_GT(client->send_bandwidth().kbps(), kNewLinkCapacity.kbps() - 300);
+}
+
+TEST(GoogCcScenario, TargetRateReducedOnPacingBufferBuildupInTrial) {
+ // Configure strict pacing to ensure build-up.
+ ScopedFieldTrials trial(
+ "WebRTC-CongestionWindow/QueueSize:100,MinBitrate:30000/"
+ "WebRTC-Video-Pacing/factor:1.0/"
+ "WebRTC-AddPacingToCongestionWindowPushback/Enabled/");
+
+ const DataRate kLinkCapacity = DataRate::KilobitsPerSec(1000);
+ const DataRate kStartRate = DataRate::KilobitsPerSec(1000);
+
+ Scenario s("googcc_unit/pacing_buffer_buildup");
+ auto* net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kLinkCapacity;
+ c->delay = TimeDelta::Millis(50);
+ });
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = kStartRate;
+ });
+ auto* route = s.CreateRoutes(
+ client, {net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow some time for the buffer to build up.
+ s.RunFor(TimeDelta::Seconds(5));
+
+ // Without trial, pacer delay reaches ~250 ms.
+ EXPECT_LT(client->GetStats().pacer_delay_ms, 150);
+}
+
+TEST(GoogCcScenario, NoBandwidthTogglingInLossControlTrial) {
+ ScopedFieldTrials trial("WebRTC-Bwe-LossBasedControl/Enabled/");
+ Scenario s("googcc_unit/no_toggling");
+ auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(2000);
+ c->loss_rate = 0.2;
+ c->delay = TimeDelta::Millis(10);
+ });
+
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = DataRate::KilobitsPerSec(300);
+ });
+ auto* route = s.CreateRoutes(
+ client, {send_net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow the controller to initialize.
+ s.RunFor(TimeDelta::Millis(250));
+
+ std::queue<DataRate> bandwidth_history;
+ const TimeDelta step = TimeDelta::Millis(50);
+ for (TimeDelta time = TimeDelta::Zero(); time < TimeDelta::Millis(2000);
+ time += step) {
+ s.RunFor(step);
+ const TimeDelta window = TimeDelta::Millis(500);
+ if (bandwidth_history.size() >= window / step)
+ bandwidth_history.pop();
+ bandwidth_history.push(client->send_bandwidth());
+ EXPECT_LT(
+ CountBandwidthDips(bandwidth_history, DataRate::KilobitsPerSec(100)),
+ 2);
+ }
+}
+
+TEST(GoogCcScenario, NoRttBackoffCollapseWhenVideoStops) {
+ ScopedFieldTrials trial("WebRTC-Bwe-MaxRttLimit/limit:2s/");
+ Scenario s("googcc_unit/rttbackoff_video_stop");
+ auto* send_net = s.CreateSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = DataRate::KilobitsPerSec(2000);
+ c->delay = TimeDelta::Millis(100);
+ });
+
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = DataRate::KilobitsPerSec(1000);
+ });
+ auto* route = s.CreateRoutes(
+ client, {send_net}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+ auto* video = s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ // Allow the controller to initialize, then stop video.
+ s.RunFor(TimeDelta::Seconds(1));
+ video->send()->Stop();
+ s.RunFor(TimeDelta::Seconds(4));
+ EXPECT_GT(client->send_bandwidth().kbps(), 1000);
+}
+
+TEST(GoogCcScenario, NoCrashOnVeryLateFeedback) {
+ Scenario s;
+ auto ret_net = s.CreateMutableSimulationNode(NetworkSimulationConfig());
+ auto* route = s.CreateRoutes(
+ s.CreateClient("send", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())},
+ s.CreateClient("return", CallClientConfig()), {ret_net->node()});
+ auto* video = s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ s.RunFor(TimeDelta::Seconds(5));
+ // Delay feedback by several minutes. This will cause removal of the send time
+ // history for the packets as long as kSendTimeHistoryWindow is configured for
+ // a shorter time span.
+ ret_net->PauseTransmissionUntil(s.Now() + TimeDelta::Seconds(300));
+ // Stopping video stream while waiting to save test execution time.
+ video->send()->Stop();
+ s.RunFor(TimeDelta::Seconds(299));
+ // Starting to cause addition of new packet to history, which cause old
+ // packets to be removed.
+ video->send()->Start();
+ // Runs until the lost packets are received. We expect that this will run
+ // without causing any runtime failures.
+ s.RunFor(TimeDelta::Seconds(2));
+}
+
+TEST(GoogCcScenario, IsFairToTCP) {
+ Scenario s("googcc_unit/tcp_fairness");
+ NetworkSimulationConfig net_conf;
+ net_conf.bandwidth = DataRate::KilobitsPerSec(1000);
+ net_conf.delay = TimeDelta::Millis(50);
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = DataRate::KilobitsPerSec(1000);
+ });
+ auto send_net = {s.CreateSimulationNode(net_conf)};
+ auto ret_net = {s.CreateSimulationNode(net_conf)};
+ auto* route = s.CreateRoutes(
+ client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
+ s.CreateVideoStream(route->forward(), VideoStreamConfig());
+ s.net()->StartCrossTraffic(CreateFakeTcpCrossTraffic(
+ s.net()->CreateRoute(send_net), s.net()->CreateRoute(ret_net),
+ FakeTcpConfig()));
+ s.RunFor(TimeDelta::Seconds(10));
+
+ // Currently only testing for the upper limit as we in practice back out
+ // quite a lot in this scenario. If this behavior is fixed, we should add a
+ // lower bound to ensure it stays fixed.
+ EXPECT_LT(client->send_bandwidth().kbps(), 750);
+}
+
+TEST(GoogCcScenario, FastRampupOnRembCapLifted) {
+ DataRate final_estimate =
+ RunRembDipScenario("googcc_unit/default_fast_rampup_on_remb_cap_lifted");
+ EXPECT_GT(final_estimate.kbps(), 1500);
+}
+
+TEST(GoogCcScenario, FallbackToLossBasedBweWithoutPacketFeedback) {
+ const DataRate kLinkCapacity = DataRate::KilobitsPerSec(1000);
+ const DataRate kStartRate = DataRate::KilobitsPerSec(1000);
+
+ Scenario s("googcc_unit/high_loss_channel", false);
+ auto* net = s.CreateMutableSimulationNode([&](NetworkSimulationConfig* c) {
+ c->bandwidth = kLinkCapacity;
+ c->delay = TimeDelta::Millis(100);
+ });
+ auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
+ c->transport.rates.start_rate = kStartRate;
+ });
+ auto* route = s.CreateRoutes(
+ client, {net->node()}, s.CreateClient("return", CallClientConfig()),
+ {s.CreateSimulationNode(NetworkSimulationConfig())});
+
+ // Create a config without packet feedback.
+ VideoStreamConfig video_config;
+ video_config.stream.packet_feedback = false;
+ s.CreateVideoStream(route->forward(), video_config);
+
+ s.RunFor(TimeDelta::Seconds(20));
+ // Bandwith does not backoff because network is normal.
+ EXPECT_GE(client->target_rate().kbps(), 500);
+
+ // Update the network to create high loss ratio
+ net->UpdateConfig([](NetworkSimulationConfig* c) {
+ c->loss_rate = 0.15;
+ });
+ s.RunFor(TimeDelta::Seconds(20));
+
+ // Bandwidth decreases thanks to loss based bwe v0.
+ EXPECT_LE(client->target_rate().kbps(), 300);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.cc
new file mode 100644
index 0000000000..2d50d08e6a
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/inter_arrival_delta.h"
+
+#include <algorithm>
+
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+static constexpr TimeDelta kBurstDeltaThreshold = TimeDelta::Millis(5);
+static constexpr TimeDelta kMaxBurstDuration = TimeDelta::Millis(100);
+constexpr TimeDelta InterArrivalDelta::kArrivalTimeOffsetThreshold;
+
+InterArrivalDelta::InterArrivalDelta(TimeDelta send_time_group_length)
+ : send_time_group_length_(send_time_group_length),
+ current_timestamp_group_(),
+ prev_timestamp_group_(),
+ num_consecutive_reordered_packets_(0) {}
+
+bool InterArrivalDelta::ComputeDeltas(Timestamp send_time,
+ Timestamp arrival_time,
+ Timestamp system_time,
+ size_t packet_size,
+ TimeDelta* send_time_delta,
+ TimeDelta* arrival_time_delta,
+ int* packet_size_delta) {
+ bool calculated_deltas = false;
+ if (current_timestamp_group_.IsFirstPacket()) {
+ // We don't have enough data to update the filter, so we store it until we
+ // have two frames of data to process.
+ current_timestamp_group_.send_time = send_time;
+ current_timestamp_group_.first_send_time = send_time;
+ current_timestamp_group_.first_arrival = arrival_time;
+ } else if (current_timestamp_group_.first_send_time > send_time) {
+ // Reordered packet.
+ return false;
+ } else if (NewTimestampGroup(arrival_time, send_time)) {
+ // First packet of a later send burst, the previous packets sample is ready.
+ if (prev_timestamp_group_.complete_time.IsFinite()) {
+ *send_time_delta =
+ current_timestamp_group_.send_time - prev_timestamp_group_.send_time;
+ *arrival_time_delta = current_timestamp_group_.complete_time -
+ prev_timestamp_group_.complete_time;
+
+ TimeDelta system_time_delta = current_timestamp_group_.last_system_time -
+ prev_timestamp_group_.last_system_time;
+
+ if (*arrival_time_delta - system_time_delta >=
+ kArrivalTimeOffsetThreshold) {
+ RTC_LOG(LS_WARNING)
+ << "The arrival time clock offset has changed (diff = "
+ << arrival_time_delta->ms() - system_time_delta.ms()
+ << " ms), resetting.";
+ Reset();
+ return false;
+ }
+ if (*arrival_time_delta < TimeDelta::Zero()) {
+ // The group of packets has been reordered since receiving its local
+ // arrival timestamp.
+ ++num_consecutive_reordered_packets_;
+ if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) {
+ RTC_LOG(LS_WARNING)
+ << "Packets between send burst arrived out of order, resetting:"
+ << " arrival_time_delta_ms=" << arrival_time_delta->ms()
+ << ", send_time_delta_ms=" << send_time_delta->ms();
+ Reset();
+ }
+ return false;
+ } else {
+ num_consecutive_reordered_packets_ = 0;
+ }
+ *packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
+ static_cast<int>(prev_timestamp_group_.size);
+ calculated_deltas = true;
+ }
+ prev_timestamp_group_ = current_timestamp_group_;
+ // The new timestamp is now the current frame.
+ current_timestamp_group_.first_send_time = send_time;
+ current_timestamp_group_.send_time = send_time;
+ current_timestamp_group_.first_arrival = arrival_time;
+ current_timestamp_group_.size = 0;
+ } else {
+ current_timestamp_group_.send_time =
+ std::max(current_timestamp_group_.send_time, send_time);
+ }
+ // Accumulate the frame size.
+ current_timestamp_group_.size += packet_size;
+ current_timestamp_group_.complete_time = arrival_time;
+ current_timestamp_group_.last_system_time = system_time;
+
+ return calculated_deltas;
+}
+
+// Assumes that `timestamp` is not reordered compared to
+// `current_timestamp_group_`.
+bool InterArrivalDelta::NewTimestampGroup(Timestamp arrival_time,
+ Timestamp send_time) const {
+ if (current_timestamp_group_.IsFirstPacket()) {
+ return false;
+ } else if (BelongsToBurst(arrival_time, send_time)) {
+ return false;
+ } else {
+ return send_time - current_timestamp_group_.first_send_time >
+ send_time_group_length_;
+ }
+}
+
+bool InterArrivalDelta::BelongsToBurst(Timestamp arrival_time,
+ Timestamp send_time) const {
+ RTC_DCHECK(current_timestamp_group_.complete_time.IsFinite());
+ TimeDelta arrival_time_delta =
+ arrival_time - current_timestamp_group_.complete_time;
+ TimeDelta send_time_delta = send_time - current_timestamp_group_.send_time;
+ if (send_time_delta.IsZero())
+ return true;
+ TimeDelta propagation_delta = arrival_time_delta - send_time_delta;
+ if (propagation_delta < TimeDelta::Zero() &&
+ arrival_time_delta <= kBurstDeltaThreshold &&
+ arrival_time - current_timestamp_group_.first_arrival < kMaxBurstDuration)
+ return true;
+ return false;
+}
+
+void InterArrivalDelta::Reset() {
+ num_consecutive_reordered_packets_ = 0;
+ current_timestamp_group_ = SendTimeGroup();
+ prev_timestamp_group_ = SendTimeGroup();
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.h
new file mode 100644
index 0000000000..4046590eeb
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/inter_arrival_delta.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_
+
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+
+namespace webrtc {
+
+// Helper class to compute the inter-arrival time delta and the size delta
+// between two send bursts. This code is branched from
+// modules/remote_bitrate_estimator/inter_arrival.
+class InterArrivalDelta {
+ public:
+ // After this many packet groups received out of order InterArrival will
+ // reset, assuming that clocks have made a jump.
+ static constexpr int kReorderedResetThreshold = 3;
+ static constexpr TimeDelta kArrivalTimeOffsetThreshold =
+ TimeDelta::Seconds(3);
+
+ // A send time group is defined as all packets with a send time which are at
+ // most send_time_group_length older than the first timestamp in that
+ // group.
+ explicit InterArrivalDelta(TimeDelta send_time_group_length);
+
+ InterArrivalDelta() = delete;
+ InterArrivalDelta(const InterArrivalDelta&) = delete;
+ InterArrivalDelta& operator=(const InterArrivalDelta&) = delete;
+
+ // This function returns true if a delta was computed, or false if the current
+ // group is still incomplete or if only one group has been completed.
+ // `send_time` is the send time.
+ // `arrival_time` is the time at which the packet arrived.
+ // `packet_size` is the size of the packet.
+ // `timestamp_delta` (output) is the computed send time delta.
+ // `arrival_time_delta` (output) is the computed arrival-time delta.
+ // `packet_size_delta` (output) is the computed size delta.
+ bool ComputeDeltas(Timestamp send_time,
+ Timestamp arrival_time,
+ Timestamp system_time,
+ size_t packet_size,
+ TimeDelta* send_time_delta,
+ TimeDelta* arrival_time_delta,
+ int* packet_size_delta);
+
+ private:
+ struct SendTimeGroup {
+ SendTimeGroup()
+ : size(0),
+ first_send_time(Timestamp::MinusInfinity()),
+ send_time(Timestamp::MinusInfinity()),
+ first_arrival(Timestamp::MinusInfinity()),
+ complete_time(Timestamp::MinusInfinity()),
+ last_system_time(Timestamp::MinusInfinity()) {}
+
+ bool IsFirstPacket() const { return complete_time.IsInfinite(); }
+
+ size_t size;
+ Timestamp first_send_time;
+ Timestamp send_time;
+ Timestamp first_arrival;
+ Timestamp complete_time;
+ Timestamp last_system_time;
+ };
+
+ // Returns true if the last packet was the end of the current batch and the
+ // packet with `send_time` is the first of a new batch.
+ bool NewTimestampGroup(Timestamp arrival_time, Timestamp send_time) const;
+
+ bool BelongsToBurst(Timestamp arrival_time, Timestamp send_time) const;
+
+ void Reset();
+
+ const TimeDelta send_time_group_length_;
+ SendTimeGroup current_timestamp_group_;
+ SendTimeGroup prev_timestamp_group_;
+ int num_consecutive_reordered_packets_;
+};
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.cc
new file mode 100644
index 0000000000..9fd537a422
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h"
+
+#include <algorithm>
+
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+LinkCapacityEstimator::LinkCapacityEstimator() {}
+
+DataRate LinkCapacityEstimator::UpperBound() const {
+ if (estimate_kbps_.has_value())
+ return DataRate::KilobitsPerSec(estimate_kbps_.value() +
+ 3 * deviation_estimate_kbps());
+ return DataRate::Infinity();
+}
+
+DataRate LinkCapacityEstimator::LowerBound() const {
+ if (estimate_kbps_.has_value())
+ return DataRate::KilobitsPerSec(
+ std::max(0.0, estimate_kbps_.value() - 3 * deviation_estimate_kbps()));
+ return DataRate::Zero();
+}
+
+void LinkCapacityEstimator::Reset() {
+ estimate_kbps_.reset();
+}
+
+void LinkCapacityEstimator::OnOveruseDetected(DataRate acknowledged_rate) {
+ Update(acknowledged_rate, 0.05);
+}
+
+void LinkCapacityEstimator::OnProbeRate(DataRate probe_rate) {
+ Update(probe_rate, 0.5);
+}
+
+void LinkCapacityEstimator::Update(DataRate capacity_sample, double alpha) {
+ double sample_kbps = capacity_sample.kbps();
+ if (!estimate_kbps_.has_value()) {
+ estimate_kbps_ = sample_kbps;
+ } else {
+ estimate_kbps_ = (1 - alpha) * estimate_kbps_.value() + alpha * sample_kbps;
+ }
+ // Estimate the variance of the link capacity estimate and normalize the
+ // variance with the link capacity estimate.
+ const double norm = std::max(estimate_kbps_.value(), 1.0);
+ double error_kbps = estimate_kbps_.value() - sample_kbps;
+ deviation_kbps_ =
+ (1 - alpha) * deviation_kbps_ + alpha * error_kbps * error_kbps / norm;
+ // 0.4 ~= 14 kbit/s at 500 kbit/s
+ // 2.5f ~= 35 kbit/s at 500 kbit/s
+ deviation_kbps_ = rtc::SafeClamp(deviation_kbps_, 0.4f, 2.5f);
+}
+
+bool LinkCapacityEstimator::has_estimate() const {
+ return estimate_kbps_.has_value();
+}
+
+DataRate LinkCapacityEstimator::estimate() const {
+ return DataRate::KilobitsPerSec(*estimate_kbps_);
+}
+
+double LinkCapacityEstimator::deviation_estimate_kbps() const {
+ // Calculate the max bit rate std dev given the normalized
+ // variance and the current throughput bitrate. The standard deviation will
+ // only be used if estimate_kbps_ has a value.
+ return sqrt(deviation_kbps_ * estimate_kbps_.value());
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.h
new file mode 100644
index 0000000000..aa23491d9d
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
+
+#include "absl/types/optional.h"
+#include "api/units/data_rate.h"
+
+namespace webrtc {
+class LinkCapacityEstimator {
+ public:
+ LinkCapacityEstimator();
+ DataRate UpperBound() const;
+ DataRate LowerBound() const;
+ void Reset();
+ void OnOveruseDetected(DataRate acknowledged_rate);
+ void OnProbeRate(DataRate probe_rate);
+ bool has_estimate() const;
+ DataRate estimate() const;
+
+ private:
+ friend class GoogCcStatePrinter;
+ void Update(DataRate capacity_sample, double alpha);
+
+ double deviation_estimate_kbps() const;
+ absl::optional<double> estimate_kbps_;
+ double deviation_kbps_ = 0.4;
+};
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build
new file mode 100644
index 0000000000..829ff47d87
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator_gn/moz.build
@@ -0,0 +1,221 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/link_capacity_estimator.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("link_capacity_estimator_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc
new file mode 100644
index 0000000000..7524c84d92
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+
+namespace webrtc {
+namespace {
+const char kBweLossBasedControl[] = "WebRTC-Bwe-LossBasedControl";
+
+// Expecting RTCP feedback to be sent with roughly 1s intervals, a 5s gap
+// indicates a channel outage.
+constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
+
+// Increase slower when RTT is high.
+double GetIncreaseFactor(const LossBasedControlConfig& config, TimeDelta rtt) {
+ // Clamp the RTT
+ if (rtt < config.increase_low_rtt) {
+ rtt = config.increase_low_rtt;
+ } else if (rtt > config.increase_high_rtt) {
+ rtt = config.increase_high_rtt;
+ }
+ auto rtt_range = config.increase_high_rtt.Get() - config.increase_low_rtt;
+ if (rtt_range <= TimeDelta::Zero()) {
+ RTC_DCHECK_NOTREACHED(); // Only on misconfiguration.
+ return config.min_increase_factor;
+ }
+ auto rtt_offset = rtt - config.increase_low_rtt;
+ auto relative_offset = std::max(0.0, std::min(rtt_offset / rtt_range, 1.0));
+ auto factor_range = config.max_increase_factor - config.min_increase_factor;
+ return config.min_increase_factor + (1 - relative_offset) * factor_range;
+}
+
+double LossFromBitrate(DataRate bitrate,
+ DataRate loss_bandwidth_balance,
+ double exponent) {
+ if (loss_bandwidth_balance >= bitrate)
+ return 1.0;
+ return pow(loss_bandwidth_balance / bitrate, exponent);
+}
+
+DataRate BitrateFromLoss(double loss,
+ DataRate loss_bandwidth_balance,
+ double exponent) {
+ if (exponent <= 0) {
+ RTC_DCHECK_NOTREACHED();
+ return DataRate::Infinity();
+ }
+ if (loss < 1e-5)
+ return DataRate::Infinity();
+ return loss_bandwidth_balance * pow(loss, -1.0 / exponent);
+}
+
+double ExponentialUpdate(TimeDelta window, TimeDelta interval) {
+ // Use the convention that exponential window length (which is really
+ // infinite) is the time it takes to dampen to 1/e.
+ if (window <= TimeDelta::Zero()) {
+ RTC_DCHECK_NOTREACHED();
+ return 1.0f;
+ }
+ return 1.0f - exp(interval / window * -1.0);
+}
+
+bool IsEnabled(const webrtc::FieldTrialsView& key_value_config,
+ absl::string_view name) {
+ return absl::StartsWith(key_value_config.Lookup(name), "Enabled");
+}
+
+} // namespace
+
+LossBasedControlConfig::LossBasedControlConfig(
+ const FieldTrialsView* key_value_config)
+ : enabled(IsEnabled(*key_value_config, kBweLossBasedControl)),
+ min_increase_factor("min_incr", 1.02),
+ max_increase_factor("max_incr", 1.08),
+ increase_low_rtt("incr_low_rtt", TimeDelta::Millis(200)),
+ increase_high_rtt("incr_high_rtt", TimeDelta::Millis(800)),
+ decrease_factor("decr", 0.99),
+ loss_window("loss_win", TimeDelta::Millis(800)),
+ loss_max_window("loss_max_win", TimeDelta::Millis(800)),
+ acknowledged_rate_max_window("ackrate_max_win", TimeDelta::Millis(800)),
+ increase_offset("incr_offset", DataRate::BitsPerSec(1000)),
+ loss_bandwidth_balance_increase("balance_incr",
+ DataRate::KilobitsPerSec(0.5)),
+ loss_bandwidth_balance_decrease("balance_decr",
+ DataRate::KilobitsPerSec(4)),
+ loss_bandwidth_balance_reset("balance_reset",
+ DataRate::KilobitsPerSec(0.1)),
+ loss_bandwidth_balance_exponent("exponent", 0.5),
+ allow_resets("resets", false),
+ decrease_interval("decr_intvl", TimeDelta::Millis(300)),
+ loss_report_timeout("timeout", TimeDelta::Millis(6000)) {
+ ParseFieldTrial(
+ {&min_increase_factor, &max_increase_factor, &increase_low_rtt,
+ &increase_high_rtt, &decrease_factor, &loss_window, &loss_max_window,
+ &acknowledged_rate_max_window, &increase_offset,
+ &loss_bandwidth_balance_increase, &loss_bandwidth_balance_decrease,
+ &loss_bandwidth_balance_reset, &loss_bandwidth_balance_exponent,
+ &allow_resets, &decrease_interval, &loss_report_timeout},
+ key_value_config->Lookup(kBweLossBasedControl));
+}
+LossBasedControlConfig::LossBasedControlConfig(const LossBasedControlConfig&) =
+ default;
+LossBasedControlConfig::~LossBasedControlConfig() = default;
+
+LossBasedBandwidthEstimation::LossBasedBandwidthEstimation(
+ const FieldTrialsView* key_value_config)
+ : config_(key_value_config),
+ average_loss_(0),
+ average_loss_max_(0),
+ loss_based_bitrate_(DataRate::Zero()),
+ acknowledged_bitrate_max_(DataRate::Zero()),
+ acknowledged_bitrate_last_update_(Timestamp::MinusInfinity()),
+ time_last_decrease_(Timestamp::MinusInfinity()),
+ has_decreased_since_last_loss_report_(false),
+ last_loss_packet_report_(Timestamp::MinusInfinity()),
+ last_loss_ratio_(0) {}
+
+void LossBasedBandwidthEstimation::UpdateLossStatistics(
+ const std::vector<PacketResult>& packet_results,
+ Timestamp at_time) {
+ if (packet_results.empty()) {
+ RTC_DCHECK_NOTREACHED();
+ return;
+ }
+ int loss_count = 0;
+ for (const auto& pkt : packet_results) {
+ loss_count += !pkt.IsReceived() ? 1 : 0;
+ }
+ last_loss_ratio_ = static_cast<double>(loss_count) / packet_results.size();
+ const TimeDelta time_passed = last_loss_packet_report_.IsFinite()
+ ? at_time - last_loss_packet_report_
+ : TimeDelta::Seconds(1);
+ last_loss_packet_report_ = at_time;
+ has_decreased_since_last_loss_report_ = false;
+
+ average_loss_ += ExponentialUpdate(config_.loss_window, time_passed) *
+ (last_loss_ratio_ - average_loss_);
+ if (average_loss_ > average_loss_max_) {
+ average_loss_max_ = average_loss_;
+ } else {
+ average_loss_max_ +=
+ ExponentialUpdate(config_.loss_max_window, time_passed) *
+ (average_loss_ - average_loss_max_);
+ }
+}
+
+void LossBasedBandwidthEstimation::UpdateAcknowledgedBitrate(
+ DataRate acknowledged_bitrate,
+ Timestamp at_time) {
+ const TimeDelta time_passed =
+ acknowledged_bitrate_last_update_.IsFinite()
+ ? at_time - acknowledged_bitrate_last_update_
+ : TimeDelta::Seconds(1);
+ acknowledged_bitrate_last_update_ = at_time;
+ if (acknowledged_bitrate > acknowledged_bitrate_max_) {
+ acknowledged_bitrate_max_ = acknowledged_bitrate;
+ } else {
+ acknowledged_bitrate_max_ -=
+ ExponentialUpdate(config_.acknowledged_rate_max_window, time_passed) *
+ (acknowledged_bitrate_max_ - acknowledged_bitrate);
+ }
+}
+
+DataRate LossBasedBandwidthEstimation::Update(Timestamp at_time,
+ DataRate min_bitrate,
+ DataRate wanted_bitrate,
+ TimeDelta last_round_trip_time) {
+ if (loss_based_bitrate_.IsZero()) {
+ loss_based_bitrate_ = wanted_bitrate;
+ }
+
+ // Only increase if loss has been low for some time.
+ const double loss_estimate_for_increase = average_loss_max_;
+ // Avoid multiple decreases from averaging over one loss spike.
+ const double loss_estimate_for_decrease =
+ std::min(average_loss_, last_loss_ratio_);
+ const bool allow_decrease =
+ !has_decreased_since_last_loss_report_ &&
+ (at_time - time_last_decrease_ >=
+ last_round_trip_time + config_.decrease_interval);
+ // If packet lost reports are too old, dont increase bitrate.
+ const bool loss_report_valid =
+ at_time - last_loss_packet_report_ < 1.2 * kMaxRtcpFeedbackInterval;
+
+ if (loss_report_valid && config_.allow_resets &&
+ loss_estimate_for_increase < loss_reset_threshold()) {
+ loss_based_bitrate_ = wanted_bitrate;
+ } else if (loss_report_valid &&
+ loss_estimate_for_increase < loss_increase_threshold()) {
+ // Increase bitrate by RTT-adaptive ratio.
+ DataRate new_increased_bitrate =
+ min_bitrate * GetIncreaseFactor(config_, last_round_trip_time) +
+ config_.increase_offset;
+ // The bitrate that would make the loss "just high enough".
+ const DataRate new_increased_bitrate_cap = BitrateFromLoss(
+ loss_estimate_for_increase, config_.loss_bandwidth_balance_increase,
+ config_.loss_bandwidth_balance_exponent);
+ new_increased_bitrate =
+ std::min(new_increased_bitrate, new_increased_bitrate_cap);
+ loss_based_bitrate_ = std::max(new_increased_bitrate, loss_based_bitrate_);
+ } else if (loss_estimate_for_decrease > loss_decrease_threshold() &&
+ allow_decrease) {
+ // The bitrate that would make the loss "just acceptable".
+ const DataRate new_decreased_bitrate_floor = BitrateFromLoss(
+ loss_estimate_for_decrease, config_.loss_bandwidth_balance_decrease,
+ config_.loss_bandwidth_balance_exponent);
+ DataRate new_decreased_bitrate =
+ std::max(decreased_bitrate(), new_decreased_bitrate_floor);
+ if (new_decreased_bitrate < loss_based_bitrate_) {
+ time_last_decrease_ = at_time;
+ has_decreased_since_last_loss_report_ = true;
+ loss_based_bitrate_ = new_decreased_bitrate;
+ }
+ }
+ return loss_based_bitrate_;
+}
+
+void LossBasedBandwidthEstimation::Initialize(DataRate bitrate) {
+ loss_based_bitrate_ = bitrate;
+ average_loss_ = 0;
+ average_loss_max_ = 0;
+}
+
+double LossBasedBandwidthEstimation::loss_reset_threshold() const {
+ return LossFromBitrate(loss_based_bitrate_,
+ config_.loss_bandwidth_balance_reset,
+ config_.loss_bandwidth_balance_exponent);
+}
+
+double LossBasedBandwidthEstimation::loss_increase_threshold() const {
+ return LossFromBitrate(loss_based_bitrate_,
+ config_.loss_bandwidth_balance_increase,
+ config_.loss_bandwidth_balance_exponent);
+}
+
+double LossBasedBandwidthEstimation::loss_decrease_threshold() const {
+ return LossFromBitrate(loss_based_bitrate_,
+ config_.loss_bandwidth_balance_decrease,
+ config_.loss_bandwidth_balance_exponent);
+}
+
+DataRate LossBasedBandwidthEstimation::decreased_bitrate() const {
+ return config_.decrease_factor * acknowledged_bitrate_max_;
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h
new file mode 100644
index 0000000000..9f69caba89
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_
+
+#include <vector>
+
+#include "api/field_trials_view.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+struct LossBasedControlConfig {
+ explicit LossBasedControlConfig(const FieldTrialsView* key_value_config);
+ LossBasedControlConfig(const LossBasedControlConfig&);
+ LossBasedControlConfig& operator=(const LossBasedControlConfig&) = default;
+ ~LossBasedControlConfig();
+ bool enabled;
+ FieldTrialParameter<double> min_increase_factor;
+ FieldTrialParameter<double> max_increase_factor;
+ FieldTrialParameter<TimeDelta> increase_low_rtt;
+ FieldTrialParameter<TimeDelta> increase_high_rtt;
+ FieldTrialParameter<double> decrease_factor;
+ FieldTrialParameter<TimeDelta> loss_window;
+ FieldTrialParameter<TimeDelta> loss_max_window;
+ FieldTrialParameter<TimeDelta> acknowledged_rate_max_window;
+ FieldTrialParameter<DataRate> increase_offset;
+ FieldTrialParameter<DataRate> loss_bandwidth_balance_increase;
+ FieldTrialParameter<DataRate> loss_bandwidth_balance_decrease;
+ FieldTrialParameter<DataRate> loss_bandwidth_balance_reset;
+ FieldTrialParameter<double> loss_bandwidth_balance_exponent;
+ FieldTrialParameter<bool> allow_resets;
+ FieldTrialParameter<TimeDelta> decrease_interval;
+ FieldTrialParameter<TimeDelta> loss_report_timeout;
+};
+
+// Estimates an upper BWE limit based on loss.
+// It requires knowledge about lost packets and acknowledged bitrate.
+// Ie, this class require transport feedback.
+class LossBasedBandwidthEstimation {
+ public:
+ explicit LossBasedBandwidthEstimation(
+ const FieldTrialsView* key_value_config);
+ // Returns the new estimate.
+ DataRate Update(Timestamp at_time,
+ DataRate min_bitrate,
+ DataRate wanted_bitrate,
+ TimeDelta last_round_trip_time);
+ void UpdateAcknowledgedBitrate(DataRate acknowledged_bitrate,
+ Timestamp at_time);
+ void Initialize(DataRate bitrate);
+ bool Enabled() const { return config_.enabled; }
+ // Returns true if LossBasedBandwidthEstimation is enabled and have
+ // received loss statistics. Ie, this class require transport feedback.
+ bool InUse() const {
+ return Enabled() && last_loss_packet_report_.IsFinite();
+ }
+ void UpdateLossStatistics(const std::vector<PacketResult>& packet_results,
+ Timestamp at_time);
+ DataRate GetEstimate() const { return loss_based_bitrate_; }
+
+ private:
+ friend class GoogCcStatePrinter;
+ void Reset(DataRate bitrate);
+ double loss_increase_threshold() const;
+ double loss_decrease_threshold() const;
+ double loss_reset_threshold() const;
+
+ DataRate decreased_bitrate() const;
+
+ const LossBasedControlConfig config_;
+ double average_loss_;
+ double average_loss_max_;
+ DataRate loss_based_bitrate_;
+ DataRate acknowledged_bitrate_max_;
+ Timestamp acknowledged_bitrate_last_update_;
+ Timestamp time_last_decrease_;
+ bool has_decreased_since_last_loss_report_;
+ Timestamp last_loss_packet_report_;
+ double last_loss_ratio_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build
new file mode 100644
index 0000000000..aae4b82520
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v1_gn/moz.build
@@ -0,0 +1,226 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("loss_based_bwe_v1_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
new file mode 100644
index 0000000000..b6efdeee9e
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2021 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
+
+#include <algorithm>
+#include <cmath>
+#include <complex>
+#include <cstddef>
+#include <cstdlib>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/types/optional.h"
+#include "api/array_view.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
+#include "rtc_base/experiments/field_trial_list.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+bool IsValid(DataRate datarate) {
+ return datarate.IsFinite();
+}
+
+bool IsValid(absl::optional<DataRate> datarate) {
+ return datarate.has_value() && IsValid(datarate.value());
+}
+
+bool IsValid(Timestamp timestamp) {
+ return timestamp.IsFinite();
+}
+
+struct PacketResultsSummary {
+ int num_packets = 0;
+ int num_lost_packets = 0;
+ DataSize total_size = DataSize::Zero();
+ Timestamp first_send_time = Timestamp::PlusInfinity();
+ Timestamp last_send_time = Timestamp::MinusInfinity();
+};
+
+// Returns a `PacketResultsSummary` where `first_send_time` is `PlusInfinity,
+// and `last_send_time` is `MinusInfinity`, if `packet_results` is empty.
+PacketResultsSummary GetPacketResultsSummary(
+ rtc::ArrayView<const PacketResult> packet_results) {
+ PacketResultsSummary packet_results_summary;
+
+ packet_results_summary.num_packets = packet_results.size();
+ for (const PacketResult& packet : packet_results) {
+ if (!packet.IsReceived()) {
+ packet_results_summary.num_lost_packets++;
+ }
+ packet_results_summary.total_size += packet.sent_packet.size;
+ packet_results_summary.first_send_time = std::min(
+ packet_results_summary.first_send_time, packet.sent_packet.send_time);
+ packet_results_summary.last_send_time = std::max(
+ packet_results_summary.last_send_time, packet.sent_packet.send_time);
+ }
+
+ return packet_results_summary;
+}
+
+double GetLossProbability(double inherent_loss,
+ DataRate loss_limited_bandwidth,
+ DataRate sending_rate) {
+ if (inherent_loss < 0.0 || inherent_loss > 1.0) {
+ RTC_LOG(LS_WARNING) << "The inherent loss must be in [0,1]: "
+ << inherent_loss;
+ inherent_loss = std::min(std::max(inherent_loss, 0.0), 1.0);
+ }
+ if (!sending_rate.IsFinite()) {
+ RTC_LOG(LS_WARNING) << "The sending rate must be finite: "
+ << ToString(sending_rate);
+ }
+ if (!loss_limited_bandwidth.IsFinite()) {
+ RTC_LOG(LS_WARNING) << "The loss limited bandwidth must be finite: "
+ << ToString(loss_limited_bandwidth);
+ }
+
+ double loss_probability = inherent_loss;
+ if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) &&
+ (sending_rate > loss_limited_bandwidth)) {
+ loss_probability += (1 - inherent_loss) *
+ (sending_rate - loss_limited_bandwidth) / sending_rate;
+ }
+ return std::min(std::max(loss_probability, 1.0e-6), 1.0 - 1.0e-6);
+}
+
+} // namespace
+
+LossBasedBweV2::LossBasedBweV2(const FieldTrialsView* key_value_config)
+ : config_(CreateConfig(key_value_config)) {
+ if (!config_.has_value()) {
+ RTC_LOG(LS_VERBOSE) << "The configuration does not specify that the "
+ "estimator should be enabled, disabling it.";
+ return;
+ }
+ if (!IsConfigValid()) {
+ RTC_LOG(LS_WARNING)
+ << "The configuration is not valid, disabling the estimator.";
+ config_.reset();
+ return;
+ }
+
+ current_estimate_.inherent_loss = config_->initial_inherent_loss_estimate;
+ observations_.resize(config_->observation_window_size);
+ temporal_weights_.resize(config_->observation_window_size);
+ instant_upper_bound_temporal_weights_.resize(
+ config_->observation_window_size);
+ CalculateTemporalWeights();
+}
+
+bool LossBasedBweV2::IsEnabled() const {
+ return config_.has_value();
+}
+
+bool LossBasedBweV2::IsReady() const {
+ return IsEnabled() && IsValid(current_estimate_.loss_limited_bandwidth) &&
+ num_observations_ > 0;
+}
+
+LossBasedBweV2::Result LossBasedBweV2::GetLossBasedResult() const {
+ Result result;
+ result.state = current_state_;
+ if (!IsReady()) {
+ if (!IsEnabled()) {
+ RTC_LOG(LS_WARNING)
+ << "The estimator must be enabled before it can be used.";
+ } else {
+ if (!IsValid(current_estimate_.loss_limited_bandwidth)) {
+ RTC_LOG(LS_WARNING)
+ << "The estimator must be initialized before it can be used.";
+ }
+ if (num_observations_ <= 0) {
+ RTC_LOG(LS_WARNING) << "The estimator must receive enough loss "
+ "statistics before it can be used.";
+ }
+ }
+ result.bandwidth_estimate = IsValid(delay_based_estimate_)
+ ? delay_based_estimate_
+ : DataRate::PlusInfinity();
+ return result;
+ }
+
+ if (IsValid(delay_based_estimate_)) {
+ result.bandwidth_estimate =
+ std::min({current_estimate_.loss_limited_bandwidth,
+ GetInstantUpperBound(), delay_based_estimate_});
+ } else {
+ result.bandwidth_estimate = std::min(
+ current_estimate_.loss_limited_bandwidth, GetInstantUpperBound());
+ }
+ return result;
+}
+
+void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) {
+ if (IsValid(acknowledged_bitrate)) {
+ acknowledged_bitrate_ = acknowledged_bitrate;
+ } else {
+ RTC_LOG(LS_WARNING) << "The acknowledged bitrate must be finite: "
+ << ToString(acknowledged_bitrate);
+ }
+}
+
+void LossBasedBweV2::SetBandwidthEstimate(DataRate bandwidth_estimate) {
+ if (IsValid(bandwidth_estimate)) {
+ current_estimate_.loss_limited_bandwidth = bandwidth_estimate;
+ } else {
+ RTC_LOG(LS_WARNING) << "The bandwidth estimate must be finite: "
+ << ToString(bandwidth_estimate);
+ }
+}
+
+void LossBasedBweV2::SetMinMaxBitrate(DataRate min_bitrate,
+ DataRate max_bitrate) {
+ if (IsValid(min_bitrate)) {
+ min_bitrate_ = min_bitrate;
+ } else {
+ RTC_LOG(LS_WARNING) << "The min bitrate must be finite: "
+ << ToString(min_bitrate);
+ }
+
+ if (IsValid(max_bitrate)) {
+ max_bitrate_ = max_bitrate;
+ } else {
+ RTC_LOG(LS_WARNING) << "The max bitrate must be finite: "
+ << ToString(max_bitrate);
+ }
+}
+
+void LossBasedBweV2::SetProbeBitrate(absl::optional<DataRate> probe_bitrate) {
+ if (probe_bitrate.has_value() && IsValid(probe_bitrate.value())) {
+ if (!IsValid(probe_bitrate_) || probe_bitrate_ > probe_bitrate.value()) {
+ probe_bitrate_ = probe_bitrate.value();
+ }
+ }
+}
+
+void LossBasedBweV2::UpdateBandwidthEstimate(
+ rtc::ArrayView<const PacketResult> packet_results,
+ DataRate delay_based_estimate,
+ BandwidthUsage delay_detector_state,
+ absl::optional<DataRate> probe_bitrate,
+ DataRate upper_link_capacity) {
+ delay_based_estimate_ = delay_based_estimate;
+ upper_link_capacity_ = upper_link_capacity;
+ if (!IsEnabled()) {
+ RTC_LOG(LS_WARNING)
+ << "The estimator must be enabled before it can be used.";
+ return;
+ }
+ SetProbeBitrate(probe_bitrate);
+ if (packet_results.empty()) {
+ RTC_LOG(LS_VERBOSE)
+ << "The estimate cannot be updated without any loss statistics.";
+ return;
+ }
+
+ if (!PushBackObservation(packet_results, delay_detector_state)) {
+ return;
+ }
+
+ if (!IsValid(current_estimate_.loss_limited_bandwidth)) {
+ RTC_LOG(LS_VERBOSE)
+ << "The estimator must be initialized before it can be used.";
+ return;
+ }
+
+ ChannelParameters best_candidate = current_estimate_;
+ double objective_max = std::numeric_limits<double>::lowest();
+ for (ChannelParameters candidate : GetCandidates()) {
+ NewtonsMethodUpdate(candidate);
+
+ const double candidate_objective = GetObjective(candidate);
+ if (candidate_objective > objective_max) {
+ objective_max = candidate_objective;
+ best_candidate = candidate;
+ }
+ }
+ if (best_candidate.loss_limited_bandwidth <
+ current_estimate_.loss_limited_bandwidth) {
+ last_time_estimate_reduced_ = last_send_time_most_recent_observation_;
+ }
+
+ // Do not increase the estimate if the average loss is greater than current
+ // inherent loss.
+ if (GetAverageReportedLossRatio() > best_candidate.inherent_loss &&
+ config_->not_increase_if_inherent_loss_less_than_average_loss &&
+ current_estimate_.loss_limited_bandwidth <
+ best_candidate.loss_limited_bandwidth) {
+ best_candidate.loss_limited_bandwidth =
+ current_estimate_.loss_limited_bandwidth;
+ }
+
+ if (IsBandwidthLimitedDueToLoss()) {
+ // Bound the estimate increase if:
+ // 1. The estimate has been increased for less than
+ // `delayed_increase_window` ago, and
+ // 2. The best candidate is greater than bandwidth_limit_in_current_window.
+ if (recovering_after_loss_timestamp_.IsFinite() &&
+ recovering_after_loss_timestamp_ + config_->delayed_increase_window >
+ last_send_time_most_recent_observation_ &&
+ best_candidate.loss_limited_bandwidth >
+ bandwidth_limit_in_current_window_) {
+ best_candidate.loss_limited_bandwidth =
+ bandwidth_limit_in_current_window_;
+ }
+
+ bool increasing_when_loss_limited =
+ IsEstimateIncreasingWhenLossLimited(best_candidate);
+ // Bound the best candidate by the acked bitrate unless there is a recent
+ // probe result.
+ if (increasing_when_loss_limited && !IsValid(probe_bitrate_) &&
+ IsValid(acknowledged_bitrate_)) {
+ best_candidate.loss_limited_bandwidth =
+ IsValid(best_candidate.loss_limited_bandwidth)
+ ? std::min(best_candidate.loss_limited_bandwidth,
+ config_->bandwidth_rampup_upper_bound_factor *
+ (*acknowledged_bitrate_))
+ : config_->bandwidth_rampup_upper_bound_factor *
+ (*acknowledged_bitrate_);
+ }
+
+ // Use probe bitrate as the estimate as probe bitrate is trusted to be
+ // correct. After being used, the probe bitrate is reset.
+ if (config_->probe_integration_enabled && IsValid(probe_bitrate_)) {
+ best_candidate.loss_limited_bandwidth =
+ std::min(probe_bitrate_, best_candidate.loss_limited_bandwidth);
+ probe_bitrate_ = DataRate::MinusInfinity();
+ }
+ }
+
+ if (IsEstimateIncreasingWhenLossLimited(best_candidate) &&
+ best_candidate.loss_limited_bandwidth < delay_based_estimate) {
+ current_state_ = LossBasedState::kIncreasing;
+ } else if (best_candidate.loss_limited_bandwidth < delay_based_estimate_) {
+ current_state_ = LossBasedState::kDecreasing;
+ } else if (best_candidate.loss_limited_bandwidth >= delay_based_estimate_) {
+ current_state_ = LossBasedState::kDelayBasedEstimate;
+ }
+ current_estimate_ = best_candidate;
+
+ if (IsBandwidthLimitedDueToLoss() &&
+ (recovering_after_loss_timestamp_.IsInfinite() ||
+ recovering_after_loss_timestamp_ + config_->delayed_increase_window <
+ last_send_time_most_recent_observation_)) {
+ bandwidth_limit_in_current_window_ =
+ std::max(kCongestionControllerMinBitrate,
+ current_estimate_.loss_limited_bandwidth *
+ config_->max_increase_factor);
+ recovering_after_loss_timestamp_ = last_send_time_most_recent_observation_;
+ }
+}
+
+bool LossBasedBweV2::IsEstimateIncreasingWhenLossLimited(
+ const ChannelParameters& best_candidate) {
+ return (current_estimate_.loss_limited_bandwidth <
+ best_candidate.loss_limited_bandwidth ||
+ (current_estimate_.loss_limited_bandwidth ==
+ best_candidate.loss_limited_bandwidth &&
+ current_state_ == LossBasedState::kIncreasing)) &&
+ IsBandwidthLimitedDueToLoss();
+}
+
+// Returns a `LossBasedBweV2::Config` iff the `key_value_config` specifies a
+// configuration for the `LossBasedBweV2` which is explicitly enabled.
+absl::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig(
+ const FieldTrialsView* key_value_config) {
+ FieldTrialParameter<bool> enabled("Enabled", true);
+ FieldTrialParameter<double> bandwidth_rampup_upper_bound_factor(
+ "BwRampupUpperBoundFactor", 1000000.0);
+ FieldTrialParameter<double> rampup_acceleration_max_factor(
+ "BwRampupAccelMaxFactor", 0.0);
+ FieldTrialParameter<TimeDelta> rampup_acceleration_maxout_time(
+ "BwRampupAccelMaxoutTime", TimeDelta::Seconds(60));
+ FieldTrialList<double> candidate_factors("CandidateFactors",
+ {1.02, 1.0, 0.95});
+ FieldTrialParameter<double> higher_bandwidth_bias_factor("HigherBwBiasFactor",
+ 0.0002);
+ FieldTrialParameter<double> higher_log_bandwidth_bias_factor(
+ "HigherLogBwBiasFactor", 0.02);
+ FieldTrialParameter<double> inherent_loss_lower_bound(
+ "InherentLossLowerBound", 1.0e-3);
+ FieldTrialParameter<double> loss_threshold_of_high_bandwidth_preference(
+ "LossThresholdOfHighBandwidthPreference", 0.15);
+ FieldTrialParameter<double> bandwidth_preference_smoothing_factor(
+ "BandwidthPreferenceSmoothingFactor", 0.002);
+ FieldTrialParameter<DataRate> inherent_loss_upper_bound_bandwidth_balance(
+ "InherentLossUpperBoundBwBalance", DataRate::KilobitsPerSec(75.0));
+ FieldTrialParameter<double> inherent_loss_upper_bound_offset(
+ "InherentLossUpperBoundOffset", 0.05);
+ FieldTrialParameter<double> initial_inherent_loss_estimate(
+ "InitialInherentLossEstimate", 0.01);
+ FieldTrialParameter<int> newton_iterations("NewtonIterations", 1);
+ FieldTrialParameter<double> newton_step_size("NewtonStepSize", 0.75);
+ FieldTrialParameter<bool> append_acknowledged_rate_candidate(
+ "AckedRateCandidate", true);
+ FieldTrialParameter<bool> append_delay_based_estimate_candidate(
+ "DelayBasedCandidate", true);
+ FieldTrialParameter<TimeDelta> observation_duration_lower_bound(
+ "ObservationDurationLowerBound", TimeDelta::Millis(250));
+ FieldTrialParameter<int> observation_window_size("ObservationWindowSize", 20);
+ FieldTrialParameter<double> sending_rate_smoothing_factor(
+ "SendingRateSmoothingFactor", 0.0);
+ FieldTrialParameter<double> instant_upper_bound_temporal_weight_factor(
+ "InstantUpperBoundTemporalWeightFactor", 0.9);
+ FieldTrialParameter<DataRate> instant_upper_bound_bandwidth_balance(
+ "InstantUpperBoundBwBalance", DataRate::KilobitsPerSec(75.0));
+ FieldTrialParameter<double> instant_upper_bound_loss_offset(
+ "InstantUpperBoundLossOffset", 0.05);
+ FieldTrialParameter<double> temporal_weight_factor("TemporalWeightFactor",
+ 0.9);
+ FieldTrialParameter<double> bandwidth_backoff_lower_bound_factor(
+ "BwBackoffLowerBoundFactor", 1.0);
+ FieldTrialParameter<bool> trendline_integration_enabled(
+ "TrendlineIntegrationEnabled", false);
+ FieldTrialParameter<int> trendline_observations_window_size(
+ "TrendlineObservationsWindowSize", 20);
+ FieldTrialParameter<double> max_increase_factor("MaxIncreaseFactor", 1.3);
+ FieldTrialParameter<TimeDelta> delayed_increase_window(
+ "DelayedIncreaseWindow", TimeDelta::Millis(300));
+ FieldTrialParameter<bool> use_acked_bitrate_only_when_overusing(
+ "UseAckedBitrateOnlyWhenOverusing", false);
+ FieldTrialParameter<bool>
+ not_increase_if_inherent_loss_less_than_average_loss(
+ "NotIncreaseIfInherentLossLessThanAverageLoss", true);
+ FieldTrialParameter<double> high_loss_rate_threshold("HighLossRateThreshold",
+ 1.0);
+ FieldTrialParameter<DataRate> bandwidth_cap_at_high_loss_rate(
+ "BandwidthCapAtHighLossRate", DataRate::KilobitsPerSec(500.0));
+ FieldTrialParameter<double> slope_of_bwe_high_loss_func(
+ "SlopeOfBweHighLossFunc", 1000);
+ FieldTrialParameter<bool> probe_integration_enabled("ProbeIntegrationEnabled",
+ false);
+ FieldTrialParameter<bool> bound_by_upper_link_capacity_when_loss_limited(
+ "BoundByUpperLinkCapacityWhenLossLimited", true);
+ if (key_value_config) {
+ ParseFieldTrial({&enabled,
+ &bandwidth_rampup_upper_bound_factor,
+ &rampup_acceleration_max_factor,
+ &rampup_acceleration_maxout_time,
+ &candidate_factors,
+ &higher_bandwidth_bias_factor,
+ &higher_log_bandwidth_bias_factor,
+ &inherent_loss_lower_bound,
+ &loss_threshold_of_high_bandwidth_preference,
+ &bandwidth_preference_smoothing_factor,
+ &inherent_loss_upper_bound_bandwidth_balance,
+ &inherent_loss_upper_bound_offset,
+ &initial_inherent_loss_estimate,
+ &newton_iterations,
+ &newton_step_size,
+ &append_acknowledged_rate_candidate,
+ &append_delay_based_estimate_candidate,
+ &observation_duration_lower_bound,
+ &observation_window_size,
+ &sending_rate_smoothing_factor,
+ &instant_upper_bound_temporal_weight_factor,
+ &instant_upper_bound_bandwidth_balance,
+ &instant_upper_bound_loss_offset,
+ &temporal_weight_factor,
+ &bandwidth_backoff_lower_bound_factor,
+ &trendline_integration_enabled,
+ &trendline_observations_window_size,
+ &max_increase_factor,
+ &delayed_increase_window,
+ &use_acked_bitrate_only_when_overusing,
+ &not_increase_if_inherent_loss_less_than_average_loss,
+ &probe_integration_enabled,
+ &high_loss_rate_threshold,
+ &bandwidth_cap_at_high_loss_rate,
+ &slope_of_bwe_high_loss_func,
+ &bound_by_upper_link_capacity_when_loss_limited},
+ key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2"));
+ }
+
+ absl::optional<Config> config;
+ if (!enabled.Get()) {
+ return config;
+ }
+ config.emplace(Config());
+ config->bandwidth_rampup_upper_bound_factor =
+ bandwidth_rampup_upper_bound_factor.Get();
+ config->rampup_acceleration_max_factor = rampup_acceleration_max_factor.Get();
+ config->rampup_acceleration_maxout_time =
+ rampup_acceleration_maxout_time.Get();
+ config->candidate_factors = candidate_factors.Get();
+ config->higher_bandwidth_bias_factor = higher_bandwidth_bias_factor.Get();
+ config->higher_log_bandwidth_bias_factor =
+ higher_log_bandwidth_bias_factor.Get();
+ config->inherent_loss_lower_bound = inherent_loss_lower_bound.Get();
+ config->loss_threshold_of_high_bandwidth_preference =
+ loss_threshold_of_high_bandwidth_preference.Get();
+ config->bandwidth_preference_smoothing_factor =
+ bandwidth_preference_smoothing_factor.Get();
+ config->inherent_loss_upper_bound_bandwidth_balance =
+ inherent_loss_upper_bound_bandwidth_balance.Get();
+ config->inherent_loss_upper_bound_offset =
+ inherent_loss_upper_bound_offset.Get();
+ config->initial_inherent_loss_estimate = initial_inherent_loss_estimate.Get();
+ config->newton_iterations = newton_iterations.Get();
+ config->newton_step_size = newton_step_size.Get();
+ config->append_acknowledged_rate_candidate =
+ append_acknowledged_rate_candidate.Get();
+ config->append_delay_based_estimate_candidate =
+ append_delay_based_estimate_candidate.Get();
+ config->observation_duration_lower_bound =
+ observation_duration_lower_bound.Get();
+ config->observation_window_size = observation_window_size.Get();
+ config->sending_rate_smoothing_factor = sending_rate_smoothing_factor.Get();
+ config->instant_upper_bound_temporal_weight_factor =
+ instant_upper_bound_temporal_weight_factor.Get();
+ config->instant_upper_bound_bandwidth_balance =
+ instant_upper_bound_bandwidth_balance.Get();
+ config->instant_upper_bound_loss_offset =
+ instant_upper_bound_loss_offset.Get();
+ config->temporal_weight_factor = temporal_weight_factor.Get();
+ config->bandwidth_backoff_lower_bound_factor =
+ bandwidth_backoff_lower_bound_factor.Get();
+ config->trendline_integration_enabled = trendline_integration_enabled.Get();
+ config->trendline_observations_window_size =
+ trendline_observations_window_size.Get();
+ config->max_increase_factor = max_increase_factor.Get();
+ config->delayed_increase_window = delayed_increase_window.Get();
+ config->use_acked_bitrate_only_when_overusing =
+ use_acked_bitrate_only_when_overusing.Get();
+ config->not_increase_if_inherent_loss_less_than_average_loss =
+ not_increase_if_inherent_loss_less_than_average_loss.Get();
+ config->high_loss_rate_threshold = high_loss_rate_threshold.Get();
+ config->bandwidth_cap_at_high_loss_rate =
+ bandwidth_cap_at_high_loss_rate.Get();
+ config->slope_of_bwe_high_loss_func = slope_of_bwe_high_loss_func.Get();
+ config->probe_integration_enabled = probe_integration_enabled.Get();
+ config->bound_by_upper_link_capacity_when_loss_limited =
+ bound_by_upper_link_capacity_when_loss_limited.Get();
+
+ return config;
+}
+
+bool LossBasedBweV2::IsConfigValid() const {
+ if (!config_.has_value()) {
+ return false;
+ }
+
+ bool valid = true;
+
+ if (config_->bandwidth_rampup_upper_bound_factor <= 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The bandwidth rampup upper bound factor must be greater than 1: "
+ << config_->bandwidth_rampup_upper_bound_factor;
+ valid = false;
+ }
+ if (config_->rampup_acceleration_max_factor < 0.0) {
+ RTC_LOG(LS_WARNING)
+ << "The rampup acceleration max factor must be non-negative.: "
+ << config_->rampup_acceleration_max_factor;
+ valid = false;
+ }
+ if (config_->rampup_acceleration_maxout_time <= TimeDelta::Zero()) {
+ RTC_LOG(LS_WARNING)
+ << "The rampup acceleration maxout time must be above zero: "
+ << config_->rampup_acceleration_maxout_time.seconds();
+ valid = false;
+ }
+ for (double candidate_factor : config_->candidate_factors) {
+ if (candidate_factor <= 0.0) {
+ RTC_LOG(LS_WARNING) << "All candidate factors must be greater than zero: "
+ << candidate_factor;
+ valid = false;
+ }
+ }
+
+ // Ensure that the configuration allows generation of at least one candidate
+ // other than the current estimate.
+ if (!config_->append_acknowledged_rate_candidate &&
+ !config_->append_delay_based_estimate_candidate &&
+ !absl::c_any_of(config_->candidate_factors,
+ [](double cf) { return cf != 1.0; })) {
+ RTC_LOG(LS_WARNING)
+ << "The configuration does not allow generating candidates. Specify "
+ "a candidate factor other than 1.0, allow the acknowledged rate "
+ "to be a candidate, and/or allow the delay based estimate to be a "
+ "candidate.";
+ valid = false;
+ }
+
+ if (config_->higher_bandwidth_bias_factor < 0.0) {
+ RTC_LOG(LS_WARNING)
+ << "The higher bandwidth bias factor must be non-negative: "
+ << config_->higher_bandwidth_bias_factor;
+ valid = false;
+ }
+ if (config_->inherent_loss_lower_bound < 0.0 ||
+ config_->inherent_loss_lower_bound >= 1.0) {
+ RTC_LOG(LS_WARNING) << "The inherent loss lower bound must be in [0, 1): "
+ << config_->inherent_loss_lower_bound;
+ valid = false;
+ }
+ if (config_->loss_threshold_of_high_bandwidth_preference < 0.0 ||
+ config_->loss_threshold_of_high_bandwidth_preference >= 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The loss threshold of high bandwidth preference must be in [0, 1): "
+ << config_->loss_threshold_of_high_bandwidth_preference;
+ valid = false;
+ }
+ if (config_->bandwidth_preference_smoothing_factor <= 0.0 ||
+ config_->bandwidth_preference_smoothing_factor > 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The bandwidth preference smoothing factor must be in (0, 1]: "
+ << config_->bandwidth_preference_smoothing_factor;
+ valid = false;
+ }
+ if (config_->inherent_loss_upper_bound_bandwidth_balance <=
+ DataRate::Zero()) {
+ RTC_LOG(LS_WARNING)
+ << "The inherent loss upper bound bandwidth balance "
+ "must be positive: "
+ << ToString(config_->inherent_loss_upper_bound_bandwidth_balance);
+ valid = false;
+ }
+ if (config_->inherent_loss_upper_bound_offset <
+ config_->inherent_loss_lower_bound ||
+ config_->inherent_loss_upper_bound_offset >= 1.0) {
+ RTC_LOG(LS_WARNING) << "The inherent loss upper bound must be greater "
+ "than or equal to the inherent "
+ "loss lower bound, which is "
+ << config_->inherent_loss_lower_bound
+ << ", and less than 1: "
+ << config_->inherent_loss_upper_bound_offset;
+ valid = false;
+ }
+ if (config_->initial_inherent_loss_estimate < 0.0 ||
+ config_->initial_inherent_loss_estimate >= 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The initial inherent loss estimate must be in [0, 1): "
+ << config_->initial_inherent_loss_estimate;
+ valid = false;
+ }
+ if (config_->newton_iterations <= 0) {
+ RTC_LOG(LS_WARNING) << "The number of Newton iterations must be positive: "
+ << config_->newton_iterations;
+ valid = false;
+ }
+ if (config_->newton_step_size <= 0.0) {
+ RTC_LOG(LS_WARNING) << "The Newton step size must be positive: "
+ << config_->newton_step_size;
+ valid = false;
+ }
+ if (config_->observation_duration_lower_bound <= TimeDelta::Zero()) {
+ RTC_LOG(LS_WARNING)
+ << "The observation duration lower bound must be positive: "
+ << ToString(config_->observation_duration_lower_bound);
+ valid = false;
+ }
+ if (config_->observation_window_size < 2) {
+ RTC_LOG(LS_WARNING) << "The observation window size must be at least 2: "
+ << config_->observation_window_size;
+ valid = false;
+ }
+ if (config_->sending_rate_smoothing_factor < 0.0 ||
+ config_->sending_rate_smoothing_factor >= 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The sending rate smoothing factor must be in [0, 1): "
+ << config_->sending_rate_smoothing_factor;
+ valid = false;
+ }
+ if (config_->instant_upper_bound_temporal_weight_factor <= 0.0 ||
+ config_->instant_upper_bound_temporal_weight_factor > 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The instant upper bound temporal weight factor must be in (0, 1]"
+ << config_->instant_upper_bound_temporal_weight_factor;
+ valid = false;
+ }
+ if (config_->instant_upper_bound_bandwidth_balance <= DataRate::Zero()) {
+ RTC_LOG(LS_WARNING)
+ << "The instant upper bound bandwidth balance must be positive: "
+ << ToString(config_->instant_upper_bound_bandwidth_balance);
+ valid = false;
+ }
+ if (config_->instant_upper_bound_loss_offset < 0.0 ||
+ config_->instant_upper_bound_loss_offset >= 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The instant upper bound loss offset must be in [0, 1): "
+ << config_->instant_upper_bound_loss_offset;
+ valid = false;
+ }
+ if (config_->temporal_weight_factor <= 0.0 ||
+ config_->temporal_weight_factor > 1.0) {
+ RTC_LOG(LS_WARNING) << "The temporal weight factor must be in (0, 1]: "
+ << config_->temporal_weight_factor;
+ valid = false;
+ }
+ if (config_->bandwidth_backoff_lower_bound_factor > 1.0) {
+ RTC_LOG(LS_WARNING)
+ << "The bandwidth backoff lower bound factor must not be greater than "
+ "1: "
+ << config_->bandwidth_backoff_lower_bound_factor;
+ valid = false;
+ }
+ if (config_->trendline_observations_window_size < 1) {
+ RTC_LOG(LS_WARNING) << "The trendline window size must be at least 1: "
+ << config_->trendline_observations_window_size;
+ valid = false;
+ }
+ if (config_->max_increase_factor <= 0.0) {
+ RTC_LOG(LS_WARNING) << "The maximum increase factor must be positive: "
+ << config_->max_increase_factor;
+ valid = false;
+ }
+ if (config_->delayed_increase_window <= TimeDelta::Zero()) {
+ RTC_LOG(LS_WARNING) << "The delayed increase window must be positive: "
+ << config_->delayed_increase_window.ms();
+ valid = false;
+ }
+ if (config_->high_loss_rate_threshold <= 0.0 ||
+ config_->high_loss_rate_threshold > 1.0) {
+ RTC_LOG(LS_WARNING) << "The high loss rate threshold must be in (0, 1]: "
+ << config_->high_loss_rate_threshold;
+ valid = false;
+ }
+ return valid;
+}
+
+double LossBasedBweV2::GetAverageReportedLossRatio() const {
+ if (num_observations_ <= 0) {
+ return 0.0;
+ }
+
+ double num_packets = 0;
+ double num_lost_packets = 0;
+ for (const Observation& observation : observations_) {
+ if (!observation.IsInitialized()) {
+ continue;
+ }
+
+ double instant_temporal_weight =
+ instant_upper_bound_temporal_weights_[(num_observations_ - 1) -
+ observation.id];
+ num_packets += instant_temporal_weight * observation.num_packets;
+ num_lost_packets += instant_temporal_weight * observation.num_lost_packets;
+ }
+
+ return num_lost_packets / num_packets;
+}
+
+DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const {
+ DataRate candidate_bandwidth_upper_bound = max_bitrate_;
+ if (IsBandwidthLimitedDueToLoss() &&
+ IsValid(bandwidth_limit_in_current_window_)) {
+ candidate_bandwidth_upper_bound = bandwidth_limit_in_current_window_;
+ }
+
+ if (config_->trendline_integration_enabled) {
+ candidate_bandwidth_upper_bound =
+ std::min(GetInstantUpperBound(), candidate_bandwidth_upper_bound);
+ if (IsValid(delay_based_estimate_)) {
+ candidate_bandwidth_upper_bound =
+ std::min(delay_based_estimate_, candidate_bandwidth_upper_bound);
+ }
+ }
+
+ if (!acknowledged_bitrate_.has_value())
+ return candidate_bandwidth_upper_bound;
+
+ if (config_->rampup_acceleration_max_factor > 0.0) {
+ const TimeDelta time_since_bandwidth_reduced = std::min(
+ config_->rampup_acceleration_maxout_time,
+ std::max(TimeDelta::Zero(), last_send_time_most_recent_observation_ -
+ last_time_estimate_reduced_));
+ const double rampup_acceleration = config_->rampup_acceleration_max_factor *
+ time_since_bandwidth_reduced /
+ config_->rampup_acceleration_maxout_time;
+
+ candidate_bandwidth_upper_bound +=
+ rampup_acceleration * (*acknowledged_bitrate_);
+ }
+ return candidate_bandwidth_upper_bound;
+}
+
+std::vector<LossBasedBweV2::ChannelParameters> LossBasedBweV2::GetCandidates()
+ const {
+ std::vector<DataRate> bandwidths;
+ bool can_increase_bitrate = TrendlineEsimateAllowBitrateIncrease();
+ for (double candidate_factor : config_->candidate_factors) {
+ if (!can_increase_bitrate && candidate_factor > 1.0) {
+ continue;
+ }
+ bandwidths.push_back(candidate_factor *
+ current_estimate_.loss_limited_bandwidth);
+ }
+
+ if (acknowledged_bitrate_.has_value() &&
+ config_->append_acknowledged_rate_candidate &&
+ TrendlineEsimateAllowEmergencyBackoff()) {
+ bandwidths.push_back(*acknowledged_bitrate_ *
+ config_->bandwidth_backoff_lower_bound_factor);
+ }
+
+ if (IsValid(delay_based_estimate_) &&
+ config_->append_delay_based_estimate_candidate) {
+ if (can_increase_bitrate &&
+ delay_based_estimate_ > current_estimate_.loss_limited_bandwidth) {
+ bandwidths.push_back(delay_based_estimate_);
+ }
+ }
+
+ const DataRate candidate_bandwidth_upper_bound =
+ GetCandidateBandwidthUpperBound();
+
+ std::vector<ChannelParameters> candidates;
+ candidates.resize(bandwidths.size());
+ for (size_t i = 0; i < bandwidths.size(); ++i) {
+ ChannelParameters candidate = current_estimate_;
+ if (config_->trendline_integration_enabled) {
+ candidate.loss_limited_bandwidth =
+ std::min(bandwidths[i], candidate_bandwidth_upper_bound);
+ } else {
+ candidate.loss_limited_bandwidth = std::min(
+ bandwidths[i], std::max(current_estimate_.loss_limited_bandwidth,
+ candidate_bandwidth_upper_bound));
+ }
+ candidate.inherent_loss = GetFeasibleInherentLoss(candidate);
+ candidates[i] = candidate;
+ }
+ return candidates;
+}
+
+LossBasedBweV2::Derivatives LossBasedBweV2::GetDerivatives(
+ const ChannelParameters& channel_parameters) const {
+ Derivatives derivatives;
+
+ for (const Observation& observation : observations_) {
+ if (!observation.IsInitialized()) {
+ continue;
+ }
+
+ double loss_probability = GetLossProbability(
+ channel_parameters.inherent_loss,
+ channel_parameters.loss_limited_bandwidth, observation.sending_rate);
+
+ double temporal_weight =
+ temporal_weights_[(num_observations_ - 1) - observation.id];
+
+ derivatives.first +=
+ temporal_weight *
+ ((observation.num_lost_packets / loss_probability) -
+ (observation.num_received_packets / (1.0 - loss_probability)));
+ derivatives.second -=
+ temporal_weight *
+ ((observation.num_lost_packets / std::pow(loss_probability, 2)) +
+ (observation.num_received_packets /
+ std::pow(1.0 - loss_probability, 2)));
+ }
+
+ if (derivatives.second >= 0.0) {
+ RTC_LOG(LS_ERROR) << "The second derivative is mathematically guaranteed "
+ "to be negative but is "
+ << derivatives.second << ".";
+ derivatives.second = -1.0e-6;
+ }
+
+ return derivatives;
+}
+
+double LossBasedBweV2::GetFeasibleInherentLoss(
+ const ChannelParameters& channel_parameters) const {
+ return std::min(
+ std::max(channel_parameters.inherent_loss,
+ config_->inherent_loss_lower_bound),
+ GetInherentLossUpperBound(channel_parameters.loss_limited_bandwidth));
+}
+
+double LossBasedBweV2::GetInherentLossUpperBound(DataRate bandwidth) const {
+ if (bandwidth.IsZero()) {
+ return 1.0;
+ }
+
+ double inherent_loss_upper_bound =
+ config_->inherent_loss_upper_bound_offset +
+ config_->inherent_loss_upper_bound_bandwidth_balance / bandwidth;
+ return std::min(inherent_loss_upper_bound, 1.0);
+}
+
+double LossBasedBweV2::AdjustBiasFactor(double loss_rate,
+ double bias_factor) const {
+ return bias_factor *
+ (config_->loss_threshold_of_high_bandwidth_preference - loss_rate) /
+ (config_->bandwidth_preference_smoothing_factor +
+ std::abs(config_->loss_threshold_of_high_bandwidth_preference -
+ loss_rate));
+}
+
+double LossBasedBweV2::GetHighBandwidthBias(DataRate bandwidth) const {
+ if (IsValid(bandwidth)) {
+ const double average_reported_loss_ratio = GetAverageReportedLossRatio();
+ return AdjustBiasFactor(average_reported_loss_ratio,
+ config_->higher_bandwidth_bias_factor) *
+ bandwidth.kbps() +
+ AdjustBiasFactor(average_reported_loss_ratio,
+ config_->higher_log_bandwidth_bias_factor) *
+ std::log(1.0 + bandwidth.kbps());
+ }
+ return 0.0;
+}
+
+double LossBasedBweV2::GetObjective(
+ const ChannelParameters& channel_parameters) const {
+ double objective = 0.0;
+
+ const double high_bandwidth_bias =
+ GetHighBandwidthBias(channel_parameters.loss_limited_bandwidth);
+
+ for (const Observation& observation : observations_) {
+ if (!observation.IsInitialized()) {
+ continue;
+ }
+
+ double loss_probability = GetLossProbability(
+ channel_parameters.inherent_loss,
+ channel_parameters.loss_limited_bandwidth, observation.sending_rate);
+
+ double temporal_weight =
+ temporal_weights_[(num_observations_ - 1) - observation.id];
+
+ objective +=
+ temporal_weight *
+ ((observation.num_lost_packets * std::log(loss_probability)) +
+ (observation.num_received_packets * std::log(1.0 - loss_probability)));
+ objective +=
+ temporal_weight * high_bandwidth_bias * observation.num_packets;
+ }
+
+ return objective;
+}
+
+DataRate LossBasedBweV2::GetSendingRate(
+ DataRate instantaneous_sending_rate) const {
+ if (num_observations_ <= 0) {
+ return instantaneous_sending_rate;
+ }
+
+ const int most_recent_observation_idx =
+ (num_observations_ - 1) % config_->observation_window_size;
+ const Observation& most_recent_observation =
+ observations_[most_recent_observation_idx];
+ DataRate sending_rate_previous_observation =
+ most_recent_observation.sending_rate;
+
+ return config_->sending_rate_smoothing_factor *
+ sending_rate_previous_observation +
+ (1.0 - config_->sending_rate_smoothing_factor) *
+ instantaneous_sending_rate;
+}
+
+DataRate LossBasedBweV2::GetInstantUpperBound() const {
+ return cached_instant_upper_bound_.value_or(max_bitrate_);
+}
+
+void LossBasedBweV2::CalculateInstantUpperBound() {
+ DataRate instant_limit = max_bitrate_;
+ const double average_reported_loss_ratio = GetAverageReportedLossRatio();
+ if (average_reported_loss_ratio > config_->instant_upper_bound_loss_offset) {
+ instant_limit = config_->instant_upper_bound_bandwidth_balance /
+ (average_reported_loss_ratio -
+ config_->instant_upper_bound_loss_offset);
+ if (average_reported_loss_ratio > config_->high_loss_rate_threshold) {
+ instant_limit = std::min(
+ instant_limit, DataRate::KilobitsPerSec(std::max(
+ static_cast<double>(min_bitrate_.kbps()),
+ config_->bandwidth_cap_at_high_loss_rate.kbps() -
+ config_->slope_of_bwe_high_loss_func *
+ average_reported_loss_ratio)));
+ }
+ }
+
+ if (IsBandwidthLimitedDueToLoss()) {
+ if (IsValid(upper_link_capacity_) &&
+ config_->bound_by_upper_link_capacity_when_loss_limited) {
+ instant_limit = std::min(instant_limit, upper_link_capacity_);
+ }
+ }
+ cached_instant_upper_bound_ = instant_limit;
+}
+
+void LossBasedBweV2::CalculateTemporalWeights() {
+ for (int i = 0; i < config_->observation_window_size; ++i) {
+ temporal_weights_[i] = std::pow(config_->temporal_weight_factor, i);
+ instant_upper_bound_temporal_weights_[i] =
+ std::pow(config_->instant_upper_bound_temporal_weight_factor, i);
+ }
+}
+
+void LossBasedBweV2::NewtonsMethodUpdate(
+ ChannelParameters& channel_parameters) const {
+ if (num_observations_ <= 0) {
+ return;
+ }
+
+ for (int i = 0; i < config_->newton_iterations; ++i) {
+ const Derivatives derivatives = GetDerivatives(channel_parameters);
+ channel_parameters.inherent_loss -=
+ config_->newton_step_size * derivatives.first / derivatives.second;
+ channel_parameters.inherent_loss =
+ GetFeasibleInherentLoss(channel_parameters);
+ }
+}
+
+bool LossBasedBweV2::TrendlineEsimateAllowBitrateIncrease() const {
+ if (!config_->trendline_integration_enabled) {
+ return true;
+ }
+
+ for (const auto& detector_state : delay_detector_states_) {
+ if (detector_state == BandwidthUsage::kBwOverusing ||
+ detector_state == BandwidthUsage::kBwUnderusing) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LossBasedBweV2::TrendlineEsimateAllowEmergencyBackoff() const {
+ if (!config_->trendline_integration_enabled) {
+ return true;
+ }
+
+ if (!config_->use_acked_bitrate_only_when_overusing) {
+ return true;
+ }
+
+ for (const auto& detector_state : delay_detector_states_) {
+ if (detector_state == BandwidthUsage::kBwOverusing) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LossBasedBweV2::PushBackObservation(
+ rtc::ArrayView<const PacketResult> packet_results,
+ BandwidthUsage delay_detector_state) {
+ delay_detector_states_.push_front(delay_detector_state);
+ if (static_cast<int>(delay_detector_states_.size()) >
+ config_->trendline_observations_window_size) {
+ delay_detector_states_.pop_back();
+ }
+
+ if (packet_results.empty()) {
+ return false;
+ }
+
+ PacketResultsSummary packet_results_summary =
+ GetPacketResultsSummary(packet_results);
+
+ partial_observation_.num_packets += packet_results_summary.num_packets;
+ partial_observation_.num_lost_packets +=
+ packet_results_summary.num_lost_packets;
+ partial_observation_.size += packet_results_summary.total_size;
+
+ // This is the first packet report we have received.
+ if (!IsValid(last_send_time_most_recent_observation_)) {
+ last_send_time_most_recent_observation_ =
+ packet_results_summary.first_send_time;
+ }
+
+ const Timestamp last_send_time = packet_results_summary.last_send_time;
+ const TimeDelta observation_duration =
+ last_send_time - last_send_time_most_recent_observation_;
+ // Too small to be meaningful.
+ if (observation_duration <= TimeDelta::Zero() ||
+ (observation_duration < config_->observation_duration_lower_bound &&
+ (delay_detector_state != BandwidthUsage::kBwOverusing ||
+ !config_->trendline_integration_enabled))) {
+ return false;
+ }
+
+ last_send_time_most_recent_observation_ = last_send_time;
+
+ Observation observation;
+ observation.num_packets = partial_observation_.num_packets;
+ observation.num_lost_packets = partial_observation_.num_lost_packets;
+ observation.num_received_packets =
+ observation.num_packets - observation.num_lost_packets;
+ observation.sending_rate =
+ GetSendingRate(partial_observation_.size / observation_duration);
+ observation.id = num_observations_++;
+ observations_[observation.id % config_->observation_window_size] =
+ observation;
+
+ partial_observation_ = PartialObservation();
+
+ CalculateInstantUpperBound();
+ return true;
+}
+
+bool LossBasedBweV2::IsBandwidthLimitedDueToLoss() const {
+ return current_state_ != LossBasedState::kDelayBasedEstimate;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
new file mode 100644
index 0000000000..9ff9cb74c6
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2021 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_
+
+#include <cstddef>
+#include <deque>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/array_view.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+
+namespace webrtc {
+
+// State of the loss based estimate, which can be either increasing/decreasing
+// when network is loss limited, or equal to the delay based estimate.
+enum class LossBasedState {
+ kIncreasing = 0,
+ kDecreasing = 1,
+ kDelayBasedEstimate = 2
+};
+
+class LossBasedBweV2 {
+ public:
+ struct Result {
+ ~Result() = default;
+ DataRate bandwidth_estimate = DataRate::Zero();
+ LossBasedState state = LossBasedState::kDelayBasedEstimate;
+ };
+ // Creates a disabled `LossBasedBweV2` if the
+ // `key_value_config` is not valid.
+ explicit LossBasedBweV2(const FieldTrialsView* key_value_config);
+
+ LossBasedBweV2(const LossBasedBweV2&) = delete;
+ LossBasedBweV2& operator=(const LossBasedBweV2&) = delete;
+
+ ~LossBasedBweV2() = default;
+
+ bool IsEnabled() const;
+ // Returns true iff a BWE can be calculated, i.e., the estimator has been
+ // initialized with a BWE and then has received enough `PacketResult`s.
+ bool IsReady() const;
+
+ // Returns `DataRate::PlusInfinity` if no BWE can be calculated.
+ Result GetLossBasedResult() const;
+
+ void SetAcknowledgedBitrate(DataRate acknowledged_bitrate);
+ void SetBandwidthEstimate(DataRate bandwidth_estimate);
+ void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
+ void UpdateBandwidthEstimate(
+ rtc::ArrayView<const PacketResult> packet_results,
+ DataRate delay_based_estimate,
+ BandwidthUsage delay_detector_state,
+ absl::optional<DataRate> probe_bitrate,
+ DataRate upper_link_capacity);
+
+ private:
+ struct ChannelParameters {
+ double inherent_loss = 0.0;
+ DataRate loss_limited_bandwidth = DataRate::MinusInfinity();
+ };
+
+ struct Config {
+ double bandwidth_rampup_upper_bound_factor = 0.0;
+ double rampup_acceleration_max_factor = 0.0;
+ TimeDelta rampup_acceleration_maxout_time = TimeDelta::Zero();
+ std::vector<double> candidate_factors;
+ double higher_bandwidth_bias_factor = 0.0;
+ double higher_log_bandwidth_bias_factor = 0.0;
+ double inherent_loss_lower_bound = 0.0;
+ double loss_threshold_of_high_bandwidth_preference = 0.0;
+ double bandwidth_preference_smoothing_factor = 0.0;
+ DataRate inherent_loss_upper_bound_bandwidth_balance =
+ DataRate::MinusInfinity();
+ double inherent_loss_upper_bound_offset = 0.0;
+ double initial_inherent_loss_estimate = 0.0;
+ int newton_iterations = 0;
+ double newton_step_size = 0.0;
+ bool append_acknowledged_rate_candidate = true;
+ bool append_delay_based_estimate_candidate = false;
+ TimeDelta observation_duration_lower_bound = TimeDelta::Zero();
+ int observation_window_size = 0;
+ double sending_rate_smoothing_factor = 0.0;
+ double instant_upper_bound_temporal_weight_factor = 0.0;
+ DataRate instant_upper_bound_bandwidth_balance = DataRate::MinusInfinity();
+ double instant_upper_bound_loss_offset = 0.0;
+ double temporal_weight_factor = 0.0;
+ double bandwidth_backoff_lower_bound_factor = 0.0;
+ bool trendline_integration_enabled = false;
+ int trendline_observations_window_size = 0;
+ double max_increase_factor = 0.0;
+ TimeDelta delayed_increase_window = TimeDelta::Zero();
+ bool use_acked_bitrate_only_when_overusing = false;
+ bool not_increase_if_inherent_loss_less_than_average_loss = false;
+ double high_loss_rate_threshold = 1.0;
+ DataRate bandwidth_cap_at_high_loss_rate = DataRate::MinusInfinity();
+ double slope_of_bwe_high_loss_func = 1000.0;
+ bool probe_integration_enabled = false;
+ bool bound_by_upper_link_capacity_when_loss_limited = false;
+ };
+
+ struct Derivatives {
+ double first = 0.0;
+ double second = 0.0;
+ };
+
+ struct Observation {
+ bool IsInitialized() const { return id != -1; }
+
+ int num_packets = 0;
+ int num_lost_packets = 0;
+ int num_received_packets = 0;
+ DataRate sending_rate = DataRate::MinusInfinity();
+ int id = -1;
+ };
+
+ struct PartialObservation {
+ int num_packets = 0;
+ int num_lost_packets = 0;
+ DataSize size = DataSize::Zero();
+ };
+
+ static absl::optional<Config> CreateConfig(
+ const FieldTrialsView* key_value_config);
+ bool IsConfigValid() const;
+
+ // Returns `0.0` if not enough loss statistics have been received.
+ double GetAverageReportedLossRatio() const;
+ std::vector<ChannelParameters> GetCandidates() const;
+ DataRate GetCandidateBandwidthUpperBound() const;
+ Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const;
+ double GetFeasibleInherentLoss(
+ const ChannelParameters& channel_parameters) const;
+ double GetInherentLossUpperBound(DataRate bandwidth) const;
+ double AdjustBiasFactor(double loss_rate, double bias_factor) const;
+ double GetHighBandwidthBias(DataRate bandwidth) const;
+ double GetObjective(const ChannelParameters& channel_parameters) const;
+ DataRate GetSendingRate(DataRate instantaneous_sending_rate) const;
+ DataRate GetInstantUpperBound() const;
+ void CalculateInstantUpperBound();
+
+ void CalculateTemporalWeights();
+ void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const;
+
+ // Returns false if there exists a kBwOverusing or kBwUnderusing in the
+ // window.
+ bool TrendlineEsimateAllowBitrateIncrease() const;
+
+ // Returns true if there exists an overusing state in the window.
+ bool TrendlineEsimateAllowEmergencyBackoff() const;
+
+ // Returns false if no observation was created.
+ bool PushBackObservation(rtc::ArrayView<const PacketResult> packet_results,
+ BandwidthUsage delay_detector_state);
+ void UpdateTrendlineEstimator(
+ const std::vector<PacketResult>& packet_feedbacks,
+ Timestamp at_time);
+ void UpdateDelayDetector(BandwidthUsage delay_detector_state);
+ bool IsEstimateIncreasingWhenLossLimited(
+ const ChannelParameters& best_candidate);
+ bool IsBandwidthLimitedDueToLoss() const;
+ void SetProbeBitrate(absl::optional<DataRate> probe_bitrate);
+
+ absl::optional<DataRate> acknowledged_bitrate_;
+ absl::optional<Config> config_;
+ ChannelParameters current_estimate_;
+ int num_observations_ = 0;
+ std::vector<Observation> observations_;
+ PartialObservation partial_observation_;
+ Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity();
+ Timestamp last_time_estimate_reduced_ = Timestamp::MinusInfinity();
+ absl::optional<DataRate> cached_instant_upper_bound_;
+ std::vector<double> instant_upper_bound_temporal_weights_;
+ std::vector<double> temporal_weights_;
+ std::deque<BandwidthUsage> delay_detector_states_;
+ Timestamp recovering_after_loss_timestamp_ = Timestamp::MinusInfinity();
+ DataRate bandwidth_limit_in_current_window_ = DataRate::PlusInfinity();
+ DataRate min_bitrate_ = DataRate::KilobitsPerSec(1);
+ DataRate max_bitrate_ = DataRate::PlusInfinity();
+ LossBasedState current_state_ = LossBasedState::kDelayBasedEstimate;
+ DataRate probe_bitrate_ = DataRate::PlusInfinity();
+ DataRate delay_based_estimate_ = DataRate::PlusInfinity();
+ DataRate upper_link_capacity_ = DataRate::PlusInfinity();
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build
new file mode 100644
index 0000000000..5728e0c4b2
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_gn/moz.build
@@ -0,0 +1,232 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("loss_based_bwe_v2_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
new file mode 100644
index 0000000000..c303c29d68
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
@@ -0,0 +1,1526 @@
+/*
+ * Copyright 2021 The WebRTC project authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
+
+#include <string>
+#include <vector>
+
+#include "api/network_state_predictor.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/strings/string_builder.h"
+#include "test/explicit_key_value_config.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+using ::webrtc::test::ExplicitKeyValueConfig;
+
+constexpr TimeDelta kObservationDurationLowerBound = TimeDelta::Millis(200);
+constexpr TimeDelta kDelayedIncreaseWindow = TimeDelta::Millis(300);
+constexpr double kMaxIncreaseFactor = 1.5;
+
+class LossBasedBweV2Test : public ::testing::TestWithParam<bool> {
+ protected:
+ std::string Config(bool enabled,
+ bool valid,
+ bool trendline_integration_enabled) {
+ char buffer[1024];
+ rtc::SimpleStringBuilder config_string(buffer);
+
+ config_string << "WebRTC-Bwe-LossBasedBweV2/";
+
+ if (enabled) {
+ config_string << "Enabled:true";
+ } else {
+ config_string << "Enabled:false";
+ }
+
+ if (valid) {
+ config_string << ",BwRampupUpperBoundFactor:1.2";
+ } else {
+ config_string << ",BwRampupUpperBoundFactor:0.0";
+ }
+
+ if (trendline_integration_enabled) {
+ config_string << ",TrendlineIntegrationEnabled:true";
+ } else {
+ config_string << ",TrendlineIntegrationEnabled:false";
+ }
+
+ config_string
+ << ",CandidateFactors:1.1|1.0|0.95,HigherBwBiasFactor:0.01,"
+ "DelayBasedCandidate:true,"
+ "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:"
+ "14kbps,"
+ "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01,"
+ "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15,"
+ "SendingRateSmoothingFactor:0.01,"
+ "InstantUpperBoundTemporalWeightFactor:0.97,"
+ "InstantUpperBoundBwBalance:90kbps,"
+ "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98";
+
+ config_string.AppendFormat(
+ ",ObservationDurationLowerBound:%dms",
+ static_cast<int>(kObservationDurationLowerBound.ms()));
+ config_string.AppendFormat(",MaxIncreaseFactor:%f", kMaxIncreaseFactor);
+ config_string.AppendFormat(",DelayedIncreaseWindow:%dms",
+ static_cast<int>(kDelayedIncreaseWindow.ms()));
+
+ config_string << "/";
+
+ return config_string.str();
+ }
+
+ std::vector<PacketResult> CreatePacketResultsWithReceivedPackets(
+ Timestamp first_packet_timestamp) {
+ std::vector<PacketResult> enough_feedback(2);
+ enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[0].sent_packet.send_time = first_packet_timestamp;
+ enough_feedback[1].sent_packet.send_time =
+ first_packet_timestamp + kObservationDurationLowerBound;
+ enough_feedback[0].receive_time =
+ first_packet_timestamp + kObservationDurationLowerBound;
+ enough_feedback[1].receive_time =
+ first_packet_timestamp + 2 * kObservationDurationLowerBound;
+ return enough_feedback;
+ }
+
+ std::vector<PacketResult> CreatePacketResultsWith10pLossRate(
+ Timestamp first_packet_timestamp) {
+ std::vector<PacketResult> enough_feedback(10);
+ enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000);
+ for (unsigned i = 0; i < enough_feedback.size(); ++i) {
+ enough_feedback[i].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[i].sent_packet.send_time =
+ first_packet_timestamp +
+ static_cast<int>(i) * kObservationDurationLowerBound;
+ enough_feedback[i].receive_time =
+ first_packet_timestamp +
+ static_cast<int>(i + 1) * kObservationDurationLowerBound;
+ }
+ enough_feedback[9].receive_time = Timestamp::PlusInfinity();
+ return enough_feedback;
+ }
+
+ std::vector<PacketResult> CreatePacketResultsWith50pLossRate(
+ Timestamp first_packet_timestamp) {
+ std::vector<PacketResult> enough_feedback(2);
+ enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[0].sent_packet.send_time = first_packet_timestamp;
+ enough_feedback[1].sent_packet.send_time =
+ first_packet_timestamp + kObservationDurationLowerBound;
+ enough_feedback[0].receive_time =
+ first_packet_timestamp + kObservationDurationLowerBound;
+ enough_feedback[1].receive_time = Timestamp::PlusInfinity();
+ return enough_feedback;
+ }
+
+ std::vector<PacketResult> CreatePacketResultsWith100pLossRate(
+ Timestamp first_packet_timestamp) {
+ std::vector<PacketResult> enough_feedback(2);
+ enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000);
+ enough_feedback[0].sent_packet.send_time = first_packet_timestamp;
+ enough_feedback[1].sent_packet.send_time =
+ first_packet_timestamp + kObservationDurationLowerBound;
+ enough_feedback[0].receive_time = Timestamp::PlusInfinity();
+ enough_feedback[1].receive_time = Timestamp::PlusInfinity();
+ return enough_feedback;
+ }
+};
+
+TEST_P(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ EXPECT_TRUE(loss_based_bandwidth_estimator.IsEnabled());
+}
+
+TEST_P(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/false, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled());
+}
+
+TEST_P(LossBasedBweV2Test, DisabledWhenGivenNonValidConfigurationValues) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/false,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled());
+}
+
+TEST_P(LossBasedBweV2Test, DisabledWhenGivenNonPositiveCandidateFactor) {
+ ExplicitKeyValueConfig key_value_config_negative_candidate_factor(
+ "WebRTC-Bwe-LossBasedBweV2/Enabled:true,CandidateFactors:-1.3|1.1/");
+ LossBasedBweV2 loss_based_bandwidth_estimator_1(
+ &key_value_config_negative_candidate_factor);
+ EXPECT_FALSE(loss_based_bandwidth_estimator_1.IsEnabled());
+
+ ExplicitKeyValueConfig key_value_config_zero_candidate_factor(
+ "WebRTC-Bwe-LossBasedBweV2/Enabled:true,CandidateFactors:0.0|1.1/");
+ LossBasedBweV2 loss_based_bandwidth_estimator_2(
+ &key_value_config_zero_candidate_factor);
+ EXPECT_FALSE(loss_based_bandwidth_estimator_2.IsEnabled());
+}
+
+TEST_P(LossBasedBweV2Test,
+ DisabledWhenGivenConfigurationThatDoesNotAllowGeneratingCandidates) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "DelayBasedCandidate:false/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled());
+}
+
+TEST_P(LossBasedBweV2Test, ReturnsDelayBasedEstimateWhenDisabled) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/false, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ /*packet_results=*/{},
+ /*delay_based_estimate=*/DataRate::KilobitsPerSec(100),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(100));
+}
+
+TEST_P(LossBasedBweV2Test,
+ ReturnsDelayBasedEstimateWhenWhenGivenNonValidConfigurationValues) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/false,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ /*packet_results=*/{},
+ /*delay_based_estimate=*/DataRate::KilobitsPerSec(100),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(100));
+}
+
+TEST_P(LossBasedBweV2Test,
+ BandwidthEstimateGivenInitializationAndThenFeedback) {
+ std::vector<PacketResult> enough_feedback =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_TRUE(loss_based_bandwidth_estimator.IsReady());
+ EXPECT_TRUE(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate.IsFinite());
+}
+
+TEST_P(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) {
+ std::vector<PacketResult> enough_feedback =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady());
+ EXPECT_TRUE(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate.IsPlusInfinity());
+}
+
+TEST_P(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) {
+ // Create packet results where the observation duration is less than the lower
+ // bound.
+ PacketResult not_enough_feedback[2];
+ not_enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000);
+ not_enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000);
+ not_enough_feedback[0].sent_packet.send_time = Timestamp::Zero();
+ not_enough_feedback[1].sent_packet.send_time =
+ Timestamp::Zero() + kObservationDurationLowerBound / 2;
+ not_enough_feedback[0].receive_time =
+ Timestamp::Zero() + kObservationDurationLowerBound / 2;
+ not_enough_feedback[1].receive_time =
+ Timestamp::Zero() + kObservationDurationLowerBound;
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady());
+ EXPECT_TRUE(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate.IsPlusInfinity());
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ not_enough_feedback, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady());
+ EXPECT_TRUE(loss_based_bandwidth_estimator.GetLossBasedResult()
+ .bandwidth_estimate.IsPlusInfinity());
+}
+
+TEST_P(LossBasedBweV2Test,
+ SetValueIsTheEstimateUntilAdditionalFeedbackHasBeenReceived) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_NE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_NE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+}
+
+TEST_P(LossBasedBweV2Test,
+ SetAcknowledgedBitrateOnlyAffectsTheBweWhenAdditionalFeedbackIsGiven) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator_1(&key_value_config);
+ LossBasedBweV2 loss_based_bandwidth_estimator_2(&key_value_config);
+
+ loss_based_bandwidth_estimator_1.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator_2.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator_1.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(660));
+
+ loss_based_bandwidth_estimator_1.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(900));
+
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator_1.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(660));
+
+ loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_NE(
+ loss_based_bandwidth_estimator_1.GetLossBasedResult().bandwidth_estimate,
+ loss_based_bandwidth_estimator_2.GetLossBasedResult().bandwidth_estimate);
+}
+
+TEST_P(LossBasedBweV2Test,
+ BandwidthEstimateIsCappedToBeTcpFairGivenTooHighLossRate) {
+ std::vector<PacketResult> enough_feedback_no_received_packets =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_no_received_packets,
+ /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(100));
+}
+
+TEST_P(LossBasedBweV2Test, BandwidthEstimateNotIncreaseWhenNetworkUnderusing) {
+ if (!GetParam()) {
+ GTEST_SKIP() << "This test should run only if "
+ "trendline_integration_enabled is enabled";
+ }
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwUnderusing, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_LE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_LE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+}
+
+// When network is normal, estimate can increase but never be higher than
+// the delay based estimate.
+TEST_P(LossBasedBweV2Test,
+ BandwidthEstimateCappedByDelayBasedEstimateWhenNetworkNormal) {
+ // Create two packet results, network is in normal state, 100% packets are
+ // received, and no delay increase.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // If the delay based estimate is infinity, then loss based estimate increases
+ // and not bounded by delay based estimate.
+ EXPECT_GT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::KilobitsPerSec(500),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // If the delay based estimate is not infinity, then loss based estimate is
+ // bounded by delay based estimate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(500));
+}
+
+// When loss based bwe receives a strong signal of overusing and an increase in
+// loss rate, it should acked bitrate for emegency backoff.
+TEST_P(LossBasedBweV2Test, UseAckedBitrateForEmegencyBackOff) {
+ // Create two packet results, first packet has 50% loss rate, second packet
+ // has 100% loss rate.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ DataRate acked_bitrate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_bitrate);
+ // Update estimate when network is overusing, and 50% loss rate.
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwOverusing,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // Update estimate again when network is continuously overusing, and 100%
+ // loss rate.
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwOverusing,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // The estimate bitrate now is backed off based on acked bitrate.
+ EXPECT_LE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ acked_bitrate);
+}
+
+// When receiving the same packet feedback, loss based bwe ignores the feedback
+// and returns the current estimate.
+TEST_P(LossBasedBweV2Test, NoBweChangeIfObservationDurationUnchanged) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_1 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ // Use the same feedback and check if the estimate is unchanged.
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+ EXPECT_EQ(estimate_2, estimate_1);
+}
+
+// When receiving feedback of packets that were sent within an observation
+// duration, and network is in the normal state, loss based bwe returns the
+// current estimate.
+TEST_P(LossBasedBweV2Test,
+ NoBweChangeIfObservationDurationIsSmallAndNetworkNormal) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound - TimeDelta::Millis(1));
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_1 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+ EXPECT_EQ(estimate_2, estimate_1);
+}
+
+// When receiving feedback of packets that were sent within an observation
+// duration, and network is in the underusing state, loss based bwe returns the
+// current estimate.
+TEST_P(LossBasedBweV2Test,
+ NoBweIncreaseIfObservationDurationIsSmallAndNetworkUnderusing) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound - TimeDelta::Millis(1));
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_1 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwUnderusing, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+ EXPECT_LE(estimate_2, estimate_1);
+}
+
+// When receiving feedback of packets that were sent within an observation
+// duration, network is overusing, and trendline integration is enabled, loss
+// based bwe updates its estimate.
+TEST_P(LossBasedBweV2Test,
+ UpdateEstimateIfObservationDurationIsSmallAndNetworkOverusing) {
+ if (!GetParam()) {
+ GTEST_SKIP() << "This test should run only if "
+ "trendline_integration_enabled is enabled";
+ }
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound - TimeDelta::Millis(1));
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_1 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwOverusing,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+ EXPECT_LT(estimate_2, estimate_1);
+}
+
+TEST_P(LossBasedBweV2Test,
+ IncreaseToDelayBasedEstimateIfNoLossOrDelayIncrease) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ 2 * kObservationDurationLowerBound);
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ delay_based_estimate);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ delay_based_estimate);
+}
+
+TEST_P(LossBasedBweV2Test,
+ IncreaseByMaxIncreaseFactorAfterLossBasedBweBacksOff) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:1.5,BwRampupUpperBoundFactor:"
+ "2.0,NotIncreaseIfInherentLossLessThanAverageLoss:false/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ LossBasedBweV2::Result result_at_loss =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+
+ // Network recovers after loss.
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ LossBasedBweV2::Result result_after_recovery =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ EXPECT_EQ(result_after_recovery.bandwidth_estimate,
+ result_at_loss.bandwidth_estimate * 1.5);
+}
+
+TEST_P(LossBasedBweV2Test,
+ LossBasedStateIsDelayBasedEstimateAfterNetworkRecovering) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:100|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:100,"
+ "BwRampupUpperBoundFactor:"
+ "2.0,NotIncreaseIfInherentLossLessThanAverageLoss:false/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(600);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ ASSERT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDecreasing);
+
+ // Network recovers after loss.
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDelayBasedEstimate);
+
+ // Network recovers continuing.
+ std::vector<PacketResult> enough_feedback_3 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * 2);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_3, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDelayBasedEstimate);
+}
+
+TEST_P(LossBasedBweV2Test,
+ LossBasedStateIsNotDelayBasedEstimateIfDelayBasedEsimtateInfinite) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:100|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:100,"
+ "BwRampupUpperBoundFactor:"
+ "2.0/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::PlusInfinity();
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ ASSERT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDecreasing);
+
+ // Network recovers after loss.
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ EXPECT_NE(loss_based_bandwidth_estimator.GetLossBasedResult().state,
+ LossBasedState::kDelayBasedEstimate);
+}
+
+// After loss based bwe backs off, the next estimate is capped by
+// a factor of acked bitrate.
+TEST_P(LossBasedBweV2Test,
+ IncreaseByFactorOfAckedBitrateAfterLossBasedBweBacksOff) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,LossThresholdOfHighBandwidthPreference:0.99,"
+ "BwRampupUpperBoundFactor:1.2,"
+ "InherentLossUpperBoundOffset:0.9,ObservationDurationLowerBound:200ms/");
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Change the acked bitrate to make sure that the estimate is bounded by a
+ // factor of acked bitrate.
+ DataRate acked_bitrate = DataRate::KilobitsPerSec(50);
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_bitrate);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // The estimate is capped by acked_bitrate * BwRampupUpperBoundFactor.
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+ EXPECT_EQ(estimate_2, acked_bitrate * 1.2);
+}
+
+// After loss based bwe backs off, the estimate is bounded during the delayed
+// window.
+TEST_P(LossBasedBweV2Test,
+ EstimateBitrateIsBoundedDuringDelayedWindowAfterLossBasedBweBacksOff) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kDelayedIncreaseWindow - TimeDelta::Millis(2));
+ std::vector<PacketResult> enough_feedback_3 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kDelayedIncreaseWindow - TimeDelta::Millis(1));
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // Increase the acknowledged bitrate to make sure that the estimate is not
+ // capped too low.
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(5000));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // The estimate is capped by current_estimate * kMaxIncreaseFactor because
+ // it recently backed off.
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_3, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // The latest estimate is the same as the previous estimate since the sent
+ // packets were sent within the DelayedIncreaseWindow.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ estimate_2);
+}
+
+// The estimate is not bounded after the delayed increase window.
+TEST_P(LossBasedBweV2Test, KeepIncreasingEstimateAfterDelayedIncreaseWindow) {
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kDelayedIncreaseWindow - TimeDelta::Millis(1));
+ std::vector<PacketResult> enough_feedback_3 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kDelayedIncreaseWindow + TimeDelta::Millis(1));
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(300));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // Increase the acknowledged bitrate to make sure that the estimate is not
+ // capped too low.
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(
+ DataRate::KilobitsPerSec(5000));
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // The estimate is capped by current_estimate * kMaxIncreaseFactor because it
+ // recently backed off.
+ DataRate estimate_2 =
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate;
+
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_3, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+ // The estimate can continue increasing after the DelayedIncreaseWindow.
+ EXPECT_GE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ estimate_2);
+}
+
+TEST_P(LossBasedBweV2Test, NotIncreaseIfInherentLossLessThanAverageLoss) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,"
+ "NotIncreaseIfInherentLossLessThanAverageLoss:true/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_10p_loss_1 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_10p_loss_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Do not increase the bitrate because inherent loss is less than average loss
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+}
+
+TEST_P(LossBasedBweV2Test,
+ SelectHighBandwidthCandidateIfLossRateIsLessThanThreshold) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|0.8,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "20,NotIncreaseIfInherentLossLessThanAverageLoss:false/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_10p_loss_1 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_10p_loss_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Because LossThresholdOfHighBandwidthPreference is 20%, the average loss is
+ // 10%, bandwidth estimate should increase.
+ EXPECT_GT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+}
+
+TEST_P(LossBasedBweV2Test,
+ SelectLowBandwidthCandidateIfLossRateIsIsHigherThanThreshold) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|0.8,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_10p_loss_1 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_10p_loss_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Because LossThresholdOfHighBandwidthPreference is 5%, the average loss is
+ // 10%, bandwidth estimate should decrease.
+ EXPECT_LT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(600));
+}
+
+TEST_P(LossBasedBweV2Test, UseProbeResultWhenRecoveringFromLoss) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:1000,"
+ "BwRampupUpperBoundFactor:2.0,ProbeIntegrationEnabled:true/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Network recovers after loss.
+ DataRate probe_estimate = DataRate::KilobitsPerSec(300);
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ probe_estimate, /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ LossBasedBweV2::Result result_after_recovery =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ EXPECT_EQ(result_after_recovery.bandwidth_estimate, probe_estimate);
+}
+
+// If BoundByUpperLinkCapacityWhenLossLimited is enabled, the estimate is
+// bounded by the upper link capacity when bandwidth is loss limited.
+TEST_P(LossBasedBweV2Test, BoundEstimateByUpperLinkCapacityWhenLossLimited) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:1000,"
+ "BwRampupUpperBoundFactor:2.0,BoundByUpperLinkCapacityWhenLossLimited:"
+ "true/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Network recovers after loss.
+ DataRate upper_link_capacity = DataRate::KilobitsPerSec(10);
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt, upper_link_capacity);
+
+ LossBasedBweV2::Result result_after_recovery =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ EXPECT_EQ(result_after_recovery.bandwidth_estimate, upper_link_capacity);
+}
+
+// If BoundByUpperLinkCapacityWhenLossLimited is enabled, the estimate is not
+// bounded by the upper link capacity when bandwidth is not loss limited.
+TEST_P(LossBasedBweV2Test,
+ NotBoundEstimateByUpperLinkCapacityWhenNotLossLimited) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:1000,"
+ "BwRampupUpperBoundFactor:2.0,BoundByUpperLinkCapacityWhenLossLimited:"
+ "true/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create a normal network without loss
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ DataRate upper_link_capacity = DataRate::KilobitsPerSec(10);
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt, upper_link_capacity);
+
+ LossBasedBweV2::Result loss_based_result =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ EXPECT_GT(loss_based_result.bandwidth_estimate, upper_link_capacity);
+}
+
+// If BoundByUpperLinkCapacityWhenLossLimited is disabled, the estimate is not
+// bounded by the upper link capacity.
+TEST_P(LossBasedBweV2Test, NotBoundEstimateByUpperLinkCapacity) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.2|1|0.5,AckedRateCandidate:true,"
+ "ObservationWindowSize:2,ObservationDurationLowerBound:200ms,"
+ "InstantUpperBoundBwBalance:10000kbps,"
+ "DelayBasedCandidate:true,MaxIncreaseFactor:1000,"
+ "BwRampupUpperBoundFactor:2.0,BoundByUpperLinkCapacityWhenLossLimited:"
+ "false/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ DataRate acked_rate = DataRate::KilobitsPerSec(300);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+ loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_rate);
+
+ // Create some loss to create the loss limited scenario.
+ std::vector<PacketResult> enough_feedback_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Network recovers after loss.
+ DataRate upper_link_capacity = DataRate::KilobitsPerSec(10);
+ std::vector<PacketResult> enough_feedback_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal,
+ /*probe_estimate=*/absl::nullopt, upper_link_capacity);
+
+ LossBasedBweV2::Result result_after_recovery =
+ loss_based_bandwidth_estimator.GetLossBasedResult();
+ EXPECT_GT(result_after_recovery.bandwidth_estimate, upper_link_capacity);
+}
+
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt10pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.09/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinMaxBitrate(
+ /*min_bitrate=*/DataRate::KilobitsPerSec(10),
+ /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_10p_loss_1 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_10p_loss_2 =
+ CreatePacketResultsWith10pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_10p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // At 10% loss rate and high loss rate threshold to be 10%, cap the estimate
+ // to be 500 * 1000-0.1 = 400kbps.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(400));
+}
+
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt50pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinMaxBitrate(
+ /*min_bitrate=*/DataRate::KilobitsPerSec(10),
+ /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_50p_loss_1 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_50p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_50p_loss_2 =
+ CreatePacketResultsWith50pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_50p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // At 50% loss rate and high loss rate threshold to be 30%, cap the estimate
+ // to be the min bitrate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(10));
+}
+
+TEST_P(LossBasedBweV2Test,
+ StricterBoundUsingHighLossRateThresholdAt100pLossRate) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinMaxBitrate(
+ /*min_bitrate=*/DataRate::KilobitsPerSec(10),
+ /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_100p_loss_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_100p_loss_2 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // At 100% loss rate and high loss rate threshold to be 30%, cap the estimate
+ // to be the min bitrate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(10));
+}
+
+TEST_P(LossBasedBweV2Test, EstimateRecoversAfterHighLoss) {
+ ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-LossBasedBweV2/"
+ "Enabled:true,CandidateFactors:1.1|1.0|0.9,AckedRateCandidate:false,"
+ "ObservationWindowSize:2,"
+ "DelayBasedCandidate:true,InstantUpperBoundBwBalance:100kbps,"
+ "ObservationDurationLowerBound:200ms,HigherBwBiasFactor:1000,"
+ "HigherLogBwBiasFactor:1000,LossThresholdOfHighBandwidthPreference:0."
+ "05,HighLossRateThreshold:0.3/");
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinMaxBitrate(
+ /*min_bitrate=*/DataRate::KilobitsPerSec(10),
+ /*max_bitrate=*/DataRate::KilobitsPerSec(1000000));
+ DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000);
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(600));
+
+ std::vector<PacketResult> enough_feedback_100p_loss_1 =
+ CreatePacketResultsWith100pLossRate(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_100p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // Make sure that the estimate is set to min bitrate because of 100% loss
+ // rate.
+ EXPECT_EQ(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(10));
+
+ // Create some feedbacks with 0 loss rate to simulate network recovering.
+ std::vector<PacketResult> enough_feedback_0p_loss_1 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_0p_loss_1, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ std::vector<PacketResult> enough_feedback_0p_loss_2 =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero() +
+ kObservationDurationLowerBound * 2);
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback_0p_loss_2, delay_based_estimate,
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ // The estimate increases as network recovers.
+ EXPECT_GT(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(10));
+}
+
+TEST_P(LossBasedBweV2Test, EstimateIsNotHigherThanMaxBitrate) {
+ ExplicitKeyValueConfig key_value_config(
+ Config(/*enabled=*/true, /*valid=*/true,
+ /*trendline_integration_enabled=*/GetParam()));
+ LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config);
+ loss_based_bandwidth_estimator.SetMinMaxBitrate(
+ /*min_bitrate=*/DataRate::KilobitsPerSec(10),
+ /*max_bitrate=*/DataRate::KilobitsPerSec(1000));
+ loss_based_bandwidth_estimator.SetBandwidthEstimate(
+ DataRate::KilobitsPerSec(1000));
+ std::vector<PacketResult> enough_feedback =
+ CreatePacketResultsWithReceivedPackets(
+ /*first_packet_timestamp=*/Timestamp::Zero());
+ loss_based_bandwidth_estimator.UpdateBandwidthEstimate(
+ enough_feedback, /*delay_based_estimate=*/DataRate::PlusInfinity(),
+ BandwidthUsage::kBwNormal, /*probe_estimate=*/absl::nullopt,
+ /*upper_link_capacity=*/DataRate::PlusInfinity());
+
+ EXPECT_LE(
+ loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate,
+ DataRate::KilobitsPerSec(1000));
+}
+
+INSTANTIATE_TEST_SUITE_P(LossBasedBweV2Tests,
+ LossBasedBweV2Test,
+ ::testing::Bool());
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc
new file mode 100644
index 0000000000..a94f653157
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.cc
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+
+namespace webrtc {
+namespace {
+// The minumum number of probes we need to receive feedback about in percent
+// in order to have a valid estimate.
+constexpr double kMinReceivedProbesRatio = .80;
+
+// The minumum number of bytes we need to receive feedback about in percent
+// in order to have a valid estimate.
+constexpr double kMinReceivedBytesRatio = .80;
+
+// The maximum |receive rate| / |send rate| ratio for a valid estimate.
+constexpr float kMaxValidRatio = 2.0f;
+
+// The minimum |receive rate| / |send rate| ratio assuming that the link is
+// not saturated, i.e. we assume that we will receive at least
+// kMinRatioForUnsaturatedLink * |send rate| if |send rate| is less than the
+// link capacity.
+constexpr float kMinRatioForUnsaturatedLink = 0.9f;
+
+// The target utilization of the link. If we know true link capacity
+// we'd like to send at 95% of that rate.
+constexpr float kTargetUtilizationFraction = 0.95f;
+
+// The maximum time period over which the cluster history is retained.
+// This is also the maximum time period beyond which a probing burst is not
+// expected to last.
+constexpr TimeDelta kMaxClusterHistory = TimeDelta::Seconds(1);
+
+// The maximum time interval between first and the last probe on a cluster
+// on the sender side as well as the receive side.
+constexpr TimeDelta kMaxProbeInterval = TimeDelta::Seconds(1);
+
+} // namespace
+
+ProbeBitrateEstimator::ProbeBitrateEstimator(RtcEventLog* event_log)
+ : event_log_(event_log) {}
+
+ProbeBitrateEstimator::~ProbeBitrateEstimator() = default;
+
+absl::optional<DataRate> ProbeBitrateEstimator::HandleProbeAndEstimateBitrate(
+ const PacketResult& packet_feedback) {
+ int cluster_id = packet_feedback.sent_packet.pacing_info.probe_cluster_id;
+ RTC_DCHECK_NE(cluster_id, PacedPacketInfo::kNotAProbe);
+
+ EraseOldClusters(packet_feedback.receive_time);
+
+ AggregatedCluster* cluster = &clusters_[cluster_id];
+
+ if (packet_feedback.sent_packet.send_time < cluster->first_send) {
+ cluster->first_send = packet_feedback.sent_packet.send_time;
+ }
+ if (packet_feedback.sent_packet.send_time > cluster->last_send) {
+ cluster->last_send = packet_feedback.sent_packet.send_time;
+ cluster->size_last_send = packet_feedback.sent_packet.size;
+ }
+ if (packet_feedback.receive_time < cluster->first_receive) {
+ cluster->first_receive = packet_feedback.receive_time;
+ cluster->size_first_receive = packet_feedback.sent_packet.size;
+ }
+ if (packet_feedback.receive_time > cluster->last_receive) {
+ cluster->last_receive = packet_feedback.receive_time;
+ }
+ cluster->size_total += packet_feedback.sent_packet.size;
+ cluster->num_probes += 1;
+
+ RTC_DCHECK_GT(
+ packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes, 0);
+ RTC_DCHECK_GT(packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes,
+ 0);
+
+ int min_probes =
+ packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes *
+ kMinReceivedProbesRatio;
+ DataSize min_size =
+ DataSize::Bytes(
+ packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes) *
+ kMinReceivedBytesRatio;
+ if (cluster->num_probes < min_probes || cluster->size_total < min_size)
+ return absl::nullopt;
+
+ TimeDelta send_interval = cluster->last_send - cluster->first_send;
+ TimeDelta receive_interval = cluster->last_receive - cluster->first_receive;
+
+ if (send_interval <= TimeDelta::Zero() || send_interval > kMaxProbeInterval ||
+ receive_interval <= TimeDelta::Zero() ||
+ receive_interval > kMaxProbeInterval) {
+ RTC_LOG(LS_INFO) << "Probing unsuccessful, invalid send/receive interval"
+ " [cluster id: "
+ << cluster_id
+ << "] [send interval: " << ToString(send_interval)
+ << "]"
+ " [receive interval: "
+ << ToString(receive_interval) << "]";
+ if (event_log_) {
+ event_log_->Log(std::make_unique<RtcEventProbeResultFailure>(
+ cluster_id, ProbeFailureReason::kInvalidSendReceiveInterval));
+ }
+ return absl::nullopt;
+ }
+ // Since the `send_interval` does not include the time it takes to actually
+ // send the last packet the size of the last sent packet should not be
+ // included when calculating the send bitrate.
+ RTC_DCHECK_GT(cluster->size_total, cluster->size_last_send);
+ DataSize send_size = cluster->size_total - cluster->size_last_send;
+ DataRate send_rate = send_size / send_interval;
+
+ // Since the `receive_interval` does not include the time it takes to
+ // actually receive the first packet the size of the first received packet
+ // should not be included when calculating the receive bitrate.
+ RTC_DCHECK_GT(cluster->size_total, cluster->size_first_receive);
+ DataSize receive_size = cluster->size_total - cluster->size_first_receive;
+ DataRate receive_rate = receive_size / receive_interval;
+
+ double ratio = receive_rate / send_rate;
+ if (ratio > kMaxValidRatio) {
+ RTC_LOG(LS_INFO) << "Probing unsuccessful, receive/send ratio too high"
+ " [cluster id: "
+ << cluster_id << "] [send: " << ToString(send_size)
+ << " / " << ToString(send_interval) << " = "
+ << ToString(send_rate)
+ << "]"
+ " [receive: "
+ << ToString(receive_size) << " / "
+ << ToString(receive_interval) << " = "
+ << ToString(receive_rate)
+ << " ]"
+ " [ratio: "
+ << ToString(receive_rate) << " / " << ToString(send_rate)
+ << " = " << ratio << " > kMaxValidRatio ("
+ << kMaxValidRatio << ")]";
+ if (event_log_) {
+ event_log_->Log(std::make_unique<RtcEventProbeResultFailure>(
+ cluster_id, ProbeFailureReason::kInvalidSendReceiveRatio));
+ }
+ return absl::nullopt;
+ }
+ RTC_LOG(LS_INFO) << "Probing successful"
+ " [cluster id: "
+ << cluster_id << "] [send: " << ToString(send_size) << " / "
+ << ToString(send_interval) << " = " << ToString(send_rate)
+ << " ]"
+ " [receive: "
+ << ToString(receive_size) << " / "
+ << ToString(receive_interval) << " = "
+ << ToString(receive_rate) << "]";
+
+ DataRate res = std::min(send_rate, receive_rate);
+ // If we're receiving at significantly lower bitrate than we were sending at,
+ // it suggests that we've found the true capacity of the link. In this case,
+ // set the target bitrate slightly lower to not immediately overuse.
+ if (receive_rate < kMinRatioForUnsaturatedLink * send_rate) {
+ RTC_DCHECK_GT(send_rate, receive_rate);
+ res = kTargetUtilizationFraction * receive_rate;
+ }
+ if (event_log_) {
+ event_log_->Log(
+ std::make_unique<RtcEventProbeResultSuccess>(cluster_id, res.bps()));
+ }
+ estimated_data_rate_ = res;
+ return estimated_data_rate_;
+}
+
+absl::optional<DataRate>
+ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrate() {
+ absl::optional<DataRate> estimated_data_rate = estimated_data_rate_;
+ estimated_data_rate_.reset();
+ return estimated_data_rate;
+}
+
+void ProbeBitrateEstimator::EraseOldClusters(Timestamp timestamp) {
+ for (auto it = clusters_.begin(); it != clusters_.end();) {
+ if (it->second.last_receive + kMaxClusterHistory < timestamp) {
+ it = clusters_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.h
new file mode 100644
index 0000000000..d5a523b7f3
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
+
+#include <limits>
+#include <map>
+
+#include "absl/types/optional.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+
+namespace webrtc {
+class RtcEventLog;
+
+class ProbeBitrateEstimator {
+ public:
+ explicit ProbeBitrateEstimator(RtcEventLog* event_log);
+ ~ProbeBitrateEstimator();
+
+ // Should be called for every probe packet we receive feedback about.
+ // Returns the estimated bitrate if the probe completes a valid cluster.
+ absl::optional<DataRate> HandleProbeAndEstimateBitrate(
+ const PacketResult& packet_feedback);
+
+ absl::optional<DataRate> FetchAndResetLastEstimatedBitrate();
+
+ private:
+ struct AggregatedCluster {
+ int num_probes = 0;
+ Timestamp first_send = Timestamp::PlusInfinity();
+ Timestamp last_send = Timestamp::MinusInfinity();
+ Timestamp first_receive = Timestamp::PlusInfinity();
+ Timestamp last_receive = Timestamp::MinusInfinity();
+ DataSize size_last_send = DataSize::Zero();
+ DataSize size_first_receive = DataSize::Zero();
+ DataSize size_total = DataSize::Zero();
+ };
+
+ // Erases old cluster data that was seen before `timestamp`.
+ void EraseOldClusters(Timestamp timestamp);
+
+ std::map<int, AggregatedCluster> clusters_;
+ RtcEventLog* const event_log_;
+ absl::optional<DataRate> estimated_data_rate_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator_unittest.cc
new file mode 100644
index 0000000000..6b4146d2bf
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_bitrate_estimator_unittest.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
+
+#include <stddef.h>
+
+#include "api/transport/network_types.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+constexpr int kDefaultMinProbes = 5;
+constexpr int kDefaultMinBytes = 5000;
+constexpr float kTargetUtilizationFraction = 0.95f;
+} // anonymous namespace
+
+class TestProbeBitrateEstimator : public ::testing::Test {
+ public:
+ TestProbeBitrateEstimator() : probe_bitrate_estimator_(nullptr) {}
+
+ // TODO(philipel): Use PacedPacketInfo when ProbeBitrateEstimator is rewritten
+ // to use that information.
+ void AddPacketFeedback(int probe_cluster_id,
+ size_t size_bytes,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ int min_probes = kDefaultMinProbes,
+ int min_bytes = kDefaultMinBytes) {
+ const Timestamp kReferenceTime = Timestamp::Seconds(1000);
+ PacketResult feedback;
+ feedback.sent_packet.send_time =
+ kReferenceTime + TimeDelta::Millis(send_time_ms);
+ feedback.sent_packet.size = DataSize::Bytes(size_bytes);
+ feedback.sent_packet.pacing_info =
+ PacedPacketInfo(probe_cluster_id, min_probes, min_bytes);
+ feedback.receive_time = kReferenceTime + TimeDelta::Millis(arrival_time_ms);
+ measured_data_rate_ =
+ probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(feedback);
+ }
+
+ protected:
+ absl::optional<DataRate> measured_data_rate_;
+ ProbeBitrateEstimator probe_bitrate_estimator_;
+};
+
+TEST_F(TestProbeBitrateEstimator, OneCluster) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+ AddPacketFeedback(0, 1000, 30, 40);
+
+ EXPECT_NEAR(measured_data_rate_->bps(), 800000, 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, OneClusterTooFewProbes) {
+ AddPacketFeedback(0, 2000, 0, 10);
+ AddPacketFeedback(0, 2000, 10, 20);
+ AddPacketFeedback(0, 2000, 20, 30);
+
+ EXPECT_FALSE(measured_data_rate_);
+}
+
+TEST_F(TestProbeBitrateEstimator, OneClusterTooFewBytes) {
+ const int kMinBytes = 6000;
+ AddPacketFeedback(0, 800, 0, 10, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 800, 10, 20, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 800, 20, 30, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 800, 30, 40, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 800, 40, 50, kDefaultMinProbes, kMinBytes);
+
+ EXPECT_FALSE(measured_data_rate_);
+}
+
+TEST_F(TestProbeBitrateEstimator, SmallCluster) {
+ const int kMinBytes = 1000;
+ AddPacketFeedback(0, 150, 0, 10, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 150, 10, 20, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 150, 20, 30, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 150, 30, 40, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 150, 40, 50, kDefaultMinProbes, kMinBytes);
+ AddPacketFeedback(0, 150, 50, 60, kDefaultMinProbes, kMinBytes);
+ EXPECT_NEAR(measured_data_rate_->bps(), 120000, 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, LargeCluster) {
+ const int kMinProbes = 30;
+ const int kMinBytes = 312500;
+
+ int64_t send_time = 0;
+ int64_t receive_time = 5;
+ for (int i = 0; i < 25; ++i) {
+ AddPacketFeedback(0, 12500, send_time, receive_time, kMinProbes, kMinBytes);
+ ++send_time;
+ ++receive_time;
+ }
+ EXPECT_NEAR(measured_data_rate_->bps(), 100000000, 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, FastReceive) {
+ AddPacketFeedback(0, 1000, 0, 15);
+ AddPacketFeedback(0, 1000, 10, 30);
+ AddPacketFeedback(0, 1000, 20, 35);
+ AddPacketFeedback(0, 1000, 30, 40);
+
+ EXPECT_NEAR(measured_data_rate_->bps(), 800000, 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, TooFastReceive) {
+ AddPacketFeedback(0, 1000, 0, 19);
+ AddPacketFeedback(0, 1000, 10, 22);
+ AddPacketFeedback(0, 1000, 20, 25);
+ AddPacketFeedback(0, 1000, 40, 27);
+
+ EXPECT_FALSE(measured_data_rate_);
+}
+
+TEST_F(TestProbeBitrateEstimator, SlowReceive) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 40);
+ AddPacketFeedback(0, 1000, 20, 70);
+ AddPacketFeedback(0, 1000, 30, 85);
+ // Expected send rate = 800 kbps, expected receive rate = 320 kbps.
+
+ EXPECT_NEAR(measured_data_rate_->bps(), kTargetUtilizationFraction * 320000,
+ 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, BurstReceive) {
+ AddPacketFeedback(0, 1000, 0, 50);
+ AddPacketFeedback(0, 1000, 10, 50);
+ AddPacketFeedback(0, 1000, 20, 50);
+ AddPacketFeedback(0, 1000, 40, 50);
+
+ EXPECT_FALSE(measured_data_rate_);
+}
+
+TEST_F(TestProbeBitrateEstimator, MultipleClusters) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+ AddPacketFeedback(0, 1000, 40, 60);
+ // Expected send rate = 600 kbps, expected receive rate = 480 kbps.
+ EXPECT_NEAR(measured_data_rate_->bps(), kTargetUtilizationFraction * 480000,
+ 10);
+
+ AddPacketFeedback(0, 1000, 50, 60);
+ // Expected send rate = 640 kbps, expected receive rate = 640 kbps.
+ EXPECT_NEAR(measured_data_rate_->bps(), 640000, 10);
+
+ AddPacketFeedback(1, 1000, 60, 70);
+ AddPacketFeedback(1, 1000, 65, 77);
+ AddPacketFeedback(1, 1000, 70, 84);
+ AddPacketFeedback(1, 1000, 75, 90);
+ // Expected send rate = 1600 kbps, expected receive rate = 1200 kbps.
+
+ EXPECT_NEAR(measured_data_rate_->bps(), kTargetUtilizationFraction * 1200000,
+ 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, IgnoreOldClusters) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+
+ AddPacketFeedback(1, 1000, 60, 70);
+ AddPacketFeedback(1, 1000, 65, 77);
+ AddPacketFeedback(1, 1000, 70, 84);
+ AddPacketFeedback(1, 1000, 75, 90);
+ // Expected send rate = 1600 kbps, expected receive rate = 1200 kbps.
+
+ EXPECT_NEAR(measured_data_rate_->bps(), kTargetUtilizationFraction * 1200000,
+ 10);
+
+ // Coming in 6s later
+ AddPacketFeedback(0, 1000, 40 + 6000, 60 + 6000);
+
+ EXPECT_FALSE(measured_data_rate_);
+}
+
+TEST_F(TestProbeBitrateEstimator, IgnoreSizeLastSendPacket) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+ AddPacketFeedback(0, 1000, 30, 40);
+ AddPacketFeedback(0, 1500, 40, 50);
+ // Expected send rate = 800 kbps, expected receive rate = 900 kbps.
+
+ EXPECT_NEAR(measured_data_rate_->bps(), 800000, 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, IgnoreSizeFirstReceivePacket) {
+ AddPacketFeedback(0, 1500, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+ AddPacketFeedback(0, 1000, 30, 40);
+ // Expected send rate = 933 kbps, expected receive rate = 800 kbps.
+
+ EXPECT_NEAR(measured_data_rate_->bps(), kTargetUtilizationFraction * 800000,
+ 10);
+}
+
+TEST_F(TestProbeBitrateEstimator, NoLastEstimatedBitrateBps) {
+ EXPECT_FALSE(probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrate());
+}
+
+TEST_F(TestProbeBitrateEstimator, FetchLastEstimatedBitrateBps) {
+ AddPacketFeedback(0, 1000, 0, 10);
+ AddPacketFeedback(0, 1000, 10, 20);
+ AddPacketFeedback(0, 1000, 20, 30);
+ AddPacketFeedback(0, 1000, 30, 40);
+
+ auto estimated_bitrate =
+ probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrate();
+ EXPECT_TRUE(estimated_bitrate);
+ EXPECT_NEAR(estimated_bitrate->bps(), 800000, 10);
+ EXPECT_FALSE(probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrate());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.cc
new file mode 100644
index 0000000000..1af943c4cb
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.cc
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/probe_controller.h"
+
+#include <algorithm>
+#include <initializer_list>
+#include <memory>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "absl/types/optional.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+// Maximum waiting time from the time of initiating probing to getting
+// the measured results back.
+constexpr TimeDelta kMaxWaitingTimeForProbingResult = TimeDelta::Seconds(1);
+
+// Default probing bitrate limit. Applied only when the application didn't
+// specify max bitrate.
+constexpr DataRate kDefaultMaxProbingBitrate = DataRate::KilobitsPerSec(5000);
+
+// If the bitrate drops to a factor `kBitrateDropThreshold` or lower
+// and we recover within `kBitrateDropTimeoutMs`, then we'll send
+// a probe at a fraction `kProbeFractionAfterDrop` of the original bitrate.
+constexpr double kBitrateDropThreshold = 0.66;
+constexpr TimeDelta kBitrateDropTimeout = TimeDelta::Seconds(5);
+constexpr double kProbeFractionAfterDrop = 0.85;
+
+// Timeout for probing after leaving ALR. If the bitrate drops significantly,
+// (as determined by the delay based estimator) and we leave ALR, then we will
+// send a probe if we recover within `kLeftAlrTimeoutMs` ms.
+constexpr TimeDelta kAlrEndedTimeout = TimeDelta::Seconds(3);
+
+// The expected uncertainty of probe result (as a fraction of the target probe
+// This is a limit on how often probing can be done when there is a BW
+// drop detected in ALR.
+constexpr TimeDelta kMinTimeBetweenAlrProbes = TimeDelta::Seconds(5);
+
+// bitrate). Used to avoid probing if the probe bitrate is close to our current
+// estimate.
+constexpr double kProbeUncertainty = 0.05;
+
+// Use probing to recover faster after large bitrate estimate drops.
+constexpr char kBweRapidRecoveryExperiment[] =
+ "WebRTC-BweRapidRecoveryExperiment";
+
+void MaybeLogProbeClusterCreated(RtcEventLog* event_log,
+ const ProbeClusterConfig& probe) {
+ RTC_DCHECK(event_log);
+ if (!event_log) {
+ return;
+ }
+
+ DataSize min_data_size = probe.target_data_rate * probe.target_duration;
+ event_log->Log(std::make_unique<RtcEventProbeClusterCreated>(
+ probe.id, probe.target_data_rate.bps(), probe.target_probe_count,
+ min_data_size.bytes()));
+}
+
+} // namespace
+
+ProbeControllerConfig::ProbeControllerConfig(
+ const FieldTrialsView* key_value_config)
+ : first_exponential_probe_scale("p1", 3.0),
+ second_exponential_probe_scale("p2", 6.0),
+ further_exponential_probe_scale("step_size", 2),
+ further_probe_threshold("further_probe_threshold", 0.7),
+ alr_probing_interval("alr_interval", TimeDelta::Seconds(5)),
+ alr_probe_scale("alr_scale", 2),
+ network_state_estimate_probing_interval("network_state_interval",
+ TimeDelta::PlusInfinity()),
+ probe_if_estimate_lower_than_network_state_estimate_ratio(
+ "est_lower_than_network_ratio",
+ 0),
+ estimate_lower_than_network_state_estimate_probing_interval(
+ "est_lower_than_network_interval",
+ TimeDelta::Seconds(3)),
+ network_state_probe_scale("network_state_scale", 1.0),
+ network_state_probe_duration("network_state_probe_duration",
+ TimeDelta::Millis(15)),
+
+ probe_on_max_allocated_bitrate_change("probe_max_allocation", true),
+ first_allocation_probe_scale("alloc_p1", 1),
+ second_allocation_probe_scale("alloc_p2", 2),
+ allocation_allow_further_probing("alloc_probe_further", false),
+ allocation_probe_max("alloc_probe_max", DataRate::PlusInfinity()),
+ min_probe_packets_sent("min_probe_packets_sent", 5),
+ min_probe_duration("min_probe_duration", TimeDelta::Millis(15)),
+ limit_probe_target_rate_to_loss_bwe("limit_probe_target_rate_to_loss_bwe",
+ false),
+ loss_limited_probe_scale("loss_limited_scale", 1.5),
+ skip_if_estimate_larger_than_fraction_of_max(
+ "skip_if_est_larger_than_fraction_of_max",
+ 0.0),
+ not_probe_if_delay_increased("not_probe_if_delay_increased", false) {
+ ParseFieldTrial({&first_exponential_probe_scale,
+ &second_exponential_probe_scale,
+ &further_exponential_probe_scale,
+ &further_probe_threshold,
+ &alr_probing_interval,
+ &alr_probe_scale,
+ &probe_on_max_allocated_bitrate_change,
+ &first_allocation_probe_scale,
+ &second_allocation_probe_scale,
+ &allocation_allow_further_probing,
+ &min_probe_duration,
+ &network_state_estimate_probing_interval,
+ &probe_if_estimate_lower_than_network_state_estimate_ratio,
+ &estimate_lower_than_network_state_estimate_probing_interval,
+ &network_state_probe_scale,
+ &network_state_probe_duration,
+ &min_probe_packets_sent,
+ &limit_probe_target_rate_to_loss_bwe,
+ &loss_limited_probe_scale,
+ &skip_if_estimate_larger_than_fraction_of_max,
+ &not_probe_if_delay_increased},
+ key_value_config->Lookup("WebRTC-Bwe-ProbingConfiguration"));
+
+ // Specialized keys overriding subsets of WebRTC-Bwe-ProbingConfiguration
+ ParseFieldTrial(
+ {&first_exponential_probe_scale, &second_exponential_probe_scale},
+ key_value_config->Lookup("WebRTC-Bwe-InitialProbing"));
+ ParseFieldTrial({&further_exponential_probe_scale, &further_probe_threshold},
+ key_value_config->Lookup("WebRTC-Bwe-ExponentialProbing"));
+ ParseFieldTrial(
+ {&alr_probing_interval, &alr_probe_scale, &loss_limited_probe_scale},
+ key_value_config->Lookup("WebRTC-Bwe-AlrProbing"));
+ ParseFieldTrial(
+ {&first_allocation_probe_scale, &second_allocation_probe_scale,
+ &allocation_allow_further_probing, &allocation_probe_max},
+ key_value_config->Lookup("WebRTC-Bwe-AllocationProbing"));
+ ParseFieldTrial({&min_probe_packets_sent, &min_probe_duration},
+ key_value_config->Lookup("WebRTC-Bwe-ProbingBehavior"));
+}
+
+ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) =
+ default;
+ProbeControllerConfig::~ProbeControllerConfig() = default;
+
+ProbeController::ProbeController(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log)
+ : enable_periodic_alr_probing_(false),
+ in_rapid_recovery_experiment_(absl::StartsWith(
+ key_value_config->Lookup(kBweRapidRecoveryExperiment),
+ "Enabled")),
+ event_log_(event_log),
+ config_(ProbeControllerConfig(key_value_config)) {
+ Reset(Timestamp::Zero());
+}
+
+ProbeController::~ProbeController() {}
+
+std::vector<ProbeClusterConfig> ProbeController::SetBitrates(
+ DataRate min_bitrate,
+ DataRate start_bitrate,
+ DataRate max_bitrate,
+ Timestamp at_time) {
+ if (start_bitrate > DataRate::Zero()) {
+ start_bitrate_ = start_bitrate;
+ estimated_bitrate_ = start_bitrate;
+ } else if (start_bitrate_.IsZero()) {
+ start_bitrate_ = min_bitrate;
+ }
+
+ // The reason we use the variable `old_max_bitrate_pbs` is because we
+ // need to set `max_bitrate_` before we call InitiateProbing.
+ DataRate old_max_bitrate = max_bitrate_;
+ max_bitrate_ =
+ max_bitrate.IsFinite() ? max_bitrate : kDefaultMaxProbingBitrate;
+
+ switch (state_) {
+ case State::kInit:
+ if (network_available_)
+ return InitiateExponentialProbing(at_time);
+ break;
+
+ case State::kWaitingForProbingResult:
+ break;
+
+ case State::kProbingComplete:
+ // If the new max bitrate is higher than both the old max bitrate and the
+ // estimate then initiate probing.
+ if (!estimated_bitrate_.IsZero() && old_max_bitrate < max_bitrate_ &&
+ estimated_bitrate_ < max_bitrate_) {
+ return InitiateProbing(at_time, {max_bitrate_}, false);
+ }
+ break;
+ }
+ return std::vector<ProbeClusterConfig>();
+}
+
+std::vector<ProbeClusterConfig> ProbeController::OnMaxTotalAllocatedBitrate(
+ DataRate max_total_allocated_bitrate,
+ Timestamp at_time) {
+ const bool in_alr = alr_start_time_.has_value();
+ const bool allow_allocation_probe = in_alr;
+
+ if (config_.probe_on_max_allocated_bitrate_change &&
+ state_ == State::kProbingComplete &&
+ max_total_allocated_bitrate != max_total_allocated_bitrate_ &&
+ estimated_bitrate_ < max_bitrate_ &&
+ estimated_bitrate_ < max_total_allocated_bitrate &&
+ allow_allocation_probe) {
+ max_total_allocated_bitrate_ = max_total_allocated_bitrate;
+
+ if (!config_.first_allocation_probe_scale)
+ return std::vector<ProbeClusterConfig>();
+
+ DataRate first_probe_rate = max_total_allocated_bitrate *
+ config_.first_allocation_probe_scale.Value();
+ DataRate probe_cap = config_.allocation_probe_max.Get();
+ first_probe_rate = std::min(first_probe_rate, probe_cap);
+ std::vector<DataRate> probes = {first_probe_rate};
+ if (config_.second_allocation_probe_scale) {
+ DataRate second_probe_rate =
+ max_total_allocated_bitrate *
+ config_.second_allocation_probe_scale.Value();
+ second_probe_rate = std::min(second_probe_rate, probe_cap);
+ if (second_probe_rate > first_probe_rate)
+ probes.push_back(second_probe_rate);
+ }
+ return InitiateProbing(at_time, probes,
+ config_.allocation_allow_further_probing.Get());
+ }
+ max_total_allocated_bitrate_ = max_total_allocated_bitrate;
+ return std::vector<ProbeClusterConfig>();
+}
+
+std::vector<ProbeClusterConfig> ProbeController::OnNetworkAvailability(
+ NetworkAvailability msg) {
+ network_available_ = msg.network_available;
+
+ if (!network_available_ && state_ == State::kWaitingForProbingResult) {
+ state_ = State::kProbingComplete;
+ min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ }
+
+ if (network_available_ && state_ == State::kInit && !start_bitrate_.IsZero())
+ return InitiateExponentialProbing(msg.at_time);
+ return std::vector<ProbeClusterConfig>();
+}
+
+std::vector<ProbeClusterConfig> ProbeController::InitiateExponentialProbing(
+ Timestamp at_time) {
+ RTC_DCHECK(network_available_);
+ RTC_DCHECK(state_ == State::kInit);
+ RTC_DCHECK_GT(start_bitrate_, DataRate::Zero());
+
+ // When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
+ // 1.2 Mbps to continue probing.
+ std::vector<DataRate> probes = {config_.first_exponential_probe_scale *
+ start_bitrate_};
+ if (config_.second_exponential_probe_scale &&
+ config_.second_exponential_probe_scale.GetOptional().value() > 0) {
+ probes.push_back(config_.second_exponential_probe_scale.Value() *
+ start_bitrate_);
+ }
+ return InitiateProbing(at_time, probes, true);
+}
+
+std::vector<ProbeClusterConfig> ProbeController::SetEstimatedBitrate(
+ DataRate bitrate,
+ BandwidthLimitedCause bandwidth_limited_cause,
+ Timestamp at_time) {
+ bandwidth_limited_cause_ = bandwidth_limited_cause;
+ if (bitrate < kBitrateDropThreshold * estimated_bitrate_) {
+ time_of_last_large_drop_ = at_time;
+ bitrate_before_last_large_drop_ = estimated_bitrate_;
+ }
+ estimated_bitrate_ = bitrate;
+
+ if (state_ == State::kWaitingForProbingResult) {
+ // Continue probing if probing results indicate channel has greater
+ // capacity.
+ DataRate network_state_estimate_probe_further_limit =
+ config_.network_state_estimate_probing_interval->IsFinite() &&
+ network_estimate_
+ ? network_estimate_->link_capacity_upper *
+ config_.further_probe_threshold
+ : DataRate::PlusInfinity();
+ RTC_LOG(LS_INFO) << "Measured bitrate: " << bitrate
+ << " Minimum to probe further: "
+ << min_bitrate_to_probe_further_ << " upper limit: "
+ << network_state_estimate_probe_further_limit;
+
+ if (bitrate > min_bitrate_to_probe_further_ &&
+ bitrate <= network_state_estimate_probe_further_limit) {
+ return InitiateProbing(
+ at_time, {config_.further_exponential_probe_scale * bitrate}, true);
+ }
+ }
+ return {};
+}
+
+void ProbeController::EnablePeriodicAlrProbing(bool enable) {
+ enable_periodic_alr_probing_ = enable;
+}
+
+void ProbeController::SetAlrStartTimeMs(
+ absl::optional<int64_t> alr_start_time_ms) {
+ if (alr_start_time_ms) {
+ alr_start_time_ = Timestamp::Millis(*alr_start_time_ms);
+ } else {
+ alr_start_time_ = absl::nullopt;
+ }
+}
+void ProbeController::SetAlrEndedTimeMs(int64_t alr_end_time_ms) {
+ alr_end_time_.emplace(Timestamp::Millis(alr_end_time_ms));
+}
+
+std::vector<ProbeClusterConfig> ProbeController::RequestProbe(
+ Timestamp at_time) {
+ // Called once we have returned to normal state after a large drop in
+ // estimated bandwidth. The current response is to initiate a single probe
+ // session (if not already probing) at the previous bitrate.
+ //
+ // If the probe session fails, the assumption is that this drop was a
+ // real one from a competing flow or a network change.
+ bool in_alr = alr_start_time_.has_value();
+ bool alr_ended_recently =
+ (alr_end_time_.has_value() &&
+ at_time - alr_end_time_.value() < kAlrEndedTimeout);
+ if (in_alr || alr_ended_recently || in_rapid_recovery_experiment_) {
+ if (state_ == State::kProbingComplete) {
+ DataRate suggested_probe =
+ kProbeFractionAfterDrop * bitrate_before_last_large_drop_;
+ DataRate min_expected_probe_result =
+ (1 - kProbeUncertainty) * suggested_probe;
+ TimeDelta time_since_drop = at_time - time_of_last_large_drop_;
+ TimeDelta time_since_probe = at_time - last_bwe_drop_probing_time_;
+ if (min_expected_probe_result > estimated_bitrate_ &&
+ time_since_drop < kBitrateDropTimeout &&
+ time_since_probe > kMinTimeBetweenAlrProbes) {
+ RTC_LOG(LS_INFO) << "Detected big bandwidth drop, start probing.";
+ // Track how often we probe in response to bandwidth drop in ALR.
+ RTC_HISTOGRAM_COUNTS_10000(
+ "WebRTC.BWE.BweDropProbingIntervalInS",
+ (at_time - last_bwe_drop_probing_time_).seconds());
+ last_bwe_drop_probing_time_ = at_time;
+ return InitiateProbing(at_time, {suggested_probe}, false);
+ }
+ }
+ }
+ return std::vector<ProbeClusterConfig>();
+}
+
+void ProbeController::SetNetworkStateEstimate(
+ webrtc::NetworkStateEstimate estimate) {
+ network_estimate_ = estimate;
+}
+
+void ProbeController::Reset(Timestamp at_time) {
+ network_available_ = true;
+ bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited;
+ state_ = State::kInit;
+ min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ time_last_probing_initiated_ = Timestamp::Zero();
+ estimated_bitrate_ = DataRate::Zero();
+ network_estimate_ = absl::nullopt;
+ start_bitrate_ = DataRate::Zero();
+ max_bitrate_ = kDefaultMaxProbingBitrate;
+ Timestamp now = at_time;
+ last_bwe_drop_probing_time_ = now;
+ alr_end_time_.reset();
+ time_of_last_large_drop_ = now;
+ bitrate_before_last_large_drop_ = DataRate::Zero();
+ max_total_allocated_bitrate_ = DataRate::Zero();
+}
+
+bool ProbeController::TimeForAlrProbe(Timestamp at_time) const {
+ if (enable_periodic_alr_probing_ && alr_start_time_) {
+ Timestamp next_probe_time =
+ std::max(*alr_start_time_, time_last_probing_initiated_) +
+ config_.alr_probing_interval;
+ return at_time >= next_probe_time;
+ }
+ return false;
+}
+
+bool ProbeController::TimeForNetworkStateProbe(Timestamp at_time) const {
+ if (!network_estimate_ ||
+ network_estimate_->link_capacity_upper.IsInfinite()) {
+ return false;
+ }
+
+ bool probe_due_to_low_estimate =
+ bandwidth_limited_cause_ == BandwidthLimitedCause::kDelayBasedLimited &&
+ estimated_bitrate_ <
+ config_.probe_if_estimate_lower_than_network_state_estimate_ratio *
+ network_estimate_->link_capacity_upper;
+ if (probe_due_to_low_estimate &&
+ config_.estimate_lower_than_network_state_estimate_probing_interval
+ ->IsFinite()) {
+ Timestamp next_probe_time =
+ time_last_probing_initiated_ +
+ config_.estimate_lower_than_network_state_estimate_probing_interval;
+ return at_time >= next_probe_time;
+ }
+
+ bool periodic_probe =
+ estimated_bitrate_ < network_estimate_->link_capacity_upper;
+ if (periodic_probe &&
+ config_.network_state_estimate_probing_interval->IsFinite()) {
+ Timestamp next_probe_time = time_last_probing_initiated_ +
+ config_.network_state_estimate_probing_interval;
+ return at_time >= next_probe_time;
+ }
+
+ return false;
+}
+
+std::vector<ProbeClusterConfig> ProbeController::Process(Timestamp at_time) {
+ if (at_time - time_last_probing_initiated_ >
+ kMaxWaitingTimeForProbingResult) {
+ if (state_ == State::kWaitingForProbingResult) {
+ RTC_LOG(LS_INFO) << "kWaitingForProbingResult: timeout";
+ state_ = State::kProbingComplete;
+ min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ }
+ }
+ if (estimated_bitrate_.IsZero() || state_ != State::kProbingComplete) {
+ return {};
+ }
+ if (TimeForAlrProbe(at_time) || TimeForNetworkStateProbe(at_time)) {
+ return InitiateProbing(
+ at_time, {estimated_bitrate_ * config_.alr_probe_scale}, true);
+ }
+ return std::vector<ProbeClusterConfig>();
+}
+
+std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
+ Timestamp now,
+ std::vector<DataRate> bitrates_to_probe,
+ bool probe_further) {
+ if (config_.skip_if_estimate_larger_than_fraction_of_max > 0) {
+ DataRate network_estimate = network_estimate_
+ ? network_estimate_->link_capacity_upper
+ : DataRate::PlusInfinity();
+ DataRate max_probe_rate =
+ max_total_allocated_bitrate_.IsZero()
+ ? max_bitrate_
+ : std::min(max_total_allocated_bitrate_, max_bitrate_);
+ if (std::min(network_estimate, estimated_bitrate_) >
+ config_.skip_if_estimate_larger_than_fraction_of_max * max_probe_rate) {
+ state_ = State::kProbingComplete;
+ min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ return {};
+ }
+ }
+
+ DataRate max_probe_bitrate = max_bitrate_;
+ if (max_total_allocated_bitrate_ > DataRate::Zero()) {
+ // If a max allocated bitrate has been configured, allow probing up to 2x
+ // that rate. This allows some overhead to account for bursty streams,
+ // which otherwise would have to ramp up when the overshoot is already in
+ // progress.
+ // It also avoids minor quality reduction caused by probes often being
+ // received at slightly less than the target probe bitrate.
+ max_probe_bitrate =
+ std::min(max_probe_bitrate, max_total_allocated_bitrate_ * 2);
+ }
+
+ DataRate estimate_capped_bitrate = DataRate::PlusInfinity();
+ if (config_.limit_probe_target_rate_to_loss_bwe) {
+ switch (bandwidth_limited_cause_) {
+ case BandwidthLimitedCause::kLossLimitedBweDecreasing:
+ // If bandwidth estimate is decreasing because of packet loss, do not
+ // send probes.
+ return {};
+ case BandwidthLimitedCause::kLossLimitedBweIncreasing:
+ estimate_capped_bitrate =
+ std::min(max_probe_bitrate,
+ estimated_bitrate_ * config_.loss_limited_probe_scale);
+ break;
+ case BandwidthLimitedCause::kDelayBasedLimited:
+ break;
+ default:
+ break;
+ }
+ }
+ if (config_.not_probe_if_delay_increased &&
+ bandwidth_limited_cause_ ==
+ BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased) {
+ return {};
+ }
+
+ if (config_.network_state_estimate_probing_interval->IsFinite() &&
+ network_estimate_ && network_estimate_->link_capacity_upper.IsFinite()) {
+ if (network_estimate_->link_capacity_upper.IsZero()) {
+ RTC_LOG(LS_INFO) << "Not sending probe, Network state estimate is zero";
+ return {};
+ }
+ estimate_capped_bitrate =
+ std::min({estimate_capped_bitrate, max_probe_bitrate,
+ network_estimate_->link_capacity_upper *
+ config_.network_state_probe_scale});
+ }
+
+ std::vector<ProbeClusterConfig> pending_probes;
+ for (DataRate bitrate : bitrates_to_probe) {
+ RTC_DCHECK(!bitrate.IsZero());
+
+ bitrate = std::min(bitrate, estimate_capped_bitrate);
+ if (bitrate > max_probe_bitrate) {
+ bitrate = max_probe_bitrate;
+ probe_further = false;
+ }
+
+ ProbeClusterConfig config;
+ config.at_time = now;
+ config.target_data_rate = bitrate;
+ if (network_estimate_ &&
+ config_.network_state_estimate_probing_interval->IsFinite()) {
+ config.target_duration = config_.network_state_probe_duration;
+ } else {
+ config.target_duration = config_.min_probe_duration;
+ }
+
+ config.target_probe_count = config_.min_probe_packets_sent;
+ config.id = next_probe_cluster_id_;
+ next_probe_cluster_id_++;
+ MaybeLogProbeClusterCreated(event_log_, config);
+ pending_probes.push_back(config);
+ }
+ time_last_probing_initiated_ = now;
+ if (probe_further) {
+ state_ = State::kWaitingForProbingResult;
+ // Dont expect probe results to be larger than a fraction of the actual
+ // probe rate.
+ min_bitrate_to_probe_further_ =
+ std::min(estimate_capped_bitrate, (*(bitrates_to_probe.end() - 1))) *
+ config_.further_probe_threshold;
+ } else {
+ state_ = State::kProbingComplete;
+ min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ }
+ return pending_probes;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.h
new file mode 100644
index 0000000000..aa8b526ab0
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
+
+#include <stdint.h>
+
+#include <initializer_list>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/transport/network_control.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+struct ProbeControllerConfig {
+ explicit ProbeControllerConfig(const FieldTrialsView* key_value_config);
+ ProbeControllerConfig(const ProbeControllerConfig&);
+ ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default;
+ ~ProbeControllerConfig();
+
+ // These parameters configure the initial probes. First we send one or two
+ // probes of sizes p1 * start_bitrate_ and p2 * start_bitrate_.
+ // Then whenever we get a bitrate estimate of at least further_probe_threshold
+ // times the size of the last sent probe we'll send another one of size
+ // step_size times the new estimate.
+ FieldTrialParameter<double> first_exponential_probe_scale;
+ FieldTrialOptional<double> second_exponential_probe_scale;
+ FieldTrialParameter<double> further_exponential_probe_scale;
+ FieldTrialParameter<double> further_probe_threshold;
+
+ // Configures how often we send ALR probes and how big they are.
+ FieldTrialParameter<TimeDelta> alr_probing_interval;
+ FieldTrialParameter<double> alr_probe_scale;
+
+ // Configures how often we send probes if NetworkStateEstimate is available.
+ FieldTrialParameter<TimeDelta> network_state_estimate_probing_interval;
+ // Periodically probe as long as the the ratio beteeen current estimate and
+ // NetworkStateEstimate is lower then this.
+ FieldTrialParameter<double>
+ probe_if_estimate_lower_than_network_state_estimate_ratio;
+ FieldTrialParameter<TimeDelta>
+ estimate_lower_than_network_state_estimate_probing_interval;
+ FieldTrialParameter<double> network_state_probe_scale;
+ // Overrides min_probe_duration if network_state_estimate_probing_interval
+ // is set and a network state estimate is known.
+ FieldTrialParameter<TimeDelta> network_state_probe_duration;
+
+ // Configures the probes emitted by changed to the allocated bitrate.
+ FieldTrialParameter<bool> probe_on_max_allocated_bitrate_change;
+ FieldTrialOptional<double> first_allocation_probe_scale;
+ FieldTrialOptional<double> second_allocation_probe_scale;
+ FieldTrialFlag allocation_allow_further_probing;
+ FieldTrialParameter<DataRate> allocation_probe_max;
+
+ // The minimum number probing packets used.
+ FieldTrialParameter<int> min_probe_packets_sent;
+ // The minimum probing duration.
+ FieldTrialParameter<TimeDelta> min_probe_duration;
+ // Periodically probe when bandwidth estimate is loss limited.
+ FieldTrialParameter<bool> limit_probe_target_rate_to_loss_bwe;
+ FieldTrialParameter<double> loss_limited_probe_scale;
+ // Dont send a probe if min(estimate, network state estimate) is larger than
+ // this fraction of the set max bitrate.
+ FieldTrialParameter<double> skip_if_estimate_larger_than_fraction_of_max;
+ // Do not send probes if network is either overusing or underusing.
+ FieldTrialParameter<bool> not_probe_if_delay_increased;
+};
+
+// Reason that bandwidth estimate is limited. Bandwidth estimate can be limited
+// by either delay based bwe, or loss based bwe when it increases/decreases the
+// estimate.
+enum class BandwidthLimitedCause {
+ kLossLimitedBweIncreasing = 0,
+ kLossLimitedBweDecreasing = 1,
+ kDelayBasedLimited = 2,
+ kDelayBasedLimitedDelayIncreased = 3,
+};
+
+// This class controls initiation of probing to estimate initial channel
+// capacity. There is also support for probing during a session when max
+// bitrate is adjusted by an application.
+class ProbeController {
+ public:
+ explicit ProbeController(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log);
+ ~ProbeController();
+
+ ProbeController(const ProbeController&) = delete;
+ ProbeController& operator=(const ProbeController&) = delete;
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> SetBitrates(
+ DataRate min_bitrate,
+ DataRate start_bitrate,
+ DataRate max_bitrate,
+ Timestamp at_time);
+
+ // The total bitrate, as opposed to the max bitrate, is the sum of the
+ // configured bitrates for all active streams.
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig>
+ OnMaxTotalAllocatedBitrate(DataRate max_total_allocated_bitrate,
+ Timestamp at_time);
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> OnNetworkAvailability(
+ NetworkAvailability msg);
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> SetEstimatedBitrate(
+ DataRate bitrate,
+ BandwidthLimitedCause bandwidth_limited_cause,
+ Timestamp at_time);
+
+ void EnablePeriodicAlrProbing(bool enable);
+
+ void SetAlrStartTimeMs(absl::optional<int64_t> alr_start_time);
+ void SetAlrEndedTimeMs(int64_t alr_end_time);
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> RequestProbe(
+ Timestamp at_time);
+
+ void SetNetworkStateEstimate(webrtc::NetworkStateEstimate estimate);
+
+ // Resets the ProbeController to a state equivalent to as if it was just
+ // created EXCEPT for `enable_periodic_alr_probing_`.
+ void Reset(Timestamp at_time);
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> Process(
+ Timestamp at_time);
+
+ // Gets the value of field trial not_probe_if_delay_increased.
+ bool DontProbeIfDelayIncreased() {
+ return config_.not_probe_if_delay_increased;
+ }
+
+ private:
+ enum class State {
+ // Initial state where no probing has been triggered yet.
+ kInit,
+ // Waiting for probing results to continue further probing.
+ kWaitingForProbingResult,
+ // Probing is complete.
+ kProbingComplete,
+ };
+
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig>
+ InitiateExponentialProbing(Timestamp at_time);
+ ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> InitiateProbing(
+ Timestamp now,
+ std::vector<DataRate> bitrates_to_probe,
+ bool probe_further);
+ bool TimeForAlrProbe(Timestamp at_time) const;
+ bool TimeForNetworkStateProbe(Timestamp at_time) const;
+
+ bool network_available_;
+ BandwidthLimitedCause bandwidth_limited_cause_ =
+ BandwidthLimitedCause::kDelayBasedLimited;
+ State state_;
+ DataRate min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
+ Timestamp time_last_probing_initiated_ = Timestamp::MinusInfinity();
+ DataRate estimated_bitrate_ = DataRate::Zero();
+ absl::optional<webrtc::NetworkStateEstimate> network_estimate_;
+ DataRate start_bitrate_ = DataRate::Zero();
+ DataRate max_bitrate_ = DataRate::PlusInfinity();
+ Timestamp last_bwe_drop_probing_time_ = Timestamp::Zero();
+ absl::optional<Timestamp> alr_start_time_;
+ absl::optional<Timestamp> alr_end_time_;
+ bool enable_periodic_alr_probing_;
+ Timestamp time_of_last_large_drop_ = Timestamp::MinusInfinity();
+ DataRate bitrate_before_last_large_drop_ = DataRate::Zero();
+ DataRate max_total_allocated_bitrate_ = DataRate::Zero();
+
+ const bool in_rapid_recovery_experiment_;
+ RtcEventLog* event_log_;
+
+ int32_t next_probe_cluster_id_ = 1;
+
+ ProbeControllerConfig config_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build
new file mode 100644
index 0000000000..4f4f573cd9
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_gn/moz.build
@@ -0,0 +1,225 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("probe_controller_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
new file mode 100644
index 0000000000..e6a5c8ceef
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/probe_controller_unittest.cc
@@ -0,0 +1,1131 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/congestion_controller/goog_cc/probe_controller.h"
+
+#include <memory>
+
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/clock.h"
+#include "test/explicit_key_value_config.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::NiceMock;
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+constexpr DataRate kMinBitrate = DataRate::BitsPerSec(100);
+constexpr DataRate kStartBitrate = DataRate::BitsPerSec(300);
+constexpr DataRate kMaxBitrate = DataRate::BitsPerSec(10000);
+
+constexpr TimeDelta kExponentialProbingTimeout = TimeDelta::Seconds(5);
+
+constexpr TimeDelta kAlrProbeInterval = TimeDelta::Seconds(5);
+constexpr TimeDelta kAlrEndedTimeout = TimeDelta::Seconds(3);
+constexpr TimeDelta kBitrateDropTimeout = TimeDelta::Seconds(5);
+} // namespace
+
+class ProbeControllerFixture {
+ public:
+ explicit ProbeControllerFixture(absl::string_view field_trials = "")
+ : field_trial_config_(field_trials), clock_(100000000L) {}
+
+ std::unique_ptr<ProbeController> CreateController() {
+ return std::make_unique<ProbeController>(&field_trial_config_,
+ &mock_rtc_event_log);
+ }
+
+ Timestamp CurrentTime() { return clock_.CurrentTime(); }
+ void AdvanceTime(TimeDelta delta) { clock_.AdvanceTime(delta); }
+
+ ExplicitKeyValueConfig field_trial_config_;
+ SimulatedClock clock_;
+ NiceMock<MockRtcEventLog> mock_rtc_event_log;
+};
+
+TEST(ProbeControllerTest, InitiatesProbingAtStart) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_GE(probes.size(), 2u);
+}
+
+TEST(ProbeControllerTest, SetsDefaultTargetDurationAndTargetProbeCount) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ std::vector<ProbeClusterConfig> probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_GE(probes.size(), 2u);
+
+ EXPECT_EQ(probes[0].target_duration, TimeDelta::Millis(15));
+ EXPECT_EQ(probes[0].target_probe_count, 5);
+}
+
+TEST(ProbeControllerTest,
+ FieldTrialsOverrideDefaultTargetDurationAndTargetProbeCount) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingBehavior/"
+ "min_probe_packets_sent:2,min_probe_duration:123ms/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ std::vector<ProbeClusterConfig> probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_GE(probes.size(), 2u);
+
+ EXPECT_EQ(probes[0].target_duration, TimeDelta::Millis(123));
+ EXPECT_EQ(probes[0].target_probe_count, 2);
+}
+
+TEST(ProbeControllerTest, ProbeOnlyWhenNetworkIsUp) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->OnNetworkAvailability(
+ {.at_time = fixture.CurrentTime(), .network_available = false});
+ probes = probe_controller->SetBitrates(kMinBitrate, kStartBitrate,
+ kMaxBitrate, fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+ probes = probe_controller->OnNetworkAvailability(
+ {.at_time = fixture.CurrentTime(), .network_available = true});
+ EXPECT_GE(probes.size(), 2u);
+}
+
+TEST(ProbeControllerTest, CanConfigureInitialProbeRateFactor) {
+ ProbeControllerFixture fixture("WebRTC-Bwe-ProbingConfiguration/p1:2,p2:3/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ EXPECT_EQ(probes[0].target_data_rate, kStartBitrate * 2);
+ EXPECT_EQ(probes[1].target_data_rate, kStartBitrate * 3);
+}
+
+TEST(ProbeControllerTest, DisableSecondInitialProbeIfRateFactorZero) {
+ ProbeControllerFixture fixture("WebRTC-Bwe-ProbingConfiguration/p1:2,p2:0/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, kStartBitrate * 2);
+}
+
+TEST(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncrease) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ // Long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate + DataRate::BitsPerSec(100),
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), kMaxBitrate.bps() + 100);
+}
+
+TEST(ProbeControllerTest, ProbesOnMaxAllocatedBitrateIncreaseOnlyWhenInAlr) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate - DataRate::BitsPerSec(1),
+ BandwidthLimitedCause::kDelayBasedLimited, fixture.CurrentTime());
+
+ // Wait long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ // Probe when in alr.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(
+ kMaxBitrate + DataRate::BitsPerSec(1), fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ EXPECT_EQ(probes.at(0).target_data_rate, kMaxBitrate);
+
+ // Do not probe when not in alr.
+ probe_controller->SetAlrStartTimeMs(absl::nullopt);
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(
+ kMaxBitrate + DataRate::BitsPerSec(2), fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, CanDisableProbingOnMaxTotalAllocatedBitrateIncrease) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "probe_max_allocation:false/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate - DataRate::BitsPerSec(1),
+ BandwidthLimitedCause::kDelayBasedLimited, fixture.CurrentTime());
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+
+ // Do no probe, since probe_max_allocation:false.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(
+ kMaxBitrate + DataRate::BitsPerSec(1), fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncreaseAtMaxBitrate) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ // Long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate + DataRate::BitsPerSec(100),
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate,
+ kMaxBitrate + DataRate::BitsPerSec(100));
+}
+
+TEST(ProbeControllerTest, TestExponentialProbing) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+
+ // Repeated probe should only be sent when estimated bitrate climbs above
+ // 0.7 * 6 * kStartBitrate = 1260.
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(1000), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(1800), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * 1800);
+}
+
+TEST(ProbeControllerTest, TestExponentialProbingTimeout) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ // Advance far enough to cause a time out in waiting for probing result.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(1800), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, RequestProbeInAlr) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_GE(probes.size(), 2u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(250), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probes = probe_controller->RequestProbe(fixture.CurrentTime());
+
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 0.85 * 500);
+}
+
+TEST(ProbeControllerTest, RequestProbeWhenAlrEndedRecently) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ probe_controller->SetAlrStartTimeMs(absl::nullopt);
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(250), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probe_controller->SetAlrEndedTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrEndedTimeout - TimeDelta::Millis(1));
+ probes = probe_controller->RequestProbe(fixture.CurrentTime());
+
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 0.85 * 500);
+}
+
+TEST(ProbeControllerTest, RequestProbeWhenAlrNotEndedRecently) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ probe_controller->SetAlrStartTimeMs(absl::nullopt);
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(250), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probe_controller->SetAlrEndedTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrEndedTimeout + TimeDelta::Millis(1));
+ probes = probe_controller->RequestProbe(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, RequestProbeWhenBweDropNotRecent) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(250), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ fixture.AdvanceTime(kBitrateDropTimeout + TimeDelta::Millis(1));
+ probes = probe_controller->RequestProbe(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, PeriodicProbing) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ Timestamp start_time = fixture.CurrentTime();
+
+ // Expect the controller to send a new probe after 5s has passed.
+ probe_controller->SetAlrStartTimeMs(start_time.ms());
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 1000);
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ // The following probe should be sent at 10s into ALR.
+ probe_controller->SetAlrStartTimeMs(start_time.ms());
+ fixture.AdvanceTime(TimeDelta::Seconds(4));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ probe_controller->SetAlrStartTimeMs(start_time.ms());
+ fixture.AdvanceTime(TimeDelta::Seconds(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, PeriodicProbingAfterReset) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ Timestamp alr_start_time = fixture.CurrentTime();
+
+ probe_controller->SetAlrStartTimeMs(alr_start_time.ms());
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probe_controller->Reset(fixture.CurrentTime());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(10));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ // Since bitrates are not yet set, no probe is sent event though we are in ALR
+ // mode.
+ EXPECT_TRUE(probes.empty());
+
+ probes = probe_controller->SetBitrates(kMinBitrate, kStartBitrate,
+ kMaxBitrate, fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+
+ // Make sure we use `kStartBitrateBps` as the estimated bitrate
+ // until SetEstimatedBitrate is called with an updated estimate.
+ fixture.AdvanceTime(TimeDelta::Seconds(10));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, kStartBitrate * 2);
+}
+
+TEST(ProbeControllerTest, TestExponentialProbingOverflow) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ const DataRate kMbpsMultiplier = DataRate::KilobitsPerSec(1000);
+ auto probes = probe_controller->SetBitrates(kMinBitrate, 10 * kMbpsMultiplier,
+ 100 * kMbpsMultiplier,
+ fixture.CurrentTime());
+ // Verify that probe bitrate is capped at the specified max bitrate.
+ probes = probe_controller->SetEstimatedBitrate(
+ 60 * kMbpsMultiplier, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, 100 * kMbpsMultiplier);
+ // Verify that repeated probes aren't sent.
+ probes = probe_controller->SetEstimatedBitrate(
+ 100 * kMbpsMultiplier, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, TestAllocatedBitrateCap) {
+ ProbeControllerFixture fixture;
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ const DataRate kMbpsMultiplier = DataRate::KilobitsPerSec(1000);
+ const DataRate kMaxBitrate = 100 * kMbpsMultiplier;
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, 10 * kMbpsMultiplier, kMaxBitrate, fixture.CurrentTime());
+
+ // Configure ALR for periodic probing.
+ probe_controller->EnablePeriodicAlrProbing(true);
+ Timestamp alr_start_time = fixture.CurrentTime();
+ probe_controller->SetAlrStartTimeMs(alr_start_time.ms());
+
+ DataRate estimated_bitrate = kMaxBitrate / 10;
+ probes = probe_controller->SetEstimatedBitrate(
+ estimated_bitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ // Set a max allocated bitrate below the current estimate.
+ DataRate max_allocated = estimated_bitrate - 1 * kMbpsMultiplier;
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(max_allocated,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty()); // No probe since lower than current max.
+
+ // Probes such as ALR capped at 2x the max allocation limit.
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, 2 * max_allocated);
+
+ // Remove allocation limit.
+ EXPECT_TRUE(
+ probe_controller
+ ->OnMaxTotalAllocatedBitrate(DataRate::Zero(), fixture.CurrentTime())
+ .empty());
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, estimated_bitrate * 2);
+}
+
+TEST(ProbeControllerTest, ConfigurableProbingFieldTrial) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "p1:2,p2:5,step_size:3,further_probe_threshold:0.8,"
+ "alloc_p1:2,alloc_p2,min_probe_packets_sent:2/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(kMinBitrate, kStartBitrate,
+ DataRate::KilobitsPerSec(5000),
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 2u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 600);
+ EXPECT_EQ(probes[0].target_probe_count, 2);
+ EXPECT_EQ(probes[1].target_data_rate.bps(), 1500);
+ EXPECT_EQ(probes[1].target_probe_count, 2);
+
+ // Repeated probe should only be sent when estimated bitrate climbs above
+ // 0.8 * 5 * kStartBitrateBps = 1200.
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(1100), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 0u);
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(1250), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 3 * 1250);
+
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(
+ DataRate::KilobitsPerSec(200), fixture.CurrentTime());
+ EXPECT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate.bps(), 400'000);
+}
+
+TEST(ProbeControllerTest, LimitAlrProbeWhenLossBasedBweLimited) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ // Expect the controller to send a new probe after 5s has passed.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500),
+ BandwidthLimitedCause::kLossLimitedBweIncreasing, fixture.CurrentTime());
+ fixture.AdvanceTime(TimeDelta::Seconds(6));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, 1.5 * DataRate::BitsPerSec(500));
+
+ probes = probe_controller->SetEstimatedBitrate(
+ 1.5 * DataRate::BitsPerSec(500),
+ BandwidthLimitedCause::kDelayBasedLimited, fixture.CurrentTime());
+ fixture.AdvanceTime(TimeDelta::Seconds(6));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ EXPECT_GT(probes[0].target_data_rate, 1.5 * 1.5 * DataRate::BitsPerSec(500));
+}
+
+TEST(ProbeControllerTest, PeriodicProbeAtUpperNetworkStateEstimate) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/network_state_interval:5s/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(5000), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ // Expect the controller to send a new probe after 5s has passed.
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = DataRate::KilobitsPerSec(6);
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+}
+
+TEST(ProbeControllerTest,
+ LimitProbeAtUpperNetworkStateEstimateIfLossBasedLimited) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ // Expect the controller to send a new probe after 5s has passed.
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = DataRate::BitsPerSec(700);
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::BitsPerSec(500),
+ BandwidthLimitedCause::kLossLimitedBweIncreasing, fixture.CurrentTime());
+ // Expect the controller to send a new probe after 5s has passed.
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ EXPECT_EQ(probes[0].target_data_rate, DataRate::BitsPerSec(700));
+}
+
+TEST(ProbeControllerTest, AlrProbesLimitedByNetworkStateEstimate) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/network_state_interval:5s/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::KilobitsPerSec(6), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, kMaxBitrate);
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = DataRate::BitsPerSec(8000);
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+}
+
+TEST(ProbeControllerTest, CanSetLongerProbeDurationAfterNetworkStateEstimate) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,network_state_probe_duration:100ms/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ DataRate::KilobitsPerSec(5), BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ EXPECT_LT(probes[0].target_duration, TimeDelta::Millis(100));
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = DataRate::KilobitsPerSec(6);
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_duration, TimeDelta::Millis(100));
+}
+
+TEST(ProbeControllerTest, ProbeInAlrIfLossBasedIncreasing) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probe_controller->EnablePeriodicAlrProbing(true);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kLossLimitedBweIncreasing,
+ fixture.CurrentTime());
+
+ // Wait long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ // Probe when in alr.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes.at(0).target_data_rate, 1.5 * kStartBitrate);
+}
+
+TEST(ProbeControllerTest, ProbeFurtherInAlrIfLossBasedIncreasing) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probe_controller->EnablePeriodicAlrProbing(true);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kLossLimitedBweIncreasing,
+ fixture.CurrentTime());
+
+ // Wait long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ // Probe when in alr.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ ASSERT_EQ(probes.at(0).target_data_rate, 1.5 * kStartBitrate);
+
+ probes = probe_controller->SetEstimatedBitrate(
+ 1.5 * kStartBitrate, BandwidthLimitedCause::kLossLimitedBweIncreasing,
+ fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_EQ(probes[0].target_data_rate, 1.5 * 1.5 * kStartBitrate);
+}
+
+TEST(ProbeControllerTest, NotProbeWhenInAlrIfLossBasedDecreases) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probe_controller->EnablePeriodicAlrProbing(true);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kLossLimitedBweDecreasing,
+ fixture.CurrentTime());
+
+ // Wait long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ // Not probe in alr when loss based estimate decreases.
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, NotProbeIfLossBasedIncreasingOutsideAlr) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probe_controller->EnablePeriodicAlrProbing(true);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kLossLimitedBweIncreasing,
+ fixture.CurrentTime());
+
+ // Wait long enough to time out exponential probing.
+ fixture.AdvanceTime(kExponentialProbingTimeout);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ probe_controller->SetAlrStartTimeMs(absl::nullopt);
+ fixture.AdvanceTime(kAlrProbeInterval + TimeDelta::Millis(1));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, ProbeFurtherWhenLossBasedIsSameAsDelayBasedEstimate) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = 5 * kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ DataRate probe_target_rate = probes[0].target_data_rate;
+ EXPECT_LT(probe_target_rate, state_estimate.link_capacity_upper);
+ // Expect that more probes are sent if BWE is the same as delay based
+ // estimate.
+ probes = probe_controller->SetEstimatedBitrate(
+ probe_target_rate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ EXPECT_EQ(probes[0].target_data_rate, 2 * probe_target_rate);
+}
+
+TEST(ProbeControllerTest, ProbeIfEstimateLowerThanNetworkStateEstimate) {
+ // Periodic probe every 1 second if estimate is lower than 50% of the
+ // NetworkStateEstimate.
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/est_lower_than_network_interval:1s,"
+ "est_lower_than_network_ratio:0.5,limit_probe_"
+ "target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ state_estimate.link_capacity_upper = kStartBitrate * 3;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_EQ(probes.size(), 1u);
+ EXPECT_GT(probes[0].target_data_rate, kStartBitrate);
+
+ // If network state not increased, send another probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+
+ // Stop probing if estimate increase. We might probe further here though.
+ probes = probe_controller->SetEstimatedBitrate(
+ 2 * kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ // No more periodic probes.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, DontProbeFurtherWhenLossLimited) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = 3 * kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+ EXPECT_LT(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+ // Expect that no more probes are sent immediately if BWE is loss limited.
+ probes = probe_controller->SetEstimatedBitrate(
+ probes[0].target_data_rate,
+ BandwidthLimitedCause::kLossLimitedBweDecreasing, fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, ProbeFurtherWhenDelayBasedLimited) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = 3 * kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+ EXPECT_LT(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+ // Since the probe was successfull, expect to continue probing.
+ probes = probe_controller->SetEstimatedBitrate(
+ probes[0].target_data_rate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+ EXPECT_EQ(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+}
+
+TEST(ProbeControllerTest,
+ ProbeFurtherIfNetworkStateEstimateIncreaseAfterProbeSent) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = 1.2 * probes[0].target_data_rate / 2;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ // No immediate further probing since probe result is low.
+ probes = probe_controller->SetEstimatedBitrate(
+ probes[0].target_data_rate / 2, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ EXPECT_LE(probes[0].target_data_rate, state_estimate.link_capacity_upper);
+ // If the network state estimate increase above the threshold to probe
+ // further, and the probe suceeed, expect a new probe.
+ state_estimate.link_capacity_upper = 3 * kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ probes = probe_controller->SetEstimatedBitrate(
+ probes[0].target_data_rate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+
+ // But no more probes if estimate is close to the link capacity.
+ probes = probe_controller->SetEstimatedBitrate(
+ state_estimate.link_capacity_upper * 0.9,
+ BandwidthLimitedCause::kDelayBasedLimited, fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, SkipAlrProbeIfEstimateLargerThanMaxProbe) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "skip_if_est_larger_than_fraction_of_max:0.9/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ fixture.AdvanceTime(TimeDelta::Seconds(10));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ // But if the max rate increase, A new probe is sent.
+ probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, 2 * kMaxBitrate, fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+}
+
+TEST(ProbeControllerTest,
+ SkipAlrProbeIfEstimateLargerThanFractionOfMaxAllocated) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "skip_if_est_larger_than_fraction_of_max:1.0/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ probe_controller->EnablePeriodicAlrProbing(true);
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate / 2, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(10));
+ probe_controller->SetAlrStartTimeMs(fixture.CurrentTime().ms());
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(kMaxBitrate / 2,
+ fixture.CurrentTime());
+ // No probes since total allocated is not higher than the current estimate.
+ EXPECT_TRUE(probes.empty());
+ fixture.AdvanceTime(TimeDelta::Seconds(2));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ // But if the max allocated increase, A new probe is sent.
+ probes = probe_controller->OnMaxTotalAllocatedBitrate(
+ kMaxBitrate / 2 + DataRate::BitsPerSec(1), fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+}
+
+TEST(ProbeControllerTest, SkipNetworkStateProbeIfEstimateLargerThanMaxProbe) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:2s,skip_if_est_larger_than_fraction_of_max:0.9/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ probe_controller->SetNetworkStateEstimate(
+ {.link_capacity_upper = 2 * kMaxBitrate});
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(10));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, SendsProbeIfNetworkStateEstimateLowerThanMaxProbe) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:2s,skip_if_est_larger_than_fraction_of_max:0.9,"
+ "/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+ probe_controller->SetNetworkStateEstimate(
+ {.link_capacity_upper = 2 * kMaxBitrate});
+ probes = probe_controller->SetEstimatedBitrate(
+ kMaxBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+
+ // Need to wait at least two seconds before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(2100));
+
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+ probe_controller->SetNetworkStateEstimate(
+ {.link_capacity_upper = 2 * kStartBitrate});
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_FALSE(probes.empty());
+}
+
+TEST(ProbeControllerTest, DontSendProbeIfNetworkStateEstimateIsZero) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,limit_probe_target_rate_to_loss_bwe:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimited,
+ fixture.CurrentTime());
+ probe_controller->SetNetworkStateEstimate(
+ {.link_capacity_upper = kStartBitrate});
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ probe_controller->SetNetworkStateEstimate(
+ {.link_capacity_upper = DataRate::Zero()});
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+ fixture.AdvanceTime(TimeDelta::Seconds(6));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+
+TEST(ProbeControllerTest, DontProbeIfDelayIncreased) {
+ ProbeControllerFixture fixture(
+ "WebRTC-Bwe-ProbingConfiguration/"
+ "network_state_interval:5s,not_probe_if_delay_increased:true/");
+ std::unique_ptr<ProbeController> probe_controller =
+ fixture.CreateController();
+
+ auto probes = probe_controller->SetBitrates(
+ kMinBitrate, kStartBitrate, kMaxBitrate, fixture.CurrentTime());
+ ASSERT_FALSE(probes.empty());
+
+ // Need to wait at least one second before process can trigger a new probe.
+ fixture.AdvanceTime(TimeDelta::Millis(1100));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ NetworkStateEstimate state_estimate;
+ state_estimate.link_capacity_upper = 3 * kStartBitrate;
+ probe_controller->SetNetworkStateEstimate(state_estimate);
+ probes = probe_controller->SetEstimatedBitrate(
+ kStartBitrate, BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased,
+ fixture.CurrentTime());
+ ASSERT_TRUE(probes.empty());
+
+ fixture.AdvanceTime(TimeDelta::Seconds(5));
+ probes = probe_controller->Process(fixture.CurrentTime());
+ EXPECT_TRUE(probes.empty());
+}
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build
new file mode 100644
index 0000000000..e189363d3e
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/pushback_controller_gn/moz.build
@@ -0,0 +1,225 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("pushback_controller_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc
new file mode 100644
index 0000000000..792a93d41e
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+RobustThroughputEstimator::RobustThroughputEstimator(
+ const RobustThroughputEstimatorSettings& settings)
+ : settings_(settings),
+ latest_discarded_send_time_(Timestamp::MinusInfinity()) {
+ RTC_DCHECK(settings.enabled);
+}
+
+RobustThroughputEstimator::~RobustThroughputEstimator() {}
+
+bool RobustThroughputEstimator::FirstPacketOutsideWindow() {
+ if (window_.empty())
+ return false;
+ if (window_.size() > settings_.max_window_packets)
+ return true;
+ TimeDelta current_window_duration =
+ window_.back().receive_time - window_.front().receive_time;
+ if (current_window_duration > settings_.max_window_duration)
+ return true;
+ if (window_.size() > settings_.window_packets &&
+ current_window_duration > settings_.min_window_duration) {
+ return true;
+ }
+ return false;
+}
+
+void RobustThroughputEstimator::IncomingPacketFeedbackVector(
+ const std::vector<PacketResult>& packet_feedback_vector) {
+ RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
+ packet_feedback_vector.end(),
+ PacketResult::ReceiveTimeOrder()));
+ for (const auto& packet : packet_feedback_vector) {
+ // Ignore packets without valid send or receive times.
+ // (This should not happen in production since lost packets are filtered
+ // out before passing the feedback vector to the throughput estimator.
+ // However, explicitly handling this case makes the estimator more robust
+ // and avoids a hard-to-detect bad state.)
+ if (packet.receive_time.IsInfinite() ||
+ packet.sent_packet.send_time.IsInfinite()) {
+ continue;
+ }
+
+ // Insert the new packet.
+ window_.push_back(packet);
+ window_.back().sent_packet.prior_unacked_data =
+ window_.back().sent_packet.prior_unacked_data *
+ settings_.unacked_weight;
+ // In most cases, receive timestamps should already be in order, but in the
+ // rare case where feedback packets have been reordered, we do some swaps to
+ // ensure that the window is sorted.
+ for (size_t i = window_.size() - 1;
+ i > 0 && window_[i].receive_time < window_[i - 1].receive_time; i--) {
+ std::swap(window_[i], window_[i - 1]);
+ }
+ }
+
+ // Remove old packets.
+ while (FirstPacketOutsideWindow()) {
+ latest_discarded_send_time_ = std::max(
+ latest_discarded_send_time_, window_.front().sent_packet.send_time);
+ window_.pop_front();
+ }
+}
+
+absl::optional<DataRate> RobustThroughputEstimator::bitrate() const {
+ if (window_.empty() || window_.size() < settings_.required_packets)
+ return absl::nullopt;
+
+ TimeDelta largest_recv_gap(TimeDelta::Zero());
+ TimeDelta second_largest_recv_gap(TimeDelta::Zero());
+ for (size_t i = 1; i < window_.size(); i++) {
+ // Find receive time gaps.
+ TimeDelta gap = window_[i].receive_time - window_[i - 1].receive_time;
+ if (gap > largest_recv_gap) {
+ second_largest_recv_gap = largest_recv_gap;
+ largest_recv_gap = gap;
+ } else if (gap > second_largest_recv_gap) {
+ second_largest_recv_gap = gap;
+ }
+ }
+
+ Timestamp first_send_time = Timestamp::PlusInfinity();
+ Timestamp last_send_time = Timestamp::MinusInfinity();
+ Timestamp first_recv_time = Timestamp::PlusInfinity();
+ Timestamp last_recv_time = Timestamp::MinusInfinity();
+ DataSize recv_size = DataSize::Bytes(0);
+ DataSize send_size = DataSize::Bytes(0);
+ DataSize first_recv_size = DataSize::Bytes(0);
+ DataSize last_send_size = DataSize::Bytes(0);
+ size_t num_sent_packets_in_window = 0;
+ for (const auto& packet : window_) {
+ if (packet.receive_time < first_recv_time) {
+ first_recv_time = packet.receive_time;
+ first_recv_size =
+ packet.sent_packet.size + packet.sent_packet.prior_unacked_data;
+ }
+ last_recv_time = std::max(last_recv_time, packet.receive_time);
+ recv_size += packet.sent_packet.size;
+ recv_size += packet.sent_packet.prior_unacked_data;
+
+ if (packet.sent_packet.send_time < latest_discarded_send_time_) {
+ // If we have dropped packets from the window that were sent after
+ // this packet, then this packet was reordered. Ignore it from
+ // the send rate computation (since the send time may be very far
+ // in the past, leading to underestimation of the send rate.)
+ // However, ignoring packets creates a risk that we end up without
+ // any packets left to compute a send rate.
+ continue;
+ }
+ if (packet.sent_packet.send_time > last_send_time) {
+ last_send_time = packet.sent_packet.send_time;
+ last_send_size =
+ packet.sent_packet.size + packet.sent_packet.prior_unacked_data;
+ }
+ first_send_time = std::min(first_send_time, packet.sent_packet.send_time);
+
+ send_size += packet.sent_packet.size;
+ send_size += packet.sent_packet.prior_unacked_data;
+ ++num_sent_packets_in_window;
+ }
+
+ // Suppose a packet of size S is sent every T milliseconds.
+ // A window of N packets would contain N*S bytes, but the time difference
+ // between the first and the last packet would only be (N-1)*T. Thus, we
+ // need to remove the size of one packet to get the correct rate of S/T.
+ // Which packet to remove (if the packets have varying sizes),
+ // depends on the network model.
+ // Suppose that 2 packets with sizes s1 and s2, are received at times t1
+ // and t2, respectively. If the packets were transmitted back to back over
+ // a bottleneck with rate capacity r, then we'd expect t2 = t1 + r * s2.
+ // Thus, r = (t2-t1) / s2, so the size of the first packet doesn't affect
+ // the difference between t1 and t2.
+ // Analoguously, if the first packet is sent at time t1 and the sender
+ // paces the packets at rate r, then the second packet can be sent at time
+ // t2 = t1 + r * s1. Thus, the send rate estimate r = (t2-t1) / s1 doesn't
+ // depend on the size of the last packet.
+ recv_size -= first_recv_size;
+ send_size -= last_send_size;
+
+ // Remove the largest gap by replacing it by the second largest gap.
+ // This is to ensure that spurious "delay spikes" (i.e. when the
+ // network stops transmitting packets for a short period, followed
+ // by a burst of delayed packets), don't cause the estimate to drop.
+ // This could cause an overestimation, which we guard against by
+ // never returning an estimate above the send rate.
+ RTC_DCHECK(first_recv_time.IsFinite());
+ RTC_DCHECK(last_recv_time.IsFinite());
+ TimeDelta recv_duration = (last_recv_time - first_recv_time) -
+ largest_recv_gap + second_largest_recv_gap;
+ recv_duration = std::max(recv_duration, TimeDelta::Millis(1));
+
+ if (num_sent_packets_in_window < settings_.required_packets) {
+ // Too few send times to calculate a reliable send rate.
+ return recv_size / recv_duration;
+ }
+
+ RTC_DCHECK(first_send_time.IsFinite());
+ RTC_DCHECK(last_send_time.IsFinite());
+ TimeDelta send_duration = last_send_time - first_send_time;
+ send_duration = std::max(send_duration, TimeDelta::Millis(1));
+
+ return std::min(send_size / send_duration, recv_size / recv_duration);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.h
new file mode 100644
index 0000000000..9d89856496
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
+
+#include <deque>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/timestamp.h"
+#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
+
+namespace webrtc {
+
+class RobustThroughputEstimator : public AcknowledgedBitrateEstimatorInterface {
+ public:
+ explicit RobustThroughputEstimator(
+ const RobustThroughputEstimatorSettings& settings);
+ ~RobustThroughputEstimator() override;
+
+ void IncomingPacketFeedbackVector(
+ const std::vector<PacketResult>& packet_feedback_vector) override;
+
+ absl::optional<DataRate> bitrate() const override;
+
+ absl::optional<DataRate> PeekRate() const override { return bitrate(); }
+ void SetAlr(bool /*in_alr*/) override {}
+ void SetAlrEndedTime(Timestamp /*alr_ended_time*/) override {}
+
+ private:
+ bool FirstPacketOutsideWindow();
+
+ const RobustThroughputEstimatorSettings settings_;
+ std::deque<PacketResult> window_;
+ Timestamp latest_discarded_send_time_ = Timestamp::MinusInfinity();
+};
+
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc
new file mode 100644
index 0000000000..95ac525640
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "test/explicit_key_value_config.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+RobustThroughputEstimatorSettings CreateRobustThroughputEstimatorSettings(
+ absl::string_view field_trial_string) {
+ test::ExplicitKeyValueConfig trials(field_trial_string);
+ RobustThroughputEstimatorSettings settings(&trials);
+ return settings;
+}
+
+class FeedbackGenerator {
+ public:
+ std::vector<PacketResult> CreateFeedbackVector(size_t number_of_packets,
+ DataSize packet_size,
+ DataRate send_rate,
+ DataRate recv_rate) {
+ std::vector<PacketResult> packet_feedback_vector(number_of_packets);
+ for (size_t i = 0; i < number_of_packets; i++) {
+ packet_feedback_vector[i].sent_packet.send_time = send_clock_;
+ packet_feedback_vector[i].sent_packet.sequence_number = sequence_number_;
+ packet_feedback_vector[i].sent_packet.size = packet_size;
+ send_clock_ += packet_size / send_rate;
+ recv_clock_ += packet_size / recv_rate;
+ sequence_number_ += 1;
+ packet_feedback_vector[i].receive_time = recv_clock_;
+ }
+ return packet_feedback_vector;
+ }
+
+ Timestamp CurrentReceiveClock() { return recv_clock_; }
+
+ void AdvanceReceiveClock(TimeDelta delta) { recv_clock_ += delta; }
+
+ void AdvanceSendClock(TimeDelta delta) { send_clock_ += delta; }
+
+ private:
+ Timestamp send_clock_ = Timestamp::Millis(100000);
+ Timestamp recv_clock_ = Timestamp::Millis(10000);
+ uint16_t sequence_number_ = 100;
+};
+
+TEST(RobustThroughputEstimatorTest, InitialEstimate) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ // No estimate until the estimator has enough data.
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(9, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ EXPECT_FALSE(throughput_estimator.bitrate().has_value());
+
+ // Estimate once `required_packets` packets have been received.
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 1, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+
+ // Estimate remains stable when send and receive rates are stable.
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 15, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+}
+
+TEST(RobustThroughputEstimatorTest, EstimateAdapts) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+
+ // 1 second, 800kbps, estimate is stable.
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+ for (int i = 0; i < 10; ++i) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+
+ // 1 second, 1600kbps, estimate increases
+ send_rate = DataRate::BytesPerSec(200000);
+ recv_rate = DataRate::BytesPerSec(200000);
+ for (int i = 0; i < 20; ++i) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_GE(throughput.value(), DataRate::BytesPerSec(100000));
+ EXPECT_LE(throughput.value(), send_rate);
+ }
+
+ // 1 second, 1600kbps, estimate is stable
+ for (int i = 0; i < 20; ++i) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+
+ // 1 second, 400kbps, estimate decreases
+ send_rate = DataRate::BytesPerSec(50000);
+ recv_rate = DataRate::BytesPerSec(50000);
+ for (int i = 0; i < 5; ++i) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_LE(throughput.value(), DataRate::BytesPerSec(200000));
+ EXPECT_GE(throughput.value(), send_rate);
+ }
+
+ // 1 second, 400kbps, estimate is stable
+ send_rate = DataRate::BytesPerSec(50000);
+ recv_rate = DataRate::BytesPerSec(50000);
+ for (int i = 0; i < 5; ++i) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+}
+
+TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(25000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ recv_rate.bytes_per_sec<double>(),
+ 0.05 * recv_rate.bytes_per_sec<double>()); // Allow 5% error
+}
+
+TEST(RobustThroughputEstimatorTest, CappedBySendRate) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(50000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>(),
+ 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error
+}
+
+TEST(RobustThroughputEstimatorTest, DelaySpike) {
+ FeedbackGenerator feedback_generator;
+ // This test uses a 500ms window to amplify the effect
+ // of a delay spike.
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true,window_duration:500ms/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+
+ // Delay spike. 25 packets sent, but none received.
+ feedback_generator.AdvanceReceiveClock(TimeDelta::Millis(250));
+
+ // Deliver all of the packets during the next 50 ms. (During this time,
+ // we'll have sent an additional 5 packets, so we need to receive 30
+ // packets at 1000 bytes each in 50 ms, i.e. 600000 bytes per second).
+ recv_rate = DataRate::BytesPerSec(600000);
+ // Estimate should not drop.
+ for (int i = 0; i < 30; ++i) {
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 1, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>(),
+ 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error
+ }
+
+ // Delivery at normal rate. When the packets received before the gap
+ // has left the estimator's window, the receive rate will be high, but the
+ // estimate should be capped by the send rate.
+ recv_rate = DataRate::BytesPerSec(100000);
+ for (int i = 0; i < 20; ++i) {
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 5, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>(),
+ 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error
+ }
+}
+
+TEST(RobustThroughputEstimatorTest, HighLoss) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+
+ // 50% loss
+ for (size_t i = 0; i < packet_feedback.size(); i++) {
+ if (i % 2 == 1) {
+ packet_feedback[i].receive_time = Timestamp::PlusInfinity();
+ }
+ }
+
+ std::sort(packet_feedback.begin(), packet_feedback.end(),
+ PacketResult::ReceiveTimeOrder());
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>() / 2,
+ 0.05 * send_rate.bytes_per_sec<double>() / 2); // Allow 5% error
+}
+
+TEST(RobustThroughputEstimatorTest, ReorderedFeedback) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+
+ std::vector<PacketResult> delayed_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 10, DataSize::Bytes(1000), send_rate, recv_rate);
+
+ // Since we're missing some feedback, it's expected that the
+ // estimate will drop.
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_LT(throughput.value(), send_rate);
+
+ // But it should completely recover as soon as we get the feedback.
+ throughput_estimator.IncomingPacketFeedbackVector(delayed_feedback);
+ throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+
+ // It should then remain stable (as if the feedbacks weren't reordered.)
+ for (int i = 0; i < 10; ++i) {
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 15, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+}
+
+TEST(RobustThroughputEstimatorTest, DeepReordering) {
+ FeedbackGenerator feedback_generator;
+ // This test uses a 500ms window to amplify the
+ // effect of reordering.
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true,window_duration:500ms/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> delayed_packets =
+ feedback_generator.CreateFeedbackVector(1, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+
+ for (int i = 0; i < 10; i++) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+
+ // Delayed packet arrives ~1 second after it should have.
+ // Since the window is 500 ms, the delayed packet was sent ~500
+ // ms before the second oldest packet. However, the send rate
+ // should not drop.
+ delayed_packets.front().receive_time =
+ feedback_generator.CurrentReceiveClock();
+ throughput_estimator.IncomingPacketFeedbackVector(delayed_packets);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>(),
+ 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error
+
+ // Thoughput should stay stable.
+ for (int i = 0; i < 10; i++) {
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ ASSERT_TRUE(throughput.has_value());
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ send_rate.bytes_per_sec<double>(),
+ 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error
+ }
+}
+
+TEST(RobustThroughputEstimatorTest, StreamPausedAndResumed) {
+ FeedbackGenerator feedback_generator;
+ RobustThroughputEstimator throughput_estimator(
+ CreateRobustThroughputEstimatorSettings(
+ "WebRTC-Bwe-RobustThroughputEstimatorSettings/"
+ "enabled:true/"));
+ DataRate send_rate(DataRate::BytesPerSec(100000));
+ DataRate recv_rate(DataRate::BytesPerSec(100000));
+
+ std::vector<PacketResult> packet_feedback =
+ feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000),
+ send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ auto throughput = throughput_estimator.bitrate();
+ EXPECT_TRUE(throughput.has_value());
+ double expected_bytes_per_sec = 100 * 1000.0;
+ EXPECT_NEAR(throughput.value().bytes_per_sec<double>(),
+ expected_bytes_per_sec,
+ 0.05 * expected_bytes_per_sec); // Allow 5% error
+
+ // No packets sent or feedback received for 60s.
+ feedback_generator.AdvanceSendClock(TimeDelta::Seconds(60));
+ feedback_generator.AdvanceReceiveClock(TimeDelta::Seconds(60));
+
+ // Resume sending packets at the same rate as before. The estimate
+ // will initially be invalid, due to lack of recent data.
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 5, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ EXPECT_FALSE(throughput.has_value());
+
+ // But be back to the normal level once we have enough data.
+ for (int i = 0; i < 4; ++i) {
+ packet_feedback = feedback_generator.CreateFeedbackVector(
+ 5, DataSize::Bytes(1000), send_rate, recv_rate);
+ throughput_estimator.IncomingPacketFeedbackVector(packet_feedback);
+ throughput = throughput_estimator.bitrate();
+ EXPECT_EQ(throughput, send_rate);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
new file mode 100644
index 0000000000..1e4db1ffaf
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <limits>
+#include <memory>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/rtc_event_log/rtc_event.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
+#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+namespace {
+constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000);
+constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300);
+constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000);
+constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000);
+constexpr int kLimitNumPackets = 20;
+constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000);
+constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000);
+constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000);
+// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals.
+constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
+
+constexpr float kDefaultLowLossThreshold = 0.02f;
+constexpr float kDefaultHighLossThreshold = 0.1f;
+constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero();
+
+struct UmaRampUpMetric {
+ const char* metric_name;
+ int bitrate_kbps;
+};
+
+const UmaRampUpMetric kUmaRampupMetrics[] = {
+ {"WebRTC.BWE.RampUpTimeTo500kbpsInMs", 500},
+ {"WebRTC.BWE.RampUpTimeTo1000kbpsInMs", 1000},
+ {"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
+const size_t kNumUmaRampupMetrics =
+ sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
+
+const char kBweLosExperiment[] = "WebRTC-BweLossExperiment";
+
+bool BweLossExperimentIsEnabled() {
+ std::string experiment_string =
+ webrtc::field_trial::FindFullName(kBweLosExperiment);
+ // The experiment is enabled iff the field trial string begins with "Enabled".
+ return absl::StartsWith(experiment_string, "Enabled");
+}
+
+bool ReadBweLossExperimentParameters(float* low_loss_threshold,
+ float* high_loss_threshold,
+ uint32_t* bitrate_threshold_kbps) {
+ RTC_DCHECK(low_loss_threshold);
+ RTC_DCHECK(high_loss_threshold);
+ RTC_DCHECK(bitrate_threshold_kbps);
+ std::string experiment_string =
+ webrtc::field_trial::FindFullName(kBweLosExperiment);
+ int parsed_values =
+ sscanf(experiment_string.c_str(), "Enabled-%f,%f,%u", low_loss_threshold,
+ high_loss_threshold, bitrate_threshold_kbps);
+ if (parsed_values == 3) {
+ RTC_CHECK_GT(*low_loss_threshold, 0.0f)
+ << "Loss threshold must be greater than 0.";
+ RTC_CHECK_LE(*low_loss_threshold, 1.0f)
+ << "Loss threshold must be less than or equal to 1.";
+ RTC_CHECK_GT(*high_loss_threshold, 0.0f)
+ << "Loss threshold must be greater than 0.";
+ RTC_CHECK_LE(*high_loss_threshold, 1.0f)
+ << "Loss threshold must be less than or equal to 1.";
+ RTC_CHECK_LE(*low_loss_threshold, *high_loss_threshold)
+ << "The low loss threshold must be less than or equal to the high loss "
+ "threshold.";
+ RTC_CHECK_GE(*bitrate_threshold_kbps, 0)
+ << "Bitrate threshold can't be negative.";
+ RTC_CHECK_LT(*bitrate_threshold_kbps,
+ std::numeric_limits<int>::max() / 1000)
+ << "Bitrate must be smaller enough to avoid overflows.";
+ return true;
+ }
+ RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweLossExperiment "
+ "experiment from field trial string. Using default.";
+ *low_loss_threshold = kDefaultLowLossThreshold;
+ *high_loss_threshold = kDefaultHighLossThreshold;
+ *bitrate_threshold_kbps = kDefaultBitrateThreshold.kbps();
+ return false;
+}
+} // namespace
+
+LinkCapacityTracker::LinkCapacityTracker()
+ : tracking_rate("rate", TimeDelta::Seconds(10)) {
+ ParseFieldTrial({&tracking_rate},
+ field_trial::FindFullName("WebRTC-Bwe-LinkCapacity"));
+}
+
+LinkCapacityTracker::~LinkCapacityTracker() {}
+
+void LinkCapacityTracker::UpdateDelayBasedEstimate(
+ Timestamp at_time,
+ DataRate delay_based_bitrate) {
+ if (delay_based_bitrate < last_delay_based_estimate_) {
+ capacity_estimate_bps_ =
+ std::min(capacity_estimate_bps_, delay_based_bitrate.bps<double>());
+ last_link_capacity_update_ = at_time;
+ }
+ last_delay_based_estimate_ = delay_based_bitrate;
+}
+
+void LinkCapacityTracker::OnStartingRate(DataRate start_rate) {
+ if (last_link_capacity_update_.IsInfinite())
+ capacity_estimate_bps_ = start_rate.bps<double>();
+}
+
+void LinkCapacityTracker::OnRateUpdate(absl::optional<DataRate> acknowledged,
+ DataRate target,
+ Timestamp at_time) {
+ if (!acknowledged)
+ return;
+ DataRate acknowledged_target = std::min(*acknowledged, target);
+ if (acknowledged_target.bps() > capacity_estimate_bps_) {
+ TimeDelta delta = at_time - last_link_capacity_update_;
+ double alpha = delta.IsFinite() ? exp(-(delta / tracking_rate.Get())) : 0;
+ capacity_estimate_bps_ = alpha * capacity_estimate_bps_ +
+ (1 - alpha) * acknowledged_target.bps<double>();
+ }
+ last_link_capacity_update_ = at_time;
+}
+
+void LinkCapacityTracker::OnRttBackoff(DataRate backoff_rate,
+ Timestamp at_time) {
+ capacity_estimate_bps_ =
+ std::min(capacity_estimate_bps_, backoff_rate.bps<double>());
+ last_link_capacity_update_ = at_time;
+}
+
+DataRate LinkCapacityTracker::estimate() const {
+ return DataRate::BitsPerSec(capacity_estimate_bps_);
+}
+
+RttBasedBackoff::RttBasedBackoff(const FieldTrialsView* key_value_config)
+ : disabled_("Disabled"),
+ configured_limit_("limit", TimeDelta::Seconds(3)),
+ drop_fraction_("fraction", 0.8),
+ drop_interval_("interval", TimeDelta::Seconds(1)),
+ bandwidth_floor_("floor", DataRate::KilobitsPerSec(5)),
+ rtt_limit_(TimeDelta::PlusInfinity()),
+ // By initializing this to plus infinity, we make sure that we never
+ // trigger rtt backoff unless packet feedback is enabled.
+ last_propagation_rtt_update_(Timestamp::PlusInfinity()),
+ last_propagation_rtt_(TimeDelta::Zero()),
+ last_packet_sent_(Timestamp::MinusInfinity()) {
+ ParseFieldTrial({&disabled_, &configured_limit_, &drop_fraction_,
+ &drop_interval_, &bandwidth_floor_},
+ key_value_config->Lookup("WebRTC-Bwe-MaxRttLimit"));
+ if (!disabled_) {
+ rtt_limit_ = configured_limit_.Get();
+ }
+}
+
+void RttBasedBackoff::UpdatePropagationRtt(Timestamp at_time,
+ TimeDelta propagation_rtt) {
+ last_propagation_rtt_update_ = at_time;
+ last_propagation_rtt_ = propagation_rtt;
+}
+
+TimeDelta RttBasedBackoff::CorrectedRtt(Timestamp at_time) const {
+ TimeDelta time_since_rtt = at_time - last_propagation_rtt_update_;
+ TimeDelta timeout_correction = time_since_rtt;
+ // Avoid timeout when no packets are being sent.
+ TimeDelta time_since_packet_sent = at_time - last_packet_sent_;
+ timeout_correction =
+ std::max(time_since_rtt - time_since_packet_sent, TimeDelta::Zero());
+ return timeout_correction + last_propagation_rtt_;
+}
+
+RttBasedBackoff::~RttBasedBackoff() = default;
+
+SendSideBandwidthEstimation::SendSideBandwidthEstimation(
+ const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log)
+ : rtt_backoff_(key_value_config),
+ lost_packets_since_last_loss_update_(0),
+ expected_packets_since_last_loss_update_(0),
+ current_target_(DataRate::Zero()),
+ last_logged_target_(DataRate::Zero()),
+ min_bitrate_configured_(kCongestionControllerMinBitrate),
+ max_bitrate_configured_(kDefaultMaxBitrate),
+ last_low_bitrate_log_(Timestamp::MinusInfinity()),
+ has_decreased_since_last_fraction_loss_(false),
+ last_loss_feedback_(Timestamp::MinusInfinity()),
+ last_loss_packet_report_(Timestamp::MinusInfinity()),
+ last_fraction_loss_(0),
+ last_logged_fraction_loss_(0),
+ last_round_trip_time_(TimeDelta::Zero()),
+ receiver_limit_(DataRate::PlusInfinity()),
+ delay_based_limit_(DataRate::PlusInfinity()),
+ time_last_decrease_(Timestamp::MinusInfinity()),
+ first_report_time_(Timestamp::MinusInfinity()),
+ initially_lost_packets_(0),
+ bitrate_at_2_seconds_(DataRate::Zero()),
+ uma_update_state_(kNoUpdate),
+ uma_rtt_state_(kNoUpdate),
+ rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
+ event_log_(event_log),
+ last_rtc_event_log_(Timestamp::MinusInfinity()),
+ low_loss_threshold_(kDefaultLowLossThreshold),
+ high_loss_threshold_(kDefaultHighLossThreshold),
+ bitrate_threshold_(kDefaultBitrateThreshold),
+ loss_based_bandwidth_estimator_v1_(key_value_config),
+ loss_based_bandwidth_estimator_v2_(key_value_config),
+ loss_based_state_(LossBasedState::kDelayBasedEstimate),
+ disable_receiver_limit_caps_only_("Disabled") {
+ RTC_DCHECK(event_log);
+ if (BweLossExperimentIsEnabled()) {
+ uint32_t bitrate_threshold_kbps;
+ if (ReadBweLossExperimentParameters(&low_loss_threshold_,
+ &high_loss_threshold_,
+ &bitrate_threshold_kbps)) {
+ RTC_LOG(LS_INFO) << "Enabled BweLossExperiment with parameters "
+ << low_loss_threshold_ << ", " << high_loss_threshold_
+ << ", " << bitrate_threshold_kbps;
+ bitrate_threshold_ = DataRate::KilobitsPerSec(bitrate_threshold_kbps);
+ }
+ }
+ ParseFieldTrial({&disable_receiver_limit_caps_only_},
+ key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly"));
+ if (LossBasedBandwidthEstimatorV2Enabled()) {
+ loss_based_bandwidth_estimator_v2_.SetMinMaxBitrate(
+ min_bitrate_configured_, max_bitrate_configured_);
+ }
+}
+
+SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
+
+void SendSideBandwidthEstimation::OnRouteChange() {
+ lost_packets_since_last_loss_update_ = 0;
+ expected_packets_since_last_loss_update_ = 0;
+ current_target_ = DataRate::Zero();
+ min_bitrate_configured_ = kCongestionControllerMinBitrate;
+ max_bitrate_configured_ = kDefaultMaxBitrate;
+ last_low_bitrate_log_ = Timestamp::MinusInfinity();
+ has_decreased_since_last_fraction_loss_ = false;
+ last_loss_feedback_ = Timestamp::MinusInfinity();
+ last_loss_packet_report_ = Timestamp::MinusInfinity();
+ last_fraction_loss_ = 0;
+ last_logged_fraction_loss_ = 0;
+ last_round_trip_time_ = TimeDelta::Zero();
+ receiver_limit_ = DataRate::PlusInfinity();
+ delay_based_limit_ = DataRate::PlusInfinity();
+ time_last_decrease_ = Timestamp::MinusInfinity();
+ first_report_time_ = Timestamp::MinusInfinity();
+ initially_lost_packets_ = 0;
+ bitrate_at_2_seconds_ = DataRate::Zero();
+ uma_update_state_ = kNoUpdate;
+ uma_rtt_state_ = kNoUpdate;
+ last_rtc_event_log_ = Timestamp::MinusInfinity();
+}
+
+void SendSideBandwidthEstimation::SetBitrates(
+ absl::optional<DataRate> send_bitrate,
+ DataRate min_bitrate,
+ DataRate max_bitrate,
+ Timestamp at_time) {
+ SetMinMaxBitrate(min_bitrate, max_bitrate);
+ if (send_bitrate) {
+ link_capacity_.OnStartingRate(*send_bitrate);
+ SetSendBitrate(*send_bitrate, at_time);
+ }
+}
+
+void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate,
+ Timestamp at_time) {
+ RTC_DCHECK_GT(bitrate, DataRate::Zero());
+ // Reset to avoid being capped by the estimate.
+ delay_based_limit_ = DataRate::PlusInfinity();
+ UpdateTargetBitrate(bitrate, at_time);
+ // Clear last sent bitrate history so the new value can be used directly
+ // and not capped.
+ min_bitrate_history_.clear();
+}
+
+void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
+ DataRate max_bitrate) {
+ min_bitrate_configured_ =
+ std::max(min_bitrate, kCongestionControllerMinBitrate);
+ if (max_bitrate > DataRate::Zero() && max_bitrate.IsFinite()) {
+ max_bitrate_configured_ = std::max(min_bitrate_configured_, max_bitrate);
+ } else {
+ max_bitrate_configured_ = kDefaultMaxBitrate;
+ }
+ loss_based_bandwidth_estimator_v2_.SetMinMaxBitrate(min_bitrate_configured_,
+ max_bitrate_configured_);
+}
+
+int SendSideBandwidthEstimation::GetMinBitrate() const {
+ return min_bitrate_configured_.bps<int>();
+}
+
+DataRate SendSideBandwidthEstimation::target_rate() const {
+ DataRate target = current_target_;
+ if (!disable_receiver_limit_caps_only_)
+ target = std::min(target, receiver_limit_);
+ return std::max(min_bitrate_configured_, target);
+}
+
+LossBasedState SendSideBandwidthEstimation::loss_based_state() const {
+ return loss_based_state_;
+}
+
+DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
+ return link_capacity_.estimate();
+}
+
+void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time,
+ DataRate bandwidth) {
+ // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
+ // limitation.
+ receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth;
+ ApplyTargetLimits(at_time);
+}
+
+void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time,
+ DataRate bitrate) {
+ link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
+ // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
+ // limitation.
+ delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate;
+ ApplyTargetLimits(at_time);
+}
+
+void SendSideBandwidthEstimation::SetAcknowledgedRate(
+ absl::optional<DataRate> acknowledged_rate,
+ Timestamp at_time) {
+ acknowledged_rate_ = acknowledged_rate;
+ if (!acknowledged_rate.has_value()) {
+ return;
+ }
+ if (LossBasedBandwidthEstimatorV1Enabled()) {
+ loss_based_bandwidth_estimator_v1_.UpdateAcknowledgedBitrate(
+ *acknowledged_rate, at_time);
+ }
+ if (LossBasedBandwidthEstimatorV2Enabled()) {
+ loss_based_bandwidth_estimator_v2_.SetAcknowledgedBitrate(
+ *acknowledged_rate);
+ }
+}
+
+void SendSideBandwidthEstimation::UpdateLossBasedEstimator(
+ const TransportPacketsFeedback& report,
+ BandwidthUsage delay_detector_state,
+ absl::optional<DataRate> probe_bitrate,
+ DataRate upper_link_capacity) {
+ if (LossBasedBandwidthEstimatorV1Enabled()) {
+ loss_based_bandwidth_estimator_v1_.UpdateLossStatistics(
+ report.packet_feedbacks, report.feedback_time);
+ }
+ if (LossBasedBandwidthEstimatorV2Enabled()) {
+ loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate(
+ report.packet_feedbacks, delay_based_limit_, delay_detector_state,
+ probe_bitrate, upper_link_capacity);
+ UpdateEstimate(report.feedback_time);
+ }
+}
+
+void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
+ int64_t number_of_packets,
+ Timestamp at_time) {
+ last_loss_feedback_ = at_time;
+ if (first_report_time_.IsInfinite())
+ first_report_time_ = at_time;
+
+ // Check sequence number diff and weight loss report
+ if (number_of_packets > 0) {
+ int64_t expected =
+ expected_packets_since_last_loss_update_ + number_of_packets;
+
+ // Don't generate a loss rate until it can be based on enough packets.
+ if (expected < kLimitNumPackets) {
+ // Accumulate reports.
+ expected_packets_since_last_loss_update_ = expected;
+ lost_packets_since_last_loss_update_ += packets_lost;
+ return;
+ }
+
+ has_decreased_since_last_fraction_loss_ = false;
+ int64_t lost_q8 =
+ std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
+ 0)
+ << 8;
+ last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255);
+
+ // Reset accumulators.
+ lost_packets_since_last_loss_update_ = 0;
+ expected_packets_since_last_loss_update_ = 0;
+ last_loss_packet_report_ = at_time;
+ UpdateEstimate(at_time);
+ }
+
+ UpdateUmaStatsPacketsLost(at_time, packets_lost);
+}
+
+void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time,
+ int packets_lost) {
+ DataRate bitrate_kbps =
+ DataRate::KilobitsPerSec((current_target_.bps() + 500) / 1000);
+ for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
+ if (!rampup_uma_stats_updated_[i] &&
+ bitrate_kbps.kbps() >= kUmaRampupMetrics[i].bitrate_kbps) {
+ RTC_HISTOGRAMS_COUNTS_100000(i, kUmaRampupMetrics[i].metric_name,
+ (at_time - first_report_time_).ms());
+ rampup_uma_stats_updated_[i] = true;
+ }
+ }
+ if (IsInStartPhase(at_time)) {
+ initially_lost_packets_ += packets_lost;
+ } else if (uma_update_state_ == kNoUpdate) {
+ uma_update_state_ = kFirstDone;
+ bitrate_at_2_seconds_ = bitrate_kbps;
+ RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitiallyLostPackets",
+ initially_lost_packets_, 0, 100, 50);
+ RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialBandwidthEstimate",
+ bitrate_at_2_seconds_.kbps(), 0, 2000, 50);
+ } else if (uma_update_state_ == kFirstDone &&
+ at_time - first_report_time_ >= kBweConverganceTime) {
+ uma_update_state_ = kDone;
+ int bitrate_diff_kbps = std::max(
+ bitrate_at_2_seconds_.kbps<int>() - bitrate_kbps.kbps<int>(), 0);
+ RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialVsConvergedDiff", bitrate_diff_kbps,
+ 0, 2000, 50);
+ }
+}
+
+void SendSideBandwidthEstimation::UpdateRtt(TimeDelta rtt, Timestamp at_time) {
+ // Update RTT if we were able to compute an RTT based on this RTCP.
+ // FlexFEC doesn't send RTCP SR, which means we won't be able to compute RTT.
+ if (rtt > TimeDelta::Zero())
+ last_round_trip_time_ = rtt;
+
+ if (!IsInStartPhase(at_time) && uma_rtt_state_ == kNoUpdate) {
+ uma_rtt_state_ = kDone;
+ RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialRtt", rtt.ms<int>(), 0, 2000, 50);
+ }
+}
+
+void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
+ if (rtt_backoff_.CorrectedRtt(at_time) > rtt_backoff_.rtt_limit_) {
+ if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
+ current_target_ > rtt_backoff_.bandwidth_floor_) {
+ time_last_decrease_ = at_time;
+ DataRate new_bitrate =
+ std::max(current_target_ * rtt_backoff_.drop_fraction_,
+ rtt_backoff_.bandwidth_floor_.Get());
+ link_capacity_.OnRttBackoff(new_bitrate, at_time);
+ UpdateTargetBitrate(new_bitrate, at_time);
+ return;
+ }
+ // TODO(srte): This is likely redundant in most cases.
+ ApplyTargetLimits(at_time);
+ return;
+ }
+
+ // We trust the REMB and/or delay-based estimate during the first 2 seconds if
+ // we haven't had any packet loss reported, to allow startup bitrate probing.
+ if (last_fraction_loss_ == 0 && IsInStartPhase(at_time)) {
+ DataRate new_bitrate = current_target_;
+ // TODO(srte): We should not allow the new_bitrate to be larger than the
+ // receiver limit here.
+ if (receiver_limit_.IsFinite())
+ new_bitrate = std::max(receiver_limit_, new_bitrate);
+ if (delay_based_limit_.IsFinite())
+ new_bitrate = std::max(delay_based_limit_, new_bitrate);
+ if (LossBasedBandwidthEstimatorV1Enabled()) {
+ loss_based_bandwidth_estimator_v1_.Initialize(new_bitrate);
+ }
+ if (LossBasedBandwidthEstimatorV2Enabled()) {
+ loss_based_bandwidth_estimator_v2_.SetBandwidthEstimate(new_bitrate);
+ }
+
+ if (new_bitrate != current_target_) {
+ min_bitrate_history_.clear();
+ if (LossBasedBandwidthEstimatorV1Enabled()) {
+ min_bitrate_history_.push_back(std::make_pair(at_time, new_bitrate));
+ } else {
+ min_bitrate_history_.push_back(
+ std::make_pair(at_time, current_target_));
+ }
+ UpdateTargetBitrate(new_bitrate, at_time);
+ return;
+ }
+ }
+ UpdateMinHistory(at_time);
+ if (last_loss_packet_report_.IsInfinite()) {
+ // No feedback received.
+ // TODO(srte): This is likely redundant in most cases.
+ ApplyTargetLimits(at_time);
+ return;
+ }
+
+ if (LossBasedBandwidthEstimatorV1ReadyForUse()) {
+ DataRate new_bitrate = loss_based_bandwidth_estimator_v1_.Update(
+ at_time, min_bitrate_history_.front().second, delay_based_limit_,
+ last_round_trip_time_);
+ UpdateTargetBitrate(new_bitrate, at_time);
+ return;
+ }
+
+ if (LossBasedBandwidthEstimatorV2ReadyForUse()) {
+ LossBasedBweV2::Result result =
+ loss_based_bandwidth_estimator_v2_.GetLossBasedResult();
+ loss_based_state_ = result.state;
+ UpdateTargetBitrate(result.bandwidth_estimate, at_time);
+ return;
+ }
+
+ TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_;
+ if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) {
+ // We only care about loss above a given bitrate threshold.
+ float loss = last_fraction_loss_ / 256.0f;
+ // We only make decisions based on loss when the bitrate is above a
+ // threshold. This is a crude way of handling loss which is uncorrelated
+ // to congestion.
+ if (current_target_ < bitrate_threshold_ || loss <= low_loss_threshold_) {
+ // Loss < 2%: Increase rate by 8% of the min bitrate in the last
+ // kBweIncreaseInterval.
+ // Note that by remembering the bitrate over the last second one can
+ // rampup up one second faster than if only allowed to start ramping
+ // at 8% per second rate now. E.g.:
+ // If sending a constant 100kbps it can rampup immediately to 108kbps
+ // whenever a receiver report is received with lower packet loss.
+ // If instead one would do: current_bitrate_ *= 1.08^(delta time),
+ // it would take over one second since the lower packet loss to achieve
+ // 108kbps.
+ DataRate new_bitrate = DataRate::BitsPerSec(
+ min_bitrate_history_.front().second.bps() * 1.08 + 0.5);
+
+ // Add 1 kbps extra, just to make sure that we do not get stuck
+ // (gives a little extra increase at low rates, negligible at higher
+ // rates).
+ new_bitrate += DataRate::BitsPerSec(1000);
+ UpdateTargetBitrate(new_bitrate, at_time);
+ return;
+ } else if (current_target_ > bitrate_threshold_) {
+ if (loss <= high_loss_threshold_) {
+ // Loss between 2% - 10%: Do nothing.
+ } else {
+ // Loss > 10%: Limit the rate decreases to once a kBweDecreaseInterval
+ // + rtt.
+ if (!has_decreased_since_last_fraction_loss_ &&
+ (at_time - time_last_decrease_) >=
+ (kBweDecreaseInterval + last_round_trip_time_)) {
+ time_last_decrease_ = at_time;
+
+ // Reduce rate:
+ // newRate = rate * (1 - 0.5*lossRate);
+ // where packetLoss = 256*lossRate;
+ DataRate new_bitrate = DataRate::BitsPerSec(
+ (current_target_.bps() *
+ static_cast<double>(512 - last_fraction_loss_)) /
+ 512.0);
+ has_decreased_since_last_fraction_loss_ = true;
+ UpdateTargetBitrate(new_bitrate, at_time);
+ return;
+ }
+ }
+ }
+ }
+ // TODO(srte): This is likely redundant in most cases.
+ ApplyTargetLimits(at_time);
+}
+
+void SendSideBandwidthEstimation::UpdatePropagationRtt(
+ Timestamp at_time,
+ TimeDelta propagation_rtt) {
+ rtt_backoff_.UpdatePropagationRtt(at_time, propagation_rtt);
+}
+
+void SendSideBandwidthEstimation::OnSentPacket(const SentPacket& sent_packet) {
+ // Only feedback-triggering packets will be reported here.
+ rtt_backoff_.last_packet_sent_ = sent_packet.send_time;
+}
+
+bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const {
+ return first_report_time_.IsInfinite() ||
+ at_time - first_report_time_ < kStartPhase;
+}
+
+void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) {
+ // Remove old data points from history.
+ // Since history precision is in ms, add one so it is able to increase
+ // bitrate if it is off by as little as 0.5ms.
+ while (!min_bitrate_history_.empty() &&
+ at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) >
+ kBweIncreaseInterval) {
+ min_bitrate_history_.pop_front();
+ }
+
+ // Typical minimum sliding-window algorithm: Pop values higher than current
+ // bitrate before pushing it.
+ while (!min_bitrate_history_.empty() &&
+ current_target_ <= min_bitrate_history_.back().second) {
+ min_bitrate_history_.pop_back();
+ }
+
+ min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
+}
+
+DataRate SendSideBandwidthEstimation::GetUpperLimit() const {
+ DataRate upper_limit = delay_based_limit_;
+ if (disable_receiver_limit_caps_only_)
+ upper_limit = std::min(upper_limit, receiver_limit_);
+ return std::min(upper_limit, max_bitrate_configured_);
+}
+
+void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate,
+ Timestamp at_time) {
+ if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
+ RTC_LOG(LS_WARNING) << "Estimated available bandwidth " << ToString(bitrate)
+ << " is below configured min bitrate "
+ << ToString(min_bitrate_configured_) << ".";
+ last_low_bitrate_log_ = at_time;
+ }
+}
+
+void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(Timestamp at_time) {
+ if (current_target_ != last_logged_target_ ||
+ last_fraction_loss_ != last_logged_fraction_loss_ ||
+ at_time - last_rtc_event_log_ > kRtcEventLogPeriod) {
+ event_log_->Log(std::make_unique<RtcEventBweUpdateLossBased>(
+ current_target_.bps(), last_fraction_loss_,
+ expected_packets_since_last_loss_update_));
+ last_logged_fraction_loss_ = last_fraction_loss_;
+ last_logged_target_ = current_target_;
+ last_rtc_event_log_ = at_time;
+ }
+}
+
+void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate,
+ Timestamp at_time) {
+ new_bitrate = std::min(new_bitrate, GetUpperLimit());
+ if (new_bitrate < min_bitrate_configured_) {
+ MaybeLogLowBitrateWarning(new_bitrate, at_time);
+ new_bitrate = min_bitrate_configured_;
+ }
+ current_target_ = new_bitrate;
+ MaybeLogLossBasedEvent(at_time);
+ link_capacity_.OnRateUpdate(acknowledged_rate_, current_target_, at_time);
+}
+
+void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) {
+ UpdateTargetBitrate(current_target_, at_time);
+}
+
+bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1Enabled() const {
+ return loss_based_bandwidth_estimator_v1_.Enabled() &&
+ !LossBasedBandwidthEstimatorV2Enabled();
+}
+
+bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1ReadyForUse()
+ const {
+ return LossBasedBandwidthEstimatorV1Enabled() &&
+ loss_based_bandwidth_estimator_v1_.InUse();
+}
+
+bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2Enabled() const {
+ return loss_based_bandwidth_estimator_v2_.IsEnabled();
+}
+
+bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2ReadyForUse()
+ const {
+ return LossBasedBandwidthEstimatorV2Enabled() &&
+ loss_based_bandwidth_estimator_v2_.IsReady();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h
new file mode 100644
index 0000000000..77510236d3
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ *
+ * FEC and NACK added bitrate is handled outside class
+ */
+
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
+
+#include <stdint.h>
+
+#include <deque>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "api/transport/network_types.h"
+#include "api/units/data_rate.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h"
+#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+class RtcEventLog;
+
+class LinkCapacityTracker {
+ public:
+ LinkCapacityTracker();
+ ~LinkCapacityTracker();
+ // Call when a new delay-based estimate is available.
+ void UpdateDelayBasedEstimate(Timestamp at_time,
+ DataRate delay_based_bitrate);
+ void OnStartingRate(DataRate start_rate);
+ void OnRateUpdate(absl::optional<DataRate> acknowledged,
+ DataRate target,
+ Timestamp at_time);
+ void OnRttBackoff(DataRate backoff_rate, Timestamp at_time);
+ DataRate estimate() const;
+
+ private:
+ FieldTrialParameter<TimeDelta> tracking_rate;
+ double capacity_estimate_bps_ = 0;
+ Timestamp last_link_capacity_update_ = Timestamp::MinusInfinity();
+ DataRate last_delay_based_estimate_ = DataRate::PlusInfinity();
+};
+
+class RttBasedBackoff {
+ public:
+ explicit RttBasedBackoff(const FieldTrialsView* key_value_config);
+ ~RttBasedBackoff();
+ void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
+ TimeDelta CorrectedRtt(Timestamp at_time) const;
+
+ FieldTrialFlag disabled_;
+ FieldTrialParameter<TimeDelta> configured_limit_;
+ FieldTrialParameter<double> drop_fraction_;
+ FieldTrialParameter<TimeDelta> drop_interval_;
+ FieldTrialParameter<DataRate> bandwidth_floor_;
+
+ public:
+ TimeDelta rtt_limit_;
+ Timestamp last_propagation_rtt_update_;
+ TimeDelta last_propagation_rtt_;
+ Timestamp last_packet_sent_;
+};
+
+class SendSideBandwidthEstimation {
+ public:
+ SendSideBandwidthEstimation() = delete;
+ SendSideBandwidthEstimation(const FieldTrialsView* key_value_config,
+ RtcEventLog* event_log);
+ ~SendSideBandwidthEstimation();
+
+ void OnRouteChange();
+
+ DataRate target_rate() const;
+ LossBasedState loss_based_state() const;
+ uint8_t fraction_loss() const { return last_fraction_loss_; }
+ TimeDelta round_trip_time() const { return last_round_trip_time_; }
+
+ DataRate GetEstimatedLinkCapacity() const;
+ // Call periodically to update estimate.
+ void UpdateEstimate(Timestamp at_time);
+ void OnSentPacket(const SentPacket& sent_packet);
+ void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
+
+ // Call when we receive a RTCP message with TMMBR or REMB.
+ void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth);
+
+ // Call when a new delay-based estimate is available.
+ void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate);
+
+ // Call when we receive a RTCP message with a ReceiveBlock.
+ void UpdatePacketsLost(int64_t packets_lost,
+ int64_t number_of_packets,
+ Timestamp at_time);
+
+ // Call when we receive a RTCP message with a ReceiveBlock.
+ void UpdateRtt(TimeDelta rtt, Timestamp at_time);
+
+ void SetBitrates(absl::optional<DataRate> send_bitrate,
+ DataRate min_bitrate,
+ DataRate max_bitrate,
+ Timestamp at_time);
+ void SetSendBitrate(DataRate bitrate, Timestamp at_time);
+ void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
+ int GetMinBitrate() const;
+ void SetAcknowledgedRate(absl::optional<DataRate> acknowledged_rate,
+ Timestamp at_time);
+ void UpdateLossBasedEstimator(const TransportPacketsFeedback& report,
+ BandwidthUsage delay_detector_state,
+ absl::optional<DataRate> probe_bitrate,
+ DataRate upper_link_capacity);
+
+ private:
+ friend class GoogCcStatePrinter;
+
+ enum UmaState { kNoUpdate, kFirstDone, kDone };
+
+ bool IsInStartPhase(Timestamp at_time) const;
+
+ void UpdateUmaStatsPacketsLost(Timestamp at_time, int packets_lost);
+
+ // Updates history of min bitrates.
+ // After this method returns min_bitrate_history_.front().second contains the
+ // min bitrate used during last kBweIncreaseIntervalMs.
+ void UpdateMinHistory(Timestamp at_time);
+
+ // Gets the upper limit for the target bitrate. This is the minimum of the
+ // delay based limit, the receiver limit and the loss based controller limit.
+ DataRate GetUpperLimit() const;
+ // Prints a warning if `bitrate` if sufficiently long time has past since last
+ // warning.
+ void MaybeLogLowBitrateWarning(DataRate bitrate, Timestamp at_time);
+ // Stores an update to the event log if the loss rate has changed, the target
+ // has changed, or sufficient time has passed since last stored event.
+ void MaybeLogLossBasedEvent(Timestamp at_time);
+
+ // Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and
+ // set `current_bitrate_` to the capped value and updates the event log.
+ void UpdateTargetBitrate(DataRate bitrate, Timestamp at_time);
+ // Applies lower and upper bounds to the current target rate.
+ // TODO(srte): This seems to be called even when limits haven't changed, that
+ // should be cleaned up.
+ void ApplyTargetLimits(Timestamp at_time);
+
+ bool LossBasedBandwidthEstimatorV1Enabled() const;
+ bool LossBasedBandwidthEstimatorV2Enabled() const;
+
+ bool LossBasedBandwidthEstimatorV1ReadyForUse() const;
+ bool LossBasedBandwidthEstimatorV2ReadyForUse() const;
+
+ RttBasedBackoff rtt_backoff_;
+ LinkCapacityTracker link_capacity_;
+
+ std::deque<std::pair<Timestamp, DataRate> > min_bitrate_history_;
+
+ // incoming filters
+ int lost_packets_since_last_loss_update_;
+ int expected_packets_since_last_loss_update_;
+
+ absl::optional<DataRate> acknowledged_rate_;
+ DataRate current_target_;
+ DataRate last_logged_target_;
+ DataRate min_bitrate_configured_;
+ DataRate max_bitrate_configured_;
+ Timestamp last_low_bitrate_log_;
+
+ bool has_decreased_since_last_fraction_loss_;
+ Timestamp last_loss_feedback_;
+ Timestamp last_loss_packet_report_;
+ uint8_t last_fraction_loss_;
+ uint8_t last_logged_fraction_loss_;
+ TimeDelta last_round_trip_time_;
+
+ // The max bitrate as set by the receiver in the call. This is typically
+ // signalled using the REMB RTCP message and is used when we don't have any
+ // send side delay based estimate.
+ DataRate receiver_limit_;
+ DataRate delay_based_limit_;
+ Timestamp time_last_decrease_;
+ Timestamp first_report_time_;
+ int initially_lost_packets_;
+ DataRate bitrate_at_2_seconds_;
+ UmaState uma_update_state_;
+ UmaState uma_rtt_state_;
+ std::vector<bool> rampup_uma_stats_updated_;
+ RtcEventLog* const event_log_;
+ Timestamp last_rtc_event_log_;
+ float low_loss_threshold_;
+ float high_loss_threshold_;
+ DataRate bitrate_threshold_;
+ LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_;
+ LossBasedBweV2 loss_based_bandwidth_estimator_v2_;
+ LossBasedState loss_based_state_;
+ FieldTrialFlag disable_receiver_limit_caps_only_;
+};
+} // namespace webrtc
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc
new file mode 100644
index 0000000000..17d1aa1ada
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
+
+#include "api/rtc_event_log/rtc_event.h"
+#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "test/explicit_key_value_config.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+MATCHER(LossBasedBweUpdateWithBitrateOnly, "") {
+ if (arg->GetType() != RtcEvent::Type::BweUpdateLossBased) {
+ return false;
+ }
+ auto bwe_event = static_cast<RtcEventBweUpdateLossBased*>(arg);
+ return bwe_event->bitrate_bps() > 0 && bwe_event->fraction_loss() == 0;
+}
+
+MATCHER(LossBasedBweUpdateWithBitrateAndLossFraction, "") {
+ if (arg->GetType() != RtcEvent::Type::BweUpdateLossBased) {
+ return false;
+ }
+ auto bwe_event = static_cast<RtcEventBweUpdateLossBased*>(arg);
+ return bwe_event->bitrate_bps() > 0 && bwe_event->fraction_loss() > 0;
+}
+
+void TestProbing(bool use_delay_based) {
+ ::testing::NiceMock<MockRtcEventLog> event_log;
+ test::ExplicitKeyValueConfig key_value_config("");
+ SendSideBandwidthEstimation bwe(&key_value_config, &event_log);
+ int64_t now_ms = 0;
+ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(100000),
+ DataRate::BitsPerSec(1500000));
+ bwe.SetSendBitrate(DataRate::BitsPerSec(200000), Timestamp::Millis(now_ms));
+
+ const int kRembBps = 1000000;
+ const int kSecondRembBps = kRembBps + 500000;
+
+ bwe.UpdatePacketsLost(/*packets_lost=*/0, /*number_of_packets=*/1,
+ Timestamp::Millis(now_ms));
+ bwe.UpdateRtt(TimeDelta::Millis(50), Timestamp::Millis(now_ms));
+
+ // Initial REMB applies immediately.
+ if (use_delay_based) {
+ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms),
+ DataRate::BitsPerSec(kRembBps));
+ } else {
+ bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms),
+ DataRate::BitsPerSec(kRembBps));
+ }
+ bwe.UpdateEstimate(Timestamp::Millis(now_ms));
+ EXPECT_EQ(kRembBps, bwe.target_rate().bps());
+
+ // Second REMB doesn't apply immediately.
+ now_ms += 2001;
+ if (use_delay_based) {
+ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms),
+ DataRate::BitsPerSec(kSecondRembBps));
+ } else {
+ bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms),
+ DataRate::BitsPerSec(kSecondRembBps));
+ }
+ bwe.UpdateEstimate(Timestamp::Millis(now_ms));
+ EXPECT_EQ(kRembBps, bwe.target_rate().bps());
+}
+
+TEST(SendSideBweTest, InitialRembWithProbing) {
+ TestProbing(false);
+}
+
+TEST(SendSideBweTest, InitialDelayBasedBweWithProbing) {
+ TestProbing(true);
+}
+
+TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) {
+ MockRtcEventLog event_log;
+ EXPECT_CALL(event_log, LogProxy(LossBasedBweUpdateWithBitrateOnly()))
+ .Times(1);
+ EXPECT_CALL(event_log,
+ LogProxy(LossBasedBweUpdateWithBitrateAndLossFraction()))
+ .Times(1);
+ test::ExplicitKeyValueConfig key_value_config("");
+ SendSideBandwidthEstimation bwe(&key_value_config, &event_log);
+ static const int kMinBitrateBps = 100000;
+ static const int kInitialBitrateBps = 1000000;
+ int64_t now_ms = 1000;
+ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps),
+ DataRate::BitsPerSec(1500000));
+ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps),
+ Timestamp::Millis(now_ms));
+
+ static const uint8_t kFractionLoss = 128;
+ static const int64_t kRttMs = 50;
+ now_ms += 10000;
+
+ EXPECT_EQ(kInitialBitrateBps, bwe.target_rate().bps());
+ EXPECT_EQ(0, bwe.fraction_loss());
+ EXPECT_EQ(0, bwe.round_trip_time().ms());
+
+ // Signal heavy loss to go down in bitrate.
+ bwe.UpdatePacketsLost(/*packets_lost=*/50, /*number_of_packets=*/100,
+ Timestamp::Millis(now_ms));
+ bwe.UpdateRtt(TimeDelta::Millis(kRttMs), Timestamp::Millis(now_ms));
+
+ // Trigger an update 2 seconds later to not be rate limited.
+ now_ms += 1000;
+ bwe.UpdateEstimate(Timestamp::Millis(now_ms));
+ EXPECT_LT(bwe.target_rate().bps(), kInitialBitrateBps);
+ // Verify that the obtained bitrate isn't hitting the min bitrate, or this
+ // test doesn't make sense. If this ever happens, update the thresholds or
+ // loss rates so that it doesn't hit min bitrate after one bitrate update.
+ EXPECT_GT(bwe.target_rate().bps(), kMinBitrateBps);
+ EXPECT_EQ(kFractionLoss, bwe.fraction_loss());
+ EXPECT_EQ(kRttMs, bwe.round_trip_time().ms());
+
+ // Triggering an update shouldn't apply further downgrade nor upgrade since
+ // there's no intermediate receiver block received indicating whether this is
+ // currently good or not.
+ int last_bitrate_bps = bwe.target_rate().bps();
+ // Trigger an update 2 seconds later to not be rate limited (but it still
+ // shouldn't update).
+ now_ms += 1000;
+ bwe.UpdateEstimate(Timestamp::Millis(now_ms));
+
+ EXPECT_EQ(last_bitrate_bps, bwe.target_rate().bps());
+ // The old loss rate should still be applied though.
+ EXPECT_EQ(kFractionLoss, bwe.fraction_loss());
+ EXPECT_EQ(kRttMs, bwe.round_trip_time().ms());
+}
+
+TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) {
+ ::testing::NiceMock<MockRtcEventLog> event_log;
+ test::ExplicitKeyValueConfig key_value_config("");
+ SendSideBandwidthEstimation bwe(&key_value_config, &event_log);
+ static const int kMinBitrateBps = 10000;
+ static const int kMaxBitrateBps = 10000000;
+ static const int kInitialBitrateBps = 300000;
+ static const int kDelayBasedBitrateBps = 350000;
+ static const int kForcedHighBitrate = 2500000;
+
+ int64_t now_ms = 0;
+
+ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps),
+ DataRate::BitsPerSec(kMaxBitrateBps));
+ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps),
+ Timestamp::Millis(now_ms));
+
+ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms),
+ DataRate::BitsPerSec(kDelayBasedBitrateBps));
+ bwe.UpdateEstimate(Timestamp::Millis(now_ms));
+ EXPECT_GE(bwe.target_rate().bps(), kInitialBitrateBps);
+ EXPECT_LE(bwe.target_rate().bps(), kDelayBasedBitrateBps);
+
+ bwe.SetSendBitrate(DataRate::BitsPerSec(kForcedHighBitrate),
+ Timestamp::Millis(now_ms));
+ EXPECT_EQ(bwe.target_rate().bps(), kForcedHighBitrate);
+}
+
+TEST(RttBasedBackoff, DefaultEnabled) {
+ test::ExplicitKeyValueConfig key_value_config("");
+ RttBasedBackoff rtt_backoff(&key_value_config);
+ EXPECT_TRUE(rtt_backoff.rtt_limit_.IsFinite());
+}
+
+TEST(RttBasedBackoff, CanBeDisabled) {
+ test::ExplicitKeyValueConfig key_value_config(
+ "WebRTC-Bwe-MaxRttLimit/Disabled/");
+ RttBasedBackoff rtt_backoff(&key_value_config);
+ EXPECT_TRUE(rtt_backoff.rtt_limit_.IsPlusInfinity());
+}
+
+TEST(SendSideBweTest, FractionLossIsNotOverflowed) {
+ MockRtcEventLog event_log;
+ test::ExplicitKeyValueConfig key_value_config("");
+ SendSideBandwidthEstimation bwe(&key_value_config, &event_log);
+ static const int kMinBitrateBps = 100000;
+ static const int kInitialBitrateBps = 1000000;
+ int64_t now_ms = 1000;
+ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps),
+ DataRate::BitsPerSec(1500000));
+ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps),
+ Timestamp::Millis(now_ms));
+
+ now_ms += 10000;
+
+ EXPECT_EQ(kInitialBitrateBps, bwe.target_rate().bps());
+ EXPECT_EQ(0, bwe.fraction_loss());
+
+ // Signal negative loss.
+ bwe.UpdatePacketsLost(/*packets_lost=*/-1, /*number_of_packets=*/100,
+ Timestamp::Millis(now_ms));
+ EXPECT_EQ(0, bwe.fraction_loss());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build
new file mode 100644
index 0000000000..c93e412e83
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bwe_gn/moz.build
@@ -0,0 +1,233 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+SOURCES += [
+ "/third_party/libwebrtc/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("send_side_bwe_gn")
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc
new file mode 100644
index 0000000000..6a8849ed6d
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/congestion_controller/goog_cc/test/goog_cc_printer.h"
+
+#include <math.h>
+
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "modules/congestion_controller/goog_cc/alr_detector.h"
+#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
+#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
+#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+void WriteTypedValue(RtcEventLogOutput* out, int value) {
+ LogWriteFormat(out, "%i", value);
+}
+void WriteTypedValue(RtcEventLogOutput* out, double value) {
+ LogWriteFormat(out, "%.6f", value);
+}
+void WriteTypedValue(RtcEventLogOutput* out, absl::optional<DataRate> value) {
+ LogWriteFormat(out, "%.0f", value ? value->bytes_per_sec<double>() : NAN);
+}
+void WriteTypedValue(RtcEventLogOutput* out, absl::optional<DataSize> value) {
+ LogWriteFormat(out, "%.0f", value ? value->bytes<double>() : NAN);
+}
+void WriteTypedValue(RtcEventLogOutput* out, absl::optional<TimeDelta> value) {
+ LogWriteFormat(out, "%.3f", value ? value->seconds<double>() : NAN);
+}
+void WriteTypedValue(RtcEventLogOutput* out, absl::optional<Timestamp> value) {
+ LogWriteFormat(out, "%.3f", value ? value->seconds<double>() : NAN);
+}
+
+template <typename F>
+class TypedFieldLogger : public FieldLogger {
+ public:
+ TypedFieldLogger(absl::string_view name, F&& getter)
+ : name_(name), getter_(std::forward<F>(getter)) {}
+ const std::string& name() const override { return name_; }
+ void WriteValue(RtcEventLogOutput* out) override {
+ WriteTypedValue(out, getter_());
+ }
+
+ private:
+ std::string name_;
+ F getter_;
+};
+
+template <typename F>
+FieldLogger* Log(absl::string_view name, F&& getter) {
+ return new TypedFieldLogger<F>(name, std::forward<F>(getter));
+}
+
+} // namespace
+GoogCcStatePrinter::GoogCcStatePrinter() {
+ for (auto* logger : CreateLoggers()) {
+ loggers_.emplace_back(logger);
+ }
+}
+
+std::deque<FieldLogger*> GoogCcStatePrinter::CreateLoggers() {
+ auto stable_estimate = [this] {
+ return DataRate::KilobitsPerSec(
+ controller_->delay_based_bwe_->rate_control_.link_capacity_
+ .estimate_kbps_.value_or(-INFINITY));
+ };
+ auto rate_control_state = [this] {
+ return static_cast<int>(
+ controller_->delay_based_bwe_->rate_control_.rate_control_state_);
+ };
+ auto trend = [this] {
+ return reinterpret_cast<TrendlineEstimator*>(
+ controller_->delay_based_bwe_->active_delay_detector_);
+ };
+ auto acknowledged_rate = [this] {
+ return controller_->acknowledged_bitrate_estimator_->bitrate();
+ };
+ auto loss_cont = [&] {
+ return &controller_->bandwidth_estimation_
+ ->loss_based_bandwidth_estimator_v1_;
+ };
+ std::deque<FieldLogger*> loggers({
+ Log("time", [=] { return target_.at_time; }),
+ Log("rtt", [=] { return target_.network_estimate.round_trip_time; }),
+ Log("target", [=] { return target_.target_rate; }),
+ Log("stable_target", [=] { return target_.stable_target_rate; }),
+ Log("pacing", [=] { return pacing_.data_rate(); }),
+ Log("padding", [=] { return pacing_.pad_rate(); }),
+ Log("window", [=] { return congestion_window_; }),
+ Log("rate_control_state", [=] { return rate_control_state(); }),
+ Log("stable_estimate", [=] { return stable_estimate(); }),
+ Log("trendline", [=] { return trend()->prev_trend_; }),
+ Log("trendline_modified_offset",
+ [=] { return trend()->prev_modified_trend_; }),
+ Log("trendline_offset_threshold", [=] { return trend()->threshold_; }),
+ Log("acknowledged_rate", [=] { return acknowledged_rate(); }),
+ Log("est_capacity", [=] { return est_.link_capacity; }),
+ Log("est_capacity_dev", [=] { return est_.link_capacity_std_dev; }),
+ Log("est_capacity_min", [=] { return est_.link_capacity_min; }),
+ Log("est_cross_traffic", [=] { return est_.cross_traffic_ratio; }),
+ Log("est_cross_delay", [=] { return est_.cross_delay_rate; }),
+ Log("est_spike_delay", [=] { return est_.spike_delay_rate; }),
+ Log("est_pre_buffer", [=] { return est_.pre_link_buffer_delay; }),
+ Log("est_post_buffer", [=] { return est_.post_link_buffer_delay; }),
+ Log("est_propagation", [=] { return est_.propagation_delay; }),
+ Log("loss_ratio", [=] { return loss_cont()->last_loss_ratio_; }),
+ Log("loss_average", [=] { return loss_cont()->average_loss_; }),
+ Log("loss_average_max", [=] { return loss_cont()->average_loss_max_; }),
+ Log("loss_thres_inc",
+ [=] { return loss_cont()->loss_increase_threshold(); }),
+ Log("loss_thres_dec",
+ [=] { return loss_cont()->loss_decrease_threshold(); }),
+ Log("loss_dec_rate", [=] { return loss_cont()->decreased_bitrate(); }),
+ Log("loss_based_rate", [=] { return loss_cont()->loss_based_bitrate_; }),
+ Log("loss_ack_rate",
+ [=] { return loss_cont()->acknowledged_bitrate_max_; }),
+ Log("data_window", [=] { return controller_->current_data_window_; }),
+ Log("pushback_target",
+ [=] { return controller_->last_pushback_target_rate_; }),
+ });
+ return loggers;
+}
+GoogCcStatePrinter::~GoogCcStatePrinter() = default;
+
+void GoogCcStatePrinter::PrintHeaders(RtcEventLogOutput* log) {
+ int ix = 0;
+ for (const auto& logger : loggers_) {
+ if (ix++)
+ log->Write(" ");
+ log->Write(logger->name());
+ }
+ log->Write("\n");
+ log->Flush();
+}
+
+void GoogCcStatePrinter::PrintState(RtcEventLogOutput* log,
+ GoogCcNetworkController* controller,
+ Timestamp at_time) {
+ controller_ = controller;
+ auto state_update = controller_->GetNetworkState(at_time);
+ target_ = state_update.target_rate.value();
+ pacing_ = state_update.pacer_config.value();
+ if (state_update.congestion_window)
+ congestion_window_ = *state_update.congestion_window;
+ if (controller_->network_estimator_) {
+ est_ = controller_->network_estimator_->GetCurrentEstimate().value_or(
+ NetworkStateEstimate());
+ }
+
+ int ix = 0;
+ for (const auto& logger : loggers_) {
+ if (ix++)
+ log->Write(" ");
+ logger->WriteValue(log);
+ }
+
+ log->Write("\n");
+ log->Flush();
+}
+
+GoogCcDebugFactory::GoogCcDebugFactory()
+ : GoogCcDebugFactory(GoogCcFactoryConfig()) {}
+
+GoogCcDebugFactory::GoogCcDebugFactory(GoogCcFactoryConfig config)
+ : GoogCcNetworkControllerFactory(std::move(config)) {}
+
+std::unique_ptr<NetworkControllerInterface> GoogCcDebugFactory::Create(
+ NetworkControllerConfig config) {
+ RTC_CHECK(controller_ == nullptr);
+ auto controller = GoogCcNetworkControllerFactory::Create(config);
+ controller_ = static_cast<GoogCcNetworkController*>(controller.get());
+ return controller;
+}
+
+void GoogCcDebugFactory::PrintState(const Timestamp at_time) {
+ if (controller_ && log_writer_) {
+ printer_.PrintState(log_writer_.get(), controller_, at_time);
+ }
+}
+
+void GoogCcDebugFactory::AttachWriter(
+ std::unique_ptr<RtcEventLogOutput> log_writer) {
+ if (log_writer) {
+ log_writer_ = std::move(log_writer);
+ printer_.PrintHeaders(log_writer_.get());
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.h
new file mode 100644
index 0000000000..16fa657e71
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/test/goog_cc_printer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TEST_GOOG_CC_PRINTER_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TEST_GOOG_CC_PRINTER_H_
+
+#include <deque>
+#include <memory>
+#include <string>
+
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/transport/goog_cc_factory.h"
+#include "api/transport/network_control.h"
+#include "api/transport/network_types.h"
+#include "api/units/timestamp.h"
+#include "modules/congestion_controller/goog_cc/goog_cc_network_control.h"
+#include "test/logging/log_writer.h"
+
+namespace webrtc {
+
+class FieldLogger {
+ public:
+ virtual ~FieldLogger() = default;
+ virtual const std::string& name() const = 0;
+ virtual void WriteValue(RtcEventLogOutput* out) = 0;
+};
+
+class GoogCcStatePrinter {
+ public:
+ GoogCcStatePrinter();
+ GoogCcStatePrinter(const GoogCcStatePrinter&) = delete;
+ GoogCcStatePrinter& operator=(const GoogCcStatePrinter&) = delete;
+ ~GoogCcStatePrinter();
+
+ void PrintHeaders(RtcEventLogOutput* log);
+ void PrintState(RtcEventLogOutput* log,
+ GoogCcNetworkController* controller,
+ Timestamp at_time);
+
+ private:
+ std::deque<FieldLogger*> CreateLoggers();
+ std::deque<std::unique_ptr<FieldLogger>> loggers_;
+
+ GoogCcNetworkController* controller_ = nullptr;
+ TargetTransferRate target_;
+ PacerConfig pacing_;
+ DataSize congestion_window_ = DataSize::PlusInfinity();
+ NetworkStateEstimate est_;
+};
+
+class GoogCcDebugFactory : public GoogCcNetworkControllerFactory {
+ public:
+ GoogCcDebugFactory();
+ explicit GoogCcDebugFactory(GoogCcFactoryConfig config);
+ std::unique_ptr<NetworkControllerInterface> Create(
+ NetworkControllerConfig config) override;
+
+ void PrintState(Timestamp at_time);
+
+ void AttachWriter(std::unique_ptr<RtcEventLogOutput> log_writer);
+
+ private:
+ GoogCcStatePrinter printer_;
+ GoogCcNetworkController* controller_ = nullptr;
+ std::unique_ptr<RtcEventLogOutput> log_writer_;
+};
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TEST_GOOG_CC_PRINTER_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc
new file mode 100644
index 0000000000..88182d4f80
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "absl/types/optional.h"
+#include "api/network_state_predictor.h"
+#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+
+namespace {
+
+// Parameters for linear least squares fit of regression line to noisy data.
+constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
+constexpr double kDefaultTrendlineThresholdGain = 4.0;
+const char kBweWindowSizeInPacketsExperiment[] =
+ "WebRTC-BweWindowSizeInPackets";
+
+size_t ReadTrendlineFilterWindowSize(const FieldTrialsView* key_value_config) {
+ std::string experiment_string =
+ key_value_config->Lookup(kBweWindowSizeInPacketsExperiment);
+ size_t window_size;
+ int parsed_values =
+ sscanf(experiment_string.c_str(), "Enabled-%zu", &window_size);
+ if (parsed_values == 1) {
+ if (window_size > 1)
+ return window_size;
+ RTC_LOG(LS_WARNING) << "Window size must be greater than 1.";
+ }
+ RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweWindowSizeInPackets"
+ " experiment from field trial string. Using default.";
+ return TrendlineEstimatorSettings::kDefaultTrendlineWindowSize;
+}
+
+absl::optional<double> LinearFitSlope(
+ const std::deque<TrendlineEstimator::PacketTiming>& packets) {
+ RTC_DCHECK(packets.size() >= 2);
+ // Compute the "center of mass".
+ double sum_x = 0;
+ double sum_y = 0;
+ for (const auto& packet : packets) {
+ sum_x += packet.arrival_time_ms;
+ sum_y += packet.smoothed_delay_ms;
+ }
+ double x_avg = sum_x / packets.size();
+ double y_avg = sum_y / packets.size();
+ // Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2
+ double numerator = 0;
+ double denominator = 0;
+ for (const auto& packet : packets) {
+ double x = packet.arrival_time_ms;
+ double y = packet.smoothed_delay_ms;
+ numerator += (x - x_avg) * (y - y_avg);
+ denominator += (x - x_avg) * (x - x_avg);
+ }
+ if (denominator == 0)
+ return absl::nullopt;
+ return numerator / denominator;
+}
+
+absl::optional<double> ComputeSlopeCap(
+ const std::deque<TrendlineEstimator::PacketTiming>& packets,
+ const TrendlineEstimatorSettings& settings) {
+ RTC_DCHECK(1 <= settings.beginning_packets &&
+ settings.beginning_packets < packets.size());
+ RTC_DCHECK(1 <= settings.end_packets &&
+ settings.end_packets < packets.size());
+ RTC_DCHECK(settings.beginning_packets + settings.end_packets <=
+ packets.size());
+ TrendlineEstimator::PacketTiming early = packets[0];
+ for (size_t i = 1; i < settings.beginning_packets; ++i) {
+ if (packets[i].raw_delay_ms < early.raw_delay_ms)
+ early = packets[i];
+ }
+ size_t late_start = packets.size() - settings.end_packets;
+ TrendlineEstimator::PacketTiming late = packets[late_start];
+ for (size_t i = late_start + 1; i < packets.size(); ++i) {
+ if (packets[i].raw_delay_ms < late.raw_delay_ms)
+ late = packets[i];
+ }
+ if (late.arrival_time_ms - early.arrival_time_ms < 1) {
+ return absl::nullopt;
+ }
+ return (late.raw_delay_ms - early.raw_delay_ms) /
+ (late.arrival_time_ms - early.arrival_time_ms) +
+ settings.cap_uncertainty;
+}
+
+constexpr double kMaxAdaptOffsetMs = 15.0;
+constexpr double kOverUsingTimeThreshold = 10;
+constexpr int kMinNumDeltas = 60;
+constexpr int kDeltaCounterMax = 1000;
+
+} // namespace
+
+constexpr char TrendlineEstimatorSettings::kKey[];
+
+TrendlineEstimatorSettings::TrendlineEstimatorSettings(
+ const FieldTrialsView* key_value_config) {
+ if (absl::StartsWith(
+ key_value_config->Lookup(kBweWindowSizeInPacketsExperiment),
+ "Enabled")) {
+ window_size = ReadTrendlineFilterWindowSize(key_value_config);
+ }
+ Parser()->Parse(key_value_config->Lookup(TrendlineEstimatorSettings::kKey));
+ if (window_size < 10 || 200 < window_size) {
+ RTC_LOG(LS_WARNING) << "Window size must be between 10 and 200 packets";
+ window_size = kDefaultTrendlineWindowSize;
+ }
+ if (enable_cap) {
+ if (beginning_packets < 1 || end_packets < 1 ||
+ beginning_packets > window_size || end_packets > window_size) {
+ RTC_LOG(LS_WARNING) << "Size of beginning and end must be between 1 and "
+ << window_size;
+ enable_cap = false;
+ beginning_packets = end_packets = 0;
+ cap_uncertainty = 0.0;
+ }
+ if (beginning_packets + end_packets > window_size) {
+ RTC_LOG(LS_WARNING)
+ << "Size of beginning plus end can't exceed the window size";
+ enable_cap = false;
+ beginning_packets = end_packets = 0;
+ cap_uncertainty = 0.0;
+ }
+ if (cap_uncertainty < 0.0 || 0.025 < cap_uncertainty) {
+ RTC_LOG(LS_WARNING) << "Cap uncertainty must be between 0 and 0.025";
+ cap_uncertainty = 0.0;
+ }
+ }
+}
+
+std::unique_ptr<StructParametersParser> TrendlineEstimatorSettings::Parser() {
+ return StructParametersParser::Create("sort", &enable_sort, //
+ "cap", &enable_cap, //
+ "beginning_packets",
+ &beginning_packets, //
+ "end_packets", &end_packets, //
+ "cap_uncertainty", &cap_uncertainty, //
+ "window_size", &window_size);
+}
+
+TrendlineEstimator::TrendlineEstimator(
+ const FieldTrialsView* key_value_config,
+ NetworkStatePredictor* network_state_predictor)
+ : settings_(key_value_config),
+ smoothing_coef_(kDefaultTrendlineSmoothingCoeff),
+ threshold_gain_(kDefaultTrendlineThresholdGain),
+ num_of_deltas_(0),
+ first_arrival_time_ms_(-1),
+ accumulated_delay_(0),
+ smoothed_delay_(0),
+ delay_hist_(),
+ k_up_(0.0087),
+ k_down_(0.039),
+ overusing_time_threshold_(kOverUsingTimeThreshold),
+ threshold_(12.5),
+ prev_modified_trend_(NAN),
+ last_update_ms_(-1),
+ prev_trend_(0.0),
+ time_over_using_(-1),
+ overuse_counter_(0),
+ hypothesis_(BandwidthUsage::kBwNormal),
+ hypothesis_predicted_(BandwidthUsage::kBwNormal),
+ network_state_predictor_(network_state_predictor) {
+ RTC_LOG(LS_INFO)
+ << "Using Trendline filter for delay change estimation with settings "
+ << settings_.Parser()->Encode() << " and "
+ << (network_state_predictor_ ? "injected" : "no")
+ << " network state predictor";
+}
+
+TrendlineEstimator::~TrendlineEstimator() {}
+
+void TrendlineEstimator::UpdateTrendline(double recv_delta_ms,
+ double send_delta_ms,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ size_t packet_size) {
+ const double delta_ms = recv_delta_ms - send_delta_ms;
+ ++num_of_deltas_;
+ num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax);
+ if (first_arrival_time_ms_ == -1)
+ first_arrival_time_ms_ = arrival_time_ms;
+
+ // Exponential backoff filter.
+ accumulated_delay_ += delta_ms;
+ BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms,
+ accumulated_delay_);
+ smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
+ (1 - smoothing_coef_) * accumulated_delay_;
+ BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms,
+ smoothed_delay_);
+
+ // Maintain packet window
+ delay_hist_.emplace_back(
+ static_cast<double>(arrival_time_ms - first_arrival_time_ms_),
+ smoothed_delay_, accumulated_delay_);
+ if (settings_.enable_sort) {
+ for (size_t i = delay_hist_.size() - 1;
+ i > 0 &&
+ delay_hist_[i].arrival_time_ms < delay_hist_[i - 1].arrival_time_ms;
+ --i) {
+ std::swap(delay_hist_[i], delay_hist_[i - 1]);
+ }
+ }
+ if (delay_hist_.size() > settings_.window_size)
+ delay_hist_.pop_front();
+
+ // Simple linear regression.
+ double trend = prev_trend_;
+ if (delay_hist_.size() == settings_.window_size) {
+ // Update trend_ if it is possible to fit a line to the data. The delay
+ // trend can be seen as an estimate of (send_rate - capacity)/capacity.
+ // 0 < trend < 1 -> the delay increases, queues are filling up
+ // trend == 0 -> the delay does not change
+ // trend < 0 -> the delay decreases, queues are being emptied
+ trend = LinearFitSlope(delay_hist_).value_or(trend);
+ if (settings_.enable_cap) {
+ absl::optional<double> cap = ComputeSlopeCap(delay_hist_, settings_);
+ // We only use the cap to filter out overuse detections, not
+ // to detect additional underuses.
+ if (trend >= 0 && cap.has_value() && trend > cap.value()) {
+ trend = cap.value();
+ }
+ }
+ }
+ BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trend);
+
+ Detect(trend, send_delta_ms, arrival_time_ms);
+}
+
+void TrendlineEstimator::Update(double recv_delta_ms,
+ double send_delta_ms,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ size_t packet_size,
+ bool calculated_deltas) {
+ if (calculated_deltas) {
+ UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms,
+ packet_size);
+ }
+ if (network_state_predictor_) {
+ hypothesis_predicted_ = network_state_predictor_->Update(
+ send_time_ms, arrival_time_ms, hypothesis_);
+ }
+}
+
+BandwidthUsage TrendlineEstimator::State() const {
+ return network_state_predictor_ ? hypothesis_predicted_ : hypothesis_;
+}
+
+void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) {
+ if (num_of_deltas_ < 2) {
+ hypothesis_ = BandwidthUsage::kBwNormal;
+ return;
+ }
+ const double modified_trend =
+ std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;
+ prev_modified_trend_ = modified_trend;
+ BWE_TEST_LOGGING_PLOT(1, "T", now_ms, modified_trend);
+ BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
+ if (modified_trend > threshold_) {
+ if (time_over_using_ == -1) {
+ // Initialize the timer. Assume that we've been
+ // over-using half of the time since the previous
+ // sample.
+ time_over_using_ = ts_delta / 2;
+ } else {
+ // Increment timer
+ time_over_using_ += ts_delta;
+ }
+ overuse_counter_++;
+ if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
+ if (trend >= prev_trend_) {
+ time_over_using_ = 0;
+ overuse_counter_ = 0;
+ hypothesis_ = BandwidthUsage::kBwOverusing;
+ }
+ }
+ } else if (modified_trend < -threshold_) {
+ time_over_using_ = -1;
+ overuse_counter_ = 0;
+ hypothesis_ = BandwidthUsage::kBwUnderusing;
+ } else {
+ time_over_using_ = -1;
+ overuse_counter_ = 0;
+ hypothesis_ = BandwidthUsage::kBwNormal;
+ }
+ prev_trend_ = trend;
+ UpdateThreshold(modified_trend, now_ms);
+}
+
+void TrendlineEstimator::UpdateThreshold(double modified_trend,
+ int64_t now_ms) {
+ if (last_update_ms_ == -1)
+ last_update_ms_ = now_ms;
+
+ if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) {
+ // Avoid adapting the threshold to big latency spikes, caused e.g.,
+ // by a sudden capacity drop.
+ last_update_ms_ = now_ms;
+ return;
+ }
+
+ const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_;
+ const int64_t kMaxTimeDeltaMs = 100;
+ int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
+ threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms;
+ threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
+ last_update_ms_ = now_ms;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.h b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.h
new file mode 100644
index 0000000000..ffda25df74
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
+#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <deque>
+#include <memory>
+#include <utility>
+
+#include "api/field_trials_view.h"
+#include "api/network_state_predictor.h"
+#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
+
+namespace webrtc {
+
+struct TrendlineEstimatorSettings {
+ static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings";
+ static constexpr unsigned kDefaultTrendlineWindowSize = 20;
+
+ TrendlineEstimatorSettings() = delete;
+ explicit TrendlineEstimatorSettings(const FieldTrialsView* key_value_config);
+
+ // Sort the packets in the window. Should be redundant,
+ // but then almost no cost.
+ bool enable_sort = false;
+
+ // Cap the trendline slope based on the minimum delay seen
+ // in the beginning_packets and end_packets respectively.
+ bool enable_cap = false;
+ unsigned beginning_packets = 7;
+ unsigned end_packets = 7;
+ double cap_uncertainty = 0.0;
+
+ // Size (in packets) of the window.
+ unsigned window_size = kDefaultTrendlineWindowSize;
+
+ std::unique_ptr<StructParametersParser> Parser();
+};
+
+class TrendlineEstimator : public DelayIncreaseDetectorInterface {
+ public:
+ TrendlineEstimator(const FieldTrialsView* key_value_config,
+ NetworkStatePredictor* network_state_predictor);
+
+ ~TrendlineEstimator() override;
+
+ TrendlineEstimator(const TrendlineEstimator&) = delete;
+ TrendlineEstimator& operator=(const TrendlineEstimator&) = delete;
+
+ // Update the estimator with a new sample. The deltas should represent deltas
+ // between timestamp groups as defined by the InterArrival class.
+ void Update(double recv_delta_ms,
+ double send_delta_ms,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ size_t packet_size,
+ bool calculated_deltas) override;
+
+ void UpdateTrendline(double recv_delta_ms,
+ double send_delta_ms,
+ int64_t send_time_ms,
+ int64_t arrival_time_ms,
+ size_t packet_size);
+
+ BandwidthUsage State() const override;
+
+ struct PacketTiming {
+ PacketTiming(double arrival_time_ms,
+ double smoothed_delay_ms,
+ double raw_delay_ms)
+ : arrival_time_ms(arrival_time_ms),
+ smoothed_delay_ms(smoothed_delay_ms),
+ raw_delay_ms(raw_delay_ms) {}
+ double arrival_time_ms;
+ double smoothed_delay_ms;
+ double raw_delay_ms;
+ };
+
+ private:
+ friend class GoogCcStatePrinter;
+ void Detect(double trend, double ts_delta, int64_t now_ms);
+
+ void UpdateThreshold(double modified_offset, int64_t now_ms);
+
+ // Parameters.
+ TrendlineEstimatorSettings settings_;
+ const double smoothing_coef_;
+ const double threshold_gain_;
+ // Used by the existing threshold.
+ int num_of_deltas_;
+ // Keep the arrival times small by using the change from the first packet.
+ int64_t first_arrival_time_ms_;
+ // Exponential backoff filtering.
+ double accumulated_delay_;
+ double smoothed_delay_;
+ // Linear least squares regression.
+ std::deque<PacketTiming> delay_hist_;
+
+ const double k_up_;
+ const double k_down_;
+ double overusing_time_threshold_;
+ double threshold_;
+ double prev_modified_trend_;
+ int64_t last_update_ms_;
+ double prev_trend_;
+ double time_over_using_;
+ int overuse_counter_;
+ BandwidthUsage hypothesis_;
+ BandwidthUsage hypothesis_predicted_;
+ NetworkStatePredictor* network_state_predictor_;
+};
+} // namespace webrtc
+
+#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator_unittest.cc b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator_unittest.cc
new file mode 100644
index 0000000000..b0195abdf5
--- /dev/null
+++ b/third_party/libwebrtc/modules/congestion_controller/goog_cc/trendline_estimator_unittest.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+class PacketTimeGenerator {
+ public:
+ PacketTimeGenerator(int64_t initial_clock, double time_between_packets)
+ : initial_clock_(initial_clock),
+ time_between_packets_(time_between_packets),
+ packets_(0) {}
+ int64_t operator()() {
+ return initial_clock_ + time_between_packets_ * packets_++;
+ }
+
+ private:
+ const int64_t initial_clock_;
+ const double time_between_packets_;
+ size_t packets_;
+};
+
+class TrendlineEstimatorTest : public testing::Test {
+ public:
+ TrendlineEstimatorTest()
+ : send_times(kPacketCount),
+ recv_times(kPacketCount),
+ packet_sizes(kPacketCount),
+ config(),
+ estimator(&config, nullptr),
+ count(1) {
+ std::fill(packet_sizes.begin(), packet_sizes.end(), kPacketSizeBytes);
+ }
+
+ void RunTestUntilStateChange() {
+ RTC_DCHECK_EQ(send_times.size(), kPacketCount);
+ RTC_DCHECK_EQ(recv_times.size(), kPacketCount);
+ RTC_DCHECK_EQ(packet_sizes.size(), kPacketCount);
+ RTC_DCHECK_GE(count, 1);
+ RTC_DCHECK_LT(count, kPacketCount);
+
+ auto initial_state = estimator.State();
+ for (; count < kPacketCount; count++) {
+ double recv_delta = recv_times[count] - recv_times[count - 1];
+ double send_delta = send_times[count] - send_times[count - 1];
+ estimator.Update(recv_delta, send_delta, send_times[count],
+ recv_times[count], packet_sizes[count], true);
+ if (estimator.State() != initial_state) {
+ return;
+ }
+ }
+ }
+
+ protected:
+ const size_t kPacketCount = 25;
+ const size_t kPacketSizeBytes = 1200;
+ std::vector<int64_t> send_times;
+ std::vector<int64_t> recv_times;
+ std::vector<size_t> packet_sizes;
+ const FieldTrialBasedConfig config;
+ TrendlineEstimator estimator;
+ size_t count;
+};
+} // namespace
+
+TEST_F(TrendlineEstimatorTest, Normal) {
+ PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
+ 20 /*20 ms between sent packets*/);
+ std::generate(send_times.begin(), send_times.end(), send_time_generator);
+
+ PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
+ 20 /*delivered at the same pace*/);
+ std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
+
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
+ EXPECT_EQ(count, kPacketCount); // All packets processed
+}
+
+TEST_F(TrendlineEstimatorTest, Overusing) {
+ PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
+ 20 /*20 ms between sent packets*/);
+ std::generate(send_times.begin(), send_times.end(), send_time_generator);
+
+ PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
+ 1.1 * 20 /*10% slower delivery*/);
+ std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
+
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
+ EXPECT_EQ(count, kPacketCount); // All packets processed
+}
+
+TEST_F(TrendlineEstimatorTest, Underusing) {
+ PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
+ 20 /*20 ms between sent packets*/);
+ std::generate(send_times.begin(), send_times.end(), send_time_generator);
+
+ PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
+ 0.85 * 20 /*15% faster delivery*/);
+ std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
+
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
+ EXPECT_EQ(count, kPacketCount); // All packets processed
+}
+
+TEST_F(TrendlineEstimatorTest, IncludesSmallPacketsByDefault) {
+ PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
+ 20 /*20 ms between sent packets*/);
+ std::generate(send_times.begin(), send_times.end(), send_time_generator);
+
+ PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
+ 1.1 * 20 /*10% slower delivery*/);
+ std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
+
+ std::fill(packet_sizes.begin(), packet_sizes.end(), 100);
+
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
+ RunTestUntilStateChange();
+ EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
+ EXPECT_EQ(count, kPacketCount); // All packets processed
+}
+
+} // namespace webrtc