Adding layering configurator and rate allocator for VP9 SVC.

The configurator decides number of spatial layers, their resolution
and bitrate thresholds based on given input resolution and maximum
number of spatial layers.

The allocator distributes available bitrate across spatial and
temporal layers. If there is not enough bitrate to provide acceptable
quality for all spatial layers allocator disables enhancement layers
one by one until the condition is met or number of layers is reduced
to one.

VP9 SVC related unit tests have been updated. Input resolution and
bitrate in these tests have been increased to the level enough to
provide desirable number of spatial layers.

Bug: webrtc:8518
Change-Id: I9df790920227c7f7dd4d42a50a856c22f0f4389b
Reviewed-on: https://webrtc-review.googlesource.com/60340
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Michael Horowitz <mhoro@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22672}
This commit is contained in:
Sergey Silkin
2018-03-28 19:32:37 +02:00
committed by Commit Bot
parent 002e710d07
commit 86684960b3
22 changed files with 785 additions and 146 deletions

View File

@ -191,26 +191,25 @@ size_t Stats::CalcLayerTargetBitrateKbps(size_t first_frame_num,
size_t spatial_layer_idx,
size_t temporal_layer_idx,
bool aggregate_independent_layers) {
std::vector<size_t> target_bitrate_kbps(temporal_layer_idx + 1, 0);
size_t target_bitrate_kbps = 0;
// We don't know if superframe includes all required spatial layers because
// of possible frame drops. Run through all frames required range, track
// maximum target bitrate per temporal layers and return sum of these.
// Assume target bitrate in frame statistic is specified per temporal layer.
// of possible frame drops. Run through all frames in specified range, find
// and return maximum target bitrate. Assume that target bitrate in frame
// statistic is specified per temporal layer.
for (size_t frame_num = first_frame_num; frame_num <= last_frame_num;
++frame_num) {
FrameStatistics superframe = AggregateFrameStatistic(
frame_num, spatial_layer_idx, aggregate_independent_layers);
if (superframe.temporal_layer_idx <= temporal_layer_idx) {
target_bitrate_kbps[superframe.temporal_layer_idx] =
std::max(target_bitrate_kbps[superframe.temporal_layer_idx],
superframe.target_bitrate_kbps);
target_bitrate_kbps =
std::max(target_bitrate_kbps, superframe.target_bitrate_kbps);
}
}
return std::accumulate(target_bitrate_kbps.begin(), target_bitrate_kbps.end(),
std::size_t {0});
RTC_DCHECK_GT(target_bitrate_kbps, 0);
return target_bitrate_kbps;
}
VideoStatistics Stats::SliceAndCalcVideoStatistic(

View File

@ -15,6 +15,7 @@
#include "media/base/h264_profile_level_id.h"
#include "media/base/mediaconstants.h"
#include "media/engine/simulcast.h"
#include "modules/video_coding/codecs/vp9/svc_config.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/cpu_info.h"
@ -49,6 +50,19 @@ void ConfigureSimulcast(VideoCodec* codec_settings) {
}
}
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);
for (size_t i = 0; i < layers.size(); ++i) {
codec_settings->spatialLayers[i] = layers[i];
}
}
std::string CodecSpecificToString(const VideoCodec& codec) {
std::stringstream ss;
switch (codec.codecType) {
@ -124,9 +138,6 @@ void TestConfig::SetCodecSettings(VideoCodecType codec_type,
codec_settings.numberOfSimulcastStreams =
num_simulcast_streams <= 1 ? 0
: static_cast<uint8_t>(num_simulcast_streams);
if (codec_settings.numberOfSimulcastStreams > 1) {
ConfigureSimulcast(&codec_settings);
}
switch (codec_settings.codecType) {
case kVideoCodecVP8:
@ -158,6 +169,13 @@ void TestConfig::SetCodecSettings(VideoCodecType codec_type,
RTC_NOTREACHED();
break;
}
if (codec_settings.numberOfSimulcastStreams > 1) {
ConfigureSimulcast(&codec_settings);
} else if (codec_settings.codecType == kVideoCodecVP9 &&
codec_settings.VP9()->numberOfSpatialLayers > 1) {
ConfigureSvc(&codec_settings);
}
}
size_t TestConfig::NumberOfCores() const {

View File

@ -350,24 +350,18 @@ void VideoProcessor::FrameEncoded(
frame_stat->encoding_successful = true;
frame_stat->encode_time_us =
GetElapsedTimeMicroseconds(frame_stat->encode_start_ns, encode_stop_ns);
if (codec_type == kVideoCodecVP9) {
const CodecSpecificInfoVP9& vp9_info = codec_specific.codecSpecific.VP9;
frame_stat->inter_layer_predicted = vp9_info.inter_layer_predicted;
// TODO(ssilkin): Implement bitrate allocation for VP9 SVC. For now set
// target for base layers equal to total target to avoid devision by zero
// at analysis.
frame_stat->target_bitrate_kbps = bitrate_allocation_.get_sum_kbps();
} else {
frame_stat->target_bitrate_kbps =
(bitrate_allocation_.GetBitrate(simulcast_svc_idx, temporal_idx) +
500) /
1000;
}
frame_stat->target_bitrate_kbps = (bitrate_allocation_.GetTemporalLayerSum(
simulcast_svc_idx, temporal_idx) +
500) /
1000;
frame_stat->length_bytes = encoded_image._length;
frame_stat->frame_type = encoded_image._frameType;
frame_stat->temporal_layer_idx = temporal_idx;
frame_stat->simulcast_svc_idx = simulcast_svc_idx;
if (codec_type == kVideoCodecVP9) {
const CodecSpecificInfoVP9& vp9_info = codec_specific.codecSpecific.VP9;
frame_stat->inter_layer_predicted = vp9_info.inter_layer_predicted;
}
frame_stat->max_nalu_size_bytes = GetMaxNaluSizeBytes(encoded_image, config_);
frame_stat->qp = encoded_image.qp_;

View File

@ -14,6 +14,8 @@
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
#define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
#include <assert.h>
#include "modules/video_coding/codecs/interface/common_constants.h"
namespace webrtc {
@ -27,6 +29,9 @@ const size_t kMaxVp9RefPics = 3;
const size_t kMaxVp9FramesInGof = 0xFF; // 8 bits
const size_t kMaxVp9NumberOfSpatialLayers = 8;
const size_t kMinVp9SpatialLayerWidth = 320;
const size_t kMinVp9SpatialLayerHeight = 180;
enum TemporalStructureMode {
kTemporalStructureMode1, // 1 temporal layer structure - i.e., IPPP...
kTemporalStructureMode2, // 2 temporal layers 01...

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/video_coding/codecs/vp9/svc_config.h"
#include <algorithm>
#include <cmath>
#include <vector>
#include "modules/video_coding/include/video_codec_interface.h"
namespace webrtc {
std::vector<SpatialLayer> GetSvcConfig(size_t input_width,
size_t input_height,
size_t num_spatial_layers,
size_t num_temporal_layers) {
RTC_DCHECK_GT(input_width, 0);
RTC_DCHECK_GT(input_height, 0);
RTC_DCHECK_GT(num_spatial_layers, 0);
RTC_DCHECK_GT(num_temporal_layers, 0);
std::vector<SpatialLayer> spatial_layers;
// Limit number of layers for given resolution.
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))));
num_spatial_layers =
std::min({num_spatial_layers, num_layers_fit_horz, num_layers_fit_vert});
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.numberOfTemporalLayers = num_temporal_layers;
// minBitrate and maxBitrate formulas were derived to fit VP9
// subjective-quality data for bit rate below which video quality is
// unacceptable and above which additional bits do not provide benefit.
// TODO(ssilkin): Add to the comment PSNR/SSIM we get at encoding certain
// video to min/max bitrate specified by those formulas.
const size_t num_pixels = spatial_layer.width * spatial_layer.height;
spatial_layer.minBitrate =
static_cast<int>(360 * std::sqrt(num_pixels) / 1000);
spatial_layer.maxBitrate =
static_cast<int>((1.5 * num_pixels + 75 * 1000) / 1000);
spatial_layer.targetBitrate =
(spatial_layer.maxBitrate - spatial_layer.minBitrate) / 2;
spatial_layers.push_back(spatial_layer);
}
return spatial_layers;
}
} // namespace webrtc

View File

@ -0,0 +1,26 @@
/* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_
#define MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_
#include <vector>
#include "common_types.h" // NOLINT(build/include)
namespace webrtc {
std::vector<SpatialLayer> GetSvcConfig(size_t input_width,
size_t input_height,
size_t num_spatial_layers,
size_t num_temporal_layers);
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
#include <algorithm>
#include <cmath>
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
const float kSpatialLayeringRateScalingFactor = 0.55f;
const float kTemporalLayeringRateScalingFactor = 0.55f;
} // namespace
SvcRateAllocator::SvcRateAllocator(const VideoCodec& codec) : codec_(codec) {
RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
}
BitrateAllocation SvcRateAllocator::GetAllocation(uint32_t total_bitrate_bps,
uint32_t framerate_fps) {
BitrateAllocation bitrate_allocation;
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);
if (codec_.mode == kScreensharing) {
// At screen sharing bitrate allocation is handled by VP9 encoder wrapper.
bitrate_allocation.SetBitrate(0, 0, total_bitrate_bps);
return bitrate_allocation;
}
std::vector<size_t> spatial_layer_bitrate_bps;
if (codec_.spatialLayers[0].maxBitrate == 0) {
// Layers' parameters are not initialized. Do simple split.
spatial_layer_bitrate_bps =
SplitBitrate(num_spatial_layers, total_bitrate_bps,
kSpatialLayeringRateScalingFactor);
} else {
// Distribute total bitrate across spatial layers. If there is not enough
// bitrate to provide all layers with at least minimum required bitrate
// then number of layers is reduced by one and distribution is repeated
// until that condition is met or if number of layers is reduced to one.
for (;; --num_spatial_layers) {
spatial_layer_bitrate_bps =
SplitBitrate(num_spatial_layers, total_bitrate_bps,
kSpatialLayeringRateScalingFactor);
bool enough_bitrate = true;
size_t excess_rate = 0;
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
RTC_DCHECK_GT(codec_.spatialLayers[sl_idx].maxBitrate, 0);
RTC_DCHECK_GT(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[sl_idx] += excess_rate;
if (spatial_layer_bitrate_bps[sl_idx] < max_bitrate_bps) {
excess_rate = 0;
} else {
excess_rate = spatial_layer_bitrate_bps[sl_idx] - max_bitrate_bps;
spatial_layer_bitrate_bps[sl_idx] = max_bitrate_bps;
}
if (spatial_layer_bitrate_bps[sl_idx] < min_bitrate_bps) {
enough_bitrate = false;
break;
}
}
if (enough_bitrate || num_spatial_layers == 1) {
break;
}
}
}
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],
kTemporalLayeringRateScalingFactor);
for (size_t tl_idx = 0; tl_idx < num_temporal_layers; ++tl_idx) {
bitrate_allocation.SetBitrate(sl_idx, num_temporal_layers - tl_idx - 1,
temporal_layer_bitrate_bps[tl_idx]);
}
}
return bitrate_allocation;
}
uint32_t SvcRateAllocator::GetPreferredBitrateBps(uint32_t framerate) {
return GetAllocation(codec_.maxBitrate * 1000, framerate).get_sum_bps();
}
std::vector<size_t> SvcRateAllocator::SplitBitrate(size_t num_layers,
size_t total_bitrate,
float rate_scaling_factor) {
std::vector<size_t> bitrates;
float denominator = 0.0f;
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
denominator += std::pow(rate_scaling_factor, layer_idx);
}
float 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;
}
return bitrates;
}
} // namespace webrtc

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_SVC_RATE_ALLOCATOR_H_
#define MODULES_VIDEO_CODING_CODECS_VP9_SVC_RATE_ALLOCATOR_H_
#include <stdint.h>
#include <vector>
#include "common_video/include/video_bitrate_allocator.h"
namespace webrtc {
class SvcRateAllocator : public VideoBitrateAllocator {
public:
explicit SvcRateAllocator(const VideoCodec& codec);
BitrateAllocation GetAllocation(uint32_t total_bitrate_bps,
uint32_t framerate_fps) override;
uint32_t GetPreferredBitrateBps(uint32_t framerate_fps) override;
private:
std::vector<size_t> SplitBitrate(size_t num_layers,
size_t total_bitrate,
float rate_scaling_factor);
const VideoCodec codec_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_CODECS_VP9_SVC_RATE_ALLOCATOR_H_

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include "modules/video_coding/codecs/vp9/svc_config.h"
#include "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
static VideoCodec Configure(size_t width,
size_t height,
size_t num_spatial_layers,
size_t num_temporal_layers) {
VideoCodec codec;
codec.width = width;
codec.height = height;
codec.codecType = kVideoCodecVP9;
std::vector<SpatialLayer> spatial_layers =
GetSvcConfig(width, height, num_spatial_layers, num_temporal_layers);
RTC_CHECK_LE(spatial_layers.size(), kMaxSpatialLayers);
codec.VP9()->numberOfSpatialLayers =
std::min<unsigned char>(num_spatial_layers, spatial_layers.size());
codec.VP9()->numberOfTemporalLayers = std::min<unsigned char>(
num_temporal_layers, spatial_layers.back().numberOfTemporalLayers);
for (size_t sl_idx = 0; sl_idx < spatial_layers.size(); ++sl_idx) {
codec.spatialLayers[sl_idx] = spatial_layers[sl_idx];
}
return codec;
}
} // namespace
TEST(SvcRateAllocatorTest, SingleLayerFor320x180Input) {
VideoCodec codec = Configure(320, 180, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
BitrateAllocation allocation = allocator.GetAllocation(1000 * 1000, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_EQ(allocation.GetSpatialLayerSum(1), 0u);
}
TEST(SvcRateAllocatorTest, TwoLayersFor640x360Input) {
VideoCodec codec = Configure(640, 360, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
BitrateAllocation allocation = allocator.GetAllocation(1000 * 1000, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_GT(allocation.GetSpatialLayerSum(1), 0u);
EXPECT_EQ(allocation.GetSpatialLayerSum(2), 0u);
}
TEST(SvcRateAllocatorTest, ThreeLayersFor1280x720Input) {
VideoCodec codec = Configure(1280, 720, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
BitrateAllocation allocation = allocator.GetAllocation(1000 * 1000, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_GT(allocation.GetSpatialLayerSum(1), 0u);
EXPECT_GT(allocation.GetSpatialLayerSum(2), 0u);
}
TEST(SvcRateAllocatorTest,
BaseLayerNonZeroBitrateEvenIfTotalIfLessThanMinimum) {
VideoCodec codec = Configure(1280, 720, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
const SpatialLayer* layers = codec.spatialLayers;
BitrateAllocation allocation =
allocator.GetAllocation(layers[0].minBitrate * 1000 / 2, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_LT(allocation.GetSpatialLayerSum(0), layers[0].minBitrate * 1000);
EXPECT_EQ(allocation.GetSpatialLayerSum(1), 0u);
}
TEST(SvcRateAllocatorTest, Disable640x360Layer) {
VideoCodec codec = Configure(1280, 720, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
const SpatialLayer* layers = codec.spatialLayers;
size_t min_bitrate_for_640x360_layer_kbps =
layers[0].minBitrate + layers[1].minBitrate;
BitrateAllocation allocation = allocator.GetAllocation(
min_bitrate_for_640x360_layer_kbps * 1000 - 1, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_EQ(allocation.GetSpatialLayerSum(1), 0u);
}
TEST(SvcRateAllocatorTest, Disable1280x720Layer) {
VideoCodec codec = Configure(1280, 720, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
const SpatialLayer* layers = codec.spatialLayers;
size_t min_bitrate_for_1280x720_layer_kbps =
layers[0].minBitrate + layers[1].minBitrate + layers[2].minBitrate;
BitrateAllocation allocation = allocator.GetAllocation(
min_bitrate_for_1280x720_layer_kbps * 1000 - 1, 30);
EXPECT_GT(allocation.GetSpatialLayerSum(0), 0u);
EXPECT_GT(allocation.GetSpatialLayerSum(1), 0u);
EXPECT_EQ(allocation.GetSpatialLayerSum(2), 0u);
}
TEST(SvcRateAllocatorTest, BitrateIsCapped) {
VideoCodec codec = Configure(1280, 720, 3, 3);
SvcRateAllocator allocator = SvcRateAllocator(codec);
const SpatialLayer* layers = codec.spatialLayers;
const uint32_t link_mbps = 100;
BitrateAllocation allocation =
allocator.GetAllocation(link_mbps * 1000000, 30);
EXPECT_EQ(allocation.get_sum_kbps(),
layers[0].maxBitrate + layers[1].maxBitrate + layers[2].maxBitrate);
EXPECT_EQ((allocation.GetSpatialLayerSum(0) + 500) / 1000,
layers[0].maxBitrate);
EXPECT_EQ((allocation.GetSpatialLayerSum(1) + 500) / 1000,
layers[1].maxBitrate);
EXPECT_EQ((allocation.GetSpatialLayerSum(2) + 500) / 1000,
layers[2].maxBitrate);
}
} // namespace webrtc

View File

@ -8,9 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "api/video/i420_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "modules/video_coding/codecs/test/video_codec_unittest.h"
#include "modules/video_coding/codecs/vp9/include/vp9.h"
#include "modules/video_coding/codecs/vp9/svc_config.h"
#include "test/video_codec_settings.h"
namespace webrtc {
@ -117,7 +119,6 @@ TEST_F(TestVp9Impl, ParserQpEqualsEncodedQp) {
int qp = 0;
ASSERT_TRUE(vp9::GetQp(encoded_frame._buffer, encoded_frame._length, &qp));
EXPECT_EQ(encoded_frame.qp_, qp);
}
@ -161,8 +162,18 @@ TEST_F(TestVp9Impl, EncoderExplicitLayering) {
codec_settings_.width = 960;
codec_settings_.height = 540;
codec_settings_.spatialLayers[0].targetBitrate = 500;
codec_settings_.spatialLayers[1].targetBitrate = 1000;
codec_settings_.spatialLayers[0].minBitrate = 200;
codec_settings_.spatialLayers[0].maxBitrate = 500;
codec_settings_.spatialLayers[0].targetBitrate =
(codec_settings_.spatialLayers[0].minBitrate +
codec_settings_.spatialLayers[0].maxBitrate) /
2;
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[0].width = codec_settings_.width / 2;
codec_settings_.spatialLayers[0].height = codec_settings_.height / 2;
@ -191,4 +202,64 @@ TEST_F(TestVp9Impl, EncoderExplicitLayering) {
0 /* max payload size (unused) */));
}
TEST_F(TestVp9Impl, EnableDisableSpatialLayers) {
// Configure encoder to produce N spatial layers. Encode few frames of layer 0
// then enable layer 1 and encode few more frames and so on until layer N-1.
// Then disable layers one by one in the same way.
const size_t num_spatial_layers = 3;
const size_t num_temporal_layers = 1;
codec_settings_.VP9()->numberOfSpatialLayers =
static_cast<unsigned char>(num_spatial_layers);
codec_settings_.VP9()->numberOfTemporalLayers =
static_cast<unsigned char>(num_temporal_layers);
std::vector<SpatialLayer> layers =
GetSvcConfig(codec_settings_.width, codec_settings_.height,
num_spatial_layers, num_temporal_layers);
for (size_t i = 0; i < layers.size(); ++i) {
codec_settings_.spatialLayers[i] = layers[i];
}
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_settings_, 1 /* number of cores */,
0 /* max payload size (unused) */));
BitrateAllocation bitrate_allocation;
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
bitrate_allocation.SetBitrate(sl_idx, 0,
layers[sl_idx].targetBitrate * 1000);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->SetRateAllocation(bitrate_allocation,
codec_settings_.maxFramerate));
const size_t num_frames_to_encode = 3;
for (size_t frame_num = 0; frame_num < num_frames_to_encode; ++frame_num) {
SetWaitForEncodedFramesThreshold(sl_idx + 1);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->Encode(*NextInputFrame(), nullptr, nullptr));
std::vector<EncodedImage> encoded_frame;
std::vector<CodecSpecificInfo> codec_specific_info;
ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info));
}
}
for (size_t i = 0; i < num_spatial_layers - 1; ++i) {
const size_t sl_idx = num_spatial_layers - i - 1;
bitrate_allocation.SetBitrate(sl_idx, 0, 0);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->SetRateAllocation(bitrate_allocation,
codec_settings_.maxFramerate));
const size_t num_frames_to_encode = 3;
for (size_t frame_num = 0; frame_num < num_frames_to_encode; ++frame_num) {
SetWaitForEncodedFramesThreshold(sl_idx);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->Encode(*NextInputFrame(), nullptr, nullptr));
std::vector<EncodedImage> encoded_frame;
std::vector<CodecSpecificInfo> codec_specific_info;
ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info));
}
}
}
} // namespace webrtc

