Ignore duplicate sent packets in TransportFeedbackAdapter

Bug: webrtc:10564
Change-Id: I617b58ef8cf5858d7a81aaa39884c5cc1ac2af6e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/133564
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27689}
diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn
index 357e1a6..bb3fe1a 100644
--- a/modules/congestion_controller/rtp/BUILD.gn
+++ b/modules/congestion_controller/rtp/BUILD.gn
@@ -64,6 +64,7 @@
     "../../../rtc_base:rtc_base_approved",
     "../../../rtc_base/network:sent_packet",
     "../../../system_wrappers",
+    "../../../system_wrappers:field_trial",
     "../../rtp_rtcp:rtp_rtcp_format",
   ]
 }
diff --git a/modules/congestion_controller/rtp/send_time_history.cc b/modules/congestion_controller/rtp/send_time_history.cc
index 76450b2..111389b 100644
--- a/modules/congestion_controller/rtp/send_time_history.cc
+++ b/modules/congestion_controller/rtp/send_time_history.cc
@@ -55,12 +55,12 @@
       std::max(last_untracked_send_time_ms_, send_time_ms);
 }
 
-bool SendTimeHistory::OnSentPacket(uint16_t sequence_number,
-                                   int64_t send_time_ms) {
+SendTimeHistory::Status SendTimeHistory::OnSentPacket(uint16_t sequence_number,
+                                                      int64_t send_time_ms) {
   int64_t unwrapped_seq_num = seq_num_unwrapper_.Unwrap(sequence_number);
   auto it = history_.find(unwrapped_seq_num);
   if (it == history_.end())
-    return false;
+    return Status::kNotAdded;
   bool packet_retransmit = it->second.send_time_ms >= 0;
   it->second.send_time_ms = send_time_ms;
   last_send_time_ms_ = std::max(last_send_time_ms_, send_time_ms);
@@ -74,7 +74,7 @@
     it->second.unacknowledged_data += pending_untracked_size_;
     pending_untracked_size_ = 0;
   }
-  return true;
+  return packet_retransmit ? Status::kDuplicate : Status::kOk;
 }
 
 absl::optional<PacketFeedback> SendTimeHistory::GetPacket(
diff --git a/modules/congestion_controller/rtp/send_time_history.h b/modules/congestion_controller/rtp/send_time_history.h
index 553ba15..65ef318 100644
--- a/modules/congestion_controller/rtp/send_time_history.h
+++ b/modules/congestion_controller/rtp/send_time_history.h
@@ -23,6 +23,8 @@
 
 class SendTimeHistory {
  public:
+  enum class Status { kNotAdded, kOk, kDuplicate };
+
   explicit SendTimeHistory(int64_t packet_age_limit_ms);
   ~SendTimeHistory();
 
@@ -32,8 +34,9 @@
   void AddUntracked(size_t packet_size, int64_t send_time_ms);
 
   // Updates packet info identified by |sequence_number| with |send_time_ms|.
-  // Return false if not found.
-  bool OnSentPacket(uint16_t sequence_number, int64_t send_time_ms);
+  // Returns a PacketSendState indicating if the packet was not found, sent,
+  // or if it was previously already marked as sent.
+  Status OnSentPacket(uint16_t sequence_number, int64_t send_time_ms);
 
   // Retrieves packet info identified by |sequence_number|.
   absl::optional<PacketFeedback> GetPacket(uint16_t sequence_number) const;
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc
index 4249e64..b1699b4 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc
@@ -19,6 +19,7 @@
 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.h"
 
 namespace webrtc {
 namespace {
@@ -47,7 +48,9 @@
 const int64_t kBaseTimestampRangeSizeUs = kBaseTimestampScaleFactor * (1 << 24);
 
 TransportFeedbackAdapter::TransportFeedbackAdapter()
-    : send_time_history_(kSendTimeHistoryWindowMs),
+    : allow_duplicates_(field_trial::IsEnabled(
+          "WebRTC-TransportFeedbackAdapter-AllowDuplicates")),
+      send_time_history_(kSendTimeHistoryWindowMs),
       current_offset_ms_(kNoTimestamp),
       last_timestamp_us_(kNoTimestamp),
       local_net_id_(0),
@@ -100,10 +103,14 @@
   rtc::CritScope cs(&lock_);
   // TODO(srte): Only use one way to indicate that packet feedback is used.
   if (sent_packet.info.included_in_feedback || sent_packet.packet_id != -1) {
-    send_time_history_.OnSentPacket(sent_packet.packet_id,
-                                    sent_packet.send_time_ms);
-    absl::optional<PacketFeedback> packet =
-        send_time_history_.GetPacket(sent_packet.packet_id);
+    SendTimeHistory::Status send_status = send_time_history_.OnSentPacket(
+        sent_packet.packet_id, sent_packet.send_time_ms);
+    absl::optional<PacketFeedback> packet;
+    if (allow_duplicates_ ||
+        send_status != SendTimeHistory::Status::kDuplicate) {
+      packet = send_time_history_.GetPacket(sent_packet.packet_id);
+    }
+
     if (packet) {
       SentPacket msg;
       msg.size = DataSize::bytes(packet->payload_size);
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.h b/modules/congestion_controller/rtp/transport_feedback_adapter.h
index 7df0baa..3fb4fdd 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter.h
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter.h
@@ -63,6 +63,8 @@
       const rtcp::TransportFeedback& feedback,
       Timestamp feedback_time);
 
+  const bool allow_duplicates_;
+
   rtc::CriticalSection lock_;
   SendTimeHistory send_time_history_ RTC_GUARDED_BY(&lock_);
   int64_t current_offset_ms_;
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
index fe982e2..c9ec37b 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
@@ -19,6 +19,7 @@
 #include "rtc_base/checks.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "system_wrappers/include/clock.h"
+#include "test/field_trial.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
@@ -393,6 +394,53 @@
                                  adapter_->GetTransportFeedbackVector());
   }
 }
+
+TEST_F(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
+  const PacketFeedback packet(100, 200, 0, 1500, kPacingInfo0);
+
+  // Add a packet and then mark it as sent.
+  adapter_->AddPacket(kSsrc, packet.sequence_number, packet.payload_size,
+                      packet.pacing_info,
+                      Timestamp::ms(clock_.TimeInMilliseconds()));
+  absl::optional<SentPacket> sent_packet =
+      adapter_->ProcessSentPacket(rtc::SentPacket(
+          packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
+  EXPECT_TRUE(sent_packet.has_value());
+
+  // Call ProcessSentPacket() again with the same sequence number. This packet
+  // has already been marked as sent and the call should be ignored.
+  absl::optional<SentPacket> duplicate_packet =
+      adapter_->ProcessSentPacket(rtc::SentPacket(
+          packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
+  EXPECT_FALSE(duplicate_packet.has_value());
+}
+
+TEST_F(TransportFeedbackAdapterTest, AllowDuplicatePacketSentCallsWithTrial) {
+  // Allow duplicates if this field trial kill-switch is enabled.
+  webrtc::test::ScopedFieldTrials field_trial(
+      "WebRTC-TransportFeedbackAdapter-AllowDuplicates/Enabled/");
+  // Re-run setup so the flags goes into effect.
+  SetUp();
+
+  const PacketFeedback packet(100, 200, 0, 1500, kPacingInfo0);
+
+  // Add a packet and then mark it as sent.
+  adapter_->AddPacket(kSsrc, packet.sequence_number, packet.payload_size,
+                      packet.pacing_info,
+                      Timestamp::ms(clock_.TimeInMilliseconds()));
+  absl::optional<SentPacket> sent_packet =
+      adapter_->ProcessSentPacket(rtc::SentPacket(
+          packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
+  EXPECT_TRUE(sent_packet.has_value());
+
+  // Call ProcessSentPacket() again with the same sequence number. This packet
+  // should still be allowed due to the field trial/
+  absl::optional<SentPacket> duplicate_packet =
+      adapter_->ProcessSentPacket(rtc::SentPacket(
+          packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
+  EXPECT_TRUE(duplicate_packet.has_value());
+}
+
 }  // namespace test
 }  // namespace webrtc_cc
 }  // namespace webrtc