mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
[feat] implementation for qos module
This commit is contained in:
@@ -5,10 +5,18 @@
|
|||||||
#define NV12_BUFFER_SIZE (1280 * 720 * 3 / 2)
|
#define NV12_BUFFER_SIZE (1280 * 720 * 3 / 2)
|
||||||
#define RTCP_RR_INTERVAL 1000
|
#define RTCP_RR_INTERVAL 1000
|
||||||
|
|
||||||
RtpVideoReceiver::RtpVideoReceiver() {}
|
RtpVideoReceiver::RtpVideoReceiver()
|
||||||
|
: receive_side_congestion_controller_(
|
||||||
|
[this](std::vector<std::unique_ptr<RtcpPacket>> packets) {
|
||||||
|
SendCombinedRtcpPacket(std::move(packets));
|
||||||
|
}) {}
|
||||||
|
|
||||||
RtpVideoReceiver::RtpVideoReceiver(std::shared_ptr<IOStatistics> io_statistics)
|
RtpVideoReceiver::RtpVideoReceiver(std::shared_ptr<IOStatistics> io_statistics)
|
||||||
: io_statistics_(io_statistics) {
|
: io_statistics_(io_statistics),
|
||||||
|
receive_side_congestion_controller_(
|
||||||
|
[this](std::vector<std::unique_ptr<RtcpPacket>> packets) {
|
||||||
|
SendCombinedRtcpPacket(std::move(packets));
|
||||||
|
}) {
|
||||||
rtcp_thread_ = std::thread(&RtpVideoReceiver::RtcpThread, this);
|
rtcp_thread_ = std::thread(&RtpVideoReceiver::RtcpThread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +39,16 @@ void RtpVideoReceiver::InsertRtpPacket(RtpPacket& rtp_packet) {
|
|||||||
rtp_statistics_->Start();
|
rtp_statistics_->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RtpPacketReceived* rtp_packet_received;
|
||||||
|
rtp_packet_received = dynamic_cast<RtpPacketReceived*>(&rtp_packet);
|
||||||
|
rtp_packet_received->set_arrival_time(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch().count());
|
||||||
|
rtp_packet_received->set_ecn(EcnMarking::kEct0);
|
||||||
|
rtp_packet_received->set_recovered(false);
|
||||||
|
rtp_packet_received->set_payload_type_frequency(0);
|
||||||
|
receive_side_congestion_controller_.OnReceivedPacket(
|
||||||
|
*rtp_packet_received, ReceiveSideCongestionController::MediaType::VIDEO);
|
||||||
|
|
||||||
last_recv_bytes_ = (uint32_t)rtp_packet.PayloadSize();
|
last_recv_bytes_ = (uint32_t)rtp_packet.PayloadSize();
|
||||||
total_rtp_payload_recv_ += (uint32_t)rtp_packet.PayloadSize();
|
total_rtp_payload_recv_ += (uint32_t)rtp_packet.PayloadSize();
|
||||||
total_rtp_packets_recv_++;
|
total_rtp_packets_recv_++;
|
||||||
@@ -327,6 +345,9 @@ int RtpVideoReceiver::SendRtcpRR(RtcpReceiverReport& rtcp_rr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RtpVideoReceiver::SendCombinedRtcpPacket(
|
||||||
|
std::vector<std::unique_ptr<RtcpPacket>> packets) {}
|
||||||
|
|
||||||
bool RtpVideoReceiver::CheckIsTimeSendRR() {
|
bool RtpVideoReceiver::CheckIsTimeSendRR() {
|
||||||
uint32_t now_ts = static_cast<uint32_t>(
|
uint32_t now_ts = static_cast<uint32_t>(
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
@@ -44,6 +44,8 @@ class RtpVideoReceiver : public ThreadBase {
|
|||||||
bool CheckIsTimeSendRR();
|
bool CheckIsTimeSendRR();
|
||||||
int SendRtcpRR(RtcpReceiverReport& rtcp_rr);
|
int SendRtcpRR(RtcpReceiverReport& rtcp_rr);
|
||||||
|
|
||||||
|
void SendCombinedRtcpPacket(std::vector<std::unique_ptr<RtcpPacket>> packets);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Process() override;
|
bool Process() override;
|
||||||
void RtcpThread();
|
void RtcpThread();
|
||||||
@@ -86,7 +88,7 @@ class RtpVideoReceiver : public ThreadBase {
|
|||||||
int rtcp_tcc_interval_ms_ = 200;
|
int rtcp_tcc_interval_ms_ = 200;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ReceiveSideCongestionController congestion_controller_;
|
ReceiveSideCongestionController receive_side_congestion_controller_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -90,10 +90,10 @@ class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
|
|||||||
template <typename U>
|
template <typename U>
|
||||||
ArrayView(U* data, size_t size)
|
ArrayView(U* data, size_t size)
|
||||||
: array_view_internal::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
|
: array_view_internal::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
|
||||||
RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
|
// RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
|
||||||
RTC_DCHECK_EQ(size, this->size());
|
// RTC_DCHECK_EQ(size, this->size());
|
||||||
RTC_DCHECK_EQ(!this->data(),
|
// RTC_DCHECK_EQ(!this->data(),
|
||||||
this->size() == 0); // data is null iff size == 0.
|
// this->size() == 0); // data is null iff size == 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
|
// Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
|
||||||
@@ -105,7 +105,7 @@ class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
|
|||||||
: ArrayView(static_cast<T*>(nullptr), size) {
|
: ArrayView(static_cast<T*>(nullptr), size) {
|
||||||
static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize,
|
static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize,
|
||||||
"");
|
"");
|
||||||
RTC_DCHECK_EQ(0, size);
|
// RTC_DCHECK_EQ(0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct an ArrayView from a C-style array.
|
// Construct an ArrayView from a C-style array.
|
||||||
@@ -182,8 +182,8 @@ class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
|
|||||||
// const, because the ArrayView doesn't own the array. (To prevent mutation,
|
// const, because the ArrayView doesn't own the array. (To prevent mutation,
|
||||||
// use a const element type.)
|
// use a const element type.)
|
||||||
T& operator[](size_t idx) const {
|
T& operator[](size_t idx) const {
|
||||||
RTC_DCHECK_LT(idx, this->size());
|
// RTC_DCHECK_LT(idx, this->size());
|
||||||
RTC_DCHECK(this->data());
|
// RTC_DCHECK(this->data());
|
||||||
return this->data()[idx];
|
return this->data()[idx];
|
||||||
}
|
}
|
||||||
T* begin() const { return this->data(); }
|
T* begin() const { return this->data(); }
|
||||||
|
|||||||
31
src/common/enc_mark.h
Normal file
31
src/common/enc_mark.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2025-01-08
|
||||||
|
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ENC_MARK_H_
|
||||||
|
#define _ENC_MARK_H_
|
||||||
|
|
||||||
|
// L4S Explicit Congestion Notification (ECN) .
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable
|
||||||
|
// Transport and CE stands for Congestion Experienced.
|
||||||
|
|
||||||
|
// RFC-3168, Section 5
|
||||||
|
// +-----+-----+
|
||||||
|
// | ECN FIELD |
|
||||||
|
// +-----+-----+
|
||||||
|
// ECT CE [Obsolete] RFC 2481 names for the ECN bits.
|
||||||
|
// 0 0 Not-ECT
|
||||||
|
// 0 1 ECT(1)
|
||||||
|
// 1 0 ECT(0)
|
||||||
|
// 1 1 CE
|
||||||
|
|
||||||
|
enum class EcnMarking {
|
||||||
|
kNotEct = 0, // Not ECN-Capable Transport
|
||||||
|
kEct1 = 1, // ECN-Capable Transport
|
||||||
|
kEct0 = 2, // Not used by L4s (or webrtc.)
|
||||||
|
kCe = 3, // Congestion experienced
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
130
src/common/mod_ops.h
Normal file
130
src/common/mod_ops.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2025-01-08
|
||||||
|
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MOD_OPS_H_
|
||||||
|
#define _MOD_OPS_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
template <unsigned long M> // NOLINT
|
||||||
|
inline unsigned long Add(unsigned long a, unsigned long b) { // NOLINT
|
||||||
|
// RTC_DCHECK_LT(a, M);
|
||||||
|
unsigned long t = M - b % M; // NOLINT
|
||||||
|
unsigned long res = a - t; // NOLINT
|
||||||
|
if (t > a) return res + M;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned long M> // NOLINT
|
||||||
|
inline unsigned long Subtract(unsigned long a, unsigned long b) { // NOLINT
|
||||||
|
// RTC_DCHECK_LT(a, M);
|
||||||
|
unsigned long sub = b % M; // NOLINT
|
||||||
|
if (a < sub) return M - (sub - a);
|
||||||
|
return a - sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the forward difference between two wrapping numbers.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// uint8_t x = 253;
|
||||||
|
// uint8_t y = 2;
|
||||||
|
//
|
||||||
|
// ForwardDiff(x, y) == 5
|
||||||
|
//
|
||||||
|
// 252 253 254 255 0 1 2 3
|
||||||
|
// #################################################
|
||||||
|
// | | x | | | | | y | |
|
||||||
|
// #################################################
|
||||||
|
// |----->----->----->----->----->
|
||||||
|
//
|
||||||
|
// ForwardDiff(y, x) == 251
|
||||||
|
//
|
||||||
|
// 252 253 254 255 0 1 2 3
|
||||||
|
// #################################################
|
||||||
|
// | | x | | | | | y | |
|
||||||
|
// #################################################
|
||||||
|
// -->-----> |----->---
|
||||||
|
//
|
||||||
|
// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the
|
||||||
|
// largest value representable by T.
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
// RTC_DCHECK_LT(a, M);
|
||||||
|
// RTC_DCHECK_LT(b, M);
|
||||||
|
return a <= b ? b - a : M - (a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
return b - a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ForwardDiff(T a, T b) {
|
||||||
|
return ForwardDiff<T, 0>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the reverse difference between two wrapping numbers.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// uint8_t x = 253;
|
||||||
|
// uint8_t y = 2;
|
||||||
|
//
|
||||||
|
// ReverseDiff(y, x) == 5
|
||||||
|
//
|
||||||
|
// 252 253 254 255 0 1 2 3
|
||||||
|
// #################################################
|
||||||
|
// | | x | | | | | y | |
|
||||||
|
// #################################################
|
||||||
|
// <-----<-----<-----<-----<-----|
|
||||||
|
//
|
||||||
|
// ReverseDiff(x, y) == 251
|
||||||
|
//
|
||||||
|
// 252 253 254 255 0 1 2 3
|
||||||
|
// #################################################
|
||||||
|
// | | x | | | | | y | |
|
||||||
|
// #################################################
|
||||||
|
// ---<-----| |<-----<--
|
||||||
|
//
|
||||||
|
// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the
|
||||||
|
// largest value representable by T.
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
// RTC_DCHECK_LT(a, M);
|
||||||
|
// RTC_DCHECK_LT(b, M);
|
||||||
|
return b <= a ? a - b : M - (b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ReverseDiff(T a, T b) {
|
||||||
|
return ReverseDiff<T, 0>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the minimum distance between to wrapping numbers.
|
||||||
|
//
|
||||||
|
// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b))
|
||||||
|
template <typename T, T M = 0>
|
||||||
|
inline T MinDiff(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
return (std::min)(ForwardDiff<T, M>(a, b), ReverseDiff<T, M>(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
73
src/common/sequence_number_unwrapper.h
Normal file
73
src/common/sequence_number_unwrapper.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2025-01-08
|
||||||
|
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SEQUENCE_NUMBER_UNWRAPPER_H_
|
||||||
|
#define _SEQUENCE_NUMBER_UNWRAPPER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "sequence_number_util.h"
|
||||||
|
|
||||||
|
// A sequence number unwrapper where the first unwrapped value equals the
|
||||||
|
// first value being unwrapped.
|
||||||
|
template <typename T, T M = 0>
|
||||||
|
class SeqNumUnwrapper {
|
||||||
|
static_assert(
|
||||||
|
std::is_unsigned<T>::value &&
|
||||||
|
std::numeric_limits<T>::max() < std::numeric_limits<int64_t>::max(),
|
||||||
|
"Type unwrapped must be an unsigned integer smaller than int64_t.");
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Unwraps `value` and updates the internal state of the unwrapper.
|
||||||
|
int64_t Unwrap(T value) {
|
||||||
|
if (!last_value_) {
|
||||||
|
last_unwrapped_ = {value};
|
||||||
|
} else {
|
||||||
|
last_unwrapped_ += Delta(*last_value_, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_value_ = value;
|
||||||
|
return last_unwrapped_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the `value` without updating the internal state of the unwrapper.
|
||||||
|
int64_t PeekUnwrap(T value) const {
|
||||||
|
if (!last_value_) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return last_unwrapped_ + Delta(*last_value_, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the unwrapper to its initial state. Unwrapped sequence numbers will
|
||||||
|
// being at 0 after resetting.
|
||||||
|
void Reset() {
|
||||||
|
last_unwrapped_ = 0;
|
||||||
|
last_value_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int64_t Delta(T last_value, T new_value) {
|
||||||
|
constexpr int64_t kBackwardAdjustment =
|
||||||
|
M == 0 ? int64_t{std::numeric_limits<T>::max()} + 1 : M;
|
||||||
|
int64_t result = ForwardDiff<T, M>(last_value, new_value);
|
||||||
|
if (!AheadOrAt<T, M>(new_value, last_value)) {
|
||||||
|
result -= kBackwardAdjustment;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t last_unwrapped_ = 0;
|
||||||
|
std::optional<T> last_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using RtpTimestampUnwrapper = SeqNumUnwrapper<uint32_t>;
|
||||||
|
using RtpSequenceNumberUnwrapper = SeqNumUnwrapper<uint16_t>;
|
||||||
|
|
||||||
|
#endif
|
||||||
73
src/common/sequence_number_util.h
Normal file
73
src/common/sequence_number_util.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2025-01-08
|
||||||
|
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SEQUENCE_NUMBER_UTIL_H_
|
||||||
|
#define _SEQUENCE_NUMBER_UTIL_H_
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "mod_ops.h"
|
||||||
|
|
||||||
|
// Test if the sequence number `a` is ahead or at sequence number `b`.
|
||||||
|
//
|
||||||
|
// If `M` is an even number and the two sequence numbers are at max distance
|
||||||
|
// from each other, then the sequence number with the highest value is
|
||||||
|
// considered to be ahead.
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M > 0), bool>::type AheadOrAt(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
const T maxDist = M / 2;
|
||||||
|
if (!(M & 1) && MinDiff<T, M>(a, b) == maxDist) return b < a;
|
||||||
|
return ForwardDiff<T, M>(b, a) <= maxDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, T M>
|
||||||
|
inline typename std::enable_if<(M == 0), bool>::type AheadOrAt(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
const T maxDist = std::numeric_limits<T>::max() / 2 + T(1);
|
||||||
|
if (a - b == maxDist) return b < a;
|
||||||
|
return ForwardDiff(b, a) < maxDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool AheadOrAt(T a, T b) {
|
||||||
|
return AheadOrAt<T, 0>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the sequence number `a` is ahead of sequence number `b`.
|
||||||
|
//
|
||||||
|
// If `M` is an even number and the two sequence numbers are at max distance
|
||||||
|
// from each other, then the sequence number with the highest value is
|
||||||
|
// considered to be ahead.
|
||||||
|
template <typename T, T M = 0>
|
||||||
|
inline bool AheadOf(T a, T b) {
|
||||||
|
static_assert(std::is_unsigned<T>::value,
|
||||||
|
"Type must be an unsigned integer.");
|
||||||
|
return a != b && AheadOrAt<T, M>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparator used to compare sequence numbers in a continuous fashion.
|
||||||
|
//
|
||||||
|
// WARNING! If used to sort sequence numbers of length M then the interval
|
||||||
|
// covered by the sequence numbers may not be larger than floor(M/2).
|
||||||
|
template <typename T, T M = 0>
|
||||||
|
struct AscendingSeqNumComp {
|
||||||
|
bool operator()(T a, T b) const { return AheadOf<T, M>(a, b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Comparator used to compare sequence numbers in a continuous fashion.
|
||||||
|
//
|
||||||
|
// WARNING! If used to sort sequence numbers of length M then the interval
|
||||||
|
// covered by the sequence numbers may not be larger than floor(M/2).
|
||||||
|
template <typename T, T M = 0>
|
||||||
|
struct DescendingSeqNumComp {
|
||||||
|
bool operator()(T a, T b) const { return AheadOf<T, M>(b, a); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -96,6 +96,10 @@ uint16_t To2BitEcn(EcnMarking ecn_marking) {
|
|||||||
return kEcnEct0 << 13;
|
return kEcnEct0 << 13;
|
||||||
case EcnMarking::kCe:
|
case EcnMarking::kCe:
|
||||||
return kEcnCe << 13;
|
return kEcnCe << 13;
|
||||||
|
default: {
|
||||||
|
LOG_FATAL("Unexpected ecn marking: {}", static_cast<int>(ecn_marking));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +197,8 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
|
|||||||
ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], 0);
|
ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], 0);
|
||||||
*position += 2;
|
*position += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
ArrayView<const PacketInfo> remaining(packets_);
|
ArrayView<const PacketInfo> remaining(packets_);
|
||||||
@@ -255,7 +261,7 @@ size_t CongestionControlFeedback::BlockLength() const {
|
|||||||
return total_size;
|
return total_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CongestionControlFeedback::Parse(const rtcp::CommonHeader& packet) {
|
bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
|
||||||
const uint8_t* payload = packet.payload();
|
const uint8_t* payload = packet.payload();
|
||||||
const uint8_t* payload_end = packet.payload() + packet.payload_size_bytes();
|
const uint8_t* payload_end = packet.payload() + packet.payload_size_bytes();
|
||||||
|
|
||||||
@@ -291,12 +297,11 @@ bool CongestionControlFeedback::Parse(const rtcp::CommonHeader& packet) {
|
|||||||
|
|
||||||
uint16_t seq_no = base_seqno + i;
|
uint16_t seq_no = base_seqno + i;
|
||||||
bool received = (packet_info & 0x8000);
|
bool received = (packet_info & 0x8000);
|
||||||
packets_.push_back(
|
packets_.push_back(PacketInfo{ssrc, seq_no,
|
||||||
{.ssrc = ssrc,
|
received
|
||||||
.sequence_number = seq_no,
|
? AtoToTimeDelta(packet_info)
|
||||||
.arrival_time_offset = received ? AtoToTimeDelta(packet_info)
|
: std::numeric_limits<int64_t>::min(),
|
||||||
: TimeDelta::MinusInfinity(),
|
ToEcnMarking(packet_info)});
|
||||||
.ecn = ToEcnMarking(packet_info)});
|
|
||||||
}
|
}
|
||||||
if (num_reports % 2) {
|
if (num_reports % 2) {
|
||||||
// 2 bytes padding
|
// 2 bytes padding
|
||||||
|
|||||||
@@ -13,29 +13,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "array_view.h"
|
#include "array_view.h"
|
||||||
|
#include "enc_mark.h"
|
||||||
#include "rtp_feedback.h"
|
#include "rtp_feedback.h"
|
||||||
|
|
||||||
// L4S Explicit Congestion Notification (ECN) .
|
|
||||||
// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable
|
|
||||||
// Transport and CE stands for Congestion Experienced.
|
|
||||||
|
|
||||||
// RFC-3168, Section 5
|
|
||||||
// +-----+-----+
|
|
||||||
// | ECN FIELD |
|
|
||||||
// +-----+-----+
|
|
||||||
// ECT CE [Obsolete] RFC 2481 names for the ECN bits.
|
|
||||||
// 0 0 Not-ECT
|
|
||||||
// 0 1 ECT(1)
|
|
||||||
// 1 0 ECT(0)
|
|
||||||
// 1 1 CE
|
|
||||||
|
|
||||||
enum class EcnMarking {
|
|
||||||
kNotEct = 0, // Not ECN-Capable Transport
|
|
||||||
kEct1 = 1, // ECN-Capable Transport
|
|
||||||
kEct0 = 2, // Not used by L4s (or webrtc.)
|
|
||||||
kCe = 3, // Congestion experienced
|
|
||||||
};
|
|
||||||
|
|
||||||
// Congestion control feedback message as specified in
|
// Congestion control feedback message as specified in
|
||||||
// https://www.rfc-editor.org/rfc/rfc8888.html
|
// https://www.rfc-editor.org/rfc/rfc8888.html
|
||||||
class CongestionControlFeedback : public RtpFeedback {
|
class CongestionControlFeedback : public RtpFeedback {
|
||||||
@@ -58,7 +38,7 @@ class CongestionControlFeedback : public RtpFeedback {
|
|||||||
uint32_t report_timestamp_compact_ntp);
|
uint32_t report_timestamp_compact_ntp);
|
||||||
CongestionControlFeedback() = default;
|
CongestionControlFeedback() = default;
|
||||||
|
|
||||||
bool Parse(const CommonHeader& packet);
|
bool Parse(const RtcpCommonHeader& packet);
|
||||||
|
|
||||||
ArrayView<const PacketInfo> packets() const { return packets_; }
|
ArrayView<const PacketInfo> packets() const { return packets_; }
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
|
|||||||
: rtcp_sender_(std::move(rtcp_sender)) {}
|
: rtcp_sender_(std::move(rtcp_sender)) {}
|
||||||
|
|
||||||
void CongestionControlFeedbackGenerator::OnReceivedPacket(
|
void CongestionControlFeedbackGenerator::OnReceivedPacket(
|
||||||
const RtpPacketReceived& packet) {
|
RtpPacketReceived& packet) {
|
||||||
marker_bit_seen_ |= packet.Marker();
|
marker_bit_seen_ |= packet.Marker();
|
||||||
if (!first_arrival_time_since_feedback_) {
|
if (!first_arrival_time_since_feedback_) {
|
||||||
first_arrival_time_since_feedback_ = packet.arrival_time();
|
first_arrival_time_since_feedback_ = packet.arrival_time();
|
||||||
@@ -63,19 +63,19 @@ int64_t CongestionControlFeedbackGenerator::Process(int64_t now_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
|
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
|
||||||
DataRate estimate) {
|
int64_t estimate) {
|
||||||
// Feedback reports should max occupy 5% of total bandwidth.
|
// Feedback reports should max occupy 5% of total bandwidth.
|
||||||
max_feedback_rate_ = estimate * 0.05;
|
max_feedback_rate_ = estimate * 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackGenerator::SetTransportOverhead(
|
void CongestionControlFeedbackGenerator::SetTransportOverhead(
|
||||||
DataSize overhead_per_packet) {
|
int64_t overhead_per_packet) {
|
||||||
packet_overhead_ = overhead_per_packet;
|
packet_overhead_ = overhead_per_packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
|
void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
|
||||||
uint32_t compact_ntp = ConvertToCompactNtp(now_ms);
|
uint32_t compact_ntp = ConvertToCompactNtp(now_ms);
|
||||||
std::vector<rtcp::CongestionControlFeedback::PacketInfo> rtcp_packet_info;
|
std::vector<CongestionControlFeedback::PacketInfo> rtcp_packet_info;
|
||||||
for (auto& [unused, tracker] : feedback_trackers_) {
|
for (auto& [unused, tracker] : feedback_trackers_) {
|
||||||
tracker.AddPacketsToFeedback(now_ms, rtcp_packet_info);
|
tracker.AddPacketsToFeedback(now_ms, rtcp_packet_info);
|
||||||
}
|
}
|
||||||
@@ -83,18 +83,18 @@ void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
|
|||||||
marker_bit_seen_ = false;
|
marker_bit_seen_ = false;
|
||||||
first_arrival_time_since_feedback_ = std::nullopt;
|
first_arrival_time_since_feedback_ = std::nullopt;
|
||||||
|
|
||||||
auto feedback = std::make_unique<rtcp::CongestionControlFeedback>(
|
auto feedback = std::make_unique<CongestionControlFeedback>(
|
||||||
std::move(rtcp_packet_info), compact_ntp);
|
std::move(rtcp_packet_info), compact_ntp);
|
||||||
CalculateNextPossibleSendTime(feedback->BlockLength(), now_ms);
|
CalculateNextPossibleSendTime(feedback->BlockLength(), now_ms);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets;
|
std::vector<std::unique_ptr<RtcpPacket>> rtcp_packets;
|
||||||
rtcp_packets.push_back(std::move(feedback));
|
rtcp_packets.push_back(std::move(feedback));
|
||||||
rtcp_sender_(std::move(rtcp_packets));
|
rtcp_sender_(std::move(rtcp_packets));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
|
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
|
||||||
int64_t feedback_size, int64_t now_ms) {
|
int64_t feedback_size, int64_t now_ms) {
|
||||||
int64_t time_since_last_sent = now - last_feedback_sent_time_;
|
int64_t time_since_last_sent = now_ms - last_feedback_sent_time_;
|
||||||
size_t debt_payed = time_since_last_sent * max_feedback_rate_;
|
size_t debt_payed = time_since_last_sent * max_feedback_rate_;
|
||||||
send_rate_debt_ =
|
send_rate_debt_ =
|
||||||
debt_payed > send_rate_debt_ ? 0 : send_rate_debt_ - debt_payed;
|
debt_payed > send_rate_debt_ ? 0 : send_rate_debt_ - debt_payed;
|
||||||
|
|||||||
@@ -10,43 +10,22 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "congestion_control_feedback_tracker.h"
|
||||||
#include "rtcp_packet.h"
|
#include "rtcp_packet.h"
|
||||||
#include "rtp_packet_received.h"
|
#include "rtp_packet_received.h"
|
||||||
|
|
||||||
class RtpTransportFeedbackGenerator {
|
class CongestionControlFeedbackGenerator {
|
||||||
public:
|
public:
|
||||||
// Function intented to be used for sending RTCP messages generated by an
|
CongestionControlFeedbackGenerator(RtcpSender feedback_sender);
|
||||||
// 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 int64_t Process(int64_t now) = 0;
|
|
||||||
|
|
||||||
virtual void OnSendBandwidthEstimateChanged(int64_t estimate) = 0;
|
|
||||||
|
|
||||||
// Overhead from transport layers below RTP. Ie, IP, SRTP.
|
|
||||||
virtual void SetTransportOverhead(DataSize overhead_per_packet) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CongestionControlFeedbackGenerator
|
|
||||||
: public RtpTransportFeedbackGenerator {
|
|
||||||
public:
|
|
||||||
CongestionControlFeedbackGenerator(
|
|
||||||
RtpTransportFeedbackGenerator::RtcpSender feedback_sender);
|
|
||||||
~CongestionControlFeedbackGenerator() = default;
|
~CongestionControlFeedbackGenerator() = default;
|
||||||
|
|
||||||
void OnReceivedPacket(const RtpPacketReceived& packet) override;
|
void OnReceivedPacket(RtpPacketReceived& packet);
|
||||||
|
|
||||||
void OnSendBandwidthEstimateChanged(int64_t estimate) override;
|
void OnSendBandwidthEstimateChanged(int64_t estimate);
|
||||||
|
|
||||||
int64_t Process(int64_t now_ms) override;
|
int64_t Process(int64_t now_ms);
|
||||||
|
|
||||||
void SetTransportOverhead(DataSize overhead_per_packet) override;
|
void SetTransportOverhead(int64_t overhead_per_packet);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t NextFeedbackTime() const;
|
int64_t NextFeedbackTime() const;
|
||||||
@@ -55,6 +34,8 @@ class CongestionControlFeedbackGenerator
|
|||||||
|
|
||||||
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
|
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
|
||||||
|
|
||||||
|
const RtcpSender rtcp_sender_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Feedback should not use more than 5% of the configured send bandwidth
|
// Feedback should not use more than 5% of the configured send bandwidth
|
||||||
// estimate. Min and max duration between feedback is configurable using field
|
// estimate. Min and max duration between feedback is configurable using field
|
||||||
@@ -73,6 +54,9 @@ class CongestionControlFeedbackGenerator
|
|||||||
int64_t packet_overhead_ = 0;
|
int64_t packet_overhead_ = 0;
|
||||||
int64_t send_rate_debt_ = 0;
|
int64_t send_rate_debt_ = 0;
|
||||||
|
|
||||||
|
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
|
||||||
|
feedback_trackers_;
|
||||||
|
|
||||||
std::optional<int64_t> first_arrival_time_since_feedback_;
|
std::optional<int64_t> first_arrival_time_since_feedback_;
|
||||||
int64_t next_possible_feedback_send_time_ = 0;
|
int64_t next_possible_feedback_send_time_ = 0;
|
||||||
int64_t last_feedback_sent_time_ = 0;
|
int64_t last_feedback_sent_time_ = 0;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "congestion_control_feedback_tracker.h"
|
#include "congestion_control_feedback_tracker.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -7,7 +8,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
void CongestionControlFeedbackTracker::ReceivedPacket(
|
void CongestionControlFeedbackTracker::ReceivedPacket(
|
||||||
const RtpPacketReceived& packet) {
|
RtpPacketReceived& packet) {
|
||||||
int64_t unwrapped_sequence_number =
|
int64_t unwrapped_sequence_number =
|
||||||
unwrapper_.Unwrap(packet.SequenceNumber());
|
unwrapper_.Unwrap(packet.SequenceNumber());
|
||||||
if (last_sequence_number_in_feedback_ &&
|
if (last_sequence_number_in_feedback_ &&
|
||||||
@@ -25,10 +26,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_.push_back({.ssrc = packet.Ssrc(),
|
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
|
||||||
.unwrapped_sequence_number = unwrapped_sequence_number,
|
packet.arrival_time(), packet.ecn()});
|
||||||
.arrival_time = packet.arrival_time(),
|
|
||||||
.ecn = packet.ecn()});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
||||||
@@ -37,10 +36,11 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
|||||||
if (packets_.empty()) {
|
if (packets_.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
absl::c_sort(packets_, [](const PacketInfo& a, const PacketInfo& b) {
|
std::sort(packets_.begin(), packets_.end(),
|
||||||
return std::tie(a.unwrapped_sequence_number, a.arrival_time) <
|
[](const PacketInfo& a, const PacketInfo& b) {
|
||||||
std::tie(b.unwrapped_sequence_number, b.arrival_time);
|
return std::tie(a.unwrapped_sequence_number, a.arrival_time) <
|
||||||
});
|
std::tie(b.unwrapped_sequence_number, b.arrival_time);
|
||||||
|
});
|
||||||
if (!last_sequence_number_in_feedback_) {
|
if (!last_sequence_number_in_feedback_) {
|
||||||
last_sequence_number_in_feedback_ =
|
last_sequence_number_in_feedback_ =
|
||||||
packets_.front().unwrapped_sequence_number - 1;
|
packets_.front().unwrapped_sequence_number - 1;
|
||||||
@@ -61,7 +61,7 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EcnMarking ecn = EcnMarking::kNotEct;
|
EcnMarking ecn = EcnMarking::kNotEct;
|
||||||
TimeDelta arrival_time_offset = TimeDelta::MinusInfinity();
|
int64_t arrival_time_offset = std::numeric_limits<int64_t>::min();
|
||||||
|
|
||||||
if (sequence_number == packet_it->unwrapped_sequence_number) {
|
if (sequence_number == packet_it->unwrapped_sequence_number) {
|
||||||
arrival_time_offset = feedback_time - packet_it->arrival_time;
|
arrival_time_offset = feedback_time - packet_it->arrival_time;
|
||||||
@@ -83,11 +83,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.push_back(
|
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
|
||||||
{.ssrc = ssrc,
|
arrival_time_offset, ecn});
|
||||||
.sequence_number = static_cast<uint16_t>(sequence_number),
|
|
||||||
.arrival_time_offset = arrival_time_offset,
|
|
||||||
.ecn = 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();
|
||||||
|
|||||||
@@ -11,13 +11,15 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "congestion_control_feedback.h"
|
#include "congestion_control_feedback.h"
|
||||||
|
#include "enc_mark.h"
|
||||||
#include "rtp_packet_received.h"
|
#include "rtp_packet_received.h"
|
||||||
|
#include "sequence_number_unwrapper.h"
|
||||||
|
|
||||||
class CongestionControlFeedbackTracker {
|
class CongestionControlFeedbackTracker {
|
||||||
public:
|
public:
|
||||||
CongestionControlFeedbackTracker() = default;
|
CongestionControlFeedbackTracker() = default;
|
||||||
|
|
||||||
void ReceivedPacket(const RtpPacketReceived& packet);
|
void ReceivedPacket(RtpPacketReceived& packet);
|
||||||
|
|
||||||
// Adds received packets to `packet_feedback`
|
// Adds received packets to `packet_feedback`
|
||||||
// RTP sequence numbers are continous from the last created feedback unless
|
// RTP sequence numbers are continous from the last created feedback unless
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "receive_side_congestion_controller.h"
|
#include "receive_side_congestion_controller.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -18,99 +19,33 @@ namespace {
|
|||||||
static const uint32_t kTimeOffsetSwitchThreshold = 30;
|
static const uint32_t kTimeOffsetSwitchThreshold = 30;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ReceiveSideCongestionController::OnRttUpdate(int64_t avg_rtt_ms,
|
|
||||||
int64_t max_rtt_ms) {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveSideCongestionController::RemoveStream(uint32_t ssrc) {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
rbe_->RemoveStream(ssrc);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t ReceiveSideCongestionController::LatestReceiveSideEstimate() const {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
return rbe_->LatestEstimate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveSideCongestionController::PickEstimator(
|
|
||||||
bool has_absolute_send_time) {
|
|
||||||
if (has_absolute_send_time) {
|
|
||||||
// If we see AST in header, switch RBE strategy immediately.
|
|
||||||
if (!using_absolute_send_time_) {
|
|
||||||
RTC_LOG(LS_INFO)
|
|
||||||
<< "WrappingBitrateEstimator: Switching to absolute send time RBE.";
|
|
||||||
using_absolute_send_time_ = true;
|
|
||||||
// rbe_ = std::make_unique<RemoteBitrateEstimatorAbsSendTime>(
|
|
||||||
// env_, &remb_throttler_);
|
|
||||||
}
|
|
||||||
packets_since_absolute_send_time_ = 0;
|
|
||||||
} else {
|
|
||||||
// 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) {
|
|
||||||
RTC_LOG(LS_INFO)
|
|
||||||
<< "WrappingBitrateEstimator: Switching to transmission "
|
|
||||||
"time offset RBE.";
|
|
||||||
using_absolute_send_time_ = false;
|
|
||||||
// rbe_ = std::make_unique<RemoteBitrateEstimatorSingleStream>(
|
|
||||||
// env_, &remb_throttler_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReceiveSideCongestionController::ReceiveSideCongestionController(
|
ReceiveSideCongestionController::ReceiveSideCongestionController(
|
||||||
const Environment& env,
|
RtcpSender feedback_sender)
|
||||||
TransportSequenceNumberFeedbackGenenerator::RtcpSender feedback_sender,
|
: congestion_control_feedback_generator_(feedback_sender),
|
||||||
RembThrottler::RembSender remb_sender,
|
|
||||||
absl::Nullable<NetworkStateEstimator*> network_state_estimator)
|
|
||||||
: env_(env),
|
|
||||||
// remb_throttler_(std::move(remb_sender), &env_.clock()),,
|
|
||||||
congestion_control_feedback_generator_(env, feedback_sender),
|
|
||||||
// rbe_(std::make_unique<RemoteBitrateEstimatorSingleStream>(
|
|
||||||
// env_, &remb_throttler_)),
|
|
||||||
using_absolute_send_time_(false),
|
using_absolute_send_time_(false),
|
||||||
packets_since_absolute_send_time_(0) {
|
packets_since_absolute_send_time_(0) {}
|
||||||
FieldTrialParameter<bool> force_send_rfc8888_feedback("force_send", false);
|
|
||||||
ParseFieldTrial(
|
|
||||||
{&force_send_rfc8888_feedback},
|
|
||||||
env.field_trials().Lookup("WebRTC-RFC8888CongestionControlFeedback"));
|
|
||||||
if (force_send_rfc8888_feedback) {
|
|
||||||
EnablSendCongestionControlFeedbackAccordingToRfc8888();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveSideCongestionController::
|
|
||||||
EnablSendCongestionControlFeedbackAccordingToRfc8888() {
|
|
||||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
||||||
send_rfc8888_congestion_feedback_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveSideCongestionController::OnReceivedPacket(
|
void ReceiveSideCongestionController::OnReceivedPacket(
|
||||||
const RtpPacketReceived& packet, MediaType media_type) {
|
RtpPacketReceived& packet, MediaType media_type) {
|
||||||
if (send_rfc8888_congestion_feedback_) {
|
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
congestion_control_feedback_generator_.OnReceivedPacket(packet);
|
||||||
congestion_control_feedback_generator_.OnReceivedPacket(packet);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
|
void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
|
||||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
int64_t send_bandwidth_estimate = int64_t::BitsPerSec(bitrate_bps);
|
int64_t send_bandwidth_estimate = bitrate_bps;
|
||||||
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
|
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
|
||||||
send_bandwidth_estimate);
|
send_bandwidth_estimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t ReceiveSideCongestionController::MaybeProcess() {
|
int64_t ReceiveSideCongestionController::MaybeProcess() {
|
||||||
int64_t now = env_.clock().CurrentTime();
|
auto now = std::chrono::system_clock::now();
|
||||||
if (send_rfc8888_congestion_feedback_) {
|
int64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
now.time_since_epoch())
|
||||||
return congestion_control_feedback_generator_.Process(now);
|
.count();
|
||||||
}
|
// RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
|
return congestion_control_feedback_generator_.Process(now_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
|
void ReceiveSideCongestionController::SetMaxDesiredReceiveBitrate(
|
||||||
|
|||||||
@@ -17,14 +17,11 @@ class ReceiveSideCongestionController {
|
|||||||
enum MediaType { VIDEO, AUDIO, DATA };
|
enum MediaType { VIDEO, AUDIO, DATA };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ReceiveSideCongestionController();
|
ReceiveSideCongestionController(RtcpSender feedback_sender);
|
||||||
~ReceiveSideCongestionController() override = default;
|
~ReceiveSideCongestionController() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type);
|
void OnReceivedPacket(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);
|
||||||
@@ -35,21 +32,11 @@ class ReceiveSideCongestionController {
|
|||||||
|
|
||||||
void SetTransportOverhead(int64_t overhead_per_packet);
|
void SetTransportOverhead(int64_t overhead_per_packet);
|
||||||
|
|
||||||
// Returns latest receive side bandwidth estimation.
|
|
||||||
// Returns zero if receive side bandwidth estimation is unavailable.
|
|
||||||
int64_t 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();
|
int64_t MaybeProcess();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PickEstimator(bool has_absolute_send_time);
|
|
||||||
|
|
||||||
// RembThrottler remb_throttler_;
|
// RembThrottler remb_throttler_;
|
||||||
|
|
||||||
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
|
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
|
||||||
@@ -58,13 +45,11 @@ class ReceiveSideCongestionController {
|
|||||||
// arbitrary thread by external projects.
|
// arbitrary thread by external projects.
|
||||||
// SequenceChecker sequence_checker_;
|
// SequenceChecker sequence_checker_;
|
||||||
|
|
||||||
bool send_rfc8888_congestion_feedback_ = false;
|
|
||||||
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
|
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
|
||||||
|
|
||||||
std::mutex mutex_;
|
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_ RTC_GUARDED_BY(mutex_);
|
uint32_t packets_since_absolute_send_time_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
// --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
//
|
//
|
||||||
// Common header for all RTCP packets, 4 octets.
|
// Common header for all RTCP packets, 4 octets.
|
||||||
bool CommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
|
bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
|
||||||
const uint8_t kVersion = 2;
|
const uint8_t kVersion = 2;
|
||||||
|
|
||||||
if (size_bytes < kHeaderSizeBytes) {
|
if (size_bytes < kHeaderSizeBytes) {
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class CommonHeader {
|
class RtcpCommonHeader {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t kHeaderSizeBytes = 4;
|
static constexpr size_t kHeaderSizeBytes = 4;
|
||||||
|
|
||||||
CommonHeader() {}
|
RtcpCommonHeader() {}
|
||||||
CommonHeader(const CommonHeader&) = default;
|
RtcpCommonHeader(const RtcpCommonHeader&) = default;
|
||||||
CommonHeader& operator=(const CommonHeader&) = default;
|
RtcpCommonHeader& operator=(const RtcpCommonHeader&) = default;
|
||||||
|
|
||||||
bool Parse(const uint8_t* buffer, size_t size_bytes);
|
bool Parse(const uint8_t* buffer, size_t size_bytes);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class RtcpPacket {
|
class RtcpPacket {
|
||||||
@@ -59,4 +60,7 @@ class RtcpPacket {
|
|||||||
uint32_t sender_ssrc_ = 0;
|
uint32_t sender_ssrc_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using RtcpSender =
|
||||||
|
std::function<void(std::vector<std::unique_ptr<RtcpPacket>> packets)>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -190,7 +190,7 @@ class RtpPacket {
|
|||||||
RtpPacket &operator=(const RtpPacket &rtp_packet);
|
RtpPacket &operator=(const RtpPacket &rtp_packet);
|
||||||
RtpPacket &operator=(RtpPacket &&rtp_packet);
|
RtpPacket &operator=(RtpPacket &&rtp_packet);
|
||||||
|
|
||||||
~RtpPacket();
|
virtual ~RtpPacket();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Set Header
|
// Set Header
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#include "rtp_packet_received.h"
|
#include "rtp_packet_received.h"
|
||||||
|
|
||||||
RtpPacketReceived::RtpPacketReceived() = default;
|
RtpPacketReceived::RtpPacketReceived() = default;
|
||||||
RtpPacketReceived::RtpPacketReceived(int64_t arrival_time)
|
|
||||||
: arrival_time_(arrival_time) {}
|
|
||||||
RtpPacketReceived::RtpPacketReceived(const RtpPacketReceived& packet) = default;
|
RtpPacketReceived::RtpPacketReceived(const RtpPacketReceived& packet) = default;
|
||||||
RtpPacketReceived::RtpPacketReceived(RtpPacketReceived&& packet) = default;
|
RtpPacketReceived::RtpPacketReceived(RtpPacketReceived&& packet) = default;
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,12 @@
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
#include "enc_mark.h"
|
||||||
#include "rtp_packet.h"
|
#include "rtp_packet.h"
|
||||||
|
|
||||||
class RtpPacketReceived : public RtpPacket {
|
class RtpPacketReceived : public RtpPacket {
|
||||||
public:
|
public:
|
||||||
RtpPacketReceived();
|
RtpPacketReceived();
|
||||||
explicit RtpPacketReceived(
|
|
||||||
int64_t arrival_time = (std::numeric_limits<int64_t>::min)());
|
|
||||||
RtpPacketReceived(const RtpPacketReceived& packet);
|
RtpPacketReceived(const RtpPacketReceived& packet);
|
||||||
RtpPacketReceived(RtpPacketReceived&& packet);
|
RtpPacketReceived(RtpPacketReceived&& packet);
|
||||||
|
|
||||||
@@ -24,8 +23,29 @@ class RtpPacketReceived : public RtpPacket {
|
|||||||
|
|
||||||
~RtpPacketReceived();
|
~RtpPacketReceived();
|
||||||
|
|
||||||
|
public:
|
||||||
|
int64_t arrival_time() const { return arrival_time_; }
|
||||||
|
void set_arrival_time(int64_t time) { arrival_time_ = time; }
|
||||||
|
|
||||||
|
// Explicit Congestion Notification (ECN), RFC-3168, Section 5.
|
||||||
|
// Used by L4S: https://www.rfc-editor.org/rfc/rfc9331.html
|
||||||
|
EcnMarking ecn() const { return ecn_; }
|
||||||
|
void set_ecn(EcnMarking ecn) { ecn_ = ecn; }
|
||||||
|
|
||||||
|
// Flag if packet was recovered via RTX or FEC.
|
||||||
|
bool recovered() const { return recovered_; }
|
||||||
|
void set_recovered(bool value) { recovered_ = value; }
|
||||||
|
|
||||||
|
int payload_type_frequency() const { return payload_type_frequency_; }
|
||||||
|
void set_payload_type_frequency(int value) {
|
||||||
|
payload_type_frequency_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t arrival_time_ = (std::numeric_limits<int64_t>::min)();
|
int64_t arrival_time_ = std::numeric_limits<int64_t>::min();
|
||||||
|
EcnMarking ecn_ = EcnMarking::kNotEct;
|
||||||
|
int payload_type_frequency_ = 0;
|
||||||
|
bool recovered_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
91
xmake.lua
91
xmake.lua
@@ -10,7 +10,8 @@ set_installdir("$(projectdir)/out")
|
|||||||
|
|
||||||
add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR",
|
add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS", "ASIO_HAS_STD_SHARED_PTR",
|
||||||
"ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC", "ASIO_HAS_STD_CHRONO",
|
"ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC", "ASIO_HAS_STD_CHRONO",
|
||||||
"ASIO_HAS_CSTDINT", "ASIO_HAS_STD_ARRAY", "ASIO_HAS_STD_SYSTEM_ERROR")
|
"ASIO_HAS_CSTDINT", "ASIO_HAS_STD_ARRAY", "ASIO_HAS_STD_SYSTEM_ERROR",
|
||||||
|
"NOMINMAX")
|
||||||
|
|
||||||
add_requires("asio 1.24.0", "nlohmann_json 3.11.3", "spdlog 1.14.1", "openfec 1.4.2", "libopus 1.5.1", "openh264 2.4.1", "dav1d 1.4.3", "libyuv 2024.5.21", "aom 3.9.0", {system = false}, {configs = {shared = false}})
|
add_requires("asio 1.24.0", "nlohmann_json 3.11.3", "spdlog 1.14.1", "openfec 1.4.2", "libopus 1.5.1", "openh264 2.4.1", "dav1d 1.4.3", "libyuv 2024.5.21", "aom 3.9.0", {system = false}, {configs = {shared = false}})
|
||||||
add_packages("asio", "nlohmann_json", "spdlog", "openfec", "libopus", "openh264", "dav1d", "libyuv", "aom")
|
add_packages("asio", "nlohmann_json", "spdlog", "openfec", "libopus", "openh264", "dav1d", "libyuv", "aom")
|
||||||
@@ -76,27 +77,6 @@ target("statistics")
|
|||||||
add_files("src/statistics/*.cpp")
|
add_files("src/statistics/*.cpp")
|
||||||
add_includedirs("src/statistics", {public = true})
|
add_includedirs("src/statistics", {public = true})
|
||||||
|
|
||||||
target("rtcp")
|
|
||||||
set_kind("object")
|
|
||||||
add_deps("log", "common")
|
|
||||||
add_files("src/rtcp/*.cpp",
|
|
||||||
"src/rtcp/rtcp_packet/*.cpp",
|
|
||||||
"src/rtcp/rtp_feedback/*.cpp")
|
|
||||||
add_includedirs("src/rtcp",
|
|
||||||
"src/rtcp/rtcp_packet",
|
|
||||||
"src/rtcp/rtp_feedback", {public = true})
|
|
||||||
|
|
||||||
target("rtp", "qos")
|
|
||||||
set_kind("object")
|
|
||||||
add_deps("log", "frame", "ringbuffer", "thread", "rtcp", "fec", "statistics")
|
|
||||||
add_files("src/rtp/*.cpp",
|
|
||||||
"src/rtp/rtp_endpoint/*.cpp",
|
|
||||||
"src/rtp/rtp_packet/*.cpp")
|
|
||||||
add_includedirs("src/rtp",
|
|
||||||
"src/rtp/rtp_endpoint",
|
|
||||||
"src/rtp/rtp_packet",
|
|
||||||
"src/qos", {public = true})
|
|
||||||
|
|
||||||
target("ice")
|
target("ice")
|
||||||
set_kind("object")
|
set_kind("object")
|
||||||
add_deps("log", "common", "ws")
|
add_deps("log", "common", "ws")
|
||||||
@@ -115,7 +95,44 @@ target("ws")
|
|||||||
set_kind("object")
|
set_kind("object")
|
||||||
add_deps("log")
|
add_deps("log")
|
||||||
add_files("src/ws/*.cpp")
|
add_files("src/ws/*.cpp")
|
||||||
add_includedirs("thirdparty/websocketpp/include", {public = true})
|
add_includedirs("src/ws",
|
||||||
|
"thirdparty/websocketpp/include", {public = true})
|
||||||
|
|
||||||
|
target("rtp")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "frame", "ringbuffer", "thread", "rtcp", "fec", "statistics")
|
||||||
|
add_files("src/rtp/*.cpp",
|
||||||
|
"src/rtp/rtp_packet/*.cpp")
|
||||||
|
add_includedirs("src/rtp",
|
||||||
|
"src/rtp/rtp_packet", {public = true})
|
||||||
|
|
||||||
|
target("rtcp")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "common")
|
||||||
|
add_files("src/rtcp/*.cpp",
|
||||||
|
"src/rtcp/rtcp_packet/*.cpp",
|
||||||
|
"src/rtcp/rtp_feedback/*.cpp")
|
||||||
|
add_includedirs("src/rtcp",
|
||||||
|
"src/rtcp/rtcp_packet",
|
||||||
|
"src/rtcp/rtp_feedback", {public = true})
|
||||||
|
|
||||||
|
target("qos")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "rtp")
|
||||||
|
add_files("src/qos/*.cpp")
|
||||||
|
add_includedirs("src/qos", {public = true})
|
||||||
|
|
||||||
|
target("channel")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "rtp", "rtcp", "ice", "qos")
|
||||||
|
add_files("src/channel/*.cpp", "src/channel/rtp_channel/*.cpp")
|
||||||
|
add_includedirs("src/channel", "src/channel/rtp_channel", {public = true})
|
||||||
|
|
||||||
|
target("transport")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "ws", "ice", "channel", "rtp", "rtcp", "statistics", "media")
|
||||||
|
add_files("src/transport/*.cpp")
|
||||||
|
add_includedirs("src/transport", {public = true})
|
||||||
|
|
||||||
target("media")
|
target("media")
|
||||||
set_kind("object")
|
set_kind("object")
|
||||||
@@ -187,41 +204,17 @@ target("media")
|
|||||||
add_includedirs("src/media/audio/encode",
|
add_includedirs("src/media/audio/encode",
|
||||||
"src/media/audio/decode", "src/interface", {public = true})
|
"src/media/audio/decode", "src/interface", {public = true})
|
||||||
|
|
||||||
target("qos")
|
|
||||||
set_kind("object")
|
|
||||||
add_deps("log", "rtp")
|
|
||||||
add_files("src/qos/*.cpp")
|
|
||||||
add_includedirs("src/qos", {public = true})
|
|
||||||
|
|
||||||
-- target("transport")
|
|
||||||
-- set_kind("object")
|
|
||||||
-- add_deps("log", "ws", "ice", "qos", "rtp", "rtcp", "statistics", "media")
|
|
||||||
-- add_files("src/transport/*.cpp")
|
|
||||||
-- add_includedirs("src/ws", "src/ice", "src/qos", {public = true})
|
|
||||||
|
|
||||||
target("transport")
|
|
||||||
set_kind("object")
|
|
||||||
add_deps("log", "ws", "ice", "channel", "rtp", "rtcp", "statistics", "media")
|
|
||||||
add_files("src/transport/*.cpp")
|
|
||||||
add_includedirs("src/ws", "src/ice", "src/channel", {public = true})
|
|
||||||
|
|
||||||
target("channel")
|
|
||||||
set_kind("object")
|
|
||||||
add_deps("log", "rtp", "rtcp", "ice")
|
|
||||||
add_files("src/channel/*.cpp")
|
|
||||||
add_includedirs("src/rtp", "src/rtcp", {public = true})
|
|
||||||
|
|
||||||
target("pc")
|
target("pc")
|
||||||
set_kind("object")
|
set_kind("object")
|
||||||
add_deps("log", "ws", "ice", "transport", "inih", "common")
|
add_deps("log", "ws", "ice", "transport", "inih", "common")
|
||||||
add_files("src/pc/*.cpp")
|
add_files("src/pc/*.cpp")
|
||||||
add_includedirs("src/transport", "src/interface", {public = true})
|
add_includedirs("src/pc", "src/interface", {public = true})
|
||||||
|
|
||||||
target("projectx")
|
target("projectx")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
add_deps("log", "pc")
|
add_deps("log", "pc")
|
||||||
add_files("src/rtc/*.cpp")
|
add_files("src/rtc/*.cpp")
|
||||||
add_includedirs("src/rtc", "src/pc", "src/interface")
|
add_includedirs("src/rtc", "src/interface")
|
||||||
|
|
||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
add_linkdirs("thirdparty/nvcodec/lib/x64")
|
add_linkdirs("thirdparty/nvcodec/lib/x64")
|
||||||
|
|||||||
Reference in New Issue
Block a user