[feat] implementation for qos module

This commit is contained in:
dijunkun
2025-01-08 17:30:13 +08:00
parent 7a84b25b5c
commit de212a8e75
32 changed files with 482 additions and 249 deletions

View File

@@ -96,6 +96,10 @@ uint16_t To2BitEcn(EcnMarking ecn_marking) {
return kEcnEct0 << 13;
case EcnMarking::kCe:
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);
*position += 2;
}
return true;
};
ArrayView<const PacketInfo> remaining(packets_);
@@ -255,7 +261,7 @@ size_t CongestionControlFeedback::BlockLength() const {
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_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;
bool received = (packet_info & 0x8000);
packets_.push_back(
{.ssrc = ssrc,
.sequence_number = seq_no,
.arrival_time_offset = received ? AtoToTimeDelta(packet_info)
: TimeDelta::MinusInfinity(),
.ecn = ToEcnMarking(packet_info)});
packets_.push_back(PacketInfo{ssrc, seq_no,
received
? AtoToTimeDelta(packet_info)
: std::numeric_limits<int64_t>::min(),
ToEcnMarking(packet_info)});
}
if (num_reports % 2) {
// 2 bytes padding

View File

@@ -13,29 +13,9 @@
#include <vector>
#include "array_view.h"
#include "enc_mark.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
// https://www.rfc-editor.org/rfc/rfc8888.html
class CongestionControlFeedback : public RtpFeedback {
@@ -58,7 +38,7 @@ class CongestionControlFeedback : public RtpFeedback {
uint32_t report_timestamp_compact_ntp);
CongestionControlFeedback() = default;
bool Parse(const CommonHeader& packet);
bool Parse(const RtcpCommonHeader& packet);
ArrayView<const PacketInfo> packets() const { return packets_; }

View File

@@ -21,7 +21,7 @@ CongestionControlFeedbackGenerator::CongestionControlFeedbackGenerator(
: rtcp_sender_(std::move(rtcp_sender)) {}
void CongestionControlFeedbackGenerator::OnReceivedPacket(
const RtpPacketReceived& packet) {
RtpPacketReceived& packet) {
marker_bit_seen_ |= packet.Marker();
if (!first_arrival_time_since_feedback_) {
first_arrival_time_since_feedback_ = packet.arrival_time();
@@ -63,19 +63,19 @@ int64_t CongestionControlFeedbackGenerator::Process(int64_t now_ms) {
}
void CongestionControlFeedbackGenerator::OnSendBandwidthEstimateChanged(
DataRate estimate) {
int64_t estimate) {
// Feedback reports should max occupy 5% of total bandwidth.
max_feedback_rate_ = estimate * 0.05;
}
void CongestionControlFeedbackGenerator::SetTransportOverhead(
DataSize overhead_per_packet) {
int64_t overhead_per_packet) {
packet_overhead_ = overhead_per_packet;
}
void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
uint32_t compact_ntp = ConvertToCompactNtp(now_ms);
std::vector<rtcp::CongestionControlFeedback::PacketInfo> rtcp_packet_info;
std::vector<CongestionControlFeedback::PacketInfo> rtcp_packet_info;
for (auto& [unused, tracker] : feedback_trackers_) {
tracker.AddPacketsToFeedback(now_ms, rtcp_packet_info);
}
@@ -83,18 +83,18 @@ void CongestionControlFeedbackGenerator::SendFeedback(int64_t now_ms) {
marker_bit_seen_ = false;
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);
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_sender_(std::move(rtcp_packets));
}
void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime(
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_;
send_rate_debt_ =
debt_payed > send_rate_debt_ ? 0 : send_rate_debt_ - debt_payed;

View File

@@ -10,43 +10,22 @@
#include <optional>
#include <vector>
#include "congestion_control_feedback_tracker.h"
#include "rtcp_packet.h"
#include "rtp_packet_received.h"
class RtpTransportFeedbackGenerator {
class CongestionControlFeedbackGenerator {
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 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(RtcpSender feedback_sender);
~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:
int64_t NextFeedbackTime() const;
@@ -55,6 +34,8 @@ class CongestionControlFeedbackGenerator
void CalculateNextPossibleSendTime(int64_t feedback_size, int64_t now_ms);
const RtcpSender rtcp_sender_;
private:
// Feedback should not use more than 5% of the configured send bandwidth
// estimate. Min and max duration between feedback is configurable using field
@@ -73,6 +54,9 @@ class CongestionControlFeedbackGenerator
int64_t packet_overhead_ = 0;
int64_t send_rate_debt_ = 0;
std::map</*ssrc=*/uint32_t, CongestionControlFeedbackTracker>
feedback_trackers_;
std::optional<int64_t> first_arrival_time_since_feedback_;
int64_t next_possible_feedback_send_time_ = 0;
int64_t last_feedback_sent_time_ = 0;

View File

@@ -1,5 +1,6 @@
#include "congestion_control_feedback_tracker.h"
#include <algorithm>
#include <cstdint>
#include <tuple>
#include <vector>
@@ -7,7 +8,7 @@
#include "log.h"
void CongestionControlFeedbackTracker::ReceivedPacket(
const RtpPacketReceived& packet) {
RtpPacketReceived& packet) {
int64_t unwrapped_sequence_number =
unwrapper_.Unwrap(packet.SequenceNumber());
if (last_sequence_number_in_feedback_ &&
@@ -25,10 +26,8 @@ void CongestionControlFeedbackTracker::ReceivedPacket(
// received.
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
}
packets_.push_back({.ssrc = packet.Ssrc(),
.unwrapped_sequence_number = unwrapped_sequence_number,
.arrival_time = packet.arrival_time(),
.ecn = packet.ecn()});
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn()});
}
void CongestionControlFeedbackTracker::AddPacketsToFeedback(
@@ -37,10 +36,11 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
if (packets_.empty()) {
return;
}
absl::c_sort(packets_, [](const PacketInfo& a, const PacketInfo& b) {
return std::tie(a.unwrapped_sequence_number, a.arrival_time) <
std::tie(b.unwrapped_sequence_number, b.arrival_time);
});
std::sort(packets_.begin(), packets_.end(),
[](const PacketInfo& a, const PacketInfo& b) {
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_) {
last_sequence_number_in_feedback_ =
packets_.front().unwrapped_sequence_number - 1;
@@ -61,7 +61,7 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
}
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) {
arrival_time_offset = feedback_time - packet_it->arrival_time;
@@ -83,11 +83,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
++packet_it;
}
} // else - the packet has not been received yet.
packet_feedback.push_back(
{.ssrc = ssrc,
.sequence_number = static_cast<uint16_t>(sequence_number),
.arrival_time_offset = arrival_time_offset,
.ecn = ecn});
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn});
}
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
packets_.clear();

View File

@@ -11,13 +11,15 @@
#include <vector>
#include "congestion_control_feedback.h"
#include "enc_mark.h"
#include "rtp_packet_received.h"
#include "sequence_number_unwrapper.h"
class CongestionControlFeedbackTracker {
public:
CongestionControlFeedbackTracker() = default;
void ReceivedPacket(const RtpPacketReceived& packet);
void ReceivedPacket(RtpPacketReceived& packet);
// Adds received packets to `packet_feedback`
// RTP sequence numbers are continous from the last created feedback unless

View File

@@ -11,6 +11,7 @@
#include "receive_side_congestion_controller.h"
#include <algorithm>
#include <chrono>
#include <memory>
#include <utility>
@@ -18,99 +19,33 @@ 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> 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(
const Environment& env,
TransportSequenceNumberFeedbackGenenerator::RtcpSender 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_)),
RtcpSender feedback_sender)
: congestion_control_feedback_generator_(feedback_sender),
using_absolute_send_time_(false),
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;
}
packets_since_absolute_send_time_(0) {}
void ReceiveSideCongestionController::OnReceivedPacket(
const RtpPacketReceived& packet, MediaType media_type) {
if (send_rfc8888_congestion_feedback_) {
// RTC_DCHECK_RUN_ON(&sequence_checker_);
congestion_control_feedback_generator_.OnReceivedPacket(packet);
return;
}
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 = int64_t::BitsPerSec(bitrate_bps);
int64_t send_bandwidth_estimate = bitrate_bps;
congestion_control_feedback_generator_.OnSendBandwidthEstimateChanged(
send_bandwidth_estimate);
}
int64_t ReceiveSideCongestionController::MaybeProcess() {
int64_t now = env_.clock().CurrentTime();
if (send_rfc8888_congestion_feedback_) {
// RTC_DCHECK_RUN_ON(&sequence_checker_);
return congestion_control_feedback_generator_.Process(now);
}
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(

View File

@@ -17,14 +17,11 @@ class ReceiveSideCongestionController {
enum MediaType { VIDEO, AUDIO, DATA };
public:
ReceiveSideCongestionController();
~ReceiveSideCongestionController() override = default;
ReceiveSideCongestionController(RtcpSender feedback_sender);
~ReceiveSideCongestionController() = default;
public:
void OnReceivedPacket(const RtpPacketReceived& packet, MediaType media_type);
// Implements CallStatsObserver.
void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
void OnReceivedPacket(RtpPacketReceived& packet, MediaType media_type);
// This is send bitrate, used to control the rate of feedback messages.
void OnBitrateChanged(int bitrate_bps);
@@ -35,21 +32,11 @@ class ReceiveSideCongestionController {
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
// call to `MaybeProcess` should be non idle.
int64_t MaybeProcess();
private:
void PickEstimator(bool has_absolute_send_time);
// RembThrottler remb_throttler_;
// TODO: bugs.webrtc.org/42224904 - Use sequence checker for all usage of
@@ -58,13 +45,11 @@ class ReceiveSideCongestionController {
// arbitrary thread by external projects.
// SequenceChecker sequence_checker_;
bool send_rfc8888_congestion_feedback_ = false;
CongestionControlFeedbackGenerator congestion_control_feedback_generator_;
std::mutex mutex_;
std::unique_ptr<RemoteBitrateEstimator> rbe_;
bool using_absolute_send_time_;
uint32_t packets_since_absolute_send_time_ RTC_GUARDED_BY(mutex_);
uint32_t packets_since_absolute_send_time_;
};
#endif