Add support for VideoLayersAllocation for Vp9 scv/ksvc and none scalable
VideoCodecInitializer::VideoEncoderConfigToVideoCodec is modified to always set correct frame rate, width and height on spatial layer 0 so the rest of the code does not need to differentiate between scalable/none scalable codecs.
Bug: webrtc:12000
Change-Id: I5a068b98ca2038621205f55e4024f949ab51587a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198540
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32890}
diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc
index 983e2a0..90a02e0 100644
--- a/modules/video_coding/video_codec_initializer.cc
+++ b/modules/video_coding/video_codec_initializer.cc
@@ -148,6 +148,12 @@
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 22d4ac5..41c172c 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -218,12 +218,12 @@
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 @@
}
}
// Encoder may drop frames internally if `maxFramerate` is set.
- spatial_layer.frame_rate_fps = std::min(
- static_cast<uint8_t>(encoder_config.simulcastStream[si].maxFramerate),
- static_cast<uint8_t>(
- (current_rate.framerate_fps * frame_rate_fraction) /
- VideoEncoder::EncoderInfo::kMaxFramerateFraction));
+ spatial_layer.frame_rate_fps = std::min<uint8_t>(
+ encoder_config.simulcastStream[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.
+ } 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<DataRate> 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<uint8_t>(
+ encoder_config.spatialLayers[si].maxFramerate,
+ (current_rate.framerate_fps * frame_rate_fraction) /
+ VideoEncoder::EncoderInfo::kMaxFramerateFraction);
+ }
}
return layers_allocation;
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index aed619d..ced05ec 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -842,8 +842,10 @@
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 @@
video_stream_encoder_->Stop();
}
-TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForV8Simulcast) {
+TEST_F(VideoStreamEncoderTest, ReportsVideoLayersAllocationForVP8Simulcast) {
ResetEncoder("VP8", /*num_streams*/ 2, 1, 1, /*screenshare*/ false,
VideoStreamEncoderSettings::BitrateAllocationCallbackType::
kVideoLayersAllocation);
@@ -4083,6 +4085,444 @@
}
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<VideoEncoderConfig::Vp8EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp8EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
+ 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,
VideoStreamEncoderSettings::BitrateAllocationCallbackType::