Merge SimulcastStream and SpatialLayer structures.

Both simulcast stream and spatial layer can be described with the same
set of parameters. There is no need in two separate definitions.

1. Original definition of SpatialLayer is removed.
2. SimulcastStream is renamed to SpatialLayer.
3. SimulcastStream is equated to SpatialLayer using typedef.

Bug: webrtc:8518
Change-Id: I90761952b032a1b71fc4bba11f74a6daaf58880a
Reviewed-on: https://webrtc-review.googlesource.com/57102
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22272}
diff --git a/common_types.h b/common_types.h
index 9545284..f41903a 100644
--- a/common_types.h
+++ b/common_types.h
@@ -496,9 +496,7 @@
   VideoCodecH264 H264;
 };
 
-// Simulcast is when the same stream is encoded multiple times with different
-// settings such as resolution.
-struct SimulcastStream {
+struct SpatialLayer {
   unsigned short width;
   unsigned short height;
   unsigned char numberOfTemporalLayers;
@@ -509,12 +507,9 @@
   bool active;                 // encoded and sent.
 };
 
-struct SpatialLayer {
-  int scaling_factor_num;
-  int scaling_factor_den;
-  int target_bitrate_bps;
-  // TODO(ivica): Add max_quantizer and min_quantizer?
-};
+// Simulcast is when the same stream is encoded multiple times with different
+// settings such as resolution.
+typedef SpatialLayer SimulcastStream;
 
 enum VideoCodecMode { kRealtimeVideo, kScreensharing };
 
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 e43be9e..442e028 100644
--- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
@@ -202,4 +202,41 @@
                   1);
 }
 
+TEST_F(TestVp9Impl, EncoderExplicitLayering) {
+  // Override default settings.
+  codec_settings_.VP9()->numberOfTemporalLayers = 1;
+  codec_settings_.VP9()->numberOfSpatialLayers = 2;
+
+  codec_settings_.width = 960;
+  codec_settings_.height = 540;
+  codec_settings_.spatialLayers[0].targetBitrate = 500;
+  codec_settings_.spatialLayers[1].targetBitrate = 1000;
+
+  codec_settings_.spatialLayers[0].width = codec_settings_.width / 2;
+  codec_settings_.spatialLayers[0].height = codec_settings_.height / 2;
+  codec_settings_.spatialLayers[1].width = codec_settings_.width;
+  codec_settings_.spatialLayers[1].height = codec_settings_.height;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            encoder_->InitEncode(&codec_settings_, 1 /* number of cores */,
+                                 0 /* max payload size (unused) */));
+
+  // Ensure it fails if scaling factors in horz/vert dimentions are different.
+  codec_settings_.spatialLayers[0].width = codec_settings_.width;
+  codec_settings_.spatialLayers[0].height = codec_settings_.height / 2;
+  codec_settings_.spatialLayers[1].width = codec_settings_.width;
+  codec_settings_.spatialLayers[1].height = codec_settings_.height;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_PARAMETER,
+            encoder_->InitEncode(&codec_settings_, 1 /* number of cores */,
+                                 0 /* max payload size (unused) */));
+
+  // Ensure it fails if scaling factor is not power of two.
+  codec_settings_.spatialLayers[0].width = codec_settings_.width / 3;
+  codec_settings_.spatialLayers[0].height = codec_settings_.height / 3;
+  codec_settings_.spatialLayers[1].width = codec_settings_.width;
+  codec_settings_.spatialLayers[1].height = codec_settings_.height;
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_PARAMETER,
+            encoder_->InitEncode(&codec_settings_, 1 /* number of cores */,
+                                 0 /* max payload size (unused) */));
+}
+
 }  // namespace webrtc
diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc
index 2fb6e55..c7c6247 100644
--- a/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -124,8 +124,7 @@
 bool VP9EncoderImpl::ExplicitlyConfiguredSpatialLayers() const {
   // We check target_bitrate_bps of the 0th layer to see if the spatial layers
   // (i.e. bitrates) were explicitly configured.
-  return num_spatial_layers_ > 1 &&
-         codec_.spatialLayers[0].target_bitrate_bps > 0;
+  return num_spatial_layers_ > 1 && codec_.spatialLayers[0].targetBitrate > 0;
 }
 
 bool VP9EncoderImpl::SetSvcRates() {
@@ -139,13 +138,13 @@
     }
     int total_bitrate_bps = 0;
     for (i = 0; i < num_spatial_layers_; ++i)
-      total_bitrate_bps += codec_.spatialLayers[i].target_bitrate_bps;
+      total_bitrate_bps += codec_.spatialLayers[i].targetBitrate * 1000;
     // If total bitrate differs now from what has been specified at the
     // beginning, update the bitrates in the same ratio as before.
     for (i = 0; i < num_spatial_layers_; ++i) {
       config_->ss_target_bitrate[i] = config_->layer_target_bitrate[i] =
           static_cast<int>(static_cast<int64_t>(config_->rc_target_bitrate) *
-                           codec_.spatialLayers[i].target_bitrate_bps /
+                           codec_.spatialLayers[i].targetBitrate * 1000 /
                            total_bitrate_bps);
     }
   } else {
@@ -412,8 +411,27 @@
   if (ExplicitlyConfiguredSpatialLayers()) {
     for (int i = 0; i < num_spatial_layers_; ++i) {
       const auto& layer = codec_.spatialLayers[i];
-      svc_params_.scaling_factor_num[i] = layer.scaling_factor_num;
-      svc_params_.scaling_factor_den[i] = layer.scaling_factor_den;
+      const int scale_factor = codec_.width / layer.width;
+      RTC_DCHECK_GT(scale_factor, 0);
+
+      // Ensure scaler factor is integer.
+      if (scale_factor * layer.width != codec_.width) {
+        return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+      }
+
+      // Ensure scale factor is the same in both dimensions.
+      if (scale_factor * layer.height != codec_.height) {
+        return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+      }
+
+      // Ensure scale factor is power of two.
+      const bool is_pow_of_two = (scale_factor & (scale_factor - 1)) == 0;
+      if (!is_pow_of_two) {
+        return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+      }
+
+      svc_params_.scaling_factor_num[i] = 1;
+      svc_params_.scaling_factor_den[i] = scale_factor;
     }
   } else {
     int scaling_factor_num = 256;
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 661363d..8e1990c 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -1326,12 +1326,18 @@
     if (descriptor.empty())
       continue;
     std::vector<int> v = VideoQualityTest::ParseCSV(descriptor);
-    RTC_CHECK_GT(v[2], 0);
+    RTC_CHECK_EQ(v.size(), 7);
 
-    SpatialLayer layer;
-    layer.scaling_factor_num = v[0] == -1 ? 1 : v[0];
-    layer.scaling_factor_den = v[1] == -1 ? 1 : v[1];
-    layer.target_bitrate_bps = v[2];
+    SpatialLayer layer = {0};
+    layer.width = v[0];
+    layer.height = v[1];
+    layer.numberOfTemporalLayers = v[2];
+    layer.maxBitrate = v[3];
+    layer.minBitrate = v[4];
+    layer.targetBitrate = v[5];
+    layer.qpMax = v[6];
+    layer.active = true;
+
     params->ss[video_idx].spatial_layers.push_back(layer);
   }
 }