Use fallback encoder if primary can't be created

In case if primary encoder can't be instantiated (max number of
instances has reached, for example), use fallback encoder.

Bug: none
Change-Id: I477bdeb7af4dcce50e36b1804ffc6ad2ab004dfd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/234500
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35161}
diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc
index a16f04d..8659c4c 100644
--- a/media/engine/simulcast_encoder_adapter.cc
+++ b/media/engine/simulcast_encoder_adapter.cc
@@ -709,17 +709,40 @@
     encoder_context = std::move(*encoder_context_iter);
     cached_encoder_contexts_.erase(encoder_context_iter);
   } else {
-    std::unique_ptr<VideoEncoder> encoder =
+    std::unique_ptr<VideoEncoder> primary_encoder =
         primary_encoder_factory_->CreateVideoEncoder(video_format_);
-    VideoEncoder::EncoderInfo primary_info = encoder->GetEncoderInfo();
-    VideoEncoder::EncoderInfo fallback_info = primary_info;
+
+    std::unique_ptr<VideoEncoder> fallback_encoder;
     if (fallback_encoder_factory_ != nullptr) {
-      std::unique_ptr<VideoEncoder> fallback_encoder =
+      fallback_encoder =
           fallback_encoder_factory_->CreateVideoEncoder(video_format_);
+    }
+
+    std::unique_ptr<VideoEncoder> encoder;
+    VideoEncoder::EncoderInfo primary_info;
+    VideoEncoder::EncoderInfo fallback_info;
+
+    if (primary_encoder != nullptr) {
+      primary_info = primary_encoder->GetEncoderInfo();
+      fallback_info = primary_info;
+
+      if (fallback_encoder == nullptr) {
+        encoder = std::move(primary_encoder);
+      } else {
+        encoder = CreateVideoEncoderSoftwareFallbackWrapper(
+            std::move(fallback_encoder), std::move(primary_encoder),
+            prefer_temporal_support);
+      }
+    } else if (fallback_encoder != nullptr) {
+      RTC_LOG(LS_WARNING) << "Failed to create primary " << video_format_.name
+                          << " encoder. Use fallback encoder.";
       fallback_info = fallback_encoder->GetEncoderInfo();
-      encoder = CreateVideoEncoderSoftwareFallbackWrapper(
-          std::move(fallback_encoder), std::move(encoder),
-          prefer_temporal_support);
+      primary_info = fallback_info;
+      encoder = std::move(fallback_encoder);
+    } else {
+      RTC_LOG(LS_ERROR) << "Failed to create primary and fallback "
+                        << video_format_.name << " encoders.";
+      return nullptr;
     }
 
     encoder_context = std::make_unique<SimulcastEncoderAdapter::EncoderContext>(
@@ -829,7 +852,10 @@
     // Create one encoder and query it.
 
     std::unique_ptr<SimulcastEncoderAdapter::EncoderContext> encoder_context =
-        FetchOrCreateEncoderContext(true);
+        FetchOrCreateEncoderContext(/*is_lowest_quality_stream=*/true);
+    if (encoder_context == nullptr) {
+      return encoder_info;
+    }
 
     const VideoEncoder::EncoderInfo& primary_info =
         encoder_context->PrimaryInfo();
diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc
index 5f3e54f..beca9d4 100644
--- a/media/engine/simulcast_encoder_adapter_unittest.cc
+++ b/media/engine/simulcast_encoder_adapter_unittest.cc
@@ -171,6 +171,9 @@
 
   const std::vector<MockVideoEncoder*>& encoders() const;
   void SetEncoderNames(const std::vector<const char*>& encoder_names);
+  void set_create_video_encode_return_nullptr(bool return_nullptr) {
+    create_video_encoder_return_nullptr_ = return_nullptr;
+  }
   void set_init_encode_return_value(int32_t value);
   void set_requested_resolution_alignments(
       std::vector<int> requested_resolution_alignments) {
@@ -183,6 +186,7 @@
   void DestroyVideoEncoder(VideoEncoder* encoder);
 
  private:
+  bool create_video_encoder_return_nullptr_ = false;
   int32_t init_encode_return_value_ = 0;
   std::vector<MockVideoEncoder*> encoders_;
   std::vector<const char*> encoder_names_;
@@ -346,6 +350,10 @@
 
 std::unique_ptr<VideoEncoder> MockVideoEncoderFactory::CreateVideoEncoder(
     const SdpVideoFormat& format) {
+  if (create_video_encoder_return_nullptr_) {
+    return nullptr;
+  }
+
   auto encoder = std::make_unique<::testing::NiceMock<MockVideoEncoder>>(this);
   encoder->set_init_encode_return_value(init_encode_return_value_);
   const char* encoder_name = encoder_names_.empty()
@@ -1711,5 +1719,46 @@
   EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder);
 }
 
+TEST_F(TestSimulcastEncoderAdapterFake,
+       UseFallbackEncoderIfCreatePrimaryEncoderFailed) {
+  // Enable support for fallback encoder factory and re-setup.
+  use_fallback_factory_ = true;
+  SetUp();
+  SimulcastTestFixtureImpl::DefaultSettings(
+      &codec_, static_cast<const int*>(kTestTemporalLayerProfile),
+      kVideoCodecVP8);
+  codec_.numberOfSimulcastStreams = 1;
+  helper_->factory()->SetEncoderNames({"primary"});
+  helper_->fallback_factory()->SetEncoderNames({"fallback"});
+
+  // Emulate failure at creating of primary encoder and verify that SEA switches
+  // to fallback encoder.
+  helper_->factory()->set_create_video_encode_return_nullptr(true);
+  EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings));
+  ASSERT_EQ(0u, helper_->factory()->encoders().size());
+  ASSERT_EQ(1u, helper_->fallback_factory()->encoders().size());
+  EXPECT_EQ("fallback", adapter_->GetEncoderInfo().implementation_name);
+}
+
+TEST_F(TestSimulcastEncoderAdapterFake,
+       InitEncodeReturnsErrorIfEncoderCannotBeCreated) {
+  // Enable support for fallback encoder factory and re-setup.
+  use_fallback_factory_ = true;
+  SetUp();
+  SimulcastTestFixtureImpl::DefaultSettings(
+      &codec_, static_cast<const int*>(kTestTemporalLayerProfile),
+      kVideoCodecVP8);
+  codec_.numberOfSimulcastStreams = 1;
+  helper_->factory()->SetEncoderNames({"primary"});
+  helper_->fallback_factory()->SetEncoderNames({"fallback"});
+
+  // Emulate failure at creating of primary and fallback encoders and verify
+  // that `InitEncode` returns an error.
+  helper_->factory()->set_create_video_encode_return_nullptr(true);
+  helper_->fallback_factory()->set_create_video_encode_return_nullptr(true);
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_MEMORY,
+            adapter_->InitEncode(&codec_, kSettings));
+}
+
 }  // namespace test
 }  // namespace webrtc