Enable initial frame drop for one active simulcast layer.

Bug: webrtc:12216
Change-Id: Ib2ac2fab45e560ba3eae30a926ce72667a257b07
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/193840
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32652}
diff --git a/video/adaptation/video_stream_encoder_resource_manager.cc b/video/adaptation/video_stream_encoder_resource_manager.cc
index c7ca4bc..697ebbe 100644
--- a/video/adaptation/video_stream_encoder_resource_manager.cc
+++ b/video/adaptation/video_stream_encoder_resource_manager.cc
@@ -58,6 +58,20 @@
   RTC_CHECK_NOTREACHED();
 }
 
+absl::optional<uint32_t> GetSingleActiveStreamPixels(const VideoCodec& codec) {
+  int num_active = 0;
+  absl::optional<uint32_t> pixels;
+  for (int i = 0; i < codec.numberOfSimulcastStreams; ++i) {
+    if (codec.simulcastStream[i].active) {
+      ++num_active;
+      pixels = codec.simulcastStream[i].width * codec.simulcastStream[i].height;
+    }
+    if (num_active > 1)
+      return absl::nullopt;
+  }
+  return pixels;
+}
+
 }  // namespace
 
 class VideoStreamEncoderResourceManager::InitialFrameDropper {
@@ -78,6 +92,10 @@
     return initial_framedrop_ < kMaxInitialFramedrop;
   }
 
+  absl::optional<uint32_t> single_active_stream_pixels() const {
+    return single_active_stream_pixels_;
+  }
+
   // Input signals.
   void SetStartBitrate(DataRate start_bitrate, int64_t now_ms) {
     set_start_bitrate_ = start_bitrate;
@@ -104,6 +122,10 @@
     }
   }
 
+  void OnEncoderSettingsUpdated(const VideoCodec& codec) {
+    single_active_stream_pixels_ = GetSingleActiveStreamPixels(codec);
+  }
+
   void OnFrameDroppedDueToSize() { ++initial_framedrop_; }
 
   void OnMaybeEncodeFrame() { initial_framedrop_ = kMaxInitialFramedrop; }
@@ -130,6 +152,7 @@
   int64_t set_start_bitrate_time_ms_;
   // Counts how many frames we've dropped in the initial framedrop phase.
   int initial_framedrop_;
+  absl::optional<uint32_t> single_active_stream_pixels_;
 };
 
 VideoStreamEncoderResourceManager::VideoStreamEncoderResourceManager(
@@ -230,7 +253,7 @@
   RTC_DCHECK(resource);
   bool inserted;
   std::tie(std::ignore, inserted) = resources_.emplace(resource, reason);
-  RTC_DCHECK(inserted) << "Resurce " << resource->Name()
+  RTC_DCHECK(inserted) << "Resource " << resource->Name()
                        << " already was inserted";
   adaptation_processor_->AddResource(resource);
 }
@@ -259,6 +282,8 @@
   RTC_DCHECK_RUN_ON(encoder_queue_);
   encoder_settings_ = std::move(encoder_settings);
   bitrate_constraint_->OnEncoderSettingsUpdated(encoder_settings_);
+  initial_frame_dropper_->OnEncoderSettingsUpdated(
+      encoder_settings_->video_codec());
   MaybeUpdateTargetFrameRate();
 }
 
@@ -339,6 +364,12 @@
   return initial_frame_dropper_->DropInitialFrames();
 }
 
+absl::optional<uint32_t>
+VideoStreamEncoderResourceManager::SingleActiveStreamPixels() const {
+  RTC_DCHECK_RUN_ON(encoder_queue_);
+  return initial_frame_dropper_->single_active_stream_pixels();
+}
+
 void VideoStreamEncoderResourceManager::OnMaybeEncodeFrame() {
   RTC_DCHECK_RUN_ON(encoder_queue_);
   initial_frame_dropper_->OnMaybeEncodeFrame();
diff --git a/video/adaptation/video_stream_encoder_resource_manager.h b/video/adaptation/video_stream_encoder_resource_manager.h
index 932d90c..5190373 100644
--- a/video/adaptation/video_stream_encoder_resource_manager.h
+++ b/video/adaptation/video_stream_encoder_resource_manager.h
@@ -121,9 +121,10 @@
                    VideoAdaptationReason reason);
   void RemoveResource(rtc::scoped_refptr<Resource> resource);
   std::vector<AdaptationConstraint*> AdaptationConstraints() const;
