diff --git a/media/BUILD.gn b/media/BUILD.gn index b253a61252..55fc59c420 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -193,6 +193,7 @@ rtc_library("rtc_simulcast_encoder_adapter") { "../modules/video_coding:video_coding_utility", "../rtc_base:checks", "../rtc_base:rtc_base_approved", + "../rtc_base/experiments:field_trial_parser", "../rtc_base/experiments:rate_control_settings", "../rtc_base/synchronization:sequence_checker", "../rtc_base/system:no_unique_address", diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc index 525d818672..10cf686329 100644 --- a/media/engine/simulcast_encoder_adapter.cc +++ b/media/engine/simulcast_encoder_adapter.cc @@ -228,6 +228,11 @@ SimulcastEncoderAdapter::SimulcastEncoderAdapter( "WebRTC-Video-PreferTemporalSupportOnBaseLayer")) { RTC_DCHECK(primary_factory); + ParseFieldTrial({&requested_resolution_alignment_override_, + &apply_alignment_to_all_simulcast_layers_override_}, + field_trial::FindFullName( + "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride")); + // The adapter is typically created on the worker thread, but operated on // the encoder task queue. encoder_queue_.Detach(); @@ -425,6 +430,27 @@ int SimulcastEncoderAdapter::Encode( return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } + if (requested_resolution_alignment_override_) { + const int alignment = *requested_resolution_alignment_override_; + if (input_image.width() % alignment != 0 || + input_image.height() % alignment != 0) { + RTC_LOG(LS_WARNING) << "Frame " << input_image.width() << "x" + << input_image.height() << " not divisible by " + << alignment; + return WEBRTC_VIDEO_CODEC_ERROR; + } + if (apply_alignment_to_all_simulcast_layers_override_.Get()) { + for (const auto& layer : encoder_contexts_) { + if (layer.width() % alignment != 0 || layer.height() % alignment != 0) { + RTC_LOG(LS_WARNING) + << "Codec " << layer.width() << "x" << layer.height() + << " not divisible by " << alignment; + return WEBRTC_VIDEO_CODEC_ERROR; + } + } + } + } + // All active streams should generate a key frame if // a key frame is requested by any stream. bool send_key_frame = false; @@ -713,10 +739,23 @@ void SimulcastEncoderAdapter::DestroyStoredEncoders() { } } +void SimulcastEncoderAdapter::OverrideFromFieldTrial( + VideoEncoder::EncoderInfo* info) const { + if (requested_resolution_alignment_override_) { + info->requested_resolution_alignment = + *requested_resolution_alignment_override_; + info->apply_alignment_to_all_simulcast_layers = + apply_alignment_to_all_simulcast_layers_override_.Get(); + } +} + VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { if (encoder_contexts_.size() == 1) { // Not using simulcast adapting functionality, just pass through. - return encoder_contexts_.front().encoder().GetEncoderInfo(); + VideoEncoder::EncoderInfo info = + encoder_contexts_.front().encoder().GetEncoderInfo(); + OverrideFromFieldTrial(&info); + return info; } VideoEncoder::EncoderInfo encoder_info; @@ -726,6 +765,7 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { encoder_info.supports_native_handle = true; encoder_info.scaling_settings.thresholds = absl::nullopt; if (encoder_contexts_.empty()) { + OverrideFromFieldTrial(&encoder_info); return encoder_info; } @@ -784,6 +824,8 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { } encoder_info.implementation_name += ")"; + OverrideFromFieldTrial(&encoder_info); + return encoder_info; } diff --git a/media/engine/simulcast_encoder_adapter.h b/media/engine/simulcast_encoder_adapter.h index 6b1b177913..d3d5d17d66 100644 --- a/media/engine/simulcast_encoder_adapter.h +++ b/media/engine/simulcast_encoder_adapter.h @@ -26,6 +26,7 @@ #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/utility/framerate_controller.h" #include "rtc_base/atomic_ops.h" +#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/synchronization/sequence_checker.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" @@ -138,6 +139,8 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { void OnDroppedFrame(size_t stream_idx); + void OverrideFromFieldTrial(VideoEncoder::EncoderInfo* info) const; + volatile int inited_; // Accessed atomically. VideoEncoderFactory* const primary_encoder_factory_; VideoEncoderFactory* const fallback_encoder_factory_; @@ -158,6 +161,14 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { const absl::optional experimental_boosted_screenshare_qp_; const bool boost_base_layer_quality_; const bool prefer_temporal_support_on_base_layer_; + + // Overrides from field trial. + // EncoderInfo::requested_resolution_alignment. + FieldTrialOptional requested_resolution_alignment_override_{ + "requested_resolution_alignment"}; + // EncoderInfo::apply_alignment_to_all_simulcast_layers. + FieldTrialFlag apply_alignment_to_all_simulcast_layers_override_{ + "apply_alignment_to_all_simulcast_layers"}; }; } // namespace webrtc diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc index 24686e813e..510db6fb5d 100644 --- a/media/engine/simulcast_encoder_adapter_unittest.cc +++ b/media/engine/simulcast_encoder_adapter_unittest.cc @@ -28,6 +28,7 @@ #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/utility/simulcast_test_fixture_impl.h" #include "rtc_base/checks.h" +#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -1291,6 +1292,42 @@ TEST_F(TestSimulcastEncoderAdapterFake, adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); } +TEST_F(TestSimulcastEncoderAdapterFake, AlignmentFromFieldTrial) { + test::ScopedFieldTrials field_trials( + "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/" + "requested_resolution_alignment:8," + "apply_alignment_to_all_simulcast_layers/"); + SetUp(); + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 3; + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + ASSERT_EQ(3u, helper_->factory()->encoders().size()); + + EXPECT_EQ(8, adapter_->GetEncoderInfo().requested_resolution_alignment); + EXPECT_TRUE( + adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); +} + +TEST_F(TestSimulcastEncoderAdapterFake, + AlignmentFromFieldTrialForSingleStream) { + test::ScopedFieldTrials field_trials( + "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/" + "requested_resolution_alignment:9/"); + SetUp(); + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 1; + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + ASSERT_EQ(1u, helper_->factory()->encoders().size()); + + EXPECT_EQ(9, adapter_->GetEncoderInfo().requested_resolution_alignment); + EXPECT_FALSE( + adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); +} + TEST_F(TestSimulcastEncoderAdapterFake, ReportsInternalSource) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile),