Replace EncoderSwitchRequestCallback with absl::AnyInvocable

This refactoring replaces the EncoderSwitchRequestCallback interface
with absl::AnyInvocable in VideoStreamEncoderSettings.

To ensure the callback survives stream recreation, the callback binding
has been moved to RecreateWebRtcStream, relying on a new `send_channel_`
back-pointer within WebRtcVideoSendStream.

Bug: b/478050997
Change-Id: I1f587d563553c4f2f3d8fd02c4d9ed4473829dc4
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/454480
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47087}
diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn
index 14cf360..d618604 100644
--- a/api/video/BUILD.gn
+++ b/api/video/BUILD.gn
@@ -349,6 +349,7 @@
     "../adaptation:resource_adaptation_api",
     "../units:data_rate",
     "../video_codecs:video_codecs_api",
+    "//third_party/abseil-cpp/absl/functional:any_invocable",
   ]
 }
 
diff --git a/api/video/video_stream_encoder_settings.h b/api/video/video_stream_encoder_settings.h
index d02bcbe..5936dc7 100644
--- a/api/video/video_stream_encoder_settings.h
+++ b/api/video/video_stream_encoder_settings.h
@@ -11,6 +11,9 @@
 #ifndef API_VIDEO_VIDEO_STREAM_ENCODER_SETTINGS_H_
 #define API_VIDEO_VIDEO_STREAM_ENCODER_SETTINGS_H_
 
+#include <optional>
+
+#include "absl/functional/any_invocable.h"
 #include "api/video/video_bitrate_allocator_factory.h"
 #include "api/video_codecs/sdp_video_format.h"
 #include "api/video_codecs/video_encoder.h"
@@ -18,18 +21,13 @@
 
 namespace webrtc {
 
-class EncoderSwitchRequestCallback {
- public:
-  virtual ~EncoderSwitchRequestCallback() {}
-
-  // Requests switch to next negotiated encoder.
-  virtual void RequestEncoderFallback() = 0;
-
-  // Requests switch to a specific encoder. If the encoder is not available and
-  // `allow_default_fallback` is `true` the default fallback is invoked.
-  virtual void RequestEncoderSwitch(const SdpVideoFormat& format,
-                                    bool allow_default_fallback) = 0;
-};
+// Requests switch to a specific encoder. If `format` is nullopt, a fallback
+// to the next negotiated encoder is requested. If the requested encoder is
+// not available and `allow_default_fallback` is `true`, the default fallback
+// is invoked.
+using EncoderSwitchRequestCallback =
+    absl::AnyInvocable<void(std::optional<SdpVideoFormat> format,
+                            bool allow_default_fallback)>;
 
 struct VideoStreamEncoderSettings {
   explicit VideoStreamEncoderSettings(
@@ -43,9 +41,6 @@
   // Ownership stays with WebrtcVideoEngine (delegated from PeerConnection).
   VideoEncoderFactory* encoder_factory = nullptr;
 
-  // Requests the WebRtcVideoChannel to perform a codec switch.
-  EncoderSwitchRequestCallback* encoder_switch_request_callback = nullptr;
-
   // Ownership stays with WebrtcVideoEngine (delegated from PeerConnection).
   VideoBitrateAllocatorFactory* bitrate_allocator_factory = nullptr;
 
diff --git a/call/BUILD.gn b/call/BUILD.gn
index dd7cbac..232c66a 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -68,6 +68,7 @@
     "../api/transport:network_control",
     "../api/units:time_delta",
     "../api/units:timestamp",
+    "../api/video:video_stream_encoder",
     "../modules/async_audio_processing",
     "../modules/congestion_controller/rtp:congestion_controller_feedback_stats",
     "../modules/rtp_rtcp",
@@ -323,6 +324,7 @@
     "../api/units:data_size",
     "../api/units:time_delta",
     "../api/units:timestamp",
+    "../api/video:video_stream_encoder",
     "../audio",
     "../logging:rtc_event_audio",
     "../logging:rtc_event_rtp_rtcp",
diff --git a/call/call.cc b/call/call.cc
index 052c8a7..40a35c9 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -43,6 +43,7 @@
 #include "api/units/data_size.h"
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
+#include "api/video/video_stream_encoder_settings.h"
 #include "audio/audio_receive_stream.h"
 #include "audio/audio_send_stream.h"
 #include "audio/audio_state.h"
@@ -225,10 +226,13 @@
 
   webrtc::VideoSendStream* CreateVideoSendStream(
       webrtc::VideoSendStream::Config config,
-      VideoEncoderConfig encoder_config) override;
+      VideoEncoderConfig encoder_config,
+      EncoderSwitchRequestCallback encoder_switch_request_callback =
+          nullptr) override;
   webrtc::VideoSendStream* CreateVideoSendStream(
       webrtc::VideoSendStream::Config config,
       VideoEncoderConfig encoder_config,
+      EncoderSwitchRequestCallback encoder_switch_request_callback,
       std::unique_ptr<FecController> fec_controller) override;
   void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) override;
 
@@ -499,6 +503,7 @@
 VideoSendStream* Call::CreateVideoSendStream(
     VideoSendStream::Config /* config */,
     VideoEncoderConfig /* encoder_config */,
+    EncoderSwitchRequestCallback /* encoder_switch_request_callback */,
     std::unique_ptr<FecController> /* fec_controller */) {
   return nullptr;
 }
@@ -879,6 +884,7 @@
 webrtc::VideoSendStream* Call::CreateVideoSendStream(
     webrtc::VideoSendStream::Config config,
     VideoEncoderConfig encoder_config,
+    EncoderSwitchRequestCallback encoder_switch_request_callback,
     std::unique_ptr<FecController> fec_controller) {
   TRACE_EVENT0("webrtc", "Call::CreateVideoSendStream");
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -902,7 +908,8 @@
       transport_send_.get(), config_.encode_metronome, bitrate_allocator_.get(),
       video_send_delay_stats_.get(), std::move(config),
       std::move(encoder_config), suspended_video_send_ssrcs_,
-      suspended_video_payload_states_, std::move(fec_controller));
+      suspended_video_payload_states_, std::move(fec_controller),
+      std::move(encoder_switch_request_callback));
 
   for (uint32_t ssrc : ssrcs) {
     RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end());
@@ -923,7 +930,8 @@
 
 webrtc::VideoSendStream* Call::CreateVideoSendStream(
     webrtc::VideoSendStream::Config config,
-    VideoEncoderConfig encoder_config) {
+    VideoEncoderConfig encoder_config,
+    EncoderSwitchRequestCallback encoder_switch_request_callback) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   if (config_.fec_controller_factory) {
     RTC_LOG(LS_INFO) << "External FEC Controller will be used.";
@@ -933,6 +941,7 @@
           ? config_.fec_controller_factory->CreateFecController(env_)
           : std::make_unique<FecControllerDefault>(env_);
   return CreateVideoSendStream(std::move(config), std::move(encoder_config),
+                               std::move(encoder_switch_request_callback),
                                std::move(fec_controller));
 }
 
