Prevent wrong scalability mode from being used when base layer inactive.

In SimulcastEncoderAdapter, use the "global" scalabilty mode from the
VideoCodec config struct for a stream only if there is single active
simulcast layer AND that layer is the first layer in the list.

Bug: webrtc:510393737
Change-Id: I37cfd4861b0b8898db3d0f5e13ec5b1f8d5cc1c1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/470520
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47648}
diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc
index 2f61bdd..2edb6be 100644
--- a/media/engine/simulcast_encoder_adapter.cc
+++ b/media/engine/simulcast_encoder_adapter.cc
@@ -1006,15 +1006,19 @@
   std::optional<ScalabilityMode> scalability_mode =
       stream_params.GetScalabilityMode();
   // To support the full set of scalability modes in the event that this is the
-  // only active encoding, prefer VideoCodec::GetScalabilityMode() if all other
-  // encodings are inactive.
-  bool only_active_stream = true;
-  for (int i = 0; i < codec.numberOfSimulcastStreams; ++i) {
-    if (i != stream_idx && codec.simulcastStream[i].active) {
-      only_active_stream = false;
-      break;
+  // only active encoding, prefer VideoCodec::GetScalabilityMode() - but only if
+  // it's the first simulcast layer in the list that is the only active one.
+  bool only_active_stream = false;
+  if (stream_idx == 0 && codec.simulcastStream[0].active) {
+    only_active_stream = true;
+    for (int i = 1; i < codec.numberOfSimulcastStreams; ++i) {
+      if (codec.simulcastStream[i].active) {
+        only_active_stream = false;
+        break;
+      }
     }
   }
+
   if (codec.GetScalabilityMode().has_value() && only_active_stream) {
     scalability_mode = codec.GetScalabilityMode();
   }
diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc
index ff843b7..9a47a62 100644
--- a/media/engine/simulcast_encoder_adapter_unittest.cc
+++ b/media/engine/simulcast_encoder_adapter_unittest.cc
@@ -2717,6 +2717,45 @@
             ScalabilityMode::kL1T3);
 }
 
+TEST_F(TestSimulcastEncoderAdapterFake,
+       ScalabilityModeWithInactiveFirstStream) {
+  SimulcastTestFixtureImpl::DefaultSettings(
+      &codec_, static_cast<const int*>(kTestTemporalLayerProfile),
+      kVideoCodecVP8);
+  codec_.numberOfSimulcastStreams = 2;
+  codec_.SetScalabilityMode(ScalabilityMode::kL1T3);
+
+  codec_.simulcastStream[0].numberOfTemporalLayers = 3;
+  codec_.simulcastStream[0].active = false;
+  codec_.simulcastStream[1].numberOfTemporalLayers = 2;
+  codec_.simulcastStream[1].active = true;
+
+  EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings));
+
+  // Only one encoder should be created for the active stream.
+  ASSERT_EQ(1u, helper_->factory()->encoders().size());
+
+  // The encoder should be configured with L1T2, matching the active stream.
+  EXPECT_EQ(helper_->factory()->encoders()[0]->codec().GetScalabilityMode(),
+            ScalabilityMode::kL1T2);
+
+  // Verify rate allocation.
+  VideoBitrateAllocation allocation;
+  allocation.SetBitrate(0, 0, 0);       // Stream 0 inactive
+  allocation.SetBitrate(1, 0, 100000);  // Stream 1, temporal 0
+  allocation.SetBitrate(1, 1, 200000);  // Stream 1, temporal 1
+
+  adapter_->SetRates(VideoEncoder::RateControlParameters(allocation, 30.0));
+
+  std::vector<MockVideoEncoder*> encoders = helper_->factory()->encoders();
+  ASSERT_EQ(1u, encoders.size());
+
+  // The active encoder should get the allocation for stream 1 mapped to its own
+  // spatial layer 0.
+  EXPECT_EQ(100000u, encoders[0]->last_set_rates().bitrate.GetBitrate(0, 0));
+  EXPECT_EQ(200000u, encoders[0]->last_set_rates().bitrate.GetBitrate(0, 1));
+}
+
 // In the case of mixed-codec simulcast, verify whether each encoder is created
 // with the specified video format.
 TEST_F(TestSimulcastEncoderAdapterFake, InitEncodeForMixedCodec) {