Added implementations for entering/exiting STARTUP, DRAIN, PROBE_BW, PROBE_RTT modes, also updated MaxBandwidthFilter class, with the filter implementation which stores three best estimates for the filter window.

BUG=webrtc:7713

Review-Url: https://codereview.webrtc.org/2982233002
Cr-Commit-Position: refs/heads/master@{#19196}
This commit is contained in:
gnish 2017-08-01 03:06:17 -07:00 committed by Commit Bot
parent 773be36bd6
commit d339dbc7d4
8 changed files with 255 additions and 68 deletions

View File

@ -11,13 +11,17 @@
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h"
#include <stdlib.h>
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/congestion_window.h"
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/max_bandwidth_filter.h"
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/min_rtt_filter.h"
namespace webrtc {
namespace testing {
namespace bwe {
namespace {
const int kFeedbackIntervalsMs = 100;
const int kFeedbackIntervalsMs = 3;
// BBR uses this value to double sending rate each round trip. Design document
// suggests using this value.
const float kHighGain = 2.885f;
@ -28,6 +32,23 @@ const float kDrainGain = 1 / kHighGain;
// experiments, according to the design document.
const float kStartupGrowthTarget = 1.25f;
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.
const int kProbeRttDurationRounds = 1;
// The least amount of milliseconds PROBE_RTT mode should last.
const int kProbeRttDurationMs = 200;
// Gain value for congestion window for assuming that network has no queues.
const float kTargetCongestionWindowGain = 1;
// Gain value for congestion window in PROBE_BW mode. In theory it should be
// equal to 1, but in practice because of delayed acks and the way networks
// 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;
} // namespace
BbrBweSender::BbrBweSender(Clock* clock)
@ -35,10 +56,19 @@ BbrBweSender::BbrBweSender(Clock* clock)
clock_(clock),
mode_(STARTUP),
max_bandwidth_filter_(new MaxBandwidthFilter()),
min_rtt_filter_(new MinRttFilter()),
congestion_window_(new CongestionWindow()),
rand_(new Random(time(NULL))),
round_count_(0),
last_packet_sent_(0),
round_trip_end_(0),
full_bandwidth_reached_(false) {
full_bandwidth_reached_(false),
cycle_start_time_ms_(0),
cycle_index_(0),
prior_in_flight_(0),
probe_rtt_start_time_ms_(0),
minimum_congestion_window_start_time_ms_(),
minimum_congestion_window_start_round_(0) {
// Initially enter Startup mode.
EnterStartup();
}
@ -81,7 +111,7 @@ void BbrBweSender::GiveFeedback(const FeedbackPacket& feedback) {
TryUpdatingCyclePhase(now_ms);
break;
case PROBE_RTT:
TryExitingProbeRtt(now_ms);
TryExitingProbeRtt(now_ms, 0);
break;
}
TryEnteringProbeRtt(now_ms);
@ -89,6 +119,14 @@ void BbrBweSender::GiveFeedback(const FeedbackPacket& feedback) {
// controllers.
}
size_t BbrBweSender::TargetCongestionWindow(float gain) {
size_t target_congestion_window =
congestion_window_->GetTargetCongestionWindow(
max_bandwidth_filter_->max_bandwidth_estimate_bps(),
min_rtt_filter_->min_rtt_ms(), gain);
return target_congestion_window;
}
bool BbrBweSender::UpdateBandwidthAndMinRtt() {
return false;
}
@ -107,14 +145,78 @@ void BbrBweSender::TryExitingStartup() {
}
}
void BbrBweSender::TryExitingDrain(int64_t now_ms) {}
void BbrBweSender::TryExitingDrain(int64_t now_ms) {
if (congestion_window_->data_inflight() <=
TargetCongestionWindow(kTargetCongestionWindowGain))
EnterProbeBw(now_ms);
}
void BbrBweSender::EnterProbeBw(int64_t now_ms) {}
// Start probing with a random gain value, which is different form 0.75,
// starting with 0.75 doesn't offer any benefits as there are no queues to be
// drained.
void BbrBweSender::EnterProbeBw(int64_t now_ms) {
mode_ = PROBE_BW;
congestion_window_gain_ = kCruisingCongestionWindowGain;
int index = rand_->Rand(kGainCycleLength - 2);
if (index == 1)
index = kGainCycleLength - 1;
pacing_gain_ = kPacingGain[index];
cycle_start_time_ms_ = now_ms;
cycle_index_ = index;
}
void BbrBweSender::TryUpdatingCyclePhase(int64_t now_ms) {}
void BbrBweSender::TryUpdatingCyclePhase(int64_t now_ms) {
// Each phase should last rougly min_rtt ms time.
bool advance_cycle_phase = false;
if (min_rtt_filter_->min_rtt_ms())
advance_cycle_phase =
now_ms - cycle_start_time_ms_ > *min_rtt_filter_->min_rtt_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_))
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))
advance_cycle_phase = true;
if (advance_cycle_phase) {
cycle_index_++;
cycle_index_ %= kGainCycleLength;
pacing_gain_ = kPacingGain[cycle_index_];
cycle_start_time_ms_ = now_ms;
}
}
void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) {}
void BbrBweSender::TryExitingProbeRtt(int64_t now_ms) {}
void BbrBweSender::TryEnteringProbeRtt(int64_t now_ms) {
if (min_rtt_filter_->min_rtt_expired(now_ms, kMinRttFilterSizeMs) &&
mode_ != PROBE_RTT) {
mode_ = PROBE_RTT;
pacing_gain_ = 1;
probe_rtt_start_time_ms_ = now_ms;
minimum_congestion_window_start_time_ms_.reset();
}
}
// 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.
void BbrBweSender::TryExitingProbeRtt(int64_t now_ms, int64_t round) {
if (!minimum_congestion_window_start_time_ms_) {
if (congestion_window_->data_inflight() <=
CongestionWindow::kMinimumCongestionWindowBytes) {
*minimum_congestion_window_start_time_ms_ = now_ms;
minimum_congestion_window_start_round_ = round;
}
} else {
if (now_ms - *minimum_congestion_window_start_time_ms_ >=
kProbeRttDurationMs &&
round - minimum_congestion_window_start_round_ >=
kProbeRttDurationRounds)
EnterProbeBw(now_ms);
}
}
int64_t BbrBweSender::TimeUntilNextProcess() {
return 100;

View File

@ -17,6 +17,8 @@
#include <vector>
#include "webrtc/modules/remote_bitrate_estimator/test/bwe.h"
#include "webrtc/rtc_base/optional.h"
#include "webrtc/rtc_base/random.h"
namespace webrtc {
namespace testing {
@ -59,13 +61,16 @@ class BbrBweSender : public BweSender {
void TryExitingStartup();
void TryExitingDrain(int64_t now_ms);
void EnterProbeBw(int64_t now_ms);
void EnterProbeRtt(int64_t now_ms);
void TryUpdatingCyclePhase(int64_t now_ms);
void TryEnteringProbeRtt(int64_t now_ms);
void TryExitingProbeRtt(int64_t now_ms);
void TryExitingProbeRtt(int64_t now_ms, int64_t round);
size_t TargetCongestionWindow(float gain);
Clock* const clock_;
Mode mode_;
std::unique_ptr<MaxBandwidthFilter> max_bandwidth_filter_;
std::unique_ptr<MinRttFilter> min_rtt_filter_;
std::unique_ptr<CongestionWindow> congestion_window_;
std::unique_ptr<Random> rand_;
uint64_t round_count_;
uint64_t last_packet_sent_;
uint64_t round_trip_end_;
@ -75,6 +80,27 @@ class BbrBweSender : public BweSender {
// If optimal bandwidth has been discovered and reached, (for example after
// Startup mode) set this variable to true.
bool full_bandwidth_reached_;
// Entering time for PROBE_BW mode's cycle phase.
int64_t cycle_start_time_ms_;
// 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_;
// Time we entered PROBE_RTT mode.
int64_t probe_rtt_start_time_ms_;
// First moment of time when data inflight decreased below
// kMinimumCongestionWindow in PROBE_RTT mode.
rtc::Optional<int64_t> minimum_congestion_window_start_time_ms_;
// First round when data inflight decreased below kMinimumCongestionWindow in
// PROBE_RTT mode.
int64_t minimum_congestion_window_start_round_;
};
class BbrBweReceiver : public BweReceiver {

View File

@ -19,29 +19,27 @@ namespace webrtc {
namespace testing {
namespace bwe {
namespace {
// kStartingCongestionWindow is used to set congestion window when bandwidth
// delay product is equal to zero, so that we don't set window to zero as well.
// Chosen randomly by me, because this value shouldn't make any significant
// difference, as bandwidth delay product is more than zero almost every time.
const int kStartingCongestionWindow = 6000;
// Size of congestion window while in PROBE_RTT mode, suggested by BBR's source
// code of QUIC's implementation.
const int kMinimumCongestionWindow = 5840;
// kStartingCongestionWindowBytes is used to set congestion window when
// bandwidth delay product is equal to zero, so that we don't set window to zero
// as well. Chosen randomly by me, because this value shouldn't make any
// significant difference, as bandwidth delay product is more than zero almost
// every time.
const int kStartingCongestionWindowBytes = 6000;
} // namespace
const int CongestionWindow::kMinimumCongestionWindowBytes;
CongestionWindow::CongestionWindow() : data_inflight_bytes_(0) {}
CongestionWindow::~CongestionWindow() {}
int CongestionWindow::GetCongestionWindow(
BbrBweSender::Mode mode,
int64_t bandwidth_estimate_bytes_per_ms,
int64_t min_rtt_ms,
float gain) {
int CongestionWindow::GetCongestionWindow(BbrBweSender::Mode mode,
int64_t bandwidth_estimate_bps,
rtc::Optional<int64_t> min_rtt_ms,
float gain) {
if (mode == BbrBweSender::PROBE_RTT)
return kMinimumCongestionWindow;
return GetTargetCongestionWindow(bandwidth_estimate_bytes_per_ms, min_rtt_ms,
gain);
return CongestionWindow::kMinimumCongestionWindowBytes;
return GetTargetCongestionWindow(bandwidth_estimate_bps, min_rtt_ms, gain);
}
void CongestionWindow::PacketSent(size_t sent_packet_size_bytes) {
@ -53,16 +51,20 @@ void CongestionWindow::AckReceived(size_t received_packet_size_bytes) {
}
int CongestionWindow::GetTargetCongestionWindow(
int64_t bandwidth_estimate_bytes_per_ms,
int64_t min_rtt_ms,
int64_t bandwidth_estimate_bps,
rtc::Optional<int64_t> min_rtt_ms,
float gain) {
int bdp = min_rtt_ms * bandwidth_estimate_bytes_per_ms;
// If we have no rtt sample yet, return the starting congestion window size.
if (!min_rtt_ms)
return gain * kStartingCongestionWindowBytes;
int bdp = *min_rtt_ms * bandwidth_estimate_bps;
int congestion_window = bdp * gain;
// Congestion window could be zero in rare cases, when either no bandwidth
// estimate is available, or path's min_rtt value is zero.
if (!congestion_window)
congestion_window = gain * kStartingCongestionWindow;
return std::max(congestion_window, kMinimumCongestionWindow);
congestion_window = gain * kStartingCongestionWindowBytes;
return std::max(congestion_window,
CongestionWindow::kMinimumCongestionWindowBytes);
}
} // namespace bwe
} // namespace testing

View File

@ -14,19 +14,25 @@
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h"
#include "webrtc/rtc_base/optional.h"
namespace webrtc {
namespace testing {
namespace bwe {
class CongestionWindow {
public:
// Size of congestion window while in PROBE_RTT mode, suggested by BBR's
// source code of QUIC's implementation.
static const int kMinimumCongestionWindowBytes = 4000;
CongestionWindow();
~CongestionWindow();
int GetCongestionWindow(BbrBweSender::Mode mode,
int64_t bandwidth_estimate,
int64_t min_rtt,
rtc::Optional<int64_t> min_rtt,
float gain);
int GetTargetCongestionWindow(int64_t bandwidth_estimate,
int64_t min_rtt,
rtc::Optional<int64_t> min_rtt,
float gain);
// Packet sent from sender, meaning it is inflight until we receive it and we
// should add packet's size to data_inflight.

View File

@ -19,7 +19,7 @@ namespace bwe {
namespace {
// These are the same values used in CongestionWindow class.
const int64_t kStartingCongestionWindow = 6000;
const int64_t kMinimumCongestionWindow = 5840;
const int64_t kMinimumCongestionWindow = 4000;
} // namespace
TEST(CongestionWindowTest, InitializationCheck) {
@ -46,35 +46,38 @@ TEST(CongestionWindowTest, DataInflight) {
TEST(CongestionWindowTest, ZeroBandwidthDelayProduct) {
CongestionWindow congestion_window;
int64_t target_congestion_window =
congestion_window.GetTargetCongestionWindow(100, 0, 2.885f);
congestion_window.GetTargetCongestionWindow(
100, rtc::Optional<int64_t>(0), 2.885f);
EXPECT_EQ(target_congestion_window, 2.885f * kStartingCongestionWindow);
}
TEST(CongestionWindowTest, BelowMinimumTargetCongestionWindow) {
CongestionWindow congestion_window;
int64_t target_congestion_window =
congestion_window.GetTargetCongestionWindow(100, 2, 2.885f);
congestion_window.GetTargetCongestionWindow(
100, rtc::Optional<int64_t>(2), 2.885f);
EXPECT_EQ(target_congestion_window, kMinimumCongestionWindow);
}
TEST(CongestionWindowTest, AboveMinimumTargetCongestionWindow) {
CongestionWindow congestion_window;
int64_t target_congestion_window =
congestion_window.GetTargetCongestionWindow(100000, 2, 2.885f);
congestion_window.GetTargetCongestionWindow(
100000, rtc::Optional<int64_t>(2), 2.885f);
EXPECT_EQ(target_congestion_window, 577000);
}
TEST(CongestionWindowTest, MinimumCongestionWindow) {
CongestionWindow congestion_window;
int64_t cwnd = congestion_window.GetCongestionWindow(BbrBweSender::PROBE_RTT,
100, 100, 2.885f);
int64_t cwnd = congestion_window.GetCongestionWindow(
BbrBweSender::PROBE_RTT, 100, rtc::Optional<int64_t>(100), 2.885f);
EXPECT_EQ(cwnd, kMinimumCongestionWindow);
}
TEST(CongestionWindowTest, CalculateCongestionWindow) {
CongestionWindow congestion_window;
int64_t cwnd = congestion_window.GetCongestionWindow(BbrBweSender::STARTUP,
100, 100, 2.885f);
int64_t cwnd = congestion_window.GetCongestionWindow(
BbrBweSender::STARTUP, 100, rtc::Optional<int64_t>(100l), 2.885f);
EXPECT_EQ(cwnd, 28850);
}
} // namespace bwe

View File

@ -16,22 +16,49 @@ namespace testing {
namespace bwe {
MaxBandwidthFilter::MaxBandwidthFilter()
: bandwidth_last_round_bytes_per_ms_(0),
round_bandwidth_updated_(0),
max_bandwidth_estimate_bytes_per_ms_(0),
max_bandwidth_estimate_bps_(0),
rounds_without_growth_(0) {}
MaxBandwidthFilter::~MaxBandwidthFilter() {}
// Rounds are units for packets rtt_time, after packet has been acknowledged,
// one round has passed from its send time.
void MaxBandwidthFilter::AddBandwidthSample(int64_t sample_bytes_per_ms,
// 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) {
if (round - round_bandwidth_updated_ >= filter_size_round ||
sample_bytes_per_ms >= max_bandwidth_estimate_bytes_per_ms_) {
max_bandwidth_estimate_bytes_per_ms_ = sample_bytes_per_ms;
round_bandwidth_updated_ = round;
if (bandwidth_samples_[0].first == 0 ||
sample_bps >= bandwidth_samples_[0].first ||
round - bandwidth_samples_[2].second >= filter_size_round)
bandwidth_samples_[0] = bandwidth_samples_[1] =
bandwidth_samples_[2] = {sample_bps, round};
if (sample_bps >= bandwidth_samples_[1].first) {
bandwidth_samples_[1] = {sample_bps, round};
bandwidth_samples_[2] = bandwidth_samples_[1];
} else {
if (sample_bps >= bandwidth_samples_[2].first)
bandwidth_samples_[2] = {sample_bps, round};
}
if (round - bandwidth_samples_[0].second >= filter_size_round) {
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) {
bandwidth_samples_[0] = bandwidth_samples_[1];
bandwidth_samples_[1] = bandwidth_samples_[2];
}
max_bandwidth_estimate_bps_ = bandwidth_samples_[0].first;
return;
}
if (bandwidth_samples_[1].first == bandwidth_samples_[0].first &&
round - bandwidth_samples_[1].second > filter_size_round / 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)
bandwidth_samples_[2] = {sample_bps, round};
max_bandwidth_estimate_bps_ = bandwidth_samples_[0].first;
}
bool MaxBandwidthFilter::FullBandwidthReached(float growth_target,
@ -40,8 +67,8 @@ bool MaxBandwidthFilter::FullBandwidthReached(float growth_target,
// found and full bandwidth is not reached.
int64_t minimal_bandwidth =
bandwidth_last_round_bytes_per_ms_ * growth_target;
if (max_bandwidth_estimate_bytes_per_ms_ >= minimal_bandwidth) {
bandwidth_last_round_bytes_per_ms_ = max_bandwidth_estimate_bytes_per_ms_;
if (max_bandwidth_estimate_bps_ >= minimal_bandwidth) {
bandwidth_last_round_bytes_per_ms_ = max_bandwidth_estimate_bps_;
rounds_without_growth_ = 0;
return false;
}

View File

@ -12,8 +12,16 @@
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_MAX_BANDWIDTH_FILTER_H_
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_ESTIMATORS_MAX_BANDWIDTH_FILTER_H_
#include <cstddef>
#include <cstdint>
#include <climits>
#include <list>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h"
#include "webrtc/modules/remote_bitrate_estimator/test/bwe.h"
namespace webrtc {
namespace testing {
@ -23,23 +31,20 @@ class MaxBandwidthFilter {
MaxBandwidthFilter();
~MaxBandwidthFilter();
int64_t max_bandwidth_estimate_bytes_per_ms() {
return max_bandwidth_estimate_bytes_per_ms_;
}
int64_t max_bandwidth_estimate_bps() { return max_bandwidth_estimate_bps_; }
// Save bandwidth sample for the current round. We save bandwidth samples for
// past 10 rounds to provide better bandwidth estimate.
// Adds bandwidth sample to the bandwidth filter.
void AddBandwidthSample(int64_t sample, int64_t round, size_t filter_size);
// Check if bandwidth has grown by certain multiplier for past x rounds,
// to decide whether or not full bandwidth was reached.
// Checks if bandwidth has grown by certain multiplier for past x rounds,
// to decide whether or full bandwidth was reached.
bool FullBandwidthReached(float growth_target, int max_rounds_without_growth);
private:
int64_t bandwidth_last_round_bytes_per_ms_;
uint64_t round_bandwidth_updated_;
int64_t max_bandwidth_estimate_bytes_per_ms_;
int64_t max_bandwidth_estimate_bps_;
int64_t rounds_without_growth_;
std::pair<int64_t, size_t> bandwidth_samples_[3];
};
} // namespace bwe
} // namespace testing

View File

@ -17,27 +17,43 @@ namespace testing {
namespace bwe {
TEST(MaxBandwidthFilterTest, InitializationCheck) {
MaxBandwidthFilter max_bandwidth_filter;
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bytes_per_ms(), 0);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 0);
}
TEST(MaxBandwidthFilterTest, AddOneBandwidthSample) {
MaxBandwidthFilter max_bandwidth_filter;
max_bandwidth_filter.AddBandwidthSample(13, 4, 10);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bytes_per_ms(), 13);
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);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bytes_per_ms(), 13);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 13);
}
TEST(MaxBandwidthFilterTest, SampleTimeOut) {
TEST(MaxBandwidthFilterTest, FirstSampleTimeOut) {
MaxBandwidthFilter max_bandwidth_filter;
max_bandwidth_filter.AddBandwidthSample(13, 5, 10);
max_bandwidth_filter.AddBandwidthSample(10, 15, 10);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bytes_per_ms(), 10);
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);
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);
EXPECT_EQ(max_bandwidth_filter.max_bandwidth_estimate_bps(), 2);
}
TEST(MaxBandwidthFilterTest, FullBandwidthReached) {