diff --git a/call/call.h b/call/call.h
index 8f58370..636c0f9 100644
--- a/call/call.h
+++ b/call/call.h
@@ -24,6 +24,7 @@
 #include "api/scoped_refptr.h"
 #include "api/task_queue/task_queue_base.h"
 #include "api/transport/bitrate_settings.h"
+#include "api/video/video_stream_encoder_settings.h"
 #include "call/audio_receive_stream.h"
 #include "call/audio_send_stream.h"
 #include "call/call_config.h"
@@ -80,11 +81,14 @@
 
   virtual VideoSendStream* CreateVideoSendStream(
       VideoSendStream::Config config,
-      VideoEncoderConfig encoder_config) = 0;
+      VideoEncoderConfig encoder_config,
+      EncoderSwitchRequestCallback encoder_switch_request_callback =
+          nullptr) = 0;
   virtual VideoSendStream* CreateVideoSendStream(
       VideoSendStream::Config config,
       VideoEncoderConfig encoder_config,
-      std::unique_ptr<FecController> fec_controller);
+      EncoderSwitchRequestCallback encoder_switch_request_callback,
+      std::unique_ptr<FecController> fec_controller) = 0;
   virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0;
 
   virtual VideoReceiveStreamInterface* CreateVideoReceiveStream(
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 47e6d7b..fbe70c9 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -857,6 +857,7 @@
       "../api/video:video_frame",
       "../api/video:video_frame_type",
       "../api/video:video_rtp_headers",
+      "../api/video:video_stream_encoder",
       "../api/video_codecs:scalability_mode",
       "../api/video_codecs:video_codecs_api",
       "../call:call_interfaces",
diff --git a/media/engine/fake_webrtc_call.cc b/media/engine/fake_webrtc_call.cc
index cc537a4..20ed986 100644
--- a/media/engine/fake_webrtc_call.cc
+++ b/media/engine/fake_webrtc_call.cc
@@ -12,6 +12,7 @@
 
 #include <cstdint>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -24,6 +25,7 @@
 #include "api/call/audio_sink.h"
 #include "api/crypto/frame_decryptor_interface.h"
 #include "api/environment/environment.h"
+#include "api/fec_controller.h"
 #include "api/frame_transformer_interface.h"
 #include "api/make_ref_counted.h"
 #include "api/media_types.h"
@@ -35,6 +37,7 @@
 #include "api/task_queue/task_queue_base.h"
 #include "api/units/timestamp.h"
 #include "api/video/video_source_interface.h"
+#include "api/video/video_stream_encoder_settings.h"
 #include "api/video_codecs/video_codec.h"
 #include "api/video_codecs/video_encoder.h"
 #include "call/audio_receive_stream.h"
@@ -586,7 +589,20 @@
 
 VideoSendStream* FakeCall::CreateVideoSendStream(
     VideoSendStream::Config config,
-    VideoEncoderConfig encoder_config) {
+    VideoEncoderConfig encoder_config,
+    EncoderSwitchRequestCallback encoder_switch_request_callback) {
+  FakeVideoSendStream* fake_stream = new FakeVideoSendStream(
+      env_, std::move(config), std::move(encoder_config));
+  video_send_streams_.push_back(fake_stream);
+  ++num_created_send_streams_;
+  return fake_stream;
+}
+
+VideoSendStream* FakeCall::CreateVideoSendStream(
+    VideoSendStream::Config config,
+    VideoEncoderConfig encoder_config,
+    EncoderSwitchRequestCallback encoder_switch_request_callback,
+    std::unique_ptr<FecController> fec_controller) {
   FakeVideoSendStream* fake_stream = new FakeVideoSendStream(
       env_, std::move(config), std::move(encoder_config));
   video_send_streams_.push_back(fake_stream);
diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h
index ac12bd5..e4dff9a 100644
--- a/media/engine/fake_webrtc_call.h
+++ b/media/engine/fake_webrtc_call.h
@@ -37,6 +37,7 @@
 #include "api/audio_codecs/audio_format.h"
 #include "api/crypto/frame_decryptor_interface.h"
 #include "api/environment/environment.h"
+#include "api/fec_controller.h"
 #include "api/frame_transformer_interface.h"
 #include "api/media_types.h"
 #include "api/rtp_headers.h"
@@ -51,6 +52,7 @@
 #include "api/video/video_frame.h"
 #include "api/video/video_sink_interface.h"
 #include "api/video/video_source_interface.h"
+#include "api/video/video_stream_encoder_settings.h"
 #include "api/video_codecs/video_codec.h"
 #include "call/audio_receive_stream.h"
 #include "call/audio_send_stream.h"
@@ -441,7 +443,15 @@
 
   VideoSendStream* CreateVideoSendStream(
       VideoSendStream::Config config,
-      VideoEncoderConfig encoder_config) override;
+      VideoEncoderConfig encoder_config,
+      EncoderSwitchRequestCallback encoder_switch_request_callback =
+          nullptr) override;
+
+  VideoSendStream* CreateVideoSendStream(
+      VideoSendStream::Config config,
+      VideoEncoderConfig encoder_config,
+      EncoderSwitchRequestCallback encoder_switch_request_callback,
+      std::unique_ptr<FecController> fec_controller) override;
   void DestroyVideoSendStream(VideoSendStream* send_stream) override;
 
   VideoReceiveStreamInterface* CreateVideoReceiveStream(
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index f1cd60d..8bfffdc 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -1239,68 +1239,63 @@
   return ApplyChangedParams(changed_params);
 }
 
-void WebRtcVideoSendChannel::RequestEncoderFallback() {
-  if (!worker_thread_->IsCurrent()) {
-    worker_thread_->PostTask(
-        SafeTask(task_safety_.flag(), [this] { RequestEncoderFallback(); }));
-    return;
-  }
-
-  RTC_DCHECK_RUN_ON(&thread_checker_);
-  if (negotiated_codecs_.size() <= 1) {
-    RTC_LOG(LS_WARNING) << "Encoder failed but no fallback codec is available";
-    return;
-  }
-
-  ChangedSenderParameters params;
-  params.negotiated_codecs = negotiated_codecs_;
-  params.negotiated_codecs->erase(params.negotiated_codecs->begin());
-  params.send_codec = params.negotiated_codecs->front();
-  if (ApplyChangedParams(params) && parameters_changed_callback_) {
-    parameters_changed_callback_();
-  }
-}
-
-void WebRtcVideoSendChannel::RequestEncoderSwitch(const SdpVideoFormat& format,
-                                                  bool allow_default_fallback) {
+void WebRtcVideoSendChannel::RequestEncoderSwitch(
+    std::optional<SdpVideoFormat> format,
+    bool allow_default_fallback) {
   if (!worker_thread_->IsCurrent()) {
     worker_thread_->PostTask(
         SafeTask(task_safety_.flag(), [this, format, allow_default_fallback] {
-          RequestEncoderSwitch(format, allow_default_fallback);
+          RequestEncoderSwitch(std::move(format), allow_default_fallback);
         }));
     return;
   }
 
   RTC_DCHECK_RUN_ON(&thread_checker_);
+  RTC_DCHECK(format.has_value() || allow_default_fallback);
 
-  for (const VideoCodecSettings& codec_setting : negotiated_codecs_) {
-    if (format.IsSameCodec(
-            {codec_setting.codec.name, codec_setting.codec.params})) {
-      VideoCodecSettings new_codec_setting = codec_setting;
-      for (const auto& kv : format.parameters) {
-        new_codec_setting.codec.params[kv.first] = kv.second;
-      }
+  ChangedSenderParameters params;
+  if (!format) {
+    if (negotiated_codecs_.size() <= 1) {
+      RTC_LOG(LS_WARNING)
+          << "Encoder failed but no fallback codec is available";
+      return;
+    }
 
-      if (send_codec() == new_codec_setting) {
-        // Already using this codec, no switch required.
-        return;
-      }
+    params.negotiated_codecs = negotiated_codecs_;
+    params.negotiated_codecs->erase(params.negotiated_codecs->begin());
+    params.send_codec = params.negotiated_codecs->front();
+  } else {
+    auto it = absl::c_find_if(
+        negotiated_codecs_, [&](const VideoCodecSettings& codec_setting) {
+          return format->IsSameCodec(
+              {codec_setting.codec.name, codec_setting.codec.params});
+        });
+    if (it == negotiated_codecs_.end()) {
+      RTC_LOG(LS_WARNING) << "Failed to switch encoder to: "
+                          << format->ToString()
+                          << ". Is default fallback allowed: "
+                          << allow_default_fallback;
 
-      ChangedSenderParameters params;
-      params.send_codec = new_codec_setting;
-      if (ApplyChangedParams(params) && parameters_changed_callback_) {
-        parameters_changed_callback_();
+      if (allow_default_fallback) {
+        RequestEncoderSwitch(std::nullopt, true);
       }
       return;
     }
+
+    VideoCodecSettings new_codec_setting = *it;
+    for (const auto& kv : format->parameters) {
+      new_codec_setting.codec.params[kv.first] = kv.second;
+    }
+
+    if (send_codec() == new_codec_setting) {
+      return;
+    }
+
+    params.send_codec = new_codec_setting;
   }
 
-  RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " << format.ToString()
-                      << ". Is default fallback allowed: "
-                      << allow_default_fallback;
-
-  if (allow_default_fallback) {
-    RequestEncoderFallback();
+  if (ApplyChangedParams(params) && parameters_changed_callback_) {
+    parameters_changed_callback_();
   }
 }
 
@@ -1581,7 +1576,6 @@
   config.encoder_settings.encoder_factory = encoder_factory_;
   config.encoder_settings.bitrate_allocator_factory =
       bitrate_allocator_factory_;
-  config.encoder_settings.encoder_switch_request_callback = this;
 
   config.crypto_options = crypto_options_;
   config.rtp.extmap_allow_mixed = ExtmapAllowMixed();
@@ -1590,7 +1584,7 @@
       video_config_.enable_send_packet_batching;
 
   WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(
-      env_, call_, sp, std::move(config), default_send_options_,
+      this, env_, call_, sp, std::move(config), default_send_options_,
       video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps,
       send_codec(), send_codecs_, send_rtp_extensions_, send_params_);
 
@@ -1814,6 +1808,7 @@
       codec_settings_list(codec_settings_list) {}
 
 WebRtcVideoSendChannel::WebRtcVideoSendStream::WebRtcVideoSendStream(
+    WebRtcVideoSendChannel* send_channel,
     const Environment& env,
     Call* call,
     const StreamParams& sp,
@@ -1827,7 +1822,8 @@
     // TODO(deadbeef): Don't duplicate information between send_params,
     // rtp_extensions, options, etc.
     const VideoSenderParameters& send_params)
-    : env_(env),
+    : send_channel_(send_channel),
+      env_(env),
       worker_thread_(call->worker_thread()),
       ssrcs_(sp.ssrcs),
       ssrc_groups_(sp.ssrc_groups),
@@ -2702,6 +2698,13 @@
       ConfigureVideoEncoderSettings(parameters_.codec_settings->codec);
 
   VideoSendStream::Config config = parameters_.config.Copy();
+
+  auto encoder_switch_request_callback =
+      [send_channel = send_channel_](std::optional<SdpVideoFormat> format,
+                                     bool allow_default_fallback) {
+        send_channel->RequestEncoderSwitch(std::move(format),
+                                           allow_default_fallback);
+      };
   if (!config.rtp.rtx.ssrcs.empty() && config.rtp.rtx.payload_type == -1) {
     RTC_LOG(LS_WARNING) << "RTX SSRCs configured but there's no configured RTX "
                            "payload type the set codec. Ignoring RTX.";
@@ -2728,8 +2731,9 @@
     // GetStats and DestroyVideoSendStream.
     VideoSendStream::Stats stats = stream_->GetStats();
     call_->DestroyVideoSendStream(stream_);
-    stream_ = call_->CreateVideoSendStream(std::move(config),
-                                           parameters_.encoder_config.Copy());
+    stream_ = call_->CreateVideoSendStream(
+        std::move(config), parameters_.encoder_config.Copy(),
+        std::move(encoder_switch_request_callback));
 
     // A new stream is created without any scaling or limitations, so these
     // flags don't apply until the new stream experiences an adaptation event.
@@ -2741,8 +2745,9 @@
 
     stream_->SetStats(stats);
   } else {
-    stream_ = call_->CreateVideoSendStream(std::move(config),
-                                           parameters_.encoder_config.Copy());
+    stream_ = call_->CreateVideoSendStream(
+        std::move(config), parameters_.encoder_config.Copy(),
+        std::move(encoder_switch_request_callback));
   }
   if (!rtp_parameters_.encodings.empty() &&
       rtp_parameters_.encodings[0].csrcs.has_value()) {
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index f0a3412..307768f 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -47,7 +47,6 @@
 #include "api/video/video_frame.h"
 #include "api/video/video_sink_interface.h"
 #include "api/video/video_source_interface.h"
-#include "api/video/video_stream_encoder_settings.h"
 #include "api/video_codecs/sdp_video_format.h"
 #include "api/video_codecs/video_encoder_factory.h"
 #include "call/call.h"
@@ -159,8 +158,7 @@
 };
 
 class WebRtcVideoSendChannel : public MediaChannelUtil,
-                               public VideoMediaSendChannelInterface,
-                               public EncoderSwitchRequestCallback {
+                               public VideoMediaSendChannelInterface {
  public:
   WebRtcVideoSendChannel(
       const Environment& env,
@@ -253,10 +251,10 @@
     ADAPTREASON_BANDWIDTH = 2,
   };
 
-  // Implements EncoderSwitchRequestCallback.
-  void RequestEncoderFallback() override;
-  void RequestEncoderSwitch(const SdpVideoFormat& format,
-                            bool allow_default_fallback) override;
+  // Called to request an encoder switch or fallback.
+  // See also EncoderSwitchRequestCallback.
+  void RequestEncoderSwitch(std::optional<SdpVideoFormat> format,
+                            bool allow_default_fallback);
 
   void GenerateSendKeyFrame(uint32_t ssrc,
                             const std::vector<std::string>& rids) override;
@@ -318,6 +316,7 @@
   class WebRtcVideoSendStream {
    public:
     WebRtcVideoSendStream(
+        WebRtcVideoSendChannel* send_channel,
         const Environment& env,
         Call* call,
         const StreamParams& sp,
@@ -402,6 +401,7 @@
     DegradationPreference GetDegradationPreference() const
         RTC_EXCLUSIVE_LOCKS_REQUIRED(&thread_checker_);
 
+    WebRtcVideoSendChannel* const send_channel_;
     const Environment env_;
     RTC_NO_UNIQUE_ADDRESS SequenceChecker thread_checker_;
     TaskQueueBase* const worker_thread_;
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 311d728..d6f9d38 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -2598,19 +2598,19 @@
   ASSERT_TRUE(codec);
   EXPECT_EQ("VP9", codec->name);
 
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
   codec = send_channel_->GetSendCodec();
   ASSERT_TRUE(codec);
   EXPECT_EQ("AV1", codec->name);
 
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
   codec = send_channel_->GetSendCodec();
   ASSERT_TRUE(codec);
   EXPECT_EQ("VP8", codec->name);
 
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
 
   FrameForwarder frame_forwarder;
@@ -2620,7 +2620,7 @@
 
 #if defined(RTC_ENABLE_VP9)
 
-TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderFallback) {
+TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchWithNullopt) {
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("VP9"));
   parameters.codecs.push_back(GetEngineCodec("VP8"));
@@ -2630,16 +2630,16 @@
   ASSERT_TRUE(codec);
   EXPECT_EQ("VP9", codec->name);
 
-  // RequestEncoderFallback will post a task to the worker thread (which is also
+  // RequestEncoderSwitch will post a task to the worker thread (which is also
   // the current thread), hence the ProcessMessages call.
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
   codec = send_channel_->GetSendCodec();
   ASSERT_TRUE(codec);
   EXPECT_EQ("VP8", codec->name);
 
   // No other codec to fall back to, keep using VP8.
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
   codec = send_channel_->GetSendCodec();
   ASSERT_TRUE(codec);
@@ -2712,9 +2712,9 @@
   ASSERT_EQ(send_codecs.size(), 2u);
   EXPECT_THAT("VP9", send_codecs[0].name);
 
-  // RequestEncoderFallback will post a task to the worker thread (which is also
+  // RequestEncoderSwitch will post a task to the worker thread (which is also
   // the current thread), hence the ProcessMessages call.
-  SendImpl()->RequestEncoderFallback();
+  SendImpl()->RequestEncoderSwitch(std::nullopt, true);
   time_controller_.AdvanceTime(kFrameDuration);
 
   send_codecs = send_channel_->GetRtpSendParameters(kSsrc).codecs;
@@ -3903,8 +3903,6 @@
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimStreamParams("cname", ssrcs));
 
-  VideoSendStream::Config config = stream->GetConfig().Copy();
-
   FrameForwarder frame_forwarder;
   EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder));
   send_channel_->SetSend(true);
@@ -3932,7 +3930,7 @@
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimStreamParams("cname", ssrcs));
 
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   // Despite 3 ssrcs provided, single layer is used.
   EXPECT_EQ(1u, config.rtp.ssrcs.size());
@@ -3994,8 +3992,6 @@
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimStreamParams("cname", ssrcs));
 
-  VideoSendStream::Config config = stream->GetConfig().Copy();
-
   FrameForwarder frame_forwarder;
   EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder));
   send_channel_->SetSend(true);
@@ -4037,8 +4033,6 @@
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimStreamParams("cname", ssrcs));
 
-  VideoSendStream::Config config = stream->GetConfig().Copy();
-
   FrameForwarder frame_forwarder;
   EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder));
   send_channel_->SetSend(true);
