MediaEngine: pass parameters-changed callback at construction

Refactor the parameters-changed notification mechanism by passing the
callback during the construction of media send channels rather than
setting it post-creation.

* Update MediaEngine interface and send channel factories to accept the
  absl::AnyInvocable parameters-changed callback at construction.
* Remove SetParametersChangedCallback.
* Promote OnParametersChanged to the RtpSenderInternal interface.
* Update RtpTransceiver to provide the callback and inform all senders.

Bug: webrtc:42222804
Change-Id: Ib9b2762615c73c22ca99e6051edec14a2c81b968
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/475080
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47828}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 7b37914..8be38ff 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -260,6 +260,7 @@
     "../rtc_base:stringutils",
     "../rtc_base/system:file_wrapper",
     "//third_party/abseil-cpp/absl/algorithm:container",
+    "//third_party/abseil-cpp/absl/functional:any_invocable",
   ]
 }
 
diff --git a/media/base/fake_media_engine.cc b/media/base/fake_media_engine.cc
index 02795f0..dff074e 100644
--- a/media/base/fake_media_engine.cc
+++ b/media/base/fake_media_engine.cc
@@ -619,11 +619,13 @@
   return scoped_refptr<AudioState>();
 }
 std::unique_ptr<VoiceMediaSendChannelInterface>
-FakeVoiceEngine::CreateSendChannel(const Environment& /*env*/,
-                                   Call* call,
-                                   const MediaConfig& /* config */,
-                                   const AudioOptions& options,
-                                   const CryptoOptions& /* crypto_options */) {
+FakeVoiceEngine::CreateSendChannel(
+    const Environment& /*env*/,
+    Call* call,
+    const MediaConfig& /* config */,
+    const AudioOptions& options,
+    const CryptoOptions& /* crypto_options */,
+    absl::AnyInvocable<void()> /* parameters_changed_callback */) {
   std::unique_ptr<FakeVoiceMediaSendChannel> ch =
       std::make_unique<FakeVoiceMediaSendChannel>(options,
                                                   call->network_thread());
@@ -702,7 +704,8 @@
     const CryptoOptions& /* crypto_options */,
     VideoBitrateAllocatorFactory* /* video_bitrate_allocator_factory */,
     VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-    /* video_encoder_switch_request_callback */) {
+    /* video_encoder_switch_request_callback */,
+    absl::AnyInvocable<void()> /* parameters_changed_callback */) {
   std::unique_ptr<FakeVideoMediaSendChannel> ch =
       std::make_unique<FakeVideoMediaSendChannel>(options,
                                                   call->network_thread());
diff --git a/media/base/fake_media_engine.h b/media/base/fake_media_engine.h
index 51b45b4..d677c38 100644
--- a/media/base/fake_media_engine.h
+++ b/media/base/fake_media_engine.h
@@ -826,7 +826,9 @@
       Call* call,
       const MediaConfig& config,
       const AudioOptions& options,
-      const CryptoOptions& crypto_options) override;
+      const CryptoOptions& crypto_options,
+      absl::AnyInvocable<void()> parameters_changed_callback =
+          nullptr) override;
   std::unique_ptr<VoiceMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
       Call* call,
@@ -953,7 +955,8 @@
       const CryptoOptions& crypto_options,
       VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
       VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-          video_encoder_switch_request_callback) override;
+          video_encoder_switch_request_callback,
+      absl::AnyInvocable<void()> parameters_changed_callback) override;
   std::unique_ptr<VideoMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
       Call* call,
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index e18dd1d..ee9d19d 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -257,10 +257,6 @@
   // Called whenever the list of sending SSRCs changes.
   virtual void SetSsrcListChangedCallback(
       absl::AnyInvocable<void(const std::set<uint32_t>&)> callback) = 0;
