Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions media/engine/simulcast_encoder_adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -684,40 +684,61 @@ void SimulcastEncoderAdapter::DestroyStoredEncoders() {
}
}


std::unique_ptr<SimulcastEncoderAdapter::EncoderContext>
SimulcastEncoderAdapter::FetchOrCreateEncoderContext(
bool is_lowest_quality_stream) {
bool prefer_temporal_support = fallback_encoder_factory_ != nullptr &&
is_lowest_quality_stream &&
prefer_temporal_support_on_base_layer_;

// Toggling of |prefer_temporal_support| requires encoder recreation. Find
// and reuse encoder with desired |prefer_temporal_support|. Otherwise, if
// Toggling of `prefer_temporal_support` requires encoder recreation. Find
// and reuse encoder with desired `prefer_temporal_support`. Otherwise, if
// there is no such encoder in the cache, create a new instance.
auto encoder_context_iter =
std::find_if(cached_encoder_contexts_.begin(),
cached_encoder_contexts_.end(), [&](auto& encoder_context) {
return encoder_context->prefer_temporal_support() ==
prefer_temporal_support;
});

std::unique_ptr<SimulcastEncoderAdapter::EncoderContext> encoder_context;
if (encoder_context_iter != cached_encoder_contexts_.end()) {
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_);
std::unique_ptr<VideoEncoder> fallback_encoder;
if (fallback_encoder_factory_ != nullptr) {
encoder = CreateVideoEncoderSoftwareFallbackWrapper(
fallback_encoder_factory_->CreateVideoEncoder(video_format_),
std::move(encoder), prefer_temporal_support);
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();
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>(
std::move(encoder), prefer_temporal_support);
}

encoder_context->encoder().RegisterEncodeCompleteCallback(
encoded_complete_callback_);
return encoder_context;
Expand Down
49 changes: 49 additions & 0 deletions media/engine/simulcast_encoder_adapter_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ class MockVideoEncoderFactory : public VideoEncoderFactory {

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) {
Expand All @@ -183,6 +186,7 @@ class MockVideoEncoderFactory : public VideoEncoderFactory {
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_;
Expand Down Expand Up @@ -340,6 +344,10 @@ std::vector<SdpVideoFormat> MockVideoEncoderFactory::GetSupportedFormats()

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()
Expand Down Expand Up @@ -1683,5 +1691,46 @@ TEST_F(TestSimulcastEncoderAdapterFake,
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