@@ -4386,7 +4380,7 @@
   const std::vector<uint32_t> rtx_ssrcs = MAKE_VECTOR(kRtxSsrcs1);
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs));
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   // Make sure NACK and FEC are enabled on the correct payload types.
   EXPECT_EQ(1000, config.rtp.nack.rtp_history_ms);
@@ -4405,7 +4399,7 @@
   EXPECT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  const VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
   EXPECT_FALSE(config.rtp.raw_payload);
 }
 
@@ -4416,7 +4410,7 @@
   EXPECT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  const VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
   EXPECT_TRUE(config.rtp.raw_payload);
 }
 
@@ -4426,7 +4420,7 @@
 // default.
 TEST_F(WebRtcVideoChannelTest, FlexfecSendCodecWithoutSsrcNotExposedByDefault) {
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.flexfec.payload_type);
   EXPECT_EQ(0U, config.rtp.flexfec.ssrc);
@@ -4436,7 +4430,7 @@
 TEST_F(WebRtcVideoChannelTest, FlexfecSendCodecWithSsrcNotExposedByDefault) {
   FakeVideoSendStream* stream = AddSendStream(
       CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc));
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.flexfec.payload_type);
   EXPECT_EQ(0U, config.rtp.flexfec.ssrc);
@@ -4634,7 +4628,7 @@
 
 TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetDefaultSendCodecsWithoutSsrc) {
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type);
   EXPECT_EQ(0U, config.rtp.flexfec.ssrc);
