[fix] fix congestion control module

This commit is contained in:
dijunkun
2025-01-17 17:33:13 +08:00
parent 6e2a52e506
commit 5bbd182a3f
53 changed files with 3376 additions and 505 deletions

View File

@@ -0,0 +1,42 @@
/*
* Copyright 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 API_MEDIA_TYPES_H_
#define API_MEDIA_TYPES_H_
#include <string>
// The cricket and webrtc have separate definitions for what a media type is.
// They're not compatible. Watch out for this.
namespace cricket {
enum MediaType {
MEDIA_TYPE_AUDIO,
MEDIA_TYPE_VIDEO,
MEDIA_TYPE_DATA,
MEDIA_TYPE_UNSUPPORTED
};
extern const char kMediaTypeAudio[];
extern const char kMediaTypeVideo[];
extern const char kMediaTypeData[];
std::string MediaTypeToString(MediaType type);
} // namespace cricket
namespace webrtc {
enum class MediaType { ANY, AUDIO, VIDEO, DATA };
} // namespace webrtc
#endif // API_MEDIA_TYPES_H_

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef API_NETWORK_STATE_PREDICTOR_H_
#define API_NETWORK_STATE_PREDICTOR_H_
#include <cstdint>
#include <memory>
#include "api/transport/bandwidth_usage.h"
namespace webrtc {
// TODO(yinwa): work in progress. API in class NetworkStatePredictor should not
// be used by other users until this comment is removed.
// NetworkStatePredictor predict network state based on current network metrics.
// Usage:
// Setup by calling Initialize.
// For each update, call Update. Update returns network state
// prediction.
class NetworkStatePredictor {
public:
virtual ~NetworkStatePredictor() {}
// Returns current network state prediction.
// Inputs: send_time_ms - packet send time.
// arrival_time_ms - packet arrival time.
// network_state - computed network state.
virtual BandwidthUsage Update(int64_t send_time_ms,
int64_t arrival_time_ms,
BandwidthUsage network_state) = 0;
};
class NetworkStatePredictorFactoryInterface {
public:
virtual std::unique_ptr<NetworkStatePredictor>
CreateNetworkStatePredictor() = 0;
virtual ~NetworkStatePredictorFactoryInterface() = default;
};
} // namespace webrtc
#endif // API_NETWORK_STATE_PREDICTOR_H_

View File

@@ -0,0 +1,124 @@
/*
* 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 API_TRANSPORT_NETWORK_CONTROL_H_
#define API_TRANSPORT_NETWORK_CONTROL_H_
#include <memory>
#include <optional>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
namespace webrtc {
class TargetTransferRateObserver {
public:
virtual ~TargetTransferRateObserver() = default;
// Called to indicate target transfer rate as well as giving information about
// the current estimate of network parameters.
virtual void OnTargetTransferRate(TargetTransferRate) = 0;
// Called to provide updates to the expected target rate in case it changes
// before the first call to OnTargetTransferRate.
virtual void OnStartRateUpdate(DataRate) {}
};
// Configuration sent to factory create function. The parameters here are
// optional to use for a network controller implementation.
struct NetworkControllerConfig {
explicit NetworkControllerConfig() {}
// The initial constraints to start with, these can be changed at any later
// time by calls to OnTargetRateConstraints. Note that the starting rate
// has to be set initially to provide a starting state for the network
// controller, even though the field is marked as optional.
TargetRateConstraints constraints;
// Initial stream specific configuration, these are changed at any later time
// by calls to OnStreamsConfig.
StreamsConfig stream_based_config;
};
// NetworkControllerInterface is implemented by network controllers. A network
// controller is a class that uses information about network state and traffic
// to estimate network parameters such as round trip time and bandwidth. Network
// controllers does not guarantee thread safety, the interface must be used in a
// non-concurrent fashion.
class NetworkControllerInterface {
public:
virtual ~NetworkControllerInterface() = default;
// Called when network availabilty changes.
virtual NetworkControlUpdate OnNetworkAvailability(NetworkAvailability) = 0;
// Called when the receiving or sending endpoint changes address.
virtual NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange) = 0;
// Called periodically with a periodicy as specified by
// NetworkControllerFactoryInterface::GetProcessInterval.
virtual NetworkControlUpdate OnProcessInterval(ProcessInterval) = 0;
// Called when remotely calculated bitrate is received.
virtual NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport) = 0;
// Called round trip time has been calculated by protocol specific mechanisms.
virtual NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate) = 0;
// Called when a packet is sent on the network.
virtual NetworkControlUpdate OnSentPacket(SentPacket) = 0;
// Called when a packet is received from the remote client.
virtual NetworkControlUpdate OnReceivedPacket(ReceivedPacket) = 0;
// Called when the stream specific configuration has been updated.
virtual NetworkControlUpdate OnStreamsConfig(StreamsConfig) = 0;
// Called when target transfer rate constraints has been changed.
virtual NetworkControlUpdate OnTargetRateConstraints(
TargetRateConstraints) = 0;
// Called when a protocol specific calculation of packet loss has been made.
virtual NetworkControlUpdate OnTransportLossReport(TransportLossReport) = 0;
// Called with per packet feedback regarding receive time.
virtual NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback) = 0;
// Called with network state estimate updates.
virtual NetworkControlUpdate OnNetworkStateEstimate(NetworkStateEstimate) = 0;
};
// NetworkControllerFactoryInterface is an interface for creating a network
// controller.
class NetworkControllerFactoryInterface {
public:
virtual ~NetworkControllerFactoryInterface() = default;
// Used to create a new network controller, requires an observer to be
// provided to handle callbacks.
virtual std::unique_ptr<NetworkControllerInterface> Create(
NetworkControllerConfig config) = 0;
// Returns the interval by which the network controller expects
// OnProcessInterval calls.
virtual TimeDelta GetProcessInterval() const = 0;
};
// Under development, subject to change without notice.
class NetworkStateEstimator {
public:
// Gets the current best estimate according to the estimator.
virtual std::optional<NetworkStateEstimate> GetCurrentEstimate() = 0;
// Called with per packet feedback regarding receive time.
// Used when the NetworkStateEstimator runs in the sending endpoint.
virtual void OnTransportPacketsFeedback(const TransportPacketsFeedback&) = 0;
// Called with per packet feedback regarding receive time.
// Used when the NetworkStateEstimator runs in the receiving endpoint.
virtual void OnReceivedPacket(const PacketResult&) {}
// Called when the receiving or sending endpoint changes address.
virtual void OnRouteChange(const NetworkRouteChange&) = 0;
virtual ~NetworkStateEstimator() = default;
};
class NetworkStateEstimatorFactory {
public:
virtual std::unique_ptr<NetworkStateEstimator> Create() = 0;
virtual ~NetworkStateEstimatorFactory() = default;
};
} // namespace webrtc
#endif // API_TRANSPORT_NETWORK_CONTROL_H_

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 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 "rtc_base/bitrate_tracker.h"
#include <optional>
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "rtc_base/rate_statistics.h"
namespace webrtc {
BitrateTracker::BitrateTracker(TimeDelta max_window_size)
: impl_(max_window_size.ms(), RateStatistics::kBpsScale) {}
std::optional<DataRate> BitrateTracker::Rate(Timestamp now) const {
if (std::optional<int64_t> rate = impl_.Rate(now.ms())) {
return DataRate::BitsPerSec(*rate);
}
return std::nullopt;
}
bool BitrateTracker::SetWindowSize(TimeDelta window_size, Timestamp now) {
return impl_.SetWindowSize(window_size.ms(), now.ms());
}
void BitrateTracker::Update(int64_t bytes, Timestamp now) {
impl_.Update(bytes, now.ms());
}
} // namespace webrtc

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 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 RTC_BASE_BITRATE_TRACKER_H_
#define RTC_BASE_BITRATE_TRACKER_H_
#include <stddef.h>
#include <stdint.h>
#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 "rtc_base/rate_statistics.h"
namespace webrtc {
// Class to estimate bitrates over running window.
// Timestamps used in Update(), Rate() and SetWindowSize() must never
// decrease for two consecutive calls.
// This class is thread unsafe.
class BitrateTracker {
public:
// max_window_sizes = Maximum window size for the rate estimation.
// Initial window size is set to this, but may be changed
// to something lower by calling SetWindowSize().
explicit BitrateTracker(TimeDelta max_window_size);
BitrateTracker(const BitrateTracker&) = default;
BitrateTracker(BitrateTracker&&) = default;
BitrateTracker& operator=(const BitrateTracker&) = delete;
BitrateTracker& operator=(BitrateTracker&&) = delete;
~BitrateTracker() = default;
// Resets instance to original state.
void Reset() { impl_.Reset(); }
// Updates bitrate with a new data point, moving averaging window as needed.
void Update(int64_t bytes, Timestamp now);
void Update(DataSize size, Timestamp now) { Update(size.bytes(), now); }
// Returns bitrate, moving averaging window as needed.
// Returns nullopt when bitrate can't be measured.
std::optional<DataRate> Rate(Timestamp now) const;
// Update the size of the averaging window. The maximum allowed value for
// `window_size` is `max_window_size` as supplied in the constructor.
bool SetWindowSize(TimeDelta window_size, Timestamp now);
private:
RateStatistics impl_;
};
} // namespace webrtc
#endif // RTC_BASE_BITRATE_TRACKER_H_

View File

@@ -0,0 +1,160 @@
/*
* 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 "rtc_base/rate_statistics.h"
#include <algorithm>
#include <limits>
#include <memory>
#include "log.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
RateStatistics::Bucket::Bucket(int64_t timestamp)
: sum(0), num_samples(0), timestamp(timestamp) {}
RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
: accumulated_count_(0),
first_timestamp_(-1),
num_samples_(0),
scale_(scale),
max_window_size_ms_(window_size_ms),
current_window_size_ms_(max_window_size_ms_) {}
RateStatistics::RateStatistics(const RateStatistics& other)
: buckets_(other.buckets_),
accumulated_count_(other.accumulated_count_),
first_timestamp_(other.first_timestamp_),
overflow_(other.overflow_),
num_samples_(other.num_samples_),
scale_(other.scale_),
max_window_size_ms_(other.max_window_size_ms_),
current_window_size_ms_(other.current_window_size_ms_) {}
RateStatistics::RateStatistics(RateStatistics&& other) = default;
RateStatistics::~RateStatistics() {}
void RateStatistics::Reset() {
accumulated_count_ = 0;
overflow_ = false;
num_samples_ = 0;
first_timestamp_ = -1;
current_window_size_ms_ = max_window_size_ms_;
buckets_.clear();
}
void RateStatistics::Update(int64_t count, int64_t now_ms) {
// Don't reset `first_timestamp_` if the last sample removed by EraseOld() was
// recent. This ensures that the window maintains its intended duration even
// when samples are received near the boundary. Use a margin of 50% of the
// current window size.
const int64_t recent_sample_time_margin = 1.5 * current_window_size_ms_;
bool last_sample_is_recent =
!buckets_.empty() &&
buckets_.back().timestamp > now_ms - recent_sample_time_margin;
EraseOld(now_ms);
if (first_timestamp_ == -1 || (num_samples_ == 0 && !last_sample_is_recent)) {
first_timestamp_ = now_ms;
}
if (buckets_.empty() || now_ms != buckets_.back().timestamp) {
if (!buckets_.empty() && now_ms < buckets_.back().timestamp) {
LOG_WARN(
"Timestamp {} is before the last added timestamp in the rate window: "
"{}, aligning to that.",
now_ms, buckets_.back().timestamp);
now_ms = buckets_.back().timestamp;
}
buckets_.emplace_back(now_ms);
}
Bucket& last_bucket = buckets_.back();
last_bucket.sum += count;
++last_bucket.num_samples;
if (std::numeric_limits<int64_t>::max() - accumulated_count_ > count) {
accumulated_count_ += count;
} else {
overflow_ = true;
}
++num_samples_;
}
std::optional<int64_t> RateStatistics::Rate(int64_t now_ms) const {
// Yeah, this const_cast ain't pretty, but the alternative is to declare most
// of the members as mutable...
const_cast<RateStatistics*>(this)->EraseOld(now_ms);
int active_window_size = 0;
if (first_timestamp_ != -1) {
if (first_timestamp_ <= now_ms - current_window_size_ms_) {
// Count window as full even if no data points currently in view, if the
// data stream started before the window.
active_window_size = current_window_size_ms_;
} else {
// Size of a single bucket is 1ms, so even if now_ms == first_timestmap_
// the window size should be 1.
active_window_size = now_ms - first_timestamp_ + 1;
}
}
// If window is a single bucket or there is only one sample in a data set that
// has not grown to the full window size, or if the accumulator has
// overflowed, treat this as rate unavailable.
if (num_samples_ == 0 || active_window_size <= 1 ||
(num_samples_ <= 1 &&
rtc::SafeLt(active_window_size, current_window_size_ms_)) ||
overflow_) {
return std::nullopt;
}
float scale = static_cast<float>(scale_) / active_window_size;
float result = accumulated_count_ * scale + 0.5f;
// Better return unavailable rate than garbage value (undefined behavior).
if (result > static_cast<float>(std::numeric_limits<int64_t>::max())) {
return std::nullopt;
}
return rtc::dchecked_cast<int64_t>(result);
}
void RateStatistics::EraseOld(int64_t now_ms) {
// New oldest time that is included in data set.
const int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
// Loop over buckets and remove too old data points.
while (!buckets_.empty() && buckets_.front().timestamp < new_oldest_time) {
const Bucket& oldest_bucket = buckets_.front();
accumulated_count_ -= oldest_bucket.sum;
num_samples_ -= oldest_bucket.num_samples;
buckets_.pop_front();
// This does not clear overflow_ even when counter is empty.
// TODO(https://bugs.webrtc.org/11247): Consider if overflow_ can be reset.
}
}
bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_) return false;
if (first_timestamp_ != -1) {
// If the window changes (e.g. decreases - removing data point, then
// increases again) we need to update the first timestamp mark as
// otherwise it indicates the window coveres a region of zeros, suddenly
// under-estimating the rate.
first_timestamp_ = std::max(first_timestamp_, now_ms - window_size_ms + 1);
}
current_window_size_ms_ = window_size_ms;
EraseOld(now_ms);
return true;
}
} // namespace webrtc

View File

@@ -0,0 +1,103 @@
/*
* 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 RTC_BASE_RATE_STATISTICS_H_
#define RTC_BASE_RATE_STATISTICS_H_
#include <stddef.h>
#include <stdint.h>
#include <deque>
#include <memory>
#include <optional>
namespace webrtc {
// Class to estimate rates based on counts in a sequence of 1-millisecond
// intervals.
// This class uses int64 for all its numbers because some rates can be very
// high; for instance, a 20 Mbit/sec video stream can wrap a 32-bit byte
// counter in 14 minutes.
// Note that timestamps used in Update(), Rate() and SetWindowSize() must never
// decrease for two consecutive calls.
// TODO(bugs.webrtc.org/11600): Migrate from int64_t to Timestamp.
class RateStatistics {
public:
static constexpr float kBpsScale = 8000.0f;
// max_window_size_ms = Maximum window size in ms for the rate estimation.
// Initial window size is set to this, but may be changed
// to something lower by calling SetWindowSize().
// scale = coefficient to convert counts/ms to desired unit
// ex: kBpsScale (8000) for bits/s if count represents bytes.
RateStatistics(int64_t max_window_size_ms, float scale);
RateStatistics(const RateStatistics& other);
RateStatistics(RateStatistics&& other);
~RateStatistics();
// Reset instance to original state.
void Reset();
// Update rate with a new data point, moving averaging window as needed.
void Update(int64_t count, int64_t now_ms);
// Note that despite this being a const method, it still updates the internal
// state (moves averaging window), but it doesn't make any alterations that
// are observable from the other methods, as long as supplied timestamps are
// from a monotonic clock. Ie, it doesn't matter if this call moves the
// window, since any subsequent call to Update or Rate would still have moved
// the window as much or more.
std::optional<int64_t> Rate(int64_t now_ms) const;
// Update the size of the averaging window. The maximum allowed value for
// window_size_ms is max_window_size_ms as supplied in the constructor.
bool SetWindowSize(int64_t window_size_ms, int64_t now_ms);
private:
void EraseOld(int64_t now_ms);
struct Bucket {
explicit Bucket(int64_t timestamp);
int64_t sum; // Sum of all samples in this bucket.
int num_samples; // Number of samples in this bucket.
const int64_t timestamp; // Timestamp this bucket corresponds to.
};
// All buckets within the time window, ordered by time.
std::deque<Bucket> buckets_;
// Total count recorded in all buckets.
int64_t accumulated_count_;
// Timestamp of the first data point seen, or -1 of none seen.
int64_t first_timestamp_;
// True if accumulated_count_ has ever grown too large to be
// contained in its integer type.
bool overflow_ = false;
// The total number of samples in the buckets.
int num_samples_;
// To convert counts/ms to desired units
const float scale_;
// The window sizes, in ms, over which the rate is calculated.
const int64_t max_window_size_ms_;
int64_t current_window_size_ms_;
};
} // namespace webrtc
#endif // RTC_BASE_RATE_STATISTICS_H_

View File

@@ -0,0 +1,98 @@
//
// 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.
//
// Borrowed from
// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h
// but adapted for clang attributes instead of the gcc.
//
// This header file contains the macro definitions for thread safety
// annotations that allow the developers to document the locking policies
// of their multi-threaded code. The annotations can also help program
// analysis tools to identify potential thread safety issues.
#ifndef RTC_BASE_THREAD_ANNOTATIONS_H_
#define RTC_BASE_THREAD_ANNOTATIONS_H_
#if defined(__clang__) && (!defined(SWIG))
#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
// Document if a shared variable/field needs to be protected by a lock.
// GUARDED_BY allows the user to specify a particular lock that should be
// held when accessing the annotated variable.
#define RTC_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
// Document if the memory location pointed to by a pointer should be guarded
// by a lock when dereferencing the pointer. Note that a pointer variable to a
// shared memory location could itself be a shared variable. For example, if a
// shared global pointer q, which is guarded by mu1, points to a shared memory
// location that is guarded by mu2, q should be annotated as follows:
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
#define RTC_PT_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
// Document the acquisition order between locks that can be held
// simultaneously by a thread. For any two locks that need to be annotated
// to establish an acquisition order, only one of them needs the annotation.
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
// and ACQUIRED_BEFORE.)
#define RTC_ACQUIRED_AFTER(x) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x))
#define RTC_ACQUIRED_BEFORE(x) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x))
// The following three annotations document the lock requirements for
// functions/methods.
// Document if a function expects certain locks to be held before it is called
#define RTC_EXCLUSIVE_LOCKS_REQUIRED(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
#define RTC_SHARED_LOCKS_REQUIRED(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
// Document the locks acquired in the body of the function. These locks
// cannot be held when calling this function (as google3's Mutex locks are
// non-reentrant).
#define RTC_LOCKS_EXCLUDED(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
// Document the lock the annotated function returns without acquiring it.
#define RTC_LOCK_RETURNED(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
// Document if a class/type is a lockable type (such as the Mutex class).
#define RTC_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(lockable)
// Document if a class is a scoped lockable type (such as the MutexLock class).
#define RTC_SCOPED_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
// The following annotations specify lock and unlock primitives.
#define RTC_EXCLUSIVE_LOCK_FUNCTION(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
#define RTC_SHARED_LOCK_FUNCTION(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
#define RTC_EXCLUSIVE_TRYLOCK_FUNCTION(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
#define RTC_SHARED_TRYLOCK_FUNCTION(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
#define RTC_UNLOCK_FUNCTION(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
#define RTC_ASSERT_EXCLUSIVE_LOCK(...) \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
// An escape hatch for thread safety analysis to ignore the annotated function.
#define RTC_NO_THREAD_SAFETY_ANALYSIS \
RTC_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif // RTC_BASE_THREAD_ANNOTATIONS_H_

View File

@@ -16,10 +16,10 @@
#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/timestamp.h"
#include "network_types.h"
namespace webrtc {

View File

@@ -15,10 +15,10 @@
#include <optional>
#include <vector>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "bitrate_estimator.h"
#include "network_types.h"
namespace webrtc {

View File

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

View File

@@ -17,9 +17,9 @@
#include <cstdio>
#include <string>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "log.h"
#include "network_types.h"
#include "overuse_detector.h"
#include "rtc_base/numerics/safe_minmax.h"

View File

@@ -15,11 +15,11 @@
#include <optional>
#include "api/transport/network_types.h"
#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
@@ -98,7 +98,7 @@ class AimdRateControl {
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_;
const bool no_bitrate_increase_in_alr_ = true;
// 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;

View File

@@ -15,9 +15,9 @@
#include <optional>
#include "api/transport/bandwidth_usage.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "bandwidth_usage.h"
namespace webrtc {

101
src/qos/clock.cc Normal file
View File

@@ -0,0 +1,101 @@
/*
* 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 "clock.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
namespace {
int64_t NtpOffsetUsCalledOnce() {
constexpr int64_t kNtpJan1970Sec = 2208988800;
int64_t clock_time = rtc::TimeMicros();
int64_t utc_time = rtc::TimeUTCMicros();
return utc_time - clock_time + kNtpJan1970Sec * rtc::kNumMicrosecsPerSec;
}
NtpTime TimeMicrosToNtp(int64_t time_us) {
static int64_t ntp_offset_us = NtpOffsetUsCalledOnce();
int64_t time_ntp_us = time_us + ntp_offset_us;
// Convert seconds to uint32 through uint64 for a well-defined cast.
// A wrap around, which will happen in 2036, is expected for NTP time.
uint32_t ntp_seconds =
static_cast<uint64_t>(time_ntp_us / rtc::kNumMicrosecsPerSec);
// Scale fractions of the second to NTP resolution.
constexpr int64_t kNtpFractionsInSecond = 1LL << 32;
int64_t us_fractions = time_ntp_us % rtc::kNumMicrosecsPerSec;
uint32_t ntp_fractions =
us_fractions * kNtpFractionsInSecond / rtc::kNumMicrosecsPerSec;
return NtpTime(ntp_seconds, ntp_fractions);
}
} // namespace
class RealTimeClock : public Clock {
public:
RealTimeClock() = default;
Timestamp CurrentTime() override {
return Timestamp::Micros(rtc::TimeMicros());
}
NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override {
return TimeMicrosToNtp(timestamp.us());
}
};
Clock* Clock::GetRealTimeClock() {
static Clock* const clock = new RealTimeClock();
return clock;
}
SimulatedClock::SimulatedClock(int64_t initial_time_us)
: time_us_(initial_time_us) {}
SimulatedClock::SimulatedClock(Timestamp initial_time)
: SimulatedClock(initial_time.us()) {}
SimulatedClock::~SimulatedClock() {}
Timestamp SimulatedClock::CurrentTime() {
return Timestamp::Micros(time_us_.load(std::memory_order_relaxed));
}
NtpTime SimulatedClock::ConvertTimestampToNtpTime(Timestamp timestamp) {
int64_t now_us = timestamp.us();
uint32_t seconds = (now_us / 1'000'000) + kNtpJan1970;
uint32_t fractions = static_cast<uint32_t>(
(now_us % 1'000'000) * kMagicNtpFractionalUnit / 1'000'000);
return NtpTime(seconds, fractions);
}
void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
AdvanceTime(TimeDelta::Millis(milliseconds));
}
void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
AdvanceTime(TimeDelta::Micros(microseconds));
}
// TODO(bugs.webrtc.org(12102): It's desirable to let a single thread own
// advancement of the clock. We could then replace this read-modify-write
// operation with just a thread checker. But currently, that breaks a couple of
// tests, in particular, RepeatingTaskTest.ClockIntegration and
// CallStatsTest.LastProcessedRtt.
void SimulatedClock::AdvanceTime(TimeDelta delta) {
time_us_.fetch_add(delta.us(), std::memory_order_relaxed);
}
} // namespace webrtc

100
src/qos/clock.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* 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 SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
#define SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
#include <stdint.h>
#include <atomic>
#include <memory>
#include "api/units/timestamp.h"
#include "ntp_time.h"
namespace webrtc {
// January 1970, in NTP seconds.
const uint32_t kNtpJan1970 = 2208988800UL;
// Magic NTP fractional unit.
const double kMagicNtpFractionalUnit = 4.294967296E+9;
// A clock interface that allows reading of absolute and relative timestamps.
class Clock {
public:
virtual ~Clock() {}
// Return a timestamp relative to an unspecified epoch.
virtual Timestamp CurrentTime() = 0;
int64_t TimeInMilliseconds() { return CurrentTime().ms(); }
int64_t TimeInMicroseconds() { return CurrentTime().us(); }
// Retrieve an NTP absolute timestamp (with an epoch of Jan 1, 1900).
NtpTime CurrentNtpTime() { return ConvertTimestampToNtpTime(CurrentTime()); }
int64_t CurrentNtpInMilliseconds() { return CurrentNtpTime().ToMs(); }
// Converts between a relative timestamp returned by this clock, to NTP time.
virtual NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) = 0;
int64_t ConvertTimestampToNtpTimeInMilliseconds(int64_t timestamp_ms) {
return ConvertTimestampToNtpTime(Timestamp::Millis(timestamp_ms)).ToMs();
}
// Converts NtpTime to a Timestamp with UTC epoch.
// A `Minus Infinity` Timestamp is returned if the NtpTime is invalid.
static Timestamp NtpToUtc(NtpTime ntp_time) {
if (!ntp_time.Valid()) {
return Timestamp::MinusInfinity();
}
// Seconds since UTC epoch.
int64_t time = ntp_time.seconds() - kNtpJan1970;
// Microseconds since UTC epoch (not including NTP fraction)
time = time * 1'000'000;
// Fractions part of the NTP time, in microseconds.
int64_t time_fraction =
DivideRoundToNearest(int64_t{ntp_time.fractions()} * 1'000'000,
NtpTime::kFractionsPerSecond);
return Timestamp::Micros(time + time_fraction);
}
// Returns an instance of the real-time system clock implementation.
static Clock* GetRealTimeClock();
};
class SimulatedClock : public Clock {
public:
// The constructors assume an epoch of Jan 1, 1970.
explicit SimulatedClock(int64_t initial_time_us);
explicit SimulatedClock(Timestamp initial_time);
~SimulatedClock() override;
// Return a timestamp with an epoch of Jan 1, 1970.
Timestamp CurrentTime() override;
NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override;
// Advance the simulated clock with a given number of milliseconds or
// microseconds.
void AdvanceTimeMilliseconds(int64_t milliseconds);
void AdvanceTimeMicroseconds(int64_t microseconds);
void AdvanceTime(TimeDelta delta);
private:
// The time is read and incremented with relaxed order. Each thread will see
// monotonically increasing time, and when threads post tasks or messages to
// one another, the synchronization done as part of the message passing should
// ensure that any causual chain of events on multiple threads also
// corresponds to monotonically increasing time.
std::atomic<int64_t> time_us_;
};
} // namespace webrtc
#endif // SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_

View File

@@ -21,7 +21,34 @@ constexpr float kDefaultPaceMultiplier = 2.5f;
// below the current throughput estimate to drain the network queues.
constexpr double kProbeDropThroughputFraction = 0.85;
CongestionControl::CongestionControl() {}
CongestionControl::CongestionControl()
: use_min_allocatable_as_lower_bound_(false),
ignore_probes_lower_than_network_estimate_(false),
limit_probes_lower_than_throughput_estimate_(false),
pace_at_max_of_bwe_and_lower_link_capacity_(false),
limit_pacingfactor_by_upper_link_capacity_estimate_(false),
probe_controller_(new ProbeController()),
bandwidth_estimation_(new SendSideBandwidthEstimation()),
alr_detector_(new AlrDetector()),
probe_bitrate_estimator_(new ProbeBitrateEstimator()),
network_estimator_(new NetworkStateEstimator()),
network_state_predictor_(new NetworkStatePredictor()),
delay_based_bwe_(new DelayBasedBwe()),
acknowledged_bitrate_estimator_(new AcknowledgedBitrateEstimator()),
initial_config_(std::nullopt),
last_loss_based_target_rate_(*config.constraints.starting_rate),
last_pushback_target_rate_(last_loss_based_target_rate_),
last_stable_target_rate_(last_loss_based_target_rate_),
last_loss_base_state_(LossBasedState::kDelayBasedEstimate),
pacing_factor_(config.stream_based_config.pacing_factor.value_or(
kDefaultPaceMultiplier)),
min_total_allocated_bitrate_(
config.stream_based_config.min_total_allocated_bitrate.value_or(
DataRate::Zero())),
max_padding_rate_(config.stream_based_config.max_padding_rate.value_or(
DataRate::Zero()))
{}
CongestionControl::~CongestionControl() {}
@@ -35,28 +62,27 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->UpdateOutstandingData(
report.data_in_flight);
report.data_in_flight.bytes());
}
int64_t max_feedback_rtt = std::numeric_limits<int64_t>::min();
int64_t min_propagation_rtt = std::numeric_limits<int64_t>::max();
int64_t max_recv_time = std::numeric_limits<int64_t>::min();
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
Timestamp max_recv_time = Timestamp::MinusInfinity();
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
for (const auto& feedback : feedbacks)
max_recv_time = std::max(max_recv_time, feedback.receive_time);
for (const auto& feedback : feedbacks) {
int64_t feedback_rtt =
TimeDelta feedback_rtt =
report.feedback_time - feedback.sent_packet.send_time;
int64_t min_pending_time = max_recv_time - feedback.receive_time;
int64_t propagation_rtt = feedback_rtt - min_pending_time;
TimeDelta min_pending_time = max_recv_time - feedback.receive_time;
TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
}
if (max_feedback_rtt != std::numeric_limits<int64_t>::min() &&
min_propagation_rtt != std::numeric_limits<int64_t>::max()) {
feedback_max_rtts_.push_back(max_feedback_rtt);
if (max_feedback_rtt.IsFinite()) {
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
const size_t kMaxFeedbackRttWindow = 32;
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
feedback_max_rtts_.pop_front();
@@ -70,19 +96,19 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
static_cast<int64_t>(0));
int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
if (delay_based_bwe_) delay_based_bwe_->OnRttUpdate(mean_rtt_ms);
if (delay_based_bwe_)
delay_based_bwe_->OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
}
int64_t feedback_min_rtt = std::numeric_limits<int64_t>::max();
TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
for (const auto& packet_feedback : feedbacks) {
int64_t pending_time = max_recv_time - packet_feedback.receive_time;
int64_t rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
TimeDelta pending_time = max_recv_time - packet_feedback.receive_time;
TimeDelta rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
// Value used for predicting NACK round trip time in FEC controller.
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
}
if (feedback_min_rtt != std::numeric_limits<int64_t>::max() &&
feedback_min_rtt != std::numeric_limits<int64_t>::min()) {
if (feedback_min_rtt.IsFinite()) {
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
}
@@ -105,7 +131,7 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
alr_detector_->GetApplicationLimitedRegionStartTime();
if (previously_in_alr_ && !alr_start_time.has_value()) {
int64_t now_ms = report.feedback_time;
int64_t now_ms = report.feedback_time.ms();
acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
probe_controller_->SetAlrEndedTimeMs(now_ms);
}
@@ -122,7 +148,11 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
}
}
std::optional<int64_t> probe_bitrate =
if (network_estimator_) {
network_estimator_->OnTransportPacketsFeedback(report);
SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
}
std::optional<DataRate> probe_bitrate =
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
@@ -138,7 +168,7 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
// based estimate, but it could happen e.g. due to packet bursts or
// encoder overshoot. We use std::min to ensure that a probe result
// below the current BWE never causes an increase.
int64_t limit =
DataRate limit =
std::min(delay_based_bwe_->last_estimate(),
*acknowledged_bitrate * kProbeDropThroughputFraction);
probe_bitrate = std::max(*probe_bitrate, limit);

View File

@@ -5,12 +5,18 @@
#include <memory>
#include "acknowledged_bitrate_estimator.h"
#include "acknowledged_bitrate_estimator_interface.h"
#include "alr_detector.h"
#include "api/network_state_predictor.h"
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "congestion_window_pushback_controller.h"
#include "delay_based_bwe.h"
#include "network_types.h"
#include "probe_controller.h"
#include "send_side_bandwidth_estimation.h"
using namespace webrtc;
class CongestionControl {
public:
CongestionControl();
@@ -21,24 +27,54 @@ class CongestionControl {
TransportPacketsFeedback report);
private:
const std::unique_ptr<CongestionWindowPushbackController>
congestion_window_pushback_controller_;
const bool use_min_allocatable_as_lower_bound_;
const bool ignore_probes_lower_than_network_estimate_;
const bool limit_probes_lower_than_throughput_estimate_;
const bool pace_at_max_of_bwe_and_lower_link_capacity_;
const bool limit_pacingfactor_by_upper_link_capacity_estimate_;
private:
std::deque<int64_t> feedback_max_rtts_;
int expected_packets_since_last_loss_update_ = 0;
int lost_packets_since_last_loss_update_ = 0;
int64_t next_loss_update_ = std::numeric_limits<int64_t>::min();
const bool packet_feedback_only_ = false;
bool previously_in_alr_ = false;
const bool limit_probes_lower_than_throughput_estimate_ = false;
std::optional<int64_t> current_data_window_;
const std::unique_ptr<ProbeController> probe_controller_;
private:
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
std::unique_ptr<AlrDetector> alr_detector_;
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
std::unique_ptr<NetworkStateEstimator> network_estimator_;
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
acknowledged_bitrate_estimator_;
std::optional<NetworkControllerConfig> initial_config_;
DataRate min_target_rate_ = DataRate::Zero();
DataRate min_data_rate_ = DataRate::Zero();
DataRate max_data_rate_ = DataRate::PlusInfinity();
std::optional<DataRate> starting_rate_;
bool first_packet_sent_ = false;
std::optional<NetworkStateEstimate> estimate_;
Timestamp next_loss_update_ = Timestamp::MinusInfinity();
int lost_packets_since_last_loss_update_ = 0;
int expected_packets_since_last_loss_update_ = 0;
std::deque<int64_t> feedback_max_rtts_;
DataRate last_loss_based_target_rate_;
DataRate last_pushback_target_rate_;
DataRate last_stable_target_rate_;
std::optional<uint8_t> last_estimated_fraction_loss_ = 0;
TimeDelta last_estimated_round_trip_time_ = TimeDelta::PlusInfinity();
double pacing_factor_;
DataRate min_total_allocated_bitrate_;
DataRate max_padding_rate_;
bool previously_in_alr_ = false;
std::optional<DataSize> current_data_window_;
};
#endif

View File

@@ -0,0 +1,125 @@
/*
* 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_generator.h"
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "clock.h"
#include "congestion_control_feedback.h"
#include "ntp_time_util.h"
#include "rtcp_packet.h"
#include "rtp_packet_received.h"
namespace webrtc {
CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
std::shared_ptr<SimulatedClock> clock, RtcpSender rtcp_sender)
: clock_(clock),
rtcp_sender_(std::move(rtcp_sender)),
min_time_between_feedback_(TimeDelta::Millis(25)),
max_time_to_wait_for_packet_with_marker_(TimeDelta::Millis(25)),
max_time_between_feedback_(TimeDelta::Millis(250)) {}
void CongestionControlFeedbackGenerator::OnReceivedPacket(
const RtpPacketReceived& packet) {
marker_bit_seen_ |= packet.Marker();
if (!first_arrival_time_since_feedback_) {
first_arrival_time_since_feedback_ = packet.arrival_time();
}
feedback_trackers_[packet.Ssrc()].ReceivedPacket(packet);
if (NextFeedbackTime() < packet.arrival_time()) {
SendFeedback(Timestamp::Micros(
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()));
}
}
Timestamp CongestionControlFeedbackGenerator::NextFeedbackTime() const {
if (!first_arrival_time_since_feedback_) {
return std::max(Timestamp::Micros(
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()) +
min_time_between_feedback_,
next_possible_feedback_send_time_);
}
if (!marker_bit_seen_) {
return std::max(next_possible_feedback_send_time_,
*first_arrival_time_since_feedback_ +
max_time_to_wait_for_packet_with_marker_);
}
return next_possible_feedback_send_time_;
}
TimeDelta CongestionControlFeedbackGenerator::Process(Timestamp now) {
if (NextFeedbackTime() <= now) {
SendFeedback(now);
}
return NextFeedbackTime() - now;
}
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
DataRate estimate) {
// Feedback reports should max occupy 5% of total bandwidth.
max_feedback_rate_ = estimate * 0.05;
}
void CongestionControlFeedbackGenerator::SetTransportOverhead(
DataSize overhead_per_packet) {
packet_overhead_ = overhead_per_packet;
}
void CongestionControlFeedbackGenerator::SendFeedback(Timestamp now) {
SimulatedClock clock(now);
uint32_t compact_ntp = CompactNtp(clock.ConvertTimestampToNtpTime(now));
std::vector<rtcp::CongestionControlFeedback::PacketInfo> rtcp_packet_info;
for (auto& [unused, tracker] : feedback_trackers_) {
tracker.AddPacketsToFeedback(now, rtcp_packet_info);
}
marker_bit_seen_ = false;
first_arrival_time_since_feedback_ = std::nullopt;
auto feedback = std::make_unique<rtcp::CongestionControlFeedback>(
std::move(rtcp_packet_info), compact_ntp);
CalculateNextPossibleSendTime(DataSize::Bytes(feedback->BlockLength()), now);
std::vector<std::unique_ptr<RtcpPacket>> rtcp_packets;
rtcp_packets.push_back(std::move(feedback));
rtcp_sender_(std::move(rtcp_packets));
}
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
DataSize feedback_size, Timestamp now) {
TimeDelta time_since_last_sent = now - last_feedback_sent_time_;
DataSize debt_payed = time_since_last_sent * max_feedback_rate_;
send_rate_debt_ = debt_payed > send_rate_debt_ ? DataSize::Zero()
: send_rate_debt_ - debt_payed;
send_rate_debt_ += feedback_size + packet_overhead_;
last_feedback_sent_time_ = now;
next_possible_feedback_send_time_ =
now + std::clamp(max_feedback_rate_.IsZero()
? TimeDelta::PlusInfinity()
: send_rate_debt_ / max_feedback_rate_,
min_time_between_feedback_, max_time_between_feedback_);
}
} // namespace webrtc

View File

@@ -1,108 +0,0 @@
#include "congestion_control_feedback_generator.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
uint32_t ConvertToCompactNtp(int64_t now_ms) {
int64_t seconds = now_ms / 1000;
int64_t milliseconds = now_ms % 1000;
uint16_t ntp_seconds = static_cast<uint16_t>(seconds & 0xFFFF);
uint16_t ntp_fraction = static_cast<uint16_t>((milliseconds * 65536) / 1000);
uint32_t compact_ntp = (ntp_seconds << 16) | ntp_fraction;
return compact_ntp;
}
CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
RtcpSender rtcp_sender)
: rtcp_sender_(std::move(rtcp_sender)) {}
void CongestionControlFeedbackGenerator::OnReceivedPacket(
RtpPacketReceived& packet) {
marker_bit_seen_ |= packet.Marker();
if (!first_arrival_time_since_feedback_) {
first_arrival_time_since_feedback_ = packet.arrival_time();
}
feedback_trackers_[packet.Ssrc()].ReceivedPacket(packet);
if (NextFeedbackTime() < packet.arrival_time()) {
auto now = std::chrono::system_clock::now();
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch())
.count();
SendFeedback(now_ms);
}
}
int64_t CongestionControlFeedbackGenerator::NextFeedbackTime() const {
auto now = std::chrono::system_clock::now();
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch())
.count();
if (!first_arrival_time_since_feedback_) {
return std::max(now_ms + min_time_between_feedback_,
next_possible_feedback_send_time_);
}
if (!marker_bit_seen_) {
return std::max(next_possible_feedback_send_time_,
*first_arrival_time_since_feedback_ +
max_time_to_wait_for_packet_with_marker_);
}
return next_possible_feedback_send_time_;
}
int64_t CongestionControlFeedbackGenerator::Process(int64_t now_ms) {
if (NextFeedbackTime() <= now_ms) {
SendFeedback(now_ms);
}
return NextFeedbackTime() - now_ms;
}
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
int64_t estimate) {
// Feedback reports should max occupy 5% of total bandwidth.
max_feedback_rate_ = estimate * 0.05;
}
void CongestionControlFeedbackGenerator::SetTransportOverhead(
int64_t overhead_per_packet) {
packet_overhead_ = overhead_per_packet;
}
void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
uint32_t compact_ntp = ConvertToCompactNtp(now_ms);
std::vector<CongestionControlFeedback::PacketInfo> rtcp_packet_info;
for (auto& [unused, tracker] : feedback_trackers_) {
tracker.AddPacketsToFeedback(now_ms, rtcp_packet_info);
}
marker_bit_seen_ = false;
first_arrival_time_since_feedback_ = std::nullopt;
auto feedback = std::make_unique<CongestionControlFeedback>(
std::move(rtcp_packet_info), compact_ntp);
CalculateNextPossibleSendTime(feedback->BlockLength(), now_ms);
std::vector<std::unique_ptr<RtcpPacket>> rtcp_packets;
rtcp_packets.push_back(std::move(feedback));
rtcp_sender_(std::move(rtcp_packets));
}
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
int64_t feedback_size, int64_t now_ms) {
int64_t time_since_last_sent = now_ms - last_feedback_sent_time_;
size_t debt_payed = time_since_last_sent * max_feedback_rate_;
send_rate_debt_ =
debt_payed > send_rate_debt_ ? 0 : send_rate_debt_ - debt_payed;
send_rate_debt_ += feedback_size + packet_overhead_;
last_feedback_sent_time_ = now_ms;
next_possible_feedback_send_time_ =
now_ms +
std::clamp(max_feedback_rate_ == 0 ? std::numeric_limits<int64_t>::max()
: send_rate_debt_ / max_feedback_rate_,
min_time_between_feedback_, max_time_between_feedback_);
}

View File

@@ -1,67 +1,88 @@
/*
* @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_GENERATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
#ifndef _CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
#define _CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "clock.h"
#include "congestion_control_feedback_tracker.h"
#include "rtcp_packet.h"
#include "rtp_packet_received.h"
#include "rtp_transport_feedback_generator.h"
class CongestionControlFeedbackGenerator {
namespace webrtc {
// The class is responsible for generating RTCP feedback packets based on
// incoming media packets. Feedback format will comply with RFC 8888.
// https://datatracker.ietf.org/doc/rfc8888/
// Feedback should not use more than 5% of the configured send bandwidth
// estimate. Min and max duration between feedback is configurable using field
// trials, but per default, min is 25ms and max is 250ms.
// If possible, given the other constraints, feedback will be sent when a packet
// with marker bit is received in order to provide feedback as soon as possible
// after receiving a complete video frame. If no packet with marker bit is
// received, feedback can be delayed up to 25ms after the first packet since the
// last sent feedback. On good networks, this means that a sender may receive
// feedback for every sent frame.
class CongestionControlFeedbackGenerator
: public RtpTransportFeedbackGenerator {
public:
CongestionControlFeedbackGenerator(RtcpSender feedback_sender);
CongestionControlFeedbackGenerator(
std::shared_ptr<SimulatedClock> clock,
RtpTransportFeedbackGenerator::RtcpSender feedback_sender);
~CongestionControlFeedbackGenerator() = default;
void OnReceivedPacket(RtpPacketReceived& packet);
void OnReceivedPacket(const RtpPacketReceived& packet) override;
void OnSendBandwidthEstimateChanged(int64_t estimate);
void OnSendBandwidthEstimateChanged(DataRate estimate) override;
int64_t Process(int64_t now_ms);
TimeDelta Process(Timestamp now) override;
void SetTransportOverhead(int64_t overhead_per_packet);
void SetTransportOverhead(DataSize overhead_per_packet) override;
private:
int64_t NextFeedbackTime() const;
Timestamp NextFeedbackTime() const;
void SendFeedback(int64_t now_ms);
void SendFeedback(Timestamp now);
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
void CalculateNextPossibleSendTime(DataSize feedback_size, Timestamp now);
std::shared_ptr<SimulatedClock> clock_;
const RtcpSender rtcp_sender_;
private:
// Feedback should not use more than 5% of the configured send bandwidth
// estimate. Min and max duration between feedback is configurable using field
// trials, but per default, min is 25ms and max is 250ms.
// If possible, given the other constraints, feedback will be sent when a
// packet with marker bit is received in order to provide feedback as soon as
// possible after receiving a complete video frame. If no packet with marker
// bit is received, feedback can be delayed up to 25ms after the first packet
// since the last sent feedback. On good networks, this means that a sender
// may receive feedback for every sent frame.
int64_t min_time_between_feedback_ = 25;
int64_t max_time_between_feedback_ = 250;
int64_t max_time_to_wait_for_packet_with_marker_ = 25;
TimeDelta min_time_between_feedback_;
TimeDelta max_time_to_wait_for_packet_with_marker_;
TimeDelta max_time_between_feedback_;
int64_t max_feedback_rate_ = 1000; // kbps
int64_t packet_overhead_ = 0;
int64_t send_rate_debt_ = 0;
DataRate max_feedback_rate_ = DataRate::KilobitsPerSec(1000);
DataSize packet_overhead_ = DataSize::Zero();
DataSize send_rate_debt_ = DataSize::Zero();
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
feedback_trackers_;
std::optional<int64_t> first_arrival_time_since_feedback_;
int64_t next_possible_feedback_send_time_ = 0;
int64_t last_feedback_sent_time_ = 0;
// std::vector<PacketInfo> packets_;
Timestamp last_feedback_sent_time_ = Timestamp::Zero();
std::optional<Timestamp> first_arrival_time_since_feedback_;
bool marker_bit_seen_ = false;
Timestamp next_possible_feedback_send_time_ = Timestamp::Zero();
};
#endif
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_

View File

@@ -41,8 +41,8 @@ void CongestionControlFeedbackTracker::ReceivedPacket(
// received.
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
}
packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn());
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn()});
}
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
@@ -89,8 +89,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
++packet_it;
}
} // else - the packet has not been received yet.
packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn);
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn});
}
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
packets_.clear();

View File

@@ -13,7 +13,17 @@
#include <algorithm>
#include <cstdint>
CongestionWindowPushbackController::CongestionWindowPushbackController() {}
#include "api/units/data_size.h"
namespace webrtc {
const int kDefaultMinPushbackTargetBitrateBps = 30000;
CongestionWindowPushbackController::CongestionWindowPushbackController()
: add_pacing_(false),
min_pushback_target_bitrate_bps_(kDefaultMinPushbackTargetBitrateBps),
current_data_window_(
DataSize::Bytes(kDefaultMinPushbackTargetBitrateBps)) {}
void CongestionWindowPushbackController::UpdateOutstandingData(
int64_t outstanding_bytes) {
@@ -24,17 +34,18 @@ void CongestionWindowPushbackController::UpdatePacingQueue(
pacing_bytes_ = pacing_bytes;
}
void CongestionWindowPushbackController::SetDataWindow(int64_t data_window) {
void CongestionWindowPushbackController::SetDataWindow(DataSize data_window) {
current_data_window_ = data_window;
}
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
uint32_t bitrate_bps) {
if (!current_data_window_ || current_data_window_ == 0) return bitrate_bps;
if (!current_data_window_ || current_data_window_->IsZero())
return bitrate_bps;
int64_t total_bytes = outstanding_bytes_;
if (add_pacing_) total_bytes += pacing_bytes_;
double fill_ratio =
total_bytes / static_cast<double>(current_data_window_.value());
total_bytes / static_cast<double>(current_data_window_->bytes());
if (fill_ratio > 1.5) {
encoding_rate_ratio_ *= 0.9;
} else if (fill_ratio > 1) {
@@ -55,3 +66,5 @@ uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
: adjusted_target_bitrate_bps;
return bitrate_bps;
}
} // namespace webrtc

View File

@@ -1,16 +1,24 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-13
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
* 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 _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#define _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#include <stdint.h>
#include <optional>
#include "api/units/data_size.h"
namespace webrtc {
// This class enables pushback from congestion window directly to video encoder.
// When the congestion window is filling up, the video encoder target bitrate
// will be reduced accordingly to accommodate the network changes. To avoid
@@ -22,15 +30,17 @@ class CongestionWindowPushbackController {
void UpdateOutstandingData(int64_t outstanding_bytes);
void UpdatePacingQueue(int64_t pacing_bytes);
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
void SetDataWindow(int64_t data_window);
void SetDataWindow(DataSize data_window);
private:
const bool add_pacing_ = true;
const uint32_t min_pushback_target_bitrate_bps_ = 10000;
std::optional<int64_t> current_data_window_ = std::nullopt;
const bool add_pacing_;
const uint32_t min_pushback_target_bitrate_bps_;
std::optional<DataSize> current_data_window_;
int64_t outstanding_bytes_ = 0;
int64_t pacing_bytes_ = 0;
double encoding_rate_ratio_ = 1.0;
};
#endif
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_

View File

@@ -17,6 +17,7 @@
#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"
@@ -24,7 +25,6 @@
#include "bwe_defines.h"
#include "inter_arrival_delta.h"
#include "log.h"
#include "network_types.h"
#include "trendline_estimator.h"
namespace webrtc {

View File

@@ -18,15 +18,15 @@
#include <vector>
#include "aimd_rate_control.h"
#include "api/transport/bandwidth_usage.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "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"
namespace webrtc {

View File

@@ -14,7 +14,7 @@
#include <cstddef>
#include "bandwidth_usage.h"
#include "api/transport/bandwidth_usage.h"
namespace webrtc {

View File

@@ -0,0 +1,66 @@
/*
* 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_INCLUDE_MODULE_COMMON_TYPES_H_
#define MODULES_INCLUDE_MODULE_COMMON_TYPES_H_
#include <stdint.h>
#include <vector>
namespace webrtc {
// Interface used by the CallStats class to distribute call statistics.
// Callbacks will be triggered as soon as the class has been registered to a
// CallStats object using RegisterStatsObserver.
class CallStatsObserver {
public:
virtual void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) = 0;
virtual ~CallStatsObserver() {}
};
// Interface used by NackModule and JitterBuffer.
class NackSender {
public:
// If `buffering_allowed`, other feedback messages (e.g. key frame requests)
// may be added to the same outgoing feedback message. In that case, it's up
// to the user of the interface to ensure that when all buffer-able messages
// have been added, the feedback message is triggered.
virtual void SendNack(const std::vector<uint16_t>& sequence_numbers,
bool buffering_allowed) = 0;
protected:
virtual ~NackSender() {}
};
// Interface used by NackModule and JitterBuffer.
class KeyFrameRequestSender {
public:
virtual void RequestKeyFrame() = 0;
protected:
virtual ~KeyFrameRequestSender() {}
};
// Interface used by LossNotificationController to communicate to RtpRtcp.
class LossNotificationSender {
public:
virtual ~LossNotificationSender() {}
virtual void SendLossNotification(uint16_t last_decoded_seq_num,
uint16_t last_received_seq_num,
bool decodability_flag,
bool buffering_allowed) = 0;
};
} // namespace webrtc
#endif // MODULES_INCLUDE_MODULE_COMMON_TYPES_H_

136
src/qos/ntp_time.h Normal file
View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
#define SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
#include <cmath>
#include <cstdint>
#include <limits>
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
class NtpTime {
public:
static constexpr uint64_t kFractionsPerSecond = 0x100000000;
NtpTime() : value_(0) {}
explicit NtpTime(uint64_t value) : value_(value) {}
NtpTime(uint32_t seconds, uint32_t fractions)
: value_(seconds * kFractionsPerSecond + fractions) {}
NtpTime(const NtpTime&) = default;
NtpTime& operator=(const NtpTime&) = default;
explicit operator uint64_t() const { return value_; }
void Set(uint32_t seconds, uint32_t fractions) {
value_ = seconds * kFractionsPerSecond + fractions;
}
void Reset() { value_ = 0; }
int64_t ToMs() const {
static constexpr double kNtpFracPerMs = 4.294967296E6; // 2^32 / 1000.
const double frac_ms = static_cast<double>(fractions()) / kNtpFracPerMs;
return 1000 * static_cast<int64_t>(seconds()) +
static_cast<int64_t>(frac_ms + 0.5);
}
// NTP standard (RFC1305, section 3.1) explicitly state value 0 is invalid.
bool Valid() const { return value_ != 0; }
uint32_t seconds() const {
return rtc::dchecked_cast<uint32_t>(value_ / kFractionsPerSecond);
}
uint32_t fractions() const {
return rtc::dchecked_cast<uint32_t>(value_ % kFractionsPerSecond);
}
private:
uint64_t value_;
};
inline bool operator==(const NtpTime& n1, const NtpTime& n2) {
return static_cast<uint64_t>(n1) == static_cast<uint64_t>(n2);
}
inline bool operator!=(const NtpTime& n1, const NtpTime& n2) {
return !(n1 == n2);
}
// Converts `int64_t` milliseconds to Q32.32-formatted fixed-point seconds.
// Performs clamping if the result overflows or underflows.
inline int64_t Int64MsToQ32x32(int64_t milliseconds) {
// TODO(bugs.webrtc.org/10893): Change to use `rtc::saturated_cast` once the
// bug has been fixed.
double result =
std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
// Explicitly cast values to double to avoid implicit conversion warnings
// The conversion of the std::numeric_limits<int64_t>::max() triggers
// -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit
// cast
if (result <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
return std::numeric_limits<int64_t>::min();
}
if (result >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
return std::numeric_limits<int64_t>::max();
}
return rtc::dchecked_cast<int64_t>(result);
}
// Converts `int64_t` milliseconds to UQ32.32-formatted fixed-point seconds.
// Performs clamping if the result overflows or underflows.
inline uint64_t Int64MsToUQ32x32(int64_t milliseconds) {
// TODO(bugs.webrtc.org/10893): Change to use `rtc::saturated_cast` once the
// bug has been fixed.
double result =
std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
// Explicitly cast values to double to avoid implicit conversion warnings
// The conversion of the std::numeric_limits<int64_t>::max() triggers
// -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit
// cast
if (result <= static_cast<double>(std::numeric_limits<uint64_t>::min())) {
return std::numeric_limits<uint64_t>::min();
}
if (result >= static_cast<double>(std::numeric_limits<uint64_t>::max())) {
return std::numeric_limits<uint64_t>::max();
}
return rtc::dchecked_cast<uint64_t>(result);
}
// Converts Q32.32-formatted fixed-point seconds to `int64_t` milliseconds.
inline int64_t Q32x32ToInt64Ms(int64_t q32x32) {
return rtc::dchecked_cast<int64_t>(
std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
}
// Converts UQ32.32-formatted fixed-point seconds to `int64_t` milliseconds.
inline int64_t UQ32x32ToInt64Ms(uint64_t q32x32) {
return rtc::dchecked_cast<int64_t>(
std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
}
// Converts UQ32.32-formatted fixed-point seconds to `int64_t` microseconds.
inline int64_t UQ32x32ToInt64Us(uint64_t q32x32) {
return rtc::dchecked_cast<int64_t>(
std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond)));
}
// Converts Q32.32-formatted fixed-point seconds to `int64_t` microseconds.
inline int64_t Q32x32ToInt64Us(int64_t q32x32) {
return rtc::dchecked_cast<int64_t>(
std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond)));
}
} // namespace webrtc
#endif // SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_

61
src/qos/ntp_time_util.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_
#define MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_
#include <stdint.h>
#include "api/units/time_delta.h"
#include "ntp_time.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
// Helper function for compact ntp representation:
// RFC 3550, Section 4. Time Format.
// Wallclock time is represented using the timestamp format of
// the Network Time Protocol (NTP).
// ...
// In some fields where a more compact representation is
// appropriate, only the middle 32 bits are used; that is, the low 16
// bits of the integer part and the high 16 bits of the fractional part.
inline uint32_t CompactNtp(NtpTime ntp) {
return (ntp.seconds() << 16) | (ntp.fractions() >> 16);
}
// Converts interval to compact ntp (1/2^16 seconds) resolution.
// Negative values converted to 0, Overlarge values converted to max uint32_t.
uint32_t SaturatedToCompactNtp(TimeDelta delta);
// Convert interval to the NTP time resolution (1/2^32 seconds ~= 0.2 ns).
// For deltas with absolute value larger than 35 minutes result is unspecified.
inline constexpr int64_t ToNtpUnits(TimeDelta delta) {
// For better precision `delta` is taken with best TimeDelta precision (us),
// then multiplaction and conversion to seconds are swapped to avoid float
// arithmetic.
// 2^31 us ~= 35.8 minutes.
return (rtc::saturated_cast<int32_t>(delta.us()) * (int64_t{1} << 32)) /
1'000'000;
}
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
// This interval can be up to ~9.1 hours (2^15 seconds).
// Values close to 2^16 seconds are considered negative.
TimeDelta CompactNtpIntervalToTimeDelta(uint32_t compact_ntp_interval);
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
// This interval can be up to ~9.1 hours (2^15 seconds).
// Values close to 2^16 seconds are considered negative and are converted to
// minimum value of 1ms.
TimeDelta CompactNtpRttToTimeDelta(uint32_t compact_ntp_interval);
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_

View File

@@ -12,7 +12,7 @@
#include <stdint.h>
#include "bandwidth_usage.h"
#include "api/transport/bandwidth_usage.h"
namespace webrtc {

View File

@@ -0,0 +1,136 @@
/*
* 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 "overuse_estimator.h"
#include <math.h>
#include <string.h>
#include <algorithm>
#include "api/transport/bandwidth_usage.h"
#include "log.h"
namespace webrtc {
namespace {
constexpr int kMinFramePeriodHistoryLength = 60;
constexpr int kDeltaCounterMax = 1000;
} // namespace
OveruseEstimator::OveruseEstimator() = default;
void OveruseEstimator::Update(int64_t t_delta, double ts_delta, int size_delta,
BandwidthUsage current_hypothesis,
int64_t /* now_ms */) {
const double min_frame_period = UpdateMinFramePeriod(ts_delta);
const double t_ts_delta = t_delta - ts_delta;
double fs_delta = size_delta;
++num_of_deltas_;
if (num_of_deltas_ > kDeltaCounterMax) {
num_of_deltas_ = kDeltaCounterMax;
}
// Update the Kalman filter.
E_[0][0] += process_noise_[0];
E_[1][1] += process_noise_[1];
if ((current_hypothesis == BandwidthUsage::kBwOverusing &&
offset_ < prev_offset_) ||
(current_hypothesis == BandwidthUsage::kBwUnderusing &&
offset_ > prev_offset_)) {
E_[1][1] += 10 * process_noise_[1];
}
const double h[2] = {fs_delta, 1.0};
const double Eh[2] = {E_[0][0] * h[0] + E_[0][1] * h[1],
E_[1][0] * h[0] + E_[1][1] * h[1]};
const double residual = t_ts_delta - slope_ * h[0] - offset_;
const bool in_stable_state =
(current_hypothesis == BandwidthUsage::kBwNormal);
const double max_residual = 3.0 * sqrt(var_noise_);
// We try to filter out very late frames. For instance periodic key
// frames doesn't fit the Gaussian model well.
if (fabs(residual) < max_residual) {
UpdateNoiseEstimate(residual, min_frame_period, in_stable_state);
} else {
UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual,
min_frame_period, in_stable_state);
}
const double denom = var_noise_ + h[0] * Eh[0] + h[1] * Eh[1];
const double K[2] = {Eh[0] / denom, Eh[1] / denom};
const double IKh[2][2] = {{1.0 - K[0] * h[0], -K[0] * h[1]},
{-K[1] * h[0], 1.0 - K[1] * h[1]}};
const double e00 = E_[0][0];
const double e01 = E_[0][1];
// Update state.
E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
// The covariance matrix must be positive semi-definite.
bool positive_semi_definite =
E_[0][0] + E_[1][1] >= 0 &&
E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0;
if (!positive_semi_definite) {
LOG_ERROR(
"The over-use estimator's covariance matrix is no longer "
"semi-definite.");
}
slope_ = slope_ + K[0] * residual;
prev_offset_ = offset_;
offset_ = offset_ + K[1] * residual;
}
double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) {
double min_frame_period = ts_delta;
if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
ts_delta_hist_.pop_front();
}
for (const double old_ts_delta : ts_delta_hist_) {
min_frame_period = std::min(old_ts_delta, min_frame_period);
}
ts_delta_hist_.push_back(ts_delta);
return min_frame_period;
}
void OveruseEstimator::UpdateNoiseEstimate(double residual, double ts_delta,
bool stable_state) {
if (!stable_state) {
return;
}
// Faster filter during startup to faster adapt to the jitter level
// of the network. `alpha` is tuned for 30 frames per second, but is scaled
// according to `ts_delta`.
double alpha = 0.01;
if (num_of_deltas_ > 10 * 30) {
alpha = 0.002;
}
// Only update the noise estimate if we're not over-using. `beta` is a
// function of alpha and the time delta since the previous update.
const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
avg_noise_ = beta * avg_noise_ + (1 - beta) * residual;
var_noise_ = beta * var_noise_ +
(1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
if (var_noise_ < 1) {
var_noise_ = 1;
}
}
} // namespace webrtc

