diff --git a/webrtc/modules/video_coding/sequence_number_util.h b/webrtc/modules/video_coding/sequence_number_util.h index 8a139a34dc..e3bc38e68e 100644 --- a/webrtc/modules/video_coding/sequence_number_util.h +++ b/webrtc/modules/video_coding/sequence_number_util.h @@ -15,6 +15,8 @@ #include #include "webrtc/rtc_base/mod_ops.h" +#include "webrtc/rtc_base/optional.h" +#include "webrtc/rtc_base/safe_compare.h" namespace webrtc { @@ -24,7 +26,7 @@ namespace webrtc { // from each other, then the sequence number with the highest value is // considered to be ahead. template -inline bool AheadOrAt(T a, T b) { +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; @@ -33,8 +35,8 @@ inline bool AheadOrAt(T a, T b) { return ForwardDiff(b, a) <= maxDist; } -template -inline bool AheadOrAt(T a, T b) { +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); @@ -43,66 +45,76 @@ inline bool AheadOrAt(T a, T b) { 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 +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); } -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); -} - -namespace internal { - -template -struct SeqNumComp; - -template -struct SeqNumComp> { +// 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); } }; -template -struct SeqNumComp> { - bool operator()(T a, T b) const { return AheadOf(a, b); } -}; - -} // namespace internal - // 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 - : private internal::SeqNumComp> { - bool operator()(T a, T b) const { - return internal::SeqNumComp>::operator()(a, - b); - } +struct DescendingSeqNumComp { + bool operator()(T a, T b) const { return AheadOf(b, a); } }; -// 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). +// A sequencer number unwrapper where the start value of the unwrapped sequence +// can be set. The unwrapped value is not allowed to wrap. template -struct DescendingSeqNumComp - : private internal::SeqNumComp> { - bool operator()(T a, T b) const { - return internal::SeqNumComp>::operator()(b, - a); +class SeqNumUnwrapper { + static_assert( + std::is_unsigned::value && + rtc::SafeLt(std::numeric_limits::max(), + std::numeric_limits::max()), + "Type unwrapped must be an unsigned integer smaller than uint64_t."); + + public: + SeqNumUnwrapper() : last_unwrapped_(0) {} + explicit SeqNumUnwrapper(uint64_t start_at) : last_unwrapped_(start_at) {} + + uint64_t Unwrap(T value) { + if (!last_value_) + last_value_.emplace(value); + + uint64_t unwrapped = 0; + if (AheadOrAt(value, *last_value_)) { + unwrapped = last_unwrapped_ + ForwardDiff(*last_value_, value); + RTC_CHECK_GE(unwrapped, last_unwrapped_); + } else { + unwrapped = last_unwrapped_ - ReverseDiff(*last_value_, value); + RTC_CHECK_LT(unwrapped, last_unwrapped_); + } + + *last_value_ = value; + last_unwrapped_ = unwrapped; + return last_unwrapped_; } + + private: + uint64_t last_unwrapped_; + rtc::Optional last_value_; }; } // namespace webrtc diff --git a/webrtc/modules/video_coding/sequence_number_util_unittest.cc b/webrtc/modules/video_coding/sequence_number_util_unittest.cc index 1ed78b95b8..12cc911e41 100644 --- a/webrtc/modules/video_coding/sequence_number_util_unittest.cc +++ b/webrtc/modules/video_coding/sequence_number_util_unittest.cc @@ -209,4 +209,108 @@ TEST_F(TestSeqNumUtil, SeqNumComparatorWithDivisor) { } } +TEST(SeqNumUnwrapper, NoBackWardWrap) { + SeqNumUnwrapper unwrapper; + EXPECT_EQ(0U, unwrapper.Unwrap(0)); + + // The unwrapped sequence is not allowed to wrap, if that happens the + // SeqNumUnwrapper should have been constructed with a higher start value. + ASSERT_DEATH_IF_SUPPORTED(unwrapper.Unwrap(255), ""); +} + +TEST(SeqNumUnwrapper, NoForwardWrap) { + SeqNumUnwrapper unwrapper(std::numeric_limits::max()); + EXPECT_EQ(std::numeric_limits::max(), unwrapper.Unwrap(0)); + + // The unwrapped sequence is not allowed to wrap, if that happens the + // SeqNumUnwrapper should have been constructed with a lower start value. + ASSERT_DEATH_IF_SUPPORTED(unwrapper.Unwrap(1), ""); +} + +TEST(SeqNumUnwrapper, ForwardWrap) { + SeqNumUnwrapper unwrapper; + EXPECT_EQ(0U, unwrapper.Unwrap(255)); + EXPECT_EQ(1U, unwrapper.Unwrap(0)); +} + +TEST(SeqNumUnwrapper, ForwardWrapWithDivisor) { + SeqNumUnwrapper unwrapper; + EXPECT_EQ(0U, unwrapper.Unwrap(30)); + EXPECT_EQ(6U, unwrapper.Unwrap(3)); +} + +TEST(SeqNumUnwrapper, BackWardWrap) { + SeqNumUnwrapper unwrapper(10); + EXPECT_EQ(10U, unwrapper.Unwrap(0)); + EXPECT_EQ(8U, unwrapper.Unwrap(254)); +} + +TEST(SeqNumUnwrapper, BackWardWrapWithDivisor) { + SeqNumUnwrapper unwrapper(10); + EXPECT_EQ(10U, unwrapper.Unwrap(0)); + EXPECT_EQ(8U, unwrapper.Unwrap(31)); +} + +TEST(SeqNumUnwrapper, Unwrap) { + SeqNumUnwrapper unwrapper; + const uint16_t kMax = std::numeric_limits::max(); + const uint16_t kMaxDist = kMax / 2 + 1; + + EXPECT_EQ(0U, unwrapper.Unwrap(0)); + EXPECT_EQ(kMaxDist, unwrapper.Unwrap(kMaxDist)); + EXPECT_EQ(0U, unwrapper.Unwrap(0)); + + EXPECT_EQ(kMaxDist, unwrapper.Unwrap(kMaxDist)); + EXPECT_EQ(kMax, unwrapper.Unwrap(kMax)); + EXPECT_EQ(kMax + 1U, unwrapper.Unwrap(0)); + EXPECT_EQ(kMax, unwrapper.Unwrap(kMax)); + EXPECT_EQ(kMaxDist, unwrapper.Unwrap(kMaxDist)); + EXPECT_EQ(0U, unwrapper.Unwrap(0)); +} + +TEST(SeqNumUnwrapper, UnwrapOddDivisor) { + SeqNumUnwrapper unwrapper(10); + + EXPECT_EQ(10U, unwrapper.Unwrap(10)); + EXPECT_EQ(11U, unwrapper.Unwrap(0)); + EXPECT_EQ(16U, unwrapper.Unwrap(5)); + EXPECT_EQ(21U, unwrapper.Unwrap(10)); + EXPECT_EQ(22U, unwrapper.Unwrap(0)); + EXPECT_EQ(17U, unwrapper.Unwrap(6)); + EXPECT_EQ(12U, unwrapper.Unwrap(1)); + EXPECT_EQ(7U, unwrapper.Unwrap(7)); + EXPECT_EQ(2U, unwrapper.Unwrap(2)); + EXPECT_EQ(0U, unwrapper.Unwrap(0)); +} + +TEST(SeqNumUnwrapper, ManyForwardWraps) { + const int kLargeNumber = 4711; + const int kMaxStep = kLargeNumber / 2; + const int kNumWraps = 100; + SeqNumUnwrapper unwrapper; + + uint16_t next_unwrap = 0; + uint64_t expected = 0; + for (int i = 0; i < kNumWraps * 2 + 1; ++i) { + EXPECT_EQ(expected, unwrapper.Unwrap(next_unwrap)); + expected += kMaxStep; + next_unwrap = (next_unwrap + kMaxStep) % kLargeNumber; + } +} + +TEST(SeqNumUnwrapper, ManyBackwardWraps) { + const int kLargeNumber = 4711; + const int kMaxStep = kLargeNumber / 2; + const int kNumWraps = 100; + SeqNumUnwrapper unwrapper(kLargeNumber * kNumWraps); + + uint16_t next_unwrap = 0; + uint64_t expected = kLargeNumber * kNumWraps; + for (uint16_t i = 0; i < kNumWraps * 2 + 1; ++i) { + EXPECT_EQ(expected, unwrapper.Unwrap(next_unwrap)); + expected -= kMaxStep; + next_unwrap = (next_unwrap + kMaxStep + 1) % kLargeNumber; + } +} + } // namespace webrtc diff --git a/webrtc/rtc_base/mod_ops.h b/webrtc/rtc_base/mod_ops.h index 211f2e3553..f8cb951e83 100644 --- a/webrtc/rtc_base/mod_ops.h +++ b/webrtc/rtc_base/mod_ops.h @@ -59,8 +59,10 @@ inline unsigned long Subtract(unsigned long a, unsigned long b) { // NOLINT // ################################################# // -->-----> |----->--- // +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. template -inline T ForwardDiff(T a, T b) { +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); @@ -68,13 +70,18 @@ inline T ForwardDiff(T a, T b) { return a <= b ? b - a : M - (a - b); } -template -inline T ForwardDiff(T a, T 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: @@ -97,8 +104,10 @@ inline T ForwardDiff(T a, T b) { // ################################################# // ---<-----| |<-----<-- // +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. template -inline T ReverseDiff(T a, T b) { +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); @@ -106,30 +115,28 @@ inline T ReverseDiff(T a, T b) { return b <= a ? a - b : M - (b - a); } -template -inline T ReverseDiff(T a, T b) { +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 +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)); } -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 // WEBRTC_RTC_BASE_MOD_OPS_H_ diff --git a/webrtc/rtc_base/mod_ops_unittest.cc b/webrtc/rtc_base/mod_ops_unittest.cc index c375305e2d..d39ea061b6 100644 --- a/webrtc/rtc_base/mod_ops_unittest.cc +++ b/webrtc/rtc_base/mod_ops_unittest.cc @@ -87,6 +87,14 @@ TEST_F(TestModOps, ForwardDiff) { } } +TEST_F(TestModOps, ForwardDiffWithDivisor) { + ASSERT_EQ(122, (ForwardDiff(0, 122))); + ASSERT_EQ(0, (ForwardDiff(122, 122))); + ASSERT_EQ(122, (ForwardDiff(1, 0))); + ASSERT_EQ(0, (ForwardDiff(0, 0))); + ASSERT_EQ(1, (ForwardDiff(122, 0))); +} + TEST_F(TestModOps, ReverseDiff) { ASSERT_EQ(0u, ReverseDiff(4711u, 4711u)); @@ -106,6 +114,14 @@ TEST_F(TestModOps, ReverseDiff) { } } +TEST_F(TestModOps, ReverseDiffWithDivisor) { + ASSERT_EQ(1, (ReverseDiff(0, 122))); + ASSERT_EQ(0, (ReverseDiff(122, 122))); + ASSERT_EQ(1, (ReverseDiff(1, 0))); + ASSERT_EQ(0, (ReverseDiff(0, 0))); + ASSERT_EQ(122, (ReverseDiff(122, 0))); +} + TEST_F(TestModOps, MinDiff) { for (uint16_t i = 0; i < 256; ++i) { ASSERT_EQ(0, MinDiff(i, i));