From 53d76c619034785ce3d1472539d0792194a4a0ce Mon Sep 17 00:00:00 2001 From: gnish Date: Tue, 15 Aug 2017 02:26:22 -0700 Subject: [PATCH] Almost full implementation of BBR's core, missing receiver side implementation, pacer, and BitrateObserver class which is responsible for communication between BBR and pacer/encoder. Significant changes: Recovery mode and a separate bucket for the high gain phase. BUG=webrtc:7713 Review-Url: https://codereview.webrtc.org/2990163002 Cr-Commit-Position: refs/heads/master@{#19349} --- .../test/estimators/bbr.cc | 315 ++++++++++++++++-- .../test/estimators/bbr.h | 142 +++++++- .../test/estimators/max_bandwidth_filter.cc | 22 +- .../test/estimators/max_bandwidth_filter.h | 6 +- .../max_bandwidth_filter_unittest.cc | 38 +-- .../test/estimators/min_rtt_filter.h | 13 +- .../estimators/min_rtt_filter_unittest.cc | 15 +- 7 files changed, 470 insertions(+), 81 deletions(-) diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc index 11979bf446..a45700b19e 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.cc @@ -12,6 +12,7 @@ #include "webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h" #include +#include #include "webrtc/modules/remote_bitrate_estimator/test/estimators/congestion_window.h" #include "webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h" @@ -21,7 +22,7 @@ namespace webrtc { namespace testing { namespace bwe { namespace { -const int kFeedbackIntervalsMs = 3; +const int kFeedbackIntervalsMs = 5; // BBR uses this value to double sending rate each round trip. Design document // suggests using this value. const float kHighGain = 2.885f; @@ -35,7 +36,7 @@ const int kMaxRoundsWithoutGrowth = 3; // Pacing gain values for Probe Bandwidth mode. const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1}; const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]); -// The least amount of rounds PROBE_RTT mode should last. +// Least number of rounds PROBE_RTT should last. const int kProbeRttDurationRounds = 1; // The least amount of milliseconds PROBE_RTT mode should last. const int kProbeRttDurationMs = 200; @@ -46,9 +47,20 @@ const float kTargetCongestionWindowGain = 1; // work, it is nice to have some extra room in congestion window for full link // utilization. Value chosen by observations on different tests. const float kCruisingCongestionWindowGain = 1.5f; -// Expiration time for min_rtt sample, which is set to 10 seconds according to -// BBR design doc. -const int64_t kMinRttFilterSizeMs = 10000; +// Pacing gain specific for Recovery mode. Chosen by experiments in simulation +// tool. +const float kRecoveryPacingGain = 0.5f; +// Congestion window gain specific for Recovery mode. Chosen by experiments in +// simulation tool. +const float kRecoveryCongestionWindowGain = 1.5f; +// Number of rounds over which average RTT is stored for Recovery mode. +const size_t kPastRttsFilterSize = 1; +// Threshold to assume average RTT has increased for a round. Chosen by +// experiments in simulation tool. +const float kRttIncreaseThreshold = 3; +// Threshold to assume average RTT has decreased for a round. Chosen by +// experiments in simulation tool. +const float kRttDecreaseThreshold = 1.5f; } // namespace BbrBweSender::BbrBweSender(Clock* clock) @@ -60,15 +72,32 @@ BbrBweSender::BbrBweSender(Clock* clock) congestion_window_(new CongestionWindow()), rand_(new Random(time(NULL))), round_count_(0), - last_packet_sent_(0), round_trip_end_(0), full_bandwidth_reached_(false), cycle_start_time_ms_(0), cycle_index_(0), - prior_in_flight_(0), + bytes_acked_(0), probe_rtt_start_time_ms_(0), - minimum_congestion_window_start_time_ms_(), - minimum_congestion_window_start_round_(0) { + minimum_congestion_window_start_time_ms_(0), + minimum_congestion_window_start_round_(0), + bytes_sent_(0), + last_packet_sent_sequence_number_(0), + last_packet_acked_sequence_number_(0), + last_packet_ack_time_(0), + last_packet_send_time_(0), + pacing_rate_bps_(0), + last_packet_send_time_during_high_gain_ms_(-1), + data_sent_before_high_gain_started_bytes_(-1), + data_sent_before_high_gain_ended_bytes_(-1), + first_packet_ack_time_during_high_gain_ms_(-1), + last_packet_ack_time_during_high_gain_ms_(-1), + data_acked_before_high_gain_started_bytes_(-1), + data_acked_before_high_gain_ended_bytes_(-1), + first_packet_seq_num_during_high_gain_(-1), + last_packet_seq_num_during_high_gain_(-1), + high_gain_over_(false), + packet_stats_(), + past_rtts_() { // Initially enter Startup mode. EnterStartup(); } @@ -79,26 +108,101 @@ int BbrBweSender::GetFeedbackIntervalMs() const { return kFeedbackIntervalsMs; } +void BbrBweSender::CalculatePacingRate() { + pacing_rate_bps_ = + max_bandwidth_filter_->max_bandwidth_estimate_bps() * pacing_gain_; +} + +void BbrBweSender::HandleLoss(uint64_t last_acked_packet, + uint64_t recently_acked_packet) { + // Logic specific to wrapping sequence numbers. + if (!last_acked_packet) + return; + for (uint16_t i = last_acked_packet + 1; + AheadOrAt(recently_acked_packet - 1, i); i++) { + congestion_window_->AckReceived(packet_stats_[i].payload_size_bytes); + } +} + +void BbrBweSender::AddToPastRtts(int64_t rtt_sample_ms) { + uint64_t last_round = 0; + if (!past_rtts_.empty()) + last_round = past_rtts_.back().round; + + // Try to add the sample to the last round. + if (last_round == round_count_ && !past_rtts_.empty()) { + past_rtts_.back().sum_of_rtts_ms += rtt_sample_ms; + past_rtts_.back().num_samples++; + } else { + // If the sample belongs to a new round, keep number of rounds in the window + // equal to |kPastRttsFilterSize|. + if (past_rtts_.size() == kPastRttsFilterSize) + past_rtts_.pop_front(); + past_rtts_.push_back( + BbrBweSender::AverageRtt(rtt_sample_ms, 1, round_count_)); + } +} + void BbrBweSender::GiveFeedback(const FeedbackPacket& feedback) { + int64_t now_ms = clock_->TimeInMilliseconds(); + last_packet_ack_time_ = now_ms; const BbrBweFeedback& fb = static_cast(feedback); // feedback_vector holds values of acknowledged packets' sequence numbers. const std::vector& feedback_vector = fb.packet_feedback_vector(); - // Check if new round started for the connection. Round is the period of time - // from sending packet to its acknowledgement. + // Go through all the packets acked, update variables/containers accordingly. + for (uint16_t sequence_number : feedback_vector) { + // Completing packet information with a recently received ack. + PacketStats* packet = &packet_stats_[sequence_number]; + bytes_acked_ += packet->payload_size_bytes; + packet->data_sent_bytes = bytes_sent_; + packet->last_sent_packet_send_time_ms = last_packet_send_time_; + packet->data_acked_bytes = bytes_acked_; + packet->ack_time_ms = now_ms; + // Logic specific to applying "bucket" to high gain, in order to have + // quicker ramp-up. We check if we started receiving acks for the packets + // sent during high gain phase. + if (packet->sequence_number == first_packet_seq_num_during_high_gain_) { + first_packet_ack_time_during_high_gain_ms_ = now_ms; + // Substracting half of the packet's size to avoid overestimation. + data_acked_before_high_gain_started_bytes_ = + bytes_acked_ - packet->payload_size_bytes / 2; + } + // If the last packet of high gain phase has been acked, high gain phase is + // over. + if (packet->sequence_number == last_packet_seq_num_during_high_gain_) { + last_packet_ack_time_during_high_gain_ms_ = now_ms; + data_acked_before_high_gain_ended_bytes_ = + bytes_acked_ - packet->payload_size_bytes / 2; + high_gain_over_ = true; + } + // Notify pacer that an ack was received, to adjust data inflight. + // TODO(gnish): Add implementation for BitrateObserver class, to notify + // pacer about incoming acks. + congestion_window_->AckReceived(packet->payload_size_bytes); + HandleLoss(last_packet_acked_sequence_number_, packet->sequence_number); + last_packet_acked_sequence_number_ = packet->sequence_number; + // Logic for wrapping sequence numbers. If round started with packet number + // x, it can never end on y, if x > y. That could happen when sequence + // numbers are wrapped after some point. + if (packet->sequence_number == 0) + round_trip_end_ = 0; + } + // Check if new round started for the connection. bool new_round_started = false; if (!feedback_vector.empty()) { - uint64_t last_acked_packet = *feedback_vector.rbegin(); - if (last_acked_packet > round_trip_end_) { + if (last_packet_acked_sequence_number_ > round_trip_end_) { new_round_started = true; round_count_++; - round_trip_end_ = last_packet_sent_; + round_trip_end_ = last_packet_sent_sequence_number_; } } + bool min_rtt_expired = false; + min_rtt_expired = + UpdateBandwidthAndMinRtt(now_ms, feedback_vector, bytes_acked_); if (new_round_started && !full_bandwidth_reached_) { full_bandwidth_reached_ = max_bandwidth_filter_->FullBandwidthReached( kStartupGrowthTarget, kMaxRoundsWithoutGrowth); } - int now_ms = clock_->TimeInMilliseconds(); switch (mode_) { break; case STARTUP: @@ -111,12 +215,25 @@ void BbrBweSender::GiveFeedback(const FeedbackPacket& feedback) { TryUpdatingCyclePhase(now_ms); break; case PROBE_RTT: - TryExitingProbeRtt(now_ms, 0); + TryExitingProbeRtt(now_ms, round_count_); + break; + case RECOVERY: + TryExitingRecovery(new_round_started); break; } TryEnteringProbeRtt(now_ms); - // TODO(gnish): implement functions updating congestion window and pacing rate - // controllers. + TryEnteringRecovery(new_round_started); // Comment this line to disable + // entering Recovery mode. + for (uint64_t f : feedback_vector) + AddToPastRtts(packet_stats_[f].ack_time_ms - packet_stats_[f].send_time_ms); + CalculatePacingRate(); + // Make sure we don't get stuck when pacing_rate is 0, because of simulation + // tool specifics. + if (!pacing_rate_bps_) + pacing_rate_bps_ = 100; + BWE_TEST_LOGGING_PLOT(1, "SendRate", now_ms, pacing_rate_bps_ / 1000); + // TODO(gnish): Add implementation for BitrateObserver class to update pacing + // rate for the pacer and the encoder. } size_t BbrBweSender::TargetCongestionWindow(float gain) { @@ -127,8 +244,82 @@ size_t BbrBweSender::TargetCongestionWindow(float gain) { return target_congestion_window; } -bool BbrBweSender::UpdateBandwidthAndMinRtt() { - return false; +rtc::Optional BbrBweSender::CalculateBandwidthSample( + size_t data_sent_bytes, + int64_t send_time_delta_ms, + size_t data_acked_bytes, + int64_t ack_time_delta_ms) { + rtc::Optional bandwidth_sample; + if (send_time_delta_ms > 0) + *bandwidth_sample = data_sent_bytes * 8000 / send_time_delta_ms; + rtc::Optional ack_rate; + if (ack_time_delta_ms > 0) + *ack_rate = data_acked_bytes * 8000 / ack_time_delta_ms; + // If send rate couldn't be calculated automaticaly set |bandwidth_sample| to + // ack_rate. + if (!bandwidth_sample) + bandwidth_sample = ack_rate; + if (bandwidth_sample && ack_rate) + *bandwidth_sample = std::min(*bandwidth_sample, *ack_rate); + return bandwidth_sample; +} + +void BbrBweSender::AddSampleForHighGain() { + if (!high_gain_over_) + return; + high_gain_over_ = false; + // Calculate data sent/acked and time elapsed only for packets sent during + // high gain phase. + size_t data_sent_bytes = data_sent_before_high_gain_ended_bytes_ - + data_sent_before_high_gain_started_bytes_; + int64_t send_time_delta_ms = last_packet_send_time_during_high_gain_ms_ - + *first_packet_send_time_during_high_gain_ms_; + size_t data_acked_bytes = data_acked_before_high_gain_ended_bytes_ - + data_acked_before_high_gain_started_bytes_; + int64_t ack_time_delta_ms = last_packet_ack_time_during_high_gain_ms_ - + first_packet_ack_time_during_high_gain_ms_; + rtc::Optional bandwidth_sample = CalculateBandwidthSample( + data_sent_bytes, send_time_delta_ms, data_acked_bytes, ack_time_delta_ms); + if (bandwidth_sample) + max_bandwidth_filter_->AddBandwidthSample(*bandwidth_sample, round_count_); + first_packet_send_time_during_high_gain_ms_.reset(); +} + +bool BbrBweSender::UpdateBandwidthAndMinRtt( + int64_t now_ms, + const std::vector& feedback_vector, + int64_t bytes_acked) { + rtc::Optional min_rtt_sample_ms; + for (uint64_t f : feedback_vector) { + PacketStats packet = packet_stats_[f]; + size_t data_sent_bytes = + packet.data_sent_bytes - packet.data_sent_before_last_sent_packet_bytes; + int64_t send_time_delta_ms = + packet.last_sent_packet_send_time_ms - packet.send_time_ms; + size_t data_acked_bytes = packet.data_acked_bytes - + packet.data_acked_before_last_acked_packet_bytes; + int64_t ack_time_delta_ms = + packet.ack_time_ms - packet.last_acked_packet_ack_time_ms; + rtc::Optional bandwidth_sample = + CalculateBandwidthSample(data_sent_bytes, send_time_delta_ms, + data_acked_bytes, ack_time_delta_ms); + if (bandwidth_sample) + max_bandwidth_filter_->AddBandwidthSample(*bandwidth_sample, + round_count_); + AddSampleForHighGain(); // Comment to disable bucket for high gain. + if (!min_rtt_sample_ms) + *min_rtt_sample_ms = packet.ack_time_ms - packet.send_time_ms; + else + *min_rtt_sample_ms = std::min(*min_rtt_sample_ms, + packet.ack_time_ms - packet.send_time_ms); + BWE_TEST_LOGGING_PLOT(1, "MinRtt", now_ms, + packet.ack_time_ms - packet.send_time_ms); + } + if (!min_rtt_sample_ms) + return false; + min_rtt_filter_->AddRttSample(*min_rtt_sample_ms, now_ms); + bool min_rtt_expired = min_rtt_filter_->MinRttExpired(now_ms); + return min_rtt_expired; } void BbrBweSender::EnterStartup() { @@ -174,12 +365,13 @@ void BbrBweSender::TryUpdatingCyclePhase(int64_t now_ms) { // If BBR was probing and it couldn't increase data inflight sufficiently in // one min_rtt time, continue probing. BBR design doc isn't clear about this, // but condition helps in quicker ramp-up and performs better. - if (pacing_gain_ > 1.0 && - prior_in_flight_ < TargetCongestionWindow(pacing_gain_)) + if (pacing_gain_ > 1.0 && congestion_window_->data_inflight() < + TargetCongestionWindow(pacing_gain_)) advance_cycle_phase = false; // If BBR has already drained queues there is no point in continuing draining // phase. - if (pacing_gain_ < 1.0 && prior_in_flight_ <= TargetCongestionWindow(1)) + if (pacing_gain_ < 1.0 && + congestion_window_->data_inflight() <= TargetCongestionWindow(1)) advance_cycle_phase = true; if (advance_cycle_phase) { cycle_index_++; @@ -190,8 +382,7 @@ void BbrBweSender::TryUpdatingCyclePhase(int64_t now_ms) { } void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) { - if (min_rtt_filter_->min_rtt_expired(now_ms, kMinRttFilterSizeMs) && - mode_ != PROBE_RTT) { + if (min_rtt_filter_->MinRttExpired(now_ms) && mode_ != PROBE_RTT) { mode_ = PROBE_RTT; pacing_gain_ = 1; probe_rtt_start_time_ms_ = now_ms; @@ -199,9 +390,10 @@ void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) { } } -// minimum_congestion_window_start_time_'s value is set to the first moment when -// data inflight was less then kMinimumCongestionWindowBytes, we should make -// sure that BBR has been in PROBE_RTT mode for at least one round or 200ms. +// |minimum_congestion_window_start_time_|'s value is set to the first moment +// when data inflight was less then +// |CongestionWindow::kMinimumCongestionWindowBytes|, we should make sure that +// BBR has been in PROBE_RTT mode for at least one round or 200ms. void BbrBweSender::TryExitingProbeRtt(int64_t now_ms, int64_t round) { if (!minimum_congestion_window_start_time_ms_) { if (congestion_window_->data_inflight() <= @@ -218,13 +410,74 @@ void BbrBweSender::TryExitingProbeRtt(int64_t now_ms, int64_t round) { } } +void BbrBweSender::TryEnteringRecovery(bool new_round_started) { + // If we are already in Recovery don't try to enter. + if (mode_ == RECOVERY || !new_round_started || !full_bandwidth_reached_) + return; + uint64_t increased_rtt_round_counter = 0; + // If average RTT for past |kPastRttsFilterSize| rounds has been more than + // some multiplier of min_rtt_ms enter Recovery. + for (BbrBweSender::AverageRtt i : past_rtts_) { + if (i.sum_of_rtts_ms / (int64_t)i.num_samples >= + *min_rtt_filter_->min_rtt_ms() * kRttIncreaseThreshold) + increased_rtt_round_counter++; + } + if (increased_rtt_round_counter < kPastRttsFilterSize) + return; + mode_ = RECOVERY; + pacing_gain_ = kRecoveryPacingGain; + congestion_window_gain_ = kRecoveryCongestionWindowGain; +} + +void BbrBweSender::TryExitingRecovery(bool new_round_started) { + if (mode_ != RECOVERY || !new_round_started || !full_bandwidth_reached_) + return; + // If average RTT for the past round has decreased sufficiently exit Recovery. + if (!past_rtts_.empty()) { + BbrBweSender::AverageRtt last_round_sample = past_rtts_.back(); + if (last_round_sample.sum_of_rtts_ms / last_round_sample.num_samples <= + *min_rtt_filter_->min_rtt_ms() * kRttDecreaseThreshold) { + EnterProbeBw(clock_->TimeInMilliseconds()); + } + } +} + int64_t BbrBweSender::TimeUntilNextProcess() { - return 100; + return 50; } void BbrBweSender::OnPacketsSent(const Packets& packets) { - last_packet_sent_ = - static_cast(packets.back())->sequence_number(); + for (Packet* packet : packets) { + if (packet->GetPacketType() == Packet::kMedia) { + MediaPacket* media_packet = static_cast(packet); + bytes_sent_ += media_packet->payload_size(); + PacketStats packet_stats = PacketStats( + media_packet->sequence_number(), 0, + media_packet->sender_timestamp_ms(), 0, last_packet_ack_time_, + media_packet->payload_size(), 0, bytes_sent_, 0, bytes_acked_); + packet_stats_[media_packet->sequence_number()] = packet_stats; + last_packet_send_time_ = media_packet->sender_timestamp_ms(); + last_packet_sent_sequence_number_ = media_packet->sequence_number(); + // If this is the first packet sent for high gain phase, save data for it. + if (!first_packet_send_time_during_high_gain_ms_ && pacing_gain_ > 1) { + *first_packet_send_time_during_high_gain_ms_ = last_packet_send_time_; + data_sent_before_high_gain_started_bytes_ = + bytes_sent_ - media_packet->payload_size() / 2; + first_packet_seq_num_during_high_gain_ = + media_packet->sequence_number(); + } + // This condition ensures that |last_packet_seq_num_during_high_gain_| + // will contain a sequence number of the last packet sent during high gain + // phase. + if (pacing_gain_ > 1) { + last_packet_send_time_during_high_gain_ms_ = last_packet_send_time_; + data_sent_before_high_gain_ended_bytes_ = + bytes_sent_ - media_packet->payload_size() / 2; + last_packet_seq_num_during_high_gain_ = media_packet->sequence_number(); + } + congestion_window_->PacketSent(media_packet->payload_size()); + } + } } void BbrBweSender::Process() {} diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h index 2782f353d0..63dc78b123 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h @@ -12,11 +12,13 @@ #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_BBR_H_ #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_BBR_H_ +#include #include #include #include #include "webrtc/modules/remote_bitrate_estimator/test/bwe.h" +#include "webrtc/modules/video_coding/sequence_number_util.h" #include "webrtc/rtc_base/optional.h" #include "webrtc/rtc_base/random.h" @@ -39,15 +41,69 @@ class BbrBweSender : public BweSender { PROBE_BW, // Temporarily limiting congestion window size in order to measure // minimum RTT. - PROBE_RTT + PROBE_RTT, + // Temporarily reducing pacing rate and congestion window, in order to + // ensure no queues are built. + RECOVERY }; + struct PacketStats { PacketStats() {} - PacketStats(int64_t send_time_, size_t payload_size_) - : send_time(send_time_), payload_size(payload_size_) {} - - int64_t send_time; - size_t payload_size; + PacketStats(uint16_t sequence_number_, + int64_t last_sent_packet_send_time_ms_, + int64_t send_time_ms_, + int64_t ack_time_ms_, + int64_t last_acked_packet_ack_time_ms_, + size_t payload_size_bytes_, + size_t data_sent_bytes_, + size_t data_sent_before_last_sent_packet_bytes_, + size_t data_acked_bytes_, + size_t data_acked_before_last_acked_packet_bytes_) + : sequence_number(sequence_number_), + last_sent_packet_send_time_ms(last_sent_packet_send_time_ms_), + send_time_ms(send_time_ms_), + ack_time_ms(ack_time_ms_), + last_acked_packet_ack_time_ms(last_acked_packet_ack_time_ms_), + payload_size_bytes(payload_size_bytes_), + data_sent_bytes(data_sent_bytes_), + data_sent_before_last_sent_packet_bytes( + data_sent_before_last_sent_packet_bytes_), + data_acked_bytes(data_acked_bytes_), + data_acked_before_last_acked_packet_bytes( + data_acked_before_last_acked_packet_bytes_) {} + // Sequence number of this packet. + uint16_t sequence_number; + // Send time of the last sent packet at ack time of this packet. + int64_t last_sent_packet_send_time_ms; + // Send time of this packet. + int64_t send_time_ms; + // Ack time of this packet. + int64_t ack_time_ms; + // Ack time of the last acked packet at send time of this packet. + int64_t last_acked_packet_ack_time_ms; + // Payload size of this packet. + size_t payload_size_bytes; + // Amount of data sent before this packet was sent. + size_t data_sent_bytes; + // Amount of data sent, before last sent packet. + size_t data_sent_before_last_sent_packet_bytes; + // Amount of data acked, before this packet was acked. + size_t data_acked_bytes; + // Amount of data acked, before last acked packet. + size_t data_acked_before_last_acked_packet_bytes; + }; + struct AverageRtt { + AverageRtt() {} + AverageRtt(int64_t sum_of_rtts_ms_, int64_t num_samples_, uint64_t round_) + : sum_of_rtts_ms(sum_of_rtts_ms_), + num_samples(num_samples_), + round(round_) {} + // Sum of RTTs over the round. + int64_t sum_of_rtts_ms; + // Number of RTT samples over the round. + int64_t num_samples; + // The number of the round average RTT is recorded for. + uint64_t round; }; void OnPacketsSent(const Packets& packets) override; int GetFeedbackIntervalMs() const override; @@ -57,14 +113,38 @@ class BbrBweSender : public BweSender { private: void EnterStartup(); - bool UpdateBandwidthAndMinRtt(); + bool UpdateBandwidthAndMinRtt(int64_t now_ms, + const std::vector& feedback_vector, + int64_t bytes_acked); void TryExitingStartup(); void TryExitingDrain(int64_t now_ms); void EnterProbeBw(int64_t now_ms); void TryUpdatingCyclePhase(int64_t now_ms); void TryEnteringProbeRtt(int64_t now_ms); void TryExitingProbeRtt(int64_t now_ms, int64_t round); + void TryEnteringRecovery(bool new_round_started); + void TryExitingRecovery(bool new_round_started); size_t TargetCongestionWindow(float gain); + void CalculatePacingRate(); + + // Calculates and returns bandwidth sample as minimum between send rate and + // ack rate, returns nothing if sample cannot be calculated. + rtc::Optional CalculateBandwidthSample(size_t data_sent, + int64_t send_time_delta_ms, + size_t data_acked, + int64_t ack_time_delta_ms); + + // Calculate and add bandwidth sample only for packets' sent during high gain + // phase. Motivation of having a seperate bucket for high gain phase is to + // achieve quicker ramp up. Slight overestimations may happen due to window + // not being as large as usual. + void AddSampleForHighGain(); + + // Declares lost packets as acked. Implements simple logic by looking at the + // gap between sequence numbers. If there is a gap between sequence numbers we + // declare those packets as lost immediately. + void HandleLoss(uint64_t last_acked_packet, uint64_t recently_acked_packet); + void AddToPastRtts(int64_t rtt_sample_ms); Clock* const clock_; Mode mode_; std::unique_ptr max_bandwidth_filter_; @@ -72,7 +152,6 @@ class BbrBweSender : public BweSender { std::unique_ptr congestion_window_; std::unique_ptr rand_; uint64_t round_count_; - uint64_t last_packet_sent_; uint64_t round_trip_end_; float pacing_gain_; float congestion_window_gain_; @@ -87,9 +166,7 @@ class BbrBweSender : public BweSender { // Index number of the currently used gain value in PROBE_BW mode, from 0 to // kGainCycleLength - 1. int64_t cycle_index_; - - // Data inflight prior to the moment when last feedback was received. - size_t prior_in_flight_; + size_t bytes_acked_; // Time we entered PROBE_RTT mode. int64_t probe_rtt_start_time_ms_; @@ -101,6 +178,48 @@ class BbrBweSender : public BweSender { // First round when data inflight decreased below kMinimumCongestionWindow in // PROBE_RTT mode. int64_t minimum_congestion_window_start_round_; + size_t bytes_sent_; + uint16_t last_packet_sent_sequence_number_; + uint16_t last_packet_acked_sequence_number_; + int64_t last_packet_ack_time_; + int64_t last_packet_send_time_; + int64_t pacing_rate_bps_; + + // Send time of a packet sent first during high gain phase. Also serves as a + // flag, holding value means that we are already in high gain. + rtc::Optional first_packet_send_time_during_high_gain_ms_; + + // Send time of a packet sent last during high gain phase. + int64_t last_packet_send_time_during_high_gain_ms_; + + // Amount of data sent, before first packet was sent during high gain phase. + int64_t data_sent_before_high_gain_started_bytes_; + + // Amount of data sent, before last packet was sent during high gain phase. + int64_t data_sent_before_high_gain_ended_bytes_; + + // Ack time of a packet acked first during high gain phase. + int64_t first_packet_ack_time_during_high_gain_ms_; + + // Ack time of a packet acked last during high gain phase. + int64_t last_packet_ack_time_during_high_gain_ms_; + + // Amount of data acked, before the first packet was acked during high gain + // phase. + int64_t data_acked_before_high_gain_started_bytes_; + + // Amount of data acked, before the last packet was acked during high gain + // phase. + int64_t data_acked_before_high_gain_ended_bytes_; + + // Sequence number of the first packet sent during high gain phase. + uint16_t first_packet_seq_num_during_high_gain_; + + // Sequence number of the last packet sent during high gain phase. + uint16_t last_packet_seq_num_during_high_gain_; + bool high_gain_over_; + std::map packet_stats_; + std::list past_rtts_; }; class BbrBweReceiver : public BweReceiver { @@ -113,6 +232,7 @@ class BbrBweReceiver : public BweReceiver { private: SimulatedClock clock_; + std::vector packet_feedbacks_; }; } // namespace bwe } // namespace testing diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.cc b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.cc index 708846b3dc..3a7e0dd062 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.cc @@ -14,6 +14,9 @@ namespace webrtc { namespace testing { namespace bwe { + +const size_t MaxBandwidthFilter::kBandwidthWindowFilterSize; + MaxBandwidthFilter::MaxBandwidthFilter() : bandwidth_last_round_bytes_per_ms_(0), max_bandwidth_estimate_bps_(0), @@ -23,12 +26,11 @@ MaxBandwidthFilter::~MaxBandwidthFilter() {} // For detailed explanation about implementing bandwidth filter this way visit // "Bbr design" doc. |sample_bps| was measured during |round|. -void MaxBandwidthFilter::AddBandwidthSample(int64_t sample_bps, - int64_t round, - size_t filter_size_round) { +void MaxBandwidthFilter::AddBandwidthSample(int64_t sample_bps, int64_t round) { if (bandwidth_samples_[0].first == 0 || sample_bps >= bandwidth_samples_[0].first || - round - bandwidth_samples_[2].second >= filter_size_round) + round - bandwidth_samples_[2].second >= + MaxBandwidthFilter::kBandwidthWindowFilterSize) bandwidth_samples_[0] = bandwidth_samples_[1] = bandwidth_samples_[2] = {sample_bps, round}; if (sample_bps >= bandwidth_samples_[1].first) { @@ -38,11 +40,13 @@ void MaxBandwidthFilter::AddBandwidthSample(int64_t sample_bps, if (sample_bps >= bandwidth_samples_[2].first) bandwidth_samples_[2] = {sample_bps, round}; } - if (round - bandwidth_samples_[0].second >= filter_size_round) { + if (round - bandwidth_samples_[0].second >= + MaxBandwidthFilter::kBandwidthWindowFilterSize) { bandwidth_samples_[0] = bandwidth_samples_[1]; bandwidth_samples_[1] = bandwidth_samples_[2]; bandwidth_samples_[2] = {sample_bps, round}; - if (round - bandwidth_samples_[0].second >= filter_size_round) { + if (round - bandwidth_samples_[0].second >= + MaxBandwidthFilter::kBandwidthWindowFilterSize) { bandwidth_samples_[0] = bandwidth_samples_[1]; bandwidth_samples_[1] = bandwidth_samples_[2]; } @@ -50,13 +54,15 @@ void MaxBandwidthFilter::AddBandwidthSample(int64_t sample_bps, return; } if (bandwidth_samples_[1].first == bandwidth_samples_[0].first && - round - bandwidth_samples_[1].second > filter_size_round / 4) { + round - bandwidth_samples_[1].second > + MaxBandwidthFilter::kBandwidthWindowFilterSize / 4) { bandwidth_samples_[2] = bandwidth_samples_[1] = {sample_bps, round}; max_bandwidth_estimate_bps_ = bandwidth_samples_[0].first; return; } if (bandwidth_samples_[2].first == bandwidth_samples_[1].first && - round - bandwidth_samples_[2].second > filter_size_round / 2) + round - bandwidth_samples_[2].second > + MaxBandwidthFilter::kBandwidthWindowFilterSize / 2) bandwidth_samples_[2] = {sample_bps, round}; max_bandwidth_estimate_bps_ = bandwidth_samples_[0].first; } diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h index 6bb320f55a..15d06760a6 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h @@ -28,13 +28,15 @@ namespace testing { namespace bwe { class MaxBandwidthFilter { public: - MaxBandwidthFilter(); + // Number of rounds for bandwidth estimate to expire. + static const size_t kBandwidthWindowFilterSize = 10; + MaxBandwidthFilter(); ~MaxBandwidthFilter(); int64_t max_bandwidth_estimate_bps() { return max_bandwidth_estimate_bps_; } // Adds bandwidth sample to the bandwidth filter. - void AddBandwidthSample(int64_t sample, int64_t round, size_t filter_size); + void AddBandwidthSample(int64_t sample, int64_t round); // Checks if bandwidth has grown by certain multiplier for past x rounds, // to decide whether or full bandwidth was reached. diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter_unittest.cc b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter_unittest.cc index 7f97f2a33d..6c13fd1b51 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter_unittest.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter_unittest.cc @@ -22,61 +22,61 @@ TEST(MaxBandwidthFilterTest, InitializationCheck) { TEST(MaxBandwidthFilterTest, AddOneBandwidthSample) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(13, 4, 10); + max_bandwidth_filter.AddBandwidthSample(13, 4); EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 13); } TEST(MaxBandwidthFilterTest, AddSeveralBandwidthSamples) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(10, 5, 10); - max_bandwidth_filter.AddBandwidthSample(13, 6, 10); + max_bandwidth_filter.AddBandwidthSample(10, 5); + max_bandwidth_filter.AddBandwidthSample(13, 6); EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 13); } TEST(MaxBandwidthFilterTest, FirstSampleTimeOut) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(13, 5, 10); - max_bandwidth_filter.AddBandwidthSample(10, 15, 10); + max_bandwidth_filter.AddBandwidthSample(13, 5); + max_bandwidth_filter.AddBandwidthSample(10, 15); EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 10); } TEST(MaxBandwidthFilterTest, SecondSampleBecomesTheFirst) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(4, 5, 10); - max_bandwidth_filter.AddBandwidthSample(3, 10, 10); - max_bandwidth_filter.AddBandwidthSample(2, 15, 10); + max_bandwidth_filter.AddBandwidthSample(4, 5); + max_bandwidth_filter.AddBandwidthSample(3, 10); + max_bandwidth_filter.AddBandwidthSample(2, 15); EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 3); } TEST(MaxBandwidthFilterTest, ThirdSampleBecomesTheFirst) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(4, 5, 10); - max_bandwidth_filter.AddBandwidthSample(3, 10, 10); - max_bandwidth_filter.AddBandwidthSample(2, 25, 10); + max_bandwidth_filter.AddBandwidthSample(4, 5); + max_bandwidth_filter.AddBandwidthSample(3, 10); + max_bandwidth_filter.AddBandwidthSample(2, 25); EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 2); } TEST(MaxBandwidthFilterTest, FullBandwidthReached) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(100, 1, 10); + max_bandwidth_filter.AddBandwidthSample(100, 1); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(110, 2, 10); + max_bandwidth_filter.AddBandwidthSample(110, 2); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(120, 3, 10); + max_bandwidth_filter.AddBandwidthSample(120, 3); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(124, 4, 10); + max_bandwidth_filter.AddBandwidthSample(124, 4); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), true); } TEST(MaxBandwidthFilterTest, FullBandwidthNotReached) { MaxBandwidthFilter max_bandwidth_filter; - max_bandwidth_filter.AddBandwidthSample(100, 1, 10); + max_bandwidth_filter.AddBandwidthSample(100, 1); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(110, 2, 10); + max_bandwidth_filter.AddBandwidthSample(110, 2); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(120, 3, 10); + max_bandwidth_filter.AddBandwidthSample(120, 3); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); - max_bandwidth_filter.AddBandwidthSample(125, 4, 10); + max_bandwidth_filter.AddBandwidthSample(125, 4); EXPECT_EQ(max_bandwidth_filter.FullBandwidthReached(1.25f, 3), false); } } // namespace bwe diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter.h b/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter.h index 8c9823c035..b4932e51ca 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter.h +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter.h @@ -20,14 +20,19 @@ namespace webrtc { namespace testing { namespace bwe { + +// Expiration time for min_rtt sample, which is set to 10 seconds according to +// BBR design doc. +const int64_t kMinRttFilterSizeMs = 10000; + class MinRttFilter { public: MinRttFilter() {} ~MinRttFilter() {} rtc::Optional min_rtt_ms() { return min_rtt_ms_; } - void add_rtt_sample(int64_t rtt_ms, int64_t now_ms) { - if (!min_rtt_ms_ || rtt_ms <= *min_rtt_ms_) { + void AddRttSample(int64_t rtt_ms, int64_t now_ms) { + if (!min_rtt_ms_ || rtt_ms <= *min_rtt_ms_ || MinRttExpired(now_ms)) { min_rtt_ms_.emplace(rtt_ms); discovery_time_ms_ = now_ms; } @@ -36,8 +41,8 @@ class MinRttFilter { // Checks whether or not last discovered min_rtt value is older than x // milliseconds. - bool min_rtt_expired(int64_t now_ms, int64_t min_rtt_filter_window_size_ms) { - return now_ms - discovery_time_ms_ >= min_rtt_filter_window_size_ms; + bool MinRttExpired(int64_t now_ms) { + return now_ms - discovery_time_ms_ >= kMinRttFilterSizeMs; } private: diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter_unittest.cc b/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter_unittest.cc index 2b7d5b59b8..a8e76e127e 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter_unittest.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter_unittest.cc @@ -23,20 +23,23 @@ TEST(MinRttFilterTest, InitializationCheck) { TEST(MinRttFilterTest, AddRttSample) { MinRttFilter min_rtt_filter; - min_rtt_filter.add_rtt_sample(120, 5); + min_rtt_filter.AddRttSample(120, 5); EXPECT_EQ(min_rtt_filter.min_rtt_ms(), 120); EXPECT_EQ(min_rtt_filter.discovery_time(), 5); - min_rtt_filter.add_rtt_sample(121, 6); + min_rtt_filter.AddRttSample(121, 6); EXPECT_EQ(min_rtt_filter.discovery_time(), 5); - min_rtt_filter.add_rtt_sample(119, 7); + min_rtt_filter.AddRttSample(119, 7); EXPECT_EQ(min_rtt_filter.discovery_time(), 7); + min_rtt_filter.AddRttSample(140, 10007); + EXPECT_EQ(min_rtt_filter.discovery_time(), 10007); + EXPECT_EQ(min_rtt_filter.min_rtt_ms(), 140); } TEST(MinRttFilterTest, MinRttExpired) { MinRttFilter min_rtt_filter; - min_rtt_filter.add_rtt_sample(120, 5); - EXPECT_EQ(min_rtt_filter.min_rtt_expired(10, 5), true); - EXPECT_EQ(min_rtt_filter.min_rtt_expired(9, 5), false); + min_rtt_filter.AddRttSample(120, 5); + EXPECT_EQ(min_rtt_filter.MinRttExpired(10006), true); + EXPECT_EQ(min_rtt_filter.MinRttExpired(10), false); } } // namespace bwe } // namespace testing