diff --git a/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/mod_ops.h b/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/mod_ops.h new file mode 100644 index 0000000000..f1c8b677b2 --- /dev/null +++ b/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/mod_ops.h @@ -0,0 +1,142 @@ +/* + * 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 RTC_BASE_NUMERICS_MOD_OPS_H_ +#define RTC_BASE_NUMERICS_MOD_OPS_H_ + +#include +#include + +// #include "rtc_base/checks.h" + +namespace webrtc { + +template // 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 // 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 +inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::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 +inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return b - a; +} + +template +inline T ForwardDiff(T a, T b) { + return ForwardDiff(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 +inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::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 +inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a - b; +} + +template +inline T ReverseDiff(T a, T b) { + return ReverseDiff(a, b); +} + +// Calculates the minimum distance between to wrapping numbers. +// +// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b)) +template +inline T MinDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return std::min(ForwardDiff(a, b), ReverseDiff(a, b)); +} + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_MOD_OPS_H_ diff --git a/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/sequence_number_util.h b/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/sequence_number_util.h new file mode 100644 index 0000000000..0fa689f9c7 --- /dev/null +++ b/worker/deps/libwebrtc/libwebrtc/rtc_base/numerics/sequence_number_util.h @@ -0,0 +1,117 @@ +/* + * 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 RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ +#define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "rtc_base/numerics/mod_ops.h" + +namespace webrtc { + +// 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 +inline typename std::enable_if<(M > 0), bool>::type AheadOrAt(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + const T maxDist = M / 2; + if (!(M & 1) && MinDiff(a, b) == maxDist) + return b < a; + return ForwardDiff(b, a) <= maxDist; +} + +template +inline typename std::enable_if<(M == 0), bool>::type AheadOrAt(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + const T maxDist = std::numeric_limits::max() / 2 + T(1); + if (a - b == maxDist) + return b < a; + return ForwardDiff(b, a) < maxDist; +} + +template +inline bool AheadOrAt(T a, T b) { + return AheadOrAt(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 +inline bool AheadOf(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a != b && AheadOrAt(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 +struct AscendingSeqNumComp { + bool operator()(T a, T b) const { return AheadOf(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 +struct DescendingSeqNumComp { + bool operator()(T a, T b) const { return AheadOf(b, a); } +}; + +// A sequence number unwrapper where the first unwrapped value equals the +// first value being unwrapped. +template +class SeqNumUnwrapper { + static_assert( + std::is_unsigned::value && + std::numeric_limits::max() < std::numeric_limits::max(), + "Type unwrapped must be an unsigned integer smaller than int64_t."); + + public: + int64_t Unwrap(T value) { + if (!last_value_) { + last_unwrapped_ = {value}; + } else { + last_unwrapped_ += ForwardDiff(*last_value_, value); + + if (!AheadOrAt(value, *last_value_)) { + constexpr int64_t kBackwardAdjustment = + M == 0 ? int64_t{std::numeric_limits::max()} + 1 : M; + last_unwrapped_ -= kBackwardAdjustment; + } + } + + last_value_ = value; + return last_unwrapped_; + } + + private: + int64_t last_unwrapped_ = 0; + absl::optional last_value_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ diff --git a/worker/include/RTC/TransportCongestionControlServer.hpp b/worker/include/RTC/TransportCongestionControlServer.hpp index 5747d13d03..c01108d900 100644 --- a/worker/include/RTC/TransportCongestionControlServer.hpp +++ b/worker/include/RTC/TransportCongestionControlServer.hpp @@ -8,7 +8,9 @@ #include "RTC/RtpPacket.hpp" #include "handles/Timer.hpp" #include +#include #include +using namespace webrtc; namespace RTC { @@ -59,6 +61,13 @@ namespace RTC void SendTransportCcFeedback(); void MaySendLimitationRembFeedback(); void UpdatePacketLoss(double packetLoss); + void OnPacketArrival(uint16_t sequence_number, uint64_t arrival_time); + void SendPeriodicFeedbacks(); + int64_t BuildFeedbackPacket( + int64_t base_sequence_number, + std::map::const_iterator begin_iterator, // |begin_iterator| is inclusive. + std::map::const_iterator end_iterator // |end_iterator| is exclusive. + ); /* Pure virtual methods inherited from webrtc::RemoteBitrateEstimator::Listener. */ public: @@ -89,6 +98,12 @@ namespace RTC uint8_t unlimitedRembCounter{ 0u }; std::deque packetLossHistory; double packetLoss{ 0 }; + SeqNumUnwrapper unwrapper; + absl::optional periodicWindowStartSeq; + // Map unwrapped seq -> time. + std::map packetArrivalTimes; + // Use buffer policy similar to webrtc + bool useBufferPolicy{ true }; }; } // namespace RTC diff --git a/worker/src/RTC/TransportCongestionControlServer.cpp b/worker/src/RTC/TransportCongestionControlServer.cpp index 8c7a016949..c8d33229da 100644 --- a/worker/src/RTC/TransportCongestionControlServer.cpp +++ b/worker/src/RTC/TransportCongestionControlServer.cpp @@ -16,6 +16,9 @@ namespace RTC static constexpr uint64_t LimitationRembInterval{ 1500u }; // In ms. static constexpr uint8_t UnlimitedRembNumPackets{ 4u }; static constexpr size_t PacketLossHistogramLength{ 24 }; + static constexpr int64_t kMaxTimeMs{ std::numeric_limits::max() / 1000 }; + // Impossible to request feedback older than what can be represented by 15 bits. + static constexpr int kMaxNumberOfPackets{ (1 << 15) }; /* Instance methods. */ @@ -109,6 +112,162 @@ namespace RTC return this->packetLoss; } + void TransportCongestionControlServer::OnPacketArrival(uint16_t sequence_number, uint64_t arrival_time) + { + if (arrival_time > kMaxTimeMs) + { + MS_WARN_DEV("arrival time out of bounds:%" PRIu64 "", arrival_time); + + return; + } + + int64_t seq = this->unwrapper.Unwrap(sequence_number); + + if (seq <= 0) + { + MS_WARN_DEV("invalid seq_num:%" PRIu16 ", unwrap-seq:%" PRId64 "", sequence_number, seq); + + return; + } + + if ( + this->periodicWindowStartSeq && + this->packetArrivalTimes.lower_bound(*this->periodicWindowStartSeq) == + this->packetArrivalTimes.end()) + { + // Start new feedback packet, cull old packets. + for (auto it = this->packetArrivalTimes.begin(); it != this->packetArrivalTimes.end() && + it->first < seq && + arrival_time - it->second >= 500;) + { + it = this->packetArrivalTimes.erase(it); + } + } + + if (!this->periodicWindowStartSeq || seq < *this->periodicWindowStartSeq) + { + this->periodicWindowStartSeq = seq; + } + + // We are only interested in the first time a packet is received. + if (this->packetArrivalTimes.find(seq) != this->packetArrivalTimes.end()) + { + return; + } + + this->packetArrivalTimes[seq] = arrival_time; + // Limit the range of sequence numbers to send feedback for. + auto first_arrival_time_to_keep = this->packetArrivalTimes.lower_bound( + this->packetArrivalTimes.rbegin()->first - kMaxNumberOfPackets); + + if (first_arrival_time_to_keep != this->packetArrivalTimes.begin()) + { + this->packetArrivalTimes.erase(this->packetArrivalTimes.begin(), first_arrival_time_to_keep); + + // |this->packetArrivalTimes| cannot be empty since we just added one element + // and the last element is not deleted. + // RTC_DCHECK(!this->packetArrivalTimes.empty()); + this->periodicWindowStartSeq = this->packetArrivalTimes.begin()->first; + } + } + + void TransportCongestionControlServer::SendPeriodicFeedbacks() + { + // |this->periodicWindowStartSeq| is the first sequence number to include in the + // current feedback packet. Some older may still be in the map, in case a + // reordering happens and we need to retransmit them. + if (!this->periodicWindowStartSeq) + { + return; + } + + for (auto begin_iterator = this->packetArrivalTimes.lower_bound(*this->periodicWindowStartSeq); + begin_iterator != this->packetArrivalTimes.cend(); + begin_iterator = this->packetArrivalTimes.lower_bound(*this->periodicWindowStartSeq)) + { + int64_t next_sequence_number = BuildFeedbackPacket( + *this->periodicWindowStartSeq, begin_iterator, this->packetArrivalTimes.cend()); + + // If build feedback packet fail, it will not be sent. + if (next_sequence_number < 0) + { + // Reset and create a new feedback packet for next periodic. + this->transportCcFeedbackPacket.reset(new RTC::RTCP::FeedbackRtpTransportPacket( + this->transportCcFeedbackSenderSsrc, this->transportCcFeedbackMediaSsrc)); + + return; + } + + this->periodicWindowStartSeq = next_sequence_number; + SendTransportCcFeedback(); + // Note: Don't erase items from this->packetArrivalTimes after sending, in case + // they need to be re-sent after a reordering. Removal will be handled + // by OnPacketArrival once packets are too old. + } + } + + int64_t TransportCongestionControlServer::BuildFeedbackPacket( + int64_t base_sequence_number, + std::map::const_iterator begin_iterator, + std::map::const_iterator end_iterator) + { + // Set base sequence numer and reference time(arrival time of first received packet in the feedback). + uint64_t ref_timestamp_ms = begin_iterator->second; + this->transportCcFeedbackPacket->AddPacket( + static_cast((base_sequence_number - 1) & 0xFFFF), + ref_timestamp_ms, + this->maxRtcpPacketLen); + + // RTC_DCHECK(begin_iterator != end_iterator); + int64_t next_sequence_number = base_sequence_number; + + for (auto it = begin_iterator; it != end_iterator; ++it) + { + auto result = this->transportCcFeedbackPacket->AddPacket( + static_cast(it->first & 0xFFFF), it->second, this->maxRtcpPacketLen); + + if (RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::SUCCESS != result) + { + // If we can't even add the first seq to the feedback packet, we won't be + // able to build it at all. + // RTC_CHECK(begin_iterator != it); + MS_WARN_DEV( + "add fail! result:%" PRIu32 ", cur-seq:%" PRId64 ", next-seq:%" PRId64 + ", base-seq:%" PRId64 "", + static_cast(result), + it->first, + next_sequence_number, + base_sequence_number); + // When add not success then update startSeq to current seq. + this->periodicWindowStartSeq = it->first; + + // Could not add timestamp, feedback packet max size exceeded. Return and + // try again with a fresh packet. + if (RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::MAX_SIZE_EXCEEDED == result) + { + break; + } + else /*if (RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::FATAL == result)*/ + { + // When add fail really then discard this feedback packet. + return -1; + } + } + + next_sequence_number = it->first + 1; + + // If the feedback packet is full, send it now. + if (this->transportCcFeedbackPacket->IsFull()) + { + MS_DEBUG_DEV("transport-cc feedback packet is full, sending feedback now"); + + break; + } + } + + return next_sequence_number; + } + void TransportCongestionControlServer::IncomingPacket(uint64_t nowMs, const RTC::RtpPacket* packet) { MS_TRACE(); @@ -131,50 +290,57 @@ namespace RTC this->transportCcFeedbackPacket->SetSenderSsrc(0u); this->transportCcFeedbackPacket->SetMediaSsrc(this->transportCcFeedbackMediaSsrc); - // Provide the feedback packet with the RTP packet info. If it fails, - // send current feedback and add the packet info to a new one. - auto result = - this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen); - - switch (result) + if (this->useBufferPolicy) + { + this->OnPacketArrival(wideSeqNumber, nowMs); + } + else { - case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::SUCCESS: + // Provide the feedback packet with the RTP packet info. If it fails, + // send current feedback and add the packet info to a new one. + auto result = + this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen); + + switch (result) { - // If the feedback packet is full, send it now. - if (this->transportCcFeedbackPacket->IsFull()) + case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::SUCCESS: { - MS_DEBUG_DEV("transport-cc feedback packet is full, sending feedback now"); + // If the feedback packet is full, send it now. + if (this->transportCcFeedbackPacket->IsFull()) + { + MS_DEBUG_DEV("transport-cc feedback packet is full, sending feedback now"); - SendTransportCcFeedback(); - } + SendTransportCcFeedback(); + } - break; - } + break; + } - case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::MAX_SIZE_EXCEEDED: - { - // Send ongoing feedback packet and add the new packet info to the - // regenerated one. - SendTransportCcFeedback(); + case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::MAX_SIZE_EXCEEDED: + { + // Send ongoing feedback packet and add the new packet info to the + // regenerated one. + SendTransportCcFeedback(); - this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen); + this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen); - break; - } + break; + } - case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::FATAL: - { - // Create a new feedback packet. - this->transportCcFeedbackPacket.reset(new RTC::RTCP::FeedbackRtpTransportPacket( - this->transportCcFeedbackSenderSsrc, this->transportCcFeedbackMediaSsrc)); + case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::FATAL: + { + // Create a new feedback packet. + this->transportCcFeedbackPacket.reset(new RTC::RTCP::FeedbackRtpTransportPacket( + this->transportCcFeedbackSenderSsrc, this->transportCcFeedbackMediaSsrc)); - // Use current packet count. - // NOTE: Do not increment it since the previous ongoing feedback - // packet was not sent. - this->transportCcFeedbackPacket->SetFeedbackPacketCount( - this->transportCcFeedbackPacketCount); + // Use current packet count. + // NOTE: Do not increment it since the previous ongoing feedback + // packet was not sent. + this->transportCcFeedbackPacket->SetFeedbackPacketCount( + this->transportCcFeedbackPacketCount); - break; + break; + } } } @@ -264,7 +430,7 @@ namespace RTC this->transportCcFeedbackPacket->SetFeedbackPacketCount(++this->transportCcFeedbackPacketCount); // Pass the latest packet info (if any) as pre base for the new feedback packet. - if (latestTimestamp > 0u) + if (latestTimestamp > 0u && !this->useBufferPolicy) { this->transportCcFeedbackPacket->AddPacket( latestWideSeqNumber, latestTimestamp, this->maxRtcpPacketLen); @@ -389,7 +555,14 @@ namespace RTC if (timer == this->transportCcFeedbackSendPeriodicTimer) { - SendTransportCcFeedback(); + if (this->useBufferPolicy) + { + SendPeriodicFeedbacks(); + } + else + { + SendTransportCcFeedback(); + } } } } // namespace RTC