Activate/deactivate VP9 spatial layers.

* Stop encoding spatial layers S(n >= N) if application deactivates
spatial layer N by setting RTCRtpEncodingParameters.active = false.

* Move calculation of padding bitrate to SvcRateAllocator class.

* Pad up to minimum required bitrate of base layer if ALR probing is
enabled.

Bug: webrtc:9350
Change-Id: I398284c943d43348def535c83263fc234c9767fa
Reviewed-on: https://webrtc-review.googlesource.com/c/113240
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25945}
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 48639c4..c5e4cdf 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -1989,7 +1989,9 @@
   RTC_DCHECK_GE(rtp_parameters_.encodings.size(),
                 encoder_config.number_of_streams);
   RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
-  encoder_config.simulcast_layers.resize(encoder_config.number_of_streams);
+
+  // Copy all provided constraints.
+  encoder_config.simulcast_layers.resize(rtp_parameters_.encodings.size());
   for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i) {
     encoder_config.simulcast_layers[i].active =
         rtp_parameters_.encodings[i].active;
@@ -2701,7 +2703,7 @@
     RTC_DCHECK_EQ(1, encoder_config.number_of_streams);
   }
   RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
-  RTC_DCHECK_EQ(encoder_config.simulcast_layers.size(),
+  RTC_DCHECK_GE(encoder_config.simulcast_layers.size(),
                 encoder_config.number_of_streams);
   std::vector<webrtc::VideoStream> layers;
 
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index 4ba3c48..5e81836 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -3112,6 +3112,38 @@
   EXPECT_TRUE(channel_->SetVideoSend(ssrcs[0], nullptr, nullptr));
 }
 
+TEST_F(Vp9SettingsTest, AllEncodingParametersCopied) {
+  cricket::VideoSendParameters send_parameters;
+  send_parameters.codecs.push_back(GetEngineCodec("VP9"));
+  ASSERT_TRUE(channel_->SetSendParameters(send_parameters));
+
+  const size_t kNumSpatialLayers = 3;
+  std::vector<uint32_t> ssrcs = MAKE_VECTOR(kSsrcs3);
+
+  FakeVideoSendStream* stream =
+      AddSendStream(CreateSimStreamParams("cname", ssrcs));
+
+  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(ssrcs[0]);
+  ASSERT_EQ(kNumSpatialLayers, parameters.encodings.size());
+  ASSERT_TRUE(parameters.encodings[0].active);
+  ASSERT_TRUE(parameters.encodings[1].active);
+  ASSERT_TRUE(parameters.encodings[2].active);
+  // Invert value to verify copying.
+  parameters.encodings[1].active = false;
+  EXPECT_TRUE(channel_->SetRtpSendParameters(ssrcs[0], parameters).ok());
+
+  webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
+
+  // number_of_streams should be 1 since all spatial layers are sent on the
+  // same SSRC. But encoding parameters of all layers is supposed to be copied
+  // and stored in simulcast_layers[].
+  EXPECT_EQ(1u, encoder_config.number_of_streams);
+  EXPECT_EQ(encoder_config.simulcast_layers.size(), kNumSpatialLayers);
+  EXPECT_TRUE(encoder_config.simulcast_layers[0].active);
+  EXPECT_FALSE(encoder_config.simulcast_layers[1].active);
+  EXPECT_TRUE(encoder_config.simulcast_layers[2].active);
+}
+
 class Vp9SettingsTestWithFieldTrial : public Vp9SettingsTest {
  public:
   explicit Vp9SettingsTestWithFieldTrial(const char* field_trials)
diff --git a/modules/video_coding/codecs/vp9/svc_config.cc b/modules/video_coding/codecs/vp9/svc_config.cc
index 0e76b09..1754195 100644
--- a/modules/video_coding/codecs/vp9/svc_config.cc
+++ b/modules/video_coding/codecs/vp9/svc_config.cc
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
-#include "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
@@ -47,6 +46,7 @@
     spatial_layer.maxBitrate =
         static_cast<int>(kMaxScreenSharingLayerBitrateKbps[sl_idx]);
     spatial_layer.targetBitrate = spatial_layer.maxBitrate;
+    spatial_layer.active = true;
     spatial_layers.push_back(spatial_layer);
   }
 
