RtpSenderInterface::SetEncoderSelector

This cl/ adds a way of setting an EncoderSelector on a specific
RtpSenderInterface. This makes it possible to easily use different
EncoderSelector on different streams within the same or different PeerConnections.

The cl/ is almost identical to the impl. of RtpSenderInterface::SetFrameEncryptor.

Iff a EncoderSelector is set on the RtpSender, it will take precedence
over the VideoEncoderFactory::GetEncoderSelector.

Bug: webrtc:14122
Change-Id: Ief4f7c06df7f1ef4ce3245de304a48e9de0ad587
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264542
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37150}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 2f46c69..afad769 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -217,6 +217,7 @@
     "video:video_bitrate_allocator_factory",
     "video:video_frame",
     "video:video_rtp_headers",
+    "video_codecs:video_codecs_api",
 
     # Basically, don't add stuff here. You might break sensitive downstream
     # targets like pnacl. API should not depend on anything outside of this
@@ -968,6 +969,17 @@
     ]
   }
 
+  rtc_library("mock_encoder_selector") {
+    visibility = [ "*" ]
+    testonly = true
+    sources = [ "test/mock_encoder_selector.h" ]
+    deps = [
+      ":libjingle_peerconnection_api",
+      "../api/video_codecs:video_codecs_api",
+      "../test:test_support",
+    ]
+  }
+
   rtc_library("fake_frame_encryptor") {
     visibility = [ "*" ]
     testonly = true
diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h
index 9ffad68..48ea864 100644
--- a/api/rtp_sender_interface.h
+++ b/api/rtp_sender_interface.h
@@ -14,6 +14,7 @@
 #ifndef API_RTP_SENDER_INTERFACE_H_
 #define API_RTP_SENDER_INTERFACE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -26,6 +27,7 @@
 #include "api/rtc_error.h"
 #include "api/rtp_parameters.h"
 #include "api/scoped_refptr.h"
+#include "api/video_codecs/video_encoder_factory.h"
 #include "rtc_base/ref_count.h"
 #include "rtc_base/system/rtc_export.h"
 
@@ -96,6 +98,12 @@
   virtual void SetEncoderToPacketizerFrameTransformer(
       rtc::scoped_refptr<FrameTransformerInterface> frame_transformer);
 
+  // Sets a user defined encoder selector.
+  // Overrides selector that is (optionally) provided by VideoEncoderFactory.
+  virtual void SetEncoderSelector(
+      std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>
+          encoder_selector) {}
+
  protected:
   ~RtpSenderInterface() override = default;
 };
diff --git a/api/test/mock_encoder_selector.h b/api/test/mock_encoder_selector.h
new file mode 100644
index 0000000..2e018d5
--- /dev/null
+++ b/api/test/mock_encoder_selector.h
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TEST_MOCK_ENCODER_SELECTOR_H_
+#define API_TEST_MOCK_ENCODER_SELECTOR_H_
+
+#include "api/video_codecs/video_encoder_factory.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockEncoderSelector
+    : public VideoEncoderFactory::EncoderSelectorInterface {
+ public:
+  MOCK_METHOD(void,
+              OnCurrentEncoder,
+              (const SdpVideoFormat& format),
+              (override));
+
+  MOCK_METHOD(absl::optional<SdpVideoFormat>,
+              OnAvailableBitrate,
+              (const DataRate& rate),
+              (override));
+
+  MOCK_METHOD(absl::optional<SdpVideoFormat>,
+              OnResolutionChange,
+              (const RenderResolution& resolution),
+              (override));
+
+  MOCK_METHOD(absl::optional<SdpVideoFormat>, OnEncoderBroken, (), (override));
+};
+
+}  // namespace webrtc
+
+#endif  // API_TEST_MOCK_ENCODER_SELECTOR_H_
diff --git a/api/video_codecs/video_encoder_factory.h b/api/video_codecs/video_encoder_factory.h
index 2914a41..d28a2a4 100644
--- a/api/video_codecs/video_encoder_factory.h
+++ b/api/video_codecs/video_encoder_factory.h
@@ -34,7 +34,10 @@
   };
 
   // An injectable class that is continuously updated with encoding conditions
