Allow ulpfec payload type reconfig without recreating receive streams.

Bug: none
Change-Id: I1c5dad7811dd93552c185145e5a1488a8e9bb863
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/271000
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37771}
diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h
index 451203b..f4ed0a9 100644
--- a/call/video_receive_stream.h
+++ b/call/video_receive_stream.h
@@ -303,6 +303,8 @@
   // Must be called on the packet delivery thread.
   virtual void SetNackHistory(TimeDelta history) = 0;
 
+  virtual void SetUlpfecPayloadType(int ulpfec_payload_type) = 0;
+
  protected:
   virtual ~VideoReceiveStreamInterface() {}
 };
diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h
index c807d31..b076fd9 100644
--- a/media/engine/fake_webrtc_call.h
+++ b/media/engine/fake_webrtc_call.h
@@ -23,6 +23,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "absl/strings/string_view.h"
@@ -297,6 +298,10 @@
     config_.rtp.nack.rtp_history_ms = history.ms();
   }
 
+  void SetUlpfecPayloadType(int ulpfec_payload_type) override {
+    config_.rtp.ulpfec_payload_type = ulpfec_payload_type;
+  }
+
   void Start() override;
   void Stop() override;
 
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index c562abe..c39bf67 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -2972,14 +2972,14 @@
   ExtractCodecInformation(recv_codecs, rtx_associated_payload_types,
                           raw_payload_types, decoders);
 
-  bool recreate_needed = false;
-
   const auto& codec = recv_codecs.front();
   if (config_.rtp.ulpfec_payload_type != codec.ulpfec.ulpfec_payload_type) {
     config_.rtp.ulpfec_payload_type = codec.ulpfec.ulpfec_payload_type;
-    recreate_needed = true;
+    stream_->SetUlpfecPayloadType(config_.rtp.ulpfec_payload_type);
   }
 
+  bool recreate_needed = false;
+
   if (config_.rtp.red_payload_type != codec.ulpfec.red_payload_type) {
     config_.rtp.red_payload_type = codec.ulpfec.red_payload_type;
     recreate_needed = true;
diff --git a/video/rtp_video_stream_receiver2.cc b/video/rtp_video_stream_receiver2.cc
index 536d16f..9aa5f0a 100644
--- a/video/rtp_video_stream_receiver2.cc
+++ b/video/rtp_video_stream_receiver2.cc
@@ -124,22 +124,25 @@
 }
 
 std::unique_ptr<UlpfecReceiver> MaybeConstructUlpfecReceiver(
-    const VideoReceiveStreamInterface::Config::Rtp& rtp,
+    uint32_t remote_ssrc,
+    int red_payload_type,
+    int ulpfec_payload_type,
+    rtc::ArrayView<const RtpExtension> extensions,
     RecoveredPacketReceiver* callback,
     Clock* clock) {
-  RTC_DCHECK_GE(rtp.ulpfec_payload_type, -1);
-  if (rtp.red_payload_type == -1)
+  RTC_DCHECK_GE(red_payload_type, -1);
+  RTC_DCHECK_GE(ulpfec_payload_type, -1);
+  if (red_payload_type == -1)
     return nullptr;
 
   // TODO(tommi, brandtr): Consider including this check too once
   // `UlpfecReceiver` has been updated to not consider both red and ulpfec
   // payload ids.
-  //  if (rtp.ulpfec_payload_type == -1)
+  //  if (ulpfec_payload_type == -1)
   //    return nullptr;
 
-  return std::make_unique<UlpfecReceiver>(rtp.remote_ssrc,
-                                          rtp.ulpfec_payload_type, callback,
-                                          rtp.extensions, clock);
+  return std::make_unique<UlpfecReceiver>(remote_ssrc, ulpfec_payload_type,
+                                          callback, extensions, clock);
 }
 
 static const int kPacketLogIntervalMs = 10000;
@@ -256,7 +259,13 @@
       forced_playout_delay_max_ms_("max_ms", absl::nullopt),
       forced_playout_delay_min_ms_("min_ms", absl::nullopt),
       rtp_receive_statistics_(rtp_receive_statistics),
-      ulpfec_receiver_(MaybeConstructUlpfecReceiver(config->rtp, this, clock)),
+      ulpfec_receiver_(
+          MaybeConstructUlpfecReceiver(config->rtp.remote_ssrc,
+                                       config->rtp.red_payload_type,
+                                       config->rtp.ulpfec_payload_type,
+                                       config->rtp.extensions,
+                                       this,
+                                       clock_)),
       packet_sink_(config->rtp.packet_sink_),
       receiving_(false),
       last_packet_log_ms_(-1),
@@ -983,6 +992,18 @@
       history.ms() > 0 ? kMaxPacketAgeToNack : kDefaultMaxReorderingThreshold);
 }
 
