Make sure VP9 encoders are reconfigured on layer activation.
When disabling a spatial layer, reconfiguration of the encoder is not
necessary (bitrate will never be assigned to the inactive layer anway).
This CL however makes sure we reconfigure the encoder when a spatial
layer is activated. Some encoder implementations may encoder the wrong
number of spatial layers if the active layers have not beens set
correctly.
Bug: webrtc:14809, b/261097903
Change-Id: I8d34aaec95eb50a9717c06ea38f25088e5a96429
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/290560
Commit-Queue: Philip Eliasson <philipel@webrtc.org>
Auto-Submit: Erik Språng <sprang@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38999}
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 9941732..c680fe1 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -150,6 +150,10 @@
if (new_send_codec.codecType == kVideoCodecVP9) {
size_t num_spatial_layers = new_send_codec.VP9().numberOfSpatialLayers;
for (unsigned char i = 0; i < num_spatial_layers; ++i) {
+ if (!new_send_codec.spatialLayers[i].active) {
+ // No need to reset when layer is inactive.
+ continue;
+ }
if (new_send_codec.spatialLayers[i].width !=
prev_send_codec.spatialLayers[i].width ||
new_send_codec.spatialLayers[i].height !=
@@ -157,7 +161,8 @@
new_send_codec.spatialLayers[i].numberOfTemporalLayers !=
prev_send_codec.spatialLayers[i].numberOfTemporalLayers ||
new_send_codec.spatialLayers[i].qpMax !=
- prev_send_codec.spatialLayers[i].qpMax) {
+ prev_send_codec.spatialLayers[i].qpMax ||
+ !prev_send_codec.spatialLayers[i].active) {
return true;
}
}
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 1f328cc..cdd4c75 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -8649,6 +8649,64 @@
video_stream_encoder_->Stop();
}
+TEST_F(VideoStreamEncoderTest, RecreatesEncoderWhenEnableVp9SpatialLayer) {
+ // Set up encoder to use VP9 SVC using two spatial layers.
+ fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true);
+ fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true);
+ VideoEncoderConfig video_encoder_config;
+ test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9,
+ /* num_streams*/ 1, &video_encoder_config);
+ video_encoder_config.max_bitrate_bps = 2 * kTargetBitrate.bps();
+ video_encoder_config.content_type =
+ VideoEncoderConfig::ContentType::kRealtimeVideo;
+ VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
+ vp9_settings.numberOfSpatialLayers = 2;
+ vp9_settings.numberOfTemporalLayers = 2;
+ vp9_settings.interLayerPred = InterLayerPredMode::kOn;
+ vp9_settings.automaticResizeOn = false;
+ video_encoder_config.encoder_specific_settings =
+ rtc::make_ref_counted<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ vp9_settings);
+ video_encoder_config.spatial_layers = GetSvcConfig(1280, 720,
+ /*fps=*/30.0,
+ /*first_active_layer=*/0,
+ /*num_spatial_layers=*/2,
+ /*num_temporal_layers=*/3,
+ /*is_screenshare=*/false);
+ ConfigureEncoder(video_encoder_config.Copy(),
+ VideoStreamEncoder::BitrateAllocationCallbackType::
+ kVideoLayersAllocation);
+
+ video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources(
+ kTargetBitrate, kTargetBitrate, kTargetBitrate, 0, 0, 0);
+
+ video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720));
+ WaitForEncodedFrame(CurrentTimeMs());
+ EXPECT_EQ(fake_encoder_.GetNumInitializations(), 1);
+
+ // Turn off the top spatial layer. This does not require an encoder reset.
+ video_encoder_config.spatial_layers[1].active = false;
+ video_stream_encoder_->ConfigureEncoder(video_encoder_config.Copy(),
+ kMaxPayloadLength, nullptr);
+
+ time_controller_.AdvanceTime(TimeDelta::Millis(33));
+ video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720));
+ WaitForEncodedFrame(CurrentTimeMs());
+ EXPECT_EQ(fake_encoder_.GetNumInitializations(), 1);
+
+ // Turn on the top spatial layer again, this does require an encoder reset.
+ video_encoder_config.spatial_layers[1].active = true;
+ video_stream_encoder_->ConfigureEncoder(video_encoder_config.Copy(),
+ kMaxPayloadLength, nullptr);
+
+ time_controller_.AdvanceTime(TimeDelta::Millis(33));
+ video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720));
+ WaitForEncodedFrame(CurrentTimeMs());
+ EXPECT_EQ(fake_encoder_.GetNumInitializations(), 2);
+
+ video_stream_encoder_->Stop();
+}
+
#endif // !defined(WEBRTC_IOS)
// Test parameters: (VideoCodecType codec, bool allow_i420_conversion)