-  // Called whenever the parameters change autonomously on the worker thread.
-  // This is used for cache invalidation on the signaling thread.
-  virtual void SetParametersChangedCallback(
-      absl::AnyInvocable<void()> callback) {}
 };
 
 class MediaReceiveChannelInterface {
diff --git a/media/base/media_engine.h b/media/base/media_engine.h
index eced04b..67850ae 100644
--- a/media/base/media_engine.h
+++ b/media/base/media_engine.h
@@ -17,6 +17,7 @@
 #include <span>
 #include <vector>
 
+#include "absl/functional/any_invocable.h"
 #include "api/audio/audio_device.h"
 #include "api/audio_codecs/audio_decoder_factory.h"
 #include "api/audio_codecs/audio_encoder_factory.h"
@@ -98,7 +99,8 @@
       Call* call,
       const MediaConfig& config,
       const AudioOptions& options,
-      const CryptoOptions& crypto_options) = 0;
+      const CryptoOptions& crypto_options,
+      absl::AnyInvocable<void()> parameters_changed_callback = nullptr) = 0;
 
   // Safe to be called from the signaling thread.
   virtual std::unique_ptr<VoiceMediaReceiveChannelInterface>
@@ -127,7 +129,8 @@
       const CryptoOptions& crypto_options,
       VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
       VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-          video_encoder_switch_request_callback) = 0;
+          video_encoder_switch_request_callback,
+      absl::AnyInvocable<void()> parameters_changed_callback) = 0;
 
   // Safe to be called from the signaling thread.
   virtual std::unique_ptr<VideoMediaReceiveChannelInterface>
@@ -162,7 +165,9 @@
       Call* call,
       const MediaConfig& config,
       const AudioOptions& options,
-      const CryptoOptions& crypto_options) override = 0;
+      const CryptoOptions& crypto_options,
+      absl::AnyInvocable<void()> parameters_changed_callback =
+          nullptr) override = 0;
 
   std::unique_ptr<VoiceMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
@@ -214,7 +219,8 @@
       const CryptoOptions& crypto_options,
       VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
       VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-          video_encoder_switch_request_callback) override = 0;
+          video_encoder_switch_request_callback,
+      absl::AnyInvocable<void()> parameters_changed_callback) override = 0;
 
   std::unique_ptr<VideoMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 5b2b511..14e0ce7 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -814,11 +814,13 @@
     const CryptoOptions& crypto_options,
     VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
     VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-        video_encoder_switch_request_callback) {
+        video_encoder_switch_request_callback,
+    absl::AnyInvocable<void()> parameters_changed_callback) {
   return std::make_unique<WebRtcVideoSendChannel>(
       env, call, config, options, crypto_options, encoder_factory_.get(),
       video_bitrate_allocator_factory,
-      std::move(video_encoder_switch_request_callback));
+      std::move(video_encoder_switch_request_callback),
+      std::move(parameters_changed_callback));
 }
 std::unique_ptr<VideoMediaReceiveChannelInterface>
 WebRtcVideoEngine::CreateReceiveChannel(const Environment& env,
@@ -924,7 +926,8 @@
     VideoEncoderFactory* encoder_factory,
     VideoBitrateAllocatorFactory* bitrate_allocator_factory,
     VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-        video_encoder_switch_request_callback)
+        video_encoder_switch_request_callback,
+    absl::AnyInvocable<void()> parameters_changed_callback)
     : MediaChannelUtil(call->network_thread(), config.enable_dscp),
       env_(env),
       worker_thread_(call->worker_thread()),
@@ -936,6 +939,7 @@
       default_send_options_(options),
       last_send_stats_log_ms_(-1),
       crypto_options_(crypto_options),
+      parameters_changed_callback_(std::move(parameters_changed_callback)),
       encoder_switch_request_callback_(
           std::move(video_encoder_switch_request_callback)) {}
 
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index efa314a..79fad08 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -109,7 +109,8 @@
       const CryptoOptions& crypto_options,
       VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
       VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-          video_encoder_switch_request_callback) override;
+          video_encoder_switch_request_callback,
+      absl::AnyInvocable<void()> parameters_changed_callback) override;
   std::unique_ptr<VideoMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
       Call* call,
@@ -181,7 +182,8 @@
       VideoEncoderFactory* encoder_factory,
       VideoBitrateAllocatorFactory* bitrate_allocator_factory,
       VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-          video_encoder_switch_request_callback);
+          video_encoder_switch_request_callback,
+      absl::AnyInvocable<void()> parameters_changed_callback = nullptr);
   ~WebRtcVideoSendChannel() override;
 
   MediaType media_type() const override { return MediaType::VIDEO; }
@@ -249,12 +251,6 @@
     ssrc_list_changed_callback_ = std::move(callback);
   }
 
