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