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 <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "network_types.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "bitrate_estimator.h"
|
#include "bitrate_estimator.h"
|
||||||
#include "network_types.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
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 <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "network_types.h"
|
|
||||||
#include "overuse_detector.h"
|
#include "overuse_detector.h"
|
||||||
#include "rtc_base/numerics/safe_minmax.h"
|
#include "rtc_base/numerics/safe_minmax.h"
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "bwe_defines.h"
|
#include "bwe_defines.h"
|
||||||
#include "link_capacity_estimator.h"
|
#include "link_capacity_estimator.h"
|
||||||
#include "network_types.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
// A rate control implementation based on additive increases of
|
// A rate control implementation based on additive increases of
|
||||||
@@ -98,7 +98,7 @@ class AimdRateControl {
|
|||||||
const bool send_side_;
|
const bool send_side_;
|
||||||
// Allow the delay based estimate to only increase as long as application
|
// Allow the delay based estimate to only increase as long as application
|
||||||
// limited region (alr) is not detected.
|
// 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.
|
// If "Disabled", estimated link capacity is not used as upper bound.
|
||||||
bool disable_estimate_bounded_increase_ = true;
|
bool disable_estimate_bounded_increase_ = true;
|
||||||
bool use_current_estimate_as_min_upper_bound_ = true;
|
bool use_current_estimate_as_min_upper_bound_ = true;
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/transport/bandwidth_usage.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
#include "bandwidth_usage.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
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.
|
// below the current throughput estimate to drain the network queues.
|
||||||
constexpr double kProbeDropThroughputFraction = 0.85;
|
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() {}
|
CongestionControl::~CongestionControl() {}
|
||||||
|
|
||||||
@@ -35,28 +62,27 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
|||||||
|
|
||||||
if (congestion_window_pushback_controller_) {
|
if (congestion_window_pushback_controller_) {
|
||||||
congestion_window_pushback_controller_->UpdateOutstandingData(
|
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();
|
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
|
||||||
int64_t min_propagation_rtt = std::numeric_limits<int64_t>::max();
|
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
|
||||||
int64_t max_recv_time = std::numeric_limits<int64_t>::min();
|
Timestamp max_recv_time = Timestamp::MinusInfinity();
|
||||||
|
|
||||||
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
|
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
|
||||||
for (const auto& feedback : feedbacks)
|
for (const auto& feedback : feedbacks)
|
||||||
max_recv_time = std::max(max_recv_time, feedback.receive_time);
|
max_recv_time = std::max(max_recv_time, feedback.receive_time);
|
||||||
|
|
||||||
for (const auto& feedback : feedbacks) {
|
for (const auto& feedback : feedbacks) {
|
||||||
int64_t feedback_rtt =
|
TimeDelta feedback_rtt =
|
||||||
report.feedback_time - feedback.sent_packet.send_time;
|
report.feedback_time - feedback.sent_packet.send_time;
|
||||||
int64_t min_pending_time = max_recv_time - feedback.receive_time;
|
TimeDelta min_pending_time = max_recv_time - feedback.receive_time;
|
||||||
int64_t propagation_rtt = feedback_rtt - min_pending_time;
|
TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
|
||||||
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
|
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
|
||||||
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
|
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_feedback_rtt != std::numeric_limits<int64_t>::min() &&
|
if (max_feedback_rtt.IsFinite()) {
|
||||||
min_propagation_rtt != std::numeric_limits<int64_t>::max()) {
|
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
|
||||||
feedback_max_rtts_.push_back(max_feedback_rtt);
|
|
||||||
const size_t kMaxFeedbackRttWindow = 32;
|
const size_t kMaxFeedbackRttWindow = 32;
|
||||||
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
|
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
|
||||||
feedback_max_rtts_.pop_front();
|
feedback_max_rtts_.pop_front();
|
||||||
@@ -70,19 +96,19 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
|||||||
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
|
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
|
||||||
static_cast<int64_t>(0));
|
static_cast<int64_t>(0));
|
||||||
int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
|
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) {
|
for (const auto& packet_feedback : feedbacks) {
|
||||||
int64_t pending_time = max_recv_time - packet_feedback.receive_time;
|
TimeDelta pending_time = max_recv_time - packet_feedback.receive_time;
|
||||||
int64_t rtt = report.feedback_time -
|
TimeDelta rtt = report.feedback_time -
|
||||||
packet_feedback.sent_packet.send_time - pending_time;
|
packet_feedback.sent_packet.send_time - pending_time;
|
||||||
// Value used for predicting NACK round trip time in FEC controller.
|
// Value used for predicting NACK round trip time in FEC controller.
|
||||||
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
|
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
|
||||||
}
|
}
|
||||||
if (feedback_min_rtt != std::numeric_limits<int64_t>::max() &&
|
if (feedback_min_rtt.IsFinite()) {
|
||||||
feedback_min_rtt != std::numeric_limits<int64_t>::min()) {
|
|
||||||
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
|
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +131,7 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
|||||||
alr_detector_->GetApplicationLimitedRegionStartTime();
|
alr_detector_->GetApplicationLimitedRegionStartTime();
|
||||||
|
|
||||||
if (previously_in_alr_ && !alr_start_time.has_value()) {
|
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);
|
acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
|
||||||
probe_controller_->SetAlrEndedTimeMs(now_ms);
|
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();
|
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
|
||||||
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
|
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
|
||||||
estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
|
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
|
// 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
|
// encoder overshoot. We use std::min to ensure that a probe result
|
||||||
// below the current BWE never causes an increase.
|
// below the current BWE never causes an increase.
|
||||||
int64_t limit =
|
DataRate limit =
|
||||||
std::min(delay_based_bwe_->last_estimate(),
|
std::min(delay_based_bwe_->last_estimate(),
|
||||||
*acknowledged_bitrate * kProbeDropThroughputFraction);
|
*acknowledged_bitrate * kProbeDropThroughputFraction);
|
||||||
probe_bitrate = std::max(*probe_bitrate, limit);
|
probe_bitrate = std::max(*probe_bitrate, limit);
|
||||||
|
|||||||
@@ -5,12 +5,18 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "acknowledged_bitrate_estimator.h"
|
#include "acknowledged_bitrate_estimator.h"
|
||||||
|
#include "acknowledged_bitrate_estimator_interface.h"
|
||||||
#include "alr_detector.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 "congestion_window_pushback_controller.h"
|
||||||
#include "delay_based_bwe.h"
|
#include "delay_based_bwe.h"
|
||||||
#include "network_types.h"
|
#include "probe_controller.h"
|
||||||
#include "send_side_bandwidth_estimation.h"
|
#include "send_side_bandwidth_estimation.h"
|
||||||
|
|
||||||
|
using namespace webrtc;
|
||||||
|
|
||||||
class CongestionControl {
|
class CongestionControl {
|
||||||
public:
|
public:
|
||||||
CongestionControl();
|
CongestionControl();
|
||||||
@@ -21,24 +27,54 @@ class CongestionControl {
|
|||||||
TransportPacketsFeedback report);
|
TransportPacketsFeedback report);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::unique_ptr<CongestionWindowPushbackController>
|
const bool use_min_allocatable_as_lower_bound_;
|
||||||
congestion_window_pushback_controller_;
|
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:
|
const std::unique_ptr<ProbeController> probe_controller_;
|
||||||
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_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
|
std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
|
||||||
std::unique_ptr<AlrDetector> alr_detector_;
|
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<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
|
#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
|
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||||||
* @Date: 2024-12-18
|
*
|
||||||
* Copyright (c) 2024 by DI JUNKUN, 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_
|
#include <cstdint>
|
||||||
#define _CONGESTION_CONTROL_FEEDBACK_GENERATOR_H_
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#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 "congestion_control_feedback_tracker.h"
|
||||||
#include "rtcp_packet.h"
|
|
||||||
#include "rtp_packet_received.h"
|
#include "rtp_packet_received.h"
|
||||||
|
#include "rtp_transport_feedback_generator.h"
|
||||||
|
|
||||||
class CongestionControlFeedbackGenerator {
|
namespace webrtc {
|
||||||
|
|
||||||
|
// The class is responsible for generating RTCP feedback packets based on
|
||||||
|
// incoming media packets. Feedback format will comply with RFC 8888.
|
||||||
|
// https://datatracker.ietf.org/doc/rfc8888/
|
||||||
|
|
||||||
|
// Feedback should not use more than 5% of the configured send bandwidth
|
||||||
|
// estimate. Min and max duration between feedback is configurable using field
|
||||||
|
// trials, but per default, min is 25ms and max is 250ms.
|
||||||
|
// If possible, given the other constraints, feedback will be sent when a packet
|
||||||
|
// with marker bit is received in order to provide feedback as soon as possible
|
||||||
|
// after receiving a complete video frame. If no packet with marker bit is
|
||||||
|
// received, feedback can be delayed up to 25ms after the first packet since the
|
||||||
|
// last sent feedback. On good networks, this means that a sender may receive
|
||||||
|
// feedback for every sent frame.
|
||||||
|
class CongestionControlFeedbackGenerator
|
||||||
|
: public RtpTransportFeedbackGenerator {
|
||||||
public:
|
public:
|
||||||
CongestionControlFeedbackGenerator(RtcpSender feedback_sender);
|
CongestionControlFeedbackGenerator(
|
||||||
|
std::shared_ptr<SimulatedClock> clock,
|
||||||
|
RtpTransportFeedbackGenerator::RtcpSender feedback_sender);
|
||||||
~CongestionControlFeedbackGenerator() = default;
|
~CongestionControlFeedbackGenerator() = default;
|
||||||
|
|
||||||
void OnReceivedPacket(RtpPacketReceived& packet);
|
void OnReceivedPacket(const RtpPacketReceived& packet) override;
|
||||||
|
|
||||||
void OnSendBandwidthEstimateChanged(int64_t estimate);
|
void OnSendBandwidthEstimateChanged(DataRate estimate) override;
|
||||||
|
|
||||||
int64_t Process(int64_t now_ms);
|
TimeDelta Process(Timestamp now) override;
|
||||||
|
|
||||||
void SetTransportOverhead(int64_t overhead_per_packet);
|
void SetTransportOverhead(DataSize overhead_per_packet) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t NextFeedbackTime() const;
|
Timestamp NextFeedbackTime() const;
|
||||||
|
|
||||||
void SendFeedback(int64_t now_ms);
|
void SendFeedback(Timestamp now);
|
||||||
|
|
||||||
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
|
void CalculateNextPossibleSendTime(DataSize feedback_size, Timestamp now);
|
||||||
|
|
||||||
|
std::shared_ptr<SimulatedClock> clock_;
|
||||||
const RtcpSender rtcp_sender_;
|
const RtcpSender rtcp_sender_;
|
||||||
|
|
||||||
private:
|
TimeDelta min_time_between_feedback_;
|
||||||
// Feedback should not use more than 5% of the configured send bandwidth
|
TimeDelta max_time_to_wait_for_packet_with_marker_;
|
||||||
// estimate. Min and max duration between feedback is configurable using field
|
TimeDelta max_time_between_feedback_;
|
||||||
// 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;
|
|
||||||
|
|
||||||
int64_t max_feedback_rate_ = 1000; // kbps
|
DataRate max_feedback_rate_ = DataRate::KilobitsPerSec(1000);
|
||||||
int64_t packet_overhead_ = 0;
|
DataSize packet_overhead_ = DataSize::Zero();
|
||||||
int64_t send_rate_debt_ = 0;
|
DataSize send_rate_debt_ = DataSize::Zero();
|
||||||
|
|
||||||
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
|
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
|
||||||
feedback_trackers_;
|
feedback_trackers_;
|
||||||
|
|
||||||
std::optional<int64_t> first_arrival_time_since_feedback_;
|
// std::vector<PacketInfo> packets_;
|
||||||
int64_t next_possible_feedback_send_time_ = 0;
|
Timestamp last_feedback_sent_time_ = Timestamp::Zero();
|
||||||
int64_t last_feedback_sent_time_ = 0;
|
std::optional<Timestamp> first_arrival_time_since_feedback_;
|
||||||
|
|
||||||
bool marker_bit_seen_ = false;
|
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.
|
// received.
|
||||||
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
|
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
|
||||||
}
|
}
|
||||||
packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number,
|
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
|
||||||
packet.arrival_time(), packet.ecn());
|
packet.arrival_time(), packet.ecn()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
||||||
@@ -89,8 +89,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
|||||||
++packet_it;
|
++packet_it;
|
||||||
}
|
}
|
||||||
} // else - the packet has not been received yet.
|
} // else - the packet has not been received yet.
|
||||||
packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
|
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
|
||||||
arrival_time_offset, ecn);
|
arrival_time_offset, ecn});
|
||||||
}
|
}
|
||||||
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
|
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
|
||||||
packets_.clear();
|
packets_.clear();
|
||||||
|
|||||||
@@ -13,7 +13,17 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#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(
|
void CongestionWindowPushbackController::UpdateOutstandingData(
|
||||||
int64_t outstanding_bytes) {
|
int64_t outstanding_bytes) {
|
||||||
@@ -24,17 +34,18 @@ void CongestionWindowPushbackController::UpdatePacingQueue(
|
|||||||
pacing_bytes_ = pacing_bytes;
|
pacing_bytes_ = pacing_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionWindowPushbackController::SetDataWindow(int64_t data_window) {
|
void CongestionWindowPushbackController::SetDataWindow(DataSize data_window) {
|
||||||
current_data_window_ = data_window;
|
current_data_window_ = data_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
|
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
|
||||||
uint32_t bitrate_bps) {
|
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_;
|
int64_t total_bytes = outstanding_bytes_;
|
||||||
if (add_pacing_) total_bytes += pacing_bytes_;
|
if (add_pacing_) total_bytes += pacing_bytes_;
|
||||||
double fill_ratio =
|
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) {
|
if (fill_ratio > 1.5) {
|
||||||
encoding_rate_ratio_ *= 0.9;
|
encoding_rate_ratio_ *= 0.9;
|
||||||
} else if (fill_ratio > 1) {
|
} else if (fill_ratio > 1) {
|
||||||
@@ -55,3 +66,5 @@ uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
|
|||||||
: adjusted_target_bitrate_bps;
|
: adjusted_target_bitrate_bps;
|
||||||
return bitrate_bps;
|
return bitrate_bps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: DI JUNKUN
|
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||||
* @Date: 2025-01-13
|
*
|
||||||
* Copyright (c) 2025 by DI JUNKUN, 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_
|
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||||
#define _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/units/data_size.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
// This class enables pushback from congestion window directly to video encoder.
|
// This class enables pushback from congestion window directly to video encoder.
|
||||||
// When the congestion window is filling up, the video encoder target bitrate
|
// When the congestion window is filling up, the video encoder target bitrate
|
||||||
// will be reduced accordingly to accommodate the network changes. To avoid
|
// will be reduced accordingly to accommodate the network changes. To avoid
|
||||||
@@ -22,15 +30,17 @@ class CongestionWindowPushbackController {
|
|||||||
void UpdateOutstandingData(int64_t outstanding_bytes);
|
void UpdateOutstandingData(int64_t outstanding_bytes);
|
||||||
void UpdatePacingQueue(int64_t pacing_bytes);
|
void UpdatePacingQueue(int64_t pacing_bytes);
|
||||||
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
|
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
|
||||||
void SetDataWindow(int64_t data_window);
|
void SetDataWindow(DataSize data_window);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const bool add_pacing_ = true;
|
const bool add_pacing_;
|
||||||
const uint32_t min_pushback_target_bitrate_bps_ = 10000;
|
const uint32_t min_pushback_target_bitrate_bps_;
|
||||||
std::optional<int64_t> current_data_window_ = std::nullopt;
|
std::optional<DataSize> current_data_window_;
|
||||||
int64_t outstanding_bytes_ = 0;
|
int64_t outstanding_bytes_ = 0;
|
||||||
int64_t pacing_bytes_ = 0;
|
int64_t pacing_bytes_ = 0;
|
||||||
double encoding_rate_ratio_ = 1.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 <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
@@ -24,7 +25,6 @@
|
|||||||
#include "bwe_defines.h"
|
#include "bwe_defines.h"
|
||||||
#include "inter_arrival_delta.h"
|
#include "inter_arrival_delta.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "network_types.h"
|
|
||||||
#include "trendline_estimator.h"
|
#include "trendline_estimator.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|||||||
@@ -18,15 +18,15 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "aimd_rate_control.h"
|
#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/data_rate.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "bandwidth_usage.h"
|
|
||||||
#include "delay_increase_detector_interface.h"
|
#include "delay_increase_detector_interface.h"
|
||||||
#include "inter_arrival.h"
|
#include "inter_arrival.h"
|
||||||
#include "inter_arrival_delta.h"
|
#include "inter_arrival_delta.h"
|
||||||
#include "link_capacity_estimator.h"
|
#include "link_capacity_estimator.h"
|
||||||
#include "network_types.h"
|
|
||||||
#include "probe_bitrate_estimator.h"
|
#include "probe_bitrate_estimator.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "bandwidth_usage.h"
|
#include "api/transport/bandwidth_usage.h"
|
||||||
|
|
||||||
namespace webrtc {
|
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 <stdint.h>
|
||||||
|
|
||||||
#include "bandwidth_usage.h"
|
#include "api/transport/bandwidth_usage.h"
|
||||||
|
|
||||||
namespace webrtc {
|
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 <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "network_types.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
@@ -14,10 +14,10 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "network_types.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
class RtcEventLog;
|
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
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||||
* @Date: 2024-12-12
|
*
|
||||||
* Copyright (c) 2024 by DI JUNKUN, 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_
|
#ifndef MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||||
#define _RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
#define MODULES_CONGESTION_CONTROLLER_INCLUDE_RECEIVE_SIDE_CONGESTION_CONTROLLER_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#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 "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"
|
#include "rtp_packet_received.h"
|
||||||
|
|
||||||
class ReceiveSideCongestionController {
|
namespace webrtc {
|
||||||
public:
|
class RemoteBitrateEstimator;
|
||||||
enum MediaType { VIDEO, AUDIO, DATA };
|
|
||||||
|
|
||||||
|
// 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:
|
public:
|
||||||
ReceiveSideCongestionController(RtcpSender feedback_sender);
|
ReceiveSideCongestionController(
|
||||||
~ReceiveSideCongestionController() = default;
|
std::shared_ptr<SimulatedClock> clock,
|
||||||
|
RtpTransportFeedbackGenerator::RtcpSender feedback_sender,
|
||||||
|
RembThrottler::RembSender remb_sender,
|
||||||
|
std::shared_ptr<NetworkStateEstimator> network_state_estimator);
|
||||||
|
|
||||||
public:
|
~ReceiveSideCongestionController() override = default;
|
||||||
void OnReceivedPacket(RtpPacketReceived& packet, MediaType media_type);
|
|
||||||
|
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.
|
// This is send bitrate, used to control the rate of feedback messages.
|
||||||
void OnBitrateChanged(int bitrate_bps);
|
void OnBitrateChanged(int bitrate_bps);
|
||||||
|
|
||||||
// Ensures the remote party is notified of the receive bitrate no larger than
|
// Ensures the remote party is notified of the receive bitrate no larger than
|
||||||
// `bitrate` using RTCP REMB.
|
// `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
|
// Runs periodic tasks if it is time to run them, returns time until next
|
||||||
// call to `MaybeProcess` should be non idle.
|
// call to `MaybeProcess` should be non idle.
|
||||||
int64_t MaybeProcess();
|
TimeDelta MaybeProcess();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// RembThrottler remb_throttler_;
|
void PickEstimator();
|
||||||
|
|
||||||
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
|
std::shared_ptr<SimulatedClock> clock_;
|
||||||
// ReceiveSideCongestionController. At the time of
|
RembThrottler remb_throttler_;
|
||||||
// writing OnReceivedPacket and MaybeProcess can unfortunately be called on an
|
|
||||||
// arbitrary thread by external projects.
|
|
||||||
// SequenceChecker sequence_checker_;
|
|
||||||
|
|
||||||
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
|
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
|
||||||
|
|
||||||
std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
|
std::unique_ptr<RemoteBitrateEstimator> rbe_;
|
||||||
bool using_absolute_send_time_;
|
bool using_absolute_send_time_;
|
||||||
uint32_t packets_since_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 <string>
|
||||||
#include <utility>
|
#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"
|
#include "log.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int64_t kBweIncreaseInterval = 1000;
|
constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000);
|
||||||
constexpr int64_t kBweDecreaseInterval = 300;
|
constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300);
|
||||||
constexpr int64_t kStartPhase = 2000;
|
constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000);
|
||||||
constexpr int64_t kBweConverganceTime = 20000;
|
constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000);
|
||||||
constexpr int kLimitNumPackets = 20;
|
constexpr int kLimitNumPackets = 20;
|
||||||
constexpr int64_t kDefaultMaxBitrate = 1000000000;
|
constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000);
|
||||||
constexpr int64_t kLowBitrateLogPeriod = 10000;
|
constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000);
|
||||||
constexpr int64_t kRtcEventLogPeriod = 5000;
|
constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000);
|
||||||
// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals.
|
// 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 kDefaultLowLossThreshold = 0.02f;
|
||||||
constexpr float kDefaultHighLossThreshold = 0.1f;
|
constexpr float kDefaultHighLossThreshold = 0.1f;
|
||||||
constexpr int64_t kDefaultBitrateThreshold = 0;
|
constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero();
|
||||||
|
|
||||||
constexpr int64_t kCongestionControllerMinBitrate = 5000;
|
|
||||||
|
|
||||||
struct UmaRampUpMetric {
|
struct UmaRampUpMetric {
|
||||||
const char* metric_name;
|
const char* metric_name;
|
||||||
@@ -51,64 +56,70 @@ const UmaRampUpMetric kUmaRampupMetrics[] = {
|
|||||||
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
|
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
|
||||||
const size_t kNumUmaRampupMetrics =
|
const size_t kNumUmaRampupMetrics =
|
||||||
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
|
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
|
||||||
|
|
||||||
|
const char kBweLosExperiment[] = "WebRTC-BweLossExperiment";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void LinkCapacityTracker::UpdateDelayBasedEstimate(
|
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_) {
|
if (delay_based_bitrate < last_delay_based_estimate_) {
|
||||||
capacity_estimate_bps_ = std::min(capacity_estimate_bps_,
|
capacity_estimate_bps_ =
|
||||||
static_cast<double>(delay_based_bitrate));
|
std::min(capacity_estimate_bps_, delay_based_bitrate.bps<double>());
|
||||||
last_link_capacity_update_ = at_time;
|
last_link_capacity_update_ = at_time;
|
||||||
}
|
}
|
||||||
last_delay_based_estimate_ = delay_based_bitrate;
|
last_delay_based_estimate_ = delay_based_bitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkCapacityTracker::OnStartingRate(int64_t start_rate) {
|
void LinkCapacityTracker::OnStartingRate(DataRate start_rate) {
|
||||||
if (IsInfinite(last_link_capacity_update_)) {
|
if (last_link_capacity_update_.IsInfinite())
|
||||||
capacity_estimate_bps_ = start_rate;
|
capacity_estimate_bps_ = start_rate.bps<double>();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkCapacityTracker::OnRateUpdate(std::optional<int64_t> acknowledged,
|
void LinkCapacityTracker::OnRateUpdate(std::optional<DataRate> acknowledged,
|
||||||
int64_t target, int64_t at_time) {
|
DataRate target, Timestamp at_time) {
|
||||||
if (!acknowledged) return;
|
if (!acknowledged) return;
|
||||||
int64_t acknowledged_target = std::min(*acknowledged, target);
|
DataRate acknowledged_target = std::min(*acknowledged, target);
|
||||||
if (acknowledged_target > capacity_estimate_bps_) {
|
if (acknowledged_target.bps() > capacity_estimate_bps_) {
|
||||||
int64_t delta = at_time - last_link_capacity_update_;
|
TimeDelta delta = at_time - last_link_capacity_update_;
|
||||||
double alpha = IsFinite(delta) ? exp(-(delta / 10)) : 0;
|
double alpha =
|
||||||
capacity_estimate_bps_ =
|
delta.IsFinite() ? exp(-(delta / TimeDelta::Seconds(10))) : 0;
|
||||||
alpha * capacity_estimate_bps_ + (1 - alpha) * acknowledged_target;
|
capacity_estimate_bps_ = alpha * capacity_estimate_bps_ +
|
||||||
|
(1 - alpha) * acknowledged_target.bps<double>();
|
||||||
}
|
}
|
||||||
last_link_capacity_update_ = at_time;
|
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_ =
|
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;
|
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()
|
RttBasedBackoff::RttBasedBackoff()
|
||||||
: disabled_(true),
|
: disabled_(true),
|
||||||
configured_limit_(3),
|
configured_limit_(TimeDelta::Seconds(3)),
|
||||||
drop_fraction_(0.8),
|
drop_fraction_(0.8),
|
||||||
drop_interval_(1),
|
drop_interval_(TimeDelta::Seconds(1)),
|
||||||
bandwidth_floor_(5),
|
bandwidth_floor_(DataRate::KilobitsPerSec(5)),
|
||||||
rtt_limit_(INT64_T_MAX),
|
rtt_limit_(TimeDelta::PlusInfinity()),
|
||||||
// By initializing this to plus infinity, we make sure that we never
|
// By initializing this to plus infinity, we make sure that we never
|
||||||
// trigger rtt backoff unless packet feedback is enabled.
|
// trigger rtt backoff unless packet feedback is enabled.
|
||||||
last_propagation_rtt_update_(INT64_T_MAX),
|
last_propagation_rtt_update_(Timestamp::PlusInfinity()),
|
||||||
last_propagation_rtt_(0),
|
last_propagation_rtt_(TimeDelta::Zero()),
|
||||||
last_packet_sent_(INT64_T_MIN) {
|
last_packet_sent_(Timestamp::MinusInfinity()) {
|
||||||
if (!disabled_) {
|
if (!disabled_) {
|
||||||
rtt_limit_ = configured_limit_;
|
rtt_limit_ = configured_limit_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RttBasedBackoff::UpdatePropagationRtt(int64_t at_time,
|
void RttBasedBackoff::UpdatePropagationRtt(Timestamp at_time,
|
||||||
int64_t propagation_rtt) {
|
TimeDelta propagation_rtt) {
|
||||||
last_propagation_rtt_update_ = at_time;
|
last_propagation_rtt_update_ = at_time;
|
||||||
last_propagation_rtt_ = propagation_rtt;
|
last_propagation_rtt_ = propagation_rtt;
|
||||||
}
|
}
|
||||||
@@ -117,11 +128,10 @@ bool RttBasedBackoff::IsRttAboveLimit() const {
|
|||||||
return CorrectedRtt() > rtt_limit_;
|
return CorrectedRtt() > rtt_limit_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t RttBasedBackoff::CorrectedRtt() const {
|
TimeDelta RttBasedBackoff::CorrectedRtt() const {
|
||||||
// Avoid timeout when no packets are being sent.
|
// Avoid timeout when no packets are being sent.
|
||||||
int64_t timeout_correction =
|
TimeDelta timeout_correction = std::max(
|
||||||
std::max(last_packet_sent_ - last_propagation_rtt_update_,
|
last_packet_sent_ - last_propagation_rtt_update_, TimeDelta::Zero());
|
||||||
static_cast<int64_t>(0));
|
|
||||||
return timeout_correction + last_propagation_rtt_;
|
return timeout_correction + last_propagation_rtt_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,61 +140,61 @@ RttBasedBackoff::~RttBasedBackoff() = default;
|
|||||||
SendSideBandwidthEstimation::SendSideBandwidthEstimation()
|
SendSideBandwidthEstimation::SendSideBandwidthEstimation()
|
||||||
: lost_packets_since_last_loss_update_(0),
|
: lost_packets_since_last_loss_update_(0),
|
||||||
expected_packets_since_last_loss_update_(0),
|
expected_packets_since_last_loss_update_(0),
|
||||||
current_target_(0),
|
current_target_(DataRate::Zero()),
|
||||||
last_logged_target_(0),
|
last_logged_target_(DataRate::Zero()),
|
||||||
min_bitrate_configured_(kCongestionControllerMinBitrate),
|
min_bitrate_configured_(kCongestionControllerMinBitrate),
|
||||||
max_bitrate_configured_(kDefaultMaxBitrate),
|
max_bitrate_configured_(kDefaultMaxBitrate),
|
||||||
last_low_bitrate_log_(INT64_T_MIN),
|
last_low_bitrate_log_(Timestamp::MinusInfinity()),
|
||||||
has_decreased_since_last_fraction_loss_(false),
|
has_decreased_since_last_fraction_loss_(false),
|
||||||
last_loss_feedback_(INT64_T_MIN),
|
last_loss_feedback_(Timestamp::MinusInfinity()),
|
||||||
last_loss_packet_report_(INT64_T_MIN),
|
last_loss_packet_report_(Timestamp::MinusInfinity()),
|
||||||
last_fraction_loss_(0),
|
last_fraction_loss_(0),
|
||||||
last_logged_fraction_loss_(0),
|
last_logged_fraction_loss_(0),
|
||||||
last_round_trip_time_(0),
|
last_round_trip_time_(TimeDelta::Zero()),
|
||||||
receiver_limit_(INT64_T_MAX),
|
receiver_limit_(DataRate::PlusInfinity()),
|
||||||
delay_based_limit_(INT64_T_MAX),
|
delay_based_limit_(DataRate::PlusInfinity()),
|
||||||
time_last_decrease_(INT64_T_MIN),
|
time_last_decrease_(Timestamp::MinusInfinity()),
|
||||||
first_report_time_(INT64_T_MIN),
|
first_report_time_(Timestamp::MinusInfinity()),
|
||||||
initially_lost_packets_(0),
|
initially_lost_packets_(0),
|
||||||
bitrate_at_2_seconds_(0),
|
bitrate_at_2_seconds_(DataRate::Zero()),
|
||||||
uma_update_state_(kNoUpdate),
|
uma_update_state_(kNoUpdate),
|
||||||
uma_rtt_state_(kNoUpdate),
|
uma_rtt_state_(kNoUpdate),
|
||||||
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
|
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false),
|
||||||
last_rtc_event_log_(INT64_T_MIN),
|
|
||||||
low_loss_threshold_(kDefaultLowLossThreshold),
|
low_loss_threshold_(kDefaultLowLossThreshold),
|
||||||
high_loss_threshold_(kDefaultHighLossThreshold),
|
high_loss_threshold_(kDefaultHighLossThreshold),
|
||||||
bitrate_threshold_(kDefaultBitrateThreshold),
|
bitrate_threshold_(kDefaultBitrateThreshold),
|
||||||
disable_receiver_limit_caps_only_(true) {}
|
disable_receiver_limit_caps_only_("Disabled") {
|
||||||
|
// rtt_backoff_ =
|
||||||
|
}
|
||||||
|
|
||||||
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
|
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::OnRouteChange() {
|
void SendSideBandwidthEstimation::OnRouteChange() {
|
||||||
lost_packets_since_last_loss_update_ = 0;
|
lost_packets_since_last_loss_update_ = 0;
|
||||||
expected_packets_since_last_loss_update_ = 0;
|
expected_packets_since_last_loss_update_ = 0;
|
||||||
current_target_ = 0;
|
current_target_ = DataRate::Zero();
|
||||||
min_bitrate_configured_ = kCongestionControllerMinBitrate;
|
min_bitrate_configured_ = kCongestionControllerMinBitrate;
|
||||||
max_bitrate_configured_ = kDefaultMaxBitrate;
|
max_bitrate_configured_ = kDefaultMaxBitrate;
|
||||||
last_low_bitrate_log_ = INT64_T_MIN;
|
last_low_bitrate_log_ = Timestamp::MinusInfinity();
|
||||||
has_decreased_since_last_fraction_loss_ = false;
|
has_decreased_since_last_fraction_loss_ = false;
|
||||||
last_loss_feedback_ = INT64_T_MIN;
|
last_loss_feedback_ = Timestamp::MinusInfinity();
|
||||||
last_loss_packet_report_ = INT64_T_MIN;
|
last_loss_packet_report_ = Timestamp::MinusInfinity();
|
||||||
last_fraction_loss_ = 0;
|
last_fraction_loss_ = 0;
|
||||||
last_logged_fraction_loss_ = 0;
|
last_logged_fraction_loss_ = 0;
|
||||||
last_round_trip_time_ = 0;
|
last_round_trip_time_ = TimeDelta::Zero();
|
||||||
receiver_limit_ = INT64_T_MAX;
|
receiver_limit_ = DataRate::PlusInfinity();
|
||||||
delay_based_limit_ = INT64_T_MAX;
|
delay_based_limit_ = DataRate::PlusInfinity();
|
||||||
time_last_decrease_ = INT64_T_MIN;
|
time_last_decrease_ = Timestamp::MinusInfinity();
|
||||||
first_report_time_ = INT64_T_MIN;
|
first_report_time_ = Timestamp::MinusInfinity();
|
||||||
initially_lost_packets_ = 0;
|
initially_lost_packets_ = 0;
|
||||||
bitrate_at_2_seconds_ = 0;
|
bitrate_at_2_seconds_ = DataRate::Zero();
|
||||||
uma_update_state_ = kNoUpdate;
|
uma_update_state_ = kNoUpdate;
|
||||||
uma_rtt_state_ = kNoUpdate;
|
uma_rtt_state_ = kNoUpdate;
|
||||||
last_rtc_event_log_ = INT64_T_MIN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::SetBitrates(
|
void SendSideBandwidthEstimation::SetBitrates(
|
||||||
std::optional<int64_t> send_bitrate, int64_t min_bitrate,
|
std::optional<DataRate> send_bitrate, DataRate min_bitrate,
|
||||||
int64_t max_bitrate, int64_t at_time) {
|
DataRate max_bitrate, Timestamp at_time) {
|
||||||
SetMinMaxBitrate(min_bitrate, max_bitrate);
|
SetMinMaxBitrate(min_bitrate, max_bitrate);
|
||||||
if (send_bitrate) {
|
if (send_bitrate) {
|
||||||
link_capacity_.OnStartingRate(*send_bitrate);
|
link_capacity_.OnStartingRate(*send_bitrate);
|
||||||
@@ -192,21 +202,21 @@ void SendSideBandwidthEstimation::SetBitrates(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::SetSendBitrate(int64_t bitrate,
|
void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate,
|
||||||
int64_t at_time) {
|
Timestamp at_time) {
|
||||||
// Reset to avoid being capped by the estimate.
|
// Reset to avoid being capped by the estimate.
|
||||||
delay_based_limit_ = INT64_T_MAX;
|
delay_based_limit_ = DataRate::PlusInfinity();
|
||||||
UpdateTargetBitrate(bitrate, at_time);
|
UpdateTargetBitrate(bitrate, at_time);
|
||||||
// Clear last sent bitrate history so the new value can be used directly
|
// Clear last sent bitrate history so the new value can be used directly
|
||||||
// and not capped.
|
// and not capped.
|
||||||
min_bitrate_history_.clear();
|
min_bitrate_history_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
|
void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate,
|
||||||
int64_t max_bitrate) {
|
DataRate max_bitrate) {
|
||||||
min_bitrate_configured_ =
|
min_bitrate_configured_ =
|
||||||
std::max(min_bitrate, kCongestionControllerMinBitrate);
|
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);
|
max_bitrate_configured_ = std::max(min_bitrate_configured_, max_bitrate);
|
||||||
} else {
|
} else {
|
||||||
max_bitrate_configured_ = kDefaultMaxBitrate;
|
max_bitrate_configured_ = kDefaultMaxBitrate;
|
||||||
@@ -214,11 +224,11 @@ void SendSideBandwidthEstimation::SetMinMaxBitrate(int64_t min_bitrate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SendSideBandwidthEstimation::GetMinBitrate() const {
|
int SendSideBandwidthEstimation::GetMinBitrate() const {
|
||||||
return min_bitrate_configured_;
|
return min_bitrate_configured_.bps<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t SendSideBandwidthEstimation::target_rate() const {
|
DataRate SendSideBandwidthEstimation::target_rate() const {
|
||||||
int64_t target = current_target_;
|
DataRate target = current_target_;
|
||||||
if (!disable_receiver_limit_caps_only_)
|
if (!disable_receiver_limit_caps_only_)
|
||||||
target = std::min(target, receiver_limit_);
|
target = std::min(target, receiver_limit_);
|
||||||
return std::max(min_bitrate_configured_, target);
|
return std::max(min_bitrate_configured_, target);
|
||||||
@@ -228,29 +238,29 @@ bool SendSideBandwidthEstimation::IsRttAboveLimit() const {
|
|||||||
return rtt_backoff_.IsRttAboveLimit();
|
return rtt_backoff_.IsRttAboveLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
|
DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const {
|
||||||
return link_capacity_.estimate();
|
return link_capacity_.estimate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdateReceiverEstimate(int64_t at_time,
|
void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time,
|
||||||
int64_t bandwidth) {
|
DataRate bandwidth) {
|
||||||
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
||||||
// limitation.
|
// limitation.
|
||||||
receiver_limit_ = !bandwidth ? INT64_T_MAX : bandwidth;
|
receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth;
|
||||||
ApplyTargetLimits(at_time);
|
ApplyTargetLimits(at_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(int64_t at_time,
|
void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time,
|
||||||
int64_t bitrate) {
|
DataRate bitrate) {
|
||||||
link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
|
link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate);
|
||||||
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
// TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no
|
||||||
// limitation.
|
// limitation.
|
||||||
delay_based_limit_ = !bitrate ? INT64_T_MAX : bitrate;
|
delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate;
|
||||||
ApplyTargetLimits(at_time);
|
ApplyTargetLimits(at_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::SetAcknowledgedRate(
|
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;
|
acknowledged_rate_ = acknowledged_rate;
|
||||||
if (!acknowledged_rate.has_value()) {
|
if (!acknowledged_rate.has_value()) {
|
||||||
return;
|
return;
|
||||||
@@ -259,11 +269,9 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate(
|
|||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
||||||
int64_t number_of_packets,
|
int64_t number_of_packets,
|
||||||
int64_t at_time) {
|
Timestamp at_time) {
|
||||||
last_loss_feedback_ = at_time;
|
last_loss_feedback_ = at_time;
|
||||||
if (IsInfinite(first_report_time_)) {
|
if (first_report_time_.IsInfinite()) first_report_time_ = at_time;
|
||||||
first_report_time_ = at_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check sequence number diff and weight loss report
|
// Check sequence number diff and weight loss report
|
||||||
if (number_of_packets > 0) {
|
if (number_of_packets > 0) {
|
||||||
@@ -281,7 +289,7 @@ void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost,
|
|||||||
has_decreased_since_last_fraction_loss_ = false;
|
has_decreased_since_last_fraction_loss_ = false;
|
||||||
int64_t lost_q8 =
|
int64_t lost_q8 =
|
||||||
std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
|
std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost,
|
||||||
static_cast<int64_t>(0))
|
0)
|
||||||
<< 8;
|
<< 8;
|
||||||
last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255);
|
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);
|
UpdateUmaStatsPacketsLost(at_time, packets_lost);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
|
void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time,
|
||||||
int packets_lost) {
|
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) {
|
for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
|
||||||
if (!rampup_uma_stats_updated_[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;
|
rampup_uma_stats_updated_[i] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,29 +321,29 @@ void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(int64_t at_time,
|
|||||||
} else if (uma_update_state_ == kFirstDone &&
|
} else if (uma_update_state_ == kFirstDone &&
|
||||||
at_time - first_report_time_ >= kBweConverganceTime) {
|
at_time - first_report_time_ >= kBweConverganceTime) {
|
||||||
uma_update_state_ = kDone;
|
uma_update_state_ = kDone;
|
||||||
int bitrate_diff_kbps =
|
int bitrate_diff_kbps = std::max(
|
||||||
std::max(bitrate_at_2_seconds_ - bitrate_kbps, static_cast<int64_t>(0));
|
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.
|
// 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.
|
// 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) {
|
if (!IsInStartPhase(at_time) && uma_rtt_state_ == kNoUpdate) {
|
||||||
uma_rtt_state_ = kDone;
|
uma_rtt_state_ = kDone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) {
|
||||||
if (rtt_backoff_.IsRttAboveLimit()) {
|
if (rtt_backoff_.IsRttAboveLimit()) {
|
||||||
if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
|
if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ &&
|
||||||
current_target_ > rtt_backoff_.bandwidth_floor_) {
|
current_target_ > rtt_backoff_.bandwidth_floor_) {
|
||||||
time_last_decrease_ = at_time;
|
time_last_decrease_ = at_time;
|
||||||
int64_t new_bitrate =
|
DataRate new_bitrate =
|
||||||
std::max(current_target_ * rtt_backoff_.drop_fraction_,
|
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);
|
link_capacity_.OnRttBackoff(new_bitrate, at_time);
|
||||||
UpdateTargetBitrate(new_bitrate, at_time);
|
UpdateTargetBitrate(new_bitrate, at_time);
|
||||||
return;
|
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 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.
|
// we haven't had any packet loss reported, to allow startup bitrate probing.
|
||||||
if (last_fraction_loss_ == 0 && IsInStartPhase(at_time)) {
|
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
|
// TODO(srte): We should not allow the new_bitrate to be larger than the
|
||||||
// receiver limit here.
|
// receiver limit here.
|
||||||
if (IsFinite(receiver_limit_)) {
|
if (receiver_limit_.IsFinite())
|
||||||
new_bitrate = std::max(receiver_limit_, new_bitrate);
|
new_bitrate = std::max(receiver_limit_, new_bitrate);
|
||||||
}
|
if (delay_based_limit_.IsFinite())
|
||||||
if (IsFinite(delay_based_limit_)) {
|
|
||||||
new_bitrate = std::max(delay_based_limit_, new_bitrate);
|
new_bitrate = std::max(delay_based_limit_, new_bitrate);
|
||||||
}
|
|
||||||
if (new_bitrate != current_target_) {
|
if (new_bitrate != current_target_) {
|
||||||
min_bitrate_history_.clear();
|
min_bitrate_history_.clear();
|
||||||
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
|
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);
|
UpdateMinHistory(at_time);
|
||||||
if (IsInfinite(last_loss_packet_report_)) {
|
if (last_loss_packet_report_.IsInfinite()) {
|
||||||
// No feedback received.
|
// No feedback received.
|
||||||
// TODO(srte): This is likely redundant in most cases.
|
// TODO(srte): This is likely redundant in most cases.
|
||||||
ApplyTargetLimits(at_time);
|
ApplyTargetLimits(at_time);
|
||||||
return;
|
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) {
|
if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) {
|
||||||
// We only care about loss above a given bitrate threshold.
|
// We only care about loss above a given bitrate threshold.
|
||||||
float loss = last_fraction_loss_ / 256.0f;
|
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),
|
// If instead one would do: current_bitrate_ *= 1.08^(delta time),
|
||||||
// it would take over one second since the lower packet loss to achieve
|
// it would take over one second since the lower packet loss to achieve
|
||||||
// 108kbps.
|
// 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
|
// 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
|
// (gives a little extra increase at low rates, negligible at higher
|
||||||
// rates).
|
// rates).
|
||||||
new_bitrate += 1000;
|
new_bitrate += DataRate::BitsPerSec(1000);
|
||||||
UpdateTargetBitrate(new_bitrate, at_time);
|
UpdateTargetBitrate(new_bitrate, at_time);
|
||||||
return;
|
return;
|
||||||
} else if (current_target_ > bitrate_threshold_) {
|
} else if (current_target_ > bitrate_threshold_) {
|
||||||
@@ -411,10 +420,10 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
|||||||
// Reduce rate:
|
// Reduce rate:
|
||||||
// newRate = rate * (1 - 0.5*lossRate);
|
// newRate = rate * (1 - 0.5*lossRate);
|
||||||
// where packetLoss = 256*lossRate;
|
// where packetLoss = 256*lossRate;
|
||||||
int64_t new_bitrate =
|
DataRate new_bitrate = DataRate::BitsPerSec(
|
||||||
(current_target_ *
|
(current_target_.bps() *
|
||||||
static_cast<double>(512 - last_fraction_loss_)) /
|
static_cast<double>(512 - last_fraction_loss_)) /
|
||||||
512.0;
|
512.0);
|
||||||
has_decreased_since_last_fraction_loss_ = true;
|
has_decreased_since_last_fraction_loss_ = true;
|
||||||
UpdateTargetBitrate(new_bitrate, at_time);
|
UpdateTargetBitrate(new_bitrate, at_time);
|
||||||
return;
|
return;
|
||||||
@@ -427,7 +436,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(int64_t at_time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::UpdatePropagationRtt(
|
void SendSideBandwidthEstimation::UpdatePropagationRtt(
|
||||||
int64_t at_time, int64_t propagation_rtt) {
|
Timestamp at_time, TimeDelta propagation_rtt) {
|
||||||
rtt_backoff_.UpdatePropagationRtt(at_time, 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;
|
rtt_backoff_.last_packet_sent_ = sent_packet.send_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendSideBandwidthEstimation::IsInStartPhase(int64_t at_time) const {
|
bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const {
|
||||||
return (IsInfinite(first_report_time_)) ||
|
return first_report_time_.IsInfinite() ||
|
||||||
at_time - first_report_time_ < kStartPhase;
|
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.
|
// Remove old data points from history.
|
||||||
// Since history precision is in ms, add one so it is able to increase
|
// 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.
|
// bitrate if it is off by as little as 0.5ms.
|
||||||
while (!min_bitrate_history_.empty() &&
|
while (!min_bitrate_history_.empty() &&
|
||||||
at_time - min_bitrate_history_.front().first + 1 >
|
at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) >
|
||||||
kBweIncreaseInterval) {
|
kBweIncreaseInterval) {
|
||||||
min_bitrate_history_.pop_front();
|
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_));
|
min_bitrate_history_.push_back(std::make_pair(at_time, current_target_));
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t SendSideBandwidthEstimation::GetUpperLimit() const {
|
DataRate SendSideBandwidthEstimation::GetUpperLimit() const {
|
||||||
int64_t upper_limit = delay_based_limit_;
|
DataRate upper_limit = delay_based_limit_;
|
||||||
if (disable_receiver_limit_caps_only_)
|
if (disable_receiver_limit_caps_only_)
|
||||||
upper_limit = std::min(upper_limit, receiver_limit_);
|
upper_limit = std::min(upper_limit, receiver_limit_);
|
||||||
return std::min(upper_limit, max_bitrate_configured_);
|
return std::min(upper_limit, max_bitrate_configured_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(int64_t bitrate,
|
void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate,
|
||||||
int64_t at_time) {
|
Timestamp at_time) {
|
||||||
if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
|
if (at_time - last_low_bitrate_log_ > kLowBitrateLogPeriod) {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Estimated available bandwidth {} is below configured min bitrate {}",
|
"Estimated available bandwidth {} is below configured min bitrate {}",
|
||||||
bitrate, min_bitrate_configured_);
|
ToString(bitrate), ToString(min_bitrate_configured_));
|
||||||
last_low_bitrate_log_ = at_time;
|
last_low_bitrate_log_ = at_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(int64_t at_time) {
|
void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate,
|
||||||
if (current_target_ != last_logged_target_ ||
|
Timestamp at_time) {
|
||||||
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) {
|
|
||||||
new_bitrate = std::min(new_bitrate, GetUpperLimit());
|
new_bitrate = std::min(new_bitrate, GetUpperLimit());
|
||||||
if (new_bitrate < min_bitrate_configured_) {
|
if (new_bitrate < min_bitrate_configured_) {
|
||||||
MaybeLogLowBitrateWarning(new_bitrate, at_time);
|
MaybeLogLowBitrateWarning(new_bitrate, at_time);
|
||||||
new_bitrate = min_bitrate_configured_;
|
new_bitrate = min_bitrate_configured_;
|
||||||
}
|
}
|
||||||
current_target_ = new_bitrate;
|
current_target_ = new_bitrate;
|
||||||
MaybeLogLossBasedEvent(at_time);
|
|
||||||
link_capacity_.OnRateUpdate(acknowledged_rate_, current_target_, 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);
|
UpdateTargetBitrate(current_target_, at_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@@ -1,63 +1,74 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: DI JUNKUN
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||||
* @Date: 2025-01-13
|
*
|
||||||
* Copyright (c) 2025 by DI JUNKUN, 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_
|
#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||||
#define _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 <deque>
|
||||||
#include <limits>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "limits_base.h"
|
#include "api/transport/bandwidth_usage.h"
|
||||||
#include "network_types.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 {
|
class LinkCapacityTracker {
|
||||||
public:
|
public:
|
||||||
LinkCapacityTracker() = default;
|
LinkCapacityTracker() = default;
|
||||||
~LinkCapacityTracker() = default;
|
~LinkCapacityTracker() = default;
|
||||||
// Call when a new delay-based estimate is available.
|
// Call when a new delay-based estimate is available.
|
||||||
void UpdateDelayBasedEstimate(int64_t at_time, int64_t delay_based_bitrate);
|
void UpdateDelayBasedEstimate(Timestamp at_time,
|
||||||
void OnStartingRate(int64_t start_rate);
|
DataRate delay_based_bitrate);
|
||||||
void OnRateUpdate(std::optional<int64_t> acknowledged, int64_t target,
|
void OnStartingRate(DataRate start_rate);
|
||||||
int64_t at_time);
|
void OnRateUpdate(std::optional<DataRate> acknowledged, DataRate target,
|
||||||
void OnRttBackoff(int64_t backoff_rate, int64_t at_time);
|
Timestamp at_time);
|
||||||
int64_t estimate() const;
|
void OnRttBackoff(DataRate backoff_rate, Timestamp at_time);
|
||||||
|
DataRate estimate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double capacity_estimate_bps_ = 0;
|
double capacity_estimate_bps_ = 0;
|
||||||
int64_t last_link_capacity_update_ = INT64_T_MIN;
|
Timestamp last_link_capacity_update_ = Timestamp::MinusInfinity();
|
||||||
int64_t last_delay_based_estimate_ = INT64_T_MAX;
|
DataRate last_delay_based_estimate_ = DataRate::PlusInfinity();
|
||||||
};
|
};
|
||||||
|
|
||||||
class RttBasedBackoff {
|
class RttBasedBackoff {
|
||||||
public:
|
public:
|
||||||
explicit RttBasedBackoff();
|
explicit RttBasedBackoff();
|
||||||
~RttBasedBackoff();
|
~RttBasedBackoff();
|
||||||
void UpdatePropagationRtt(int64_t at_time, int64_t propagation_rtt);
|
void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt);
|
||||||
bool IsRttAboveLimit() const;
|
bool IsRttAboveLimit() const;
|
||||||
|
|
||||||
bool disabled_;
|
bool disabled_;
|
||||||
int64_t configured_limit_;
|
TimeDelta configured_limit_;
|
||||||
double drop_fraction_;
|
double drop_fraction_;
|
||||||
int64_t drop_interval_;
|
TimeDelta drop_interval_;
|
||||||
int64_t bandwidth_floor_;
|
DataRate bandwidth_floor_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int64_t rtt_limit_;
|
TimeDelta rtt_limit_;
|
||||||
int64_t last_propagation_rtt_update_;
|
Timestamp last_propagation_rtt_update_;
|
||||||
int64_t last_propagation_rtt_;
|
TimeDelta last_propagation_rtt_;
|
||||||
int64_t last_packet_sent_;
|
Timestamp last_packet_sent_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t CorrectedRtt() const;
|
TimeDelta CorrectedRtt() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SendSideBandwidthEstimation {
|
class SendSideBandwidthEstimation {
|
||||||
@@ -67,112 +78,108 @@ class SendSideBandwidthEstimation {
|
|||||||
|
|
||||||
void OnRouteChange();
|
void OnRouteChange();
|
||||||
|
|
||||||
int64_t target_rate() const;
|
DataRate target_rate() const;
|
||||||
// Return whether the current rtt is higher than the rtt limited configured in
|
// Return whether the current rtt is higher than the rtt limited configured in
|
||||||
// RttBasedBackoff.
|
// RttBasedBackoff.
|
||||||
bool IsRttAboveLimit() const;
|
bool IsRttAboveLimit() const;
|
||||||
uint8_t fraction_loss() const { return last_fraction_loss_; }
|
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.
|
// Call periodically to update estimate.
|
||||||
void UpdateEstimate(int64_t at_time);
|
void UpdateEstimate(Timestamp at_time);
|
||||||
void OnSentPacket(const SentPacket& sent_packet);
|
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.
|
// 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.
|
// 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.
|
// Call when we receive a RTCP message with a ReceiveBlock.
|
||||||
void UpdatePacketsLost(int64_t packets_lost, int64_t number_of_packets,
|
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.
|
// 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,
|
void SetBitrates(std::optional<DataRate> send_bitrate, DataRate min_bitrate,
|
||||||
int64_t max_bitrate, int64_t at_time);
|
DataRate max_bitrate, Timestamp at_time);
|
||||||
void SetSendBitrate(int64_t bitrate, int64_t at_time);
|
void SetSendBitrate(DataRate bitrate, Timestamp at_time);
|
||||||
void SetMinMaxBitrate(int64_t min_bitrate, int64_t max_bitrate);
|
void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate);
|
||||||
int GetMinBitrate() const;
|
int GetMinBitrate() const;
|
||||||
void SetAcknowledgedRate(std::optional<int64_t> acknowledged_rate,
|
void SetAcknowledgedRate(std::optional<DataRate> acknowledged_rate,
|
||||||
int64_t at_time);
|
Timestamp at_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class GoogCcStatePrinter;
|
friend class GoogCcStatePrinter;
|
||||||
|
|
||||||
enum UmaState { kNoUpdate, kFirstDone, kDone };
|
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.
|
// Updates history of min bitrates.
|
||||||
// After this method returns min_bitrate_history_.front().second contains the
|
// After this method returns min_bitrate_history_.front().second contains the
|
||||||
// min bitrate used during last kBweIncreaseIntervalMs.
|
// 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
|
// 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.
|
// 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
|
// Prints a warning if `bitrate` if sufficiently long time has past since last
|
||||||
// warning.
|
// warning.
|
||||||
void MaybeLogLowBitrateWarning(int64_t bitrate, int64_t at_time);
|
void MaybeLogLowBitrateWarning(DataRate bitrate, Timestamp 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);
|
|
||||||
|
|
||||||
// Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and
|
// Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and
|
||||||
// set `current_bitrate_` to the capped value and updates the event log.
|
// 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.
|
// Applies lower and upper bounds to the current target rate.
|
||||||
// TODO(srte): This seems to be called even when limits haven't changed, that
|
// TODO(srte): This seems to be called even when limits haven't changed, that
|
||||||
// should be cleaned up.
|
// should be cleaned up.
|
||||||
void ApplyTargetLimits(int64_t at_time);
|
void ApplyTargetLimits(Timestamp at_time);
|
||||||
|
|
||||||
RttBasedBackoff rtt_backoff_;
|
RttBasedBackoff rtt_backoff_;
|
||||||
LinkCapacityTracker link_capacity_;
|
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
|
// incoming filters
|
||||||
int lost_packets_since_last_loss_update_;
|
int lost_packets_since_last_loss_update_;
|
||||||
int expected_packets_since_last_loss_update_;
|
int expected_packets_since_last_loss_update_;
|
||||||
|
|
||||||
std::optional<int64_t> acknowledged_rate_;
|
std::optional<DataRate> acknowledged_rate_;
|
||||||
int64_t current_target_;
|
DataRate current_target_;
|
||||||
int64_t last_logged_target_;
|
DataRate last_logged_target_;
|
||||||
int64_t min_bitrate_configured_;
|
DataRate min_bitrate_configured_;
|
||||||
int64_t max_bitrate_configured_;
|
DataRate max_bitrate_configured_;
|
||||||
int64_t last_low_bitrate_log_;
|
Timestamp last_low_bitrate_log_;
|
||||||
|
|
||||||
bool has_decreased_since_last_fraction_loss_;
|
bool has_decreased_since_last_fraction_loss_;
|
||||||
int64_t last_loss_feedback_;
|
Timestamp last_loss_feedback_;
|
||||||
int64_t last_loss_packet_report_;
|
Timestamp last_loss_packet_report_;
|
||||||
uint8_t last_fraction_loss_;
|
uint8_t last_fraction_loss_;
|
||||||
uint8_t last_logged_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
|
// 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
|
// signalled using the REMB RTCP message and is used when we don't have any
|
||||||
// send side delay based estimate.
|
// send side delay based estimate.
|
||||||
int64_t receiver_limit_;
|
DataRate receiver_limit_;
|
||||||
int64_t delay_based_limit_;
|
DataRate delay_based_limit_;
|
||||||
int64_t time_last_decrease_;
|
Timestamp time_last_decrease_;
|
||||||
int64_t first_report_time_;
|
Timestamp first_report_time_;
|
||||||
int initially_lost_packets_;
|
int initially_lost_packets_;
|
||||||
int64_t bitrate_at_2_seconds_;
|
DataRate bitrate_at_2_seconds_;
|
||||||
UmaState uma_update_state_;
|
UmaState uma_update_state_;
|
||||||
UmaState uma_rtt_state_;
|
UmaState uma_rtt_state_;
|
||||||
std::vector<bool> rampup_uma_stats_updated_;
|
std::vector<bool> rampup_uma_stats_updated_;
|
||||||
int64_t last_rtc_event_log_;
|
|
||||||
float low_loss_threshold_;
|
float low_loss_threshold_;
|
||||||
float high_loss_threshold_;
|
float high_loss_threshold_;
|
||||||
int64_t bitrate_threshold_;
|
DataRate bitrate_threshold_;
|
||||||
bool disable_receiver_limit_caps_only_;
|
bool disable_receiver_limit_caps_only_;
|
||||||
};
|
};
|
||||||
|
} // namespace webrtc
|
||||||
#endif
|
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/transport/network_types.h"
|
||||||
#include "congestion_control_feedback.h"
|
#include "congestion_control_feedback.h"
|
||||||
#include "network_route.h"
|
#include "network_route.h"
|
||||||
#include "network_types.h"
|
|
||||||
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
||||||
|
|
||||||
struct PacketFeedback {
|
struct PacketFeedback {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "bandwidth_usage.h"
|
#include "api/transport/bandwidth_usage.h"
|
||||||
|
#include "delay_increase_detector_interface.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
|
|
||||||
|
static constexpr int kAbsSendTimeFraction = 18;
|
||||||
|
|
||||||
enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13
|
enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13
|
||||||
|
|
||||||
struct RTPHeader {
|
struct RTPHeader {
|
||||||
|
|||||||
Reference in New Issue
Block a user