-  void SetParametersChangedCallback(
-      absl::AnyInvocable<void()> callback) override {
-    RTC_DCHECK_RUN_ON(worker_thread_);
-    parameters_changed_callback_ = std::move(callback);
-  }
-
   // Implemented for VideoMediaChannelTest.
   bool sending() const {
     RTC_DCHECK_RUN_ON(worker_thread_);
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index fae0d86..b3b6692 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -725,7 +725,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
 
   EXPECT_TRUE(send_channel->AddSendStream(StreamParams::CreateLegacy(123)));
 
@@ -741,7 +741,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   EXPECT_TRUE(send_channel->AddSendStream(StreamParams::CreateLegacy(123)));
   VideoMediaSendInfo send_info;
   send_channel->GetStats(&send_info);
@@ -855,7 +855,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), std::move(callback));
+          video_bitrate_allocator_factory_.get(), std::move(callback), nullptr);
 
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("VP8"));
@@ -1003,7 +1003,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   VideoSenderParameters parameters;
   // We need to look up the codec in the engine to get the correct payload type.
   for (const SdpVideoFormat& format : encoder_factory_->GetSupportedFormats()) {
@@ -1186,7 +1186,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(send_channel->SetSenderParameters(parameters));
@@ -1218,7 +1218,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("VP8"));
   EXPECT_TRUE(send_channel->SetSenderParameters(parameters));
@@ -1257,7 +1257,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(send_channel->SetSenderParameters(parameters));
@@ -1288,7 +1288,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine_->CreateSendChannel(
           env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(),
-          video_bitrate_allocator_factory_.get(), nullptr);
+          video_bitrate_allocator_factory_.get(), nullptr, nullptr);
 
   VideoSenderParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("H264"));
@@ -1557,7 +1557,7 @@
   std::unique_ptr<VideoMediaSendChannelInterface> send_channel =
       engine.CreateSendChannel(env, call.get(), GetMediaConfig(),
                                VideoOptions(), CryptoOptions(),
-                               rate_allocator_factory.get(), nullptr);
+                               rate_allocator_factory.get(), nullptr, nullptr);
 
   VideoSenderParameters send_parameters;
   send_parameters.codecs.push_back(engine_codecs.at(0));
@@ -1698,7 +1698,7 @@
         network_interface_(env_) {
     send_channel_ = engine_.CreateSendChannel(
         env_, call_.get(), MediaConfig(), VideoOptions(), CryptoOptions(),
-        video_bitrate_allocator_factory_.get(), nullptr);
+        video_bitrate_allocator_factory_.get(), nullptr, nullptr);
     receive_channel_ = engine_.CreateReceiveChannel(
         env_, call_.get(), MediaConfig(), VideoOptions(), CryptoOptions());
 
@@ -1883,7 +1883,7 @@
     media_config.video.enable_cpu_adaptation = false;
     send_channel_ = engine_->CreateSendChannel(
         env_, call_.get(), media_config, VideoOptions(), CryptoOptions(),
-        video_bitrate_allocator_factory_.get(), nullptr);
+        video_bitrate_allocator_factory_.get(), nullptr, nullptr);
     receive_channel_ = engine_->CreateReceiveChannel(
         env_, call_.get(), media_config, VideoOptions(), CryptoOptions());
     send_channel_->OnReadyToSend(true);
@@ -2789,7 +2789,8 @@
     fake_call_ = std::make_unique<FakeCall>(env_);
     send_channel_ = engine_->CreateSendChannel(
         env_, fake_call_.get(), GetMediaConfig(), VideoOptions(),
-        CryptoOptions(), video_bitrate_allocator_factory_.get(), nullptr);
+        CryptoOptions(), video_bitrate_allocator_factory_.get(), nullptr,
+        nullptr);
     receive_channel_ =
         engine_->CreateReceiveChannel(env_, fake_call_.get(), GetMediaConfig(),
                                       VideoOptions(), CryptoOptions());
@@ -3614,7 +3615,7 @@
 
   send_channel_ = engine_->CreateSendChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   receive_channel_ = engine_->CreateReceiveChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions());
   send_channel_->OnReadyToSend(true);