-  // If true, the VideoStreamEncoder should eexecute its logic to maybe drop
-  // frames baseed on size and bitrate.
+  // If true, the VideoStreamEncoder should execute its logic to maybe drop
+  // frames based on size and bitrate.
   bool DropInitialFrames() const;
+  absl::optional<uint32_t> SingleActiveStreamPixels() const;
 
   // VideoSourceRestrictionsListener implementation.
   // Updates |video_source_restrictions_|.
diff --git a/video/quality_scaling_tests.cc b/video/quality_scaling_tests.cc
index 65a23db..b72b25b 100644
--- a/video/quality_scaling_tests.cc
+++ b/video/quality_scaling_tests.cc
@@ -56,6 +56,7 @@
  protected:
   void RunTest(VideoEncoderFactory* encoder_factory,
                const std::string& payload_name,
+               const std::vector<bool>& streams_active,
                int start_bps,
                bool automatic_resize,
                bool frame_dropping,
@@ -67,6 +68,7 @@
 
 void QualityScalingTest::RunTest(VideoEncoderFactory* encoder_factory,
                                  const std::string& payload_name,
+                                 const std::vector<bool>& streams_active,
                                  int start_bps,
                                  bool automatic_resize,
                                  bool frame_dropping,
@@ -77,6 +79,7 @@
    public:
     ScalingObserver(VideoEncoderFactory* encoder_factory,
                     const std::string& payload_name,
+                    const std::vector<bool>& streams_active,
                     int start_bps,
                     bool automatic_resize,
                     bool frame_dropping,
@@ -84,6 +87,7 @@
         : SendTest(expect_adaptation ? kDefaultTimeoutMs : kTimeoutMs),
           encoder_factory_(encoder_factory),
           payload_name_(payload_name),
+          streams_active_(streams_active),
           start_bps_(start_bps),
           automatic_resize_(automatic_resize),
           frame_dropping_(frame_dropping),
@@ -108,6 +112,10 @@
       bitrate_config->start_bitrate_bps = start_bps_;
     }
 
+    size_t GetNumVideoStreams() const override {
+      return streams_active_.size();
+    }
+
     void ModifyVideoConfigs(
         VideoSendStream::Config* send_config,
         std::vector<VideoReceiveStream::Config>* receive_configs,
@@ -117,7 +125,15 @@
       send_config->rtp.payload_type = kVideoSendPayloadType;
       const VideoCodecType codec_type = PayloadStringToCodecType(payload_name_);
       encoder_config->codec_type = codec_type;
-      encoder_config->max_bitrate_bps = start_bps_;
+      encoder_config->max_bitrate_bps =
+          std::max(start_bps_, encoder_config->max_bitrate_bps);
+      double scale_factor = 1.0;
+      for (int i = streams_active_.size() - 1; i >= 0; --i) {
+        VideoStream& stream = encoder_config->simulcast_layers[i];
+        stream.active = streams_active_[i];
+        stream.scale_resolution_down_by = scale_factor;
+        scale_factor *= 2.0;
+      }
       SetEncoderSpecific(encoder_config, codec_type, automatic_resize_,
                          frame_dropping_);
     }
@@ -129,12 +145,13 @@
 
     VideoEncoderFactory* const encoder_factory_;
     const std::string payload_name_;
+    const std::vector<bool> streams_active_;
     const int start_bps_;
     const bool automatic_resize_;
     const bool frame_dropping_;
     const bool expect_adaptation_;
-  } test(encoder_factory, payload_name, start_bps, automatic_resize,
-         frame_dropping, expect_adaptation);
+  } test(encoder_factory, payload_name, streams_active, start_bps,
+         automatic_resize, frame_dropping, expect_adaptation);
 
   RunBaseTest(&test);
 }
