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::