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) {