-  // and selects the best encoder given those conditions.
+  // and selects the best encoder given those conditions. An implementation is
+  // typically stateful to avoid toggling between different encoders, which is
+  // costly due to recreation of objects, a new codec will always start with a
+  // key-frame.
   class EncoderSelectorInterface {
    public:
     virtual ~EncoderSelectorInterface() {}
@@ -96,6 +99,22 @@
   virtual std::unique_ptr<VideoEncoder> CreateVideoEncoder(
       const SdpVideoFormat& format) = 0;
 
+  // This method creates a EncoderSelector to use for a VideoSendStream.
+  // (and hence should probably been called CreateEncoderSelector()).
+  //
+  // Note: This method is unsuitable if encoding several streams that
+  // are using same VideoEncoderFactory (either by several streams in one
+  // PeerConnection or streams with different PeerConnection but same
+  // PeerConnectionFactory). This is due to the fact that the method is not
+  // given any stream identifier, nor is the EncoderSelectorInterface given any
+  // stream identifiers, i.e one does not know which stream is being encoded
+  // with help of the selector.
+  //
+  // In such scenario, the `RtpSenderInterface::SetEncoderSelector` is
+  // recommended.
+  //
+  // TODO(bugs.webrtc.org:14122): Deprecate and remove in favor of
+  // `RtpSenderInterface::SetEncoderSelector`.
   virtual std::unique_ptr<EncoderSelectorInterface> GetEncoderSelector() const {
     return nullptr;
   }
diff --git a/call/video_send_stream.h b/call/video_send_stream.h
index 356d8c8..1202a23 100644
--- a/call/video_send_stream.h
+++ b/call/video_send_stream.h
@@ -190,6 +190,11 @@
     // default.
     rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor;
 
+    // An optional encoder selector provided by the user.
+    // Overrides VideoEncoderFactory::GetEncoderSelector().
+    // Owned by RtpSenderBase.
+    VideoEncoderFactory::EncoderSelectorInterface* encoder_selector = nullptr;
+
     // Per PeerConnection cryptography options.
     CryptoOptions crypto_options;
 
diff --git a/media/BUILD.gn b/media/BUILD.gn
index ac6c378..9370200 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -587,6 +587,7 @@
         ":rtc_simulcast_encoder_adapter",
         "../api:create_simulcast_test_fixture_api",
         "../api:libjingle_peerconnection_api",
+        "../api:mock_encoder_selector",
         "../api:mock_video_bitrate_allocator",
         "../api:mock_video_bitrate_allocator_factory",
         "../api:mock_video_codec_factory",
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index bb07f15..ec78ec8 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -34,6 +34,7 @@
 #include "api/video/video_source_interface.h"
 #include "api/video/video_timing.h"
 #include "api/video_codecs/video_encoder_config.h"
+#include "api/video_codecs/video_encoder_factory.h"
 #include "call/video_receive_stream.h"
 #include "common_video/include/quality_limitation_reason.h"
 #include "media/base/codec.h"
@@ -244,6 +245,13 @@
   // Enable network condition based codec switching.
   virtual void SetVideoCodecSwitchingEnabled(bool enabled);
 
+  // note: The encoder_selector object must remain valid for the lifetime of the
+  // MediaChannel, unless replaced.
+  virtual void SetEncoderSelector(
+      uint32_t ssrc,
+      webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+  }
+
   // Base method to send packet using NetworkInterface.
   bool SendPacket(rtc::CopyOnWriteBuffer* packet,
                   const rtc::PacketOptions& options);
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 2751f94..278f87b 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -1942,6 +1942,18 @@
   }
 }
 
+void WebRtcVideoChannel::SetEncoderSelector(
+    uint32_t ssrc,
+    webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+  RTC_DCHECK_RUN_ON(&thread_checker_);
+  auto matching_stream = send_streams_.find(ssrc);
+  if (matching_stream != send_streams_.end()) {
+    matching_stream->second->SetEncoderSelector(encoder_selector);
+  } else {
+    RTC_LOG(LS_ERROR) << "No stream found to attach encoder selector";
+  }
+}
+
 void WebRtcVideoChannel::SetVideoCodecSwitchingEnabled(bool enabled) {
   RTC_DCHECK_RUN_ON(&thread_checker_);
   allow_codec_switching_ = enabled;
@@ -2388,6 +2400,18 @@
   }
 }
 