@@ -70,13 +70,13 @@
   num_spatial_layers =
       std::min({num_spatial_layers, num_layers_fit_horz, num_layers_fit_vert});
 
-  float top_fraction = 0.;
   for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
     SpatialLayer spatial_layer = {0};
     spatial_layer.width = input_width >> (num_spatial_layers - sl_idx - 1);
     spatial_layer.height = input_height >> (num_spatial_layers - sl_idx - 1);
     spatial_layer.maxFramerate = max_framerate_fps;
     spatial_layer.numberOfTemporalLayers = num_temporal_layers;
+    spatial_layer.active = true;
 
     // minBitrate and maxBitrate formulas were derived from
     // subjective-quality data to determing bit rates below which video
@@ -96,14 +96,8 @@
     spatial_layer.targetBitrate =
         (spatial_layer.minBitrate + spatial_layer.maxBitrate) / 2;
     spatial_layers.push_back(spatial_layer);
-    top_fraction += std::pow(kSpatialLayeringRateScalingFactor, sl_idx);
   }
-  // Compute spatial_layers[num_spatial_layers - 1].targetBitrate, which is
-  // used to set max_padding_bitrate_. Set max_padding_bitrate_ equal to the
-  // minimum total bit rate required to support all spatial layers.
-  spatial_layers[num_spatial_layers - 1].targetBitrate =
-      static_cast<unsigned int>(
-          spatial_layers[num_spatial_layers - 1].minBitrate * top_fraction);
+
   return spatial_layers;
 }
 
diff --git a/modules/video_coding/codecs/vp9/svc_rate_allocator.cc b/modules/video_coding/codecs/vp9/svc_rate_allocator.cc
index cf74e08..0535abd 100644
--- a/modules/video_coding/codecs/vp9/svc_rate_allocator.cc
+++ b/modules/video_coding/codecs/vp9/svc_rate_allocator.cc
@@ -23,8 +23,85 @@
 const float kSpatialLayeringRateScalingFactor = 0.55f;
 const float kTemporalLayeringRateScalingFactor = 0.55f;
 
+static size_t GetNumActiveSpatialLayers(const VideoCodec& codec) {
+  RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
+  RTC_DCHECK_GT(codec.VP9().numberOfSpatialLayers, 0u);
+
+  size_t num_spatial_layers = 0;
+  for (; num_spatial_layers < codec.VP9().numberOfSpatialLayers;
+       ++num_spatial_layers) {
+    if (!codec.spatialLayers[num_spatial_layers].active) {
+      // TODO(bugs.webrtc.org/9350): Deactivation of middle layer is not
+      // implemented. For now deactivation of a VP9 layer deactivates all
+      // layers above the deactivated one.
+      break;
+    }
+  }
+
+  return num_spatial_layers;
+}
+
+static bool AdjustAndVerify(const VideoCodec& codec,
+                            std::vector<size_t>* spatial_layer_bitrate_bps) {
+  bool enough_bitrate = true;
+  size_t excess_rate = 0;
+  for (size_t sl_idx = 0;
+       sl_idx < spatial_layer_bitrate_bps->size() && enough_bitrate; ++sl_idx) {
+    RTC_DCHECK_GT(codec.spatialLayers[sl_idx].maxBitrate, 0);
+    RTC_DCHECK_GE(codec.spatialLayers[sl_idx].maxBitrate,
+                  codec.spatialLayers[sl_idx].minBitrate);
+
+    const size_t min_bitrate_bps =
+        codec.spatialLayers[sl_idx].minBitrate * 1000;
+    const size_t max_bitrate_bps =
+        codec.spatialLayers[sl_idx].maxBitrate * 1000;
+
+    spatial_layer_bitrate_bps->at(sl_idx) += excess_rate;
+    if (spatial_layer_bitrate_bps->at(sl_idx) < max_bitrate_bps) {
+      excess_rate = 0;
+    } else {
+      excess_rate = spatial_layer_bitrate_bps->at(sl_idx) - max_bitrate_bps;
+      spatial_layer_bitrate_bps->at(sl_idx) = max_bitrate_bps;
+    }
+
+    size_t bitrate_bps = spatial_layer_bitrate_bps->at(sl_idx);
+    enough_bitrate = (bitrate_bps >= min_bitrate_bps);
+  }
+
+  return enough_bitrate;
+}
+
+static std::vector<size_t> SplitBitrate(size_t num_layers,
+                                        size_t total_bitrate,
+                                        float rate_scaling_factor) {
+  std::vector<size_t> bitrates;
+
+  double denominator = 0.0;
+  for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
+    denominator += std::pow(rate_scaling_factor, layer_idx);
+  }
+
+  double numerator = std::pow(rate_scaling_factor, num_layers - 1);
+  for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
+    bitrates.push_back(numerator * total_bitrate / denominator);
+    numerator /= rate_scaling_factor;
+  }
+
+  const size_t sum = std::accumulate(bitrates.begin(), bitrates.end(), 0);
+  // Ensure the sum of split bitrates doesn't exceed the total bitrate.
+  RTC_DCHECK_LE(sum, total_bitrate);
+
+  // Keep the sum of split bitrates equal to the total bitrate by adding bits,
+  // which were lost due to rounding, to the latest layer.
+  bitrates.back() += total_bitrate - sum;
+
+  return bitrates;
+}
+
 SvcRateAllocator::SvcRateAllocator(const VideoCodec& codec) : codec_(codec) {
   RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
+  RTC_DCHECK_GT(codec.VP9().numberOfSpatialLayers, 0u);
+  RTC_DCHECK_GT(codec.VP9().numberOfTemporalLayers, 0u);
 }
 
 VideoBitrateAllocation SvcRateAllocator::GetAllocation(
@@ -36,24 +113,27 @@
 
   if (codec_.spatialLayers[0].targetBitrate == 0) {
     // Delegate rate distribution to VP9 encoder wrapper if bitrate thresholds
-    // are not initialized.
+    // are not set.
     VideoBitrateAllocation bitrate_allocation;
     bitrate_allocation.SetBitrate(0, 0, total_bitrate_bps);
     return bitrate_allocation;
-  } else if (codec_.mode == VideoCodecMode::kRealtimeVideo) {
-    return GetAllocationNormalVideo(total_bitrate_bps);
+  }
+
+  size_t num_spatial_layers = GetNumActiveSpatialLayers(codec_);
+  if (num_spatial_layers == 0) {
+    return VideoBitrateAllocation();  // All layers are deactivated.
+  }
+
+  if (codec_.mode == VideoCodecMode::kRealtimeVideo) {
+    return GetAllocationNormalVideo(total_bitrate_bps, num_spatial_layers);
   } else {
-    return GetAllocationScreenSharing(total_bitrate_bps);
+    return GetAllocationScreenSharing(total_bitrate_bps, num_spatial_layers);
   }
 }
 
 VideoBitrateAllocation SvcRateAllocator::GetAllocationNormalVideo(
-    uint32_t total_bitrate_bps) const {
-  size_t num_spatial_layers = codec_.VP9().numberOfSpatialLayers;
-  RTC_CHECK(num_spatial_layers > 0);
-  size_t num_temporal_layers = codec_.VP9().numberOfTemporalLayers;
-  RTC_CHECK(num_temporal_layers > 0);
-
+    uint32_t total_bitrate_bps,
+    size_t num_spatial_layers) const {
   std::vector<size_t> spatial_layer_bitrate_bps;
 
   // Distribute total bitrate across spatial layers. If there is not enough
@@ -65,7 +145,8 @@
         SplitBitrate(num_spatial_layers, total_bitrate_bps,
                      kSpatialLayeringRateScalingFactor);
 
-    const bool enough_bitrate = AdjustAndVerify(&spatial_layer_bitrate_bps);
+    const bool enough_bitrate =
+        AdjustAndVerify(codec_, &spatial_layer_bitrate_bps);
     if (enough_bitrate || num_spatial_layers == 1) {
       break;
     }
@@ -73,6 +154,7 @@
 
   VideoBitrateAllocation bitrate_allocation;
 
+  const size_t num_temporal_layers = codec_.VP9().numberOfTemporalLayers;
   for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
     std::vector<size_t> temporal_layer_bitrate_bps =
         SplitBitrate(num_temporal_layers, spatial_layer_bitrate_bps[sl_idx],
@@ -102,41 +184,9 @@
   return bitrate_allocation;
 }
 
-bool SvcRateAllocator::AdjustAndVerify(
-    std::vector<size_t>* spatial_layer_bitrate_bps) const {
-  bool enough_bitrate = true;
-  size_t excess_rate = 0;
-  for (size_t sl_idx = 0;
-       sl_idx < spatial_layer_bitrate_bps->size() && enough_bitrate; ++sl_idx) {
-    RTC_DCHECK_GT(codec_.spatialLayers[sl_idx].maxBitrate, 0);
-    RTC_DCHECK_GE(codec_.spatialLayers[sl_idx].maxBitrate,
-                  codec_.spatialLayers[sl_idx].minBitrate);
-
-    const size_t min_bitrate_bps =
-        codec_.spatialLayers[sl_idx].minBitrate * 1000;
-    const size_t max_bitrate_bps =
-        codec_.spatialLayers[sl_idx].maxBitrate * 1000;
-
-    spatial_layer_bitrate_bps->at(sl_idx) += excess_rate;
-    if (spatial_layer_bitrate_bps->at(sl_idx) < max_bitrate_bps) {
-      excess_rate = 0;
-    } else {
-      excess_rate = spatial_layer_bitrate_bps->at(sl_idx) - max_bitrate_bps;
-      spatial_layer_bitrate_bps->at(sl_idx) = max_bitrate_bps;
-    }
-
-    enough_bitrate = (spatial_layer_bitrate_bps->at(sl_idx) >= min_bitrate_bps);
-  }
-
-  return enough_bitrate;
-}
-
 VideoBitrateAllocation SvcRateAllocator::GetAllocationScreenSharing(
-    uint32_t total_bitrate_bps) const {
-  const size_t num_spatial_layers = codec_.VP9().numberOfSpatialLayers;
-  RTC_CHECK(num_spatial_layers > 0);
-  RTC_CHECK_EQ(codec_.VP9().numberOfTemporalLayers, 1U);
-
+    uint32_t total_bitrate_bps,
+    size_t num_spatial_layers) const {
   VideoBitrateAllocation bitrate_allocation;
 
   // Add next layer after bitrate of previous layer has reached its maximum.
@@ -160,32 +210,43 @@
   return bitrate_allocation;
 }
 
-std::vector<size_t> SvcRateAllocator::SplitBitrate(
-    size_t num_layers,
-    size_t total_bitrate,
-    float rate_scaling_factor) const {
-  std::vector<size_t> bitrates;
+uint32_t SvcRateAllocator::GetMaxBitrateBps(const VideoCodec& codec) {
+  const size_t num_spatial_layers = GetNumActiveSpatialLayers(codec);
 
-  double denominator = 0.0;
-  for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
-    denominator += std::pow(rate_scaling_factor, layer_idx);
+  uint32_t max_bitrate_kbps = 0;
+  for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
+    max_bitrate_kbps += codec.spatialLayers[sl_idx].maxBitrate;
   }
 
-  double numerator = std::pow(rate_scaling_factor, num_layers - 1);
-  for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
-    bitrates.push_back(numerator * total_bitrate / denominator);
-    numerator /= rate_scaling_factor;
+  if (codec.maxBitrate != 0) {
+    max_bitrate_kbps = std::min(max_bitrate_kbps, codec.maxBitrate);
   }
 
-  const size_t sum = std::accumulate(bitrates.begin(), bitrates.end(), 0);
-  // Ensure the sum of split bitrates doesn't exceed the total bitrate.
-  RTC_DCHECK_LE(sum, total_bitrate);
+  return max_bitrate_kbps * 1000;
+}
 
-  // Keep the sum of split bitrates equal to the total bitrate by adding bits,
-  // which were lost due to rounding, to the latest layer.
-  bitrates.back() += total_bitrate - sum;
+uint32_t SvcRateAllocator::GetPaddingBitrateBps(const VideoCodec& codec) {
+  const size_t num_spatial_layers = GetNumActiveSpatialLayers(codec);
 
-  return bitrates;
+  if (codec.mode == VideoCodecMode::kRealtimeVideo) {
+    float scale_factor = 0.0;
+    for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
+      scale_factor += std::pow(kSpatialLayeringRateScalingFactor, sl_idx);
+    }
+    uint32_t min_bitrate_bps =
+        codec.spatialLayers[num_spatial_layers - 1].minBitrate * 1000;
+    return static_cast<uint32_t>(min_bitrate_bps * scale_factor);
+  }
+
+  RTC_DCHECK(codec.mode == VideoCodecMode::kScreensharing);
+
+  uint32_t min_bitrate_kbps = 0;
+  for (size_t sl_idx = 0; sl_idx < num_spatial_layers - 1; ++sl_idx) {
+    min_bitrate_kbps += codec.spatialLayers[sl_idx].maxBitrate;
+  }
+  min_bitrate_kbps += codec.spatialLayers[num_spatial_layers - 1].minBitrate;
+
+  return min_bitrate_kbps * 1000;
 }
 
 }  // namespace webrtc