@@ -4644,7 +4638,7 @@
 TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetDefaultSendCodecsWithSsrc) {
   FakeVideoSendStream* stream = AddSendStream(
       CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc));
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type);
   EXPECT_EQ(kFlexfecSsrc, config.rtp.flexfec.ssrc);
@@ -4658,7 +4652,7 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.ulpfec.ulpfec_payload_type);
   EXPECT_EQ(-1, config.rtp.ulpfec.red_payload_type);
@@ -4670,7 +4664,7 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.flexfec.payload_type);
 }
@@ -4717,7 +4711,7 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.flexfec.payload_type);
   EXPECT_EQ(0u, config.rtp.flexfec.ssrc);
@@ -4733,7 +4727,7 @@
 
   FakeVideoSendStream* stream = AddSendStream(
       CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc));
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(-1, config.rtp.flexfec.payload_type);
   EXPECT_EQ(0u, config.rtp.flexfec.ssrc);
@@ -4820,7 +4814,7 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
 
   FakeVideoSendStream* stream = AddSendStream();
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(GetEngineCodec("ulpfec").id, config.rtp.ulpfec.ulpfec_payload_type);
 
@@ -4828,8 +4822,8 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
   stream = fake_call_->GetVideoSendStreams()[0];
   ASSERT_TRUE(stream != nullptr);
-  config = stream->GetConfig().Copy();
-  EXPECT_EQ(-1, config.rtp.ulpfec.ulpfec_payload_type)
+  const VideoSendStream::Config& config2 = stream->GetConfig();
+  EXPECT_EQ(-1, config2.rtp.ulpfec.ulpfec_payload_type)
       << "SetSendCodec without ULPFEC should disable current ULPFEC.";
 }
 
@@ -4842,7 +4836,7 @@
 
   FakeVideoSendStream* stream = AddSendStream(
       CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc));
