diff --git a/webrtc/base/mod_ops.h b/webrtc/base/mod_ops.h index cf65fdd4df..74bd905fd5 100644 --- a/webrtc/base/mod_ops.h +++ b/webrtc/base/mod_ops.h @@ -113,26 +113,21 @@ inline T ReverseDiff(T a, T b) { return a - b; } -// Test if the sequence number a is ahead or at sequence number b. -// If the two sequence numbers are at max distance from each other -// then the sequence number with highest value is considered to -// be ahead. -template -inline bool AheadOrAt(T a, T 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."); - const T maxDist = std::numeric_limits::max() / 2 + T(1); - if (a - b == maxDist) - return b < a; - return ForwardDiff(b, a) < maxDist; + return std::min(ForwardDiff(a, b), ReverseDiff(a, b)); } -// Test if sequence number a is ahead of sequence number b. template -inline bool AheadOf(T a, T b) { +inline T MinDiff(T a, T b) { static_assert(std::is_unsigned::value, "Type must be an unsigned integer."); - return a != b && AheadOrAt(a, b); + return std::min(ForwardDiff(a, b), ReverseDiff(a, b)); } } // namespace webrtc diff --git a/webrtc/base/mod_ops_unittest.cc b/webrtc/base/mod_ops_unittest.cc index 76484bc16b..df59c1313b 100644 --- a/webrtc/base/mod_ops_unittest.cc +++ b/webrtc/base/mod_ops_unittest.cc @@ -36,7 +36,6 @@ TEST_F(TestModOps, Add) { } TEST_F(TestModOps, AddLarge) { - // NOLINTNEXTLINE const unsigned long D = ulmax - 10ul; // NOLINT unsigned long l = D - 1ul; // NOLINT ASSERT_EQ(D - 2ul, Add(l, l)); @@ -107,96 +106,35 @@ TEST_F(TestModOps, ReverseDiff) { } } -TEST_F(TestModOps, AheadOrAt) { - uint8_t x = 0; - uint8_t y = 0; - ASSERT_TRUE(AheadOrAt(x, y)); - ++x; - ASSERT_TRUE(AheadOrAt(x, y)); - ASSERT_FALSE(AheadOrAt(y, x)); - for (int i = 0; i < 256; ++i) { - ASSERT_TRUE(AheadOrAt(x, y)); - ++x; - ++y; +TEST_F(TestModOps, MinDiff) { + for (uint16_t i = 0; i < 256; ++i) { + ASSERT_EQ(0, MinDiff(i, i)); + ASSERT_EQ(1, MinDiff(i - 1, i)); + ASSERT_EQ(1, MinDiff(i + 1, i)); } - x = 128; - y = 0; - ASSERT_TRUE(AheadOrAt(x, y)); - ASSERT_FALSE(AheadOrAt(y, x)); + for (uint8_t i = 0; i < 128; ++i) + ASSERT_EQ(i, MinDiff(0, i)); - x = 129; - ASSERT_FALSE(AheadOrAt(x, y)); - ASSERT_TRUE(AheadOrAt(y, x)); - ASSERT_TRUE(AheadOrAt(x, y)); - ASSERT_FALSE(AheadOrAt(y, x)); + for (uint8_t i = 0; i < 128; ++i) + ASSERT_EQ(128 - i, MinDiff(0, 128 + i)); } -TEST_F(TestModOps, AheadOf) { - uint8_t x = 0; - uint8_t y = 0; - ASSERT_FALSE(AheadOf(x, y)); - ++x; - ASSERT_TRUE(AheadOf(x, y)); - ASSERT_FALSE(AheadOf(y, x)); - for (int i = 0; i < 256; ++i) { - ASSERT_TRUE(AheadOf(x, y)); - ++x; - ++y; - } +TEST_F(TestModOps, MinDiffWitDivisor) { + ASSERT_EQ(5u, (MinDiff(0, 5))); + ASSERT_EQ(5u, (MinDiff(0, 6))); + ASSERT_EQ(5u, (MinDiff(5, 0))); + ASSERT_EQ(5u, (MinDiff(6, 0))); - x = 128; - y = 0; - for (int i = 0; i < 128; ++i) { - ASSERT_TRUE(AheadOf(x, y)); - ASSERT_FALSE(AheadOf(y, x)); - x++; - y++; - } + const uint16_t D = 4711; - for (int i = 0; i < 128; ++i) { - ASSERT_FALSE(AheadOf(x, y)); - ASSERT_TRUE(AheadOf(y, x)); - x++; - y++; - } + for (uint16_t i = 0; i < D / 2; ++i) + ASSERT_EQ(i, (MinDiff(0, i))); - x = 129; - y = 0; - ASSERT_FALSE(AheadOf(x, y)); - ASSERT_TRUE(AheadOf(y, x)); - ASSERT_TRUE(AheadOf(x, y)); - ASSERT_FALSE(AheadOf(y, x)); -} + ASSERT_EQ(D / 2, (MinDiff(0, D / 2))); -TEST_F(TestModOps, ForwardDiffWithDivisor) { - const uint8_t kDivisor = 211; - - for (uint8_t i = 0; i < kDivisor - 1; ++i) { - ASSERT_EQ(0, (ForwardDiff(i, i))); - ASSERT_EQ(1, (ForwardDiff(i, i + 1))); - ASSERT_EQ(kDivisor - 1, (ForwardDiff(i + 1, i))); - } - - for (uint8_t i = 1; i < kDivisor; ++i) { - ASSERT_EQ(i, (ForwardDiff(0, i))); - ASSERT_EQ(kDivisor - i, (ForwardDiff(i, 0))); - } -} - -TEST_F(TestModOps, ReverseDiffWithDivisor) { - const uint8_t kDivisor = 241; - - for (uint8_t i = 0; i < kDivisor - 1; ++i) { - ASSERT_EQ(0, (ReverseDiff(i, i))); - ASSERT_EQ(kDivisor - 1, (ReverseDiff(i, i + 1))); - ASSERT_EQ(1, (ReverseDiff(i + 1, i))); - } - - for (uint8_t i = 1; i < kDivisor; ++i) { - ASSERT_EQ(kDivisor - i, (ReverseDiff(0, i))); - ASSERT_EQ(i, (ReverseDiff(i, 0))); - } + for (uint16_t i = 0; i < D / 2; ++i) + ASSERT_EQ(D / 2 - i, (MinDiff(0, D / 2 - i))); } } // namespace webrtc diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index aea7916401..25a9b2934d 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -371,6 +371,7 @@ 'video_coding/nack_module_unittest.cc', 'video_coding/receiver_unittest.cc', 'video_coding/session_info_unittest.cc', + 'video_coding/sequence_number_util_unittest.cc', 'video_coding/timing_unittest.cc', 'video_coding/video_coding_robustness_unittest.cc', 'video_coding/video_receiver_unittest.cc', diff --git a/webrtc/modules/video_coding/histogram.cc b/webrtc/modules/video_coding/histogram.cc index e07d50ba6f..f2aa6eabb6 100644 --- a/webrtc/modules/video_coding/histogram.cc +++ b/webrtc/modules/video_coding/histogram.cc @@ -12,7 +12,7 @@ #include -#include "webrtc/base/mod_ops.h" +#include "webrtc/modules/video_coding/sequence_number_util.h" namespace webrtc { namespace video_coding { diff --git a/webrtc/modules/video_coding/nack_module.cc b/webrtc/modules/video_coding/nack_module.cc index 80f84968eb..1b12afe0f0 100644 --- a/webrtc/modules/video_coding/nack_module.cc +++ b/webrtc/modules/video_coding/nack_module.cc @@ -15,7 +15,6 @@ #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" -#include "webrtc/base/mod_ops.h" #include "webrtc/modules/utility/include/process_thread.h" namespace webrtc { diff --git a/webrtc/modules/video_coding/nack_module.h b/webrtc/modules/video_coding/nack_module.h index 8e0a79b2cb..7163a8e905 100644 --- a/webrtc/modules/video_coding/nack_module.h +++ b/webrtc/modules/video_coding/nack_module.h @@ -16,12 +16,12 @@ #include #include "webrtc/base/criticalsection.h" -#include "webrtc/base/mod_ops.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/modules/include/module.h" #include "webrtc/modules/video_coding/include/video_coding_defines.h" #include "webrtc/modules/video_coding/packet.h" #include "webrtc/modules/video_coding/histogram.h" +#include "webrtc/modules/video_coding/sequence_number_util.h" #include "webrtc/system_wrappers/include/clock.h" namespace webrtc { diff --git a/webrtc/modules/video_coding/nack_module_unittest.cc b/webrtc/modules/video_coding/nack_module_unittest.cc index fab03ce332..3870742016 100644 --- a/webrtc/modules/video_coding/nack_module_unittest.cc +++ b/webrtc/modules/video_coding/nack_module_unittest.cc @@ -15,7 +15,6 @@ #include "webrtc/modules/video_coding/include/video_coding_defines.h" #include "webrtc/modules/video_coding/nack_module.h" #include "webrtc/system_wrappers/include/clock.h" -#include "webrtc/base/mod_ops.h" namespace webrtc { class TestNackModule : public ::testing::Test, diff --git a/webrtc/modules/video_coding/sequence_number_util.h b/webrtc/modules/video_coding/sequence_number_util.h new file mode 100644 index 0000000000..828e68a768 --- /dev/null +++ b/webrtc/modules/video_coding/sequence_number_util.h @@ -0,0 +1,110 @@ +/* + * 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 WEBRTC_MODULES_VIDEO_CODING_SEQUENCE_NUMBER_UTIL_H_ +#define WEBRTC_MODULES_VIDEO_CODING_SEQUENCE_NUMBER_UTIL_H_ + +#include +#include + +#include "webrtc/base/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 bool 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 bool 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; +} + +// 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); +} + +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> { + 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); + } +}; + +// 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 + : private internal::SeqNumComp> { + bool operator()(T a, T b) const { + return internal::SeqNumComp>::operator()(b, + a); + } +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_SEQUENCE_NUMBER_UTIL_H_ diff --git a/webrtc/modules/video_coding/sequence_number_util_unittest.cc b/webrtc/modules/video_coding/sequence_number_util_unittest.cc new file mode 100644 index 0000000000..8028efde2f --- /dev/null +++ b/webrtc/modules/video_coding/sequence_number_util_unittest.cc @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/video_coding/sequence_number_util.h" + +namespace webrtc { +class TestSeqNumUtil : public ::testing::Test { + protected: + // Can't use std::numeric_limits::max() since + // MSVC doesn't support constexpr. + static const unsigned long ulmax = ~0ul; // NOLINT +}; + +TEST_F(TestSeqNumUtil, AheadOrAt) { + uint8_t x = 0; + uint8_t y = 0; + ASSERT_TRUE(AheadOrAt(x, y)); + ++x; + ASSERT_TRUE(AheadOrAt(x, y)); + ASSERT_FALSE(AheadOrAt(y, x)); + for (int i = 0; i < 256; ++i) { + ASSERT_TRUE(AheadOrAt(x, y)); + ++x; + ++y; + } + + x = 128; + y = 0; + ASSERT_TRUE(AheadOrAt(x, y)); + ASSERT_FALSE(AheadOrAt(y, x)); + + x = 129; + ASSERT_FALSE(AheadOrAt(x, y)); + ASSERT_TRUE(AheadOrAt(y, x)); + ASSERT_TRUE(AheadOrAt(x, y)); + ASSERT_FALSE(AheadOrAt(y, x)); +} + +TEST_F(TestSeqNumUtil, AheadOrAtWithDivisor) { + ASSERT_TRUE((AheadOrAt(5, 0))); + ASSERT_FALSE((AheadOrAt(6, 0))); + ASSERT_FALSE((AheadOrAt(0, 5))); + ASSERT_TRUE((AheadOrAt(0, 6))); + + ASSERT_TRUE((AheadOrAt(5, 0))); + ASSERT_FALSE((AheadOrAt(6, 0))); + ASSERT_FALSE((AheadOrAt(0, 5))); + ASSERT_TRUE((AheadOrAt(0, 6))); + + const uint8_t D = 211; + uint8_t x = 0; + for (int i = 0; i < D; ++i) { + uint8_t next_x = Add(x, 1); + ASSERT_TRUE((AheadOrAt(i, i))); + ASSERT_TRUE((AheadOrAt(next_x, i))); + ASSERT_FALSE((AheadOrAt(i, next_x))); + x = next_x; + } +} + +TEST_F(TestSeqNumUtil, AheadOf) { + uint8_t x = 0; + uint8_t y = 0; + ASSERT_FALSE(AheadOf(x, y)); + ++x; + ASSERT_TRUE(AheadOf(x, y)); + ASSERT_FALSE(AheadOf(y, x)); + for (int i = 0; i < 256; ++i) { + ASSERT_TRUE(AheadOf(x, y)); + ++x; + ++y; + } + + x = 128; + y = 0; + for (int i = 0; i < 128; ++i) { + ASSERT_TRUE(AheadOf(x, y)); + ASSERT_FALSE(AheadOf(y, x)); + x++; + y++; + } + + for (int i = 0; i < 128; ++i) { + ASSERT_FALSE(AheadOf(x, y)); + ASSERT_TRUE(AheadOf(y, x)); + x++; + y++; + } + + x = 129; + y = 0; + ASSERT_FALSE(AheadOf(x, y)); + ASSERT_TRUE(AheadOf(y, x)); + ASSERT_TRUE(AheadOf(x, y)); + ASSERT_FALSE(AheadOf(y, x)); +} + +TEST_F(TestSeqNumUtil, AheadOfWithDivisor) { + ASSERT_TRUE((AheadOf(5, 0))); + ASSERT_FALSE((AheadOf(6, 0))); + ASSERT_FALSE((AheadOf(0, 5))); + ASSERT_TRUE((AheadOf(0, 6))); + + ASSERT_TRUE((AheadOf(5, 0))); + ASSERT_FALSE((AheadOf(6, 0))); + ASSERT_FALSE((AheadOf(0, 5))); + ASSERT_TRUE((AheadOf(0, 6))); + + const uint8_t D = 211; + uint8_t x = 0; + for (int i = 0; i < D; ++i) { + uint8_t next_x = Add(x, 1); + ASSERT_FALSE((AheadOf(i, i))); + ASSERT_TRUE((AheadOf(next_x, i))); + ASSERT_FALSE((AheadOf(i, next_x))); + x = next_x; + } +} + +TEST_F(TestSeqNumUtil, ForwardDiffWithDivisor) { + const uint8_t kDivisor = 211; + + for (uint8_t i = 0; i < kDivisor - 1; ++i) { + ASSERT_EQ(0, (ForwardDiff(i, i))); + ASSERT_EQ(1, (ForwardDiff(i, i + 1))); + ASSERT_EQ(kDivisor - 1, (ForwardDiff(i + 1, i))); + } + + for (uint8_t i = 1; i < kDivisor; ++i) { + ASSERT_EQ(i, (ForwardDiff(0, i))); + ASSERT_EQ(kDivisor - i, (ForwardDiff(i, 0))); + } +} + +TEST_F(TestSeqNumUtil, ReverseDiffWithDivisor) { + const uint8_t kDivisor = 241; + + for (uint8_t i = 0; i < kDivisor - 1; ++i) { + ASSERT_EQ(0, (ReverseDiff(i, i))); + ASSERT_EQ(kDivisor - 1, (ReverseDiff(i, i + 1))); + ASSERT_EQ(1, (ReverseDiff(i + 1, i))); + } + + for (uint8_t i = 1; i < kDivisor; ++i) { + ASSERT_EQ(kDivisor - i, (ReverseDiff(0, i))); + ASSERT_EQ(i, (ReverseDiff(i, 0))); + } +} + +TEST_F(TestSeqNumUtil, SeqNumComparator) { + std::set> seq_nums_asc; + std::set> seq_nums_desc; + + uint8_t x = 0; + for (int i = 0; i < 128; ++i) { + seq_nums_asc.insert(x); + seq_nums_desc.insert(x); + ASSERT_EQ(x, *seq_nums_asc.begin()); + ASSERT_EQ(x, *seq_nums_desc.rbegin()); + ++x; + } + + seq_nums_asc.clear(); + seq_nums_desc.clear(); + x = 199; + for (int i = 0; i < 128; ++i) { + seq_nums_asc.insert(x); + seq_nums_desc.insert(x); + ASSERT_EQ(x, *seq_nums_asc.begin()); + ASSERT_EQ(x, *seq_nums_desc.rbegin()); + ++x; + } +} + +TEST_F(TestSeqNumUtil, SeqNumComparatorWithDivisor) { + const uint8_t D = 223; + + std::set> seq_nums_asc; + std::set> seq_nums_desc; + + uint8_t x = 0; + for (int i = 0; i < D / 2; ++i) { + seq_nums_asc.insert(x); + seq_nums_desc.insert(x); + ASSERT_EQ(x, *seq_nums_asc.begin()); + ASSERT_EQ(x, *seq_nums_desc.rbegin()); + x = Add(x, 1); + } + + seq_nums_asc.clear(); + seq_nums_desc.clear(); + x = 200; + for (int i = 0; i < D / 2; ++i) { + seq_nums_asc.insert(x); + seq_nums_desc.insert(x); + ASSERT_EQ(x, *seq_nums_asc.begin()); + ASSERT_EQ(x, *seq_nums_desc.rbegin()); + x = Add(x, 1); + } +} + +} // namespace webrtc