Allow software fallback on lowest simulcast stream for temporal support

Bug: webrtc:11324
Change-Id: Ie505be0cda74c0444065d86c3727671c62bd4842
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/167527
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Evan Shrubsole <eshr@google.com>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30437}
diff --git a/api/video_codecs/video_encoder_software_fallback_wrapper.cc b/api/video_codecs/video_encoder_software_fallback_wrapper.cc
index fe32741..9edc9b0 100644
--- a/api/video_codecs/video_encoder_software_fallback_wrapper.cc
+++ b/api/video_codecs/video_encoder_software_fallback_wrapper.cc
@@ -15,7 +15,6 @@
 #include <cstdio>
 #include <memory>
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "absl/types/optional.h"
@@ -25,6 +24,7 @@
 #include "api/video_codecs/video_codec.h"
 #include "api/video_codecs/video_encoder.h"
 #include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/utility/simulcast_utility.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "system_wrappers/include/field_trial.h"
@@ -33,52 +33,89 @@
 
 namespace {
 
+// If forced fallback is allowed, either:
+//
+// 1) The forced fallback is requested if the resolution is less than or equal
+//    to |max_pixels_|. The resolution is allowed to be scaled down to
+//    |min_pixels_|.
+//
+// 2) The forced fallback is requested if temporal support is preferred and the
+//    SW fallback supports temporal layers while the HW encoder does not.
+
+struct ForcedFallbackParams {
+ public:
+  bool SupportsResolutionBasedSwitch(const VideoCodec& codec) const {
+    return enable_resolution_based_switch &&
+           codec.codecType == kVideoCodecVP8 &&
+           codec.numberOfSimulcastStreams <= 1 &&
+           codec.VP8().numberOfTemporalLayers == 1 &&
+           codec.width * codec.height <= max_pixels;
+  }
+
+  bool SupportsTemporalBasedSwitch(const VideoCodec& codec) const {
+    return enable_temporal_based_switch &&
+           SimulcastUtility::NumberOfTemporalLayers(codec, 0) > 1;
+  }
+
+  bool enable_temporal_based_switch = false;
+  bool enable_resolution_based_switch = false;
+  int min_pixels = 320 * 180;
+  int max_pixels = 320 * 240;
+};
+
 const char kVp8ForceFallbackEncoderFieldTrial[] =
     "WebRTC-VP8-Forced-Fallback-Encoder-v2";
 
-bool EnableForcedFallback() {
-  return field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial);
-}
-
-bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
-  return codec_settings.codecType == kVideoCodecVP8 &&
-         codec_settings.numberOfSimulcastStreams <= 1 &&
-         codec_settings.VP8().numberOfTemporalLayers == 1;
-}
-
-void GetForcedFallbackParamsFromFieldTrialGroup(int* param_min_pixels,
-                                                int* param_max_pixels,
-                                                int minimum_max_pixels) {
-  RTC_DCHECK(param_min_pixels);
-  RTC_DCHECK(param_max_pixels);
-  std::string group =
+absl::optional<ForcedFallbackParams> ParseFallbackParamsFromFieldTrials(
+    const VideoEncoder& main_encoder) {
+  const std::string field_trial =
       webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
-  if (group.empty())
-    return;
+  if (field_trial.find("Enabled") != 0) {
+    return absl::nullopt;
+  }
 
-  int min_pixels;
-  int max_pixels;
-  int min_bps;
-  if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels,
-             &min_bps) != 3) {
+  int max_pixels_lower_bound =
+      main_encoder.GetEncoderInfo().scaling_settings.min_pixels_per_frame - 1;
+
+  ForcedFallbackParams params;
+  params.enable_resolution_based_switch = true;
+
+  int min_bps = 0;
+  if (sscanf(field_trial.c_str(), "Enabled-%d,%d,%d", &params.min_pixels,
+             &params.max_pixels, &min_bps) != 3) {
     RTC_LOG(LS_WARNING)
         << "Invalid number of forced fallback parameters provided.";
-    return;
-  }
-  if (min_pixels <= 0 || max_pixels < minimum_max_pixels ||
-      max_pixels < min_pixels || min_bps <= 0) {
+    return absl::nullopt;
+  } else if (params.min_pixels <= 0 ||
+             params.max_pixels < max_pixels_lower_bound ||
+             params.max_pixels < params.min_pixels || min_bps <= 0) {
     RTC_LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
-    return;
+    return absl::nullopt;
   }
-  *param_min_pixels = min_pixels;
-  *param_max_pixels = max_pixels;
+
+  return params;
+}
+
+absl::optional<ForcedFallbackParams> GetForcedFallbackParams(
+    bool prefer_temporal_support,
+    const VideoEncoder& main_encoder) {
+  absl::optional<ForcedFallbackParams> params =
+      ParseFallbackParamsFromFieldTrials(main_encoder);
+  if (prefer_temporal_support) {
+    if (!params.has_value()) {
+      params.emplace();
+    }
+    params->enable_temporal_based_switch = prefer_temporal_support;
+  }
+  return params;
 }
 
 class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder {
  public:
   VideoEncoderSoftwareFallbackWrapper(
       std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
-      std::unique_ptr<webrtc::VideoEncoder> hw_encoder);
+      std::unique_ptr<webrtc::VideoEncoder> hw_encoder,
+      bool prefer_temporal_support);
   ~VideoEncoderSoftwareFallbackWrapper() override;
 
   void SetFecControllerOverride(
@@ -106,28 +143,28 @@
   EncoderInfo GetEncoderInfo() const override;
 
  private:
-  bool InitFallbackEncoder();
-
-  // If |forced_fallback_possible_| is true:
-  // The forced fallback is requested if the resolution is less than or equal to
-  // |max_pixels_|. The resolution is allowed to be scaled down to
-  // |min_pixels_|.
-  class ForcedFallbackParams {
-   public:
-    bool IsValid(const VideoCodec& codec) const {
-      return codec.width * codec.height <= max_pixels_;
-    }
-
-    bool active_ = false;
-    int min_pixels_ = 320 * 180;
-    int max_pixels_ = 320 * 240;
-  };
-
+  bool InitFallbackEncoder(bool is_forced);
   bool TryInitForcedFallbackEncoder();
-  bool TryReInitForcedFallbackEncoder();
-  void ValidateSettingsForForcedFallback();
-  bool IsForcedFallbackActive() const;
-  void MaybeModifyCodecForFallback();
+  bool IsFallbackActive() const;
+
+  VideoEncoder* current_encoder() {
+    switch (encoder_state_) {
+      case EncoderState::kUninitialized:
+        RTC_LOG(LS_WARNING)
+            << "Trying to access encoder in uninitialized fallback wrapper.";
+        // Return main encoder to preserve previous behavior.
+        ABSL_FALLTHROUGH_INTENDED;
+      case EncoderState::kMainEncoderUsed:
+        return encoder_.get();
+      case EncoderState::kFallbackDueToFailure:
+      case EncoderState::kForcedFallback:
+        return fallback_encoder_.get();
+    }
+  }
+
+  // Updates encoder with last observed parameters, such as callbacks, rates,
+  // etc.
+  void PrimeEncoder(VideoEncoder* encoder) const;
 
   // Settings used in the last InitEncode call and used if a dynamic fallback to
   // software is required.
@@ -137,65 +174,95 @@
   // The last rate control settings, if set.
   absl::optional<RateControlParameters> rate_control_parameters_;
 
-  // The last channel parameters set, and a flag for noting they are set.
-  bool channel_parameters_set_;
-  uint32_t packet_loss_;
-  int64_t rtt_;
+  // The last channel parameters set.
+  absl::optional<float> packet_loss_;
+  absl::optional<int64_t> rtt_;
+  FecControllerOverride* fec_controller_override_;
+  absl::optional<LossNotification> loss_notification_;
 
-  bool use_fallback_encoder_;
+  enum class EncoderState {
+    kUninitialized,
+    kMainEncoderUsed,
+    kFallbackDueToFailure,
+    kForcedFallback
+  };
+
+  EncoderState encoder_state_;
   const std::unique_ptr<webrtc::VideoEncoder> encoder_;
-
   const std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_;
+
   EncodedImageCallback* callback_;
 
-  bool forced_fallback_possible_;
-  ForcedFallbackParams forced_fallback_;
+  const absl::optional<ForcedFallbackParams> fallback_params_;
 };
 
 VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
     std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
