[feat] use original webrtc header defines

This commit is contained in:
dijunkun
2025-01-16 17:33:46 +08:00
parent a8e9609736
commit 6e2a52e506
90 changed files with 6959 additions and 546 deletions

View File

@@ -16,48 +16,46 @@
#include <utility>
#include <vector>
#include "bitrate_estimator.h"
#include "log.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "network_types.h"
namespace webrtc {
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator()
: AcknowledgedBitrateEstimator(std::make_unique<BitrateEstimator>()) {}
AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
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) {
if (!std::is_sorted(packet_feedback_vector.begin(),
packet_feedback_vector.end(),
PacketResult::ReceiveTimeOrder())) {
LOG_FATAL("packet_feedback_vector is not sorted");
}
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();
}
int64_t acknowledged_estimate = packet.sent_packet.size;
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_);
}
}
std::optional<int64_t> AcknowledgedBitrateEstimator::bitrate() const {
std::optional<DataRate> AcknowledgedBitrateEstimator::bitrate() const {
return bitrate_estimator_->bitrate();
}
std::optional<int64_t> AcknowledgedBitrateEstimator::PeekRate() const {
std::optional<DataRate> AcknowledgedBitrateEstimator::PeekRate() const {
return bitrate_estimator_->PeekRate();
}
void AcknowledgedBitrateEstimator::SetAlrEndedTime(int64_t alr_ended_time) {
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

@@ -1,19 +1,27 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-14
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#define _ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#include <memory>
#include <optional>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "bitrate_estimator.h"
#include "network_types.h"
namespace webrtc {
class AcknowledgedBitrateEstimator {
public:
AcknowledgedBitrateEstimator(
@@ -24,15 +32,17 @@ class AcknowledgedBitrateEstimator {
void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector);
std::optional<int64_t> bitrate() const;
std::optional<int64_t> PeekRate() const;
std::optional<DataRate> bitrate() const;
std::optional<DataRate> PeekRate() const;
void SetAlr(bool in_alr);
void SetAlrEndedTime(int64_t alr_ended_time);
void SetAlrEndedTime(Timestamp alr_ended_time);
private:
std::optional<int64_t> alr_ended_time_;
std::optional<Timestamp> alr_ended_time_;
bool in_alr_;
std::unique_ptr<BitrateEstimator> bitrate_estimator_;
};
#endif
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_

View File

@@ -0,0 +1,330 @@
/*
* Copyright (c) 2014 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 "aimd_rate_control.h"
#include <inttypes.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include "api/units/data_rate.h"
#include "log.h"
#include "network_types.h"
#include "overuse_detector.h"
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace {
constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200);
constexpr double kDefaultBackoffFactor = 0.85;
constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor";
} // namespace
AimdRateControl::AimdRateControl() : AimdRateControl(/* send_side =*/false) {}
AimdRateControl::AimdRateControl(bool send_side)
: min_configured_bitrate_(kCongestionControllerMinBitrate),
max_configured_bitrate_(DataRate::KilobitsPerSec(30000)),
current_bitrate_(max_configured_bitrate_),
latest_estimated_throughput_(current_bitrate_),
link_capacity_(),
rate_control_state_(RateControlState::kRcHold),
time_last_bitrate_change_(Timestamp::MinusInfinity()),
time_last_bitrate_decrease_(Timestamp::MinusInfinity()),
time_first_throughput_estimate_(Timestamp::MinusInfinity()),
bitrate_is_initialized_(false),
in_alr_(false),
rtt_(kDefaultRtt),
send_side_(send_side) {}
AimdRateControl::~AimdRateControl() {}
void AimdRateControl::SetStartBitrate(DataRate start_bitrate) {
current_bitrate_ = start_bitrate;
latest_estimated_throughput_ = current_bitrate_;
bitrate_is_initialized_ = true;
}
void AimdRateControl::SetMinBitrate(DataRate min_bitrate) {
min_configured_bitrate_ = min_bitrate;
current_bitrate_ = std::max(min_bitrate, current_bitrate_);
}
bool AimdRateControl::ValidEstimate() const { return bitrate_is_initialized_; }
TimeDelta AimdRateControl::GetFeedbackInterval() const {
// Estimate how often we can send RTCP if we allocate up to 5% of bandwidth
// to feedback.
const DataSize kRtcpSize = DataSize::Bytes(80);
const DataRate rtcp_bitrate = current_bitrate_ * 0.05;
const TimeDelta interval = kRtcpSize / rtcp_bitrate;
const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200);
const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000);
return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval);
}
bool AimdRateControl::TimeToReduceFurther(Timestamp at_time,
DataRate estimated_throughput) const {
const TimeDelta bitrate_reduction_interval =
rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200));
if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) {
return true;
}
if (ValidEstimate()) {
// TODO(terelius/holmer): Investigate consequences of increasing
// the threshold to 0.95 * LatestEstimate().
const DataRate threshold = 0.5 * LatestEstimate();
return estimated_throughput < threshold;
}
return false;
}
bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const {
return ValidEstimate() &&
TimeToReduceFurther(at_time,
LatestEstimate() / 2 - DataRate::BitsPerSec(1));
}
DataRate AimdRateControl::LatestEstimate() const { return current_bitrate_; }
void AimdRateControl::SetRtt(TimeDelta rtt) { rtt_ = rtt; }
DataRate AimdRateControl::Update(const RateControlInput& input,
Timestamp at_time) {
// Set the initial bit rate value to what we're receiving the first half
// second.
// TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code.
if (!bitrate_is_initialized_) {
const TimeDelta kInitializationTime = TimeDelta::Seconds(5);
if (time_first_throughput_estimate_.IsInfinite()) {
if (input.estimated_throughput) time_first_throughput_estimate_ = at_time;
} else if (at_time - time_first_throughput_estimate_ >
kInitializationTime &&
input.estimated_throughput) {
current_bitrate_ = *input.estimated_throughput;
bitrate_is_initialized_ = true;
}
}
ChangeBitrate(input, at_time);
return current_bitrate_;
}
void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) {
in_alr_ = in_alr;
}
void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) {
bitrate_is_initialized_ = true;
DataRate prev_bitrate = current_bitrate_;
current_bitrate_ = ClampBitrate(bitrate);
time_last_bitrate_change_ = at_time;
if (current_bitrate_ < prev_bitrate) {
time_last_bitrate_decrease_ = at_time;
}
}
double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const {
const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30;
DataSize frame_size = current_bitrate_ * kFrameInterval;
const DataSize kPacketSize = DataSize::Bytes(1200);
double packets_per_frame = std::ceil(frame_size / kPacketSize);
DataSize avg_packet_size = frame_size / packets_per_frame;
// Approximate the over-use estimator delay to 100 ms.
TimeDelta response_time = rtt_ + TimeDelta::Millis(100);
response_time = response_time * 2;
double increase_rate_bps_per_second =
(avg_packet_size / response_time).bps<double>();
double kMinIncreaseRateBpsPerSecond = 4000;
return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second);
}
TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const {
const TimeDelta kMinPeriod = TimeDelta::Seconds(2);
const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3);
const TimeDelta kMaxPeriod = TimeDelta::Seconds(50);
double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond();
if (!last_decrease_) return kDefaultPeriod;
double time_to_recover_decrease_seconds =
last_decrease_->bps() / increase_rate_bps_per_second;
TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds);
return period.Clamped(kMinPeriod, kMaxPeriod);
}
void AimdRateControl::ChangeBitrate(const RateControlInput& input,
Timestamp at_time) {
std::optional<DataRate> new_bitrate;
DataRate estimated_throughput =
input.estimated_throughput.value_or(latest_estimated_throughput_);
if (input.estimated_throughput)
latest_estimated_throughput_ = *input.estimated_throughput;
// An over-use should always trigger us to reduce the bitrate, even though
// we have not yet established our first estimate. By acting on the over-use,
// we will end up with a valid estimate.
if (!bitrate_is_initialized_ &&
input.bw_state != BandwidthUsage::kBwOverusing)
return;
ChangeState(input, at_time);
switch (rate_control_state_) {
case RateControlState::kRcHold:
break;
case RateControlState::kRcIncrease: {
if (estimated_throughput > link_capacity_.UpperBound())
link_capacity_.Reset();
// We limit the new bitrate based on the troughput to avoid unlimited
// bitrate increases. We allow a bit more lag at very low rates to not too
// easily get stuck if the encoder produces uneven outputs.
DataRate increase_limit =
1.5 * estimated_throughput + DataRate::KilobitsPerSec(10);
if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) {
// Do not increase the delay based estimate in alr since the estimator
// will not be able to get transport feedback necessary to detect if
// the new estimate is correct.
// If we have previously increased above the limit (for instance due to
// probing), we don't allow further changes.
increase_limit = current_bitrate_;
}
if (current_bitrate_ < increase_limit) {
DataRate increased_bitrate = DataRate::MinusInfinity();
if (link_capacity_.has_estimate()) {
// The link_capacity estimate is reset if the measured throughput
// is too far from the estimate. We can therefore assume that our
// target rate is reasonably close to link capacity and use additive
// increase.
DataRate additive_increase =
AdditiveRateIncrease(at_time, time_last_bitrate_change_);
increased_bitrate = current_bitrate_ + additive_increase;
} else {
// If we don't have an estimate of the link capacity, use faster ramp
// up to discover the capacity.
DataRate multiplicative_increase = MultiplicativeRateIncrease(
at_time, time_last_bitrate_change_, current_bitrate_);
increased_bitrate = current_bitrate_ + multiplicative_increase;
}
new_bitrate = std::min(increased_bitrate, increase_limit);
}
time_last_bitrate_change_ = at_time;
break;
}
case RateControlState::kRcDecrease: {
DataRate decreased_bitrate = DataRate::PlusInfinity();
// Set bit rate to something slightly lower than the measured throughput
// to get rid of any self-induced delay.
decreased_bitrate = estimated_throughput * beta_;
if (decreased_bitrate > DataRate::KilobitsPerSec(5)) {
decreased_bitrate -= DataRate::KilobitsPerSec(5);
}
if (decreased_bitrate > current_bitrate_) {
// TODO(terelius): The link_capacity estimate may be based on old
// throughput measurements. Relying on them may lead to unnecessary
// BWE drops.
if (link_capacity_.has_estimate()) {
decreased_bitrate = beta_ * link_capacity_.estimate();
}
}
// Avoid increasing the rate when over-using.
if (decreased_bitrate < current_bitrate_) {
new_bitrate = decreased_bitrate;
}
if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) {
if (!new_bitrate.has_value()) {
last_decrease_ = DataRate::Zero();
} else {
last_decrease_ = current_bitrate_ - *new_bitrate;
}
}
if (estimated_throughput < link_capacity_.LowerBound()) {
// The current throughput is far from the estimated link capacity. Clear
// the estimate to allow an immediate update in OnOveruseDetected.
link_capacity_.Reset();
}
bitrate_is_initialized_ = true;
link_capacity_.OnOveruseDetected(estimated_throughput);
// Stay on hold until the pipes are cleared.
rate_control_state_ = RateControlState::kRcHold;
time_last_bitrate_change_ = at_time;
time_last_bitrate_decrease_ = at_time;
break;
}
default:
break;
}
current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_));
}
DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const {
new_bitrate = std::max(new_bitrate, min_configured_bitrate_);
return new_bitrate;
}
DataRate AimdRateControl::MultiplicativeRateIncrease(
Timestamp at_time, Timestamp last_time, DataRate current_bitrate) const {
double alpha = 1.08;
if (last_time.IsFinite()) {
auto time_since_last_update = at_time - last_time;
alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0));
}
DataRate multiplicative_increase =
std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000));
return multiplicative_increase;
}
DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time,
Timestamp last_time) const {
double time_period_seconds = (at_time - last_time).seconds<double>();
double data_rate_increase_bps =
GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds;
return DataRate::BitsPerSec(data_rate_increase_bps);
}
void AimdRateControl::ChangeState(const RateControlInput& input,
Timestamp at_time) {
switch (input.bw_state) {
case BandwidthUsage::kBwNormal:
if (rate_control_state_ == RateControlState::kRcHold) {
time_last_bitrate_change_ = at_time;
rate_control_state_ = RateControlState::kRcIncrease;
}
break;
case BandwidthUsage::kBwOverusing:
if (rate_control_state_ != RateControlState::kRcDecrease) {
rate_control_state_ = RateControlState::kRcDecrease;
}
break;
case BandwidthUsage::kBwUnderusing:
rate_control_state_ = RateControlState::kRcHold;
break;
default:
break;
}
}
} // namespace webrtc