View File

@@ -0,0 +1,66 @@
/*
* 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_OVERUSE_ESTIMATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
#include <stdint.h>
#include <deque>
#include "api/transport/bandwidth_usage.h"
namespace webrtc {
class OveruseEstimator {
public:
OveruseEstimator();
OveruseEstimator(const OveruseEstimator&) = delete;
OveruseEstimator& operator=(const OveruseEstimator&) = delete;
~OveruseEstimator() = default;
// Update the estimator with a new sample. The deltas should represent deltas
// between timestamp groups as defined by the InterArrival class.
// `current_hypothesis` should be the hypothesis of the over-use detector at
// this time.
void Update(int64_t t_delta,
double ts_delta,
int size_delta,
BandwidthUsage current_hypothesis,
int64_t now_ms);
// Returns the estimated noise/jitter variance in ms^2.
double var_noise() const { return var_noise_; }
// Returns the estimated inter-arrival time delta offset in ms.
double offset() const { return offset_; }
// Returns the number of deltas which the current over-use estimator state is
// based on.
int num_of_deltas() const { return num_of_deltas_; }
private:
double UpdateMinFramePeriod(double ts_delta);
void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state);
int num_of_deltas_ = 0;
double slope_ = 8.0 / 512.0;
double offset_ = 0;
double prev_offset_ = 0;
double E_[2][2] = {{100.0, 0.0}, {0.0, 1e-1}};
double process_noise_[2] = {1e-13, 1e-3};
double avg_noise_ = 0.0;
double var_noise_ = 50.0;
std::deque<double> ts_delta_hist_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_

View File

@@ -14,12 +14,12 @@
#include <memory>
#include <optional>
#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 "log.h"
#include "network_types.h"
namespace webrtc {
namespace {

View File

@@ -14,10 +14,10 @@
#include <map>
#include <optional>
#include "api/transport/network_types.h"
#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;

536
src/qos/probe_controller.cc Normal file
View File

@@ -0,0 +1,536 @@
/*
* 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_controller.h"
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <optional>
#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 "log.h"
namespace webrtc {
namespace {
// Maximum waiting time from the time of initiating probing to getting
// the measured results back.
constexpr TimeDelta kMaxWaitingTimeForProbingResult = TimeDelta::Seconds(1);
// Default probing bitrate limit. Applied only when the application didn't
// specify max bitrate.
constexpr DataRate kDefaultMaxProbingBitrate = DataRate::KilobitsPerSec(5000);
// If the bitrate drops to a factor `kBitrateDropThreshold` or lower
// and we recover within `kBitrateDropTimeoutMs`, then we'll send
// a probe at a fraction `kProbeFractionAfterDrop` of the original bitrate.
constexpr double kBitrateDropThreshold = 0.66;
constexpr TimeDelta kBitrateDropTimeout = TimeDelta::Seconds(5);
constexpr double kProbeFractionAfterDrop = 0.85;
// Timeout for probing after leaving ALR. If the bitrate drops significantly,
// (as determined by the delay based estimator) and we leave ALR, then we will
// send a probe if we recover within `kLeftAlrTimeoutMs` ms.
constexpr TimeDelta kAlrEndedTimeout = TimeDelta::Seconds(3);
// The expected uncertainty of probe result (as a fraction of the target probe
// This is a limit on how often probing can be done when there is a BW
// drop detected in ALR.
constexpr TimeDelta kMinTimeBetweenAlrProbes = TimeDelta::Seconds(5);
// bitrate). Used to avoid probing if the probe bitrate is close to our current
// estimate.
constexpr double kProbeUncertainty = 0.05;
// Use probing to recover faster after large bitrate estimate drops.
constexpr char kBweRapidRecoveryExperiment[] =
"WebRTC-BweRapidRecoveryExperiment";
} // namespace
ProbeControllerConfig::ProbeControllerConfig()
: first_exponential_probe_scale(3.0),
second_exponential_probe_scale(6.0),
further_exponential_probe_scale(2),
further_probe_threshold(0.7),
abort_further_probe_if_max_lower_than_current(false),
repeated_initial_probing_time_period(TimeDelta::Seconds(5)),
initial_probe_duration(TimeDelta::Millis(100)),
initial_min_probe_delta(TimeDelta::Millis(20)),
alr_probing_interval(TimeDelta::Seconds(5)),
alr_probe_scale(2),
network_state_estimate_probing_interval(TimeDelta::PlusInfinity()),
probe_if_estimate_lower_than_network_state_estimate_ratio(0),
estimate_lower_than_network_state_estimate_probing_interval(
TimeDelta::Seconds(3)),
network_state_probe_scale(1.0),
network_state_probe_duration(TimeDelta::Millis(15)),
network_state_min_probe_delta(TimeDelta::Millis(20)),
probe_on_max_allocated_bitrate_change(true),
first_allocation_probe_scale(1),
second_allocation_probe_scale(2),
allocation_probe_limit_by_current_scale(2),
min_probe_packets_sent(5),
min_probe_duration(TimeDelta::Millis(15)),
min_probe_delta(TimeDelta::Millis(2)),
loss_limited_probe_scale(1.5),
skip_if_estimate_larger_than_fraction_of_max(0.0),
skip_probe_max_allocated_scale(1.0) {}
ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) =
default;
ProbeControllerConfig::~ProbeControllerConfig() = default;
ProbeController::ProbeController()
: network_available_(false), enable_periodic_alr_probing_(false) {
Reset(Timestamp::Zero());
}
ProbeController::~ProbeController() {}
std::vector<ProbeClusterConfig> ProbeController::SetBitrates(
DataRate min_bitrate, DataRate start_bitrate, DataRate max_bitrate,
Timestamp at_time) {
if (start_bitrate > DataRate::Zero()) {
start_bitrate_ = start_bitrate;
estimated_bitrate_ = start_bitrate;
} else if (start_bitrate_.IsZero()) {
start_bitrate_ = min_bitrate;
}
// The reason we use the variable `old_max_bitrate_pbs` is because we
// need to set `max_bitrate_` before we call InitiateProbing.
DataRate old_max_bitrate = max_bitrate_;
max_bitrate_ =
max_bitrate.IsFinite() ? max_bitrate : kDefaultMaxProbingBitrate;
switch (state_) {
case State::kInit:
if (network_available_) return InitiateExponentialProbing(at_time);
break;
case State::kWaitingForProbingResult:
break;
case State::kProbingComplete:
// If the new max bitrate is higher than both the old max bitrate and the
// estimate then initiate probing.
if (!estimated_bitrate_.IsZero() && old_max_bitrate < max_bitrate_ &&
estimated_bitrate_ < max_bitrate_) {
return InitiateProbing(at_time, {max_bitrate_}, false);
}
break;
}
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::OnMaxTotalAllocatedBitrate(
DataRate max_total_allocated_bitrate, Timestamp at_time) {
const bool in_alr = alr_start_time_.has_value();
const bool allow_allocation_probe = in_alr;
if (config_.probe_on_max_allocated_bitrate_change &&
state_ == State::kProbingComplete &&
max_total_allocated_bitrate != max_total_allocated_bitrate_ &&
estimated_bitrate_ < max_bitrate_ &&
estimated_bitrate_ < max_total_allocated_bitrate &&
allow_allocation_probe) {
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
if (!config_.first_allocation_probe_scale)
return std::vector<ProbeClusterConfig>();
DataRate first_probe_rate =
max_total_allocated_bitrate * config_.first_allocation_probe_scale;
const DataRate current_bwe_limit =
config_.allocation_probe_limit_by_current_scale * estimated_bitrate_;
bool limited_by_current_bwe = current_bwe_limit < first_probe_rate;
if (limited_by_current_bwe) {
first_probe_rate = current_bwe_limit;
}
std::vector<DataRate> probes = {first_probe_rate};
if (!limited_by_current_bwe && config_.second_allocation_probe_scale) {
DataRate second_probe_rate =
max_total_allocated_bitrate * config_.second_allocation_probe_scale;
limited_by_current_bwe = current_bwe_limit < second_probe_rate;
if (limited_by_current_bwe) {
second_probe_rate = current_bwe_limit;
}
if (second_probe_rate > first_probe_rate)
probes.push_back(second_probe_rate);
}
bool allow_further_probing = limited_by_current_bwe;
return InitiateProbing(at_time, probes, allow_further_probing);
}
if (!max_total_allocated_bitrate.IsZero()) {
last_allowed_repeated_initial_probe_ = at_time;
}
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
return std::vector<ProbeClusterConfig>();
}
std::vector<ProbeClusterConfig> ProbeController::OnNetworkAvailability(
NetworkAvailability msg) {
network_available_ = msg.network_available;
if (!network_available_ && state_ == State::kWaitingForProbingResult) {
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
}
if (network_available_ && state_ == State::kInit && !start_bitrate_.IsZero())
return InitiateExponentialProbing(msg.at_time);
return std::vector<ProbeClusterConfig>();
}
void ProbeController::UpdateState(State new_state) {
switch (new_state) {
case State::kInit:
state_ = State::kInit;
break;
case State::kWaitingForProbingResult:
state_ = State::kWaitingForProbingResult;
break;
case State::kProbingComplete:
state_ = State::kProbingComplete;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
break;
}
}
std::vector<ProbeClusterConfig> ProbeController::InitiateExponentialProbing(
Timestamp at_time) {
// When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
// 1.2 Mbps to continue probing.
std::vector<DataRate> probes = {config_.first_exponential_probe_scale *
start_bitrate_};
if (config_.second_exponential_probe_scale &&
config_.second_exponential_probe_scale > 0) {
probes.push_back(config_.second_exponential_probe_scale * start_bitrate_);
}
if (repeated_initial_probing_enabled_ &&
max_total_allocated_bitrate_.IsZero()) {
last_allowed_repeated_initial_probe_ =
at_time + config_.repeated_initial_probing_time_period;
LOG_INFO("Repeated initial probing enabled, last allowed probe: {} now: {}",
last_allowed_repeated_initial_probe_, at_time);
}
return InitiateProbing(at_time, probes, true);
}
std::vector<ProbeClusterConfig> ProbeController::SetEstimatedBitrate(
DataRate bitrate, BandwidthLimitedCause bandwidth_limited_cause,
Timestamp at_time) {
bandwidth_limited_cause_ = bandwidth_limited_cause;
if (bitrate < kBitrateDropThreshold * estimated_bitrate_) {
time_of_last_large_drop_ = at_time;
bitrate_before_last_large_drop_ = estimated_bitrate_;
}
estimated_bitrate_ = bitrate;
if (state_ == State::kWaitingForProbingResult) {
// Continue probing if probing results indicate channel has greater
// capacity unless we already reached the needed bitrate.
if (config_.abort_further_probe_if_max_lower_than_current &&
(bitrate > max_bitrate_ ||
(!max_total_allocated_bitrate_.IsZero() &&
bitrate > 2 * max_total_allocated_bitrate_))) {
// No need to continue probing.
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
}
DataRate network_state_estimate_probe_further_limit =
config_.network_state_estimate_probing_interval.IsFinite() &&
network_estimate_ &&
network_estimate_->link_capacity_upper.IsFinite()
? network_estimate_->link_capacity_upper *
config_.further_probe_threshold
: DataRate::PlusInfinity();
LOG_INFO(
"Measured bitrate: {} Minimum to probe further: {} upper limit: {}",
bitrate, min_bitrate_to_probe_further_,
network_state_estimate_probe_further_limit);
if (bitrate > min_bitrate_to_probe_further_ &&
bitrate <= network_state_estimate_probe_further_limit) {
return InitiateProbing(
at_time, {config_.further_exponential_probe_scale * bitrate}, true);
}
}
return {};
}
void ProbeController::EnablePeriodicAlrProbing(bool enable) {
enable_periodic_alr_probing_ = enable;
}
void ProbeController::EnableRepeatedInitialProbing(bool enable) {
repeated_initial_probing_enabled_ = enable;
}
void ProbeController::SetAlrStartTimeMs(
std::optional<int64_t> alr_start_time_ms) {
if (alr_start_time_ms) {
alr_start_time_ = Timestamp::Millis(*alr_start_time_ms);
} else {
alr_start_time_ = std::nullopt;
}
}
void ProbeController::SetAlrEndedTimeMs(int64_t alr_end_time_ms) {
alr_end_time_.emplace(Timestamp::Millis(alr_end_time_ms));
}
std::vector<ProbeClusterConfig> ProbeController::RequestProbe(
Timestamp at_time) {
// Called once we have returned to normal state after a large drop in
// estimated bandwidth. The current response is to initiate a single probe
// session (if not already probing) at the previous bitrate.
//
// If the probe session fails, the assumption is that this drop was a
// real one from a competing flow or a network change.
bool in_alr = alr_start_time_.has_value();
bool alr_ended_recently =
(alr_end_time_.has_value() &&
at_time - alr_end_time_.value() < kAlrEndedTimeout);
if (in_alr || alr_ended_recently) {
if (state_ == State::kProbingComplete) {
DataRate suggested_probe =
kProbeFractionAfterDrop * bitrate_before_last_large_drop_;
DataRate min_expected_probe_result =
(1 - kProbeUncertainty) * suggested_probe;
TimeDelta time_since_drop = at_time - time_of_last_large_drop_;
TimeDelta time_since_probe = at_time - last_bwe_drop_probing_time_;
if (min_expected_probe_result > estimated_bitrate_ &&
time_since_drop < kBitrateDropTimeout &&
time_since_probe > kMinTimeBetweenAlrProbes) {
LOG_INFO("Detected big bandwidth drop, start probing");
last_bwe_drop_probing_time_ = at_time;
return InitiateProbing(at_time, {suggested_probe}, false);
}
}
}
return std::vector<ProbeClusterConfig>();
}
void ProbeController::SetNetworkStateEstimate(
webrtc::NetworkStateEstimate estimate) {
network_estimate_ = estimate;
}
void ProbeController::Reset(Timestamp at_time) {
bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited;
state_ = State::kInit;
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
time_last_probing_initiated_ = Timestamp::Zero();
estimated_bitrate_ = DataRate::Zero();
network_estimate_ = std::nullopt;
start_bitrate_ = DataRate::Zero();
max_bitrate_ = kDefaultMaxProbingBitrate;
Timestamp now = at_time;
last_bwe_drop_probing_time_ = now;
alr_end_time_.reset();
time_of_last_large_drop_ = now;
bitrate_before_last_large_drop_ = DataRate::Zero();
}
bool ProbeController::TimeForAlrProbe(Timestamp at_time) const {
if (enable_periodic_alr_probing_ && alr_start_time_) {
Timestamp next_probe_time =
std::max(*alr_start_time_, time_last_probing_initiated_) +
config_.alr_probing_interval;
return at_time >= next_probe_time;
}
return false;
}
bool ProbeController::TimeForNetworkStateProbe(Timestamp at_time) const {
if (!network_estimate_ ||
network_estimate_->link_capacity_upper.IsInfinite()) {
return false;
}
bool probe_due_to_low_estimate =
bandwidth_limited_cause_ == BandwidthLimitedCause::kDelayBasedLimited &&
estimated_bitrate_ <
config_.probe_if_estimate_lower_than_network_state_estimate_ratio *
network_estimate_->link_capacity_upper;
if (probe_due_to_low_estimate &&
config_.estimate_lower_than_network_state_estimate_probing_interval
.IsFinite()) {
Timestamp next_probe_time =
time_last_probing_initiated_ +
config_.estimate_lower_than_network_state_estimate_probing_interval;
return at_time >= next_probe_time;
}
bool periodic_probe =
estimated_bitrate_ < network_estimate_->link_capacity_upper;
if (periodic_probe &&
config_.network_state_estimate_probing_interval.IsFinite()) {
Timestamp next_probe_time = time_last_probing_initiated_ +
config_.network_state_estimate_probing_interval;
return at_time >= next_probe_time;
}
return false;
}
bool ProbeController::TimeForNextRepeatedInitialProbe(Timestamp at_time) const {
if (state_ != State::kWaitingForProbingResult &&
last_allowed_repeated_initial_probe_ > at_time) {
Timestamp next_probe_time =
time_last_probing_initiated_ + kMaxWaitingTimeForProbingResult;
if (at_time >= next_probe_time) {
return true;
}
}
return false;
}
std::vector<ProbeClusterConfig> ProbeController::Process(Timestamp at_time) {
if (at_time - time_last_probing_initiated_ >
kMaxWaitingTimeForProbingResult) {
if (state_ == State::kWaitingForProbingResult) {
LOG_INFO("kWaitingForProbingResult: timeout");
UpdateState(State::kProbingComplete);
}
}
if (estimated_bitrate_.IsZero() || state_ != State::kProbingComplete) {
return {};
}
if (TimeForNextRepeatedInitialProbe(at_time)) {
return InitiateProbing(
at_time, {estimated_bitrate_ * config_.first_exponential_probe_scale},
true);
}
if (TimeForAlrProbe(at_time) || TimeForNetworkStateProbe(at_time)) {
return InitiateProbing(
at_time, {estimated_bitrate_ * config_.alr_probe_scale}, true);
}
return std::vector<ProbeClusterConfig>();
}
ProbeClusterConfig ProbeController::CreateProbeClusterConfig(Timestamp at_time,
DataRate bitrate) {
ProbeClusterConfig config;
config.at_time = at_time;
config.target_data_rate = bitrate;
if (network_estimate_ &&
config_.network_state_estimate_probing_interval.IsFinite() &&
network_estimate_->link_capacity_upper.IsFinite() &&
network_estimate_->link_capacity_upper >= bitrate) {
config.target_duration = config_.network_state_probe_duration;
config.min_probe_delta = config_.network_state_min_probe_delta;
} else if (at_time < last_allowed_repeated_initial_probe_) {
config.target_duration = config_.initial_probe_duration;
config.min_probe_delta = config_.initial_min_probe_delta;
} else {
config.target_duration = config_.min_probe_duration;
config.min_probe_delta = config_.min_probe_delta;
}
config.target_probe_count = config_.min_probe_packets_sent;
config.id = next_probe_cluster_id_;
next_probe_cluster_id_++;
return config;
}
std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
Timestamp now, std::vector<DataRate> bitrates_to_probe,
bool probe_further) {
if (config_.skip_if_estimate_larger_than_fraction_of_max > 0) {
DataRate network_estimate = network_estimate_
? network_estimate_->link_capacity_upper
: DataRate::PlusInfinity();
DataRate max_probe_rate =
max_total_allocated_bitrate_.IsZero()
? max_bitrate_
: std::min(config_.skip_probe_max_allocated_scale *
max_total_allocated_bitrate_,
max_bitrate_);
if (std::min(network_estimate, estimated_bitrate_) >
config_.skip_if_estimate_larger_than_fraction_of_max * max_probe_rate) {
UpdateState(State::kProbingComplete);
return {};
}
}
DataRate max_probe_bitrate = max_bitrate_;
if (max_total_allocated_bitrate_ > DataRate::Zero()) {
// If a max allocated bitrate has been configured, allow probing up to 2x
// that rate. This allows some overhead to account for bursty streams,
// which otherwise would have to ramp up when the overshoot is already in
// progress.
// It also avoids minor quality reduction caused by probes often being
// received at slightly less than the target probe bitrate.
max_probe_bitrate =
std::min(max_probe_bitrate, max_total_allocated_bitrate_ * 2);
}
switch (bandwidth_limited_cause_) {
case BandwidthLimitedCause::kRttBasedBackOffHighRtt:
case BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased:
case BandwidthLimitedCause::kLossLimitedBwe:
LOG_INFO("Not sending probe in bandwidth limited state {}",
static_cast<int>(bandwidth_limited_cause_));
return {};
case BandwidthLimitedCause::kLossLimitedBweIncreasing:
max_probe_bitrate =
std::min(max_probe_bitrate,
estimated_bitrate_ * config_.loss_limited_probe_scale);
break;
case BandwidthLimitedCause::kDelayBasedLimited:
break;
default:
break;
}
if (config_.network_state_estimate_probing_interval.IsFinite() &&
network_estimate_ && network_estimate_->link_capacity_upper.IsFinite()) {
if (network_estimate_->link_capacity_upper.IsZero()) {
LOG_INFO("Not sending probe, Network state estimate is zero");
return {};
}
max_probe_bitrate = std::min(
{max_probe_bitrate,
std::max(estimated_bitrate_, network_estimate_->link_capacity_upper *
config_.network_state_probe_scale)});
}
std::vector<ProbeClusterConfig> pending_probes;
for (DataRate bitrate : bitrates_to_probe) {
if (bitrate >= max_probe_bitrate) {
bitrate = max_probe_bitrate;
probe_further = false;
}
pending_probes.push_back(CreateProbeClusterConfig(now, bitrate));
}
time_last_probing_initiated_ = now;
if (probe_further) {
UpdateState(State::kWaitingForProbingResult);
// Dont expect probe results to be larger than a fraction of the actual
// probe rate.
min_bitrate_to_probe_further_ = pending_probes.back().target_data_rate *
config_.further_probe_threshold;
} else {
UpdateState(State::kProbingComplete);
}
return pending_probes;
}
} // namespace webrtc

202
src/qos/probe_controller.h Normal file
View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
#include <stdint.h>
#include <optional>
#include <vector>
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
struct ProbeControllerConfig {
ProbeControllerConfig();
ProbeControllerConfig(const ProbeControllerConfig&);
ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default;
~ProbeControllerConfig();
// These parameters configure the initial probes. First we send one or two
// probes of sizes p1 * start_bitrate_ and p2 * start_bitrate_.
// Then whenever we get a bitrate estimate of at least further_probe_threshold
// times the size of the last sent probe we'll send another one of size
// step_size times the new estimate.
double first_exponential_probe_scale;
double second_exponential_probe_scale;
double further_exponential_probe_scale;
double further_probe_threshold;
bool abort_further_probe_if_max_lower_than_current;
// Duration of time from the first initial probe where repeated initial probes
// are sent if repeated initial probing is enabled.
TimeDelta repeated_initial_probing_time_period;
// The minimum probing duration of an individual probe during
// the repeated_initial_probing_time_period.
TimeDelta initial_probe_duration;
// Delta time between sent bursts of packets in a probe during
// the repeated_initial_probing_time_period.
TimeDelta initial_min_probe_delta;
// Configures how often we send ALR probes and how big they are.
TimeDelta alr_probing_interval;
double alr_probe_scale;
// Configures how often we send probes if NetworkStateEstimate is available.
TimeDelta network_state_estimate_probing_interval;
// Periodically probe as long as the ratio between current estimate and
// NetworkStateEstimate is lower then this.
double probe_if_estimate_lower_than_network_state_estimate_ratio;
TimeDelta estimate_lower_than_network_state_estimate_probing_interval;
double network_state_probe_scale;
// Overrides min_probe_duration if network_state_estimate_probing_interval
// is set and a network state estimate is known and equal or higher than the
// probe target.
TimeDelta network_state_probe_duration;
// Overrides min_probe_delta if network_state_estimate_probing_interval
// is set and a network state estimate is known and equal or higher than the
// probe target.
TimeDelta network_state_min_probe_delta;
// Configures the probes emitted by changed to the allocated bitrate.
bool probe_on_max_allocated_bitrate_change;
double first_allocation_probe_scale;
double second_allocation_probe_scale;
double allocation_probe_limit_by_current_scale;
// The minimum number probing packets used.
int min_probe_packets_sent;
// The minimum probing duration.
TimeDelta min_probe_duration;
// Delta time between sent bursts of packets in a probe.
TimeDelta min_probe_delta;
double loss_limited_probe_scale;
// Don't send a probe if min(estimate, network state estimate) is larger than
// this fraction of the set max or max allocated bitrate.
double skip_if_estimate_larger_than_fraction_of_max;
// Scale factor of the max allocated bitrate. Used when deciding if a probe
// can be skiped due to that the estimate is already high enough.
double skip_probe_max_allocated_scale;
};
// Reason that bandwidth estimate is limited. Bandwidth estimate can be limited
// by either delay based bwe, or loss based bwe when it increases/decreases the
// estimate.
enum class BandwidthLimitedCause : int {
kLossLimitedBweIncreasing = 0,
kLossLimitedBwe = 1,
kDelayBasedLimited = 2,
kDelayBasedLimitedDelayIncreased = 3,
kRttBasedBackOffHighRtt = 4
};
// This class controls initiation of probing to estimate initial channel
// capacity. There is also support for probing during a session when max
// bitrate is adjusted by an application.
class ProbeController {
public:
ProbeController();
~ProbeController();
ProbeController(const ProbeController&) = delete;
ProbeController& operator=(const ProbeController&) = delete;
std::vector<ProbeClusterConfig> SetBitrates(DataRate min_bitrate,
DataRate start_bitrate,
DataRate max_bitrate,
Timestamp at_time);
// The total bitrate, as opposed to the max bitrate, is the sum of the
// configured bitrates for all active streams.
std::vector<ProbeClusterConfig> OnMaxTotalAllocatedBitrate(
DataRate max_total_allocated_bitrate, Timestamp at_time);
std::vector<ProbeClusterConfig> OnNetworkAvailability(
NetworkAvailability msg);
std::vector<ProbeClusterConfig> SetEstimatedBitrate(
DataRate bitrate, BandwidthLimitedCause bandwidth_limited_cause,
Timestamp at_time);
void EnablePeriodicAlrProbing(bool enable);
// Probes are sent periodically every 1s during the first 5s after the network
// becomes available or until OnMaxTotalAllocatedBitrate is invoked with a
// none zero max_total_allocated_bitrate (there are active streams being
// sent.) Probe rate is up to max configured bitrate configured via
// SetBitrates.
void EnableRepeatedInitialProbing(bool enable);
void SetAlrStartTimeMs(std::optional<int64_t> alr_start_time);
void SetAlrEndedTimeMs(int64_t alr_end_time);
std::vector<ProbeClusterConfig> RequestProbe(Timestamp at_time);
void SetNetworkStateEstimate(webrtc::NetworkStateEstimate estimate);
// Resets the ProbeController to a state equivalent to as if it was just
// created EXCEPT for configuration settings like
// `enable_periodic_alr_probing_` `network_available_` and
// `max_total_allocated_bitrate_`.
void Reset(Timestamp at_time);
std::vector<ProbeClusterConfig> Process(Timestamp at_time);
private:
enum class State {
// Initial state where no probing has been triggered yet.
kInit,
// Waiting for probing results to continue further probing.
kWaitingForProbingResult,
// Probing is complete.
kProbingComplete,
};
void UpdateState(State new_state);
std::vector<ProbeClusterConfig> InitiateExponentialProbing(Timestamp at_time);
std::vector<ProbeClusterConfig> InitiateProbing(
Timestamp now, std::vector<DataRate> bitrates_to_probe,
bool probe_further);
bool TimeForAlrProbe(Timestamp at_time) const;
bool TimeForNetworkStateProbe(Timestamp at_time) const;
bool TimeForNextRepeatedInitialProbe(Timestamp at_time) const;
ProbeClusterConfig CreateProbeClusterConfig(Timestamp at_time,
DataRate bitrate);
bool network_available_;
bool repeated_initial_probing_enabled_ = false;
Timestamp last_allowed_repeated_initial_probe_ = Timestamp::MinusInfinity();
BandwidthLimitedCause bandwidth_limited_cause_ =
BandwidthLimitedCause::kDelayBasedLimited;
State state_;
DataRate min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
Timestamp time_last_probing_initiated_ = Timestamp::MinusInfinity();
DataRate estimated_bitrate_ = DataRate::Zero();
std::optional<webrtc::NetworkStateEstimate> network_estimate_;
DataRate start_bitrate_ = DataRate::Zero();
DataRate max_bitrate_ = DataRate::PlusInfinity();
Timestamp last_bwe_drop_probing_time_ = Timestamp::Zero();
std::optional<Timestamp> alr_start_time_;
std::optional<Timestamp> alr_end_time_;
bool enable_periodic_alr_probing_;
Timestamp time_of_last_large_drop_ = Timestamp::MinusInfinity();
DataRate bitrate_before_last_large_drop_ = DataRate::Zero();
DataRate max_total_allocated_bitrate_ = DataRate::Zero();
int32_t next_probe_cluster_id_ = 1;
ProbeControllerConfig config_;
};
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "receive_side_congestion_controller.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include "api/media_types.h"
#include "api/transport/network_control.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 "log.h"
#include "remote_bitrate_estimator_single_stream.h"
#include "rtp_packet_received.h"
namespace webrtc {
namespace {
static const uint32_t kTimeOffsetSwitchThreshold = 30;
} // namespace
void ReceiveSideCongestionController::OnRttUpdate(int64_t avg_rtt_ms,
int64_t max_rtt_ms) {
std::lock_guard<std::mutex> lock(mutex_);
rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
}
void ReceiveSideCongestionController::RemoveStream(uint32_t ssrc) {
std::lock_guard<std::mutex> lock(mutex_);
rbe_->RemoveStream(ssrc);
}
DataRate ReceiveSideCongestionController::LatestReceiveSideEstimate() const {
std::lock_guard<std::mutex> lock(mutex_);
return rbe_->LatestEstimate();
}
void ReceiveSideCongestionController::PickEstimator() {
// When we don't see AST, wait for a few packets before going back to TOF.
if (using_absolute_send_time_) {
++packets_since_absolute_send_time_;
if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) {
LOG_INFO(
"WrappingBitrateEstimator: Switching to transmission "
"time offset RBE.");
using_absolute_send_time_ = false;
rbe_ = std::make_unique<RemoteBitrateEstimatorSingleStream>(
clock_, &remb_throttler_);
}
}
}
ReceiveSideCongestionController::ReceiveSideCongestionController(
std::shared_ptr<SimulatedClock> clock,
RtpTransportFeedbackGenerator::RtcpSender feedback_sender,
RembThrottler::RembSender remb_sender,
std::shared_ptr<NetworkStateEstimator> network_state_estimator)
: clock_(clock),
remb_throttler_(std::move(remb_sender), clock.get()),
congestion_control_feedback_generator_(clock, feedback_sender),
rbe_(std::make_unique<RemoteBitrateEstimatorSingleStream>(
clock, &remb_throttler_)),
using_absolute_send_time_(false),
packets_since_absolute_send_time_(0) {}
void ReceiveSideCongestionController::OnReceivedPacket(
const RtpPacketReceived& packet, MediaType media_type) {
congestion_control_feedback_generator_.OnReceivedPacket(packet);
return;
}
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
DataRate send_bandwidth_estimate = DataRate::BitsPerSec(bitrate_bps);
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
send_bandwidth_estimate);
}
TimeDelta ReceiveSideCongestionController::MaybeProcess() {
Timestamp now = clock_->CurrentTime();
TimeDelta time_until = congestion_control_feedback_generator_.Process(now);
return std::max(time_until, TimeDelta::Zero());
}
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
DataRate bitrate) {
remb_throttler_.SetMaxDesiredReceiveBitrate(bitrate);
}
void ReceiveSideCongestionController::SetTransportOverhead(
DataSize overhead_per_packet) {
congestion_control_feedback_generator_.SetTransportOverhead(
overhead_per_packet);
}
} // namespace webrtc

View File

@@ -1,61 +0,0 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "receive_side_congestion_controller.h"
#include <algorithm>
#include <chrono>
#include <memory>
#include <utility>
namespace {
static const uint32_t kTimeOffsetSwitchThreshold = 30;
} // namespace
ReceiveSideCongestionController::ReceiveSideCongestionController(
RtcpSender feedback_sender)
: congestion_control_feedback_generator_(feedback_sender),
using_absolute_send_time_(false),
packets_since_absolute_send_time_(0) {}
void ReceiveSideCongestionController::OnReceivedPacket(
RtpPacketReceived& packet, MediaType media_type) {
// RTC_DCHECK_RUN_ON(&sequence_checker_);
congestion_control_feedback_generator_.OnReceivedPacket(packet);
return;
}
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
// RTC_DCHECK_RUN_ON(&sequence_checker_);
int64_t send_bandwidth_estimate = bitrate_bps;
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
send_bandwidth_estimate);
}
int64_t ReceiveSideCongestionController::MaybeProcess() {
auto now = std::chrono::system_clock::now();
int64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch())
.count();
// RTC_DCHECK_RUN_ON(&sequence_checker_);
return congestion_control_feedback_generator_.Process(now_ms);
}
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
int64_t bitrate) {
// remb_throttler_.SetMaxDesiredReceiveBitrate(bitrate);
}
void ReceiveSideCongestionController::SetTransportOverhead(
int64_t overhead_per_packet) {
// RTC_DCHECK_RUN_ON(&sequence_checker_);
congestion_control_feedback_generator_.SetTransportOverhead(
overhead_per_packet);
}

View File

@@ -1,55 +1,89 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-12-12
* Copyright (c) 2024 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 _RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#define _RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#ifndef MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#define MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
#include <cstdint>
#include <memory>
#include <mutex>
#include "api/media_types.h"
#include "api/transport/network_control.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "congestion_control_feedback_generator.h"
#include "module_common_types.h"
#include "remb_throttler.h"
#include "rtc_base/thread_annotations.h"
#include "rtp_packet_received.h"
class ReceiveSideCongestionController {
public:
enum MediaType { VIDEO, AUDIO, DATA };
namespace webrtc {
class RemoteBitrateEstimator;
// This class represents the congestion control state for receive
// streams. For send side bandwidth estimation, this is simply
// relaying for each received RTP packet back to the sender. While for
// receive side bandwidth estimation, we do the estimation locally and
// send our results back to the sender.
class ReceiveSideCongestionController : public CallStatsObserver {
public:
ReceiveSideCongestionController(RtcpSender feedback_sender);
~ReceiveSideCongestionController() = default;
ReceiveSideCongestionController(
std::shared_ptr<SimulatedClock> clock,
RtpTransportFeedbackGenerator::RtcpSender feedback_sender,
RembThrottler::RembSender remb_sender,
std::shared_ptr<NetworkStateEstimator> network_state_estimator);
public:
void OnReceivedPacket(RtpPacketReceived& packet, MediaType media_type);
~ReceiveSideCongestionController() override = default;
void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type);
// Implements CallStatsObserver.
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
// This is send bitrate, used to control the rate of feedback messages.
void OnBitrateChanged(int bitrate_bps);
// Ensures the remote party is notified of the receive bitrate no larger than
// `bitrate` using RTCP REMB.
void SetMaxDesiredReceiveBitrate(int64_t bitrate);
void SetMaxDesiredReceiveBitrate(DataRate bitrate);
void SetTransportOverhead(int64_t overhead_per_packet);
void SetTransportOverhead(DataSize overhead_per_packet);
// Returns latest receive side bandwidth estimation.
// Returns zero if receive side bandwidth estimation is unavailable.
DataRate LatestReceiveSideEstimate() const;
// Removes stream from receive side bandwidth estimation.
// Noop if receive side bwe is not used or stream doesn't participate in it.
void RemoveStream(uint32_t ssrc);
// Runs periodic tasks if it is time to run them, returns time until next
// call to `MaybeProcess` should be non idle.
int64_t MaybeProcess();
TimeDelta MaybeProcess();
private:
// RembThrottler remb_throttler_;
void PickEstimator();
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
// ReceiveSideCongestionController. At the time of
// writing OnReceivedPacket and MaybeProcess can unfortunately be called on an
// arbitrary thread by external projects.
// SequenceChecker sequence_checker_;
std::shared_ptr<SimulatedClock> clock_;
RembThrottler remb_throttler_;
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
std::mutex mutex_;
mutable std::mutex mutex_;
std::unique_ptr<RemoteBitrateEstimator> rbe_;
bool using_absolute_send_time_;
uint32_t packets_since_absolute_send_time_;
};
#endif
} // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_

64
src/qos/remb_throttler.cc Normal file
View File

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

54
src/qos/remb_throttler.h Normal file
View File

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

View File

@@ -0,0 +1,65 @@
/*
* 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.
*/
// This class estimates the incoming available bandwidth.
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
#include <cstdint>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "clock.h"
#include "module_common_types.h"
#include "rtp_packet_received.h"
namespace webrtc {
class Clock;
// RemoteBitrateObserver is used to signal changes in bitrate estimates for
// the incoming streams.
class RemoteBitrateObserver {
public:
// Called when a receive channel group has a new bitrate estimate for the
// incoming streams.
virtual void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
uint32_t bitrate) = 0;
virtual ~RemoteBitrateObserver() {}
};
class RemoteBitrateEstimator : public CallStatsObserver {
public:
~RemoteBitrateEstimator() override {}
// Called for each incoming packet. Updates the incoming payload bitrate
// estimate and the over-use detector. If an over-use is detected the
// remote bitrate estimate will be updated.
virtual void IncomingPacket(const RtpPacketReceived& rtp_packet) = 0;
// Removes all data for `ssrc`.
virtual void RemoveStream(uint32_t ssrc) = 0;
// Returns latest estimate or DataRate::Zero() if estimation is unavailable.
virtual DataRate LatestEstimate() const = 0;
virtual TimeDelta Process() = 0;
protected:
static constexpr TimeDelta kProcessInterval = TimeDelta::Millis(500);
static constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_

View File

@@ -0,0 +1,181 @@
/*
* 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 "remote_bitrate_estimator_single_stream.h"
#include <cstdint>
#include <optional>
#include <utility>
#include "aimd_rate_control.h"
#include "bwe_defines.h"
#include "clock.h"
#include "inter_arrival.h"
#include "log.h"
#include "overuse_detector.h"
#include "overuse_estimator.h"
#include "rtp_packet_received.h"
namespace webrtc {
namespace {
constexpr int kTimestampGroupLengthMs = 5;
constexpr double kTimestampToMs = 1.0 / 90.0;
} // namespace
RemoteBitrateEstimatorSingleStream::Detector::Detector()
: last_packet_time(Timestamp::Zero()),
inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs) {}
RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream(
std::shared_ptr<SimulatedClock> clock, RemoteBitrateObserver* observer)
: clock_(clock),
observer_(observer),
incoming_bitrate_(kBitrateWindow),
last_valid_incoming_bitrate_(DataRate::Zero()),
process_interval_(kProcessInterval),
uma_recorded_(false) {
LOG_INFO("RemoteBitrateEstimatorSingleStream: Instantiating");
}
RemoteBitrateEstimatorSingleStream::~RemoteBitrateEstimatorSingleStream() =
default;
void RemoteBitrateEstimatorSingleStream::IncomingPacket(
const RtpPacketReceived& rtp_packet) {
std::optional<int32_t> transmission_time_offset = rtp_packet.Timestamp();
if (!uma_recorded_) {
BweNames type = transmission_time_offset.has_value()
? BweNames::kReceiverTOffset
: BweNames::kReceiverNoExtension;
uma_recorded_ = true;
}
uint32_t ssrc = rtp_packet.Ssrc();
uint32_t rtp_timestamp =
rtp_packet.Timestamp() + transmission_time_offset.value_or(0);
Timestamp now = clock_->CurrentTime();
Detector& estimator = overuse_detectors_[ssrc];
estimator.last_packet_time = now;
// Check if incoming bitrate estimate is valid, and if it needs to be reset.
std::optional<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
if (incoming_bitrate) {
last_valid_incoming_bitrate_ = *incoming_bitrate;
} else if (last_valid_incoming_bitrate_ > DataRate::Zero()) {
// Incoming bitrate had a previous valid value, but now not enough data
// point are left within the current window. Reset incoming bitrate
// estimator so that the window size will only contain new data points.
incoming_bitrate_.Reset();
last_valid_incoming_bitrate_ = DataRate::Zero();
}
size_t payload_size = rtp_packet.payload_size() + rtp_packet.padding_size();
incoming_bitrate_.Update(payload_size, now);
const BandwidthUsage prior_state = estimator.detector.State();
uint32_t timestamp_delta = 0;
int64_t time_delta = 0;
int size_delta = 0;
int64_t now_ms = now.ms();
if (estimator.inter_arrival.ComputeDeltas(
rtp_timestamp, rtp_packet.arrival_time().ms(), now_ms, payload_size,
&timestamp_delta, &time_delta, &size_delta)) {
double timestamp_delta_ms = timestamp_delta * kTimestampToMs;
estimator.estimator.Update(time_delta, timestamp_delta_ms, size_delta,
estimator.detector.State(), now_ms);
estimator.detector.Detect(estimator.estimator.offset(), timestamp_delta_ms,
estimator.estimator.num_of_deltas(), now_ms);
}
if (estimator.detector.State() == BandwidthUsage::kBwOverusing) {
std::optional<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
if (incoming_bitrate.has_value() &&
(prior_state != BandwidthUsage::kBwOverusing ||
remote_rate_.TimeToReduceFurther(now, *incoming_bitrate))) {
// The first overuse should immediately trigger a new estimate.
// We also have to update the estimate immediately if we are overusing
// and the target bitrate is too high compared to what we are receiving.
UpdateEstimate(now);
}
}
}
TimeDelta RemoteBitrateEstimatorSingleStream::Process() {
Timestamp now = clock_->CurrentTime();
Timestamp next_process_time = last_process_time_.has_value()
? *last_process_time_ + process_interval_
: now;
// TODO(bugs.webrtc.org/13756): Removing rounding to milliseconds after
// investigating why tests fails without that rounding.
if (now.ms() >= next_process_time.ms()) {
UpdateEstimate(now);
last_process_time_ = now;
return process_interval_;
}
return next_process_time - now;
}
void RemoteBitrateEstimatorSingleStream::UpdateEstimate(Timestamp now) {
BandwidthUsage bw_state = BandwidthUsage::kBwNormal;
auto it = overuse_detectors_.begin();
while (it != overuse_detectors_.end()) {
if (now - it->second.last_packet_time > kStreamTimeOut) {
// This over-use detector hasn't received packets for `kStreamTimeOut`
// and is considered stale.
overuse_detectors_.erase(it++);
} else {
// Make sure that we trigger an over-use if any of the over-use detectors
// is detecting over-use.
if (it->second.detector.State() > bw_state) {
bw_state = it->second.detector.State();
}
++it;
}
}
// We can't update the estimate if we don't have any active streams.
if (overuse_detectors_.empty()) {
return;
}
const RateControlInput input(bw_state, incoming_bitrate_.Rate(now));
uint32_t target_bitrate = remote_rate_.Update(input, now).bps<uint32_t>();
if (remote_rate_.ValidEstimate()) {
process_interval_ = remote_rate_.GetFeedbackInterval();
if (observer_)
observer_->OnReceiveBitrateChanged(GetSsrcs(), target_bitrate);
}
}
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms,
int64_t /* max_rtt_ms */) {
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
}
void RemoteBitrateEstimatorSingleStream::RemoveStream(uint32_t ssrc) {
overuse_detectors_.erase(ssrc);
}
DataRate RemoteBitrateEstimatorSingleStream::LatestEstimate() const {
if (!remote_rate_.ValidEstimate() || overuse_detectors_.empty()) {
return DataRate::Zero();
}
return remote_rate_.LatestEstimate();
}
std::vector<uint32_t> RemoteBitrateEstimatorSingleStream::GetSsrcs() const {
std::vector<uint32_t> ssrcs;
ssrcs.reserve(overuse_detectors_.size());
for (const auto& [ssrc, unused] : overuse_detectors_) {
ssrcs.push_back(ssrc);
}
return ssrcs;
}
} // namespace webrtc

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#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 "inter_arrival.h"
#include "overuse_detector.h"
#include "overuse_estimator.h"
#include "remote_bitrate_estimator.h"
#include "rtc_base/bitrate_tracker.h"
namespace webrtc {
class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
public:
RemoteBitrateEstimatorSingleStream(std::shared_ptr<SimulatedClock> clock,
RemoteBitrateObserver* observer);
RemoteBitrateEstimatorSingleStream() = delete;
RemoteBitrateEstimatorSingleStream(
const RemoteBitrateEstimatorSingleStream&) = delete;
RemoteBitrateEstimatorSingleStream& operator=(
const RemoteBitrateEstimatorSingleStream&) = delete;
~RemoteBitrateEstimatorSingleStream() override;
void IncomingPacket(const RtpPacketReceived& rtp_packet) override;
TimeDelta Process() override;
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void RemoveStream(uint32_t ssrc) override;
DataRate LatestEstimate() const override;
private:
struct Detector {
Detector();
Timestamp last_packet_time;
InterArrival inter_arrival;
OveruseEstimator estimator;
OveruseDetector detector;
};
// Triggers a new estimate calculation.
void UpdateEstimate(Timestamp now);
std::vector<uint32_t> GetSsrcs() const;
std::shared_ptr<SimulatedClock> clock_;
RemoteBitrateObserver* observer_;
std::map<uint32_t, Detector> overuse_detectors_;
BitrateTracker incoming_bitrate_;
DataRate last_valid_incoming_bitrate_;
AimdRateControl remote_rate_;
std::optional<Timestamp> last_process_time_;
TimeDelta process_interval_;
bool uma_recorded_;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_

View File

@@ -0,0 +1,47 @@
/*
* 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_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_
#define MODULES_REMOTE_BITRATE_ESTIMATOR_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_
#include <memory>
#include <vector>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtp_packet_received.h"
namespace webrtc {
class RtpTransportFeedbackGenerator {
public:
// Function intented to be used for sending RTCP messages generated by an
// implementation of this class.
using RtcpSender =
std::function<void(std::vector<std::unique_ptr<RtcpPacket>> packets)>;
virtual ~RtpTransportFeedbackGenerator() = default;
virtual void OnReceivedPacket(const RtpPacketReceived& packet) = 0;
// Sends periodic feedback if it is time to send it.
// Returns time until next call to Process should be made.
virtual TimeDelta Process(Timestamp now) = 0;
virtual void OnSendBandwidthEstimateChanged(DataRate estimate) = 0;
// Overhead from transport layers below RTP. Ie, IP, SRTP.
virtual void SetTransportOverhead(DataSize overhead_per_packet) = 0;
};
} // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_

View File

@@ -20,25 +20,30 @@
#include <string>
#include <utility>
#include "api/transport/bandwidth_usage.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "bwe_defines.h"
#include "log.h"
namespace webrtc {
namespace {
constexpr int64_t kBweIncreaseInterval = 1000;
constexpr int64_t kBweDecreaseInterval = 300;
constexpr int64_t kStartPhase = 2000;
constexpr int64_t kBweConverganceTime = 20000;
constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000);
constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300);
constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000);
constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000);
constexpr int kLimitNumPackets = 20;
constexpr int64_t kDefaultMaxBitrate = 1000000000;
constexpr int64_t kLowBitrateLogPeriod = 10000;
constexpr int64_t kRtcEventLogPeriod = 5000;
constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000);
constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000);
constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000);
// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals.
constexpr int64_t kMaxRtcpFeedbackInterval = 5000;
constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
constexpr float kDefaultLowLossThreshold = 0.02f;
constexpr float kDefaultHighLossThreshold = 0.1f;
constexpr int64_t kDefaultBitrateThreshold = 0;
constexpr int64_t kCongestionControllerMinBitrate = 5000;
constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero();
struct UmaRampUpMetric {
const char* metric_name;
@@ -51,64 +56,70 @@ const UmaRampUpMetric kUmaRampupMetrics[] = {
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
const size_t kNumUmaRampupMetrics =
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
const char kBweLosExperiment[] = "WebRTC-BweLossExperiment";
} // namespace
void LinkCapacityTracker::UpdateDelayBasedEstimate(
int64_t at_time, int64_t delay_based_bitrate) {
Timestamp at_time, DataRate delay_based_bitrate) {
if (delay_based_bitrate < last_delay_based_estimate_) {
capacity_estimate_bps_ = std::min(capacity_estimate_bps_,
static_cast<double>(delay_based_bitrate));
capacity_estimate_bps_ =
std::min(capacity_estimate_bps_, delay_based_bitrate.bps<double>());
last_link_capacity_update_ = at_time;
}
last_delay_based_estimate_ = delay_based_bitrate;
}
void LinkCapacityTracker::OnStartingRate(int64_t start_rate) {
if (IsInfinite(last_link_capacity_update_)) {
capacity_estimate_bps_ = start_rate;
}
void LinkCapacityTracker::OnStartingRate(DataRate start_rate) {
if (last_link_capacity_update_.IsInfinite())
capacity_estimate_bps_ = start_rate.bps<double>();
}
void LinkCapacityTracker::OnRateUpdate(std::optional<int64_t> acknowledged,
int64_t target, int64_t at_time) {
void LinkCapacityTracker::OnRateUpdate(std::optional<DataRate> acknowledged,
DataRate target, Timestamp at_time) {
if (!acknowledged) return;
int64_t acknowledged_target = std::min(*acknowledged, target);
if (acknowledged_target > capacity_estimate_bps_) {
int64_t delta = at_time - last_link_capacity_update_;
double alpha = IsFinite(delta) ? exp(-(delta / 10)) : 0;
capacity_estimate_bps_ =
alpha * capacity_estimate_bps_ + (1 - alpha) * acknowledged_target;
DataRate acknowledged_target = std::min(*acknowledged, target);
if (acknowledged_target.bps() > capacity_estimate_bps_) {
TimeDelta delta = at_time - last_link_capacity_update_;
double alpha =
delta.IsFinite() ? exp(-(delta / TimeDelta::Seconds(10))) : 0;
capacity_estimate_bps_ = alpha * capacity_estimate_bps_ +
(1 - alpha) * acknowledged_target.bps<double>();
}
last_link_capacity_update_ = at_time;
}
void LinkCapacityTracker::OnRttBackoff(int64_t backoff_rate, int64_t at_time) {
void LinkCapacityTracker::OnRttBackoff(DataRate backoff_rate,
Timestamp at_time) {
capacity_estimate_bps_ =
std::min(capacity_estimate_bps_, static_cast<double>(backoff_rate));
std::min(capacity_estimate_bps_, backoff_rate.bps<double>());
last_link_capacity_update_ = at_time;
}
int64_t LinkCapacityTracker::estimate() const { return capacity_estimate_bps_; }
DataRate LinkCapacityTracker::estimate() const {
return DataRate::BitsPerSec(capacity_estimate_bps_);
}
RttBasedBackoff::RttBasedBackoff()
: disabled_(true),
configured_limit_(3),
configured_limit_(TimeDelta::Seconds(3)),
drop_fraction_(0.8),
drop_interval_(1),
bandwidth_floor_(5),
rtt_limit_(INT64_T_MAX),
drop_interval_(TimeDelta::Seconds(1)),
bandwidth_floor_(DataRate::KilobitsPerSec(5)),
rtt_limit_(TimeDelta::PlusInfinity()),
// By initializing this to plus infinity, we make sure that we never
// trigger rtt backoff unless packet feedback is enabled.
last_propagation_rtt_update_(INT64_T_MAX),
last_propagation_rtt_(0),
last_packet_sent_(INT64_T_MIN) {
last_propagation_rtt_update_(Timestamp::PlusInfinity()),
last_propagation_rtt_(TimeDelta::Zero()),
last_packet_sent_(Timestamp::MinusInfinity()) {
if (!disabled_) {
rtt_limit_ = configured_limit_;
}
}
void RttBasedBackoff::UpdatePropagationRtt(int64_t at_time,
int64_t propagation_rtt) {
void RttBasedBackoff::UpdatePropagationRtt(Timestamp at_time,
TimeDelta propagation_rtt) {
last_propagation_rtt_update_ = at_time;
last_propagation_rtt_ = propagation_rtt;
}
@@ -117,11 +128,10 @@ bool RttBasedBackoff::IsRttAboveLimit() const {
return CorrectedRtt() > rtt_limit_;
}
int64_t RttBasedBackoff::CorrectedRtt() const {
TimeDelta RttBasedBackoff::CorrectedRtt() const {
// Avoid timeout when no packets are being sent.
int64_t timeout_correction =
std::max(last_packet_sent_ - last_propagation_rtt_update_,
static_cast<int64_t>(0));
TimeDelta timeout_correction = std::max(
last_packet_sent_ - last_propagation_rtt_update_, TimeDelta::Zero());
return timeout_correction + last_propagation_rtt_;
}
@@ -130,61 +140,61 @@ RttBasedBackoff::~RttBasedBackoff() = default;
SendSideBandwidthEstimation::SendSideBandwidthEstimation()
: lost_packets_since_last_loss_update_(0),
expected_packets_since_last_loss_update_(0),
current_target_(0),
last_logged_target_(0),
current_target_(DataRate::Zero()),
last_logged_target_(DataRate::Zero()),
min_bitrate_configured_(kCongestionControllerMinBitrate),
max_bitrate_configured_(kDefaultMaxBitrate),
last_low_bitrate_log_(INT64_T_MIN),
last_low_bitrate_log_(Timestamp::MinusInfinity()),
has_decreased_since_last_fraction_loss_(false),
last_loss_feedback_(INT64_T_MIN),
last_loss_packet_report_(INT64_T_MIN),
last_loss_feedback_(Timestamp::MinusInfinity()),
last_loss_packet_report_(Timestamp::MinusInfinity()),
last_fraction_loss_(0),
last_logged_fraction_loss_(0),
last_round_trip_time_(0),
receiver_limit_(INT64_T_MAX),
delay_based_limit_(INT64_T_MAX),
time_last_decrease_(INT64_T_MIN),
first_report_time_(INT64_T_MIN),
last_round_trip_time_(TimeDelta::Zero()),
receiver_limit_(DataRate::PlusInfinity()),
delay_based_limit_(DataRate::PlusInfinity()),
time_last_decrease_(Timestamp::MinusInfinity()),
first_report_time_(Timestamp::MinusInfinity()),
initially_lost_packets_(0),
bitrate_at_2_seconds_(0),
bitrate_at_2_seconds_(DataRate::Zero()),
uma_update_state_(kNoUpdate),
uma_rtt_state_(kNoUpdate),
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
last_rtc_event_log_(INT64_T_MIN),
low_loss_threshold_(kDefaultLowLossThreshold),
high_loss_threshold_(kDefaultHighLossThreshold),
bitrate_threshold_(kDefaultBitrateThreshold),
disable_receiver_limit_caps_only_(true) {}
disable_receiver_limit_caps_only_("Disabled") {
// rtt_backoff_ =
}
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
void SendSideBandwidthEstimation::OnRouteChange() {
lost_packets_since_last_loss_update_ = 0;
expected_packets_since_last_loss_update_ = 0;
current_target_ = 0;
current_target_ = DataRate::Zero();
min_bitrate_configured_ = kCongestionControllerMinBitrate;
max_bitrate_configured_ = kDefaultMaxBitrate;
last_low_bitrate_log_ = INT64_T_MIN;
last_low_bitrate_log_ = Timestamp::MinusInfinity();
has_decreased_since_last_fraction_loss_ = false;
last_loss_feedback_ = INT64_T_MIN;
last_loss_packet_report_ = INT64_T_MIN;
last_loss_feedback_ = Timestamp::MinusInfinity();
last_loss_packet_report_ = Timestamp::MinusInfinity();
last_fraction_loss_ = 0;
last_logged_fraction_loss_ = 0;
last_round_trip_time_ = 0;
receiver_limit_ = INT64_T_MAX;
delay_based_limit_ = INT64_T_MAX;
time_last_decrease_ = INT64_T_MIN;
first_report_time_ = INT64_T_MIN;
last_round_trip_time_ = TimeDelta::Zero();
receiver_limit_ = DataRate::PlusInfinity();
delay_based_limit_ = DataRate::PlusInfinity();
time_last_decrease_ = Timestamp::MinusInfinity();
first_report_time_ = Timestamp::MinusInfinity();
initially_lost_packets_ = 0;
bitrate_at_2_seconds_ = 0;
bitrate_at_2_seconds_ = DataRate::Zero();
uma_update_state_ = kNoUpdate;
uma_rtt_state_ = kNoUpdate;
last_rtc_event_log_ = INT64_T_MIN;
}
void SendSideBandwidthEstimation::SetBitrates(
std::optional<int64_t> send_bitrate, int64_t min_bitrate,
int64_t max_bitrate, int64_t at_time) {
std::optional<DataRate> send_bitrate, DataRate min_bitrate,
DataRate max_bitrate, Timestamp at_time) {
SetMinMaxBitrate(min_bitrate, max_bitrate);
if (send_bitrate) {
link_capacity_.OnStartingRate(*send_bitrate);
@@ -192,21 +202,21 @@ void SendSideBandwidthEstimation::SetBitrates(
}
}
void SendSideBandwidthEstimation::SetSendBitrate(int64_t bitrate,
int64_t at_time) {
void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate,
Timestamp at_time) {
// Reset to avoid being capped by the estimate.
delay_based_limit_ = INT64_T_MAX;
delay_based_limit_ = DataRate::PlusInfinity();
UpdateTargetBitrate(bitrate, at_time);
// Clear last sent bitrate history so the new value can be used directly
// and not capped.
min_bitrate_history_.clear();
}
void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
int64_t max_bitrate) {
void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
DataRate max_bitrate) {
min_bitrate_configured_ =
std::max(min_bitrate, kCongestionControllerMinBitrate);
if (max_bitrate > 0 && IsFinite(max_bitrate)) {
if (max_bitrate > DataRate::Zero() && max_bitrate.IsFinite()) {
max_bitrate_configured_ = std::max(min_bitrate_configured_, max_bitrate);
} else {
max_bitrate_configured_ = kDefaultMaxBitrate;
@@ -214,11 +224,11 @@ void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
}
int SendSideBandwidthEstimation::GetMinBitrate() const {
return min_bitrate_configured_;
return min_bitrate_configured_.bps<int>();
}
int64_t SendSideBandwidthEstimation::target_rate() const {
int64_t target = current_target_;
DataRate SendSideBandwidthEstimation::target_rate() const {
DataRate target = current_target_;
if (!disable_receiver_limit_caps_only_)
target = std::min(target, receiver_limit_);
return std::max(min_bitrate_configured_, target);
@@ -228,29 +238,29 @@ bool SendSideBandwidthEstimation::IsRttAboveLimit() const {
return rtt_backoff_.IsRttAboveLimit();
}
int64_t SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
return link_capacity_.estimate();
}
void SendSideBandwidthEstimation::UpdateReceiverEstimate(int64_t at_time,
int64_t bandwidth) {
void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time,
DataRate bandwidth) {
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
// limitation.
receiver_limit_ = !bandwidth ? INT64_T_MAX : bandwidth;
receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth;
ApplyTargetLimits(at_time);
}
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(int64_t at_time,
int64_t bitrate) {
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time,
DataRate bitrate) {
link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
// limitation.
delay_based_limit_ = !bitrate ? INT64_T_MAX : bitrate;
delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate;
ApplyTargetLimits(at_time);
}
void SendSideBandwidthEstimation::SetAcknowledgedRate(
std::optional<int64_t> acknowledged_rate, int64_t at_time) {
std::optional<DataRate> acknowledged_rate, Timestamp at_time) {
acknowledged_rate_ = acknowledged_rate;
if (!acknowledged_rate.has_value()) {
return;
@@ -259,11 +269,9 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate(
void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
int64_t number_of_packets,
int64_t at_time) {
Timestamp at_time) {
last_loss_feedback_ = at_time;
if (IsInfinite(first_report_time_)) {
first_report_time_ = at_time;
}
if (first_report_time_.IsInfinite()) first_report_time_ = at_time;
// Check sequence number diff and weight loss report
if (number_of_packets > 0) {
@@ -281,7 +289,7 @@ void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
has_decreased_since_last_fraction_loss_ = false;
int64_t lost_q8 =
std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
static_cast<int64_t>(0))
0)
<< 8;
last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255);
@@ -295,12 +303,13 @@ void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
UpdateUmaStatsPacketsLost(at_time, packets_lost);
}
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time,
int packets_lost) {
int64_t bitrate_kbps = (current_target_ + 500) / 1000;
DataRate bitrate_kbps =
DataRate::KilobitsPerSec((current_target_.bps() + 500) / 1000);
for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
if (!rampup_uma_stats_updated_[i] &&
bitrate_kbps >= kUmaRampupMetrics[i].bitrate_kbps) {
bitrate_kbps.kbps() >= kUmaRampupMetrics[i].bitrate_kbps) {
rampup_uma_stats_updated_[i] = true;
}
}
@@ -312,29 +321,29 @@ void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
} else if (uma_update_state_ == kFirstDone &&
at_time - first_report_time_ >= kBweConverganceTime) {
uma_update_state_ = kDone;
int bitrate_diff_kbps =
std::max(bitrate_at_2_seconds_ - bitrate_kbps, static_cast<int64_t>(0));
int bitrate_diff_kbps = std::max(
bitrate_at_2_seconds_.kbps<int>() - bitrate_kbps.kbps<int>(), 0);
}
}
void SendSideBandwidthEstimation::UpdateRtt(int64_t rtt, int64_t at_time) {
void SendSideBandwidthEstimation::UpdateRtt(TimeDelta rtt, Timestamp at_time) {
// Update RTT if we were able to compute an RTT based on this RTCP.
// FlexFEC doesn't send RTCP SR, which means we won't be able to compute RTT.
if (rtt > 0) last_round_trip_time_ = rtt;
if (rtt > TimeDelta::Zero()) last_round_trip_time_ = rtt;
if (!IsInStartPhase(at_time) && uma_rtt_state_ == kNoUpdate) {
uma_rtt_state_ = kDone;
}
}
void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
if (rtt_backoff_.IsRttAboveLimit()) {
if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
current_target_ > rtt_backoff_.bandwidth_floor_) {
time_last_decrease_ = at_time;
int64_t new_bitrate =
DataRate new_bitrate =
std::max(current_target_ * rtt_backoff_.drop_fraction_,
static_cast<double>(rtt_backoff_.bandwidth_floor_));
rtt_backoff_.bandwidth_floor_);
link_capacity_.OnRttBackoff(new_bitrate, at_time);
UpdateTargetBitrate(new_bitrate, at_time);
return;
@@ -347,15 +356,14 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
// We trust the REMB and/or delay-based estimate during the first 2 seconds if
// we haven't had any packet loss reported, to allow startup bitrate probing.
if (last_fraction_loss_ == 0 && IsInStartPhase(at_time)) {
int64_t new_bitrate = current_target_;
DataRate new_bitrate = current_target_;
// TODO(srte): We should not allow the new_bitrate to be larger than the
// receiver limit here.
if (IsFinite(receiver_limit_)) {
if (receiver_limit_.IsFinite())
new_bitrate = std::max(receiver_limit_, new_bitrate);
}
if (IsFinite(delay_based_limit_)) {
if (delay_based_limit_.IsFinite())
new_bitrate = std::max(delay_based_limit_, new_bitrate);
}
if (new_bitrate != current_target_) {
min_bitrate_history_.clear();
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
@@ -364,14 +372,14 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
}
}
UpdateMinHistory(at_time);
if (IsInfinite(last_loss_packet_report_)) {
if (last_loss_packet_report_.IsInfinite()) {
// No feedback received.
// TODO(srte): This is likely redundant in most cases.
ApplyTargetLimits(at_time);
return;
}
int64_t time_since_loss_packet_report = at_time - last_loss_packet_report_;
TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_;
if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) {
// We only care about loss above a given bitrate threshold.
float loss = last_fraction_loss_ / 256.0f;
@@ -389,12 +397,13 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
// If instead one would do: current_bitrate_ *= 1.08^(delta time),
// it would take over one second since the lower packet loss to achieve
// 108kbps.
int64_t new_bitrate = min_bitrate_history_.front().second * 1.08 + 0.5;
DataRate new_bitrate = DataRate::BitsPerSec(
min_bitrate_history_.front().second.bps() * 1.08 + 0.5);
// Add 1 kbps extra, just to make sure that we do not get stuck
// (gives a little extra increase at low rates, negligible at higher
// rates).
new_bitrate += 1000;
new_bitrate += DataRate::BitsPerSec(1000);
UpdateTargetBitrate(new_bitrate, at_time);
return;
} else if (current_target_ > bitrate_threshold_) {
@@ -411,10 +420,10 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
// Reduce rate:
// newRate = rate * (1 - 0.5*lossRate);
// where packetLoss = 256*lossRate;
int64_t new_bitrate =
(current_target_ *
DataRate new_bitrate = DataRate::BitsPerSec(
(current_target_.bps() *
static_cast<double>(512 - last_fraction_loss_)) /
512.0;
512.0);
has_decreased_since_last_fraction_loss_ = true;
UpdateTargetBitrate(new_bitrate, at_time);
return;
@@ -427,7 +436,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
}
void SendSideBandwidthEstimation::UpdatePropagationRtt(
int64_t at_time, int64_t propagation_rtt) {
Timestamp at_time, TimeDelta propagation_rtt) {
rtt_backoff_.UpdatePropagationRtt(at_time, propagation_rtt);
}
@@ -436,17 +445,17 @@ void SendSideBandwidthEstimation::OnSentPacket(const SentPacket& sent_packet) {
rtt_backoff_.last_packet_sent_ = sent_packet.send_time;
}
bool SendSideBandwidthEstimation::IsInStartPhase(int64_t at_time) const {
return (IsInfinite(first_report_time_)) ||
bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const {
return first_report_time_.IsInfinite() ||
at_time - first_report_time_ < kStartPhase;
}
void SendSideBandwidthEstimation::UpdateMinHistory(int64_t at_time) {
void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) {
// Remove old data points from history.
// Since history precision is in ms, add one so it is able to increase
// bitrate if it is off by as little as 0.5ms.
while (!min_bitrate_history_.empty() &&
at_time - min_bitrate_history_.front().first + 1 >
at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) >
kBweIncreaseInterval) {
min_bitrate_history_.pop_front();
}
@@ -461,45 +470,36 @@ void SendSideBandwidthEstimation::UpdateMinHistory(int64_t at_time) {
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
}
int64_t SendSideBandwidthEstimation::GetUpperLimit() const {
int64_t upper_limit = delay_based_limit_;
DataRate SendSideBandwidthEstimation::GetUpperLimit() const {
DataRate upper_limit = delay_based_limit_;
if (disable_receiver_limit_caps_only_)
upper_limit = std::min(upper_limit, receiver_limit_);
return std::min(upper_limit, max_bitrate_configured_);
}
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(int64_t bitrate,
int64_t at_time) {
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate,
Timestamp at_time) {
if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
LOG_WARN(
"Estimated available bandwidth {} is below configured min bitrate {}",
bitrate, min_bitrate_configured_);
ToString(bitrate), ToString(min_bitrate_configured_));
last_low_bitrate_log_ = at_time;
}
}
void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(int64_t at_time) {
if (current_target_ != last_logged_target_ ||
last_fraction_loss_ != last_logged_fraction_loss_ ||
at_time - last_rtc_event_log_ > kRtcEventLogPeriod) {
last_logged_fraction_loss_ = last_fraction_loss_;
last_logged_target_ = current_target_;
last_rtc_event_log_ = at_time;
}
}
void SendSideBandwidthEstimation::UpdateTargetBitrate(int64_t new_bitrate,
int64_t at_time) {
void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate,
Timestamp at_time) {
new_bitrate = std::min(new_bitrate, GetUpperLimit());
if (new_bitrate < min_bitrate_configured_) {
MaybeLogLowBitrateWarning(new_bitrate, at_time);
new_bitrate = min_bitrate_configured_;
}
current_target_ = new_bitrate;
MaybeLogLossBasedEvent(at_time);
link_capacity_.OnRateUpdate(acknowledged_rate_, current_target_, at_time);
}
void SendSideBandwidthEstimation::ApplyTargetLimits(int64_t at_time) {
void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) {
UpdateTargetBitrate(current_target_, at_time);
}
} // namespace webrtc

View File

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

View File

@@ -14,9 +14,9 @@
#include <tuple>
#include <vector>
#include "api/transport/network_types.h"
#include "congestion_control_feedback.h"
#include "network_route.h"
#include "network_types.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
struct PacketFeedback {

View File

@@ -16,7 +16,8 @@
#include <deque>
#include <memory>
#include "bandwidth_usage.h"
#include "api/transport/bandwidth_usage.h"
#include "delay_increase_detector_interface.h"
namespace webrtc {

View File

@@ -14,6 +14,8 @@
#include "api/units/timestamp.h"
static constexpr int kAbsSendTimeFraction = 18;
enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13
struct RTPHeader {