-  VideoSendStream::Config config = stream->GetConfig().Copy();
+  const VideoSendStream::Config& config = stream->GetConfig();
 
   EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type);
   EXPECT_EQ(kFlexfecSsrc, config.rtp.flexfec.ssrc);
@@ -4853,8 +4847,8 @@
   ASSERT_TRUE(send_channel_->SetSenderParameters(parameters));
   stream = fake_call_->GetVideoSendStreams()[0];
   ASSERT_TRUE(stream != nullptr);
-  config = stream->GetConfig().Copy();
-  EXPECT_EQ(-1, config.rtp.flexfec.payload_type)
+  const VideoSendStream::Config& config2 = stream->GetConfig();
+  EXPECT_EQ(-1, config2.rtp.flexfec.payload_type)
       << "SetSendCodec without FlexFEC should disable current FlexFEC.";
 }
 
diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc
index d0d8352..f2fcba4 100644
--- a/pc/peer_connection_rtp_unittest.cc
+++ b/pc/peer_connection_rtp_unittest.cc
@@ -36,7 +36,6 @@
 #include "api/test/rtc_error_matchers.h"
 #include "api/units/data_rate.h"
 #include "api/video/render_resolution.h"
-#include "api/video/video_stream_encoder_settings.h"
 #include "api/video_codecs/sdp_video_format.h"
 #include "api/video_codecs/video_decoder_factory_template.h"
 #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
@@ -2138,11 +2137,10 @@
   worker_thread->BlockingCall([&] {
     // Simulate VideoStreamEncoder calling RequestEncoderSwitch after getting
     // the format from the EncoderSelector.
-    auto* switch_callback = static_cast<webrtc::EncoderSwitchRequestCallback*>(
-        static_cast<webrtc::WebRtcVideoSendChannel*>(
-            media_channel->AsVideoSendChannel()));
-    switch_callback->RequestEncoderSwitch(format_to_switch_to,
-                                          /*allow_default_fallback=*/false);
+    auto* video_send_channel = static_cast<webrtc::WebRtcVideoSendChannel*>(
+        media_channel->AsVideoSendChannel());
+    video_send_channel->RequestEncoderSwitch(std::move(format_to_switch_to),
+                                             /*allow_default_fallback=*/false);
   });
 
   // Allow the signaling thread to process the cache invalidation task posted
@@ -2186,15 +2184,13 @@
 
   worker_thread->BlockingCall([&] {
     // Simulate an encoder fallback on the worker thread.
-    auto* fallback_callback =
-        static_cast<webrtc::EncoderSwitchRequestCallback*>(
-            static_cast<webrtc::WebRtcVideoSendChannel*>(
-                media_channel->AsVideoSendChannel()));
-    fallback_callback->RequestEncoderFallback();
+    auto* video_send_channel = static_cast<webrtc::WebRtcVideoSendChannel*>(
+        media_channel->AsVideoSendChannel());
+    video_send_channel->RequestEncoderSwitch(std::nullopt, true);
   });
 
   // Allow the signaling thread to process the cache invalidation task posted
-  // by RequestEncoderFallback.
+  // by RequestEncoderSwitch(std::nullopt, true).
   run_loop_.Flush();
 
   // Call GetParameters. This triggers an internal consistency check in the
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index 5205092..0701dca 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -389,7 +389,7 @@
   // This should be possible without a blocking call to the worker, perhaps done
   // asynchronously. At the moment this is complicated by the fact that
   // construction of the channels actually changes the settings of the engine.