109
src/qos/aimd_rate_control.h Normal file
View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2014 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_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
#include <stdint.h>
#include <optional>
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "bwe_defines.h"
#include "link_capacity_estimator.h"
#include "network_types.h"
namespace webrtc {
// A rate control implementation based on additive increases of
// bitrate when no over-use is detected and multiplicative decreases when
// over-uses are detected. When we think the available bandwidth has changes or
// is unknown, we will switch to a "slow-start mode" where we increase
// multiplicatively.
class AimdRateControl {
public:
explicit AimdRateControl();
AimdRateControl(bool send_side);
~AimdRateControl();
// Returns true if the target bitrate has been initialized. This happens
// either if it has been explicitly set via SetStartBitrate/SetEstimate, or if
// we have measured a throughput.
bool ValidEstimate() const;
void SetStartBitrate(DataRate start_bitrate);
void SetMinBitrate(DataRate min_bitrate);
TimeDelta GetFeedbackInterval() const;
// Returns true if the bitrate estimate hasn't been changed for more than
// an RTT, or if the estimated_throughput is less than half of the current
// estimate. Should be used to decide if we should reduce the rate further
// when over-using.
bool TimeToReduceFurther(Timestamp at_time,
DataRate estimated_throughput) const;
// As above. To be used if overusing before we have measured a throughput.
bool InitialTimeToReduceFurther(Timestamp at_time) const;
DataRate LatestEstimate() const;
void SetRtt(TimeDelta rtt);
DataRate Update(const RateControlInput& input, Timestamp at_time);
void SetInApplicationLimitedRegion(bool in_alr);
void SetEstimate(DataRate bitrate, Timestamp at_time);
// Returns the increase rate when used bandwidth is near the link capacity.
double GetNearMaxIncreaseRateBpsPerSecond() const;
// Returns the expected time between overuse signals (assuming steady state).
TimeDelta GetExpectedBandwidthPeriod() const;
private:
enum class RateControlState { kRcHold, kRcIncrease, kRcDecrease };
friend class GoogCcStatePrinter;
// Update the target bitrate based on, among other things, the current rate
// control state, the current target bitrate and the estimated throughput.
// When in the "increase" state the bitrate will be increased either
// additively or multiplicatively depending on the rate control region. When
// in the "decrease" state the bitrate will be decreased to slightly below the
// current throughput. When in the "hold" state the bitrate will be kept
// constant to allow built up queues to drain.
void ChangeBitrate(const RateControlInput& input, Timestamp at_time);
DataRate ClampBitrate(DataRate new_bitrate) const;
DataRate MultiplicativeRateIncrease(Timestamp at_time, Timestamp last_ms,
DataRate current_bitrate) const;
DataRate AdditiveRateIncrease(Timestamp at_time, Timestamp last_time) const;
void UpdateChangePeriod(Timestamp at_time);
void ChangeState(const RateControlInput& input, Timestamp at_time);
DataRate min_configured_bitrate_;
DataRate max_configured_bitrate_;
DataRate current_bitrate_;
DataRate latest_estimated_throughput_;
LinkCapacityEstimator link_capacity_;
std::optional<NetworkStateEstimate> network_estimate_;
RateControlState rate_control_state_;
Timestamp time_last_bitrate_change_;
Timestamp time_last_bitrate_decrease_;
Timestamp time_first_throughput_estimate_;
bool bitrate_is_initialized_;
double beta_;
bool in_alr_;
TimeDelta rtt_;
const bool send_side_;
// Allow the delay based estimate to only increase as long as application
// limited region (alr) is not detected.
const bool no_bitrate_increase_in_alr_;
// If "Disabled", estimated link capacity is not used as upper bound.
bool disable_estimate_bounded_increase_ = true;
bool use_current_estimate_as_min_upper_bound_ = true;
std::optional<DataRate> last_decrease_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_

View File

@@ -10,17 +10,19 @@
#include "alr_detector.h"
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include "rtc_base/time_utils.h"
namespace webrtc {
AlrDetector::AlrDetector(AlrDetectorConfig config)
: conf_(config), alr_budget_(0, true) {}
AlrDetector::AlrDetector() : alr_budget_(0, true) {}
AlrDetector::~AlrDetector() {}
void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) {
@@ -38,10 +40,7 @@ void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_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(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
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_) {
@@ -60,3 +59,5 @@ std::optional<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
const {
return alr_started_time_ms_;
}
} // namespace webrtc

View File

@@ -1,11 +1,15 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-14
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _ALR_DETECTOR_H_
#define _ALR_DETECTOR_H_
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#include <stddef.h>
#include <stdint.h>
@@ -15,6 +19,10 @@
#include "interval_budget.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
@@ -56,5 +64,6 @@ class AlrDetector {
IntervalBudget alr_budget_;
std::optional<int64_t> alr_started_time_ms_;
};
} // namespace webrtc
#endif
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_

17
src/qos/bandwidth_usage.h Normal file
View File

@@ -0,0 +1,17 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-15
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _BANDWIDTH_USAGE_H_
#define _BANDWIDTH_USAGE_H_
enum class BandwidthUsage {
kBwNormal = 0,
kBwUnderusing = 1,
kBwOverusing = 2,
kLast
};
#endif

View File

@@ -15,6 +15,13 @@
#include <cstdint>
#include <optional>
#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 {
constexpr int kInitialRateWindowMs = 500;
constexpr int kRateWindowMs = 150;
@@ -33,9 +40,9 @@ BitrateEstimator::BitrateEstimator()
uncertainty_scale_(10.0),
uncertainty_scale_in_alr_(uncertainty_scale_),
small_sample_uncertainty_scale_(uncertainty_scale_),
small_sample_threshold_(0),
uncertainty_symmetry_cap_(0),
estimate_floor_(0),
small_sample_threshold_(DataSize::Zero()),
uncertainty_symmetry_cap_(DataRate::Zero()),
estimate_floor_(DataRate::Zero()),
current_window_ms_(0),
prev_time_ms_(-1),
bitrate_estimate_kbps_(-1.0f),
@@ -43,14 +50,14 @@ BitrateEstimator::BitrateEstimator()
BitrateEstimator::~BitrateEstimator() = default;
void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) {
int rate_window_ms = noninitial_window_ms_.Get();
void BitrateEstimator::Update(Timestamp at_time, DataSize amount, bool in_alr) {
int rate_window_ms = noninitial_window_ms_;
// 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();
if (bitrate_estimate_kbps_ < 0.f) rate_window_ms = initial_window_ms_;
bool is_small_sample = false;
float bitrate_sample_kbps =
UpdateWindow(at_time, amount, rate_window_ms, &is_small_sample);
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.
@@ -73,8 +80,7 @@ void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) {
float sample_uncertainty =
scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) /
(bitrate_estimate_kbps_ +
std::min(bitrate_sample_kbps,
static_cast<float>(uncertainty_symmetry_cap_)));
std::min(bitrate_sample_kbps, uncertainty_symmetry_cap_.kbps<float>()));
float sample_var = sample_uncertainty * sample_uncertainty;
// Update a bayesian estimate of the rate, weighting it lower if the sample
@@ -86,7 +92,7 @@ void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) {
pred_bitrate_estimate_var * bitrate_sample_kbps) /
(sample_var + pred_bitrate_estimate_var);
bitrate_estimate_kbps_ =
std::max(bitrate_estimate_kbps_, static_cast<float>(estimate_floor_));
std::max(bitrate_estimate_kbps_, estimate_floor_.kbps<float>());
bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
(sample_var + pred_bitrate_estimate_var);
}
@@ -111,7 +117,7 @@ float BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes,
prev_time_ms_ = now_ms;
float bitrate_sample = -1.0f;
if (current_window_ms_ >= rate_window_ms) {
*is_small_sample = sum_ < small_sample_threshold_;
*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;
@@ -120,13 +126,14 @@ float BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes,
return bitrate_sample;
}
std::optional<int64_t> BitrateEstimator::bitrate() const {
std::optional<DataRate> BitrateEstimator::bitrate() const {
if (bitrate_estimate_kbps_ < 0.f) return std::nullopt;
return static_cast<int64_t>(bitrate_estimate_kbps_);
return DataRate::KilobitsPerSec(bitrate_estimate_kbps_);
}
std::optional<int64_t> BitrateEstimator::PeekRate() const {
if (current_window_ms_ > 0) return sum_ / current_window_ms_;
std::optional<DataRate> BitrateEstimator::PeekRate() const {
if (current_window_ms_ > 0)
return DataSize::Bytes(sum_) / TimeDelta::Millis(current_window_ms_);
return std::nullopt;
}
@@ -135,3 +142,5 @@ void BitrateEstimator::ExpectFastRateChange() {
// bitrate to change fast for the next few samples.
bitrate_estimate_var_ += 200;
}
} // namespace webrtc

View File

@@ -1,18 +1,27 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-14
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _BITRATE_ESTIMATOR_H_
#define _BITRATE_ESTIMATOR_H_
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#include <stdint.h>
#include <optional>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "constrained.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
@@ -20,12 +29,12 @@
// unrelated to congestion.
class BitrateEstimator {
public:
explicit BitrateEstimator();
BitrateEstimator();
virtual ~BitrateEstimator();
virtual void Update(int64_t at_time, int64_t amount, bool in_alr);
virtual void Update(Timestamp at_time, DataSize amount, bool in_alr);
virtual std::optional<int64_t> bitrate() const;
std::optional<int64_t> PeekRate() const;
virtual std::optional<DataRate> bitrate() const;
std::optional<DataRate> PeekRate() const;
virtual void ExpectFastRateChange();
@@ -38,13 +47,15 @@ class BitrateEstimator {
double uncertainty_scale_;
double uncertainty_scale_in_alr_;
double small_sample_uncertainty_scale_;
int64_t small_sample_threshold_;
int64_t uncertainty_symmetry_cap_;
int64_t estimate_floor_;
DataSize small_sample_threshold_;
DataRate uncertainty_symmetry_cap_;
DataRate estimate_floor_;
int64_t current_window_ms_;
int64_t prev_time_ms_;
float bitrate_estimate_kbps_;
float bitrate_estimate_var_;
};
#endif
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_

48
src/qos/bwe_defines.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_
#include <stdint.h>
#include <optional>
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "bandwidth_usage.h"
namespace webrtc {
inline constexpr DataRate kCongestionControllerMinBitrate =
DataRate::BitsPerSec(5'000);
inline constexpr TimeDelta kBitrateWindow = TimeDelta::Seconds(1);
extern const char kBweTypeHistogram[];
enum BweNames {
kReceiverNoExtension = 0,
kReceiverTOffset = 1,
kReceiverAbsSendTime = 2,
kSendSideTransportSeqNum = 3,
kBweNamesMax = 4
};
struct RateControlInput {
RateControlInput(BandwidthUsage bw_state,
const std::optional<DataRate>& estimated_throughput);
~RateControlInput();
BandwidthUsage bw_state;
std::optional<DataRate> estimated_throughput;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_

View File

@@ -122,10 +122,6 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
}
}
if (network_estimator_) {
network_estimator_->OnTransportPacketsFeedback(report);
SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
}
std::optional<int64_t> probe_bitrate =
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&

View File

@@ -7,6 +7,7 @@
#include "acknowledged_bitrate_estimator.h"
#include "alr_detector.h"
#include "congestion_window_pushback_controller.h"
#include "delay_based_bwe.h"
#include "network_types.h"
#include "send_side_bandwidth_estimation.h"

View File

@@ -1,3 +1,13 @@
/*
* Copyright (c) 2024 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 "congestion_control_feedback.h"
#include <algorithm>
@@ -6,10 +16,17 @@
#include <utility>
#include <vector>
#include "api/array_view.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "byte_io.h"
#include "common_header.h"
#include "log.h"
#include "rtc_base/network/ecn_marking.h"
namespace webrtc {
namespace rtcp {
// rfc8888 - RTP Congestion Control Feedback
/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P| FMT=11 | PT = 205 | length |
@@ -38,23 +55,6 @@
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
// for this implementation, only one stream is supported.
/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P| FMT=11 | PT = 205 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of RTCP packet sender |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of 1st RTP Stream |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | num_reports |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|ECN| Arrival time offset | ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Report Timestamp (32 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
namespace {
constexpr size_t kSenderSsrcLength = 4;
@@ -78,60 +78,55 @@ constexpr uint16_t kEcnCe = 0x03;
// measurement. If the measurement is unavailable or if the arrival time of the
// RTP packet is after the time represented by the RTS field, then an ATO value
// of 0x1FFF MUST be reported for the packet.
uint16_t To13bitAto(int64_t arrival_time_offset) {
if (arrival_time_offset < 0) {
uint16_t To13bitAto(TimeDelta arrival_time_offset) {
if (arrival_time_offset < TimeDelta::Zero()) {
return 0x1FFF;
}
return std::min(static_cast<int64_t>(1024 * (arrival_time_offset / 1000)),
int64_t{0x1FFE});
return std::min(
static_cast<int64_t>(1024 * arrival_time_offset.seconds<float>()),
int64_t{0x1FFE});
}
int64_t AtoToTimeDelta(uint16_t receive_info) {
TimeDelta AtoToTimeDelta(uint16_t receive_info) {
// receive_info
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// ato -> second
const uint16_t ato = receive_info & 0x1FFF;
if (ato == 0x1FFE) {
return std::numeric_limits<int64_t>::max();
return TimeDelta::PlusInfinity();
}
if (ato == 0x1FFF) {
return std::numeric_limits<int64_t>::min();
return TimeDelta::MinusInfinity();
}
return ato / 1024;
return TimeDelta::Seconds(ato) / 1024;
}
uint16_t To2BitEcn(EcnMarking ecn_marking) {
uint16_t To2BitEcn(rtc::EcnMarking ecn_marking) {
switch (ecn_marking) {
case EcnMarking::kNotEct:
case rtc::EcnMarking::kNotEct:
return 0;
case EcnMarking::kEct1:
case rtc::EcnMarking::kEct1:
return kEcnEct1 << 13;
case EcnMarking::kEct0:
case rtc::EcnMarking::kEct0:
return kEcnEct0 << 13;
case EcnMarking::kCe:
case rtc::EcnMarking::kCe:
return kEcnCe << 13;
default: {
LOG_FATAL("Unexpected ecn marking: {}", static_cast<int>(ecn_marking));
return 0;
}
}
}
EcnMarking ToEcnMarking(uint16_t receive_info) {
rtc::EcnMarking ToEcnMarking(uint16_t receive_info) {
const uint16_t ecn = (receive_info >> 13) & 0b11;
if (ecn == kEcnEct1) {
return EcnMarking::kEct1;
return rtc::EcnMarking::kEct1;
}
if (ecn == kEcnEct0) {
return EcnMarking::kEct0;
return rtc::EcnMarking::kEct0;
}
if (ecn == kEcnCe) {
return EcnMarking::kCe;
return rtc::EcnMarking::kCe;
}
return EcnMarking::kNotEct;
return rtc::EcnMarking::kNotEct;
}
} // namespace
@@ -145,9 +140,9 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
size_t max_length,
PacketReadyCallback callback) const {
// Ensure there is enough room for this packet.
// while (*position + BlockLength() > max_length) {
// if (!OnBufferFull(buffer, position, callback)) return false;
// }
while (*position + BlockLength() > max_length) {
if (!OnBufferFull(buffer, position, callback)) return false;
}
const size_t position_end = *position + BlockLength();
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -161,14 +156,14 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
*position += 4;
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of 1st RTP Stream |
// | SSRC of nth RTP Stream |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | num_reports |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset | ... .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
auto write_report_for_ssrc = [&](std::vector<PacketInfo> packets) {
// . .
auto write_report_for_ssrc = [&](rtc::ArrayView<const PacketInfo> packets) {
// SSRC of nth RTP stream.
ByteWriter<uint32_t>::WriteBigEndian(&buffer[*position], packets[0].ssrc);
*position += 4;
@@ -180,27 +175,18 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
// num_reports
uint16_t num_reports = packets.size();
if (static_cast<uint16_t>(packets[packets.size() - 1].sequence_number -
packets[0].sequence_number + 1) !=
packets.size()) {
LOG_FATAL("Expected continous rtp sequence numbers");
return false;
}
// Each report block MUST NOT include more than 16384 packet metric
// blocks (i.e., it MUST NOT report on more than one quarter of the
// sequence number space in a single report).
if (num_reports > 16384) {
LOG_FATAL("Unexpected number of reports: {}", num_reports);
return false;
LOG_ERROR("Unexpected number of reports:{}", num_reports);
return;
}
ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], num_reports);
*position += 2;
for (const PacketInfo& packet : packets) {
bool received =
(packet.arrival_time_offset != std::numeric_limits<int64_t>::min()) &&
(packet.arrival_time_offset != std::numeric_limits<int64_t>::max());
bool received = packet.arrival_time_offset.IsFinite();
uint16_t packet_info = 0;
if (received) {
packet_info = 0x8000 | To2BitEcn(packet.ecn) |
@@ -214,17 +200,20 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], 0);
*position += 2;
}
return true;
};
if (!packets_.empty()) {
rtc::ArrayView<const PacketInfo> remaining(packets_);
while (!remaining.empty()) {
int number_of_packets_for_ssrc = 0;
uint32_t ssrc = packets_[0].ssrc;
for (const PacketInfo& packet_info : packets_) {
uint32_t ssrc = remaining[0].ssrc;
for (const PacketInfo& packet_info : remaining) {
if (packet_info.ssrc != ssrc) {
break;
}
++number_of_packets_for_ssrc;
}
write_report_for_ssrc(packets_);
write_report_for_ssrc(remaining.subview(0, number_of_packets_for_ssrc));
remaining = remaining.subview(number_of_packets_for_ssrc);
}
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -234,9 +223,6 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
report_timestamp_compact_ntp_);
*position += 4;
if (*position != position_end) {
return false;
}
return true;
}
@@ -257,6 +243,15 @@ size_t CongestionControlFeedback::BlockLength() const {
uint32_t ssrc = packets_.front().ssrc;
uint16_t first_sequence_number = packets_.front().sequence_number;
for (size_t i = 0; i < packets_.size(); ++i) {
if (packets_[i].ssrc != ssrc) {
uint16_t number_of_packets =
packets_[i - 1].sequence_number - first_sequence_number + 1;
total_size += increase_size_per_ssrc(number_of_packets);
ssrc = packets_[i].ssrc;
first_sequence_number = packets_[i].sequence_number;
}
}
uint16_t number_of_packets =
packets_.back().sequence_number - first_sequence_number + 1;
total_size += increase_size_per_ssrc(number_of_packets);
@@ -264,7 +259,7 @@ size_t CongestionControlFeedback::BlockLength() const {
return total_size;
}
bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
bool CongestionControlFeedback::Parse(const rtcp::CommonHeader& packet) {
const uint8_t* payload = packet.payload();
const uint8_t* payload_end = packet.payload() + packet.payload_size_bytes();
@@ -300,11 +295,10 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
uint16_t seq_no = base_seqno + i;
bool received = (packet_info & 0x8000);
packets_.push_back(PacketInfo{ssrc, seq_no,
received
? AtoToTimeDelta(packet_info)
: std::numeric_limits<int64_t>::min(),
ToEcnMarking(packet_info)});
packets_.push_back(
{ssrc, seq_no,
received ? AtoToTimeDelta(packet_info) : TimeDelta::MinusInfinity(),
ToEcnMarking(packet_info)});
}
if (num_reports % 2) {
// 2 bytes padding
@@ -312,4 +306,6 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
}
}
return payload == payload_end;
}
}
} // namespace rtcp
} // namespace webrtc

View File

@@ -1,20 +1,28 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-12-18
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
* Copyright (c) 2024 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 _CONGESTION_CONTROL_FEEDBACK_H_
#define _CONGESTION_CONTROL_FEEDBACK_H_
#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_
#include <cstddef>
#include <cstdint>
#include <limits>
#include <vector>
#include "enc_mark.h"
#include "api/array_view.h"
#include "api/units/time_delta.h"
#include "common_header.h"
#include "rtc_base/network/ecn_marking.h"
#include "rtp_feedback.h"
namespace webrtc {
namespace rtcp {
// Congestion control feedback message as specified in
// https://www.rfc-editor.org/rfc/rfc8888.html
class CongestionControlFeedback : public RtpFeedback {
@@ -24,8 +32,8 @@ class CongestionControlFeedback : public RtpFeedback {
uint16_t sequence_number = 0;
// Time offset from report timestamp. Minus infinity if the packet has not
// been received.
int64_t arrival_time_offset = std::numeric_limits<int64_t>::min();
EcnMarking ecn = EcnMarking::kNotEct;
TimeDelta arrival_time_offset = TimeDelta::MinusInfinity();
rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
};
static constexpr uint8_t kFeedbackMessageType = 11;
@@ -37,9 +45,9 @@ class CongestionControlFeedback : public RtpFeedback {
uint32_t report_timestamp_compact_ntp);
CongestionControlFeedback() = default;
bool Parse(const RtcpCommonHeader& packet);
bool Parse(const CommonHeader& packet);
std::vector<PacketInfo> packets() const { return packets_; }
rtc::ArrayView<const PacketInfo> packets() const { return packets_; }
uint32_t report_timestamp_compact_ntp() const {
return report_timestamp_compact_ntp_;
@@ -55,4 +63,7 @@ class CongestionControlFeedback : public RtpFeedback {
uint32_t report_timestamp_compact_ntp_ = 0;
};
#endif
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_

View File

@@ -1,14 +1,29 @@
/*
* Copyright (c) 2024 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 "congestion_control_feedback_tracker.h"
#include <algorithm>
#include <cstdint>
#include <tuple>
#include <vector>
#include "log.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "congestion_control_feedback.h"
#include "rtp_packet_received.h"
namespace webrtc {
void CongestionControlFeedbackTracker::ReceivedPacket(
RtpPacketReceived& packet) {
const RtpPacketReceived& packet) {
int64_t unwrapped_sequence_number =
unwrapper_.Unwrap(packet.SequenceNumber());
if (last_sequence_number_in_feedback_ &&
@@ -26,13 +41,13 @@ void CongestionControlFeedbackTracker::ReceivedPacket(
// received.
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
}
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn()});
packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn());
}
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
int64_t feedback_time,
std::vector<CongestionControlFeedback::PacketInfo>& packet_feedback) {
Timestamp feedback_time,
std::vector<rtcp::CongestionControlFeedback::PacketInfo>& packet_feedback) {
if (packets_.empty()) {
return;
}
@@ -51,17 +66,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
for (int64_t sequence_number = *last_sequence_number_in_feedback_ + 1;
sequence_number <= packets_.back().unwrapped_sequence_number;
++sequence_number) {
if (packet_it == packets_.end()) {
LOG_FATAL("Invalid packet_it");
return;
}
if (ssrc != packet_it->ssrc) {
LOG_FATAL("Invalid ssrc");
return;
}
EcnMarking ecn = EcnMarking::kNotEct;
int64_t arrival_time_offset = std::numeric_limits<int64_t>::min();
rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
TimeDelta arrival_time_offset = TimeDelta::MinusInfinity();
if (sequence_number == packet_it->unwrapped_sequence_number) {
arrival_time_offset = feedback_time - packet_it->arrival_time;
@@ -70,22 +76,24 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
while (packet_it != packets_.end() &&
packet_it->unwrapped_sequence_number == sequence_number) {
// According to RFC 8888:
// If duplicate copies of a particular RTP packet are received, then the
// arrival time of the first copy to arrive MUST be reported. If any of
// the copies of the duplicated packet are ECN-CE marked, then an ECN-CE
// mark MUST be reported for that packet; otherwise, the ECN mark of the
// first copy to arrive is reported.
if (packet_it->ecn == EcnMarking::kCe) {
ecn = EcnMarking::kCe;
// If duplicate copies of a particular RTP packet are received, then
// the arrival time of the first copy to arrive MUST be reported. If
// any of the copies of the duplicated packet are ECN-CE marked, then
// an ECN-CE mark MUST be reported for that packet; otherwise, the ECN
// mark of the first copy to arrive is reported.
if (packet_it->ecn == rtc::EcnMarking::kCe) {
ecn = rtc::EcnMarking::kCe;
}
LOG_WARN("Received duplicate packet ssrc: {} seq: {} ecn: {}", ssrc,
LOG_WARN("Received duplicate packet ssrc:{} seq:{} ecn:{}", ssrc,
static_cast<uint16_t>(sequence_number), static_cast<int>(ecn));
++packet_it;
}
} // else - the packet has not been received yet.
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn});
packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn);
}
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
packets_.clear();
}
} // namespace webrtc

View File

@@ -1,40 +1,50 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-12-18
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
* Copyright (c) 2024 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_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
#ifndef _CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
#define _CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
#include <cstdint>
#include <optional>
#include <vector>
#include "api/units/timestamp.h"
#include "congestion_control_feedback.h"
#include "enc_mark.h"
#include "rtc_base/network/ecn_marking.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtp_packet_received.h"
#include "sequence_number_unwrapper.h"
namespace webrtc {
// CongestionControlFeedbackTracker is reponsible for creating and keeping track
// of feedback sent for a specific SSRC when feedback is sent according to
// https://datatracker.ietf.org/doc/rfc8888/
class CongestionControlFeedbackTracker {
public:
CongestionControlFeedbackTracker() = default;
void ReceivedPacket(RtpPacketReceived& packet);
void ReceivedPacket(const RtpPacketReceived& packet);
// Adds received packets to `packet_feedback`
// RTP sequence numbers are continous from the last created feedback unless
// reordering has occured between feedback packets. If so, the sequence
// number range may overlap with previousely sent feedback.
void AddPacketsToFeedback(
int64_t feedback_time,
std::vector<CongestionControlFeedback::PacketInfo>& packet_feedback);
Timestamp feedback_time,
std::vector<rtcp::CongestionControlFeedback::PacketInfo>&
packet_feedback);
private:
struct PacketInfo {
uint32_t ssrc;
int64_t unwrapped_sequence_number = 0;
int64_t arrival_time;
EcnMarking ecn = EcnMarking::kNotEct;
Timestamp arrival_time;
rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
};
std::optional<int64_t> last_sequence_number_in_feedback_;
@@ -43,4 +53,6 @@ class CongestionControlFeedbackTracker {
std::vector<PacketInfo> packets_;
};
#endif
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_

View File

@@ -17,11 +17,20 @@
#include <utility>
#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"
#include "bwe_defines.h"
#include "inter_arrival_delta.h"
#include "log.h"
#include "network_types.h"
#include "trendline_estimator.h"
namespace webrtc {
namespace {
constexpr int64_t kStreamTimeOut = int64_t::Seconds(2);
constexpr int64_t kSendTimeGroupLength = int64_t::Millis(5);
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.
@@ -30,72 +39,40 @@ constexpr uint32_t kFixedSsrc = 0;
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(int64_t::Zero()),
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_(int64_t::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_)),
DelayBasedBwe::DelayBasedBwe()
: audio_packets_since_last_video_(0),
last_video_packet_recv_time_(Timestamp::MinusInfinity()),
last_seen_packet_(Timestamp::MinusInfinity()),
video_delay_detector_(new TrendlineEstimator()),
audio_delay_detector_(new TrendlineEstimator()),
active_delay_detector_(video_delay_detector_.get()),
last_seen_packet_(int64_t::MinusInfinity()),
uma_recorded_(false),
rate_control_(*key_value_config, /*send_side=*/true),
prev_bitrate_(int64_t::Zero()),
prev_state_(BandwidthUsage::kBwNormal) {
RTC_LOG(LS_INFO)
<< "Initialized DelayBasedBwe with separate audio overuse detection"
<< separate_audio_.Parser()->Encode();
}
rate_control_(true),
prev_bitrate_(DataRate::Zero()),
prev_state_(BandwidthUsage::kBwNormal) {}
DelayBasedBwe::~DelayBasedBwe() {}
DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg, std::optional<int64_t> acked_bitrate,
std::optional<int64_t> probe_bitrate,
std::optional<NetworkStateEstimate> network_estimate, bool in_alr) {
RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
const TransportPacketsFeedback& msg, std::optional<DataRate> acked_bitrate,
std::optional<DataRate> probe_bitrate, bool in_alr) {
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.";
LOG_WARN("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;
@@ -117,14 +94,12 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
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,
int64_t at_time) {
Timestamp at_time) {
// Reset if the stream has timed out.
if (last_seen_packet_.IsInfinite() ||
at_time - last_seen_packet_ > kStreamTimeOut) {
@@ -133,10 +108,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
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_));
video_delay_detector_.reset(new TrendlineEstimator());
audio_delay_detector_.reset(new TrendlineEstimator());
active_delay_detector_ = video_delay_detector_.get();
}
last_seen_packet_ = at_time;
@@ -163,8 +136,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
}
DataSize packet_size = packet_feedback.sent_packet.size;
int64_t send_delta = int64_t::Zero();
int64_t recv_delta = int64_t::Zero();
TimeDelta send_delta = TimeDelta::Zero();
TimeDelta recv_delta = TimeDelta::Zero();
int size_delta = 0;
InterArrivalDelta* inter_arrival_for_packet =
@@ -182,16 +155,16 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
packet_size.bytes(), calculated_deltas);
}
int64_t DelayBasedBwe::TriggerOveruse(int64_t at_time,
std::optional<int64_t> link_capacity) {
DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,
std::optional<DataRate> link_capacity) {
RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity);
return rate_control_.Update(input, at_time);
}
DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
std::optional<int64_t> acked_bitrate, std::optional<int64_t> probe_bitrate,
std::optional<NetworkStateEstimate> /* state_estimate */,
bool recovered_from_overuse, bool /* in_alr */, int64_t at_time) {
std::optional<DataRate> acked_bitrate,
std::optional<DataRate> probe_bitrate, bool recovered_from_overuse,
bool /* in_alr */, Timestamp at_time) {
Result result;
// Currently overusing the bandwidth.
@@ -226,12 +199,7 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
BandwidthUsage detector_state = active_delay_detector_->State();
if ((result.updated && prev_bitrate_ != result.target_bitrate) ||
detector_state != prev_state_) {
int64_t bitrate = result.updated ? result.target_bitrate : prev_bitrate_;
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventBweUpdateDelayBased>(
bitrate.bps(), detector_state));
}
DataRate bitrate = result.updated ? result.target_bitrate : prev_bitrate_;
prev_bitrate_ = bitrate;
prev_state_ = detector_state;
@@ -241,26 +209,24 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
return result;
}
bool DelayBasedBwe::UpdateEstimate(int64_t at_time,
std::optional<int64_t> acked_bitrate,
int64_t* target_rate) {
bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
std::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(int64_t avg_rtt) {
void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) {
rate_control_.SetRtt(avg_rtt);
}
bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
int64_t* bitrate) const {
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};
@@ -268,18 +234,19 @@ bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
return true;
}
void DelayBasedBwe::SetStartBitrate(int64_t start_bitrate) {
RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: "
<< ToString(start_bitrate);
void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) {
LOG_INFO("BWE Setting start bitrate to: {}", ToString(start_bitrate));
rate_control_.SetStartBitrate(start_bitrate);
}
void DelayBasedBwe::SetMinBitrate(int64_t min_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);
}
int64_t DelayBasedBwe::GetExpectedBwePeriod() const {
TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const {
return rate_control_.GetExpectedBandwidthPeriod();
}
} // namespace webrtc