+void WebRtcVideoChannel::WebRtcVideoSendStream::SetEncoderSelector(
+    webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+  RTC_DCHECK_RUN_ON(&thread_checker_);
+  parameters_.config.encoder_selector = encoder_selector;
+  if (stream_) {
+    RTC_LOG(LS_INFO)
+        << "RecreateWebRtcStream (send) because of SetEncoderSelector, ssrc="
+        << parameters_.config.rtp.ssrcs[0];
+    RecreateWebRtcStream();
+  }
+}
+
 void WebRtcVideoChannel::WebRtcVideoSendStream::UpdateSendState() {
   RTC_DCHECK_RUN_ON(&thread_checker_);
   if (sending_) {
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index e50276c..b2780c4 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -191,6 +191,12 @@
                          rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
                              frame_encryptor) override;
 
+  // note: The encoder_selector object must remain valid for the lifetime of the
+  // MediaChannel, unless replaced.
+  void SetEncoderSelector(uint32_t ssrc,
+                          webrtc::VideoEncoderFactory::EncoderSelectorInterface*
+                              encoder_selector) override;
+
   void SetVideoCodecSwitchingEnabled(bool enabled) override;
 
   bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
@@ -351,6 +357,12 @@
     bool SetVideoSend(const VideoOptions* options,
                       rtc::VideoSourceInterface<webrtc::VideoFrame>* source);
 
+    // note: The encoder_selector object must remain valid for the lifetime of
+    // the MediaChannel, unless replaced.
+    void SetEncoderSelector(
+        webrtc::VideoEncoderFactory::EncoderSelectorInterface*
+            encoder_selector);
+
     void SetSend(bool send);
 
     const std::vector<uint32_t>& GetSsrcs() const;
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 52f5ca6..b93b4f7 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -23,6 +23,7 @@
 #include "api/rtc_event_log/rtc_event_log.h"
 #include "api/rtp_parameters.h"
 #include "api/task_queue/default_task_queue_factory.h"
+#include "api/test/mock_encoder_selector.h"
 #include "api/test/mock_video_bitrate_allocator.h"
 #include "api/test/mock_video_bitrate_allocator_factory.h"
 #include "api/test/mock_video_decoder_factory.h"
@@ -8983,4 +8984,29 @@
   EXPECT_THAT(config.rtp.rids, ElementsAreArray(rids));
 }
 
+TEST_F(WebRtcVideoChannelBaseTest, EncoderSelectorSwitchCodec) {
+  VideoCodec vp9 = GetEngineCodec("VP9");
+
+  cricket::VideoSendParameters parameters;
+  parameters.codecs.push_back(GetEngineCodec("VP8"));
+  parameters.codecs.push_back(vp9);
+  EXPECT_TRUE(channel_->SetSendParameters(parameters));
+  channel_->SetSend(true);
+
+  VideoCodec codec;
+  ASSERT_TRUE(channel_->GetSendCodec(&codec));
+  EXPECT_EQ("VP8", codec.name);
+
+  webrtc::MockEncoderSelector encoder_selector;
+  EXPECT_CALL(encoder_selector, OnAvailableBitrate)
+      .WillRepeatedly(Return(webrtc::SdpVideoFormat("VP9")));
+
+  channel_->SetEncoderSelector(kSsrc, &encoder_selector);
+
+  rtc::Thread::Current()->ProcessMessages(30);
+
+  ASSERT_TRUE(channel_->GetSendCodec(&codec));
+  EXPECT_EQ("VP9", codec.name);
+}
+
 }  // namespace cricket
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index d4acaed..385ecf2 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -2343,6 +2343,7 @@
       "../api:libjingle_logging_api",
       "../api:libjingle_peerconnection_api",
       "../api:media_stream_interface",
+      "../api:mock_encoder_selector",
       "../api:mock_video_track",
       "../api:packet_socket_factory",
       "../api:priority",
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 9f40648..6358bce 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -49,6 +49,7 @@
 #include "api/stats/rtc_stats.h"
 #include "api/stats/rtc_stats_report.h"
 #include "api/stats/rtcstats_objects.h"
