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:
henrik.lundin 2017-04-23 23:54:13 -07:00 committed by Commit bot
parent 25ab4b2a58
commit 06863c9ce5
2 changed files with 106 additions and 43 deletions

View File

@ -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,

View File

@ -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