@@ -150,7 +167,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -165,7 +182,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -182,7 +199,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -197,7 +214,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -212,10 +229,57 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kLowStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
+TEST_F(QualityScalingTest, NoAdaptDownForLowStartBitrate_Simulcast) {
+  // VP8 QP thresholds, low:1, high:127 -> normal QP.
+  test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
+
+  // QualityScaler disabled.
+  const bool kAutomaticResize = false;
+  const bool kFrameDropping = true;
+  const bool kExpectAdapt = false;
+
+  test::FunctionVideoEncoderFactory encoder_factory(
+      []() { return VP8Encoder::Create(); });
+  RunTest(&encoder_factory, "VP8", {true, true}, kLowStartBps, kAutomaticResize,
+          kFrameDropping, kExpectAdapt);
+}
+
+TEST_F(QualityScalingTest,
+       AdaptsDownForLowStartBitrate_SimulcastOneActiveHighRes) {
+  // VP8 QP thresholds, low:1, high:127 -> normal QP.
+  test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
+
+  // QualityScaler enabled.
+  const bool kAutomaticResize = true;
+  const bool kFrameDropping = true;
+  const bool kExpectAdapt = true;
+
+  test::FunctionVideoEncoderFactory encoder_factory(
+      []() { return VP8Encoder::Create(); });
+  RunTest(&encoder_factory, "VP8", {false, false, true}, kLowStartBps,
+          kAutomaticResize, kFrameDropping, kExpectAdapt);
+}
+
+TEST_F(QualityScalingTest,
+       NoAdaptDownForLowStartBitrate_SimulcastOneActiveLowRes) {
+  // VP8 QP thresholds, low:1, high:127 -> normal QP.
+  test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
+
+  // QualityScaler enabled.
+  const bool kAutomaticResize = true;
+  const bool kFrameDropping = true;
+  const bool kExpectAdapt = false;
+
+  test::FunctionVideoEncoderFactory encoder_factory(
+      []() { return VP8Encoder::Create(); });
+  RunTest(&encoder_factory, "VP8", {true, false, false}, kLowStartBps,
+          kAutomaticResize, kFrameDropping, kExpectAdapt);
+}
+
 TEST_F(QualityScalingTest, NoAdaptDownForLowStartBitrateWithScalingOff) {
   // VP8 QP thresholds, low:1, high:127 -> normal QP.
   test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
@@ -227,7 +291,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP8Encoder::Create(); });
-  RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP8", {true}, kLowStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -243,7 +307,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return VP9Encoder::Create(); });
-  RunTest(&encoder_factory, "VP9", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "VP9", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 
@@ -259,7 +323,7 @@
 
   test::FunctionVideoEncoderFactory encoder_factory(
       []() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
-  RunTest(&encoder_factory, "H264", kHighStartBps, kAutomaticResize,
+  RunTest(&encoder_factory, "H264", {true}, kHighStartBps, kAutomaticResize,
           kFrameDropping, kExpectAdapt);
 }
 #endif  // defined(WEBRTC_USE_H264)
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 836b20c..c197d23 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -565,6 +565,7 @@
 void VideoStreamEncoder::SetStartBitrate(int start_bitrate_bps) {
   encoder_queue_.PostTask([this, start_bitrate_bps] {
     RTC_DCHECK_RUN_ON(&encoder_queue_);
+    RTC_LOG(LS_INFO) << "SetStartBitrate " << start_bitrate_bps;
     encoder_target_bitrate_bps_ =
         start_bitrate_bps != 0 ? absl::optional<uint32_t>(start_bitrate_bps)
                                : absl::nullopt;
@@ -1848,14 +1849,20 @@
   bool simulcast_or_svc =
       (send_codec_.codecType == VideoCodecType::kVideoCodecVP9 &&
        send_codec_.VP9().numberOfSpatialLayers > 1) ||
-      send_codec_.numberOfSimulcastStreams > 1 ||
-      encoder_config_.simulcast_layers.size() > 1;
+      ((send_codec_.numberOfSimulcastStreams > 1 ||
+        encoder_config_.simulcast_layers.size() > 1) &&
+       !stream_resource_manager_.SingleActiveStreamPixels());
 
   if (simulcast_or_svc || !stream_resource_manager_.DropInitialFrames() ||
       !encoder_target_bitrate_bps_.has_value()) {
     return false;
   }
 
+  if (send_codec_.numberOfSimulcastStreams > 1 &&
+      stream_resource_manager_.SingleActiveStreamPixels()) {
+    pixel_count = stream_resource_manager_.SingleActiveStreamPixels().value();
+  }
+
   absl::optional<VideoEncoder::ResolutionBitrateLimits> encoder_bitrate_limits =
       encoder_->GetEncoderInfo().GetEncoderBitrateLimitsForResolution(
           pixel_count);