@@ -3627,7 +3628,7 @@
   media_config.video.suspend_below_min_bitrate = false;
   send_channel_ = engine_->CreateSendChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   receive_channel_ = engine_->CreateReceiveChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions());
   send_channel_->OnReadyToSend(true);
@@ -4243,7 +4244,7 @@
   media_config.video.enable_cpu_adaptation = true;
   send_channel_ = engine_->CreateSendChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   receive_channel_ = engine_->CreateReceiveChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions());
 
@@ -4297,7 +4298,7 @@
   media_config.video.enable_cpu_adaptation = true;
   send_channel_ = engine_->CreateSendChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   receive_channel_ = engine_->CreateReceiveChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions());
   send_channel_->OnReadyToSend(true);
@@ -4333,7 +4334,7 @@
   }
   send_channel_ = engine_->CreateSendChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   receive_channel_ = engine_->CreateReceiveChannel(
       env_, fake_call_.get(), media_config, VideoOptions(), CryptoOptions());
   send_channel_->OnReadyToSend(true);
@@ -5738,7 +5739,7 @@
 
   send_channel = engine_->CreateSendChannel(
       env_, call_.get(), config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
 
   send_channel->SetInterface(network_interface.get());
   // Default value when DSCP is disabled should be DSCP_DEFAULT.
@@ -5750,7 +5751,7 @@
   config.enable_dscp = true;
   send_channel = engine_->CreateSendChannel(
       env_, call_.get(), config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   send_channel->SetInterface(network_interface.get());
   EXPECT_EQ(DSCP_DEFAULT, network_interface->dscp());
 
@@ -5781,7 +5782,7 @@
   config.enable_dscp = false;
   send_channel = engine_->CreateSendChannel(
       env_, call_.get(), config, VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory_.get(), nullptr);
+      video_bitrate_allocator_factory_.get(), nullptr, nullptr);
   send_channel->SetInterface(network_interface.get());
   EXPECT_EQ(DSCP_DEFAULT, network_interface->dscp());
   send_channel->SetInterface(nullptr);
@@ -9808,7 +9809,7 @@
     decoder_factory_->AddSupportedVideoCodecType("VP8");
     send_channel_ = engine_.CreateSendChannel(
         env_, &fake_call_, GetMediaConfig(), VideoOptions(), CryptoOptions(),
-        mock_rate_allocator_factory_.get(), nullptr);
+        mock_rate_allocator_factory_.get(), nullptr, nullptr);
     receive_channel_ = engine_.CreateReceiveChannel(
         env_, &fake_call_, GetMediaConfig(), VideoOptions(), CryptoOptions());
     send_channel_->OnReadyToSend(true);
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index a074b80..9954e24 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -581,13 +581,16 @@
 }
 
 std::unique_ptr<VoiceMediaSendChannelInterface>
-WebRtcVoiceEngine::CreateSendChannel(const Environment& env,
-                                     Call* call,
-                                     const MediaConfig& config,
-                                     const AudioOptions& options,
-                                     const CryptoOptions& crypto_options) {
-  return std::make_unique<WebRtcVoiceSendChannel>(env, this, config, options,
-                                                  crypto_options, call);
+WebRtcVoiceEngine::CreateSendChannel(
+    const Environment& env,
+    Call* call,
+    const MediaConfig& config,
+    const AudioOptions& options,
+    const CryptoOptions& crypto_options,
+    absl::AnyInvocable<void()> parameters_changed_callback) {
+  return std::make_unique<WebRtcVoiceSendChannel>(
+      env, this, config, options, crypto_options, call,
+      std::move(parameters_changed_callback));
 }
 
 std::unique_ptr<VoiceMediaReceiveChannelInterface>
@@ -1262,7 +1265,8 @@
     const MediaConfig& config,
     const AudioOptions& options,
     const CryptoOptions& crypto_options,
-    Call* call)
+    Call* call,
+    absl::AnyInvocable<void()> parameters_changed_callback)
     : MediaChannelUtil(call->network_thread(), config.enable_dscp),
       env_(env),
       worker_thread_(call->worker_thread()),
@@ -1270,7 +1274,8 @@
       options_(options),
       call_(call),
       audio_config_(config.audio),
