Make recv_deltas optional in TransportFeedback packets

Bug: webrtc:10263
Change-Id: I49c4a4710a5c34a62b53080e708c310a8484831b
Reviewed-on: https://webrtc-review.googlesource.com/c/122543
Commit-Queue: Johannes Kron <kron@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26687}
diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
index 81eda9e..ee8b93a 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
@@ -262,10 +262,14 @@
 }
 
 TransportFeedback::TransportFeedback()
+    : TransportFeedback(/*include_timestamps=*/true) {}
+
+TransportFeedback::TransportFeedback(bool include_timestamps)
     : base_seq_no_(0),
       num_seq_no_(0),
       base_time_ticks_(0),
       feedback_seq_(0),
+      include_timestamps_(include_timestamps),
       last_timestamp_us_(0),
       size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
 
@@ -276,6 +280,7 @@
       num_seq_no_(other.num_seq_no_),
       base_time_ticks_(other.base_time_ticks_),
       feedback_seq_(other.feedback_seq_),
+      include_timestamps_(other.include_timestamps_),
       last_timestamp_us_(other.last_timestamp_us_),
       packets_(std::move(other.packets_)),
       encoded_chunks_(std::move(other.encoded_chunks_)),
@@ -301,19 +306,25 @@
 
 bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
                                           int64_t timestamp_us) {
-  // Convert to ticks and round.
-  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;
+  // 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.
+    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;
 
-  int16_t delta = static_cast<int16_t>(delta_full);
-  // If larger than 16bit signed, we can't represent it - need new fb packet.
-  if (delta != delta_full) {
-    RTC_LOG(LS_WARNING) << "Delta value too large ( >= 2^16 ticks )";
-    return false;
+    delta = static_cast<int16_t>(delta_full);
+    // If larger than 16bit signed, we can't represent it - need new fb packet.
+    if (delta != delta_full) {
+      RTC_LOG(LS_WARNING) << "Delta value too large ( >= 2^16 ticks )";
+      return false;
+    }
   }
 
   uint16_t next_seq_no = base_seq_no_ + num_seq_no_;
@@ -332,7 +343,9 @@
 
   packets_.emplace_back(sequence_number, delta);
   last_timestamp_us_ += delta * kDeltaScaleFactor;
-  size_bytes_ += delta_size;
+  if (include_timestamps_) {
+    size_bytes_ += delta_size;
+  }
   return true;
 }
 
@@ -400,38 +413,57 @@
   num_seq_no_ = status_count;
 
   uint16_t seq_no = base_seq_no_;
+  size_t recv_delta_size = 0;
   for (size_t delta_size : delta_sizes) {
-    if (index + delta_size > end_index) {
-      RTC_LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
-      Clear();
-      return false;
-    }
-    switch (delta_size) {
-      case 0:
-        break;
-      case 1: {
-        int16_t delta = payload[index];
-        packets_.emplace_back(seq_no, delta);
-        last_timestamp_us_ += delta * kDeltaScaleFactor;
-        index += delta_size;
-        break;
-      }
-      case 2: {
-        int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
-        packets_.emplace_back(seq_no, delta);
-        last_timestamp_us_ += delta * kDeltaScaleFactor;
-        index += delta_size;
-        break;
-      }
-      case 3:
+    recv_delta_size += delta_size;
+  }
+
+  // Determine if timestamps, that is, recv_delta are included in the packet.
+  if (end_index >= index + recv_delta_size) {
+    for (size_t delta_size : delta_sizes) {
+      if (index + delta_size > end_index) {
+        RTC_LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
         Clear();
-        RTC_LOG(LS_WARNING) << "Invalid delta_size for seq_no " << seq_no;
         return false;
-      default:
-        RTC_NOTREACHED();
-        break;
+      }
+      switch (delta_size) {
+        case 0:
+          break;
+        case 1: {
+          int16_t delta = payload[index];
+          packets_.emplace_back(seq_no, delta);
+          last_timestamp_us_ += delta * kDeltaScaleFactor;
+          index += delta_size;
+          break;
+        }
+        case 2: {
+          int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
+          packets_.emplace_back(seq_no, delta);
+          last_timestamp_us_ += delta * kDeltaScaleFactor;
+          index += delta_size;
+          break;
+        }
+        case 3:
+          Clear();
+          RTC_LOG(LS_WARNING) << "Invalid delta_size for seq_no " << seq_no;
+
+          return false;
+        default:
+          RTC_NOTREACHED();
+          break;
+      }
+      ++seq_no;
     }
-    ++seq_no;
+  } else {
+    // The packet does not contain receive deltas.
+    include_timestamps_ = false;
+    for (size_t delta_size : delta_sizes) {
+      // Use delta sizes to detect if packet was received.
+      if (delta_size > 0) {
+        packets_.emplace_back(seq_no, 0);
+      }
+      ++seq_no;
+    }
   }
   size_bytes_ = RtcpPacket::kHeaderLength + index;
   RTC_DCHECK_LE(index, end_index);
@@ -495,7 +527,9 @@
       timestamp_us += packet_it->delta_us();
       ++packet_it;
     }
-    packet_size += delta_size;
+    if (include_timestamps_) {
+      packet_size += delta_size;
+    }
     ++seq_no;
   }
   if (packet_it != packets_.end()) {
@@ -566,13 +600,15 @@
     *position += 2;
   }
 