diff --git a/modules/video_coding/codecs/vp9/svc_rate_allocator.h b/modules/video_coding/codecs/vp9/svc_rate_allocator.h
index 8aa60ff..fc4c67f 100644
--- a/modules/video_coding/codecs/vp9/svc_rate_allocator.h
+++ b/modules/video_coding/codecs/vp9/svc_rate_allocator.h
@@ -21,9 +21,6 @@
 
 namespace webrtc {
 
-extern const float kSpatialLayeringRateScalingFactor;
-extern const float kTemporalLayeringRateScalingFactor;
-
 class SvcRateAllocator : public VideoBitrateAllocator {
  public:
   explicit SvcRateAllocator(const VideoCodec& codec);
@@ -31,15 +28,17 @@
   VideoBitrateAllocation GetAllocation(uint32_t total_bitrate_bps,
                                        uint32_t framerate_fps) override;
 
+  static uint32_t GetMaxBitrateBps(const VideoCodec& codec);
+  static uint32_t GetPaddingBitrateBps(const VideoCodec& codec);
+
  private:
   VideoBitrateAllocation GetAllocationNormalVideo(
-      uint32_t total_bitrate_bps) const;
+      uint32_t total_bitrate_bps,
+      size_t num_spatial_layers) const;
+
   VideoBitrateAllocation GetAllocationScreenSharing(
-      uint32_t total_bitrate_bps) const;
-  std::vector<size_t> SplitBitrate(size_t num_layers,
-                                   size_t total_bitrate,
-                                   float rate_scaling_factor) const;
-  bool AdjustAndVerify(std::vector<size_t>* spatial_layer_bitrate_bps) const;
+      uint32_t total_bitrate_bps,
+      size_t num_spatial_layers) const;
 
   const VideoCodec codec_;
 };
