mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
[fix] fix congestion control module
This commit is contained in:
42
src/common/api/media_types.h
Normal file
42
src/common/api/media_types.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_MEDIA_TYPES_H_
|
||||
#define API_MEDIA_TYPES_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
// The cricket and webrtc have separate definitions for what a media type is.
|
||||
// They're not compatible. Watch out for this.
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum MediaType {
|
||||
MEDIA_TYPE_AUDIO,
|
||||
MEDIA_TYPE_VIDEO,
|
||||
MEDIA_TYPE_DATA,
|
||||
MEDIA_TYPE_UNSUPPORTED
|
||||
};
|
||||
|
||||
extern const char kMediaTypeAudio[];
|
||||
extern const char kMediaTypeVideo[];
|
||||
extern const char kMediaTypeData[];
|
||||
|
||||
std::string MediaTypeToString(MediaType type);
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum class MediaType { ANY, AUDIO, VIDEO, DATA };
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_MEDIA_TYPES_H_
|
||||
51
src/common/api/network_state_predictor.h
Normal file
51
src/common/api/network_state_predictor.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_NETWORK_STATE_PREDICTOR_H_
|
||||
#define API_NETWORK_STATE_PREDICTOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(yinwa): work in progress. API in class NetworkStatePredictor should not
|
||||
// be used by other users until this comment is removed.
|
||||
|
||||
// NetworkStatePredictor predict network state based on current network metrics.
|
||||
// Usage:
|
||||
// Setup by calling Initialize.
|
||||
// For each update, call Update. Update returns network state
|
||||
// prediction.
|
||||
class NetworkStatePredictor {
|
||||
public:
|
||||
virtual ~NetworkStatePredictor() {}
|
||||
|
||||
// Returns current network state prediction.
|
||||
// Inputs: send_time_ms - packet send time.
|
||||
// arrival_time_ms - packet arrival time.
|
||||
// network_state - computed network state.
|
||||
virtual BandwidthUsage Update(int64_t send_time_ms,
|
||||
int64_t arrival_time_ms,
|
||||
BandwidthUsage network_state) = 0;
|
||||
};
|
||||
|
||||
class NetworkStatePredictorFactoryInterface {
|
||||
public:
|
||||
virtual std::unique_ptr<NetworkStatePredictor>
|
||||
CreateNetworkStatePredictor() = 0;
|
||||
virtual ~NetworkStatePredictorFactoryInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_NETWORK_STATE_PREDICTOR_H_
|
||||
124
src/common/api/transport/network_control.h
Normal file
124
src/common/api/transport/network_control.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_TRANSPORT_NETWORK_CONTROL_H_
|
||||
#define API_TRANSPORT_NETWORK_CONTROL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class TargetTransferRateObserver {
|
||||
public:
|
||||
virtual ~TargetTransferRateObserver() = default;
|
||||
// Called to indicate target transfer rate as well as giving information about
|
||||
// the current estimate of network parameters.
|
||||
virtual void OnTargetTransferRate(TargetTransferRate) = 0;
|
||||
// Called to provide updates to the expected target rate in case it changes
|
||||
// before the first call to OnTargetTransferRate.
|
||||
virtual void OnStartRateUpdate(DataRate) {}
|
||||
};
|
||||
|
||||
// Configuration sent to factory create function. The parameters here are
|
||||
// optional to use for a network controller implementation.
|
||||
struct NetworkControllerConfig {
|
||||
explicit NetworkControllerConfig() {}
|
||||
|
||||
// The initial constraints to start with, these can be changed at any later
|
||||
// time by calls to OnTargetRateConstraints. Note that the starting rate
|
||||
// has to be set initially to provide a starting state for the network
|
||||
// controller, even though the field is marked as optional.
|
||||
TargetRateConstraints constraints;
|
||||
// Initial stream specific configuration, these are changed at any later time
|
||||
// by calls to OnStreamsConfig.
|
||||
StreamsConfig stream_based_config;
|
||||
};
|
||||
|
||||
// NetworkControllerInterface is implemented by network controllers. A network
|
||||
// controller is a class that uses information about network state and traffic
|
||||
// to estimate network parameters such as round trip time and bandwidth. Network
|
||||
// controllers does not guarantee thread safety, the interface must be used in a
|
||||
// non-concurrent fashion.
|
||||
class NetworkControllerInterface {
|
||||
public:
|
||||
virtual ~NetworkControllerInterface() = default;
|
||||
|
||||
// Called when network availabilty changes.
|
||||
virtual NetworkControlUpdate OnNetworkAvailability(NetworkAvailability) = 0;
|
||||
// Called when the receiving or sending endpoint changes address.
|
||||
virtual NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange) = 0;
|
||||
// Called periodically with a periodicy as specified by
|
||||
// NetworkControllerFactoryInterface::GetProcessInterval.
|
||||
virtual NetworkControlUpdate OnProcessInterval(ProcessInterval) = 0;
|
||||
// Called when remotely calculated bitrate is received.
|
||||
virtual NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport) = 0;
|
||||
// Called round trip time has been calculated by protocol specific mechanisms.
|
||||
virtual NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate) = 0;
|
||||
// Called when a packet is sent on the network.
|
||||
virtual NetworkControlUpdate OnSentPacket(SentPacket) = 0;
|
||||
// Called when a packet is received from the remote client.
|
||||
virtual NetworkControlUpdate OnReceivedPacket(ReceivedPacket) = 0;
|
||||
// Called when the stream specific configuration has been updated.
|
||||
virtual NetworkControlUpdate OnStreamsConfig(StreamsConfig) = 0;
|
||||
// Called when target transfer rate constraints has been changed.
|
||||
virtual NetworkControlUpdate OnTargetRateConstraints(
|
||||
TargetRateConstraints) = 0;
|
||||
// Called when a protocol specific calculation of packet loss has been made.
|
||||
virtual NetworkControlUpdate OnTransportLossReport(TransportLossReport) = 0;
|
||||
// Called with per packet feedback regarding receive time.
|
||||
virtual NetworkControlUpdate OnTransportPacketsFeedback(
|
||||
TransportPacketsFeedback) = 0;
|
||||
// Called with network state estimate updates.
|
||||
virtual NetworkControlUpdate OnNetworkStateEstimate(NetworkStateEstimate) = 0;
|
||||
};
|
||||
|
||||
// NetworkControllerFactoryInterface is an interface for creating a network
|
||||
// controller.
|
||||
class NetworkControllerFactoryInterface {
|
||||
public:
|
||||
virtual ~NetworkControllerFactoryInterface() = default;
|
||||
|
||||
// Used to create a new network controller, requires an observer to be
|
||||
// provided to handle callbacks.
|
||||
virtual std::unique_ptr<NetworkControllerInterface> Create(
|
||||
NetworkControllerConfig config) = 0;
|
||||
// Returns the interval by which the network controller expects
|
||||
// OnProcessInterval calls.
|
||||
virtual TimeDelta GetProcessInterval() const = 0;
|
||||
};
|
||||
|
||||
// Under development, subject to change without notice.
|
||||
class NetworkStateEstimator {
|
||||
public:
|
||||
// Gets the current best estimate according to the estimator.
|
||||
virtual std::optional<NetworkStateEstimate> GetCurrentEstimate() = 0;
|
||||
// Called with per packet feedback regarding receive time.
|
||||
// Used when the NetworkStateEstimator runs in the sending endpoint.
|
||||
virtual void OnTransportPacketsFeedback(const TransportPacketsFeedback&) = 0;
|
||||
// Called with per packet feedback regarding receive time.
|
||||
// Used when the NetworkStateEstimator runs in the receiving endpoint.
|
||||
virtual void OnReceivedPacket(const PacketResult&) {}
|
||||
// Called when the receiving or sending endpoint changes address.
|
||||
virtual void OnRouteChange(const NetworkRouteChange&) = 0;
|
||||
virtual ~NetworkStateEstimator() = default;
|
||||
};
|
||||
class NetworkStateEstimatorFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<NetworkStateEstimator> Create() = 0;
|
||||
virtual ~NetworkStateEstimatorFactory() = default;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_TRANSPORT_NETWORK_CONTROL_H_
|
||||
39
src/common/rtc_base/bitrate_tracker.cc
Normal file
39
src/common/rtc_base/bitrate_tracker.cc
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/bitrate_tracker.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/rate_statistics.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
BitrateTracker::BitrateTracker(TimeDelta max_window_size)
|
||||
: impl_(max_window_size.ms(), RateStatistics::kBpsScale) {}
|
||||
|
||||
std::optional<DataRate> BitrateTracker::Rate(Timestamp now) const {
|
||||
if (std::optional<int64_t> rate = impl_.Rate(now.ms())) {
|
||||
return DataRate::BitsPerSec(*rate);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool BitrateTracker::SetWindowSize(TimeDelta window_size, Timestamp now) {
|
||||
return impl_.SetWindowSize(window_size.ms(), now.ms());
|
||||
}
|
||||
|
||||
void BitrateTracker::Update(int64_t bytes, Timestamp now) {
|
||||
impl_.Update(bytes, now.ms());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
64
src/common/rtc_base/bitrate_tracker.h
Normal file
64
src/common/rtc_base/bitrate_tracker.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_BITRATE_TRACKER_H_
|
||||
#define RTC_BASE_BITRATE_TRACKER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/rate_statistics.h"
|
||||
|
||||
namespace webrtc {
|
||||
// Class to estimate bitrates over running window.
|
||||
// Timestamps used in Update(), Rate() and SetWindowSize() must never
|
||||
// decrease for two consecutive calls.
|
||||
// This class is thread unsafe.
|
||||
class BitrateTracker {
|
||||
public:
|
||||
// max_window_sizes = Maximum window size for the rate estimation.
|
||||
// Initial window size is set to this, but may be changed
|
||||
// to something lower by calling SetWindowSize().
|
||||
explicit BitrateTracker(TimeDelta max_window_size);
|
||||
|
||||
BitrateTracker(const BitrateTracker&) = default;
|
||||
BitrateTracker(BitrateTracker&&) = default;
|
||||
BitrateTracker& operator=(const BitrateTracker&) = delete;
|
||||
BitrateTracker& operator=(BitrateTracker&&) = delete;
|
||||
|
||||
~BitrateTracker() = default;
|
||||
|
||||
// Resets instance to original state.
|
||||
void Reset() { impl_.Reset(); }
|
||||
|
||||
// Updates bitrate with a new data point, moving averaging window as needed.
|
||||
void Update(int64_t bytes, Timestamp now);
|
||||
void Update(DataSize size, Timestamp now) { Update(size.bytes(), now); }
|
||||
|
||||
// Returns bitrate, moving averaging window as needed.
|
||||
// Returns nullopt when bitrate can't be measured.
|
||||
std::optional<DataRate> Rate(Timestamp now) const;
|
||||
|
||||
// Update the size of the averaging window. The maximum allowed value for
|
||||
// `window_size` is `max_window_size` as supplied in the constructor.
|
||||
bool SetWindowSize(TimeDelta window_size, Timestamp now);
|
||||
|
||||
private:
|
||||
RateStatistics impl_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_BITRATE_TRACKER_H_
|
||||
160
src/common/rtc_base/rate_statistics.cc
Normal file
160
src/common/rtc_base/rate_statistics.cc
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/rate_statistics.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include "log.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
RateStatistics::Bucket::Bucket(int64_t timestamp)
|
||||
: sum(0), num_samples(0), timestamp(timestamp) {}
|
||||
|
||||
RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
|
||||
: accumulated_count_(0),
|
||||
first_timestamp_(-1),
|
||||
num_samples_(0),
|
||||
scale_(scale),
|
||||
max_window_size_ms_(window_size_ms),
|
||||
current_window_size_ms_(max_window_size_ms_) {}
|
||||
|
||||
RateStatistics::RateStatistics(const RateStatistics& other)
|
||||
: buckets_(other.buckets_),
|
||||
accumulated_count_(other.accumulated_count_),
|
||||
first_timestamp_(other.first_timestamp_),
|
||||
overflow_(other.overflow_),
|
||||
num_samples_(other.num_samples_),
|
||||
scale_(other.scale_),
|
||||
max_window_size_ms_(other.max_window_size_ms_),
|
||||
current_window_size_ms_(other.current_window_size_ms_) {}
|
||||
|
||||
RateStatistics::RateStatistics(RateStatistics&& other) = default;
|
||||
|
||||
RateStatistics::~RateStatistics() {}
|
||||
|
||||
void RateStatistics::Reset() {
|
||||
accumulated_count_ = 0;
|
||||
overflow_ = false;
|
||||
num_samples_ = 0;
|
||||
first_timestamp_ = -1;
|
||||
current_window_size_ms_ = max_window_size_ms_;
|
||||
buckets_.clear();
|
||||
}
|
||||
|
||||
void RateStatistics::Update(int64_t count, int64_t now_ms) {
|
||||
// Don't reset `first_timestamp_` if the last sample removed by EraseOld() was
|
||||
// recent. This ensures that the window maintains its intended duration even
|
||||
// when samples are received near the boundary. Use a margin of 50% of the
|
||||
// current window size.
|
||||
const int64_t recent_sample_time_margin = 1.5 * current_window_size_ms_;
|
||||
bool last_sample_is_recent =
|
||||
!buckets_.empty() &&
|
||||
buckets_.back().timestamp > now_ms - recent_sample_time_margin;
|
||||
|
||||
EraseOld(now_ms);
|
||||
if (first_timestamp_ == -1 || (num_samples_ == 0 && !last_sample_is_recent)) {
|
||||
first_timestamp_ = now_ms;
|
||||
}
|
||||
|
||||
if (buckets_.empty() || now_ms != buckets_.back().timestamp) {
|
||||
if (!buckets_.empty() && now_ms < buckets_.back().timestamp) {
|
||||
LOG_WARN(
|
||||
"Timestamp {} is before the last added timestamp in the rate window: "
|
||||
"{}, aligning to that.",
|
||||
now_ms, buckets_.back().timestamp);
|
||||
now_ms = buckets_.back().timestamp;
|
||||
}
|
||||
buckets_.emplace_back(now_ms);
|
||||
}
|
||||
Bucket& last_bucket = buckets_.back();
|
||||
last_bucket.sum += count;
|
||||
++last_bucket.num_samples;
|
||||
|
||||
if (std::numeric_limits<int64_t>::max() - accumulated_count_ > count) {
|
||||
accumulated_count_ += count;
|
||||
} else {
|
||||
overflow_ = true;
|
||||
}
|
||||
++num_samples_;
|
||||
}
|
||||
|
||||
std::optional<int64_t> RateStatistics::Rate(int64_t now_ms) const {
|
||||
// Yeah, this const_cast ain't pretty, but the alternative is to declare most
|
||||
// of the members as mutable...
|
||||
const_cast<RateStatistics*>(this)->EraseOld(now_ms);
|
||||
|
||||
int active_window_size = 0;
|
||||
if (first_timestamp_ != -1) {
|
||||
if (first_timestamp_ <= now_ms - current_window_size_ms_) {
|
||||
// Count window as full even if no data points currently in view, if the
|
||||
// data stream started before the window.
|
||||
active_window_size = current_window_size_ms_;
|
||||
} else {
|
||||
// Size of a single bucket is 1ms, so even if now_ms == first_timestmap_
|
||||
// the window size should be 1.
|
||||
active_window_size = now_ms - first_timestamp_ + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If window is a single bucket or there is only one sample in a data set that
|
||||
// has not grown to the full window size, or if the accumulator has
|
||||
// overflowed, treat this as rate unavailable.
|
||||
if (num_samples_ == 0 || active_window_size <= 1 ||
|
||||
(num_samples_ <= 1 &&
|
||||
rtc::SafeLt(active_window_size, current_window_size_ms_)) ||
|
||||
overflow_) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
float scale = static_cast<float>(scale_) / active_window_size;
|
||||
float result = accumulated_count_ * scale + 0.5f;
|
||||
|
||||
// Better return unavailable rate than garbage value (undefined behavior).
|
||||
if (result > static_cast<float>(std::numeric_limits<int64_t>::max())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return rtc::dchecked_cast<int64_t>(result);
|
||||
}
|
||||
|
||||
void RateStatistics::EraseOld(int64_t now_ms) {
|
||||
// New oldest time that is included in data set.
|
||||
const int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
|
||||
|
||||
// Loop over buckets and remove too old data points.
|
||||
while (!buckets_.empty() && buckets_.front().timestamp < new_oldest_time) {
|
||||
const Bucket& oldest_bucket = buckets_.front();
|
||||
accumulated_count_ -= oldest_bucket.sum;
|
||||
num_samples_ -= oldest_bucket.num_samples;
|
||||
buckets_.pop_front();
|
||||
// This does not clear overflow_ even when counter is empty.
|
||||
// TODO(https://bugs.webrtc.org/11247): Consider if overflow_ can be reset.
|
||||
}
|
||||
}
|
||||
|
||||
bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
|
||||
if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_) return false;
|
||||
if (first_timestamp_ != -1) {
|
||||
// If the window changes (e.g. decreases - removing data point, then
|
||||
// increases again) we need to update the first timestamp mark as
|
||||
// otherwise it indicates the window coveres a region of zeros, suddenly
|
||||
// under-estimating the rate.
|
||||
first_timestamp_ = std::max(first_timestamp_, now_ms - window_size_ms + 1);
|
||||
}
|
||||
current_window_size_ms_ = window_size_ms;
|
||||
EraseOld(now_ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
103
src/common/rtc_base/rate_statistics.h
Normal file
103
src/common/rtc_base/rate_statistics.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_RATE_STATISTICS_H_
|
||||
#define RTC_BASE_RATE_STATISTICS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Class to estimate rates based on counts in a sequence of 1-millisecond
|
||||
// intervals.
|
||||
|
||||
// This class uses int64 for all its numbers because some rates can be very
|
||||
// high; for instance, a 20 Mbit/sec video stream can wrap a 32-bit byte
|
||||
// counter in 14 minutes.
|
||||
|
||||
// Note that timestamps used in Update(), Rate() and SetWindowSize() must never
|
||||
// decrease for two consecutive calls.
|
||||
// TODO(bugs.webrtc.org/11600): Migrate from int64_t to Timestamp.
|
||||
|
||||
class RateStatistics {
|
||||
public:
|
||||
static constexpr float kBpsScale = 8000.0f;
|
||||
|
||||
// max_window_size_ms = Maximum window size in ms for the rate estimation.
|
||||
// Initial window size is set to this, but may be changed
|
||||
// to something lower by calling SetWindowSize().
|
||||
// scale = coefficient to convert counts/ms to desired unit
|
||||
// ex: kBpsScale (8000) for bits/s if count represents bytes.
|
||||
RateStatistics(int64_t max_window_size_ms, float scale);
|
||||
|
||||
RateStatistics(const RateStatistics& other);
|
||||
|
||||
RateStatistics(RateStatistics&& other);
|
||||
|
||||
~RateStatistics();
|
||||
|
||||
// Reset instance to original state.
|
||||
void Reset();
|
||||
|
||||
// Update rate with a new data point, moving averaging window as needed.
|
||||
void Update(int64_t count, int64_t now_ms);
|
||||
|
||||
// Note that despite this being a const method, it still updates the internal
|
||||
// state (moves averaging window), but it doesn't make any alterations that
|
||||
// are observable from the other methods, as long as supplied timestamps are
|
||||
// from a monotonic clock. Ie, it doesn't matter if this call moves the
|
||||
// window, since any subsequent call to Update or Rate would still have moved
|
||||
// the window as much or more.
|
||||
std::optional<int64_t> Rate(int64_t now_ms) const;
|
||||
|
||||
// Update the size of the averaging window. The maximum allowed value for
|
||||
// window_size_ms is max_window_size_ms as supplied in the constructor.
|
||||
bool SetWindowSize(int64_t window_size_ms, int64_t now_ms);
|
||||
|
||||
private:
|
||||
void EraseOld(int64_t now_ms);
|
||||
|
||||
struct Bucket {
|
||||
explicit Bucket(int64_t timestamp);
|
||||
int64_t sum; // Sum of all samples in this bucket.
|
||||
int num_samples; // Number of samples in this bucket.
|
||||
const int64_t timestamp; // Timestamp this bucket corresponds to.
|
||||
};
|
||||
// All buckets within the time window, ordered by time.
|
||||
std::deque<Bucket> buckets_;
|
||||
|
||||
// Total count recorded in all buckets.
|
||||
int64_t accumulated_count_;
|
||||
|
||||
// Timestamp of the first data point seen, or -1 of none seen.
|
||||
int64_t first_timestamp_;
|
||||
|
||||
// True if accumulated_count_ has ever grown too large to be
|
||||
// contained in its integer type.
|
||||
bool overflow_ = false;
|
||||
|
||||
// The total number of samples in the buckets.
|
||||
int num_samples_;
|
||||
|
||||
// To convert counts/ms to desired units
|
||||
const float scale_;
|
||||
|
||||
// The window sizes, in ms, over which the rate is calculated.
|
||||
const int64_t max_window_size_ms_;
|
||||
int64_t current_window_size_ms_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_RATE_STATISTICS_H_
|
||||
98
src/common/rtc_base/thread_annotations.h
Normal file
98
src/common/rtc_base/thread_annotations.h
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// Borrowed from
|
||||
// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h
|
||||
// but adapted for clang attributes instead of the gcc.
|
||||
//
|
||||
// This header file contains the macro definitions for thread safety
|
||||
// annotations that allow the developers to document the locking policies
|
||||
// of their multi-threaded code. The annotations can also help program
|
||||
// analysis tools to identify potential thread safety issues.
|
||||
|
||||
#ifndef RTC_BASE_THREAD_ANNOTATIONS_H_
|
||||
#define RTC_BASE_THREAD_ANNOTATIONS_H_
|
||||
|
||||
#if defined(__clang__) && (!defined(SWIG))
|
||||
#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
// Document if a shared variable/field needs to be protected by a lock.
|
||||
// GUARDED_BY allows the user to specify a particular lock that should be
|
||||
// held when accessing the annotated variable.
|
||||
#define RTC_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
// Document if the memory location pointed to by a pointer should be guarded
|
||||
// by a lock when dereferencing the pointer. Note that a pointer variable to a
|
||||
// shared memory location could itself be a shared variable. For example, if a
|
||||
// shared global pointer q, which is guarded by mu1, points to a shared memory
|
||||
// location that is guarded by mu2, q should be annotated as follows:
|
||||
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
|
||||
#define RTC_PT_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
|
||||
// Document the acquisition order between locks that can be held
|
||||
// simultaneously by a thread. For any two locks that need to be annotated
|
||||
// to establish an acquisition order, only one of them needs the annotation.
|
||||
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
|
||||
// and ACQUIRED_BEFORE.)
|
||||
#define RTC_ACQUIRED_AFTER(x) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x))
|
||||
#define RTC_ACQUIRED_BEFORE(x) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x))
|
||||
|
||||
// The following three annotations document the lock requirements for
|
||||
// functions/methods.
|
||||
|
||||
// Document if a function expects certain locks to be held before it is called
|
||||
#define RTC_EXCLUSIVE_LOCKS_REQUIRED(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||
#define RTC_SHARED_LOCKS_REQUIRED(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||
|
||||
// Document the locks acquired in the body of the function. These locks
|
||||
// cannot be held when calling this function (as google3's Mutex locks are
|
||||
// non-reentrant).
|
||||
#define RTC_LOCKS_EXCLUDED(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
|
||||
// Document the lock the annotated function returns without acquiring it.
|
||||
#define RTC_LOCK_RETURNED(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
// Document if a class/type is a lockable type (such as the Mutex class).
|
||||
#define RTC_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(lockable)
|
||||
|
||||
// Document if a class is a scoped lockable type (such as the MutexLock class).
|
||||
#define RTC_SCOPED_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
// The following annotations specify lock and unlock primitives.
|
||||
#define RTC_EXCLUSIVE_LOCK_FUNCTION(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
||||
|
||||
#define RTC_SHARED_LOCK_FUNCTION(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
||||
|
||||
#define RTC_EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define RTC_SHARED_TRYLOCK_FUNCTION(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define RTC_UNLOCK_FUNCTION(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
||||
|
||||
#define RTC_ASSERT_EXCLUSIVE_LOCK(...) \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
|
||||
|
||||
// An escape hatch for thread safety analysis to ignore the annotated function.
|
||||
#define RTC_NO_THREAD_SAFETY_ANALYSIS \
|
||||
RTC_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
#endif // RTC_BASE_THREAD_ANNOTATIONS_H_
|
||||
@@ -16,10 +16,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "bitrate_estimator.h"
|
||||
#include "network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
78
src/qos/acknowledged_bitrate_estimator_interface.h
Normal file
78
src/qos/acknowledged_bitrate_estimator_interface.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct RobustThroughputEstimatorSettings {
|
||||
static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings";
|
||||
|
||||
RobustThroughputEstimatorSettings();
|
||||
|
||||
// Set `enabled` to true to use the RobustThroughputEstimator, false to use
|
||||
// the AcknowledgedBitrateEstimator.
|
||||
bool enabled = true;
|
||||
|
||||
// The estimator keeps the smallest window containing at least
|
||||
// `window_packets` and at least the packets received during the last
|
||||
// `min_window_duration` milliseconds.
|
||||
// (This means that it may store more than `window_packets` at high bitrates,
|
||||
// and a longer duration than `min_window_duration` at low bitrates.)
|
||||
// However, if will never store more than kMaxPackets (for performance
|
||||
// reasons), and never longer than max_window_duration (to avoid very old
|
||||
// packets influencing the estimate for example when sending is paused).
|
||||
unsigned window_packets = 20;
|
||||
unsigned max_window_packets = 500;
|
||||
TimeDelta min_window_duration = TimeDelta::Seconds(1);
|
||||
TimeDelta max_window_duration = TimeDelta::Seconds(5);
|
||||
|
||||
// The estimator window requires at least `required_packets` packets
|
||||
// to produce an estimate.
|
||||
unsigned required_packets = 10;
|
||||
|
||||
// If audio packets aren't included in allocation (i.e. the
|
||||
// estimated available bandwidth is divided only among the video
|
||||
// streams), then `unacked_weight` should be set to 0.
|
||||
// If audio packets are included in allocation, but not in bandwidth
|
||||
// estimation (i.e. they don't have transport-wide sequence numbers,
|
||||
// but we nevertheless divide the estimated available bandwidth among
|
||||
// both audio and video streams), then `unacked_weight` should be set to 1.
|
||||
// If all packets have transport-wide sequence numbers, then the value
|
||||
// of `unacked_weight` doesn't matter.
|
||||
double unacked_weight = 1.0;
|
||||
};
|
||||
|
||||
class AcknowledgedBitrateEstimatorInterface {
|
||||
public:
|
||||
static std::unique_ptr<AcknowledgedBitrateEstimatorInterface> Create();
|
||||
virtual ~AcknowledgedBitrateEstimatorInterface();
|
||||
|
||||
virtual void IncomingPacketFeedbackVector(
|
||||
const std::vector<PacketResult>& packet_feedback_vector) = 0;
|
||||
virtual std::optional<DataRate> bitrate() const = 0;
|
||||
virtual std::optional<DataRate> PeekRate() const = 0;
|
||||
virtual void SetAlr(bool in_alr) = 0;
|
||||
virtual void SetAlrEndedTime(Timestamp alr_ended_time) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_
|
||||
@@ -17,9 +17,9 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "log.h"
|
||||
#include "network_types.h"
|
||||
#include "overuse_detector.h"
|
||||
#include "rtc_base/numerics/safe_minmax.h"
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "bwe_defines.h"
|
||||
#include "link_capacity_estimator.h"
|
||||
#include "network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
// A rate control implementation based on additive increases of
|
||||
@@ -98,7 +98,7 @@ class AimdRateControl {
|
||||
const bool send_side_;
|
||||
// Allow the delay based estimate to only increase as long as application
|
||||
// limited region (alr) is not detected.
|
||||
const bool no_bitrate_increase_in_alr_;
|
||||
const bool no_bitrate_increase_in_alr_ = true;
|
||||
// If "Disabled", estimated link capacity is not used as upper bound.
|
||||
bool disable_estimate_bounded_increase_ = true;
|
||||
bool use_current_estimate_as_min_upper_bound_ = true;
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "bandwidth_usage.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
101
src/qos/clock.cc
Normal file
101
src/qos/clock.cc
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
int64_t NtpOffsetUsCalledOnce() {
|
||||
constexpr int64_t kNtpJan1970Sec = 2208988800;
|
||||
int64_t clock_time = rtc::TimeMicros();
|
||||
int64_t utc_time = rtc::TimeUTCMicros();
|
||||
return utc_time - clock_time + kNtpJan1970Sec * rtc::kNumMicrosecsPerSec;
|
||||
}
|
||||
|
||||
NtpTime TimeMicrosToNtp(int64_t time_us) {
|
||||
static int64_t ntp_offset_us = NtpOffsetUsCalledOnce();
|
||||
|
||||
int64_t time_ntp_us = time_us + ntp_offset_us;
|
||||
|
||||
// Convert seconds to uint32 through uint64 for a well-defined cast.
|
||||
// A wrap around, which will happen in 2036, is expected for NTP time.
|
||||
uint32_t ntp_seconds =
|
||||
static_cast<uint64_t>(time_ntp_us / rtc::kNumMicrosecsPerSec);
|
||||
|
||||
// Scale fractions of the second to NTP resolution.
|
||||
constexpr int64_t kNtpFractionsInSecond = 1LL << 32;
|
||||
int64_t us_fractions = time_ntp_us % rtc::kNumMicrosecsPerSec;
|
||||
uint32_t ntp_fractions =
|
||||
us_fractions * kNtpFractionsInSecond / rtc::kNumMicrosecsPerSec;
|
||||
|
||||
return NtpTime(ntp_seconds, ntp_fractions);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class RealTimeClock : public Clock {
|
||||
public:
|
||||
RealTimeClock() = default;
|
||||
|
||||
Timestamp CurrentTime() override {
|
||||
return Timestamp::Micros(rtc::TimeMicros());
|
||||
}
|
||||
|
||||
NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override {
|
||||
return TimeMicrosToNtp(timestamp.us());
|
||||
}
|
||||
};
|
||||
|
||||
Clock* Clock::GetRealTimeClock() {
|
||||
static Clock* const clock = new RealTimeClock();
|
||||
return clock;
|
||||
}
|
||||
|
||||
SimulatedClock::SimulatedClock(int64_t initial_time_us)
|
||||
: time_us_(initial_time_us) {}
|
||||
|
||||
SimulatedClock::SimulatedClock(Timestamp initial_time)
|
||||
: SimulatedClock(initial_time.us()) {}
|
||||
|
||||
SimulatedClock::~SimulatedClock() {}
|
||||
|
||||
Timestamp SimulatedClock::CurrentTime() {
|
||||
return Timestamp::Micros(time_us_.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
NtpTime SimulatedClock::ConvertTimestampToNtpTime(Timestamp timestamp) {
|
||||
int64_t now_us = timestamp.us();
|
||||
uint32_t seconds = (now_us / 1'000'000) + kNtpJan1970;
|
||||
uint32_t fractions = static_cast<uint32_t>(
|
||||
(now_us % 1'000'000) * kMagicNtpFractionalUnit / 1'000'000);
|
||||
return NtpTime(seconds, fractions);
|
||||
}
|
||||
|
||||
void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
|
||||
AdvanceTime(TimeDelta::Millis(milliseconds));
|
||||
}
|
||||
|
||||
void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
|
||||
AdvanceTime(TimeDelta::Micros(microseconds));
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org(12102): It's desirable to let a single thread own
|
||||
// advancement of the clock. We could then replace this read-modify-write
|
||||
// operation with just a thread checker. But currently, that breaks a couple of
|
||||
// tests, in particular, RepeatingTaskTest.ClockIntegration and
|
||||
// CallStatsTest.LastProcessedRtt.
|
||||
void SimulatedClock::AdvanceTime(TimeDelta delta) {
|
||||
time_us_.fetch_add(delta.us(), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
100
src/qos/clock.h
Normal file
100
src/qos/clock.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
|
||||
#define SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "api/units/timestamp.h"
|
||||
#include "ntp_time.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// January 1970, in NTP seconds.
|
||||
const uint32_t kNtpJan1970 = 2208988800UL;
|
||||
|
||||
// Magic NTP fractional unit.
|
||||
const double kMagicNtpFractionalUnit = 4.294967296E+9;
|
||||
|
||||
// A clock interface that allows reading of absolute and relative timestamps.
|
||||
class Clock {
|
||||
public:
|
||||
virtual ~Clock() {}
|
||||
|
||||
// Return a timestamp relative to an unspecified epoch.
|
||||
virtual Timestamp CurrentTime() = 0;
|
||||
int64_t TimeInMilliseconds() { return CurrentTime().ms(); }
|
||||
int64_t TimeInMicroseconds() { return CurrentTime().us(); }
|
||||
|
||||
// Retrieve an NTP absolute timestamp (with an epoch of Jan 1, 1900).
|
||||
NtpTime CurrentNtpTime() { return ConvertTimestampToNtpTime(CurrentTime()); }
|
||||
int64_t CurrentNtpInMilliseconds() { return CurrentNtpTime().ToMs(); }
|
||||
|
||||
// Converts between a relative timestamp returned by this clock, to NTP time.
|
||||
virtual NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) = 0;
|
||||
int64_t ConvertTimestampToNtpTimeInMilliseconds(int64_t timestamp_ms) {
|
||||
return ConvertTimestampToNtpTime(Timestamp::Millis(timestamp_ms)).ToMs();
|
||||
}
|
||||
|
||||
// Converts NtpTime to a Timestamp with UTC epoch.
|
||||
// A `Minus Infinity` Timestamp is returned if the NtpTime is invalid.
|
||||
static Timestamp NtpToUtc(NtpTime ntp_time) {
|
||||
if (!ntp_time.Valid()) {
|
||||
return Timestamp::MinusInfinity();
|
||||
}
|
||||
// Seconds since UTC epoch.
|
||||
int64_t time = ntp_time.seconds() - kNtpJan1970;
|
||||
// Microseconds since UTC epoch (not including NTP fraction)
|
||||
time = time * 1'000'000;
|
||||
// Fractions part of the NTP time, in microseconds.
|
||||
int64_t time_fraction =
|
||||
DivideRoundToNearest(int64_t{ntp_time.fractions()} * 1'000'000,
|
||||
NtpTime::kFractionsPerSecond);
|
||||
return Timestamp::Micros(time + time_fraction);
|
||||
}
|
||||
|
||||
// Returns an instance of the real-time system clock implementation.
|
||||
static Clock* GetRealTimeClock();
|
||||
};
|
||||
|
||||
class SimulatedClock : public Clock {
|
||||
public:
|
||||
// The constructors assume an epoch of Jan 1, 1970.
|
||||
explicit SimulatedClock(int64_t initial_time_us);
|
||||
explicit SimulatedClock(Timestamp initial_time);
|
||||
~SimulatedClock() override;
|
||||
|
||||
// Return a timestamp with an epoch of Jan 1, 1970.
|
||||
Timestamp CurrentTime() override;
|
||||
|
||||
NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override;
|
||||
|
||||
// Advance the simulated clock with a given number of milliseconds or
|
||||
// microseconds.
|
||||
void AdvanceTimeMilliseconds(int64_t milliseconds);
|
||||
void AdvanceTimeMicroseconds(int64_t microseconds);
|
||||
void AdvanceTime(TimeDelta delta);
|
||||
|
||||
private:
|
||||
// The time is read and incremented with relaxed order. Each thread will see
|
||||
// monotonically increasing time, and when threads post tasks or messages to
|
||||
// one another, the synchronization done as part of the message passing should
|
||||
// ensure that any causual chain of events on multiple threads also
|
||||
// corresponds to monotonically increasing time.
|
||||
std::atomic<int64_t> time_us_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
|
||||
@@ -21,7 +21,34 @@ constexpr float kDefaultPaceMultiplier = 2.5f;
|
||||
// below the current throughput estimate to drain the network queues.
|
||||
constexpr double kProbeDropThroughputFraction = 0.85;
|
||||
|
||||
CongestionControl::CongestionControl() {}
|
||||
CongestionControl::CongestionControl()
|
||||
: use_min_allocatable_as_lower_bound_(false),
|
||||
ignore_probes_lower_than_network_estimate_(false),
|
||||
limit_probes_lower_than_throughput_estimate_(false),
|
||||
pace_at_max_of_bwe_and_lower_link_capacity_(false),
|
||||
limit_pacingfactor_by_upper_link_capacity_estimate_(false),
|
||||
probe_controller_(new ProbeController()),
|
||||
bandwidth_estimation_(new SendSideBandwidthEstimation()),
|
||||
alr_detector_(new AlrDetector()),
|
||||
probe_bitrate_estimator_(new ProbeBitrateEstimator()),
|
||||
network_estimator_(new NetworkStateEstimator()),
|
||||
network_state_predictor_(new NetworkStatePredictor()),
|
||||
delay_based_bwe_(new DelayBasedBwe()),
|
||||
acknowledged_bitrate_estimator_(new AcknowledgedBitrateEstimator()),
|
||||
initial_config_(std::nullopt),
|
||||
last_loss_based_target_rate_(*config.constraints.starting_rate),
|
||||
last_pushback_target_rate_(last_loss_based_target_rate_),
|
||||
last_stable_target_rate_(last_loss_based_target_rate_),
|
||||
last_loss_base_state_(LossBasedState::kDelayBasedEstimate),
|
||||
pacing_factor_(config.stream_based_config.pacing_factor.value_or(
|
||||
kDefaultPaceMultiplier)),
|
||||
min_total_allocated_bitrate_(
|
||||
config.stream_based_config.min_total_allocated_bitrate.value_or(
|
||||
DataRate::Zero())),
|
||||
max_padding_rate_(config.stream_based_config.max_padding_rate.value_or(
|
||||
DataRate::Zero()))
|
||||
|
||||
{}
|
||||
|
||||
CongestionControl::~CongestionControl() {}
|
||||
|
||||
@@ -35,28 +62,27 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
|
||||
if (congestion_window_pushback_controller_) {
|
||||
congestion_window_pushback_controller_->UpdateOutstandingData(
|
||||
report.data_in_flight);
|
||||
report.data_in_flight.bytes());
|
||||
}
|
||||
int64_t max_feedback_rtt = std::numeric_limits<int64_t>::min();
|
||||
int64_t min_propagation_rtt = std::numeric_limits<int64_t>::max();
|
||||
int64_t max_recv_time = std::numeric_limits<int64_t>::min();
|
||||
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
|
||||
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
|
||||
Timestamp max_recv_time = Timestamp::MinusInfinity();
|
||||
|
||||
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
|
||||
for (const auto& feedback : feedbacks)
|
||||
max_recv_time = std::max(max_recv_time, feedback.receive_time);
|
||||
|
||||
for (const auto& feedback : feedbacks) {
|
||||
int64_t feedback_rtt =
|
||||
TimeDelta feedback_rtt =
|
||||
report.feedback_time - feedback.sent_packet.send_time;
|
||||
int64_t min_pending_time = max_recv_time - feedback.receive_time;
|
||||
int64_t propagation_rtt = feedback_rtt - min_pending_time;
|
||||
TimeDelta min_pending_time = max_recv_time - feedback.receive_time;
|
||||
TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
|
||||
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
|
||||
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
|
||||
}
|
||||
|
||||
if (max_feedback_rtt != std::numeric_limits<int64_t>::min() &&
|
||||
min_propagation_rtt != std::numeric_limits<int64_t>::max()) {
|
||||
feedback_max_rtts_.push_back(max_feedback_rtt);
|
||||
if (max_feedback_rtt.IsFinite()) {
|
||||
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
|
||||
const size_t kMaxFeedbackRttWindow = 32;
|
||||
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
|
||||
feedback_max_rtts_.pop_front();
|
||||
@@ -70,19 +96,19 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
|
||||
static_cast<int64_t>(0));
|
||||
int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
|
||||
if (delay_based_bwe_) delay_based_bwe_->OnRttUpdate(mean_rtt_ms);
|
||||
if (delay_based_bwe_)
|
||||
delay_based_bwe_->OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
|
||||
}
|
||||
|
||||
int64_t feedback_min_rtt = std::numeric_limits<int64_t>::max();
|
||||
TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
|
||||
for (const auto& packet_feedback : feedbacks) {
|
||||
int64_t pending_time = max_recv_time - packet_feedback.receive_time;
|
||||
int64_t rtt = report.feedback_time -
|
||||
TimeDelta pending_time = max_recv_time - packet_feedback.receive_time;
|
||||
TimeDelta rtt = report.feedback_time -
|
||||
packet_feedback.sent_packet.send_time - pending_time;
|
||||
// Value used for predicting NACK round trip time in FEC controller.
|
||||
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
|
||||
}
|
||||
if (feedback_min_rtt != std::numeric_limits<int64_t>::max() &&
|
||||
feedback_min_rtt != std::numeric_limits<int64_t>::min()) {
|
||||
if (feedback_min_rtt.IsFinite()) {
|
||||
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
|
||||
}
|
||||
|
||||
@@ -105,7 +131,7 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
alr_detector_->GetApplicationLimitedRegionStartTime();
|
||||
|
||||
if (previously_in_alr_ && !alr_start_time.has_value()) {
|
||||
int64_t now_ms = report.feedback_time;
|
||||
int64_t now_ms = report.feedback_time.ms();
|
||||
acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
|
||||
probe_controller_->SetAlrEndedTimeMs(now_ms);
|
||||
}
|
||||
@@ -122,7 +148,11 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int64_t> probe_bitrate =
|
||||
if (network_estimator_) {
|
||||
network_estimator_->OnTransportPacketsFeedback(report);
|
||||
SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
|
||||
}
|
||||
std::optional<DataRate> probe_bitrate =
|
||||
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
|
||||
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
|
||||
estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
|
||||
@@ -138,7 +168,7 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
// based estimate, but it could happen e.g. due to packet bursts or
|
||||
// encoder overshoot. We use std::min to ensure that a probe result
|
||||
// below the current BWE never causes an increase.
|
||||
int64_t limit =
|
||||
DataRate limit =
|
||||
std::min(delay_based_bwe_->last_estimate(),
|
||||
*acknowledged_bitrate * kProbeDropThroughputFraction);
|
||||
probe_bitrate = std::max(*probe_bitrate, limit);
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
#include <memory>
|
||||
|
||||
#include "acknowledged_bitrate_estimator.h"
|
||||
#include "acknowledged_bitrate_estimator_interface.h"
|
||||
#include "alr_detector.h"
|
||||
#include "api/network_state_predictor.h"
|
||||
#include "api/transport/network_control.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "congestion_window_pushback_controller.h"
|
||||
#include "delay_based_bwe.h"
|
||||
#include "network_types.h"
|
||||
#include "probe_controller.h"
|
||||
#include "send_side_bandwidth_estimation.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
class CongestionControl {
|
||||
public:
|
||||
CongestionControl();
|
||||
@@ -21,24 +27,54 @@ class CongestionControl {
|
||||
TransportPacketsFeedback report);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<CongestionWindowPushbackController>
|
||||
congestion_window_pushback_controller_;
|
||||
const bool use_min_allocatable_as_lower_bound_;
|
||||
const bool ignore_probes_lower_than_network_estimate_;
|
||||
const bool limit_probes_lower_than_throughput_estimate_;
|
||||
const bool pace_at_max_of_bwe_and_lower_link_capacity_;
|
||||
const bool limit_pacingfactor_by_upper_link_capacity_estimate_;
|
||||
|
||||
private:
|
||||
std::deque<int64_t> feedback_max_rtts_;
|
||||
int expected_packets_since_last_loss_update_ = 0;
|
||||
int lost_packets_since_last_loss_update_ = 0;
|
||||
int64_t next_loss_update_ = std::numeric_limits<int64_t>::min();
|
||||
const bool packet_feedback_only_ = false;
|
||||
bool previously_in_alr_ = false;
|
||||
const bool limit_probes_lower_than_throughput_estimate_ = false;
|
||||
std::optional<int64_t> current_data_window_;
|
||||
const std::unique_ptr<ProbeController> probe_controller_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
|
||||
std::unique_ptr<AlrDetector> alr_detector_;
|
||||
std::unique_ptr<AcknowledgedBitrateEstimator> acknowledged_bitrate_estimator_;
|
||||
std::unique_ptr<ProbeBitrateEstimator> probe_bitrate_estimator_;
|
||||
std::unique_ptr<NetworkStateEstimator> network_estimator_;
|
||||
std::unique_ptr<NetworkStatePredictor> network_state_predictor_;
|
||||
std::unique_ptr<DelayBasedBwe> delay_based_bwe_;
|
||||
std::unique_ptr<AcknowledgedBitrateEstimatorInterface>
|
||||
acknowledged_bitrate_estimator_;
|
||||
|
||||
std::optional<NetworkControllerConfig> initial_config_;
|
||||
|
||||
DataRate min_target_rate_ = DataRate::Zero();
|
||||
DataRate min_data_rate_ = DataRate::Zero();
|
||||
DataRate max_data_rate_ = DataRate::PlusInfinity();
|
||||
std::optional<DataRate> starting_rate_;
|
||||
|
||||
bool first_packet_sent_ = false;
|
||||
|
||||
std::optional<NetworkStateEstimate> estimate_;
|
||||
|
||||
Timestamp next_loss_update_ = Timestamp::MinusInfinity();
|
||||
int lost_packets_since_last_loss_update_ = 0;
|
||||
int expected_packets_since_last_loss_update_ = 0;
|
||||
|
||||
std::deque<int64_t> feedback_max_rtts_;
|
||||
|
||||
DataRate last_loss_based_target_rate_;
|
||||
DataRate last_pushback_target_rate_;
|
||||
DataRate last_stable_target_rate_;
|
||||
|
||||
std::optional<uint8_t> last_estimated_fraction_loss_ = 0;
|
||||
TimeDelta last_estimated_round_trip_time_ = TimeDelta::PlusInfinity();
|
||||
|
||||
double pacing_factor_;
|
||||
DataRate min_total_allocated_bitrate_;
|
||||
DataRate max_padding_rate_;
|
||||
|
||||
bool previously_in_alr_ = false;
|
||||
|
||||
std::optional<DataSize> current_data_window_;
|
||||
};
|
||||
|
||||
#endif
|
||||
125
src/qos/congestion_control_feedback_generator.cc
Normal file
125
src/qos/congestion_control_feedback_generator.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "congestion_control_feedback_generator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "clock.h"
|
||||
#include "congestion_control_feedback.h"
|
||||
#include "ntp_time_util.h"
|
||||
#include "rtcp_packet.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
|
||||
std::shared_ptr<SimulatedClock> clock, RtcpSender rtcp_sender)
|
||||
: clock_(clock),
|
||||
rtcp_sender_(std::move(rtcp_sender)),
|
||||
min_time_between_feedback_(TimeDelta::Millis(25)),
|
||||
max_time_to_wait_for_packet_with_marker_(TimeDelta::Millis(25)),
|
||||
max_time_between_feedback_(TimeDelta::Millis(250)) {}
|
||||
|
||||
void CongestionControlFeedbackGenerator::OnReceivedPacket(
|
||||
const RtpPacketReceived& packet) {
|
||||
marker_bit_seen_ |= packet.Marker();
|
||||
if (!first_arrival_time_since_feedback_) {
|
||||
first_arrival_time_since_feedback_ = packet.arrival_time();
|
||||
}
|
||||
feedback_trackers_[packet.Ssrc()].ReceivedPacket(packet);
|
||||
if (NextFeedbackTime() < packet.arrival_time()) {
|
||||
SendFeedback(Timestamp::Micros(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()));
|
||||
}
|
||||
}
|
||||
|
||||
Timestamp CongestionControlFeedbackGenerator::NextFeedbackTime() const {
|
||||
if (!first_arrival_time_since_feedback_) {
|
||||
return std::max(Timestamp::Micros(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()) +
|
||||
min_time_between_feedback_,
|
||||
next_possible_feedback_send_time_);
|
||||
}
|
||||
|
||||
if (!marker_bit_seen_) {
|
||||
return std::max(next_possible_feedback_send_time_,
|
||||
*first_arrival_time_since_feedback_ +
|
||||
max_time_to_wait_for_packet_with_marker_);
|
||||
}
|
||||
return next_possible_feedback_send_time_;
|
||||
}
|
||||
|
||||
TimeDelta CongestionControlFeedbackGenerator::Process(Timestamp now) {
|
||||
if (NextFeedbackTime() <= now) {
|
||||
SendFeedback(now);
|
||||
}
|
||||
return NextFeedbackTime() - now;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
|
||||
DataRate estimate) {
|
||||
// Feedback reports should max occupy 5% of total bandwidth.
|
||||
max_feedback_rate_ = estimate * 0.05;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::SetTransportOverhead(
|
||||
DataSize overhead_per_packet) {
|
||||
packet_overhead_ = overhead_per_packet;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::SendFeedback(Timestamp now) {
|
||||
SimulatedClock clock(now);
|
||||
uint32_t compact_ntp = CompactNtp(clock.ConvertTimestampToNtpTime(now));
|
||||
std::vector<rtcp::CongestionControlFeedback::PacketInfo> rtcp_packet_info;
|
||||
for (auto& [unused, tracker] : feedback_trackers_) {
|
||||
tracker.AddPacketsToFeedback(now, rtcp_packet_info);
|
||||
}
|
||||
marker_bit_seen_ = false;
|
||||
first_arrival_time_since_feedback_ = std::nullopt;
|
||||
|
||||
auto feedback = std::make_unique<rtcp::CongestionControlFeedback>(
|
||||
std::move(rtcp_packet_info), compact_ntp);
|
||||
CalculateNextPossibleSendTime(DataSize::Bytes(feedback->BlockLength()), now);
|
||||
|
||||
std::vector<std::unique_ptr<RtcpPacket>> rtcp_packets;
|
||||
rtcp_packets.push_back(std::move(feedback));
|
||||
rtcp_sender_(std::move(rtcp_packets));
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
|
||||
DataSize feedback_size, Timestamp now) {
|
||||
TimeDelta time_since_last_sent = now - last_feedback_sent_time_;
|
||||
DataSize debt_payed = time_since_last_sent * max_feedback_rate_;
|
||||
send_rate_debt_ = debt_payed > send_rate_debt_ ? DataSize::Zero()
|
||||
: send_rate_debt_ - debt_payed;
|
||||
send_rate_debt_ += feedback_size + packet_overhead_;
|
||||
last_feedback_sent_time_ = now;
|
||||
next_possible_feedback_send_time_ =
|
||||
now + std::clamp(max_feedback_rate_.IsZero()
|
||||
? TimeDelta::PlusInfinity()
|
||||
: send_rate_debt_ / max_feedback_rate_,
|
||||
min_time_between_feedback_, max_time_between_feedback_);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -1,108 +0,0 @@
|
||||
#include "congestion_control_feedback_generator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
uint32_t ConvertToCompactNtp(int64_t now_ms) {
|
||||
int64_t seconds = now_ms / 1000;
|
||||
int64_t milliseconds = now_ms % 1000;
|
||||
uint16_t ntp_seconds = static_cast<uint16_t>(seconds & 0xFFFF);
|
||||
uint16_t ntp_fraction = static_cast<uint16_t>((milliseconds * 65536) / 1000);
|
||||
uint32_t compact_ntp = (ntp_seconds << 16) | ntp_fraction;
|
||||
return compact_ntp;
|
||||
}
|
||||
|
||||
CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
|
||||
RtcpSender rtcp_sender)
|
||||
: rtcp_sender_(std::move(rtcp_sender)) {}
|
||||
|
||||
void CongestionControlFeedbackGenerator::OnReceivedPacket(
|
||||
RtpPacketReceived& packet) {
|
||||
marker_bit_seen_ |= packet.Marker();
|
||||
if (!first_arrival_time_since_feedback_) {
|
||||
first_arrival_time_since_feedback_ = packet.arrival_time();
|
||||
}
|
||||
feedback_trackers_[packet.Ssrc()].ReceivedPacket(packet);
|
||||
if (NextFeedbackTime() < packet.arrival_time()) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch())
|
||||
.count();
|
||||
SendFeedback(now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t CongestionControlFeedbackGenerator::NextFeedbackTime() const {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch())
|
||||
.count();
|
||||
|
||||
if (!first_arrival_time_since_feedback_) {
|
||||
return std::max(now_ms + min_time_between_feedback_,
|
||||
next_possible_feedback_send_time_);
|
||||
}
|
||||
|
||||
if (!marker_bit_seen_) {
|
||||
return std::max(next_possible_feedback_send_time_,
|
||||
*first_arrival_time_since_feedback_ +
|
||||
max_time_to_wait_for_packet_with_marker_);
|
||||
}
|
||||
return next_possible_feedback_send_time_;
|
||||
}
|
||||
|
||||
int64_t CongestionControlFeedbackGenerator::Process(int64_t now_ms) {
|
||||
if (NextFeedbackTime() <= now_ms) {
|
||||
SendFeedback(now_ms);
|
||||
}
|
||||
return NextFeedbackTime() - now_ms;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
|
||||
int64_t estimate) {
|
||||
// Feedback reports should max occupy 5% of total bandwidth.
|
||||
max_feedback_rate_ = estimate * 0.05;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::SetTransportOverhead(
|
||||
int64_t overhead_per_packet) {
|
||||
packet_overhead_ = overhead_per_packet;
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
|
||||
uint32_t compact_ntp = ConvertToCompactNtp(now_ms);
|
||||
std::vector<CongestionControlFeedback::PacketInfo> rtcp_packet_info;
|
||||
for (auto& [unused, tracker] : feedback_trackers_) {
|
||||
tracker.AddPacketsToFeedback(now_ms, rtcp_packet_info);
|
||||
}
|
||||
|
||||
marker_bit_seen_ = false;
|
||||
first_arrival_time_since_feedback_ = std::nullopt;
|
||||
|
||||
auto feedback = std::make_unique<CongestionControlFeedback>(
|
||||
std::move(rtcp_packet_info), compact_ntp);
|
||||
CalculateNextPossibleSendTime(feedback->BlockLength(), now_ms);
|
||||
|
||||
std::vector<std::unique_ptr<RtcpPacket>> rtcp_packets;
|
||||
rtcp_packets.push_back(std::move(feedback));
|
||||
rtcp_sender_(std::move(rtcp_packets));
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
|
||||
int64_t feedback_size, int64_t now_ms) {
|
||||
int64_t time_since_last_sent = now_ms - last_feedback_sent_time_;
|
||||
size_t debt_payed = time_since_last_sent * max_feedback_rate_;
|
||||
send_rate_debt_ =
|
||||
debt_payed > send_rate_debt_ ? 0 : send_rate_debt_ - debt_payed;
|
||||
send_rate_debt_ += feedback_size + packet_overhead_;
|
||||
last_feedback_sent_time_ = now_ms;
|
||||
next_possible_feedback_send_time_ =
|
||||
now_ms +
|
||||
std::clamp(max_feedback_rate_ == 0 ? std::numeric_limits<int64_t>::max()
|
||||
: send_rate_debt_ / max_feedback_rate_,
|
||||
min_time_between_feedback_, max_time_between_feedback_);
|
||||
}
|
||||
@@ -1,67 +1,88 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-12-18
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
||||
#define MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
||||
|
||||
#ifndef _CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
||||
#define _CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "clock.h"
|
||||
#include "congestion_control_feedback_tracker.h"
|
||||
#include "rtcp_packet.h"
|
||||
#include "rtp_packet_received.h"
|
||||
#include "rtp_transport_feedback_generator.h"
|
||||
|
||||
class CongestionControlFeedbackGenerator {
|
||||
public:
|
||||
CongestionControlFeedbackGenerator(RtcpSender feedback_sender);
|
||||
~CongestionControlFeedbackGenerator() = default;
|
||||
namespace webrtc {
|
||||
|
||||
void OnReceivedPacket(RtpPacketReceived& packet);
|
||||
// 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/
|
||||
|
||||
void OnSendBandwidthEstimateChanged(int64_t estimate);
|
||||
|
||||
int64_t Process(int64_t now_ms);
|
||||
|
||||
void SetTransportOverhead(int64_t overhead_per_packet);
|
||||
|
||||
private:
|
||||
int64_t NextFeedbackTime() const;
|
||||
|
||||
void SendFeedback(int64_t now_ms);
|
||||
|
||||
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
|
||||
|
||||
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;
|
||||
// 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(
|
||||
std::shared_ptr<SimulatedClock> clock,
|
||||
RtpTransportFeedbackGenerator::RtcpSender feedback_sender);
|
||||
~CongestionControlFeedbackGenerator() = default;
|
||||
|
||||
int64_t max_feedback_rate_ = 1000; // kbps
|
||||
int64_t packet_overhead_ = 0;
|
||||
int64_t send_rate_debt_ = 0;
|
||||
void OnReceivedPacket(const RtpPacketReceived& packet) override;
|
||||
|
||||
void OnSendBandwidthEstimateChanged(DataRate estimate) override;
|
||||
|
||||
TimeDelta Process(Timestamp now) override;
|
||||
|
||||
void SetTransportOverhead(DataSize overhead_per_packet) override;
|
||||
|
||||
private:
|
||||
Timestamp NextFeedbackTime() const;
|
||||
|
||||
void SendFeedback(Timestamp now);
|
||||
|
||||
void CalculateNextPossibleSendTime(DataSize feedback_size, Timestamp now);
|
||||
|
||||
std::shared_ptr<SimulatedClock> clock_;
|
||||
const RtcpSender rtcp_sender_;
|
||||
|
||||
TimeDelta min_time_between_feedback_;
|
||||
TimeDelta max_time_to_wait_for_packet_with_marker_;
|
||||
TimeDelta max_time_between_feedback_;
|
||||
|
||||
DataRate max_feedback_rate_ = DataRate::KilobitsPerSec(1000);
|
||||
DataSize packet_overhead_ = DataSize::Zero();
|
||||
DataSize send_rate_debt_ = DataSize::Zero();
|
||||
|
||||
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
|
||||
feedback_trackers_;
|
||||
|
||||
std::optional<int64_t> first_arrival_time_since_feedback_;
|
||||
int64_t next_possible_feedback_send_time_ = 0;
|
||||
int64_t last_feedback_sent_time_ = 0;
|
||||
|
||||
// std::vector<PacketInfo> packets_;
|
||||
Timestamp last_feedback_sent_time_ = Timestamp::Zero();
|
||||
std::optional<Timestamp> first_arrival_time_since_feedback_;
|
||||
bool marker_bit_seen_ = false;
|
||||
Timestamp next_possible_feedback_send_time_ = Timestamp::Zero();
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
||||
|
||||
@@ -41,8 +41,8 @@ void CongestionControlFeedbackTracker::ReceivedPacket(
|
||||
// received.
|
||||
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
|
||||
}
|
||||
packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number,
|
||||
packet.arrival_time(), packet.ecn());
|
||||
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
|
||||
packet.arrival_time(), packet.ecn()});
|
||||
}
|
||||
|
||||
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
||||
@@ -89,8 +89,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
||||
++packet_it;
|
||||
}
|
||||
} // else - the packet has not been received yet.
|
||||
packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
|
||||
arrival_time_offset, ecn);
|
||||
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
|
||||
arrival_time_offset, ecn});
|
||||
}
|
||||
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
|
||||
packets_.clear();
|
||||
|
||||
@@ -13,7 +13,17 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
CongestionWindowPushbackController::CongestionWindowPushbackController() {}
|
||||
#include "api/units/data_size.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int kDefaultMinPushbackTargetBitrateBps = 30000;
|
||||
|
||||
CongestionWindowPushbackController::CongestionWindowPushbackController()
|
||||
: add_pacing_(false),
|
||||
min_pushback_target_bitrate_bps_(kDefaultMinPushbackTargetBitrateBps),
|
||||
current_data_window_(
|
||||
DataSize::Bytes(kDefaultMinPushbackTargetBitrateBps)) {}
|
||||
|
||||
void CongestionWindowPushbackController::UpdateOutstandingData(
|
||||
int64_t outstanding_bytes) {
|
||||
@@ -24,17 +34,18 @@ void CongestionWindowPushbackController::UpdatePacingQueue(
|
||||
pacing_bytes_ = pacing_bytes;
|
||||
}
|
||||
|
||||
void CongestionWindowPushbackController::SetDataWindow(int64_t data_window) {
|
||||
void CongestionWindowPushbackController::SetDataWindow(DataSize data_window) {
|
||||
current_data_window_ = data_window;
|
||||
}
|
||||
|
||||
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
|
||||
uint32_t bitrate_bps) {
|
||||
if (!current_data_window_ || current_data_window_ == 0) return bitrate_bps;
|
||||
if (!current_data_window_ || current_data_window_->IsZero())
|
||||
return bitrate_bps;
|
||||
int64_t total_bytes = outstanding_bytes_;
|
||||
if (add_pacing_) total_bytes += pacing_bytes_;
|
||||
double fill_ratio =
|
||||
total_bytes / static_cast<double>(current_data_window_.value());
|
||||
total_bytes / static_cast<double>(current_data_window_->bytes());
|
||||
if (fill_ratio > 1.5) {
|
||||
encoding_rate_ratio_ *= 0.9;
|
||||
} else if (fill_ratio > 1) {
|
||||
@@ -55,3 +66,5 @@ uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
|
||||
: adjusted_target_bitrate_bps;
|
||||
return bitrate_bps;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -1,16 +1,24 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-01-13
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||
#define _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/units/data_size.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This class enables pushback from congestion window directly to video encoder.
|
||||
// When the congestion window is filling up, the video encoder target bitrate
|
||||
// will be reduced accordingly to accommodate the network changes. To avoid
|
||||
@@ -22,15 +30,17 @@ class CongestionWindowPushbackController {
|
||||
void UpdateOutstandingData(int64_t outstanding_bytes);
|
||||
void UpdatePacingQueue(int64_t pacing_bytes);
|
||||
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
|
||||
void SetDataWindow(int64_t data_window);
|
||||
void SetDataWindow(DataSize data_window);
|
||||
|
||||
private:
|
||||
const bool add_pacing_ = true;
|
||||
const uint32_t min_pushback_target_bitrate_bps_ = 10000;
|
||||
std::optional<int64_t> current_data_window_ = std::nullopt;
|
||||
const bool add_pacing_;
|
||||
const uint32_t min_pushback_target_bitrate_bps_;
|
||||
std::optional<DataSize> current_data_window_;
|
||||
int64_t outstanding_bytes_ = 0;
|
||||
int64_t pacing_bytes_ = 0;
|
||||
double encoding_rate_ratio_ = 1.0;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
@@ -24,7 +25,6 @@
|
||||
#include "bwe_defines.h"
|
||||
#include "inter_arrival_delta.h"
|
||||
#include "log.h"
|
||||
#include "network_types.h"
|
||||
#include "trendline_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
#include <vector>
|
||||
|
||||
#include "aimd_rate_control.h"
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "bandwidth_usage.h"
|
||||
#include "delay_increase_detector_interface.h"
|
||||
#include "inter_arrival.h"
|
||||
#include "inter_arrival_delta.h"
|
||||
#include "link_capacity_estimator.h"
|
||||
#include "network_types.h"
|
||||
#include "probe_bitrate_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "bandwidth_usage.h"
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
66
src/qos/module_common_types.h
Normal file
66
src/qos/module_common_types.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_INCLUDE_MODULE_COMMON_TYPES_H_
|
||||
#define MODULES_INCLUDE_MODULE_COMMON_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Interface used by the CallStats class to distribute call statistics.
|
||||
// Callbacks will be triggered as soon as the class has been registered to a
|
||||
// CallStats object using RegisterStatsObserver.
|
||||
class CallStatsObserver {
|
||||
public:
|
||||
virtual void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) = 0;
|
||||
|
||||
virtual ~CallStatsObserver() {}
|
||||
};
|
||||
|
||||
// Interface used by NackModule and JitterBuffer.
|
||||
class NackSender {
|
||||
public:
|
||||
// If `buffering_allowed`, other feedback messages (e.g. key frame requests)
|
||||
// may be added to the same outgoing feedback message. In that case, it's up
|
||||
// to the user of the interface to ensure that when all buffer-able messages
|
||||
// have been added, the feedback message is triggered.
|
||||
virtual void SendNack(const std::vector<uint16_t>& sequence_numbers,
|
||||
bool buffering_allowed) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~NackSender() {}
|
||||
};
|
||||
|
||||
// Interface used by NackModule and JitterBuffer.
|
||||
class KeyFrameRequestSender {
|
||||
public:
|
||||
virtual void RequestKeyFrame() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~KeyFrameRequestSender() {}
|
||||
};
|
||||
|
||||
// Interface used by LossNotificationController to communicate to RtpRtcp.
|
||||
class LossNotificationSender {
|
||||
public:
|
||||
virtual ~LossNotificationSender() {}
|
||||
|
||||
virtual void SendLossNotification(uint16_t last_decoded_seq_num,
|
||||
uint16_t last_received_seq_num,
|
||||
bool decodability_flag,
|
||||
bool buffering_allowed) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_INCLUDE_MODULE_COMMON_TYPES_H_
|
||||
136
src/qos/ntp_time.h
Normal file
136
src/qos/ntp_time.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
|
||||
#define SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class NtpTime {
|
||||
public:
|
||||
static constexpr uint64_t kFractionsPerSecond = 0x100000000;
|
||||
NtpTime() : value_(0) {}
|
||||
explicit NtpTime(uint64_t value) : value_(value) {}
|
||||
NtpTime(uint32_t seconds, uint32_t fractions)
|
||||
: value_(seconds * kFractionsPerSecond + fractions) {}
|
||||
|
||||
NtpTime(const NtpTime&) = default;
|
||||
NtpTime& operator=(const NtpTime&) = default;
|
||||
explicit operator uint64_t() const { return value_; }
|
||||
|
||||
void Set(uint32_t seconds, uint32_t fractions) {
|
||||
value_ = seconds * kFractionsPerSecond + fractions;
|
||||
}
|
||||
void Reset() { value_ = 0; }
|
||||
|
||||
int64_t ToMs() const {
|
||||
static constexpr double kNtpFracPerMs = 4.294967296E6; // 2^32 / 1000.
|
||||
const double frac_ms = static_cast<double>(fractions()) / kNtpFracPerMs;
|
||||
return 1000 * static_cast<int64_t>(seconds()) +
|
||||
static_cast<int64_t>(frac_ms + 0.5);
|
||||
}
|
||||
// NTP standard (RFC1305, section 3.1) explicitly state value 0 is invalid.
|
||||
bool Valid() const { return value_ != 0; }
|
||||
|
||||
uint32_t seconds() const {
|
||||
return rtc::dchecked_cast<uint32_t>(value_ / kFractionsPerSecond);
|
||||
}
|
||||
uint32_t fractions() const {
|
||||
return rtc::dchecked_cast<uint32_t>(value_ % kFractionsPerSecond);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t value_;
|
||||
};
|
||||
|
||||
inline bool operator==(const NtpTime& n1, const NtpTime& n2) {
|
||||
return static_cast<uint64_t>(n1) == static_cast<uint64_t>(n2);
|
||||
}
|
||||
inline bool operator!=(const NtpTime& n1, const NtpTime& n2) {
|
||||
return !(n1 == n2);
|
||||
}
|
||||
|
||||
// Converts `int64_t` milliseconds to Q32.32-formatted fixed-point seconds.
|
||||
// Performs clamping if the result overflows or underflows.
|
||||
inline int64_t Int64MsToQ32x32(int64_t milliseconds) {
|
||||
// TODO(bugs.webrtc.org/10893): Change to use `rtc::saturated_cast` once the
|
||||
// bug has been fixed.
|
||||
double result =
|
||||
std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
|
||||
|
||||
// Explicitly cast values to double to avoid implicit conversion warnings
|
||||
// The conversion of the std::numeric_limits<int64_t>::max() triggers
|
||||
// -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit
|
||||
// cast
|
||||
if (result <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
|
||||
return std::numeric_limits<int64_t>::min();
|
||||
}
|
||||
|
||||
if (result >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
|
||||
return std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
|
||||
return rtc::dchecked_cast<int64_t>(result);
|
||||
}
|
||||
|
||||
// Converts `int64_t` milliseconds to UQ32.32-formatted fixed-point seconds.
|
||||
// Performs clamping if the result overflows or underflows.
|
||||
inline uint64_t Int64MsToUQ32x32(int64_t milliseconds) {
|
||||
// TODO(bugs.webrtc.org/10893): Change to use `rtc::saturated_cast` once the
|
||||
// bug has been fixed.
|
||||
double result =
|
||||
std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
|
||||
|
||||
// Explicitly cast values to double to avoid implicit conversion warnings
|
||||
// The conversion of the std::numeric_limits<int64_t>::max() triggers
|
||||
// -Wimplicit-int-float-conversion warning in clang 10.0.0 without explicit
|
||||
// cast
|
||||
if (result <= static_cast<double>(std::numeric_limits<uint64_t>::min())) {
|
||||
return std::numeric_limits<uint64_t>::min();
|
||||
}
|
||||
|
||||
if (result >= static_cast<double>(std::numeric_limits<uint64_t>::max())) {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
return rtc::dchecked_cast<uint64_t>(result);
|
||||
}
|
||||
|
||||
// Converts Q32.32-formatted fixed-point seconds to `int64_t` milliseconds.
|
||||
inline int64_t Q32x32ToInt64Ms(int64_t q32x32) {
|
||||
return rtc::dchecked_cast<int64_t>(
|
||||
std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
|
||||
}
|
||||
|
||||
// Converts UQ32.32-formatted fixed-point seconds to `int64_t` milliseconds.
|
||||
inline int64_t UQ32x32ToInt64Ms(uint64_t q32x32) {
|
||||
return rtc::dchecked_cast<int64_t>(
|
||||
std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
|
||||
}
|
||||
|
||||
// Converts UQ32.32-formatted fixed-point seconds to `int64_t` microseconds.
|
||||
inline int64_t UQ32x32ToInt64Us(uint64_t q32x32) {
|
||||
return rtc::dchecked_cast<int64_t>(
|
||||
std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond)));
|
||||
}
|
||||
|
||||
// Converts Q32.32-formatted fixed-point seconds to `int64_t` microseconds.
|
||||
inline int64_t Q32x32ToInt64Us(int64_t q32x32) {
|
||||
return rtc::dchecked_cast<int64_t>(
|
||||
std::round(q32x32 * (1'000'000.0 / NtpTime::kFractionsPerSecond)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
|
||||
61
src/qos/ntp_time_util.h
Normal file
61
src/qos/ntp_time_util.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "ntp_time.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Helper function for compact ntp representation:
|
||||
// RFC 3550, Section 4. Time Format.
|
||||
// Wallclock time is represented using the timestamp format of
|
||||
// the Network Time Protocol (NTP).
|
||||
// ...
|
||||
// In some fields where a more compact representation is
|
||||
// appropriate, only the middle 32 bits are used; that is, the low 16
|
||||
// bits of the integer part and the high 16 bits of the fractional part.
|
||||
inline uint32_t CompactNtp(NtpTime ntp) {
|
||||
return (ntp.seconds() << 16) | (ntp.fractions() >> 16);
|
||||
}
|
||||
|
||||
// Converts interval to compact ntp (1/2^16 seconds) resolution.
|
||||
// Negative values converted to 0, Overlarge values converted to max uint32_t.
|
||||
uint32_t SaturatedToCompactNtp(TimeDelta delta);
|
||||
|
||||
// Convert interval to the NTP time resolution (1/2^32 seconds ~= 0.2 ns).
|
||||
// For deltas with absolute value larger than 35 minutes result is unspecified.
|
||||
inline constexpr int64_t ToNtpUnits(TimeDelta delta) {
|
||||
// For better precision `delta` is taken with best TimeDelta precision (us),
|
||||
// then multiplaction and conversion to seconds are swapped to avoid float
|
||||
// arithmetic.
|
||||
// 2^31 us ~= 35.8 minutes.
|
||||
return (rtc::saturated_cast<int32_t>(delta.us()) * (int64_t{1} << 32)) /
|
||||
1'000'000;
|
||||
}
|
||||
|
||||
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
|
||||
// This interval can be up to ~9.1 hours (2^15 seconds).
|
||||
// Values close to 2^16 seconds are considered negative.
|
||||
TimeDelta CompactNtpIntervalToTimeDelta(uint32_t compact_ntp_interval);
|
||||
|
||||
// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta.
|
||||
// This interval can be up to ~9.1 hours (2^15 seconds).
|
||||
// Values close to 2^16 seconds are considered negative and are converted to
|
||||
// minimum value of 1ms.
|
||||
TimeDelta CompactNtpRttToTimeDelta(uint32_t compact_ntp_interval);
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_NTP_TIME_UTIL_H_
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bandwidth_usage.h"
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
136
src/qos/overuse_estimator.cc
Normal file
136
src/qos/overuse_estimator.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "overuse_estimator.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
constexpr int kMinFramePeriodHistoryLength = 60;
|
||||
constexpr int kDeltaCounterMax = 1000;
|
||||
|
||||
} // namespace
|
||||
|
||||
OveruseEstimator::OveruseEstimator() = default;
|
||||
|
||||
void OveruseEstimator::Update(int64_t t_delta, double ts_delta, int size_delta,
|
||||
BandwidthUsage current_hypothesis,
|
||||
int64_t /* now_ms */) {
|
||||
const double min_frame_period = UpdateMinFramePeriod(ts_delta);
|
||||
const double t_ts_delta = t_delta - ts_delta;
|
||||
double fs_delta = size_delta;
|
||||
|
||||
++num_of_deltas_;
|
||||
if (num_of_deltas_ > kDeltaCounterMax) {
|
||||
num_of_deltas_ = kDeltaCounterMax;
|
||||
}
|
||||
|
||||
// Update the Kalman filter.
|
||||
E_[0][0] += process_noise_[0];
|
||||
E_[1][1] += process_noise_[1];
|
||||
|
||||
if ((current_hypothesis == BandwidthUsage::kBwOverusing &&
|
||||
offset_ < prev_offset_) ||
|
||||
(current_hypothesis == BandwidthUsage::kBwUnderusing &&
|
||||
offset_ > prev_offset_)) {
|
||||
E_[1][1] += 10 * process_noise_[1];
|
||||
}
|
||||
|
||||
const double h[2] = {fs_delta, 1.0};
|
||||
const double Eh[2] = {E_[0][0] * h[0] + E_[0][1] * h[1],
|
||||
E_[1][0] * h[0] + E_[1][1] * h[1]};
|
||||
|
||||
const double residual = t_ts_delta - slope_ * h[0] - offset_;
|
||||
|
||||
const bool in_stable_state =
|
||||
(current_hypothesis == BandwidthUsage::kBwNormal);
|
||||
const double max_residual = 3.0 * sqrt(var_noise_);
|
||||
// We try to filter out very late frames. For instance periodic key
|
||||
// frames doesn't fit the Gaussian model well.
|
||||
if (fabs(residual) < max_residual) {
|
||||
UpdateNoiseEstimate(residual, min_frame_period, in_stable_state);
|
||||
} else {
|
||||
UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual,
|
||||
min_frame_period, in_stable_state);
|
||||
}
|
||||
|
||||
const double denom = var_noise_ + h[0] * Eh[0] + h[1] * Eh[1];
|
||||
|
||||
const double K[2] = {Eh[0] / denom, Eh[1] / denom};
|
||||
|
||||
const double IKh[2][2] = {{1.0 - K[0] * h[0], -K[0] * h[1]},
|
||||
{-K[1] * h[0], 1.0 - K[1] * h[1]}};
|
||||
const double e00 = E_[0][0];
|
||||
const double e01 = E_[0][1];
|
||||
|
||||
// Update state.
|
||||
E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
|
||||
E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
|
||||
E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
|
||||
E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
|
||||
|
||||
// The covariance matrix must be positive semi-definite.
|
||||
bool positive_semi_definite =
|
||||
E_[0][0] + E_[1][1] >= 0 &&
|
||||
E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0;
|
||||
if (!positive_semi_definite) {
|
||||
LOG_ERROR(
|
||||
"The over-use estimator's covariance matrix is no longer "
|
||||
"semi-definite.");
|
||||
}
|
||||
|
||||
slope_ = slope_ + K[0] * residual;
|
||||
prev_offset_ = offset_;
|
||||
offset_ = offset_ + K[1] * residual;
|
||||
}
|
||||
|
||||
double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) {
|
||||
double min_frame_period = ts_delta;
|
||||
if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
|
||||
ts_delta_hist_.pop_front();
|
||||
}
|
||||
for (const double old_ts_delta : ts_delta_hist_) {
|
||||
min_frame_period = std::min(old_ts_delta, min_frame_period);
|
||||
}
|
||||
ts_delta_hist_.push_back(ts_delta);
|
||||
return min_frame_period;
|
||||
}
|
||||
|
||||
void OveruseEstimator::UpdateNoiseEstimate(double residual, double ts_delta,
|
||||
bool stable_state) {
|
||||
if (!stable_state) {
|
||||
return;
|
||||
}
|
||||
// Faster filter during startup to faster adapt to the jitter level
|
||||
// of the network. `alpha` is tuned for 30 frames per second, but is scaled
|
||||
// according to `ts_delta`.
|
||||
double alpha = 0.01;
|
||||
if (num_of_deltas_ > 10 * 30) {
|
||||
alpha = 0.002;
|
||||
}
|
||||
// Only update the noise estimate if we're not over-using. `beta` is a
|
||||
// function of alpha and the time delta since the previous update.
|
||||
const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
|
||||
avg_noise_ = beta * avg_noise_ + (1 - beta) * residual;
|
||||
var_noise_ = beta * var_noise_ +
|
||||
(1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
|
||||
if (var_noise_ < 1) {
|
||||
var_noise_ = 1;
|
||||
}
|
||||
}
|
||||
} // namespace webrtc
|
||||
66
src/qos/overuse_estimator.h
Normal file
66
src/qos/overuse_estimator.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
||||
#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class OveruseEstimator {
|
||||
public:
|
||||
OveruseEstimator();
|
||||
|
||||
OveruseEstimator(const OveruseEstimator&) = delete;
|
||||
OveruseEstimator& operator=(const OveruseEstimator&) = delete;
|
||||
|
||||
~OveruseEstimator() = default;
|
||||
|
||||
// Update the estimator with a new sample. The deltas should represent deltas
|
||||
// between timestamp groups as defined by the InterArrival class.
|
||||
// `current_hypothesis` should be the hypothesis of the over-use detector at
|
||||
// this time.
|
||||
void Update(int64_t t_delta,
|
||||
double ts_delta,
|
||||
int size_delta,
|
||||
BandwidthUsage current_hypothesis,
|
||||
int64_t now_ms);
|
||||
|
||||
// Returns the estimated noise/jitter variance in ms^2.
|
||||
double var_noise() const { return var_noise_; }
|
||||
|
||||
// Returns the estimated inter-arrival time delta offset in ms.
|
||||
double offset() const { return offset_; }
|
||||
|
||||
// Returns the number of deltas which the current over-use estimator state is
|
||||
// based on.
|
||||
int num_of_deltas() const { return num_of_deltas_; }
|
||||
|
||||
private:
|
||||
double UpdateMinFramePeriod(double ts_delta);
|
||||
void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state);
|
||||
|
||||
int num_of_deltas_ = 0;
|
||||
double slope_ = 8.0 / 512.0;
|
||||
double offset_ = 0;
|
||||
double prev_offset_ = 0;
|
||||
double E_[2][2] = {{100.0, 0.0}, {0.0, 1e-1}};
|
||||
double process_noise_[2] = {1e-13, 1e-3};
|
||||
double avg_noise_ = 0.0;
|
||||
double var_noise_ = 50.0;
|
||||
std::deque<double> ts_delta_hist_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_
|
||||
@@ -14,12 +14,12 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "log.h"
|
||||
#include "network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "network_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
class RtcEventLog;
|
||||
|
||||
536
src/qos/probe_controller.cc
Normal file
536
src/qos/probe_controller.cc
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "probe_controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
// Maximum waiting time from the time of initiating probing to getting
|
||||
// the measured results back.
|
||||
constexpr TimeDelta kMaxWaitingTimeForProbingResult = TimeDelta::Seconds(1);
|
||||
|
||||
// Default probing bitrate limit. Applied only when the application didn't
|
||||
// specify max bitrate.
|
||||
constexpr DataRate kDefaultMaxProbingBitrate = DataRate::KilobitsPerSec(5000);
|
||||
|
||||
// If the bitrate drops to a factor `kBitrateDropThreshold` or lower
|
||||
// and we recover within `kBitrateDropTimeoutMs`, then we'll send
|
||||
// a probe at a fraction `kProbeFractionAfterDrop` of the original bitrate.
|
||||
constexpr double kBitrateDropThreshold = 0.66;
|
||||
constexpr TimeDelta kBitrateDropTimeout = TimeDelta::Seconds(5);
|
||||
constexpr double kProbeFractionAfterDrop = 0.85;
|
||||
|
||||
// Timeout for probing after leaving ALR. If the bitrate drops significantly,
|
||||
// (as determined by the delay based estimator) and we leave ALR, then we will
|
||||
// send a probe if we recover within `kLeftAlrTimeoutMs` ms.
|
||||
constexpr TimeDelta kAlrEndedTimeout = TimeDelta::Seconds(3);
|
||||
|
||||
// The expected uncertainty of probe result (as a fraction of the target probe
|
||||
// This is a limit on how often probing can be done when there is a BW
|
||||
// drop detected in ALR.
|
||||
constexpr TimeDelta kMinTimeBetweenAlrProbes = TimeDelta::Seconds(5);
|
||||
|
||||
// bitrate). Used to avoid probing if the probe bitrate is close to our current
|
||||
// estimate.
|
||||
constexpr double kProbeUncertainty = 0.05;
|
||||
|
||||
// Use probing to recover faster after large bitrate estimate drops.
|
||||
constexpr char kBweRapidRecoveryExperiment[] =
|
||||
"WebRTC-BweRapidRecoveryExperiment";
|
||||
|
||||
} // namespace
|
||||
|
||||
ProbeControllerConfig::ProbeControllerConfig()
|
||||
: first_exponential_probe_scale(3.0),
|
||||
second_exponential_probe_scale(6.0),
|
||||
further_exponential_probe_scale(2),
|
||||
further_probe_threshold(0.7),
|
||||
abort_further_probe_if_max_lower_than_current(false),
|
||||
repeated_initial_probing_time_period(TimeDelta::Seconds(5)),
|
||||
initial_probe_duration(TimeDelta::Millis(100)),
|
||||
initial_min_probe_delta(TimeDelta::Millis(20)),
|
||||
alr_probing_interval(TimeDelta::Seconds(5)),
|
||||
alr_probe_scale(2),
|
||||
network_state_estimate_probing_interval(TimeDelta::PlusInfinity()),
|
||||
probe_if_estimate_lower_than_network_state_estimate_ratio(0),
|
||||
estimate_lower_than_network_state_estimate_probing_interval(
|
||||
TimeDelta::Seconds(3)),
|
||||
network_state_probe_scale(1.0),
|
||||
network_state_probe_duration(TimeDelta::Millis(15)),
|
||||
network_state_min_probe_delta(TimeDelta::Millis(20)),
|
||||
probe_on_max_allocated_bitrate_change(true),
|
||||
first_allocation_probe_scale(1),
|
||||
second_allocation_probe_scale(2),
|
||||
allocation_probe_limit_by_current_scale(2),
|
||||
min_probe_packets_sent(5),
|
||||
min_probe_duration(TimeDelta::Millis(15)),
|
||||
min_probe_delta(TimeDelta::Millis(2)),
|
||||
loss_limited_probe_scale(1.5),
|
||||
skip_if_estimate_larger_than_fraction_of_max(0.0),
|
||||
skip_probe_max_allocated_scale(1.0) {}
|
||||
|
||||
ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) =
|
||||
default;
|
||||
ProbeControllerConfig::~ProbeControllerConfig() = default;
|
||||
|
||||
ProbeController::ProbeController()
|
||||
: network_available_(false), enable_periodic_alr_probing_(false) {
|
||||
Reset(Timestamp::Zero());
|
||||
}
|
||||
|
||||
ProbeController::~ProbeController() {}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::SetBitrates(
|
||||
DataRate min_bitrate, DataRate start_bitrate, DataRate max_bitrate,
|
||||
Timestamp at_time) {
|
||||
if (start_bitrate > DataRate::Zero()) {
|
||||
start_bitrate_ = start_bitrate;
|
||||
estimated_bitrate_ = start_bitrate;
|
||||
} else if (start_bitrate_.IsZero()) {
|
||||
start_bitrate_ = min_bitrate;
|
||||
}
|
||||
|
||||
// The reason we use the variable `old_max_bitrate_pbs` is because we
|
||||
// need to set `max_bitrate_` before we call InitiateProbing.
|
||||
DataRate old_max_bitrate = max_bitrate_;
|
||||
max_bitrate_ =
|
||||
max_bitrate.IsFinite() ? max_bitrate : kDefaultMaxProbingBitrate;
|
||||
|
||||
switch (state_) {
|
||||
case State::kInit:
|
||||
if (network_available_) return InitiateExponentialProbing(at_time);
|
||||
break;
|
||||
|
||||
case State::kWaitingForProbingResult:
|
||||
break;
|
||||
|
||||
case State::kProbingComplete:
|
||||
// If the new max bitrate is higher than both the old max bitrate and the
|
||||
// estimate then initiate probing.
|
||||
if (!estimated_bitrate_.IsZero() && old_max_bitrate < max_bitrate_ &&
|
||||
estimated_bitrate_ < max_bitrate_) {
|
||||
return InitiateProbing(at_time, {max_bitrate_}, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::OnMaxTotalAllocatedBitrate(
|
||||
DataRate max_total_allocated_bitrate, Timestamp at_time) {
|
||||
const bool in_alr = alr_start_time_.has_value();
|
||||
const bool allow_allocation_probe = in_alr;
|
||||
if (config_.probe_on_max_allocated_bitrate_change &&
|
||||
state_ == State::kProbingComplete &&
|
||||
max_total_allocated_bitrate != max_total_allocated_bitrate_ &&
|
||||
estimated_bitrate_ < max_bitrate_ &&
|
||||
estimated_bitrate_ < max_total_allocated_bitrate &&
|
||||
allow_allocation_probe) {
|
||||
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
|
||||
|
||||
if (!config_.first_allocation_probe_scale)
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
|
||||
DataRate first_probe_rate =
|
||||
max_total_allocated_bitrate * config_.first_allocation_probe_scale;
|
||||
const DataRate current_bwe_limit =
|
||||
config_.allocation_probe_limit_by_current_scale * estimated_bitrate_;
|
||||
bool limited_by_current_bwe = current_bwe_limit < first_probe_rate;
|
||||
if (limited_by_current_bwe) {
|
||||
first_probe_rate = current_bwe_limit;
|
||||
}
|
||||
|
||||
std::vector<DataRate> probes = {first_probe_rate};
|
||||
if (!limited_by_current_bwe && config_.second_allocation_probe_scale) {
|
||||
DataRate second_probe_rate =
|
||||
max_total_allocated_bitrate * config_.second_allocation_probe_scale;
|
||||
limited_by_current_bwe = current_bwe_limit < second_probe_rate;
|
||||
if (limited_by_current_bwe) {
|
||||
second_probe_rate = current_bwe_limit;
|
||||
}
|
||||
if (second_probe_rate > first_probe_rate)
|
||||
probes.push_back(second_probe_rate);
|
||||
}
|
||||
bool allow_further_probing = limited_by_current_bwe;
|
||||
|
||||
return InitiateProbing(at_time, probes, allow_further_probing);
|
||||
}
|
||||
if (!max_total_allocated_bitrate.IsZero()) {
|
||||
last_allowed_repeated_initial_probe_ = at_time;
|
||||
}
|
||||
|
||||
max_total_allocated_bitrate_ = max_total_allocated_bitrate;
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::OnNetworkAvailability(
|
||||
NetworkAvailability msg) {
|
||||
network_available_ = msg.network_available;
|
||||
|
||||
if (!network_available_ && state_ == State::kWaitingForProbingResult) {
|
||||
state_ = State::kProbingComplete;
|
||||
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
|
||||
}
|
||||
|
||||
if (network_available_ && state_ == State::kInit && !start_bitrate_.IsZero())
|
||||
return InitiateExponentialProbing(msg.at_time);
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
}
|
||||
|
||||
void ProbeController::UpdateState(State new_state) {
|
||||
switch (new_state) {
|
||||
case State::kInit:
|
||||
state_ = State::kInit;
|
||||
break;
|
||||
case State::kWaitingForProbingResult:
|
||||
state_ = State::kWaitingForProbingResult;
|
||||
break;
|
||||
case State::kProbingComplete:
|
||||
state_ = State::kProbingComplete;
|
||||
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::InitiateExponentialProbing(
|
||||
Timestamp at_time) {
|
||||
// When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
|
||||
// 1.2 Mbps to continue probing.
|
||||
std::vector<DataRate> probes = {config_.first_exponential_probe_scale *
|
||||
start_bitrate_};
|
||||
if (config_.second_exponential_probe_scale &&
|
||||
config_.second_exponential_probe_scale > 0) {
|
||||
probes.push_back(config_.second_exponential_probe_scale * start_bitrate_);
|
||||
}
|
||||
if (repeated_initial_probing_enabled_ &&
|
||||
max_total_allocated_bitrate_.IsZero()) {
|
||||
last_allowed_repeated_initial_probe_ =
|
||||
at_time + config_.repeated_initial_probing_time_period;
|
||||
LOG_INFO("Repeated initial probing enabled, last allowed probe: {} now: {}",
|
||||
last_allowed_repeated_initial_probe_, at_time);
|
||||
}
|
||||
|
||||
return InitiateProbing(at_time, probes, true);
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::SetEstimatedBitrate(
|
||||
DataRate bitrate, BandwidthLimitedCause bandwidth_limited_cause,
|
||||
Timestamp at_time) {
|
||||
bandwidth_limited_cause_ = bandwidth_limited_cause;
|
||||
if (bitrate < kBitrateDropThreshold * estimated_bitrate_) {
|
||||
time_of_last_large_drop_ = at_time;
|
||||
bitrate_before_last_large_drop_ = estimated_bitrate_;
|
||||
}
|
||||
estimated_bitrate_ = bitrate;
|
||||
|
||||
if (state_ == State::kWaitingForProbingResult) {
|
||||
// Continue probing if probing results indicate channel has greater
|
||||
// capacity unless we already reached the needed bitrate.
|
||||
if (config_.abort_further_probe_if_max_lower_than_current &&
|
||||
(bitrate > max_bitrate_ ||
|
||||
(!max_total_allocated_bitrate_.IsZero() &&
|
||||
bitrate > 2 * max_total_allocated_bitrate_))) {
|
||||
// No need to continue probing.
|
||||
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
|
||||
}
|
||||
DataRate network_state_estimate_probe_further_limit =
|
||||
config_.network_state_estimate_probing_interval.IsFinite() &&
|
||||
network_estimate_ &&
|
||||
network_estimate_->link_capacity_upper.IsFinite()
|
||||
? network_estimate_->link_capacity_upper *
|
||||
config_.further_probe_threshold
|
||||
: DataRate::PlusInfinity();
|
||||
LOG_INFO(
|
||||
"Measured bitrate: {} Minimum to probe further: {} upper limit: {}",
|
||||
bitrate, min_bitrate_to_probe_further_,
|
||||
network_state_estimate_probe_further_limit);
|
||||
|
||||
if (bitrate > min_bitrate_to_probe_further_ &&
|
||||
bitrate <= network_state_estimate_probe_further_limit) {
|
||||
return InitiateProbing(
|
||||
at_time, {config_.further_exponential_probe_scale * bitrate}, true);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ProbeController::EnablePeriodicAlrProbing(bool enable) {
|
||||
enable_periodic_alr_probing_ = enable;
|
||||
}
|
||||
|
||||
void ProbeController::EnableRepeatedInitialProbing(bool enable) {
|
||||
repeated_initial_probing_enabled_ = enable;
|
||||
}
|
||||
|
||||
void ProbeController::SetAlrStartTimeMs(
|
||||
std::optional<int64_t> alr_start_time_ms) {
|
||||
if (alr_start_time_ms) {
|
||||
alr_start_time_ = Timestamp::Millis(*alr_start_time_ms);
|
||||
} else {
|
||||
alr_start_time_ = std::nullopt;
|
||||
}
|
||||
}
|
||||
void ProbeController::SetAlrEndedTimeMs(int64_t alr_end_time_ms) {
|
||||
alr_end_time_.emplace(Timestamp::Millis(alr_end_time_ms));
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::RequestProbe(
|
||||
Timestamp at_time) {
|
||||
// Called once we have returned to normal state after a large drop in
|
||||
// estimated bandwidth. The current response is to initiate a single probe
|
||||
// session (if not already probing) at the previous bitrate.
|
||||
//
|
||||
// If the probe session fails, the assumption is that this drop was a
|
||||
// real one from a competing flow or a network change.
|
||||
bool in_alr = alr_start_time_.has_value();
|
||||
bool alr_ended_recently =
|
||||
(alr_end_time_.has_value() &&
|
||||
at_time - alr_end_time_.value() < kAlrEndedTimeout);
|
||||
if (in_alr || alr_ended_recently) {
|
||||
if (state_ == State::kProbingComplete) {
|
||||
DataRate suggested_probe =
|
||||
kProbeFractionAfterDrop * bitrate_before_last_large_drop_;
|
||||
DataRate min_expected_probe_result =
|
||||
(1 - kProbeUncertainty) * suggested_probe;
|
||||
TimeDelta time_since_drop = at_time - time_of_last_large_drop_;
|
||||
TimeDelta time_since_probe = at_time - last_bwe_drop_probing_time_;
|
||||
if (min_expected_probe_result > estimated_bitrate_ &&
|
||||
time_since_drop < kBitrateDropTimeout &&
|
||||
time_since_probe > kMinTimeBetweenAlrProbes) {
|
||||
LOG_INFO("Detected big bandwidth drop, start probing");
|
||||
last_bwe_drop_probing_time_ = at_time;
|
||||
return InitiateProbing(at_time, {suggested_probe}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
}
|
||||
|
||||
void ProbeController::SetNetworkStateEstimate(
|
||||
webrtc::NetworkStateEstimate estimate) {
|
||||
network_estimate_ = estimate;
|
||||
}
|
||||
|
||||
void ProbeController::Reset(Timestamp at_time) {
|
||||
bandwidth_limited_cause_ = BandwidthLimitedCause::kDelayBasedLimited;
|
||||
state_ = State::kInit;
|
||||
min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
|
||||
time_last_probing_initiated_ = Timestamp::Zero();
|
||||
estimated_bitrate_ = DataRate::Zero();
|
||||
network_estimate_ = std::nullopt;
|
||||
start_bitrate_ = DataRate::Zero();
|
||||
max_bitrate_ = kDefaultMaxProbingBitrate;
|
||||
Timestamp now = at_time;
|
||||
last_bwe_drop_probing_time_ = now;
|
||||
alr_end_time_.reset();
|
||||
time_of_last_large_drop_ = now;
|
||||
bitrate_before_last_large_drop_ = DataRate::Zero();
|
||||
}
|
||||
|
||||
bool ProbeController::TimeForAlrProbe(Timestamp at_time) const {
|
||||
if (enable_periodic_alr_probing_ && alr_start_time_) {
|
||||
Timestamp next_probe_time =
|
||||
std::max(*alr_start_time_, time_last_probing_initiated_) +
|
||||
config_.alr_probing_interval;
|
||||
return at_time >= next_probe_time;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProbeController::TimeForNetworkStateProbe(Timestamp at_time) const {
|
||||
if (!network_estimate_ ||
|
||||
network_estimate_->link_capacity_upper.IsInfinite()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool probe_due_to_low_estimate =
|
||||
bandwidth_limited_cause_ == BandwidthLimitedCause::kDelayBasedLimited &&
|
||||
estimated_bitrate_ <
|
||||
config_.probe_if_estimate_lower_than_network_state_estimate_ratio *
|
||||
network_estimate_->link_capacity_upper;
|
||||
if (probe_due_to_low_estimate &&
|
||||
config_.estimate_lower_than_network_state_estimate_probing_interval
|
||||
.IsFinite()) {
|
||||
Timestamp next_probe_time =
|
||||
time_last_probing_initiated_ +
|
||||
config_.estimate_lower_than_network_state_estimate_probing_interval;
|
||||
return at_time >= next_probe_time;
|
||||
}
|
||||
|
||||
bool periodic_probe =
|
||||
estimated_bitrate_ < network_estimate_->link_capacity_upper;
|
||||
if (periodic_probe &&
|
||||
config_.network_state_estimate_probing_interval.IsFinite()) {
|
||||
Timestamp next_probe_time = time_last_probing_initiated_ +
|
||||
config_.network_state_estimate_probing_interval;
|
||||
return at_time >= next_probe_time;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProbeController::TimeForNextRepeatedInitialProbe(Timestamp at_time) const {
|
||||
if (state_ != State::kWaitingForProbingResult &&
|
||||
last_allowed_repeated_initial_probe_ > at_time) {
|
||||
Timestamp next_probe_time =
|
||||
time_last_probing_initiated_ + kMaxWaitingTimeForProbingResult;
|
||||
if (at_time >= next_probe_time) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::Process(Timestamp at_time) {
|
||||
if (at_time - time_last_probing_initiated_ >
|
||||
kMaxWaitingTimeForProbingResult) {
|
||||
if (state_ == State::kWaitingForProbingResult) {
|
||||
LOG_INFO("kWaitingForProbingResult: timeout");
|
||||
UpdateState(State::kProbingComplete);
|
||||
}
|
||||
}
|
||||
if (estimated_bitrate_.IsZero() || state_ != State::kProbingComplete) {
|
||||
return {};
|
||||
}
|
||||
if (TimeForNextRepeatedInitialProbe(at_time)) {
|
||||
return InitiateProbing(
|
||||
at_time, {estimated_bitrate_ * config_.first_exponential_probe_scale},
|
||||
true);
|
||||
}
|
||||
if (TimeForAlrProbe(at_time) || TimeForNetworkStateProbe(at_time)) {
|
||||
return InitiateProbing(
|
||||
at_time, {estimated_bitrate_ * config_.alr_probe_scale}, true);
|
||||
}
|
||||
return std::vector<ProbeClusterConfig>();
|
||||
}
|
||||
|
||||
ProbeClusterConfig ProbeController::CreateProbeClusterConfig(Timestamp at_time,
|
||||
DataRate bitrate) {
|
||||
ProbeClusterConfig config;
|
||||
config.at_time = at_time;
|
||||
config.target_data_rate = bitrate;
|
||||
if (network_estimate_ &&
|
||||
config_.network_state_estimate_probing_interval.IsFinite() &&
|
||||
network_estimate_->link_capacity_upper.IsFinite() &&
|
||||
network_estimate_->link_capacity_upper >= bitrate) {
|
||||
config.target_duration = config_.network_state_probe_duration;
|
||||
config.min_probe_delta = config_.network_state_min_probe_delta;
|
||||
} else if (at_time < last_allowed_repeated_initial_probe_) {
|
||||
config.target_duration = config_.initial_probe_duration;
|
||||
config.min_probe_delta = config_.initial_min_probe_delta;
|
||||
} else {
|
||||
config.target_duration = config_.min_probe_duration;
|
||||
config.min_probe_delta = config_.min_probe_delta;
|
||||
}
|
||||
config.target_probe_count = config_.min_probe_packets_sent;
|
||||
config.id = next_probe_cluster_id_;
|
||||
next_probe_cluster_id_++;
|
||||
return config;
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
|
||||
Timestamp now, std::vector<DataRate> bitrates_to_probe,
|
||||
bool probe_further) {
|
||||
if (config_.skip_if_estimate_larger_than_fraction_of_max > 0) {
|
||||
DataRate network_estimate = network_estimate_
|
||||
? network_estimate_->link_capacity_upper
|
||||
: DataRate::PlusInfinity();
|
||||
DataRate max_probe_rate =
|
||||
max_total_allocated_bitrate_.IsZero()
|
||||
? max_bitrate_
|
||||
: std::min(config_.skip_probe_max_allocated_scale *
|
||||
max_total_allocated_bitrate_,
|
||||
max_bitrate_);
|
||||
if (std::min(network_estimate, estimated_bitrate_) >
|
||||
config_.skip_if_estimate_larger_than_fraction_of_max * max_probe_rate) {
|
||||
UpdateState(State::kProbingComplete);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
DataRate max_probe_bitrate = max_bitrate_;
|
||||
if (max_total_allocated_bitrate_ > DataRate::Zero()) {
|
||||
// If a max allocated bitrate has been configured, allow probing up to 2x
|
||||
// that rate. This allows some overhead to account for bursty streams,
|
||||
// which otherwise would have to ramp up when the overshoot is already in
|
||||
// progress.
|
||||
// It also avoids minor quality reduction caused by probes often being
|
||||
// received at slightly less than the target probe bitrate.
|
||||
max_probe_bitrate =
|
||||
std::min(max_probe_bitrate, max_total_allocated_bitrate_ * 2);
|
||||
}
|
||||
|
||||
switch (bandwidth_limited_cause_) {
|
||||
case BandwidthLimitedCause::kRttBasedBackOffHighRtt:
|
||||
case BandwidthLimitedCause::kDelayBasedLimitedDelayIncreased:
|
||||
case BandwidthLimitedCause::kLossLimitedBwe:
|
||||
LOG_INFO("Not sending probe in bandwidth limited state {}",
|
||||
static_cast<int>(bandwidth_limited_cause_));
|
||||
return {};
|
||||
case BandwidthLimitedCause::kLossLimitedBweIncreasing:
|
||||
max_probe_bitrate =
|
||||
std::min(max_probe_bitrate,
|
||||
estimated_bitrate_ * config_.loss_limited_probe_scale);
|
||||
break;
|
||||
case BandwidthLimitedCause::kDelayBasedLimited:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (config_.network_state_estimate_probing_interval.IsFinite() &&
|
||||
network_estimate_ && network_estimate_->link_capacity_upper.IsFinite()) {
|
||||
if (network_estimate_->link_capacity_upper.IsZero()) {
|
||||
LOG_INFO("Not sending probe, Network state estimate is zero");
|
||||
return {};
|
||||
}
|
||||
max_probe_bitrate = std::min(
|
||||
{max_probe_bitrate,
|
||||
std::max(estimated_bitrate_, network_estimate_->link_capacity_upper *
|
||||
config_.network_state_probe_scale)});
|
||||
}
|
||||
|
||||
std::vector<ProbeClusterConfig> pending_probes;
|
||||
for (DataRate bitrate : bitrates_to_probe) {
|
||||
if (bitrate >= max_probe_bitrate) {
|
||||
bitrate = max_probe_bitrate;
|
||||
probe_further = false;
|
||||
}
|
||||
pending_probes.push_back(CreateProbeClusterConfig(now, bitrate));
|
||||
}
|
||||
time_last_probing_initiated_ = now;
|
||||
if (probe_further) {
|
||||
UpdateState(State::kWaitingForProbingResult);
|
||||
// Dont expect probe results to be larger than a fraction of the actual
|
||||
// probe rate.
|
||||
min_bitrate_to_probe_further_ = pending_probes.back().target_data_rate *
|
||||
config_.further_probe_threshold;
|
||||
} else {
|
||||
UpdateState(State::kProbingComplete);
|
||||
}
|
||||
return pending_probes;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
202
src/qos/probe_controller.h
Normal file
202
src/qos/probe_controller.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct ProbeControllerConfig {
|
||||
ProbeControllerConfig();
|
||||
ProbeControllerConfig(const ProbeControllerConfig&);
|
||||
ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default;
|
||||
~ProbeControllerConfig();
|
||||
|
||||
// These parameters configure the initial probes. First we send one or two
|
||||
// probes of sizes p1 * start_bitrate_ and p2 * start_bitrate_.
|
||||
// Then whenever we get a bitrate estimate of at least further_probe_threshold
|
||||
// times the size of the last sent probe we'll send another one of size
|
||||
// step_size times the new estimate.
|
||||
double first_exponential_probe_scale;
|
||||
double second_exponential_probe_scale;
|
||||
double further_exponential_probe_scale;
|
||||
double further_probe_threshold;
|
||||
bool abort_further_probe_if_max_lower_than_current;
|
||||
// Duration of time from the first initial probe where repeated initial probes
|
||||
// are sent if repeated initial probing is enabled.
|
||||
TimeDelta repeated_initial_probing_time_period;
|
||||
// The minimum probing duration of an individual probe during
|
||||
// the repeated_initial_probing_time_period.
|
||||
TimeDelta initial_probe_duration;
|
||||
// Delta time between sent bursts of packets in a probe during
|
||||
// the repeated_initial_probing_time_period.
|
||||
TimeDelta initial_min_probe_delta;
|
||||
// Configures how often we send ALR probes and how big they are.
|
||||
TimeDelta alr_probing_interval;
|
||||
double alr_probe_scale;
|
||||
// Configures how often we send probes if NetworkStateEstimate is available.
|
||||
TimeDelta network_state_estimate_probing_interval;
|
||||
// Periodically probe as long as the ratio between current estimate and
|
||||
// NetworkStateEstimate is lower then this.
|
||||
double probe_if_estimate_lower_than_network_state_estimate_ratio;
|
||||
TimeDelta estimate_lower_than_network_state_estimate_probing_interval;
|
||||
double network_state_probe_scale;
|
||||
// Overrides min_probe_duration if network_state_estimate_probing_interval
|
||||
// is set and a network state estimate is known and equal or higher than the
|
||||
// probe target.
|
||||
TimeDelta network_state_probe_duration;
|
||||
// Overrides min_probe_delta if network_state_estimate_probing_interval
|
||||
// is set and a network state estimate is known and equal or higher than the
|
||||
// probe target.
|
||||
TimeDelta network_state_min_probe_delta;
|
||||
|
||||
// Configures the probes emitted by changed to the allocated bitrate.
|
||||
bool probe_on_max_allocated_bitrate_change;
|
||||
double first_allocation_probe_scale;
|
||||
double second_allocation_probe_scale;
|
||||
double allocation_probe_limit_by_current_scale;
|
||||
|
||||
// The minimum number probing packets used.
|
||||
int min_probe_packets_sent;
|
||||
// The minimum probing duration.
|
||||
TimeDelta min_probe_duration;
|
||||
// Delta time between sent bursts of packets in a probe.
|
||||
TimeDelta min_probe_delta;
|
||||
double loss_limited_probe_scale;
|
||||
// Don't send a probe if min(estimate, network state estimate) is larger than
|
||||
// this fraction of the set max or max allocated bitrate.
|
||||
double skip_if_estimate_larger_than_fraction_of_max;
|
||||
// Scale factor of the max allocated bitrate. Used when deciding if a probe
|
||||
// can be skiped due to that the estimate is already high enough.
|
||||
double skip_probe_max_allocated_scale;
|
||||
};
|
||||
|
||||
// Reason that bandwidth estimate is limited. Bandwidth estimate can be limited
|
||||
// by either delay based bwe, or loss based bwe when it increases/decreases the
|
||||
// estimate.
|
||||
enum class BandwidthLimitedCause : int {
|
||||
kLossLimitedBweIncreasing = 0,
|
||||
kLossLimitedBwe = 1,
|
||||
kDelayBasedLimited = 2,
|
||||
kDelayBasedLimitedDelayIncreased = 3,
|
||||
kRttBasedBackOffHighRtt = 4
|
||||
};
|
||||
|
||||
// This class controls initiation of probing to estimate initial channel
|
||||
// capacity. There is also support for probing during a session when max
|
||||
// bitrate is adjusted by an application.
|
||||
class ProbeController {
|
||||
public:
|
||||
ProbeController();
|
||||
~ProbeController();
|
||||
|
||||
ProbeController(const ProbeController&) = delete;
|
||||
ProbeController& operator=(const ProbeController&) = delete;
|
||||
|
||||
std::vector<ProbeClusterConfig> SetBitrates(DataRate min_bitrate,
|
||||
DataRate start_bitrate,
|
||||
DataRate max_bitrate,
|
||||
Timestamp at_time);
|
||||
|
||||
// The total bitrate, as opposed to the max bitrate, is the sum of the
|
||||
// configured bitrates for all active streams.
|
||||
std::vector<ProbeClusterConfig> OnMaxTotalAllocatedBitrate(
|
||||
DataRate max_total_allocated_bitrate, Timestamp at_time);
|
||||
|
||||
std::vector<ProbeClusterConfig> OnNetworkAvailability(
|
||||
NetworkAvailability msg);
|
||||
|
||||
std::vector<ProbeClusterConfig> SetEstimatedBitrate(
|
||||
DataRate bitrate, BandwidthLimitedCause bandwidth_limited_cause,
|
||||
Timestamp at_time);
|
||||
|
||||
void EnablePeriodicAlrProbing(bool enable);
|
||||
|
||||
// Probes are sent periodically every 1s during the first 5s after the network
|
||||
// becomes available or until OnMaxTotalAllocatedBitrate is invoked with a
|
||||
// none zero max_total_allocated_bitrate (there are active streams being
|
||||
// sent.) Probe rate is up to max configured bitrate configured via
|
||||
// SetBitrates.
|
||||
void EnableRepeatedInitialProbing(bool enable);
|
||||
|
||||
void SetAlrStartTimeMs(std::optional<int64_t> alr_start_time);
|
||||
void SetAlrEndedTimeMs(int64_t alr_end_time);
|
||||
|
||||
std::vector<ProbeClusterConfig> RequestProbe(Timestamp at_time);
|
||||
|
||||
void SetNetworkStateEstimate(webrtc::NetworkStateEstimate estimate);
|
||||
|
||||
// Resets the ProbeController to a state equivalent to as if it was just
|
||||
// created EXCEPT for configuration settings like
|
||||
// `enable_periodic_alr_probing_` `network_available_` and
|
||||
// `max_total_allocated_bitrate_`.
|
||||
void Reset(Timestamp at_time);
|
||||
|
||||
std::vector<ProbeClusterConfig> Process(Timestamp at_time);
|
||||
|
||||
private:
|
||||
enum class State {
|
||||
// Initial state where no probing has been triggered yet.
|
||||
kInit,
|
||||
// Waiting for probing results to continue further probing.
|
||||
kWaitingForProbingResult,
|
||||
// Probing is complete.
|
||||
kProbingComplete,
|
||||
};
|
||||
|
||||
void UpdateState(State new_state);
|
||||
std::vector<ProbeClusterConfig> InitiateExponentialProbing(Timestamp at_time);
|
||||
std::vector<ProbeClusterConfig> InitiateProbing(
|
||||
Timestamp now, std::vector<DataRate> bitrates_to_probe,
|
||||
bool probe_further);
|
||||
bool TimeForAlrProbe(Timestamp at_time) const;
|
||||
bool TimeForNetworkStateProbe(Timestamp at_time) const;
|
||||
bool TimeForNextRepeatedInitialProbe(Timestamp at_time) const;
|
||||
ProbeClusterConfig CreateProbeClusterConfig(Timestamp at_time,
|
||||
DataRate bitrate);
|
||||
|
||||
bool network_available_;
|
||||
bool repeated_initial_probing_enabled_ = false;
|
||||
Timestamp last_allowed_repeated_initial_probe_ = Timestamp::MinusInfinity();
|
||||
BandwidthLimitedCause bandwidth_limited_cause_ =
|
||||
BandwidthLimitedCause::kDelayBasedLimited;
|
||||
State state_;
|
||||
DataRate min_bitrate_to_probe_further_ = DataRate::PlusInfinity();
|
||||
Timestamp time_last_probing_initiated_ = Timestamp::MinusInfinity();
|
||||
DataRate estimated_bitrate_ = DataRate::Zero();
|
||||
std::optional<webrtc::NetworkStateEstimate> network_estimate_;
|
||||
DataRate start_bitrate_ = DataRate::Zero();
|
||||
DataRate max_bitrate_ = DataRate::PlusInfinity();
|
||||
Timestamp last_bwe_drop_probing_time_ = Timestamp::Zero();
|
||||
std::optional<Timestamp> alr_start_time_;
|
||||
std::optional<Timestamp> alr_end_time_;
|
||||
bool enable_periodic_alr_probing_;
|
||||
Timestamp time_of_last_large_drop_ = Timestamp::MinusInfinity();
|
||||
DataRate bitrate_before_last_large_drop_ = DataRate::Zero();
|
||||
DataRate max_total_allocated_bitrate_ = DataRate::Zero();
|
||||
|
||||
int32_t next_probe_cluster_id_ = 1;
|
||||
|
||||
ProbeControllerConfig config_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_CONTROLLER_H_
|
||||
107
src/qos/receive_side_congestion_controller.cc
Normal file
107
src/qos/receive_side_congestion_controller.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "receive_side_congestion_controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/media_types.h"
|
||||
#include "api/transport/network_control.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "log.h"
|
||||
#include "remote_bitrate_estimator_single_stream.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
static const uint32_t kTimeOffsetSwitchThreshold = 30;
|
||||
} // namespace
|
||||
|
||||
void ReceiveSideCongestionController::OnRttUpdate(int64_t avg_rtt_ms,
|
||||
int64_t max_rtt_ms) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::RemoveStream(uint32_t ssrc) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
rbe_->RemoveStream(ssrc);
|
||||
}
|
||||
|
||||
DataRate ReceiveSideCongestionController::LatestReceiveSideEstimate() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return rbe_->LatestEstimate();
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::PickEstimator() {
|
||||
// When we don't see AST, wait for a few packets before going back to TOF.
|
||||
if (using_absolute_send_time_) {
|
||||
++packets_since_absolute_send_time_;
|
||||
if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) {
|
||||
LOG_INFO(
|
||||
"WrappingBitrateEstimator: Switching to transmission "
|
||||
"time offset RBE.");
|
||||
using_absolute_send_time_ = false;
|
||||
rbe_ = std::make_unique<RemoteBitrateEstimatorSingleStream>(
|
||||
clock_, &remb_throttler_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReceiveSideCongestionController::ReceiveSideCongestionController(
|
||||
std::shared_ptr<SimulatedClock> clock,
|
||||
RtpTransportFeedbackGenerator::RtcpSender feedback_sender,
|
||||
RembThrottler::RembSender remb_sender,
|
||||
std::shared_ptr<NetworkStateEstimator> network_state_estimator)
|
||||
: clock_(clock),
|
||||
remb_throttler_(std::move(remb_sender), clock.get()),
|
||||
congestion_control_feedback_generator_(clock, feedback_sender),
|
||||
rbe_(std::make_unique<RemoteBitrateEstimatorSingleStream>(
|
||||
clock, &remb_throttler_)),
|
||||
using_absolute_send_time_(false),
|
||||
packets_since_absolute_send_time_(0) {}
|
||||
|
||||
void ReceiveSideCongestionController::OnReceivedPacket(
|
||||
const RtpPacketReceived& packet, MediaType media_type) {
|
||||
congestion_control_feedback_generator_.OnReceivedPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
|
||||
DataRate send_bandwidth_estimate = DataRate::BitsPerSec(bitrate_bps);
|
||||
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
|
||||
send_bandwidth_estimate);
|
||||
}
|
||||
|
||||
TimeDelta ReceiveSideCongestionController::MaybeProcess() {
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
TimeDelta time_until = congestion_control_feedback_generator_.Process(now);
|
||||
return std::max(time_until, TimeDelta::Zero());
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
|
||||
DataRate bitrate) {
|
||||
remb_throttler_.SetMaxDesiredReceiveBitrate(bitrate);
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::SetTransportOverhead(
|
||||
DataSize overhead_per_packet) {
|
||||
congestion_control_feedback_generator_.SetTransportOverhead(
|
||||
overhead_per_packet);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "receive_side_congestion_controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
static const uint32_t kTimeOffsetSwitchThreshold = 30;
|
||||
} // namespace
|
||||
|
||||
ReceiveSideCongestionController::ReceiveSideCongestionController(
|
||||
RtcpSender feedback_sender)
|
||||
: congestion_control_feedback_generator_(feedback_sender),
|
||||
using_absolute_send_time_(false),
|
||||
packets_since_absolute_send_time_(0) {}
|
||||
|
||||
void ReceiveSideCongestionController::OnReceivedPacket(
|
||||
RtpPacketReceived& packet, MediaType media_type) {
|
||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
congestion_control_feedback_generator_.OnReceivedPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
|
||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
int64_t send_bandwidth_estimate = bitrate_bps;
|
||||
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
|
||||
send_bandwidth_estimate);
|
||||
}
|
||||
|
||||
int64_t ReceiveSideCongestionController::MaybeProcess() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
int64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch())
|
||||
.count();
|
||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
return congestion_control_feedback_generator_.Process(now_ms);
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
|
||||
int64_t bitrate) {
|
||||
// remb_throttler_.SetMaxDesiredReceiveBitrate(bitrate);
|
||||
}
|
||||
|
||||
void ReceiveSideCongestionController::SetTransportOverhead(
|
||||
int64_t overhead_per_packet) {
|
||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
congestion_control_feedback_generator_.SetTransportOverhead(
|
||||
overhead_per_packet);
|
||||
}
|
||||
@@ -1,55 +1,89 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-12-12
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef _RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||
#define _RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "api/media_types.h"
|
||||
#include "api/transport/network_control.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "congestion_control_feedback_generator.h"
|
||||
#include "module_common_types.h"
|
||||
#include "remb_throttler.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
class ReceiveSideCongestionController {
|
||||
public:
|
||||
enum MediaType { VIDEO, AUDIO, DATA };
|
||||
namespace webrtc {
|
||||
class RemoteBitrateEstimator;
|
||||
|
||||
// This class represents the congestion control state for receive
|
||||
// streams. For send side bandwidth estimation, this is simply
|
||||
// relaying for each received RTP packet back to the sender. While for
|
||||
// receive side bandwidth estimation, we do the estimation locally and
|
||||
// send our results back to the sender.
|
||||
class ReceiveSideCongestionController : public CallStatsObserver {
|
||||
public:
|
||||
ReceiveSideCongestionController(RtcpSender feedback_sender);
|
||||
~ReceiveSideCongestionController() = default;
|
||||
ReceiveSideCongestionController(
|
||||
std::shared_ptr<SimulatedClock> clock,
|
||||
RtpTransportFeedbackGenerator::RtcpSender feedback_sender,
|
||||
RembThrottler::RembSender remb_sender,
|
||||
std::shared_ptr<NetworkStateEstimator> network_state_estimator);
|
||||
|
||||
public:
|
||||
void OnReceivedPacket(RtpPacketReceived& packet, MediaType media_type);
|
||||
~ReceiveSideCongestionController() override = default;
|
||||
|
||||
void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type);
|
||||
|
||||
// Implements CallStatsObserver.
|
||||
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
|
||||
|
||||
// This is send bitrate, used to control the rate of feedback messages.
|
||||
void OnBitrateChanged(int bitrate_bps);
|
||||
|
||||
// Ensures the remote party is notified of the receive bitrate no larger than
|
||||
// `bitrate` using RTCP REMB.
|
||||
void SetMaxDesiredReceiveBitrate(int64_t bitrate);
|
||||
void SetMaxDesiredReceiveBitrate(DataRate bitrate);
|
||||
|
||||
void SetTransportOverhead(int64_t overhead_per_packet);
|
||||
void SetTransportOverhead(DataSize overhead_per_packet);
|
||||
|
||||
// Returns latest receive side bandwidth estimation.
|
||||
// Returns zero if receive side bandwidth estimation is unavailable.
|
||||
DataRate LatestReceiveSideEstimate() const;
|
||||
|
||||
// Removes stream from receive side bandwidth estimation.
|
||||
// Noop if receive side bwe is not used or stream doesn't participate in it.
|
||||
void RemoveStream(uint32_t ssrc);
|
||||
|
||||
// Runs periodic tasks if it is time to run them, returns time until next
|
||||
// call to `MaybeProcess` should be non idle.
|
||||
int64_t MaybeProcess();
|
||||
TimeDelta MaybeProcess();
|
||||
|
||||
private:
|
||||
// RembThrottler remb_throttler_;
|
||||
void PickEstimator();
|
||||
|
||||
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
|
||||
// ReceiveSideCongestionController. At the time of
|
||||
// writing OnReceivedPacket and MaybeProcess can unfortunately be called on an
|
||||
// arbitrary thread by external projects.
|
||||
// SequenceChecker sequence_checker_;
|
||||
std::shared_ptr<SimulatedClock> clock_;
|
||||
RembThrottler remb_throttler_;
|
||||
|
||||
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
|
||||
|
||||
std::mutex mutex_;
|
||||
mutable std::mutex mutex_;
|
||||
std::unique_ptr<RemoteBitrateEstimator> rbe_;
|
||||
bool using_absolute_send_time_;
|
||||
uint32_t packets_since_absolute_send_time_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||
|
||||
64
src/qos/remb_throttler.cc
Normal file
64
src/qos/remb_throttler.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "remb_throttler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex> // Include the mutex header
|
||||
#include <utility>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
constexpr TimeDelta kRembSendInterval = TimeDelta::Millis(200);
|
||||
} // namespace
|
||||
|
||||
RembThrottler::RembThrottler(RembSender remb_sender, Clock* clock)
|
||||
: remb_sender_(std::move(remb_sender)),
|
||||
clock_(clock),
|
||||
last_remb_time_(Timestamp::MinusInfinity()),
|
||||
last_send_remb_bitrate_(DataRate::PlusInfinity()),
|
||||
max_remb_bitrate_(DataRate::PlusInfinity()) {}
|
||||
|
||||
void RembThrottler::OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
|
||||
uint32_t bitrate_bps) {
|
||||
DataRate receive_bitrate = DataRate::BitsPerSec(bitrate_bps);
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_); // Use std::lock_guard
|
||||
// % threshold for if we should send a new REMB asap.
|
||||
const int64_t kSendThresholdPercent = 103;
|
||||
if (receive_bitrate * kSendThresholdPercent / 100 >
|
||||
last_send_remb_bitrate_ &&
|
||||
now < last_remb_time_ + kRembSendInterval) {
|
||||
return;
|
||||
}
|
||||
last_remb_time_ = now;
|
||||
last_send_remb_bitrate_ = receive_bitrate;
|
||||
receive_bitrate = std::min(last_send_remb_bitrate_, max_remb_bitrate_);
|
||||
}
|
||||
remb_sender_(receive_bitrate.bps(), ssrcs);
|
||||
}
|
||||
|
||||
void RembThrottler::SetMaxDesiredReceiveBitrate(DataRate bitrate) {
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_); // Use std::lock_guard
|
||||
max_remb_bitrate_ = bitrate;
|
||||
if (now - last_remb_time_ < kRembSendInterval &&
|
||||
!last_send_remb_bitrate_.IsZero() &&
|
||||
last_send_remb_bitrate_ <= max_remb_bitrate_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
remb_sender_(bitrate.bps(), /*ssrcs=*/{});
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
54
src/qos/remb_throttler.h
Normal file
54
src/qos/remb_throttler.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "remote_bitrate_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// RembThrottler is a helper class used for throttling RTCP REMB messages.
|
||||
// Throttles small changes to the received BWE within 200ms.
|
||||
class RembThrottler : public RemoteBitrateObserver {
|
||||
public:
|
||||
using RembSender =
|
||||
std::function<void(int64_t bitrate_bps, std::vector<uint32_t> ssrcs)>;
|
||||
RembThrottler(RembSender remb_sender, Clock* clock);
|
||||
|
||||
// Ensures the remote party is notified of the receive bitrate no larger than
|
||||
// `bitrate` using RTCP REMB.
|
||||
void SetMaxDesiredReceiveBitrate(DataRate bitrate);
|
||||
|
||||
// Implements RemoteBitrateObserver;
|
||||
// Called every time there is a new bitrate estimate for a receive channel
|
||||
// group. This call will trigger a new RTCP REMB packet if the bitrate
|
||||
// estimate has decreased or if no RTCP REMB packet has been sent for
|
||||
// a certain time interval.
|
||||
void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
|
||||
uint32_t bitrate_bps) override;
|
||||
|
||||
private:
|
||||
const RembSender remb_sender_;
|
||||
Clock* const clock_;
|
||||
std::mutex mutex_;
|
||||
Timestamp last_remb_time_;
|
||||
DataRate last_send_remb_bitrate_;
|
||||
DataRate max_remb_bitrate_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_REMB_THROTTLER_H_
|
||||
65
src/qos/remote_bitrate_estimator.h
Normal file
65
src/qos/remote_bitrate_estimator.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// This class estimates the incoming available bandwidth.
|
||||
|
||||
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
|
||||
#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "clock.h"
|
||||
#include "module_common_types.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
|
||||
// RemoteBitrateObserver is used to signal changes in bitrate estimates for
|
||||
// the incoming streams.
|
||||
class RemoteBitrateObserver {
|
||||
public:
|
||||
// Called when a receive channel group has a new bitrate estimate for the
|
||||
// incoming streams.
|
||||
virtual void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
|
||||
uint32_t bitrate) = 0;
|
||||
|
||||
virtual ~RemoteBitrateObserver() {}
|
||||
};
|
||||
|
||||
class RemoteBitrateEstimator : public CallStatsObserver {
|
||||
public:
|
||||
~RemoteBitrateEstimator() override {}
|
||||
|
||||
// Called for each incoming packet. Updates the incoming payload bitrate
|
||||
// estimate and the over-use detector. If an over-use is detected the
|
||||
// remote bitrate estimate will be updated.
|
||||
virtual void IncomingPacket(const RtpPacketReceived& rtp_packet) = 0;
|
||||
|
||||
// Removes all data for `ssrc`.
|
||||
virtual void RemoveStream(uint32_t ssrc) = 0;
|
||||
|
||||
// Returns latest estimate or DataRate::Zero() if estimation is unavailable.
|
||||
virtual DataRate LatestEstimate() const = 0;
|
||||
|
||||
virtual TimeDelta Process() = 0;
|
||||
|
||||
protected:
|
||||
static constexpr TimeDelta kProcessInterval = TimeDelta::Millis(500);
|
||||
static constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_
|
||||
181
src/qos/remote_bitrate_estimator_single_stream.cc
Normal file
181
src/qos/remote_bitrate_estimator_single_stream.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "remote_bitrate_estimator_single_stream.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "aimd_rate_control.h"
|
||||
#include "bwe_defines.h"
|
||||
#include "clock.h"
|
||||
#include "inter_arrival.h"
|
||||
#include "log.h"
|
||||
#include "overuse_detector.h"
|
||||
#include "overuse_estimator.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
constexpr int kTimestampGroupLengthMs = 5;
|
||||
constexpr double kTimestampToMs = 1.0 / 90.0;
|
||||
|
||||
} // namespace
|
||||
|
||||
RemoteBitrateEstimatorSingleStream::Detector::Detector()
|
||||
: last_packet_time(Timestamp::Zero()),
|
||||
inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs) {}
|
||||
|
||||
RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream(
|
||||
std::shared_ptr<SimulatedClock> clock, RemoteBitrateObserver* observer)
|
||||
: clock_(clock),
|
||||
observer_(observer),
|
||||
incoming_bitrate_(kBitrateWindow),
|
||||
last_valid_incoming_bitrate_(DataRate::Zero()),
|
||||
process_interval_(kProcessInterval),
|
||||
uma_recorded_(false) {
|
||||
LOG_INFO("RemoteBitrateEstimatorSingleStream: Instantiating");
|
||||
}
|
||||
|
||||
RemoteBitrateEstimatorSingleStream::~RemoteBitrateEstimatorSingleStream() =
|
||||
default;
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::IncomingPacket(
|
||||
const RtpPacketReceived& rtp_packet) {
|
||||
std::optional<int32_t> transmission_time_offset = rtp_packet.Timestamp();
|
||||
if (!uma_recorded_) {
|
||||
BweNames type = transmission_time_offset.has_value()
|
||||
? BweNames::kReceiverTOffset
|
||||
: BweNames::kReceiverNoExtension;
|
||||
uma_recorded_ = true;
|
||||
}
|
||||
uint32_t ssrc = rtp_packet.Ssrc();
|
||||
uint32_t rtp_timestamp =
|
||||
rtp_packet.Timestamp() + transmission_time_offset.value_or(0);
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
Detector& estimator = overuse_detectors_[ssrc];
|
||||
estimator.last_packet_time = now;
|
||||
|
||||
// Check if incoming bitrate estimate is valid, and if it needs to be reset.
|
||||
std::optional<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
|
||||
if (incoming_bitrate) {
|
||||
last_valid_incoming_bitrate_ = *incoming_bitrate;
|
||||
} else if (last_valid_incoming_bitrate_ > DataRate::Zero()) {
|
||||
// Incoming bitrate had a previous valid value, but now not enough data
|
||||
// point are left within the current window. Reset incoming bitrate
|
||||
// estimator so that the window size will only contain new data points.
|
||||
incoming_bitrate_.Reset();
|
||||
last_valid_incoming_bitrate_ = DataRate::Zero();
|
||||
}
|
||||
size_t payload_size = rtp_packet.payload_size() + rtp_packet.padding_size();
|
||||
incoming_bitrate_.Update(payload_size, now);
|
||||
|
||||
const BandwidthUsage prior_state = estimator.detector.State();
|
||||
uint32_t timestamp_delta = 0;
|
||||
int64_t time_delta = 0;
|
||||
int size_delta = 0;
|
||||
int64_t now_ms = now.ms();
|
||||
if (estimator.inter_arrival.ComputeDeltas(
|
||||
rtp_timestamp, rtp_packet.arrival_time().ms(), now_ms, payload_size,
|
||||
×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<DataRate> incoming_bitrate = incoming_bitrate_.Rate(now);
|
||||
if (incoming_bitrate.has_value() &&
|
||||
(prior_state != BandwidthUsage::kBwOverusing ||
|
||||
remote_rate_.TimeToReduceFurther(now, *incoming_bitrate))) {
|
||||
// The first overuse should immediately trigger a new estimate.
|
||||
// We also have to update the estimate immediately if we are overusing
|
||||
// and the target bitrate is too high compared to what we are receiving.
|
||||
UpdateEstimate(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeDelta RemoteBitrateEstimatorSingleStream::Process() {
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
Timestamp next_process_time = last_process_time_.has_value()
|
||||
? *last_process_time_ + process_interval_
|
||||
: now;
|
||||
// TODO(bugs.webrtc.org/13756): Removing rounding to milliseconds after
|
||||
// investigating why tests fails without that rounding.
|
||||
if (now.ms() >= next_process_time.ms()) {
|
||||
UpdateEstimate(now);
|
||||
last_process_time_ = now;
|
||||
return process_interval_;
|
||||
}
|
||||
|
||||
return next_process_time - now;
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::UpdateEstimate(Timestamp now) {
|
||||
BandwidthUsage bw_state = BandwidthUsage::kBwNormal;
|
||||
auto it = overuse_detectors_.begin();
|
||||
while (it != overuse_detectors_.end()) {
|
||||
if (now - it->second.last_packet_time > kStreamTimeOut) {
|
||||
// This over-use detector hasn't received packets for `kStreamTimeOut`
|
||||
// and is considered stale.
|
||||
overuse_detectors_.erase(it++);
|
||||
} else {
|
||||
// Make sure that we trigger an over-use if any of the over-use detectors
|
||||
// is detecting over-use.
|
||||
if (it->second.detector.State() > bw_state) {
|
||||
bw_state = it->second.detector.State();
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// We can't update the estimate if we don't have any active streams.
|
||||
if (overuse_detectors_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const RateControlInput input(bw_state, incoming_bitrate_.Rate(now));
|
||||
uint32_t target_bitrate = remote_rate_.Update(input, now).bps<uint32_t>();
|
||||
if (remote_rate_.ValidEstimate()) {
|
||||
process_interval_ = remote_rate_.GetFeedbackInterval();
|
||||
if (observer_)
|
||||
observer_->OnReceiveBitrateChanged(GetSsrcs(), target_bitrate);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms,
|
||||
int64_t /* max_rtt_ms */) {
|
||||
remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms));
|
||||
}
|
||||
|
||||
void RemoteBitrateEstimatorSingleStream::RemoveStream(uint32_t ssrc) {
|
||||
overuse_detectors_.erase(ssrc);
|
||||
}
|
||||
|
||||
DataRate RemoteBitrateEstimatorSingleStream::LatestEstimate() const {
|
||||
if (!remote_rate_.ValidEstimate() || overuse_detectors_.empty()) {
|
||||
return DataRate::Zero();
|
||||
}
|
||||
return remote_rate_.LatestEstimate();
|
||||
}
|
||||
|
||||
std::vector<uint32_t> RemoteBitrateEstimatorSingleStream::GetSsrcs() const {
|
||||
std::vector<uint32_t> ssrcs;
|
||||
ssrcs.reserve(overuse_detectors_.size());
|
||||
for (const auto& [ssrc, unused] : overuse_detectors_) {
|
||||
ssrcs.push_back(ssrc);
|
||||
}
|
||||
return ssrcs;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
80
src/qos/remote_bitrate_estimator_single_stream.h
Normal file
80
src/qos/remote_bitrate_estimator_single_stream.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
|
||||
#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "aimd_rate_control.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "inter_arrival.h"
|
||||
#include "overuse_detector.h"
|
||||
#include "overuse_estimator.h"
|
||||
#include "remote_bitrate_estimator.h"
|
||||
#include "rtc_base/bitrate_tracker.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator {
|
||||
public:
|
||||
RemoteBitrateEstimatorSingleStream(std::shared_ptr<SimulatedClock> clock,
|
||||
RemoteBitrateObserver* observer);
|
||||
|
||||
RemoteBitrateEstimatorSingleStream() = delete;
|
||||
RemoteBitrateEstimatorSingleStream(
|
||||
const RemoteBitrateEstimatorSingleStream&) = delete;
|
||||
RemoteBitrateEstimatorSingleStream& operator=(
|
||||
const RemoteBitrateEstimatorSingleStream&) = delete;
|
||||
|
||||
~RemoteBitrateEstimatorSingleStream() override;
|
||||
|
||||
void IncomingPacket(const RtpPacketReceived& rtp_packet) override;
|
||||
TimeDelta Process() override;
|
||||
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
|
||||
void RemoveStream(uint32_t ssrc) override;
|
||||
DataRate LatestEstimate() const override;
|
||||
|
||||
private:
|
||||
struct Detector {
|
||||
Detector();
|
||||
|
||||
Timestamp last_packet_time;
|
||||
InterArrival inter_arrival;
|
||||
OveruseEstimator estimator;
|
||||
OveruseDetector detector;
|
||||
};
|
||||
|
||||
// Triggers a new estimate calculation.
|
||||
void UpdateEstimate(Timestamp now);
|
||||
|
||||
std::vector<uint32_t> GetSsrcs() const;
|
||||
|
||||
std::shared_ptr<SimulatedClock> clock_;
|
||||
RemoteBitrateObserver* observer_;
|
||||
std::map<uint32_t, Detector> overuse_detectors_;
|
||||
BitrateTracker incoming_bitrate_;
|
||||
DataRate last_valid_incoming_bitrate_;
|
||||
AimdRateControl remote_rate_;
|
||||
std::optional<Timestamp> last_process_time_;
|
||||
TimeDelta process_interval_;
|
||||
bool uma_recorded_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_
|
||||
47
src/qos/rtp_transport_feedback_generator.h
Normal file
47
src/qos/rtp_transport_feedback_generator.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_
|
||||
#define MODULES_REMOTE_BITRATE_ESTIMATOR_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/data_size.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtp_packet_received.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpTransportFeedbackGenerator {
|
||||
public:
|
||||
// Function intented to be used for sending RTCP messages generated by an
|
||||
// implementation of this class.
|
||||
using RtcpSender =
|
||||
std::function<void(std::vector<std::unique_ptr<RtcpPacket>> packets)>;
|
||||
virtual ~RtpTransportFeedbackGenerator() = default;
|
||||
|
||||
virtual void OnReceivedPacket(const RtpPacketReceived& packet) = 0;
|
||||
|
||||
// Sends periodic feedback if it is time to send it.
|
||||
// Returns time until next call to Process should be made.
|
||||
virtual TimeDelta Process(Timestamp now) = 0;
|
||||
|
||||
virtual void OnSendBandwidthEstimateChanged(DataRate estimate) = 0;
|
||||
|
||||
// Overhead from transport layers below RTP. Ie, IP, SRTP.
|
||||
virtual void SetTransportOverhead(DataSize overhead_per_packet) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_RTP_TRANSPORT_FEEDBACK_GENERATOR_H_
|
||||
@@ -20,25 +20,30 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "bwe_defines.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
constexpr int64_t kBweIncreaseInterval = 1000;
|
||||
constexpr int64_t kBweDecreaseInterval = 300;
|
||||
constexpr int64_t kStartPhase = 2000;
|
||||
constexpr int64_t kBweConverganceTime = 20000;
|
||||
constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000);
|
||||
constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300);
|
||||
constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000);
|
||||
constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000);
|
||||
constexpr int kLimitNumPackets = 20;
|
||||
constexpr int64_t kDefaultMaxBitrate = 1000000000;
|
||||
constexpr int64_t kLowBitrateLogPeriod = 10000;
|
||||
constexpr int64_t kRtcEventLogPeriod = 5000;
|
||||
constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000);
|
||||
constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000);
|
||||
constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000);
|
||||
// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals.
|
||||
constexpr int64_t kMaxRtcpFeedbackInterval = 5000;
|
||||
constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000);
|
||||
|
||||
constexpr float kDefaultLowLossThreshold = 0.02f;
|
||||
constexpr float kDefaultHighLossThreshold = 0.1f;
|
||||
constexpr int64_t kDefaultBitrateThreshold = 0;
|
||||
|
||||
constexpr int64_t kCongestionControllerMinBitrate = 5000;
|
||||
constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero();
|
||||
|
||||
struct UmaRampUpMetric {
|
||||
const char* metric_name;
|
||||
@@ -51,64 +56,70 @@ const UmaRampUpMetric kUmaRampupMetrics[] = {
|
||||
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
|
||||
const size_t kNumUmaRampupMetrics =
|
||||
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
|
||||
|
||||
const char kBweLosExperiment[] = "WebRTC-BweLossExperiment";
|
||||
|
||||
} // namespace
|
||||
|
||||
void LinkCapacityTracker::UpdateDelayBasedEstimate(
|
||||
int64_t at_time, int64_t delay_based_bitrate) {
|
||||
Timestamp at_time, DataRate delay_based_bitrate) {
|
||||
if (delay_based_bitrate < last_delay_based_estimate_) {
|
||||
capacity_estimate_bps_ = std::min(capacity_estimate_bps_,
|
||||
static_cast<double>(delay_based_bitrate));
|
||||
capacity_estimate_bps_ =
|
||||
std::min(capacity_estimate_bps_, delay_based_bitrate.bps<double>());
|
||||
last_link_capacity_update_ = at_time;
|
||||
}
|
||||
last_delay_based_estimate_ = delay_based_bitrate;
|
||||
}
|
||||
|
||||
void LinkCapacityTracker::OnStartingRate(int64_t start_rate) {
|
||||
if (IsInfinite(last_link_capacity_update_)) {
|
||||
capacity_estimate_bps_ = start_rate;
|
||||
}
|
||||
void LinkCapacityTracker::OnStartingRate(DataRate start_rate) {
|
||||
if (last_link_capacity_update_.IsInfinite())
|
||||
capacity_estimate_bps_ = start_rate.bps<double>();
|
||||
}
|
||||
|
||||
void LinkCapacityTracker::OnRateUpdate(std::optional<int64_t> acknowledged,
|
||||
int64_t target, int64_t at_time) {
|
||||
void LinkCapacityTracker::OnRateUpdate(std::optional<DataRate> acknowledged,
|
||||
DataRate target, Timestamp at_time) {
|
||||
if (!acknowledged) return;
|
||||
int64_t acknowledged_target = std::min(*acknowledged, target);
|
||||
if (acknowledged_target > capacity_estimate_bps_) {
|
||||
int64_t delta = at_time - last_link_capacity_update_;
|
||||
double alpha = IsFinite(delta) ? exp(-(delta / 10)) : 0;
|
||||
capacity_estimate_bps_ =
|
||||
alpha * capacity_estimate_bps_ + (1 - alpha) * acknowledged_target;
|
||||
DataRate acknowledged_target = std::min(*acknowledged, target);
|
||||
if (acknowledged_target.bps() > capacity_estimate_bps_) {
|
||||
TimeDelta delta = at_time - last_link_capacity_update_;
|
||||
double alpha =
|
||||
delta.IsFinite() ? exp(-(delta / TimeDelta::Seconds(10))) : 0;
|
||||
capacity_estimate_bps_ = alpha * capacity_estimate_bps_ +
|
||||
(1 - alpha) * acknowledged_target.bps<double>();
|
||||
}
|
||||
last_link_capacity_update_ = at_time;
|
||||
}
|
||||
|
||||
void LinkCapacityTracker::OnRttBackoff(int64_t backoff_rate, int64_t at_time) {
|
||||
void LinkCapacityTracker::OnRttBackoff(DataRate backoff_rate,
|
||||
Timestamp at_time) {
|
||||
capacity_estimate_bps_ =
|
||||
std::min(capacity_estimate_bps_, static_cast<double>(backoff_rate));
|
||||
std::min(capacity_estimate_bps_, backoff_rate.bps<double>());
|
||||
last_link_capacity_update_ = at_time;
|
||||
}
|
||||
|
||||
int64_t LinkCapacityTracker::estimate() const { return capacity_estimate_bps_; }
|
||||
DataRate LinkCapacityTracker::estimate() const {
|
||||
return DataRate::BitsPerSec(capacity_estimate_bps_);
|
||||
}
|
||||
|
||||
RttBasedBackoff::RttBasedBackoff()
|
||||
: disabled_(true),
|
||||
configured_limit_(3),
|
||||
configured_limit_(TimeDelta::Seconds(3)),
|
||||
drop_fraction_(0.8),
|
||||
drop_interval_(1),
|
||||
bandwidth_floor_(5),
|
||||
rtt_limit_(INT64_T_MAX),
|
||||
drop_interval_(TimeDelta::Seconds(1)),
|
||||
bandwidth_floor_(DataRate::KilobitsPerSec(5)),
|
||||
rtt_limit_(TimeDelta::PlusInfinity()),
|
||||
// By initializing this to plus infinity, we make sure that we never
|
||||
// trigger rtt backoff unless packet feedback is enabled.
|
||||
last_propagation_rtt_update_(INT64_T_MAX),
|
||||
last_propagation_rtt_(0),
|
||||
last_packet_sent_(INT64_T_MIN) {
|
||||
last_propagation_rtt_update_(Timestamp::PlusInfinity()),
|
||||
last_propagation_rtt_(TimeDelta::Zero()),
|
||||
last_packet_sent_(Timestamp::MinusInfinity()) {
|
||||
if (!disabled_) {
|
||||
rtt_limit_ = configured_limit_;
|
||||
}
|
||||
}
|
||||
|
||||
void RttBasedBackoff::UpdatePropagationRtt(int64_t at_time,
|
||||
int64_t propagation_rtt) {
|
||||
void RttBasedBackoff::UpdatePropagationRtt(Timestamp at_time,
|
||||
TimeDelta propagation_rtt) {
|
||||
last_propagation_rtt_update_ = at_time;
|
||||
last_propagation_rtt_ = propagation_rtt;
|
||||
}
|
||||
@@ -117,11 +128,10 @@ bool RttBasedBackoff::IsRttAboveLimit() const {
|
||||
return CorrectedRtt() > rtt_limit_;
|
||||
}
|
||||
|
||||
int64_t RttBasedBackoff::CorrectedRtt() const {
|
||||
TimeDelta RttBasedBackoff::CorrectedRtt() const {
|
||||
// Avoid timeout when no packets are being sent.
|
||||
int64_t timeout_correction =
|
||||
std::max(last_packet_sent_ - last_propagation_rtt_update_,
|
||||
static_cast<int64_t>(0));
|
||||
TimeDelta timeout_correction = std::max(
|
||||
last_packet_sent_ - last_propagation_rtt_update_, TimeDelta::Zero());
|
||||
return timeout_correction + last_propagation_rtt_;
|
||||
}
|
||||
|
||||
@@ -130,61 +140,61 @@ RttBasedBackoff::~RttBasedBackoff() = default;
|
||||
SendSideBandwidthEstimation::SendSideBandwidthEstimation()
|
||||
: lost_packets_since_last_loss_update_(0),
|
||||
expected_packets_since_last_loss_update_(0),
|
||||
current_target_(0),
|
||||
last_logged_target_(0),
|
||||
current_target_(DataRate::Zero()),
|
||||
last_logged_target_(DataRate::Zero()),
|
||||
min_bitrate_configured_(kCongestionControllerMinBitrate),
|
||||
max_bitrate_configured_(kDefaultMaxBitrate),
|
||||
last_low_bitrate_log_(INT64_T_MIN),
|
||||
last_low_bitrate_log_(Timestamp::MinusInfinity()),
|
||||
has_decreased_since_last_fraction_loss_(false),
|
||||
last_loss_feedback_(INT64_T_MIN),
|
||||
last_loss_packet_report_(INT64_T_MIN),
|
||||
last_loss_feedback_(Timestamp::MinusInfinity()),
|
||||
last_loss_packet_report_(Timestamp::MinusInfinity()),
|
||||
last_fraction_loss_(0),
|
||||
last_logged_fraction_loss_(0),
|
||||
last_round_trip_time_(0),
|
||||
receiver_limit_(INT64_T_MAX),
|
||||
delay_based_limit_(INT64_T_MAX),
|
||||
time_last_decrease_(INT64_T_MIN),
|
||||
first_report_time_(INT64_T_MIN),
|
||||
last_round_trip_time_(TimeDelta::Zero()),
|
||||
receiver_limit_(DataRate::PlusInfinity()),
|
||||
delay_based_limit_(DataRate::PlusInfinity()),
|
||||
time_last_decrease_(Timestamp::MinusInfinity()),
|
||||
first_report_time_(Timestamp::MinusInfinity()),
|
||||
initially_lost_packets_(0),
|
||||
bitrate_at_2_seconds_(0),
|
||||
bitrate_at_2_seconds_(DataRate::Zero()),
|
||||
uma_update_state_(kNoUpdate),
|
||||
uma_rtt_state_(kNoUpdate),
|
||||
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
|
||||
last_rtc_event_log_(INT64_T_MIN),
|
||||
low_loss_threshold_(kDefaultLowLossThreshold),
|
||||
high_loss_threshold_(kDefaultHighLossThreshold),
|
||||
bitrate_threshold_(kDefaultBitrateThreshold),
|
||||
disable_receiver_limit_caps_only_(true) {}
|
||||
disable_receiver_limit_caps_only_("Disabled") {
|
||||
// rtt_backoff_ =
|
||||
}
|
||||
|
||||
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
|
||||
|
||||
void SendSideBandwidthEstimation::OnRouteChange() {
|
||||
lost_packets_since_last_loss_update_ = 0;
|
||||
expected_packets_since_last_loss_update_ = 0;
|
||||
current_target_ = 0;
|
||||
current_target_ = DataRate::Zero();
|
||||
min_bitrate_configured_ = kCongestionControllerMinBitrate;
|
||||
max_bitrate_configured_ = kDefaultMaxBitrate;
|
||||
last_low_bitrate_log_ = INT64_T_MIN;
|
||||
last_low_bitrate_log_ = Timestamp::MinusInfinity();
|
||||
has_decreased_since_last_fraction_loss_ = false;
|
||||
last_loss_feedback_ = INT64_T_MIN;
|
||||
last_loss_packet_report_ = INT64_T_MIN;
|
||||
last_loss_feedback_ = Timestamp::MinusInfinity();
|
||||
last_loss_packet_report_ = Timestamp::MinusInfinity();
|
||||
last_fraction_loss_ = 0;
|
||||
last_logged_fraction_loss_ = 0;
|
||||
last_round_trip_time_ = 0;
|
||||
receiver_limit_ = INT64_T_MAX;
|
||||
delay_based_limit_ = INT64_T_MAX;
|
||||
time_last_decrease_ = INT64_T_MIN;
|
||||
first_report_time_ = INT64_T_MIN;
|
||||
last_round_trip_time_ = TimeDelta::Zero();
|
||||
receiver_limit_ = DataRate::PlusInfinity();
|
||||
delay_based_limit_ = DataRate::PlusInfinity();
|
||||
time_last_decrease_ = Timestamp::MinusInfinity();
|
||||
first_report_time_ = Timestamp::MinusInfinity();
|
||||
initially_lost_packets_ = 0;
|
||||
bitrate_at_2_seconds_ = 0;
|
||||
bitrate_at_2_seconds_ = DataRate::Zero();
|
||||
uma_update_state_ = kNoUpdate;
|
||||
uma_rtt_state_ = kNoUpdate;
|
||||
last_rtc_event_log_ = INT64_T_MIN;
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::SetBitrates(
|
||||
std::optional<int64_t> send_bitrate, int64_t min_bitrate,
|
||||
int64_t max_bitrate, int64_t at_time) {
|
||||
std::optional<DataRate> send_bitrate, DataRate min_bitrate,
|
||||
DataRate max_bitrate, Timestamp at_time) {
|
||||
SetMinMaxBitrate(min_bitrate, max_bitrate);
|
||||
if (send_bitrate) {
|
||||
link_capacity_.OnStartingRate(*send_bitrate);
|
||||
@@ -192,21 +202,21 @@ void SendSideBandwidthEstimation::SetBitrates(
|
||||
}
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::SetSendBitrate(int64_t bitrate,
|
||||
int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate,
|
||||
Timestamp at_time) {
|
||||
// Reset to avoid being capped by the estimate.
|
||||
delay_based_limit_ = INT64_T_MAX;
|
||||
delay_based_limit_ = DataRate::PlusInfinity();
|
||||
UpdateTargetBitrate(bitrate, at_time);
|
||||
// Clear last sent bitrate history so the new value can be used directly
|
||||
// and not capped.
|
||||
min_bitrate_history_.clear();
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
|
||||
int64_t max_bitrate) {
|
||||
void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
|
||||
DataRate max_bitrate) {
|
||||
min_bitrate_configured_ =
|
||||
std::max(min_bitrate, kCongestionControllerMinBitrate);
|
||||
if (max_bitrate > 0 && IsFinite(max_bitrate)) {
|
||||
if (max_bitrate > DataRate::Zero() && max_bitrate.IsFinite()) {
|
||||
max_bitrate_configured_ = std::max(min_bitrate_configured_, max_bitrate);
|
||||
} else {
|
||||
max_bitrate_configured_ = kDefaultMaxBitrate;
|
||||
@@ -214,11 +224,11 @@ void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
|
||||
}
|
||||
|
||||
int SendSideBandwidthEstimation::GetMinBitrate() const {
|
||||
return min_bitrate_configured_;
|
||||
return min_bitrate_configured_.bps<int>();
|
||||
}
|
||||
|
||||
int64_t SendSideBandwidthEstimation::target_rate() const {
|
||||
int64_t target = current_target_;
|
||||
DataRate SendSideBandwidthEstimation::target_rate() const {
|
||||
DataRate target = current_target_;
|
||||
if (!disable_receiver_limit_caps_only_)
|
||||
target = std::min(target, receiver_limit_);
|
||||
return std::max(min_bitrate_configured_, target);
|
||||
@@ -228,29 +238,29 @@ bool SendSideBandwidthEstimation::IsRttAboveLimit() const {
|
||||
return rtt_backoff_.IsRttAboveLimit();
|
||||
}
|
||||
|
||||
int64_t SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
|
||||
DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
|
||||
return link_capacity_.estimate();
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateReceiverEstimate(int64_t at_time,
|
||||
int64_t bandwidth) {
|
||||
void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time,
|
||||
DataRate bandwidth) {
|
||||
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
||||
// limitation.
|
||||
receiver_limit_ = !bandwidth ? INT64_T_MAX : bandwidth;
|
||||
receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth;
|
||||
ApplyTargetLimits(at_time);
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(int64_t at_time,
|
||||
int64_t bitrate) {
|
||||
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time,
|
||||
DataRate bitrate) {
|
||||
link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
|
||||
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
||||
// limitation.
|
||||
delay_based_limit_ = !bitrate ? INT64_T_MAX : bitrate;
|
||||
delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate;
|
||||
ApplyTargetLimits(at_time);
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::SetAcknowledgedRate(
|
||||
std::optional<int64_t> acknowledged_rate, int64_t at_time) {
|
||||
std::optional<DataRate> acknowledged_rate, Timestamp at_time) {
|
||||
acknowledged_rate_ = acknowledged_rate;
|
||||
if (!acknowledged_rate.has_value()) {
|
||||
return;
|
||||
@@ -259,11 +269,9 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate(
|
||||
|
||||
void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
||||
int64_t number_of_packets,
|
||||
int64_t at_time) {
|
||||
Timestamp at_time) {
|
||||
last_loss_feedback_ = at_time;
|
||||
if (IsInfinite(first_report_time_)) {
|
||||
first_report_time_ = at_time;
|
||||
}
|
||||
if (first_report_time_.IsInfinite()) first_report_time_ = at_time;
|
||||
|
||||
// Check sequence number diff and weight loss report
|
||||
if (number_of_packets > 0) {
|
||||
@@ -281,7 +289,7 @@ void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
||||
has_decreased_since_last_fraction_loss_ = false;
|
||||
int64_t lost_q8 =
|
||||
std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
|
||||
static_cast<int64_t>(0))
|
||||
0)
|
||||
<< 8;
|
||||
last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255);
|
||||
|
||||
@@ -295,12 +303,13 @@ void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
||||
UpdateUmaStatsPacketsLost(at_time, packets_lost);
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
|
||||
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time,
|
||||
int packets_lost) {
|
||||
int64_t bitrate_kbps = (current_target_ + 500) / 1000;
|
||||
DataRate bitrate_kbps =
|
||||
DataRate::KilobitsPerSec((current_target_.bps() + 500) / 1000);
|
||||
for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
|
||||
if (!rampup_uma_stats_updated_[i] &&
|
||||
bitrate_kbps >= kUmaRampupMetrics[i].bitrate_kbps) {
|
||||
bitrate_kbps.kbps() >= kUmaRampupMetrics[i].bitrate_kbps) {
|
||||
rampup_uma_stats_updated_[i] = true;
|
||||
}
|
||||
}
|
||||
@@ -312,29 +321,29 @@ void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
|
||||
} else if (uma_update_state_ == kFirstDone &&
|
||||
at_time - first_report_time_ >= kBweConverganceTime) {
|
||||
uma_update_state_ = kDone;
|
||||
int bitrate_diff_kbps =
|
||||
std::max(bitrate_at_2_seconds_ - bitrate_kbps, static_cast<int64_t>(0));
|
||||
int bitrate_diff_kbps = std::max(
|
||||
bitrate_at_2_seconds_.kbps<int>() - bitrate_kbps.kbps<int>(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateRtt(int64_t rtt, int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::UpdateRtt(TimeDelta rtt, Timestamp at_time) {
|
||||
// Update RTT if we were able to compute an RTT based on this RTCP.
|
||||
// FlexFEC doesn't send RTCP SR, which means we won't be able to compute RTT.
|
||||
if (rtt > 0) last_round_trip_time_ = rtt;
|
||||
if (rtt > TimeDelta::Zero()) last_round_trip_time_ = rtt;
|
||||
|
||||
if (!IsInStartPhase(at_time) && uma_rtt_state_ == kNoUpdate) {
|
||||
uma_rtt_state_ = kDone;
|
||||
}
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
|
||||
if (rtt_backoff_.IsRttAboveLimit()) {
|
||||
if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
|
||||
current_target_ > rtt_backoff_.bandwidth_floor_) {
|
||||
time_last_decrease_ = at_time;
|
||||
int64_t new_bitrate =
|
||||
DataRate new_bitrate =
|
||||
std::max(current_target_ * rtt_backoff_.drop_fraction_,
|
||||
static_cast<double>(rtt_backoff_.bandwidth_floor_));
|
||||
rtt_backoff_.bandwidth_floor_);
|
||||
link_capacity_.OnRttBackoff(new_bitrate, at_time);
|
||||
UpdateTargetBitrate(new_bitrate, at_time);
|
||||
return;
|
||||
@@ -347,15 +356,14 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
// We trust the REMB and/or delay-based estimate during the first 2 seconds if
|
||||
// we haven't had any packet loss reported, to allow startup bitrate probing.
|
||||
if (last_fraction_loss_ == 0 && IsInStartPhase(at_time)) {
|
||||
int64_t new_bitrate = current_target_;
|
||||
DataRate new_bitrate = current_target_;
|
||||
// TODO(srte): We should not allow the new_bitrate to be larger than the
|
||||
// receiver limit here.
|
||||
if (IsFinite(receiver_limit_)) {
|
||||
if (receiver_limit_.IsFinite())
|
||||
new_bitrate = std::max(receiver_limit_, new_bitrate);
|
||||
}
|
||||
if (IsFinite(delay_based_limit_)) {
|
||||
if (delay_based_limit_.IsFinite())
|
||||
new_bitrate = std::max(delay_based_limit_, new_bitrate);
|
||||
}
|
||||
|
||||
if (new_bitrate != current_target_) {
|
||||
min_bitrate_history_.clear();
|
||||
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
|
||||
@@ -364,14 +372,14 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
}
|
||||
}
|
||||
UpdateMinHistory(at_time);
|
||||
if (IsInfinite(last_loss_packet_report_)) {
|
||||
if (last_loss_packet_report_.IsInfinite()) {
|
||||
// No feedback received.
|
||||
// TODO(srte): This is likely redundant in most cases.
|
||||
ApplyTargetLimits(at_time);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t time_since_loss_packet_report = at_time - last_loss_packet_report_;
|
||||
TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_;
|
||||
if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) {
|
||||
// We only care about loss above a given bitrate threshold.
|
||||
float loss = last_fraction_loss_ / 256.0f;
|
||||
@@ -389,12 +397,13 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
// If instead one would do: current_bitrate_ *= 1.08^(delta time),
|
||||
// it would take over one second since the lower packet loss to achieve
|
||||
// 108kbps.
|
||||
int64_t new_bitrate = min_bitrate_history_.front().second * 1.08 + 0.5;
|
||||
DataRate new_bitrate = DataRate::BitsPerSec(
|
||||
min_bitrate_history_.front().second.bps() * 1.08 + 0.5);
|
||||
|
||||
// Add 1 kbps extra, just to make sure that we do not get stuck
|
||||
// (gives a little extra increase at low rates, negligible at higher
|
||||
// rates).
|
||||
new_bitrate += 1000;
|
||||
new_bitrate += DataRate::BitsPerSec(1000);
|
||||
UpdateTargetBitrate(new_bitrate, at_time);
|
||||
return;
|
||||
} else if (current_target_ > bitrate_threshold_) {
|
||||
@@ -411,10 +420,10 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
// Reduce rate:
|
||||
// newRate = rate * (1 - 0.5*lossRate);
|
||||
// where packetLoss = 256*lossRate;
|
||||
int64_t new_bitrate =
|
||||
(current_target_ *
|
||||
DataRate new_bitrate = DataRate::BitsPerSec(
|
||||
(current_target_.bps() *
|
||||
static_cast<double>(512 - last_fraction_loss_)) /
|
||||
512.0;
|
||||
512.0);
|
||||
has_decreased_since_last_fraction_loss_ = true;
|
||||
UpdateTargetBitrate(new_bitrate, at_time);
|
||||
return;
|
||||
@@ -427,7 +436,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdatePropagationRtt(
|
||||
int64_t at_time, int64_t propagation_rtt) {
|
||||
Timestamp at_time, TimeDelta propagation_rtt) {
|
||||
rtt_backoff_.UpdatePropagationRtt(at_time, propagation_rtt);
|
||||
}
|
||||
|
||||
@@ -436,17 +445,17 @@ void SendSideBandwidthEstimation::OnSentPacket(const SentPacket& sent_packet) {
|
||||
rtt_backoff_.last_packet_sent_ = sent_packet.send_time;
|
||||
}
|
||||
|
||||
bool SendSideBandwidthEstimation::IsInStartPhase(int64_t at_time) const {
|
||||
return (IsInfinite(first_report_time_)) ||
|
||||
bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const {
|
||||
return first_report_time_.IsInfinite() ||
|
||||
at_time - first_report_time_ < kStartPhase;
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateMinHistory(int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) {
|
||||
// Remove old data points from history.
|
||||
// Since history precision is in ms, add one so it is able to increase
|
||||
// bitrate if it is off by as little as 0.5ms.
|
||||
while (!min_bitrate_history_.empty() &&
|
||||
at_time - min_bitrate_history_.front().first + 1 >
|
||||
at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) >
|
||||
kBweIncreaseInterval) {
|
||||
min_bitrate_history_.pop_front();
|
||||
}
|
||||
@@ -461,45 +470,36 @@ void SendSideBandwidthEstimation::UpdateMinHistory(int64_t at_time) {
|
||||
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
|
||||
}
|
||||
|
||||
int64_t SendSideBandwidthEstimation::GetUpperLimit() const {
|
||||
int64_t upper_limit = delay_based_limit_;
|
||||
DataRate SendSideBandwidthEstimation::GetUpperLimit() const {
|
||||
DataRate upper_limit = delay_based_limit_;
|
||||
if (disable_receiver_limit_caps_only_)
|
||||
upper_limit = std::min(upper_limit, receiver_limit_);
|
||||
return std::min(upper_limit, max_bitrate_configured_);
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(int64_t bitrate,
|
||||
int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate,
|
||||
Timestamp at_time) {
|
||||
if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
|
||||
LOG_WARN(
|
||||
"Estimated available bandwidth {} is below configured min bitrate {}",
|
||||
bitrate, min_bitrate_configured_);
|
||||
ToString(bitrate), ToString(min_bitrate_configured_));
|
||||
last_low_bitrate_log_ = at_time;
|
||||
}
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(int64_t at_time) {
|
||||
if (current_target_ != last_logged_target_ ||
|
||||
last_fraction_loss_ != last_logged_fraction_loss_ ||
|
||||
at_time - last_rtc_event_log_ > kRtcEventLogPeriod) {
|
||||
last_logged_fraction_loss_ = last_fraction_loss_;
|
||||
last_logged_target_ = current_target_;
|
||||
last_rtc_event_log_ = at_time;
|
||||
}
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::UpdateTargetBitrate(int64_t new_bitrate,
|
||||
int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate,
|
||||
Timestamp at_time) {
|
||||
new_bitrate = std::min(new_bitrate, GetUpperLimit());
|
||||
if (new_bitrate < min_bitrate_configured_) {
|
||||
MaybeLogLowBitrateWarning(new_bitrate, at_time);
|
||||
new_bitrate = min_bitrate_configured_;
|
||||
}
|
||||
current_target_ = new_bitrate;
|
||||
MaybeLogLossBasedEvent(at_time);
|
||||
link_capacity_.OnRateUpdate(acknowledged_rate_, current_target_, at_time);
|
||||
}
|
||||
|
||||
void SendSideBandwidthEstimation::ApplyTargetLimits(int64_t at_time) {
|
||||
void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) {
|
||||
UpdateTargetBitrate(current_target_, at_time);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -1,63 +1,74 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-01-13
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*
|
||||
* FEC and NACK added bitrate is handled outside class
|
||||
*/
|
||||
|
||||
#ifndef _SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||
#define _SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "limits_base.h"
|
||||
#include "network_types.h"
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class LinkCapacityTracker {
|
||||
public:
|
||||
LinkCapacityTracker() = default;
|
||||
~LinkCapacityTracker() = default;
|
||||
// Call when a new delay-based estimate is available.
|
||||
void UpdateDelayBasedEstimate(int64_t at_time, int64_t delay_based_bitrate);
|
||||
void OnStartingRate(int64_t start_rate);
|
||||
void OnRateUpdate(std::optional<int64_t> acknowledged, int64_t target,
|
||||
int64_t at_time);
|
||||
void OnRttBackoff(int64_t backoff_rate, int64_t at_time);
|
||||
int64_t estimate() const;
|
||||
void UpdateDelayBasedEstimate(Timestamp at_time,
|
||||
DataRate delay_based_bitrate);
|
||||
void OnStartingRate(DataRate start_rate);
|
||||
void OnRateUpdate(std::optional<DataRate> acknowledged, DataRate target,
|
||||
Timestamp at_time);
|
||||
void OnRttBackoff(DataRate backoff_rate, Timestamp at_time);
|
||||
DataRate estimate() const;
|
||||
|
||||
private:
|
||||
double capacity_estimate_bps_ = 0;
|
||||
int64_t last_link_capacity_update_ = INT64_T_MIN;
|
||||
int64_t last_delay_based_estimate_ = INT64_T_MAX;
|
||||
Timestamp last_link_capacity_update_ = Timestamp::MinusInfinity();
|
||||
DataRate last_delay_based_estimate_ = DataRate::PlusInfinity();
|
||||
};
|
||||
|
||||
class RttBasedBackoff {
|
||||
public:
|
||||
explicit RttBasedBackoff();
|
||||
~RttBasedBackoff();
|
||||
void UpdatePropagationRtt(int64_t at_time, int64_t propagation_rtt);
|
||||
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
|
||||
bool IsRttAboveLimit() const;
|
||||
|
||||
bool disabled_;
|
||||
int64_t configured_limit_;
|
||||
TimeDelta configured_limit_;
|
||||
double drop_fraction_;
|
||||
int64_t drop_interval_;
|
||||
int64_t bandwidth_floor_;
|
||||
TimeDelta drop_interval_;
|
||||
DataRate bandwidth_floor_;
|
||||
|
||||
public:
|
||||
int64_t rtt_limit_;
|
||||
int64_t last_propagation_rtt_update_;
|
||||
int64_t last_propagation_rtt_;
|
||||
int64_t last_packet_sent_;
|
||||
TimeDelta rtt_limit_;
|
||||
Timestamp last_propagation_rtt_update_;
|
||||
TimeDelta last_propagation_rtt_;
|
||||
Timestamp last_packet_sent_;
|
||||
|
||||
private:
|
||||
int64_t CorrectedRtt() const;
|
||||
TimeDelta CorrectedRtt() const;
|
||||
};
|
||||
|
||||
class SendSideBandwidthEstimation {
|
||||
@@ -67,112 +78,108 @@ class SendSideBandwidthEstimation {
|
||||
|
||||
void OnRouteChange();
|
||||
|
||||
int64_t target_rate() const;
|
||||
DataRate target_rate() const;
|
||||
// Return whether the current rtt is higher than the rtt limited configured in
|
||||
// RttBasedBackoff.
|
||||
bool IsRttAboveLimit() const;
|
||||
uint8_t fraction_loss() const { return last_fraction_loss_; }
|
||||
int64_t round_trip_time() const { return last_round_trip_time_; }
|
||||
TimeDelta round_trip_time() const { return last_round_trip_time_; }
|
||||
|
||||
int64_t GetEstimatedLinkCapacity() const;
|
||||
DataRate GetEstimatedLinkCapacity() const;
|
||||
// Call periodically to update estimate.
|
||||
void UpdateEstimate(int64_t at_time);
|
||||
void UpdateEstimate(Timestamp at_time);
|
||||
void OnSentPacket(const SentPacket& sent_packet);
|
||||
void UpdatePropagationRtt(int64_t at_time, int64_t propagation_rtt);
|
||||
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
|
||||
|
||||
// Call when we receive a RTCP message with TMMBR or REMB.
|
||||
void UpdateReceiverEstimate(int64_t at_time, int64_t bandwidth);
|
||||
void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth);
|
||||
|
||||
// Call when a new delay-based estimate is available.
|
||||
void UpdateDelayBasedEstimate(int64_t at_time, int64_t bitrate);
|
||||
void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate);
|
||||
|
||||
// Call when we receive a RTCP message with a ReceiveBlock.
|
||||
void UpdatePacketsLost(int64_t packets_lost, int64_t number_of_packets,
|
||||
int64_t at_time);
|
||||
Timestamp at_time);
|
||||
|
||||
// Call when we receive a RTCP message with a ReceiveBlock.
|
||||
void UpdateRtt(int64_t rtt, int64_t at_time);
|
||||
void UpdateRtt(TimeDelta rtt, Timestamp at_time);
|
||||
|
||||
void SetBitrates(std::optional<int64_t> send_bitrate, int64_t min_bitrate,
|
||||
int64_t max_bitrate, int64_t at_time);
|
||||
void SetSendBitrate(int64_t bitrate, int64_t at_time);
|
||||
void SetMinMaxBitrate(int64_t min_bitrate, int64_t max_bitrate);
|
||||
void SetBitrates(std::optional<DataRate> send_bitrate, DataRate min_bitrate,
|
||||
DataRate max_bitrate, Timestamp at_time);
|
||||
void SetSendBitrate(DataRate bitrate, Timestamp at_time);
|
||||
void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
|
||||
int GetMinBitrate() const;
|
||||
void SetAcknowledgedRate(std::optional<int64_t> acknowledged_rate,
|
||||
int64_t at_time);
|
||||
void SetAcknowledgedRate(std::optional<DataRate> acknowledged_rate,
|
||||
Timestamp at_time);
|
||||
|
||||
private:
|
||||
friend class GoogCcStatePrinter;
|
||||
|
||||
enum UmaState { kNoUpdate, kFirstDone, kDone };
|
||||
|
||||
bool IsInStartPhase(int64_t at_time) const;
|
||||
bool IsInStartPhase(Timestamp at_time) const;
|
||||
|
||||
void UpdateUmaStatsPacketsLost(int64_t at_time, int packets_lost);
|
||||
void UpdateUmaStatsPacketsLost(Timestamp at_time, int packets_lost);
|
||||
|
||||
// Updates history of min bitrates.
|
||||
// After this method returns min_bitrate_history_.front().second contains the
|
||||
// min bitrate used during last kBweIncreaseIntervalMs.
|
||||
void UpdateMinHistory(int64_t at_time);
|
||||
void UpdateMinHistory(Timestamp at_time);
|
||||
|
||||
// Gets the upper limit for the target bitrate. This is the minimum of the
|
||||
// delay based limit, the receiver limit and the loss based controller limit.
|
||||
int64_t GetUpperLimit() const;
|
||||
DataRate GetUpperLimit() const;
|
||||
// Prints a warning if `bitrate` if sufficiently long time has past since last
|
||||
// warning.
|
||||
void MaybeLogLowBitrateWarning(int64_t bitrate, int64_t at_time);
|
||||
// Stores an update to the event log if the loss rate has changed, the target
|
||||
// has changed, or sufficient time has passed since last stored event.
|
||||
void MaybeLogLossBasedEvent(int64_t at_time);
|
||||
void MaybeLogLowBitrateWarning(DataRate bitrate, Timestamp at_time);
|
||||
|
||||
// Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and
|
||||
// set `current_bitrate_` to the capped value and updates the event log.
|
||||
void UpdateTargetBitrate(int64_t bitrate, int64_t at_time);
|
||||
void UpdateTargetBitrate(DataRate bitrate, Timestamp at_time);
|
||||
// Applies lower and upper bounds to the current target rate.
|
||||
// TODO(srte): This seems to be called even when limits haven't changed, that
|
||||
// should be cleaned up.
|
||||
void ApplyTargetLimits(int64_t at_time);
|
||||
void ApplyTargetLimits(Timestamp at_time);
|
||||
|
||||
RttBasedBackoff rtt_backoff_;
|
||||
LinkCapacityTracker link_capacity_;
|
||||
|
||||
std::deque<std::pair<int64_t, int64_t> > min_bitrate_history_;
|
||||
std::deque<std::pair<Timestamp, DataRate> > min_bitrate_history_;
|
||||
|
||||
// incoming filters
|
||||
int lost_packets_since_last_loss_update_;
|
||||
int expected_packets_since_last_loss_update_;
|
||||
|
||||
std::optional<int64_t> acknowledged_rate_;
|
||||
int64_t current_target_;
|
||||
int64_t last_logged_target_;
|
||||
int64_t min_bitrate_configured_;
|
||||
int64_t max_bitrate_configured_;
|
||||
int64_t last_low_bitrate_log_;
|
||||
std::optional<DataRate> acknowledged_rate_;
|
||||
DataRate current_target_;
|
||||
DataRate last_logged_target_;
|
||||
DataRate min_bitrate_configured_;
|
||||
DataRate max_bitrate_configured_;
|
||||
Timestamp last_low_bitrate_log_;
|
||||
|
||||
bool has_decreased_since_last_fraction_loss_;
|
||||
int64_t last_loss_feedback_;
|
||||
int64_t last_loss_packet_report_;
|
||||
Timestamp last_loss_feedback_;
|
||||
Timestamp last_loss_packet_report_;
|
||||
uint8_t last_fraction_loss_;
|
||||
uint8_t last_logged_fraction_loss_;
|
||||
int64_t last_round_trip_time_;
|
||||
TimeDelta last_round_trip_time_;
|
||||
|
||||
// The max bitrate as set by the receiver in the call. This is typically
|
||||
// signalled using the REMB RTCP message and is used when we don't have any
|
||||
// send side delay based estimate.
|
||||
int64_t receiver_limit_;
|
||||
int64_t delay_based_limit_;
|
||||
int64_t time_last_decrease_;
|
||||
int64_t first_report_time_;
|
||||
DataRate receiver_limit_;
|
||||
DataRate delay_based_limit_;
|
||||
Timestamp time_last_decrease_;
|
||||
Timestamp first_report_time_;
|
||||
int initially_lost_packets_;
|
||||
int64_t bitrate_at_2_seconds_;
|
||||
DataRate bitrate_at_2_seconds_;
|
||||
UmaState uma_update_state_;
|
||||
UmaState uma_rtt_state_;
|
||||
std::vector<bool> rampup_uma_stats_updated_;
|
||||
int64_t last_rtc_event_log_;
|
||||
float low_loss_threshold_;
|
||||
float high_loss_threshold_;
|
||||
int64_t bitrate_threshold_;
|
||||
DataRate bitrate_threshold_;
|
||||
bool disable_receiver_limit_caps_only_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "api/transport/network_types.h"
|
||||
#include "congestion_control_feedback.h"
|
||||
#include "network_route.h"
|
||||
#include "network_types.h"
|
||||
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
||||
|
||||
struct PacketFeedback {
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include "bandwidth_usage.h"
|
||||
#include "api/transport/bandwidth_usage.h"
|
||||
#include "delay_increase_detector_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
static constexpr int kAbsSendTimeFraction = 18;
|
||||
|
||||
enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13
|
||||
|
||||
struct RTPHeader {
|
||||
|
||||
Reference in New Issue
Block a user