diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn index f65cde43c3..6f2b853f8f 100644 --- a/modules/congestion_controller/BUILD.gn +++ b/modules/congestion_controller/BUILD.gn @@ -49,7 +49,6 @@ if (rtc_include_tests) { "../../test:test_support", "../../test/scenario", "../pacing", - "bbr:bbr_unittests", "goog_cc:estimators", "goog_cc:goog_cc_unittests", "pcc:pcc_unittests", diff --git a/modules/congestion_controller/bbr/BUILD.gn b/modules/congestion_controller/bbr/BUILD.gn deleted file mode 100644 index bc9d78f334..0000000000 --- a/modules/congestion_controller/bbr/BUILD.gn +++ /dev/null @@ -1,138 +0,0 @@ -# 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") - -rtc_library("bbr") { - sources = [ - "bbr_factory.cc", - "bbr_factory.h", - ] - deps = [ - ":bbr_controller", - "../../../api/transport:network_control", - "../../../api/units:time_delta", - "../../../rtc_base:rtc_base_approved", - ] -} - -rtc_library("bbr_controller") { - visibility = [ ":*" ] - sources = [ - "bbr_network_controller.cc", - "bbr_network_controller.h", - ] - deps = [ - ":bandwidth_sampler", - ":loss_rate_filter", - ":rtt_stats", - ":windowed_filter", - "../../../api/transport:network_control", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - "../../../rtc_base/experiments:field_trial_parser", - "../../../system_wrappers:field_trial", - "//third_party/abseil-cpp/absl/base:core_headers", - "//third_party/abseil-cpp/absl/types:optional", - ] -} - -rtc_library("bandwidth_sampler") { - visibility = [ ":*" ] - sources = [ - "bandwidth_sampler.cc", - "bandwidth_sampler.h", - ] - deps = [ - ":packet_number_indexed_queue", - "../../../api/units:data_rate", - "../../../api/units:data_size", - "../../../api/units:time_delta", - "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - "//third_party/abseil-cpp/absl/types:optional", - ] -} - -rtc_library("data_transfer_tracker") { - visibility = [ ":*" ] - sources = [ - "data_transfer_tracker.cc", - "data_transfer_tracker.h", - ] - deps = [ - "../../../api/units:data_size", - "../../../api/units:time_delta", - "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - ] -} - -rtc_source_set("packet_number_indexed_queue") { - visibility = [ ":*" ] - sources = [ "packet_number_indexed_queue.h" ] - deps = [ "../../../rtc_base:checks" ] -} - -rtc_library("loss_rate_filter") { - visibility = [ ":*" ] - sources = [ - "loss_rate_filter.cc", - "loss_rate_filter.h", - ] - deps = [ "//third_party/abseil-cpp/absl/types:optional" ] -} -rtc_library("rtt_stats") { - visibility = [ ":*" ] - sources = [ - "rtt_stats.cc", - "rtt_stats.h", - ] - deps = [ - "../../../api/units:time_delta", - "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - ] -} -rtc_source_set("windowed_filter") { - visibility = [ ":*" ] - sources = [ "windowed_filter.h" ] -} -if (rtc_include_tests) { - rtc_library("bbr_unittests") { - testonly = true - sources = [ - "bandwidth_sampler_unittest.cc", - "bbr_network_controller_unittest.cc", - "data_transfer_tracker_unittest.cc", - "loss_rate_filter_unittest.cc", - "packet_number_indexed_queue_unittest.cc", - "rtt_stats_unittest.cc", - "windowed_filter_unittest.cc", - ] - deps = [ - ":bandwidth_sampler", - ":bbr", - ":bbr_controller", - ":data_transfer_tracker", - ":loss_rate_filter", - ":packet_number_indexed_queue", - ":rtt_stats", - ":windowed_filter", - "../../../api/units:data_rate", - "../../../api/units:time_delta", - "../../../api/units:timestamp", - "../../../rtc_base:logging", - "../../../test:test_support", - "../../../test/scenario", - ] - } -} diff --git a/modules/congestion_controller/bbr/bandwidth_sampler.cc b/modules/congestion_controller/bbr/bandwidth_sampler.cc deleted file mode 100644 index f61e1401e8..0000000000 --- a/modules/congestion_controller/bbr/bandwidth_sampler.cc +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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. - */ -// Based on the Quic implementation in Chromium. - -#include "modules/congestion_controller/bbr/bandwidth_sampler.h" - -#include - -#include "rtc_base/logging.h" - -namespace webrtc { -namespace bbr { -namespace { -constexpr int64_t kMaxTrackedPackets = 10000; -} - -BandwidthSampler::BandwidthSampler() - : total_data_sent_(DataSize::Zero()), - total_data_acked_(DataSize::Zero()), - total_data_sent_at_last_acked_packet_(DataSize::Zero()), - last_acked_packet_sent_time_(), - last_acked_packet_ack_time_(), - last_sent_packet_(0), - is_app_limited_(false), - end_of_app_limited_phase_(0), - connection_state_map_() {} - -BandwidthSampler::~BandwidthSampler() {} - -void BandwidthSampler::OnPacketSent(Timestamp sent_time, - int64_t packet_number, - DataSize data_size, - DataSize data_in_flight) { - last_sent_packet_ = packet_number; - - total_data_sent_ += data_size; - - // If there are no packets in flight, the time at which the new transmission - // opens can be treated as the A_0 point for the purpose of bandwidth - // sampling. This underestimates bandwidth to some extent, and produces some - // artificially low samples for most packets in flight, but it provides with - // samples at important points where we would not have them otherwise, most - // importantly at the beginning of the connection. - if (data_in_flight.IsZero()) { - last_acked_packet_ack_time_ = sent_time; - total_data_sent_at_last_acked_packet_ = total_data_sent_; - - // In this situation ack compression is not a concern, set send rate to - // effectively infinite. - last_acked_packet_sent_time_ = sent_time; - } - - if (!connection_state_map_.IsEmpty() && - packet_number > - connection_state_map_.last_packet() + kMaxTrackedPackets) { - RTC_LOG(LS_WARNING) - << "BandwidthSampler in-flight packet map has exceeded maximum " - "number " - "of tracked packets."; - } - - bool success = - connection_state_map_.Emplace(packet_number, sent_time, data_size, *this); - if (!success) - RTC_LOG(LS_WARNING) << "BandwidthSampler failed to insert the packet " - "into the map, most likely because it's already " - "in it."; -} - -BandwidthSample BandwidthSampler::OnPacketAcknowledged(Timestamp ack_time, - int64_t packet_number) { - ConnectionStateOnSentPacket* sent_packet_pointer = - connection_state_map_.GetEntry(packet_number); - if (sent_packet_pointer == nullptr) { - return BandwidthSample(); - } - BandwidthSample sample = - OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer); - connection_state_map_.Remove(packet_number); - return sample; -} - -BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( - Timestamp ack_time, - int64_t packet_number, - const ConnectionStateOnSentPacket& sent_packet) { - total_data_acked_ += sent_packet.size; - total_data_sent_at_last_acked_packet_ = sent_packet.total_data_sent; - last_acked_packet_sent_time_ = sent_packet.sent_time; - last_acked_packet_ack_time_ = ack_time; - - // Exit app-limited phase once a packet that was sent while the connection is - // not app-limited is acknowledged. - if (is_app_limited_ && packet_number > end_of_app_limited_phase_) { - is_app_limited_ = false; - } - - // There might have been no packets acknowledged at the moment when the - // current packet was sent. In that case, there is no bandwidth sample to - // make. - if (!sent_packet.last_acked_packet_sent_time || - !sent_packet.last_acked_packet_ack_time) { - return BandwidthSample(); - } - - // Infinite rate indicates that the sampler is supposed to discard the - // current send rate sample and use only the ack rate. - DataRate send_rate = DataRate::Infinity(); - if (sent_packet.sent_time > *sent_packet.last_acked_packet_sent_time) { - DataSize sent_delta = sent_packet.total_data_sent - - sent_packet.total_data_sent_at_last_acked_packet; - TimeDelta time_delta = - sent_packet.sent_time - *sent_packet.last_acked_packet_sent_time; - send_rate = sent_delta / time_delta; - } - - // During the slope calculation, ensure that ack time of the current packet is - // always larger than the time of the previous packet, otherwise division by - // zero or integer underflow can occur. - if (ack_time <= *sent_packet.last_acked_packet_ack_time) { - RTC_LOG(LS_WARNING) - << "Time of the previously acked packet is larger than the time " - "of the current packet."; - return BandwidthSample(); - } - DataSize ack_delta = - total_data_acked_ - sent_packet.total_data_acked_at_the_last_acked_packet; - TimeDelta time_delta = ack_time - *sent_packet.last_acked_packet_ack_time; - DataRate ack_rate = ack_delta / time_delta; - - BandwidthSample sample; - sample.bandwidth = std::min(send_rate, ack_rate); - // Note: this sample does not account for delayed acknowledgement time. This - // means that the RTT measurements here can be artificially high, especially - // on low bandwidth connections. - sample.rtt = ack_time - sent_packet.sent_time; - // A sample is app-limited if the packet was sent during the app-limited - // phase. - sample.is_app_limited = sent_packet.is_app_limited; - return sample; -} - -void BandwidthSampler::OnPacketLost(int64_t packet_number) { - connection_state_map_.Remove(packet_number); -} - -void BandwidthSampler::OnAppLimited() { - is_app_limited_ = true; - end_of_app_limited_phase_ = last_sent_packet_; -} - -void BandwidthSampler::RemoveObsoletePackets(int64_t least_unacked) { - while (!connection_state_map_.IsEmpty() && - connection_state_map_.first_packet() < least_unacked) { - connection_state_map_.Remove(connection_state_map_.first_packet()); - } -} - -DataSize BandwidthSampler::total_data_acked() const { - return total_data_acked_; -} - -bool BandwidthSampler::is_app_limited() const { - return is_app_limited_; -} - -int64_t BandwidthSampler::end_of_app_limited_phase() const { - return end_of_app_limited_phase_; -} - -BandwidthSampler::ConnectionStateOnSentPacket::ConnectionStateOnSentPacket( - Timestamp sent_time, - DataSize size, - const BandwidthSampler& sampler) - : sent_time(sent_time), - size(size), - total_data_sent(sampler.total_data_sent_), - total_data_sent_at_last_acked_packet( - sampler.total_data_sent_at_last_acked_packet_), - last_acked_packet_sent_time(sampler.last_acked_packet_sent_time_), - last_acked_packet_ack_time(sampler.last_acked_packet_ack_time_), - total_data_acked_at_the_last_acked_packet(sampler.total_data_acked_), - is_app_limited(sampler.is_app_limited_) {} - -BandwidthSampler::ConnectionStateOnSentPacket::ConnectionStateOnSentPacket() - : sent_time(Timestamp::MinusInfinity()), - size(DataSize::Zero()), - total_data_sent(DataSize::Zero()), - total_data_sent_at_last_acked_packet(DataSize::Zero()), - last_acked_packet_sent_time(), - last_acked_packet_ack_time(), - total_data_acked_at_the_last_acked_packet(DataSize::Zero()), - is_app_limited(false) {} - -BandwidthSampler::ConnectionStateOnSentPacket::~ConnectionStateOnSentPacket() {} - -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/bandwidth_sampler.h b/modules/congestion_controller/bbr/bandwidth_sampler.h deleted file mode 100644 index 7e0a44e61d..0000000000 --- a/modules/congestion_controller/bbr/bandwidth_sampler.h +++ /dev/null @@ -1,261 +0,0 @@ -/* - * 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. - */ -// Based on the Quic implementation in Chromium. - -#ifndef MODULES_CONGESTION_CONTROLLER_BBR_BANDWIDTH_SAMPLER_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_BANDWIDTH_SAMPLER_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 "modules/congestion_controller/bbr/packet_number_indexed_queue.h" - -namespace webrtc { -namespace bbr { - -namespace test { -class BandwidthSamplerPeer; -} // namespace test - -struct BandwidthSample { - // The bandwidth at that particular sample. Zero if no valid bandwidth sample - // is available. - DataRate bandwidth; - - // The RTT measurement at this particular sample. Zero if no RTT sample is - // available. Does not correct for delayed ack time. - TimeDelta rtt; - - // Indicates whether the sample might be artificially low because the sender - // did not have enough data to send in order to saturate the link. - bool is_app_limited; - - BandwidthSample() - : bandwidth(DataRate::Zero()), - rtt(TimeDelta::Zero()), - is_app_limited(false) {} -}; - -// BandwidthSampler keeps track of sent and acknowledged packets and outputs a -// bandwidth sample for every packet acknowledged. The samples are taken for -// individual packets, and are not filtered; the consumer has to filter the -// bandwidth samples itself. In certain cases, the sampler will locally severely -// underestimate the bandwidth, hence a maximum filter with a size of at least -// one RTT is recommended. -// -// This class bases its samples on the slope of two curves: the number of -// data_size sent over time, and the number of data_size acknowledged as -// received over time. It produces a sample of both slopes for every packet that -// gets acknowledged, based on a slope between two points on each of the -// corresponding curves. Note that due to the packet loss, the number of -// data_size on each curve might get further and further away from each other, -// meaning that it is not feasible to compare byte values coming from different -// curves with each other. -// -// The obvious points for measuring slope sample are the ones corresponding to -// the packet that was just acknowledged. Let us denote them as S_1 (point at -// which the current packet was sent) and A_1 (point at which the current packet -// was acknowledged). However, taking a slope requires two points on each line, -// so estimating bandwidth requires picking a packet in the past with respect to -// which the slope is measured. -// -// For that purpose, BandwidthSampler always keeps track of the most recently -// acknowledged packet, and records it together with every outgoing packet. -// When a packet gets acknowledged (A_1), it has not only information about when -// it itself was sent (S_1), but also the information about the latest -// acknowledged packet right before it was sent (S_0 and A_0). -// -// Based on that data, send and ack rate are estimated as: -// send_rate = (data_size(S_1) - data_size(S_0)) / (time(S_1) - time(S_0)) -// ack_rate = (data_size(A_1) - data_size(A_0)) / (time(A_1) - time(A_0)) -// -// Here, the ack rate is intuitively the rate we want to treat as bandwidth. -// However, in certain cases (e.g. ack compression) the ack rate at a point may -// end up higher than the rate at which the data was originally sent, which is -// not indicative of the real bandwidth. Hence, we use the send rate as an upper -// bound, and the sample value is -// rate_sample = min(send_rate, ack_rate) -// -// An important edge case handled by the sampler is tracking the app-limited -// samples. There are multiple meaning of "app-limited" used interchangeably, -// hence it is important to understand and to be able to distinguish between -// them. -// -// Meaning 1: connection state. The connection is said to be app-limited when -// there is no outstanding data to send. This means that certain bandwidth -// samples in the future would not be an accurate indication of the link -// capacity, and it is important to inform consumer about that. Whenever -// connection becomes app-limited, the sampler is notified via OnAppLimited() -// method. -// -// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth -// sampler becomes notified about the connection being app-limited, it enters -// app-limited phase. In that phase, all *sent* packets are marked as -// app-limited. Note that the connection itself does not have to be -// app-limited during the app-limited phase, and in fact it will not be -// (otherwise how would it send packets?). The boolean flag below indicates -// whether the sampler is in that phase. -// -// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is -// sent during the app-limited phase, the resulting sample related to the -// packet will be marked as app-limited. -// -// With the terminology issue out of the way, let us consider the question of -// what kind of situation it addresses. -// -// Consider a scenario where we first send packets 1 to 20 at a regular -// bandwidth, and then immediately run out of data. After a few seconds, we send -// packets 21 to 60, and only receive ack for 21 between sending packets 40 and -// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0 -// we use to compute the slope is going to be packet 20, a few seconds apart -// from the current packet, hence the resulting estimate would be extremely low -// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21, -// meaning that the bandwidth sample would exclude the quiescence. -// -// Based on the analysis of that scenario, we implement the following rule: once -// OnAppLimited() is called, all sent packets will produce app-limited samples -// up until an ack for a packet that was sent after OnAppLimited() was called. -// Note that while the scenario above is not the only scenario when the -// connection is app-limited, the approach works in other cases too. -class BandwidthSampler { - public: - BandwidthSampler(); - ~BandwidthSampler(); - // Inputs the sent packet information into the sampler. Assumes that all - // packets are sent in order. The information about the packet will not be - // released from the sampler until the packet is either acknowledged or - // declared lost. - void OnPacketSent(Timestamp sent_time, - int64_t packet_number, - DataSize data_size, - DataSize data_in_flight); - - // Notifies the sampler that the |packet_number| is acknowledged. Returns a - // bandwidth sample. If no bandwidth sample is available, bandwidth is set to - // DataRate::Zero(). - BandwidthSample OnPacketAcknowledged(Timestamp ack_time, - int64_t packet_number); - - // Informs the sampler that a packet is considered lost and it should no - // longer keep track of it. - void OnPacketLost(int64_t packet_number); - - // Informs the sampler that the connection is currently app-limited, causing - // the sampler to enter the app-limited phase. The phase will expire by - // itself. - void OnAppLimited(); - - // Remove all the packets lower than the specified packet number. - void RemoveObsoletePackets(int64_t least_unacked); - - // Total number of data_size currently acknowledged by the receiver. - DataSize total_data_acked() const; - - // Application-limited information exported for debugging. - bool is_app_limited() const; - int64_t end_of_app_limited_phase() const; - - private: - friend class test::BandwidthSamplerPeer; - // ConnectionStateOnSentPacket represents the information about a sent packet - // and the state of the connection at the moment the packet was sent, - // specifically the information about the most recently acknowledged packet at - // that moment. - struct ConnectionStateOnSentPacket { - // Time at which the packet is sent. - Timestamp sent_time; - - // Size of the packet. - DataSize size; - - // The value of |total_data_sent_| at the time the packet was sent. - // Includes the packet itself. - DataSize total_data_sent; - - // The value of |total_data_sent_at_last_acked_packet_| at the time the - // packet was sent. - DataSize total_data_sent_at_last_acked_packet; - - // The value of |last_acked_packet_sent_time_| at the time the packet was - // sent. - absl::optional last_acked_packet_sent_time; - - // The value of |last_acked_packet_ack_time_| at the time the packet was - // sent. - absl::optional last_acked_packet_ack_time; - - // The value of |total_data_acked_| at the time the packet was - // sent. - DataSize total_data_acked_at_the_last_acked_packet; - - // The value of |is_app_limited_| at the time the packet was - // sent. - bool is_app_limited; - - // Snapshot constructor. Records the current state of the bandwidth - // sampler. - ConnectionStateOnSentPacket(Timestamp sent_time, - DataSize size, - const BandwidthSampler& sampler); - - // Default constructor. Required to put this structure into - // PacketNumberIndexedQueue. - ConnectionStateOnSentPacket(); - ~ConnectionStateOnSentPacket(); - }; - - // The total number of congestion controlled data_size sent during the - // connection. - DataSize total_data_sent_; - - // The total number of congestion controlled data_size which were - // acknowledged. - DataSize total_data_acked_; - - // The value of |total_data_sent_| at the time the last acknowledged packet - // was sent. Valid only when |last_acked_packet_sent_time_| is valid. - DataSize total_data_sent_at_last_acked_packet_; - - // The time at which the last acknowledged packet was sent. Set to - // Timestamp::Zero() if no valid timestamp is available. - absl::optional last_acked_packet_sent_time_; - - // The time at which the most recent packet was acknowledged. - absl::optional last_acked_packet_ack_time_; - - // The most recently sent packet. - int64_t last_sent_packet_; - - // Indicates whether the bandwidth sampler is currently in an app-limited - // phase. - bool is_app_limited_; - - // The packet that will be acknowledged after this one will cause the sampler - // to exit the app-limited phase. - int64_t end_of_app_limited_phase_; - - // Record of the connection state at the point where each packet in flight was - // sent, indexed by the packet number. - PacketNumberIndexedQueue connection_state_map_; - - // Handles the actual bandwidth calculations, whereas the outer method handles - // retrieving and removing |sent_packet|. - BandwidthSample OnPacketAcknowledgedInner( - Timestamp ack_time, - int64_t packet_number, - const ConnectionStateOnSentPacket& sent_packet); -}; - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_BANDWIDTH_SAMPLER_H_ diff --git a/modules/congestion_controller/bbr/bandwidth_sampler_unittest.cc b/modules/congestion_controller/bbr/bandwidth_sampler_unittest.cc deleted file mode 100644 index 04ee64e1d2..0000000000 --- a/modules/congestion_controller/bbr/bandwidth_sampler_unittest.cc +++ /dev/null @@ -1,337 +0,0 @@ -/* - * 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. - */ -// Based on the Quic implementation in Chromium. - -#include "modules/congestion_controller/bbr/bandwidth_sampler.h" - -#include - -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { -namespace test { - -class BandwidthSamplerPeer { - public: - static size_t GetNumberOfTrackedPackets(const BandwidthSampler& sampler) { - return sampler.connection_state_map_.number_of_present_entries(); - } - - static DataSize GetPacketSize(const BandwidthSampler& sampler, - int64_t packet_number) { - return sampler.connection_state_map_.GetEntry(packet_number)->size; - } -}; - -const int64_t kRegularPacketSizeBytes = 1280; -// Enforce divisibility for some of the tests. -static_assert((kRegularPacketSizeBytes & 31) == 0, - "kRegularPacketSizeBytes has to be five times divisible by 2"); - -const DataSize kRegularPacketSize = DataSize::Bytes(kRegularPacketSizeBytes); - -// A test fixture with utility methods for BandwidthSampler tests. -class BandwidthSamplerTest : public ::testing::Test { - protected: - BandwidthSamplerTest() - : clock_(Timestamp::Seconds(100)), bytes_in_flight_(DataSize::Zero()) {} - - Timestamp clock_; - BandwidthSampler sampler_; - DataSize bytes_in_flight_; - - void SendPacketInner(int64_t packet_number, DataSize bytes) { - sampler_.OnPacketSent(clock_, packet_number, bytes, bytes_in_flight_); - bytes_in_flight_ += bytes; - } - - void SendPacket(int64_t packet_number) { - SendPacketInner(packet_number, kRegularPacketSize); - } - - BandwidthSample AckPacketInner(int64_t packet_number) { - DataSize size = - BandwidthSamplerPeer::GetPacketSize(sampler_, packet_number); - bytes_in_flight_ -= size; - return sampler_.OnPacketAcknowledged(clock_, packet_number); - } - - // Acknowledge receipt of a packet and expect it to be not app-limited. - DataRate AckPacket(int64_t packet_number) { - BandwidthSample sample = AckPacketInner(packet_number); - EXPECT_FALSE(sample.is_app_limited); - return sample.bandwidth; - } - - void LosePacket(int64_t packet_number) { - DataSize size = - BandwidthSamplerPeer::GetPacketSize(sampler_, packet_number); - bytes_in_flight_ -= size; - sampler_.OnPacketLost(packet_number); - } - - // Sends one packet and acks it. Then, send 20 packets. Finally, send - // another 20 packets while acknowledging previous 20. - void Send40PacketsAndAckFirst20(TimeDelta time_between_packets) { - // Send 20 packets at a constant inter-packet time. - for (int64_t i = 1; i <= 20; i++) { - SendPacket(i); - clock_ += time_between_packets; - } - - // Ack packets 1 to 20, while sending new packets at the same rate as - // before. - for (int64_t i = 1; i <= 20; i++) { - AckPacket(i); - SendPacket(i + 20); - clock_ += time_between_packets; - } - } -}; - -// Test the sampler in a simple stop-and-wait sender setting. -TEST_F(BandwidthSamplerTest, SendAndWait) { - TimeDelta time_between_packets = TimeDelta::Millis(10); - DataRate expected_bandwidth = - kRegularPacketSize * 100 / TimeDelta::Seconds(1); - - // Send packets at the constant bandwidth. - for (int64_t i = 1; i < 20; i++) { - SendPacket(i); - clock_ += time_between_packets; - DataRate current_sample = AckPacket(i); - EXPECT_EQ(expected_bandwidth, current_sample); - } - - // Send packets at the exponentially decreasing bandwidth. - for (int64_t i = 20; i < 25; i++) { - time_between_packets = time_between_packets * 2; - expected_bandwidth = expected_bandwidth * 0.5; - - SendPacket(i); - clock_ += time_between_packets; - DataRate current_sample = AckPacket(i); - EXPECT_EQ(expected_bandwidth, current_sample); - } - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Test the sampler during regular windowed sender scenario with fixed -// CWND of 20. -TEST_F(BandwidthSamplerTest, SendPaced) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - DataRate expected_bandwidth = kRegularPacketSize / time_between_packets; - - Send40PacketsAndAckFirst20(time_between_packets); - - // Ack the packets 21 to 40, arriving at the correct bandwidth. - DataRate last_bandwidth = DataRate::Zero(); - for (int64_t i = 21; i <= 40; i++) { - last_bandwidth = AckPacket(i); - EXPECT_EQ(expected_bandwidth, last_bandwidth); - clock_ += time_between_packets; - } - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Test the sampler in a scenario where 50% of packets is consistently lost. -TEST_F(BandwidthSamplerTest, SendWithLosses) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - DataRate expected_bandwidth = kRegularPacketSize / time_between_packets * 0.5; - - // Send 20 packets, each 1 ms apart. - for (int64_t i = 1; i <= 20; i++) { - SendPacket(i); - clock_ += time_between_packets; - } - - // Ack packets 1 to 20, losing every even-numbered packet, while sending new - // packets at the same rate as before. - for (int64_t i = 1; i <= 20; i++) { - if (i % 2 == 0) { - AckPacket(i); - } else { - LosePacket(i); - } - SendPacket(i + 20); - clock_ += time_between_packets; - } - - // Ack the packets 21 to 40 with the same loss pattern. - DataRate last_bandwidth = DataRate::Zero(); - for (int64_t i = 21; i <= 40; i++) { - if (i % 2 == 0) { - last_bandwidth = AckPacket(i); - EXPECT_EQ(expected_bandwidth, last_bandwidth); - } else { - LosePacket(i); - } - clock_ += time_between_packets; - } - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Simulate a situation where ACKs arrive in burst and earlier than usual, thus -// producing an ACK rate which is higher than the original send rate. -TEST_F(BandwidthSamplerTest, CompressedAck) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - DataRate expected_bandwidth = kRegularPacketSize / time_between_packets; - - Send40PacketsAndAckFirst20(time_between_packets); - - // Simulate an RTT somewhat lower than the one for 1-to-21 transmission. - clock_ += time_between_packets * 15; - - // Ack the packets 21 to 40 almost immediately at once. - DataRate last_bandwidth = DataRate::Zero(); - TimeDelta ridiculously_small_time_delta = TimeDelta::Micros(20); - for (int64_t i = 21; i <= 40; i++) { - last_bandwidth = AckPacket(i); - clock_ += ridiculously_small_time_delta; - } - EXPECT_EQ(expected_bandwidth, last_bandwidth); - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Tests receiving ACK packets in the reverse order. -TEST_F(BandwidthSamplerTest, ReorderedAck) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - DataRate expected_bandwidth = kRegularPacketSize / time_between_packets; - - Send40PacketsAndAckFirst20(time_between_packets); - - // Ack the packets 21 to 40 in the reverse order, while sending packets 41 to - // 60. - DataRate last_bandwidth = DataRate::Zero(); - for (int64_t i = 0; i < 20; i++) { - last_bandwidth = AckPacket(40 - i); - EXPECT_EQ(expected_bandwidth, last_bandwidth); - SendPacket(41 + i); - clock_ += time_between_packets; - } - - // Ack the packets 41 to 60, now in the regular order. - for (int64_t i = 41; i <= 60; i++) { - last_bandwidth = AckPacket(i); - EXPECT_EQ(expected_bandwidth, last_bandwidth); - clock_ += time_between_packets; - } - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Test the app-limited logic. -TEST_F(BandwidthSamplerTest, AppLimited) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - DataRate expected_bandwidth = kRegularPacketSize / time_between_packets; - - Send40PacketsAndAckFirst20(time_between_packets); - - // We are now app-limited. Ack 21 to 40 as usual, but do not send anything for - // now. - sampler_.OnAppLimited(); - for (int64_t i = 21; i <= 40; i++) { - DataRate current_sample = AckPacket(i); - EXPECT_EQ(expected_bandwidth, current_sample); - clock_ += time_between_packets; - } - - // Enter quiescence. - clock_ += TimeDelta::Seconds(1); - - // Send packets 41 to 60, all of which would be marked as app-limited. - for (int64_t i = 41; i <= 60; i++) { - SendPacket(i); - clock_ += time_between_packets; - } - - // Ack packets 41 to 60, while sending packets 61 to 80. 41 to 60 should be - // app-limited and underestimate the bandwidth due to that. - for (int64_t i = 41; i <= 60; i++) { - BandwidthSample sample = AckPacketInner(i); - EXPECT_TRUE(sample.is_app_limited); - EXPECT_LT(sample.bandwidth, 0.7f * expected_bandwidth); - - SendPacket(i + 20); - clock_ += time_between_packets; - } - - // Run out of packets, and then ack packet 61 to 80, all of which should have - // correct non-app-limited samples. - for (int64_t i = 61; i <= 80; i++) { - DataRate last_bandwidth = AckPacket(i); - EXPECT_EQ(expected_bandwidth, last_bandwidth); - clock_ += time_between_packets; - } - - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - EXPECT_TRUE(bytes_in_flight_.IsZero()); -} - -// Test the samples taken at the first flight of packets sent. -TEST_F(BandwidthSamplerTest, FirstRoundTrip) { - const TimeDelta time_between_packets = TimeDelta::Millis(1); - const TimeDelta rtt = TimeDelta::Millis(800); - const int num_packets = 10; - const DataSize num_bytes = kRegularPacketSize * num_packets; - const DataRate real_bandwidth = num_bytes / rtt; - - for (int64_t i = 1; i <= 10; i++) { - SendPacket(i); - clock_ += time_between_packets; - } - - clock_ += rtt - num_packets * time_between_packets; - - DataRate last_sample = DataRate::Zero(); - for (int64_t i = 1; i <= 10; i++) { - DataRate sample = AckPacket(i); - EXPECT_GT(sample, last_sample); - last_sample = sample; - clock_ += time_between_packets; - } - - // The final measured sample for the first flight of sample is expected to be - // smaller than the real bandwidth, yet it should not lose more than 10%. The - // specific value of the error depends on the difference between the RTT and - // the time it takes to exhaust the congestion window (i.e. in the limit when - // all packets are sent simultaneously, last sample would indicate the real - // bandwidth). - EXPECT_LT(last_sample, real_bandwidth); - EXPECT_GT(last_sample, 0.9f * real_bandwidth); -} - -// Test sampler's ability to remove obsolete packets. -TEST_F(BandwidthSamplerTest, RemoveObsoletePackets) { - SendPacket(1); - SendPacket(2); - SendPacket(3); - SendPacket(4); - SendPacket(5); - - clock_ += TimeDelta::Millis(100); - - EXPECT_EQ(5u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - sampler_.RemoveObsoletePackets(4); - EXPECT_EQ(2u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - sampler_.OnPacketLost(4); - EXPECT_EQ(1u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); - AckPacket(5); - EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); -} - -} // namespace test -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/bbr_factory.cc b/modules/congestion_controller/bbr/bbr_factory.cc deleted file mode 100644 index c20123721a..0000000000 --- a/modules/congestion_controller/bbr/bbr_factory.cc +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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/bbr/bbr_factory.h" - -#include - -#include "modules/congestion_controller/bbr/bbr_network_controller.h" - -namespace webrtc { - -BbrNetworkControllerFactory::BbrNetworkControllerFactory() {} - -std::unique_ptr BbrNetworkControllerFactory::Create( - NetworkControllerConfig config) { - return std::make_unique(config); -} - -TimeDelta BbrNetworkControllerFactory::GetProcessInterval() const { - return TimeDelta::PlusInfinity(); -} - -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/bbr_factory.h b/modules/congestion_controller/bbr/bbr_factory.h deleted file mode 100644 index 9b371551ea..0000000000 --- a/modules/congestion_controller/bbr/bbr_factory.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_BBR_BBR_FACTORY_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_BBR_FACTORY_H_ - -#include - -#include "api/transport/network_control.h" -#include "api/units/time_delta.h" - -namespace webrtc { - -class BbrNetworkControllerFactory : public NetworkControllerFactoryInterface { - public: - BbrNetworkControllerFactory(); - std::unique_ptr Create( - NetworkControllerConfig config) override; - TimeDelta GetProcessInterval() const override; -}; -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_BBR_FACTORY_H_ diff --git a/modules/congestion_controller/bbr/bbr_network_controller.cc b/modules/congestion_controller/bbr/bbr_network_controller.cc deleted file mode 100644 index 250fddb152..0000000000 --- a/modules/congestion_controller/bbr/bbr_network_controller.cc +++ /dev/null @@ -1,955 +0,0 @@ -/* - * 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/bbr/bbr_network_controller.h" - -#include -#include -#include -#include - -#include "absl/base/macros.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" - -namespace webrtc { -namespace bbr { -namespace { - -// If greater than zero, mean RTT variation is multiplied by the specified -// factor and added to the congestion window limit. -const double kBbrRttVariationWeight = 0.0f; - -// Congestion window gain for QUIC BBR during PROBE_BW phase. -const double kProbeBWCongestionWindowGain = 2.0f; - -// The maximum packet size of any QUIC packet, based on ethernet's max size, -// minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an -// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's -// max packet size is 1500 bytes, 1500 - 48 = 1452. -const DataSize kMaxPacketSize = DataSize::Bytes(1452); - -// Default maximum packet size used in the Linux TCP implementation. -// Used in QUIC for congestion window computations in bytes. -constexpr DataSize kDefaultTCPMSS = DataSize::Bytes(1460); -// Constants based on TCP defaults. -constexpr DataSize kMaxSegmentSize = kDefaultTCPMSS; - -// The gain used for the slow start, equal to 2/ln(2). -const double kHighGain = 2.885f; -// The gain used in STARTUP after loss has been detected. -// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth -// in measured bandwidth. -const double kStartupAfterLossGain = 1.5; -// The gain used to drain the queue after the slow start. -const double kDrainGain = 1.f / kHighGain; - -// The length of the gain cycle. -const size_t kGainCycleLength = 8; -// The size of the bandwidth filter window, in round-trips. -const BbrRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2; - -// The time after which the current min_rtt value expires. -constexpr int64_t kMinRttExpirySeconds = 10; -// The minimum time the connection can spend in PROBE_RTT mode. -constexpr int64_t kProbeRttTimeMs = 200; -// If the bandwidth does not increase by the factor of |kStartupGrowthTarget| -// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection -// will exit the STARTUP mode. -const double kStartupGrowthTarget = 1.25; -// Coefficient to determine if a new RTT is sufficiently similar to min_rtt that -// we don't need to enter PROBE_RTT. -const double kSimilarMinRttThreshold = 1.125; - -constexpr int64_t kInitialBandwidthKbps = 300; - -const int64_t kInitialCongestionWindowPackets = 32; -// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. -// Does not inflate the pacing rate. -const int64_t kDefaultMinCongestionWindowPackets = 4; -const int64_t kDefaultMaxCongestionWindowPackets = 2000; - -const char kBbrConfigTrial[] = "WebRTC-BweBbrConfig"; - -} // namespace - -BbrNetworkController::BbrControllerConfig::BbrControllerConfig( - std::string field_trial) - : probe_bw_pacing_gain_offset("probe_bw_pacing_gain_offset", 0.25), - encoder_rate_gain("encoder_rate_gain", 1), - encoder_rate_gain_in_probe_rtt("encoder_rate_gain_in_probe_rtt", 1), - exit_startup_rtt_threshold("exit_startup_rtt_threshold", - TimeDelta::PlusInfinity()), - initial_congestion_window( - "initial_cwin", - kInitialCongestionWindowPackets * kDefaultTCPMSS), - min_congestion_window( - "min_cwin", - kDefaultMinCongestionWindowPackets * kDefaultTCPMSS), - max_congestion_window( - "max_cwin", - kDefaultMaxCongestionWindowPackets * kDefaultTCPMSS), - probe_rtt_congestion_window_gain("probe_rtt_cwin_gain", 0.75), - pacing_rate_as_target("pacing_rate_as_target", false), - exit_startup_on_loss("exit_startup_on_loss", true), - num_startup_rtts("num_startup_rtts", 3), - rate_based_recovery("rate_based_recovery", false), - max_aggregation_bytes_multiplier("max_aggregation_bytes_multiplier", 0), - slower_startup("slower_startup", false), - rate_based_startup("rate_based_startup", false), - initial_conservation_in_startup("initial_conservation", - CONSERVATION, - { - {"NOT_IN_RECOVERY", NOT_IN_RECOVERY}, - {"CONSERVATION", CONSERVATION}, - {"MEDIUM_GROWTH", MEDIUM_GROWTH}, - {"GROWTH", GROWTH}, - }), - fully_drain_queue("fully_drain_queue", false), - max_ack_height_window_multiplier("max_ack_height_window_multiplier", 1), - probe_rtt_based_on_bdp("probe_rtt_based_on_bdp", false), - probe_rtt_skipped_if_similar_rtt("probe_rtt_skipped_if_similar_rtt", - false), - probe_rtt_disabled_if_app_limited("probe_rtt_disabled_if_app_limited", - false) { - ParseFieldTrial( - { - &exit_startup_on_loss, - &encoder_rate_gain, - &encoder_rate_gain_in_probe_rtt, - &exit_startup_rtt_threshold, - &fully_drain_queue, - &initial_congestion_window, - &initial_conservation_in_startup, - &max_ack_height_window_multiplier, - &max_aggregation_bytes_multiplier, - &max_congestion_window, - &min_congestion_window, - &num_startup_rtts, - &pacing_rate_as_target, - &probe_bw_pacing_gain_offset, - &probe_rtt_based_on_bdp, - &probe_rtt_congestion_window_gain, - &probe_rtt_disabled_if_app_limited, - &probe_rtt_skipped_if_similar_rtt, - &rate_based_recovery, - &rate_based_startup, - &slower_startup, - }, - field_trial); -} -BbrNetworkController::BbrControllerConfig::~BbrControllerConfig() = default; -BbrNetworkController::BbrControllerConfig::BbrControllerConfig( - const BbrControllerConfig&) = default; -BbrNetworkController::BbrControllerConfig -BbrNetworkController::BbrControllerConfig::FromTrial() { - return BbrControllerConfig( - webrtc::field_trial::FindFullName(kBbrConfigTrial)); -} - -BbrNetworkController::DebugState::DebugState(const BbrNetworkController& sender) - : mode(sender.mode_), - max_bandwidth(sender.max_bandwidth_.GetBest()), - round_trip_count(sender.round_trip_count_), - gain_cycle_index(sender.cycle_current_offset_), - congestion_window(sender.congestion_window_), - is_at_full_bandwidth(sender.is_at_full_bandwidth_), - bandwidth_at_last_round(sender.bandwidth_at_last_round_), - rounds_without_bandwidth_gain(sender.rounds_without_bandwidth_gain_), - min_rtt(sender.min_rtt_), - min_rtt_timestamp(sender.min_rtt_timestamp_), - recovery_state(sender.recovery_state_), - recovery_window(sender.recovery_window_), - last_sample_is_app_limited(sender.last_sample_is_app_limited_), - end_of_app_limited_phase(sender.sampler_->end_of_app_limited_phase()) {} - -BbrNetworkController::DebugState::DebugState(const DebugState& state) = default; - -BbrNetworkController::BbrNetworkController(NetworkControllerConfig config) - : config_(BbrControllerConfig::FromTrial()), - rtt_stats_(), - random_(10), - loss_rate_(), - mode_(STARTUP), - sampler_(new BandwidthSampler()), - round_trip_count_(0), - last_sent_packet_(0), - current_round_trip_end_(0), - max_bandwidth_(kBandwidthWindowSize, DataRate::Zero(), 0), - default_bandwidth_(DataRate::KilobitsPerSec(kInitialBandwidthKbps)), - max_ack_height_(kBandwidthWindowSize, DataSize::Zero(), 0), - aggregation_epoch_start_time_(), - aggregation_epoch_bytes_(DataSize::Zero()), - bytes_acked_since_queue_drained_(DataSize::Zero()), - max_aggregation_bytes_multiplier_(0), - min_rtt_(TimeDelta::Zero()), - last_rtt_(TimeDelta::Zero()), - min_rtt_timestamp_(Timestamp::MinusInfinity()), - congestion_window_(config_.initial_congestion_window), - initial_congestion_window_(config_.initial_congestion_window), - min_congestion_window_(config_.min_congestion_window), - max_congestion_window_(config_.max_congestion_window), - pacing_rate_(DataRate::Zero()), - pacing_gain_(1), - congestion_window_gain_constant_(kProbeBWCongestionWindowGain), - rtt_variance_weight_(kBbrRttVariationWeight), - cycle_current_offset_(0), - last_cycle_start_(Timestamp::MinusInfinity()), - is_at_full_bandwidth_(false), - rounds_without_bandwidth_gain_(0), - bandwidth_at_last_round_(DataRate::Zero()), - exiting_quiescence_(false), - exit_probe_rtt_at_(), - probe_rtt_round_passed_(false), - last_sample_is_app_limited_(false), - recovery_state_(NOT_IN_RECOVERY), - end_recovery_at_(), - recovery_window_(max_congestion_window_), - app_limited_since_last_probe_rtt_(false), - min_rtt_since_last_probe_rtt_(TimeDelta::PlusInfinity()) { - RTC_LOG(LS_INFO) << "Creating BBR controller"; - if (config.constraints.starting_rate) - default_bandwidth_ = *config.constraints.starting_rate; - constraints_ = config.constraints; - Reset(); -} - -BbrNetworkController::~BbrNetworkController() {} - -void BbrNetworkController::Reset() { - round_trip_count_ = 0; - rounds_without_bandwidth_gain_ = 0; - if (config_.num_startup_rtts > 0) { - is_at_full_bandwidth_ = false; - EnterStartupMode(); - } else { - is_at_full_bandwidth_ = true; - EnterProbeBandwidthMode(constraints_->at_time); - } -} - -NetworkControlUpdate BbrNetworkController::CreateRateUpdate( - Timestamp at_time) const { - DataRate bandwidth = BandwidthEstimate(); - if (bandwidth.IsZero()) - bandwidth = default_bandwidth_; - TimeDelta rtt = GetMinRtt(); - DataRate pacing_rate = PacingRate(); - DataRate target_rate = - config_.pacing_rate_as_target ? pacing_rate : bandwidth; - - if (mode_ == PROBE_RTT) - target_rate = target_rate * config_.encoder_rate_gain_in_probe_rtt; - else - target_rate = target_rate * config_.encoder_rate_gain; - target_rate = std::min(target_rate, pacing_rate); - - if (constraints_) { - if (constraints_->max_data_rate) { - target_rate = std::min(target_rate, *constraints_->max_data_rate); - pacing_rate = std::min(pacing_rate, *constraints_->max_data_rate); - } - if (constraints_->min_data_rate) { - target_rate = std::max(target_rate, *constraints_->min_data_rate); - pacing_rate = std::max(pacing_rate, *constraints_->min_data_rate); - } - } - - NetworkControlUpdate update; - - TargetTransferRate target_rate_msg; - target_rate_msg.network_estimate.at_time = at_time; - target_rate_msg.network_estimate.round_trip_time = rtt; - - // TODO(srte): Fill in field below with proper value. - target_rate_msg.network_estimate.loss_rate_ratio = 0; - // In in PROBE_BW, target bandwidth is expected to vary over the cycle period. - // In other modes the is no given period, therefore the same value as in - // PROBE_BW is used for consistency. - target_rate_msg.network_estimate.bwe_period = - rtt * static_cast(kGainCycleLength); - - target_rate_msg.target_rate = target_rate; - target_rate_msg.at_time = at_time; - update.target_rate = target_rate_msg; - - PacerConfig pacer_config; - // A small time window ensures an even pacing rate. - pacer_config.time_window = rtt * 0.25; - pacer_config.data_window = pacer_config.time_window * pacing_rate; - - if (IsProbingForMoreBandwidth()) - pacer_config.pad_window = pacer_config.data_window; - else - pacer_config.pad_window = DataSize::Zero(); - - pacer_config.at_time = at_time; - update.pacer_config = pacer_config; - - update.congestion_window = GetCongestionWindow(); - return update; -} - -NetworkControlUpdate BbrNetworkController::OnNetworkAvailability( - NetworkAvailability msg) { - Reset(); - rtt_stats_.OnConnectionMigration(); - return CreateRateUpdate(msg.at_time); -} - -NetworkControlUpdate BbrNetworkController::OnNetworkRouteChange( - NetworkRouteChange msg) { - constraints_ = msg.constraints; - Reset(); - if (msg.constraints.starting_rate) - default_bandwidth_ = *msg.constraints.starting_rate; - - rtt_stats_.OnConnectionMigration(); - return CreateRateUpdate(msg.at_time); -} - -NetworkControlUpdate BbrNetworkController::OnProcessInterval( - ProcessInterval msg) { - return CreateRateUpdate(msg.at_time); -} - -NetworkControlUpdate BbrNetworkController::OnStreamsConfig(StreamsConfig msg) { - return NetworkControlUpdate(); -} - -NetworkControlUpdate BbrNetworkController::OnTargetRateConstraints( - TargetRateConstraints msg) { - constraints_ = msg; - return CreateRateUpdate(msg.at_time); -} - -bool BbrNetworkController::InSlowStart() const { - return mode_ == STARTUP; -} - -NetworkControlUpdate BbrNetworkController::OnSentPacket(SentPacket msg) { - last_sent_packet_ = msg.sequence_number; - - if (msg.data_in_flight.IsZero() && sampler_->is_app_limited()) { - exiting_quiescence_ = true; - } - - if (!aggregation_epoch_start_time_) { - aggregation_epoch_start_time_ = msg.send_time; - } - - sampler_->OnPacketSent(msg.send_time, msg.sequence_number, msg.size, - msg.data_in_flight); - return NetworkControlUpdate(); -} - -bool BbrNetworkController::CanSend(DataSize bytes_in_flight) { - return bytes_in_flight < GetCongestionWindow(); -} - -DataRate BbrNetworkController::PacingRate() const { - if (pacing_rate_.IsZero()) { - return kHighGain * initial_congestion_window_ / GetMinRtt(); - } - return pacing_rate_; -} - -DataRate BbrNetworkController::BandwidthEstimate() const { - return max_bandwidth_.GetBest(); -} - -DataSize BbrNetworkController::GetCongestionWindow() const { - if (mode_ == PROBE_RTT) { - return ProbeRttCongestionWindow(); - } - - if (InRecovery() && !config_.rate_based_recovery && - !(config_.rate_based_startup && mode_ == STARTUP)) { - return std::min(congestion_window_, recovery_window_); - } - - return congestion_window_; -} - -double BbrNetworkController::GetPacingGain(int round_offset) const { - if (round_offset == 0) - return 1 + config_.probe_bw_pacing_gain_offset; - else if (round_offset == 1) - return 1 - config_.probe_bw_pacing_gain_offset; - else - return 1; -} - -bool BbrNetworkController::InRecovery() const { - return recovery_state_ != NOT_IN_RECOVERY; -} - -bool BbrNetworkController::IsProbingForMoreBandwidth() const { - return (mode_ == PROBE_BW && pacing_gain_ > 1) || mode_ == STARTUP; -} - -NetworkControlUpdate BbrNetworkController::OnTransportPacketsFeedback( - TransportPacketsFeedback msg) { - if (msg.packet_feedbacks.empty()) - return NetworkControlUpdate(); - - Timestamp feedback_recv_time = msg.feedback_time; - SentPacket last_sent_packet = msg.PacketsWithFeedback().back().sent_packet; - - Timestamp send_time = last_sent_packet.send_time; - TimeDelta send_delta = feedback_recv_time - send_time; - rtt_stats_.UpdateRtt(send_delta, TimeDelta::Zero(), feedback_recv_time); - - const DataSize total_data_acked_before = sampler_->total_data_acked(); - - bool is_round_start = false; - bool min_rtt_expired = false; - - std::vector lost_packets = msg.LostWithSendInfo(); - DiscardLostPackets(lost_packets); - - std::vector acked_packets = msg.ReceivedWithSendInfo(); - - int packets_sent = - static_cast(lost_packets.size() + acked_packets.size()); - int packets_lost = static_cast(lost_packets.size()); - loss_rate_.UpdateWithLossStatus(msg.feedback_time.ms(), packets_sent, - packets_lost); - - // Input the new data into the BBR model of the connection. - if (!acked_packets.empty()) { - int64_t last_acked_packet = - acked_packets.rbegin()->sent_packet.sequence_number; - - is_round_start = UpdateRoundTripCounter(last_acked_packet); - min_rtt_expired = - UpdateBandwidthAndMinRtt(msg.feedback_time, acked_packets); - UpdateRecoveryState(last_acked_packet, !lost_packets.empty(), - is_round_start); - - const DataSize data_acked = - sampler_->total_data_acked() - total_data_acked_before; - - UpdateAckAggregationBytes(msg.feedback_time, data_acked); - if (max_aggregation_bytes_multiplier_ > 0) { - if (msg.data_in_flight <= - 1.25 * GetTargetCongestionWindow(pacing_gain_)) { - bytes_acked_since_queue_drained_ = DataSize::Zero(); - } else { - bytes_acked_since_queue_drained_ += data_acked; - } - } - } - - // Handle logic specific to PROBE_BW mode. - if (mode_ == PROBE_BW) { - UpdateGainCyclePhase(msg.feedback_time, msg.prior_in_flight, - !lost_packets.empty()); - } - - // Handle logic specific to STARTUP and DRAIN modes. - if (is_round_start && !is_at_full_bandwidth_) { - CheckIfFullBandwidthReached(); - } - MaybeExitStartupOrDrain(msg); - - // Handle logic specific to PROBE_RTT. - MaybeEnterOrExitProbeRtt(msg, is_round_start, min_rtt_expired); - - // Calculate number of packets acked and lost. - DataSize data_acked = sampler_->total_data_acked() - total_data_acked_before; - DataSize data_lost = DataSize::Zero(); - for (const PacketResult& packet : lost_packets) { - data_lost += packet.sent_packet.size; - } - - // After the model is updated, recalculate the pacing rate and congestion - // window. - CalculatePacingRate(); - CalculateCongestionWindow(data_acked); - CalculateRecoveryWindow(data_acked, data_lost, msg.data_in_flight); - // Cleanup internal state. - if (!acked_packets.empty()) { - sampler_->RemoveObsoletePackets( - acked_packets.back().sent_packet.sequence_number); - } - return CreateRateUpdate(msg.feedback_time); -} - -NetworkControlUpdate BbrNetworkController::OnRemoteBitrateReport( - RemoteBitrateReport msg) { - return NetworkControlUpdate(); -} -NetworkControlUpdate BbrNetworkController::OnRoundTripTimeUpdate( - RoundTripTimeUpdate msg) { - return NetworkControlUpdate(); -} -NetworkControlUpdate BbrNetworkController::OnTransportLossReport( - TransportLossReport msg) { - return NetworkControlUpdate(); -} - -NetworkControlUpdate BbrNetworkController::OnReceivedPacket( - ReceivedPacket msg) { - return NetworkControlUpdate(); -} - -NetworkControlUpdate BbrNetworkController::OnNetworkStateEstimate( - NetworkStateEstimate msg) { - return NetworkControlUpdate(); -} - -TimeDelta BbrNetworkController::GetMinRtt() const { - return !min_rtt_.IsZero() ? min_rtt_ - : TimeDelta::Micros(rtt_stats_.initial_rtt_us()); -} - -DataSize BbrNetworkController::GetTargetCongestionWindow(double gain) const { - DataSize bdp = GetMinRtt() * BandwidthEstimate(); - DataSize congestion_window = gain * bdp; - - // BDP estimate will be zero if no bandwidth samples are available yet. - if (congestion_window.IsZero()) { - congestion_window = gain * initial_congestion_window_; - } - - return std::max(congestion_window, min_congestion_window_); -} - -DataSize BbrNetworkController::ProbeRttCongestionWindow() const { - if (config_.probe_rtt_based_on_bdp) { - return GetTargetCongestionWindow(config_.probe_rtt_congestion_window_gain); - } - return min_congestion_window_; -} - -void BbrNetworkController::EnterStartupMode() { - mode_ = STARTUP; - pacing_gain_ = kHighGain; - congestion_window_gain_ = kHighGain; -} - -void BbrNetworkController::EnterProbeBandwidthMode(Timestamp now) { - mode_ = PROBE_BW; - congestion_window_gain_ = congestion_window_gain_constant_; - - // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is - // excluded because in that case increased gain and decreased gain would not - // follow each other. - cycle_current_offset_ = random_.Rand(kGainCycleLength - 2); - if (cycle_current_offset_ >= 1) { - cycle_current_offset_ += 1; - } - - last_cycle_start_ = now; - pacing_gain_ = GetPacingGain(cycle_current_offset_); -} - -void BbrNetworkController::DiscardLostPackets( - const std::vector& lost_packets) { - for (const PacketResult& packet : lost_packets) { - sampler_->OnPacketLost(packet.sent_packet.sequence_number); - } -} - -bool BbrNetworkController::UpdateRoundTripCounter(int64_t last_acked_packet) { - if (last_acked_packet > current_round_trip_end_) { - round_trip_count_++; - current_round_trip_end_ = last_sent_packet_; - return true; - } - - return false; -} - -bool BbrNetworkController::UpdateBandwidthAndMinRtt( - Timestamp now, - const std::vector& acked_packets) { - TimeDelta sample_rtt = TimeDelta::PlusInfinity(); - for (const auto& packet : acked_packets) { - BandwidthSample bandwidth_sample = - sampler_->OnPacketAcknowledged(now, packet.sent_packet.sequence_number); - last_sample_is_app_limited_ = bandwidth_sample.is_app_limited; - if (!bandwidth_sample.rtt.IsZero()) { - sample_rtt = std::min(sample_rtt, bandwidth_sample.rtt); - } - - if (!bandwidth_sample.is_app_limited || - bandwidth_sample.bandwidth > BandwidthEstimate()) { - max_bandwidth_.Update(bandwidth_sample.bandwidth, round_trip_count_); - } - } - - // If none of the RTT samples are valid, return immediately. - if (sample_rtt.IsInfinite()) { - return false; - } - - last_rtt_ = sample_rtt; - min_rtt_since_last_probe_rtt_ = - std::min(min_rtt_since_last_probe_rtt_, sample_rtt); - - const TimeDelta kMinRttExpiry = TimeDelta::Seconds(kMinRttExpirySeconds); - // Do not expire min_rtt if none was ever available. - bool min_rtt_expired = - !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry)); - - if (min_rtt_expired || sample_rtt < min_rtt_ || min_rtt_.IsZero()) { - if (ShouldExtendMinRttExpiry()) { - min_rtt_expired = false; - } else { - min_rtt_ = sample_rtt; - } - min_rtt_timestamp_ = now; - // Reset since_last_probe_rtt fields. - min_rtt_since_last_probe_rtt_ = TimeDelta::PlusInfinity(); - app_limited_since_last_probe_rtt_ = false; - } - - return min_rtt_expired; -} - -bool BbrNetworkController::ShouldExtendMinRttExpiry() const { - if (config_.probe_rtt_disabled_if_app_limited && - app_limited_since_last_probe_rtt_) { - // Extend the current min_rtt if we've been app limited recently. - return true; - } - const bool min_rtt_increased_since_last_probe = - min_rtt_since_last_probe_rtt_ > min_rtt_ * kSimilarMinRttThreshold; - if (config_.probe_rtt_skipped_if_similar_rtt && - app_limited_since_last_probe_rtt_ && - !min_rtt_increased_since_last_probe) { - // Extend the current min_rtt if we've been app limited recently and an rtt - // has been measured in that time that's less than 12.5% more than the - // current min_rtt. - return true; - } - return false; -} - -void BbrNetworkController::UpdateGainCyclePhase(Timestamp now, - DataSize prior_in_flight, - bool has_losses) { - // In most cases, the cycle is advanced after an RTT passes. - bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt(); - - // If the pacing gain is above 1.0, the connection is trying to probe the - // bandwidth by increasing the number of bytes in flight to at least - // pacing_gain * BDP. Make sure that it actually reaches the target, as long - // as there are no losses suggesting that the buffers are not able to hold - // that much. - if (pacing_gain_ > 1.0 && !has_losses && - prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) { - should_advance_gain_cycling = false; - } - - // If pacing gain is below 1.0, the connection is trying to drain the extra - // queue which could have been incurred by probing prior to it. If the number - // of bytes in flight falls down to the estimated BDP value earlier, conclude - // that the queue has been successfully drained and exit this cycle early. - if (pacing_gain_ < 1.0 && prior_in_flight <= GetTargetCongestionWindow(1)) { - should_advance_gain_cycling = true; - } - - if (should_advance_gain_cycling) { - cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength; - last_cycle_start_ = now; - // Stay in low gain mode until the target BDP is hit. - // Low gain mode will be exited immediately when the target BDP is achieved. - if (config_.fully_drain_queue && pacing_gain_ < 1 && - GetPacingGain(cycle_current_offset_) == 1 && - prior_in_flight > GetTargetCongestionWindow(1)) { - return; - } - pacing_gain_ = GetPacingGain(cycle_current_offset_); - } -} - -void BbrNetworkController::CheckIfFullBandwidthReached() { - if (last_sample_is_app_limited_) { - return; - } - - DataRate target = bandwidth_at_last_round_ * kStartupGrowthTarget; - if (BandwidthEstimate() >= target) { - bandwidth_at_last_round_ = BandwidthEstimate(); - rounds_without_bandwidth_gain_ = 0; - return; - } - - rounds_without_bandwidth_gain_++; - if ((rounds_without_bandwidth_gain_ >= config_.num_startup_rtts) || - (config_.exit_startup_on_loss && InRecovery())) { - is_at_full_bandwidth_ = true; - } -} - -void BbrNetworkController::MaybeExitStartupOrDrain( - const TransportPacketsFeedback& msg) { - TimeDelta exit_threshold = config_.exit_startup_rtt_threshold; - TimeDelta rtt_delta = last_rtt_ - min_rtt_; - if (mode_ == STARTUP && - (is_at_full_bandwidth_ || rtt_delta > exit_threshold)) { - if (rtt_delta > exit_threshold) - RTC_LOG(LS_INFO) << "Exiting startup due to rtt increase from: " - << ToString(min_rtt_) << " to:" << ToString(last_rtt_) - << " > " << ToString(min_rtt_ + exit_threshold); - mode_ = DRAIN; - pacing_gain_ = kDrainGain; - congestion_window_gain_ = kHighGain; - } - if (mode_ == DRAIN && msg.data_in_flight <= GetTargetCongestionWindow(1)) { - EnterProbeBandwidthMode(msg.feedback_time); - } -} - -void BbrNetworkController::MaybeEnterOrExitProbeRtt( - const TransportPacketsFeedback& msg, - bool is_round_start, - bool min_rtt_expired) { - if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) { - mode_ = PROBE_RTT; - pacing_gain_ = 1; - // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| - // is at the target small value. - exit_probe_rtt_at_.reset(); - } - - if (mode_ == PROBE_RTT) { - sampler_->OnAppLimited(); - - if (!exit_probe_rtt_at_) { - // If the window has reached the appropriate size, schedule exiting - // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but - // we allow an extra packet since QUIC checks CWND before sending a - // packet. - if (msg.data_in_flight < ProbeRttCongestionWindow() + kMaxPacketSize) { - exit_probe_rtt_at_ = - msg.feedback_time + TimeDelta::Millis(kProbeRttTimeMs); - probe_rtt_round_passed_ = false; - } - } else { - if (is_round_start) { - probe_rtt_round_passed_ = true; - } - if (msg.feedback_time >= *exit_probe_rtt_at_ && probe_rtt_round_passed_) { - min_rtt_timestamp_ = msg.feedback_time; - if (!is_at_full_bandwidth_) { - EnterStartupMode(); - } else { - EnterProbeBandwidthMode(msg.feedback_time); - } - } - } - } - - exiting_quiescence_ = false; -} - -void BbrNetworkController::UpdateRecoveryState(int64_t last_acked_packet, - bool has_losses, - bool is_round_start) { - // Exit recovery when there are no losses for a round. - if (has_losses) { - end_recovery_at_ = last_sent_packet_; - } - - switch (recovery_state_) { - case NOT_IN_RECOVERY: - // Enter conservation on the first loss. - if (has_losses) { - recovery_state_ = CONSERVATION; - if (mode_ == STARTUP) { - recovery_state_ = config_.initial_conservation_in_startup; - } - // This will cause the |recovery_window_| to be set to the correct - // value in CalculateRecoveryWindow(). - recovery_window_ = DataSize::Zero(); - // Since the conservation phase is meant to be lasting for a whole - // round, extend the current round as if it were started right now. - current_round_trip_end_ = last_sent_packet_; - } - break; - - case CONSERVATION: - case MEDIUM_GROWTH: - if (is_round_start) { - recovery_state_ = GROWTH; - } - ABSL_FALLTHROUGH_INTENDED; - case GROWTH: - // Exit recovery if appropriate. - if (!has_losses && - (!end_recovery_at_ || last_acked_packet > *end_recovery_at_)) { - recovery_state_ = NOT_IN_RECOVERY; - } - - break; - } -} - -void BbrNetworkController::UpdateAckAggregationBytes( - Timestamp ack_time, - DataSize newly_acked_bytes) { - if (!aggregation_epoch_start_time_) { - RTC_LOG(LS_ERROR) - << "Received feedback before information about sent packets."; - RTC_DCHECK(aggregation_epoch_start_time_.has_value()); - return; - } - // Compute how many bytes are expected to be delivered, assuming max bandwidth - // is correct. - DataSize expected_bytes_acked = - max_bandwidth_.GetBest() * (ack_time - *aggregation_epoch_start_time_); - // Reset the current aggregation epoch as soon as the ack arrival rate is less - // than or equal to the max bandwidth. - if (aggregation_epoch_bytes_ <= expected_bytes_acked) { - // Reset to start measuring a new aggregation epoch. - aggregation_epoch_bytes_ = newly_acked_bytes; - aggregation_epoch_start_time_ = ack_time; - return; - } - - // Compute how many extra bytes were delivered vs max bandwidth. - // Include the bytes most recently acknowledged to account for stretch acks. - aggregation_epoch_bytes_ += newly_acked_bytes; - max_ack_height_.Update(aggregation_epoch_bytes_ - expected_bytes_acked, - round_trip_count_); -} - -void BbrNetworkController::CalculatePacingRate() { - if (BandwidthEstimate().IsZero()) { - return; - } - - DataRate target_rate = pacing_gain_ * BandwidthEstimate(); - if (config_.rate_based_recovery && InRecovery()) { - pacing_rate_ = pacing_gain_ * max_bandwidth_.GetThirdBest(); - } - if (is_at_full_bandwidth_) { - pacing_rate_ = target_rate; - return; - } - - // Pace at the rate of initial_window / RTT as soon as RTT measurements are - // available. - if (pacing_rate_.IsZero() && !rtt_stats_.min_rtt().IsZero()) { - pacing_rate_ = initial_congestion_window_ / rtt_stats_.min_rtt(); - return; - } - // Slow the pacing rate in STARTUP once loss has ever been detected. - const bool has_ever_detected_loss = end_recovery_at_.has_value(); - if (config_.slower_startup && has_ever_detected_loss) { - pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate(); - return; - } - - // Do not decrease the pacing rate during the startup. - pacing_rate_ = std::max(pacing_rate_, target_rate); -} - -void BbrNetworkController::CalculateCongestionWindow(DataSize bytes_acked) { - if (mode_ == PROBE_RTT) { - return; - } - - DataSize target_window = GetTargetCongestionWindow(congestion_window_gain_); - - if (rtt_variance_weight_ > 0.f && !BandwidthEstimate().IsZero()) { - target_window += rtt_variance_weight_ * rtt_stats_.mean_deviation() * - BandwidthEstimate(); - } else if (max_aggregation_bytes_multiplier_ > 0 && is_at_full_bandwidth_) { - // Subtracting only half the bytes_acked_since_queue_drained ensures sending - // doesn't completely stop for a long period of time if the queue hasn't - // been drained recently. - if (max_aggregation_bytes_multiplier_ * max_ack_height_.GetBest() > - bytes_acked_since_queue_drained_ / 2) { - target_window += - max_aggregation_bytes_multiplier_ * max_ack_height_.GetBest() - - bytes_acked_since_queue_drained_ / 2; - } - } else if (is_at_full_bandwidth_) { - target_window += max_ack_height_.GetBest(); - } - - // Instead of immediately setting the target CWND as the new one, BBR grows - // the CWND towards |target_window| by only increasing it |bytes_acked| at a - // time. - if (is_at_full_bandwidth_) { - congestion_window_ = - std::min(target_window, congestion_window_ + bytes_acked); - } else if (congestion_window_ < target_window || - sampler_->total_data_acked() < initial_congestion_window_) { - // If the connection is not yet out of startup phase, do not decrease the - // window. - congestion_window_ = congestion_window_ + bytes_acked; - } - - // Enforce the limits on the congestion window. - congestion_window_ = std::max(congestion_window_, min_congestion_window_); - congestion_window_ = std::min(congestion_window_, max_congestion_window_); -} - -void BbrNetworkController::CalculateRecoveryWindow(DataSize bytes_acked, - DataSize bytes_lost, - DataSize bytes_in_flight) { - if (config_.rate_based_recovery || - (config_.rate_based_startup && mode_ == STARTUP)) { - return; - } - - if (recovery_state_ == NOT_IN_RECOVERY) { - return; - } - - // Set up the initial recovery window. - if (recovery_window_.IsZero()) { - recovery_window_ = bytes_in_flight + bytes_acked; - recovery_window_ = std::max(min_congestion_window_, recovery_window_); - return; - } - - // Remove losses from the recovery window, while accounting for a potential - // integer underflow. - recovery_window_ = recovery_window_ >= bytes_lost - ? recovery_window_ - bytes_lost - : kMaxSegmentSize; - - // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, - // release additional |bytes_acked| to achieve a slow-start-like behavior. - // In MEDIUM_GROWTH, release |bytes_acked| / 2 to split the difference. - if (recovery_state_ == GROWTH) { - recovery_window_ += bytes_acked; - } else if (recovery_state_ == MEDIUM_GROWTH) { - recovery_window_ += bytes_acked / 2; - } - - // Sanity checks. Ensure that we always allow to send at least - // |bytes_acked| in response. - recovery_window_ = std::max(recovery_window_, bytes_in_flight + bytes_acked); - recovery_window_ = std::max(min_congestion_window_, recovery_window_); -} - -void BbrNetworkController::OnApplicationLimited(DataSize bytes_in_flight) { - if (bytes_in_flight >= GetCongestionWindow()) { - return; - } - - app_limited_since_last_probe_rtt_ = true; - sampler_->OnAppLimited(); - - RTC_LOG(LS_INFO) << "Becoming application limited. Last sent packet: " - << last_sent_packet_ - << ", CWND: " << ToString(GetCongestionWindow()); -} -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/bbr_network_controller.h b/modules/congestion_controller/bbr/bbr_network_controller.h deleted file mode 100644 index 6114970405..0000000000 --- a/modules/congestion_controller/bbr/bbr_network_controller.h +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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. - */ - -// BBR (Bottleneck Bandwidth and RTT) congestion control algorithm. -// Based on the Quic BBR implementation in Chromium. - -#ifndef MODULES_CONGESTION_CONTROLLER_BBR_BBR_NETWORK_CONTROLLER_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_BBR_NETWORK_CONTROLLER_H_ - -#include -#include -#include -#include - -#include "absl/types/optional.h" -#include "api/transport/network_control.h" -#include "api/transport/network_types.h" -#include "modules/congestion_controller/bbr/bandwidth_sampler.h" -#include "modules/congestion_controller/bbr/loss_rate_filter.h" -#include "modules/congestion_controller/bbr/rtt_stats.h" -#include "modules/congestion_controller/bbr/windowed_filter.h" -#include "rtc_base/experiments/field_trial_parser.h" -#include "rtc_base/experiments/field_trial_units.h" -#include "rtc_base/random.h" - -namespace webrtc { -namespace bbr { - -typedef int64_t BbrRoundTripCount; - -// BbrSender implements BBR congestion control algorithm. BBR aims to estimate -// the current available Bottleneck Bandwidth and RTT (hence the name), and -// regulates the pacing rate and the size of the congestion window based on -// those signals. -// -// BBR relies on pacing in order to function properly. Do not use BBR when -// pacing is disabled. -class BbrNetworkController : public NetworkControllerInterface { - public: - enum Mode { - // Startup phase of the connection. - STARTUP, - // After achieving the highest possible bandwidth during the startup, lower - // the pacing rate in order to drain the queue. - DRAIN, - // Cruising mode. - PROBE_BW, - // Temporarily slow down sending in order to empty the buffer and measure - // the real minimum RTT. - PROBE_RTT, - }; - - // Indicates how the congestion control limits the amount of bytes in flight. - enum RecoveryState { - // Do not limit. - NOT_IN_RECOVERY = 0, - // Allow an extra outstanding byte for each byte acknowledged. - CONSERVATION = 1, - // Allow 1.5 extra outstanding bytes for each byte acknowledged. - MEDIUM_GROWTH = 2, - // Allow two extra outstanding bytes for each byte acknowledged (slow - // start). - GROWTH = 3 - }; - struct BbrControllerConfig { - FieldTrialParameter probe_bw_pacing_gain_offset; - FieldTrialParameter encoder_rate_gain; - FieldTrialParameter encoder_rate_gain_in_probe_rtt; - // RTT delta to determine if startup should be exited due to increased RTT. - FieldTrialParameter exit_startup_rtt_threshold; - - FieldTrialParameter initial_congestion_window; - FieldTrialParameter min_congestion_window; - FieldTrialParameter max_congestion_window; - - FieldTrialParameter probe_rtt_congestion_window_gain; - FieldTrialParameter pacing_rate_as_target; - - // Configurable in QUIC BBR: - FieldTrialParameter exit_startup_on_loss; - // The number of RTTs to stay in STARTUP mode. Defaults to 3. - FieldTrialParameter num_startup_rtts; - // When true, recovery is rate based rather than congestion window based. - FieldTrialParameter rate_based_recovery; - FieldTrialParameter max_aggregation_bytes_multiplier; - // When true, pace at 1.5x and disable packet conservation in STARTUP. - FieldTrialParameter slower_startup; - // When true, disables packet conservation in STARTUP. - FieldTrialParameter rate_based_startup; - // Used as the initial packet conservation mode when first entering - // recovery. - FieldTrialEnum initial_conservation_in_startup; - // If true, will not exit low gain mode until bytes_in_flight drops below - // BDP or it's time for high gain mode. - FieldTrialParameter fully_drain_queue; - - FieldTrialParameter max_ack_height_window_multiplier; - // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets. - FieldTrialParameter probe_rtt_based_on_bdp; - // If true, skip probe_rtt and update the timestamp of the existing min_rtt - // to now if min_rtt over the last cycle is within 12.5% of the current - // min_rtt. Even if the min_rtt is 12.5% too low, the 25% gain cycling and - // 2x CWND gain should overcome an overly small min_rtt. - FieldTrialParameter probe_rtt_skipped_if_similar_rtt; - // If true, disable PROBE_RTT entirely as long as the connection was - // recently app limited. - FieldTrialParameter probe_rtt_disabled_if_app_limited; - - explicit BbrControllerConfig(std::string field_trial); - ~BbrControllerConfig(); - BbrControllerConfig(const BbrControllerConfig&); - static BbrControllerConfig FromTrial(); - }; - - // Debug state can be exported in order to troubleshoot potential congestion - // control issues. - struct DebugState { - explicit DebugState(const BbrNetworkController& sender); - DebugState(const DebugState& state); - - Mode mode; - DataRate max_bandwidth; - BbrRoundTripCount round_trip_count; - int gain_cycle_index; - DataSize congestion_window; - - bool is_at_full_bandwidth; - DataRate bandwidth_at_last_round; - BbrRoundTripCount rounds_without_bandwidth_gain; - - TimeDelta min_rtt; - Timestamp min_rtt_timestamp; - - RecoveryState recovery_state; - DataSize recovery_window; - - bool last_sample_is_app_limited; - int64_t end_of_app_limited_phase; - }; - - explicit BbrNetworkController(NetworkControllerConfig config); - ~BbrNetworkController() override; - - // NetworkControllerInterface - NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override; - NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override; - NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override; - NetworkControlUpdate OnSentPacket(SentPacket msg) override; - NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override; - NetworkControlUpdate OnTargetRateConstraints( - TargetRateConstraints msg) override; - NetworkControlUpdate OnTransportPacketsFeedback( - TransportPacketsFeedback msg) override; - - // Part of remote bitrate estimation api, not implemented for BBR - NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override; - NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override; - NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override; - NetworkControlUpdate OnReceivedPacket(ReceivedPacket msg) override; - NetworkControlUpdate OnNetworkStateEstimate( - NetworkStateEstimate msg) override; - - NetworkControlUpdate CreateRateUpdate(Timestamp at_time) const; - - private: - void Reset(); - bool InSlowStart() const; - bool InRecovery() const; - bool IsProbingForMoreBandwidth() const; - - bool CanSend(DataSize bytes_in_flight); - DataRate PacingRate() const; - DataRate BandwidthEstimate() const; - DataSize GetCongestionWindow() const; - - double GetPacingGain(int round_offset) const; - - void OnApplicationLimited(DataSize bytes_in_flight); - // End implementation of SendAlgorithmInterface. - - typedef WindowedFilter, - BbrRoundTripCount, - BbrRoundTripCount> - MaxBandwidthFilter; - - typedef WindowedFilter, - BbrRoundTripCount, - BbrRoundTripCount> - MaxAckDelayFilter; - - typedef WindowedFilter, - BbrRoundTripCount, - BbrRoundTripCount> - MaxAckHeightFilter; - - // Returns the current estimate of the RTT of the connection. Outside of the - // edge cases, this is minimum RTT. - TimeDelta GetMinRtt() const; - // Returns whether the connection has achieved full bandwidth required to exit - // the slow start. - bool IsAtFullBandwidth() const; - // Computes the target congestion window using the specified gain. - DataSize GetTargetCongestionWindow(double gain) const; - // The target congestion window during PROBE_RTT. - DataSize ProbeRttCongestionWindow() const; - // Returns true if the current min_rtt should be kept and we should not enter - // PROBE_RTT immediately. - bool ShouldExtendMinRttExpiry() const; - - // Enters the STARTUP mode. - void EnterStartupMode(); - // Enters the PROBE_BW mode. - void EnterProbeBandwidthMode(Timestamp now); - - // Discards the lost packets from BandwidthSampler state. - void DiscardLostPackets(const std::vector& lost_packets); - // Updates the round-trip counter if a round-trip has passed. Returns true if - // the counter has been advanced. - // |last_acked_packet| is the sequence number of the last acked packet. - bool UpdateRoundTripCounter(int64_t last_acked_packet); - // Updates the current bandwidth and min_rtt estimate based on the samples for - // the received acknowledgements. Returns true if min_rtt has expired. - bool UpdateBandwidthAndMinRtt(Timestamp now, - const std::vector& acked_packets); - // Updates the current gain used in PROBE_BW mode. - void UpdateGainCyclePhase(Timestamp now, - DataSize prior_in_flight, - bool has_losses); - // Tracks for how many round-trips the bandwidth has not increased - // significantly. - void CheckIfFullBandwidthReached(); - // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if - // appropriate. - void MaybeExitStartupOrDrain(const TransportPacketsFeedback&); - // Decides whether to enter or exit PROBE_RTT. - void MaybeEnterOrExitProbeRtt(const TransportPacketsFeedback& msg, - bool is_round_start, - bool min_rtt_expired); - // Determines whether BBR needs to enter, exit or advance state of the - // recovery. - void UpdateRecoveryState(int64_t last_acked_packet, - bool has_losses, - bool is_round_start); - - // Updates the ack aggregation max filter in bytes. - void UpdateAckAggregationBytes(Timestamp ack_time, - DataSize newly_acked_bytes); - - // Determines the appropriate pacing rate for the connection. - void CalculatePacingRate(); - // Determines the appropriate congestion window for the connection. - void CalculateCongestionWindow(DataSize bytes_acked); - // Determines the approriate window that constrains the - // in-flight during recovery. - void CalculateRecoveryWindow(DataSize bytes_acked, - DataSize bytes_lost, - DataSize bytes_in_flight); - - BbrControllerConfig config_; - - RttStats rtt_stats_; - webrtc::Random random_; - LossRateFilter loss_rate_; - - absl::optional constraints_; - - Mode mode_; - - // Bandwidth sampler provides BBR with the bandwidth measurements at - // individual points. - std::unique_ptr sampler_; - - // The number of the round trips that have occurred during the connection. - BbrRoundTripCount round_trip_count_ = 0; - - // The packet number of the most recently sent packet. - int64_t last_sent_packet_; - // Acknowledgement of any packet after |current_round_trip_end_| will cause - // the round trip counter to advance. - int64_t current_round_trip_end_; - - // The filter that tracks the maximum bandwidth over the multiple recent - // round-trips. - MaxBandwidthFilter max_bandwidth_; - - DataRate default_bandwidth_; - - // Tracks the maximum number of bytes acked faster than the sending rate. - MaxAckHeightFilter max_ack_height_; - - // The time this aggregation started and the number of bytes acked during it. - absl::optional aggregation_epoch_start_time_; - DataSize aggregation_epoch_bytes_; - - // The number of bytes acknowledged since the last time bytes in flight - // dropped below the target window. - DataSize bytes_acked_since_queue_drained_; - - // The muliplier for calculating the max amount of extra CWND to add to - // compensate for ack aggregation. - double max_aggregation_bytes_multiplier_; - - // Minimum RTT estimate. Automatically expires within 10 seconds (and - // triggers PROBE_RTT mode) if no new value is sampled during that period. - TimeDelta min_rtt_; - TimeDelta last_rtt_; - // The time at which the current value of |min_rtt_| was assigned. - Timestamp min_rtt_timestamp_; - - // The maximum allowed number of bytes in flight. - DataSize congestion_window_; - - // The initial value of the |congestion_window_|. - DataSize initial_congestion_window_; - - // The smallest value the |congestion_window_| can achieve. - DataSize min_congestion_window_; - - // The largest value the |congestion_window_| can achieve. - DataSize max_congestion_window_; - - // The current pacing rate of the connection. - DataRate pacing_rate_; - - // The gain currently applied to the pacing rate. - double pacing_gain_; - // The gain currently applied to the congestion window. - double congestion_window_gain_; - - // The gain used for the congestion window during PROBE_BW. Latched from - // quic_bbr_cwnd_gain flag. - const double congestion_window_gain_constant_; - // The coefficient by which mean RTT variance is added to the congestion - // window. Latched from quic_bbr_rtt_variation_weight flag. - const double rtt_variance_weight_; - - // Number of round-trips in PROBE_BW mode, used for determining the current - // pacing gain cycle. - int cycle_current_offset_; - // The time at which the last pacing gain cycle was started. - Timestamp last_cycle_start_; - - // Indicates whether the connection has reached the full bandwidth mode. - bool is_at_full_bandwidth_; - // Number of rounds during which there was no significant bandwidth increase. - BbrRoundTripCount rounds_without_bandwidth_gain_; - // The bandwidth compared to which the increase is measured. - DataRate bandwidth_at_last_round_; - - // Set to true upon exiting quiescence. - bool exiting_quiescence_; - - // Time at which PROBE_RTT has to be exited. Setting it to zero indicates - // that the time is yet unknown as the number of packets in flight has not - // reached the required value. - absl::optional exit_probe_rtt_at_; - // Indicates whether a round-trip has passed since PROBE_RTT became active. - bool probe_rtt_round_passed_; - - // Indicates whether the most recent bandwidth sample was marked as - // app-limited. - bool last_sample_is_app_limited_; - - // Current state of recovery. - RecoveryState recovery_state_; - // Receiving acknowledgement of a packet after |end_recovery_at_| will cause - // BBR to exit the recovery mode. A set value indicates at least one - // loss has been detected, so it must not be reset. - absl::optional end_recovery_at_; - // A window used to limit the number of bytes in flight during loss recovery. - DataSize recovery_window_; - - bool app_limited_since_last_probe_rtt_; - TimeDelta min_rtt_since_last_probe_rtt_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BbrNetworkController); -}; - -// Used in log output -std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) - std::ostream& os, // no-presubmit-check TODO(webrtc:8982) - const BbrNetworkController::Mode& mode); - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_BBR_NETWORK_CONTROLLER_H_ diff --git a/modules/congestion_controller/bbr/bbr_network_controller_unittest.cc b/modules/congestion_controller/bbr/bbr_network_controller_unittest.cc deleted file mode 100644 index cbf09876eb..0000000000 --- a/modules/congestion_controller/bbr/bbr_network_controller_unittest.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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/bbr/bbr_network_controller.h" - -#include -#include - -#include "modules/congestion_controller/bbr/bbr_factory.h" -#include "test/gmock.h" -#include "test/gtest.h" -#include "test/scenario/scenario.h" - -using ::testing::_; -using ::testing::AllOf; -using ::testing::Field; -using ::testing::Ge; -using ::testing::Le; -using ::testing::Matcher; -using ::testing::NiceMock; -using ::testing::Property; -using ::testing::StrictMock; - -namespace webrtc { -namespace test { -namespace { - -const DataRate kInitialBitrate = DataRate::KilobitsPerSec(60); -const Timestamp kDefaultStartTime = Timestamp::Millis(10000000); - -constexpr double kDataRateMargin = 0.3; -constexpr double kMinDataRateFactor = 1 - kDataRateMargin; -constexpr double kMaxDataRateFactor = 1 + kDataRateMargin; -inline Matcher TargetRateCloseTo(DataRate rate) { - DataRate min_data_rate = rate * kMinDataRateFactor; - DataRate max_data_rate = rate * kMaxDataRateFactor; - return Field(&TargetTransferRate::target_rate, - AllOf(Ge(min_data_rate), Le(max_data_rate))); -} - -NetworkControllerConfig InitialConfig( - int starting_bandwidth_kbps = kInitialBitrate.kbps(), - int min_data_rate_kbps = 0, - int max_data_rate_kbps = 5 * kInitialBitrate.kbps()) { - NetworkControllerConfig config; - config.constraints.at_time = kDefaultStartTime; - 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); - return config; -} - -ProcessInterval InitialProcessInterval() { - ProcessInterval process_interval; - process_interval.at_time = kDefaultStartTime; - return process_interval; -} - -NetworkRouteChange CreateRouteChange(Timestamp at_time, - DataRate start_rate, - DataRate min_rate = DataRate::Zero(), - DataRate max_rate = DataRate::Infinity()) { - NetworkRouteChange route_change; - route_change.at_time = at_time; - route_change.constraints.at_time = at_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; -} -} // namespace - -class BbrNetworkControllerTest : public ::testing::Test { - protected: - BbrNetworkControllerTest() {} - ~BbrNetworkControllerTest() override {} -}; - -TEST_F(BbrNetworkControllerTest, SendsConfigurationOnFirstProcess) { - std::unique_ptr controller_; - controller_.reset(new bbr::BbrNetworkController(InitialConfig())); - - NetworkControlUpdate update = - controller_->OnProcessInterval(InitialProcessInterval()); - EXPECT_THAT(*update.target_rate, TargetRateCloseTo(kInitialBitrate)); - EXPECT_THAT(*update.pacer_config, - Property(&PacerConfig::data_rate, Ge(kInitialBitrate))); - EXPECT_THAT(*update.congestion_window, Property(&DataSize::IsFinite, true)); -} - -TEST_F(BbrNetworkControllerTest, SendsConfigurationOnNetworkRouteChanged) { - std::unique_ptr controller_; - controller_.reset(new bbr::BbrNetworkController(InitialConfig())); - - NetworkControlUpdate update = - controller_->OnProcessInterval(InitialProcessInterval()); - EXPECT_TRUE(update.target_rate.has_value()); - EXPECT_TRUE(update.pacer_config.has_value()); - EXPECT_TRUE(update.congestion_window.has_value()); - - DataRate new_bitrate = DataRate::BitsPerSec(200000); - update = controller_->OnNetworkRouteChange( - CreateRouteChange(kDefaultStartTime, new_bitrate)); - EXPECT_THAT(*update.target_rate, TargetRateCloseTo(new_bitrate)); - EXPECT_THAT(*update.pacer_config, - Property(&PacerConfig::data_rate, Ge(kInitialBitrate))); - EXPECT_TRUE(update.congestion_window.has_value()); -} - -// Bandwidth estimation is updated when feedbacks are received. -// Feedbacks which show an increasing delay cause the estimation to be reduced. -TEST_F(BbrNetworkControllerTest, UpdatesTargetSendRate) { - BbrNetworkControllerFactory factory; - Scenario s("bbr_unit/updates_rate", 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); }); - auto* client = s.CreateClient("send", config); - const DataSize kOverhead = DataSize::Bytes(38); // IPV4 + UDP + SRTP - auto routes = s.CreateRoutes(client, {send_net->node()}, kOverhead, - s.CreateClient("recv", CallClientConfig()), - {ret_net->node()}, kOverhead); - s.CreateVideoStream(routes->forward(), VideoStreamConfig()); - - s.RunFor(TimeDelta::Seconds(25)); - EXPECT_NEAR(client->send_bandwidth().kbps(), 450, 100); - - send_net->UpdateConfig([](NetworkSimulationConfig* c) { - c->bandwidth = DataRate::KilobitsPerSec(800); - c->delay = TimeDelta::Millis(100); - }); - - s.RunFor(TimeDelta::Seconds(20)); - EXPECT_NEAR(client->send_bandwidth().kbps(), 750, 150); - - send_net->UpdateConfig([](NetworkSimulationConfig* c) { - c->bandwidth = DataRate::KilobitsPerSec(200); - c->delay = TimeDelta::Millis(200); - }); - ret_net->UpdateConfig( - [](NetworkSimulationConfig* c) { c->delay = TimeDelta::Millis(200); }); - - s.RunFor(TimeDelta::Seconds(35)); - EXPECT_NEAR(client->send_bandwidth().kbps(), 170, 50); -} - -} // namespace test -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/data_transfer_tracker.cc b/modules/congestion_controller/bbr/data_transfer_tracker.cc deleted file mode 100644 index 9d811475b4..0000000000 --- a/modules/congestion_controller/bbr/data_transfer_tracker.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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/bbr/data_transfer_tracker.h" - -#include "rtc_base/checks.h" - -namespace webrtc { -namespace bbr { - -DataTransferTracker::DataTransferTracker() {} - -DataTransferTracker::~DataTransferTracker() {} - -void DataTransferTracker::AddSample(DataSize size_delta, - Timestamp send_time, - Timestamp ack_time) { - size_sum_ += size_delta; - - RTC_DCHECK(samples_.empty() || ack_time >= samples_.back().ack_time); - - if (!samples_.empty() && ack_time == samples_.back().ack_time) { - samples_.back().send_time = send_time; - samples_.back().size_sum = size_sum_; - } else { - Sample new_sample; - new_sample.ack_time = ack_time; - new_sample.send_time = send_time; - new_sample.size_delta = size_delta; - new_sample.size_sum = size_sum_; - samples_.push_back(new_sample); - } -} - -void DataTransferTracker::ClearOldSamples(Timestamp excluding_end) { - while (!samples_.empty() && samples_.front().ack_time < excluding_end) { - samples_.pop_front(); - } -} - -DataTransferTracker::Result DataTransferTracker::GetRatesByAckTime( - Timestamp covered_start, - Timestamp including_end) { - Result res; - // Last sample before covered_start. - const Sample* window_begin = nullptr; - // Sample at end time or first sample after end time- - const Sample* window_end = nullptr; - // To handle the case when the first sample is after covered_start. - if (samples_.front().ack_time < including_end) - window_begin = &samples_.front(); - // To handle the case when the last sample is before including_end. - if (samples_.back().ack_time > covered_start) - window_end = &samples_.back(); - for (const auto& sample : samples_) { - if (sample.ack_time < covered_start) { - window_begin = &sample; - } else if (sample.ack_time >= including_end) { - window_end = &sample; - break; - } - } - if (window_begin != nullptr && window_end != nullptr) { - res.acked_data = window_end->size_sum - window_begin->size_sum; - res.send_timespan = window_end->send_time - window_begin->send_time; - res.ack_timespan = window_end->ack_time - window_begin->ack_time; - } else { - res.acked_data = DataSize::Zero(); - res.ack_timespan = including_end - covered_start; - res.send_timespan = TimeDelta::Zero(); - } - return res; -} - -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/data_transfer_tracker.h b/modules/congestion_controller/bbr/data_transfer_tracker.h deleted file mode 100644 index 29dd7a3235..0000000000 --- a/modules/congestion_controller/bbr/data_transfer_tracker.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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_BBR_DATA_TRANSFER_TRACKER_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_DATA_TRANSFER_TRACKER_H_ - -#include - -#include "api/units/data_size.h" -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" - -namespace webrtc { -namespace bbr { -class DataTransferTracker { - public: - struct Result { - TimeDelta ack_timespan = TimeDelta::Zero(); - TimeDelta send_timespan = TimeDelta::Zero(); - DataSize acked_data = DataSize::Zero(); - }; - DataTransferTracker(); - ~DataTransferTracker(); - void AddSample(DataSize size_delta, Timestamp send_time, Timestamp ack_time); - void ClearOldSamples(Timestamp excluding_end); - - // Get the average data rate in the window that starts with the last ack which - // comes before covered_start and ends at the first ack that comes after or at - // including_end. - Result GetRatesByAckTime(Timestamp covered_start, Timestamp including_end); - - private: - struct Sample { - Timestamp ack_time = Timestamp::PlusInfinity(); - Timestamp send_time = Timestamp::PlusInfinity(); - DataSize size_delta = DataSize::Zero(); - DataSize size_sum = DataSize::Zero(); - }; - std::deque samples_; - DataSize size_sum_ = DataSize::Zero(); -}; -} // namespace bbr -} // namespace webrtc -#endif // MODULES_CONGESTION_CONTROLLER_BBR_DATA_TRANSFER_TRACKER_H_ diff --git a/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc b/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc deleted file mode 100644 index c9177ed612..0000000000 --- a/modules/congestion_controller/bbr/data_transfer_tracker_unittest.cc +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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/bbr/data_transfer_tracker.h" - -#include - -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { -namespace test { -namespace { -struct ResultForTest { - int64_t ack_span_ms; - int64_t send_span_ms; - int64_t acked_bytes; -}; -class DataTransferTrackerForTest : public DataTransferTracker { - public: - void AddSample(int bytes, int send_time_ms, int ack_time_ms) { - DataTransferTracker::AddSample(DataSize::Bytes(bytes), - Timestamp::Millis(send_time_ms), - Timestamp::Millis(ack_time_ms)); - } - - void ClearOldSamples(int excluding_end_ms) { - DataTransferTracker::ClearOldSamples(Timestamp::Millis(excluding_end_ms)); - } - ResultForTest GetRatesByAckTime(int covered_start_ms, int including_end_ms) { - auto result = DataTransferTracker::GetRatesByAckTime( - Timestamp::Millis(covered_start_ms), - Timestamp::Millis(including_end_ms)); - return ResultForTest{result.ack_timespan.ms(), result.send_timespan.ms(), - result.acked_data.bytes()}; - } -}; - -} // namespace - -TEST(DataTransferTrackerTest, TracksData) { - DataTransferTrackerForTest calc; - // Since we dont have any previous reference for the first packet, it won't be - // counted. - calc.AddSample(5555, 100000, 100100); - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100040, 100140); - calc.AddSample(1000, 100060, 100160); - - auto result = calc.GetRatesByAckTime(100000, 100200); - EXPECT_EQ(result.acked_bytes, 3000); - EXPECT_EQ(result.ack_span_ms, 60); - EXPECT_EQ(result.send_span_ms, 60); -} - -TEST(DataTransferTrackerTest, CoversStartTime) { - DataTransferTrackerForTest calc; - calc.AddSample(5555, 100000, 100100); - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100040, 100140); - calc.AddSample(1000, 100060, 100160); - calc.AddSample(1000, 100080, 100180); - - auto result = calc.GetRatesByAckTime(100140, 100200); - EXPECT_EQ(result.acked_bytes, 3000); - EXPECT_EQ(result.ack_span_ms, 60); - EXPECT_EQ(result.send_span_ms, 60); -} - -TEST(DataTransferTrackerTest, IncludesEndExcludesPastEnd) { - DataTransferTrackerForTest calc; - calc.AddSample(5555, 100000, 100100); - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100040, 100140); - calc.AddSample(1000, 100060, 100160); - calc.AddSample(1000, 100080, 100180); - - auto result = calc.GetRatesByAckTime(100120, 100160); - EXPECT_EQ(result.acked_bytes, 3000); - EXPECT_EQ(result.ack_span_ms, 60); - EXPECT_EQ(result.send_span_ms, 60); -} - -TEST(DataTransferTrackerTest, AccumulatesDuplicates) { - DataTransferTrackerForTest calc; - calc.AddSample(5555, 100000, 100100); - // Two packets at same time, should be accumulated. - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100060, 100160); - // Two packets at same time, should be accumulated. - calc.AddSample(1000, 100100, 100200); - calc.AddSample(1000, 100100, 100200); - calc.AddSample(1000, 100120, 100220); - - auto result = calc.GetRatesByAckTime(100120, 100200); - EXPECT_EQ(result.acked_bytes, 5000); - EXPECT_EQ(result.ack_span_ms, 100); - EXPECT_EQ(result.send_span_ms, 100); -} - -TEST(DataTransferTrackerTest, RemovesOldData) { - DataTransferTrackerForTest calc; - calc.AddSample(5555, 100000, 100100); - calc.AddSample(1000, 100020, 100120); - calc.AddSample(1000, 100040, 100140); - calc.AddSample(1000, 100060, 100160); - calc.AddSample(1000, 100080, 100180); - { - auto result = calc.GetRatesByAckTime(100120, 100200); - EXPECT_EQ(result.acked_bytes, 4000); - EXPECT_EQ(result.ack_span_ms, 80); - EXPECT_EQ(result.send_span_ms, 80); - } - // Note that this operation means that the packet acked at 100140 will not be - // counted any more, just used as time reference. - calc.ClearOldSamples(100140); - { - auto result = calc.GetRatesByAckTime(100120, 100200); - EXPECT_EQ(result.acked_bytes, 2000); - EXPECT_EQ(result.ack_span_ms, 40); - EXPECT_EQ(result.send_span_ms, 40); - } -} -} // namespace test -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/loss_rate_filter.cc b/modules/congestion_controller/bbr/loss_rate_filter.cc deleted file mode 100644 index 302e64a429..0000000000 --- a/modules/congestion_controller/bbr/loss_rate_filter.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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/bbr/loss_rate_filter.h" - -namespace webrtc { -namespace bbr { -namespace { -// From SendSideBandwidthEstimation. -const int kLimitNumPackets = 20; -// From RTCPSender video report interval. -const int64_t kUpdateIntervalMs = 1000; -} // namespace - -LossRateFilter::LossRateFilter() - : lost_packets_since_last_loss_update_(0), - expected_packets_since_last_loss_update_(0), - loss_rate_estimate_(0.0), - next_loss_update_ms_(0) {} - -void LossRateFilter::UpdateWithLossStatus(int64_t feedback_time, - int packets_sent, - int packets_lost) { - lost_packets_since_last_loss_update_ += packets_lost; - expected_packets_since_last_loss_update_ += packets_sent; - - if (feedback_time >= next_loss_update_ms_ && - expected_packets_since_last_loss_update_ >= kLimitNumPackets) { - int64_t lost = lost_packets_since_last_loss_update_; - int64_t expected = expected_packets_since_last_loss_update_; - loss_rate_estimate_ = static_cast(lost) / expected; - next_loss_update_ms_ = feedback_time + kUpdateIntervalMs; - lost_packets_since_last_loss_update_ = 0; - expected_packets_since_last_loss_update_ = 0; - } -} - -double LossRateFilter::GetLossRate() const { - return loss_rate_estimate_; -} -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/loss_rate_filter.h b/modules/congestion_controller/bbr/loss_rate_filter.h deleted file mode 100644 index 6a89c73e46..0000000000 --- a/modules/congestion_controller/bbr/loss_rate_filter.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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_BBR_LOSS_RATE_FILTER_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_LOSS_RATE_FILTER_H_ - -#include - -namespace webrtc { -namespace bbr { - -// Loss rate filter based on the implementation in SendSideBandwidthEstimation -// and the RTCPSender receiver report interval for video. -class LossRateFilter { - public: - LossRateFilter(); - void UpdateWithLossStatus(int64_t feedback_time_ms, - int packets_sent, - int packets_lost); - double GetLossRate() const; - - private: - int lost_packets_since_last_loss_update_; - int expected_packets_since_last_loss_update_; - double loss_rate_estimate_; - int64_t next_loss_update_ms_; -}; - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_LOSS_RATE_FILTER_H_ diff --git a/modules/congestion_controller/bbr/loss_rate_filter_unittest.cc b/modules/congestion_controller/bbr/loss_rate_filter_unittest.cc deleted file mode 100644 index 24603d8c7d..0000000000 --- a/modules/congestion_controller/bbr/loss_rate_filter_unittest.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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/bbr/loss_rate_filter.h" - -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { - -namespace { -const Timestamp kTestStartTime = Timestamp::Seconds(100000); -} // namespace - -TEST(LossRateFilterTest, AccumulatesToOne) { - LossRateFilter filter; - Timestamp current_time = kTestStartTime; - for (int i = 0; i < 10; i++) { - filter.UpdateWithLossStatus(current_time.ms(), 10, 10); - current_time += TimeDelta::Seconds(1); - } - EXPECT_NEAR(filter.GetLossRate(), 1.0, 0.01); -} - -TEST(LossRateFilterTest, StaysAtZero) { - LossRateFilter filter; - Timestamp current_time = kTestStartTime; - for (int i = 0; i < 10; i++) { - filter.UpdateWithLossStatus(current_time.ms(), 10, 0); - current_time += TimeDelta::Seconds(1); - } - EXPECT_NEAR(filter.GetLossRate(), 0.0, 0.01); -} - -TEST(LossRateFilterTest, VariesWithInput) { - LossRateFilter filter; - Timestamp current_time = kTestStartTime; - for (int j = 0; j < 10; j++) { - for (int i = 0; i < 5; i++) { - filter.UpdateWithLossStatus(current_time.ms(), 10, 10); - current_time += TimeDelta::Seconds(1); - } - EXPECT_NEAR(filter.GetLossRate(), 1.0, 0.1); - for (int i = 0; i < 5; i++) { - filter.UpdateWithLossStatus(current_time.ms(), 10, 0); - current_time += TimeDelta::Seconds(1); - } - EXPECT_NEAR(filter.GetLossRate(), 0.0, 0.1); - } -} - -TEST(LossRateFilterTest, DetectsChangingRate) { - LossRateFilter filter; - Timestamp current_time = kTestStartTime; - for (int per_decile = 0; per_decile < 10; per_decile += 1) { - // Update every 200 ms for 2 seconds - for (int i = 0; i < 10; i++) { - current_time += TimeDelta::Millis(200); - filter.UpdateWithLossStatus(current_time.ms(), 10, per_decile); - } - EXPECT_NEAR(filter.GetLossRate(), per_decile / 10.0, 0.05); - } -} -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/packet_number_indexed_queue.h b/modules/congestion_controller/bbr/packet_number_indexed_queue.h deleted file mode 100644 index b072191284..0000000000 --- a/modules/congestion_controller/bbr/packet_number_indexed_queue.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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. - */ - -// Based on the Quic implementation in Chromium. - -#ifndef MODULES_CONGESTION_CONTROLLER_BBR_PACKET_NUMBER_INDEXED_QUEUE_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_PACKET_NUMBER_INDEXED_QUEUE_H_ - -#include -#include - -#include -#include -#include - -#include "rtc_base/checks.h" - -namespace webrtc { -namespace bbr { - -// PacketNumberIndexedQueue is a queue of mostly continuous numbered entries -// which supports the following operations: -// - adding elements to the end of the queue, or at some point past the end -// - removing elements in any order -// - retrieving elements -// If all elements are inserted in order, all of the operations above are -// amortized O(1) time. -// -// Internally, the data structure is a deque where each element is marked as -// present or not. The deque starts at the lowest present index. Whenever an -// element is removed, it's marked as not present, and the front of the deque is -// cleared of elements that are not present. -// -// The tail of the queue is not cleared due to the assumption of entries being -// inserted in order, though removing all elements of the queue will return it -// to its initial state. -// -// Note that this data structure is inherently hazardous, since an addition of -// just two entries will cause it to consume all of the memory available. -// Because of that, it is not a general-purpose container and should not be used -// as one. -template -class PacketNumberIndexedQueue { - public: - PacketNumberIndexedQueue() - : number_of_present_entries_(0), first_packet_(0) {} - - // Retrieve the entry associated with the packet number. Returns the pointer - // to the entry in case of success, or nullptr if the entry does not exist. - T* GetEntry(int64_t packet_number); - const T* GetEntry(int64_t packet_number) const; - - // Inserts data associated |packet_number| into (or past) the end of the - // queue, filling up the missing intermediate entries as necessary. Returns - // true if the element has been inserted successfully, false if it was already - // in the queue or inserted out of order. - template - bool Emplace(int64_t packet_number, Args&&... args); - - // Removes data associated with |packet_number| and frees the slots in the - // queue as necessary. - bool Remove(int64_t packet_number); - - bool IsEmpty() const { return number_of_present_entries_ == 0; } - - // Returns the number of entries in the queue. - size_t number_of_present_entries() const { - return number_of_present_entries_; - } - - // Returns the number of entries allocated in the underlying deque. This is - // proportional to the memory usage of the queue. - size_t entry_slots_used() const { return entries_.size(); } - - // Packet number of the first entry in the queue. Zero if the queue is empty. - int64_t first_packet() const { return first_packet_; } - - // Packet number of the last entry ever inserted in the queue. Note that the - // entry in question may have already been removed. Zero if the queue is - // empty. - int64_t last_packet() const { - if (IsEmpty()) { - return 0; - } - return first_packet_ + entries_.size() - 1; - } - - private: - // Wrapper around T used to mark whether the entry is actually in the map. - struct EntryWrapper { - T data; - bool present; - - EntryWrapper() : data(), present(false) {} - - template - explicit EntryWrapper(Args&&... args) - : data(std::forward(args)...), present(true) {} - }; - - // Cleans up unused slots in the front after removing an element. - void Cleanup(); - - const EntryWrapper* GetEntryWrapper(int64_t offset) const; - EntryWrapper* GetEntryWrapper(int64_t offset) { - const auto* const_this = this; - return const_cast(const_this->GetEntryWrapper(offset)); - } - - std::deque entries_; - size_t number_of_present_entries_; - int64_t first_packet_; -}; - -template -T* PacketNumberIndexedQueue::GetEntry(int64_t packet_number) { - EntryWrapper* entry = GetEntryWrapper(packet_number); - if (entry == nullptr) { - return nullptr; - } - return &entry->data; -} - -template -const T* PacketNumberIndexedQueue::GetEntry(int64_t packet_number) const { - const EntryWrapper* entry = GetEntryWrapper(packet_number); - if (entry == nullptr) { - return nullptr; - } - return &entry->data; -} - -template -template -bool PacketNumberIndexedQueue::Emplace(int64_t packet_number, - Args&&... args) { - if (IsEmpty()) { - RTC_DCHECK(entries_.empty()); - RTC_DCHECK_EQ(0u, first_packet_); - - entries_.emplace_back(std::forward(args)...); - number_of_present_entries_ = 1; - first_packet_ = packet_number; - return true; - } - - // Do not allow insertion out-of-order. - if (packet_number <= last_packet()) { - return false; - } - - // Handle potentially missing elements. - int64_t offset = packet_number - first_packet_; - if (offset > static_cast(entries_.size())) { - entries_.resize(offset); - } - - number_of_present_entries_++; - entries_.emplace_back(std::forward(args)...); - RTC_DCHECK_EQ(packet_number, last_packet()); - return true; -} - -template -bool PacketNumberIndexedQueue::Remove(int64_t packet_number) { - EntryWrapper* entry = GetEntryWrapper(packet_number); - if (entry == nullptr) { - return false; - } - entry->present = false; - number_of_present_entries_--; - - if (packet_number == first_packet()) { - Cleanup(); - } - return true; -} - -template -void PacketNumberIndexedQueue::Cleanup() { - while (!entries_.empty() && !entries_.front().present) { - entries_.pop_front(); - first_packet_++; - } - if (entries_.empty()) { - first_packet_ = 0; - } -} - -template -auto PacketNumberIndexedQueue::GetEntryWrapper(int64_t offset) const - -> const EntryWrapper* { - if (offset < first_packet_) { - return nullptr; - } - - offset -= first_packet_; - if (offset >= static_cast(entries_.size())) { - return nullptr; - } - - const EntryWrapper* entry = &entries_[offset]; - if (!entry->present) { - return nullptr; - } - - return entry; -} - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_PACKET_NUMBER_INDEXED_QUEUE_H_ diff --git a/modules/congestion_controller/bbr/packet_number_indexed_queue_unittest.cc b/modules/congestion_controller/bbr/packet_number_indexed_queue_unittest.cc deleted file mode 100644 index c402083714..0000000000 --- a/modules/congestion_controller/bbr/packet_number_indexed_queue_unittest.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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/bbr/packet_number_indexed_queue.h" - -#include - -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { -namespace { - -class PacketNumberIndexedQueueTest : public ::testing::Test { - public: - PacketNumberIndexedQueueTest() {} - - protected: - PacketNumberIndexedQueue queue_; -}; - -TEST_F(PacketNumberIndexedQueueTest, InitialState) { - EXPECT_TRUE(queue_.IsEmpty()); - EXPECT_EQ(0u, queue_.first_packet()); - EXPECT_EQ(0u, queue_.last_packet()); - EXPECT_EQ(0u, queue_.number_of_present_entries()); - EXPECT_EQ(0u, queue_.entry_slots_used()); -} - -TEST_F(PacketNumberIndexedQueueTest, InsertingContinuousElements) { - ASSERT_TRUE(queue_.Emplace(1001, "one")); - EXPECT_EQ("one", *queue_.GetEntry(1001)); - - ASSERT_TRUE(queue_.Emplace(1002, "two")); - EXPECT_EQ("two", *queue_.GetEntry(1002)); - - EXPECT_FALSE(queue_.IsEmpty()); - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(1002u, queue_.last_packet()); - EXPECT_EQ(2u, queue_.number_of_present_entries()); - EXPECT_EQ(2u, queue_.entry_slots_used()); -} - -TEST_F(PacketNumberIndexedQueueTest, InsertingOutOfOrder) { - queue_.Emplace(1001, "one"); - - ASSERT_TRUE(queue_.Emplace(1003, "three")); - EXPECT_EQ(nullptr, queue_.GetEntry(1002)); - EXPECT_EQ("three", *queue_.GetEntry(1003)); - - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(1003u, queue_.last_packet()); - EXPECT_EQ(2u, queue_.number_of_present_entries()); - EXPECT_EQ(3u, queue_.entry_slots_used()); - - ASSERT_FALSE(queue_.Emplace(1002, "two")); -} - -TEST_F(PacketNumberIndexedQueueTest, InsertingIntoPast) { - queue_.Emplace(1001, "one"); - EXPECT_FALSE(queue_.Emplace(1000, "zero")); -} - -TEST_F(PacketNumberIndexedQueueTest, InsertingDuplicate) { - queue_.Emplace(1001, "one"); - EXPECT_FALSE(queue_.Emplace(1001, "one")); -} - -TEST_F(PacketNumberIndexedQueueTest, RemoveInTheMiddle) { - queue_.Emplace(1001, "one"); - queue_.Emplace(1002, "two"); - queue_.Emplace(1003, "three"); - - ASSERT_TRUE(queue_.Remove(1002)); - EXPECT_EQ(nullptr, queue_.GetEntry(1002)); - - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(1003u, queue_.last_packet()); - EXPECT_EQ(2u, queue_.number_of_present_entries()); - EXPECT_EQ(3u, queue_.entry_slots_used()); - - EXPECT_FALSE(queue_.Emplace(1002, "two")); - EXPECT_TRUE(queue_.Emplace(1004, "four")); -} - -TEST_F(PacketNumberIndexedQueueTest, RemoveAtImmediateEdges) { - queue_.Emplace(1001, "one"); - queue_.Emplace(1002, "two"); - queue_.Emplace(1003, "three"); - ASSERT_TRUE(queue_.Remove(1001)); - EXPECT_EQ(nullptr, queue_.GetEntry(1001)); - ASSERT_TRUE(queue_.Remove(1003)); - EXPECT_EQ(nullptr, queue_.GetEntry(1003)); - - EXPECT_EQ(1002u, queue_.first_packet()); - EXPECT_EQ(1003u, queue_.last_packet()); - EXPECT_EQ(1u, queue_.number_of_present_entries()); - EXPECT_EQ(2u, queue_.entry_slots_used()); - - EXPECT_TRUE(queue_.Emplace(1004, "four")); -} - -TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantFront) { - queue_.Emplace(1001, "one"); - queue_.Emplace(1002, "one (kinda)"); - queue_.Emplace(2001, "two"); - - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(2001u, queue_.last_packet()); - EXPECT_EQ(3u, queue_.number_of_present_entries()); - EXPECT_EQ(1001u, queue_.entry_slots_used()); - - ASSERT_TRUE(queue_.Remove(1002)); - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(2001u, queue_.last_packet()); - EXPECT_EQ(2u, queue_.number_of_present_entries()); - EXPECT_EQ(1001u, queue_.entry_slots_used()); - - ASSERT_TRUE(queue_.Remove(1001)); - EXPECT_EQ(2001u, queue_.first_packet()); - EXPECT_EQ(2001u, queue_.last_packet()); - EXPECT_EQ(1u, queue_.number_of_present_entries()); - EXPECT_EQ(1u, queue_.entry_slots_used()); -} - -TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantBack) { - queue_.Emplace(1001, "one"); - queue_.Emplace(2001, "two"); - - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(2001u, queue_.last_packet()); - - ASSERT_TRUE(queue_.Remove(2001)); - EXPECT_EQ(1001u, queue_.first_packet()); - EXPECT_EQ(2001u, queue_.last_packet()); -} - -TEST_F(PacketNumberIndexedQueueTest, ClearAndRepopulate) { - queue_.Emplace(1001, "one"); - queue_.Emplace(2001, "two"); - - ASSERT_TRUE(queue_.Remove(1001)); - ASSERT_TRUE(queue_.Remove(2001)); - EXPECT_TRUE(queue_.IsEmpty()); - EXPECT_EQ(0u, queue_.first_packet()); - EXPECT_EQ(0u, queue_.last_packet()); - - EXPECT_TRUE(queue_.Emplace(101, "one")); - EXPECT_TRUE(queue_.Emplace(201, "two")); - EXPECT_EQ(101u, queue_.first_packet()); - EXPECT_EQ(201u, queue_.last_packet()); -} - -TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsThatNeverExisted) { - ASSERT_FALSE(queue_.Remove(1000)); - queue_.Emplace(1001, "one"); - ASSERT_FALSE(queue_.Remove(1000)); - ASSERT_FALSE(queue_.Remove(1002)); -} - -TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsTwice) { - queue_.Emplace(1001, "one"); - ASSERT_TRUE(queue_.Remove(1001)); - ASSERT_FALSE(queue_.Remove(1001)); - ASSERT_FALSE(queue_.Remove(1001)); -} - -TEST_F(PacketNumberIndexedQueueTest, ConstGetter) { - queue_.Emplace(1001, "one"); - const auto& const_queue = queue_; - - EXPECT_EQ("one", *const_queue.GetEntry(1001)); - EXPECT_EQ(nullptr, const_queue.GetEntry(1002)); -} - -} // namespace -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/rtt_stats.cc b/modules/congestion_controller/bbr/rtt_stats.cc deleted file mode 100644 index 2973463f49..0000000000 --- a/modules/congestion_controller/bbr/rtt_stats.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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/bbr/rtt_stats.h" - -#include -#include -#include - -#include "rtc_base/logging.h" - -namespace webrtc { -namespace bbr { -namespace { - -// Default initial rtt used before any samples are received. -const int kInitialRttMs = 100; -const double kAlpha = 0.125; -const double kOneMinusAlpha = (1 - kAlpha); -const double kBeta = 0.25; -const double kOneMinusBeta = (1 - kBeta); -const int64_t kNumMicrosPerMilli = 1000; -} // namespace - -RttStats::RttStats() - : latest_rtt_(TimeDelta::Zero()), - min_rtt_(TimeDelta::Zero()), - smoothed_rtt_(TimeDelta::Zero()), - previous_srtt_(TimeDelta::Zero()), - mean_deviation_(TimeDelta::Zero()), - initial_rtt_us_(kInitialRttMs * kNumMicrosPerMilli) {} - -void RttStats::ExpireSmoothedMetrics() { - mean_deviation_ = - std::max(mean_deviation_, (smoothed_rtt_ - latest_rtt_).Abs()); - smoothed_rtt_ = std::max(smoothed_rtt_, latest_rtt_); -} - -// Updates the RTT based on a new sample. -void RttStats::UpdateRtt(TimeDelta send_delta, - TimeDelta ack_delay, - Timestamp now) { - if (send_delta.IsInfinite() || send_delta <= TimeDelta::Zero()) { - RTC_LOG(LS_WARNING) << "Ignoring measured send_delta, because it's is " - "either infinite, zero, or negative. send_delta = " - << ToString(send_delta); - return; - } - - // Update min_rtt_ first. min_rtt_ does not use an rtt_sample corrected for - // ack_delay but the raw observed send_delta, since poor clock granularity at - // the client may cause a high ack_delay to result in underestimation of the - // min_rtt_. - if (min_rtt_.IsZero() || min_rtt_ > send_delta) { - min_rtt_ = send_delta; - } - - // Correct for ack_delay if information received from the peer results in a - // positive RTT sample. Otherwise, we use the send_delta as a reasonable - // measure for smoothed_rtt. - TimeDelta rtt_sample = send_delta; - previous_srtt_ = smoothed_rtt_; - - if (rtt_sample > ack_delay) { - rtt_sample = rtt_sample - ack_delay; - } - latest_rtt_ = rtt_sample; - // First time call. - if (smoothed_rtt_.IsZero()) { - smoothed_rtt_ = rtt_sample; - mean_deviation_ = rtt_sample / 2; - } else { - mean_deviation_ = kOneMinusBeta * mean_deviation_ + - kBeta * (smoothed_rtt_ - rtt_sample).Abs(); - smoothed_rtt_ = kOneMinusAlpha * smoothed_rtt_ + kAlpha * rtt_sample; - RTC_LOG(LS_VERBOSE) << " smoothed_rtt(us):" << smoothed_rtt_.us() - << " mean_deviation(us):" << mean_deviation_.us(); - } -} - -void RttStats::OnConnectionMigration() { - latest_rtt_ = TimeDelta::Zero(); - min_rtt_ = TimeDelta::Zero(); - smoothed_rtt_ = TimeDelta::Zero(); - mean_deviation_ = TimeDelta::Zero(); - initial_rtt_us_ = kInitialRttMs * kNumMicrosPerMilli; -} - -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/rtt_stats.h b/modules/congestion_controller/bbr/rtt_stats.h deleted file mode 100644 index e8f0a8ba43..0000000000 --- a/modules/congestion_controller/bbr/rtt_stats.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ -// A convenience class to store RTT samples and calculate smoothed RTT. -// From the Quic BBR implementation in Chromium. - -#ifndef MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_ - -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" -#include "rtc_base/checks.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/logging.h" - -namespace webrtc { -namespace bbr { - -class RttStats { - public: - RttStats(); - - // Updates the RTT from an incoming ack which is received |send_delta| after - // the packet is sent and the peer reports the ack being delayed |ack_delay|. - void UpdateRtt(TimeDelta send_delta, TimeDelta ack_delay, Timestamp now); - - // Causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt - // is larger. The mean deviation is increased to the most recent deviation if - // it's larger. - void ExpireSmoothedMetrics(); - - // Called when connection migrates and RTT measurement needs to be reset. - void OnConnectionMigration(); - - // Returns the EWMA smoothed RTT for the connection. - // May return Zero if no valid updates have occurred. - TimeDelta smoothed_rtt() const { return smoothed_rtt_; } - - // Returns the EWMA smoothed RTT prior to the most recent RTT sample. - TimeDelta previous_srtt() const { return previous_srtt_; } - - int64_t initial_rtt_us() const { return initial_rtt_us_; } - - // Sets an initial RTT to be used for SmoothedRtt before any RTT updates. - void set_initial_rtt_us(int64_t initial_rtt_us) { - RTC_DCHECK_GE(initial_rtt_us, 0); - if (initial_rtt_us <= 0) { - RTC_LOG(LS_ERROR) << "Attempt to set initial rtt to <= 0."; - return; - } - initial_rtt_us_ = initial_rtt_us; - } - - // The most recent RTT measurement. - // May return Zero if no valid updates have occurred. - TimeDelta latest_rtt() const { return latest_rtt_; } - - // Returns the min_rtt for the entire connection. - // May return Zero if no valid updates have occurred. - TimeDelta min_rtt() const { return min_rtt_; } - - TimeDelta mean_deviation() const { return mean_deviation_; } - - private: - TimeDelta latest_rtt_; - TimeDelta min_rtt_; - TimeDelta smoothed_rtt_; - TimeDelta previous_srtt_; - // Mean RTT deviation during this session. - // Approximation of standard deviation, the error is roughly 1.25 times - // larger than the standard deviation, for a normally distributed signal. - TimeDelta mean_deviation_; - int64_t initial_rtt_us_; - - RTC_DISALLOW_COPY_AND_ASSIGN(RttStats); -}; - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_ diff --git a/modules/congestion_controller/bbr/rtt_stats_unittest.cc b/modules/congestion_controller/bbr/rtt_stats_unittest.cc deleted file mode 100644 index 133292e5ec..0000000000 --- a/modules/congestion_controller/bbr/rtt_stats_unittest.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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/bbr/rtt_stats.h" - -#include - -#include -#include - -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { -namespace test { - -class RttStatsTest : public ::testing::Test { - protected: - RttStats rtt_stats_; -}; - -TEST_F(RttStatsTest, DefaultsBeforeUpdate) { - EXPECT_LT(0u, rtt_stats_.initial_rtt_us()); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.min_rtt()); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.smoothed_rtt()); -} - -TEST_F(RttStatsTest, SmoothedRtt) { - // Verify that ack_delay is corrected for in Smoothed RTT. - rtt_stats_.UpdateRtt(TimeDelta::Millis(300), TimeDelta::Millis(100), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.smoothed_rtt()); - // Verify that effective RTT of zero does not change Smoothed RTT. - rtt_stats_.UpdateRtt(TimeDelta::Millis(200), TimeDelta::Millis(200), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.smoothed_rtt()); - // Verify that large erroneous ack_delay does not change Smoothed RTT. - rtt_stats_.UpdateRtt(TimeDelta::Millis(200), TimeDelta::Millis(300), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.smoothed_rtt()); -} - -// Ensure that the potential rounding artifacts in EWMA calculation do not cause -// the SRTT to drift too far from the exact value. -TEST_F(RttStatsTest, SmoothedRttStability) { - for (int64_t time = 3; time < 20000; time++) { - RttStats stats; - for (int64_t i = 0; i < 100; i++) { - stats.UpdateRtt(TimeDelta::Micros(time), TimeDelta::Millis(0), - Timestamp::Millis(0)); - int64_t time_delta_us = stats.smoothed_rtt().us() - time; - ASSERT_LE(std::abs(time_delta_us), 1); - } - } -} - -TEST_F(RttStatsTest, PreviousSmoothedRtt) { - // Verify that ack_delay is corrected for in Smoothed RTT. - rtt_stats_.UpdateRtt(TimeDelta::Millis(300), TimeDelta::Millis(100), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.previous_srtt()); - // Ensure the previous SRTT is 200ms after a 100ms sample. - rtt_stats_.UpdateRtt(TimeDelta::Millis(100), TimeDelta::Zero(), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(100), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Micros(187500).us(), rtt_stats_.smoothed_rtt().us()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.previous_srtt()); -} - -TEST_F(RttStatsTest, MinRtt) { - rtt_stats_.UpdateRtt(TimeDelta::Millis(200), TimeDelta::Zero(), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.min_rtt()); - rtt_stats_.UpdateRtt(TimeDelta::Millis(10), TimeDelta::Zero(), - Timestamp::Millis(0) + TimeDelta::Millis(10)); - EXPECT_EQ(TimeDelta::Millis(10), rtt_stats_.min_rtt()); - rtt_stats_.UpdateRtt(TimeDelta::Millis(50), TimeDelta::Zero(), - Timestamp::Millis(0) + TimeDelta::Millis(20)); - EXPECT_EQ(TimeDelta::Millis(10), rtt_stats_.min_rtt()); - rtt_stats_.UpdateRtt(TimeDelta::Millis(50), TimeDelta::Zero(), - Timestamp::Millis(0) + TimeDelta::Millis(30)); - EXPECT_EQ(TimeDelta::Millis(10), rtt_stats_.min_rtt()); - rtt_stats_.UpdateRtt(TimeDelta::Millis(50), TimeDelta::Zero(), - Timestamp::Millis(0) + TimeDelta::Millis(40)); - EXPECT_EQ(TimeDelta::Millis(10), rtt_stats_.min_rtt()); - // Verify that ack_delay does not go into recording of min_rtt_. - rtt_stats_.UpdateRtt(TimeDelta::Millis(7), TimeDelta::Millis(2), - Timestamp::Millis(0) + TimeDelta::Millis(50)); - EXPECT_EQ(TimeDelta::Millis(7), rtt_stats_.min_rtt()); -} - -TEST_F(RttStatsTest, ExpireSmoothedMetrics) { - TimeDelta initial_rtt = TimeDelta::Millis(10); - rtt_stats_.UpdateRtt(initial_rtt, TimeDelta::Zero(), Timestamp::Millis(0)); - EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); - EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); - - EXPECT_EQ(0.5 * initial_rtt, rtt_stats_.mean_deviation()); - - // Update once with a 20ms RTT. - TimeDelta doubled_rtt = 2 * initial_rtt; - rtt_stats_.UpdateRtt(doubled_rtt, TimeDelta::Zero(), Timestamp::Millis(0)); - EXPECT_EQ(1.125 * initial_rtt, rtt_stats_.smoothed_rtt()); - - // Expire the smoothed metrics, increasing smoothed rtt and mean deviation. - rtt_stats_.ExpireSmoothedMetrics(); - EXPECT_EQ(doubled_rtt, rtt_stats_.smoothed_rtt()); - EXPECT_EQ(0.875 * initial_rtt, rtt_stats_.mean_deviation()); - - // Now go back down to 5ms and expire the smoothed metrics, and ensure the - // mean deviation increases to 15ms. - TimeDelta half_rtt = 0.5 * initial_rtt; - rtt_stats_.UpdateRtt(half_rtt, TimeDelta::Zero(), Timestamp::Millis(0)); - EXPECT_GT(doubled_rtt, rtt_stats_.smoothed_rtt()); - EXPECT_LT(initial_rtt, rtt_stats_.mean_deviation()); -} - -TEST_F(RttStatsTest, UpdateRttWithBadSendDeltas) { - // Make sure we ignore bad RTTs. - - TimeDelta initial_rtt = TimeDelta::Millis(10); - rtt_stats_.UpdateRtt(initial_rtt, TimeDelta::Zero(), Timestamp::Millis(0)); - EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); - EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); - - std::vector bad_send_deltas; - bad_send_deltas.push_back(TimeDelta::Zero()); - bad_send_deltas.push_back(TimeDelta::PlusInfinity()); - bad_send_deltas.push_back(TimeDelta::Micros(-1000)); - - for (TimeDelta bad_send_delta : bad_send_deltas) { - rtt_stats_.UpdateRtt(bad_send_delta, TimeDelta::Zero(), - Timestamp::Millis(0)); - EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); - EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); - } -} - -TEST_F(RttStatsTest, ResetAfterConnectionMigrations) { - rtt_stats_.UpdateRtt(TimeDelta::Millis(300), TimeDelta::Millis(100), - Timestamp::Millis(0)); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Millis(200), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(TimeDelta::Millis(300), rtt_stats_.min_rtt()); - - // Reset rtt stats on connection migrations. - rtt_stats_.OnConnectionMigration(); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.latest_rtt()); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.smoothed_rtt()); - EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.min_rtt()); -} - -} // namespace test -} // namespace bbr -} // namespace webrtc diff --git a/modules/congestion_controller/bbr/windowed_filter.h b/modules/congestion_controller/bbr/windowed_filter.h deleted file mode 100644 index 14185a5306..0000000000 --- a/modules/congestion_controller/bbr/windowed_filter.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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_BBR_WINDOWED_FILTER_H_ -#define MODULES_CONGESTION_CONTROLLER_BBR_WINDOWED_FILTER_H_ - -// From the Quic BBR implementation in Chromium - -// Implements Kathleen Nichols' algorithm for tracking the minimum (or maximum) -// estimate of a stream of samples over some fixed time interval. (E.g., -// the minimum RTT over the past five minutes.) The algorithm keeps track of -// the best, second best, and third best min (or max) estimates, maintaining an -// invariant that the measurement time of the n'th best >= n-1'th best. - -// The algorithm works as follows. On a reset, all three estimates are set to -// the same sample. The second best estimate is then recorded in the second -// quarter of the window, and a third best estimate is recorded in the second -// half of the window, bounding the worst case error when the true min is -// monotonically increasing (or true max is monotonically decreasing) over the -// window. -// -// A new best sample replaces all three estimates, since the new best is lower -// (or higher) than everything else in the window and it is the most recent. -// The window thus effectively gets reset on every new min. The same property -// holds true for second best and third best estimates. Specifically, when a -// sample arrives that is better than the second best but not better than the -// best, it replaces the second and third best estimates but not the best -// estimate. Similarly, a sample that is better than the third best estimate -// but not the other estimates replaces only the third best estimate. -// -// Finally, when the best expires, it is replaced by the second best, which in -// turn is replaced by the third best. The newest sample replaces the third -// best. - -namespace webrtc { -namespace bbr { - -// Compares two values and returns true if the first is less than or equal -// to the second. -template -struct MinFilter { - bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } -}; - -// Compares two values and returns true if the first is greater than or equal -// to the second. -template -struct MaxFilter { - bool operator()(const T& lhs, const T& rhs) const { return lhs >= rhs; } -}; - -// Use the following to construct a windowed filter object of type T. -// For example, a min filter using Timestamp as the time type: -// WindowedFilter, Timestamp, TimeDelta> -// ObjectName; -// A max filter using 64-bit integers as the time type: -// WindowedFilter, uint64_t, int64_t> ObjectName; -// Specifically, this template takes four arguments: -// 1. T -- type of the measurement that is being filtered. -// 2. Compare -- MinFilter or MaxFilter, depending on the type of filter -// desired. -// 3. TimeT -- the type used to represent timestamps. -// 4. TimeDeltaT -- the type used to represent continuous time intervals between -// two timestamps. Has to be the type of (a - b) if both |a| and |b| are -// of type TimeT. -template -class WindowedFilter { - public: - // |window_length| is the period after which a best estimate expires. - // |zero_value| is used as the uninitialized value for objects of T. - // Importantly, |zero_value| should be an invalid value for a true sample. - WindowedFilter(TimeDeltaT window_length, T zero_value, TimeT zero_time) - : window_length_(window_length), - zero_value_(zero_value), - estimates_{Sample(zero_value_, zero_time), - Sample(zero_value_, zero_time), - Sample(zero_value_, zero_time)} {} - - // Changes the window length. Does not update any current samples. - void SetWindowLength(TimeDeltaT window_length) { - window_length_ = window_length; - } - - // Updates best estimates with |sample|, and expires and updates best - // estimates as necessary. - void Update(T new_sample, TimeT new_time) { - // Reset all estimates if they have not yet been initialized, if new sample - // is a new best, or if the newest recorded estimate is too old. - if (estimates_[0].sample == zero_value_ || - Compare()(new_sample, estimates_[0].sample) || - new_time - estimates_[2].time > window_length_) { - Reset(new_sample, new_time); - return; - } - - if (Compare()(new_sample, estimates_[1].sample)) { - estimates_[1] = Sample(new_sample, new_time); - estimates_[2] = estimates_[1]; - } else if (Compare()(new_sample, estimates_[2].sample)) { - estimates_[2] = Sample(new_sample, new_time); - } - - // Expire and update estimates as necessary. - if (new_time - estimates_[0].time > window_length_) { - // The best estimate hasn't been updated for an entire window, so promote - // second and third best estimates. - estimates_[0] = estimates_[1]; - estimates_[1] = estimates_[2]; - estimates_[2] = Sample(new_sample, new_time); - // Need to iterate one more time. Check if the new best estimate is - // outside the window as well, since it may also have been recorded a - // long time ago. Don't need to iterate once more since we cover that - // case at the beginning of the method. - if (new_time - estimates_[0].time > window_length_) { - estimates_[0] = estimates_[1]; - estimates_[1] = estimates_[2]; - } - return; - } - if (estimates_[1].sample == estimates_[0].sample && - new_time - estimates_[1].time > window_length_ >> 2) { - // A quarter of the window has passed without a better sample, so the - // second-best estimate is taken from the second quarter of the window. - estimates_[2] = estimates_[1] = Sample(new_sample, new_time); - return; - } - - if (estimates_[2].sample == estimates_[1].sample && - new_time - estimates_[2].time > window_length_ >> 1) { - // We've passed a half of the window without a better estimate, so take - // a third-best estimate from the second half of the window. - estimates_[2] = Sample(new_sample, new_time); - } - } - - // Resets all estimates to new sample. - void Reset(T new_sample, TimeT new_time) { - estimates_[0] = estimates_[1] = estimates_[2] = - Sample(new_sample, new_time); - } - - T GetBest() const { return estimates_[0].sample; } - T GetSecondBest() const { return estimates_[1].sample; } - T GetThirdBest() const { return estimates_[2].sample; } - - private: - struct Sample { - T sample; - TimeT time; - Sample(T init_sample, TimeT init_time) - : sample(init_sample), time(init_time) {} - }; - - TimeDeltaT window_length_; // Time length of window. - T zero_value_; // Uninitialized value of T. - Sample estimates_[3]; // Best estimate is element 0. -}; - -} // namespace bbr -} // namespace webrtc - -#endif // MODULES_CONGESTION_CONTROLLER_BBR_WINDOWED_FILTER_H_ diff --git a/modules/congestion_controller/bbr/windowed_filter_unittest.cc b/modules/congestion_controller/bbr/windowed_filter_unittest.cc deleted file mode 100644 index d537806794..0000000000 --- a/modules/congestion_controller/bbr/windowed_filter_unittest.cc +++ /dev/null @@ -1,372 +0,0 @@ -/* - * 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/bbr/windowed_filter.h" - -#include - -#include -#include - -#include "api/units/data_rate.h" -#include "api/units/time_delta.h" -#include "rtc_base/logging.h" -#include "test/gtest.h" - -namespace webrtc { -namespace bbr { -namespace test { -class WindowedFilterTest : public ::testing::Test { - public: - // Set the window to 99ms, so 25ms is more than a quarter rtt. - WindowedFilterTest() - : windowed_min_rtt_(99, TimeDelta::Zero(), 0), - windowed_max_bw_(99, DataRate::Zero(), 0) {} - - // Sets up windowed_min_rtt_ to have the following values: - // Best = 20ms, recorded at 25ms - // Second best = 40ms, recorded at 75ms - // Third best = 50ms, recorded at 100ms - void InitializeMinFilter() { - int64_t now_ms = 0; - TimeDelta rtt_sample = TimeDelta::Millis(10); - for (int i = 0; i < 5; ++i) { - windowed_min_rtt_.Update(rtt_sample, now_ms); - RTC_LOG(LS_VERBOSE) << "i: " << i << " sample: " << ToString(rtt_sample) - << " mins: " - " " - << ToString(windowed_min_rtt_.GetBest()) << " " - << ToString(windowed_min_rtt_.GetSecondBest()) << " " - << ToString(windowed_min_rtt_.GetThirdBest()); - now_ms += 25; - rtt_sample = rtt_sample + TimeDelta::Millis(10); - } - EXPECT_EQ(TimeDelta::Millis(20), windowed_min_rtt_.GetBest()); - EXPECT_EQ(TimeDelta::Millis(40), windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(TimeDelta::Millis(50), windowed_min_rtt_.GetThirdBest()); - } - - // Sets up windowed_max_bw_ to have the following values: - // Best = 900 bps, recorded at 25ms - // Second best = 700 bps, recorded at 75ms - // Third best = 600 bps, recorded at 100ms - void InitializeMaxFilter() { - int64_t now_ms = 0; - DataRate bw_sample = DataRate::BitsPerSec(1000); - for (int i = 0; i < 5; ++i) { - windowed_max_bw_.Update(bw_sample, now_ms); - RTC_LOG(LS_VERBOSE) << "i: " << i << " sample: " << ToString(bw_sample) - << " maxs: " - " " - << ToString(windowed_max_bw_.GetBest()) << " " - << ToString(windowed_max_bw_.GetSecondBest()) << " " - << ToString(windowed_max_bw_.GetThirdBest()); - now_ms += 25; - bw_sample = DataRate::BitsPerSec(bw_sample.bps() - 100); - } - EXPECT_EQ(DataRate::BitsPerSec(900), windowed_max_bw_.GetBest()); - EXPECT_EQ(DataRate::BitsPerSec(700), windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(DataRate::BitsPerSec(600), windowed_max_bw_.GetThirdBest()); - } - - protected: - WindowedFilter, int64_t, int64_t> - windowed_min_rtt_; - WindowedFilter, int64_t, int64_t> - windowed_max_bw_; -}; - -namespace { -// Test helper function: updates the filter with a lot of small values in order -// to ensure that it is not susceptible to noise. -void UpdateWithIrrelevantSamples( - WindowedFilter, uint64_t, uint64_t>* filter, - uint64_t max_value, - uint64_t time) { - for (uint64_t i = 0; i < 1000; i++) { - filter->Update(i % max_value, time); - } -} -} // namespace - -TEST_F(WindowedFilterTest, UninitializedEstimates) { - EXPECT_EQ(TimeDelta::Zero(), windowed_min_rtt_.GetBest()); - EXPECT_EQ(TimeDelta::Zero(), windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(TimeDelta::Zero(), windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(DataRate::Zero(), windowed_max_bw_.GetBest()); - EXPECT_EQ(DataRate::Zero(), windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(DataRate::Zero(), windowed_max_bw_.GetThirdBest()); -} - -TEST_F(WindowedFilterTest, MonotonicallyIncreasingMin) { - int64_t now_ms = 0; - TimeDelta rtt_sample = TimeDelta::Millis(10); - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(TimeDelta::Millis(10), windowed_min_rtt_.GetBest()); - - // Gradually increase the rtt samples and ensure the windowed min rtt starts - // rising. - for (int i = 0; i < 6; ++i) { - now_ms += 25; - rtt_sample = rtt_sample + TimeDelta::Millis(10); - windowed_min_rtt_.Update(rtt_sample, now_ms); - RTC_LOG(LS_VERBOSE) << "i: " << i << " sample: " << rtt_sample.ms() - << " mins: " - " " - << windowed_min_rtt_.GetBest().ms() << " " - << windowed_min_rtt_.GetSecondBest().ms() << " " - << windowed_min_rtt_.GetThirdBest().ms(); - if (i < 3) { - EXPECT_EQ(TimeDelta::Millis(10), windowed_min_rtt_.GetBest()); - } else if (i == 3) { - EXPECT_EQ(TimeDelta::Millis(20), windowed_min_rtt_.GetBest()); - } else if (i < 6) { - EXPECT_EQ(TimeDelta::Millis(40), windowed_min_rtt_.GetBest()); - } - } -} - -TEST_F(WindowedFilterTest, MonotonicallyDecreasingMax) { - int64_t now_ms = 0; - DataRate bw_sample = DataRate::BitsPerSec(1000); - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(DataRate::BitsPerSec(1000), windowed_max_bw_.GetBest()); - - // Gradually decrease the bw samples and ensure the windowed max bw starts - // decreasing. - for (int i = 0; i < 6; ++i) { - now_ms += 25; - bw_sample = DataRate::BitsPerSec(bw_sample.bps() - 100); - windowed_max_bw_.Update(bw_sample, now_ms); - RTC_LOG(LS_VERBOSE) << "i: " << i << " sample: " << bw_sample.bps() - << " maxs: " - " " - << windowed_max_bw_.GetBest().bps() << " " - << windowed_max_bw_.GetSecondBest().bps() << " " - << windowed_max_bw_.GetThirdBest().bps(); - if (i < 3) { - EXPECT_EQ(DataRate::BitsPerSec(1000), windowed_max_bw_.GetBest()); - } else if (i == 3) { - EXPECT_EQ(DataRate::BitsPerSec(900), windowed_max_bw_.GetBest()); - } else if (i < 6) { - EXPECT_EQ(DataRate::BitsPerSec(700), windowed_max_bw_.GetBest()); - } - } -} - -TEST_F(WindowedFilterTest, SampleChangesThirdBestMin) { - InitializeMinFilter(); - // RTT sample lower than the third-choice min-rtt sets that, but nothing else. - TimeDelta rtt_sample = - windowed_min_rtt_.GetThirdBest() - TimeDelta::Millis(5); - // This assert is necessary to avoid triggering -Wstrict-overflow - // See crbug/616957 - ASSERT_GT(windowed_min_rtt_.GetThirdBest(), TimeDelta::Millis(5)); - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(TimeDelta::Millis(40), windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(TimeDelta::Millis(20), windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, SampleChangesThirdBestMax) { - InitializeMaxFilter(); - // BW sample higher than the third-choice max sets that, but nothing else. - DataRate bw_sample = - DataRate::BitsPerSec(windowed_max_bw_.GetThirdBest().bps() + 50); - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(DataRate::BitsPerSec(700), windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(DataRate::BitsPerSec(900), windowed_max_bw_.GetBest()); -} - -TEST_F(WindowedFilterTest, SampleChangesSecondBestMin) { - InitializeMinFilter(); - // RTT sample lower than the second-choice min sets that and also - // the third-choice min. - TimeDelta rtt_sample = - windowed_min_rtt_.GetSecondBest() - TimeDelta::Millis(5); - // This assert is necessary to avoid triggering -Wstrict-overflow - // See crbug/616957 - ASSERT_GT(windowed_min_rtt_.GetSecondBest(), TimeDelta::Millis(5)); - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(TimeDelta::Millis(20), windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, SampleChangesSecondBestMax) { - InitializeMaxFilter(); - // BW sample higher than the second-choice max sets that and also - // the third-choice max. - DataRate bw_sample = - DataRate::BitsPerSec(windowed_max_bw_.GetSecondBest().bps() + 50); - - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(DataRate::BitsPerSec(900), windowed_max_bw_.GetBest()); -} - -TEST_F(WindowedFilterTest, SampleChangesAllMins) { - InitializeMinFilter(); - // RTT sample lower than the first-choice min-rtt sets that and also - // the second and third-choice mins. - TimeDelta rtt_sample = windowed_min_rtt_.GetBest() - TimeDelta::Millis(5); - // This assert is necessary to avoid triggering -Wstrict-overflow - // See crbug/616957 - ASSERT_GT(windowed_min_rtt_.GetBest(), TimeDelta::Millis(5)); - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, SampleChangesAllMaxs) { - InitializeMaxFilter(); - // BW sample higher than the first-choice max sets that and also - // the second and third-choice maxs. - DataRate bw_sample = - DataRate::BitsPerSec(windowed_max_bw_.GetBest().bps() + 50); - // Latest sample was recorded at 100ms. - int64_t now_ms = 101; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireBestMin) { - InitializeMinFilter(); - TimeDelta old_third_best = windowed_min_rtt_.GetThirdBest(); - TimeDelta old_second_best = windowed_min_rtt_.GetSecondBest(); - TimeDelta rtt_sample = old_third_best + TimeDelta::Millis(5); - // Best min sample was recorded at 25ms, so expiry time is 124ms. - int64_t now_ms = 125; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(old_third_best, windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(old_second_best, windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireBestMax) { - InitializeMaxFilter(); - DataRate old_third_best = windowed_max_bw_.GetThirdBest(); - DataRate old_second_best = windowed_max_bw_.GetSecondBest(); - DataRate bw_sample = DataRate::BitsPerSec(old_third_best.bps() - 50); - // Best max sample was recorded at 25ms, so expiry time is 124ms. - int64_t now_ms = 125; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(old_third_best, windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(old_second_best, windowed_max_bw_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireSecondBestMin) { - InitializeMinFilter(); - TimeDelta old_third_best = windowed_min_rtt_.GetThirdBest(); - TimeDelta rtt_sample = old_third_best + TimeDelta::Millis(5); - // Second best min sample was recorded at 75ms, so expiry time is 174ms. - int64_t now_ms = 175; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(old_third_best, windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireSecondBestMax) { - InitializeMaxFilter(); - DataRate old_third_best = windowed_max_bw_.GetThirdBest(); - DataRate bw_sample = DataRate::BitsPerSec(old_third_best.bps() - 50); - // Second best max sample was recorded at 75ms, so expiry time is 174ms. - int64_t now_ms = 175; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(old_third_best, windowed_max_bw_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireAllMins) { - InitializeMinFilter(); - TimeDelta rtt_sample = - windowed_min_rtt_.GetThirdBest() + TimeDelta::Millis(5); - // This assert is necessary to avoid triggering -Wstrict-overflow - // See crbug/616957 - ASSERT_LT(windowed_min_rtt_.GetThirdBest(), TimeDelta::PlusInfinity()); - // Third best min sample was recorded at 100ms, so expiry time is 199ms. - int64_t now_ms = 200; - windowed_min_rtt_.Update(rtt_sample, now_ms); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); - EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest()); -} - -TEST_F(WindowedFilterTest, ExpireAllMaxs) { - InitializeMaxFilter(); - DataRate bw_sample = - DataRate::BitsPerSec(windowed_max_bw_.GetThirdBest().bps() - 50); - // Third best max sample was recorded at 100ms, so expiry time is 199ms. - int64_t now_ms = 200; - windowed_max_bw_.Update(bw_sample, now_ms); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); - EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest()); -} - -// Test the windowed filter where the time used is an exact counter instead of a -// timestamp. This is useful if, for example, the time is measured in round -// trips. -TEST_F(WindowedFilterTest, ExpireCounterBasedMax) { - // Create a window which starts at t = 0 and expires after two cycles. - WindowedFilter, uint64_t, uint64_t> max_filter( - 2, 0, 0); - - const uint64_t kBest = 50000; - // Insert 50000 at t = 1. - max_filter.Update(50000, 1); - EXPECT_EQ(kBest, max_filter.GetBest()); - UpdateWithIrrelevantSamples(&max_filter, 20, 1); - EXPECT_EQ(kBest, max_filter.GetBest()); - - // Insert 40000 at t = 2. Nothing is expected to expire. - max_filter.Update(40000, 2); - EXPECT_EQ(kBest, max_filter.GetBest()); - UpdateWithIrrelevantSamples(&max_filter, 20, 2); - EXPECT_EQ(kBest, max_filter.GetBest()); - - // Insert 30000 at t = 3. Nothing is expected to expire yet. - max_filter.Update(30000, 3); - EXPECT_EQ(kBest, max_filter.GetBest()); - UpdateWithIrrelevantSamples(&max_filter, 20, 3); - EXPECT_EQ(kBest, max_filter.GetBest()); - RTC_LOG(LS_VERBOSE) << max_filter.GetSecondBest(); - RTC_LOG(LS_VERBOSE) << max_filter.GetThirdBest(); - - // Insert 20000 at t = 4. 50000 at t = 1 expires, so 40000 becomes the new - // maximum. - const uint64_t kNewBest = 40000; - max_filter.Update(20000, 4); - EXPECT_EQ(kNewBest, max_filter.GetBest()); - UpdateWithIrrelevantSamples(&max_filter, 20, 4); - EXPECT_EQ(kNewBest, max_filter.GetBest()); -} - -} // namespace test -} // namespace bbr -} // namespace webrtc diff --git a/test/BUILD.gn b/test/BUILD.gn index 2e22476c80..a78b5adbb2 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -29,7 +29,6 @@ group("test") { deps += [ ":test_main", ":test_support_unittests", - "scenario/scenario_tests", ] } } diff --git a/test/scenario/scenario_tests/BUILD.gn b/test/scenario/scenario_tests/BUILD.gn deleted file mode 100644 index 74ee1a768b..0000000000 --- a/test/scenario/scenario_tests/BUILD.gn +++ /dev/null @@ -1,28 +0,0 @@ -# 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") - -if (rtc_include_tests) { - rtc_test("scenario_tests") { - testonly = true - sources = [ "bbr_performance.cc" ] - deps = [ - "../:scenario", - "../..:test_main", - "../../:field_trial", - "../../:fileutils", - "../../:test_common", - "../../:test_support", - "../../../modules/congestion_controller/bbr", - "../../../rtc_base:rtc_base_approved", - "../../../rtc_base/experiments:field_trial_parser", - "//testing/gtest", - ] - } -} diff --git a/test/scenario/scenario_tests/bbr_performance.cc b/test/scenario/scenario_tests/bbr_performance.cc deleted file mode 100644 index 0023d905d3..0000000000 --- a/test/scenario/scenario_tests/bbr_performance.cc +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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/bbr/bbr_factory.h" -#include "rtc_base/experiments/field_trial_parser.h" -#include "rtc_base/experiments/field_trial_units.h" -#include "rtc_base/random.h" -#include "test/field_trial.h" -#include "test/gtest.h" -#include "test/scenario/scenario.h" - -namespace webrtc { -namespace test { -namespace { -constexpr int64_t kRunTimeMs = 60000; - -using ::testing::Combine; -using ::testing::make_tuple; -using ::testing::tuple; -using ::testing::Values; - -using Codec = VideoStreamConfig::Encoder::Codec; -using CodecImpl = VideoStreamConfig::Encoder::Implementation; - -struct CallTestConfig { - struct Scenario { - FieldTrialParameter random_seed; - FieldTrialFlag return_traffic; - FieldTrialParameter capacity; - FieldTrialParameter propagation_delay; - FieldTrialParameter cross_traffic; - FieldTrialParameter delay_noise; - FieldTrialParameter loss_rate; - Scenario() - : random_seed("rs", 1), - return_traffic("ret"), - capacity("bw", DataRate::KilobitsPerSec(300)), - propagation_delay("dl", TimeDelta::Millis(100)), - cross_traffic("ct", DataRate::Zero()), - delay_noise("dn", TimeDelta::Zero()), - loss_rate("pl", 0) {} - void Parse(std::string config_str) { - ParseFieldTrial( - {&random_seed, &return_traffic, &capacity, &propagation_delay, - &cross_traffic, &delay_noise, &loss_rate}, - config_str); - } - } scenario; - struct Tuning { - FieldTrialFlag use_bbr; - FieldTrialFlag bbr_no_target_rate; - FieldTrialOptional bbr_initial_window; - FieldTrialParameter bbr_encoder_gain; - Tuning() - : use_bbr("bbr"), - bbr_no_target_rate("notr"), - bbr_initial_window("iw", DataSize::Bytes(8000)), - bbr_encoder_gain("eg", 0.8) {} - void Parse(std::string config_str) { - ParseFieldTrial( - { - &use_bbr, - &bbr_no_target_rate, - &bbr_initial_window, - &bbr_encoder_gain, - }, - config_str); - } - } tuning; - - void Parse(std::string scenario_string, std::string tuning_string) { - scenario.Parse(scenario_string); - tuning.Parse(tuning_string); - scenario_str = scenario_string; - tuning_str = tuning_string; - } - std::string scenario_str; - std::string tuning_str; - - std::string BbrTrial() const { - char trial_buf[1024]; - rtc::SimpleStringBuilder trial(trial_buf); - trial << "WebRTC-BweBbrConfig/"; - trial << "encoder_rate_gain_in_probe_rtt:0.5"; - trial.AppendFormat(",encoder_rate_gain:%.1lf", - tuning.bbr_encoder_gain.Get()); - if (tuning.bbr_no_target_rate) - trial << ",pacing_rate_as_target:1"; - if (tuning.bbr_initial_window) - trial << ",initial_cwin:" << tuning.bbr_initial_window->bytes(); - trial << "/"; - return trial.str(); - } - std::string FieldTrials() const { - std::string trials; - if (tuning.use_bbr) { - trials += - "WebRTC-BweCongestionController/Enabled,BBR/" - "WebRTC-Pacer-DrainQueue/Disabled/" - "WebRTC-Pacer-PadInSilence/Enabled/" - "WebRTC-Pacer-BlockAudio/Disabled/" - "WebRTC-Audio-SendSideBwe/Enabled/" - "WebRTC-SendSideBwe-WithOverhead/Enabled/"; - trials += BbrTrial(); - } - return trials; - } - - std::string Name() const { - char raw_name[1024]; - rtc::SimpleStringBuilder name(raw_name); - for (char c : scenario_str + "__tun__" + tuning_str) { - if (c == ':') { - continue; - } else if (c == ',') { - name << "_"; - } else if (c == '%') { - name << "p"; - } else { - name << c; - } - } - return name.str(); - } -}; -} // namespace -class BbrScenarioTest - : public ::testing::Test, - public ::testing::WithParamInterface> { - public: - BbrScenarioTest() { - conf_.Parse(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam())); - field_trial_.reset(new test::ScopedFieldTrials(conf_.FieldTrials())); - } - CallTestConfig conf_; - - private: - std::unique_ptr field_trial_; -}; - -TEST_P(BbrScenarioTest, ReceivesVideo) { - BbrNetworkControllerFactory bbr_factory; - Scenario s("bbr_test_gen/bbr__" + conf_.Name()); - CallClientConfig call_config; - if (conf_.tuning.use_bbr) { - call_config.transport.cc_factory = &bbr_factory; - } - call_config.transport.rates.min_rate = DataRate::KilobitsPerSec(30); - call_config.transport.rates.max_rate = DataRate::KilobitsPerSec(1800); - - CallClient* alice = s.CreateClient("send", call_config); - CallClient* bob = s.CreateClient("return", call_config); - NetworkSimulationConfig net_conf; - net_conf.bandwidth = conf_.scenario.capacity; - net_conf.delay = conf_.scenario.propagation_delay; - net_conf.loss_rate = conf_.scenario.loss_rate; - net_conf.delay_std_dev = conf_.scenario.delay_noise; - auto* send_net = s.CreateMutableSimulationNode(net_conf); - auto* ret_net = s.CreateMutableSimulationNode(net_conf); - auto route = - s.CreateRoutes(alice, {send_net->node()}, bob, {ret_net->node()}); - - VideoStreamPair* alice_video = - s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) { - c->encoder.fake.max_rate = DataRate::KilobitsPerSec(1800); - }); - s.CreateAudioStream(route->forward(), [&](AudioStreamConfig* c) { - if (conf_.tuning.use_bbr) { - c->stream.in_bandwidth_estimation = true; - c->encoder.fixed_rate = DataRate::KilobitsPerSec(31); - } - }); - - VideoStreamPair* bob_video = nullptr; - if (conf_.scenario.return_traffic) { - bob_video = - s.CreateVideoStream(route->reverse(), [&](VideoStreamConfig* c) { - c->encoder.fake.max_rate = DataRate::KilobitsPerSec(1800); - }); - s.CreateAudioStream(route->reverse(), [&](AudioStreamConfig* c) { - if (conf_.tuning.use_bbr) { - c->stream.in_bandwidth_estimation = true; - c->encoder.fixed_rate = DataRate::KilobitsPerSec(31); - } - }); - } - RandomWalkConfig cross_config; - cross_config.peak_rate = conf_.scenario.cross_traffic; - cross_config.random_seed = conf_.scenario.random_seed; - auto* cross_traffic = s.net()->CreateRandomWalkCrossTraffic( - s.net()->CreateTrafficRoute({send_net->node()}), cross_config); - - s.CreatePrinter("send.stats.txt", TimeDelta::Millis(100), - {alice->StatsPrinter(), alice_video->send()->StatsPrinter(), - cross_traffic->StatsPrinter(), send_net->ConfigPrinter()}); - - std::vector return_printers{ - bob->StatsPrinter(), ColumnPrinter::Fixed("cross_traffic_rate", "0"), - ret_net->ConfigPrinter()}; - if (bob_video) - return_printers.push_back(bob_video->send()->StatsPrinter()); - s.CreatePrinter("return.stats.txt", TimeDelta::Millis(100), return_printers); - - s.RunFor(TimeDelta::Millis(kRunTimeMs)); -} - -INSTANTIATE_TEST_SUITE_P(Selected, - BbrScenarioTest, - Values(make_tuple("rs:1,bw:150,dl:100,ct:100", - "bbr"))); - -INSTANTIATE_TEST_SUITE_P( - OneWayTuning, - BbrScenarioTest, - Values(make_tuple("bw:150,dl:100", "bbr,iw:,eg:100%,notr"), - make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:100%,notr"), - make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:100%"), - make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:80%"))); - -INSTANTIATE_TEST_SUITE_P(OneWayTuned, - BbrScenarioTest, - Values(make_tuple("bw:150,dl:100", "bbr"), - make_tuple("bw:150,dl:100", ""), - make_tuple("bw:800,dl:100", "bbr"), - make_tuple("bw:800,dl:100", ""))); - -INSTANTIATE_TEST_SUITE_P(OneWayDegraded, - BbrScenarioTest, - Values(make_tuple("bw:150,dl:100,dn:30,pl:5%", "bbr"), - make_tuple("bw:150,dl:100,dn:30,pl:5%", ""), - - make_tuple("bw:150,ct:100,dl:100", "bbr"), - make_tuple("bw:150,ct:100,dl:100", ""), - - make_tuple("bw:800,dl:100,dn:30,pl:5%", "bbr"), - make_tuple("bw:800,dl:100,dn:30,pl:5%", ""), - - make_tuple("bw:800,ct:600,dl:100", "bbr"), - make_tuple("bw:800,ct:600,dl:100", ""))); - -INSTANTIATE_TEST_SUITE_P(TwoWay, - BbrScenarioTest, - Values(make_tuple("ret,bw:150,dl:100", "bbr"), - make_tuple("ret,bw:150,dl:100", ""), - make_tuple("ret,bw:800,dl:100", "bbr"), - make_tuple("ret,bw:800,dl:100", ""), - make_tuple("ret,bw:150,dl:50", "bbr"), - make_tuple("ret,bw:150,dl:50", ""))); -} // namespace test -} // namespace webrtc