Consider frame orientation for number of spatial layers in VP9.

Addresses case where 540*960 would not get a 135*240 layer.

Bug: webrtc:13469
Change-Id: Icc291c65114fb400cc71659d76a786e359e5996c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/239820
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Konrad Hofbauer <hofbauer@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35507}
diff --git a/modules/video_coding/codecs/vp9/include/vp9_globals.h b/modules/video_coding/codecs/vp9/include/vp9_globals.h
index bbd83f3..87dafe4 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 = 240;
-const size_t kMinVp9SpatialLayerHeight = 135;
+const size_t kMinVp9SpatialLayerLongSideLength = 240;
+const size_t kMinVp9SpatialLayerShortSideLength = 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 cc7743a..92818eb 100644
--- a/modules/video_coding/codecs/vp9/svc_config.cc
+++ b/modules/video_coding/codecs/vp9/svc_config.cc
@@ -69,12 +69,15 @@
   std::vector<SpatialLayer> spatial_layers;
 
   // Limit number of layers for given resolution.
+  const bool is_landscape = input_width >= input_height;
+  const size_t min_width = is_landscape ? kMinVp9SpatialLayerLongSideLength
+                                        : kMinVp9SpatialLayerShortSideLength;
+  const size_t min_height = is_landscape ? kMinVp9SpatialLayerShortSideLength
+                                         : kMinVp9SpatialLayerLongSideLength;
   const size_t num_layers_fit_horz = static_cast<size_t>(std::floor(
-      1 + std::max(0.0f,
-                   std::log2(1.0f * input_width / kMinVp9SpatialLayerWidth))));
-  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))));
+      1 + std::max(0.0f, std::log2(1.0f * input_width / min_width))));
+  const size_t num_layers_fit_vert = static_cast<size_t>(std::floor(
+      1 + std::max(0.0f, std::log2(1.0f * input_height / min_height))));
   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) {
diff --git a/modules/video_coding/codecs/vp9/svc_config_unittest.cc b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
index 1891628..77d75ee 100644
--- a/modules/video_coding/codecs/vp9/svc_config_unittest.cc
+++ b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
@@ -22,10 +22,23 @@
   const size_t first_active_layer = 0;
   const size_t num_spatial_layers = 2;
 
-  std::vector<SpatialLayer> spatial_layers =
-      GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1),
-                   kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30,
-                   first_active_layer, max_num_spatial_layers, 1, false);
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
+      kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
+      first_active_layer, max_num_spatial_layers, 1, false);
+
+  EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
+}
+
+TEST(SvcConfig, NumSpatialLayersPortrait) {
+  const size_t max_num_spatial_layers = 6;
+  const size_t first_active_layer = 0;
+  const size_t num_spatial_layers = 2;
+
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1),
+      kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), 30,
+      first_active_layer, max_num_spatial_layers, 1, false);
 
   EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
 }
@@ -34,11 +47,22 @@
   const size_t max_num_spatial_layers = 6;
   const size_t first_active_layer = 5;
 
-  std::vector<SpatialLayer> spatial_layers =
-      GetSvcConfig(kMinVp9SpatialLayerWidth, kMinVp9SpatialLayerHeight, 30,
-                   first_active_layer, max_num_spatial_layers, 1, false);
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerLongSideLength, kMinVp9SpatialLayerShortSideLength, 30,
+      first_active_layer, max_num_spatial_layers, 1, false);
   EXPECT_EQ(spatial_layers.size(), 1u);
-  EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerWidth);
+  EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerLongSideLength);
+}
+
+TEST(SvcConfig, AlwaysSendsAtLeastOneLayerPortrait) {
+  const size_t max_num_spatial_layers = 6;
+  const size_t first_active_layer = 5;
+
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerShortSideLength, kMinVp9SpatialLayerLongSideLength, 30,
+      first_active_layer, max_num_spatial_layers, 1, false);
+  EXPECT_EQ(spatial_layers.size(), 1u);
+  EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerShortSideLength);
 }
 
 TEST(SvcConfig, EnforcesMinimalRequiredParity) {
@@ -71,22 +95,22 @@
   const size_t num_spatial_layers = 4;
   const size_t first_active_layer = 2;
 
-  std::vector<SpatialLayer> spatial_layers =
-      GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1),
-                   kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30,
-                   first_active_layer, num_spatial_layers, 1, false);
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
+      kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
+      first_active_layer, num_spatial_layers, 1, false);
   EXPECT_EQ(spatial_layers.size(), 2u);
   EXPECT_EQ(spatial_layers.back().width,
-            kMinVp9SpatialLayerWidth << (num_spatial_layers - 1));
+            kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1));
 }
 
 TEST(SvcConfig, BitrateThresholds) {
   const size_t first_active_layer = 0;
   const size_t num_spatial_layers = 3;
-  std::vector<SpatialLayer> spatial_layers =
-      GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1),
-                   kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30,
-                   first_active_layer, num_spatial_layers, 1, false);
+  std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
+      kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
+      kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
+      first_active_layer, num_spatial_layers, 1, false);
 
   EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
 
diff --git a/modules/video_coding/video_codec_initializer_unittest.cc b/modules/video_coding/video_codec_initializer_unittest.cc
index 6c1c2e7..902f991 100644
--- a/modules/video_coding/video_codec_initializer_unittest.cc
+++ b/modules/video_coding/video_codec_initializer_unittest.cc
@@ -302,8 +302,8 @@
   VideoStream stream = DefaultStream();
   stream.num_temporal_layers = 3;
   // Set resolution which is only enough to produce 2 spatial layers.
-  stream.width = kMinVp9SpatialLayerWidth * 2;
-  stream.height = kMinVp9SpatialLayerHeight * 2;
+  stream.width = kMinVp9SpatialLayerLongSideLength * 2;
+  stream.height = kMinVp9SpatialLayerShortSideLength * 2;
 
   streams_.push_back(stream);