View File

@@ -1,11 +1,15 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-14
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _DELAY_BASED_BWE_H_
#define _DELAY_BASED_BWE_H_
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#include <stdint.h>
@@ -13,27 +17,29 @@
#include <optional>
#include <vector>
#include "aimd_rate_control.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "bandwidth_usage.h"
#include "delay_increase_detector_interface.h"
#include "inter_arrival.h"
#include "inter_arrival_delta.h"
#include "link_capacity_estimator.h"
#include "network_types.h"
#include "probe_bitrate_estimator.h"
enum class BandwidthUsage {
kBwNormal = 0,
kBwUnderusing = 1,
kBwOverusing = 2,
kLast
};
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;
int64_t time_threshold = int64_t::Seconds(1);
std::unique_ptr<StructParametersParser> Parser();
TimeDelta time_threshold = TimeDelta::Seconds(1);
};
class DelayBasedBwe {
@@ -43,60 +49,51 @@ class DelayBasedBwe {
~Result() = default;
bool updated;
bool probe;
int64_t target_bitrate = int64_t::Zero();
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();
DelayBasedBwe(const DelayBasedBwe&) = delete;
DelayBasedBwe& operator=(const DelayBasedBwe&) = delete;
virtual ~DelayBasedBwe();
Result IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg, std::optional<int64_t> acked_bitrate,
std::optional<int64_t> probe_bitrate,
std::optional<NetworkStateEstimate> network_estimate, bool in_alr);
void OnRttUpdate(int64_t avg_rtt);
bool LatestEstimate(std::vector<uint32_t>* ssrcs, int64_t* bitrate) const;
void SetStartBitrate(int64_t start_bitrate);
void SetMinBitrate(int64_t min_bitrate);
int64_t GetExpectedBwePeriod() const;
int64_t TriggerOveruse(int64_t at_time, std::optional<int64_t> link_capacity);
int64_t last_estimate() const { return prev_bitrate_; }
Result IncomingPacketFeedbackVector(const TransportPacketsFeedback& msg,
std::optional<DataRate> acked_bitrate,
std::optional<DataRate> probe_bitrate,
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,
std::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,
int64_t at_time);
Result MaybeUpdateEstimate(std::optional<int64_t> acked_bitrate,
std::optional<int64_t> probe_bitrate,
std::optional<NetworkStateEstimate> state_estimate,
Timestamp at_time);
Result MaybeUpdateEstimate(std::optional<DataRate> acked_bitrate,
std::optional<DataRate> probe_bitrate,
bool recovered_from_overuse, bool in_alr,
int64_t at_time);
Timestamp at_time);
// Updates the current remote rate estimate and returns true if a valid
// estimate exists.
bool UpdateEstimate(int64_t at_time, std::optional<int64_t> acked_bitrate,
int64_t* target_rate);
rtc::RaceChecker network_race_;
RtcEventLog* const event_log_;
const FieldTrialsView* const key_value_config_;
bool UpdateEstimate(Timestamp at_time, std::optional<DataRate> acked_bitrate,
DataRate* target_rate);
// 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_;
int64_t last_video_packet_recv_time_;
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_;
@@ -105,11 +102,13 @@ class DelayBasedBwe {
std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_;
DelayIncreaseDetectorInterface* active_delay_detector_;
int64_t last_seen_packet_;
Timestamp last_seen_packet_;
bool uma_recorded_;
AimdRateControl rate_control_;
int64_t prev_bitrate_;
DataRate prev_bitrate_;
BandwidthUsage prev_state_;
};
#endif
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_

