Refactor rtcp::TransportFeedback to use Timestamp and TimeDelta internally
Bug: webrtc:13757
Change-Id: I9815e54288a064c6c8ff40f130b52786b4e398b7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264559
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37149}
diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
index 3059778..719fbf6 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
@@ -42,10 +42,8 @@
// * 8 bytes FeedbackPacket header.
// * 2 bytes for one chunk.
constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
-constexpr int kBaseScaleFactor =
- TransportFeedback::kDeltaScaleFactor * (1 << 8);
-constexpr int64_t kTimeWrapPeriodUs = (1ll << 24) * kBaseScaleFactor;
-constexpr TimeDelta kTimeWrapPeriod = TimeDelta::Micros(kTimeWrapPeriodUs);
+constexpr TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
+constexpr TimeDelta kTimeWrapPeriod = kBaseTimeTick * (1 << 24);
// Message format
//
@@ -274,7 +272,7 @@
base_time_ticks_(0),
feedback_seq_(0),
include_timestamps_(include_timestamps),
- last_timestamp_us_(0),
+ last_timestamp_(Timestamp::Zero()),
size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
TransportFeedback::TransportFeedback(const TransportFeedback&) = default;
@@ -286,7 +284,7 @@
base_time_ticks_(other.base_time_ticks_),
feedback_seq_(other.feedback_seq_),
include_timestamps_(other.include_timestamps_),
- last_timestamp_us_(other.last_timestamp_us_),
+ last_timestamp_(other.last_timestamp_),
received_packets_(std::move(other.received_packets_)),
all_packets_(std::move(other.all_packets_)),
encoded_chunks_(std::move(other.encoded_chunks_)),
@@ -298,12 +296,12 @@
TransportFeedback::~TransportFeedback() {}
void TransportFeedback::SetBase(uint16_t base_sequence,
- int64_t ref_timestamp_us) {
+ Timestamp ref_timestamp) {
RTC_DCHECK_EQ(num_seq_no_, 0);
- RTC_DCHECK_GE(ref_timestamp_us, 0);
base_seq_no_ = base_sequence;
- base_time_ticks_ = (ref_timestamp_us % kTimeWrapPeriodUs) / kBaseScaleFactor;
- last_timestamp_us_ = GetBaseTimeUs();
+ base_time_ticks_ =
+ (ref_timestamp.us() % kTimeWrapPeriod.us()) / kBaseTimeTick.us();
+ last_timestamp_ = BaseTime();
}
void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) {
@@ -311,19 +309,25 @@
}
bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
- int64_t timestamp_us) {
+ Timestamp timestamp) {
// Set delta to zero if timestamps are not included, this will simplify the
// encoding process.
int16_t delta = 0;
if (include_timestamps_) {
// Convert to ticks and round.
+ if (last_timestamp_ > timestamp) {
+ timestamp += (last_timestamp_ - timestamp).RoundUpTo(kTimeWrapPeriod);
+ }
+ RTC_DCHECK_GE(timestamp, last_timestamp_);
int64_t delta_full =
- (timestamp_us - last_timestamp_us_) % kTimeWrapPeriodUs;
- if (delta_full > kTimeWrapPeriodUs / 2)
- delta_full -= kTimeWrapPeriodUs;
- delta_full +=
- delta_full < 0 ? -(kDeltaScaleFactor / 2) : kDeltaScaleFactor / 2;
- delta_full /= kDeltaScaleFactor;
+ (timestamp - last_timestamp_).us() % kTimeWrapPeriod.us();
+ if (delta_full > kTimeWrapPeriod.us() / 2) {
+ delta_full -= kTimeWrapPeriod.us();
+ delta_full -= kDeltaTick.us() / 2;
+ } else {
+ delta_full += kDeltaTick.us() / 2;
+ }
+ delta_full /= kDeltaTick.us();
delta = static_cast<int16_t>(delta_full);
// If larger than 16bit signed, we can't represent it - need new fb packet.
@@ -353,7 +357,7 @@
received_packets_.emplace_back(sequence_number, delta);
if (include_lost_)
all_packets_.emplace_back(sequence_number, delta);
- last_timestamp_us_ += delta * kDeltaScaleFactor;
+ last_timestamp_ += delta * kDeltaTick;
if (include_timestamps_) {
size_bytes_ += delta_size;
}
@@ -376,17 +380,20 @@
}
Timestamp TransportFeedback::BaseTime() const {
- return Timestamp::Zero() +
- TimeDelta::Micros(int64_t{base_time_ticks_} * kBaseScaleFactor);
+ // Add an extra kTimeWrapPeriod to allow add received packets arrived earlier
+ // than the first added packet (and thus allow to record negative deltas)
+ // even when base_time_ticks_ == 0.
+ return Timestamp::Zero() + kTimeWrapPeriod +
+ int64_t{base_time_ticks_} * kBaseTimeTick;
}
int64_t TransportFeedback::GetBaseTimeUs() const {
// Historically BaseTime was stored as signed integer and could be negative.
// However with new api it is not possible, but for compatibility with legacy
// tests return base time as negative when it used to be negative.
- int64_t base_time_us = BaseTime().us() % kTimeWrapPeriodUs;
- if (base_time_us >= kTimeWrapPeriodUs / 2) {
- return base_time_us - kTimeWrapPeriodUs;
+ int64_t base_time_us = BaseTime().us() % kTimeWrapPeriod.us();
+ if (base_time_us >= kTimeWrapPeriod.us() / 2) {
+ return base_time_us - kTimeWrapPeriod.us();
} else {
return base_time_us;
}
@@ -483,7 +490,7 @@
received_packets_.emplace_back(seq_no, delta);
if (include_lost_)
all_packets_.emplace_back(seq_no, delta);
- last_timestamp_us_ += delta * kDeltaScaleFactor;
+ last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
@@ -492,7 +499,7 @@
received_packets_.emplace_back(seq_no, delta);
if (include_lost_)
all_packets_.emplace_back(seq_no, delta);
- last_timestamp_us_ += delta * kDeltaScaleFactor;
+ last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
@@ -562,7 +569,7 @@
<< num_seq_no_;
return false;
}
- int64_t timestamp_us = GetBaseTimeUs();
+ Timestamp timestamp = BaseTime();
auto packet_it = received_packets_.begin();
uint16_t seq_no = base_seq_no_;
for (DeltaSize delta_size : delta_sizes) {
@@ -584,7 +591,7 @@
<< " doesn't fit into one byte";
return false;
}
- timestamp_us += packet_it->delta_us();
+ timestamp += packet_it->delta();
++packet_it;
}
if (include_timestamps_) {
@@ -597,9 +604,10 @@
<< packet_it->sequence_number();
return false;
}
- if (timestamp_us != last_timestamp_us_) {
- RTC_LOG(LS_ERROR) << "Last timestamp mismatch. Calculated: " << timestamp_us
- << ". Saved: " << last_timestamp_us_;
+ if (timestamp != last_timestamp_) {
+ RTC_LOG(LS_ERROR) << "Last timestamp mismatch. Calculated: "
+ << ToLogString(timestamp)
+ << ". Saved: " << ToLogString(last_timestamp_);
return false;
}
if (size_bytes_ != packet_size) {
@@ -684,7 +692,7 @@
void TransportFeedback::Clear() {
num_seq_no_ = 0;
- last_timestamp_us_ = GetBaseTimeUs();
+ last_timestamp_ = BaseTime();
received_packets_.clear();
all_packets_.clear();
encoded_chunks_.clear();
diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
index 2163374..8c92708 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
@@ -41,8 +41,8 @@
uint16_t sequence_number() const { return sequence_number_; }
int16_t delta_ticks() const { return delta_ticks_; }
// ABSL_DEPRECATED("Use delta() that returns TimeDelta")
- int32_t delta_us() const { return delta_ticks_ * kDeltaScaleFactor; }
- TimeDelta delta() const { return TimeDelta::Micros(delta_us()); }
+ int32_t delta_us() const { return delta().us(); }
+ TimeDelta delta() const { return delta_ticks_ * kDeltaTick; }
bool received() const { return received_; }
private:
@@ -71,20 +71,20 @@
~TransportFeedback() override;
// ABSL_DEPRECATED("Use version that takes Timestamp")
- void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
- int64_t ref_timestamp_us); // Reference timestamp for this msg.
- void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
- Timestamp ref_timestamp) { // Reference timestamp for this msg.
- SetBase(base_sequence, ref_timestamp.us());
+ void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
+ int64_t ref_timestamp_us) { // Reference timestamp for this msg.
+ SetBase(base_sequence, Timestamp::Micros(ref_timestamp_us));
}
+ void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
+ Timestamp ref_timestamp); // Reference timestamp for this msg.
void SetFeedbackSequenceNumber(uint8_t feedback_sequence);
// NOTE: This method requires increasing sequence numbers (excepting wraps).
// ABSL_DEPRECATED("Use version that takes Timestamp")
- bool AddReceivedPacket(uint16_t sequence_number, int64_t timestamp_us);
- bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp) {
- return AddReceivedPacket(sequence_number, timestamp.us());
+ bool AddReceivedPacket(uint16_t sequence_number, int64_t timestamp_us) {
+ return AddReceivedPacket(sequence_number, Timestamp::Micros(timestamp_us));
}
+ bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp);
const std::vector<ReceivedPacket>& GetReceivedPackets() const;
const std::vector<ReceivedPacket>& GetAllPackets() const;
@@ -189,7 +189,7 @@
uint8_t feedback_seq_;
bool include_timestamps_;
- int64_t last_timestamp_us_;
+ Timestamp last_timestamp_;
std::vector<ReceivedPacket> received_packets_;
std::vector<ReceivedPacket> all_packets_;
// All but last encoded packet chunks.
diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
index 6003df4..b3b1175 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
@@ -14,6 +14,9 @@
#include <memory>
#include <utility>
+#include "api/array_view.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "test/gmock.h"
@@ -23,74 +26,103 @@
namespace {
using rtcp::TransportFeedback;
+using ::testing::AllOf;
+using ::testing::Each;
using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::Property;
+using ::testing::SizeIs;
-static const int kHeaderSize = 20;
-static const int kStatusChunkSize = 2;
-static const int kSmallDeltaSize = 1;
-static const int kLargeDeltaSize = 2;
+constexpr int kHeaderSize = 20;
+constexpr int kStatusChunkSize = 2;
+constexpr int kSmallDeltaSize = 1;
+constexpr int kLargeDeltaSize = 2;
-static const int64_t kDeltaLimit = 0xFF * TransportFeedback::kDeltaScaleFactor;
+constexpr TimeDelta kDeltaLimit = 0xFF * TransportFeedback::kDeltaTick;
+constexpr TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
+constexpr TimeDelta kBaseTimeWrapPeriod = kBaseTimeTick * (1 << 24);
+
+MATCHER_P2(Near, value, max_abs_error, "") {
+ return value - max_abs_error <= arg && arg <= value + max_abs_error;
+}
+
+MATCHER(IsValidFeedback, "") {
+ rtcp::CommonHeader rtcp_header;
+ TransportFeedback feedback;
+ return rtcp_header.Parse(std::data(arg), std::size(arg)) &&
+ rtcp_header.type() == TransportFeedback::kPacketType &&
+ rtcp_header.fmt() == TransportFeedback::kFeedbackMessageType &&
+ feedback.Parse(rtcp_header);
+}
+
+TransportFeedback Parse(rtc::ArrayView<const uint8_t> buffer) {
+ rtcp::CommonHeader header;
+ RTC_DCHECK(header.Parse(buffer.data(), buffer.size()));
+ RTC_DCHECK_EQ(header.type(), TransportFeedback::kPacketType);
+ RTC_DCHECK_EQ(header.fmt(), TransportFeedback::kFeedbackMessageType);
+ TransportFeedback feedback;
+ RTC_DCHECK(feedback.Parse(header));
+ return feedback;
+}
class FeedbackTester {
public:
FeedbackTester() : FeedbackTester(true) {}
explicit FeedbackTester(bool include_timestamps)
: expected_size_(kAnySize),
- default_delta_(TransportFeedback::kDeltaScaleFactor * 4),
+ default_delta_(TransportFeedback::kDeltaTick * 4),
include_timestamps_(include_timestamps) {}
void WithExpectedSize(size_t expected_size) {
expected_size_ = expected_size;
}
- void WithDefaultDelta(int64_t delta) { default_delta_ = delta; }
+ void WithDefaultDelta(TimeDelta delta) { default_delta_ = delta; }
- void WithInput(const uint16_t received_seq[],
- const int64_t received_ts[],
- uint16_t length) {
- std::unique_ptr<int64_t[]> temp_timestamps;
- if (received_ts == nullptr) {
- temp_timestamps.reset(new int64_t[length]);
- GenerateReceiveTimestamps(received_seq, length, temp_timestamps.get());
- received_ts = temp_timestamps.get();
+ void WithInput(rtc::ArrayView<const uint16_t> received_seq,
+ rtc::ArrayView<const Timestamp> received_ts = {}) {
+ std::vector<Timestamp> temp_timestamps;
+ if (received_ts.empty()) {
+ temp_timestamps = GenerateReceiveTimestamps(received_seq);
+ received_ts = temp_timestamps;
}
+ RTC_DCHECK_EQ(received_seq.size(), received_ts.size());
- expected_seq_.clear();
expected_deltas_.clear();
- feedback_.reset(new TransportFeedback(include_timestamps_));
+ feedback_.emplace(include_timestamps_);
feedback_->SetBase(received_seq[0], received_ts[0]);
ASSERT_TRUE(feedback_->IsConsistent());
+ // First delta is special: it doesn't represent the delta between two times,
+ // but a compensation for the reduced precision of the base time.
+ EXPECT_TRUE(feedback_->AddReceivedPacket(received_seq[0], received_ts[0]));
+ // GetBaseDelta suppose to return balanced diff between base time of the new
+ // feedback message (stored internally) and base time of the old feedback
+ // message (passed as parameter), but first delta is the difference between
+ // 1st timestamp (passed as parameter) and base time (stored internally),
+ // thus to get the first delta need to negate whatever GetBaseDelta returns.
+ expected_deltas_.push_back(-feedback_->GetBaseDelta(received_ts[0]));
- int64_t last_time = feedback_->GetBaseTimeUs();
- for (int i = 0; i < length; ++i) {
- int64_t time = received_ts[i];
- EXPECT_TRUE(feedback_->AddReceivedPacket(received_seq[i], time));
-
- if (last_time != -1) {
- int64_t delta = time - last_time;
- expected_deltas_.push_back(delta);
- }
- last_time = time;
+ for (size_t i = 1; i < received_ts.size(); ++i) {
+ EXPECT_TRUE(
+ feedback_->AddReceivedPacket(received_seq[i], received_ts[i]));
+ expected_deltas_.push_back(received_ts[i] - received_ts[i - 1]);
}
ASSERT_TRUE(feedback_->IsConsistent());
- expected_seq_.insert(expected_seq_.begin(), &received_seq[0],
- &received_seq[length]);
+ expected_seq_.assign(received_seq.begin(), received_seq.end());
}
void VerifyPacket() {
ASSERT_TRUE(feedback_->IsConsistent());
serialized_ = feedback_->Build();
VerifyInternal();
- feedback_ =
- TransportFeedback::ParseFrom(serialized_.data(), serialized_.size());
- ASSERT_NE(nullptr, feedback_);
+
+ feedback_.emplace(Parse(serialized_));
ASSERT_TRUE(feedback_->IsConsistent());
EXPECT_EQ(include_timestamps_, feedback_->IncludeTimestamps());
VerifyInternal();
}
- static const size_t kAnySize = static_cast<size_t>(0) - 1;
+ static constexpr size_t kAnySize = static_cast<size_t>(0) - 1;
private:
void VerifyInternal() {
@@ -102,37 +134,39 @@
}
std::vector<uint16_t> actual_seq_nos;
- std::vector<int64_t> actual_deltas_us;
+ std::vector<TimeDelta> actual_deltas;
for (const auto& packet : feedback_->GetReceivedPackets()) {
actual_seq_nos.push_back(packet.sequence_number());
- actual_deltas_us.push_back(packet.delta_us());
+ actual_deltas.push_back(packet.delta());
}
EXPECT_THAT(actual_seq_nos, ElementsAreArray(expected_seq_));
if (include_timestamps_) {
- EXPECT_THAT(actual_deltas_us, ElementsAreArray(expected_deltas_));
+ EXPECT_THAT(actual_deltas, ElementsAreArray(expected_deltas_));
}
}
- void GenerateReceiveTimestamps(const uint16_t seq[],
- const size_t length,
- int64_t* timestamps) {
- uint16_t last_seq = seq[0];
- int64_t offset = 0;
+ std::vector<Timestamp> GenerateReceiveTimestamps(
+ rtc::ArrayView<const uint16_t> seq_nums) {
+ RTC_DCHECK(!seq_nums.empty());
+ uint16_t last_seq = seq_nums[0];
+ Timestamp time = Timestamp::Zero();
+ std::vector<Timestamp> result;
- for (size_t i = 0; i < length; ++i) {
- if (seq[i] < last_seq)
- offset += 0x10000 * default_delta_;
- last_seq = seq[i];
+ for (uint16_t seq : seq_nums) {
+ if (seq < last_seq)
+ time += 0x10000 * default_delta_;
+ last_seq = seq;
- timestamps[i] = offset + (last_seq * default_delta_);
+ result.push_back(time + last_seq * default_delta_);
}
+ return result;
}
std::vector<uint16_t> expected_seq_;
- std::vector<int64_t> expected_deltas_;
+ std::vector<TimeDelta> expected_deltas_;
size_t expected_size_;
- int64_t default_delta_;
- std::unique_ptr<TransportFeedback> feedback_;
+ TimeDelta default_delta_;
+ absl::optional<TransportFeedback> feedback_;
rtc::Buffer serialized_;
bool include_timestamps_;
};
@@ -153,18 +187,17 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
TEST(RtcpPacketTest, TransportFeedbackOneBitVectorNoRecvDelta) {
const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13};
- const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
const size_t kExpectedSizeBytes = kHeaderSize + kStatusChunkSize;
FeedbackTester test(/*include_timestamps=*/false);
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
@@ -176,7 +209,7 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
@@ -189,7 +222,7 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
@@ -202,7 +235,7 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
@@ -214,8 +247,8 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaScaleFactor);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaTick);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
@@ -227,32 +260,32 @@
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaScaleFactor);
- test.WithInput(kReceived, nullptr, kLength);
+ test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaTick);
+ test.WithInput(kReceived);
test.VerifyPacket();
}
TEST(RtcpPacketTest, TransportFeedbackWithLargeBaseTimeIsConsistent) {
TransportFeedback tb;
- constexpr int64_t kTimestampUs =
- int64_t{0x7fff'ffff} * TransportFeedback::kDeltaScaleFactor;
- tb.SetBase(/*base_sequence=*/0, /*ref_timestamp_us=*/kTimestampUs);
- tb.AddReceivedPacket(/*base_sequence=*/0, /*ref_timestamp_us=*/kTimestampUs);
+ constexpr Timestamp kTimestamp =
+ Timestamp::Zero() + int64_t{0x7fff'ffff} * TransportFeedback::kDeltaTick;
+ tb.SetBase(/*base_sequence=*/0, /*ref_timestamp=*/kTimestamp);
+ tb.AddReceivedPacket(/*base_sequence=*/0, /*ref_timestamp=*/kTimestamp);
EXPECT_TRUE(tb.IsConsistent());
}
TEST(RtcpPacketTest, TransportFeedbackLargeAndNegativeDeltas) {
const uint16_t kReceived[] = {1, 2, 6, 7, 8};
- const int64_t kReceiveTimes[] = {
- 2000, 1000, 4000, 3000,
- 3000 + TransportFeedback::kDeltaScaleFactor * (1 << 8)};
- const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Millis(2), Timestamp::Millis(1), Timestamp::Millis(4),
+ Timestamp::Millis(3),
+ Timestamp::Millis(3) + TransportFeedback::kDeltaTick * (1 << 8)};
const size_t kExpectedSizeBytes =
kHeaderSize + kStatusChunkSize + (3 * kLargeDeltaSize) + kSmallDeltaSize;
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kLength);
+ test.WithInput(kReceived, kReceiveTimes);
test.VerifyPacket();
}
@@ -264,14 +297,15 @@
const size_t kPacketCount = (1 << 13) - 1 + 14;
const uint16_t kReceived[] = {0, kPacketCount};
- const int64_t kReceiveTimes[] = {1000, 2000};
+ const Timestamp kReceiveTimes[] = {Timestamp::Millis(1),
+ Timestamp::Millis(2)};
const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
const size_t kExpectedSizeBytes =
kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kLength);
+ test.WithInput(kReceived, kReceiveTimes);
test.VerifyPacket();
}
@@ -282,44 +316,45 @@
// * 1-bit vector chunk (1xreceived + 13xdropped)
const uint16_t kReceived[] = {0, (14 * 2) + 1};
- const int64_t kReceiveTimes[] = {1000, 2000};
+ const Timestamp kReceiveTimes[] = {Timestamp::Millis(1),
+ Timestamp::Millis(2)};
const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
const size_t kExpectedSizeBytes =
kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kLength);
+ test.WithInput(kReceived, kReceiveTimes);
test.VerifyPacket();
}
TEST(RtcpPacketTest, TransportFeedbackOneToTwoBitVector) {
const size_t kTwoBitVectorCapacity = 7;
const uint16_t kReceived[] = {0, kTwoBitVectorCapacity - 1};
- const int64_t kReceiveTimes[] = {
- 0, kDeltaLimit + TransportFeedback::kDeltaScaleFactor};
- const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Zero(),
+ Timestamp::Zero() + kDeltaLimit + TransportFeedback::kDeltaTick};
const size_t kExpectedSizeBytes =
kHeaderSize + kStatusChunkSize + kSmallDeltaSize + kLargeDeltaSize;
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kLength);
+ test.WithInput(kReceived, kReceiveTimes);
test.VerifyPacket();
}
TEST(RtcpPacketTest, TransportFeedbackOneToTwoBitVectorSimpleSplit) {
const size_t kTwoBitVectorCapacity = 7;
const uint16_t kReceived[] = {0, kTwoBitVectorCapacity};
- const int64_t kReceiveTimes[] = {
- 0, kDeltaLimit + TransportFeedback::kDeltaScaleFactor};
- const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Zero(),
+ Timestamp::Zero() + kDeltaLimit + TransportFeedback::kDeltaTick};
const size_t kExpectedSizeBytes =
kHeaderSize + (kStatusChunkSize * 2) + kSmallDeltaSize + kLargeDeltaSize;
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kLength);
+ test.WithInput(kReceived, kReceiveTimes);
test.VerifyPacket();
}
@@ -328,7 +363,7 @@
// SSSSSSSSLSSSSSSSSSSSS. This will cause a 1:2 split at the L.
// After split there will be two symbols in symbol_vec: SL.
- const int64_t kLargeDelta = TransportFeedback::kDeltaScaleFactor * (1 << 8);
+ const TimeDelta kLargeDelta = TransportFeedback::kDeltaTick * (1 << 8);
const size_t kNumPackets = (3 * 7) + 1;
const size_t kExpectedSizeBytes = kHeaderSize + (kStatusChunkSize * 3) +
(kSmallDeltaSize * (kNumPackets - 1)) +
@@ -338,107 +373,138 @@
for (size_t i = 0; i < kNumPackets; ++i)
kReceived[i] = i;
- int64_t kReceiveTimes[kNumPackets];
- kReceiveTimes[0] = 1000;
+ std::vector<Timestamp> receive_times;
+ receive_times.reserve(kNumPackets);
+ receive_times.push_back(Timestamp::Millis(1));
for (size_t i = 1; i < kNumPackets; ++i) {
- int delta = (i == 8) ? kLargeDelta : 1000;
- kReceiveTimes[i] = kReceiveTimes[i - 1] + delta;
+ TimeDelta delta = (i == 8) ? kLargeDelta : TimeDelta::Millis(1);
+ receive_times.push_back(receive_times.back() + delta);
}
FeedbackTester test;
test.WithExpectedSize(kExpectedSizeBytes);
- test.WithInput(kReceived, kReceiveTimes, kNumPackets);
+ test.WithInput(kReceived, receive_times);
test.VerifyPacket();
}
TEST(RtcpPacketTest, TransportFeedbackAliasing) {
TransportFeedback feedback;
- feedback.SetBase(0, 0);
+ feedback.SetBase(0, Timestamp::Zero());
const int kSamples = 100;
- const int64_t kTooSmallDelta = TransportFeedback::kDeltaScaleFactor / 3;
+ const TimeDelta kTooSmallDelta = TransportFeedback::kDeltaTick / 3;
for (int i = 0; i < kSamples; ++i)
- feedback.AddReceivedPacket(i, i * kTooSmallDelta);
+ feedback.AddReceivedPacket(i, Timestamp::Zero() + i * kTooSmallDelta);
feedback.Build();
- int64_t accumulated_delta = 0;
+ TimeDelta accumulated_delta = TimeDelta::Zero();
int num_samples = 0;
for (const auto& packet : feedback.GetReceivedPackets()) {
- accumulated_delta += packet.delta_us();
- int64_t expected_time = num_samples * kTooSmallDelta;
+ accumulated_delta += packet.delta();
+ TimeDelta expected_time = num_samples * kTooSmallDelta;
++num_samples;
- EXPECT_NEAR(expected_time, accumulated_delta,
- TransportFeedback::kDeltaScaleFactor / 2);
+ EXPECT_THAT(accumulated_delta,
+ Near(expected_time, TransportFeedback::kDeltaTick / 2));
}
}
TEST(RtcpPacketTest, TransportFeedbackLimits) {
// Sequence number wrap above 0x8000.
std::unique_ptr<TransportFeedback> packet(new TransportFeedback());
- packet->SetBase(0, 0);
- EXPECT_TRUE(packet->AddReceivedPacket(0x0, 0));
- EXPECT_TRUE(packet->AddReceivedPacket(0x8000, 1000));
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_TRUE(packet->AddReceivedPacket(0x8000, Timestamp::Millis(1)));
packet.reset(new TransportFeedback());
- packet->SetBase(0, 0);
- EXPECT_TRUE(packet->AddReceivedPacket(0x0, 0));
- EXPECT_FALSE(packet->AddReceivedPacket(0x8000 + 1, 1000));
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_FALSE(packet->AddReceivedPacket(0x8000 + 1, Timestamp::Millis(1)));
// Packet status count max 0xFFFF.
packet.reset(new TransportFeedback());
- packet->SetBase(0, 0);
- EXPECT_TRUE(packet->AddReceivedPacket(0x0, 0));
- EXPECT_TRUE(packet->AddReceivedPacket(0x8000, 1000));
- EXPECT_TRUE(packet->AddReceivedPacket(0xFFFE, 2000));
- EXPECT_FALSE(packet->AddReceivedPacket(0xFFFF, 3000));
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_TRUE(packet->AddReceivedPacket(0x8000, Timestamp::Millis(1)));
+ EXPECT_TRUE(packet->AddReceivedPacket(0xFFFE, Timestamp::Millis(2)));
+ EXPECT_FALSE(packet->AddReceivedPacket(0xFFFF, Timestamp::Millis(3)));
// Too large delta.
packet.reset(new TransportFeedback());
- packet->SetBase(0, 0);
- int64_t kMaxPositiveTimeDelta = std::numeric_limits<int16_t>::max() *
- TransportFeedback::kDeltaScaleFactor;
- EXPECT_FALSE(packet->AddReceivedPacket(
- 1, kMaxPositiveTimeDelta + TransportFeedback::kDeltaScaleFactor));
- EXPECT_TRUE(packet->AddReceivedPacket(1, kMaxPositiveTimeDelta));
+ packet->SetBase(0, Timestamp::Zero());
+ TimeDelta kMaxPositiveTimeDelta =
+ std::numeric_limits<int16_t>::max() * TransportFeedback::kDeltaTick;
+ EXPECT_FALSE(packet->AddReceivedPacket(1, Timestamp::Zero() +
+ kMaxPositiveTimeDelta +
+ TransportFeedback::kDeltaTick));
+ EXPECT_TRUE(
+ packet->AddReceivedPacket(1, Timestamp::Zero() + kMaxPositiveTimeDelta));
// Too large negative delta.
packet.reset(new TransportFeedback());
- packet->SetBase(0, 0);
- int64_t kMaxNegativeTimeDelta = std::numeric_limits<int16_t>::min() *
- TransportFeedback::kDeltaScaleFactor;
+ TimeDelta kMaxNegativeTimeDelta =
+ std::numeric_limits<int16_t>::min() * TransportFeedback::kDeltaTick;
+ // Use larger base time to avoid kBaseTime + kNegativeDelta to be negative.
+ Timestamp kBaseTime = Timestamp::Seconds(1'000'000);
+ packet->SetBase(0, kBaseTime);
EXPECT_FALSE(packet->AddReceivedPacket(
- 1, kMaxNegativeTimeDelta - TransportFeedback::kDeltaScaleFactor));
- EXPECT_TRUE(packet->AddReceivedPacket(1, kMaxNegativeTimeDelta));
-
- // Base time at maximum value.
- int64_t kMaxBaseTime =
- static_cast<int64_t>(TransportFeedback::kDeltaScaleFactor) * (1L << 8) *
- ((1L << 23) - 1);
- packet.reset(new TransportFeedback());
- packet->SetBase(0, kMaxBaseTime);
- EXPECT_TRUE(packet->AddReceivedPacket(0, kMaxBaseTime));
- // Serialize and de-serialize (verify 24bit parsing).
- rtc::Buffer raw_packet = packet->Build();
- packet = TransportFeedback::ParseFrom(raw_packet.data(), raw_packet.size());
- EXPECT_EQ(kMaxBaseTime, packet->GetBaseTimeUs());
-
- // Base time above maximum value.
- int64_t kTooLargeBaseTime =
- kMaxBaseTime + (TransportFeedback::kDeltaScaleFactor * (1L << 8));
- packet.reset(new TransportFeedback());
- packet->SetBase(0, kTooLargeBaseTime);
- packet->AddReceivedPacket(0, kTooLargeBaseTime);
- raw_packet = packet->Build();
- packet = TransportFeedback::ParseFrom(raw_packet.data(), raw_packet.size());
- EXPECT_NE(kTooLargeBaseTime, packet->GetBaseTimeUs());
+ 1, kBaseTime + kMaxNegativeTimeDelta - TransportFeedback::kDeltaTick));
+ EXPECT_TRUE(packet->AddReceivedPacket(1, kBaseTime + kMaxNegativeTimeDelta));
// TODO(sprang): Once we support max length lower than RTCP length limit,
// add back test for max size in bytes.
}
+TEST(RtcpPacketTest, BaseTimeIsConsistentAcrossMultiplePackets) {
+ constexpr Timestamp kMaxBaseTime =
+ Timestamp::Zero() + kBaseTimeWrapPeriod - kBaseTimeTick;
+
+ TransportFeedback packet1;
+ packet1.SetBase(0, kMaxBaseTime);
+ packet1.AddReceivedPacket(0, kMaxBaseTime);
+ // Build and parse packet to simulate sending it over the wire.
+ TransportFeedback parsed_packet1 = Parse(packet1.Build());
+
+ TransportFeedback packet2;
+ packet2.SetBase(1, kMaxBaseTime + kBaseTimeTick);
+ packet2.AddReceivedPacket(1, kMaxBaseTime + kBaseTimeTick);
+ TransportFeedback parsed_packet2 = Parse(packet2.Build());
+
+ EXPECT_EQ(parsed_packet2.GetBaseDelta(parsed_packet1.BaseTime()),
+ kBaseTimeTick);
+}
+
+TEST(RtcpPacketTest, SupportsMaximumNumberOfNegativeDeltas) {
+ TransportFeedback feedback;
+ // Use large base time to avoid hitting zero limit while filling the feedback,
+ // but use multiple of kBaseTimeWrapPeriod to hit edge case where base time
+ // is recorded as zero in the raw rtcp packet.
+ Timestamp time = Timestamp::Zero() + 1'000 * kBaseTimeWrapPeriod;
+ feedback.SetBase(0, time);
+ static constexpr TimeDelta kMinDelta =
+ TransportFeedback::kDeltaTick * std::numeric_limits<int16_t>::min();
+ uint16_t num_received_rtp_packets = 0;
+ time += kMinDelta;
+ while (feedback.AddReceivedPacket(++num_received_rtp_packets, time)) {
+ ASSERT_GE(time, Timestamp::Zero() - kMinDelta);
+ time += kMinDelta;
+ }
+ // Subtract one last packet that failed to add.
+ --num_received_rtp_packets;
+ EXPECT_TRUE(feedback.IsConsistent());
+
+ TransportFeedback parsed = Parse(feedback.Build());
+ EXPECT_EQ(parsed.GetReceivedPackets().size(), num_received_rtp_packets);
+ EXPECT_THAT(parsed.GetReceivedPackets(),
+ AllOf(SizeIs(num_received_rtp_packets),
+ Each(Property(&TransportFeedback::ReceivedPacket::delta,
+ Eq(kMinDelta)))));
+ EXPECT_GE(parsed.BaseTime(),
+ Timestamp::Zero() - kMinDelta * num_received_rtp_packets);
+}
+
TEST(RtcpPacketTest, TransportFeedbackPadding) {
const size_t kExpectedSizeBytes =
kHeaderSize + kStatusChunkSize + kSmallDeltaSize;
@@ -447,8 +513,8 @@
4 * kExpectedSizeWords - kExpectedSizeBytes;
TransportFeedback feedback;
- feedback.SetBase(0, 0);
- EXPECT_TRUE(feedback.AddReceivedPacket(0, 0));
+ feedback.SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(feedback.AddReceivedPacket(0, Timestamp::Zero()));
rtc::Buffer packet = feedback.Build();
EXPECT_EQ(kExpectedSizeWords * 4, packet.size());
@@ -475,10 +541,7 @@
&mod_buffer[2], ByteReader<uint16_t>::ReadBigEndian(&mod_buffer[2]) +
((kPaddingBytes + 3) / 4));
- std::unique_ptr<TransportFeedback> parsed_packet(
- TransportFeedback::ParseFrom(mod_buffer, kExpectedSizeWithPadding));
- ASSERT_TRUE(parsed_packet != nullptr);
- EXPECT_EQ(kExpectedSizeWords * 4, packet.size()); // Padding not included.
+ EXPECT_THAT(mod_buffer, IsValidFeedback());
}
TEST(RtcpPacketTest, TransportFeedbackPaddingBackwardsCompatibility) {
@@ -489,8 +552,8 @@
4 * kExpectedSizeWords - kExpectedSizeBytes;
TransportFeedback feedback;
- feedback.SetBase(0, 0);
- EXPECT_TRUE(feedback.AddReceivedPacket(0, 0));
+ feedback.SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(feedback.AddReceivedPacket(0, Timestamp::Zero()));
rtc::Buffer packet = feedback.Build();
EXPECT_EQ(kExpectedSizeWords * 4, packet.size());
@@ -510,47 +573,40 @@
const uint8_t padding_flag = 1 << 5;
mod_buffer[0] &= ~padding_flag; // Unset padding flag.
- std::unique_ptr<TransportFeedback> parsed_packet(
- TransportFeedback::ParseFrom(mod_buffer, kExpectedSizeWords * 4));
- ASSERT_TRUE(parsed_packet != nullptr);
- EXPECT_EQ(kExpectedSizeWords * 4, packet.size());
+ EXPECT_THAT(mod_buffer, IsValidFeedback());
}
TEST(RtcpPacketTest, TransportFeedbackCorrectlySplitsVectorChunks) {
const int kOneBitVectorCapacity = 14;
- const int64_t kLargeTimeDelta =
- TransportFeedback::kDeltaScaleFactor * (1 << 8);
+ const TimeDelta kLargeTimeDelta = TransportFeedback::kDeltaTick * (1 << 8);
// Test that a number of small deltas followed by a large delta results in a
// correct split into multiple chunks, as needed.
for (int deltas = 0; deltas <= kOneBitVectorCapacity + 1; ++deltas) {
TransportFeedback feedback;
- feedback.SetBase(0, 0);
+ feedback.SetBase(0, Timestamp::Zero());
for (int i = 0; i < deltas; ++i)
- feedback.AddReceivedPacket(i, i * 1000);
- feedback.AddReceivedPacket(deltas, deltas * 1000 + kLargeTimeDelta);
+ feedback.AddReceivedPacket(i, Timestamp::Millis(i));
+ feedback.AddReceivedPacket(deltas,
+ Timestamp::Millis(deltas) + kLargeTimeDelta);
- rtc::Buffer serialized_packet = feedback.Build();
- std::unique_ptr<TransportFeedback> deserialized_packet =
- TransportFeedback::ParseFrom(serialized_packet.data(),
- serialized_packet.size());
- EXPECT_TRUE(deserialized_packet != nullptr);
+ EXPECT_THAT(feedback.Build(), IsValidFeedback());
}
}
TEST(RtcpPacketTest, TransportFeedbackMoveConstructor) {
const int kSamples = 100;
- const int64_t kDelta = TransportFeedback::kDeltaScaleFactor;
const uint16_t kBaseSeqNo = 7531;
- const int64_t kBaseTimestampUs = 123456789;
+ const Timestamp kBaseTimestamp = Timestamp::Micros(123'456'789);
const uint8_t kFeedbackSeqNo = 90;
TransportFeedback feedback;
- feedback.SetBase(kBaseSeqNo, kBaseTimestampUs);
+ feedback.SetBase(kBaseSeqNo, kBaseTimestamp);
feedback.SetFeedbackSequenceNumber(kFeedbackSeqNo);
for (int i = 0; i < kSamples; ++i) {
- feedback.AddReceivedPacket(kBaseSeqNo + i, kBaseTimestampUs + i * kDelta);
+ feedback.AddReceivedPacket(
+ kBaseSeqNo + i, kBaseTimestamp + i * TransportFeedback::kDeltaTick);
}
EXPECT_TRUE(feedback.IsConsistent());
@@ -567,49 +623,42 @@
TEST(TransportFeedbackTest, ReportsMissingPackets) {
const uint16_t kBaseSeqNo = 1000;
- const int64_t kBaseTimestampUs = 10000;
+ const Timestamp kBaseTimestamp = Timestamp::Millis(10);
const uint8_t kFeedbackSeqNo = 90;
TransportFeedback feedback_builder(/*include_timestamps*/ true);
- feedback_builder.SetBase(kBaseSeqNo, kBaseTimestampUs);
+ feedback_builder.SetBase(kBaseSeqNo, kBaseTimestamp);
feedback_builder.SetFeedbackSequenceNumber(kFeedbackSeqNo);
- feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, kBaseTimestampUs);
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, kBaseTimestamp);
// Packet losses indicated by jump in sequence number.
- feedback_builder.AddReceivedPacket(kBaseSeqNo + 3, kBaseTimestampUs + 2000);
- rtc::Buffer coded = feedback_builder.Build();
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 3,
+ kBaseTimestamp + TimeDelta::Millis(2));
- rtcp::CommonHeader header;
- header.Parse(coded.data(), coded.size());
- TransportFeedback feedback(/*include_timestamps*/ true,
- /*include_lost*/ true);
- feedback.Parse(header);
- auto packets = feedback.GetAllPackets();
- EXPECT_TRUE(packets[0].received());
- EXPECT_FALSE(packets[1].received());
- EXPECT_FALSE(packets[2].received());
- EXPECT_TRUE(packets[3].received());
+ EXPECT_THAT(
+ Parse(feedback_builder.Build()).GetAllPackets(),
+ ElementsAre(
+ Property(&TransportFeedback::ReceivedPacket::received, true),
+ Property(&TransportFeedback::ReceivedPacket::received, false),
+ Property(&TransportFeedback::ReceivedPacket::received, false),
+ Property(&TransportFeedback::ReceivedPacket::received, true)));
}
TEST(TransportFeedbackTest, ReportsMissingPacketsWithoutTimestamps) {
const uint16_t kBaseSeqNo = 1000;
const uint8_t kFeedbackSeqNo = 90;
TransportFeedback feedback_builder(/*include_timestamps*/ false);
- feedback_builder.SetBase(kBaseSeqNo, 10000);
+ feedback_builder.SetBase(kBaseSeqNo, Timestamp::Millis(10));
feedback_builder.SetFeedbackSequenceNumber(kFeedbackSeqNo);
- feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, /*timestamp_us*/ 0);
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, Timestamp::Zero());
// Packet losses indicated by jump in sequence number.
- feedback_builder.AddReceivedPacket(kBaseSeqNo + 3, /*timestamp_us*/ 0);
- rtc::Buffer coded = feedback_builder.Build();
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 3, Timestamp::Zero());
- rtcp::CommonHeader header;
- header.Parse(coded.data(), coded.size());
- TransportFeedback feedback(/*include_timestamps*/ true,
- /*include_lost*/ true);
- feedback.Parse(header);
- auto packets = feedback.GetAllPackets();
- EXPECT_TRUE(packets[0].received());
- EXPECT_FALSE(packets[1].received());
- EXPECT_FALSE(packets[2].received());
- EXPECT_TRUE(packets[3].received());
+ EXPECT_THAT(
+ Parse(feedback_builder.Build()).GetAllPackets(),
+ ElementsAre(
+ Property(&TransportFeedback::ReceivedPacket::received, true),
+ Property(&TransportFeedback::ReceivedPacket::received, false),
+ Property(&TransportFeedback::ReceivedPacket::received, false),
+ Property(&TransportFeedback::ReceivedPacket::received, true)));
}
} // namespace
} // namespace webrtc