Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,7 @@
danilchap@webrtc.org
linderborg@webrtc.org
stefan@webrtc.org
terelius@webrtc.org
mflodman@webrtc.org
yinwa@webrtc.org
perkj@webrtc.org

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
#include "rtc_base/checks.h"
namespace webrtc {
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
const FieldTrialsView* key_value_config)
: AcknowledgedBitrateEstimator(
key_value_config,
std::make_unique<BitrateEstimator>(key_value_config)) {}
AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
const FieldTrialsView* key_value_config,
std::unique_ptr<BitrateEstimator> bitrate_estimator)
: in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {}
void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) {
RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
packet_feedback_vector.end(),
PacketResult::ReceiveTimeOrder()));
for (const auto& packet : packet_feedback_vector) {
if (alr_ended_time_ && packet.sent_packet.send_time > *alr_ended_time_) {
bitrate_estimator_->ExpectFastRateChange();
alr_ended_time_.reset();
}
DataSize acknowledged_estimate = packet.sent_packet.size;
acknowledged_estimate += packet.sent_packet.prior_unacked_data;
bitrate_estimator_->Update(packet.receive_time, acknowledged_estimate,
in_alr_);
}
}
absl::optional<DataRate> AcknowledgedBitrateEstimator::bitrate() const {
return bitrate_estimator_->bitrate();
}
absl::optional<DataRate> AcknowledgedBitrateEstimator::PeekRate() const {
return bitrate_estimator_->PeekRate();
}
void AcknowledgedBitrateEstimator::SetAlrEndedTime(Timestamp alr_ended_time) {
alr_ended_time_.emplace(alr_ended_time);
}
void AcknowledgedBitrateEstimator::SetAlr(bool in_alr) {
in_alr_ = in_alr;
}
} // namespace webrtc

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
namespace webrtc {
class AcknowledgedBitrateEstimator
: public AcknowledgedBitrateEstimatorInterface {
public:
AcknowledgedBitrateEstimator(
const FieldTrialsView* key_value_config,
std::unique_ptr<BitrateEstimator> bitrate_estimator);
explicit AcknowledgedBitrateEstimator(
const FieldTrialsView* key_value_config);
~AcknowledgedBitrateEstimator() override;
void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) override;
absl::optional<DataRate> bitrate() const override;
absl::optional<DataRate> PeekRate() const override;
void SetAlr(bool in_alr) override;
void SetAlrEndedTime(Timestamp alr_ended_time) override;
private:
absl::optional<Timestamp> alr_ended_time_;
bool in_alr_;
std::unique_ptr<BitrateEstimator> bitrate_estimator_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include <algorithm>
#include <memory>
#include "api/field_trials_view.h"
#include "api/units/time_delta.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/logging.h"
namespace webrtc {
constexpr char RobustThroughputEstimatorSettings::kKey[];
RobustThroughputEstimatorSettings::RobustThroughputEstimatorSettings(
const FieldTrialsView* key_value_config) {
Parser()->Parse(
key_value_config->Lookup(RobustThroughputEstimatorSettings::kKey));
if (window_packets < 10 || 1000 < window_packets) {
RTC_LOG(LS_WARNING) << "Window size must be between 10 and 1000 packets";
window_packets = 20;
}
if (max_window_packets < 10 || 1000 < max_window_packets) {
RTC_LOG(LS_WARNING)
<< "Max window size must be between 10 and 1000 packets";
max_window_packets = 500;
}
max_window_packets = std::max(max_window_packets, window_packets);
if (required_packets < 10 || 1000 < required_packets) {
RTC_LOG(LS_WARNING) << "Required number of initial packets must be between "
"10 and 1000 packets";
required_packets = 10;
}
required_packets = std::min(required_packets, window_packets);
if (min_window_duration < TimeDelta::Millis(100) ||
TimeDelta::Millis(3000) < min_window_duration) {
RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 3000 ms";
min_window_duration = TimeDelta::Millis(750);
}
if (max_window_duration < TimeDelta::Seconds(1) ||
TimeDelta::Seconds(15) < max_window_duration) {
RTC_LOG(LS_WARNING) << "Max window duration must be between 1 and 15 s";
max_window_duration = TimeDelta::Seconds(5);
}
min_window_duration = std::min(min_window_duration, max_window_duration);
if (unacked_weight < 0.0 || 1.0 < unacked_weight) {
RTC_LOG(LS_WARNING)
<< "Weight for prior unacked size must be between 0 and 1.";
unacked_weight = 1.0;
}
}
std::unique_ptr<StructParametersParser>
RobustThroughputEstimatorSettings::Parser() {
return StructParametersParser::Create(
"enabled", &enabled, //
"window_packets", &window_packets, //
"max_window_packets", &max_window_packets, //
"window_duration", &min_window_duration, //
"max_window_duration", &max_window_duration, //
"required_packets", &required_packets, //
"unacked_weight", &unacked_weight);
}
AcknowledgedBitrateEstimatorInterface::
~AcknowledgedBitrateEstimatorInterface() {}
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
AcknowledgedBitrateEstimatorInterface::Create(
const FieldTrialsView* key_value_config) {
RobustThroughputEstimatorSettings simplified_estimator_settings(
key_value_config);
if (simplified_estimator_settings.enabled) {
return std::make_unique<RobustThroughputEstimator>(
simplified_estimator_settings);
}
return std::make_unique<AcknowledgedBitrateEstimator>(key_value_config);
}
} // namespace webrtc

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
namespace webrtc {
struct RobustThroughputEstimatorSettings {
static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings";
RobustThroughputEstimatorSettings() = delete;
explicit RobustThroughputEstimatorSettings(
const FieldTrialsView* key_value_config);
// Set `enabled` to true to use the RobustThroughputEstimator, false to use
// the AcknowledgedBitrateEstimator.
bool enabled = true;
// The estimator keeps the smallest window containing at least
// `window_packets` and at least the packets received during the last
// `min_window_duration` milliseconds.
// (This means that it may store more than `window_packets` at high bitrates,
// and a longer duration than `min_window_duration` at low bitrates.)
// However, if will never store more than kMaxPackets (for performance
// reasons), and never longer than max_window_duration (to avoid very old
// packets influencing the estimate for example when sending is paused).
unsigned window_packets = 20;
unsigned max_window_packets = 500;
TimeDelta min_window_duration = TimeDelta::Seconds(1);
TimeDelta max_window_duration = TimeDelta::Seconds(5);
// The estimator window requires at least `required_packets` packets
// to produce an estimate.
unsigned required_packets = 10;
// If audio packets aren't included in allocation (i.e. the
// estimated available bandwidth is divided only among the video
// streams), then `unacked_weight` should be set to 0.
// If audio packets are included in allocation, but not in bandwidth
// estimation (i.e. they don't have transport-wide sequence numbers,
// but we nevertheless divide the estimated available bandwidth among
// both audio and video streams), then `unacked_weight` should be set to 1.
// If all packets have transport-wide sequence numbers, then the value
// of `unacked_weight` doesn't matter.
double unacked_weight = 1.0;
std::unique_ptr<StructParametersParser> Parser();
};
class AcknowledgedBitrateEstimatorInterface {
public:
static std::unique_ptr<AcknowledgedBitrateEstimatorInterface> Create(
const FieldTrialsView* key_value_config);
virtual ~AcknowledgedBitrateEstimatorInterface();
virtual void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) = 0;
virtual absl::optional<DataRate> bitrate() const = 0;
virtual absl::optional<DataRate> PeekRate() const = 0;
virtual void SetAlr(bool in_alr) = 0;
virtual void SetAlrEndedTime(Timestamp alr_ended_time) = 0;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/alr_detector.h"
#include <cstdint>
#include <cstdio>
#include <memory>
#include "api/rtc_event_log/rtc_event.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "logging/rtc_event_log/events/rtc_event_alr_state.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
namespace {
AlrDetectorConfig GetConfigFromTrials(const FieldTrialsView* key_value_config) {
RTC_CHECK(AlrExperimentSettings::MaxOneFieldTrialEnabled(*key_value_config));
absl::optional<AlrExperimentSettings> experiment_settings =
AlrExperimentSettings::CreateFromFieldTrial(
*key_value_config,
AlrExperimentSettings::kScreenshareProbingBweExperimentName);
if (!experiment_settings) {
experiment_settings = AlrExperimentSettings::CreateFromFieldTrial(
*key_value_config,
AlrExperimentSettings::kStrictPacingAndProbingExperimentName);
}
AlrDetectorConfig conf;
if (experiment_settings) {
conf.bandwidth_usage_ratio =
experiment_settings->alr_bandwidth_usage_percent / 100.0;
conf.start_budget_level_ratio =
experiment_settings->alr_start_budget_level_percent / 100.0;
conf.stop_budget_level_ratio =
experiment_settings->alr_stop_budget_level_percent / 100.0;
}
conf.Parser()->Parse(
key_value_config->Lookup("WebRTC-AlrDetectorParameters"));
return conf;
}
} // namespace
std::unique_ptr<StructParametersParser> AlrDetectorConfig::Parser() {
return StructParametersParser::Create( //
"bw_usage", &bandwidth_usage_ratio, //
"start", &start_budget_level_ratio, //
"stop", &stop_budget_level_ratio);
}
AlrDetector::AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log)
: conf_(config), alr_budget_(0, true), event_log_(event_log) {}
AlrDetector::AlrDetector(const FieldTrialsView* key_value_config)
: AlrDetector(GetConfigFromTrials(key_value_config), nullptr) {}
AlrDetector::AlrDetector(const FieldTrialsView* key_value_config,
RtcEventLog* event_log)
: AlrDetector(GetConfigFromTrials(key_value_config), event_log) {}
AlrDetector::~AlrDetector() {}
void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) {
if (!last_send_time_ms_.has_value()) {
last_send_time_ms_ = send_time_ms;
// Since the duration for sending the bytes is unknwon, return without
// updating alr state.
return;
}
int64_t delta_time_ms = send_time_ms - *last_send_time_ms_;
last_send_time_ms_ = send_time_ms;
alr_budget_.UseBudget(bytes_sent);
alr_budget_.IncreaseBudget(delta_time_ms);
bool state_changed = false;
if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio &&
!alr_started_time_ms_) {
alr_started_time_ms_.emplace(rtc::TimeMillis());
state_changed = true;
} else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio &&
alr_started_time_ms_) {
state_changed = true;
alr_started_time_ms_.reset();
}
if (event_log_ && state_changed) {
event_log_->Log(
std::make_unique<RtcEventAlrState>(alr_started_time_ms_.has_value()));
}
}
void AlrDetector::SetEstimatedBitrate(int bitrate_bps) {
RTC_DCHECK(bitrate_bps);
int target_rate_kbps =
static_cast<double>(bitrate_bps) * conf_.bandwidth_usage_ratio / 1000;
alr_budget_.set_target_rate_kbps(target_rate_kbps);
}
absl::optional<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
const {
return alr_started_time_ms_;
}
} // namespace webrtc

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "modules/pacing/interval_budget.h"
#include "rtc_base/experiments/alr_experiment.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
namespace webrtc {
class RtcEventLog;
struct AlrDetectorConfig {
// Sent traffic ratio as a function of network capacity used to determine
// application-limited region. ALR region start when bandwidth usage drops
// below kAlrStartUsageRatio and ends when it raises above
// kAlrEndUsageRatio. NOTE: This is intentionally conservative at the moment
// until BW adjustments of application limited region is fine tuned.
double bandwidth_usage_ratio = 0.65;
double start_budget_level_ratio = 0.80;
double stop_budget_level_ratio = 0.50;
std::unique_ptr<StructParametersParser> Parser();
};
// Application limited region detector is a class that utilizes signals of
// elapsed time and bytes sent to estimate whether network traffic is
// currently limited by the application's ability to generate traffic.
//
// AlrDetector provides a signal that can be utilized to adjust
// estimate bandwidth.
// Note: This class is not thread-safe.
class AlrDetector {
public:
AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log);
explicit AlrDetector(const FieldTrialsView* key_value_config);
AlrDetector(const FieldTrialsView* key_value_config, RtcEventLog* event_log);
~AlrDetector();
void OnBytesSent(size_t bytes_sent, int64_t send_time_ms);
// Set current estimated bandwidth.
void SetEstimatedBitrate(int bitrate_bps);
// Returns time in milliseconds when the current application-limited region
// started or empty result if the sender is currently not application-limited.
absl::optional<int64_t> GetApplicationLimitedRegionStartTime() const;
private:
friend class GoogCcStatePrinter;
const AlrDetectorConfig conf_;
absl::optional<int64_t> last_send_time_ms_;
IntervalBudget alr_budget_;
absl::optional<int64_t> alr_started_time_ms_;
RtcEventLog* event_log_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_

View file

@ -0,0 +1,170 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/bitrate_estimator.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
namespace {
constexpr int kInitialRateWindowMs = 500;
constexpr int kRateWindowMs = 150;
constexpr int kMinRateWindowMs = 150;
constexpr int kMaxRateWindowMs = 1000;
const char kBweThroughputWindowConfig[] = "WebRTC-BweThroughputWindowConfig";
} // namespace
BitrateEstimator::BitrateEstimator(const FieldTrialsView* key_value_config)
: sum_(0),
initial_window_ms_("initial_window_ms",
kInitialRateWindowMs,
kMinRateWindowMs,
kMaxRateWindowMs),
noninitial_window_ms_("window_ms",
kRateWindowMs,
kMinRateWindowMs,
kMaxRateWindowMs),
uncertainty_scale_("scale", 10.0),
uncertainty_scale_in_alr_("scale_alr", uncertainty_scale_),
small_sample_uncertainty_scale_("scale_small", uncertainty_scale_),
small_sample_threshold_("small_thresh", DataSize::Zero()),
uncertainty_symmetry_cap_("symmetry_cap", DataRate::Zero()),
estimate_floor_("floor", DataRate::Zero()),
current_window_ms_(0),
prev_time_ms_(-1),
bitrate_estimate_kbps_(-1.0f),
bitrate_estimate_var_(50.0f) {
// E.g WebRTC-BweThroughputWindowConfig/initial_window_ms:350,window_ms:250/
ParseFieldTrial(
{&initial_window_ms_, &noninitial_window_ms_, &uncertainty_scale_,
&uncertainty_scale_in_alr_, &small_sample_uncertainty_scale_,
&small_sample_threshold_, &uncertainty_symmetry_cap_, &estimate_floor_},
key_value_config->Lookup(kBweThroughputWindowConfig));
}
BitrateEstimator::~BitrateEstimator() = default;
void BitrateEstimator::Update(Timestamp at_time, DataSize amount, bool in_alr) {
int rate_window_ms = noninitial_window_ms_.Get();
// We use a larger window at the beginning to get a more stable sample that
// we can use to initialize the estimate.
if (bitrate_estimate_kbps_ < 0.f)
rate_window_ms = initial_window_ms_.Get();
bool is_small_sample = false;
float bitrate_sample_kbps = UpdateWindow(at_time.ms(), amount.bytes(),
rate_window_ms, &is_small_sample);
if (bitrate_sample_kbps < 0.0f)
return;
if (bitrate_estimate_kbps_ < 0.0f) {
// This is the very first sample we get. Use it to initialize the estimate.
bitrate_estimate_kbps_ = bitrate_sample_kbps;
return;
}
// Optionally use higher uncertainty for very small samples to avoid dropping
// estimate and for samples obtained in ALR.
float scale = uncertainty_scale_;
if (is_small_sample && bitrate_sample_kbps < bitrate_estimate_kbps_) {
scale = small_sample_uncertainty_scale_;
} else if (in_alr && bitrate_sample_kbps < bitrate_estimate_kbps_) {
// Optionally use higher uncertainty for samples obtained during ALR.
scale = uncertainty_scale_in_alr_;
}
// Define the sample uncertainty as a function of how far away it is from the
// current estimate. With low values of uncertainty_symmetry_cap_ we add more
// uncertainty to increases than to decreases. For higher values we approach
// symmetry.
float sample_uncertainty =
scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) /
(bitrate_estimate_kbps_ +
std::min(bitrate_sample_kbps,
uncertainty_symmetry_cap_.Get().kbps<float>()));
float sample_var = sample_uncertainty * sample_uncertainty;
// Update a bayesian estimate of the rate, weighting it lower if the sample
// uncertainty is large.
// The bitrate estimate uncertainty is increased with each update to model
// that the bitrate changes over time.
float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f;
bitrate_estimate_kbps_ = (sample_var * bitrate_estimate_kbps_ +
pred_bitrate_estimate_var * bitrate_sample_kbps) /
(sample_var + pred_bitrate_estimate_var);
bitrate_estimate_kbps_ =
std::max(bitrate_estimate_kbps_, estimate_floor_.Get().kbps<float>());
bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
(sample_var + pred_bitrate_estimate_var);
BWE_TEST_LOGGING_PLOT(1, "acknowledged_bitrate", at_time.ms(),
bitrate_estimate_kbps_ * 1000);
}
float BitrateEstimator::UpdateWindow(int64_t now_ms,
int bytes,
int rate_window_ms,
bool* is_small_sample) {
RTC_DCHECK(is_small_sample != nullptr);
// Reset if time moves backwards.
if (now_ms < prev_time_ms_) {
prev_time_ms_ = -1;
sum_ = 0;
current_window_ms_ = 0;
}
if (prev_time_ms_ >= 0) {
current_window_ms_ += now_ms - prev_time_ms_;
// Reset if nothing has been received for more than a full window.
if (now_ms - prev_time_ms_ > rate_window_ms) {
sum_ = 0;
current_window_ms_ %= rate_window_ms;
}
}
prev_time_ms_ = now_ms;
float bitrate_sample = -1.0f;
if (current_window_ms_ >= rate_window_ms) {
*is_small_sample = sum_ < small_sample_threshold_->bytes();
bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
current_window_ms_ -= rate_window_ms;
sum_ = 0;
}
sum_ += bytes;
return bitrate_sample;
}
absl::optional<DataRate> BitrateEstimator::bitrate() const {
if (bitrate_estimate_kbps_ < 0.f)
return absl::nullopt;
return DataRate::KilobitsPerSec(bitrate_estimate_kbps_);
}
absl::optional<DataRate> BitrateEstimator::PeekRate() const {
if (current_window_ms_ > 0)
return DataSize::Bytes(sum_) / TimeDelta::Millis(current_window_ms_);
return absl::nullopt;
}
void BitrateEstimator::ExpectFastRateChange() {
// By setting the bitrate-estimate variance to a higher value we allow the
// bitrate to change fast for the next few samples.
bitrate_estimate_var_ += 200;
}
} // namespace webrtc

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
// Computes a bayesian estimate of the throughput given acks containing
// the arrival time and payload size. Samples which are far from the current
// estimate or are based on few packets are given a smaller weight, as they
// are considered to be more likely to have been caused by, e.g., delay spikes
// unrelated to congestion.
class BitrateEstimator {
public:
explicit BitrateEstimator(const FieldTrialsView* key_value_config);
virtual ~BitrateEstimator();
virtual void Update(Timestamp at_time, DataSize amount, bool in_alr);
virtual absl::optional<DataRate> bitrate() const;
absl::optional<DataRate> PeekRate() const;
virtual void ExpectFastRateChange();
private:
float UpdateWindow(int64_t now_ms,
int bytes,
int rate_window_ms,
bool* is_small_sample);
int sum_;
FieldTrialConstrained<int> initial_window_ms_;
FieldTrialConstrained<int> noninitial_window_ms_;
FieldTrialParameter<double> uncertainty_scale_;
FieldTrialParameter<double> uncertainty_scale_in_alr_;
FieldTrialParameter<double> small_sample_uncertainty_scale_;
FieldTrialParameter<DataSize> small_sample_threshold_;
FieldTrialParameter<DataRate> uncertainty_symmetry_cap_;
FieldTrialParameter<DataRate> estimate_floor_;
int64_t current_window_ms_;
int64_t prev_time_ms_;
float bitrate_estimate_kbps_;
float bitrate_estimate_var_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
#include <algorithm>
#include <cstdint>
#include "absl/strings/match.h"
#include "api/field_trials_view.h"
#include "api/units/data_size.h"
#include "rtc_base/experiments/rate_control_settings.h"
namespace webrtc {
CongestionWindowPushbackController::CongestionWindowPushbackController(
const FieldTrialsView* key_value_config)
: add_pacing_(
absl::StartsWith(key_value_config->Lookup(
"WebRTC-AddPacingToCongestionWindowPushback"),
"Enabled")),
min_pushback_target_bitrate_bps_(
RateControlSettings::ParseFromKeyValueConfig(key_value_config)
.CongestionWindowMinPushbackTargetBitrateBps()),
current_data_window_(
RateControlSettings::ParseFromKeyValueConfig(key_value_config)
.CongestionWindowInitialDataWindow()) {}
void CongestionWindowPushbackController::UpdateOutstandingData(
int64_t outstanding_bytes) {
outstanding_bytes_ = outstanding_bytes;
}
void CongestionWindowPushbackController::UpdatePacingQueue(
int64_t pacing_bytes) {
pacing_bytes_ = pacing_bytes;
}
void CongestionWindowPushbackController::SetDataWindow(DataSize data_window) {
current_data_window_ = data_window;
}
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
uint32_t bitrate_bps) {
if (!current_data_window_ || current_data_window_->IsZero())
return bitrate_bps;
int64_t total_bytes = outstanding_bytes_;
if (add_pacing_)
total_bytes += pacing_bytes_;
double fill_ratio =
total_bytes / static_cast<double>(current_data_window_->bytes());
if (fill_ratio > 1.5) {
encoding_rate_ratio_ *= 0.9;
} else if (fill_ratio > 1) {
encoding_rate_ratio_ *= 0.95;
} else if (fill_ratio < 0.1) {
encoding_rate_ratio_ = 1.0;
} else {
encoding_rate_ratio_ *= 1.05;
encoding_rate_ratio_ = std::min(encoding_rate_ratio_, 1.0);
}
uint32_t adjusted_target_bitrate_bps =
static_cast<uint32_t>(bitrate_bps * encoding_rate_ratio_);
// Do not adjust below the minimum pushback bitrate but do obey if the
// original estimate is below it.
bitrate_bps = adjusted_target_bitrate_bps < min_pushback_target_bitrate_bps_
? std::min(bitrate_bps, min_pushback_target_bitrate_bps_)
: adjusted_target_bitrate_bps;
return bitrate_bps;
}
} // namespace webrtc

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/units/data_size.h"
namespace webrtc {
// This class enables pushback from congestion window directly to video encoder.
// When the congestion window is filling up, the video encoder target bitrate
// will be reduced accordingly to accommodate the network changes. To avoid
// pausing video too frequently, a minimum encoder target bitrate threshold is
// used to prevent video pause due to a full congestion window.
class CongestionWindowPushbackController {
public:
explicit CongestionWindowPushbackController(
const FieldTrialsView* key_value_config);
void UpdateOutstandingData(int64_t outstanding_bytes);
void UpdatePacingQueue(int64_t pacing_bytes);
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
void SetDataWindow(DataSize data_window);
private:
const bool add_pacing_;
const uint32_t min_pushback_target_bitrate_bps_;
absl::optional<DataSize> current_data_window_;
int64_t outstanding_bytes_ = 0;
int64_t pacing_bytes_ = 0;
double encoding_rate_ratio_ = 1.0;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_

View file

@ -0,0 +1,314 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "modules/congestion_controller/goog_cc/inter_arrival_delta.h"
#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/race_checker.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
constexpr TimeDelta kSendTimeGroupLength = TimeDelta::Millis(5);
// This ssrc is used to fulfill the current API but will be removed
// after the API has been changed.
constexpr uint32_t kFixedSsrc = 0;
} // namespace
constexpr char BweSeparateAudioPacketsSettings::kKey[];
BweSeparateAudioPacketsSettings::BweSeparateAudioPacketsSettings(
const FieldTrialsView* key_value_config) {
Parser()->Parse(
key_value_config->Lookup(BweSeparateAudioPacketsSettings::kKey));
}
std::unique_ptr<StructParametersParser>
BweSeparateAudioPacketsSettings::Parser() {
return StructParametersParser::Create( //
"enabled", &enabled, //
"packet_threshold", &packet_threshold, //
"time_threshold", &time_threshold);
}
DelayBasedBwe::Result::Result()
: updated(false),
probe(false),
target_bitrate(DataRate::Zero()),
recovered_from_overuse(false),
delay_detector_state(BandwidthUsage::kBwNormal) {}
DelayBasedBwe::DelayBasedBwe(const FieldTrialsView* key_value_config,
RtcEventLog* event_log,
NetworkStatePredictor* network_state_predictor)
: event_log_(event_log),
key_value_config_(key_value_config),
separate_audio_(key_value_config),
audio_packets_since_last_video_(0),
last_video_packet_recv_time_(Timestamp::MinusInfinity()),
network_state_predictor_(network_state_predictor),
video_delay_detector_(
new TrendlineEstimator(key_value_config_, network_state_predictor_)),
audio_delay_detector_(
new TrendlineEstimator(key_value_config_, network_state_predictor_)),
active_delay_detector_(video_delay_detector_.get()),
last_seen_packet_(Timestamp::MinusInfinity()),
uma_recorded_(false),
rate_control_(*key_value_config, /*send_side=*/true),
prev_bitrate_(DataRate::Zero()),
prev_state_(BandwidthUsage::kBwNormal) {
RTC_LOG(LS_INFO)
<< "Initialized DelayBasedBwe with separate audio overuse detection"
<< separate_audio_.Parser()->Encode();
}
DelayBasedBwe::~DelayBasedBwe() {}
DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg,
absl::optional<DataRate> acked_bitrate,
absl::optional<DataRate> probe_bitrate,
absl::optional<NetworkStateEstimate> network_estimate,
bool in_alr) {
RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
auto packet_feedback_vector = msg.SortedByReceiveTime();
// TODO(holmer): An empty feedback vector here likely means that
// all acks were too late and that the send time history had
// timed out. We should reduce the rate when this occurs.
if (packet_feedback_vector.empty()) {
RTC_LOG(LS_WARNING) << "Very late feedback received.";
return DelayBasedBwe::Result();
}
if (!uma_recorded_) {
RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
BweNames::kSendSideTransportSeqNum,
BweNames::kBweNamesMax);
uma_recorded_ = true;
}
bool delayed_feedback = true;
bool recovered_from_overuse = false;
BandwidthUsage prev_detector_state = active_delay_detector_->State();
for (const auto& packet_feedback : packet_feedback_vector) {
delayed_feedback = false;
IncomingPacketFeedback(packet_feedback, msg.feedback_time);
if (prev_detector_state == BandwidthUsage::kBwUnderusing &&
active_delay_detector_->State() == BandwidthUsage::kBwNormal) {
recovered_from_overuse = true;
}
prev_detector_state = active_delay_detector_->State();
}
if (delayed_feedback) {
// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
// against building very large network queues.
return Result();
}
rate_control_.SetInApplicationLimitedRegion(in_alr);
rate_control_.SetNetworkStateEstimate(network_estimate);
return MaybeUpdateEstimate(acked_bitrate, probe_bitrate,
std::move(network_estimate),
recovered_from_overuse, in_alr, msg.feedback_time);
}
void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
Timestamp at_time) {
// Reset if the stream has timed out.
if (last_seen_packet_.IsInfinite() ||
at_time - last_seen_packet_ > kStreamTimeOut) {
video_inter_arrival_delta_ =
std::make_unique<InterArrivalDelta>(kSendTimeGroupLength);
audio_inter_arrival_delta_ =
std::make_unique<InterArrivalDelta>(kSendTimeGroupLength);
video_delay_detector_.reset(
new TrendlineEstimator(key_value_config_, network_state_predictor_));
audio_delay_detector_.reset(
new TrendlineEstimator(key_value_config_, network_state_predictor_));
active_delay_detector_ = video_delay_detector_.get();
}
last_seen_packet_ = at_time;
// As an alternative to ignoring small packets, we can separate audio and
// video packets for overuse detection.
DelayIncreaseDetectorInterface* delay_detector_for_packet =
video_delay_detector_.get();
if (separate_audio_.enabled) {
if (packet_feedback.sent_packet.audio) {
delay_detector_for_packet = audio_delay_detector_.get();
audio_packets_since_last_video_++;
if (audio_packets_since_last_video_ > separate_audio_.packet_threshold &&
packet_feedback.receive_time - last_video_packet_recv_time_ >
separate_audio_.time_threshold) {
active_delay_detector_ = audio_delay_detector_.get();
}
} else {
audio_packets_since_last_video_ = 0;
last_video_packet_recv_time_ =
std::max(last_video_packet_recv_time_, packet_feedback.receive_time);
active_delay_detector_ = video_delay_detector_.get();
}
}
DataSize packet_size = packet_feedback.sent_packet.size;
TimeDelta send_delta = TimeDelta::Zero();
TimeDelta recv_delta = TimeDelta::Zero();
int size_delta = 0;
InterArrivalDelta* inter_arrival_for_packet =
(separate_audio_.enabled && packet_feedback.sent_packet.audio)
? audio_inter_arrival_delta_.get()
: video_inter_arrival_delta_.get();
bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas(
packet_feedback.sent_packet.send_time, packet_feedback.receive_time,
at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta);
delay_detector_for_packet->Update(recv_delta.ms<double>(),
send_delta.ms<double>(),
packet_feedback.sent_packet.send_time.ms(),
packet_feedback.receive_time.ms(),
packet_size.bytes(), calculated_deltas);
}
DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,
absl::optional<DataRate> link_capacity) {
RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity);
return rate_control_.Update(input, at_time);
}
DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
absl::optional<DataRate> acked_bitrate,
absl::optional<DataRate> probe_bitrate,
absl::optional<NetworkStateEstimate> state_estimate,
bool recovered_from_overuse,
bool in_alr,
Timestamp at_time) {
Result result;
// Currently overusing the bandwidth.
if (active_delay_detector_->State() == BandwidthUsage::kBwOverusing) {
if (acked_bitrate &&
rate_control_.TimeToReduceFurther(at_time, *acked_bitrate)) {
result.updated =
UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
} else if (!acked_bitrate && rate_control_.ValidEstimate() &&
rate_control_.InitialTimeToReduceFurther(at_time)) {
// Overusing before we have a measured acknowledged bitrate. Reduce send
// rate by 50% every 200 ms.
// TODO(tschumim): Improve this and/or the acknowledged bitrate estimator
// so that we (almost) always have a bitrate estimate.
rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2, at_time);
result.updated = true;
result.probe = false;
result.target_bitrate = rate_control_.LatestEstimate();
}
} else {
if (probe_bitrate) {
result.probe = true;
result.updated = true;
rate_control_.SetEstimate(*probe_bitrate, at_time);
result.target_bitrate = rate_control_.LatestEstimate();
} else {
result.updated =
UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate);
result.recovered_from_overuse = recovered_from_overuse;
}
}
BandwidthUsage detector_state = active_delay_detector_->State();
if ((result.updated && prev_bitrate_ != result.target_bitrate) ||
detector_state != prev_state_) {
DataRate bitrate = result.updated ? result.target_bitrate : prev_bitrate_;
BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", at_time.ms(), bitrate.bps());
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventBweUpdateDelayBased>(
bitrate.bps(), detector_state));
}
prev_bitrate_ = bitrate;
prev_state_ = detector_state;
}
result.delay_detector_state = detector_state;
return result;
}
bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
absl::optional<DataRate> acked_bitrate,
DataRate* target_rate) {
const RateControlInput input(active_delay_detector_->State(), acked_bitrate);
*target_rate = rate_control_.Update(input, at_time);
return rate_control_.ValidEstimate();
}
void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) {
rate_control_.SetRtt(avg_rtt);
}
bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
DataRate* bitrate) const {
// Currently accessed from both the process thread (see
// ModuleRtpRtcpImpl::Process()) and the configuration thread (see
// Call::GetStats()). Should in the future only be accessed from a single
// thread.
RTC_DCHECK(ssrcs);
RTC_DCHECK(bitrate);
if (!rate_control_.ValidEstimate())
return false;
*ssrcs = {kFixedSsrc};
*bitrate = rate_control_.LatestEstimate();
return true;
}
void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) {
RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: "
<< ToString(start_bitrate);
rate_control_.SetStartBitrate(start_bitrate);
}
void DelayBasedBwe::SetMinBitrate(DataRate min_bitrate) {
// Called from both the configuration thread and the network thread. Shouldn't
// be called from the network thread in the future.
rate_control_.SetMinBitrate(min_bitrate);
}
TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const {
return rate_control_.GetExpectedBandwidthPeriod();
}
} // namespace webrtc

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "modules/congestion_controller/goog_cc/inter_arrival_delta.h"
#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h"
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"
#include "modules/remote_bitrate_estimator/inter_arrival.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/race_checker.h"
namespace webrtc {
class RtcEventLog;
struct BweSeparateAudioPacketsSettings {
static constexpr char kKey[] = "WebRTC-Bwe-SeparateAudioPackets";
BweSeparateAudioPacketsSettings() = default;
explicit BweSeparateAudioPacketsSettings(
const FieldTrialsView* key_value_config);
bool enabled = false;
int packet_threshold = 10;
TimeDelta time_threshold = TimeDelta::Seconds(1);
std::unique_ptr<StructParametersParser> Parser();
};
class DelayBasedBwe {
public:
struct Result {
Result();
~Result() = default;
bool updated;
bool probe;
DataRate target_bitrate = DataRate::Zero();
bool recovered_from_overuse;
BandwidthUsage delay_detector_state;
};
explicit DelayBasedBwe(const FieldTrialsView* key_value_config,
RtcEventLog* event_log,
NetworkStatePredictor* network_state_predictor);
DelayBasedBwe() = delete;
DelayBasedBwe(const DelayBasedBwe&) = delete;
DelayBasedBwe& operator=(const DelayBasedBwe&) = delete;
virtual ~DelayBasedBwe();
Result IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg,
absl::optional<DataRate> acked_bitrate,
absl::optional<DataRate> probe_bitrate,
absl::optional<NetworkStateEstimate> network_estimate,
bool in_alr);
void OnRttUpdate(TimeDelta avg_rtt);
bool LatestEstimate(std::vector<uint32_t>* ssrcs, DataRate* bitrate) const;
void SetStartBitrate(DataRate start_bitrate);
void SetMinBitrate(DataRate min_bitrate);
TimeDelta GetExpectedBwePeriod() const;
DataRate TriggerOveruse(Timestamp at_time,
absl::optional<DataRate> link_capacity);
DataRate last_estimate() const { return prev_bitrate_; }
BandwidthUsage last_state() const { return prev_state_; }
private:
friend class GoogCcStatePrinter;
void IncomingPacketFeedback(const PacketResult& packet_feedback,
Timestamp at_time);
Result MaybeUpdateEstimate(
absl::optional<DataRate> acked_bitrate,
absl::optional<DataRate> probe_bitrate,
absl::optional<NetworkStateEstimate> state_estimate,
bool recovered_from_overuse,
bool in_alr,
Timestamp at_time);
// Updates the current remote rate estimate and returns true if a valid
// estimate exists.
bool UpdateEstimate(Timestamp at_time,
absl::optional<DataRate> acked_bitrate,
DataRate* target_rate);
rtc::RaceChecker network_race_;
RtcEventLog* const event_log_;
const FieldTrialsView* const key_value_config_;
// Alternatively, run two separate overuse detectors for audio and video,
// and fall back to the audio one if we haven't seen a video packet in a
// while.
BweSeparateAudioPacketsSettings separate_audio_;
int64_t audio_packets_since_last_video_;
Timestamp last_video_packet_recv_time_;
NetworkStatePredictor* network_state_predictor_;
std::unique_ptr<InterArrival> video_inter_arrival_;
std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_;
std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_;
std::unique_ptr<InterArrival> audio_inter_arrival_;
std::unique_ptr<InterArrivalDelta> audio_inter_arrival_delta_;
std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_;
DelayIncreaseDetectorInterface* active_delay_detector_;
Timestamp last_seen_packet_;
bool uma_recorded_;
AimdRateControl rate_control_;
DataRate prev_bitrate_;
BandwidthUsage prev_state_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include <cstdint>
#include "api/network_state_predictor.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h"
#include "system_wrappers/include/clock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
constexpr int kNumProbesCluster0 = 5;
constexpr int kNumProbesCluster1 = 8;
const PacedPacketInfo kPacingInfo0(0, kNumProbesCluster0, 2000);
const PacedPacketInfo kPacingInfo1(1, kNumProbesCluster1, 4000);
constexpr float kTargetUtilizationFraction = 0.95f;
} // namespace
TEST_F(DelayBasedBweTest, ProbeDetection) {
int64_t now_ms = clock_.TimeInMilliseconds();
// First burst sent at 8 * 1000 / 10 = 800 kbps.
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(10);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo0);
}
EXPECT_TRUE(bitrate_observer_.updated());
// Second burst sent at 8 * 1000 / 5 = 1600 kbps.
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(5);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GT(bitrate_observer_.latest_bitrate(), 1500000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionNonPacedPackets) {
int64_t now_ms = clock_.TimeInMilliseconds();
// First burst sent at 8 * 1000 / 10 = 800 kbps, but with every other packet
// not being paced which could mess things up.
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(5);
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, now_ms, 1000, kPacingInfo0);
// Non-paced packet, arriving 5 ms after.
clock_.AdvanceTimeMilliseconds(5);
IncomingFeedback(now_ms, now_ms, 100, PacedPacketInfo());
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GT(bitrate_observer_.latest_bitrate(), 800000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionFasterArrival) {
int64_t now_ms = clock_.TimeInMilliseconds();
// First burst sent at 8 * 1000 / 10 = 800 kbps.
// Arriving at 8 * 1000 / 5 = 1600 kbps.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster0; ++i) {
clock_.AdvanceTimeMilliseconds(1);
send_time_ms += 10;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo0);
}
EXPECT_FALSE(bitrate_observer_.updated());
}
TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrival) {
int64_t now_ms = clock_.TimeInMilliseconds();
// First burst sent at 8 * 1000 / 5 = 1600 kbps.
// Arriving at 8 * 1000 / 7 = 1142 kbps.
// Since the receive rate is significantly below the send rate, we expect to
// use 95% of the estimated capacity.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(7);
send_time_ms += 5;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
kTargetUtilizationFraction * 1140000u, 10000u);
}
TEST_F(DelayBasedBweTest, ProbeDetectionSlowerArrivalHighBitrate) {
int64_t now_ms = clock_.TimeInMilliseconds();
// Burst sent at 8 * 1000 / 1 = 8000 kbps.
// Arriving at 8 * 1000 / 2 = 4000 kbps.
// Since the receive rate is significantly below the send rate, we expect to
// use 95% of the estimated capacity.
int64_t send_time_ms = 0;
for (int i = 0; i < kNumProbesCluster1; ++i) {
clock_.AdvanceTimeMilliseconds(2);
send_time_ms += 1;
now_ms = clock_.TimeInMilliseconds();
IncomingFeedback(now_ms, send_time_ms, 1000, kPacingInfo1);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(bitrate_observer_.latest_bitrate(),
kTargetUtilizationFraction * 4000000u, 10000u);
}
TEST_F(DelayBasedBweTest, GetExpectedBwePeriodMs) {
auto default_interval = bitrate_estimator_->GetExpectedBwePeriod();
EXPECT_GT(default_interval.ms(), 0);
CapacityDropTestHelper(1, true, 533, 0);
auto interval = bitrate_estimator_->GetExpectedBwePeriod();
EXPECT_GT(interval.ms(), 0);
EXPECT_NE(interval.ms(), default_interval.ms());
}
TEST_F(DelayBasedBweTest, InitialBehavior) {
InitialBehaviorTestHelper(730000);
}
TEST_F(DelayBasedBweTest, InitializeResult) {
DelayBasedBwe::Result result;
EXPECT_EQ(result.delay_detector_state, BandwidthUsage::kBwNormal);
}
TEST_F(DelayBasedBweTest, RateIncreaseReordering) {
RateIncreaseReorderingTestHelper(730000);
}
TEST_F(DelayBasedBweTest, RateIncreaseRtpTimestamps) {
RateIncreaseRtpTimestampsTestHelper(617);
}
TEST_F(DelayBasedBweTest, CapacityDropOneStream) {
CapacityDropTestHelper(1, false, 500, 0);
}
TEST_F(DelayBasedBweTest, CapacityDropPosOffsetChange) {
CapacityDropTestHelper(1, false, 867, 30000);
}
TEST_F(DelayBasedBweTest, CapacityDropNegOffsetChange) {
CapacityDropTestHelper(1, false, 933, -30000);
}
TEST_F(DelayBasedBweTest, CapacityDropOneStreamWrap) {
CapacityDropTestHelper(1, true, 533, 0);
}
TEST_F(DelayBasedBweTest, TestTimestampGrouping) {
TestTimestampGroupingTestHelper();
}
TEST_F(DelayBasedBweTest, TestShortTimeoutAndWrap) {
// Simulate a client leaving and rejoining the call after 35 seconds. This
// will make abs send time wrap, so if streams aren't timed out properly
// the next 30 seconds of packets will be out of order.
TestWrappingHelper(35);
}
TEST_F(DelayBasedBweTest, TestLongTimeoutAndWrap) {
// Simulate a client leaving and rejoining the call after some multiple of
// 64 seconds later. This will cause a zero difference in abs send times due
// to the wrap, but a big difference in arrival time, if streams aren't
// properly timed out.
TestWrappingHelper(10 * 64);
}
TEST_F(DelayBasedBweTest, TestInitialOveruse) {
const DataRate kStartBitrate = DataRate::KilobitsPerSec(300);
const DataRate kInitialCapacity = DataRate::KilobitsPerSec(200);
const uint32_t kDummySsrc = 0;
// High FPS to ensure that we send a lot of packets in a short time.
const int kFps = 90;
stream_generator_->AddStream(new test::RtpStream(kFps, kStartBitrate.bps()));
stream_generator_->set_capacity_bps(kInitialCapacity.bps());
// Needed to initialize the AimdRateControl.
bitrate_estimator_->SetStartBitrate(kStartBitrate);
// Produce 40 frames (in 1/3 second) and give them to the estimator.
int64_t bitrate_bps = kStartBitrate.bps();
bool seen_overuse = false;
for (int i = 0; i < 40; ++i) {
bool overuse = GenerateAndProcessFrame(kDummySsrc, bitrate_bps);
if (overuse) {
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_LE(bitrate_observer_.latest_bitrate(), kInitialCapacity.bps());
EXPECT_GT(bitrate_observer_.latest_bitrate(),
0.8 * kInitialCapacity.bps());
bitrate_bps = bitrate_observer_.latest_bitrate();
seen_overuse = true;
break;
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
}
EXPECT_TRUE(seen_overuse);
EXPECT_LE(bitrate_observer_.latest_bitrate(), kInitialCapacity.bps());
EXPECT_GT(bitrate_observer_.latest_bitrate(), 0.8 * kInitialCapacity.bps());
}
TEST_F(DelayBasedBweTest, TestTimestampPrecisionHandling) {
// This test does some basic checks to make sure that timestamps with higher
// than millisecond precision are handled properly and do not cause any
// problems in the estimator. Specifically, previously reported in
// webrtc:14023 and described in more details there, the rounding to the
// nearest milliseconds caused discrepancy in the accumulated delay. This lead
// to false-positive overuse detection.
// Technical details of the test:
// Send times(ms): 0.000, 9.725, 20.000, 29.725, 40.000, 49.725, ...
// Recv times(ms): 0.500, 10.000, 20.500, 30.000, 40.500, 50.000, ...
// Send deltas(ms): 9.750, 10.250, 9.750, 10.250, 9.750, ...
// Recv deltas(ms): 9.500, 10.500, 9.500, 10.500, 9.500, ...
// There is no delay building up between the send times and the receive times,
// therefore this case should never lead to an overuse detection. However, if
// the time deltas were accidentally rounded to the nearest milliseconds, then
// all the send deltas would be equal to 10ms while some recv deltas would
// round up to 11ms which would lead in a false illusion of delay build up.
uint32_t last_bitrate = bitrate_observer_.latest_bitrate();
for (int i = 0; i < 1000; ++i) {
clock_.AdvanceTimeMicroseconds(500);
IncomingFeedback(clock_.CurrentTime(),
clock_.CurrentTime() - TimeDelta::Micros(500), 1000,
PacedPacketInfo());
clock_.AdvanceTimeMicroseconds(9500);
IncomingFeedback(clock_.CurrentTime(),
clock_.CurrentTime() - TimeDelta::Micros(250), 1000,
PacedPacketInfo());
clock_.AdvanceTimeMicroseconds(10000);
// The bitrate should never decrease in this test.
EXPECT_LE(last_bitrate, bitrate_observer_.latest_bitrate());
last_bitrate = bitrate_observer_.latest_bitrate();
}
}
} // namespace webrtc

View file

@ -0,0 +1,543 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include "rtc_base/checks.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
constexpr size_t kMtu = 1200;
constexpr uint32_t kAcceptedBitrateErrorBps = 50000;
// Number of packets needed before we have a valid estimate.
constexpr int kNumInitialPackets = 2;
constexpr int kInitialProbingPackets = 5;
namespace test {
void TestBitrateObserver::OnReceiveBitrateChanged(uint32_t bitrate) {
latest_bitrate_ = bitrate;
updated_ = true;
}
RtpStream::RtpStream(int fps, int bitrate_bps)
: fps_(fps), bitrate_bps_(bitrate_bps), next_rtp_time_(0) {
RTC_CHECK_GT(fps_, 0);
}
// Generates a new frame for this stream. If called too soon after the
// previous frame, no frame will be generated. The frame is split into
// packets.
int64_t RtpStream::GenerateFrame(int64_t time_now_us,
int64_t* next_sequence_number,
std::vector<PacketResult>* packets) {
if (time_now_us < next_rtp_time_) {
return next_rtp_time_;
}
RTC_CHECK(packets != NULL);
size_t bits_per_frame = (bitrate_bps_ + fps_ / 2) / fps_;
size_t n_packets =
std::max<size_t>((bits_per_frame + 4 * kMtu) / (8 * kMtu), 1u);
size_t payload_size = (bits_per_frame + 4 * n_packets) / (8 * n_packets);
for (size_t i = 0; i < n_packets; ++i) {
PacketResult packet;
packet.sent_packet.send_time =
Timestamp::Micros(time_now_us + kSendSideOffsetUs);
packet.sent_packet.size = DataSize::Bytes(payload_size);
packet.sent_packet.sequence_number = (*next_sequence_number)++;
packets->push_back(packet);
}
next_rtp_time_ = time_now_us + (1000000 + fps_ / 2) / fps_;
return next_rtp_time_;
}
// The send-side time when the next frame can be generated.
int64_t RtpStream::next_rtp_time() const {
return next_rtp_time_;
}
void RtpStream::set_bitrate_bps(int bitrate_bps) {
ASSERT_GE(bitrate_bps, 0);
bitrate_bps_ = bitrate_bps;
}
int RtpStream::bitrate_bps() const {
return bitrate_bps_;
}
bool RtpStream::Compare(const std::unique_ptr<RtpStream>& lhs,
const std::unique_ptr<RtpStream>& rhs) {
return lhs->next_rtp_time_ < rhs->next_rtp_time_;
}
StreamGenerator::StreamGenerator(int capacity, int64_t time_now)
: capacity_(capacity), prev_arrival_time_us_(time_now) {}
StreamGenerator::~StreamGenerator() = default;
// Add a new stream.
void StreamGenerator::AddStream(RtpStream* stream) {
streams_.push_back(std::unique_ptr<RtpStream>(stream));
}
// Set the link capacity.
void StreamGenerator::set_capacity_bps(int capacity_bps) {
ASSERT_GT(capacity_bps, 0);
capacity_ = capacity_bps;
}
// Divides `bitrate_bps` among all streams. The allocated bitrate per stream
// is decided by the current allocation ratios.
void StreamGenerator::SetBitrateBps(int bitrate_bps) {
ASSERT_GE(streams_.size(), 0u);
int total_bitrate_before = 0;
for (const auto& stream : streams_) {
total_bitrate_before += stream->bitrate_bps();
}
int64_t bitrate_before = 0;
int total_bitrate_after = 0;
for (const auto& stream : streams_) {
bitrate_before += stream->bitrate_bps();
int64_t bitrate_after =
(bitrate_before * bitrate_bps + total_bitrate_before / 2) /
total_bitrate_before;
stream->set_bitrate_bps(bitrate_after - total_bitrate_after);
total_bitrate_after += stream->bitrate_bps();
}
ASSERT_EQ(bitrate_before, total_bitrate_before);
EXPECT_EQ(total_bitrate_after, bitrate_bps);
}
// TODO(holmer): Break out the channel simulation part from this class to make
// it possible to simulate different types of channels.
int64_t StreamGenerator::GenerateFrame(int64_t time_now_us,
int64_t* next_sequence_number,
std::vector<PacketResult>* packets) {
RTC_CHECK(packets != NULL);
RTC_CHECK(packets->empty());
RTC_CHECK_GT(capacity_, 0);
auto it =
std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
(*it)->GenerateFrame(time_now_us, next_sequence_number, packets);
for (PacketResult& packet : *packets) {
int capacity_bpus = capacity_ / 1000;
int64_t required_network_time_us =
(8 * 1000 * packet.sent_packet.size.bytes() + capacity_bpus / 2) /
capacity_bpus;
prev_arrival_time_us_ =
std::max(time_now_us + required_network_time_us,
prev_arrival_time_us_ + required_network_time_us);
packet.receive_time = Timestamp::Micros(prev_arrival_time_us_);
}
it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare);
return std::max((*it)->next_rtp_time(), time_now_us);
}
} // namespace test
DelayBasedBweTest::DelayBasedBweTest()
: field_trial(std::make_unique<test::ScopedFieldTrials>(
"WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:true/")),
clock_(100000000),
acknowledged_bitrate_estimator_(
AcknowledgedBitrateEstimatorInterface::Create(&field_trial_config_)),
probe_bitrate_estimator_(new ProbeBitrateEstimator(nullptr)),
bitrate_estimator_(
new DelayBasedBwe(&field_trial_config_, nullptr, nullptr)),
stream_generator_(new test::StreamGenerator(1e6, // Capacity.
clock_.TimeInMicroseconds())),
arrival_time_offset_ms_(0),
next_sequence_number_(0),
first_update_(true) {}
DelayBasedBweTest::~DelayBasedBweTest() {}
void DelayBasedBweTest::AddDefaultStream() {
stream_generator_->AddStream(new test::RtpStream(30, 3e5));
}
const uint32_t DelayBasedBweTest::kDefaultSsrc = 0;
void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
size_t payload_size) {
IncomingFeedback(arrival_time_ms, send_time_ms, payload_size,
PacedPacketInfo());
}
void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
size_t payload_size,
const PacedPacketInfo& pacing_info) {
RTC_CHECK_GE(arrival_time_ms + arrival_time_offset_ms_, 0);
IncomingFeedback(Timestamp::Millis(arrival_time_ms + arrival_time_offset_ms_),
Timestamp::Millis(send_time_ms), payload_size, pacing_info);
}
void DelayBasedBweTest::IncomingFeedback(Timestamp receive_time,
Timestamp send_time,
size_t payload_size,
const PacedPacketInfo& pacing_info) {
PacketResult packet;
packet.receive_time = receive_time;
packet.sent_packet.send_time = send_time;
packet.sent_packet.size = DataSize::Bytes(payload_size);
packet.sent_packet.pacing_info = pacing_info;
packet.sent_packet.sequence_number = next_sequence_number_++;
if (packet.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe)
probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(packet);
TransportPacketsFeedback msg;
msg.feedback_time = Timestamp::Millis(clock_.TimeInMilliseconds());
msg.packet_feedbacks.push_back(packet);
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
msg.SortedByReceiveTime());
DelayBasedBwe::Result result =
bitrate_estimator_->IncomingPacketFeedbackVector(
msg, acknowledged_bitrate_estimator_->bitrate(),
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(),
/*network_estimate*/ absl::nullopt, /*in_alr*/ false);
if (result.updated) {
bitrate_observer_.OnReceiveBitrateChanged(result.target_bitrate.bps());
}
}
// Generates a frame of packets belonging to a stream at a given bitrate and
// with a given ssrc. The stream is pushed through a very simple simulated
// network, and is then given to the receive-side bandwidth estimator.
// Returns true if an over-use was seen, false otherwise.
// The StreamGenerator::updated() should be used to check for any changes in
// target bitrate after the call to this function.
bool DelayBasedBweTest::GenerateAndProcessFrame(uint32_t ssrc,
uint32_t bitrate_bps) {
stream_generator_->SetBitrateBps(bitrate_bps);
std::vector<PacketResult> packets;
int64_t next_time_us = stream_generator_->GenerateFrame(
clock_.TimeInMicroseconds(), &next_sequence_number_, &packets);
if (packets.empty())
return false;
bool overuse = false;
bitrate_observer_.Reset();
clock_.AdvanceTimeMicroseconds(packets.back().receive_time.us() -
clock_.TimeInMicroseconds());
for (auto& packet : packets) {
RTC_CHECK_GE(packet.receive_time.ms() + arrival_time_offset_ms_, 0);
packet.receive_time += TimeDelta::Millis(arrival_time_offset_ms_);
if (packet.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe)
probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(packet);
}
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(packets);
TransportPacketsFeedback msg;
msg.packet_feedbacks = packets;
msg.feedback_time = Timestamp::Millis(clock_.TimeInMilliseconds());
DelayBasedBwe::Result result =
bitrate_estimator_->IncomingPacketFeedbackVector(
msg, acknowledged_bitrate_estimator_->bitrate(),
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(),
/*network_estimate*/ absl::nullopt, /*in_alr*/ false);
if (result.updated) {
bitrate_observer_.OnReceiveBitrateChanged(result.target_bitrate.bps());
if (!first_update_ && result.target_bitrate.bps() < bitrate_bps)
overuse = true;
first_update_ = false;
}
clock_.AdvanceTimeMicroseconds(next_time_us - clock_.TimeInMicroseconds());
return overuse;
}
// Run the bandwidth estimator with a stream of `number_of_frames` frames, or
// until it reaches `target_bitrate`.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t DelayBasedBweTest::SteadyStateRun(uint32_t ssrc,
int max_number_of_frames,
uint32_t start_bitrate,
uint32_t min_bitrate,
uint32_t max_bitrate,
uint32_t target_bitrate) {
uint32_t bitrate_bps = start_bitrate;
bool bitrate_update_seen = false;
// Produce `number_of_frames` frames and give them to the estimator.
for (int i = 0; i < max_number_of_frames; ++i) {
bool overuse = GenerateAndProcessFrame(ssrc, bitrate_bps);
if (overuse) {
EXPECT_LT(bitrate_observer_.latest_bitrate(), max_bitrate);
EXPECT_GT(bitrate_observer_.latest_bitrate(), min_bitrate);
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_update_seen = true;
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
if (bitrate_update_seen && bitrate_bps > target_bitrate) {
break;
}
}
EXPECT_TRUE(bitrate_update_seen);
return bitrate_bps;
}
void DelayBasedBweTest::InitialBehaviorTestHelper(
uint32_t expected_converge_bitrate) {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
const PacedPacketInfo kPacingInfo(0, 5, 5000);
DataRate bitrate = DataRate::Zero();
int64_t send_time_ms = 0;
std::vector<uint32_t> ssrcs;
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
EXPECT_EQ(0u, ssrcs.size());
clock_.AdvanceTimeMilliseconds(1000);
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
EXPECT_FALSE(bitrate_observer_.updated());
bitrate_observer_.Reset();
clock_.AdvanceTimeMilliseconds(1000);
// Inserting packets for 5 seconds to get a valid estimate.
for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
// NOTE!!! If the following line is moved under the if case then this test
// wont work on windows realease bots.
PacedPacketInfo pacing_info =
i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
if (i == kNumInitialPackets) {
EXPECT_FALSE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
EXPECT_EQ(0u, ssrcs.size());
EXPECT_FALSE(bitrate_observer_.updated());
bitrate_observer_.Reset();
}
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, kMtu,
pacing_info);
clock_.AdvanceTimeMilliseconds(1000 / kFramerate);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate));
ASSERT_EQ(1u, ssrcs.size());
EXPECT_EQ(kDefaultSsrc, ssrcs.front());
EXPECT_NEAR(expected_converge_bitrate, bitrate.bps(),
kAcceptedBitrateErrorBps);
EXPECT_TRUE(bitrate_observer_.updated());
bitrate_observer_.Reset();
EXPECT_EQ(bitrate_observer_.latest_bitrate(), bitrate.bps());
}
void DelayBasedBweTest::RateIncreaseReorderingTestHelper(
uint32_t expected_bitrate_bps) {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
const PacedPacketInfo kPacingInfo(0, 5, 5000);
int64_t send_time_ms = 0;
// Inserting packets for five seconds to get a valid estimate.
for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) {
// NOTE!!! If the following line is moved under the if case then this test
// wont work on windows realease bots.
PacedPacketInfo pacing_info =
i < kInitialProbingPackets ? kPacingInfo : PacedPacketInfo();
// TODO(sprang): Remove this hack once the single stream estimator is gone,
// as it doesn't do anything in Process().
if (i == kNumInitialPackets) {
// Process after we have enough frames to get a valid input rate estimate.
EXPECT_FALSE(bitrate_observer_.updated()); // No valid estimate.
}
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, kMtu,
pacing_info);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
kAcceptedBitrateErrorBps);
for (int i = 0; i < 10; ++i) {
clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
send_time_ms += 2 * kFrameIntervalMs;
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
IncomingFeedback(clock_.TimeInMilliseconds(),
send_time_ms - kFrameIntervalMs, 1000);
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_.latest_bitrate(),
kAcceptedBitrateErrorBps);
}
// Make sure we initially increase the bitrate as expected.
void DelayBasedBweTest::RateIncreaseRtpTimestampsTestHelper(
int expected_iterations) {
// This threshold corresponds approximately to increasing linearly with
// bitrate(i) = 1.04 * bitrate(i-1) + 1000
// until bitrate(i) > 500000, with bitrate(1) ~= 30000.
uint32_t bitrate_bps = 30000;
int iterations = 0;
AddDefaultStream();
// Feed the estimator with a stream of packets and verify that it reaches
// 500 kbps at the expected time.
while (bitrate_bps < 5e5) {
bool overuse = GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
if (overuse) {
EXPECT_GT(bitrate_observer_.latest_bitrate(), bitrate_bps);
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
} else if (bitrate_observer_.updated()) {
bitrate_bps = bitrate_observer_.latest_bitrate();
bitrate_observer_.Reset();
}
++iterations;
}
ASSERT_EQ(expected_iterations, iterations);
}
void DelayBasedBweTest::CapacityDropTestHelper(
int number_of_streams,
bool wrap_time_stamp,
uint32_t expected_bitrate_drop_delta,
int64_t receiver_clock_offset_change_ms) {
const int kFramerate = 30;
const int kStartBitrate = 900e3;
const int kMinExpectedBitrate = 800e3;
const int kMaxExpectedBitrate = 1100e3;
const uint32_t kInitialCapacityBps = 1000e3;
const uint32_t kReducedCapacityBps = 500e3;
int steady_state_time = 0;
if (number_of_streams <= 1) {
steady_state_time = 10;
AddDefaultStream();
} else {
steady_state_time = 10 * number_of_streams;
int bitrate_sum = 0;
int kBitrateDenom = number_of_streams * (number_of_streams - 1);
for (int i = 0; i < number_of_streams; i++) {
// First stream gets half available bitrate, while the rest share the
// remaining half i.e.: 1/2 = Sum[n/(N*(N-1))] for n=1..N-1 (rounded up)
int bitrate = kStartBitrate / 2;
if (i > 0) {
bitrate = (kStartBitrate * i + kBitrateDenom / 2) / kBitrateDenom;
}
stream_generator_->AddStream(new test::RtpStream(kFramerate, bitrate));
bitrate_sum += bitrate;
}
ASSERT_EQ(bitrate_sum, kStartBitrate);
}
// Run in steady state to make the estimator converge.
stream_generator_->set_capacity_bps(kInitialCapacityBps);
uint32_t bitrate_bps = SteadyStateRun(
kDefaultSsrc, steady_state_time * kFramerate, kStartBitrate,
kMinExpectedBitrate, kMaxExpectedBitrate, kInitialCapacityBps);
EXPECT_NEAR(kInitialCapacityBps, bitrate_bps, 180000u);
bitrate_observer_.Reset();
// Add an offset to make sure the BWE can handle it.
arrival_time_offset_ms_ += receiver_clock_offset_change_ms;
// Reduce the capacity and verify the decrease time.
stream_generator_->set_capacity_bps(kReducedCapacityBps);
int64_t overuse_start_time = clock_.TimeInMilliseconds();
int64_t bitrate_drop_time = -1;
for (int i = 0; i < 100 * number_of_streams; ++i) {
GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps);
if (bitrate_drop_time == -1 &&
bitrate_observer_.latest_bitrate() <= kReducedCapacityBps) {
bitrate_drop_time = clock_.TimeInMilliseconds();
}
if (bitrate_observer_.updated())
bitrate_bps = bitrate_observer_.latest_bitrate();
}
EXPECT_NEAR(expected_bitrate_drop_delta,
bitrate_drop_time - overuse_start_time, 33);
}
void DelayBasedBweTest::TestTimestampGroupingTestHelper() {
const int kFramerate = 50; // 50 fps to avoid rounding errors.
const int kFrameIntervalMs = 1000 / kFramerate;
int64_t send_time_ms = 0;
// Initial set of frames to increase the bitrate. 6 seconds to have enough
// time for the first estimate to be generated and for Process() to be called.
for (int i = 0; i <= 6 * kFramerate; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
EXPECT_TRUE(bitrate_observer_.updated());
EXPECT_GE(bitrate_observer_.latest_bitrate(), 400000u);
// Insert batches of frames which were sent very close in time. Also simulate
// capacity over-use to see that we back off correctly.
const int kTimestampGroupLength = 15;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < kTimestampGroupLength; ++j) {
// Insert `kTimestampGroupLength` frames with just 1 timestamp ticks in
// between. Should be treated as part of the same group by the estimator.
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 100);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs / kTimestampGroupLength);
send_time_ms += 1;
}
// Increase time until next batch to simulate over-use.
clock_.AdvanceTimeMilliseconds(10);
send_time_ms += kFrameIntervalMs - kTimestampGroupLength;
}
EXPECT_TRUE(bitrate_observer_.updated());
// Should have reduced the estimate.
EXPECT_LT(bitrate_observer_.latest_bitrate(), 400000u);
}
void DelayBasedBweTest::TestWrappingHelper(int silence_time_s) {
const int kFramerate = 100;
const int kFrameIntervalMs = 1000 / kFramerate;
int64_t send_time_ms = 0;
for (size_t i = 0; i < 3000; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
DataRate bitrate_before = DataRate::Zero();
std::vector<uint32_t> ssrcs;
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before);
clock_.AdvanceTimeMilliseconds(silence_time_s * 1000);
send_time_ms += silence_time_s * 1000;
for (size_t i = 0; i < 24; ++i) {
IncomingFeedback(clock_.TimeInMilliseconds(), send_time_ms, 1000);
clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs);
send_time_ms += kFrameIntervalMs;
}
DataRate bitrate_after = DataRate::Zero();
bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after);
EXPECT_LT(bitrate_after, bitrate_before);
}
} // namespace webrtc