-    std::unique_ptr<webrtc::VideoEncoder> hw_encoder)
-    : channel_parameters_set_(false),
-      packet_loss_(0),
-      rtt_(0),
-      use_fallback_encoder_(false),
+    std::unique_ptr<webrtc::VideoEncoder> hw_encoder,
+    bool prefer_temporal_support)
+    : fec_controller_override_(nullptr),
+      encoder_state_(EncoderState::kUninitialized),
       encoder_(std::move(hw_encoder)),
       fallback_encoder_(std::move(sw_encoder)),
       callback_(nullptr),
-      forced_fallback_possible_(EnableForcedFallback()) {
+      fallback_params_(
+          GetForcedFallbackParams(prefer_temporal_support, *encoder_)) {
   RTC_DCHECK(fallback_encoder_);
-  if (forced_fallback_possible_) {
-    GetForcedFallbackParamsFromFieldTrialGroup(
-        &forced_fallback_.min_pixels_, &forced_fallback_.max_pixels_,
-        encoder_->GetEncoderInfo().scaling_settings.min_pixels_per_frame -
-            1);  // No HW below.
-  }
 }
+
 VideoEncoderSoftwareFallbackWrapper::~VideoEncoderSoftwareFallbackWrapper() =
     default;
 
-bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
+void VideoEncoderSoftwareFallbackWrapper::PrimeEncoder(
+    VideoEncoder* encoder) const {
+  RTC_DCHECK(encoder);
+  // Replay callback, rates, and channel parameters.
+  if (callback_) {
+    encoder->RegisterEncodeCompleteCallback(callback_);
+  }
+  if (rate_control_parameters_) {
+    encoder->SetRates(*rate_control_parameters_);
+  }
+  if (rtt_.has_value()) {
+    encoder->OnRttUpdate(rtt_.value());
+  }
+  if (packet_loss_.has_value()) {
+    encoder->OnPacketLossRateUpdate(packet_loss_.value());
+  }
+  if (fec_controller_override_) {
+    encoder->SetFecControllerOverride(fec_controller_override_);
+  }
+  if (loss_notification_.has_value()) {
+    encoder->OnLossNotification(loss_notification_.value());
+  }
+}
+
+bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder(bool is_forced) {
   RTC_LOG(LS_WARNING) << "Encoder falling back to software encoding.";
 
   RTC_DCHECK(encoder_settings_.has_value());
   const int ret = fallback_encoder_->InitEncode(&codec_settings_,
                                                 encoder_settings_.value());
-  use_fallback_encoder_ = (ret == WEBRTC_VIDEO_CODEC_OK);
-  if (!use_fallback_encoder_) {
+
+  if (ret != WEBRTC_VIDEO_CODEC_OK) {
     RTC_LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
     fallback_encoder_->Release();
     return false;
   }
-  // Replay callback, rates, and channel parameters.
-  if (callback_)
-    fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
-  if (rate_control_parameters_)
-    fallback_encoder_->SetRates(*rate_control_parameters_);
 
-  // Since we're switching to the fallback encoder, Release the real encoder. It
-  // may be re-initialized via InitEncode later, and it will continue to get
-  // Set calls for rates and channel parameters in the meantime.
-  encoder_->Release();
+  if (encoder_state_ == EncoderState::kMainEncoderUsed) {
+    // Since we're switching to the fallback encoder, Release the real encoder.
+    // It may be re-initialized via InitEncode later, and it will continue to
+    // get Set calls for rates and channel parameters in the meantime.
+    encoder_->Release();
+  }
+
+  if (is_forced) {
+    encoder_state_ = EncoderState::kForcedFallback;
+  } else {
+    encoder_state_ = EncoderState::kFallbackDueToFailure;
+  }
+
   return true;
 }
 
@@ -204,8 +271,9 @@
   // It is important that only one of those would ever interact with the
   // |fec_controller_override| at a given time. This is the responsibility
   // of |this| to maintain.
-  encoder_->SetFecControllerOverride(fec_controller_override);
-  fallback_encoder_->SetFecControllerOverride(fec_controller_override);
+
+  fec_controller_override_ = fec_controller_override;
+  current_encoder()->SetFecControllerOverride(fec_controller_override);
 }
 
 int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
@@ -217,93 +285,94 @@
   encoder_settings_ = settings;
   // Clear stored rate/channel parameters.
   rate_control_parameters_ = absl::nullopt;
-  ValidateSettingsForForcedFallback();
 
-  // Try to reinit forced software codec if it is in use.
-  if (TryReInitForcedFallbackEncoder()) {
-    return WEBRTC_VIDEO_CODEC_OK;
-  }
+  RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized)
+      << "InitEncode() should never be called on an active instance!";
+
   // Try to init forced software codec if it should be used.
   if (TryInitForcedFallbackEncoder()) {
+    PrimeEncoder(current_encoder());
     return WEBRTC_VIDEO_CODEC_OK;
   }
