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}
This commit is contained in:
parent
2ee076dfa3
commit
53d76c6190
@ -12,6 +12,7 @@
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/estimators/bbr.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#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<uint16_t>(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<const BbrBweFeedback&>(feedback);
|
||||
// feedback_vector holds values of acknowledged packets' sequence numbers.
|
||||
const std::vector<uint64_t>& 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<int64_t> 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<int64_t> bandwidth_sample;
|
||||
if (send_time_delta_ms > 0)
|
||||
*bandwidth_sample = data_sent_bytes * 8000 / send_time_delta_ms;
|
||||
rtc::Optional<int64_t> 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<int64_t> 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<uint64_t>& feedback_vector,
|
||||
int64_t bytes_acked) {
|
||||
rtc::Optional<int64_t> 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<int64_t> 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<const MediaPacket*>(packets.back())->sequence_number();
|
||||
for (Packet* packet : packets) {
|
||||
if (packet->GetPacketType() == Packet::kMedia) {
|
||||
MediaPacket* media_packet = static_cast<MediaPacket*>(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() {}
|
||||
|
||||
@ -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 <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<uint64_t>& 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<int64_t> 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<MaxBandwidthFilter> max_bandwidth_filter_;
|
||||
@ -72,7 +152,6 @@ class BbrBweSender : public BweSender {
|
||||
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_;
|
||||
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<int64_t> 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<int64_t, PacketStats> packet_stats_;
|
||||
std::list<AverageRtt> past_rtts_;
|
||||
};
|
||||
|
||||
class BbrBweReceiver : public BweReceiver {
|
||||
@ -113,6 +232,7 @@ class BbrBweReceiver : public BweReceiver {
|
||||
|
||||
private:
|
||||
SimulatedClock clock_;
|
||||
std::vector<uint64_t> packet_feedbacks_;
|
||||
};
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<int64_t> 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:
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user