-  context_->worker_thread()->BlockingCall([&]() mutable {
+  context_->worker_thread()->BlockingCall([&]() {
     RTC_DCHECK_RUN_ON(this->context()->worker_thread());
     auto channels = CreateMediaContentChannels(
         media_type_, env_, media_engine(), call, media_config, audio_options,
diff --git a/test/call_test.cc b/test/call_test.cc
index c25a6c8..40cbc44 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -607,7 +607,7 @@
     if (fec_controller_factory_) {
       video_send_streams_[i] = sender_call_->CreateVideoSendStream(
           video_send_configs_[i].Copy(), video_encoder_configs_[i].Copy(),
-          fec_controller_factory_->CreateFecController(send_env_));
+          nullptr, fec_controller_factory_->CreateFecController(send_env_));
     } else {
       video_send_streams_[i] = sender_call_->CreateVideoSendStream(
           video_send_configs_[i].Copy(), video_encoder_configs_[i].Copy());
diff --git a/test/scenario/video_stream.cc b/test/scenario/video_stream.cc
index 8cffa68..86e5111 100644
--- a/test/scenario/video_stream.cc
+++ b/test/scenario/video_stream.cc
@@ -428,7 +428,7 @@
   sender_->SendTask([&] {
     if (config.stream.fec_controller_factory) {
       send_stream_ = sender_->call_->CreateVideoSendStream(
-          std::move(send_config), std::move(encoder_config),
+          std::move(send_config), std::move(encoder_config), nullptr,
           config.stream.fec_controller_factory->CreateFecController(
               sender_->env_));
     } else {
diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc
index d6ff87e..ad9130b 100644
--- a/video/video_send_stream_impl.cc
+++ b/video/video_send_stream_impl.cc
@@ -343,11 +343,12 @@
     const Environment& env,
     int num_cpu_cores,
     SendStatisticsProxy* stats_proxy,
-    const VideoStreamEncoderSettings& encoder_settings,
+    VideoStreamEncoderSettings encoder_settings,
     VideoStreamEncoder::BitrateAllocationCallbackType
         bitrate_allocation_callback_type,
     Metronome* metronome,
-    VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+    VideoEncoderFactory::EncoderSelectorInterface* encoder_selector,
+    EncoderSwitchRequestCallback encoder_switch_request_callback) {
   std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue =
       env.task_queue_factory().CreateTaskQueue(
           "VideoEncoderQueue",
@@ -356,13 +357,13 @@
               : TaskQueueFactory::Priority::kNormal);
   TaskQueueBase* encoder_queue_ptr = encoder_queue.get();
   return std::make_unique<VideoStreamEncoder>(
-      env, num_cpu_cores, stats_proxy, encoder_settings,
+      env, num_cpu_cores, stats_proxy, std::move(encoder_settings),
       std::make_unique<OveruseFrameDetector>(env, stats_proxy),
       FrameCadenceAdapterInterface::Create(
           &env.clock(), encoder_queue_ptr, metronome,
           /*worker_queue=*/TaskQueueBase::Current(), env.field_trials()),
       std::move(encoder_queue), bitrate_allocation_callback_type,
-      encoder_selector);
+      encoder_selector, std::move(encoder_switch_request_callback));
 }
 
 bool HasActiveEncodings(const VideoEncoderConfig& config) {
@@ -398,6 +399,7 @@
     const std::map<uint32_t, RtpState>& suspended_ssrcs,
     const std::map<uint32_t, RtpPayloadState>& suspended_payload_states,
     std::unique_ptr<FecController> fec_controller,
+    EncoderSwitchRequestCallback encoder_switch_request_callback,
     std::unique_ptr<VideoStreamEncoderInterface> video_stream_encoder_for_test)
     : env_(env),
       transport_(transport),
@@ -415,11 +417,12 @@
                     env_,
                     num_cpu_cores,
                     &stats_proxy_,
-                    config_.encoder_settings,
+                    std::move(config_.encoder_settings),
                     GetBitrateAllocationCallbackType(config_,
                                                      env_.field_trials()),
                     metronome,
-                    config_.encoder_selector)),
+                    config_.encoder_selector,
+                    std::move(encoder_switch_request_callback))),
       encoder_feedback_(
           env_,
           SupportsPerLayerPictureLossIndication(
diff --git a/video/video_send_stream_impl.h b/video/video_send_stream_impl.h
index 9ed2308..88845a1 100644
--- a/video/video_send_stream_impl.h
+++ b/video/video_send_stream_impl.h
@@ -41,6 +41,7 @@
 #include "api/video/video_frame.h"
 #include "api/video/video_layers_allocation.h"
 #include "api/video/video_source_interface.h"
+#include "api/video/video_stream_encoder_settings.h"
 #include "api/video_codecs/video_encoder.h"
 #include "call/bitrate_allocator.h"
 #include "call/rtp_config.h"
@@ -88,20 +89,22 @@
   using RtpStateMap = std::map<uint32_t, RtpState>;
   using RtpPayloadStateMap = std::map<uint32_t, RtpPayloadState>;
 
-  VideoSendStreamImpl(const Environment& env,
-                      int num_cpu_cores,
-                      RtcpRttStats* call_stats,
-                      RtpTransportControllerSendInterface* transport,
-                      Metronome* metronome,
-                      BitrateAllocatorInterface* bitrate_allocator,
-                      SendDelayStats* send_delay_stats,
-                      VideoSendStream::Config config,
-                      VideoEncoderConfig encoder_config,
-                      const RtpStateMap& suspended_ssrcs,
-                      const RtpPayloadStateMap& suspended_payload_states,
-                      std::unique_ptr<FecController> fec_controller,
-                      std::unique_ptr<VideoStreamEncoderInterface>
-                          video_stream_encoder_for_test = nullptr);
+  VideoSendStreamImpl(
+      const Environment& env,
+      int num_cpu_cores,
+      RtcpRttStats* call_stats,
+      RtpTransportControllerSendInterface* transport,
+      Metronome* metronome,
+      BitrateAllocatorInterface* bitrate_allocator,
+      SendDelayStats* send_delay_stats,
+      VideoSendStream::Config config,
+      VideoEncoderConfig encoder_config,
+      const RtpStateMap& suspended_ssrcs,
+      const RtpPayloadStateMap& suspended_payload_states,
+      std::unique_ptr<FecController> fec_controller,
+      EncoderSwitchRequestCallback encoder_switch_request_callback = nullptr,
+      std::unique_ptr<VideoStreamEncoderInterface>
+          video_stream_encoder_for_test = nullptr);
   ~VideoSendStreamImpl() override;
 
   void DeliverRtcp(std::span<const uint8_t> packet);
diff --git a/video/video_send_stream_impl_unittest.cc b/video/video_send_stream_impl_unittest.cc
index 1c30024..4bead83 100644
--- a/video/video_send_stream_impl_unittest.cc
+++ b/video/video_send_stream_impl_unittest.cc
@@ -207,7 +207,9 @@
         /*metronome=*/nullptr, &bitrate_allocator_, &send_delay_stats_,
         config_.Copy(), std::move(encoder_config), suspended_ssrcs,
         suspended_payload_states,
-        /*fec_controller=*/nullptr, std::move(video_stream_encoder));
+        /*fec_controller=*/nullptr,
+        /*encoder_switch_request_callback=*/nullptr,
+        std::move(video_stream_encoder));
 
     // The call to GetStartBitrate() executes asynchronously on the tq.
     // Ensure all tasks get to run.
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 3a56e77..177ae42 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -706,23 +706,26 @@
     const Environment& env,
     uint32_t number_of_cores,
     VideoStreamEncoderObserver* encoder_stats_observer,
-    const VideoStreamEncoderSettings& settings,
+    VideoStreamEncoderSettings settings,
     std::unique_ptr<OveruseFrameDetector> overuse_detector,
     std::unique_ptr<FrameCadenceAdapterInterface> frame_cadence_adapter,
     std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue,
     BitrateAllocationCallbackType allocation_cb_type,
-    VideoEncoderFactory::EncoderSelectorInterface* encoder_selector)
+    VideoEncoderFactory::EncoderSelectorInterface* encoder_selector,
+    EncoderSwitchRequestCallback encoder_switch_request_callback)
     : env_(env),
       worker_queue_(TaskQueueBase::Current()),
       number_of_cores_(number_of_cores),
-      settings_(settings),
+      settings_(std::move(settings)),
+      encoder_switch_request_callback_(
+          std::move(encoder_switch_request_callback)),
       allocation_cb_type_(allocation_cb_type),
       rate_control_settings_(env_.field_trials()),
       encoder_selector_from_constructor_(encoder_selector),
       encoder_selector_from_factory_(
           encoder_selector_from_constructor_
               ? nullptr
-              : settings.encoder_factory->GetEncoderSelector()),
+              : settings_.encoder_factory->GetEncoderSelector()),
       encoder_selector_(encoder_selector_from_constructor_
                             ? encoder_selector_from_constructor_
                             : encoder_selector_from_factory_.get()),
@@ -1568,7 +1571,7 @@
 
 void VideoStreamEncoder::RequestEncoderSwitch() {
   bool is_encoder_switching_supported =
-      settings_.encoder_switch_request_callback != nullptr;
+      encoder_switch_request_callback_ != nullptr;
   bool is_encoder_selector_available = encoder_selector_ != nullptr;
 
   RTC_LOG(LS_INFO) << "RequestEncoderSwitch."
@@ -1591,7 +1594,8 @@
     if (!env_.field_trials().IsDisabled(
             kSwitchEncoderFollowCodecPreferenceOrderFieldTrial)) {
       encoder_fallback_requested_ = true;
-      settings_.encoder_switch_request_callback->RequestEncoderFallback();
+      encoder_switch_request_callback_(std::nullopt,
+                                       /*allow_default_fallback=*/false);
       return;
     } else {
       preferred_fallback_encoder =
@@ -1599,8 +1603,8 @@
     }
   }
 