-  forced_fallback_.active_ = false;
 
   int32_t ret = encoder_->InitEncode(codec_settings, settings);
   if (ret == WEBRTC_VIDEO_CODEC_OK) {
-    if (use_fallback_encoder_) {
-      RTC_LOG(LS_WARNING)
-          << "InitEncode OK, no longer using the software fallback encoder.";
-      fallback_encoder_->Release();
-      use_fallback_encoder_ = false;
-    }
-    if (callback_)
-      encoder_->RegisterEncodeCompleteCallback(callback_);
+    encoder_state_ = EncoderState::kMainEncoderUsed;
+    PrimeEncoder(current_encoder());
     return ret;
   }
+
   // Try to instantiate software codec.
-  if (InitFallbackEncoder()) {
+  if (InitFallbackEncoder(/*is_forced=*/false)) {
+    PrimeEncoder(current_encoder());
     return WEBRTC_VIDEO_CODEC_OK;
   }
-  // Software encoder failed, use original return code.
+
+  // Software encoder failed too, use original return code.
+  encoder_state_ = EncoderState::kUninitialized;
   return ret;
 }
 
 int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
     EncodedImageCallback* callback) {
   callback_ = callback;
-  int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
-  if (use_fallback_encoder_)
-    return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
-  return ret;
+  return current_encoder()->RegisterEncodeCompleteCallback(callback);
 }
 
 int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