+#include "api/test/mock_encoder_selector.h"
 #include "api/transport/rtp/rtp_source.h"
 #include "api/uma_metrics.h"
 #include "api/units/time_delta.h"
@@ -3593,6 +3594,35 @@
             callee_track->state());
 }
 
+TEST_P(PeerConnectionIntegrationTest, EndToEndRtpSenderVideoEncoderSelector) {
+  ASSERT_TRUE(
+      CreateOneDirectionalPeerConnectionWrappers(/*caller_to_callee=*/true));
+  ConnectFakeSignaling();
+  // Add one-directional video, from caller to callee.
+  rtc::scoped_refptr<webrtc::VideoTrackInterface> caller_track =
+      caller()->CreateLocalVideoTrack();
+  auto sender = caller()->AddTrack(caller_track);
+  PeerConnectionInterface::RTCOfferAnswerOptions options;
+  options.offer_to_receive_video = 0;
+  caller()->SetOfferAnswerOptions(options);
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  ASSERT_EQ(callee()->pc()->GetReceivers().size(), 1u);
+
+  std::unique_ptr<MockEncoderSelector> encoder_selector =
+      std::make_unique<MockEncoderSelector>();
+  EXPECT_CALL(*encoder_selector, OnCurrentEncoder);
+
+  sender->SetEncoderSelector(std::move(encoder_selector));
+
+  // Expect video to be received in one direction.
+  MediaExpectations media_expectations;
+  media_expectations.CallerExpectsNoVideo();
+  media_expectations.CalleeExpectsSomeVideo();
+
+  EXPECT_TRUE(ExpectNewFrames(media_expectations));
+}
+
 }  // namespace
 
 }  // namespace webrtc
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index 6c4c578..d8d4b52 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -132,6 +132,23 @@
   }
 }
 
+void RtpSenderBase::SetEncoderSelector(
+    std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>
+        encoder_selector) {
+  RTC_DCHECK_RUN_ON(signaling_thread_);
+  encoder_selector_ = std::move(encoder_selector);
+  SetEncoderSelectorOnChannel();
+}
+
+void RtpSenderBase::SetEncoderSelectorOnChannel() {
+  RTC_DCHECK_RUN_ON(signaling_thread_);
+  if (media_channel_ && ssrc_ && !stopped_) {
+    worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+      media_channel_->SetEncoderSelector(ssrc_, encoder_selector_.get());
+    });
+  }
+}
+
 void RtpSenderBase::SetMediaChannel(cricket::MediaChannel* media_channel) {
   RTC_DCHECK(media_channel == nullptr ||
              media_channel->media_type() == media_type());
@@ -321,6 +338,9 @@
   if (frame_transformer_) {
     SetEncoderToPacketizerFrameTransformer(frame_transformer_);
   }
+  if (encoder_selector_) {
+    SetEncoderSelectorOnChannel();
+  }
 }
 
 void RtpSenderBase::Stop() {
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index 569a600..4c1d307 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -185,6 +185,12 @@
   void SetEncoderToPacketizerFrameTransformer(
       rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) override;
 
+  void SetEncoderSelector(
+      std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>
+          encoder_selector) override;
+
+  void SetEncoderSelectorOnChannel();
+
   void SetTransceiverAsStopped() override {
     RTC_DCHECK_RUN_ON(signaling_thread_);
     is_transceiver_stopped_ = true;
@@ -247,6 +253,8 @@
   SetStreamsObserver* set_streams_observer_ = nullptr;
 
   rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
+  std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>
+      encoder_selector_;
 };
 
 // LocalAudioSinkAdapter receives data callback as a sink to the local
diff --git a/pc/rtp_sender_proxy.h b/pc/rtp_sender_proxy.h
index 2f8fe2c..140b5ff 100644
--- a/pc/rtp_sender_proxy.h
+++ b/pc/rtp_sender_proxy.h
@@ -11,6 +11,7 @@
 #ifndef PC_RTP_SENDER_PROXY_H_
 #define PC_RTP_SENDER_PROXY_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -44,6 +45,9 @@
 PROXY_METHOD1(void,
               SetEncoderToPacketizerFrameTransformer,
               rtc::scoped_refptr<FrameTransformerInterface>)
+PROXY_METHOD1(void,
+              SetEncoderSelector,
+              std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>)
 END_PROXY_MAP(RtpSender)
 
 }  // namespace webrtc
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 6f6a2d3..86fd0d9 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -115,7 +115,8 @@
     const VideoStreamEncoderSettings& encoder_settings,
     VideoStreamEncoder::BitrateAllocationCallbackType
         bitrate_allocation_callback_type,
