diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 983e2a07a8..90a02e0c2d 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -148,6 +148,12 @@ VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec( video_codec.maxBitrate = kEncoderMinBitrateKbps; video_codec.maxFramerate = max_framerate; + video_codec.spatialLayers[0] = {0}; + video_codec.spatialLayers[0].width = video_codec.width; + video_codec.spatialLayers[0].height = video_codec.height; + video_codec.spatialLayers[0].maxFramerate = max_framerate; + video_codec.spatialLayers[0].numberOfTemporalLayers = + streams[0].num_temporal_layers.value_or(1); // Set codec specific options if (config.encoder_specific_settings) diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 22d4ac50f3..41c172cf53 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -218,12 +218,12 @@ VideoLayersAllocation CreateVideoLayersAllocation( return layers_allocation; } - if (encoder_config.numberOfSimulcastStreams > 0) { + if (encoder_config.numberOfSimulcastStreams > 1) { layers_allocation.resolution_and_frame_rate_is_valid = true; for (int si = 0; si < encoder_config.numberOfSimulcastStreams; ++si) { if (!target_bitrate.IsSpatialLayerUsed(si) || target_bitrate.GetSpatialLayerSum(si) == 0) { - break; + continue; } layers_allocation.active_spatial_layers.emplace_back(); VideoLayersAllocation::SpatialLayer& spatial_layer = @@ -258,14 +258,80 @@ VideoLayersAllocation CreateVideoLayersAllocation( } } // Encoder may drop frames internally if `maxFramerate` is set. - spatial_layer.frame_rate_fps = std::min( - static_cast(encoder_config.simulcastStream[si].maxFramerate), - static_cast( - (current_rate.framerate_fps * frame_rate_fraction) / - VideoEncoder::EncoderInfo::kMaxFramerateFraction)); + spatial_layer.frame_rate_fps = std::min( + encoder_config.simulcastStream[si].maxFramerate, + (current_rate.framerate_fps * frame_rate_fraction) / + VideoEncoder::EncoderInfo::kMaxFramerateFraction); + } + } else if (encoder_config.numberOfSimulcastStreams == 1) { + // TODO(bugs.webrtc.org/12000): Implement support for AV1 with + // scalability. + const bool higher_spatial_depend_on_lower = + encoder_config.codecType == kVideoCodecVP9 && + encoder_config.VP9().interLayerPred == InterLayerPredMode::kOn; + layers_allocation.resolution_and_frame_rate_is_valid = true; + + std::vector aggregated_spatial_bitrate( + webrtc::kMaxTemporalStreams, DataRate::Zero()); + for (int si = 0; si < webrtc::kMaxSpatialLayers; ++si) { + layers_allocation.resolution_and_frame_rate_is_valid = true; + if (!target_bitrate.IsSpatialLayerUsed(si) || + target_bitrate.GetSpatialLayerSum(si) == 0) { + break; + } + layers_allocation.active_spatial_layers.emplace_back(); + VideoLayersAllocation::SpatialLayer& spatial_layer = + layers_allocation.active_spatial_layers.back(); + spatial_layer.width = encoder_config.spatialLayers[si].width; + spatial_layer.height = encoder_config.spatialLayers[si].height; + spatial_layer.rtp_stream_index = 0; + spatial_layer.spatial_id = si; + auto frame_rate_fraction = + VideoEncoder::EncoderInfo::kMaxFramerateFraction; + if (encoder_info.fps_allocation[si].size() == 1) { + // One TL is signalled to be used by the encoder. Do not distribute + // bitrate allocation across TLs (use sum at tl:0). + DataRate aggregated_temporal_bitrate = + DataRate::BitsPerSec(target_bitrate.GetSpatialLayerSum(si)); + aggregated_spatial_bitrate[0] += aggregated_temporal_bitrate; + if (higher_spatial_depend_on_lower) { + spatial_layer.target_bitrate_per_temporal_layer.push_back( + aggregated_spatial_bitrate[0]); + } else { + spatial_layer.target_bitrate_per_temporal_layer.push_back( + aggregated_temporal_bitrate); + } + frame_rate_fraction = encoder_info.fps_allocation[si][0]; + } else { // Temporal layers are supported. + DataRate aggregated_temporal_bitrate = DataRate::Zero(); + for (size_t ti = 0; + ti < encoder_config.spatialLayers[si].numberOfTemporalLayers; + ++ti) { + if (!target_bitrate.HasBitrate(si, ti)) { + break; + } + if (ti < encoder_info.fps_allocation[si].size()) { + // Use frame rate of the top used temporal layer. + frame_rate_fraction = encoder_info.fps_allocation[si][ti]; + } + aggregated_temporal_bitrate += + DataRate::BitsPerSec(target_bitrate.GetBitrate(si, ti)); + if (higher_spatial_depend_on_lower) { + spatial_layer.target_bitrate_per_temporal_layer.push_back( + aggregated_temporal_bitrate + aggregated_spatial_bitrate[ti]); + aggregated_spatial_bitrate[ti] += aggregated_temporal_bitrate; + } else { + spatial_layer.target_bitrate_per_temporal_layer.push_back( + aggregated_temporal_bitrate); + } + } + } + // Encoder may drop frames internally if `maxFramerate` is set. + spatial_layer.frame_rate_fps = std::min( + encoder_config.spatialLayers[si].maxFramerate, + (current_rate.framerate_fps * frame_rate_fraction) / + VideoEncoder::EncoderInfo::kMaxFramerateFraction); } - } else { - // TODO(bugs.webrtc.org/12000): Implement support for kSVC and full SVC. } return layers_allocation; diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc index aed619db26..ced05ec916 100644 --- a/video/video_stream_encoder_unittest.cc +++ b/video/video_stream_encoder_unittest.cc @@ -842,8 +842,10 @@ class VideoStreamEncoderTest : public ::testing::Test { info.is_hardware_accelerated = is_hardware_accelerated_; for (int i = 0; i < kMaxSpatialLayers; ++i) { if (temporal_layers_supported_[i]) { + info.fps_allocation[i].clear(); int num_layers = temporal_layers_supported_[i].value() ? 2 : 1; - info.fps_allocation[i].resize(num_layers); + for (int tid = 0; tid < num_layers; ++tid) + info.fps_allocation[i].push_back(255 / (num_layers - tid)); } } } @@ -4030,7 +4032,7 @@ TEST_F(VideoStreamEncoderTest, ReportsVideoBitrateAllocation) { video_stream_encoder_->Stop(); } -TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForV8Simulcast) { +TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForVP8Simulcast) { ResetEncoder("VP8", /*num_streams*/ 2, 1, 1, /*screenshare*/ false, VideoStreamEncoderSettings::BitrateAllocationCallbackType:: kVideoLayersAllocation); @@ -4082,6 +4084,444 @@ TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForV8Simulcast) { video_stream_encoder_->Stop(); } +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForVP8WithMidleLayerDisabled) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP8, + /* num_streams*/ 3, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + video_encoder_config.content_type = + VideoEncoderConfig::ContentType::kRealtimeVideo; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + VideoEncoder::GetDefaultVp8Settings()); + for (auto& layer : video_encoder_config.simulcast_layers) { + layer.num_temporal_layers = 2; + } + // Simulcast layers are used for enabling/disabling streams. + video_encoder_config.simulcast_layers[0].active = true; + video_encoder_config.simulcast_layers[1].active = false; + video_encoder_config.simulcast_layers[2].active = true; + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_LT(last_layer_allocation.active_spatial_layers[0].width, 1280); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].width, 1280); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForVP8WithMidleAndHighestLayerDisabled) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP8, + /* num_streams*/ 3, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + video_encoder_config.content_type = + VideoEncoderConfig::ContentType::kRealtimeVideo; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + VideoEncoder::GetDefaultVp8Settings()); + for (auto& layer : video_encoder_config.simulcast_layers) { + layer.num_temporal_layers = 2; + } + // Simulcast layers are used for enabling/disabling streams. + video_encoder_config.simulcast_layers[0].active = true; + video_encoder_config.simulcast_layers[1].active = false; + video_encoder_config.simulcast_layers[2].active = false; + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(1)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_LT(last_layer_allocation.active_spatial_layers[0].width, 1280); + + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForV9SvcWithTemporalLayerSupport) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + 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 = + new rtc::RefCountedObject( + vp9_settings); + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].width, 640); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].height, 360); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].frame_rate_fps, 30); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].width, 1280); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].height, 720); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].frame_rate_fps, 30); + + // Since full SVC is used, expect the top layer to utilize the full target + // rate. + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer[1], + DataRate::BitsPerSec(kTargetBitrateBps)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForV9SvcWithoutTemporalLayerSupport) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, false); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, false); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + 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 = + new rtc::RefCountedObject( + vp9_settings); + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(1)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer, + SizeIs(1)); + // Since full SVC is used, expect the top layer to utilize the full target + // rate. + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer[0], + DataRate::BitsPerSec(kTargetBitrateBps)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForVP9KSvcWithTemporalLayerSupport) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + 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::kOnKeyPic; + vp9_settings.automaticResizeOn = false; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + vp9_settings); + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + // Since KSVC is, spatial layers are independend except on key frames. + EXPECT_LT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer[1], + DataRate::BitsPerSec(kTargetBitrateBps)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForV9SvcWithLowestLayerDisabled) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + video_encoder_config.content_type = + VideoEncoderConfig::ContentType::kRealtimeVideo; + VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings(); + vp9_settings.numberOfSpatialLayers = 3; + vp9_settings.numberOfTemporalLayers = 2; + vp9_settings.interLayerPred = InterLayerPredMode::kOn; + vp9_settings.automaticResizeOn = false; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + vp9_settings); + // Simulcast layers are used for enabling/disabling streams. + video_encoder_config.simulcast_layers.resize(3); + video_encoder_config.simulcast_layers[0].active = false; + video_encoder_config.simulcast_layers[1].active = true; + video_encoder_config.simulcast_layers[2].active = true; + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].width, 640); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].spatial_id, 0); + + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].width, 1280); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].spatial_id, 1); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + // Since full SVC is used, expect the top layer to utilize the full target + // rate. + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer[1], + DataRate::BitsPerSec(kTargetBitrateBps)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForV9SvcWithHighestLayerDisabled) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + video_encoder_config.content_type = + VideoEncoderConfig::ContentType::kRealtimeVideo; + VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings(); + vp9_settings.numberOfSpatialLayers = 3; + vp9_settings.numberOfTemporalLayers = 2; + vp9_settings.interLayerPred = InterLayerPredMode::kOn; + vp9_settings.automaticResizeOn = false; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + vp9_settings); + // Simulcast layers are used for enabling/disabling streams. + video_encoder_config.simulcast_layers.resize(3); + video_encoder_config.simulcast_layers[2].active = false; + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(2)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].width, 320); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].spatial_id, 0); + + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].width, 640); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[1].spatial_id, 1); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[1] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, + ReportsVideoLayersAllocationForV9SvcWithAllButHighestLayerDisabled) { + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true); + fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true); + video_send_config_.encoder_settings.allocation_cb_type = + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation; + VideoEncoderConfig video_encoder_config; + test::FillEncoderConfiguration(VideoCodecType::kVideoCodecVP9, + /* num_streams*/ 1, &video_encoder_config); + video_encoder_config.max_bitrate_bps = 2 * kTargetBitrateBps; + video_encoder_config.content_type = + VideoEncoderConfig::ContentType::kRealtimeVideo; + VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings(); + vp9_settings.numberOfSpatialLayers = 3; + vp9_settings.numberOfTemporalLayers = 2; + vp9_settings.interLayerPred = InterLayerPredMode::kOn; + vp9_settings.automaticResizeOn = false; + video_encoder_config.encoder_specific_settings = + new rtc::RefCountedObject( + vp9_settings); + // Simulcast layers are used for enabling/disabling streams. + video_encoder_config.simulcast_layers.resize(3); + video_encoder_config.simulcast_layers[0].active = false; + video_encoder_config.simulcast_layers[1].active = false; + video_encoder_config.simulcast_layers[2].active = true; + ConfigureEncoder(std::move(video_encoder_config)); + + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(1)); + EXPECT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(2)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].width, 1280); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].spatial_id, 0); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer[1], + DataRate::BitsPerSec(kTargetBitrateBps)); + video_stream_encoder_->Stop(); +} + +TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForH264) { + ResetEncoder("H264", 1, 1, 1, false, + VideoStreamEncoderSettings::BitrateAllocationCallbackType:: + kVideoLayersAllocation); + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), + DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0); + + video_source_.IncomingCapturedFrame(CreateFrame(CurrentTimeMs(), 1280, 720)); + WaitForEncodedFrame(CurrentTimeMs()); + EXPECT_EQ(sink_.number_of_layers_allocations(), 1); + VideoLayersAllocation last_layer_allocation = + sink_.GetLastVideoLayersAllocation(); + + ASSERT_THAT(last_layer_allocation.active_spatial_layers, SizeIs(1)); + ASSERT_THAT(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer, + SizeIs(1)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0] + .target_bitrate_per_temporal_layer[0], + DataRate::BitsPerSec(kTargetBitrateBps)); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].width, 1280); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].height, 720); + EXPECT_EQ(last_layer_allocation.active_spatial_layers[0].frame_rate_fps, 30); + video_stream_encoder_->Stop(); +} + TEST_F(VideoStreamEncoderTest, ReportsUpdatedVideoLayersAllocationWhenBweChanges) { ResetEncoder("VP8", /*num_streams*/ 2, 1, 1, /*screenshare*/ false,