diff --git a/src/common/api/media_types.h b/src/common/api/media_types.h new file mode 100644 index 0000000..4691dfd --- /dev/null +++ b/src/common/api/media_types.h @@ -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 + +// 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_ diff --git a/src/common/api/network_state_predictor.h b/src/common/api/network_state_predictor.h new file mode 100644 index 0000000..d3063ce --- /dev/null +++ b/src/common/api/network_state_predictor.h @@ -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 +#include + +#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 + CreateNetworkStatePredictor() = 0; + virtual ~NetworkStatePredictorFactoryInterface() = default; +}; + +} // namespace webrtc + +#endif // API_NETWORK_STATE_PREDICTOR_H_ diff --git a/src/qos/bandwidth_usage.h b/src/common/api/transport/bandwidth_usage.h similarity index 100% rename from src/qos/bandwidth_usage.h rename to src/common/api/transport/bandwidth_usage.h diff --git a/src/common/api/transport/network_control.h b/src/common/api/transport/network_control.h new file mode 100644 index 0000000..06a0329 --- /dev/null +++ b/src/common/api/transport/network_control.h @@ -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 +#include + +#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 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 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 Create() = 0; + virtual ~NetworkStateEstimatorFactory() = default; +}; +} // namespace webrtc + +#endif // API_TRANSPORT_NETWORK_CONTROL_H_ diff --git a/src/common/network_types.h b/src/common/api/transport/network_types.h similarity index 100% rename from src/common/network_types.h rename to src/common/api/transport/network_types.h diff --git a/src/common/rtc_base/bitrate_tracker.cc b/src/common/rtc_base/bitrate_tracker.cc new file mode 100644 index 0000000..0146d3c --- /dev/null +++ b/src/common/rtc_base/bitrate_tracker.cc @@ -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 + +#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 BitrateTracker::Rate(Timestamp now) const { + if (std::optional 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 diff --git a/src/common/rtc_base/bitrate_tracker.h b/src/common/rtc_base/bitrate_tracker.h new file mode 100644 index 0000000..c6332b8 --- /dev/null +++ b/src/common/rtc_base/bitrate_tracker.h @@ -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 +#include + +#include + +#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 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_ diff --git a/src/common/rtc_base/rate_statistics.cc b/src/common/rtc_base/rate_statistics.cc new file mode 100644 index 0000000..2bb8cb5 --- /dev/null +++ b/src/common/rtc_base/rate_statistics.cc @@ -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 +#include +#include + +#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::max() - accumulated_count_ > count) { + accumulated_count_ += count; + } else { + overflow_ = true; + } + ++num_samples_; +} + +std::optional 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(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(scale_) / active_window_size; + float result = accumulated_count_ * scale + 0.5f; + + // Better return unavailable rate than garbage value (undefined behavior). + if (result > static_cast(std::numeric_limits::max())) { + return std::nullopt; + } + return rtc::dchecked_cast(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 diff --git a/src/common/rtc_base/rate_statistics.h b/src/common/rtc_base/rate_statistics.h new file mode 100644 index 0000000..3eefb27 --- /dev/null +++ b/src/common/rtc_base/rate_statistics.h @@ -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 +#include + +#include +#include +#include + +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 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 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_ diff --git a/src/common/rtc_base/thread_annotations.h b/src/common/rtc_base/thread_annotations.h new file mode 100644 index 0000000..689f668 --- /dev/null +++ b/src/common/rtc_base/thread_annotations.h @@ -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_ diff --git a/src/qos/acknowledged_bitrate_estimator.cc b/src/qos/acknowledged_bitrate_estimator.cc index 8287131..e69b1a9 100644 --- a/src/qos/acknowledged_bitrate_estimator.cc +++ b/src/qos/acknowledged_bitrate_estimator.cc @@ -16,10 +16,10 @@ #include #include +#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 { diff --git a/src/qos/acknowledged_bitrate_estimator.h b/src/qos/acknowledged_bitrate_estimator.h index b349534..3b6122b 100644 --- a/src/qos/acknowledged_bitrate_estimator.h +++ b/src/qos/acknowledged_bitrate_estimator.h @@ -15,10 +15,10 @@ #include #include +#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 { diff --git a/src/qos/acknowledged_bitrate_estimator_interface.h b/src/qos/acknowledged_bitrate_estimator_interface.h new file mode 100644 index 0000000..c847d7a --- /dev/null +++ b/src/qos/acknowledged_bitrate_estimator_interface.h @@ -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 +#include +#include + +#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 Create(); + virtual ~AcknowledgedBitrateEstimatorInterface(); + + virtual void IncomingPacketFeedbackVector( + const std::vector& packet_feedback_vector) = 0; + virtual std::optional bitrate() const = 0; + virtual std::optional 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_ diff --git a/src/qos/aimd_rate_control.cc b/src/qos/aimd_rate_control.cc index af8bca3..31a067a 100644 --- a/src/qos/aimd_rate_control.cc +++ b/src/qos/aimd_rate_control.cc @@ -17,9 +17,9 @@ #include #include +#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" diff --git a/src/qos/aimd_rate_control.h b/src/qos/aimd_rate_control.h index 5bd8d0f..6c95640 100644 --- a/src/qos/aimd_rate_control.h +++ b/src/qos/aimd_rate_control.h @@ -15,11 +15,11 @@ #include +#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; diff --git a/src/qos/bwe_defines.h b/src/qos/bwe_defines.h index 7326cd6..e3f4e8c 100644 --- a/src/qos/bwe_defines.h +++ b/src/qos/bwe_defines.h @@ -15,9 +15,9 @@ #include +#include "api/transport/bandwidth_usage.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" -#include "bandwidth_usage.h" namespace webrtc { diff --git a/src/qos/clock.cc b/src/qos/clock.cc new file mode 100644 index 0000000..7775790 --- /dev/null +++ b/src/qos/clock.cc @@ -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(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( + (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 diff --git a/src/qos/clock.h b/src/qos/clock.h new file mode 100644 index 0000000..9946d65 --- /dev/null +++ b/src/qos/clock.h @@ -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 + +#include +#include + +#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 time_us_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_ diff --git a/src/qos/congestion_control.cpp b/src/qos/congestion_control.cpp index 4aaae88..8c9d93a 100644 --- a/src/qos/congestion_control.cpp +++ b/src/qos/congestion_control.cpp @@ -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::min(); - int64_t min_propagation_rtt = std::numeric_limits::max(); - int64_t max_recv_time = std::numeric_limits::min(); + TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity(); + TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity(); + Timestamp max_recv_time = Timestamp::MinusInfinity(); std::vector 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::min() && - min_propagation_rtt != std::numeric_limits::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(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::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::max() && - feedback_min_rtt != std::numeric_limits::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 probe_bitrate = + if (network_estimator_) { + network_estimator_->OnTransportPacketsFeedback(report); + SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate()); + } + std::optional 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); diff --git a/src/qos/congestion_control.h b/src/qos/congestion_control.h index f1a2641..7431c26 100644 --- a/src/qos/congestion_control.h +++ b/src/qos/congestion_control.h @@ -5,12 +5,18 @@ #include #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 - 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 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::min(); - const bool packet_feedback_only_ = false; - bool previously_in_alr_ = false; - const bool limit_probes_lower_than_throughput_estimate_ = false; - std::optional current_data_window_; + const std::unique_ptr probe_controller_; - private: std::unique_ptr bandwidth_estimation_; std::unique_ptr alr_detector_; - std::unique_ptr acknowledged_bitrate_estimator_; + std::unique_ptr probe_bitrate_estimator_; + std::unique_ptr network_estimator_; + std::unique_ptr network_state_predictor_; std::unique_ptr delay_based_bwe_; + std::unique_ptr + acknowledged_bitrate_estimator_; + + std::optional initial_config_; + + DataRate min_target_rate_ = DataRate::Zero(); + DataRate min_data_rate_ = DataRate::Zero(); + DataRate max_data_rate_ = DataRate::PlusInfinity(); + std::optional starting_rate_; + + bool first_packet_sent_ = false; + + std::optional 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 feedback_max_rtts_; + + DataRate last_loss_based_target_rate_; + DataRate last_pushback_target_rate_; + DataRate last_stable_target_rate_; + + std::optional 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 current_data_window_; }; #endif \ No newline at end of file diff --git a/src/qos/congestion_control_feedback_generator.cc b/src/qos/congestion_control_feedback_generator.cc new file mode 100644 index 0000000..e6c601a --- /dev/null +++ b/src/qos/congestion_control_feedback_generator.cc @@ -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 +#include +#include +#include +#include +#include +#include + +#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 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::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::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_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( + std::move(rtcp_packet_info), compact_ntp); + CalculateNextPossibleSendTime(DataSize::Bytes(feedback->BlockLength()), now); + + std::vector> 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 diff --git a/src/qos/congestion_control_feedback_generator.cpp b/src/qos/congestion_control_feedback_generator.cpp deleted file mode 100644 index 4c2047f..0000000 --- a/src/qos/congestion_control_feedback_generator.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "congestion_control_feedback_generator.h" - -#include -#include -#include -#include -#include -#include - -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(seconds & 0xFFFF); - uint16_t ntp_fraction = static_cast((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( - 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( - 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 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( - std::move(rtcp_packet_info), compact_ntp); - CalculateNextPossibleSendTime(feedback->BlockLength(), now_ms); - - std::vector> 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::max() - : send_rate_debt_ / max_feedback_rate_, - min_time_between_feedback_, max_time_between_feedback_); -} diff --git a/src/qos/congestion_control_feedback_generator.h b/src/qos/congestion_control_feedback_generator.h index af43ae6..2e87377 100644 --- a/src/qos/congestion_control_feedback_generator.h +++ b/src/qos/congestion_control_feedback_generator.h @@ -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 +#include +#include #include -#include +#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 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 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 feedback_trackers_; - std::optional first_arrival_time_since_feedback_; - int64_t next_possible_feedback_send_time_ = 0; - int64_t last_feedback_sent_time_ = 0; - + // std::vector packets_; + Timestamp last_feedback_sent_time_ = Timestamp::Zero(); + std::optional first_arrival_time_since_feedback_; bool marker_bit_seen_ = false; + Timestamp next_possible_feedback_send_time_ = Timestamp::Zero(); }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_ diff --git a/src/qos/congestion_control_feedback_tracker.cc b/src/qos/congestion_control_feedback_tracker.cc index 749c546..d3735f7 100644 --- a/src/qos/congestion_control_feedback_tracker.cc +++ b/src/qos/congestion_control_feedback_tracker.cc @@ -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(sequence_number), - arrival_time_offset, ecn); + packet_feedback.push_back({ssrc, static_cast(sequence_number), + arrival_time_offset, ecn}); } last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number; packets_.clear(); diff --git a/src/qos/congestion_window_pushback_controller.cpp b/src/qos/congestion_window_pushback_controller.cc similarity index 76% rename from src/qos/congestion_window_pushback_controller.cpp rename to src/qos/congestion_window_pushback_controller.cc index da46937..f78099a 100644 --- a/src/qos/congestion_window_pushback_controller.cpp +++ b/src/qos/congestion_window_pushback_controller.cc @@ -13,7 +13,17 @@ #include #include -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(current_data_window_.value()); + total_bytes / static_cast(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 diff --git a/src/qos/congestion_window_pushback_controller.h b/src/qos/congestion_window_pushback_controller.h index b4de439..7825e41 100644 --- a/src/qos/congestion_window_pushback_controller.h +++ b/src/qos/congestion_window_pushback_controller.h @@ -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 #include +#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 current_data_window_ = std::nullopt; + const bool add_pacing_; + const uint32_t min_pushback_target_bitrate_bps_; + std::optional current_data_window_; int64_t outstanding_bytes_ = 0; int64_t pacing_bytes_ = 0; double encoding_rate_ratio_ = 1.0; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_ diff --git a/src/qos/delay_based_bwe.cc b/src/qos/delay_based_bwe.cc index dc5a2af..0c2146a 100644 --- a/src/qos/delay_based_bwe.cc +++ b/src/qos/delay_based_bwe.cc @@ -17,6 +17,7 @@ #include #include +#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 { diff --git a/src/qos/delay_based_bwe.h b/src/qos/delay_based_bwe.h index 4a292bf..ac5db64 100644 --- a/src/qos/delay_based_bwe.h +++ b/src/qos/delay_based_bwe.h @@ -18,15 +18,15 @@ #include #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 { diff --git a/src/qos/delay_increase_detector_interface.h b/src/qos/delay_increase_detector_interface.h index d305426..3e1d5f6 100644 --- a/src/qos/delay_increase_detector_interface.h +++ b/src/qos/delay_increase_detector_interface.h @@ -14,7 +14,7 @@ #include -#include "bandwidth_usage.h" +#include "api/transport/bandwidth_usage.h" namespace webrtc { diff --git a/src/qos/module_common_types.h b/src/qos/module_common_types.h new file mode 100644 index 0000000..8e4e546 --- /dev/null +++ b/src/qos/module_common_types.h @@ -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 + +#include + +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& 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_ diff --git a/src/qos/ntp_time.h b/src/qos/ntp_time.h new file mode 100644 index 0000000..25f6229 --- /dev/null +++ b/src/qos/ntp_time.h @@ -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 +#include +#include + +#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(fractions()) / kNtpFracPerMs; + return 1000 * static_cast(seconds()) + + static_cast(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(value_ / kFractionsPerSecond); + } + uint32_t fractions() const { + return rtc::dchecked_cast(value_ % kFractionsPerSecond); + } + + private: + uint64_t value_; +}; + +inline bool operator==(const NtpTime& n1, const NtpTime& n2) { + return static_cast(n1) == static_cast(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::max() triggers + // -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit + // cast + if (result <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + + if (result >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + + return rtc::dchecked_cast(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::max() triggers + // -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit + // cast + if (result <= static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + + if (result >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + + return rtc::dchecked_cast(result); +} + +// Converts Q32.32-formatted fixed-point seconds to `int64_t` milliseconds. +inline int64_t Q32x32ToInt64Ms(int64_t q32x32) { + return rtc::dchecked_cast( + 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( + 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( + 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( + std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond))); +} + +} // namespace webrtc +#endif // SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_ diff --git a/src/qos/ntp_time_util.h b/src/qos/ntp_time_util.h new file mode 100644 index 0000000..4305964 --- /dev/null +++ b/src/qos/ntp_time_util.h @@ -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 + +#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(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_ diff --git a/src/qos/overuse_detector.h b/src/qos/overuse_detector.h index 726a12f..077b5f8 100644 --- a/src/qos/overuse_detector.h +++ b/src/qos/overuse_detector.h @@ -12,7 +12,7 @@ #include -#include "bandwidth_usage.h" +#include "api/transport/bandwidth_usage.h" namespace webrtc { diff --git a/src/qos/overuse_estimator.cc b/src/qos/overuse_estimator.cc new file mode 100644 index 0000000..5576d38 --- /dev/null +++ b/src/qos/overuse_estimator.cc @@ -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 +#include + +#include + +#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 diff --git a/src/qos/overuse_estimator.h b/src/qos/overuse_estimator.h new file mode 100644 index 0000000..edde6e1 --- /dev/null +++ b/src/qos/overuse_estimator.h @@ -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 + +#include + +#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 ts_delta_hist_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ diff --git a/src/qos/probe_bitrate_estimator.cc b/src/qos/probe_bitrate_estimator.cc index 6161ac8..d6d2c97 100644 --- a/src/qos/probe_bitrate_estimator.cc +++ b/src/qos/probe_bitrate_estimator.cc @@ -14,12 +14,12 @@ #include #include +#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 { diff --git a/src/qos/probe_bitrate_estimator.h b/src/qos/probe_bitrate_estimator.h index 1218510..a6d8861 100644 --- a/src/qos/probe_bitrate_estimator.h +++ b/src/qos/probe_bitrate_estimator.h @@ -14,10 +14,10 @@ #include #include +#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; diff --git a/src/qos/probe_controller.cc b/src/qos/probe_controller.cc new file mode 100644 index 0000000..10408fc --- /dev/null +++ b/src/qos/probe_controller.cc @@ -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 +#include +#include +#include +#include +#include + +#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 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(); +} + +std::vector 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(); + + 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 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(); +} + +std::vector 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(); +} + +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 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 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 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 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 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(); +} + +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 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 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 ProbeController::InitiateProbing( + Timestamp now, std::vector 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(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 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 diff --git a/src/qos/probe_controller.h b/src/qos/probe_controller.h new file mode 100644 index 0000000..b4602e8 --- /dev/null +++ b/src/qos/probe_controller.h @@ -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 + +#include +#include + +#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 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 OnMaxTotalAllocatedBitrate( + DataRate max_total_allocated_bitrate, Timestamp at_time); + + std::vector OnNetworkAvailability( + NetworkAvailability msg); + + std::vector 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 alr_start_time); + void SetAlrEndedTimeMs(int64_t alr_end_time); + + std::vector 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 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 InitiateExponentialProbing(Timestamp at_time); + std::vector InitiateProbing( + Timestamp now, std::vector 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 network_estimate_; + DataRate start_bitrate_ = DataRate::Zero(); + DataRate max_bitrate_ = DataRate::PlusInfinity(); + Timestamp last_bwe_drop_probing_time_ = Timestamp::Zero(); + std::optional alr_start_time_; + std::optional 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_ diff --git a/src/qos/receive_side_congestion_controller.cc b/src/qos/receive_side_congestion_controller.cc new file mode 100644 index 0000000..6a14db0 --- /dev/null +++ b/src/qos/receive_side_congestion_controller.cc @@ -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 +#include +#include +#include + +#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 lock(mutex_); + rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms); +} + +void ReceiveSideCongestionController::RemoveStream(uint32_t ssrc) { + std::lock_guard lock(mutex_); + rbe_->RemoveStream(ssrc); +} + +DataRate ReceiveSideCongestionController::LatestReceiveSideEstimate() const { + std::lock_guard 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( + clock_, &remb_throttler_); + } + } +} + +ReceiveSideCongestionController::ReceiveSideCongestionController( + std::shared_ptr clock, + RtpTransportFeedbackGenerator::RtcpSender feedback_sender, + RembThrottler::RembSender remb_sender, + std::shared_ptr network_state_estimator) + : clock_(clock), + remb_throttler_(std::move(remb_sender), clock.get()), + congestion_control_feedback_generator_(clock, feedback_sender), + rbe_(std::make_unique( + 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 diff --git a/src/qos/receive_side_congestion_controller.cpp b/src/qos/receive_side_congestion_controller.cpp deleted file mode 100644 index 644abb5..0000000 --- a/src/qos/receive_side_congestion_controller.cpp +++ /dev/null @@ -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 -#include -#include -#include - -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( - 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); -} diff --git a/src/qos/receive_side_congestion_controller.h b/src/qos/receive_side_congestion_controller.h index b242c01..5e7997e 100644 --- a/src/qos/receive_side_congestion_controller.h +++ b/src/qos/receive_side_congestion_controller.h @@ -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 +#include #include +#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 clock, + RtpTransportFeedbackGenerator::RtcpSender feedback_sender, + RembThrottler::RembSender remb_sender, + std::shared_ptr 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 clock_; + RembThrottler remb_throttler_; CongestionControlFeedbackGenerator congestion_control_feedback_generator_; - std::mutex mutex_; + mutable std::mutex mutex_; + std::unique_ptr rbe_; bool using_absolute_send_time_; uint32_t packets_since_absolute_send_time_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_ diff --git a/src/qos/remb_throttler.cc b/src/qos/remb_throttler.cc new file mode 100644 index 0000000..0a763d6 --- /dev/null +++ b/src/qos/remb_throttler.cc @@ -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 +#include // Include the mutex header +#include + +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& ssrcs, + uint32_t bitrate_bps) { + DataRate receive_bitrate = DataRate::BitsPerSec(bitrate_bps); + Timestamp now = clock_->CurrentTime(); + { + std::lock_guard 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 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 diff --git a/src/qos/remb_throttler.h b/src/qos/remb_throttler.h new file mode 100644 index 0000000..4455077 --- /dev/null +++ b/src/qos/remb_throttler.h @@ -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 +#include +#include + +#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 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& 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_ diff --git a/src/qos/remote_bitrate_estimator.h b/src/qos/remote_bitrate_estimator.h new file mode 100644 index 0000000..7def2f5 --- /dev/null +++ b/src/qos/remote_bitrate_estimator.h @@ -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 +#include + +#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& 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_ diff --git a/src/qos/remote_bitrate_estimator_single_stream.cc b/src/qos/remote_bitrate_estimator_single_stream.cc new file mode 100644 index 0000000..e0c2620 --- /dev/null +++ b/src/qos/remote_bitrate_estimator_single_stream.cc @@ -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 +#include +#include + +#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 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 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 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, + ×tamp_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 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(); + 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 RemoteBitrateEstimatorSingleStream::GetSsrcs() const { + std::vector ssrcs; + ssrcs.reserve(overuse_detectors_.size()); + for (const auto& [ssrc, unused] : overuse_detectors_) { + ssrcs.push_back(ssrc); + } + return ssrcs; +} + +} // namespace webrtc diff --git a/src/qos/remote_bitrate_estimator_single_stream.h b/src/qos/remote_bitrate_estimator_single_stream.h new file mode 100644 index 0000000..c22b0f3 --- /dev/null +++ b/src/qos/remote_bitrate_estimator_single_stream.h @@ -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 +#include + +#include +#include +#include + +#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 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 GetSsrcs() const; + + std::shared_ptr clock_; + RemoteBitrateObserver* observer_; + std::map overuse_detectors_; + BitrateTracker incoming_bitrate_; + DataRate last_valid_incoming_bitrate_; + AimdRateControl remote_rate_; + std::optional last_process_time_; + TimeDelta process_interval_; + bool uma_recorded_; +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_ diff --git a/src/qos/rtp_transport_feedback_generator.h b/src/qos/rtp_transport_feedback_generator.h new file mode 100644 index 0000000..fa9d3c7 --- /dev/null +++ b/src/qos/rtp_transport_feedback_generator.h @@ -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 +#include + +#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> 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_ diff --git a/src/qos/send_side_bandwidth_estimation.cpp b/src/qos/send_side_bandwidth_estimation.cc similarity index 62% rename from src/qos/send_side_bandwidth_estimation.cpp rename to src/qos/send_side_bandwidth_estimation.cc index 8974225..06b6766 100644 --- a/src/qos/send_side_bandwidth_estimation.cpp +++ b/src/qos/send_side_bandwidth_estimation.cc @@ -20,25 +20,30 @@ #include #include +#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(delay_based_bitrate)); + capacity_estimate_bps_ = + std::min(capacity_estimate_bps_, delay_based_bitrate.bps()); 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(); } -void LinkCapacityTracker::OnRateUpdate(std::optional acknowledged, - int64_t target, int64_t at_time) { +void LinkCapacityTracker::OnRateUpdate(std::optional 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(); } 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(backoff_rate)); + std::min(capacity_estimate_bps_, backoff_rate.bps()); 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(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 send_bitrate, int64_t min_bitrate, - int64_t max_bitrate, int64_t at_time) { + std::optional 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(); } -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 acknowledged_rate, int64_t at_time) { + std::optional 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(lost_packets_since_last_loss_update_ + packets_lost, - static_cast(0)) + 0) << 8; last_fraction_loss_ = std::min(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(0)); + int bitrate_diff_kbps = std::max( + bitrate_at_2_seconds_.kbps() - bitrate_kbps.kbps(), 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(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(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 diff --git a/src/qos/send_side_bandwidth_estimation.h b/src/qos/send_side_bandwidth_estimation.h index 51829b4..c3e3845 100644 --- a/src/qos/send_side_bandwidth_estimation.h +++ b/src/qos/send_side_bandwidth_estimation.h @@ -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 -#include -#include #include -#include #include #include #include #include -#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 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 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 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 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 acknowledged_rate, - int64_t at_time); + void SetAcknowledgedRate(std::optional 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 > min_bitrate_history_; + std::deque > min_bitrate_history_; // incoming filters int lost_packets_since_last_loss_update_; int expected_packets_since_last_loss_update_; - std::optional 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 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 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 \ No newline at end of file +} // namespace webrtc +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ diff --git a/src/qos/transport_feedback_adapter.h b/src/qos/transport_feedback_adapter.h index 52b9d53..6510625 100644 --- a/src/qos/transport_feedback_adapter.h +++ b/src/qos/transport_feedback_adapter.h @@ -14,9 +14,9 @@ #include #include +#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 { diff --git a/src/qos/trendline_estimator.h b/src/qos/trendline_estimator.h index 79081e8..25ff5b5 100644 --- a/src/qos/trendline_estimator.h +++ b/src/qos/trendline_estimator.h @@ -16,7 +16,8 @@ #include #include -#include "bandwidth_usage.h" +#include "api/transport/bandwidth_usage.h" +#include "delay_increase_detector_interface.h" namespace webrtc { diff --git a/src/rtp/rtp_packet/rtp_header.h b/src/rtp/rtp_packet/rtp_header.h index 7358227..3de3ce7 100644 --- a/src/rtp/rtp_packet/rtp_header.h +++ b/src/rtp/rtp_packet/rtp_header.h @@ -14,6 +14,8 @@ #include "api/units/timestamp.h" +static constexpr int kAbsSendTimeFraction = 18; + enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13 struct RTPHeader {