diff --git a/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc b/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc
index e430123..3c3f020 100644
--- a/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc
+++ b/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc
@@ -164,4 +164,89 @@
   EXPECT_EQ(allocation.GetSpatialLayerSum(1) / 1000, layers[1].minBitrate);
 }
 
+TEST(SvcRateAllocatorTest, DeativateLayers) {
+  for (int deactivated_idx = 2; deactivated_idx >= 0; --deactivated_idx) {
+    VideoCodec codec = Configure(1280, 720, 3, 1, false);
+    EXPECT_LE(codec.VP9()->numberOfSpatialLayers, 3U);
+
+    codec.spatialLayers[deactivated_idx].active = false;
+
+    SvcRateAllocator allocator = SvcRateAllocator(codec);
+
+    VideoBitrateAllocation allocation =
+        allocator.GetAllocation(10 * 1000 * 1000, 30);
+
+    // Ensure layers spatial_idx < deactivated_idx are activated.
+    for (int spatial_idx = 0; spatial_idx < deactivated_idx; ++spatial_idx) {
+      EXPECT_GT(allocation.GetSpatialLayerSum(spatial_idx), 0UL);
+    }
+
+    // Ensure layers spatial_idx >= deactivated_idx are deactivated.
+    for (int spatial_idx = deactivated_idx; spatial_idx < 3; ++spatial_idx) {
+      EXPECT_EQ(allocation.GetSpatialLayerSum(spatial_idx), 0UL);
+    }
+  }
+}
+
+class SvcRateAllocatorTestParametrizedContentType
+    : public testing::Test,
+      public testing::WithParamInterface<bool> {
+ public:
+  SvcRateAllocatorTestParametrizedContentType()
+      : is_screen_sharing_(GetParam()) {}
+
+  const bool is_screen_sharing_;
+};
+
+TEST_P(SvcRateAllocatorTestParametrizedContentType, MaxBitrate) {
+  VideoCodec codec = Configure(1280, 720, 3, 1, is_screen_sharing_);
+  EXPECT_EQ(
+      SvcRateAllocator::GetMaxBitrateBps(codec),
+      (codec.spatialLayers[0].maxBitrate + codec.spatialLayers[1].maxBitrate +
+       codec.spatialLayers[2].maxBitrate) *
+          1000);
+
+  // Deactivate middle layer. This causes deactivation of top layer as well.
+  codec.spatialLayers[1].active = false;
+  EXPECT_EQ(SvcRateAllocator::GetMaxBitrateBps(codec),
+            codec.spatialLayers[0].maxBitrate * 1000);
+}
+
+TEST_P(SvcRateAllocatorTestParametrizedContentType, PaddingBitrate) {
+  VideoCodec codec = Configure(1280, 720, 3, 1, is_screen_sharing_);
+  SvcRateAllocator allocator = SvcRateAllocator(codec);
+
+  uint32_t padding_bitrate_bps = SvcRateAllocator::GetPaddingBitrateBps(codec);
+
+  VideoBitrateAllocation allocation =
+      allocator.GetAllocation(padding_bitrate_bps, 30);
+  EXPECT_GT(allocation.GetSpatialLayerSum(0), 0UL);
+  EXPECT_GT(allocation.GetSpatialLayerSum(1), 0UL);
+  EXPECT_GT(allocation.GetSpatialLayerSum(2), 0UL);
+
+  // Allocate 90% of padding bitrate. Top layer should be disabled.
+  allocation = allocator.GetAllocation(9 * padding_bitrate_bps / 10, 30);
+  EXPECT_GT(allocation.GetSpatialLayerSum(0), 0UL);
+  EXPECT_GT(allocation.GetSpatialLayerSum(1), 0UL);
+  EXPECT_EQ(allocation.GetSpatialLayerSum(2), 0UL);
+
+  // Deactivate top layer.
+  codec.spatialLayers[2].active = false;
+
+  padding_bitrate_bps = SvcRateAllocator::GetPaddingBitrateBps(codec);
+  allocation = allocator.GetAllocation(padding_bitrate_bps, 30);
+  EXPECT_GT(allocation.GetSpatialLayerSum(0), 0UL);
+  EXPECT_GT(allocation.GetSpatialLayerSum(1), 0UL);
+  EXPECT_EQ(allocation.GetSpatialLayerSum(2), 0UL);
+
+  allocation = allocator.GetAllocation(9 * padding_bitrate_bps / 10, 30);
+  EXPECT_GT(allocation.GetSpatialLayerSum(0), 0UL);
+  EXPECT_EQ(allocation.GetSpatialLayerSum(1), 0UL);
+  EXPECT_EQ(allocation.GetSpatialLayerSum(2), 0UL);
+}
+
+INSTANTIATE_TEST_CASE_P(_,
+                        SvcRateAllocatorTestParametrizedContentType,
+                        ::testing::Bool());
+
 }  // namespace webrtc
diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
index 5386a8e..9faeb5a 100644
--- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
@@ -336,12 +336,15 @@
       (codec_settings_.spatialLayers[0].minBitrate +
        codec_settings_.spatialLayers[0].maxBitrate) /
       2;
+  codec_settings_.spatialLayers[0].active = true;
+
   codec_settings_.spatialLayers[1].minBitrate = 400;
   codec_settings_.spatialLayers[1].maxBitrate = 1500;
   codec_settings_.spatialLayers[1].targetBitrate =
       (codec_settings_.spatialLayers[1].minBitrate +
        codec_settings_.spatialLayers[1].maxBitrate) /
       2;
+  codec_settings_.spatialLayers[1].active = true;
 
   codec_settings_.spatialLayers[0].width = codec_settings_.width / 2;
   codec_settings_.spatialLayers[0].height = codec_settings_.height / 2;
@@ -1034,6 +1037,7 @@
         codec_settings_.startBitrate;
     codec_settings_.spatialLayers[sl_idx].targetBitrate =
         codec_settings_.startBitrate;
+    codec_settings_.spatialLayers[sl_idx].active = true;
 
     bitrate_allocation.SetBitrate(
         sl_idx, 0, codec_settings_.spatialLayers[sl_idx].targetBitrate * 1000);
@@ -1090,6 +1094,7 @@
   codec_settings_.spatialLayers[0].minBitrate = codec_settings_.startBitrate;
   codec_settings_.spatialLayers[0].maxBitrate = codec_settings_.startBitrate;
   codec_settings_.spatialLayers[0].targetBitrate = codec_settings_.startBitrate;
