diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 096d7e4132..7ec13afc17 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -106,6 +106,8 @@ rtc_library("estimators") { 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", @@ -113,6 +115,8 @@ rtc_library("estimators") { "median_slope_estimator.h", "probe_bitrate_estimator.cc", "probe_bitrate_estimator.h", + "robust_throughput_estimator.cc", + "robust_throughput_estimator.h", "trendline_estimator.cc", "trendline_estimator.h", ] @@ -249,6 +253,7 @@ if (rtc_include_tests) { "median_slope_estimator_unittest.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", ] diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h index a1730075c0..97dd965fa4 100644 --- a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h @@ -18,11 +18,13 @@ #include "api/transport/network_types.h" #include "api/transport/webrtc_key_value_config.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 { +class AcknowledgedBitrateEstimator + : public AcknowledgedBitrateEstimatorInterface { public: AcknowledgedBitrateEstimator( const WebRtcKeyValueConfig* key_value_config, @@ -30,14 +32,14 @@ class AcknowledgedBitrateEstimator { explicit AcknowledgedBitrateEstimator( const WebRtcKeyValueConfig* key_value_config); - ~AcknowledgedBitrateEstimator(); + ~AcknowledgedBitrateEstimator() override; void IncomingPacketFeedbackVector( - const std::vector& packet_feedback_vector); - absl::optional bitrate() const; - absl::optional PeekRate() const; - void SetAlr(bool in_alr); - void SetAlrEndedTime(Timestamp alr_ended_time); + const std::vector& packet_feedback_vector) override; + absl::optional bitrate() const override; + absl::optional PeekRate() const override; + void SetAlr(bool in_alr) override; + void SetAlrEndedTime(Timestamp alr_ended_time) override; private: absl::optional alr_ended_time_; diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc new file mode 100644 index 0000000000..e86ab63d50 --- /dev/null +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc @@ -0,0 +1,62 @@ +/* + * 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 "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 WebRtcKeyValueConfig* key_value_config) { + Parser()->Parse( + key_value_config->Lookup(RobustThroughputEstimatorSettings::kKey)); + if (min_packets < 10 || kMaxPackets < min_packets) { + RTC_LOG(LS_WARNING) << "Window size must be between 10 and " << kMaxPackets + << " packets"; + min_packets = 20; + } + if (window_duration < TimeDelta::ms(100) || + TimeDelta::ms(2000) < window_duration) { + RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 2000 ms"; + window_duration = TimeDelta::ms(500); + } +} + +std::unique_ptr +RobustThroughputEstimatorSettings::Parser() { + return StructParametersParser::Create("enabled", &enabled, // + "reduce_bias", &reduce_bias, // + "assume_shared_link", + &assume_shared_link, // + "min_packets", &min_packets, // + "window_duration", &window_duration); +} + +AcknowledgedBitrateEstimatorInterface:: + ~AcknowledgedBitrateEstimatorInterface() {} + +std::unique_ptr +AcknowledgedBitrateEstimatorInterface::Create( + const WebRtcKeyValueConfig* key_value_config) { + RobustThroughputEstimatorSettings simplified_estimator_settings( + key_value_config); + if (simplified_estimator_settings.enabled) { + return std::make_unique( + simplified_estimator_settings); + } + return std::make_unique(key_value_config); +} + +} // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h new file mode 100644 index 0000000000..0b29a2c71a --- /dev/null +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h @@ -0,0 +1,71 @@ +/* + * 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 +#include + +#include "absl/types/optional.h" +#include "api/transport/network_types.h" +#include "api/transport/webrtc_key_value_config.h" +#include "api/units/data_rate.h" +#include "rtc_base/experiments/struct_parameters_parser.h" + +namespace webrtc { + +struct RobustThroughputEstimatorSettings { + static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings"; + static constexpr size_t kMaxPackets = 500; + + RobustThroughputEstimatorSettings() = delete; + explicit RobustThroughputEstimatorSettings( + const WebRtcKeyValueConfig* key_value_config); + + bool enabled = false; // Set to true to use RobustThroughputEstimator. + + // The estimator handles delay spikes by removing the largest receive time + // gap, but this introduces some bias that may lead to overestimation when + // there isn't any delay spike. If |reduce_bias| is true, we instead replace + // the largest receive time gap by the second largest. This reduces the bias + // at the cost of not completely removing the genuine delay spikes. + bool reduce_bias = true; + + // If |assume_shared_link| is false, we ignore the size of the first packet + // when computing the receive rate. Otherwise, we remove half of the first + // and last packet's sizes. + bool assume_shared_link = false; + + // The estimator window keeps at least |min_packets| packets and up to + // kMaxPackets received during the last |window_duration|. + unsigned min_packets = 20; + TimeDelta window_duration = TimeDelta::ms(500); + + std::unique_ptr Parser(); +}; + +class AcknowledgedBitrateEstimatorInterface { + public: + static std::unique_ptr Create( + const WebRtcKeyValueConfig* key_value_config); + virtual ~AcknowledgedBitrateEstimatorInterface(); + + virtual void IncomingPacketFeedbackVector( + const std::vector& packet_feedback_vector) = 0; + virtual absl::optional bitrate() const = 0; + virtual absl::optional 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/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc index e8e2ffdeb5..92e6f02f52 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc @@ -149,7 +149,7 @@ DelayBasedBweTest::DelayBasedBweTest() : field_trial(), clock_(100000000), acknowledged_bitrate_estimator_( - std::make_unique(&field_trial_config_)), + AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)), probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)), bitrate_estimator_( new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)), @@ -163,7 +163,7 @@ DelayBasedBweTest::DelayBasedBweTest(const std::string& field_trial_string) std::make_unique(field_trial_string)), clock_(100000000), acknowledged_bitrate_estimator_( - std::make_unique(&field_trial_config_)), + AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)), probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)), bitrate_estimator_( new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)), diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h index a3b4c35953..608cd6bc70 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h @@ -169,7 +169,8 @@ class DelayBasedBweTest : public ::testing::Test { field_trial; // Must be initialized first. SimulatedClock clock_; // Time at the receiver. test::TestBitrateObserver bitrate_observer_; - std::unique_ptr acknowledged_bitrate_estimator_; + std::unique_ptr + acknowledged_bitrate_estimator_; const std::unique_ptr probe_bitrate_estimator_; std::unique_ptr bitrate_estimator_; std::unique_ptr stream_generator_; diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index 52ea5cecc1..25cdee61b5 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -23,7 +23,6 @@ #include "api/units/time_delta.h" #include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" -#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h" #include "modules/congestion_controller/goog_cc/alr_detector.h" #include "modules/congestion_controller/goog_cc/probe_controller.h" #include "modules/remote_bitrate_estimator/include/bwe_defines.h" @@ -32,6 +31,7 @@ #include "rtc_base/logging.h" namespace webrtc { + namespace { // From RTCPSender video report interval. constexpr TimeDelta kLossUpdateInterval = TimeDelta::Millis<1000>(); @@ -96,7 +96,7 @@ GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config, event_log_, network_state_predictor_.get())), acknowledged_bitrate_estimator_( - std::make_unique(key_value_config_)), + 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_), @@ -146,8 +146,8 @@ NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange( } } - acknowledged_bitrate_estimator_.reset( - new AcknowledgedBitrateEstimator(key_value_config_)); + acknowledged_bitrate_estimator_ = + AcknowledgedBitrateEstimatorInterface::Create(key_value_config_); probe_bitrate_estimator_.reset(new ProbeBitrateEstimator(event_log_)); if (network_estimator_) network_estimator_->OnRouteChange(msg); diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/modules/congestion_controller/goog_cc/goog_cc_network_control.h index 02ac49de5d..5b3ae94d99 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.h +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.h @@ -27,7 +27,7 @@ #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.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" @@ -100,7 +100,8 @@ class GoogCcNetworkController : public NetworkControllerInterface { std::unique_ptr network_estimator_; std::unique_ptr network_state_predictor_; std::unique_ptr delay_based_bwe_; - std::unique_ptr acknowledged_bitrate_estimator_; + std::unique_ptr + acknowledged_bitrate_estimator_; absl::optional initial_config_; diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc b/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc new file mode 100644 index 0000000000..5966bc0a7e --- /dev/null +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc @@ -0,0 +1,132 @@ +/* + * 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 + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +RobustThroughputEstimator::RobustThroughputEstimator( + const RobustThroughputEstimatorSettings& settings) + : settings_(settings) { + RTC_DCHECK(settings.enabled); +} + +RobustThroughputEstimator::~RobustThroughputEstimator() {} + +void RobustThroughputEstimator::IncomingPacketFeedbackVector( + const std::vector& 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) { + // Insert the new packet. + window_.push_back(packet); + // 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 (window_.size() > settings_.kMaxPackets || + (window_.size() > settings_.min_packets && + packet.receive_time - window_.front().receive_time > + settings_.window_duration)) { + window_.pop_front(); + } + } +} + +absl::optional RobustThroughputEstimator::bitrate() const { + if (window_.size() < settings_.min_packets) + return absl::nullopt; + + TimeDelta largest_recv_gap(TimeDelta::ms(0)); + TimeDelta second_largest_recv_gap(TimeDelta::ms(0)); + 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 min_send_time = window_[0].sent_packet.send_time; + Timestamp max_send_time = window_[0].sent_packet.send_time; + Timestamp min_recv_time = window_[0].receive_time; + Timestamp max_recv_time = window_[0].receive_time; + DataSize data_size = DataSize::bytes(0); + for (const auto& packet : window_) { + min_send_time = std::min(min_send_time, packet.sent_packet.send_time); + max_send_time = std::max(max_send_time, packet.sent_packet.send_time); + min_recv_time = std::min(min_recv_time, packet.receive_time); + max_recv_time = std::max(max_recv_time, packet.receive_time); + data_size += packet.sent_packet.size; + data_size += packet.sent_packet.prior_unacked_data; + } + + // 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 one packet. + DataSize recv_size = data_size; + DataSize send_size = data_size; + if (settings_.assume_shared_link) { + // Depending on how the bottleneck queue is implemented, a large packet + // may delay sending of sebsequent packets, so the delay between packets + // i and i+1 depends on the size of both packets. In this case we minimize + // the maximum error by removing half of both the first and last packet + // size. + DataSize first_last_average_size = + (window_.front().sent_packet.size + + window_.front().sent_packet.prior_unacked_data + + window_.back().sent_packet.size + + window_.back().sent_packet.prior_unacked_data) / + 2; + recv_size -= first_last_average_size; + send_size -= first_last_average_size; + } else { + // In the simpler case where the delay between packets i and i+1 only + // depends on the size of packet i+1, the first packet doesn't give us + // any information. Analogously, we assume that the start send time + // for the last packet doesn't depend on the size of the packet. + recv_size -= (window_.front().sent_packet.size + + window_.front().sent_packet.prior_unacked_data); + send_size -= (window_.back().sent_packet.size + + window_.back().sent_packet.prior_unacked_data); + } + + // Remove the largest gap by replacing it by the second largest gap + // or the average gap. + TimeDelta send_duration = max_send_time - min_send_time; + TimeDelta recv_duration = (max_recv_time - min_recv_time) - largest_recv_gap; + if (settings_.reduce_bias) { + recv_duration += second_largest_recv_gap; + } else { + recv_duration += recv_duration / (window_.size() - 2); + } + + send_duration = std::max(send_duration, TimeDelta::ms(1)); + recv_duration = std::max(recv_duration, TimeDelta::ms(1)); + return std::min(send_size / send_duration, recv_size / recv_duration); +} + +} // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator.h b/modules/congestion_controller/goog_cc/robust_throughput_estimator.h new file mode 100644 index 0000000000..de48a9b599 --- /dev/null +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator.h @@ -0,0 +1,48 @@ +/* + * 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 +#include +#include + +#include "absl/types/optional.h" +#include "api/transport/network_types.h" +#include "api/transport/webrtc_key_value_config.h" +#include "api/units/data_rate.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& packet_feedback_vector) override; + + absl::optional bitrate() const override; + + absl::optional PeekRate() const override { return bitrate(); } + void SetAlr(bool /*in_alr*/) override {} + void SetAlrEndedTime(Timestamp /*alr_ended_time*/) override {} + + private: + const RobustThroughputEstimatorSettings settings_; + std::deque window_; +}; + +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_ diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc b/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc new file mode 100644 index 0000000000..83773422c1 --- /dev/null +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc @@ -0,0 +1,167 @@ +/* + * 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 "api/transport/field_trial_based_config.h" +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { +std::vector CreateFeedbackVector(size_t number_of_packets, + DataSize packet_size, + TimeDelta send_increment, + TimeDelta recv_increment, + Timestamp* send_clock, + Timestamp* recv_clock, + uint16_t* sequence_number) { + std::vector packet_feedback_vector(number_of_packets); + for (size_t i = 0; i < number_of_packets; i++) { + packet_feedback_vector[i].receive_time = *recv_clock; + 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 += send_increment; + *recv_clock += recv_increment; + *sequence_number += 1; + } + return packet_feedback_vector; +} +} // anonymous namespace + +TEST(RobustThroughputEstimatorTest, SteadyRate) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," + "window_duration:100ms/"); + FieldTrialBasedConfig field_trial_config; + RobustThroughputEstimatorSettings settings(&field_trial_config); + RobustThroughputEstimator throughput_estimator(settings); + DataSize packet_size(DataSize::bytes(1000)); + Timestamp send_clock(Timestamp::ms(100000)); + Timestamp recv_clock(Timestamp::ms(10000)); + TimeDelta send_increment(TimeDelta::ms(10)); + TimeDelta recv_increment(TimeDelta::ms(10)); + uint16_t sequence_number = 100; + std::vector packet_feedback = + CreateFeedbackVector(9, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + EXPECT_FALSE(throughput_estimator.bitrate().has_value()); + + packet_feedback = + CreateFeedbackVector(11, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, + 0.05 * 100 * 1000.0); // Allow 5% error +} + +TEST(RobustThroughputEstimatorTest, DelaySpike) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," + "window_duration:100ms/"); + FieldTrialBasedConfig field_trial_config; + RobustThroughputEstimatorSettings settings(&field_trial_config); + RobustThroughputEstimator throughput_estimator(settings); + DataSize packet_size(DataSize::bytes(1000)); + Timestamp send_clock(Timestamp::ms(100000)); + Timestamp recv_clock(Timestamp::ms(10000)); + TimeDelta send_increment(TimeDelta::ms(10)); + TimeDelta recv_increment(TimeDelta::ms(10)); + uint16_t sequence_number = 100; + std::vector packet_feedback = + CreateFeedbackVector(20, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, + 0.05 * 100 * 1000.0); // Allow 5% error + + // Delay spike + recv_clock += TimeDelta::ms(40); + + // Faster delivery after the gap + recv_increment = TimeDelta::ms(2); + packet_feedback = + CreateFeedbackVector(5, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, + 0.05 * 100 * 1000.0); // Allow 5% error + + // Delivery at normal rate. This will be capped by the send rate. + recv_increment = TimeDelta::ms(10); + packet_feedback = + CreateFeedbackVector(5, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, + 0.05 * 100 * 1000.0); // Allow 5% error +} + +TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," + "window_duration:100ms/"); + FieldTrialBasedConfig field_trial_config; + RobustThroughputEstimatorSettings settings(&field_trial_config); + RobustThroughputEstimator throughput_estimator(settings); + DataSize packet_size(DataSize::bytes(1000)); + Timestamp send_clock(Timestamp::ms(100000)); + Timestamp recv_clock(Timestamp::ms(10000)); + TimeDelta send_increment(TimeDelta::ms(10)); + TimeDelta recv_increment(TimeDelta::ms(40)); + uint16_t sequence_number = 100; + std::vector packet_feedback = + CreateFeedbackVector(20, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 25 * 1000.0, + 0.05 * 25 * 1000.0); // Allow 5% error +} + +TEST(RobustThroughputEstimatorTest, CappedBySendRate) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," + "window_duration:100ms/"); + FieldTrialBasedConfig field_trial_config; + RobustThroughputEstimatorSettings settings(&field_trial_config); + RobustThroughputEstimator throughput_estimator(settings); + DataSize packet_size(DataSize::bytes(1000)); + Timestamp send_clock(Timestamp::ms(100000)); + Timestamp recv_clock(Timestamp::ms(10000)); + TimeDelta send_increment(TimeDelta::ms(20)); + TimeDelta recv_increment(TimeDelta::ms(10)); + uint16_t sequence_number = 100; + std::vector packet_feedback = + CreateFeedbackVector(20, packet_size, send_increment, recv_increment, + &send_clock, &recv_clock, &sequence_number); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), 50 * 1000.0, + 0.05 * 50 * 1000.0); // Allow 5% error +} + +} // namespace webrtc*/ diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc index 106e8daa9f..1ab7eb9043 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc @@ -1269,14 +1269,14 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { FieldTrialBasedConfig field_trial_config_; // The event_log_visualizer should normally not be compiled with // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE since the normal plots won't work. - // However, compiling with BWE_TEST_LOGGING, running with --plot_sendside_bwe + // However, compiling with BWE_TEST_LOGGING, running with --plot=sendside_bwe // and piping the output to plot_dynamics.py can be used as a hack to get the // internal state of various BWE components. In this case, it is important // we don't instantiate the AcknowledgedBitrateEstimator both here and in // GoogCcNetworkController since that would lead to duplicate outputs. - AcknowledgedBitrateEstimator acknowledged_bitrate_estimator( - &field_trial_config_, - std::make_unique(&field_trial_config_)); + std::unique_ptr + acknowledged_bitrate_estimator( + AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)); #endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) int64_t time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); @@ -1321,7 +1321,8 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { feedback_msg->SortedByReceiveTime(); if (!feedback.empty()) { #if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) - acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback); + acknowledged_bitrate_estimator->IncomingPacketFeedbackVector( + feedback); #endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) for (const PacketResult& packet : feedback) acked_bitrate.Update(packet.sent_packet.size.bytes(), @@ -1334,7 +1335,7 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { float y = bitrate_bps.value_or(0) / 1000; acked_time_series.points.emplace_back(x, y); #if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) - y = acknowledged_bitrate_estimator.bitrate() + y = acknowledged_bitrate_estimator->bitrate() .value_or(DataRate::Zero()) .kbps(); acked_estimate_time_series.points.emplace_back(x, y);