+int RtpVideoStreamReceiver2::ulpfec_payload_type() const {
+  RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
+  return ulpfec_receiver_ ? ulpfec_receiver_->ulpfec_payload_type() : -1;
+}
+
+void RtpVideoStreamReceiver2::set_ulpfec_payload_type(int payload_type) {
+  RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
+  ulpfec_receiver_ = MaybeConstructUlpfecReceiver(
+      config_.rtp.remote_ssrc, config_.rtp.red_payload_type, payload_type,
+      config_.rtp.extensions, this, clock_);
+}
+
 absl::optional<int64_t> RtpVideoStreamReceiver2::LastReceivedPacketMs() const {
   RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
   if (last_received_rtp_system_time_) {
@@ -1040,16 +1061,18 @@
 void RtpVideoStreamReceiver2::ParseAndHandleEncapsulatingHeader(
     const RtpPacketReceived& packet) {
   RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
-  if (packet.PayloadType() == config_.rtp.red_payload_type &&
-      packet.payload_size() > 0) {
-    if (packet.payload()[0] == config_.rtp.ulpfec_payload_type) {
-      // Notify video_receiver about received FEC packets to avoid NACKing these
-      // packets.
-      NotifyReceiverOfEmptyPacket(packet.SequenceNumber());
-    }
-    if (ulpfec_receiver_ && ulpfec_receiver_->AddReceivedRedPacket(packet)) {
-      ulpfec_receiver_->ProcessReceivedFec();
-    }
+  RTC_DCHECK_EQ(packet.PayloadType(), config_.rtp.red_payload_type);
+
+  if (!ulpfec_receiver_ || packet.payload_size() == 0U)
+    return;
+
+  if (packet.payload()[0] == ulpfec_receiver_->ulpfec_payload_type()) {
+    // Notify video_receiver about received FEC packets to avoid NACKing these
+    // packets.
+    NotifyReceiverOfEmptyPacket(packet.SequenceNumber());
+  }
+  if (ulpfec_receiver_->AddReceivedRedPacket(packet)) {
+    ulpfec_receiver_->ProcessReceivedFec();
   }
 }
 
diff --git a/video/rtp_video_stream_receiver2.h b/video/rtp_video_stream_receiver2.h
index c341536..4fa216c 100644
--- a/video/rtp_video_stream_receiver2.h
+++ b/video/rtp_video_stream_receiver2.h
@@ -198,6 +198,9 @@
 
   void SetNackHistory(TimeDelta history);
 
+  int ulpfec_payload_type() const;
+  void set_ulpfec_payload_type(int payload_type);
+
   absl::optional<int64_t> LastReceivedPacketMs() const;
   absl::optional<int64_t> LastReceivedKeyframePacketMs() const;
 
@@ -320,7 +323,8 @@
   FieldTrialOptional<int> forced_playout_delay_max_ms_;
   FieldTrialOptional<int> forced_playout_delay_min_ms_;
   ReceiveStatistics* const rtp_receive_statistics_;
-  std::unique_ptr<UlpfecReceiver> ulpfec_receiver_;
+  std::unique_ptr<UlpfecReceiver> ulpfec_receiver_
+      RTC_GUARDED_BY(packet_sequence_checker_);
 
   RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_task_checker_;
   // TODO(bugs.webrtc.org/11993): This checker conceptually represents
diff --git a/video/video_receive_stream2.cc b/video/video_receive_stream2.cc
index 413c45a..5015c2d 100644
--- a/video/video_receive_stream2.cc
+++ b/video/video_receive_stream2.cc
@@ -349,7 +349,8 @@
   }
 
   const bool protected_by_fec =
-      config_.rtp.protected_by_flexfec || config_.rtp.ulpfec_payload_type != -1;
+      config_.rtp.protected_by_flexfec ||
+      rtp_video_stream_receiver_.ulpfec_payload_type() != -1;
 
   if (config_.rtp.nack.rtp_history_ms > 0 && protected_by_fec) {
     frame_buffer_->SetProtectionMode(kProtectionNackFEC);
@@ -530,7 +531,8 @@
   const_cast<int&>(config_.rtp.nack.rtp_history_ms) = history.ms();
 
   const bool protected_by_fec =
-      config_.rtp.protected_by_flexfec || config_.rtp.ulpfec_payload_type != -1;
+      config_.rtp.protected_by_flexfec ||
+      rtp_video_stream_receiver_.ulpfec_payload_type() != -1;
 
   frame_buffer_->SetProtectionMode(history.ms() > 0 && protected_by_fec
                                        ? kProtectionNackFEC
@@ -549,6 +551,11 @@
   frame_buffer_->SetMaxWaits(max_wait_for_keyframe, max_wait_for_frame);
 }
 
+void VideoReceiveStream2::SetUlpfecPayloadType(int payload_type) {
+  RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
+  rtp_video_stream_receiver_.set_ulpfec_payload_type(payload_type);
+}
+
 void VideoReceiveStream2::CreateAndRegisterExternalDecoder(
     const Decoder& decoder) {
   TRACE_EVENT0("webrtc",
diff --git a/video/video_receive_stream2.h b/video/video_receive_stream2.h
index 39ed2c3..fccec05 100644
--- a/video/video_receive_stream2.h
+++ b/video/video_receive_stream2.h
@@ -11,6 +11,7 @@
 #ifndef VIDEO_VIDEO_RECEIVE_STREAM2_H_
 #define VIDEO_VIDEO_RECEIVE_STREAM2_H_
 
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -150,6 +151,7 @@
   void SetFlexFecProtection(RtpPacketSinkInterface* flexfec_sink) override;
   void SetLossNotificationEnabled(bool enabled) override;
   void SetNackHistory(TimeDelta history) override;
+  void SetUlpfecPayloadType(int payload_type) override;
 
   webrtc::VideoReceiveStreamInterface::Stats GetStats() const override;