Allow HVGA Vp9 SVC to have 2 spatial layers and remove excessive rounding

Bug: webrtc:11652
Change-Id: I8bfa91c3115d6ebb17beefbb2a5e51efbbd599e0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/177000
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31502}
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index 296ab8b..faff336 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -536,6 +536,7 @@
     "../../api/video_codecs:video_codecs_api",
     "../../common_video",
     "../../rtc_base:checks",
+    "../../rtc_base:logging",
     "../../rtc_base/experiments:stable_target_rate_experiment",
   ]
   absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector" ]
diff --git a/modules/video_coding/codecs/vp9/include/vp9_globals.h b/modules/video_coding/codecs/vp9/include/vp9_globals.h
index c685312..6f9d099 100644
--- a/modules/video_coding/codecs/vp9/include/vp9_globals.h
+++ b/modules/video_coding/codecs/vp9/include/vp9_globals.h
@@ -30,8 +30,8 @@
 const size_t kMaxVp9FramesInGof = 0xFF;  // 8 bits
 const size_t kMaxVp9NumberOfSpatialLayers = 8;
 
-const size_t kMinVp9SpatialLayerWidth = 320;
-const size_t kMinVp9SpatialLayerHeight = 180;
+const size_t kMinVp9SpatialLayerWidth = 240;
+const size_t kMinVp9SpatialLayerHeight = 135;
 
 enum TemporalStructureMode {
   kTemporalStructureMode1,  // 1 temporal layer structure - i.e., IPPP...
diff --git a/modules/video_coding/codecs/vp9/svc_config.cc b/modules/video_coding/codecs/vp9/svc_config.cc
index b73661c..118d8a7 100644
--- a/modules/video_coding/codecs/vp9/svc_config.cc
+++ b/modules/video_coding/codecs/vp9/svc_config.cc
@@ -16,6 +16,7 @@
 
 #include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
 
 namespace webrtc {
 
@@ -74,13 +75,20 @@
   const size_t num_layers_fit_vert = static_cast<size_t>(
       std::floor(1 + std::max(0.0f, std::log2(1.0f * input_height /
                                               kMinVp9SpatialLayerHeight))));
-  num_spatial_layers =
-      std::min({num_spatial_layers, num_layers_fit_horz, num_layers_fit_vert});
+  const size_t limited_num_spatial_layers =
+      std::min(num_layers_fit_horz, num_layers_fit_vert);
+  if (limited_num_spatial_layers < num_spatial_layers) {
+    RTC_LOG(LS_WARNING) << "Reducing number of spatial layers from "
+                        << num_spatial_layers << " to "
+                        << limited_num_spatial_layers
+                        << " due to low input resolution.";
+    num_spatial_layers = limited_num_spatial_layers;
+  }
   // First active layer must be configured.
   num_spatial_layers = std::max(num_spatial_layers, first_active_layer + 1);
 
   // Ensure top layer is even enough.
-  int required_divisiblity = 1 << num_spatial_layers;
+  int required_divisiblity = 1 << (num_spatial_layers - first_active_layer - 1);
   input_width = input_width - input_width % required_divisiblity;
   input_height = input_height - input_height % required_divisiblity;
 
diff --git a/modules/video_coding/codecs/vp9/svc_config_unittest.cc b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
index abc67a2..1891628 100644
--- a/modules/video_coding/codecs/vp9/svc_config_unittest.cc
+++ b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
@@ -41,6 +41,32 @@
   EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerWidth);
 }
 
+TEST(SvcConfig, EnforcesMinimalRequiredParity) {
+  const size_t max_num_spatial_layers = 3;
+  const size_t kOddSize = 1023;
+
+  std::vector<SpatialLayer> spatial_layers =
+      GetSvcConfig(kOddSize, kOddSize, 30,
+                   /*first_active_layer=*/1, max_num_spatial_layers, 1, false);
+  // Since there are 2 layers total (1, 2), divisiblity by 2 is required.
+  EXPECT_EQ(spatial_layers.back().width, kOddSize - 1);
+  EXPECT_EQ(spatial_layers.back().width, kOddSize - 1);
+
+  spatial_layers =
+      GetSvcConfig(kOddSize, kOddSize, 30,
+                   /*first_active_layer=*/0, max_num_spatial_layers, 1, false);
+  // Since there are 3 layers total (0, 1, 2), divisiblity by 4 is required.
+  EXPECT_EQ(spatial_layers.back().width, kOddSize - 3);
+  EXPECT_EQ(spatial_layers.back().width, kOddSize - 3);
+
+  spatial_layers =
+      GetSvcConfig(kOddSize, kOddSize, 30,
+                   /*first_active_layer=*/2, max_num_spatial_layers, 1, false);
+  // Since there is only 1 layer active (2), divisiblity by 1 is required.
+  EXPECT_EQ(spatial_layers.back().width, kOddSize);
+  EXPECT_EQ(spatial_layers.back().width, kOddSize);
+}
+
 TEST(SvcConfig, SkipsInactiveLayers) {
   const size_t num_spatial_layers = 4;
   const size_t first_active_layer = 2;