Add support for receiving CongestionControlFeedback to RTCPReceiver

Support for parsing the packet is gated behind field trial
WebRTC-RFC8888CongestionControlFeedback/Enabled/.

Bug: webrtc:15368
Change-Id: Ib4478e821fe5a43510af5131543e7861cf54d901
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/348664
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42215}
diff --git a/experiments/field_trials.py b/experiments/field_trials.py
index 73419bf..ed192a6 100755
--- a/experiments/field_trials.py
+++ b/experiments/field_trials.py
@@ -113,6 +113,9 @@
     FieldTrial('WebRTC-ReceiveBufferSize',
                42225927,
                date(2024, 4, 1)),
+    FieldTrial('WebRTC-RFC8888CongestionControlFeedback',
+               42225697,
+               date(2025, 1, 30)),
     FieldTrial('WebRTC-RtcEventLogEncodeDependencyDescriptor',
                42225280,
                date(2024, 4, 1)),
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index c63603f..3f69b02 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -299,6 +299,7 @@
     "../../api/rtc_event_log",
     "../../api/task_queue:pending_task_safety_flag",
     "../../api/task_queue:task_queue",
+    "../../api/transport:field_trial_based_config",
     "../../api/transport/rtp:dependency_descriptor",
     "../../api/transport/rtp:rtp_source",
     "../../api/units:data_rate",
@@ -715,6 +716,7 @@
       "../../test:mock_transport",
       "../../test:rtp_test_utils",
       "../../test:run_loop",
+      "../../test:scoped_key_value_config",
       "../../test:test_support",
       "../../test/time_controller:time_controller",
       "../video_coding:codec_globals_headers",
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index 37410e2..abd06c1 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -29,6 +29,7 @@
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
 #include "modules/rtp_rtcp/include/report_block_data.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
 #include "system_wrappers/include/clock.h"
 
@@ -107,7 +108,7 @@
   kRtcpXrReceiverReferenceTime = 0x40000,
   kRtcpXrDlrrReportBlock = 0x80000,
   kRtcpTransportFeedback = 0x100000,
-  kRtcpXrTargetBitrate = 0x200000
+  kRtcpXrTargetBitrate = 0x200000,
 };
 
 enum class KeyFrameReqMethod : uint8_t {
@@ -164,6 +165,10 @@
 
   virtual void OnTransportFeedback(Timestamp receive_time,
                                    const rtcp::TransportFeedback& feedback) {}
+  // RFC 8888 congestion control feedback.
+  virtual void OnCongestionControlFeedback(
+      Timestamp receive_time,
+      const rtcp::CongestionControlFeedback& feedback) {}
   virtual void OnReceiverEstimatedMaxBitrate(Timestamp receive_time,
                                              DataRate bitrate) {}
 
diff --git a/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h b/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h
index 16b7db7..90dd948 100644
--- a/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h
+++ b/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h
@@ -33,6 +33,11 @@
               (Timestamp receive_time, const rtcp::TransportFeedback& feedback),
               (override));
   MOCK_METHOD(void,
+              OnCongestionControlFeedback,
+              (Timestamp receive_time,
+               const rtcp::CongestionControlFeedback& feedback),
+              (override));
+  MOCK_METHOD(void,
               OnReceiverEstimatedMaxBitrate,
               (Timestamp receive_time, DataRate bitrate),
               (override));
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
index 63f44fb..9df4955 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -19,13 +19,16 @@
 #include <utility>
 #include <vector>
 
+#include "api/transport/field_trial_based_config.h"
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
 #include "api/video/video_bitrate_allocation.h"
 #include "api/video/video_bitrate_allocator.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/fir.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
@@ -131,6 +134,7 @@
   absl::optional<TimeDelta> rtt;
   uint32_t receiver_estimated_max_bitrate_bps = 0;
   std::unique_ptr<rtcp::TransportFeedback> transport_feedback;
+  absl::optional<rtcp::CongestionControlFeedback> congestion_control_feedback;
   absl::optional<VideoBitrateAllocation> target_bitrate_allocation;
   absl::optional<NetworkStateEstimate> network_state_estimate;
   std::unique_ptr<rtcp::LossNotification> loss_notification;
