diff --git a/webrtc/modules/include/module_common_types.h b/webrtc/modules/include/module_common_types.h index ffa07980a0..ba1c6a7f1d 100644 --- a/webrtc/modules/include/module_common_types.h +++ b/webrtc/modules/include/module_common_types.h @@ -20,11 +20,12 @@ #include "webrtc/api/video/video_rotation.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/deprecation.h" +#include "webrtc/base/optional.h" #include "webrtc/base/safe_conversions.h" #include "webrtc/common_types.h" +#include "webrtc/modules/video_coding/codecs/h264/include/h264_globals.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_globals.h" #include "webrtc/modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "webrtc/modules/video_coding/codecs/h264/include/h264_globals.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -476,28 +477,30 @@ inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) { return *this; } +template +inline bool IsNewer(U value, U prev_value) { + static_assert(!std::numeric_limits::is_signed, "U must be unsigned"); + // kBreakpoint is the half-way mark for the type U. For instance, for a + // uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000. + constexpr U kBreakpoint = (std::numeric_limits::max() >> 1) + 1; + // Distinguish between elements that are exactly kBreakpoint apart. + // If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true, + // IsNewer(t2,t1)=false + // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. + if (value - prev_value == kBreakpoint) { + return value > prev_value; + } + return value != prev_value && + static_cast(value - prev_value) < kBreakpoint; +} + inline bool IsNewerSequenceNumber(uint16_t sequence_number, uint16_t prev_sequence_number) { - // Distinguish between elements that are exactly 0x8000 apart. - // If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false - // rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false. - if (static_cast(sequence_number - prev_sequence_number) == 0x8000) { - return sequence_number > prev_sequence_number; - } - return sequence_number != prev_sequence_number && - static_cast(sequence_number - prev_sequence_number) < 0x8000; + return IsNewer(sequence_number, prev_sequence_number); } inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) { - // Distinguish between elements that are exactly 0x80000000 apart. - // If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true, - // IsNewer(t2,t1)=false - // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. - if (static_cast(timestamp - prev_timestamp) == 0x80000000) { - return timestamp > prev_timestamp; - } - return timestamp != prev_timestamp && - static_cast(timestamp - prev_timestamp) < 0x80000000; + return IsNewer(timestamp, prev_timestamp); } inline uint16_t LatestSequenceNumber(uint16_t sequence_number1, @@ -511,46 +514,57 @@ inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) { return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2; } -// Utility class to unwrap a sequence number to a larger type, for easier -// handling large ranges. Note that sequence numbers will never be unwrapped -// to a negative value. -class SequenceNumberUnwrapper { +// Utility class to unwrap a number to a larger type. The numbers will never be +// unwrapped to a negative value. +template +class Unwrapper { + static_assert(!std::numeric_limits::is_signed, "U must be unsigned"); + static_assert(std::numeric_limits::max() <= + std::numeric_limits::max(), + "U must not be wider than 32 bits"); + public: - SequenceNumberUnwrapper() : last_seq_(-1) {} + // Get the unwrapped value, but don't update the internal state. + int64_t UnwrapWithoutUpdate(U value) { + if (!last_value_) + return value; - // Get the unwrapped sequence, but don't update the internal state. - int64_t UnwrapWithoutUpdate(uint16_t sequence_number) { - if (last_seq_ == -1) - return sequence_number; + constexpr int64_t kMaxPlusOne = + static_cast(std::numeric_limits::max()) + 1; - uint16_t cropped_last = static_cast(last_seq_); - int64_t delta = sequence_number - cropped_last; - if (IsNewerSequenceNumber(sequence_number, cropped_last)) { + U cropped_last = static_cast(*last_value_); + int64_t delta = value - cropped_last; + if (IsNewer(value, cropped_last)) { if (delta < 0) - delta += (1 << 16); // Wrap forwards. - } else if (delta > 0 && (last_seq_ + delta - (1 << 16)) >= 0) { - // If sequence_number is older but delta is positive, this is a backwards + delta += kMaxPlusOne; // Wrap forwards. + } else if (delta > 0 && (*last_value_ + delta - kMaxPlusOne) >= 0) { + // If value is older but delta is positive, this is a backwards // wrap-around. However, don't wrap backwards past 0 (unwrapped). - delta -= (1 << 16); + delta -= kMaxPlusOne; } - return last_seq_ + delta; + return *last_value_ + delta; } - // Only update the internal state to the specified last (unwrapped) sequence. - void UpdateLast(int64_t last_sequence) { last_seq_ = last_sequence; } + // Only update the internal state to the specified last (unwrapped) value. + void UpdateLast(int64_t last_value) { + last_value_ = rtc::Optional(last_value); + } - // Unwrap the sequence number and update the internal state. - int64_t Unwrap(uint16_t sequence_number) { - int64_t unwrapped = UnwrapWithoutUpdate(sequence_number); + // Unwrap the value and update the internal state. + int64_t Unwrap(U value) { + int64_t unwrapped = UnwrapWithoutUpdate(value); UpdateLast(unwrapped); return unwrapped; } private: - int64_t last_seq_; + rtc::Optional last_value_; }; +using SequenceNumberUnwrapper = Unwrapper; +using TimestampUnwrapper = Unwrapper; + struct PacedPacketInfo { PacedPacketInfo() {} PacedPacketInfo(int probe_cluster_id, diff --git a/webrtc/modules/module_common_types_unittest.cc b/webrtc/modules/module_common_types_unittest.cc index 159e26babe..e4d5033b88 100644 --- a/webrtc/modules/module_common_types_unittest.cc +++ b/webrtc/modules/module_common_types_unittest.cc @@ -119,11 +119,11 @@ TEST(SequenceNumberUnwrapper, Limits) { EXPECT_EQ(0, unwrapper.Unwrap(0)); EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000)); // Delta is exactly 0x8000 but current is lower than input, wrap backwards. - EXPECT_EQ(0x0, unwrapper.Unwrap(0x0000)); + EXPECT_EQ(0, unwrapper.Unwrap(0)); EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000)); EXPECT_EQ(0xFFFF, unwrapper.Unwrap(0xFFFF)); - EXPECT_EQ(0x10000, unwrapper.Unwrap(0x0000)); + EXPECT_EQ(0x10000, unwrapper.Unwrap(0)); EXPECT_EQ(0xFFFF, unwrapper.Unwrap(0xFFFF)); EXPECT_EQ(0x8000, unwrapper.Unwrap(0x8000)); EXPECT_EQ(0, unwrapper.Unwrap(0)); @@ -172,4 +172,53 @@ TEST(SequenceNumberUnwrapper, BackwardWraps) { } } +TEST(TimestampUnwrapper, Limits) { + TimestampUnwrapper unwrapper; + + EXPECT_EQ(0, unwrapper.Unwrap(0)); + EXPECT_EQ(0x80000000, unwrapper.Unwrap(0x80000000)); + // Delta is exactly 0x80000000 but current is lower than input, wrap + // backwards. + EXPECT_EQ(0, unwrapper.Unwrap(0)); + + EXPECT_EQ(0x80000000, unwrapper.Unwrap(0x80000000)); + EXPECT_EQ(0xFFFFFFFF, unwrapper.Unwrap(0xFFFFFFFF)); + EXPECT_EQ(0x100000000, unwrapper.Unwrap(0x00000000)); + EXPECT_EQ(0xFFFFFFFF, unwrapper.Unwrap(0xFFFFFFFF)); + EXPECT_EQ(0x80000000, unwrapper.Unwrap(0x80000000)); + EXPECT_EQ(0, unwrapper.Unwrap(0)); + + // Don't allow negative values. + EXPECT_EQ(0xFFFFFFFF, unwrapper.Unwrap(0xFFFFFFFF)); +} + +TEST(TimestampUnwrapper, ForwardWraps) { + int64_t ts = 0; + TimestampUnwrapper unwrapper; + + const int64_t kMaxIncrease = 0x80000000 - 1; + const int kNumWraps = 4; + for (int i = 0; i < kNumWraps * 2; ++i) { + int64_t unwrapped = + unwrapper.Unwrap(static_cast(ts & 0xFFFFFFFF)); + EXPECT_EQ(ts, unwrapped); + ts += kMaxIncrease; + } +} + +TEST(TimestampUnwrapper, BackwardWraps) { + TimestampUnwrapper unwrapper; + + const int64_t kMaxDecrease = 0x80000000 - 1; + const int kNumWraps = 4; + int64_t ts = kNumWraps * 2 * kMaxDecrease; + unwrapper.UpdateLast(ts); + for (int i = 0; i <= kNumWraps * 2; ++i) { + int64_t unwrapped = + unwrapper.Unwrap(static_cast(ts & 0xFFFFFFFF)); + EXPECT_EQ(ts, unwrapped); + ts -= kMaxDecrease; + } +} + } // namespace webrtc