View file

@ -0,0 +1,190 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/transport/field_trial_based_config.h"
#include "api/transport/network_types.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "system_wrappers/include/clock.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
class TestBitrateObserver {
public:
TestBitrateObserver() : updated_(false), latest_bitrate_(0) {}
~TestBitrateObserver() {}
void OnReceiveBitrateChanged(uint32_t bitrate);
void Reset() { updated_ = false; }
bool updated() const { return updated_; }
uint32_t latest_bitrate() const { return latest_bitrate_; }
private:
bool updated_;
uint32_t latest_bitrate_;
};
class RtpStream {
public:
enum { kSendSideOffsetUs = 1000000 };
RtpStream(int fps, int bitrate_bps);
RtpStream(const RtpStream&) = delete;
RtpStream& operator=(const RtpStream&) = delete;
// Generates a new frame for this stream. If called too soon after the
// previous frame, no frame will be generated. The frame is split into
// packets.
int64_t GenerateFrame(int64_t time_now_us,
int64_t* next_sequence_number,
std::vector<PacketResult>* packets);
// The send-side time when the next frame can be generated.
int64_t next_rtp_time() const;
void set_bitrate_bps(int bitrate_bps);
int bitrate_bps() const;
static bool Compare(const std::unique_ptr<RtpStream>& lhs,
const std::unique_ptr<RtpStream>& rhs);
private:
int fps_;
int bitrate_bps_;
int64_t next_rtp_time_;
};
class StreamGenerator {
public:
StreamGenerator(int capacity, int64_t time_now);
~StreamGenerator();
StreamGenerator(const StreamGenerator&) = delete;
StreamGenerator& operator=(const StreamGenerator&) = delete;
// Add a new stream.
void AddStream(RtpStream* stream);
// Set the link capacity.
void set_capacity_bps(int capacity_bps);
// Divides `bitrate_bps` among all streams. The allocated bitrate per stream
// is decided by the initial allocation ratios.
void SetBitrateBps(int bitrate_bps);
// Set the RTP timestamp offset for the stream identified by `ssrc`.
void set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset);
// TODO(holmer): Break out the channel simulation part from this class to make
// it possible to simulate different types of channels.
int64_t GenerateFrame(int64_t time_now_us,
int64_t* next_sequence_number,
std::vector<PacketResult>* packets);
private:
// Capacity of the simulated channel in bits per second.
int capacity_;
// The time when the last packet arrived.
int64_t prev_arrival_time_us_;
// All streams being transmitted on this simulated channel.
std::vector<std::unique_ptr<RtpStream>> streams_;
};
} // namespace test
class DelayBasedBweTest : public ::testing::Test {
public:
DelayBasedBweTest();
~DelayBasedBweTest() override;
protected:
void AddDefaultStream();
// Helpers to insert a single packet into the delay-based BWE.
void IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
size_t payload_size);
void IncomingFeedback(int64_t arrival_time_ms,
int64_t send_time_ms,
size_t payload_size,
const PacedPacketInfo& pacing_info);
void IncomingFeedback(Timestamp receive_time,
Timestamp send_time,
size_t payload_size,
const PacedPacketInfo& pacing_info);
// Generates a frame of packets belonging to a stream at a given bitrate and
// with a given ssrc. The stream is pushed through a very simple simulated
// network, and is then given to the receive-side bandwidth estimator.
// Returns true if an over-use was seen, false otherwise.
// The StreamGenerator::updated() should be used to check for any changes in
// target bitrate after the call to this function.
bool GenerateAndProcessFrame(uint32_t ssrc, uint32_t bitrate_bps);
// Run the bandwidth estimator with a stream of `number_of_frames` frames, or
// until it reaches `target_bitrate`.
// Can for instance be used to run the estimator for some time to get it
// into a steady state.
uint32_t SteadyStateRun(uint32_t ssrc,
int number_of_frames,
uint32_t start_bitrate,
uint32_t min_bitrate,
uint32_t max_bitrate,
uint32_t target_bitrate);
void TestTimestampGroupingTestHelper();
void TestWrappingHelper(int silence_time_s);
void InitialBehaviorTestHelper(uint32_t expected_converge_bitrate);
void RateIncreaseReorderingTestHelper(uint32_t expected_bitrate);
void RateIncreaseRtpTimestampsTestHelper(int expected_iterations);
void CapacityDropTestHelper(int number_of_streams,
bool wrap_time_stamp,
uint32_t expected_bitrate_drop_delta,
int64_t receiver_clock_offset_change_ms);
static const uint32_t kDefaultSsrc;
FieldTrialBasedConfig field_trial_config_;
std::unique_ptr<test::ScopedFieldTrials>
field_trial; // Must be initialized first.
SimulatedClock clock_; // Time at the receiver.
test::TestBitrateObserver bitrate_observer_;
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
acknowledged_bitrate_estimator_;
const std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
std::unique_ptr<DelayBasedBwe> bitrate_estimator_;
std::unique_ptr<test::StreamGenerator> stream_generator_;
int64_t arrival_time_offset_ms_;
int64_t next_sequence_number_;
bool first_update_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_UNITTEST_HELPER_H_

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_
#include <stdint.h>
#include <cstddef>
#include "api/network_state_predictor.h"
namespace webrtc {
class DelayIncreaseDetectorInterface {
public:
DelayIncreaseDetectorInterface() {}
virtual ~DelayIncreaseDetectorInterface() {}
DelayIncreaseDetectorInterface(const DelayIncreaseDetectorInterface&) =
delete;
DelayIncreaseDetectorInterface& operator=(
const DelayIncreaseDetectorInterface&) = delete;
// Update the detector with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
virtual void Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) = 0;
virtual BandwidthUsage State() const = 0;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_

View file

@ -0,0 +1,747 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/goog_cc_network_control.h"
#include <stdio.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <numeric>
#include <utility>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include "modules/congestion_controller/goog_cc/alr_detector.h"
#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/probe_controller.h"
#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/rate_control_settings.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// From RTCPSender video report interval.
constexpr TimeDelta kLossUpdateInterval = TimeDelta::Millis(1000);
// Pacing-rate relative to our target send rate.
// Multiplicative factor that is applied to the target bitrate to calculate
// the number of bytes that can be transmitted per interval.
// Increasing this factor will result in lower delays in cases of bitrate
// overshoots from the encoder.
constexpr float kDefaultPaceMultiplier = 2.5f;
// If the probe result is far below the current throughput estimate
// it's unlikely that the probe is accurate, so we don't want to drop too far.
// However, if we actually are overusing, we want to drop to something slightly
// below the current throughput estimate to drain the network queues.
constexpr double kProbeDropThroughputFraction = 0.85;
bool IsEnabled(const FieldTrialsView* config, absl::string_view key) {
return absl::StartsWith(config->Lookup(key), "Enabled");
}
bool IsNotDisabled(const FieldTrialsView* config, absl::string_view key) {
return !absl::StartsWith(config->Lookup(key), "Disabled");
}
BandwidthLimitedCause GetBandwidthLimitedCause(LossBasedState loss_based_state,
bool is_rtt_above_limit,
BandwidthUsage bandwidth_usage) {
if (bandwidth_usage == BandwidthUsage::kBwOverusing ||
bandwidth_usage == BandwidthUsage::kBwUnderusing) {
return BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased;
} else if (is_rtt_above_limit) {
return BandwidthLimitedCause::kRttBasedBackOffHighRtt;
}
switch (loss_based_state) {
case LossBasedState::kDecreasing:
// Probes may not be sent in this state.
return BandwidthLimitedCause::kLossLimitedBwe;
case webrtc::LossBasedState::kIncreaseUsingPadding:
// Probes may not be sent in this state.
return BandwidthLimitedCause::kLossLimitedBwe;
case LossBasedState::kIncreasing:
// Probes may be sent in this state.
return BandwidthLimitedCause::kLossLimitedBweIncreasing;
case LossBasedState::kDelayBasedEstimate:
return BandwidthLimitedCause::kDelayBasedLimited;
}
}
} // namespace
GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config,
GoogCcConfig goog_cc_config)
: key_value_config_(config.key_value_config ? config.key_value_config
: &trial_based_config_),
event_log_(config.event_log),
packet_feedback_only_(goog_cc_config.feedback_only),
safe_reset_on_route_change_("Enabled"),
safe_reset_acknowledged_rate_("ack"),
use_min_allocatable_as_lower_bound_(
IsNotDisabled(key_value_config_, "WebRTC-Bwe-MinAllocAsLowerBound")),
ignore_probes_lower_than_network_estimate_(IsNotDisabled(
key_value_config_,
"WebRTC-Bwe-IgnoreProbesLowerThanNetworkStateEstimate")),
limit_probes_lower_than_throughput_estimate_(
IsNotDisabled(key_value_config_,
"WebRTC-Bwe-LimitProbesLowerThanThroughputEstimate")),
rate_control_settings_(
RateControlSettings::ParseFromKeyValueConfig(key_value_config_)),
pace_at_max_of_bwe_and_lower_link_capacity_(
IsEnabled(key_value_config_,
"WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity")),
probe_controller_(
new ProbeController(key_value_config_, config.event_log)),
congestion_window_pushback_controller_(
rate_control_settings_.UseCongestionWindowPushback()
? std::make_unique<CongestionWindowPushbackController>(
key_value_config_)
: nullptr),
bandwidth_estimation_(
std::make_unique<SendSideBandwidthEstimation>(key_value_config_,
event_log_)),
alr_detector_(
std::make_unique<AlrDetector>(key_value_config_, config.event_log)),
probe_bitrate_estimator_(new ProbeBitrateEstimator(config.event_log)),
network_estimator_(std::move(goog_cc_config.network_state_estimator)),
network_state_predictor_(
std::move(goog_cc_config.network_state_predictor)),
delay_based_bwe_(new DelayBasedBwe(key_value_config_,
event_log_,
network_state_predictor_.get())),
acknowledged_bitrate_estimator_(
AcknowledgedBitrateEstimatorInterface::Create(key_value_config_)),
initial_config_(config),
last_loss_based_target_rate_(*config.constraints.starting_rate),
last_pushback_target_rate_(last_loss_based_target_rate_),
last_stable_target_rate_(last_loss_based_target_rate_),
last_loss_base_state_(LossBasedState::kDelayBasedEstimate),
pacing_factor_(config.stream_based_config.pacing_factor.value_or(
kDefaultPaceMultiplier)),
min_total_allocated_bitrate_(
config.stream_based_config.min_total_allocated_bitrate.value_or(
DataRate::Zero())),
max_padding_rate_(config.stream_based_config.max_padding_rate.value_or(
DataRate::Zero())) {
RTC_DCHECK(config.constraints.at_time.IsFinite());
ParseFieldTrial(
{&safe_reset_on_route_change_, &safe_reset_acknowledged_rate_},
key_value_config_->Lookup("WebRTC-Bwe-SafeResetOnRouteChange"));
if (delay_based_bwe_)
delay_based_bwe_->SetMinBitrate(kCongestionControllerMinBitrate);
}
GoogCcNetworkController::~GoogCcNetworkController() {}
NetworkControlUpdate GoogCcNetworkController::OnNetworkAvailability(
NetworkAvailability msg) {
NetworkControlUpdate update;
update.probe_cluster_configs = probe_controller_->OnNetworkAvailability(msg);
return update;
}
NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange(
NetworkRouteChange msg) {
if (safe_reset_on_route_change_) {
absl::optional<DataRate> estimated_bitrate;
if (safe_reset_acknowledged_rate_) {
estimated_bitrate = acknowledged_bitrate_estimator_->bitrate();
if (!estimated_bitrate)
estimated_bitrate = acknowledged_bitrate_estimator_->PeekRate();
} else {
estimated_bitrate = bandwidth_estimation_->target_rate();
}
if (estimated_bitrate) {
if (msg.constraints.starting_rate) {
msg.constraints.starting_rate =
std::min(*msg.constraints.starting_rate, *estimated_bitrate);
} else {
msg.constraints.starting_rate = estimated_bitrate;
}
}
}
acknowledged_bitrate_estimator_ =
AcknowledgedBitrateEstimatorInterface::Create(key_value_config_);
probe_bitrate_estimator_.reset(new ProbeBitrateEstimator(event_log_));
if (network_estimator_)
network_estimator_->OnRouteChange(msg);
delay_based_bwe_.reset(new DelayBasedBwe(key_value_config_, event_log_,
network_state_predictor_.get()));
bandwidth_estimation_->OnRouteChange();
probe_controller_->Reset(msg.at_time);
NetworkControlUpdate update;
update.probe_cluster_configs = ResetConstraints(msg.constraints);
MaybeTriggerOnNetworkChanged(&update, msg.at_time);
return update;
}
NetworkControlUpdate GoogCcNetworkController::OnProcessInterval(
ProcessInterval msg) {
NetworkControlUpdate update;
if (initial_config_) {
update.probe_cluster_configs =
ResetConstraints(initial_config_->constraints);
update.pacer_config = GetPacingRates(msg.at_time);
if (initial_config_->stream_based_config.requests_alr_probing) {
probe_controller_->EnablePeriodicAlrProbing(
*initial_config_->stream_based_config.requests_alr_probing);
}
absl::optional<DataRate> total_bitrate =
initial_config_->stream_based_config.max_total_allocated_bitrate;
if (total_bitrate) {
auto probes = probe_controller_->OnMaxTotalAllocatedBitrate(
*total_bitrate, msg.at_time);
update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
probes.begin(), probes.end());
}
initial_config_.reset();
}
if (congestion_window_pushback_controller_ && msg.pacer_queue) {
congestion_window_pushback_controller_->UpdatePacingQueue(
msg.pacer_queue->bytes());
}
bandwidth_estimation_->UpdateEstimate(msg.at_time);
absl::optional<int64_t> start_time_ms =
alr_detector_->GetApplicationLimitedRegionStartTime();
probe_controller_->SetAlrStartTimeMs(start_time_ms);
auto probes = probe_controller_->Process(msg.at_time);
update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
probes.begin(), probes.end());
if (rate_control_settings_.UseCongestionWindow() &&
!feedback_max_rtts_.empty()) {
UpdateCongestionWindowSize();
}
if (congestion_window_pushback_controller_ && current_data_window_) {
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
update.congestion_window = current_data_window_;
}
MaybeTriggerOnNetworkChanged(&update, msg.at_time);
return update;
}
NetworkControlUpdate GoogCcNetworkController::OnRemoteBitrateReport(
RemoteBitrateReport msg) {
if (packet_feedback_only_) {
RTC_LOG(LS_ERROR) << "Received REMB for packet feedback only GoogCC";
return NetworkControlUpdate();
}
bandwidth_estimation_->UpdateReceiverEstimate(msg.receive_time,
msg.bandwidth);
BWE_TEST_LOGGING_PLOT(1, "REMB_kbps", msg.receive_time.ms(),
msg.bandwidth.bps() / 1000);
return NetworkControlUpdate();
}
NetworkControlUpdate GoogCcNetworkController::OnRoundTripTimeUpdate(
RoundTripTimeUpdate msg) {
if (packet_feedback_only_ || msg.smoothed)
return NetworkControlUpdate();
RTC_DCHECK(!msg.round_trip_time.IsZero());
if (delay_based_bwe_)
delay_based_bwe_->OnRttUpdate(msg.round_trip_time);
bandwidth_estimation_->UpdateRtt(msg.round_trip_time, msg.receive_time);
return NetworkControlUpdate();
}
NetworkControlUpdate GoogCcNetworkController::OnSentPacket(
SentPacket sent_packet) {
alr_detector_->OnBytesSent(sent_packet.size.bytes(),
sent_packet.send_time.ms());
acknowledged_bitrate_estimator_->SetAlr(
alr_detector_->GetApplicationLimitedRegionStartTime().has_value());
if (!first_packet_sent_) {
first_packet_sent_ = true;
// Initialize feedback time to send time to allow estimation of RTT until
// first feedback is received.
bandwidth_estimation_->UpdatePropagationRtt(sent_packet.send_time,
TimeDelta::Zero());
}
bandwidth_estimation_->OnSentPacket(sent_packet);
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->UpdateOutstandingData(
sent_packet.data_in_flight.bytes());
NetworkControlUpdate update;
MaybeTriggerOnNetworkChanged(&update, sent_packet.send_time);
return update;
} else {
return NetworkControlUpdate();
}
}
NetworkControlUpdate GoogCcNetworkController::OnReceivedPacket(
ReceivedPacket received_packet) {
return NetworkControlUpdate();
}
NetworkControlUpdate GoogCcNetworkController::OnStreamsConfig(
StreamsConfig msg) {
NetworkControlUpdate update;
if (msg.requests_alr_probing) {
probe_controller_->EnablePeriodicAlrProbing(*msg.requests_alr_probing);
}
if (msg.max_total_allocated_bitrate) {
update.probe_cluster_configs =
probe_controller_->OnMaxTotalAllocatedBitrate(
*msg.max_total_allocated_bitrate, msg.at_time);
}
bool pacing_changed = false;
if (msg.pacing_factor && *msg.pacing_factor != pacing_factor_) {
pacing_factor_ = *msg.pacing_factor;
pacing_changed = true;
}
if (msg.min_total_allocated_bitrate &&
*msg.min_total_allocated_bitrate != min_total_allocated_bitrate_) {
min_total_allocated_bitrate_ = *msg.min_total_allocated_bitrate;
pacing_changed = true;
if (use_min_allocatable_as_lower_bound_) {
ClampConstraints();
delay_based_bwe_->SetMinBitrate(min_data_rate_);
bandwidth_estimation_->SetMinMaxBitrate(min_data_rate_, max_data_rate_);
}
}
if (msg.max_padding_rate && *msg.max_padding_rate != max_padding_rate_) {
max_padding_rate_ = *msg.max_padding_rate;
pacing_changed = true;
}
if (pacing_changed)
update.pacer_config = GetPacingRates(msg.at_time);
return update;
}
NetworkControlUpdate GoogCcNetworkController::OnTargetRateConstraints(
TargetRateConstraints constraints) {
NetworkControlUpdate update;
update.probe_cluster_configs = ResetConstraints(constraints);
MaybeTriggerOnNetworkChanged(&update, constraints.at_time);
return update;
}
void GoogCcNetworkController::ClampConstraints() {
// TODO(holmer): We should make sure the default bitrates are set to 10 kbps,
// and that we don't try to set the min bitrate to 0 from any applications.
// The congestion controller should allow a min bitrate of 0.
min_data_rate_ = std::max(min_target_rate_, kCongestionControllerMinBitrate);
if (use_min_allocatable_as_lower_bound_) {
min_data_rate_ = std::max(min_data_rate_, min_total_allocated_bitrate_);
}
if (max_data_rate_ < min_data_rate_) {
RTC_LOG(LS_WARNING) << "max bitrate smaller than min bitrate";
max_data_rate_ = min_data_rate_;
}
if (starting_rate_ && starting_rate_ < min_data_rate_) {
RTC_LOG(LS_WARNING) << "start bitrate smaller than min bitrate";
starting_rate_ = min_data_rate_;
}
}
std::vector<ProbeClusterConfig> GoogCcNetworkController::ResetConstraints(
TargetRateConstraints new_constraints) {
min_target_rate_ = new_constraints.min_data_rate.value_or(DataRate::Zero());
max_data_rate_ =
new_constraints.max_data_rate.value_or(DataRate::PlusInfinity());
starting_rate_ = new_constraints.starting_rate;
ClampConstraints();
bandwidth_estimation_->SetBitrates(starting_rate_, min_data_rate_,
max_data_rate_, new_constraints.at_time);
if (starting_rate_)
delay_based_bwe_->SetStartBitrate(*starting_rate_);
delay_based_bwe_->SetMinBitrate(min_data_rate_);
return probe_controller_->SetBitrates(
min_data_rate_, starting_rate_.value_or(DataRate::Zero()), max_data_rate_,
new_constraints.at_time);
}
NetworkControlUpdate GoogCcNetworkController::OnTransportLossReport(
TransportLossReport msg) {
if (packet_feedback_only_)
return NetworkControlUpdate();
int64_t total_packets_delta =
msg.packets_received_delta + msg.packets_lost_delta;
bandwidth_estimation_->UpdatePacketsLost(
msg.packets_lost_delta, total_packets_delta, msg.receive_time);
return NetworkControlUpdate();
}
void GoogCcNetworkController::UpdateCongestionWindowSize() {
TimeDelta min_feedback_max_rtt = TimeDelta::Millis(
*std::min_element(feedback_max_rtts_.begin(), feedback_max_rtts_.end()));
const DataSize kMinCwnd = DataSize::Bytes(2 * 1500);
TimeDelta time_window =
min_feedback_max_rtt +
TimeDelta::Millis(
rate_control_settings_.GetCongestionWindowAdditionalTimeMs());
DataSize data_window = last_loss_based_target_rate_ * time_window;
if (current_data_window_) {
data_window =
std::max(kMinCwnd, (data_window + current_data_window_.value()) / 2);
} else {
data_window = std::max(kMinCwnd, data_window);
}
current_data_window_ = data_window;
}
NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
if (report.packet_feedbacks.empty()) {
// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
// against building very large network queues.
return NetworkControlUpdate();
}
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->UpdateOutstandingData(
report.data_in_flight.bytes());
}
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
Timestamp max_recv_time = Timestamp::MinusInfinity();
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
for (const auto& feedback : feedbacks)
max_recv_time = std::max(max_recv_time, feedback.receive_time);
for (const auto& feedback : feedbacks) {
TimeDelta feedback_rtt =
report.feedback_time - feedback.sent_packet.send_time;
TimeDelta min_pending_time = max_recv_time - feedback.receive_time;
TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
}
if (max_feedback_rtt.IsFinite()) {
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
const size_t kMaxFeedbackRttWindow = 32;
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
feedback_max_rtts_.pop_front();
// TODO(srte): Use time since last unacknowledged packet.
bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time,
min_propagation_rtt);
}
if (packet_feedback_only_) {
if (!feedback_max_rtts_.empty()) {
int64_t sum_rtt_ms =
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
static_cast<int64_t>(0));
int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
if (delay_based_bwe_)
delay_based_bwe_->OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
}
TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
for (const auto& packet_feedback : feedbacks) {
TimeDelta pending_time = max_recv_time - packet_feedback.receive_time;
TimeDelta rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
// Value used for predicting NACK round trip time in FEC controller.
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
}
if (feedback_min_rtt.IsFinite()) {
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
}
expected_packets_since_last_loss_update_ +=
report.PacketsWithFeedback().size();
for (const auto& packet_feedback : report.PacketsWithFeedback()) {
if (!packet_feedback.IsReceived())
lost_packets_since_last_loss_update_ += 1;
}
if (report.feedback_time > next_loss_update_) {
next_loss_update_ = report.feedback_time + kLossUpdateInterval;
bandwidth_estimation_->UpdatePacketsLost(
lost_packets_since_last_loss_update_,
expected_packets_since_last_loss_update_, report.feedback_time);
expected_packets_since_last_loss_update_ = 0;
lost_packets_since_last_loss_update_ = 0;
}
}
absl::optional<int64_t> alr_start_time =
alr_detector_->GetApplicationLimitedRegionStartTime();
if (previously_in_alr_ && !alr_start_time.has_value()) {
int64_t now_ms = report.feedback_time.ms();
acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
probe_controller_->SetAlrEndedTimeMs(now_ms);
}
previously_in_alr_ = alr_start_time.has_value();
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
report.SortedByReceiveTime());
auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate();
bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate,
report.feedback_time);
for (const auto& feedback : report.SortedByReceiveTime()) {
if (feedback.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe) {
probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback);
}
}
if (network_estimator_) {
network_estimator_->OnTransportPacketsFeedback(report);
auto prev_estimate = estimate_;
estimate_ = network_estimator_->GetCurrentEstimate();
// TODO(srte): Make OnTransportPacketsFeedback signal whether the state
// changed to avoid the need for this check.
if (estimate_ && (!prev_estimate || estimate_->last_feed_time !=
prev_estimate->last_feed_time)) {
event_log_->Log(std::make_unique<RtcEventRemoteEstimate>(
estimate_->link_capacity_lower, estimate_->link_capacity_upper));
probe_controller_->SetNetworkStateEstimate(*estimate_);
}
}
absl::optional<DataRate> probe_bitrate =
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
*probe_bitrate < estimate_->link_capacity_lower) {
probe_bitrate.reset();
}
if (limit_probes_lower_than_throughput_estimate_ && probe_bitrate &&
acknowledged_bitrate) {
// Limit the backoff to something slightly below the acknowledged
// bitrate. ("Slightly below" because we want to drain the queues
// if we are actually overusing.)
// The acknowledged bitrate shouldn't normally be higher than the delay
// based estimate, but it could happen e.g. due to packet bursts or
// encoder overshoot. We use std::min to ensure that a probe result
// below the current BWE never causes an increase.
DataRate limit =
std::min(delay_based_bwe_->last_estimate(),
*acknowledged_bitrate * kProbeDropThroughputFraction);
probe_bitrate = std::max(*probe_bitrate, limit);
}
NetworkControlUpdate update;
bool recovered_from_overuse = false;
DelayBasedBwe::Result result;
result = delay_based_bwe_->IncomingPacketFeedbackVector(
report, acknowledged_bitrate, probe_bitrate, estimate_,
alr_start_time.has_value());
if (result.updated) {
if (result.probe) {
bandwidth_estimation_->SetSendBitrate(result.target_bitrate,
report.feedback_time);
}
// Since SetSendBitrate now resets the delay-based estimate, we have to
// call UpdateDelayBasedEstimate after SetSendBitrate.
bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time,
result.target_bitrate);
}
bandwidth_estimation_->UpdateLossBasedEstimator(
report, result.delay_detector_state, probe_bitrate,
alr_start_time.has_value());
if (result.updated) {
// Update the estimate in the ProbeController, in case we want to probe.
MaybeTriggerOnNetworkChanged(&update, report.feedback_time);
}
recovered_from_overuse = result.recovered_from_overuse;
if (recovered_from_overuse) {
probe_controller_->SetAlrStartTimeMs(alr_start_time);
auto probes = probe_controller_->RequestProbe(report.feedback_time);
update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
probes.begin(), probes.end());
}
// No valid RTT could be because send-side BWE isn't used, in which case
// we don't try to limit the outstanding packets.
if (rate_control_settings_.UseCongestionWindow() &&
max_feedback_rtt.IsFinite()) {
UpdateCongestionWindowSize();
}
if (congestion_window_pushback_controller_ && current_data_window_) {
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
update.congestion_window = current_data_window_;
}
return update;
}
NetworkControlUpdate GoogCcNetworkController::OnNetworkStateEstimate(
NetworkStateEstimate msg) {
estimate_ = msg;
return NetworkControlUpdate();
}
NetworkControlUpdate GoogCcNetworkController::GetNetworkState(
Timestamp at_time) const {
NetworkControlUpdate update;
update.target_rate = TargetTransferRate();
update.target_rate->network_estimate.at_time = at_time;
update.target_rate->network_estimate.loss_rate_ratio =
last_estimated_fraction_loss_.value_or(0) / 255.0;
update.target_rate->network_estimate.round_trip_time =
last_estimated_round_trip_time_;
update.target_rate->network_estimate.bwe_period =
delay_based_bwe_->GetExpectedBwePeriod();
update.target_rate->at_time = at_time;
update.target_rate->target_rate = last_pushback_target_rate_;
update.target_rate->stable_target_rate =
bandwidth_estimation_->GetEstimatedLinkCapacity();
update.pacer_config = GetPacingRates(at_time);
update.congestion_window = current_data_window_;
return update;
}
void GoogCcNetworkController::MaybeTriggerOnNetworkChanged(
NetworkControlUpdate* update,
Timestamp at_time) {
uint8_t fraction_loss = bandwidth_estimation_->fraction_loss();
TimeDelta round_trip_time = bandwidth_estimation_->round_trip_time();
DataRate loss_based_target_rate = bandwidth_estimation_->target_rate();
LossBasedState loss_based_state = bandwidth_estimation_->loss_based_state();
DataRate pushback_target_rate = loss_based_target_rate;
BWE_TEST_LOGGING_PLOT(1, "fraction_loss_%", at_time.ms(),
(fraction_loss * 100) / 256);
BWE_TEST_LOGGING_PLOT(1, "rtt_ms", at_time.ms(), round_trip_time.ms());
BWE_TEST_LOGGING_PLOT(1, "Target_bitrate_kbps", at_time.ms(),
loss_based_target_rate.kbps());
double cwnd_reduce_ratio = 0.0;
if (congestion_window_pushback_controller_) {
int64_t pushback_rate =
congestion_window_pushback_controller_->UpdateTargetBitrate(
loss_based_target_rate.bps());
pushback_rate = std::max<int64_t>(bandwidth_estimation_->GetMinBitrate(),
pushback_rate);
pushback_target_rate = DataRate::BitsPerSec(pushback_rate);
if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {
cwnd_reduce_ratio = static_cast<double>(loss_based_target_rate.bps() -
pushback_target_rate.bps()) /
loss_based_target_rate.bps();
}
}
DataRate stable_target_rate =
bandwidth_estimation_->GetEstimatedLinkCapacity();
stable_target_rate = std::min(stable_target_rate, pushback_target_rate);
if ((loss_based_target_rate != last_loss_based_target_rate_) ||
(loss_based_state != last_loss_base_state_) ||
(fraction_loss != last_estimated_fraction_loss_) ||
(round_trip_time != last_estimated_round_trip_time_) ||
(pushback_target_rate != last_pushback_target_rate_) ||
(stable_target_rate != last_stable_target_rate_)) {
last_loss_based_target_rate_ = loss_based_target_rate;
last_pushback_target_rate_ = pushback_target_rate;
last_estimated_fraction_loss_ = fraction_loss;
last_estimated_round_trip_time_ = round_trip_time;
last_stable_target_rate_ = stable_target_rate;
last_loss_base_state_ = loss_based_state;
alr_detector_->SetEstimatedBitrate(loss_based_target_rate.bps());
TimeDelta bwe_period = delay_based_bwe_->GetExpectedBwePeriod();
TargetTransferRate target_rate_msg;
target_rate_msg.at_time = at_time;
if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {
target_rate_msg.target_rate = loss_based_target_rate;
target_rate_msg.cwnd_reduce_ratio = cwnd_reduce_ratio;
} else {
target_rate_msg.target_rate = pushback_target_rate;
}
target_rate_msg.stable_target_rate = stable_target_rate;
target_rate_msg.network_estimate.at_time = at_time;
target_rate_msg.network_estimate.round_trip_time = round_trip_time;
target_rate_msg.network_estimate.loss_rate_ratio = fraction_loss / 255.0f;
target_rate_msg.network_estimate.bwe_period = bwe_period;
update->target_rate = target_rate_msg;
auto probes = probe_controller_->SetEstimatedBitrate(
loss_based_target_rate,
GetBandwidthLimitedCause(bandwidth_estimation_->loss_based_state(),
bandwidth_estimation_->IsRttAboveLimit(),
delay_based_bwe_->last_state()),
at_time);
update->probe_cluster_configs.insert(update->probe_cluster_configs.end(),
probes.begin(), probes.end());
update->pacer_config = GetPacingRates(at_time);
RTC_LOG(LS_VERBOSE) << "bwe " << at_time.ms() << " pushback_target_bps="
<< last_pushback_target_rate_.bps()
<< " estimate_bps=" << loss_based_target_rate.bps();
}
}
PacerConfig GoogCcNetworkController::GetPacingRates(Timestamp at_time) const {
// Pacing rate is based on target rate before congestion window pushback,
// because we don't want to build queues in the pacer when pushback occurs.
DataRate pacing_rate = DataRate::Zero();
if (pace_at_max_of_bwe_and_lower_link_capacity_ && estimate_ &&
!bandwidth_estimation_->PaceAtLossBasedEstimate()) {
pacing_rate =
std::max({min_total_allocated_bitrate_, estimate_->link_capacity_lower,
last_loss_based_target_rate_}) *
pacing_factor_;
} else {
pacing_rate =
std::max(min_total_allocated_bitrate_, last_loss_based_target_rate_) *
pacing_factor_;
}
DataRate padding_rate =
(last_loss_base_state_ == LossBasedState::kIncreaseUsingPadding)
? std::max(max_padding_rate_, last_loss_based_target_rate_)
: max_padding_rate_;
padding_rate = std::min(padding_rate, last_pushback_target_rate_);
PacerConfig msg;
msg.at_time = at_time;
msg.time_window = TimeDelta::Seconds(1);
msg.data_window = pacing_rate * msg.time_window;
msg.pad_window = padding_rate * msg.time_window;
return msg;
}
} // namespace webrtc

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_
#include <stdint.h>
#include <deque>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/field_trial_based_config.h"
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include "modules/congestion_controller/goog_cc/alr_detector.h"
#include "modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h"
#include "modules/congestion_controller/goog_cc/delay_based_bwe.h"
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include "modules/congestion_controller/goog_cc/probe_controller.h"
#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/rate_control_settings.h"
namespace webrtc {
struct GoogCcConfig {
std::unique_ptr<NetworkStateEstimator> network_state_estimator = nullptr;
std::unique_ptr<NetworkStatePredictor> network_state_predictor = nullptr;
bool feedback_only = false;
};
class GoogCcNetworkController : public NetworkControllerInterface {
public:
GoogCcNetworkController(NetworkControllerConfig config,
GoogCcConfig goog_cc_config);
GoogCcNetworkController() = delete;
GoogCcNetworkController(const GoogCcNetworkController&) = delete;
GoogCcNetworkController& operator=(const GoogCcNetworkController&) = delete;
~GoogCcNetworkController() override;
// NetworkControllerInterface
NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override;
NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override;
NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override;
NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
NetworkControlUpdate OnSentPacket(SentPacket msg) override;
NetworkControlUpdate OnReceivedPacket(ReceivedPacket msg) override;
NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override;
NetworkControlUpdate OnTargetRateConstraints(
TargetRateConstraints msg) override;
NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override;
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback msg) override;
NetworkControlUpdate OnNetworkStateEstimate(
NetworkStateEstimate msg) override;
NetworkControlUpdate GetNetworkState(Timestamp at_time) const;
private:
friend class GoogCcStatePrinter;
std::vector<ProbeClusterConfig> ResetConstraints(
TargetRateConstraints new_constraints);
void ClampConstraints();
void MaybeTriggerOnNetworkChanged(NetworkControlUpdate* update,
Timestamp at_time);
void UpdateCongestionWindowSize();
PacerConfig GetPacingRates(Timestamp at_time) const;
const FieldTrialBasedConfig trial_based_config_;
const FieldTrialsView* const key_value_config_;
RtcEventLog* const event_log_;
const bool packet_feedback_only_;
FieldTrialFlag safe_reset_on_route_change_;
FieldTrialFlag safe_reset_acknowledged_rate_;
const bool use_min_allocatable_as_lower_bound_;
const bool ignore_probes_lower_than_network_estimate_;
const bool limit_probes_lower_than_throughput_estimate_;
const RateControlSettings rate_control_settings_;
const bool pace_at_max_of_bwe_and_lower_link_capacity_;
const std::unique_ptr<ProbeController> probe_controller_;
const std::unique_ptr<CongestionWindowPushbackController>
congestion_window_pushback_controller_;
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
std::unique_ptr<AlrDetector> alr_detector_;
std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
std::unique_ptr<NetworkStateEstimator> network_estimator_;
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
acknowledged_bitrate_estimator_;
absl::optional<NetworkControllerConfig> initial_config_;
DataRate min_target_rate_ = DataRate::Zero();
DataRate min_data_rate_ = DataRate::Zero();
DataRate max_data_rate_ = DataRate::PlusInfinity();
absl::optional<DataRate> starting_rate_;
bool first_packet_sent_ = false;
absl::optional<NetworkStateEstimate> estimate_;
Timestamp next_loss_update_ = Timestamp::MinusInfinity();
int lost_packets_since_last_loss_update_ = 0;
int expected_packets_since_last_loss_update_ = 0;
std::deque<int64_t> feedback_max_rtts_;
DataRate last_loss_based_target_rate_;
DataRate last_pushback_target_rate_;
DataRate last_stable_target_rate_;
LossBasedState last_loss_base_state_;
absl::optional<uint8_t> last_estimated_fraction_loss_ = 0;
TimeDelta last_estimated_round_trip_time_ = TimeDelta::PlusInfinity();
double pacing_factor_;
DataRate min_total_allocated_bitrate_;
DataRate max_padding_rate_;
bool previously_in_alr_ = false;
absl::optional<DataSize> current_data_window_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_GOOG_CC_NETWORK_CONTROL_H_

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/inter_arrival_delta.h"
#include <algorithm>
#include <cstddef>
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
static constexpr TimeDelta kBurstDeltaThreshold = TimeDelta::Millis(5);
static constexpr TimeDelta kMaxBurstDuration = TimeDelta::Millis(100);
constexpr TimeDelta InterArrivalDelta::kArrivalTimeOffsetThreshold;
InterArrivalDelta::InterArrivalDelta(TimeDelta send_time_group_length)
: send_time_group_length_(send_time_group_length),
current_timestamp_group_(),
prev_timestamp_group_(),
num_consecutive_reordered_packets_(0) {}
bool InterArrivalDelta::ComputeDeltas(Timestamp send_time,
Timestamp arrival_time,
Timestamp system_time,
size_t packet_size,
TimeDelta* send_time_delta,
TimeDelta* arrival_time_delta,
int* packet_size_delta) {
bool calculated_deltas = false;
if (current_timestamp_group_.IsFirstPacket()) {
// We don't have enough data to update the filter, so we store it until we
// have two frames of data to process.
current_timestamp_group_.send_time = send_time;
current_timestamp_group_.first_send_time = send_time;
current_timestamp_group_.first_arrival = arrival_time;
} else if (current_timestamp_group_.first_send_time > send_time) {
// Reordered packet.
return false;
} else if (NewTimestampGroup(arrival_time, send_time)) {
// First packet of a later send burst, the previous packets sample is ready.
if (prev_timestamp_group_.complete_time.IsFinite()) {
*send_time_delta =
current_timestamp_group_.send_time - prev_timestamp_group_.send_time;
*arrival_time_delta = current_timestamp_group_.complete_time -
prev_timestamp_group_.complete_time;
TimeDelta system_time_delta = current_timestamp_group_.last_system_time -
prev_timestamp_group_.last_system_time;
if (*arrival_time_delta - system_time_delta >=
kArrivalTimeOffsetThreshold) {
RTC_LOG(LS_WARNING)
<< "The arrival time clock offset has changed (diff = "
<< arrival_time_delta->ms() - system_time_delta.ms()
<< " ms), resetting.";
Reset();
return false;
}
if (*arrival_time_delta < TimeDelta::Zero()) {
// The group of packets has been reordered since receiving its local
// arrival timestamp.
++num_consecutive_reordered_packets_;
if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) {
RTC_LOG(LS_WARNING)
<< "Packets between send burst arrived out of order, resetting:"
<< " arrival_time_delta_ms=" << arrival_time_delta->ms()
<< ", send_time_delta_ms=" << send_time_delta->ms();
Reset();
}
return false;
} else {
num_consecutive_reordered_packets_ = 0;
}
*packet_size_delta = static_cast<int>(current_timestamp_group_.size) -
static_cast<int>(prev_timestamp_group_.size);
calculated_deltas = true;
}
prev_timestamp_group_ = current_timestamp_group_;
// The new timestamp is now the current frame.
current_timestamp_group_.first_send_time = send_time;
current_timestamp_group_.send_time = send_time;
current_timestamp_group_.first_arrival = arrival_time;
current_timestamp_group_.size = 0;
} else {
current_timestamp_group_.send_time =
std::max(current_timestamp_group_.send_time, send_time);
}
// Accumulate the frame size.
current_timestamp_group_.size += packet_size;
current_timestamp_group_.complete_time = arrival_time;
current_timestamp_group_.last_system_time = system_time;
return calculated_deltas;
}
// Assumes that `timestamp` is not reordered compared to
// `current_timestamp_group_`.
bool InterArrivalDelta::NewTimestampGroup(Timestamp arrival_time,
Timestamp send_time) const {
if (current_timestamp_group_.IsFirstPacket()) {
return false;
} else if (BelongsToBurst(arrival_time, send_time)) {
return false;
} else {
return send_time - current_timestamp_group_.first_send_time >
send_time_group_length_;
}
}
bool InterArrivalDelta::BelongsToBurst(Timestamp arrival_time,
Timestamp send_time) const {
RTC_DCHECK(current_timestamp_group_.complete_time.IsFinite());
TimeDelta arrival_time_delta =
arrival_time - current_timestamp_group_.complete_time;
TimeDelta send_time_delta = send_time - current_timestamp_group_.send_time;
if (send_time_delta.IsZero())
return true;
TimeDelta propagation_delta = arrival_time_delta - send_time_delta;
if (propagation_delta < TimeDelta::Zero() &&
arrival_time_delta <= kBurstDeltaThreshold &&
arrival_time - current_timestamp_group_.first_arrival < kMaxBurstDuration)
return true;
return false;
}
void InterArrivalDelta::Reset() {
num_consecutive_reordered_packets_ = 0;
current_timestamp_group_ = SendTimeGroup();
prev_timestamp_group_ = SendTimeGroup();
}
} // namespace webrtc

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_
#include <cstddef>
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
// Helper class to compute the inter-arrival time delta and the size delta
// between two send bursts. This code is branched from
// modules/remote_bitrate_estimator/inter_arrival.
class InterArrivalDelta {
public:
// After this many packet groups received out of order InterArrival will
// reset, assuming that clocks have made a jump.
static constexpr int kReorderedResetThreshold = 3;
static constexpr TimeDelta kArrivalTimeOffsetThreshold =
TimeDelta::Seconds(3);
// A send time group is defined as all packets with a send time which are at
// most send_time_group_length older than the first timestamp in that
// group.
explicit InterArrivalDelta(TimeDelta send_time_group_length);
InterArrivalDelta() = delete;
InterArrivalDelta(const InterArrivalDelta&) = delete;
InterArrivalDelta& operator=(const InterArrivalDelta&) = delete;
// This function returns true if a delta was computed, or false if the current
// group is still incomplete or if only one group has been completed.
// `send_time` is the send time.
// `arrival_time` is the time at which the packet arrived.
// `packet_size` is the size of the packet.
// `timestamp_delta` (output) is the computed send time delta.
// `arrival_time_delta` (output) is the computed arrival-time delta.
// `packet_size_delta` (output) is the computed size delta.
bool ComputeDeltas(Timestamp send_time,
Timestamp arrival_time,
Timestamp system_time,
size_t packet_size,
TimeDelta* send_time_delta,
TimeDelta* arrival_time_delta,
int* packet_size_delta);
private:
struct SendTimeGroup {
SendTimeGroup()
: size(0),
first_send_time(Timestamp::MinusInfinity()),
send_time(Timestamp::MinusInfinity()),
first_arrival(Timestamp::MinusInfinity()),
complete_time(Timestamp::MinusInfinity()),
last_system_time(Timestamp::MinusInfinity()) {}
bool IsFirstPacket() const { return complete_time.IsInfinite(); }
size_t size;
Timestamp first_send_time;
Timestamp send_time;
Timestamp first_arrival;
Timestamp complete_time;
Timestamp last_system_time;
};
// Returns true if the last packet was the end of the current batch and the
// packet with `send_time` is the first of a new batch.
bool NewTimestampGroup(Timestamp arrival_time, Timestamp send_time) const;
bool BelongsToBurst(Timestamp arrival_time, Timestamp send_time) const;
void Reset();
const TimeDelta send_time_group_length_;
SendTimeGroup current_timestamp_group_;
SendTimeGroup prev_timestamp_group_;
int num_consecutive_reordered_packets_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_

View file

@ -0,0 +1,79 @@
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h"
#include <algorithm>
#include <cmath>
#include "api/units/data_rate.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
LinkCapacityEstimator::LinkCapacityEstimator() {}
DataRate LinkCapacityEstimator::UpperBound() const {
if (estimate_kbps_.has_value())
return DataRate::KilobitsPerSec(estimate_kbps_.value() +
3 * deviation_estimate_kbps());
return DataRate::Infinity();
}
DataRate LinkCapacityEstimator::LowerBound() const {
if (estimate_kbps_.has_value())
return DataRate::KilobitsPerSec(
std::max(0.0, estimate_kbps_.value() - 3 * deviation_estimate_kbps()));
return DataRate::Zero();
}
void LinkCapacityEstimator::Reset() {
estimate_kbps_.reset();
}
void LinkCapacityEstimator::OnOveruseDetected(DataRate acknowledged_rate) {
Update(acknowledged_rate, 0.05);
}
void LinkCapacityEstimator::OnProbeRate(DataRate probe_rate) {
Update(probe_rate, 0.5);
}
void LinkCapacityEstimator::Update(DataRate capacity_sample, double alpha) {
double sample_kbps = capacity_sample.kbps();
if (!estimate_kbps_.has_value()) {
estimate_kbps_ = sample_kbps;
} else {
estimate_kbps_ = (1 - alpha) * estimate_kbps_.value() + alpha * sample_kbps;
}
// Estimate the variance of the link capacity estimate and normalize the
// variance with the link capacity estimate.
const double norm = std::max(estimate_kbps_.value(), 1.0);
double error_kbps = estimate_kbps_.value() - sample_kbps;
deviation_kbps_ =
(1 - alpha) * deviation_kbps_ + alpha * error_kbps * error_kbps / norm;
// 0.4 ~= 14 kbit/s at 500 kbit/s
// 2.5f ~= 35 kbit/s at 500 kbit/s
deviation_kbps_ = rtc::SafeClamp(deviation_kbps_, 0.4f, 2.5f);
}
bool LinkCapacityEstimator::has_estimate() const {
return estimate_kbps_.has_value();
}
DataRate LinkCapacityEstimator::estimate() const {
return DataRate::KilobitsPerSec(*estimate_kbps_);
}
double LinkCapacityEstimator::deviation_estimate_kbps() const {
// Calculate the max bit rate std dev given the normalized
// variance and the current throughput bitrate. The standard deviation will
// only be used if estimate_kbps_ has a value.
return sqrt(deviation_kbps_ * estimate_kbps_.value());
}
} // namespace webrtc