-      crypto_options_(crypto_options) {
+      crypto_options_(crypto_options),
+      parameters_changed_callback_(std::move(parameters_changed_callback)) {
   RTC_LOG(LS_VERBOSE) << "WebRtcVoiceSendChannel::WebRtcVoiceSendChannel";
   RTC_DCHECK(call);
 }
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 9d268b8..e45456b 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -99,7 +99,9 @@
       Call* call,
       const MediaConfig& config,
       const AudioOptions& options,
-      const CryptoOptions& crypto_options) override;
+      const CryptoOptions& crypto_options,
+      absl::AnyInvocable<void()> parameters_changed_callback =
+          nullptr) override;
 
   std::unique_ptr<VoiceMediaReceiveChannelInterface> CreateReceiveChannel(
       const Environment& env,
@@ -170,12 +172,14 @@
 class WebRtcVoiceSendChannel final : public MediaChannelUtil,
                                      public VoiceMediaSendChannelInterface {
  public:
-  WebRtcVoiceSendChannel(const Environment& env,
-                         WebRtcVoiceEngine* engine,
-                         const MediaConfig& config,
-                         const AudioOptions& options,
-                         const CryptoOptions& crypto_options,
-                         Call* call);
+  WebRtcVoiceSendChannel(
+      const Environment& env,
+      WebRtcVoiceEngine* engine,
+      const MediaConfig& config,
+      const AudioOptions& options,
+      const CryptoOptions& crypto_options,
+      Call* call,
+      absl::AnyInvocable<void()> parameters_changed_callback = nullptr);
 
   WebRtcVoiceSendChannel() = delete;
   WebRtcVoiceSendChannel(const WebRtcVoiceSendChannel&) = delete;
@@ -304,6 +308,9 @@
   // Callback invoked whenever the list of SSRCs changes.
   absl::AnyInvocable<void(const std::set<uint32_t>&)>
       ssrc_list_changed_callback_ RTC_GUARDED_BY(worker_thread_);
+
+  absl::AnyInvocable<void()> parameters_changed_callback_
+      RTC_GUARDED_BY(worker_thread_);
 };
 
 class WebRtcVoiceReceiveChannel final
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index 0f8480a..4c1a91f 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -250,7 +250,7 @@
       media_type_(media_type),
       stream_ids_(GetUniqueStreamIds(stream_ids)),
       send_codecs_(std::move(send_codecs)),
-      media_channel_(nullptr),  // Will be set in SetMediaChannel().
+      media_channel_(media_channel),
       set_streams_observer_(set_streams_observer),
       worker_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
           /*alive=*/media_channel != nullptr,
@@ -261,15 +261,6 @@
       enable_sframe_at_owner_(std::move(enable_sframe_at_owner)) {
   RTC_DCHECK(worker_thread_);
   init_parameters_.encodings = std::move(init_send_encodings);
-  if (media_channel) {
-    // When initialized with a valid media channel, we need to be running on the
-    // worker thread in order to set things up properly.
-    RTC_DCHECK_RUN_ON(worker_thread_);
-    SetMediaChannel(media_channel);
-  } else {
-    // Otherwise, we're less picky (but probably running on the signaling
-    // thread).
-  }
 }
 
 RtpSenderBase::~RtpSenderBase() {
@@ -340,31 +331,19 @@
     return;
   }
 
-  if (media_channel_) {
-    media_channel_->SetParametersChangedCallback(nullptr);
-  }
-
   // Note that setting the media_channel_ to nullptr and clearing the send state
   // via ClearSend_w, are separate operations. Stopping the actual send
   // operation, needs to be done via any of the paths that end up with a call to
   // ClearSend_w(), such as DetachTrackAndGetStopTask().
   media_channel_ = media_channel;
-  if (media_channel_) {
-    media_channel_->SetParametersChangedCallback(
-        [this] { OnParametersChanged(); });
-  }
   media_channel_ ? worker_safety_->SetAlive() : worker_safety_->SetNotAlive();
 }
 
 void RtpSenderBase::OnParametersChanged() {
-  RTC_DCHECK_RUN_ON(worker_thread_);
-  RTC_LOG(LS_INFO) << "RtpSender: OnParametersChanged signaled.";
-  signaling_thread_->PostTask(SafeTask(signaling_safety_.flag(), [this] {
-    RTC_DCHECK_RUN_ON(signaling_thread_);
-    cached_parameters_.reset();
-    last_transaction_id_.reset();
-    RTC_LOG(LS_INFO) << "RtpSender: OnParametersChanged cache cleared.";
-  }));
+  RTC_DCHECK_RUN_ON(signaling_thread_);
+  cached_parameters_.reset();
+  last_transaction_id_.reset();
+  RTC_LOG(LS_INFO) << "RtpSender: OnParametersChanged cache cleared.";
 }
 
 RtpParameters RtpSenderBase::GetParametersInternal(bool may_use_cache,
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index 1eeda57..1ddaa5a 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -122,6 +122,7 @@
   virtual std::vector<Codec> GetSendCodecs() const = 0;
 
   virtual void NotifyFirstPacketSent() = 0;
+  virtual void OnParametersChanged() = 0;
 };
 
 // Shared implementation for RtpSenderInternal interface.