+  codec_settings_.spatialLayers[0].active = true;
 
   bitrate_allocation.SetBitrate(
       0, 0, codec_settings_.spatialLayers[0].targetBitrate * 1000);
diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc
index cd3f1d7..311fb86 100644
--- a/modules/video_coding/video_codec_initializer.cc
+++ b/modules/video_coding/video_codec_initializer.cc
@@ -192,6 +192,14 @@
           spatial_layers.back().minBitrate = video_codec.minBitrate;
           spatial_layers.back().maxBitrate = video_codec.maxBitrate;
         }
+
+        for (size_t spatial_idx = 0;
+             spatial_idx < config.simulcast_layers.size() &&
+             spatial_idx < spatial_layers.size();
+             ++spatial_idx) {
+          spatial_layers[spatial_layers.size() - spatial_idx - 1].active =
+              config.simulcast_layers[spatial_idx].active;
+        }
       }
 
       RTC_DCHECK(!spatial_layers.empty());
diff --git a/modules/video_coding/video_codec_initializer_unittest.cc b/modules/video_coding/video_codec_initializer_unittest.cc
index 8a0ed26..75be427 100644
--- a/modules/video_coding/video_codec_initializer_unittest.cc
+++ b/modules/video_coding/video_codec_initializer_unittest.cc
@@ -313,4 +313,36 @@
             kDefaultMaxBitrateBps / 1000);
 }
 
+TEST_F(VideoCodecInitializerTest, Vp9DeactivateLayers) {
+  SetUpFor(VideoCodecType::kVideoCodecVP9, 3, 1, false);
+  VideoStream stream = DefaultStream();
+  streams_.push_back(stream);
+
+  config_.simulcast_layers.resize(3);
+
+  // Activate all layers.
+  config_.simulcast_layers[0].active = true;
+  config_.simulcast_layers[1].active = true;
+  config_.simulcast_layers[2].active = true;
+  EXPECT_TRUE(InitializeCodec());
+  EXPECT_TRUE(codec_out_.spatialLayers[0].active);
+  EXPECT_TRUE(codec_out_.spatialLayers[1].active);
+  EXPECT_TRUE(codec_out_.spatialLayers[2].active);
+
+  // Deactivate top layer.
+  config_.simulcast_layers[0].active = false;
+  EXPECT_TRUE(InitializeCodec());
+  EXPECT_TRUE(codec_out_.spatialLayers[0].active);
+  EXPECT_TRUE(codec_out_.spatialLayers[1].active);
+  EXPECT_FALSE(codec_out_.spatialLayers[2].active);
+
+  // Deactivate middle layer.
+  config_.simulcast_layers[0].active = true;
+  config_.simulcast_layers[1].active = false;
+  EXPECT_TRUE(InitializeCodec());
+  EXPECT_TRUE(codec_out_.spatialLayers[0].active);
+  EXPECT_FALSE(codec_out_.spatialLayers[1].active);
+  EXPECT_TRUE(codec_out_.spatialLayers[2].active);
+}
+
 }  // namespace webrtc
