Add TimestampUnwrapper and generalize the code
BUG=webrtc:7467 CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.chromium.mac:mac_chromium_rel_ng,ios-device;master.tryserver.chromium.win:win_chromium_rel_ng;master.tryserver.chromium.android:android_compile_dbg,linux_android_rel_ng Review-Url: https://codereview.webrtc.org/2813593003 Cr-Commit-Position: refs/heads/master@{#17833}
This commit is contained in:
parent
25ab4b2a58
commit
06863c9ce5
@ -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 <typename U>
|
||||
inline bool IsNewer(U value, U prev_value) {
|
||||
static_assert(!std::numeric_limits<U>::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<U>::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<U>(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<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
|
||||
return sequence_number > prev_sequence_number;
|
||||
}
|
||||
return sequence_number != prev_sequence_number &&
|
||||
static_cast<uint16_t>(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<uint32_t>(timestamp - prev_timestamp) == 0x80000000) {
|
||||
return timestamp > prev_timestamp;
|
||||
}
|
||||
return timestamp != prev_timestamp &&
|
||||
static_cast<uint32_t>(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 <typename U>
|
||||
class Unwrapper {
|
||||
static_assert(!std::numeric_limits<U>::is_signed, "U must be unsigned");
|
||||
static_assert(std::numeric_limits<U>::max() <=
|
||||
std::numeric_limits<uint32_t>::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<int64_t>(std::numeric_limits<U>::max()) + 1;
|
||||
|
||||
uint16_t cropped_last = static_cast<uint16_t>(last_seq_);
|
||||
int64_t delta = sequence_number - cropped_last;
|
||||
if (IsNewerSequenceNumber(sequence_number, cropped_last)) {
|
||||
U cropped_last = static_cast<U>(*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<int64_t>(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<int64_t> last_value_;
|
||||
};
|
||||
|
||||
using SequenceNumberUnwrapper = Unwrapper<uint16_t>;
|
||||
using TimestampUnwrapper = Unwrapper<uint32_t>;
|
||||
|
||||
struct PacedPacketInfo {
|
||||
PacedPacketInfo() {}
|
||||
PacedPacketInfo(int probe_cluster_id,
|
||||
|
||||
@ -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<uint32_t>(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<uint32_t>(ts & 0xFFFFFFFF));
|
||||
EXPECT_EQ(ts, unwrapped);
|
||||
ts -= kMaxDecrease;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user