SimulcastEncoderAdapter: Add field trial for EncoderInfo settings.

Allowed settings:
- requested_resolution_alignment
- apply_alignment_to_all_simulcast_layers


Bug: none
Change-Id: Ic4c733fd1134b9d097a2d19963eef1b676058f49
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/201626
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33010}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index b253a61..55fc59c 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -193,6 +193,7 @@
     "../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 525d818..10cf686 100644
--- a/media/engine/simulcast_encoder_adapter.cc
+++ b/media/engine/simulcast_encoder_adapter.cc
@@ -228,6 +228,11 @@
           "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 @@
     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::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 @@
   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 @@
   }
   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 6b1b177..d3d5d17 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 @@
 
   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 @@
   const absl::optional<unsigned int> 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<int> 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 24686e8..510db6f 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 @@
       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<const int*>(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<const int*>(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<const int*>(kTestTemporalLayerProfile),