@@ -140,6 +144,8 @@
                            ModuleRtpRtcpImpl2* owner)
     : clock_(config.clock),
       receiver_only_(config.receiver_only),
+      enable_congestion_controller_feedback_(FieldTrialBasedConfig().IsEnabled(
+          "WebRTC-RFC8888CongestionControlFeedback")),
       rtp_rtcp_(owner),
       registered_ssrcs_(false, config),
       network_link_rtcp_observer_(config.network_link_rtcp_observer),
@@ -167,6 +173,8 @@
                            ModuleRtpRtcp* owner)
     : clock_(config.clock),
       receiver_only_(config.receiver_only),
+      enable_congestion_controller_feedback_(FieldTrialBasedConfig().IsEnabled(
+          "WebRTC-RFC8888CongestionControlFeedback")),
       rtp_rtcp_(owner),
       registered_ssrcs_(true, config),
       network_link_rtcp_observer_(config.network_link_rtcp_observer),
@@ -438,6 +446,13 @@
           case rtcp::TransportFeedback::kFeedbackMessageType:
             HandleTransportFeedback(rtcp_block, packet_information);
             break;
+          case rtcp::CongestionControlFeedback::kFeedbackMessageType:
+            if (enable_congestion_controller_feedback_) {
+              valid = HandleCongestionControlFeedback(rtcp_block,
+                                                      packet_information);
+              break;
+            }
+            ABSL_FALLTHROUGH_INTENDED;
           default:
             ++num_skipped_packets_;
             break;
@@ -1048,6 +1063,17 @@
   }
 }
 
+bool RTCPReceiver::HandleCongestionControlFeedback(
+    const CommonHeader& rtcp_block,
+    PacketInformation* packet_information) {
+  rtcp::CongestionControlFeedback feedback;
+  if (!feedback.Parse(rtcp_block)) {
+    return false;
+  }
+  packet_information->congestion_control_feedback.emplace(std::move(feedback));
+  return true;
+}
+
 void RTCPReceiver::NotifyTmmbrUpdated() {
   // Find bounding set.
   std::vector<rtcp::TmmbItem> bounding =
@@ -1137,6 +1163,10 @@
       network_link_rtcp_observer_->OnTransportFeedback(
           now, *packet_information.transport_feedback);
     }
+    if (packet_information.congestion_control_feedback) {
+      network_link_rtcp_observer_->OnCongestionControlFeedback(
+          now, *packet_information.congestion_control_feedback);
+    }
   }
 
   if ((packet_information.packet_type_flags & kRtcpSr) ||
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h
index 20e314b..6bfd1e1 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.h
+++ b/modules/rtp_rtcp/source/rtcp_receiver.h
@@ -342,6 +342,9 @@
   void HandleTransportFeedback(const rtcp::CommonHeader& rtcp_block,
                                PacketInformation* packet_information)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
+  bool HandleCongestionControlFeedback(const rtcp::CommonHeader& rtcp_block,
+                                       PacketInformation* packet_information)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
 
   bool RtcpRrTimeoutLocked(Timestamp now)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
@@ -351,6 +354,7 @@
 
   Clock* const clock_;
   const bool receiver_only_;
+  const bool enable_congestion_controller_feedback_;
   ModuleRtpRtcp* const rtp_rtcp_;
   // The set of registered local SSRCs.
   RegisteredSsrcs registered_ssrcs_;
diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
index 1f5f138..a88e90e 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
@@ -26,6 +26,7 @@
 #include "modules/rtp_rtcp/source/rtcp_packet/app.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/fir.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/nack.h"
@@ -44,6 +45,7 @@
 #include "system_wrappers/include/ntp_time.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
+#include "test/scoped_key_value_config.h"
 
 namespace webrtc {
 namespace {
@@ -64,6 +66,7 @@
 using ::testing::StrEq;
 using ::testing::StrictMock;
 using ::testing::UnorderedElementsAre;
+using ::webrtc::test::ScopedKeyValueConfig;
 
 class MockRtcpPacketTypeCounterObserver : public RtcpPacketTypeCounterObserver {
  public:
@@ -1740,6 +1743,54 @@
   receiver.IncomingPacket(packet.Build());
 }
 
+TEST(RtcpReceiverTest, NotifiesNetworkLinkObserverOnCongestionControlFeedback) {
+  ScopedKeyValueConfig trials(
+      "WebRTC-RFC8888CongestionControlFeedback/Enabled/");
+  ReceiverMocks mocks;
+  RtpRtcpInterface::Configuration config = DefaultConfiguration(&mocks);
+  RTCPReceiver receiver(config, &mocks.rtp_rtcp_impl);
+  receiver.SetRemoteSSRC(kSenderSsrc);
+
+  rtcp::CongestionControlFeedback packet({{
+                                             .ssrc = 123,
+                                             .sequence_number = 1,
+                                         }},
+                                         /*report_timestamp_compact_ntp=*/324);
+  packet.SetSenderSsrc(kSenderSsrc);
+
+  EXPECT_CALL(
+      mocks.network_link_rtcp_observer,
+      OnCongestionControlFeedback(
+          mocks.clock.CurrentTime(),
+          Property(&rtcp::CongestionControlFeedback::packets, SizeIs(1))));
+  receiver.IncomingPacket(packet.Build());
+}
+
+TEST(RtcpReceiverTest, HandlesInvalidCongestionControlFeedback) {
+  ScopedKeyValueConfig trials(
+      "WebRTC-RFC8888CongestionControlFeedback/Enabled/");
+  ReceiverMocks mocks;
+  RtpRtcpInterface::Configuration config = DefaultConfiguration(&mocks);
+  RTCPReceiver receiver(config, &mocks.rtp_rtcp_impl);
+  receiver.SetRemoteSSRC(kSenderSsrc);
+
+  rtcp::CongestionControlFeedback packet({{
+                                             .ssrc = 123,
+                                             .sequence_number = 1,
+                                         }},
+                                         /*report_timestamp_compact_ntp=*/324);
+  packet.SetSenderSsrc(kSenderSsrc);
+  rtc::Buffer built_packet = packet.Build();
+  // Modify the CongestionControlFeedback packet so that it is invalid.
+  const size_t kNumReportsOffset = 14;
+  ByteWriter<uint16_t>::WriteBigEndian(&built_packet.data()[kNumReportsOffset],
+                                       42);
+
+  EXPECT_CALL(mocks.network_link_rtcp_observer, OnCongestionControlFeedback)
+      .Times(0);
+  receiver.IncomingPacket(built_packet);
+}
+
 TEST(RtcpReceiverTest,
      NotifiesNetworkLinkObserverOnTransportFeedbackOnRtxSsrc) {
   ReceiverMocks mocks;
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index 623347e..c667b64 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -222,6 +222,7 @@
     "../../modules/rtp_rtcp:rtp_rtcp_format",
     "../../rtc_base:checks",
     "../../system_wrappers",
+    "../../system_wrappers:field_trial",
   ]
   seed_corpus = "corpora/rtcp-corpus"
 }
diff --git a/test/fuzzers/rtcp_receiver_fuzzer.cc b/test/fuzzers/rtcp_receiver_fuzzer.cc
index e61f6c0..5dedf58 100644
--- a/test/fuzzers/rtcp_receiver_fuzzer.cc
+++ b/test/fuzzers/rtcp_receiver_fuzzer.cc
@@ -10,8 +10,8 @@
 #include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
 #include "modules/rtp_rtcp/source/rtcp_receiver.h"
 #include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
-#include "rtc_base/checks.h"
 #include "system_wrappers/include/clock.h"
+#include "system_wrappers/include/field_trial.h"
 
 namespace webrtc {
 namespace {
@@ -37,7 +37,8 @@
   if (size > kMaxInputLenBytes) {
     return;
   }
-
+  field_trial::InitFieldTrialsFromString(
+      "WebRTC-RFC8888CongestionControlFeedback/Enabled/");
   NullModuleRtpRtcp rtp_rtcp_module;
   SimulatedClock clock(1234);