-  settings_.encoder_switch_request_callback->RequestEncoderSwitch(
-      *preferred_fallback_encoder, /*allow_default_fallback=*/true);
+  encoder_switch_request_callback_(*preferred_fallback_encoder,
+                                   /*allow_default_fallback=*/true);
 }
 
 void VideoStreamEncoder::OnEncoderSettingsChanged() {
@@ -1936,11 +1940,11 @@
       video_frame.is_texture() != last_frame_info_->is_texture) {
     if ((!last_frame_info_ || video_frame.width() != last_frame_info_->width ||
          video_frame.height() != last_frame_info_->height) &&
-        settings_.encoder_switch_request_callback && encoder_selector_) {
+        encoder_switch_request_callback_ && encoder_selector_) {
       if (auto encoder = encoder_selector_->OnResolutionChange(
               {video_frame.width(), video_frame.height()})) {
-        settings_.encoder_switch_request_callback->RequestEncoderSwitch(
-            *encoder, /*allow_default_fallback=*/false);
+        encoder_switch_request_callback_(*encoder,
+                                         /*allow_default_fallback=*/false);
       }
     }
 
@@ -2526,11 +2530,11 @@
   const bool video_is_suspended = target_bitrate == DataRate::Zero();
   const bool video_suspension_changed = video_is_suspended != EncoderPaused();
 
-  if (!video_is_suspended && settings_.encoder_switch_request_callback &&
+  if (!video_is_suspended && encoder_switch_request_callback_ &&
       encoder_selector_) {
     if (auto encoder = encoder_selector_->OnAvailableBitrate(link_allocation)) {
-      settings_.encoder_switch_request_callback->RequestEncoderSwitch(
-          *encoder, /*allow_default_fallback=*/false);
+      encoder_switch_request_callback_(*encoder,
+                                       /*allow_default_fallback=*/false);
     }
   }
 
diff --git a/video/video_stream_encoder.h b/video/video_stream_encoder.h
index 2e28008..f6c4ca7 100644
--- a/video/video_stream_encoder.h
+++ b/video/video_stream_encoder.h
@@ -93,13 +93,13 @@
       const Environment& env,
       uint32_t number_of_cores,
       VideoStreamEncoderObserver* encoder_stats_observer,
-      const VideoStreamEncoderSettings& settings,
+      VideoStreamEncoderSettings settings,
       std::unique_ptr<OveruseFrameDetector> overuse_detector,
       std::unique_ptr<FrameCadenceAdapterInterface> frame_cadence_adapter,
       std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue,
       BitrateAllocationCallbackType allocation_cb_type,
-      VideoEncoderFactory::EncoderSelectorInterface* encoder_selector =
-          nullptr);
+      VideoEncoderFactory::EncoderSelectorInterface* encoder_selector = nullptr,
+      EncoderSwitchRequestCallback encoder_switch_request_callback = nullptr);
   ~VideoStreamEncoder() override;
 
   VideoStreamEncoder(const VideoStreamEncoder&) = delete;
@@ -307,6 +307,7 @@
 
   EncoderSink* sink_ = nullptr;
   const VideoStreamEncoderSettings settings_;
+  EncoderSwitchRequestCallback encoder_switch_request_callback_;
   const BitrateAllocationCallbackType allocation_cb_type_;
   const RateControlSettings rate_control_settings_;
 
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 5a3eba7..41c91a7 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -138,6 +138,7 @@
 using ::testing::Lt;
 using ::testing::Matcher;
 using ::testing::Mock;
+using ::testing::MockFunction;
 using ::testing::NiceMock;
 using ::testing::Optional;
 using ::testing::Return;
@@ -440,21 +441,24 @@
       std::unique_ptr<FrameCadenceAdapterInterface> cadence_adapter,
       std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue,
       SendStatisticsProxy* stats_proxy,
-      const VideoStreamEncoderSettings& settings,
+      VideoStreamEncoderSettings settings,
       VideoStreamEncoder::BitrateAllocationCallbackType
           allocation_callback_type,
-      int num_cores)
+      int num_cores,
+      EncoderSwitchRequestCallback encoder_switch_request_callback = nullptr)
       : VideoStreamEncoder(
             env,
             num_cores,
             stats_proxy,
-            settings,
+            std::move(settings),
             std::unique_ptr<OveruseFrameDetector>(
                 overuse_detector_proxy_ =
                     new CpuOveruseDetectorProxy(env, stats_proxy)),
             std::move(cadence_adapter),
             std::move(encoder_queue),
-            allocation_callback_type),
+            allocation_callback_type,
+            nullptr,  // encoder_selector
+            std::move(encoder_switch_request_callback)),
         time_controller_(time_controller),
         fake_cpu_resource_(FakeResource::Create("FakeResource[CPU]")),
         fake_quality_resource_(FakeResource::Create("FakeResource[QP]")),
@@ -760,7 +764,7 @@
     auto result = std::make_unique<AdaptedVideoStreamEncoder>(
         env_,
         /*number_of_cores=*/1,
-        /*stats_proxy=*/stats_proxy_.get(), encoder_settings_,
+        /*stats_proxy=*/stats_proxy_.get(), std::move(encoder_settings_),
         std::make_unique<CpuOveruseDetectorProxy>(env_,
                                                   /*stats_proxy=*/nullptr),
         std::move(zero_hertz_adapter), std::move(encoder_queue),
@@ -902,7 +906,8 @@
             video_send_config_,
             VideoEncoderConfig::ContentType::kRealtimeVideo,
             field_trials_)),
-        sink_(&time_controller_, &fake_encoder_) {}
+        sink_(&time_controller_, &fake_encoder_),
+        encoder_switch_request_callback_(nullptr) {}
 
   void SetUp() override {
     metrics::Reset();
@@ -953,11 +958,12 @@
       env_ = factory.Create();
     }
 
+    VideoStreamEncoderSettings settings = video_send_config_.encoder_settings;
     video_stream_encoder_ = std::make_unique<VideoStreamEncoderUnderTest>(
         env_, &time_controller_, std::move(cadence_adapter),
-        std::move(encoder_queue), stats_proxy_.get(),
-        video_send_config_.encoder_settings, allocation_callback_type,
-        num_cores);
+        std::move(encoder_queue), stats_proxy_.get(), std::move(settings),
+        allocation_callback_type, num_cores,
+        std::move(encoder_switch_request_callback_));
     video_stream_encoder_->SetSink(&sink_, /*rotation_applied=*/false);
     video_stream_encoder_->SetSource(&video_source_,
                                      DegradationPreference::MAINTAIN_FRAMERATE);
@@ -1770,6 +1776,7 @@
   TestSink sink_;
   AdaptingFrameForwarder video_source_{&time_controller_};
   std::unique_ptr<VideoStreamEncoderUnderTest> video_stream_encoder_;
+  EncoderSwitchRequestCallback encoder_switch_request_callback_;
 };
 
 TEST_F(VideoStreamEncoderTest, EncodeOneFrame) {
@@ -8429,13 +8436,6 @@
   video_stream_encoder_->Stop();
 }
 
