mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
[feat] use original webrtc header defines
This commit is contained in:
@@ -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
|
||||
@@ -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_
|
||||
|
||||
330
src/qos/aimd_rate_control.cc
Normal file
330
src/qos/aimd_rate_control.cc
Normal 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
109
src/qos/aimd_rate_control.h
Normal 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_
|
||||
@@ -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
|
||||
@@ -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
17
src/qos/bandwidth_usage.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
48
src/qos/bwe_defines.h
Normal 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_
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
|
||||
42
src/qos/delay_increase_detector_interface.h
Normal file
42
src/qos/delay_increase_detector_interface.h
Normal 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
150
src/qos/inter_arrival.cc
Normal 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
93
src/qos/inter_arrival.h
Normal 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_
|
||||
138
src/qos/inter_arrival_delta.cc
Normal file
138
src/qos/inter_arrival_delta.cc
Normal 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
|
||||
89
src/qos/inter_arrival_delta.h
Normal file
89
src/qos/inter_arrival_delta.h
Normal 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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
|
||||
77
src/qos/link_capacity_estimator.cc
Normal file
77
src/qos/link_capacity_estimator.cc
Normal 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
|
||||
39
src/qos/link_capacity_estimator.h
Normal file
39
src/qos/link_capacity_estimator.h
Normal 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_
|
||||
94
src/qos/overuse_detector.cc
Normal file
94
src/qos/overuse_detector.cc
Normal 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
|
||||
52
src/qos/overuse_detector.h
Normal file
52
src/qos/overuse_detector.h
Normal 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_
|
||||
169
src/qos/probe_bitrate_estimator.cc
Normal file
169
src/qos/probe_bitrate_estimator.cc
Normal 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
|
||||
58
src/qos/probe_bitrate_estimator.h
Normal file
58
src/qos/probe_bitrate_estimator.h
Normal 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_
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
260
src/qos/trendline_estimator.cc
Normal file
260
src/qos/trendline_estimator.cc
Normal 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
|
||||
109
src/qos/trendline_estimator.h
Normal file
109
src/qos/trendline_estimator.h
Normal 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_
|
||||
Reference in New Issue
Block a user