diff --git a/video/BUILD.gn b/video/BUILD.gn
index af08bb7..a554213 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -190,6 +190,7 @@
     "../common_video:common_video",
     "../modules/video_coding",
     "../modules/video_coding:video_coding_utility",
+    "../modules/video_coding:webrtc_vp9_helpers",
     "../rtc_base:checks",
     "../rtc_base:criticalsection",
     "../rtc_base:logging",
diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc
index 99f6888..c6d19d0 100644
--- a/video/video_send_stream_impl.cc
+++ b/video/video_send_stream_impl.cc
@@ -555,7 +555,8 @@
   const VideoCodecType codec_type =
       PayloadStringToCodecType(config_->rtp.payload_name);
   if (codec_type == kVideoCodecVP9) {
-    max_padding_bitrate_ = streams[0].target_bitrate_bps;
+    max_padding_bitrate_ = has_alr_probing_ ? streams[0].min_bitrate_bps
+                                            : streams[0].target_bitrate_bps;
   } else {
     max_padding_bitrate_ = CalculateMaxPadBitrateBps(
         streams, min_transmit_bitrate_bps, config_->suspend_below_min_bitrate,
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 410c331..8cabc03 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -18,6 +18,7 @@
 #include "api/video/encoded_image.h"
 #include "api/video/i420_buffer.h"
 #include "api/video/video_bitrate_allocator_factory.h"
+#include "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
 #include "modules/video_coding/include/video_codec_initializer.h"
 #include "modules/video_coding/include/video_coding.h"
 #include "rtc_base/arraysize.h"
@@ -550,24 +551,13 @@
 
   // Set min_bitrate_bps, max_bitrate_bps, and max padding bit rate for VP9.
   if (encoder_config_.codec_type == kVideoCodecVP9) {
-    RTC_DCHECK_EQ(1U, streams.size());
-    int max_encoder_bitrate_kbps = 0;
-    for (int i = 0; i < codec.VP9()->numberOfSpatialLayers; ++i) {
-      max_encoder_bitrate_kbps += codec.spatialLayers[i].maxBitrate;
-    }
     // Lower max bitrate to the level codec actually can produce.
-    streams[0].max_bitrate_bps =
-        std::min(streams[0].max_bitrate_bps, max_encoder_bitrate_kbps * 1000);
+    streams[0].max_bitrate_bps = std::min<int>(
+        streams[0].max_bitrate_bps, SvcRateAllocator::GetMaxBitrateBps(codec));
     streams[0].min_bitrate_bps = codec.spatialLayers[0].minBitrate * 1000;
-    // Pass along the value of maximum padding bit rate from
-    // spatialLayers[].targetBitrate to streams[0].target_bitrate_bps.
-    // TODO(ssilkin): There should be some margin between max padding bitrate
-    // and max encoder bitrate. With the current logic they can be equal.
+    // target_bitrate_bps specifies the maximum padding bitrate.
     streams[0].target_bitrate_bps =
-        std::min(static_cast<unsigned int>(streams[0].max_bitrate_bps),
-                 codec.spatialLayers[codec.VP9()->numberOfSpatialLayers - 1]
-                         .targetBitrate *
-                     1000);
+        SvcRateAllocator::GetPaddingBitrateBps(codec);
   }
 
   codec.startBitrate =