@@ -253,6 +254,7 @@
   std::vector<Codec> GetSendCodecs() const override { return send_codecs_; }
 
   void NotifyFirstPacketSent() override;
+  void OnParametersChanged() override;
   void SetObserver(RtpSenderObserverInterface* observer) override;
 
  protected:
@@ -261,9 +263,6 @@
     cached_parameters_.reset();
   }
 
-  // Called by the media channel when parameters change autonomously on the
-  // worker thread (e.g., encoder fallback).
-  void OnParametersChanged();
   // If `set_streams_observer` is not null, it is invoked when SetStreams()
   // is called. `set_streams_observer` is not owned by this object. If not
   // null, it must be valid at least until this sender becomes stopped.
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index dad9fd6..5c72c1a 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -136,7 +136,7 @@
         env_, &fake_call_, MediaConfig(), AudioOptions(), CryptoOptions());
     video_media_send_channel_ = media_engine_->video().CreateSendChannel(
         env_, &fake_call_, MediaConfig(), VideoOptions(), CryptoOptions(),
-        video_bitrate_allocator_factory_.get(), nullptr);
+        video_bitrate_allocator_factory_.get(), nullptr, nullptr);
     voice_media_receive_channel_ = media_engine_->voice().CreateReceiveChannel(
         env_, &fake_call_, MediaConfig(), AudioOptions(), CryptoOptions());
     video_media_receive_channel_ = media_engine_->video().CreateReceiveChannel(
@@ -1376,7 +1376,7 @@
           CreateBuiltinVideoBitrateAllocatorFactory();
   auto video_media_send_channel = media_engine->video().CreateSendChannel(
       env, &fake_call, MediaConfig(), VideoOptions(), CryptoOptions(),
-      video_bitrate_allocator_factory.get(), nullptr);
+      video_bitrate_allocator_factory.get(), nullptr, nullptr);
 
   scoped_refptr<MediaStreamInterface> local_stream =
       MediaStream::Create(kStreamId1);
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index 9be0a7b..7133a44 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -232,11 +232,13 @@
     const CryptoOptions& crypto_options,
     VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
     VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
-        video_encoder_switch_request_callback = nullptr) {
+        video_encoder_switch_request_callback = nullptr,
+    absl::AnyInvocable<void()> parameters_changed_callback = nullptr) {
   if (media_type == MediaType::AUDIO) {
     RTC_DCHECK(voice_factory);
-    return {voice_factory->CreateSendChannel(env, call, media_config,
-                                             audio_options, crypto_options),
+    return {voice_factory->CreateSendChannel(
+                env, call, media_config, audio_options, crypto_options,
+                std::move(parameters_changed_callback)),
             voice_factory->CreateReceiveChannel(env, call, media_config,
                                                 audio_options, crypto_options)};
   }
@@ -244,7 +246,8 @@
   return {video_factory->CreateSendChannel(
               env, call, media_config, video_options, crypto_options,
               video_bitrate_allocator_factory,
-              std::move(video_encoder_switch_request_callback)),
+              std::move(video_encoder_switch_request_callback),
+              std::move(parameters_changed_callback)),
           video_factory->CreateReceiveChannel(env, call, media_config,
                                               video_options, crypto_options)};
 }
@@ -391,12 +394,14 @@
   }
 
   auto encoder_switch_callback = GetEncoderSwitchRequestCallback();