View File

@@ -0,0 +1,42 @@
/*
* 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 "bandwidth_usage.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_

150
src/qos/inter_arrival.cc Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2013 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 "inter_arrival.h"
#include "log.h"
#include "module_common_types_public.h"
namespace webrtc {
static const int kBurstDeltaThresholdMs = 5;
static const int kMaxBurstDurationMs = 100;
InterArrival::InterArrival(uint32_t timestamp_group_length_ticks,
double timestamp_to_ms_coeff)
: kTimestampGroupLengthTicks(timestamp_group_length_ticks),
current_timestamp_group_(),
prev_timestamp_group_(),
timestamp_to_ms_coeff_(timestamp_to_ms_coeff),
num_consecutive_reordered_packets_(0) {}
bool InterArrival::ComputeDeltas(uint32_t timestamp, int64_t arrival_time_ms,
int64_t system_time_ms, size_t packet_size,
uint32_t* timestamp_delta,
int64_t* arrival_time_delta_ms,
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_.timestamp = timestamp;
current_timestamp_group_.first_timestamp = timestamp;
current_timestamp_group_.first_arrival_ms = arrival_time_ms;
} else if (!PacketInOrder(timestamp)) {
return false;
} else if (NewTimestampGroup(arrival_time_ms, timestamp)) {
// First packet of a later frame, the previous frame sample is ready.
if (prev_timestamp_group_.complete_time_ms >= 0) {
*timestamp_delta =
current_timestamp_group_.timestamp - prev_timestamp_group_.timestamp;
*arrival_time_delta_ms = current_timestamp_group_.complete_time_ms -
prev_timestamp_group_.complete_time_ms;
// Check system time differences to see if we have an unproportional jump
// in arrival time. In that case reset the inter-arrival computations.
int64_t system_time_delta_ms =
current_timestamp_group_.last_system_time_ms -
prev_timestamp_group_.last_system_time_ms;
if (*arrival_time_delta_ms - system_time_delta_ms >=
kArrivalTimeOffsetThresholdMs) {
LOG_WARN(
"The arrival time clock offset has changed (diff = {} ms), "
"resetting",
*arrival_time_delta_ms - system_time_delta_ms);
Reset();
return false;
}
if (*arrival_time_delta_ms < 0) {
// The group of packets has been reordered since receiving its local
// arrival timestamp.
++num_consecutive_reordered_packets_;
if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) {
LOG_WARN(
"Packets are being reordered on the path from the socket to the "
"bandwidth estimator. Ignoring this packet for bandwidth "
"estimation, resetting");
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_timestamp = timestamp;
current_timestamp_group_.timestamp = timestamp;
current_timestamp_group_.first_arrival_ms = arrival_time_ms;
current_timestamp_group_.size = 0;
} else {
current_timestamp_group_.timestamp =
LatestTimestamp(current_timestamp_group_.timestamp, timestamp);
}
// Accumulate the frame size.
current_timestamp_group_.size += packet_size;
current_timestamp_group_.complete_time_ms = arrival_time_ms;
current_timestamp_group_.last_system_time_ms = system_time_ms;
return calculated_deltas;
}
bool InterArrival::PacketInOrder(uint32_t timestamp) {
if (current_timestamp_group_.IsFirstPacket()) {
return true;
} else {
// Assume that a diff which is bigger than half the timestamp interval
// (32 bits) must be due to reordering. This code is almost identical to
// that in IsNewerTimestamp() in module_common_types.h.
uint32_t timestamp_diff =
timestamp - current_timestamp_group_.first_timestamp;
return timestamp_diff < 0x80000000;
}
}
// Assumes that `timestamp` is not reordered compared to
// `current_timestamp_group_`.
bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms,
uint32_t timestamp) const {
if (current_timestamp_group_.IsFirstPacket()) {
return false;
} else if (BelongsToBurst(arrival_time_ms, timestamp)) {
return false;
} else {
uint32_t timestamp_diff =
timestamp - current_timestamp_group_.first_timestamp;
return timestamp_diff > kTimestampGroupLengthTicks;
}
}
bool InterArrival::BelongsToBurst(int64_t arrival_time_ms,
uint32_t timestamp) const {
int64_t arrival_time_delta_ms =
arrival_time_ms - current_timestamp_group_.complete_time_ms;
uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp;
int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5;
if (ts_delta_ms == 0) return true;
int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms;
if (propagation_delta_ms < 0 &&
arrival_time_delta_ms <= kBurstDeltaThresholdMs &&
arrival_time_ms - current_timestamp_group_.first_arrival_ms <
kMaxBurstDurationMs)
return true;
return false;
}
void InterArrival::Reset() {
num_consecutive_reordered_packets_ = 0;
current_timestamp_group_ = TimestampGroup();
prev_timestamp_group_ = TimestampGroup();
}
} // namespace webrtc

93
src/qos/inter_arrival.h Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2013 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_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
// Helper class to compute the inter-arrival time delta and the size delta
// between two timestamp groups. A timestamp is a 32 bit unsigned number with
// a client defined rate.
class InterArrival {
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 int64_t kArrivalTimeOffsetThresholdMs = 3000;
// A timestamp group is defined as all packets with a timestamp which are at
// most timestamp_group_length_ticks older than the first timestamp in that
// group.
InterArrival(uint32_t timestamp_group_length_ticks,
double timestamp_to_ms_coeff);
InterArrival() = delete;
InterArrival(const InterArrival&) = delete;
InterArrival& operator=(const InterArrival&) = 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.
// `timestamp` is the timestamp.
// `arrival_time_ms` is the local time at which the packet arrived.
// `packet_size` is the size of the packet.
// `timestamp_delta` (output) is the computed timestamp delta.
// `arrival_time_delta_ms` (output) is the computed arrival-time delta.
// `packet_size_delta` (output) is the computed size delta.
bool ComputeDeltas(uint32_t timestamp,
int64_t arrival_time_ms,
int64_t system_time_ms,
size_t packet_size,
uint32_t* timestamp_delta,
int64_t* arrival_time_delta_ms,
int* packet_size_delta);
private:
struct TimestampGroup {
TimestampGroup()
: size(0),
first_timestamp(0),
timestamp(0),
first_arrival_ms(-1),
complete_time_ms(-1) {}
bool IsFirstPacket() const { return complete_time_ms == -1; }
size_t size;
uint32_t first_timestamp;
uint32_t timestamp;
int64_t first_arrival_ms;
int64_t complete_time_ms;
int64_t last_system_time_ms;
};
// Returns true if the packet with timestamp `timestamp` arrived in order.
bool PacketInOrder(uint32_t timestamp);
// Returns true if the last packet was the end of the current batch and the
// packet with `timestamp` is the first of a new batch.
bool NewTimestampGroup(int64_t arrival_time_ms, uint32_t timestamp) const;
bool BelongsToBurst(int64_t arrival_time_ms, uint32_t timestamp) const;
void Reset();
const uint32_t kTimestampGroupLengthTicks;
TimestampGroup current_timestamp_group_;
TimestampGroup prev_timestamp_group_;
double timestamp_to_ms_coeff_;
int num_consecutive_reordered_packets_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_

View File

@@ -0,0 +1,138 @@
/*
* 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 "inter_arrival_delta.h"
#include <algorithm>
#include <cstddef>
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "log.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) {
LOG_WARN(
"The arrival time clock offset has changed (diff = {} ms), "
"resetting.",
arrival_time_delta->ms() - system_time_delta.ms());
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) {
LOG_WARN(
"Packets between send burst arrived out of order, resetting: "
"arrival_time_delta_ms={}, send_time_delta_ms={}",
arrival_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 {
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,89 @@
/*
* 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

@@ -14,6 +14,9 @@
#include <cstddef>
#include <cstdint>
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace {
constexpr int64_t kWindowMs = 500;
}
@@ -51,7 +54,7 @@ void IntervalBudget::UseBudget(size_t bytes) {
}
size_t IntervalBudget::bytes_remaining() const {
return static_cast<size_t>(std::max<int64_t>(0, bytes_remaining_));
return rtc::saturated_cast<size_t>(std::max<int64_t>(0, bytes_remaining_));
}
double IntervalBudget::budget_ratio() const {
@@ -60,3 +63,5 @@ double IntervalBudget::budget_ratio() const {
}
int IntervalBudget::target_rate_kbps() const { return target_rate_kbps_; }
} // namespace webrtc

View File

@@ -1,15 +1,21 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-14
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _INTERVAL_BUDGET_H_
#define _INTERVAL_BUDGET_H_
#ifndef MODULES_PACING_INTERVAL_BUDGET_H_
#define MODULES_PACING_INTERVAL_BUDGET_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
// TODO(tschumim): Reflector IntervalBudget so that we can set a under- and
// over-use budget in ms.
class IntervalBudget {
@@ -33,4 +39,6 @@ class IntervalBudget {
bool can_build_up_underuse_;
};
#endif
} // namespace webrtc
#endif // MODULES_PACING_INTERVAL_BUDGET_H_

View File

@@ -0,0 +1,77 @@
/*
* 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 "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,39 @@
/*
* 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 <optional>
#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;
std::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,94 @@
/*
* 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 "overuse_detector.h"
#include <math.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include "rtc_base/numerics/safe_minmax.h"
namespace webrtc {
namespace {
constexpr double kMaxAdaptOffsetMs = 15.0;
constexpr double kOverUsingTimeThreshold = 10;
constexpr int kMaxNumDeltas = 60;
constexpr double kUp = 0.0087;
constexpr double kDown = 0.039;
} // namespace
OveruseDetector::OveruseDetector() = default;
BandwidthUsage OveruseDetector::State() const { return hypothesis_; }
BandwidthUsage OveruseDetector::Detect(double offset, double ts_delta,
int num_of_deltas, int64_t now_ms) {
if (num_of_deltas < 2) {
return BandwidthUsage::kBwNormal;
}
const double T = std::min(num_of_deltas, kMaxNumDeltas) * offset;
if (T > 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_ > kOverUsingTimeThreshold && overuse_counter_ > 1) {
if (offset >= prev_offset_) {
time_over_using_ = 0;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwOverusing;
}
}
} else if (T < -threshold_) {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwUnderusing;
} else {
time_over_using_ = -1;
overuse_counter_ = 0;
hypothesis_ = BandwidthUsage::kBwNormal;
}
prev_offset_ = offset;
UpdateThreshold(T, now_ms);
return hypothesis_;
}
void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) {
if (last_update_ms_ == -1) last_update_ms_ = now_ms;
if (fabs(modified_offset) > 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_offset) < threshold_ ? kDown : kUp;
const int64_t kMaxTimeDeltaMs = 100;
int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms;
threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
last_update_ms_ = now_ms;
}
} // namespace webrtc

View File

@@ -0,0 +1,52 @@
/*
* 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.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_
#include <stdint.h>
#include "bandwidth_usage.h"
namespace webrtc {
class OveruseDetector {
public:
OveruseDetector();
OveruseDetector(const OveruseDetector&) = delete;
OveruseDetector& operator=(const OveruseDetector&) = delete;
~OveruseDetector() = default;
// Update the detection state based on the estimated inter-arrival time delta
// offset. `timestamp_delta` is the delta between the last timestamp which the
// estimated offset is based on and the last timestamp on which the last
// offset was based on, representing the time between detector updates.
// `num_of_deltas` is the number of deltas the offset estimate is based on.
// Returns the state after the detection update.
BandwidthUsage Detect(double offset, double timestamp_delta,
int num_of_deltas, int64_t now_ms);
// Returns the current detector state.
BandwidthUsage State() const;
private:
void UpdateThreshold(double modified_offset, int64_t now_ms);
double threshold_ = 12.5;
int64_t last_update_ms_ = -1;
double prev_offset_ = 0.0;
double time_over_using_ = -1;
int overuse_counter_ = 0;
BandwidthUsage hypothesis_ = BandwidthUsage::kBwNormal;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_

View File

@@ -0,0 +1,169 @@
/*
* 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 "probe_bitrate_estimator.h"
#include <algorithm>
#include <memory>
#include <optional>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "log.h"
#include "network_types.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() {}
ProbeBitrateEstimator::~ProbeBitrateEstimator() = default;
std::optional<DataRate> ProbeBitrateEstimator::HandleProbeAndEstimateBitrate(
const PacketResult& packet_feedback) {
int cluster_id = packet_feedback.sent_packet.pacing_info.probe_cluster_id;
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;
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 std::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) {
LOG_INFO(
"Probing unsuccessful, invalid send/receive interval [cluster id: {}] "
"[send interval: {}] [receive interval: {}]",
cluster_id, ToString(send_interval), ToString(receive_interval));
return std::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.
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.
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) {
LOG_INFO(
"Probing unsuccessful, receive/send ratio too high [cluster id: {}] "
"[send: {} / {} = {}] [receive: {} / {} = {} ] [ratio: {} / {} = {}> "
"kMaxValidRatio ({})]",
cluster_id, ToString(send_size), ToString(send_interval),
ToString(send_rate), ToString(receive_size), ToString(receive_interval),
ToString(receive_rate), ToString(receive_rate), ToString(send_rate),
ratio, kMaxValidRatio);
return std::nullopt;
}
LOG_INFO(
"Probing successful [cluster id: {}] [send: {} / {} = {} ] [receive: {} "
"/ {} = {}]",
cluster_id, ToString(send_size), ToString(send_interval),
ToString(send_rate), 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) {
res = kTargetUtilizationFraction * receive_rate;
}
estimated_data_rate_ = res;
return estimated_data_rate_;
}
std::optional<DataRate>
ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrate() {
std::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,58 @@
/*
* 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 <optional>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "network_types.h"
namespace webrtc {
class RtcEventLog;
class ProbeBitrateEstimator {
public:
explicit ProbeBitrateEstimator();
~ProbeBitrateEstimator();
// Should be called for every probe packet we receive feedback about.
// Returns the estimated bitrate if the probe completes a valid cluster.
std::optional<DataRate> HandleProbeAndEstimateBitrate(
const PacketResult& packet_feedback);
std::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_;
std::optional<DataRate> estimated_data_rate_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_

View File

@@ -127,7 +127,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::max()) {
result.receive_time = current_offset_ - packet_info.arrival_time_offset;
supports_ecn &= packet_info.ecn != EcnMarking::kNotEct;
supports_ecn &= packet_info.ecn != rtc::EcnMarking::kNotEct;
}
result.ecn = packet_info.ecn;
packet_result_vector.push_back(result);

View File

@@ -17,7 +17,7 @@
#include "congestion_control_feedback.h"
#include "network_route.h"
#include "network_types.h"
#include "sequence_number_unwrapper.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
struct PacketFeedback {
PacketFeedback() = default;

View File

@@ -0,0 +1,260 @@
/*
* 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 "trendline_estimator.h"
#include <math.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "log.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";
std::optional<double> LinearFitSlope(
const std::deque<TrendlineEstimator::PacketTiming>& packets) {
// 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 std::nullopt;
return numerator / denominator;
}
std::optional<double> ComputeSlopeCap(
const std::deque<TrendlineEstimator::PacketTiming>& packets,
const TrendlineEstimatorSettings& settings) {
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 std::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() {
if (window_size < 10 || 200 < window_size) {
LOG_WARN("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) {
LOG_WARN("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) {
LOG_WARN("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) {
LOG_WARN("Cap uncertainty must be between 0 and 0.025");
cap_uncertainty = 0.0;
}
}
}
TrendlineEstimator::TrendlineEstimator()
: 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) {}
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;
smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
(1 - smoothing_coef_) * accumulated_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) {
std::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();
}
}
}
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);
}
}
BandwidthUsage TrendlineEstimator::State() const { return 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;
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,109 @@
/*
* 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 "bandwidth_usage.h"
namespace webrtc {
struct TrendlineEstimatorSettings {
static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings";
static constexpr unsigned kDefaultTrendlineWindowSize = 20;
TrendlineEstimatorSettings();
// 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;
};
class TrendlineEstimator : public DelayIncreaseDetectorInterface {
public:
TrendlineEstimator();
~TrendlineEstimator();
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);
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;
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_trend, 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_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_