View file

@ -0,0 +1,38 @@
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
namespace webrtc {
class LinkCapacityEstimator {
public:
LinkCapacityEstimator();
DataRate UpperBound() const;
DataRate LowerBound() const;
void Reset();
void OnOveruseDetected(DataRate acknowledged_rate);
void OnProbeRate(DataRate probe_rate);
bool has_estimate() const;
DataRate estimate() const;
private:
friend class GoogCcStatePrinter;
void Update(DataRate capacity_sample, double alpha);
double deviation_estimate_kbps() const;
absl::optional<double> estimate_kbps_;
double deviation_kbps_ = 0.4;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_

View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h"
#include <algorithm>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
namespace {
const char kBweLossBasedControl[] = "WebRTC-Bwe-LossBasedControl";
// Expecting RTCP feedback to be sent with roughly 1s intervals, a 5s gap
// indicates a channel outage.
constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
// Increase slower when RTT is high.
double GetIncreaseFactor(const LossBasedControlConfig& config, TimeDelta rtt) {
// Clamp the RTT
if (rtt < config.increase_low_rtt) {
rtt = config.increase_low_rtt;
} else if (rtt > config.increase_high_rtt) {
rtt = config.increase_high_rtt;
}
auto rtt_range = config.increase_high_rtt.Get() - config.increase_low_rtt;
if (rtt_range <= TimeDelta::Zero()) {
RTC_DCHECK_NOTREACHED(); // Only on misconfiguration.
return config.min_increase_factor;
}
auto rtt_offset = rtt - config.increase_low_rtt;
auto relative_offset = std::max(0.0, std::min(rtt_offset / rtt_range, 1.0));
auto factor_range = config.max_increase_factor - config.min_increase_factor;
return config.min_increase_factor + (1 - relative_offset) * factor_range;
}
double LossFromBitrate(DataRate bitrate,
DataRate loss_bandwidth_balance,
double exponent) {
if (loss_bandwidth_balance >= bitrate)
return 1.0;
return pow(loss_bandwidth_balance / bitrate, exponent);
}
DataRate BitrateFromLoss(double loss,
DataRate loss_bandwidth_balance,
double exponent) {
if (exponent <= 0) {
RTC_DCHECK_NOTREACHED();
return DataRate::Infinity();
}
if (loss < 1e-5)
return DataRate::Infinity();
return loss_bandwidth_balance * pow(loss, -1.0 / exponent);
}
double ExponentialUpdate(TimeDelta window, TimeDelta interval) {
// Use the convention that exponential window length (which is really
// infinite) is the time it takes to dampen to 1/e.
if (window <= TimeDelta::Zero()) {
RTC_DCHECK_NOTREACHED();
return 1.0f;
}
return 1.0f - exp(interval / window * -1.0);
}
bool IsEnabled(const webrtc::FieldTrialsView& key_value_config,
absl::string_view name) {
return absl::StartsWith(key_value_config.Lookup(name), "Enabled");
}
} // namespace
LossBasedControlConfig::LossBasedControlConfig(
const FieldTrialsView* key_value_config)
: enabled(IsEnabled(*key_value_config, kBweLossBasedControl)),
min_increase_factor("min_incr", 1.02),
max_increase_factor("max_incr", 1.08),
increase_low_rtt("incr_low_rtt", TimeDelta::Millis(200)),
increase_high_rtt("incr_high_rtt", TimeDelta::Millis(800)),
decrease_factor("decr", 0.99),
loss_window("loss_win", TimeDelta::Millis(800)),
loss_max_window("loss_max_win", TimeDelta::Millis(800)),
acknowledged_rate_max_window("ackrate_max_win", TimeDelta::Millis(800)),
increase_offset("incr_offset", DataRate::BitsPerSec(1000)),
loss_bandwidth_balance_increase("balance_incr",
DataRate::KilobitsPerSec(0.5)),
loss_bandwidth_balance_decrease("balance_decr",
DataRate::KilobitsPerSec(4)),
loss_bandwidth_balance_reset("balance_reset",
DataRate::KilobitsPerSec(0.1)),
loss_bandwidth_balance_exponent("exponent", 0.5),
allow_resets("resets", false),
decrease_interval("decr_intvl", TimeDelta::Millis(300)),
loss_report_timeout("timeout", TimeDelta::Millis(6000)) {
ParseFieldTrial(
{&min_increase_factor, &max_increase_factor, &increase_low_rtt,
&increase_high_rtt, &decrease_factor, &loss_window, &loss_max_window,
&acknowledged_rate_max_window, &increase_offset,
&loss_bandwidth_balance_increase, &loss_bandwidth_balance_decrease,
&loss_bandwidth_balance_reset, &loss_bandwidth_balance_exponent,
&allow_resets, &decrease_interval, &loss_report_timeout},
key_value_config->Lookup(kBweLossBasedControl));
}
LossBasedControlConfig::LossBasedControlConfig(const LossBasedControlConfig&) =
default;
LossBasedControlConfig::~LossBasedControlConfig() = default;
LossBasedBandwidthEstimation::LossBasedBandwidthEstimation(
const FieldTrialsView* key_value_config)
: config_(key_value_config),
average_loss_(0),
average_loss_max_(0),
loss_based_bitrate_(DataRate::Zero()),
acknowledged_bitrate_max_(DataRate::Zero()),
acknowledged_bitrate_last_update_(Timestamp::MinusInfinity()),
time_last_decrease_(Timestamp::MinusInfinity()),
has_decreased_since_last_loss_report_(false),
last_loss_packet_report_(Timestamp::MinusInfinity()),
last_loss_ratio_(0) {}
void LossBasedBandwidthEstimation::UpdateLossStatistics(
const std::vector<PacketResult>& packet_results,
Timestamp at_time) {
if (packet_results.empty()) {
RTC_DCHECK_NOTREACHED();
return;
}
int loss_count = 0;
for (const auto& pkt : packet_results) {
loss_count += !pkt.IsReceived() ? 1 : 0;
}
last_loss_ratio_ = static_cast<double>(loss_count) / packet_results.size();
const TimeDelta time_passed = last_loss_packet_report_.IsFinite()
? at_time - last_loss_packet_report_
: TimeDelta::Seconds(1);
last_loss_packet_report_ = at_time;
has_decreased_since_last_loss_report_ = false;
average_loss_ += ExponentialUpdate(config_.loss_window, time_passed) *
(last_loss_ratio_ - average_loss_);
if (average_loss_ > average_loss_max_) {
average_loss_max_ = average_loss_;
} else {
average_loss_max_ +=
ExponentialUpdate(config_.loss_max_window, time_passed) *
(average_loss_ - average_loss_max_);
}
}
void LossBasedBandwidthEstimation::UpdateAcknowledgedBitrate(
DataRate acknowledged_bitrate,
Timestamp at_time) {
const TimeDelta time_passed =
acknowledged_bitrate_last_update_.IsFinite()
? at_time - acknowledged_bitrate_last_update_
: TimeDelta::Seconds(1);
acknowledged_bitrate_last_update_ = at_time;
if (acknowledged_bitrate > acknowledged_bitrate_max_) {
acknowledged_bitrate_max_ = acknowledged_bitrate;
} else {
acknowledged_bitrate_max_ -=
ExponentialUpdate(config_.acknowledged_rate_max_window, time_passed) *
(acknowledged_bitrate_max_ - acknowledged_bitrate);
}
}
DataRate LossBasedBandwidthEstimation::Update(Timestamp at_time,
DataRate min_bitrate,
DataRate wanted_bitrate,
TimeDelta last_round_trip_time) {
if (loss_based_bitrate_.IsZero()) {
loss_based_bitrate_ = wanted_bitrate;
}
// Only increase if loss has been low for some time.
const double loss_estimate_for_increase = average_loss_max_;
// Avoid multiple decreases from averaging over one loss spike.
const double loss_estimate_for_decrease =
std::min(average_loss_, last_loss_ratio_);
const bool allow_decrease =
!has_decreased_since_last_loss_report_ &&
(at_time - time_last_decrease_ >=
last_round_trip_time + config_.decrease_interval);
// If packet lost reports are too old, dont increase bitrate.
const bool loss_report_valid =
at_time - last_loss_packet_report_ < 1.2 * kMaxRtcpFeedbackInterval;
if (loss_report_valid && config_.allow_resets &&
loss_estimate_for_increase < loss_reset_threshold()) {
loss_based_bitrate_ = wanted_bitrate;
} else if (loss_report_valid &&
loss_estimate_for_increase < loss_increase_threshold()) {
// Increase bitrate by RTT-adaptive ratio.
DataRate new_increased_bitrate =
min_bitrate * GetIncreaseFactor(config_, last_round_trip_time) +
config_.increase_offset;
// The bitrate that would make the loss "just high enough".
const DataRate new_increased_bitrate_cap = BitrateFromLoss(
loss_estimate_for_increase, config_.loss_bandwidth_balance_increase,
config_.loss_bandwidth_balance_exponent);
new_increased_bitrate =
std::min(new_increased_bitrate, new_increased_bitrate_cap);
loss_based_bitrate_ = std::max(new_increased_bitrate, loss_based_bitrate_);
} else if (loss_estimate_for_decrease > loss_decrease_threshold() &&
allow_decrease) {
// The bitrate that would make the loss "just acceptable".
const DataRate new_decreased_bitrate_floor = BitrateFromLoss(
loss_estimate_for_decrease, config_.loss_bandwidth_balance_decrease,
config_.loss_bandwidth_balance_exponent);
DataRate new_decreased_bitrate =
std::max(decreased_bitrate(), new_decreased_bitrate_floor);
if (new_decreased_bitrate < loss_based_bitrate_) {
time_last_decrease_ = at_time;
has_decreased_since_last_loss_report_ = true;
loss_based_bitrate_ = new_decreased_bitrate;
}
}
return loss_based_bitrate_;
}
void LossBasedBandwidthEstimation::Initialize(DataRate bitrate) {
loss_based_bitrate_ = bitrate;
average_loss_ = 0;
average_loss_max_ = 0;
}
double LossBasedBandwidthEstimation::loss_reset_threshold() const {
return LossFromBitrate(loss_based_bitrate_,
config_.loss_bandwidth_balance_reset,
config_.loss_bandwidth_balance_exponent);
}
double LossBasedBandwidthEstimation::loss_increase_threshold() const {
return LossFromBitrate(loss_based_bitrate_,
config_.loss_bandwidth_balance_increase,
config_.loss_bandwidth_balance_exponent);
}
double LossBasedBandwidthEstimation::loss_decrease_threshold() const {
return LossFromBitrate(loss_based_bitrate_,
config_.loss_bandwidth_balance_decrease,
config_.loss_bandwidth_balance_exponent);
}
DataRate LossBasedBandwidthEstimation::decreased_bitrate() const {
return config_.decrease_factor * acknowledged_bitrate_max_;
}
} // namespace webrtc

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_
#include <vector>
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
struct LossBasedControlConfig {
explicit LossBasedControlConfig(const FieldTrialsView* key_value_config);
LossBasedControlConfig(const LossBasedControlConfig&);
LossBasedControlConfig& operator=(const LossBasedControlConfig&) = default;
~LossBasedControlConfig();
bool enabled;
FieldTrialParameter<double> min_increase_factor;
FieldTrialParameter<double> max_increase_factor;
FieldTrialParameter<TimeDelta> increase_low_rtt;
FieldTrialParameter<TimeDelta> increase_high_rtt;
FieldTrialParameter<double> decrease_factor;
FieldTrialParameter<TimeDelta> loss_window;
FieldTrialParameter<TimeDelta> loss_max_window;
FieldTrialParameter<TimeDelta> acknowledged_rate_max_window;
FieldTrialParameter<DataRate> increase_offset;
FieldTrialParameter<DataRate> loss_bandwidth_balance_increase;
FieldTrialParameter<DataRate> loss_bandwidth_balance_decrease;
FieldTrialParameter<DataRate> loss_bandwidth_balance_reset;
FieldTrialParameter<double> loss_bandwidth_balance_exponent;
FieldTrialParameter<bool> allow_resets;
FieldTrialParameter<TimeDelta> decrease_interval;
FieldTrialParameter<TimeDelta> loss_report_timeout;
};
// Estimates an upper BWE limit based on loss.
// It requires knowledge about lost packets and acknowledged bitrate.
// Ie, this class require transport feedback.
class LossBasedBandwidthEstimation {
public:
explicit LossBasedBandwidthEstimation(
const FieldTrialsView* key_value_config);
// Returns the new estimate.
DataRate Update(Timestamp at_time,
DataRate min_bitrate,
DataRate wanted_bitrate,
TimeDelta last_round_trip_time);
void UpdateAcknowledgedBitrate(DataRate acknowledged_bitrate,
Timestamp at_time);
void Initialize(DataRate bitrate);
bool Enabled() const { return config_.enabled; }
// Returns true if LossBasedBandwidthEstimation is enabled and have
// received loss statistics. Ie, this class require transport feedback.
bool InUse() const {
return Enabled() && last_loss_packet_report_.IsFinite();
}
void UpdateLossStatistics(const std::vector<PacketResult>& packet_results,
Timestamp at_time);
DataRate GetEstimate() const { return loss_based_bitrate_; }
private:
friend class GoogCcStatePrinter;
void Reset(DataRate bitrate);
double loss_increase_threshold() const;
double loss_decrease_threshold() const;
double loss_reset_threshold() const;
DataRate decreased_bitrate() const;
const LossBasedControlConfig config_;
double average_loss_;
double average_loss_max_;
DataRate loss_based_bitrate_;
DataRate acknowledged_bitrate_max_;
Timestamp acknowledged_bitrate_last_update_;
Timestamp time_last_decrease_;
bool has_decreased_since_last_loss_report_;
Timestamp last_loss_packet_report_;
double last_loss_ratio_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BANDWIDTH_ESTIMATION_H_

View file

@ -0,0 +1,223 @@
/*
* Copyright 2021 The WebRTC project authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_
#include <vector>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/field_trials_view.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
// State of the loss based estimate, which can be either increasing/decreasing
// when network is loss limited, or equal to the delay based estimate.
enum class LossBasedState {
kIncreasing = 0,
// TODO(bugs.webrtc.org/12707): Remove one of the increasing states once we
// have decided if padding is usefull for ramping up when BWE is loss
// limited.
kIncreaseUsingPadding = 1,
kDecreasing = 2,
kDelayBasedEstimate = 3
};
class LossBasedBweV2 {
public:
struct Result {
~Result() = default;
DataRate bandwidth_estimate = DataRate::Zero();
// State is used by goog_cc, which later sends probe requests to probe
// controller if state is kIncreasing.
LossBasedState state = LossBasedState::kDelayBasedEstimate;
};
// Creates a disabled `LossBasedBweV2` if the
// `key_value_config` is not valid.
explicit LossBasedBweV2(const FieldTrialsView* key_value_config);
LossBasedBweV2(const LossBasedBweV2&) = delete;
LossBasedBweV2& operator=(const LossBasedBweV2&) = delete;
~LossBasedBweV2() = default;
bool IsEnabled() const;
// Returns true iff a BWE can be calculated, i.e., the estimator has been
// initialized with a BWE and then has received enough `PacketResult`s.
bool IsReady() const;
// Returns true if loss based BWE is ready to be used in the start phase.
bool ReadyToUseInStartPhase() const;
// Returns true if loss based BWE can be used in the start phase.
bool UseInStartPhase() const;
// Returns `DataRate::PlusInfinity` if no BWE can be calculated.
Result GetLossBasedResult() const;
void SetAcknowledgedBitrate(DataRate acknowledged_bitrate);
void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
void UpdateBandwidthEstimate(
rtc::ArrayView<const PacketResult> packet_results,
DataRate delay_based_estimate,
bool in_alr);
bool PaceAtLossBasedEstimate() const;
// For unit testing only.
void SetBandwidthEstimate(DataRate bandwidth_estimate);
private:
struct ChannelParameters {
double inherent_loss = 0.0;
DataRate loss_limited_bandwidth = DataRate::MinusInfinity();
};
struct Config {
double bandwidth_rampup_upper_bound_factor = 0.0;
double bandwidth_rampup_upper_bound_factor_in_hold = 0;
double bandwidth_rampup_hold_threshold = 0;
double rampup_acceleration_max_factor = 0.0;
TimeDelta rampup_acceleration_maxout_time = TimeDelta::Zero();
std::vector<double> candidate_factors;
double higher_bandwidth_bias_factor = 0.0;
double higher_log_bandwidth_bias_factor = 0.0;
double inherent_loss_lower_bound = 0.0;
double loss_threshold_of_high_bandwidth_preference = 0.0;
double bandwidth_preference_smoothing_factor = 0.0;
DataRate inherent_loss_upper_bound_bandwidth_balance =
DataRate::MinusInfinity();
double inherent_loss_upper_bound_offset = 0.0;
double initial_inherent_loss_estimate = 0.0;
int newton_iterations = 0;
double newton_step_size = 0.0;
bool append_acknowledged_rate_candidate = true;
bool append_delay_based_estimate_candidate = false;
bool append_upper_bound_candidate_in_alr = false;
TimeDelta observation_duration_lower_bound = TimeDelta::Zero();
int observation_window_size = 0;
double sending_rate_smoothing_factor = 0.0;
double instant_upper_bound_temporal_weight_factor = 0.0;
DataRate instant_upper_bound_bandwidth_balance = DataRate::MinusInfinity();
double instant_upper_bound_loss_offset = 0.0;
double temporal_weight_factor = 0.0;
double bandwidth_backoff_lower_bound_factor = 0.0;
double max_increase_factor = 0.0;
TimeDelta delayed_increase_window = TimeDelta::Zero();
bool not_increase_if_inherent_loss_less_than_average_loss = false;
bool not_use_acked_rate_in_alr = false;
bool use_in_start_phase = false;
int min_num_observations = 0;
double lower_bound_by_acked_rate_factor = 0.0;
double hold_duration_factor = 0.0;
bool use_byte_loss_rate = false;
TimeDelta padding_duration = TimeDelta::Zero();
bool bound_best_candidate = false;
bool pace_at_loss_based_estimate = false;
};
struct Derivatives {
double first = 0.0;
double second = 0.0;
};
struct Observation {
bool IsInitialized() const { return id != -1; }
int num_packets = 0;
int num_lost_packets = 0;
int num_received_packets = 0;
DataRate sending_rate = DataRate::MinusInfinity();
DataSize size = DataSize::Zero();
DataSize lost_size = DataSize::Zero();
int id = -1;
};
struct PartialObservation {
int num_packets = 0;
int num_lost_packets = 0;
DataSize size = DataSize::Zero();
DataSize lost_size = DataSize::Zero();
};
struct PaddingInfo {
DataRate padding_rate = DataRate::MinusInfinity();
Timestamp padding_timestamp = Timestamp::MinusInfinity();
};
struct HoldInfo {
Timestamp timestamp = Timestamp::MinusInfinity();
TimeDelta duration = TimeDelta::Zero();
DataRate rate = DataRate::PlusInfinity();
};
static absl::optional<Config> CreateConfig(
const FieldTrialsView* key_value_config);
bool IsConfigValid() const;
// Returns `0.0` if not enough loss statistics have been received.
double GetAverageReportedLossRatio() const;
double GetAverageReportedPacketLossRatio() const;
double GetAverageReportedByteLossRatio() const;
std::vector<ChannelParameters> GetCandidates(bool in_alr) const;
DataRate GetCandidateBandwidthUpperBound() const;
Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const;
double GetFeasibleInherentLoss(
const ChannelParameters& channel_parameters) const;
double GetInherentLossUpperBound(DataRate bandwidth) const;
double AdjustBiasFactor(double loss_rate, double bias_factor) const;
double GetHighBandwidthBias(DataRate bandwidth) const;
double GetObjective(const ChannelParameters& channel_parameters) const;
DataRate GetSendingRate(DataRate instantaneous_sending_rate) const;
DataRate GetInstantUpperBound() const;
void CalculateInstantUpperBound();
DataRate GetInstantLowerBound() const;
void CalculateInstantLowerBound();
void CalculateTemporalWeights();
void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const;
// Returns false if no observation was created.
bool PushBackObservation(rtc::ArrayView<const PacketResult> packet_results);
bool IsEstimateIncreasingWhenLossLimited(DataRate old_estimate,
DataRate new_estimate);
bool IsInLossLimitedState() const;
bool CanKeepIncreasingState(DataRate estimate) const;
absl::optional<DataRate> acknowledged_bitrate_;
absl::optional<Config> config_;
ChannelParameters current_best_estimate_;
int num_observations_ = 0;
std::vector<Observation> observations_;
PartialObservation partial_observation_;
Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity();
Timestamp last_time_estimate_reduced_ = Timestamp::MinusInfinity();
absl::optional<DataRate> cached_instant_upper_bound_;
absl::optional<DataRate> cached_instant_lower_bound_;
std::vector<double> instant_upper_bound_temporal_weights_;
std::vector<double> temporal_weights_;
Timestamp recovering_after_loss_timestamp_ = Timestamp::MinusInfinity();
DataRate bandwidth_limit_in_current_window_ = DataRate::PlusInfinity();
DataRate min_bitrate_ = DataRate::KilobitsPerSec(1);
DataRate max_bitrate_ = DataRate::PlusInfinity();
DataRate delay_based_estimate_ = DataRate::PlusInfinity();
LossBasedBweV2::Result loss_based_result_ = LossBasedBweV2::Result();
HoldInfo last_hold_info_ = HoldInfo();
PaddingInfo last_padding_info_ = PaddingInfo();
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h"
#include <algorithm>
#include <memory>
#include "absl/types/optional.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// The minumum number of probes we need to receive feedback about in percent
// in order to have a valid estimate.
constexpr double kMinReceivedProbesRatio = .80;
// The minumum number of bytes we need to receive feedback about in percent
// in order to have a valid estimate.
constexpr double kMinReceivedBytesRatio = .80;
// The maximum |receive rate| / |send rate| ratio for a valid estimate.
constexpr float kMaxValidRatio = 2.0f;
// The minimum |receive rate| / |send rate| ratio assuming that the link is
// not saturated, i.e. we assume that we will receive at least
// kMinRatioForUnsaturatedLink * |send rate| if |send rate| is less than the
// link capacity.
constexpr float kMinRatioForUnsaturatedLink = 0.9f;
// The target utilization of the link. If we know true link capacity
// we'd like to send at 95% of that rate.
constexpr float kTargetUtilizationFraction = 0.95f;
// The maximum time period over which the cluster history is retained.
// This is also the maximum time period beyond which a probing burst is not
// expected to last.
constexpr TimeDelta kMaxClusterHistory = TimeDelta::Seconds(1);
// The maximum time interval between first and the last probe on a cluster
// on the sender side as well as the receive side.
constexpr TimeDelta kMaxProbeInterval = TimeDelta::Seconds(1);
} // namespace
ProbeBitrateEstimator::ProbeBitrateEstimator(RtcEventLog* event_log)
: event_log_(event_log) {}
ProbeBitrateEstimator::~ProbeBitrateEstimator() = default;
absl::optional<DataRate> ProbeBitrateEstimator::HandleProbeAndEstimateBitrate(
const PacketResult& packet_feedback) {
int cluster_id = packet_feedback.sent_packet.pacing_info.probe_cluster_id;
RTC_DCHECK_NE(cluster_id, PacedPacketInfo::kNotAProbe);
EraseOldClusters(packet_feedback.receive_time);
AggregatedCluster* cluster = &clusters_[cluster_id];
if (packet_feedback.sent_packet.send_time < cluster->first_send) {
cluster->first_send = packet_feedback.sent_packet.send_time;
}
if (packet_feedback.sent_packet.send_time > cluster->last_send) {
cluster->last_send = packet_feedback.sent_packet.send_time;
cluster->size_last_send = packet_feedback.sent_packet.size;
}
if (packet_feedback.receive_time < cluster->first_receive) {
cluster->first_receive = packet_feedback.receive_time;
cluster->size_first_receive = packet_feedback.sent_packet.size;
}
if (packet_feedback.receive_time > cluster->last_receive) {
cluster->last_receive = packet_feedback.receive_time;
}
cluster->size_total += packet_feedback.sent_packet.size;
cluster->num_probes += 1;
RTC_DCHECK_GT(
packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes, 0);
RTC_DCHECK_GT(packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes,
0);
int min_probes =
packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes *
kMinReceivedProbesRatio;
DataSize min_size =
DataSize::Bytes(
packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes) *
kMinReceivedBytesRatio;
if (cluster->num_probes < min_probes || cluster->size_total < min_size)
return absl::nullopt;
TimeDelta send_interval = cluster->last_send - cluster->first_send;
TimeDelta receive_interval = cluster->last_receive - cluster->first_receive;
if (send_interval <= TimeDelta::Zero() || send_interval > kMaxProbeInterval ||
receive_interval <= TimeDelta::Zero() ||
receive_interval > kMaxProbeInterval) {
RTC_LOG(LS_INFO) << "Probing unsuccessful, invalid send/receive interval"
" [cluster id: "
<< cluster_id
<< "] [send interval: " << ToString(send_interval)
<< "]"
" [receive interval: "
<< ToString(receive_interval) << "]";
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventProbeResultFailure>(
cluster_id, ProbeFailureReason::kInvalidSendReceiveInterval));
}
return absl::nullopt;
}
// Since the `send_interval` does not include the time it takes to actually
// send the last packet the size of the last sent packet should not be
// included when calculating the send bitrate.
RTC_DCHECK_GT(cluster->size_total, cluster->size_last_send);
DataSize send_size = cluster->size_total - cluster->size_last_send;
DataRate send_rate = send_size / send_interval;
// Since the `receive_interval` does not include the time it takes to
// actually receive the first packet the size of the first received packet
// should not be included when calculating the receive bitrate.
RTC_DCHECK_GT(cluster->size_total, cluster->size_first_receive);
DataSize receive_size = cluster->size_total - cluster->size_first_receive;
DataRate receive_rate = receive_size / receive_interval;
double ratio = receive_rate / send_rate;
if (ratio > kMaxValidRatio) {
RTC_LOG(LS_INFO) << "Probing unsuccessful, receive/send ratio too high"
" [cluster id: "
<< cluster_id << "] [send: " << ToString(send_size)
<< " / " << ToString(send_interval) << " = "
<< ToString(send_rate)
<< "]"
" [receive: "
<< ToString(receive_size) << " / "
<< ToString(receive_interval) << " = "
<< ToString(receive_rate)
<< " ]"
" [ratio: "
<< ToString(receive_rate) << " / " << ToString(send_rate)
<< " = " << ratio << " > kMaxValidRatio ("
<< kMaxValidRatio << ")]";
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventProbeResultFailure>(
cluster_id, ProbeFailureReason::kInvalidSendReceiveRatio));
}
return absl::nullopt;
}
RTC_LOG(LS_INFO) << "Probing successful"
" [cluster id: "
<< cluster_id << "] [send: " << ToString(send_size) << " / "
<< ToString(send_interval) << " = " << ToString(send_rate)
<< " ]"
" [receive: "
<< ToString(receive_size) << " / "
<< ToString(receive_interval) << " = "
<< ToString(receive_rate) << "]";
DataRate res = std::min(send_rate, receive_rate);
// If we're receiving at significantly lower bitrate than we were sending at,
// it suggests that we've found the true capacity of the link. In this case,
// set the target bitrate slightly lower to not immediately overuse.
if (receive_rate < kMinRatioForUnsaturatedLink * send_rate) {
RTC_DCHECK_GT(send_rate, receive_rate);
res = kTargetUtilizationFraction * receive_rate;
}
if (event_log_) {
event_log_->Log(
std::make_unique<RtcEventProbeResultSuccess>(cluster_id, res.bps()));
}
estimated_data_rate_ = res;
return estimated_data_rate_;
}
absl::optional<DataRate>
ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrate() {
absl::optional<DataRate> estimated_data_rate = estimated_data_rate_;
estimated_data_rate_.reset();
return estimated_data_rate;
}
void ProbeBitrateEstimator::EraseOldClusters(Timestamp timestamp) {
for (auto it = clusters_.begin(); it != clusters_.end();) {
if (it->second.last_receive + kMaxClusterHistory < timestamp) {
it = clusters_.erase(it);
} else {
++it;
}
}
}
} // namespace webrtc

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
#include <map>
#include "absl/types/optional.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
namespace webrtc {
class RtcEventLog;
class ProbeBitrateEstimator {
public:
explicit ProbeBitrateEstimator(RtcEventLog* event_log);
~ProbeBitrateEstimator();
// Should be called for every probe packet we receive feedback about.
// Returns the estimated bitrate if the probe completes a valid cluster.
absl::optional<DataRate> HandleProbeAndEstimateBitrate(
const PacketResult& packet_feedback);
absl::optional<DataRate> FetchAndResetLastEstimatedBitrate();
private:
struct AggregatedCluster {
int num_probes = 0;
Timestamp first_send = Timestamp::PlusInfinity();
Timestamp last_send = Timestamp::MinusInfinity();
Timestamp first_receive = Timestamp::PlusInfinity();
Timestamp last_receive = Timestamp::MinusInfinity();
DataSize size_last_send = DataSize::Zero();
DataSize size_first_receive = DataSize::Zero();
DataSize size_total = DataSize::Zero();
};
// Erases old cluster data that was seen before `timestamp`.
void EraseOldClusters(Timestamp timestamp);
std::map<int, AggregatedCluster> clusters_;
RtcEventLog* const event_log_;
absl::optional<DataRate> estimated_data_rate_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_

View file

@ -0,0 +1,555 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/probe_controller.h"
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <vector>
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
// Maximum waiting time from the time of initiating probing to getting
// the measured results back.
constexpr TimeDelta kMaxWaitingTimeForProbingResult = TimeDelta::Seconds(1);
// Default probing bitrate limit. Applied only when the application didn't
// specify max bitrate.
constexpr DataRate kDefaultMaxProbingBitrate = DataRate::KilobitsPerSec(5000);
// If the bitrate drops to a factor `kBitrateDropThreshold` or lower
// and we recover within `kBitrateDropTimeoutMs`, then we'll send
// a probe at a fraction `kProbeFractionAfterDrop` of the original bitrate.
constexpr double kBitrateDropThreshold = 0.66;
constexpr TimeDelta kBitrateDropTimeout = TimeDelta::Seconds(5);
constexpr double kProbeFractionAfterDrop = 0.85;
// Timeout for probing after leaving ALR. If the bitrate drops significantly,
// (as determined by the delay based estimator) and we leave ALR, then we will
// send a probe if we recover within `kLeftAlrTimeoutMs` ms.
constexpr TimeDelta kAlrEndedTimeout = TimeDelta::Seconds(3);
// The expected uncertainty of probe result (as a fraction of the target probe
// This is a limit on how often probing can be done when there is a BW
// drop detected in ALR.
constexpr TimeDelta kMinTimeBetweenAlrProbes = TimeDelta::Seconds(5);
// bitrate). Used to avoid probing if the probe bitrate is close to our current
// estimate.
constexpr double kProbeUncertainty = 0.05;
// Use probing to recover faster after large bitrate estimate drops.
constexpr char kBweRapidRecoveryExperiment[] =
"WebRTC-BweRapidRecoveryExperiment";
void MaybeLogProbeClusterCreated(RtcEventLog* event_log,
const ProbeClusterConfig& probe) {
RTC_DCHECK(event_log);
if (!event_log) {
return;
}
DataSize min_data_size = probe.target_data_rate * probe.target_duration;
event_log->Log(std::make_unique<RtcEventProbeClusterCreated>(
probe.id, probe.target_data_rate.bps(), probe.target_probe_count,
min_data_size.bytes()));
}
} // namespace
ProbeControllerConfig::ProbeControllerConfig(
const FieldTrialsView* key_value_config)
: first_exponential_probe_scale("p1", 3.0),
second_exponential_probe_scale("p2", 6.0),
further_exponential_probe_scale("step_size", 2),
further_probe_threshold("further_probe_threshold", 0.7),
alr_probing_interval("alr_interval", TimeDelta::Seconds(5)),
alr_probe_scale("alr_scale", 2),
network_state_estimate_probing_interval("network_state_interval",
TimeDelta::PlusInfinity()),
probe_if_estimate_lower_than_network_state_estimate_ratio(
"est_lower_than_network_ratio",
0),
estimate_lower_than_network_state_estimate_probing_interval(
"est_lower_than_network_interval",
TimeDelta::Seconds(3)),
network_state_probe_scale("network_state_scale", 1.0),
network_state_probe_duration("network_state_probe_duration",
TimeDelta::Millis(15)),
probe_on_max_allocated_bitrate_change("probe_max_allocation", true),
first_allocation_probe_scale("alloc_p1", 1),
second_allocation_probe_scale("alloc_p2", 2),
allocation_probe_limit_by_current_scale("alloc_current_bwe_limit"),
min_probe_packets_sent("min_probe_packets_sent", 5),
min_probe_duration("min_probe_duration", TimeDelta::Millis(15)),
loss_limited_probe_scale("loss_limited_scale", 1.5),
skip_if_estimate_larger_than_fraction_of_max(
"skip_if_est_larger_than_fraction_of_max",
0.0) {
ParseFieldTrial(
{&first_exponential_probe_scale, &second_exponential_probe_scale,
&further_exponential_probe_scale, &further_probe_threshold,
&alr_probing_interval, &alr_probe_scale,
&probe_on_max_allocated_bitrate_change, &first_allocation_probe_scale,
&second_allocation_probe_scale, &allocation_probe_limit_by_current_scale,
&min_probe_duration, &network_state_estimate_probing_interval,
&probe_if_estimate_lower_than_network_state_estimate_ratio,
&estimate_lower_than_network_state_estimate_probing_interval,
&network_state_probe_scale, &network_state_probe_duration,
&min_probe_packets_sent, &loss_limited_probe_scale,
&skip_if_estimate_larger_than_fraction_of_max},
key_value_config->Lookup("WebRTC-Bwe-ProbingConfiguration"));
// Specialized keys overriding subsets of WebRTC-Bwe-ProbingConfiguration
ParseFieldTrial(
{&first_exponential_probe_scale, &second_exponential_probe_scale},
key_value_config->Lookup("WebRTC-Bwe-InitialProbing"));
ParseFieldTrial({&further_exponential_probe_scale, &further_probe_threshold},
key_value_config->Lookup("WebRTC-Bwe-ExponentialProbing"));
ParseFieldTrial(
{&alr_probing_interval, &alr_probe_scale, &loss_limited_probe_scale},
key_value_config->Lookup("WebRTC-Bwe-AlrProbing"));
ParseFieldTrial(
{&first_allocation_probe_scale, &second_allocation_probe_scale,
&allocation_probe_limit_by_current_scale},
key_value_config->Lookup("WebRTC-Bwe-AllocationProbing"));
ParseFieldTrial({&min_probe_packets_sent, &min_probe_duration},
key_value_config->Lookup("WebRTC-Bwe-ProbingBehavior"));
}
ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) =
default;
ProbeControllerConfig::~ProbeControllerConfig() = default;
ProbeController::ProbeController(const FieldTrialsView* key_value_config,
RtcEventLog* event_log)
: network_available_(false),
enable_periodic_alr_probing_(false),
in_rapid_recovery_experiment_(absl::StartsWith(
key_value_config->Lookup(kBweRapidRecoveryExperiment),
"Enabled")),
event_log_(event_log),
config_(ProbeControllerConfig(key_value_config)) {
Reset(Timestamp::Zero());
}
ProbeController::~ProbeController() {}
std::vector<ProbeClusterConfig> ProbeController::SetBitrates(
DataRate min_bitrate,
DataRate start_bitrate,
DataRate max_bitrate,
Timestamp at_time) {
if (start_bitrate > DataRate::Zero()) {
start_bitrate_ = start_bitrate;
estimated_bitrate_ = start_bitrate;
} else if (start_bitrate_.IsZero()) {
start_bitrate_ = min_bitrate;
}
// The reason we use the variable `old_max_bitrate_pbs` is because we
// need to set `max_bitrate_` before we call InitiateProbing.
DataRate old_max_bitrate = max_bitrate_;
max_bitrate_ =
max_bitrate.IsFinite() ? max_bitrate : kDefaultMaxProbingBitrate;
switch (state_) {
case State::kInit:
if (network_available_)
return InitiateExponentialProbing(at_time);
break;
case State::kWaitingForProbingResult:
break;
case State::kProbingComplete:
// If the new max bitrate is higher than both the old max bitrate and the
// estimate then initiate probing.
if (!estimated_bitrate_.IsZero() && old_max_bitrate < max_bitrate_ &&
estimated_bitrate_ < max_bitrate_) {
return InitiateProbing(at_time, {max_bitrate_}, false);
}
break;
}
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::OnMaxTotalAllocatedBitrate(
DataRate max_total_allocated_bitrate,
Timestamp at_time) {
const bool in_alr = alr_start_time_.has_value();
const bool allow_allocation_probe = in_alr;
if (config_.probe_on_max_allocated_bitrate_change &&
state_ == State::kProbingComplete &&
max_total_allocated_bitrate != max_total_allocated_bitrate_ &&
estimated_bitrate_ < max_bitrate_ &&
estimated_bitrate_ < max_total_allocated_bitrate &&
allow_allocation_probe) {
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
if (!config_.first_allocation_probe_scale)
return std::vector<ProbeClusterConfig>();
DataRate first_probe_rate = max_total_allocated_bitrate *
config_.first_allocation_probe_scale.Value();
DataRate current_bwe_limit =
!config_.allocation_probe_limit_by_current_scale
? DataRate::PlusInfinity()
: estimated_bitrate_ *
config_.allocation_probe_limit_by_current_scale.Value();
bool limited_by_current_bwe = current_bwe_limit < first_probe_rate;
if (limited_by_current_bwe) {
first_probe_rate = current_bwe_limit;
}
std::vector<DataRate> probes = {first_probe_rate};
if (!limited_by_current_bwe && config_.second_allocation_probe_scale) {
DataRate second_probe_rate =
max_total_allocated_bitrate *
config_.second_allocation_probe_scale.Value();
limited_by_current_bwe = current_bwe_limit < second_probe_rate;
if (limited_by_current_bwe) {
second_probe_rate = current_bwe_limit;
}
if (second_probe_rate > first_probe_rate)
probes.push_back(second_probe_rate);
}
bool allow_further_probing = limited_by_current_bwe;
return InitiateProbing(at_time, probes, allow_further_probing);
}
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::OnNetworkAvailability(
NetworkAvailability msg) {
network_available_ = msg.network_available;
if (!network_available_ && state_ == State::kWaitingForProbingResult) {
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
}
if (network_available_ && state_ == State::kInit && !start_bitrate_.IsZero())
return InitiateExponentialProbing(msg.at_time);
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::InitiateExponentialProbing(
Timestamp at_time) {
RTC_DCHECK(network_available_);
RTC_DCHECK(state_ == State::kInit);
RTC_DCHECK_GT(start_bitrate_, DataRate::Zero());
// When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
// 1.2 Mbps to continue probing.
std::vector<DataRate> probes = {config_.first_exponential_probe_scale *
start_bitrate_};
if (config_.second_exponential_probe_scale &&
config_.second_exponential_probe_scale.GetOptional().value() > 0) {
probes.push_back(config_.second_exponential_probe_scale.Value() *
start_bitrate_);
}
return InitiateProbing(at_time, probes, true);
}
std::vector<ProbeClusterConfig> ProbeController::SetEstimatedBitrate(
DataRate bitrate,
BandwidthLimitedCause bandwidth_limited_cause,
Timestamp at_time) {
bandwidth_limited_cause_ = bandwidth_limited_cause;
if (bitrate < kBitrateDropThreshold * estimated_bitrate_) {
time_of_last_large_drop_ = at_time;
bitrate_before_last_large_drop_ = estimated_bitrate_;
}
estimated_bitrate_ = bitrate;
if (state_ == State::kWaitingForProbingResult) {
// Continue probing if probing results indicate channel has greater
// capacity.
DataRate network_state_estimate_probe_further_limit =
config_.network_state_estimate_probing_interval->IsFinite() &&
network_estimate_
? network_estimate_->link_capacity_upper *
config_.further_probe_threshold
: DataRate::PlusInfinity();
RTC_LOG(LS_INFO) << "Measured bitrate: " << bitrate
<< " Minimum to probe further: "
<< min_bitrate_to_probe_further_ << " upper limit: "
<< network_state_estimate_probe_further_limit;
if (bitrate > min_bitrate_to_probe_further_ &&
bitrate <= network_state_estimate_probe_further_limit) {
return InitiateProbing(
at_time, {config_.further_exponential_probe_scale * bitrate}, true);
}
}
return {};
}
void ProbeController::EnablePeriodicAlrProbing(bool enable) {
enable_periodic_alr_probing_ = enable;
}
void ProbeController::SetAlrStartTimeMs(
absl::optional<int64_t> alr_start_time_ms) {
if (alr_start_time_ms) {
alr_start_time_ = Timestamp::Millis(*alr_start_time_ms);
} else {
alr_start_time_ = absl::nullopt;
}
}
void ProbeController::SetAlrEndedTimeMs(int64_t alr_end_time_ms) {
alr_end_time_.emplace(Timestamp::Millis(alr_end_time_ms));
}
std::vector<ProbeClusterConfig> ProbeController::RequestProbe(
Timestamp at_time) {
// Called once we have returned to normal state after a large drop in
// estimated bandwidth. The current response is to initiate a single probe
// session (if not already probing) at the previous bitrate.
//
// If the probe session fails, the assumption is that this drop was a
// real one from a competing flow or a network change.
bool in_alr = alr_start_time_.has_value();
bool alr_ended_recently =
(alr_end_time_.has_value() &&
at_time - alr_end_time_.value() < kAlrEndedTimeout);
if (in_alr || alr_ended_recently || in_rapid_recovery_experiment_) {
if (state_ == State::kProbingComplete) {
DataRate suggested_probe =
kProbeFractionAfterDrop * bitrate_before_last_large_drop_;
DataRate min_expected_probe_result =
(1 - kProbeUncertainty) * suggested_probe;
TimeDelta time_since_drop = at_time - time_of_last_large_drop_;
TimeDelta time_since_probe = at_time - last_bwe_drop_probing_time_;
if (min_expected_probe_result > estimated_bitrate_ &&
time_since_drop < kBitrateDropTimeout &&
time_since_probe > kMinTimeBetweenAlrProbes) {
RTC_LOG(LS_INFO) << "Detected big bandwidth drop, start probing.";
// Track how often we probe in response to bandwidth drop in ALR.
RTC_HISTOGRAM_COUNTS_10000(
"WebRTC.BWE.BweDropProbingIntervalInS",
(at_time - last_bwe_drop_probing_time_).seconds());
last_bwe_drop_probing_time_ = at_time;
return InitiateProbing(at_time, {suggested_probe}, false);
}
}
}
return std::vector<ProbeClusterConfig>();
}
void ProbeController::SetNetworkStateEstimate(
webrtc::NetworkStateEstimate estimate) {
network_estimate_ = estimate;
}
void ProbeController::Reset(Timestamp at_time) {
bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited;
state_ = State::kInit;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
time_last_probing_initiated_ = Timestamp::Zero();
estimated_bitrate_ = DataRate::Zero();
network_estimate_ = absl::nullopt;
start_bitrate_ = DataRate::Zero();
max_bitrate_ = kDefaultMaxProbingBitrate;
Timestamp now = at_time;
last_bwe_drop_probing_time_ = now;
alr_end_time_.reset();
time_of_last_large_drop_ = now;
bitrate_before_last_large_drop_ = DataRate::Zero();
max_total_allocated_bitrate_ = DataRate::Zero();
}
bool ProbeController::TimeForAlrProbe(Timestamp at_time) const {
if (enable_periodic_alr_probing_ && alr_start_time_) {
Timestamp next_probe_time =
std::max(*alr_start_time_, time_last_probing_initiated_) +
config_.alr_probing_interval;
return at_time >= next_probe_time;
}
return false;
}
bool ProbeController::TimeForNetworkStateProbe(Timestamp at_time) const {
if (!network_estimate_ ||
network_estimate_->link_capacity_upper.IsInfinite()) {
return false;
}
bool probe_due_to_low_estimate =
bandwidth_limited_cause_ == BandwidthLimitedCause::kDelayBasedLimited &&
estimated_bitrate_ <
config_.probe_if_estimate_lower_than_network_state_estimate_ratio *
network_estimate_->link_capacity_upper;
if (probe_due_to_low_estimate &&
config_.estimate_lower_than_network_state_estimate_probing_interval
->IsFinite()) {
Timestamp next_probe_time =
time_last_probing_initiated_ +
config_.estimate_lower_than_network_state_estimate_probing_interval;
return at_time >= next_probe_time;
}
bool periodic_probe =
estimated_bitrate_ < network_estimate_->link_capacity_upper;
if (periodic_probe &&
config_.network_state_estimate_probing_interval->IsFinite()) {
Timestamp next_probe_time = time_last_probing_initiated_ +
config_.network_state_estimate_probing_interval;
return at_time >= next_probe_time;
}
return false;
}
std::vector<ProbeClusterConfig> ProbeController::Process(Timestamp at_time) {
if (at_time - time_last_probing_initiated_ >
kMaxWaitingTimeForProbingResult) {
if (state_ == State::kWaitingForProbingResult) {
RTC_LOG(LS_INFO) << "kWaitingForProbingResult: timeout";
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
}
}
if (estimated_bitrate_.IsZero() || state_ != State::kProbingComplete) {
return {};
}
if (TimeForAlrProbe(at_time) || TimeForNetworkStateProbe(at_time)) {
return InitiateProbing(
at_time, {estimated_bitrate_ * config_.alr_probe_scale}, true);
}
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
Timestamp now,
std::vector<DataRate> bitrates_to_probe,
bool probe_further) {
if (config_.skip_if_estimate_larger_than_fraction_of_max > 0) {
DataRate network_estimate = network_estimate_
? network_estimate_->link_capacity_upper
: DataRate::PlusInfinity();
DataRate max_probe_rate =
max_total_allocated_bitrate_.IsZero()
? max_bitrate_
: std::min(max_total_allocated_bitrate_, max_bitrate_);
if (std::min(network_estimate, estimated_bitrate_) >
config_.skip_if_estimate_larger_than_fraction_of_max * max_probe_rate) {
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
return {};
}
}
DataRate max_probe_bitrate = max_bitrate_;
if (max_total_allocated_bitrate_ > DataRate::Zero()) {
// If a max allocated bitrate has been configured, allow probing up to 2x
// that rate. This allows some overhead to account for bursty streams,
// which otherwise would have to ramp up when the overshoot is already in
// progress.
// It also avoids minor quality reduction caused by probes often being
// received at slightly less than the target probe bitrate.
max_probe_bitrate =
std::min(max_probe_bitrate, max_total_allocated_bitrate_ * 2);
}
DataRate estimate_capped_bitrate = DataRate::PlusInfinity();
switch (bandwidth_limited_cause_) {
case BandwidthLimitedCause::kRttBasedBackOffHighRtt:
case BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased:
case BandwidthLimitedCause::kLossLimitedBwe:
RTC_LOG(LS_INFO) << "Not sending probe in bandwidth limited state.";
return {};
case BandwidthLimitedCause::kLossLimitedBweIncreasing:
estimate_capped_bitrate =
std::min(max_probe_bitrate,
estimated_bitrate_ * config_.loss_limited_probe_scale);
break;
case BandwidthLimitedCause::kDelayBasedLimited:
break;
default:
break;
}
if (config_.network_state_estimate_probing_interval->IsFinite() &&
network_estimate_ && network_estimate_->link_capacity_upper.IsFinite()) {
if (network_estimate_->link_capacity_upper.IsZero()) {
RTC_LOG(LS_INFO) << "Not sending probe, Network state estimate is zero";
return {};
}
estimate_capped_bitrate = std::min(
{estimate_capped_bitrate, max_probe_bitrate,
std::max(estimated_bitrate_, network_estimate_->link_capacity_upper *
config_.network_state_probe_scale)});
}
std::vector<ProbeClusterConfig> pending_probes;
for (DataRate bitrate : bitrates_to_probe) {
RTC_DCHECK(!bitrate.IsZero());
bitrate = std::min(bitrate, estimate_capped_bitrate);
if (bitrate > max_probe_bitrate) {
bitrate = max_probe_bitrate;
probe_further = false;
}
ProbeClusterConfig config;
config.at_time = now;
config.target_data_rate = bitrate;
if (network_estimate_ &&
config_.network_state_estimate_probing_interval->IsFinite()) {
config.target_duration = config_.network_state_probe_duration;
} else {
config.target_duration = config_.min_probe_duration;
}
config.target_probe_count = config_.min_probe_packets_sent;
config.id = next_probe_cluster_id_;
next_probe_cluster_id_++;
MaybeLogProbeClusterCreated(event_log_, config);
pending_probes.push_back(config);
}
time_last_probing_initiated_ = now;
if (probe_further) {
state_ = State::kWaitingForProbingResult;
// Dont expect probe results to be larger than a fraction of the actual
// probe rate.
min_bitrate_to_probe_further_ =
std::min(estimate_capped_bitrate, (*(bitrates_to_probe.end() - 1))) *
config_.further_probe_threshold;
} else {
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
}
return pending_probes;
}
} // namespace webrtc

View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
#include <stdint.h>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
struct ProbeControllerConfig {
explicit ProbeControllerConfig(const FieldTrialsView* key_value_config);
ProbeControllerConfig(const ProbeControllerConfig&);
ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default;
~ProbeControllerConfig();
// These parameters configure the initial probes. First we send one or two
// probes of sizes p1 * start_bitrate_ and p2 * start_bitrate_.
// Then whenever we get a bitrate estimate of at least further_probe_threshold
// times the size of the last sent probe we'll send another one of size
// step_size times the new estimate.
FieldTrialParameter<double> first_exponential_probe_scale;
FieldTrialOptional<double> second_exponential_probe_scale;
FieldTrialParameter<double> further_exponential_probe_scale;
FieldTrialParameter<double> further_probe_threshold;
// Configures how often we send ALR probes and how big they are.
FieldTrialParameter<TimeDelta> alr_probing_interval;
FieldTrialParameter<double> alr_probe_scale;
// Configures how often we send probes if NetworkStateEstimate is available.
FieldTrialParameter<TimeDelta> network_state_estimate_probing_interval;
// Periodically probe as long as the the ratio beteeen current estimate and
// NetworkStateEstimate is lower then this.
FieldTrialParameter<double>
probe_if_estimate_lower_than_network_state_estimate_ratio;
FieldTrialParameter<TimeDelta>
estimate_lower_than_network_state_estimate_probing_interval;
FieldTrialParameter<double> network_state_probe_scale;
// Overrides min_probe_duration if network_state_estimate_probing_interval
// is set and a network state estimate is known.
FieldTrialParameter<TimeDelta> network_state_probe_duration;
// Configures the probes emitted by changed to the allocated bitrate.
FieldTrialParameter<bool> probe_on_max_allocated_bitrate_change;
FieldTrialOptional<double> first_allocation_probe_scale;
FieldTrialOptional<double> second_allocation_probe_scale;
FieldTrialOptional<double> allocation_probe_limit_by_current_scale;
// The minimum number probing packets used.
FieldTrialParameter<int> min_probe_packets_sent;
// The minimum probing duration.
FieldTrialParameter<TimeDelta> min_probe_duration;
FieldTrialParameter<double> loss_limited_probe_scale;
// Dont send a probe if min(estimate, network state estimate) is larger than
// this fraction of the set max bitrate.
FieldTrialParameter<double> skip_if_estimate_larger_than_fraction_of_max;
};
// Reason that bandwidth estimate is limited. Bandwidth estimate can be limited
// by either delay based bwe, or loss based bwe when it increases/decreases the
// estimate.
enum class BandwidthLimitedCause {
kLossLimitedBweIncreasing = 0,
kLossLimitedBwe = 1,
kDelayBasedLimited = 2,
kDelayBasedLimitedDelayIncreased = 3,
kRttBasedBackOffHighRtt = 4
};
// This class controls initiation of probing to estimate initial channel
// capacity. There is also support for probing during a session when max
// bitrate is adjusted by an application.
class ProbeController {
public:
explicit ProbeController(const FieldTrialsView* key_value_config,
RtcEventLog* event_log);
~ProbeController();
ProbeController(const ProbeController&) = delete;
ProbeController& operator=(const ProbeController&) = delete;
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> SetBitrates(
DataRate min_bitrate,
DataRate start_bitrate,
DataRate max_bitrate,
Timestamp at_time);
// The total bitrate, as opposed to the max bitrate, is the sum of the
// configured bitrates for all active streams.
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig>
OnMaxTotalAllocatedBitrate(DataRate max_total_allocated_bitrate,
Timestamp at_time);
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> OnNetworkAvailability(
NetworkAvailability msg);
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> SetEstimatedBitrate(
DataRate bitrate,
BandwidthLimitedCause bandwidth_limited_cause,
Timestamp at_time);
void EnablePeriodicAlrProbing(bool enable);
void SetAlrStartTimeMs(absl::optional<int64_t> alr_start_time);
void SetAlrEndedTimeMs(int64_t alr_end_time);
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> RequestProbe(
Timestamp at_time);
void SetNetworkStateEstimate(webrtc::NetworkStateEstimate estimate);
// Resets the ProbeController to a state equivalent to as if it was just
// created EXCEPT for `enable_periodic_alr_probing_` and
// `network_available_`.
void Reset(Timestamp at_time);
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> Process(
Timestamp at_time);
private:
enum class State {
// Initial state where no probing has been triggered yet.
kInit,
// Waiting for probing results to continue further probing.
kWaitingForProbingResult,
// Probing is complete.
kProbingComplete,
};
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig>
InitiateExponentialProbing(Timestamp at_time);
ABSL_MUST_USE_RESULT std::vector<ProbeClusterConfig> InitiateProbing(
Timestamp now,
std::vector<DataRate> bitrates_to_probe,
bool probe_further);
bool TimeForAlrProbe(Timestamp at_time) const;
bool TimeForNetworkStateProbe(Timestamp at_time) const;
bool network_available_;
BandwidthLimitedCause bandwidth_limited_cause_ =
BandwidthLimitedCause::kDelayBasedLimited;
State state_;
DataRate min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
Timestamp time_last_probing_initiated_ = Timestamp::MinusInfinity();
DataRate estimated_bitrate_ = DataRate::Zero();
absl::optional<webrtc::NetworkStateEstimate> network_estimate_;
DataRate start_bitrate_ = DataRate::Zero();
DataRate max_bitrate_ = DataRate::PlusInfinity();
Timestamp last_bwe_drop_probing_time_ = Timestamp::Zero();
absl::optional<Timestamp> alr_start_time_;
absl::optional<Timestamp> alr_end_time_;
bool enable_periodic_alr_probing_;
Timestamp time_of_last_large_drop_ = Timestamp::MinusInfinity();
DataRate bitrate_before_last_large_drop_ = DataRate::Zero();
DataRate max_total_allocated_bitrate_ = DataRate::Zero();
const bool in_rapid_recovery_experiment_;
RtcEventLog* event_log_;
int32_t next_probe_cluster_id_ = 1;
ProbeControllerConfig config_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h"
#include <stddef.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
RobustThroughputEstimator::RobustThroughputEstimator(
const RobustThroughputEstimatorSettings& settings)
: settings_(settings),
latest_discarded_send_time_(Timestamp::MinusInfinity()) {
RTC_DCHECK(settings.enabled);
}
RobustThroughputEstimator::~RobustThroughputEstimator() {}
bool RobustThroughputEstimator::FirstPacketOutsideWindow() {
if (window_.empty())
return false;
if (window_.size() > settings_.max_window_packets)
return true;
TimeDelta current_window_duration =
window_.back().receive_time - window_.front().receive_time;
if (current_window_duration > settings_.max_window_duration)
return true;
if (window_.size() > settings_.window_packets &&
current_window_duration > settings_.min_window_duration) {
return true;
}
return false;
}
void RobustThroughputEstimator::IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) {
RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(),
packet_feedback_vector.end(),
PacketResult::ReceiveTimeOrder()));
for (const auto& packet : packet_feedback_vector) {
// Ignore packets without valid send or receive times.
// (This should not happen in production since lost packets are filtered
// out before passing the feedback vector to the throughput estimator.
// However, explicitly handling this case makes the estimator more robust
// and avoids a hard-to-detect bad state.)
if (packet.receive_time.IsInfinite() ||
packet.sent_packet.send_time.IsInfinite()) {
continue;
}
// Insert the new packet.
window_.push_back(packet);
window_.back().sent_packet.prior_unacked_data =
window_.back().sent_packet.prior_unacked_data *
settings_.unacked_weight;
// In most cases, receive timestamps should already be in order, but in the
// rare case where feedback packets have been reordered, we do some swaps to
// ensure that the window is sorted.
for (size_t i = window_.size() - 1;
i > 0 && window_[i].receive_time < window_[i - 1].receive_time; i--) {
std::swap(window_[i], window_[i - 1]);
}
constexpr TimeDelta kMaxReorderingTime = TimeDelta::Seconds(1);
const TimeDelta receive_delta =
(window_.back().receive_time - packet.receive_time);
if (receive_delta > kMaxReorderingTime) {
RTC_LOG(LS_WARNING)
<< "Severe packet re-ordering or timestamps offset changed: "
<< receive_delta;
window_.clear();
latest_discarded_send_time_ = Timestamp::MinusInfinity();
}
}
// Remove old packets.
while (FirstPacketOutsideWindow()) {
latest_discarded_send_time_ = std::max(
latest_discarded_send_time_, window_.front().sent_packet.send_time);
window_.pop_front();
}
}
absl::optional<DataRate> RobustThroughputEstimator::bitrate() const {
if (window_.empty() || window_.size() < settings_.required_packets)
return absl::nullopt;
TimeDelta largest_recv_gap(TimeDelta::Zero());
TimeDelta second_largest_recv_gap(TimeDelta::Zero());
for (size_t i = 1; i < window_.size(); i++) {
// Find receive time gaps.
TimeDelta gap = window_[i].receive_time - window_[i - 1].receive_time;
if (gap > largest_recv_gap) {
second_largest_recv_gap = largest_recv_gap;
largest_recv_gap = gap;
} else if (gap > second_largest_recv_gap) {
second_largest_recv_gap = gap;
}
}
Timestamp first_send_time = Timestamp::PlusInfinity();
Timestamp last_send_time = Timestamp::MinusInfinity();
Timestamp first_recv_time = Timestamp::PlusInfinity();
Timestamp last_recv_time = Timestamp::MinusInfinity();
DataSize recv_size = DataSize::Bytes(0);
DataSize send_size = DataSize::Bytes(0);
DataSize first_recv_size = DataSize::Bytes(0);
DataSize last_send_size = DataSize::Bytes(0);
size_t num_sent_packets_in_window = 0;
for (const auto& packet : window_) {
if (packet.receive_time < first_recv_time) {
first_recv_time = packet.receive_time;
first_recv_size =
packet.sent_packet.size + packet.sent_packet.prior_unacked_data;
}
last_recv_time = std::max(last_recv_time, packet.receive_time);
recv_size += packet.sent_packet.size;
recv_size += packet.sent_packet.prior_unacked_data;
if (packet.sent_packet.send_time < latest_discarded_send_time_) {
// If we have dropped packets from the window that were sent after
// this packet, then this packet was reordered. Ignore it from
// the send rate computation (since the send time may be very far
// in the past, leading to underestimation of the send rate.)
// However, ignoring packets creates a risk that we end up without
// any packets left to compute a send rate.
continue;
}
if (packet.sent_packet.send_time > last_send_time) {
last_send_time = packet.sent_packet.send_time;
last_send_size =
packet.sent_packet.size + packet.sent_packet.prior_unacked_data;
}
first_send_time = std::min(first_send_time, packet.sent_packet.send_time);
send_size += packet.sent_packet.size;
send_size += packet.sent_packet.prior_unacked_data;
++num_sent_packets_in_window;
}
// Suppose a packet of size S is sent every T milliseconds.
// A window of N packets would contain N*S bytes, but the time difference
// between the first and the last packet would only be (N-1)*T. Thus, we
// need to remove the size of one packet to get the correct rate of S/T.
// Which packet to remove (if the packets have varying sizes),
// depends on the network model.
// Suppose that 2 packets with sizes s1 and s2, are received at times t1
// and t2, respectively. If the packets were transmitted back to back over
// a bottleneck with rate capacity r, then we'd expect t2 = t1 + r * s2.
// Thus, r = (t2-t1) / s2, so the size of the first packet doesn't affect
// the difference between t1 and t2.
// Analoguously, if the first packet is sent at time t1 and the sender
// paces the packets at rate r, then the second packet can be sent at time
// t2 = t1 + r * s1. Thus, the send rate estimate r = (t2-t1) / s1 doesn't
// depend on the size of the last packet.
recv_size -= first_recv_size;
send_size -= last_send_size;
// Remove the largest gap by replacing it by the second largest gap.
// This is to ensure that spurious "delay spikes" (i.e. when the
// network stops transmitting packets for a short period, followed
// by a burst of delayed packets), don't cause the estimate to drop.
// This could cause an overestimation, which we guard against by
// never returning an estimate above the send rate.
RTC_DCHECK(first_recv_time.IsFinite());
RTC_DCHECK(last_recv_time.IsFinite());
TimeDelta recv_duration = (last_recv_time - first_recv_time) -
largest_recv_gap + second_largest_recv_gap;
recv_duration = std::max(recv_duration, TimeDelta::Millis(1));
if (num_sent_packets_in_window < settings_.required_packets) {
// Too few send times to calculate a reliable send rate.
return recv_size / recv_duration;
}
RTC_DCHECK(first_send_time.IsFinite());
RTC_DCHECK(last_send_time.IsFinite());
TimeDelta send_duration = last_send_time - first_send_time;
send_duration = std::max(send_duration, TimeDelta::Millis(1));
return std::min(send_size / send_duration, recv_size / recv_duration);
}
} // namespace webrtc

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_
#include <deque>
#include <vector>
#include "absl/types/optional.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h"
namespace webrtc {
class RobustThroughputEstimator : public AcknowledgedBitrateEstimatorInterface {
public:
explicit RobustThroughputEstimator(
const RobustThroughputEstimatorSettings& settings);
~RobustThroughputEstimator() override;
void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) override;
absl::optional<DataRate> bitrate() const override;
absl::optional<DataRate> PeekRate() const override { return bitrate(); }
void SetAlr(bool /*in_alr*/) override {}
void SetAlrEndedTime(Timestamp /*alr_ended_time*/) override {}
private:
bool FirstPacketOutsideWindow();
const RobustThroughputEstimatorSettings settings_;
std::deque<PacketResult> window_;
Timestamp latest_discarded_send_time_ = Timestamp::MinusInfinity();
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_

View file

@ -0,0 +1,711 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h"
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000);
constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300);
constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000);
constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000);
constexpr int kLimitNumPackets = 20;
constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000);
constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000);
constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000);
// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals.
constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
constexpr float kDefaultLowLossThreshold = 0.02f;
constexpr float kDefaultHighLossThreshold = 0.1f;
constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero();
struct UmaRampUpMetric {
const char* metric_name;
int bitrate_kbps;
};
const UmaRampUpMetric kUmaRampupMetrics[] = {
{"WebRTC.BWE.RampUpTimeTo500kbpsInMs", 500},
{"WebRTC.BWE.RampUpTimeTo1000kbpsInMs", 1000},
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
const size_t kNumUmaRampupMetrics =
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
const char kBweLosExperiment[] = "WebRTC-BweLossExperiment";
bool BweLossExperimentIsEnabled() {
std::string experiment_string =
webrtc::field_trial::FindFullName(kBweLosExperiment);
// The experiment is enabled iff the field trial string begins with "Enabled".
return absl::StartsWith(experiment_string, "Enabled");
}
bool ReadBweLossExperimentParameters(float* low_loss_threshold,
float* high_loss_threshold,
uint32_t* bitrate_threshold_kbps) {
RTC_DCHECK(low_loss_threshold);
RTC_DCHECK(high_loss_threshold);
RTC_DCHECK(bitrate_threshold_kbps);
std::string experiment_string =
webrtc::field_trial::FindFullName(kBweLosExperiment);
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%f,%f,%u", low_loss_threshold,
high_loss_threshold, bitrate_threshold_kbps);
if (parsed_values == 3) {
RTC_CHECK_GT(*low_loss_threshold, 0.0f)
<< "Loss threshold must be greater than 0.";
RTC_CHECK_LE(*low_loss_threshold, 1.0f)
<< "Loss threshold must be less than or equal to 1.";
RTC_CHECK_GT(*high_loss_threshold, 0.0f)
<< "Loss threshold must be greater than 0.";
RTC_CHECK_LE(*high_loss_threshold, 1.0f)
<< "Loss threshold must be less than or equal to 1.";
RTC_CHECK_LE(*low_loss_threshold, *high_loss_threshold)
<< "The low loss threshold must be less than or equal to the high loss "
"threshold.";
RTC_CHECK_GE(*bitrate_threshold_kbps, 0)
<< "Bitrate threshold can't be negative.";
RTC_CHECK_LT(*bitrate_threshold_kbps,
std::numeric_limits<int>::max() / 1000)
<< "Bitrate must be smaller enough to avoid overflows.";
return true;
}
RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweLossExperiment "
"experiment from field trial string. Using default.";
*low_loss_threshold = kDefaultLowLossThreshold;
*high_loss_threshold = kDefaultHighLossThreshold;
*bitrate_threshold_kbps = kDefaultBitrateThreshold.kbps();
return false;
}
} // namespace
LinkCapacityTracker::LinkCapacityTracker()
: tracking_rate("rate", TimeDelta::Seconds(10)) {
ParseFieldTrial({&tracking_rate},
field_trial::FindFullName("WebRTC-Bwe-LinkCapacity"));
}
LinkCapacityTracker::~LinkCapacityTracker() {}
void LinkCapacityTracker::UpdateDelayBasedEstimate(
Timestamp at_time,
DataRate delay_based_bitrate) {
if (delay_based_bitrate < last_delay_based_estimate_) {
capacity_estimate_bps_ =
std::min(capacity_estimate_bps_, delay_based_bitrate.bps<double>());
last_link_capacity_update_ = at_time;
}
last_delay_based_estimate_ = delay_based_bitrate;
}
void LinkCapacityTracker::OnStartingRate(DataRate start_rate) {
if (last_link_capacity_update_.IsInfinite())
capacity_estimate_bps_ = start_rate.bps<double>();
}
void LinkCapacityTracker::OnRateUpdate(absl::optional<DataRate> acknowledged,
DataRate target,
Timestamp at_time) {
if (!acknowledged)
return;
DataRate acknowledged_target = std::min(*acknowledged, target);
if (acknowledged_target.bps() > capacity_estimate_bps_) {
TimeDelta delta = at_time - last_link_capacity_update_;
double alpha = delta.IsFinite() ? exp(-(delta / tracking_rate.Get())) : 0;
capacity_estimate_bps_ = alpha * capacity_estimate_bps_ +
(1 - alpha) * acknowledged_target.bps<double>();
}
last_link_capacity_update_ = at_time;
}
void LinkCapacityTracker::OnRttBackoff(DataRate backoff_rate,
Timestamp at_time) {
capacity_estimate_bps_ =
std::min(capacity_estimate_bps_, backoff_rate.bps<double>());
last_link_capacity_update_ = at_time;
}
DataRate LinkCapacityTracker::estimate() const {
return DataRate::BitsPerSec(capacity_estimate_bps_);
}
RttBasedBackoff::RttBasedBackoff(const FieldTrialsView* key_value_config)
: disabled_("Disabled"),
configured_limit_("limit", TimeDelta::Seconds(3)),
drop_fraction_("fraction", 0.8),
drop_interval_("interval", TimeDelta::Seconds(1)),
bandwidth_floor_("floor", DataRate::KilobitsPerSec(5)),
rtt_limit_(TimeDelta::PlusInfinity()),
// By initializing this to plus infinity, we make sure that we never
// trigger rtt backoff unless packet feedback is enabled.
last_propagation_rtt_update_(Timestamp::PlusInfinity()),
last_propagation_rtt_(TimeDelta::Zero()),
last_packet_sent_(Timestamp::MinusInfinity()) {
ParseFieldTrial({&disabled_, &configured_limit_, &drop_fraction_,
&drop_interval_, &bandwidth_floor_},
key_value_config->Lookup("WebRTC-Bwe-MaxRttLimit"));
if (!disabled_) {
rtt_limit_ = configured_limit_.Get();
}
}
void RttBasedBackoff::UpdatePropagationRtt(Timestamp at_time,
TimeDelta propagation_rtt) {
last_propagation_rtt_update_ = at_time;
last_propagation_rtt_ = propagation_rtt;
}
bool RttBasedBackoff::IsRttAboveLimit() const {
return CorrectedRtt() > rtt_limit_;
}
TimeDelta RttBasedBackoff::CorrectedRtt() const {
// Avoid timeout when no packets are being sent.
TimeDelta timeout_correction = std::max(
last_packet_sent_ - last_propagation_rtt_update_, TimeDelta::Zero());
return timeout_correction + last_propagation_rtt_;
}
RttBasedBackoff::~RttBasedBackoff() = default;
SendSideBandwidthEstimation::SendSideBandwidthEstimation(
const FieldTrialsView* key_value_config, RtcEventLog* event_log)
: key_value_config_(key_value_config),
rtt_backoff_(key_value_config),
lost_packets_since_last_loss_update_(0),
expected_packets_since_last_loss_update_(0),
current_target_(DataRate::Zero()),
last_logged_target_(DataRate::Zero()),
min_bitrate_configured_(kCongestionControllerMinBitrate),
max_bitrate_configured_(kDefaultMaxBitrate),
last_low_bitrate_log_(Timestamp::MinusInfinity()),
has_decreased_since_last_fraction_loss_(false),
last_loss_feedback_(Timestamp::MinusInfinity()),
last_loss_packet_report_(Timestamp::MinusInfinity()),
last_fraction_loss_(0),
last_logged_fraction_loss_(0),
last_round_trip_time_(TimeDelta::Zero()),
receiver_limit_(DataRate::PlusInfinity()),
delay_based_limit_(DataRate::PlusInfinity()),
time_last_decrease_(Timestamp::MinusInfinity()),
first_report_time_(Timestamp::MinusInfinity()),
initially_lost_packets_(0),
bitrate_at_2_seconds_(DataRate::Zero()),
uma_update_state_(kNoUpdate),
uma_rtt_state_(kNoUpdate),
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
event_log_(event_log),
last_rtc_event_log_(Timestamp::MinusInfinity()),
low_loss_threshold_(kDefaultLowLossThreshold),
high_loss_threshold_(kDefaultHighLossThreshold),
bitrate_threshold_(kDefaultBitrateThreshold),
loss_based_bandwidth_estimator_v1_(key_value_config),
loss_based_bandwidth_estimator_v2_(new LossBasedBweV2(key_value_config)),
loss_based_state_(LossBasedState::kDelayBasedEstimate),
disable_receiver_limit_caps_only_("Disabled") {
RTC_DCHECK(event_log);
if (BweLossExperimentIsEnabled()) {
uint32_t bitrate_threshold_kbps;
if (ReadBweLossExperimentParameters(&low_loss_threshold_,
&high_loss_threshold_,
&bitrate_threshold_kbps)) {
RTC_LOG(LS_INFO) << "Enabled BweLossExperiment with parameters "
<< low_loss_threshold_ << ", " << high_loss_threshold_
<< ", " << bitrate_threshold_kbps;
bitrate_threshold_ = DataRate::KilobitsPerSec(bitrate_threshold_kbps);
}
}
ParseFieldTrial({&disable_receiver_limit_caps_only_},
key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly"));
if (LossBasedBandwidthEstimatorV2Enabled()) {
loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate(
min_bitrate_configured_, max_bitrate_configured_);
}
}
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
void SendSideBandwidthEstimation::OnRouteChange() {
lost_packets_since_last_loss_update_ = 0;
expected_packets_since_last_loss_update_ = 0;
current_target_ = DataRate::Zero();
min_bitrate_configured_ = kCongestionControllerMinBitrate;
max_bitrate_configured_ = kDefaultMaxBitrate;
last_low_bitrate_log_ = Timestamp::MinusInfinity();
has_decreased_since_last_fraction_loss_ = false;
last_loss_feedback_ = Timestamp::MinusInfinity();
last_loss_packet_report_ = Timestamp::MinusInfinity();
last_fraction_loss_ = 0;
last_logged_fraction_loss_ = 0;
last_round_trip_time_ = TimeDelta::Zero();
receiver_limit_ = DataRate::PlusInfinity();
delay_based_limit_ = DataRate::PlusInfinity();
time_last_decrease_ = Timestamp::MinusInfinity();
first_report_time_ = Timestamp::MinusInfinity();
initially_lost_packets_ = 0;
bitrate_at_2_seconds_ = DataRate::Zero();
uma_update_state_ = kNoUpdate;
uma_rtt_state_ = kNoUpdate;
last_rtc_event_log_ = Timestamp::MinusInfinity();
if (LossBasedBandwidthEstimatorV2Enabled() &&
loss_based_bandwidth_estimator_v2_->UseInStartPhase()) {
loss_based_bandwidth_estimator_v2_.reset(
new LossBasedBweV2(key_value_config_));
}
}
void SendSideBandwidthEstimation::SetBitrates(
absl::optional<DataRate> send_bitrate,
DataRate min_bitrate,
DataRate max_bitrate,
Timestamp at_time) {
SetMinMaxBitrate(min_bitrate, max_bitrate);
if (send_bitrate) {
link_capacity_.OnStartingRate(*send_bitrate);
SetSendBitrate(*send_bitrate, at_time);
}
}
void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate,
Timestamp at_time) {
RTC_DCHECK_GT(bitrate, DataRate::Zero());
// Reset to avoid being capped by the estimate.
delay_based_limit_ = DataRate::PlusInfinity();
UpdateTargetBitrate(bitrate, at_time);
// Clear last sent bitrate history so the new value can be used directly
// and not capped.
min_bitrate_history_.clear();
}
void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
DataRate max_bitrate) {
min_bitrate_configured_ =
std::max(min_bitrate, kCongestionControllerMinBitrate);
if (max_bitrate > DataRate::Zero() && max_bitrate.IsFinite()) {
max_bitrate_configured_ = std::max(min_bitrate_configured_, max_bitrate);
} else {
max_bitrate_configured_ = kDefaultMaxBitrate;
}
loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate(min_bitrate_configured_,
max_bitrate_configured_);
}
int SendSideBandwidthEstimation::GetMinBitrate() const {
return min_bitrate_configured_.bps<int>();
}
DataRate SendSideBandwidthEstimation::target_rate() const {
DataRate target = current_target_;
if (!disable_receiver_limit_caps_only_)
target = std::min(target, receiver_limit_);
return std::max(min_bitrate_configured_, target);
}
LossBasedState SendSideBandwidthEstimation::loss_based_state() const {
return loss_based_state_;
}
bool SendSideBandwidthEstimation::IsRttAboveLimit() const {
return rtt_backoff_.IsRttAboveLimit();
}
DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
return link_capacity_.estimate();
}
void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time,
DataRate bandwidth) {
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
// limitation.
receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth;
ApplyTargetLimits(at_time);
}
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time,
DataRate bitrate) {
link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
// limitation.
delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate;
ApplyTargetLimits(at_time);
}
void SendSideBandwidthEstimation::SetAcknowledgedRate(
absl::optional<DataRate> acknowledged_rate,
Timestamp at_time) {
acknowledged_rate_ = acknowledged_rate;
if (!acknowledged_rate.has_value()) {
return;
}
if (LossBasedBandwidthEstimatorV1Enabled()) {
loss_based_bandwidth_estimator_v1_.UpdateAcknowledgedBitrate(
*acknowledged_rate, at_time);
}
if (LossBasedBandwidthEstimatorV2Enabled()) {
loss_based_bandwidth_estimator_v2_->SetAcknowledgedBitrate(
*acknowledged_rate);
}
}
void SendSideBandwidthEstimation::UpdateLossBasedEstimator(
const TransportPacketsFeedback& report,
BandwidthUsage delay_detector_state,
absl::optional<DataRate> probe_bitrate,
bool in_alr) {
if (LossBasedBandwidthEstimatorV1Enabled()) {
loss_based_bandwidth_estimator_v1_.UpdateLossStatistics(
report.packet_feedbacks, report.feedback_time);
}
if (LossBasedBandwidthEstimatorV2Enabled()) {
loss_based_bandwidth_estimator_v2_->UpdateBandwidthEstimate(
report.packet_feedbacks, delay_based_limit_, in_alr);
UpdateEstimate(report.feedback_time);
}
}
void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
int64_t number_of_packets,
Timestamp at_time) {
last_loss_feedback_ = at_time;
if (first_report_time_.IsInfinite())
first_report_time_ = at_time;
// Check sequence number diff and weight loss report
if (number_of_packets > 0) {
int64_t expected =
expected_packets_since_last_loss_update_ + number_of_packets;
// Don't generate a loss rate until it can be based on enough packets.
if (expected < kLimitNumPackets) {
// Accumulate reports.
expected_packets_since_last_loss_update_ = expected;
lost_packets_since_last_loss_update_ += packets_lost;
return;
}
has_decreased_since_last_fraction_loss_ = false;
int64_t lost_q8 =
std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
0)
<< 8;
last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255);
// Reset accumulators.
lost_packets_since_last_loss_update_ = 0;
expected_packets_since_last_loss_update_ = 0;
last_loss_packet_report_ = at_time;
UpdateEstimate(at_time);
}
UpdateUmaStatsPacketsLost(at_time, packets_lost);
}
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time,
int packets_lost) {
DataRate bitrate_kbps =
DataRate::KilobitsPerSec((current_target_.bps() + 500) / 1000);
for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
if (!rampup_uma_stats_updated_[i] &&
bitrate_kbps.kbps() >= kUmaRampupMetrics[i].bitrate_kbps) {
RTC_HISTOGRAMS_COUNTS_100000(i, kUmaRampupMetrics[i].metric_name,
(at_time - first_report_time_).ms());
rampup_uma_stats_updated_[i] = true;
}
}
if (IsInStartPhase(at_time)) {
initially_lost_packets_ += packets_lost;
} else if (uma_update_state_ == kNoUpdate) {
uma_update_state_ = kFirstDone;
bitrate_at_2_seconds_ = bitrate_kbps;
RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitiallyLostPackets",
initially_lost_packets_, 0, 100, 50);
RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialBandwidthEstimate",
bitrate_at_2_seconds_.kbps(), 0, 2000, 50);
} else if (uma_update_state_ == kFirstDone &&
at_time - first_report_time_ >= kBweConverganceTime) {
uma_update_state_ = kDone;
int bitrate_diff_kbps = std::max(
bitrate_at_2_seconds_.kbps<int>() - bitrate_kbps.kbps<int>(), 0);
RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialVsConvergedDiff", bitrate_diff_kbps,
0, 2000, 50);
}
}
void SendSideBandwidthEstimation::UpdateRtt(TimeDelta rtt, Timestamp at_time) {
// Update RTT if we were able to compute an RTT based on this RTCP.
// FlexFEC doesn't send RTCP SR, which means we won't be able to compute RTT.
if (rtt > TimeDelta::Zero())
last_round_trip_time_ = rtt;
if (!IsInStartPhase(at_time) && uma_rtt_state_ == kNoUpdate) {
uma_rtt_state_ = kDone;
RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialRtt", rtt.ms<int>(), 0, 2000, 50);
}
}
void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
if (rtt_backoff_.IsRttAboveLimit()) {
if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
current_target_ > rtt_backoff_.bandwidth_floor_) {
time_last_decrease_ = at_time;
DataRate new_bitrate =
std::max(current_target_ * rtt_backoff_.drop_fraction_,
rtt_backoff_.bandwidth_floor_.Get());
link_capacity_.OnRttBackoff(new_bitrate, at_time);
UpdateTargetBitrate(new_bitrate, at_time);
return;
}
// TODO(srte): This is likely redundant in most cases.
ApplyTargetLimits(at_time);
return;
}
// We trust the REMB and/or delay-based estimate during the first 2 seconds if
// we haven't had any packet loss reported, to allow startup bitrate probing.
if (last_fraction_loss_ == 0 && IsInStartPhase(at_time) &&
!loss_based_bandwidth_estimator_v2_->ReadyToUseInStartPhase()) {
DataRate new_bitrate = current_target_;
// TODO(srte): We should not allow the new_bitrate to be larger than the
// receiver limit here.
if (receiver_limit_.IsFinite())
new_bitrate = std::max(receiver_limit_, new_bitrate);
if (delay_based_limit_.IsFinite())
new_bitrate = std::max(delay_based_limit_, new_bitrate);
if (LossBasedBandwidthEstimatorV1Enabled()) {
loss_based_bandwidth_estimator_v1_.Initialize(new_bitrate);
}
if (new_bitrate != current_target_) {
min_bitrate_history_.clear();
if (LossBasedBandwidthEstimatorV1Enabled()) {
min_bitrate_history_.push_back(std::make_pair(at_time, new_bitrate));
} else {
min_bitrate_history_.push_back(
std::make_pair(at_time, current_target_));
}
UpdateTargetBitrate(new_bitrate, at_time);
return;
}
}
UpdateMinHistory(at_time);
if (last_loss_packet_report_.IsInfinite()) {
// No feedback received.
// TODO(srte): This is likely redundant in most cases.
ApplyTargetLimits(at_time);
return;
}
if (LossBasedBandwidthEstimatorV1ReadyForUse()) {
DataRate new_bitrate = loss_based_bandwidth_estimator_v1_.Update(
at_time, min_bitrate_history_.front().second, delay_based_limit_,
last_round_trip_time_);
UpdateTargetBitrate(new_bitrate, at_time);
return;
}
if (LossBasedBandwidthEstimatorV2ReadyForUse()) {
LossBasedBweV2::Result result =
loss_based_bandwidth_estimator_v2_->GetLossBasedResult();
loss_based_state_ = result.state;
UpdateTargetBitrate(result.bandwidth_estimate, at_time);
return;
}
TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_;
if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) {
// We only care about loss above a given bitrate threshold.
float loss = last_fraction_loss_ / 256.0f;
// We only make decisions based on loss when the bitrate is above a
// threshold. This is a crude way of handling loss which is uncorrelated
// to congestion.
if (current_target_ < bitrate_threshold_ || loss <= low_loss_threshold_) {
// Loss < 2%: Increase rate by 8% of the min bitrate in the last
// kBweIncreaseInterval.
// Note that by remembering the bitrate over the last second one can
// rampup up one second faster than if only allowed to start ramping
// at 8% per second rate now. E.g.:
// If sending a constant 100kbps it can rampup immediately to 108kbps
// whenever a receiver report is received with lower packet loss.
// If instead one would do: current_bitrate_ *= 1.08^(delta time),
// it would take over one second since the lower packet loss to achieve
// 108kbps.
DataRate new_bitrate = DataRate::BitsPerSec(
min_bitrate_history_.front().second.bps() * 1.08 + 0.5);
// Add 1 kbps extra, just to make sure that we do not get stuck
// (gives a little extra increase at low rates, negligible at higher
// rates).
new_bitrate += DataRate::BitsPerSec(1000);
UpdateTargetBitrate(new_bitrate, at_time);
return;
} else if (current_target_ > bitrate_threshold_) {
if (loss <= high_loss_threshold_) {
// Loss between 2% - 10%: Do nothing.
} else {
// Loss > 10%: Limit the rate decreases to once a kBweDecreaseInterval
// + rtt.
if (!has_decreased_since_last_fraction_loss_ &&
(at_time - time_last_decrease_) >=
(kBweDecreaseInterval + last_round_trip_time_)) {
time_last_decrease_ = at_time;
// Reduce rate:
// newRate = rate * (1 - 0.5*lossRate);
// where packetLoss = 256*lossRate;
DataRate new_bitrate = DataRate::BitsPerSec(
(current_target_.bps() *
static_cast<double>(512 - last_fraction_loss_)) /
512.0);
has_decreased_since_last_fraction_loss_ = true;
UpdateTargetBitrate(new_bitrate, at_time);
return;
}
}
}
}
// TODO(srte): This is likely redundant in most cases.
ApplyTargetLimits(at_time);
}
void SendSideBandwidthEstimation::UpdatePropagationRtt(
Timestamp at_time,
TimeDelta propagation_rtt) {
rtt_backoff_.UpdatePropagationRtt(at_time, propagation_rtt);
}
void SendSideBandwidthEstimation::OnSentPacket(const SentPacket& sent_packet) {
// Only feedback-triggering packets will be reported here.
rtt_backoff_.last_packet_sent_ = sent_packet.send_time;
}
bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const {
return first_report_time_.IsInfinite() ||
at_time - first_report_time_ < kStartPhase;
}
void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) {
// Remove old data points from history.
// Since history precision is in ms, add one so it is able to increase
// bitrate if it is off by as little as 0.5ms.
while (!min_bitrate_history_.empty() &&
at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) >
kBweIncreaseInterval) {
min_bitrate_history_.pop_front();
}
// Typical minimum sliding-window algorithm: Pop values higher than current
// bitrate before pushing it.
while (!min_bitrate_history_.empty() &&
current_target_ <= min_bitrate_history_.back().second) {
min_bitrate_history_.pop_back();
}
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
}
DataRate SendSideBandwidthEstimation::GetUpperLimit() const {
DataRate upper_limit = delay_based_limit_;
if (disable_receiver_limit_caps_only_)
upper_limit = std::min(upper_limit, receiver_limit_);
return std::min(upper_limit, max_bitrate_configured_);
}
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate,
Timestamp at_time) {
if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
RTC_LOG(LS_WARNING) << "Estimated available bandwidth " << ToString(bitrate)
<< " is below configured min bitrate "
<< ToString(min_bitrate_configured_) << ".";
last_low_bitrate_log_ = at_time;
}
}
void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(Timestamp at_time) {
if (current_target_ != last_logged_target_ ||
last_fraction_loss_ != last_logged_fraction_loss_ ||
at_time - last_rtc_event_log_ > kRtcEventLogPeriod) {
event_log_->Log(std::make_unique<RtcEventBweUpdateLossBased>(
current_target_.bps(), last_fraction_loss_,
expected_packets_since_last_loss_update_));
last_logged_fraction_loss_ = last_fraction_loss_;
last_logged_target_ = current_target_;
last_rtc_event_log_ = at_time;
}
}
void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate,
Timestamp at_time) {
new_bitrate = std::min(new_bitrate, GetUpperLimit());
if (new_bitrate < min_bitrate_configured_) {
MaybeLogLowBitrateWarning(new_bitrate, at_time);
new_bitrate = min_bitrate_configured_;
}
current_target_ = new_bitrate;
MaybeLogLossBasedEvent(at_time);
link_capacity_.OnRateUpdate(acknowledged_rate_, current_target_, at_time);
}
void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) {
UpdateTargetBitrate(current_target_, at_time);
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1Enabled() const {
return loss_based_bandwidth_estimator_v1_.Enabled() &&
!LossBasedBandwidthEstimatorV2Enabled();
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1ReadyForUse()
const {
return LossBasedBandwidthEstimatorV1Enabled() &&
loss_based_bandwidth_estimator_v1_.InUse();
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2Enabled() const {
return loss_based_bandwidth_estimator_v2_->IsEnabled();
}
bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2ReadyForUse()
const {
return loss_based_bandwidth_estimator_v2_->IsReady();
}
bool SendSideBandwidthEstimation::PaceAtLossBasedEstimate() const {
return LossBasedBandwidthEstimatorV2ReadyForUse() &&
loss_based_bandwidth_estimator_v2_->PaceAtLossBasedEstimate();
}
} // namespace webrtc

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*
* FEC and NACK added bitrate is handled outside class
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
#include <stdint.h>
#include <deque>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h"
#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class RtcEventLog;
class LinkCapacityTracker {
public:
LinkCapacityTracker();
~LinkCapacityTracker();
// Call when a new delay-based estimate is available.
void UpdateDelayBasedEstimate(Timestamp at_time,
DataRate delay_based_bitrate);
void OnStartingRate(DataRate start_rate);
void OnRateUpdate(absl::optional<DataRate> acknowledged,
DataRate target,
Timestamp at_time);
void OnRttBackoff(DataRate backoff_rate, Timestamp at_time);
DataRate estimate() const;
private:
FieldTrialParameter<TimeDelta> tracking_rate;
double capacity_estimate_bps_ = 0;
Timestamp last_link_capacity_update_ = Timestamp::MinusInfinity();
DataRate last_delay_based_estimate_ = DataRate::PlusInfinity();
};
class RttBasedBackoff {
public:
explicit RttBasedBackoff(const FieldTrialsView* key_value_config);
~RttBasedBackoff();
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
bool IsRttAboveLimit() const;
FieldTrialFlag disabled_;
FieldTrialParameter<TimeDelta> configured_limit_;
FieldTrialParameter<double> drop_fraction_;
FieldTrialParameter<TimeDelta> drop_interval_;
FieldTrialParameter<DataRate> bandwidth_floor_;
public:
TimeDelta rtt_limit_;
Timestamp last_propagation_rtt_update_;
TimeDelta last_propagation_rtt_;
Timestamp last_packet_sent_;
private:
TimeDelta CorrectedRtt() const;
};
class SendSideBandwidthEstimation {
public:
SendSideBandwidthEstimation() = delete;
SendSideBandwidthEstimation(const FieldTrialsView* key_value_config,
RtcEventLog* event_log);
~SendSideBandwidthEstimation();
void OnRouteChange();
DataRate target_rate() const;
LossBasedState loss_based_state() const;
// Return whether the current rtt is higher than the rtt limited configured in
// RttBasedBackoff.
bool IsRttAboveLimit() const;
uint8_t fraction_loss() const { return last_fraction_loss_; }
TimeDelta round_trip_time() const { return last_round_trip_time_; }
DataRate GetEstimatedLinkCapacity() const;
// Call periodically to update estimate.
void UpdateEstimate(Timestamp at_time);
void OnSentPacket(const SentPacket& sent_packet);
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
// Call when we receive a RTCP message with TMMBR or REMB.
void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth);
// Call when a new delay-based estimate is available.
void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate);
// Call when we receive a RTCP message with a ReceiveBlock.
void UpdatePacketsLost(int64_t packets_lost,
int64_t number_of_packets,
Timestamp at_time);
// Call when we receive a RTCP message with a ReceiveBlock.
void UpdateRtt(TimeDelta rtt, Timestamp at_time);
void SetBitrates(absl::optional<DataRate> send_bitrate,
DataRate min_bitrate,
DataRate max_bitrate,
Timestamp at_time);
void SetSendBitrate(DataRate bitrate, Timestamp at_time);
void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
int GetMinBitrate() const;
void SetAcknowledgedRate(absl::optional<DataRate> acknowledged_rate,
Timestamp at_time);
void UpdateLossBasedEstimator(const TransportPacketsFeedback& report,
BandwidthUsage delay_detector_state,
absl::optional<DataRate> probe_bitrate,
bool in_alr);
bool PaceAtLossBasedEstimate() const;
private:
friend class GoogCcStatePrinter;
enum UmaState { kNoUpdate, kFirstDone, kDone };
bool IsInStartPhase(Timestamp at_time) const;
void UpdateUmaStatsPacketsLost(Timestamp at_time, int packets_lost);
// Updates history of min bitrates.
// After this method returns min_bitrate_history_.front().second contains the
// min bitrate used during last kBweIncreaseIntervalMs.
void UpdateMinHistory(Timestamp at_time);
// Gets the upper limit for the target bitrate. This is the minimum of the
// delay based limit, the receiver limit and the loss based controller limit.
DataRate GetUpperLimit() const;
// Prints a warning if `bitrate` if sufficiently long time has past since last
// warning.
void MaybeLogLowBitrateWarning(DataRate bitrate, Timestamp at_time);
// Stores an update to the event log if the loss rate has changed, the target
// has changed, or sufficient time has passed since last stored event.
void MaybeLogLossBasedEvent(Timestamp at_time);
// Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and
// set `current_bitrate_` to the capped value and updates the event log.
void UpdateTargetBitrate(DataRate bitrate, Timestamp at_time);
// Applies lower and upper bounds to the current target rate.
// TODO(srte): This seems to be called even when limits haven't changed, that
// should be cleaned up.
void ApplyTargetLimits(Timestamp at_time);
bool LossBasedBandwidthEstimatorV1Enabled() const;
bool LossBasedBandwidthEstimatorV2Enabled() const;
bool LossBasedBandwidthEstimatorV1ReadyForUse() const;
bool LossBasedBandwidthEstimatorV2ReadyForUse() const;
const FieldTrialsView* key_value_config_;
RttBasedBackoff rtt_backoff_;
LinkCapacityTracker link_capacity_;
std::deque<std::pair<Timestamp, DataRate> > min_bitrate_history_;
// incoming filters
int lost_packets_since_last_loss_update_;
int expected_packets_since_last_loss_update_;
absl::optional<DataRate> acknowledged_rate_;
DataRate current_target_;
DataRate last_logged_target_;
DataRate min_bitrate_configured_;
DataRate max_bitrate_configured_;
Timestamp last_low_bitrate_log_;
bool has_decreased_since_last_fraction_loss_;
Timestamp last_loss_feedback_;
Timestamp last_loss_packet_report_;
uint8_t last_fraction_loss_;
uint8_t last_logged_fraction_loss_;
TimeDelta last_round_trip_time_;
// The max bitrate as set by the receiver in the call. This is typically
// signalled using the REMB RTCP message and is used when we don't have any
// send side delay based estimate.
DataRate receiver_limit_;
DataRate delay_based_limit_;
Timestamp time_last_decrease_;
Timestamp first_report_time_;
int initially_lost_packets_;
DataRate bitrate_at_2_seconds_;
UmaState uma_update_state_;
UmaState uma_rtt_state_;
std::vector<bool> rampup_uma_stats_updated_;
RtcEventLog* const event_log_;
Timestamp last_rtc_event_log_;
float low_loss_threshold_;
float high_loss_threshold_;
DataRate bitrate_threshold_;
LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_;
std::unique_ptr<LossBasedBweV2> loss_based_bandwidth_estimator_v2_;
LossBasedState loss_based_state_;
FieldTrialFlag disable_receiver_limit_caps_only_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_

View file

@ -0,0 +1,339 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
#include <math.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <deque>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/match.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace {
// Parameters for linear least squares fit of regression line to noisy data.
constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
constexpr double kDefaultTrendlineThresholdGain = 4.0;
const char kBweWindowSizeInPacketsExperiment[] =
"WebRTC-BweWindowSizeInPackets";
size_t ReadTrendlineFilterWindowSize(const FieldTrialsView* key_value_config) {
std::string experiment_string =
key_value_config->Lookup(kBweWindowSizeInPacketsExperiment);
size_t window_size;
int parsed_values =
sscanf(experiment_string.c_str(), "Enabled-%zu", &window_size);
if (parsed_values == 1) {
if (window_size > 1)
return window_size;
RTC_LOG(LS_WARNING) << "Window size must be greater than 1.";
}
RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweWindowSizeInPackets"
" experiment from field trial string. Using default.";
return TrendlineEstimatorSettings::kDefaultTrendlineWindowSize;
}
absl::optional<double> LinearFitSlope(
const std::deque<TrendlineEstimator::PacketTiming>& packets) {
RTC_DCHECK(packets.size() >= 2);
// Compute the "center of mass".
double sum_x = 0;
double sum_y = 0;
for (const auto& packet : packets) {
sum_x += packet.arrival_time_ms;
sum_y += packet.smoothed_delay_ms;
}
double x_avg = sum_x / packets.size();
double y_avg = sum_y / packets.size();
// Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2
double numerator = 0;
double denominator = 0;
for (const auto& packet : packets) {
double x = packet.arrival_time_ms;
double y = packet.smoothed_delay_ms;
numerator += (x - x_avg) * (y - y_avg);
denominator += (x - x_avg) * (x - x_avg);
}
if (denominator == 0)
return absl::nullopt;
return numerator / denominator;
}
absl::optional<double> ComputeSlopeCap(
const std::deque<TrendlineEstimator::PacketTiming>& packets,
const TrendlineEstimatorSettings& settings) {
RTC_DCHECK(1 <= settings.beginning_packets &&
settings.beginning_packets < packets.size());
RTC_DCHECK(1 <= settings.end_packets &&
settings.end_packets < packets.size());
RTC_DCHECK(settings.beginning_packets + settings.end_packets <=
packets.size());
TrendlineEstimator::PacketTiming early = packets[0];
for (size_t i = 1; i < settings.beginning_packets; ++i) {
if (packets[i].raw_delay_ms < early.raw_delay_ms)
early = packets[i];
}
size_t late_start = packets.size() - settings.end_packets;
TrendlineEstimator::PacketTiming late = packets[late_start];
for (size_t i = late_start + 1; i < packets.size(); ++i) {
if (packets[i].raw_delay_ms < late.raw_delay_ms)
late = packets[i];
}
if (late.arrival_time_ms - early.arrival_time_ms < 1) {
return absl::nullopt;
}
return (late.raw_delay_ms - early.raw_delay_ms) /
(late.arrival_time_ms - early.arrival_time_ms) +
settings.cap_uncertainty;
}
constexpr double kMaxAdaptOffsetMs = 15.0;
constexpr double kOverUsingTimeThreshold = 10;
constexpr int kMinNumDeltas = 60;
constexpr int kDeltaCounterMax = 1000;
} // namespace
constexpr char TrendlineEstimatorSettings::kKey[];
TrendlineEstimatorSettings::TrendlineEstimatorSettings(
const FieldTrialsView* key_value_config) {
if (absl::StartsWith(
key_value_config->Lookup(kBweWindowSizeInPacketsExperiment),
"Enabled")) {
window_size = ReadTrendlineFilterWindowSize(key_value_config);
}
Parser()->Parse(key_value_config->Lookup(TrendlineEstimatorSettings::kKey));
if (window_size < 10 || 200 < window_size) {
RTC_LOG(LS_WARNING) << "Window size must be between 10 and 200 packets";
window_size = kDefaultTrendlineWindowSize;
}
if (enable_cap) {
if (beginning_packets < 1 || end_packets < 1 ||
beginning_packets > window_size || end_packets > window_size) {
RTC_LOG(LS_WARNING) << "Size of beginning and end must be between 1 and "
<< window_size;
enable_cap = false;
beginning_packets = end_packets = 0;
cap_uncertainty = 0.0;
}
if (beginning_packets + end_packets > window_size) {
RTC_LOG(LS_WARNING)
<< "Size of beginning plus end can't exceed the window size";
enable_cap = false;
beginning_packets = end_packets = 0;
cap_uncertainty = 0.0;
}
if (cap_uncertainty < 0.0 || 0.025 < cap_uncertainty) {
RTC_LOG(LS_WARNING) << "Cap uncertainty must be between 0 and 0.025";
cap_uncertainty = 0.0;
}
}
}
std::unique_ptr<StructParametersParser> TrendlineEstimatorSettings::Parser() {
return StructParametersParser::Create("sort", &enable_sort, //
"cap", &enable_cap, //
"beginning_packets",
&beginning_packets, //
"end_packets", &end_packets, //
"cap_uncertainty", &cap_uncertainty, //
"window_size", &window_size);
}
TrendlineEstimator::TrendlineEstimator(
const FieldTrialsView* key_value_config,
NetworkStatePredictor* network_state_predictor)
: settings_(key_value_config),
smoothing_coef_(kDefaultTrendlineSmoothingCoeff),
threshold_gain_(kDefaultTrendlineThresholdGain),
num_of_deltas_(0),
first_arrival_time_ms_(-1),
accumulated_delay_(0),
smoothed_delay_(0),
delay_hist_(),
k_up_(0.0087),
k_down_(0.039),
overusing_time_threshold_(kOverUsingTimeThreshold),
threshold_(12.5),
prev_modified_trend_(NAN),
last_update_ms_(-1),
prev_trend_(0.0),
time_over_using_(-1),
overuse_counter_(0),
hypothesis_(BandwidthUsage::kBwNormal),
hypothesis_predicted_(BandwidthUsage::kBwNormal),
network_state_predictor_(network_state_predictor) {
RTC_LOG(LS_INFO)
<< "Using Trendline filter for delay change estimation with settings "
<< settings_.Parser()->Encode() << " and "
<< (network_state_predictor_ ? "injected" : "no")
<< " network state predictor";
}
TrendlineEstimator::~TrendlineEstimator() {}
void TrendlineEstimator::UpdateTrendline(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size) {
const double delta_ms = recv_delta_ms - send_delta_ms;
++num_of_deltas_;
num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax);
if (first_arrival_time_ms_ == -1)
first_arrival_time_ms_ = arrival_time_ms;
// Exponential backoff filter.
accumulated_delay_ += delta_ms;
BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms,
accumulated_delay_);
smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
(1 - smoothing_coef_) * accumulated_delay_;
BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms,
smoothed_delay_);
// Maintain packet window
delay_hist_.emplace_back(
static_cast<double>(arrival_time_ms - first_arrival_time_ms_),
smoothed_delay_, accumulated_delay_);
if (settings_.enable_sort) {
for (size_t i = delay_hist_.size() - 1;
i > 0 &&
delay_hist_[i].arrival_time_ms < delay_hist_[i - 1].arrival_time_ms;
--i) {
std::swap(delay_hist_[i], delay_hist_[i - 1]);
}
}
if (delay_hist_.size() > settings_.window_size)
delay_hist_.pop_front();
// Simple linear regression.
double trend = prev_trend_;
if (delay_hist_.size() == settings_.window_size) {
// Update trend_ if it is possible to fit a line to the data. The delay
// trend can be seen as an estimate of (send_rate - capacity)/capacity.
// 0 < trend < 1 -> the delay increases, queues are filling up
// trend == 0 -> the delay does not change
// trend < 0 -> the delay decreases, queues are being emptied
trend = LinearFitSlope(delay_hist_).value_or(trend);
if (settings_.enable_cap) {
absl::optional<double> cap = ComputeSlopeCap(delay_hist_, settings_);
// We only use the cap to filter out overuse detections, not
// to detect additional underuses.
if (trend >= 0 && cap.has_value() && trend > cap.value()) {
trend = cap.value();
}
}
}
BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trend);
Detect(trend, send_delta_ms, arrival_time_ms);
}
void TrendlineEstimator::Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) {
if (calculated_deltas) {
UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms,
packet_size);
}
if (network_state_predictor_) {
hypothesis_predicted_ = network_state_predictor_->Update(
send_time_ms, arrival_time_ms, hypothesis_);
}
}
BandwidthUsage TrendlineEstimator::State() const {
return network_state_predictor_ ? hypothesis_predicted_ : hypothesis_;
}
void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) {
if (num_of_deltas_ < 2) {
hypothesis_ = BandwidthUsage::kBwNormal;
return;
}
const double modified_trend =
std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;
prev_modified_trend_ = modified_trend;
BWE_TEST_LOGGING_PLOT(1, "T", now_ms, modified_trend);
BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
if (modified_trend > threshold_) {
if (time_over_using_ == -1) {
// Initialize the timer. Assume that we've been
// over-using half of the time since the previous
// sample.
time_over_using_ = ts_delta / 2;
} else {
// Increment timer
time_over_using_ += ts_delta;
}
overuse_counter_++;
if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
if (trend >= prev_trend_) {
time_over_using_ = 0;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwOverusing;
}
}
} else if (modified_trend < -threshold_) {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwUnderusing;
} else {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwNormal;
}
prev_trend_ = trend;
UpdateThreshold(modified_trend, now_ms);
}
void TrendlineEstimator::UpdateThreshold(double modified_trend,
int64_t now_ms) {
if (last_update_ms_ == -1)
last_update_ms_ = now_ms;
if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) {
// Avoid adapting the threshold to big latency spikes, caused e.g.,
// by a sudden capacity drop.
last_update_ms_ = now_ms;
return;
}
const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_;
const int64_t kMaxTimeDeltaMs = 100;
int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms;
threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
last_update_ms_ = now_ms;
}
} // namespace webrtc

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
#include <stddef.h>
#include <stdint.h>
#include <deque>
#include <memory>
#include "api/field_trials_view.h"
#include "api/network_state_predictor.h"
#include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
namespace webrtc {
struct TrendlineEstimatorSettings {
static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings";
static constexpr unsigned kDefaultTrendlineWindowSize = 20;
TrendlineEstimatorSettings() = delete;
explicit TrendlineEstimatorSettings(const FieldTrialsView* key_value_config);
// Sort the packets in the window. Should be redundant,
// but then almost no cost.
bool enable_sort = false;
// Cap the trendline slope based on the minimum delay seen
// in the beginning_packets and end_packets respectively.
bool enable_cap = false;
unsigned beginning_packets = 7;
unsigned end_packets = 7;
double cap_uncertainty = 0.0;
// Size (in packets) of the window.
unsigned window_size = kDefaultTrendlineWindowSize;
std::unique_ptr<StructParametersParser> Parser();
};
class TrendlineEstimator : public DelayIncreaseDetectorInterface {
public:
TrendlineEstimator(const FieldTrialsView* key_value_config,
NetworkStatePredictor* network_state_predictor);
~TrendlineEstimator() override;
TrendlineEstimator(const TrendlineEstimator&) = delete;
TrendlineEstimator& operator=(const TrendlineEstimator&) = delete;
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
void Update(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size,
bool calculated_deltas) override;
void UpdateTrendline(double recv_delta_ms,
double send_delta_ms,
int64_t send_time_ms,
int64_t arrival_time_ms,
size_t packet_size);
BandwidthUsage State() const override;
struct PacketTiming {
PacketTiming(double arrival_time_ms,
double smoothed_delay_ms,
double raw_delay_ms)
: arrival_time_ms(arrival_time_ms),
smoothed_delay_ms(smoothed_delay_ms),
raw_delay_ms(raw_delay_ms) {}
double arrival_time_ms;
double smoothed_delay_ms;
double raw_delay_ms;
};
private:
friend class GoogCcStatePrinter;
void Detect(double trend, double ts_delta, int64_t now_ms);
void UpdateThreshold(double modified_offset, int64_t now_ms);
// Parameters.
TrendlineEstimatorSettings settings_;
const double smoothing_coef_;
const double threshold_gain_;
// Used by the existing threshold.
int num_of_deltas_;
// Keep the arrival times small by using the change from the first packet.
int64_t first_arrival_time_ms_;
// Exponential backoff filtering.
double accumulated_delay_;
double smoothed_delay_;
// Linear least squares regression.
std::deque<PacketTiming> delay_hist_;
const double k_up_;
const double k_down_;
double overusing_time_threshold_;
double threshold_;
double prev_modified_trend_;
int64_t last_update_ms_;
double prev_trend_;
double time_over_using_;
int overuse_counter_;
BandwidthUsage hypothesis_;
BandwidthUsage hypothesis_predicted_;
NetworkStatePredictor* network_state_predictor_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/goog_cc/trendline_estimator.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "api/network_state_predictor.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/checks.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
class PacketTimeGenerator {
public:
PacketTimeGenerator(int64_t initial_clock, double time_between_packets)
: initial_clock_(initial_clock),
time_between_packets_(time_between_packets),
packets_(0) {}
int64_t operator()() {
return initial_clock_ + time_between_packets_ * packets_++;
}
private:
const int64_t initial_clock_;
const double time_between_packets_;
size_t packets_;
};
class TrendlineEstimatorTest : public testing::Test {
public:
TrendlineEstimatorTest()
: send_times(kPacketCount),
recv_times(kPacketCount),
packet_sizes(kPacketCount),
config(),
estimator(&config, nullptr),
count(1) {
std::fill(packet_sizes.begin(), packet_sizes.end(), kPacketSizeBytes);
}
void RunTestUntilStateChange() {
RTC_DCHECK_EQ(send_times.size(), kPacketCount);
RTC_DCHECK_EQ(recv_times.size(), kPacketCount);
RTC_DCHECK_EQ(packet_sizes.size(), kPacketCount);
RTC_DCHECK_GE(count, 1);
RTC_DCHECK_LT(count, kPacketCount);
auto initial_state = estimator.State();
for (; count < kPacketCount; count++) {
double recv_delta = recv_times[count] - recv_times[count - 1];
double send_delta = send_times[count] - send_times[count - 1];
estimator.Update(recv_delta, send_delta, send_times[count],
recv_times[count], packet_sizes[count], true);
if (estimator.State() != initial_state) {
return;
}
}
}
protected:
const size_t kPacketCount = 25;
const size_t kPacketSizeBytes = 1200;
std::vector<int64_t> send_times;
std::vector<int64_t> recv_times;
std::vector<size_t> packet_sizes;
const FieldTrialBasedConfig config;
TrendlineEstimator estimator;
size_t count;
};
} // namespace
TEST_F(TrendlineEstimatorTest, Normal) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
20 /*delivered at the same pace*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST_F(TrendlineEstimatorTest, Overusing) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
1.1 * 20 /*10% slower delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST_F(TrendlineEstimatorTest, Underusing) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
0.85 * 20 /*15% faster delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwUnderusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
TEST_F(TrendlineEstimatorTest, IncludesSmallPacketsByDefault) {
PacketTimeGenerator send_time_generator(123456789 /*initial clock*/,
20 /*20 ms between sent packets*/);
std::generate(send_times.begin(), send_times.end(), send_time_generator);
PacketTimeGenerator recv_time_generator(987654321 /*initial clock*/,
1.1 * 20 /*10% slower delivery*/);
std::generate(recv_times.begin(), recv_times.end(), recv_time_generator);
std::fill(packet_sizes.begin(), packet_sizes.end(), 100);
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwNormal);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
RunTestUntilStateChange();
EXPECT_EQ(estimator.State(), BandwidthUsage::kBwOverusing);
EXPECT_EQ(count, kPacketCount); // All packets processed
}
} // namespace webrtc

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#include <memory>
#include <vector>
#include "api/transport/network_control.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "modules/congestion_controller/remb_throttler.h"
#include "modules/pacing/packet_router.h"
#include "modules/remote_bitrate_estimator/remote_estimator_proxy.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
class RemoteBitrateEstimator;
// This class represents the congestion control state for receive
// streams. For send side bandwidth estimation, this is simply
// relaying for each received RTP packet back to the sender. While for
// receive side bandwidth estimation, we do the estimation locally and
// send our results back to the sender.
class ReceiveSideCongestionController : public CallStatsObserver {
public:
ReceiveSideCongestionController(
Clock* clock,
RemoteEstimatorProxy::TransportFeedbackSender feedback_sender,
RembThrottler::RembSender remb_sender,
NetworkStateEstimator* network_state_estimator);
~ReceiveSideCongestionController() override {}
void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type);
// Implements CallStatsObserver.
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
// This is send bitrate, used to control the rate of feedback messages.
void OnBitrateChanged(int bitrate_bps);
// Ensures the remote party is notified of the receive bitrate no larger than
// `bitrate` using RTCP REMB.
void SetMaxDesiredReceiveBitrate(DataRate bitrate);
void SetTransportOverhead(DataSize overhead_per_packet);
// Returns latest receive side bandwidth estimation.
// Returns zero if receive side bandwidth estimation is unavailable.
DataRate LatestReceiveSideEstimate() const;
// Removes stream from receive side bandwidth estimation.
// Noop if receive side bwe is not used or stream doesn't participate in it.
void RemoveStream(uint32_t ssrc);
// Runs periodic tasks if it is time to run them, returns time until next
// call to `MaybeProcess` should be non idle.
TimeDelta MaybeProcess();
private:
void PickEstimator(bool has_absolute_send_time)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Clock& clock_;
RembThrottler remb_throttler_;
RemoteEstimatorProxy remote_estimator_proxy_;
mutable Mutex mutex_;
std::unique_ptr<RemoteBitrateEstimator> rbe_ RTC_GUARDED_BY(mutex_);
bool using_absolute_send_time_ RTC_GUARDED_BY(mutex_);
uint32_t packets_since_absolute_send_time_ RTC_GUARDED_BY(mutex_);
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/bitrate_controller.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <memory>
#include <utility>
#include <vector>
namespace webrtc {
namespace pcc {
PccBitrateController::PccBitrateController(double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
double rtt_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double rtt_gradient_threshold,
double delay_gradient_negative_bound)
: PccBitrateController(initial_conversion_factor,
initial_dynamic_boundary,
dynamic_boundary_increment,
std::make_unique<ModifiedVivaceUtilityFunction>(
rtt_gradient_coefficient,
loss_coefficient,
throughput_coefficient,
throughput_power,
rtt_gradient_threshold,
delay_gradient_negative_bound)) {}
PccBitrateController::PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function)
: consecutive_boundary_adjustments_number_(0),
initial_dynamic_boundary_(initial_dynamic_boundary),
dynamic_boundary_increment_(dynamic_boundary_increment),
utility_function_(std::move(utility_function)),
step_size_adjustments_number_(0),
initial_conversion_factor_(initial_conversion_factor) {}
PccBitrateController::~PccBitrateController() = default;
double PccBitrateController::ComputeStepSize(double utility_gradient) {
// Computes number of consecutive step size adjustments.
if (utility_gradient > 0) {
step_size_adjustments_number_ =
std::max<int64_t>(step_size_adjustments_number_ + 1, 1);
} else if (utility_gradient < 0) {
step_size_adjustments_number_ =
std::min<int64_t>(step_size_adjustments_number_ - 1, -1);
} else {
step_size_adjustments_number_ = 0;
}
// Computes step size amplifier.
int64_t step_size_amplifier = 1;
if (std::abs(step_size_adjustments_number_) <= 3) {
step_size_amplifier =
std::max<int64_t>(std::abs(step_size_adjustments_number_), 1);
} else {
step_size_amplifier = 2 * std::abs(step_size_adjustments_number_) - 3;
}
return step_size_amplifier * initial_conversion_factor_;
}
double PccBitrateController::ApplyDynamicBoundary(double rate_change,
double bitrate) {
double rate_change_abs = std::abs(rate_change);
int64_t rate_change_sign = (rate_change > 0) ? 1 : -1;
if (consecutive_boundary_adjustments_number_ * rate_change_sign < 0) {
consecutive_boundary_adjustments_number_ = 0;
}
double dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
double boundary = bitrate * dynamic_change_boundary;
if (rate_change_abs > boundary) {
consecutive_boundary_adjustments_number_ += rate_change_sign;
return boundary * rate_change_sign;
}
// Rate change smaller than boundary. Reset boundary to the smallest possible
// that would allow the change.
while (rate_change_abs <= boundary &&
consecutive_boundary_adjustments_number_ * rate_change_sign > 0) {
consecutive_boundary_adjustments_number_ -= rate_change_sign;
dynamic_change_boundary =
initial_dynamic_boundary_ +
std::abs(consecutive_boundary_adjustments_number_) *
dynamic_boundary_increment_;
boundary = bitrate * dynamic_change_boundary;
}
consecutive_boundary_adjustments_number_ += rate_change_sign;
return rate_change;
}
absl::optional<DataRate>
PccBitrateController::ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval) {
double utility_value = utility_function_->Compute(monitor_interval);
if (previous_utility_.has_value() && utility_value <= previous_utility_) {
return absl::nullopt;
}
previous_utility_ = utility_value;
return monitor_interval.GetTargetSendingRate();
}
DataRate PccBitrateController::ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& intervals,
DataRate bandwith_estimate) {
double first_utility = utility_function_->Compute(intervals[0]);
double second_utility = utility_function_->Compute(intervals[1]);
double first_bitrate_bps = intervals[0].GetTargetSendingRate().bps();
double second_bitrate_bps = intervals[1].GetTargetSendingRate().bps();
double gradient = (first_utility - second_utility) /
(first_bitrate_bps - second_bitrate_bps);
double rate_change_bps = gradient * ComputeStepSize(gradient); // delta_r
rate_change_bps =
ApplyDynamicBoundary(rate_change_bps, bandwith_estimate.bps());
return DataRate::BitsPerSec(
std::max(0.0, bandwith_estimate.bps() + rate_change_bps));
}
} // namespace pcc
} // namespace webrtc

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
#include "modules/congestion_controller/pcc/monitor_interval.h"
#include "modules/congestion_controller/pcc/utility_function.h"
namespace webrtc {
namespace pcc {
class PccBitrateController {
public:
PccBitrateController(double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
double rtt_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double rtt_gradient_threshold,
double delay_gradient_negative_bound);
PccBitrateController(
double initial_conversion_factor,
double initial_dynamic_boundary,
double dynamic_boundary_increment,
std::unique_ptr<PccUtilityFunctionInterface> utility_function);
absl::optional<DataRate> ComputeRateUpdateForSlowStartMode(
const PccMonitorInterval& monitor_interval);
DataRate ComputeRateUpdateForOnlineLearningMode(
const std::vector<PccMonitorInterval>& block,
DataRate bandwidth_estimate);
~PccBitrateController();
private:
double ApplyDynamicBoundary(double rate_change, double bitrate);
double ComputeStepSize(double utility_gradient);
// Dynamic boundary variables:
int64_t consecutive_boundary_adjustments_number_;
const double initial_dynamic_boundary_;
const double dynamic_boundary_increment_;
const std::unique_ptr<PccUtilityFunctionInterface> utility_function_;
// Step Size variables:
int64_t step_size_adjustments_number_;
const double initial_conversion_factor_;
absl::optional<double> previous_utility_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_BITRATE_CONTROLLER_H_

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/monitor_interval.h"
#include <stddef.h>
#include <cmath>
#include "rtc_base/logging.h"
namespace webrtc {
namespace pcc {
PccMonitorInterval::PccMonitorInterval(DataRate target_sending_rate,
Timestamp start_time,
TimeDelta duration)
: target_sending_rate_(target_sending_rate),
start_time_(start_time),
interval_duration_(duration),
received_packets_size_(DataSize::Zero()),
feedback_collection_done_(false) {}
PccMonitorInterval::~PccMonitorInterval() = default;
PccMonitorInterval::PccMonitorInterval(const PccMonitorInterval& other) =
default;
void PccMonitorInterval::OnPacketsFeedback(
const std::vector<PacketResult>& packets_results) {
for (const PacketResult& packet_result : packets_results) {
if (packet_result.sent_packet.send_time <= start_time_) {
continue;
}
// Here we assume that if some packets are reordered with packets sent
// after the end of the monitor interval, then they are lost. (Otherwise
// it is not clear how long should we wait for packets feedback to arrive).
if (packet_result.sent_packet.send_time >
start_time_ + interval_duration_) {
feedback_collection_done_ = true;
return;
}
if (!packet_result.IsReceived()) {
lost_packets_sent_time_.push_back(packet_result.sent_packet.send_time);
} else {
received_packets_.push_back(
{packet_result.receive_time - packet_result.sent_packet.send_time,
packet_result.sent_packet.send_time});
received_packets_size_ += packet_result.sent_packet.size;
}
}
}
// For the formula used in computations see formula for "slope" in the second
// method:
// https://www.johndcook.com/blog/2008/10/20/comparing-two-ways-to-fit-a-line-to-data/
double PccMonitorInterval::ComputeDelayGradient(
double delay_gradient_threshold) const {
// Early return to prevent division by 0 in case all packets are sent at the
// same time.
if (received_packets_.empty() || received_packets_.front().sent_time ==
received_packets_.back().sent_time) {
return 0;
}
double sum_times = 0;
for (const ReceivedPacket& packet : received_packets_) {
double time_delta_us =
(packet.sent_time - received_packets_[0].sent_time).us();
sum_times += time_delta_us;
}
double sum_squared_scaled_time_deltas = 0;
double sum_scaled_time_delta_dot_delay = 0;
for (const ReceivedPacket& packet : received_packets_) {
double time_delta_us =
(packet.sent_time - received_packets_[0].sent_time).us();
double delay = packet.delay.us();
double scaled_time_delta_us =
time_delta_us - sum_times / received_packets_.size();
sum_squared_scaled_time_deltas +=
scaled_time_delta_us * scaled_time_delta_us;
sum_scaled_time_delta_dot_delay += scaled_time_delta_us * delay;
}
double rtt_gradient =
sum_scaled_time_delta_dot_delay / sum_squared_scaled_time_deltas;
if (std::abs(rtt_gradient) < delay_gradient_threshold)
rtt_gradient = 0;
return rtt_gradient;
}
bool PccMonitorInterval::IsFeedbackCollectionDone() const {
return feedback_collection_done_;
}
Timestamp PccMonitorInterval::GetEndTime() const {
return start_time_ + interval_duration_;
}
double PccMonitorInterval::GetLossRate() const {
size_t packets_lost = lost_packets_sent_time_.size();
size_t packets_received = received_packets_.size();
if (packets_lost == 0)
return 0;
return static_cast<double>(packets_lost) / (packets_lost + packets_received);
}
DataRate PccMonitorInterval::GetTargetSendingRate() const {
return target_sending_rate_;
}
DataRate PccMonitorInterval::GetTransmittedPacketsRate() const {
if (received_packets_.empty()) {
return target_sending_rate_;
}
Timestamp receive_time_of_first_packet =
received_packets_.front().sent_time + received_packets_.front().delay;
Timestamp receive_time_of_last_packet =
received_packets_.back().sent_time + received_packets_.back().delay;
if (receive_time_of_first_packet == receive_time_of_last_packet) {
RTC_LOG(LS_WARNING)
<< "All packets in monitor interval were received at the same time.";
return target_sending_rate_;
}
return received_packets_size_ /
(receive_time_of_last_packet - receive_time_of_first_packet);
}
} // namespace pcc
} // namespace webrtc

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_MONITOR_INTERVAL_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_MONITOR_INTERVAL_H_
#include <vector>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
namespace pcc {
// PCC divides time into consecutive monitor intervals which are used to test
// consequences for performance of sending at a certain rate.
class PccMonitorInterval {
public:
PccMonitorInterval(DataRate target_sending_rate,
Timestamp start_time,
TimeDelta duration);
~PccMonitorInterval();
PccMonitorInterval(const PccMonitorInterval& other);
void OnPacketsFeedback(const std::vector<PacketResult>& packets_results);
// Returns true if got complete information about packets.
// Notice, this only happens when received feedback about the first packet
// which were sent after the end of the monitor interval. If such event
// doesn't occur, we don't mind anyway and stay in the same state.
bool IsFeedbackCollectionDone() const;
Timestamp GetEndTime() const;
double GetLossRate() const;
// Estimates the gradient using linear regression on the 2-dimensional
// dataset (sampled packets delay, time of sampling).
double ComputeDelayGradient(double delay_gradient_threshold) const;
DataRate GetTargetSendingRate() const;
// How fast receiving side gets packets.
DataRate GetTransmittedPacketsRate() const;
private:
struct ReceivedPacket {
TimeDelta delay;
Timestamp sent_time;
};
// Target bitrate used to generate and pace the outgoing packets.
// Actually sent bitrate might not match the target exactly.
DataRate target_sending_rate_;
// Start time is not included into interval while end time is included.
Timestamp start_time_;
TimeDelta interval_duration_;
// Vectors below updates while receiving feedback.
std::vector<ReceivedPacket> received_packets_;
std::vector<Timestamp> lost_packets_sent_time_;
DataSize received_packets_size_;
bool feedback_collection_done_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_MONITOR_INTERVAL_H_

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/pcc_factory.h"
#include <memory>
#include "modules/congestion_controller/pcc/pcc_network_controller.h"
namespace webrtc {
PccNetworkControllerFactory::PccNetworkControllerFactory() {}
std::unique_ptr<NetworkControllerInterface> PccNetworkControllerFactory::Create(
NetworkControllerConfig config) {
return std::make_unique<pcc::PccNetworkController>(config);
}
TimeDelta PccNetworkControllerFactory::GetProcessInterval() const {
return TimeDelta::PlusInfinity();
}
} // namespace webrtc

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_
#include <memory>
#include "api/transport/network_control.h"
#include "api/units/time_delta.h"
namespace webrtc {
class PccNetworkControllerFactory : public NetworkControllerFactoryInterface {
public:
PccNetworkControllerFactory();
std::unique_ptr<NetworkControllerInterface> Create(
NetworkControllerConfig config) override;
TimeDelta GetProcessInterval() const override;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_PCC_FACTORY_H_

View file

@ -0,0 +1,391 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/pcc_network_controller.h"
#include <algorithm>
#include "absl/types/optional.h"
#include "api/units/data_size.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace pcc {
namespace {
constexpr int64_t kInitialRttMs = 200;
constexpr int64_t kInitialBandwidthKbps = 300;
constexpr double kMonitorIntervalDurationRatio = 1;
constexpr double kDefaultSamplingStep = 0.05;
constexpr double kTimeoutRatio = 2;
constexpr double kAlphaForRtt = 0.9;
constexpr double kSlowStartModeIncrease = 1.5;
constexpr double kAlphaForPacketInterval = 0.9;
constexpr int64_t kMinPacketsNumberPerInterval = 20;
const TimeDelta kMinDurationOfMonitorInterval = TimeDelta::Millis(50);
const TimeDelta kStartupDuration = TimeDelta::Millis(500);
constexpr double kMinRateChangeBps = 4000;
constexpr DataRate kMinRateHaveMultiplicativeRateChange = DataRate::BitsPerSec(
static_cast<int64_t>(kMinRateChangeBps / kDefaultSamplingStep));
// Bitrate controller constants.
constexpr double kInitialConversionFactor = 5;
constexpr double kInitialDynamicBoundary = 0.1;
constexpr double kDynamicBoundaryIncrement = 0.1;
// Utility function parameters.
constexpr double kRttGradientCoefficientBps = 0.005;
constexpr double kLossCoefficientBps = 10;
constexpr double kThroughputCoefficient = 0.001;
constexpr double kThroughputPower = 0.9;
constexpr double kRttGradientThreshold = 0.01;
constexpr double kDelayGradientNegativeBound = 0.1;
constexpr int64_t kNumberOfPacketsToKeep = 20;
const uint64_t kRandomSeed = 100;
} // namespace
PccNetworkController::PccNetworkController(NetworkControllerConfig config)
: start_time_(Timestamp::PlusInfinity()),
last_sent_packet_time_(Timestamp::PlusInfinity()),
smoothed_packets_sending_interval_(TimeDelta::Zero()),
mode_(Mode::kStartup),
default_bandwidth_(DataRate::KilobitsPerSec(kInitialBandwidthKbps)),
bandwidth_estimate_(default_bandwidth_),
rtt_tracker_(TimeDelta::Millis(kInitialRttMs), kAlphaForRtt),
monitor_interval_timeout_(TimeDelta::Millis(kInitialRttMs) *
kTimeoutRatio),
monitor_interval_length_strategy_(MonitorIntervalLengthStrategy::kFixed),
monitor_interval_duration_ratio_(kMonitorIntervalDurationRatio),
sampling_step_(kDefaultSamplingStep),
monitor_interval_timeout_ratio_(kTimeoutRatio),
min_packets_number_per_interval_(kMinPacketsNumberPerInterval),
bitrate_controller_(kInitialConversionFactor,
kInitialDynamicBoundary,
kDynamicBoundaryIncrement,
kRttGradientCoefficientBps,
kLossCoefficientBps,
kThroughputCoefficient,
kThroughputPower,
kRttGradientThreshold,
kDelayGradientNegativeBound),
monitor_intervals_duration_(TimeDelta::Zero()),
complete_feedback_monitor_interval_number_(0),
random_generator_(kRandomSeed) {
if (config.constraints.starting_rate) {
default_bandwidth_ = *config.constraints.starting_rate;
bandwidth_estimate_ = default_bandwidth_;
}
}
PccNetworkController::~PccNetworkController() {}
NetworkControlUpdate PccNetworkController::CreateRateUpdate(
Timestamp at_time) const {
DataRate sending_rate = DataRate::Zero();
if (monitor_intervals_.empty() ||
(monitor_intervals_.size() >= monitor_intervals_bitrates_.size() &&
at_time >= monitor_intervals_.back().GetEndTime())) {
sending_rate = bandwidth_estimate_;
} else {
sending_rate = monitor_intervals_.back().GetTargetSendingRate();
}
// Set up config when sending rate is computed.
NetworkControlUpdate update;
// Set up target rate to encoder.
TargetTransferRate target_rate_msg;
target_rate_msg.at_time = at_time;
target_rate_msg.network_estimate.at_time = at_time;
target_rate_msg.network_estimate.round_trip_time = rtt_tracker_.GetRtt();
// TODO(koloskova): Add correct estimate.
target_rate_msg.network_estimate.loss_rate_ratio = 0;
target_rate_msg.network_estimate.bwe_period =
monitor_interval_duration_ratio_ * rtt_tracker_.GetRtt();
target_rate_msg.target_rate = sending_rate;
update.target_rate = target_rate_msg;
// Set up pacing/padding target rate.
PacerConfig pacer_config;
pacer_config.at_time = at_time;
pacer_config.time_window = TimeDelta::Millis(1);
pacer_config.data_window = sending_rate * pacer_config.time_window;
pacer_config.pad_window = sending_rate * pacer_config.time_window;
update.pacer_config = pacer_config;
return update;
}
NetworkControlUpdate PccNetworkController::OnSentPacket(SentPacket msg) {
// Start new monitor interval if previous has finished.
// Monitor interval is initialized in OnProcessInterval function.
if (start_time_.IsInfinite()) {
start_time_ = msg.send_time;
monitor_intervals_duration_ = kStartupDuration;
monitor_intervals_bitrates_ = {bandwidth_estimate_};
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
complete_feedback_monitor_interval_number_ = 0;
}
if (last_sent_packet_time_.IsFinite()) {
smoothed_packets_sending_interval_ =
(msg.send_time - last_sent_packet_time_) * kAlphaForPacketInterval +
(1 - kAlphaForPacketInterval) * smoothed_packets_sending_interval_;
}
last_sent_packet_time_ = msg.send_time;
if (!monitor_intervals_.empty() &&
msg.send_time >= monitor_intervals_.back().GetEndTime() &&
monitor_intervals_bitrates_.size() > monitor_intervals_.size()) {
// Start new monitor interval.
monitor_intervals_.emplace_back(
monitor_intervals_bitrates_[monitor_intervals_.size()], msg.send_time,
monitor_intervals_duration_);
}
if (IsTimeoutExpired(msg.send_time)) {
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet.size;
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
bandwidth_estimate_ =
std::min<DataRate>(bandwidth_estimate_ * 0.5, receiving_rate);
if (mode_ == Mode::kSlowStart)
mode_ = Mode::kOnlineLearning;
}
if (mode_ == Mode::kStartup &&
msg.send_time - start_time_ >= kStartupDuration) {
DataSize received_size = DataSize::Zero();
for (size_t i = 1; i < last_received_packets_.size(); ++i) {
received_size += last_received_packets_[i].sent_packet.size;
}
TimeDelta sending_time = TimeDelta::Zero();
if (last_received_packets_.size() > 0)
sending_time = last_received_packets_.back().receive_time -
last_received_packets_.front().receive_time;
DataRate receiving_rate = bandwidth_estimate_;
if (sending_time > TimeDelta::Zero())
receiving_rate = received_size / sending_time;
bandwidth_estimate_ = receiving_rate;
monitor_intervals_.clear();
mode_ = Mode::kSlowStart;
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
monitor_intervals_bitrates_ = {bandwidth_estimate_};
monitor_intervals_.emplace_back(bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
bandwidth_estimate_ = bandwidth_estimate_ * (1 / kSlowStartModeIncrease);
complete_feedback_monitor_interval_number_ = 0;
return CreateRateUpdate(msg.send_time);
}
if (IsFeedbackCollectionDone() || IsTimeoutExpired(msg.send_time)) {
// Creating new monitor intervals.
monitor_intervals_.clear();
monitor_interval_timeout_ =
rtt_tracker_.GetRtt() * monitor_interval_timeout_ratio_;
monitor_intervals_duration_ = ComputeMonitorIntervalsDuration();
complete_feedback_monitor_interval_number_ = 0;
// Compute bitrates and start first monitor interval.
if (mode_ == Mode::kSlowStart) {
monitor_intervals_bitrates_ = {kSlowStartModeIncrease *
bandwidth_estimate_};
monitor_intervals_.emplace_back(
kSlowStartModeIncrease * bandwidth_estimate_, msg.send_time,
monitor_intervals_duration_);
} else {
RTC_DCHECK(mode_ == Mode::kOnlineLearning || mode_ == Mode::kDoubleCheck);
monitor_intervals_.clear();
int64_t sign = 2 * (random_generator_.Rand(0, 1) % 2) - 1;
RTC_DCHECK_GE(sign, -1);
RTC_DCHECK_LE(sign, 1);
if (bandwidth_estimate_ >= kMinRateHaveMultiplicativeRateChange) {
monitor_intervals_bitrates_ = {
bandwidth_estimate_ * (1 + sign * sampling_step_),
bandwidth_estimate_ * (1 - sign * sampling_step_)};
} else {
monitor_intervals_bitrates_ = {
DataRate::BitsPerSec(std::max<double>(
bandwidth_estimate_.bps() + sign * kMinRateChangeBps, 0)),
DataRate::BitsPerSec(std::max<double>(
bandwidth_estimate_.bps() - sign * kMinRateChangeBps, 0))};
}
monitor_intervals_.emplace_back(monitor_intervals_bitrates_[0],
msg.send_time,
monitor_intervals_duration_);
}
}
return CreateRateUpdate(msg.send_time);
}
TimeDelta PccNetworkController::ComputeMonitorIntervalsDuration() const {
TimeDelta monitor_intervals_duration = TimeDelta::Zero();
if (monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kAdaptive) {
monitor_intervals_duration = std::max(
rtt_tracker_.GetRtt() * monitor_interval_duration_ratio_,
smoothed_packets_sending_interval_ * min_packets_number_per_interval_);
} else {
RTC_DCHECK(monitor_interval_length_strategy_ ==
MonitorIntervalLengthStrategy::kFixed);
monitor_intervals_duration =
smoothed_packets_sending_interval_ * min_packets_number_per_interval_;
}
monitor_intervals_duration =
std::max(kMinDurationOfMonitorInterval, monitor_intervals_duration);
return monitor_intervals_duration;
}
bool PccNetworkController::IsTimeoutExpired(Timestamp current_time) const {
if (complete_feedback_monitor_interval_number_ >= monitor_intervals_.size()) {
return false;
}
return current_time -
monitor_intervals_[complete_feedback_monitor_interval_number_]
.GetEndTime() >=
monitor_interval_timeout_;
}
bool PccNetworkController::IsFeedbackCollectionDone() const {
return complete_feedback_monitor_interval_number_ >=
monitor_intervals_bitrates_.size();
}
NetworkControlUpdate PccNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback msg) {
if (msg.packet_feedbacks.empty())
return NetworkControlUpdate();
// Save packets to last_received_packets_ array.
for (const PacketResult& packet_result : msg.ReceivedWithSendInfo()) {
last_received_packets_.push_back(packet_result);
}
while (last_received_packets_.size() > kNumberOfPacketsToKeep) {
last_received_packets_.pop_front();
}
rtt_tracker_.OnPacketsFeedback(msg.PacketsWithFeedback(), msg.feedback_time);
// Skip rate update in case when online learning mode just started, but
// corresponding monitor intervals were not started yet.
if (mode_ == Mode::kOnlineLearning &&
monitor_intervals_bitrates_.size() < 2) {
return NetworkControlUpdate();
}
if (!IsFeedbackCollectionDone() && !monitor_intervals_.empty()) {
while (complete_feedback_monitor_interval_number_ <
monitor_intervals_.size()) {
monitor_intervals_[complete_feedback_monitor_interval_number_]
.OnPacketsFeedback(msg.PacketsWithFeedback());
if (!monitor_intervals_[complete_feedback_monitor_interval_number_]
.IsFeedbackCollectionDone())
break;
++complete_feedback_monitor_interval_number_;
}
}
if (IsFeedbackCollectionDone()) {
if (mode_ == Mode::kDoubleCheck) {
mode_ = Mode::kOnlineLearning;
} else if (NeedDoubleCheckMeasurments()) {
mode_ = Mode::kDoubleCheck;
}
if (mode_ != Mode::kDoubleCheck)
UpdateSendingRateAndMode();
}
return NetworkControlUpdate();
}
bool PccNetworkController::NeedDoubleCheckMeasurments() const {
if (mode_ == Mode::kSlowStart) {
return false;
}
double first_loss_rate = monitor_intervals_[0].GetLossRate();
double second_loss_rate = monitor_intervals_[1].GetLossRate();
DataRate first_bitrate = monitor_intervals_[0].GetTargetSendingRate();
DataRate second_bitrate = monitor_intervals_[1].GetTargetSendingRate();
if ((first_bitrate.bps() - second_bitrate.bps()) *
(first_loss_rate - second_loss_rate) <
0) {
return true;
}
return false;
}
void PccNetworkController::UpdateSendingRateAndMode() {
if (monitor_intervals_.empty() || !IsFeedbackCollectionDone()) {
return;
}
if (mode_ == Mode::kSlowStart) {
DataRate old_bandwidth_estimate = bandwidth_estimate_;
bandwidth_estimate_ =
bitrate_controller_
.ComputeRateUpdateForSlowStartMode(monitor_intervals_[0])
.value_or(bandwidth_estimate_);
if (bandwidth_estimate_ <= old_bandwidth_estimate)
mode_ = Mode::kOnlineLearning;
} else {
RTC_DCHECK(mode_ == Mode::kOnlineLearning);
bandwidth_estimate_ =
bitrate_controller_.ComputeRateUpdateForOnlineLearningMode(
monitor_intervals_, bandwidth_estimate_);
}
}
NetworkControlUpdate PccNetworkController::OnNetworkAvailability(
NetworkAvailability msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnNetworkRouteChange(
NetworkRouteChange msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnProcessInterval(
ProcessInterval msg) {
return CreateRateUpdate(msg.at_time);
}
NetworkControlUpdate PccNetworkController::OnTargetRateConstraints(
TargetRateConstraints msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRemoteBitrateReport(
RemoteBitrateReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnRoundTripTimeUpdate(
RoundTripTimeUpdate) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnTransportLossReport(
TransportLossReport) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnStreamsConfig(StreamsConfig msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnReceivedPacket(
ReceivedPacket msg) {
return NetworkControlUpdate();
}
NetworkControlUpdate PccNetworkController::OnNetworkStateEstimate(
NetworkStateEstimate msg) {
return NetworkControlUpdate();
}
} // namespace pcc
} // namespace webrtc

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_
#include <stddef.h>
#include <stdint.h>
#include <deque>
#include <vector>
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/congestion_controller/pcc/bitrate_controller.h"
#include "modules/congestion_controller/pcc/monitor_interval.h"
#include "modules/congestion_controller/pcc/rtt_tracker.h"
#include "rtc_base/random.h"
namespace webrtc {
namespace pcc {
// PCC (Performance-oriented Congestion Control) Vivace is a congestion
// control algorithm based on online (convex) optimization in machine learning.
// It divides time into consecutive Monitor Intervals (MI) to test sending
// rates r(1 + eps), r(1 - eps) for the current sending rate r.
// At the end of each MI it computes utility function to transform the
// performance statistics into a numerical value. Then it updates current
// sending rate using gradient ascent to maximize utility function.
class PccNetworkController : public NetworkControllerInterface {
public:
enum class Mode {
kStartup,
// Slow start phase of PCC doubles sending rate each monitor interval.
kSlowStart,
// After getting the first decrease in utility function PCC exits slow start
// and enters the online learning phase.
kOnlineLearning,
// If we got that sending with the lower rate resulted in higher packet
// loss, then the measurements are unreliable and we need to double check
// them.
kDoubleCheck
};
enum class MonitorIntervalLengthStrategy {
// Monitor interval length adaptive when it is proportional to packets RTT.
kAdaptive,
// Monitor interval length is fixed when it is equal to the time of sending
// predefined amount of packets (kMinPacketsNumberPerInterval).
kFixed
};
explicit PccNetworkController(NetworkControllerConfig config);
~PccNetworkController() override;
// NetworkControllerInterface
NetworkControlUpdate OnNetworkAvailability(NetworkAvailability msg) override;
NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange msg) override;
NetworkControlUpdate OnProcessInterval(ProcessInterval msg) override;
NetworkControlUpdate OnSentPacket(SentPacket msg) override;
NetworkControlUpdate OnTargetRateConstraints(
TargetRateConstraints msg) override;
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback msg) override;
// Part of remote bitrate estimation api, not implemented for PCC
NetworkControlUpdate OnStreamsConfig(StreamsConfig msg) override;
NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport msg) override;
NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate msg) override;
NetworkControlUpdate OnTransportLossReport(TransportLossReport msg) override;
NetworkControlUpdate OnReceivedPacket(ReceivedPacket msg) override;
NetworkControlUpdate OnNetworkStateEstimate(
NetworkStateEstimate msg) override;
private:
void UpdateSendingRateAndMode();
NetworkControlUpdate CreateRateUpdate(Timestamp at_time) const;
TimeDelta ComputeMonitorIntervalsDuration() const;
bool NeedDoubleCheckMeasurments() const;
bool IsTimeoutExpired(Timestamp current_time) const;
bool IsFeedbackCollectionDone() const;
Timestamp start_time_;
Timestamp last_sent_packet_time_;
TimeDelta smoothed_packets_sending_interval_;
Mode mode_;
// Default value used for initializing bandwidth.
DataRate default_bandwidth_;
// Current estimate r.
DataRate bandwidth_estimate_;
RttTracker rtt_tracker_;
TimeDelta monitor_interval_timeout_;
const MonitorIntervalLengthStrategy monitor_interval_length_strategy_;
const double monitor_interval_duration_ratio_;
const double sampling_step_; // Epsilon.
const double monitor_interval_timeout_ratio_;
const int64_t min_packets_number_per_interval_;
PccBitrateController bitrate_controller_;
std::vector<PccMonitorInterval> monitor_intervals_;
std::vector<DataRate> monitor_intervals_bitrates_;
TimeDelta monitor_intervals_duration_;
size_t complete_feedback_monitor_interval_number_;
webrtc::Random random_generator_;
std::deque<PacketResult> last_received_packets_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_PCC_NETWORK_CONTROLLER_H_

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/rtt_tracker.h"
#include <algorithm>
namespace webrtc {
namespace pcc {
RttTracker::RttTracker(TimeDelta initial_rtt, double alpha)
: rtt_estimate_(initial_rtt), alpha_(alpha) {}
void RttTracker::OnPacketsFeedback(
const std::vector<PacketResult>& packet_feedbacks,
Timestamp feedback_received_time) {
TimeDelta packet_rtt = TimeDelta::MinusInfinity();
for (const PacketResult& packet_result : packet_feedbacks) {
if (!packet_result.IsReceived())
continue;
packet_rtt = std::max<TimeDelta>(
packet_rtt,
feedback_received_time - packet_result.sent_packet.send_time);
}
if (packet_rtt.IsFinite())
rtt_estimate_ = (1 - alpha_) * rtt_estimate_ + alpha_ * packet_rtt;
}
TimeDelta RttTracker::GetRtt() const {
return rtt_estimate_;
}
} // namespace pcc
} // namespace webrtc

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_RTT_TRACKER_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_RTT_TRACKER_H_
#include <vector>
#include "api/transport/network_types.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
namespace pcc {
class RttTracker {
public:
RttTracker(TimeDelta initial_rtt, double alpha);
// Updates RTT estimate.
void OnPacketsFeedback(const std::vector<PacketResult>& packet_feedbacks,
Timestamp feedback_received_time);
TimeDelta GetRtt() const;
private:
TimeDelta rtt_estimate_;
double alpha_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_RTT_TRACKER_H_

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/pcc/utility_function.h"
#include <algorithm>
#include <cmath>
#include "api/units/data_rate.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace pcc {
VivaceUtilityFunction::VivaceUtilityFunction(
double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound)
: delay_gradient_coefficient_(delay_gradient_coefficient),
loss_coefficient_(loss_coefficient),
throughput_power_(throughput_power),
throughput_coefficient_(throughput_coefficient),
delay_gradient_threshold_(delay_gradient_threshold),
delay_gradient_negative_bound_(delay_gradient_negative_bound) {
RTC_DCHECK_GE(delay_gradient_negative_bound_, 0);
}
double VivaceUtilityFunction::Compute(
const PccMonitorInterval& monitor_interval) const {
RTC_DCHECK(monitor_interval.IsFeedbackCollectionDone());
double bitrate = monitor_interval.GetTargetSendingRate().bps();
double loss_rate = monitor_interval.GetLossRate();
double rtt_gradient =
monitor_interval.ComputeDelayGradient(delay_gradient_threshold_);
rtt_gradient = std::max(rtt_gradient, -delay_gradient_negative_bound_);
return (throughput_coefficient_ * std::pow(bitrate, throughput_power_)) -
(delay_gradient_coefficient_ * bitrate * rtt_gradient) -
(loss_coefficient_ * bitrate * loss_rate);
}
VivaceUtilityFunction::~VivaceUtilityFunction() = default;
ModifiedVivaceUtilityFunction::ModifiedVivaceUtilityFunction(
double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound)
: delay_gradient_coefficient_(delay_gradient_coefficient),
loss_coefficient_(loss_coefficient),
throughput_power_(throughput_power),
throughput_coefficient_(throughput_coefficient),
delay_gradient_threshold_(delay_gradient_threshold),
delay_gradient_negative_bound_(delay_gradient_negative_bound) {
RTC_DCHECK_GE(delay_gradient_negative_bound_, 0);
}
double ModifiedVivaceUtilityFunction::Compute(
const PccMonitorInterval& monitor_interval) const {
RTC_DCHECK(monitor_interval.IsFeedbackCollectionDone());
double bitrate = monitor_interval.GetTargetSendingRate().bps();
double loss_rate = monitor_interval.GetLossRate();
double rtt_gradient =
monitor_interval.ComputeDelayGradient(delay_gradient_threshold_);
rtt_gradient = std::max(rtt_gradient, -delay_gradient_negative_bound_);
return (throughput_coefficient_ * std::pow(bitrate, throughput_power_) *
bitrate) -
(delay_gradient_coefficient_ * bitrate * bitrate * rtt_gradient) -
(loss_coefficient_ * bitrate * bitrate * loss_rate);
}
ModifiedVivaceUtilityFunction::~ModifiedVivaceUtilityFunction() = default;
} // namespace pcc
} // namespace webrtc

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_PCC_UTILITY_FUNCTION_H_
#define MODULES_CONGESTION_CONTROLLER_PCC_UTILITY_FUNCTION_H_
#include "modules/congestion_controller/pcc/monitor_interval.h"
namespace webrtc {
namespace pcc {
// Utility function is used by PCC to transform the performance statistics
// (sending rate, loss rate, packets latency) gathered at one monitor interval
// into a numerical value.
// https://www.usenix.org/conference/nsdi18/presentation/dong
class PccUtilityFunctionInterface {
public:
virtual double Compute(const PccMonitorInterval& monitor_interval) const = 0;
virtual ~PccUtilityFunctionInterface() = default;
};
// Vivace utility function were suggested in the paper "PCC Vivace:
// Online-Learning Congestion Control", Mo Dong et all.
class VivaceUtilityFunction : public PccUtilityFunctionInterface {
public:
VivaceUtilityFunction(double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound);
double Compute(const PccMonitorInterval& monitor_interval) const override;
~VivaceUtilityFunction() override;
private:
const double delay_gradient_coefficient_;
const double loss_coefficient_;
const double throughput_power_;
const double throughput_coefficient_;
const double delay_gradient_threshold_;
const double delay_gradient_negative_bound_;
};
// This utility function were obtained by tuning Vivace utility function.
// The main difference is that gradient of modified utilify funtion (as well as
// rate updates) scales proportionally to the sending rate which leads to
// better performance in case of single sender.
class ModifiedVivaceUtilityFunction : public PccUtilityFunctionInterface {
public:
ModifiedVivaceUtilityFunction(double delay_gradient_coefficient,
double loss_coefficient,
double throughput_coefficient,
double throughput_power,
double delay_gradient_threshold,
double delay_gradient_negative_bound);
double Compute(const PccMonitorInterval& monitor_interval) const override;
~ModifiedVivaceUtilityFunction() override;
private:
const double delay_gradient_coefficient_;
const double loss_coefficient_;
const double throughput_power_;
const double throughput_coefficient_;
const double delay_gradient_threshold_;
const double delay_gradient_negative_bound_;
};
} // namespace pcc
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_PCC_UTILITY_FUNCTION_H_

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/include/receive_side_congestion_controller.h"
#include "api/media_types.h"
#include "api/units/data_rate.h"
#include "modules/pacing/packet_router.h"
#include "modules/remote_bitrate_estimator/include/bwe_defines.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
static const uint32_t kTimeOffsetSwitchThreshold = 30;
} // namespace
void ReceiveSideCongestionController::OnRttUpdate(int64_t avg_rtt_ms,
int64_t max_rtt_ms) {
MutexLock lock(&mutex_);
rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
}
void ReceiveSideCongestionController::RemoveStream(uint32_t ssrc) {
MutexLock lock(&mutex_);
rbe_->RemoveStream(ssrc);
}
DataRate ReceiveSideCongestionController::LatestReceiveSideEstimate() const {
MutexLock lock(&mutex_);
return rbe_->LatestEstimate();
}
void ReceiveSideCongestionController::PickEstimator(
bool has_absolute_send_time) {
if (has_absolute_send_time) {
// If we see AST in header, switch RBE strategy immediately.
if (!using_absolute_send_time_) {
RTC_LOG(LS_INFO)
<< "WrappingBitrateEstimator: Switching to absolute send time RBE.";
using_absolute_send_time_ = true;
rbe_ = std::make_unique<RemoteBitrateEstimatorAbsSendTime>(
&remb_throttler_, &clock_);
}
packets_since_absolute_send_time_ = 0;
} else {
// When we don't see AST, wait for a few packets before going back to TOF.
if (using_absolute_send_time_) {
++packets_since_absolute_send_time_;
if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) {
RTC_LOG(LS_INFO)
<< "WrappingBitrateEstimator: Switching to transmission "
"time offset RBE.";
using_absolute_send_time_ = false;
rbe_ = std::make_unique<RemoteBitrateEstimatorSingleStream>(
&remb_throttler_, &clock_);
}
}
}
}
ReceiveSideCongestionController::ReceiveSideCongestionController(
Clock* clock,
RemoteEstimatorProxy::TransportFeedbackSender feedback_sender,
RembThrottler::RembSender remb_sender,
NetworkStateEstimator* network_state_estimator)
: clock_(*clock),
remb_throttler_(std::move(remb_sender), clock),
remote_estimator_proxy_(std::move(feedback_sender),
network_state_estimator),
rbe_(new RemoteBitrateEstimatorSingleStream(&remb_throttler_, clock)),
using_absolute_send_time_(false),
packets_since_absolute_send_time_(0) {}
void ReceiveSideCongestionController::OnReceivedPacket(
const RtpPacketReceived& packet,
MediaType media_type) {
bool has_transport_sequence_number =
packet.HasExtension<TransportSequenceNumber>() ||
packet.HasExtension<TransportSequenceNumberV2>();
if (media_type == MediaType::AUDIO && !has_transport_sequence_number) {
// For audio, we only support send side BWE.
return;
}
if (has_transport_sequence_number) {
// Send-side BWE.
remote_estimator_proxy_.IncomingPacket(packet);
} else {
// Receive-side BWE.
MutexLock lock(&mutex_);
PickEstimator(packet.HasExtension<AbsoluteSendTime>());
rbe_->IncomingPacket(packet);
}
}
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
remote_estimator_proxy_.OnBitrateChanged(bitrate_bps);
}
TimeDelta ReceiveSideCongestionController::MaybeProcess() {
Timestamp now = clock_.CurrentTime();
mutex_.Lock();
TimeDelta time_until_rbe = rbe_->Process();
mutex_.Unlock();
TimeDelta time_until_rep = remote_estimator_proxy_.Process(now);
TimeDelta time_until = std::min(time_until_rbe, time_until_rep);
return std::max(time_until, TimeDelta::Zero());
}
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
DataRate bitrate) {
remb_throttler_.SetMaxDesiredReceiveBitrate(bitrate);
}
void ReceiveSideCongestionController::SetTransportOverhead(
DataSize overhead_per_packet) {
remote_estimator_proxy_.SetTransportOverhead(overhead_per_packet);
}
} // namespace webrtc

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/include/receive_side_congestion_controller.h"
#include "api/test/network_emulation/create_cross_traffic.h"
#include "api/test/network_emulation/cross_traffic.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/scenario/scenario.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::_;
using ::testing::AtLeast;
using ::testing::ElementsAre;
using ::testing::MockFunction;
constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(60'000);
TEST(ReceiveSideCongestionControllerTest, SendsRembWithAbsSendTime) {
static constexpr DataSize kPayloadSize = DataSize::Bytes(1000);
MockFunction<void(std::vector<std::unique_ptr<rtcp::RtcpPacket>>)>
feedback_sender;
MockFunction<void(uint64_t, std::vector<uint32_t>)> remb_sender;
SimulatedClock clock_(123456);
ReceiveSideCongestionController controller(
&clock_, feedback_sender.AsStdFunction(), remb_sender.AsStdFunction(),
nullptr);
RtpHeaderExtensionMap extensions;
extensions.Register<AbsoluteSendTime>(1);
RtpPacketReceived packet(&extensions);
packet.SetSsrc(0x11eb21c);
packet.ReserveExtension<AbsoluteSendTime>();
packet.SetPayloadSize(kPayloadSize.bytes());
EXPECT_CALL(remb_sender, Call(_, ElementsAre(packet.Ssrc())))
.Times(AtLeast(1));
for (int i = 0; i < 10; ++i) {
clock_.AdvanceTime(kPayloadSize / kInitialBitrate);
Timestamp now = clock_.CurrentTime();
packet.SetExtension<AbsoluteSendTime>(AbsoluteSendTime::To24Bits(now));
packet.set_arrival_time(now);
controller.OnReceivedPacket(packet, MediaType::VIDEO);
}
}
TEST(ReceiveSideCongestionControllerTest,
SendsRembAfterSetMaxDesiredReceiveBitrate) {
MockFunction<void(std::vector<std::unique_ptr<rtcp::RtcpPacket>>)>
feedback_sender;
MockFunction<void(uint64_t, std::vector<uint32_t>)> remb_sender;
SimulatedClock clock_(123456);
ReceiveSideCongestionController controller(
&clock_, feedback_sender.AsStdFunction(), remb_sender.AsStdFunction(),
nullptr);
EXPECT_CALL(remb_sender, Call(123, _));
controller.SetMaxDesiredReceiveBitrate(DataRate::BitsPerSec(123));
}
TEST(ReceiveSideCongestionControllerTest, ConvergesToCapacity) {
Scenario s("receive_cc_unit/converge");
NetworkSimulationConfig net_conf;
net_conf.bandwidth = DataRate::KilobitsPerSec(1000);
net_conf.delay = TimeDelta::Millis(50);
auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
c->transport.rates.start_rate = DataRate::KilobitsPerSec(300);
});
auto* route = s.CreateRoutes(client, {s.CreateSimulationNode(net_conf)},
s.CreateClient("return", CallClientConfig()),
{s.CreateSimulationNode(net_conf)});
VideoStreamConfig video;
video.stream.packet_feedback = false;
s.CreateVideoStream(route->forward(), video);
s.RunFor(TimeDelta::Seconds(30));
EXPECT_NEAR(client->send_bandwidth().kbps(), 900, 150);
}
TEST(ReceiveSideCongestionControllerTest, IsFairToTCP) {
Scenario s("receive_cc_unit/tcp_fairness");
NetworkSimulationConfig net_conf;
net_conf.bandwidth = DataRate::KilobitsPerSec(1000);
net_conf.delay = TimeDelta::Millis(50);
auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
c->transport.rates.start_rate = DataRate::KilobitsPerSec(1000);
});
auto send_net = {s.CreateSimulationNode(net_conf)};
auto ret_net = {s.CreateSimulationNode(net_conf)};
auto* route = s.CreateRoutes(
client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
VideoStreamConfig video;
video.stream.packet_feedback = false;
s.CreateVideoStream(route->forward(), video);
s.net()->StartCrossTraffic(CreateFakeTcpCrossTraffic(
s.net()->CreateRoute(send_net), s.net()->CreateRoute(ret_net),
FakeTcpConfig()));
s.RunFor(TimeDelta::Seconds(30));
// For some reason we get outcompeted by TCP here, this should probably be
// fixed and a lower bound should be added to the test.
EXPECT_LT(client->send_bandwidth().kbps(), 750);
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/remb_throttler.h"
#include <algorithm>
#include <utility>
namespace webrtc {
namespace {
constexpr TimeDelta kRembSendInterval = TimeDelta::Millis(200);
} // namespace
RembThrottler::RembThrottler(RembSender remb_sender, Clock* clock)
: remb_sender_(std::move(remb_sender)),
clock_(clock),
last_remb_time_(Timestamp::MinusInfinity()),
last_send_remb_bitrate_(DataRate::PlusInfinity()),
max_remb_bitrate_(DataRate::PlusInfinity()) {}
void RembThrottler::OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
uint32_t bitrate_bps) {
DataRate receive_bitrate = DataRate::BitsPerSec(bitrate_bps);
Timestamp now = clock_->CurrentTime();
{
MutexLock lock(&mutex_);
// % threshold for if we should send a new REMB asap.
const int64_t kSendThresholdPercent = 103;
if (receive_bitrate * kSendThresholdPercent / 100 >
last_send_remb_bitrate_ &&
now < last_remb_time_ + kRembSendInterval) {
return;
}
last_remb_time_ = now;
last_send_remb_bitrate_ = receive_bitrate;
receive_bitrate = std::min(last_send_remb_bitrate_, max_remb_bitrate_);
}
remb_sender_(receive_bitrate.bps(), ssrcs);
}
void RembThrottler::SetMaxDesiredReceiveBitrate(DataRate bitrate) {
Timestamp now = clock_->CurrentTime();
{
MutexLock lock(&mutex_);
max_remb_bitrate_ = bitrate;
if (now - last_remb_time_ < kRembSendInterval &&
!last_send_remb_bitrate_.IsZero() &&
last_send_remb_bitrate_ <= max_remb_bitrate_) {
return;
}
}
remb_sender_(bitrate.bps(), /*ssrcs=*/{});
}
} // namespace webrtc

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_
#define MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_
#include <functional>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
// RembThrottler is a helper class used for throttling RTCP REMB messages.
// Throttles small changes to the received BWE within 200ms.
class RembThrottler : public RemoteBitrateObserver {
public:
using RembSender =
std::function<void(int64_t bitrate_bps, std::vector<uint32_t> ssrcs)>;
RembThrottler(RembSender remb_sender, Clock* clock);
// Ensures the remote party is notified of the receive bitrate no larger than
// `bitrate` using RTCP REMB.
void SetMaxDesiredReceiveBitrate(DataRate bitrate);
// Implements RemoteBitrateObserver;
// Called every time there is a new bitrate estimate for a receive channel
// group. This call will trigger a new RTCP REMB packet if the bitrate
// estimate has decreased or if no RTCP REMB packet has been sent for
// a certain time interval.
void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
uint32_t bitrate_bps) override;
private:
const RembSender remb_sender_;
Clock* const clock_;
mutable Mutex mutex_;
Timestamp last_remb_time_ RTC_GUARDED_BY(mutex_);
DataRate last_send_remb_bitrate_ RTC_GUARDED_BY(mutex_);
DataRate max_remb_bitrate_ RTC_GUARDED_BY(mutex_);
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/rtp/control_handler.h"
#include <algorithm>
#include <vector>
#include "api/units/data_rate.h"
#include "modules/pacing/pacing_controller.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
void CongestionControlHandler::SetTargetRate(
TargetTransferRate new_target_rate) {
RTC_DCHECK_RUN_ON(&sequenced_checker_);
RTC_CHECK(new_target_rate.at_time.IsFinite());
last_incoming_ = new_target_rate;
}
void CongestionControlHandler::SetNetworkAvailability(bool network_available) {
RTC_DCHECK_RUN_ON(&sequenced_checker_);
network_available_ = network_available;
}
void CongestionControlHandler::SetPacerQueue(TimeDelta expected_queue_time) {
RTC_DCHECK_RUN_ON(&sequenced_checker_);
pacer_expected_queue_ms_ = expected_queue_time.ms();
}
absl::optional<TargetTransferRate> CongestionControlHandler::GetUpdate() {
RTC_DCHECK_RUN_ON(&sequenced_checker_);
if (!last_incoming_.has_value())
return absl::nullopt;
TargetTransferRate new_outgoing = *last_incoming_;
DataRate log_target_rate = new_outgoing.target_rate;
bool pause_encoding = false;
if (!network_available_) {
pause_encoding = true;
} else if (pacer_expected_queue_ms_ >
PacingController::kMaxExpectedQueueLength.ms()) {
pause_encoding = true;
}
if (pause_encoding)
new_outgoing.target_rate = DataRate::Zero();
if (!last_reported_ ||
last_reported_->target_rate != new_outgoing.target_rate ||
(!new_outgoing.target_rate.IsZero() &&
(last_reported_->network_estimate.loss_rate_ratio !=
new_outgoing.network_estimate.loss_rate_ratio ||
last_reported_->network_estimate.round_trip_time !=
new_outgoing.network_estimate.round_trip_time))) {
if (encoder_paused_in_last_report_ != pause_encoding)
RTC_LOG(LS_INFO) << "Bitrate estimate state changed, BWE: "
<< ToString(log_target_rate) << ".";
encoder_paused_in_last_report_ = pause_encoding;
last_reported_ = new_outgoing;
return new_outgoing;
}
return absl::nullopt;
}
} // namespace webrtc

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_CONTROL_HANDLER_H_
#define MODULES_CONGESTION_CONTROLLER_RTP_CONTROL_HANDLER_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/sequence_checker.h"
#include "api/transport/network_types.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "rtc_base/system/no_unique_address.h"
namespace webrtc {
// This is used to observe the network controller state and route calls to
// the proper handler. It also keeps cached values for safe asynchronous use.
// This makes sure that things running on the worker queue can't access state
// in RtpTransportControllerSend, which would risk causing data race on
// destruction unless members are properly ordered.
class CongestionControlHandler {
public:
CongestionControlHandler() = default;
CongestionControlHandler(const CongestionControlHandler&) = delete;
CongestionControlHandler& operator=(const CongestionControlHandler&) = delete;
~CongestionControlHandler() = default;
void SetTargetRate(TargetTransferRate new_target_rate);
void SetNetworkAvailability(bool network_available);
void SetPacerQueue(TimeDelta expected_queue_time);
absl::optional<TargetTransferRate> GetUpdate();
private:
absl::optional<TargetTransferRate> last_incoming_;
absl::optional<TargetTransferRate> last_reported_;
bool network_available_ = true;
bool encoder_paused_in_last_report_ = false;
int64_t pacer_expected_queue_ms_ = 0;
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequenced_checker_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_RTP_CONTROL_HANDLER_H_

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/rtp/transport_feedback_adapter.h"
#include <stdlib.h>
#include <algorithm>
#include <cmath>
#include <utility>
#include "absl/algorithm/container.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
constexpr TimeDelta kSendTimeHistoryWindow = TimeDelta::Seconds(60);
void InFlightBytesTracker::AddInFlightPacketBytes(
const PacketFeedback& packet) {
RTC_DCHECK(packet.sent.send_time.IsFinite());
auto it = in_flight_data_.find(packet.network_route);
if (it != in_flight_data_.end()) {
it->second += packet.sent.size;
} else {
in_flight_data_.insert({packet.network_route, packet.sent.size});
}
}
void InFlightBytesTracker::RemoveInFlightPacketBytes(
const PacketFeedback& packet) {
if (packet.sent.send_time.IsInfinite())
return;
auto it = in_flight_data_.find(packet.network_route);
if (it != in_flight_data_.end()) {
RTC_DCHECK_GE(it->second, packet.sent.size);
it->second -= packet.sent.size;
if (it->second.IsZero())
in_flight_data_.erase(it);
}
}
DataSize InFlightBytesTracker::GetOutstandingData(
const rtc::NetworkRoute& network_route) const {
auto it = in_flight_data_.find(network_route);
if (it != in_flight_data_.end()) {
return it->second;
} else {
return DataSize::Zero();
}
}
// Comparator for consistent map with NetworkRoute as key.
bool InFlightBytesTracker::NetworkRouteComparator::operator()(
const rtc::NetworkRoute& a,
const rtc::NetworkRoute& b) const {
if (a.local.network_id() != b.local.network_id())
return a.local.network_id() < b.local.network_id();
if (a.remote.network_id() != b.remote.network_id())
return a.remote.network_id() < b.remote.network_id();
if (a.local.adapter_id() != b.local.adapter_id())
return a.local.adapter_id() < b.local.adapter_id();
if (a.remote.adapter_id() != b.remote.adapter_id())
return a.remote.adapter_id() < b.remote.adapter_id();
if (a.local.uses_turn() != b.local.uses_turn())
return a.local.uses_turn() < b.local.uses_turn();
if (a.remote.uses_turn() != b.remote.uses_turn())
return a.remote.uses_turn() < b.remote.uses_turn();
return a.connected < b.connected;
}
TransportFeedbackAdapter::TransportFeedbackAdapter() = default;
void TransportFeedbackAdapter::AddPacket(const RtpPacketSendInfo& packet_info,
size_t overhead_bytes,
Timestamp creation_time) {
PacketFeedback packet;
packet.creation_time = creation_time;
packet.sent.sequence_number =
seq_num_unwrapper_.Unwrap(packet_info.transport_sequence_number);
packet.sent.size = DataSize::Bytes(packet_info.length + overhead_bytes);
packet.sent.audio = packet_info.packet_type == RtpPacketMediaType::kAudio;
packet.network_route = network_route_;
packet.sent.pacing_info = packet_info.pacing_info;
while (!history_.empty() &&
creation_time - history_.begin()->second.creation_time >
kSendTimeHistoryWindow) {
// TODO(sprang): Warn if erasing (too many) old items?
if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_)
in_flight_.RemoveInFlightPacketBytes(history_.begin()->second);
history_.erase(history_.begin());
}
history_.insert(std::make_pair(packet.sent.sequence_number, packet));
}
absl::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
const rtc::SentPacket& sent_packet) {
auto send_time = Timestamp::Millis(sent_packet.send_time_ms);
// TODO(srte): Only use one way to indicate that packet feedback is used.
if (sent_packet.info.included_in_feedback || sent_packet.packet_id != -1) {
int64_t unwrapped_seq_num =
seq_num_unwrapper_.Unwrap(sent_packet.packet_id);
auto it = history_.find(unwrapped_seq_num);
if (it != history_.end()) {
bool packet_retransmit = it->second.sent.send_time.IsFinite();
it->second.sent.send_time = send_time;
last_send_time_ = std::max(last_send_time_, send_time);
// TODO(srte): Don't do this on retransmit.
if (!pending_untracked_size_.IsZero()) {
if (send_time < last_untracked_send_time_)
RTC_LOG(LS_WARNING)
<< "appending acknowledged data for out of order packet. (Diff: "
<< ToString(last_untracked_send_time_ - send_time) << " ms.)";
it->second.sent.prior_unacked_data += pending_untracked_size_;
pending_untracked_size_ = DataSize::Zero();
}
if (!packet_retransmit) {
if (it->second.sent.sequence_number > last_ack_seq_num_)
in_flight_.AddInFlightPacketBytes(it->second);
it->second.sent.data_in_flight = GetOutstandingData();
return it->second.sent;
}
}
} else if (sent_packet.info.included_in_allocation) {
if (send_time < last_send_time_) {
RTC_LOG(LS_WARNING) << "ignoring untracked data for out of order packet.";
}
pending_untracked_size_ +=
DataSize::Bytes(sent_packet.info.packet_size_bytes);
last_untracked_send_time_ = std::max(last_untracked_send_time_, send_time);
}
return absl::nullopt;
}
absl::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ProcessTransportFeedback(
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time) {
if (feedback.GetPacketStatusCount() == 0) {
RTC_LOG(LS_INFO) << "Empty transport feedback packet received.";
return absl::nullopt;
}
TransportPacketsFeedback msg;
msg.feedback_time = feedback_receive_time;
msg.prior_in_flight = in_flight_.GetOutstandingData(network_route_);
msg.packet_feedbacks =
ProcessTransportFeedbackInner(feedback, feedback_receive_time);
if (msg.packet_feedbacks.empty())
return absl::nullopt;
auto it = history_.find(last_ack_seq_num_);
if (it != history_.end()) {
msg.first_unacked_send_time = it->second.sent.send_time;
}
msg.data_in_flight = in_flight_.GetOutstandingData(network_route_);
return msg;
}
void TransportFeedbackAdapter::SetNetworkRoute(
const rtc::NetworkRoute& network_route) {
network_route_ = network_route;
}
DataSize TransportFeedbackAdapter::GetOutstandingData() const {
return in_flight_.GetOutstandingData(network_route_);
}
std::vector<PacketResult>
TransportFeedbackAdapter::ProcessTransportFeedbackInner(
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time) {
// Add timestamp deltas to a local time base selected on first packet arrival.
// This won't be the true time base, but makes it easier to manually inspect
// time stamps.
if (last_timestamp_.IsInfinite()) {
current_offset_ = feedback_receive_time;
} else {
// TODO(srte): We shouldn't need to do rounding here.
const TimeDelta delta = feedback.GetBaseDelta(last_timestamp_)
.RoundDownTo(TimeDelta::Millis(1));
// Protect against assigning current_offset_ negative value.
if (delta < Timestamp::Zero() - current_offset_) {
RTC_LOG(LS_WARNING) << "Unexpected feedback timestamp received.";
current_offset_ = feedback_receive_time;
} else {
current_offset_ += delta;
}
}
last_timestamp_ = feedback.BaseTime();
std::vector<PacketResult> packet_result_vector;
packet_result_vector.reserve(feedback.GetPacketStatusCount());
size_t failed_lookups = 0;
size_t ignored = 0;
feedback.ForAllPackets(
[&](uint16_t sequence_number, TimeDelta delta_since_base) {
int64_t seq_num = seq_num_unwrapper_.Unwrap(sequence_number);
if (seq_num > last_ack_seq_num_) {
// Starts at history_.begin() if last_ack_seq_num_ < 0, since any
// valid sequence number is >= 0.
for (auto it = history_.upper_bound(last_ack_seq_num_);
it != history_.upper_bound(seq_num); ++it) {
in_flight_.RemoveInFlightPacketBytes(it->second);
}
last_ack_seq_num_ = seq_num;
}
auto it = history_.find(seq_num);
if (it == history_.end()) {
++failed_lookups;
return;
}
if (it->second.sent.send_time.IsInfinite()) {
// TODO(srte): Fix the tests that makes this happen and make this a
// DCHECK.
RTC_DLOG(LS_ERROR)
<< "Received feedback before packet was indicated as sent";
return;
}
PacketFeedback packet_feedback = it->second;
if (delta_since_base.IsFinite()) {
packet_feedback.receive_time =
current_offset_ +
delta_since_base.RoundDownTo(TimeDelta::Millis(1));
// Note: Lost packets are not removed from history because they might
// be reported as received by a later feedback.
history_.erase(it);
}
if (packet_feedback.network_route == network_route_) {
PacketResult result;
result.sent_packet = packet_feedback.sent;
result.receive_time = packet_feedback.receive_time;
packet_result_vector.push_back(result);
} else {
++ignored;
}
});
if (failed_lookups > 0) {
RTC_LOG(LS_WARNING) << "Failed to lookup send time for " << failed_lookups
<< " packet" << (failed_lookups > 1 ? "s" : "")
<< ". Send time history too small?";
}
if (ignored > 0) {
RTC_LOG(LS_INFO) << "Ignoring " << ignored
<< " packets because they were sent on a different route.";
}
return packet_result_vector;
}
} // namespace webrtc

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
#define MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
#include <deque>
#include <map>
#include <utility>
#include <vector>
#include "api/sequence_checker.h"
#include "api/transport/network_types.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/network_route.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
struct PacketFeedback {
PacketFeedback() = default;
// Time corresponding to when this object was created.
Timestamp creation_time = Timestamp::MinusInfinity();
SentPacket sent;
// Time corresponding to when the packet was received. Timestamped with the
// receiver's clock. For unreceived packet, Timestamp::PlusInfinity() is
// used.
Timestamp receive_time = Timestamp::PlusInfinity();
// The network route that this packet is associated with.
rtc::NetworkRoute network_route;
};
class InFlightBytesTracker {
public:
void AddInFlightPacketBytes(const PacketFeedback& packet);
void RemoveInFlightPacketBytes(const PacketFeedback& packet);
DataSize GetOutstandingData(const rtc::NetworkRoute& network_route) const;
private:
struct NetworkRouteComparator {
bool operator()(const rtc::NetworkRoute& a,
const rtc::NetworkRoute& b) const;
};
std::map<rtc::NetworkRoute, DataSize, NetworkRouteComparator> in_flight_data_;
};
class TransportFeedbackAdapter {
public:
TransportFeedbackAdapter();
void AddPacket(const RtpPacketSendInfo& packet_info,
size_t overhead_bytes,
Timestamp creation_time);
absl::optional<SentPacket> ProcessSentPacket(
const rtc::SentPacket& sent_packet);
absl::optional<TransportPacketsFeedback> ProcessTransportFeedback(
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time);
void SetNetworkRoute(const rtc::NetworkRoute& network_route);
DataSize GetOutstandingData() const;
private:
enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate };
std::vector<PacketResult> ProcessTransportFeedbackInner(
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time);
DataSize pending_untracked_size_ = DataSize::Zero();
Timestamp last_send_time_ = Timestamp::MinusInfinity();
Timestamp last_untracked_send_time_ = Timestamp::MinusInfinity();
RtpSequenceNumberUnwrapper seq_num_unwrapper_;
std::map<int64_t, PacketFeedback> history_;
// Sequence numbers are never negative, using -1 as it always < a real
// sequence number.
int64_t last_ack_seq_num_ = -1;
InFlightBytesTracker in_flight_;
Timestamp current_offset_ = Timestamp::MinusInfinity();
Timestamp last_timestamp_ = Timestamp::MinusInfinity();
rtc::NetworkRoute network_route_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/congestion_controller/rtp/transport_feedback_demuxer.h"
#include "absl/algorithm/container.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
namespace webrtc {
namespace {
static const size_t kMaxPacketsInHistory = 5000;
}
TransportFeedbackDemuxer::TransportFeedbackDemuxer() {
// In case the construction thread is different from where the registration
// and callbacks occur, detach from the construction thread.
observer_checker_.Detach();
}
void TransportFeedbackDemuxer::RegisterStreamFeedbackObserver(
std::vector<uint32_t> ssrcs,
StreamFeedbackObserver* observer) {
RTC_DCHECK_RUN_ON(&observer_checker_);
RTC_DCHECK(observer);
RTC_DCHECK(absl::c_find_if(observers_, [=](const auto& pair) {
return pair.second == observer;
}) == observers_.end());
observers_.push_back({ssrcs, observer});
}
void TransportFeedbackDemuxer::DeRegisterStreamFeedbackObserver(
StreamFeedbackObserver* observer) {
RTC_DCHECK_RUN_ON(&observer_checker_);
RTC_DCHECK(observer);
const auto it = absl::c_find_if(
observers_, [=](const auto& pair) { return pair.second == observer; });
RTC_DCHECK(it != observers_.end());
observers_.erase(it);
}
void TransportFeedbackDemuxer::AddPacket(const RtpPacketSendInfo& packet_info) {
RTC_DCHECK_RUN_ON(&observer_checker_);
StreamFeedbackObserver::StreamPacketInfo info;
info.ssrc = packet_info.media_ssrc;
info.rtp_sequence_number = packet_info.rtp_sequence_number;
info.received = false;
info.is_retransmission =
packet_info.packet_type == RtpPacketMediaType::kRetransmission;
history_.insert(
{seq_num_unwrapper_.Unwrap(packet_info.transport_sequence_number), info});
while (history_.size() > kMaxPacketsInHistory) {
history_.erase(history_.begin());
}
}
void TransportFeedbackDemuxer::OnTransportFeedback(
const rtcp::TransportFeedback& feedback) {
RTC_DCHECK_RUN_ON(&observer_checker_);
std::vector<StreamFeedbackObserver::StreamPacketInfo> stream_feedbacks;
feedback.ForAllPackets(
[&](uint16_t sequence_number, TimeDelta delta_since_base) {
RTC_DCHECK_RUN_ON(&observer_checker_);
auto it = history_.find(seq_num_unwrapper_.PeekUnwrap(sequence_number));
if (it != history_.end()) {
auto packet_info = it->second;
packet_info.received = delta_since_base.IsFinite();
stream_feedbacks.push_back(std::move(packet_info));
if (delta_since_base.IsFinite())
history_.erase(it);
}
});
for (auto& observer : observers_) {
std::vector<StreamFeedbackObserver::StreamPacketInfo> selected_feedback;
for (const auto& packet_info : stream_feedbacks) {
if (absl::c_count(observer.first, packet_info.ssrc) > 0) {
selected_feedback.push_back(packet_info);
}
}
if (!selected_feedback.empty()) {
observer.second->OnPacketFeedbackVector(std::move(selected_feedback));
}
}
}
} // namespace webrtc

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_DEMUXER_H_
#define MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_DEMUXER_H_
#include <map>
#include <utility>
#include <vector>
#include "api/sequence_checker.h"
#include "modules/include/module_common_types_public.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/system/no_unique_address.h"
namespace webrtc {
// Implementation of StreamFeedbackProvider that provides a way for
// implementations of StreamFeedbackObserver to register for feedback callbacks
// for a given set of SSRCs.
// Registration methods need to be called from the same execution context
// (thread or task queue) and callbacks to
// StreamFeedbackObserver::OnPacketFeedbackVector will be made in that same
// context.
// TODO(tommi): This appears to be the only implementation of this interface.
// Do we need the interface?
class TransportFeedbackDemuxer final : public StreamFeedbackProvider {
public:
TransportFeedbackDemuxer();
// Implements StreamFeedbackProvider interface
void RegisterStreamFeedbackObserver(
std::vector<uint32_t> ssrcs,
StreamFeedbackObserver* observer) override;
void DeRegisterStreamFeedbackObserver(
StreamFeedbackObserver* observer) override;
void AddPacket(const RtpPacketSendInfo& packet_info);
void OnTransportFeedback(const rtcp::TransportFeedback& feedback);
private:
RTC_NO_UNIQUE_ADDRESS SequenceChecker observer_checker_;
RtpSequenceNumberUnwrapper seq_num_unwrapper_
RTC_GUARDED_BY(&observer_checker_);
std::map<int64_t, StreamFeedbackObserver::StreamPacketInfo> history_
RTC_GUARDED_BY(&observer_checker_);
// Maps a set of ssrcs to corresponding observer. Vectors are used rather than
// set/map to ensure that the processing order is consistent independently of
// the randomized ssrcs.
std::vector<std::pair<std::vector<uint32_t>, StreamFeedbackObserver*>>
observers_ RTC_GUARDED_BY(&observer_checker_);
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_DEMUXER_H_