-    const FieldTrialsView& field_trials) {
+    const FieldTrialsView& field_trials,
+    webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
   std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue =
       task_queue_factory->CreateTaskQueue("EncoderQueue",
                                           TaskQueueFactory::Priority::NORMAL);
@@ -125,7 +126,8 @@
       std::make_unique<OveruseFrameDetector>(stats_proxy, field_trials),
       FrameCadenceAdapterInterface::Create(clock, encoder_queue_ptr,
                                            field_trials),
-      std::move(encoder_queue), bitrate_allocation_callback_type, field_trials);
+      std::move(encoder_queue), bitrate_allocation_callback_type, field_trials,
+      encoder_selector);
 }
 
 }  // namespace
@@ -160,7 +162,8 @@
           &stats_proxy_,
           config_.encoder_settings,
           GetBitrateAllocationCallbackType(config_, field_trials),
-          field_trials)),
+          field_trials,
+          config_.encoder_selector)),
       encoder_feedback_(
           clock,
           config_.rtp.ssrcs,
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 31a9e25..791bdad 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -615,7 +615,8 @@
     std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
         encoder_queue,
     BitrateAllocationCallbackType allocation_cb_type,
-    const FieldTrialsView& field_trials)
+    const FieldTrialsView& field_trials,
+    webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector)
     : field_trials_(field_trials),
       worker_queue_(TaskQueueBase::Current()),
       number_of_cores_(number_of_cores),
@@ -623,7 +624,14 @@
       settings_(settings),
       allocation_cb_type_(allocation_cb_type),
       rate_control_settings_(RateControlSettings::ParseFromFieldTrials()),
-      encoder_selector_(settings.encoder_factory->GetEncoderSelector()),
+      encoder_selector_from_constructor_(encoder_selector),
+      encoder_selector_from_factory_(
+          encoder_selector_from_constructor_
+              ? nullptr
+              : settings.encoder_factory->GetEncoderSelector()),
+      encoder_selector_(encoder_selector_from_constructor_
+                            ? encoder_selector_from_constructor_
+                            : encoder_selector_from_factory_.get()),
       encoder_stats_observer_(encoder_stats_observer),
       cadence_callback_(*this),
       frame_cadence_adapter_(std::move(frame_cadence_adapter)),
diff --git a/video/video_stream_encoder.h b/video/video_stream_encoder.h
index c68a153..34d7895 100644
--- a/video/video_stream_encoder.h
+++ b/video/video_stream_encoder.h
@@ -81,7 +81,9 @@
       std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
           encoder_queue,
       BitrateAllocationCallbackType allocation_cb_type,
-      const FieldTrialsView& field_trials);
+      const FieldTrialsView& field_trials,
+      webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector =
+          nullptr);
   ~VideoStreamEncoder() override;
 
   VideoStreamEncoder(const VideoStreamEncoder&) = delete;
@@ -262,8 +264,14 @@
   const BitrateAllocationCallbackType allocation_cb_type_;
   const RateControlSettings rate_control_settings_;
 
+  webrtc::VideoEncoderFactory::EncoderSelectorInterface* const
+      encoder_selector_from_constructor_;
   std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface> const
-      encoder_selector_;
+      encoder_selector_from_factory_;
+  // Pointing to either encoder_selector_from_constructor_ or
+  // encoder_selector_from_factory_ but can be nullptr.
+  VideoEncoderFactory::EncoderSelectorInterface* const encoder_selector_;
+
   VideoStreamEncoderObserver* const encoder_stats_observer_;
   // Adapter that avoids public inheritance of the cadence adapter's callback
   // interface.