+  auto parameters_changed_callback = GetParametersChangedCallback();
   std::vector<Codec> send_codecs = GetSendCodecs();
 
   worker_tasks.AddWithFinalizer(
       [this, call, media_config, audio_options, video_options, crypto_options,
        video_bitrate_allocator_factory,
        encoder_switch_callback = std::move(encoder_switch_callback),
+       parameters_changed_callback = std::move(parameters_changed_callback),
        sender_id = std::string(sender_id), init_send_encodings,
        simulcast_rejected, initial_simulcast_layers, track, stream_ids,
        send_codecs = std::move(send_codecs),
@@ -406,8 +411,8 @@
         auto channels = CreateMediaContentChannels(
             media_type_, env_, voice_channel_factory(), video_channel_factory(),
             call, media_config, audio_options, video_options, crypto_options,
-            video_bitrate_allocator_factory,
-            std::move(encoder_switch_callback));
+            video_bitrate_allocator_factory, std::move(encoder_switch_callback),
+            std::move(parameters_changed_callback));
         auto sender = CreateSender(
             media_type_, env_, context_, legacy_stats_, set_streams_observer_,
             sender_id,
@@ -496,6 +501,9 @@
         OnPacketReceived(packet.Ssrc(), flag);
       };
 
+  auto encoder_switch_callback = GetEncoderSwitchRequestCallback();
+  auto parameters_changed_callback = GetParametersChangedCallback();
+
   // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to
   // the worker thread. We shouldn't be using the `call_ptr_` hack here but
   // simply be on the worker thread and use `call_` (update upstream code).
@@ -503,6 +511,8 @@
       [this, mid_str = std::string(mid), call_ptr, media_config, srtp_required,
        crypto_options, audio_options, video_options,
        video_bitrate_allocator_factory,
+       encoder_switch_callback = std::move(encoder_switch_callback),
+       parameters_changed_callback = std::move(parameters_changed_callback),
        callbacks = std::move(callbacks)]() mutable
           -> RTCErrorOr<ScopedOperationsBatcher::FinalizerTask> {
         RTC_DCHECK_RUN_ON(context()->worker_thread());
@@ -528,7 +538,8 @@
               media_type(), env_, voice_channel_factory(),
               video_channel_factory(), call_ptr, media_config, audio_options,
               video_options, crypto_options, video_bitrate_allocator_factory,
-              GetEncoderSwitchRequestCallback());
+              std::move(encoder_switch_callback),
+              std::move(parameters_changed_callback));
           media_send_channel = std::move(channels.first);
           media_receive_channel = std::move(channels.second);
           SetMediaChannels(media_send_channel.get(),
@@ -727,6 +738,18 @@
       };
 }
 
+absl::AnyInvocable<void()> RtpTransceiver::GetParametersChangedCallback() {
+  RTC_DCHECK(signaling_thread_safety_);
+  return [this, signaling_safety = signaling_thread_safety_]() {
+    thread_->PostTask(SafeTask(signaling_safety, [this]() {
+      RTC_DCHECK_RUN_ON(thread_);
+      for (const auto& sender : senders_) {
+        sender->internal()->OnParametersChanged();
+      }
+    }));
+  };
+}
+
 // RTC_RUN_ON(context()->worker_thread());
 void RtpTransceiver::ClearMediaChannelReferences() {
   SetMediaChannels(nullptr, nullptr);
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index c5ce21c..a7e0985 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -476,6 +476,8 @@
   VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
   GetEncoderSwitchRequestCallback();
 
+  absl::AnyInvocable<void()> GetParametersChangedCallback();
+
   RTCError UpdateCodecPreferencesCaches(
       const std::vector<RtpCodecCapability>& codecs);
   // Helper function for handling extensions during O/A
diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h
index 8b3c9a0..94d682c 100644
--- a/pc/test/mock_rtp_sender_internal.h
+++ b/pc/test/mock_rtp_sender_internal.h
@@ -146,6 +146,7 @@
               (const std::vector<std::string>&),
               (override));
   MOCK_METHOD(void, NotifyFirstPacketSent, (), (override));
+  MOCK_METHOD(void, OnParametersChanged, (), (override));
 };
 
 }  // namespace webrtc