Add frame rate parameter to SpatialLayer struct.
This will allow us to configure VP9 encoder to produce spatial layers
with different frame rates.
Bug: webrtc:9650
Change-Id: I3a9c58072003b8a8da681d5291d8f7ede7f52fa4
Reviewed-on: https://webrtc-review.googlesource.com/95427
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24435}
diff --git a/common_types.h b/common_types.h
index c1c5b4e..beb6bca 100644
--- a/common_types.h
+++ b/common_types.h
@@ -361,6 +361,7 @@
unsigned short width;
unsigned short height;
+ float maxFramerate; // fps.
unsigned char numberOfTemporalLayers;
unsigned int maxBitrate; // kilobits/sec.
unsigned int targetBitrate; // kilobits/sec.
diff --git a/media/engine/vp8_encoder_simulcast_proxy_unittest.cc b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc
index dfa35cb..47e57aa 100644
--- a/media/engine/vp8_encoder_simulcast_proxy_unittest.cc
+++ b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc
@@ -63,12 +63,30 @@
"SimulcastEncoderAdapter (Fake, Fake, Fake)";
VideoCodec codec_settings;
webrtc::test::CodecSettings(kVideoCodecVP8, &codec_settings);
- codec_settings.simulcastStream[0] = {
- test::kTestWidth, test::kTestHeight, 2, 2000, 1000, 1000, 56};
- codec_settings.simulcastStream[1] = {
- test::kTestWidth, test::kTestHeight, 2, 3000, 1000, 1000, 56};
- codec_settings.simulcastStream[2] = {
- test::kTestWidth, test::kTestHeight, 2, 5000, 1000, 1000, 56};
+ codec_settings.simulcastStream[0] = {test::kTestWidth,
+ test::kTestHeight,
+ test::kTestFrameRate,
+ 2,
+ 2000,
+ 1000,
+ 1000,
+ 56};
+ codec_settings.simulcastStream[1] = {test::kTestWidth,
+ test::kTestHeight,
+ test::kTestFrameRate,
+ 2,
+ 3000,
+ 1000,
+ 1000,
+ 56};
+ codec_settings.simulcastStream[2] = {test::kTestWidth,
+ test::kTestHeight,
+ test::kTestFrameRate,
+ 2,
+ 5000,
+ 1000,
+ 1000,
+ 56};
codec_settings.numberOfSimulcastStreams = 3;
NiceMock<MockEncoder>* mock_encoder = new NiceMock<MockEncoder>();
diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
index a5c76ad..2958a9a 100644
--- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
+++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
@@ -70,11 +70,11 @@
void ConfigureSvc(VideoCodec* codec_settings) {
RTC_CHECK_EQ(kVideoCodecVP9, codec_settings->codecType);
- const std::vector<SpatialLayer> layers =
- GetSvcConfig(codec_settings->width, codec_settings->height,
- codec_settings->VP9()->numberOfSpatialLayers,
- codec_settings->VP9()->numberOfTemporalLayers,
- /* is_screen_sharing = */ false);
+ const std::vector<SpatialLayer> layers = GetSvcConfig(
+ codec_settings->width, codec_settings->height, kMaxFramerateFps,
+ codec_settings->VP9()->numberOfSpatialLayers,
+ codec_settings->VP9()->numberOfTemporalLayers,
+ /* is_screen_sharing = */ false);
ASSERT_EQ(codec_settings->VP9()->numberOfSpatialLayers, layers.size())
<< "GetSvcConfig returned fewer spatial layers than configured.";
diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
index be92b34..aaa10dd 100644
--- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
@@ -31,6 +31,7 @@
constexpr int kDefaultMinPixelsPerFrame = 320 * 180;
constexpr int kWidth = 172;
constexpr int kHeight = 144;
+constexpr float kFramerateFps = 30;
} // namespace
class TestVp8Impl : public VideoCodecUnitTest {
@@ -182,57 +183,57 @@
TEST_F(TestVp8Impl, ChecksSimulcastSettings) {
codec_settings_.numberOfSimulcastStreams = 2;
// Reslutions are not scaled by 2, temporal layers do not match.
- codec_settings_.simulcastStream[0] = {kWidth, kHeight, 2, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[1] = {kWidth, kHeight, 3, 4000,
- 3000, 2000, 80};
+ codec_settings_.simulcastStream[0] = {kWidth, kHeight, kFramerateFps, 2,
+ 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[1] = {kWidth, kHeight, 30, 3,
+ 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
codec_settings_.numberOfSimulcastStreams = 3;
// Reslutions are not scaled by 2.
- codec_settings_.simulcastStream[0] = {kWidth / 2, kHeight / 2, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
- 3000, 2000, 80};
+ codec_settings_.simulcastStream[0] = {
+ kWidth / 2, kHeight / 2, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[1] = {
+ kWidth / 2, kHeight / 2, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[2] = {kWidth, kHeight, 30, 1,
+ 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
// Reslutions are not scaled by 2.
- codec_settings_.simulcastStream[0] = {kWidth, kHeight, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[1] = {kWidth, kHeight, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
- 3000, 2000, 80};
+ codec_settings_.simulcastStream[0] = {kWidth, kHeight, kFramerateFps, 1,
+ 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[1] = {kWidth, kHeight, kFramerateFps, 1,
+ 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[2] = {kWidth, kHeight, kFramerateFps, 1,
+ 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
// Temporal layers do not match.
- codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 2, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[2] = {kWidth, kHeight, 3, 4000,
- 3000, 2000, 80};
+ codec_settings_.simulcastStream[0] = {
+ kWidth / 4, kHeight / 4, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[1] = {
+ kWidth / 2, kHeight / 2, kFramerateFps, 2, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[2] = {kWidth, kHeight, kFramerateFps, 3,
+ 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
// Resolutions do not match codec config.
codec_settings_.simulcastStream[0] = {
- kWidth / 4 + 1, kHeight / 4 + 1, 1, 4000, 3000, 2000, 80};
+ kWidth / 4 + 1, kHeight / 4 + 1, kFramerateFps, 1, 4000, 3000, 2000, 80};
codec_settings_.simulcastStream[1] = {
- kWidth / 2 + 2, kHeight / 2 + 2, 1, 4000, 3000, 2000, 80};
- codec_settings_.simulcastStream[2] = {kWidth + 4, kHeight + 4, 1, 4000,
- 3000, 2000, 80};
+ kWidth / 2 + 2, kHeight / 2 + 2, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[2] = {
+ kWidth + 4, kHeight + 4, kFramerateFps, 1, 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
// Everything fine: scaling by 2, top resolution matches video, temporal
// settings are the same for all layers.
- codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000,
- 3000, 2000, 80};
- codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
- 3000, 2000, 80};
+ codec_settings_.simulcastStream[0] = {
+ kWidth / 4, kHeight / 4, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[1] = {
+ kWidth / 2, kHeight / 2, kFramerateFps, 1, 4000, 3000, 2000, 80};
+ codec_settings_.simulcastStream[2] = {kWidth, kHeight, kFramerateFps, 1,
+ 4000, 3000, 2000, 80};
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
}
diff --git a/modules/video_coding/codecs/vp9/svc_config.cc b/modules/video_coding/codecs/vp9/svc_config.cc
index 22902fe..6807698 100644
--- a/modules/video_coding/codecs/vp9/svc_config.cc
+++ b/modules/video_coding/codecs/vp9/svc_config.cc
@@ -23,11 +23,13 @@
const size_t kMinVp9SvcBitrateKbps = 30;
const size_t kMaxNumLayersForScreenSharing = 2;
+const float kMaxScreenSharingLayerFramerateFps[] = {5.0, 5.0};
const size_t kMaxScreenSharingLayerBitrateKbps[] = {200, 500};
} // namespace
std::vector<SpatialLayer> ConfigureSvcScreenSharing(size_t input_width,
size_t input_height,
+ float max_framerate_fps,
size_t num_spatial_layers) {
num_spatial_layers =
std::min(num_spatial_layers, kMaxNumLayersForScreenSharing);
@@ -37,6 +39,8 @@
SpatialLayer spatial_layer = {0};
spatial_layer.width = input_width;
spatial_layer.height = input_height;
+ spatial_layer.maxFramerate =
+ std::min(kMaxScreenSharingLayerFramerateFps[sl_idx], max_framerate_fps);
spatial_layer.numberOfTemporalLayers = 1;
spatial_layer.minBitrate = static_cast<int>(kMinVp9SvcBitrateKbps);
spatial_layer.maxBitrate =
@@ -50,6 +54,7 @@
std::vector<SpatialLayer> ConfigureSvcNormalVideo(size_t input_width,
size_t input_height,
+ float max_framerate_fps,
size_t num_spatial_layers,
size_t num_temporal_layers) {
std::vector<SpatialLayer> spatial_layers;
@@ -69,6 +74,7 @@
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;
// minBitrate and maxBitrate formulas were derived from
@@ -102,6 +108,7 @@
std::vector<SpatialLayer> GetSvcConfig(size_t input_width,
size_t input_height,
+ float max_framerate_fps,
size_t num_spatial_layers,
size_t num_temporal_layers,
bool is_screen_sharing) {
@@ -112,9 +119,9 @@
if (is_screen_sharing) {
return ConfigureSvcScreenSharing(input_width, input_height,
- num_spatial_layers);
+ max_framerate_fps, num_spatial_layers);
} else {
- return ConfigureSvcNormalVideo(input_width, input_height,
+ return ConfigureSvcNormalVideo(input_width, input_height, max_framerate_fps,
num_spatial_layers, num_temporal_layers);
}
}
diff --git a/modules/video_coding/codecs/vp9/svc_config.h b/modules/video_coding/codecs/vp9/svc_config.h
index c8561a4..f671ced 100644
--- a/modules/video_coding/codecs/vp9/svc_config.h
+++ b/modules/video_coding/codecs/vp9/svc_config.h
@@ -18,6 +18,7 @@
std::vector<SpatialLayer> GetSvcConfig(size_t input_width,
size_t input_height,
+ float max_framerate_fps,
size_t num_spatial_layers,
size_t num_temporal_layers,
bool is_screen_sharing);
diff --git a/modules/video_coding/codecs/vp9/svc_config_unittest.cc b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
index ebefdda..257c5df 100644
--- a/modules/video_coding/codecs/vp9/svc_config_unittest.cc
+++ b/modules/video_coding/codecs/vp9/svc_config_unittest.cc
@@ -23,7 +23,7 @@
std::vector<SpatialLayer> spatial_layers =
GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1),
- kMinVp9SpatialLayerHeight << (num_spatial_layers - 1),
+ kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30,
max_num_spatial_layers, 1, false);
EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
@@ -33,7 +33,7 @@
const size_t num_spatial_layers = 3;
std::vector<SpatialLayer> spatial_layers =
GetSvcConfig(kMinVp9SpatialLayerWidth << (num_spatial_layers - 1),
- kMinVp9SpatialLayerHeight << (num_spatial_layers - 1),
+ kMinVp9SpatialLayerHeight << (num_spatial_layers - 1), 30,
num_spatial_layers, 1, false);
EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
@@ -47,13 +47,14 @@
TEST(SvcConfig, ScreenSharing) {
std::vector<SpatialLayer> spatial_layers =
- GetSvcConfig(1920, 1080, 3, 3, true);
+ GetSvcConfig(1920, 1080, 30, 3, 3, true);
EXPECT_EQ(spatial_layers.size(), 2UL);
for (const SpatialLayer& layer : spatial_layers) {
EXPECT_EQ(layer.width, 1920);
EXPECT_EQ(layer.height, 1080);
+ EXPECT_EQ(layer.maxFramerate, 5);
EXPECT_EQ(layer.numberOfTemporalLayers, 1);
EXPECT_LE(layer.minBitrate, layer.maxBitrate);
EXPECT_LE(layer.minBitrate, layer.targetBitrate);
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 1b6aa10..048bf7d 100644
--- a/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc
+++ b/modules/video_coding/codecs/vp9/svc_rate_allocator_unittest.cc
@@ -29,7 +29,7 @@
: VideoCodecMode::kRealtimeVideo;
std::vector<SpatialLayer> spatial_layers =
- GetSvcConfig(width, height, num_spatial_layers, num_temporal_layers,
+ GetSvcConfig(width, height, 30, num_spatial_layers, num_temporal_layers,
is_screen_sharing);
RTC_CHECK_LE(spatial_layers.size(), kMaxSpatialLayers);
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 a6774a4..5b5db88 100644
--- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
@@ -82,9 +82,9 @@
codec_settings_.VP9()->numberOfTemporalLayers = 1;
codec_settings_.VP9()->frameDroppingOn = false;
- std::vector<SpatialLayer> layers =
- GetSvcConfig(codec_settings_.width, codec_settings_.height,
- num_spatial_layers, 1, false);
+ std::vector<SpatialLayer> layers = GetSvcConfig(
+ codec_settings_.width, codec_settings_.height,
+ codec_settings_.maxFramerate, num_spatial_layers, 1, false);
for (size_t i = 0; i < layers.size(); ++i) {
codec_settings_.spatialLayers[i] = layers[i];
}
diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc
index fe59afc..e7125ce 100644
--- a/modules/video_coding/video_codec_initializer.cc
+++ b/modules/video_coding/video_codec_initializer.cc
@@ -126,6 +126,7 @@
sim_stream->width = static_cast<uint16_t>(streams[i].width);
sim_stream->height = static_cast<uint16_t>(streams[i].height);
+ sim_stream->maxFramerate = streams[i].max_framerate;
sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
@@ -198,11 +199,11 @@
// Layering is set explicitly.
spatial_layers = config.spatial_layers;
} else {
- spatial_layers =
- GetSvcConfig(video_codec.width, video_codec.height,
- video_codec.VP9()->numberOfSpatialLayers,
- video_codec.VP9()->numberOfTemporalLayers,
- video_codec.mode == VideoCodecMode::kScreensharing);
+ spatial_layers = GetSvcConfig(
+ video_codec.width, video_codec.height, video_codec.maxFramerate,
+ video_codec.VP9()->numberOfSpatialLayers,
+ video_codec.VP9()->numberOfTemporalLayers,
+ video_codec.mode == VideoCodecMode::kScreensharing);
const bool no_spatial_layering = (spatial_layers.size() == 1);
if (no_spatial_layering) {