View File

@ -24,6 +24,7 @@
#include "common_video/include/video_frame_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "modules/video_coding/codecs/vp9/screenshare_layers.h"
#include "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
#include "rtc_base/checks.h"
#include "rtc_base/keep_ref_until_done.h"
#include "rtc_base/logging.h"
@ -122,25 +123,21 @@ bool VP9EncoderImpl::ExplicitlyConfiguredSpatialLayers() const {
return num_spatial_layers_ > 1 && codec_.spatialLayers[0].targetBitrate > 0;
}
bool VP9EncoderImpl::SetSvcRates() {
bool VP9EncoderImpl::SetSvcRates(const BitrateAllocation& bitrate_allocation) {
uint8_t i = 0;
config_->rc_target_bitrate = bitrate_allocation.get_sum_kbps();
spatial_layer_->ConfigureBitrate(bitrate_allocation.get_sum_kbps(), 0);
if (ExplicitlyConfiguredSpatialLayers()) {
if (num_temporal_layers_ > 1) {
RTC_LOG(LS_ERROR) << "Multiple temporal layers when manually specifying "
"spatial layers not implemented yet!";
return false;
}
int total_bitrate_bps = 0;
for (i = 0; i < num_spatial_layers_; ++i)
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].targetBitrate * 1000 /
total_bitrate_bps);
for (size_t sl_idx = 0; sl_idx < num_spatial_layers_; ++sl_idx) {
config_->ss_target_bitrate[sl_idx] =
bitrate_allocation.GetSpatialLayerSum(sl_idx) / 1000;
for (size_t tl_idx = 0; tl_idx < num_temporal_layers_; ++tl_idx) {
config_->layer_target_bitrate[sl_idx * num_temporal_layers_ + tl_idx] =
bitrate_allocation.GetTemporalLayerSum(sl_idx, tl_idx) / 1000;
}
}
} else {
float rate_ratio[VPX_MAX_LAYERS] = {0};
@ -212,12 +209,9 @@ int VP9EncoderImpl::SetRateAllocation(
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
// TODO(sprang): Actually use BitrateAllocation layer info.
config_->rc_target_bitrate = bitrate_allocation.get_sum_kbps();
codec_.maxFramerate = frame_rate;
spatial_layer_->ConfigureBitrate(bitrate_allocation.get_sum_kbps(), 0);
if (!SetSvcRates()) {
if (!SetSvcRates(bitrate_allocation)) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
@ -439,7 +433,10 @@ int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) {
}
}
if (!SetSvcRates()) {
SvcRateAllocator init_allocator(codec_);
BitrateAllocation allocation = init_allocator.GetAllocation(
inst->startBitrate * 1000, inst->maxFramerate);
if (!SetSvcRates(allocation)) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}

View File

@ -77,7 +77,7 @@ class VP9EncoderImpl : public VP9Encoder {
uint32_t timestamp);
bool ExplicitlyConfiguredSpatialLayers() const;
bool SetSvcRates();
bool SetSvcRates(const BitrateAllocation& bitrate_allocation);
// Used for flexible mode to set the flags and buffer references used
// by the encoder. Also calculates the references used by the RTP