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