-struct MockEncoderSwitchRequestCallback : public EncoderSwitchRequestCallback {
-  MOCK_METHOD(void, RequestEncoderFallback, (), (override));
-  MOCK_METHOD(void,
-              RequestEncoderSwitch,
-              (const SdpVideoFormat& format, bool allow_default_fallback),
-              (override));
-};
 
 TEST_F(VideoStreamEncoderTest, EncoderSelectorCurrentEncoderIsSignaled) {
   constexpr int kDontCare = 100;
@@ -8465,9 +8465,9 @@
   constexpr int kDontCare = 100;
 
   NiceMock<MockEncoderSelector> encoder_selector;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &fake_encoder_, &encoder_selector);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8478,8 +8478,8 @@
   ON_CALL(encoder_selector, OnAvailableBitrate)
       .WillByDefault(Return(SdpVideoFormat("AV1")));
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "AV1"),
-                                   /*allow_default_fallback=*/false));
+              Call(Optional(Field(&SdpVideoFormat::name, "AV1")),
+                   /*allow_default_fallback=*/false));
 
   video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources(
       /*target_bitrate=*/DataRate::KilobitsPerSec(50),
@@ -8495,9 +8495,9 @@
 
 TEST_F(VideoStreamEncoderTest, EncoderSelectorResolutionSwitch) {
   NiceMock<MockEncoderSelector> encoder_selector;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &fake_encoder_, &encoder_selector);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8510,8 +8510,8 @@
   EXPECT_CALL(encoder_selector, OnResolutionChange(RenderResolution(320, 240)))
       .WillOnce(Return(SdpVideoFormat("AV1")));
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "AV1"),
-                                   /*allow_default_fallback=*/false));
+              Call(Optional(Field(&SdpVideoFormat::name, "AV1")),
+                   /*allow_default_fallback=*/false));
 
   video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources(
       /*target_bitrate=*/DataRate::KilobitsPerSec(800),
@@ -8536,9 +8536,9 @@
 
   NiceMock<MockVideoEncoder> video_encoder;
   NiceMock<MockEncoderSelector> encoder_selector;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, &encoder_selector);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8564,8 +8564,8 @@
 
   Event encode_attempted;
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "AV2"),
-                                   /*allow_default_fallback=*/true))
+              Call(Optional(Field(&SdpVideoFormat::name, "AV2")),
+                   /*allow_default_fallback=*/true))
       .WillOnce([&encode_attempted]() { encode_attempted.Set(); });
 
   video_source_.IncomingCapturedFrame(CreateFrame(1, kDontCare, kDontCare));
@@ -8585,9 +8585,9 @@
 TEST_F(VideoStreamEncoderTest, SwitchEncoderOnInitFailureWithEncoderSelector) {
   NiceMock<MockVideoEncoder> video_encoder;
   NiceMock<MockEncoderSelector> encoder_selector;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, &encoder_selector);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8608,8 +8608,8 @@
 
   Event encode_attempted;
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "AV2"),
-                                   /*allow_default_fallback=*/true))
+              Call(Optional(Field(&SdpVideoFormat::name, "AV2")),
+                   /*allow_default_fallback=*/true))
       .WillOnce([&encode_attempted]() { encode_attempted.Set(); });
 
   video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
@@ -8632,9 +8632,9 @@
       "WebRTC-SwitchEncoderFollowCodecPreferenceOrder", "Disabled");
 
   NiceMock<MockVideoEncoder> video_encoder;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, /*encoder_selector=*/nullptr);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8656,8 +8656,8 @@
 
   Event encode_attempted;
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "VP8"),
-                                   /*allow_default_fallback=*/true))
+              Call(Optional(Field(&SdpVideoFormat::name, "VP8")),
+                   /*allow_default_fallback=*/true))
       .WillOnce([&encode_attempted]() { encode_attempted.Set(); });
 
   video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
@@ -8682,9 +8682,9 @@
   constexpr int kDontCare = 100;
 
   NiceMock<MockEncoderSelector> encoder_selector;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory =
       std::make_unique<test::VideoEncoderNullableProxyFactory>(
           /*encoder=*/nullptr, &encoder_selector);
@@ -8706,8 +8706,8 @@
       .WillByDefault(Return(SdpVideoFormat("AV2")));
   Event encode_attempted;
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "AV2"),
-                                   /*allow_default_fallback=*/_))
+              Call(Optional(Field(&SdpVideoFormat::name, "AV2")),
+                   /*allow_default_fallback=*/_))
       .WillOnce([&encode_attempted]() { encode_attempted.Set(); });
 
   video_source_.IncomingCapturedFrame(CreateFrame(1, kDontCare, kDontCare));
@@ -8730,9 +8730,9 @@
   constexpr int kNumFrames = 8;
 
   NiceMock<MockVideoEncoder> video_encoder;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, /*encoder_selector=*/nullptr);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8754,7 +8754,7 @@
   EXPECT_CALL(video_encoder, Encode)
       .WillOnce(Return(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE));
 
-  EXPECT_CALL(switch_callback, RequestEncoderFallback());
+  EXPECT_CALL(switch_callback, Call(Eq(std::nullopt), _));
 
   VideoFrame frame = CreateFrame(1, kDontCare, kDontCare);
   for (int i = 0; i < kNumFrames; ++i) {
@@ -8791,9 +8791,9 @@
       "WebRTC-SwitchEncoderFollowCodecPreferenceOrder", "Enabled");
 
   NiceMock<MockVideoEncoder> video_encoder;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, /*encoder_selector=*/nullptr);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8820,7 +8820,7 @@
       .WillOnce(Return(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE))
       .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK));
 
-  EXPECT_CALL(switch_callback, RequestEncoderFallback());
+  EXPECT_CALL(switch_callback, Call(Eq(std::nullopt), _));
 
   // Encode() will be called once and will return a failure code. All subsequent
   // frames will be dropped.
@@ -8866,9 +8866,9 @@
       "WebRTC-SwitchEncoderFollowCodecPreferenceOrder", "Disabled");
 
   NiceMock<MockVideoEncoder> video_encoder;
-  StrictMock<MockEncoderSwitchRequestCallback> switch_callback;
-  video_send_config_.encoder_settings.encoder_switch_request_callback =
-      &switch_callback;
+  StrictMock<MockFunction<void(std::optional<SdpVideoFormat>, bool)>>
+      switch_callback;
+  encoder_switch_request_callback_ = switch_callback.AsStdFunction();
   auto encoder_factory = std::make_unique<test::VideoEncoderProxyFactory>(
       &video_encoder, /*encoder_selector=*/nullptr);
   video_send_config_.encoder_settings.encoder_factory = encoder_factory.get();
@@ -8896,8 +8896,8 @@
 
   // Fallback request will be asking for switching to VP8.
   EXPECT_CALL(switch_callback,
-              RequestEncoderSwitch(Field(&SdpVideoFormat::name, "VP8"),
-                                   /*allow_default_fallback=*/true));
+              Call(Optional(Field(&SdpVideoFormat::name, "VP8")),
+                   /*allow_default_fallback=*/true));
 
   VideoFrame frame = CreateFrame(1, kDontCare, kDontCare);
   video_source_.IncomingCapturedFrame(frame);
@@ -10433,7 +10433,7 @@
   // doing anything else. This should be fine since the posted init task will
   // simply be deleted.
   VideoStreamEncoder encoder(
-      env, 1, &stats_proxy, encoder_settings,
+      env, 1, &stats_proxy, std::move(encoder_settings),
       std::make_unique<CpuOveruseDetectorProxy>(env, &stats_proxy),
       std::move(adapter), std::move(encoder_queue),
       VideoStreamEncoder::BitrateAllocationCallbackType::