-  return use_fallback_encoder_ ? fallback_encoder_->Release()
-                               : encoder_->Release();
+  if (encoder_state_ == EncoderState::kUninitialized) {
+    return WEBRTC_VIDEO_CODEC_OK;
+  }
+  int32_t ret = current_encoder()->Release();
+  encoder_state_ = EncoderState::kUninitialized;
+  return ret;
 }
 
 int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
     const VideoFrame& frame,
     const std::vector<VideoFrameType>* frame_types) {
-  if (use_fallback_encoder_)
-    return fallback_encoder_->Encode(frame, frame_types);
-  int32_t ret = encoder_->Encode(frame, frame_types);
-  // If requested, try a software fallback.
-  bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
-  if (fallback_requested && InitFallbackEncoder()) {
-    // Start using the fallback with this frame.
-    return fallback_encoder_->Encode(frame, frame_types);
+  switch (encoder_state_) {
+    case EncoderState::kUninitialized:
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    case EncoderState::kMainEncoderUsed: {
+      int32_t ret = encoder_->Encode(frame, frame_types);
+      // If requested, try a software fallback.
+      bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
+      if (fallback_requested && InitFallbackEncoder(/*is_forced=*/false)) {
+        // Start using the fallback with this frame.
+        PrimeEncoder(current_encoder());
+        return fallback_encoder_->Encode(frame, frame_types);
+      }
+      // Fallback encoder failed too, return original error code.
+      return ret;
+    }
+    case EncoderState::kFallbackDueToFailure:
+    case EncoderState::kForcedFallback:
+      return fallback_encoder_->Encode(frame, frame_types);
   }
-  return ret;
 }
 
 void VideoEncoderSoftwareFallbackWrapper::SetRates(
     const RateControlParameters& parameters) {
   rate_control_parameters_ = parameters;
-  encoder_->SetRates(parameters);
-  if (use_fallback_encoder_)
-    fallback_encoder_->SetRates(parameters);
+  return current_encoder()->SetRates(parameters);
 }
 
 void VideoEncoderSoftwareFallbackWrapper::OnPacketLossRateUpdate(
     float packet_loss_rate) {
-  VideoEncoder* encoder =
-      use_fallback_encoder_ ? fallback_encoder_.get() : encoder_.get();
-  encoder->OnPacketLossRateUpdate(packet_loss_rate);
+  packet_loss_ = packet_loss_rate;
+  current_encoder()->OnPacketLossRateUpdate(packet_loss_rate);
 }
 
 void VideoEncoderSoftwareFallbackWrapper::OnRttUpdate(int64_t rtt_ms) {
-  VideoEncoder* encoder =
-      use_fallback_encoder_ ? fallback_encoder_.get() : encoder_.get();
-  encoder->OnRttUpdate(rtt_ms);
+  rtt_ = rtt_ms;
+  current_encoder()->OnRttUpdate(rtt_ms);
 }
 
 void VideoEncoderSoftwareFallbackWrapper::OnLossNotification(
     const LossNotification& loss_notification) {
-  VideoEncoder* encoder =
-      use_fallback_encoder_ ? fallback_encoder_.get() : encoder_.get();
-  encoder->OnLossNotification(loss_notification);
+  loss_notification_ = loss_notification;
+  current_encoder()->OnLossNotification(loss_notification);
 }
 
 VideoEncoder::EncoderInfo VideoEncoderSoftwareFallbackWrapper::GetEncoderInfo()
@@ -312,17 +381,17 @@
   EncoderInfo default_encoder_info = encoder_->GetEncoderInfo();
 
   EncoderInfo info =
-      use_fallback_encoder_ ? fallback_encoder_info : default_encoder_info;
+      IsFallbackActive() ? fallback_encoder_info : default_encoder_info;
 
-  if (forced_fallback_possible_) {
-    const auto settings = forced_fallback_.active_
+  if (fallback_params_.has_value()) {
+    const auto settings = (encoder_state_ == EncoderState::kForcedFallback)
                               ? fallback_encoder_info.scaling_settings
                               : default_encoder_info.scaling_settings;
     info.scaling_settings =
         settings.thresholds
             ? VideoEncoder::ScalingSettings(settings.thresholds->low,
                                             settings.thresholds->high,
-                                            forced_fallback_.min_pixels_)
+                                            fallback_params_->min_pixels)
             : VideoEncoder::ScalingSettings::kOff;
   } else {
     info.scaling_settings = default_encoder_info.scaling_settings;
@@ -331,72 +400,82 @@
   return info;
 }
 
-bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
-  return (forced_fallback_possible_ && use_fallback_encoder_ &&
-          forced_fallback_.active_);
+bool VideoEncoderSoftwareFallbackWrapper::IsFallbackActive() const {
+  return encoder_state_ == EncoderState::kForcedFallback ||
+         encoder_state_ == EncoderState::kFallbackDueToFailure;
 }
 
 bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() {
-  if (!forced_fallback_possible_ || use_fallback_encoder_) {
-    return false;
-  }
-  // Fallback not active.
-  if (!forced_fallback_.IsValid(codec_settings_)) {
-    return false;
-  }
-  // Settings valid, try to instantiate software codec.
-  RTC_LOG(LS_INFO) << "Request forced SW encoder fallback: "
-                   << codec_settings_.width << "x" << codec_settings_.height;
-  if (!InitFallbackEncoder()) {
-    return false;
-  }
-  forced_fallback_.active_ = true;
-  return true;
-}
-
-bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
-  if (!IsForcedFallbackActive()) {
+  if (!fallback_params_) {
     return false;
   }
 
-  // Forced fallback active.
-  if (!forced_fallback_.IsValid(codec_settings_)) {
-    RTC_LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
-    return false;
+  RTC_DCHECK_EQ(encoder_state_, EncoderState::kUninitialized);
+
+  if (fallback_params_->SupportsResolutionBasedSwitch(codec_settings_)) {
+    // Settings valid, try to instantiate software codec.
+    RTC_LOG(LS_INFO) << "Request forced SW encoder fallback: "
+                     << codec_settings_.width << "x" << codec_settings_.height;
+    return InitFallbackEncoder(/*is_forced=*/true);
   }
 
-  // Settings valid, reinitialize the forced fallback encoder.
-  RTC_DCHECK(encoder_settings_.has_value());
-  if (fallback_encoder_->InitEncode(&codec_settings_,
-                                    encoder_settings_.value()) !=
-      WEBRTC_VIDEO_CODEC_OK) {
-    RTC_LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
-    return false;
-  }
-  return true;
-}
-
-void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
-  if (!forced_fallback_possible_)
-    return;
-
-  if (!IsForcedFallbackPossible(codec_settings_)) {
-    if (IsForcedFallbackActive()) {
-      fallback_encoder_->Release();
-      use_fallback_encoder_ = false;
+  if (fallback_params_->SupportsTemporalBasedSwitch(codec_settings_)) {
+    // First init main encoder to see if that supports temporal layers.
+    if (encoder_->InitEncode(&codec_settings_, encoder_settings_.value()) ==
+        WEBRTC_VIDEO_CODEC_OK) {
+      encoder_state_ = EncoderState::kMainEncoderUsed;
     }
-    RTC_LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
-    forced_fallback_possible_ = false;
+
+    if (encoder_state_ == EncoderState::kMainEncoderUsed &&
+        encoder_->GetEncoderInfo().fps_allocation[0].size() > 1) {
+      // Primary encoder already supports temporal layers, use that instead.
+      return true;
+    }
+
+    // Try to initialize fallback and check if it supports temporal layers.
+    if (fallback_encoder_->InitEncode(&codec_settings_,
+                                      encoder_settings_.value()) ==
+        WEBRTC_VIDEO_CODEC_OK) {
+      if (fallback_encoder_->GetEncoderInfo().fps_allocation[0].size() > 1) {
+        // Fallback encoder available and supports temporal layers, use it!
+        if (encoder_state_ == EncoderState::kMainEncoderUsed) {
+          // Main encoder initialized but does not support temporal layers,
+          // release it again.
+          encoder_->Release();
+        }
+        encoder_state_ = EncoderState::kForcedFallback;
+        RTC_LOG(LS_INFO)
+            << "Forced switch to SW encoder due to temporal support.";
+        return true;
+      } else {
+        // Fallback encoder intialization succeeded, but it does not support
+        // temporal layers either - release it.
+        fallback_encoder_->Release();
+      }
+    }
+
+    if (encoder_state_ == EncoderState::kMainEncoderUsed) {
+      // Main encoder already initialized - make use of it.
+      RTC_LOG(LS_INFO)
+          << "Cannot fall back for temporal support since fallback that "
+             "supports is not available. Using main encoder instead.";
+      return true;
+    }
   }
+
+  // Neither forced fallback mode supported.
+  return false;
 }
 
 }  // namespace
 
 std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper(
     std::unique_ptr<VideoEncoder> sw_fallback_encoder,
-    std::unique_ptr<VideoEncoder> hw_encoder) {
+    std::unique_ptr<VideoEncoder> hw_encoder,
+    bool prefer_temporal_support) {
   return std::make_unique<VideoEncoderSoftwareFallbackWrapper>(
-      std::move(sw_fallback_encoder), std::move(hw_encoder));
+      std::move(sw_fallback_encoder), std::move(hw_encoder),
+      prefer_temporal_support);
 }
 
 }  // namespace webrtc