-  for (const auto& received_packet : packets_) {
-    int16_t delta = received_packet.delta_ticks();
-    if (delta >= 0 && delta <= 0xFF) {
-      packet[(*position)++] = delta;
-    } else {
-      ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
-      *position += 2;
+  if (include_timestamps_) {
+    for (const auto& received_packet : packets_) {
+      int16_t delta = received_packet.delta_ticks();
+      if (delta >= 0 && delta <= 0xFF) {
+        packet[(*position)++] = delta;
+      } else {
+        ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
+        *position += 2;
+      }
     }
   }
 
diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
index b7bed9d..174ef6b 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
@@ -45,6 +45,10 @@
   static constexpr size_t kMaxReportedPackets = 0xffff;
 
   TransportFeedback();
+  explicit TransportFeedback(
+      bool include_timestamps);  // If |include_timestamps| is set to false, the
+                                 // created packet will not contain the receive
+                                 // delta block.
   TransportFeedback(const TransportFeedback&);
   TransportFeedback(TransportFeedback&&);
 
@@ -65,6 +69,9 @@
   // Get the reference time in microseconds, including any precision loss.
   int64_t GetBaseTimeUs() const;
 
+  // Does the feedback packet contain timestamp information?
+  bool IncludeTimestamps() const { return include_timestamps_; }
+
   bool Parse(const CommonHeader& packet);
   static std::unique_ptr<TransportFeedback> ParseFrom(const uint8_t* buffer,
                                                       size_t length);
@@ -141,6 +148,7 @@
   uint16_t num_seq_no_;
   int32_t base_time_ticks_;
   uint8_t feedback_seq_;
+  bool include_timestamps_;
 
   int64_t last_timestamp_us_;
   std::vector<ReceivedPacket> packets_;
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 43eeeb9..0bb2d47 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
@@ -33,9 +33,11 @@
 
 class FeedbackTester {
  public:
-  FeedbackTester()
+  FeedbackTester() : FeedbackTester(true) {}
+  explicit FeedbackTester(bool include_timestamps)
       : expected_size_(kAnySize),
-        default_delta_(TransportFeedback::kDeltaScaleFactor * 4) {}
+        default_delta_(TransportFeedback::kDeltaScaleFactor * 4),
+        include_timestamps_(include_timestamps) {}
 
   void WithExpectedSize(size_t expected_size) {
     expected_size_ = expected_size;
@@ -46,16 +48,16 @@
   void WithInput(const uint16_t received_seq[],
                  const int64_t received_ts[],
                  uint16_t length) {
-    std::unique_ptr<int64_t[]> temp_deltas;
+    std::unique_ptr<int64_t[]> temp_timestamps;
     if (received_ts == nullptr) {
-      temp_deltas.reset(new int64_t[length]);
-      GenerateDeltas(received_seq, length, temp_deltas.get());
-      received_ts = temp_deltas.get();
+      temp_timestamps.reset(new int64_t[length]);
+      GenerateReceiveTimestamps(received_seq, length, temp_timestamps.get());
+      received_ts = temp_timestamps.get();
     }
 
     expected_seq_.clear();
     expected_deltas_.clear();
-    feedback_.reset(new TransportFeedback());
+    feedback_.reset(new TransportFeedback(include_timestamps_));
     feedback_->SetBase(received_seq[0], received_ts[0]);
     ASSERT_TRUE(feedback_->IsConsistent());
 
@@ -81,8 +83,9 @@
     VerifyInternal();
     feedback_ =
         TransportFeedback::ParseFrom(serialized_.data(), serialized_.size());
-    ASSERT_TRUE(feedback_->IsConsistent());
     ASSERT_NE(nullptr, feedback_);
+    ASSERT_TRUE(feedback_->IsConsistent());
+    EXPECT_EQ(include_timestamps_, feedback_->IncludeTimestamps());
     VerifyInternal();
   }
 
@@ -104,12 +107,14 @@
       actual_deltas_us.push_back(packet.delta_us());
     }
     EXPECT_THAT(actual_seq_nos, ElementsAreArray(expected_seq_));
-    EXPECT_THAT(actual_deltas_us, ElementsAreArray(expected_deltas_));
+    if (include_timestamps_) {
+      EXPECT_THAT(actual_deltas_us, ElementsAreArray(expected_deltas_));
+    }
   }
 
-  void GenerateDeltas(const uint16_t seq[],
-                      const size_t length,
-                      int64_t* deltas) {
+  void GenerateReceiveTimestamps(const uint16_t seq[],
+                                 const size_t length,
+                                 int64_t* timestamps) {
     uint16_t last_seq = seq[0];
     int64_t offset = 0;
 
@@ -118,7 +123,7 @@
         offset += 0x10000 * default_delta_;
       last_seq = seq[i];
 
-      deltas[i] = offset + (last_seq * default_delta_);
+      timestamps[i] = offset + (last_seq * default_delta_);
     }
   }
 
@@ -128,8 +133,17 @@
   int64_t default_delta_;
   std::unique_ptr<TransportFeedback> feedback_;
   rtc::Buffer serialized_;
+  bool include_timestamps_;
 };
 
+// The following tests use FeedbackTester that simulates received packets as
+// specified by the parameters |received_seq[]| and |received_ts[]| (optional).
+// The following is verified in these tests:
+// - Expected size of serialized packet.
+// - Expected sequence numbers and receive deltas.
+// - Sequence numbers and receive deltas are persistent after serialization
+//   followed by parsing.
+// - The internal state of a feedback packet is consistent.
 TEST(RtcpPacketTest, TransportFeedbackOneBitVector) {
   const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13};
   const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
@@ -142,6 +156,17 @@
   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.VerifyPacket();
+}
+
 TEST(RtcpPacketTest, TransportFeedbackFullOneBitVector) {
   const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13, 14};
   const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);