media/gpu/VP9VaapiVideoEncoderDelegate: Support spatial SVC encoding
This CL adds the spatial SVC encoding support for VP9VaapiVideoEncoderDelegate. Bug: 1186051 Change-Id: I5129246f41420d86b9ecd9f0d488d9d25721e43b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2897010 Commit-Queue: Zhaoliang Ma <zhaoliang.ma@intel.com> Reviewed-by: Hirokazu Honda <hiroh@chromium.org> Cr-Commit-Position: refs/heads/master@{#896018}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
3ccd773c99
commit
6f92ac0be6
@ -168,6 +168,8 @@ VP9SVCLayers::VP9SVCLayers(const std::vector<SpatialLayer>& spatial_layers)
|
||||
gfx::Size(spatial_layer.width, spatial_layer.height));
|
||||
}
|
||||
active_spatial_layer_resolutions_ = spatial_layer_resolutions_;
|
||||
begin_active_layer_ = 0;
|
||||
end_active_layer_ = active_spatial_layer_resolutions_.size();
|
||||
DCHECK_LE(num_temporal_layers_, kMaxSupportedTemporalLayers);
|
||||
DCHECK(!spatial_layer_resolutions_.empty());
|
||||
DCHECK_LE(spatial_layer_resolutions_.size(), kMaxSpatialLayers);
|
||||
@ -177,9 +179,10 @@ VP9SVCLayers::~VP9SVCLayers() = default;
|
||||
|
||||
bool VP9SVCLayers::UpdateEncodeJob(bool is_key_frame_requested,
|
||||
size_t kf_period_frames) {
|
||||
if (is_key_frame_requested) {
|
||||
if (force_key_frame_ || is_key_frame_requested) {
|
||||
frame_num_ = 0;
|
||||
spatial_idx_ = 0;
|
||||
force_key_frame_ = false;
|
||||
}
|
||||
|
||||
if (spatial_idx_ == active_spatial_layer_resolutions_.size()) {
|
||||
@ -191,10 +194,111 @@ bool VP9SVCLayers::UpdateEncodeJob(bool is_key_frame_requested,
|
||||
return frame_num_ == 0 && spatial_idx_ == 0;
|
||||
}
|
||||
|
||||
bool VP9SVCLayers::MaybeUpdateActiveLayer(
|
||||
VideoBitrateAllocation* bitrate_allocation) {
|
||||
// Don't update active layer if current picture haven't completed SVC
|
||||
// encoding. Since the |spatial_idx_| is updated in the beginning of next
|
||||
// encoding, so the |spatial_idx_| equals 0 (only for the first frame) or the
|
||||
// number of active spatial layers indicates the complement of SVC picture
|
||||
// encoding.
|
||||
if (spatial_idx_ != 0 &&
|
||||
spatial_idx_ != active_spatial_layer_resolutions_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t begin_active_layer = kMaxSpatialLayers;
|
||||
size_t end_active_layer = spatial_layer_resolutions_.size();
|
||||
for (size_t sid = 0; sid < spatial_layer_resolutions_.size(); ++sid) {
|
||||
size_t sum = 0;
|
||||
for (size_t tid = 0; tid < num_temporal_layers_; ++tid) {
|
||||
const int tl_bitrate = bitrate_allocation->GetBitrateBps(sid, tid);
|
||||
// A bitrate of a temporal layer must be zero if the bitrates of lower
|
||||
// temporal layers are zero, e.g. {0, 0, 100}.
|
||||
if (tid > 0 && tl_bitrate > 0 && sum == 0)
|
||||
return false;
|
||||
// A bitrate of a temporal layer must not be zero if the bitrates of lower
|
||||
// temporal layers are not zero, e.g. {100, 0, 0}.
|
||||
if (tid > 0 && tl_bitrate == 0 && sum != 0)
|
||||
return false;
|
||||
|
||||
sum += static_cast<size_t>(tl_bitrate);
|
||||
}
|
||||
|
||||
// Check if the temporal layers larger than |num_temporal_layers_| are zero.
|
||||
for (size_t tid = num_temporal_layers_;
|
||||
tid < VideoBitrateAllocation::kMaxTemporalLayers; ++tid) {
|
||||
if (bitrate_allocation->GetBitrateBps(sid, tid) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sum == 0) {
|
||||
// This is the first non-active spatial layer in the end side.
|
||||
if (begin_active_layer != kMaxSpatialLayers) {
|
||||
end_active_layer = sid;
|
||||
break;
|
||||
}
|
||||
// No active spatial layer is found yet. Try the upper spatial layer.
|
||||
continue;
|
||||
}
|
||||
// This is the lowest active layer.
|
||||
if (begin_active_layer == kMaxSpatialLayers)
|
||||
begin_active_layer = sid;
|
||||
}
|
||||
// Check if all the bitrates of unsupported temporal and spatial layers are
|
||||
// zero.
|
||||
for (size_t sid = end_active_layer;
|
||||
sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
|
||||
for (size_t tid = 0; tid < VideoBitrateAllocation::kMaxTemporalLayers;
|
||||
++tid) {
|
||||
if (bitrate_allocation->GetBitrateBps(sid, tid) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// No active layer is found.
|
||||
if (begin_active_layer == kMaxSpatialLayers)
|
||||
return false;
|
||||
|
||||
DCHECK_LT(begin_active_layer_, end_active_layer_);
|
||||
DCHECK_LE(end_active_layer_ - begin_active_layer_,
|
||||
spatial_layer_resolutions_.size());
|
||||
|
||||
// Remove non active spatial layer bitrate if |begin_active_layer| > 0.
|
||||
if (begin_active_layer > 0) {
|
||||
for (size_t sid = begin_active_layer; sid < end_active_layer; ++sid) {
|
||||
for (size_t tid = 0; tid < num_temporal_layers_; ++tid) {
|
||||
int bitrate = bitrate_allocation->GetBitrateBps(sid, tid);
|
||||
bitrate_allocation->SetBitrate(sid - begin_active_layer, tid, bitrate);
|
||||
bitrate_allocation->SetBitrate(sid, tid, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset SVC parameters and force to produce key frame if active layer
|
||||
// changed.
|
||||
if (begin_active_layer != begin_active_layer_ ||
|
||||
end_active_layer != end_active_layer_) {
|
||||
// Update the stored active layer range.
|
||||
begin_active_layer_ = begin_active_layer;
|
||||
end_active_layer_ = end_active_layer;
|
||||
active_spatial_layer_resolutions_ = {
|
||||
spatial_layer_resolutions_.begin() + begin_active_layer,
|
||||
spatial_layer_resolutions_.begin() + end_active_layer};
|
||||
force_key_frame_ = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VP9SVCLayers::FillUsedRefFramesAndMetadata(
|
||||
VP9Picture* picture,
|
||||
std::array<bool, kVp9NumRefsPerFrame>* ref_frames_used) {
|
||||
DCHECK(picture->frame_hdr);
|
||||
// Update the spatial layer size for VP9FrameHeader.
|
||||
gfx::Size updated_size = active_spatial_layer_resolutions_[spatial_idx_];
|
||||
picture->frame_hdr->render_width = updated_size.width();
|
||||
picture->frame_hdr->render_height = updated_size.height();
|
||||
picture->frame_hdr->frame_width = updated_size.width();
|
||||
picture->frame_hdr->frame_height = updated_size.height();
|
||||
|
||||
// Initialize |metadata_for_encoding| with default values.
|
||||
picture->metadata_for_encoding.emplace();
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "media/video/video_encode_accelerator.h"
|
||||
|
||||
namespace media {
|
||||
class VideoBitrateAllocation;
|
||||
class VP9Picture;
|
||||
struct Vp9Metadata;
|
||||
|
||||
@ -46,6 +47,10 @@ class VP9SVCLayers {
|
||||
// Returns true if EncodeJob needs to produce key frame.
|
||||
bool UpdateEncodeJob(bool is_key_frame_requested, size_t kf_period_frames);
|
||||
|
||||
// Activate/Deactivate spatial layers via |bitrate_allocation|.
|
||||
// Returns whether (de)updating is successful.
|
||||
bool MaybeUpdateActiveLayer(VideoBitrateAllocation* bitrate_allocation);
|
||||
|
||||
// Sets |picture|'s used reference frames and |ref_frames_used| so that they
|
||||
// structure valid temporal layers. This also fills |picture|'s
|
||||
// |metadata_for_encoding|.
|
||||
@ -54,6 +59,9 @@ class VP9SVCLayers {
|
||||
std::array<bool, kVp9NumRefsPerFrame>* ref_frames_used);
|
||||
|
||||
size_t num_temporal_layers() const { return num_temporal_layers_; }
|
||||
const std::vector<gfx::Size>& active_spatial_layer_resolutions() const {
|
||||
return active_spatial_layer_resolutions_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Useful functions to construct refresh flag and detect reference frames
|
||||
@ -76,11 +84,18 @@ class VP9SVCLayers {
|
||||
const size_t temporal_pattern_size_;
|
||||
size_t spatial_idx_ = 0;
|
||||
size_t frame_num_ = 0;
|
||||
bool force_key_frame_ = false;
|
||||
|
||||
// Resolutions for all spatial layers and active spatial layers.
|
||||
std::vector<gfx::Size> spatial_layer_resolutions_;
|
||||
std::vector<gfx::Size> active_spatial_layer_resolutions_;
|
||||
|
||||
// Stores the active layer range, only used to judge whether active range has
|
||||
// changed in |MaybeUpdateActiveLayer|, then
|
||||
// |active_spatial_layer_resolutions_| needs update.
|
||||
size_t begin_active_layer_;
|
||||
size_t end_active_layer_;
|
||||
|
||||
// The pattern index used for reference frames slots.
|
||||
uint8_t pattern_index_of_ref_frames_slots_[kMaxNumUsedReferenceFrames] = {};
|
||||
};
|
||||
|
@ -178,6 +178,93 @@ void VP9SVCLayersTest::VerifyRefFrames(
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies the bitrate check in MaybeUpdateActiveLayer().
|
||||
TEST_F(VP9SVCLayersTest, MaybeUpdateActiveLayer) {
|
||||
constexpr size_t kNumSpatialLayers = 3;
|
||||
constexpr size_t kNumTemporalLayers = 3;
|
||||
const std::vector<VP9SVCLayers::SpatialLayer> spatial_layers =
|
||||
GetDefaultSVCLayers(kNumSpatialLayers, kNumTemporalLayers);
|
||||
VP9SVCLayers svc_layers(spatial_layers);
|
||||
|
||||
// Set Default bitrate allocation.
|
||||
int layer_rate = 1;
|
||||
VideoBitrateAllocation allocation;
|
||||
for (size_t sid = 0; sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
|
||||
for (size_t tid = 0; tid < VideoBitrateAllocation::kMaxTemporalLayers;
|
||||
++tid) {
|
||||
allocation.SetBitrate(sid, tid, layer_rate++);
|
||||
}
|
||||
}
|
||||
DCHECK_LT(kNumSpatialLayers, VideoBitrateAllocation::kMaxSpatialLayers);
|
||||
DCHECK_LT(kNumTemporalLayers, VideoBitrateAllocation::kMaxTemporalLayers);
|
||||
EXPECT_FALSE(svc_layers.MaybeUpdateActiveLayer(&allocation));
|
||||
|
||||
// Set unsupported temporal layer bitrate to 0.
|
||||
for (size_t sid = 0; sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
|
||||
for (size_t tid = kNumTemporalLayers;
|
||||
tid < VideoBitrateAllocation::kMaxTemporalLayers; ++tid) {
|
||||
allocation.SetBitrate(sid, tid, 0);
|
||||
}
|
||||
}
|
||||
EXPECT_FALSE(svc_layers.MaybeUpdateActiveLayer(&allocation));
|
||||
|
||||
// Set unsupported spatial layer bitrate to 0.
|
||||
for (size_t sid = kNumSpatialLayers;
|
||||
sid < VideoBitrateAllocation::kMaxSpatialLayers; ++sid) {
|
||||
for (size_t tid = 0; tid < VideoBitrateAllocation::kMaxTemporalLayers;
|
||||
++tid) {
|
||||
allocation.SetBitrate(sid, tid, 0);
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(svc_layers.MaybeUpdateActiveLayer(&allocation));
|
||||
|
||||
// Set lower temporal layer bitrate to zero, e.g. {0, 2, 3}.
|
||||
allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/0, 0);
|
||||
EXPECT_FALSE(svc_layers.MaybeUpdateActiveLayer(&allocation));
|
||||
|
||||
allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/0, 1);
|
||||
// Set upper temporal layer bitrate to 0, e.g. {1, 2, 0}.
|
||||
allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/2, 0);
|
||||
EXPECT_FALSE(svc_layers.MaybeUpdateActiveLayer(&allocation));
|
||||
|
||||
allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/2, 3);
|
||||
// Deactivate SL0 and SL1 and verify the new bitrate allocation.
|
||||
constexpr int kNumDeactivatedLowerSpatialLayer = 2;
|
||||
VideoBitrateAllocation new_allocation = allocation;
|
||||
for (size_t sid = 0; sid < kNumDeactivatedLowerSpatialLayer; ++sid) {
|
||||
for (size_t tid = 0; tid < kNumTemporalLayers; ++tid)
|
||||
new_allocation.SetBitrate(sid, tid, 0);
|
||||
}
|
||||
EXPECT_TRUE(svc_layers.MaybeUpdateActiveLayer(&new_allocation));
|
||||
for (size_t sid = 0; sid < kNumSpatialLayers; ++sid) {
|
||||
for (size_t tid = 0; tid < kNumTemporalLayers; ++tid) {
|
||||
if (sid + kNumDeactivatedLowerSpatialLayer <
|
||||
VideoBitrateAllocation::kMaxSpatialLayers)
|
||||
EXPECT_EQ(new_allocation.GetBitrateBps(sid, tid),
|
||||
allocation.GetBitrateBps(
|
||||
sid + kNumDeactivatedLowerSpatialLayer, tid));
|
||||
else
|
||||
EXPECT_EQ(new_allocation.GetBitrateBps(sid, tid), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Deactivate SL2 and verify the new bitrate allocation.
|
||||
new_allocation = allocation;
|
||||
constexpr int kNumActiveSpatialLayer = 2;
|
||||
for (size_t tid = 0; tid < kNumTemporalLayers; ++tid)
|
||||
new_allocation.SetBitrate(/*spatial_index=*/2, tid, 0);
|
||||
EXPECT_TRUE(svc_layers.MaybeUpdateActiveLayer(&new_allocation));
|
||||
for (size_t sid = 0; sid < kNumSpatialLayers; ++sid) {
|
||||
for (size_t tid = 0; tid < kNumTemporalLayers; ++tid) {
|
||||
if (sid < kNumActiveSpatialLayer)
|
||||
EXPECT_EQ(new_allocation.GetBitrateBps(sid, tid),
|
||||
allocation.GetBitrateBps(sid, tid));
|
||||
else
|
||||
EXPECT_EQ(new_allocation.GetBitrateBps(sid, tid), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(VP9SVCLayersTest, ) {
|
||||
const size_t num_spatial_layers = ::testing::get<0>(GetParam());
|
||||
const size_t num_temporal_layers = ::testing::get<1>(GetParam());
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "base/bits.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "media/gpu/macros.h"
|
||||
#include "media/gpu/vaapi/vaapi_common.h"
|
||||
#include "media/gpu/vaapi/vaapi_wrapper.h"
|
||||
@ -25,9 +26,6 @@ namespace {
|
||||
// Keyframe period.
|
||||
constexpr size_t kKFPeriod = 3000;
|
||||
|
||||
// Arbitrarily chosen bitrate window size for rate control, in ms.
|
||||
constexpr int kCPBWindowSizeMs = 500;
|
||||
|
||||
// Quantization parameter. They are vp9 ac/dc indices and their ranges are
|
||||
// 0-255. Based on WebRTC's defaults.
|
||||
constexpr uint8_t kMinQP = 4;
|
||||
@ -68,6 +66,12 @@ uint8_t QindexToQuantizer(uint8_t q_index) {
|
||||
return base::size(kQuantizerToQindex) - 1;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/752720): remove this in favor of std::gcd if c++17 is enabled
|
||||
// to use.
|
||||
int GCD(int a, int b) {
|
||||
return a == 0 ? b : GCD(b % a, a);
|
||||
}
|
||||
|
||||
// The return value is expressed as a percentage of the average. For example,
|
||||
// to allocate no more than 4.5 frames worth of bitrate to a keyframe, the
|
||||
// return value is 450.
|
||||
@ -91,39 +95,43 @@ uint32_t MaxSizeOfKeyframeAsPercentage(uint32_t optimal_buffer_size,
|
||||
|
||||
VideoBitrateAllocation GetDefaultVideoBitrateAllocation(
|
||||
const VideoEncodeAccelerator::Config& config) {
|
||||
DCHECK(!config.HasSpatialLayer()) << "Spatial layers are not supported.";
|
||||
VideoBitrateAllocation bitrate_allocation;
|
||||
if (!config.HasTemporalLayer()) {
|
||||
if (!config.HasTemporalLayer() && !config.HasSpatialLayer()) {
|
||||
bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate);
|
||||
return bitrate_allocation;
|
||||
}
|
||||
|
||||
const auto& spatial_layer = config.spatial_layers[0];
|
||||
const size_t num_temporal_layers = spatial_layer.num_of_temporal_layers;
|
||||
DCHECK_GT(num_temporal_layers, 1u);
|
||||
DCHECK_LE(num_temporal_layers, 3u);
|
||||
constexpr double kTemporalLayersBitrateScaleFactors
|
||||
[][VP9SVCLayers::kMaxSupportedTemporalLayers] = {
|
||||
{0.50, 0.50, 0.00}, // For two temporal layers.
|
||||
{0.25, 0.25, 0.50}, // For three temporal layers.
|
||||
};
|
||||
DCHECK_LE(config.spatial_layers.size(), VP9SVCLayers::kMaxSpatialLayers);
|
||||
for (size_t sid = 0; sid < config.spatial_layers.size(); ++sid) {
|
||||
const auto& spatial_layer = config.spatial_layers[sid];
|
||||
const size_t num_temporal_layers = spatial_layer.num_of_temporal_layers;
|
||||
DCHECK_LE(num_temporal_layers, VP9SVCLayers::kMaxSupportedTemporalLayers);
|
||||
constexpr double kTemporalLayersBitrateScaleFactors
|
||||
[][VP9SVCLayers::kMaxSupportedTemporalLayers] = {
|
||||
{1.00, 0.00, 0.00}, // For one temporal layer.
|
||||
{0.50, 0.50, 0.00}, // For two temporal layers.
|
||||
{0.25, 0.25, 0.50}, // For three temporal layers.
|
||||
};
|
||||
|
||||
const uint32_t bitrate_bps = spatial_layer.bitrate_bps;
|
||||
for (size_t i = 0; i < num_temporal_layers; ++i) {
|
||||
const double factor =
|
||||
kTemporalLayersBitrateScaleFactors[num_temporal_layers - 2][i];
|
||||
bitrate_allocation.SetBitrate(
|
||||
0 /* spatial_index */, i,
|
||||
base::checked_cast<int>(bitrate_bps * factor));
|
||||
const uint32_t bitrate_bps = spatial_layer.bitrate_bps;
|
||||
for (size_t tid = 0; tid < num_temporal_layers; ++tid) {
|
||||
const double factor =
|
||||
kTemporalLayersBitrateScaleFactors[num_temporal_layers - 1][tid];
|
||||
bitrate_allocation.SetBitrate(
|
||||
sid, tid, base::checked_cast<int>(bitrate_bps * factor));
|
||||
}
|
||||
}
|
||||
return bitrate_allocation;
|
||||
}
|
||||
|
||||
libvpx::VP9RateControlRtcConfig CreateRateControlConfig(
|
||||
const gfx::Size encode_size,
|
||||
const VP9VaapiVideoEncoderDelegate::EncodeParams& encode_params,
|
||||
const VideoBitrateAllocation& bitrate_allocation,
|
||||
const size_t num_temporal_layers) {
|
||||
const size_t num_temporal_layers,
|
||||
const std::vector<gfx::Size>& spatial_layer_resolutions) {
|
||||
DCHECK(!spatial_layer_resolutions.empty());
|
||||
const gfx::Size& encode_size = spatial_layer_resolutions.back();
|
||||
const size_t num_spatial_layers = spatial_layer_resolutions.size();
|
||||
libvpx::VP9RateControlRtcConfig rc_cfg{};
|
||||
rc_cfg.rc_mode = VPX_CBR;
|
||||
rc_cfg.width = encode_size.width();
|
||||
@ -143,19 +151,24 @@ libvpx::VP9RateControlRtcConfig CreateRateControlConfig(
|
||||
rc_cfg.buf_optimal_sz, encode_params.framerate);
|
||||
rc_cfg.framerate = encode_params.framerate;
|
||||
|
||||
// Spatial layers variables.
|
||||
rc_cfg.ss_number_layers = 1;
|
||||
rc_cfg.scaling_factor_num[0] = 1;
|
||||
rc_cfg.scaling_factor_den[0] = 1;
|
||||
// Fill temporal layers variables.
|
||||
// Fill spatial/temporal layers variables.
|
||||
rc_cfg.ss_number_layers = num_spatial_layers;
|
||||
rc_cfg.ts_number_layers = num_temporal_layers;
|
||||
int bitrate_sum = 0;
|
||||
for (size_t ti = 0; ti < num_temporal_layers; ti++) {
|
||||
rc_cfg.max_quantizers[ti] = rc_cfg.max_quantizer;
|
||||
rc_cfg.min_quantizers[ti] = rc_cfg.min_quantizer;
|
||||
bitrate_sum += bitrate_allocation.GetBitrateBps(0, ti);
|
||||
rc_cfg.layer_target_bitrate[ti] = bitrate_sum / 1000;
|
||||
rc_cfg.ts_rate_decimator[ti] = 1u << (num_temporal_layers - ti - 1);
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
int gcd =
|
||||
GCD(encode_size.height(), spatial_layer_resolutions[sid].height());
|
||||
rc_cfg.scaling_factor_num[sid] =
|
||||
spatial_layer_resolutions[sid].height() / gcd;
|
||||
rc_cfg.scaling_factor_den[sid] = encode_size.height() / gcd;
|
||||
int bitrate_sum = 0;
|
||||
for (size_t tid = 0; tid < num_temporal_layers; ++tid) {
|
||||
size_t idx = sid * num_temporal_layers + tid;
|
||||
rc_cfg.max_quantizers[idx] = rc_cfg.max_quantizer;
|
||||
rc_cfg.min_quantizers[idx] = rc_cfg.min_quantizer;
|
||||
bitrate_sum += bitrate_allocation.GetBitrateBps(sid, tid);
|
||||
rc_cfg.layer_target_bitrate[idx] = bitrate_sum / 1000;
|
||||
rc_cfg.ts_rate_decimator[tid] = 1u << (num_temporal_layers - tid - 1);
|
||||
}
|
||||
}
|
||||
return rc_cfg;
|
||||
}
|
||||
@ -171,8 +184,6 @@ static scoped_refptr<base::RefCountedBytes> MakeRefCountedBytes(void* ptr,
|
||||
VP9VaapiVideoEncoderDelegate::EncodeParams::EncodeParams()
|
||||
: kf_period_frames(kKFPeriod),
|
||||
framerate(0),
|
||||
cpb_window_size_ms(kCPBWindowSizeMs),
|
||||
cpb_size_bits(0),
|
||||
initial_qp(kDefaultQP),
|
||||
min_qp(kMinQP),
|
||||
max_qp(kMaxQP),
|
||||
@ -200,10 +211,6 @@ bool VP9VaapiVideoEncoderDelegate::Initialize(
|
||||
DVLOGF(1) << "Invalid profile: " << GetProfileName(config.output_profile);
|
||||
return false;
|
||||
}
|
||||
if (config.HasSpatialLayer()) {
|
||||
DVLOGF(1) << "Spatial layer encoding is not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.input_visible_size.IsEmpty()) {
|
||||
DVLOGF(1) << "Input visible size could not be empty";
|
||||
@ -228,34 +235,68 @@ bool VP9VaapiVideoEncoderDelegate::Initialize(
|
||||
auto initial_bitrate_allocation = GetDefaultVideoBitrateAllocation(config);
|
||||
|
||||
size_t num_temporal_layers = 1;
|
||||
if (config.HasTemporalLayer()) {
|
||||
size_t num_spatial_layers = 1;
|
||||
std::vector<gfx::Size> spatial_layer_resolutions;
|
||||
if (config.HasTemporalLayer() || config.HasSpatialLayer()) {
|
||||
num_spatial_layers = config.spatial_layers.size();
|
||||
num_temporal_layers = config.spatial_layers[0].num_of_temporal_layers;
|
||||
if (num_temporal_layers == 1 && !config.HasSpatialLayer()) {
|
||||
VLOGF(1) << "VP9SVCLayers only supports single temporal layer with more "
|
||||
"than one spatial layer encoding";
|
||||
DCHECK(num_spatial_layers != 1 || num_temporal_layers != 1);
|
||||
for (size_t sid = 1; sid < num_spatial_layers; ++sid) {
|
||||
if (num_temporal_layers !=
|
||||
config.spatial_layers[sid].num_of_temporal_layers) {
|
||||
VLOGF(1) << "The temporal layer sizes among spatial layers must be "
|
||||
"identical";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (num_spatial_layers > VP9SVCLayers::kMaxSpatialLayers ||
|
||||
num_temporal_layers > VP9SVCLayers::kMaxSupportedTemporalLayers) {
|
||||
VLOGF(1) << "Unsupported amount of spatial/temporal layers: "
|
||||
<< ", Spatial layer number: " << num_spatial_layers
|
||||
<< ", Temporal layer number: " << num_temporal_layers;
|
||||
return false;
|
||||
}
|
||||
if (num_temporal_layers > VP9SVCLayers::kMaxSupportedTemporalLayers) {
|
||||
VLOGF(1) << "Unsupported amount of temporal layers: "
|
||||
<< num_temporal_layers;
|
||||
if (num_spatial_layers > 1 &&
|
||||
config.inter_layer_pred !=
|
||||
VideoEncodeAccelerator::Config::InterLayerPredMode::kOnKeyPic) {
|
||||
std::string inter_layer_pred;
|
||||
if (config.inter_layer_pred ==
|
||||
VideoEncodeAccelerator::Config::InterLayerPredMode::kOn)
|
||||
inter_layer_pred = base::StringPrintf("InterLayerPredMode::kOn");
|
||||
else
|
||||
inter_layer_pred = base::StringPrintf("InterLayerPredMode::kOff");
|
||||
VLOGF(1) << "Support only k-SVC encoding. inter_layer_pred="
|
||||
<< inter_layer_pred;
|
||||
return false;
|
||||
}
|
||||
for (const auto& spatial_layer : config.spatial_layers) {
|
||||
spatial_layer_resolutions.emplace_back(
|
||||
gfx::Size(spatial_layer.width, spatial_layer.height));
|
||||
}
|
||||
svc_layers_ = std::make_unique<VP9SVCLayers>(config.spatial_layers);
|
||||
}
|
||||
current_params_.max_qp = kMaxQPForSoftwareRateCtrl;
|
||||
|
||||
// Store layer size for vp9 simple stream.
|
||||
if (spatial_layer_resolutions.empty())
|
||||
spatial_layer_resolutions.push_back(visible_size_);
|
||||
|
||||
// |rate_ctrl_| might be injected for tests.
|
||||
if (!rate_ctrl_) {
|
||||
rate_ctrl_ = VP9RateControl::Create(CreateRateControlConfig(
|
||||
visible_size_, current_params_, initial_bitrate_allocation,
|
||||
num_temporal_layers));
|
||||
current_params_, initial_bitrate_allocation, num_temporal_layers,
|
||||
spatial_layer_resolutions));
|
||||
}
|
||||
if (!rate_ctrl_)
|
||||
return false;
|
||||
|
||||
return UpdateRates(initial_bitrate_allocation,
|
||||
DCHECK(!pending_update_rates_);
|
||||
pending_update_rates_ =
|
||||
std::make_pair(initial_bitrate_allocation,
|
||||
config.initial_framerate.value_or(
|
||||
VideoEncodeAccelerator::kDefaultFramerate));
|
||||
|
||||
return ApplyPendingUpdateRates();
|
||||
}
|
||||
|
||||
gfx::Size VP9VaapiVideoEncoderDelegate::GetCodedSize() const {
|
||||
@ -274,6 +315,9 @@ size_t VP9VaapiVideoEncoderDelegate::GetMaxNumOfRefFrames() const {
|
||||
bool VP9VaapiVideoEncoderDelegate::PrepareEncodeJob(EncodeJob* encode_job) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
if (!ApplyPendingUpdateRates())
|
||||
return false;
|
||||
|
||||
if (svc_layers_) {
|
||||
if (svc_layers_->UpdateEncodeJob(encode_job->IsKeyframeRequested(),
|
||||
current_params_.kf_period_frames)) {
|
||||
@ -332,6 +376,38 @@ void VP9VaapiVideoEncoderDelegate::BitrateControlUpdate(
|
||||
rate_ctrl_->PostEncodeUpdate(encoded_chunk_size_bytes);
|
||||
}
|
||||
|
||||
bool VP9VaapiVideoEncoderDelegate::ApplyPendingUpdateRates() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (!pending_update_rates_)
|
||||
return true;
|
||||
|
||||
VLOGF(2) << "New bitrate: " << pending_update_rates_->first.GetSumBps()
|
||||
<< ", New framerate: " << pending_update_rates_->second;
|
||||
|
||||
current_params_.bitrate_allocation = pending_update_rates_->first;
|
||||
current_params_.framerate = pending_update_rates_->second;
|
||||
pending_update_rates_.reset();
|
||||
|
||||
// Update active layer status in |svc_layers_|, and key frame is produced when
|
||||
// active layer changed.
|
||||
if (svc_layers_ &&
|
||||
!svc_layers_->MaybeUpdateActiveLayer(¤t_params_.bitrate_allocation))
|
||||
return false;
|
||||
|
||||
if (!rate_ctrl_)
|
||||
return true;
|
||||
|
||||
const size_t num_temporal_layers =
|
||||
svc_layers_ ? svc_layers_->num_temporal_layers() : 1u;
|
||||
std::vector<gfx::Size> spatial_layer_resolutions = {visible_size_};
|
||||
if (svc_layers_)
|
||||
spatial_layer_resolutions = svc_layers_->active_spatial_layer_resolutions();
|
||||
rate_ctrl_->UpdateRateControl(CreateRateControlConfig(
|
||||
current_params_, current_params_.bitrate_allocation, num_temporal_layers,
|
||||
spatial_layer_resolutions));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VP9VaapiVideoEncoderDelegate::UpdateRates(
|
||||
const VideoBitrateAllocation& bitrate_allocation,
|
||||
uint32_t framerate) {
|
||||
@ -340,27 +416,11 @@ bool VP9VaapiVideoEncoderDelegate::UpdateRates(
|
||||
if (bitrate_allocation.GetSumBps() == 0 || framerate == 0)
|
||||
return false;
|
||||
|
||||
if (current_params_.bitrate_allocation == bitrate_allocation &&
|
||||
current_params_.framerate == framerate) {
|
||||
return true;
|
||||
pending_update_rates_ = std::make_pair(bitrate_allocation, framerate);
|
||||
if (current_params_.bitrate_allocation == pending_update_rates_->first &&
|
||||
current_params_.framerate == pending_update_rates_->second) {
|
||||
pending_update_rates_.reset();
|
||||
}
|
||||
VLOGF(2) << "New bitrate: " << bitrate_allocation.GetSumBps()
|
||||
<< ", New framerate: " << framerate;
|
||||
|
||||
current_params_.bitrate_allocation = bitrate_allocation;
|
||||
current_params_.framerate = framerate;
|
||||
|
||||
current_params_.cpb_size_bits =
|
||||
current_params_.bitrate_allocation.GetSumBps() *
|
||||
current_params_.cpb_window_size_ms / 1000;
|
||||
|
||||
if (!rate_ctrl_)
|
||||
return true;
|
||||
|
||||
const size_t num_temporal_layers =
|
||||
svc_layers_ ? svc_layers_->num_temporal_layers() : 1u;
|
||||
rate_ctrl_->UpdateRateControl(CreateRateControlConfig(
|
||||
visible_size_, current_params_, bitrate_allocation, num_temporal_layers));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,7 +452,7 @@ void VP9VaapiVideoEncoderDelegate::SetFrameHeader(
|
||||
|
||||
*picture->frame_hdr = GetDefaultFrameHeader(keyframe);
|
||||
if (svc_layers_) {
|
||||
// Reference frame settings for temporal layer stream.
|
||||
// Reference frame settings for k-SVC stream.
|
||||
svc_layers_->FillUsedRefFramesAndMetadata(picture, ref_frames_used);
|
||||
// Enable error resilient mode so that the syntax of a frame can be decoded
|
||||
// independently of previous frames.
|
||||
@ -424,12 +484,19 @@ void VP9VaapiVideoEncoderDelegate::SetFrameHeader(
|
||||
if (picture->metadata_for_encoding) {
|
||||
frame_params.temporal_layer_id =
|
||||
picture->metadata_for_encoding->temporal_idx;
|
||||
frame_params.spatial_layer_id = picture->metadata_for_encoding->spatial_idx;
|
||||
}
|
||||
rate_ctrl_->ComputeQP(frame_params);
|
||||
picture->frame_hdr->quant_params.base_q_idx = rate_ctrl_->GetQP();
|
||||
picture->frame_hdr->loop_filter.level = rate_ctrl_->GetLoopfilterLevel();
|
||||
DVLOGF(4) << "qp=" << rate_ctrl_->GetQP()
|
||||
<< ", filter_level=" << rate_ctrl_->GetLoopfilterLevel();
|
||||
DVLOGF(4) << "qp="
|
||||
<< static_cast<int>(picture->frame_hdr->quant_params.base_q_idx)
|
||||
<< ", filter_level="
|
||||
<< static_cast<int>(picture->frame_hdr->loop_filter.level)
|
||||
<< ", frame_params.temporal_layer_id:"
|
||||
<< frame_params.temporal_layer_id
|
||||
<< ", frame_params.spatial_layer_id:"
|
||||
<< frame_params.spatial_layer_id;
|
||||
}
|
||||
|
||||
void VP9VaapiVideoEncoderDelegate::UpdateReferenceFrames(
|
||||
|
@ -36,12 +36,6 @@ class VP9VaapiVideoEncoderDelegate : public VaapiVideoEncoderDelegate {
|
||||
// Framerate in FPS.
|
||||
uint32_t framerate;
|
||||
|
||||
// Bitrate window size in ms.
|
||||
unsigned int cpb_window_size_ms;
|
||||
|
||||
// Coded picture buffer size in bits.
|
||||
unsigned int cpb_size_bits;
|
||||
|
||||
// Quantization parameter. They are vp9 ac/dc indices and their ranges are
|
||||
// 0-255.
|
||||
uint8_t initial_qp;
|
||||
@ -73,6 +67,8 @@ class VP9VaapiVideoEncoderDelegate : public VaapiVideoEncoderDelegate {
|
||||
|
||||
void set_rate_ctrl_for_testing(std::unique_ptr<VP9RateControl> rate_ctrl);
|
||||
|
||||
bool ApplyPendingUpdateRates();
|
||||
|
||||
Vp9FrameHeader GetDefaultFrameHeader(const bool keyframe) const;
|
||||
void SetFrameHeader(bool keyframe,
|
||||
VP9Picture* picture,
|
||||
@ -105,6 +101,9 @@ class VP9VaapiVideoEncoderDelegate : public VaapiVideoEncoderDelegate {
|
||||
Vp9ReferenceFrameVector reference_frames_;
|
||||
std::unique_ptr<VP9SVCLayers> svc_layers_;
|
||||
|
||||
absl::optional<std::pair<VideoBitrateAllocation, uint32_t>>
|
||||
pending_update_rates_;
|
||||
|
||||
std::unique_ptr<VP9RateControl> rate_ctrl_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(VP9VaapiVideoEncoderDelegate);
|
||||
|
@ -34,6 +34,22 @@ namespace {
|
||||
|
||||
constexpr size_t kDefaultMaxNumRefFrames = kVp9NumRefsPerFrame;
|
||||
|
||||
constexpr double kSpatialLayersBitrateScaleFactors[][3] = {
|
||||
{1.00, 0.00, 0.00}, // For one spatial layer.
|
||||
{0.30, 0.70, 0.00}, // For two spatial layers.
|
||||
{0.07, 0.23, 0.70}, // For three spatial layers.
|
||||
};
|
||||
constexpr int kSpatialLayersResolutionScaleDenom[][3] = {
|
||||
{1, 0, 0}, // For one spatial layer.
|
||||
{2, 1, 0}, // For two spatial layers.
|
||||
{4, 2, 1}, // For three spatial layers.
|
||||
};
|
||||
constexpr double kTemporalLayersBitrateScaleFactors[][3] = {
|
||||
{1.00, 0.00, 0.00}, // For one temporal layer.
|
||||
{0.50, 0.50, 0.00}, // For two temporal layers.
|
||||
{0.25, 0.25, 0.50}, // For three temporal layers.
|
||||
};
|
||||
|
||||
VaapiVideoEncoderDelegate::Config kDefaultVaapiVideoEncoderDelegateConfig{
|
||||
kDefaultMaxNumRefFrames,
|
||||
VaapiVideoEncoderDelegate::BitrateControl::kConstantQuantizationParameter};
|
||||
@ -55,14 +71,34 @@ constexpr std::array<bool, kVp9NumRefsPerFrame> kRefFramesUsedForInterFrame = {
|
||||
true, true, true};
|
||||
|
||||
void GetTemporalLayer(bool keyframe,
|
||||
int index,
|
||||
int frame_num,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers,
|
||||
std::array<bool, kVp9NumRefsPerFrame>* ref_frames_used,
|
||||
absl::optional<uint8_t>* temporal_layer_id) {
|
||||
switch (num_temporal_layers) {
|
||||
case 1:
|
||||
*ref_frames_used =
|
||||
keyframe ? kRefFramesUsedForKeyFrame : kRefFramesUsedForInterFrame;
|
||||
if (num_spatial_layers > 1) {
|
||||
// K-SVC stream.
|
||||
if (keyframe) {
|
||||
*ref_frames_used = keyframe ? kRefFramesUsedForKeyFrame
|
||||
: kRefFramesUsedForInterFrame;
|
||||
return;
|
||||
}
|
||||
|
||||
*temporal_layer_id = 0;
|
||||
{
|
||||
constexpr std::tuple<uint8_t, std::array<bool, kVp9NumRefsPerFrame>>
|
||||
kOneTemporalLayersDescription[] = {{0, {true, false, false}}};
|
||||
const auto& layer_info = kOneTemporalLayersDescription
|
||||
[frame_num % base::size(kOneTemporalLayersDescription)];
|
||||
std::tie(*temporal_layer_id, *ref_frames_used) = layer_info;
|
||||
}
|
||||
} else {
|
||||
// Simple stream.
|
||||
*ref_frames_used =
|
||||
keyframe ? kRefFramesUsedForKeyFrame : kRefFramesUsedForInterFrame;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (keyframe) {
|
||||
@ -81,7 +117,7 @@ void GetTemporalLayer(bool keyframe,
|
||||
{0, {true, false, false}}, {1, {true, true, false}},
|
||||
};
|
||||
const auto& layer_info = kTwoTemporalLayersDescription
|
||||
[index % base::size(kTwoTemporalLayersDescription)];
|
||||
[frame_num % base::size(kTwoTemporalLayersDescription)];
|
||||
std::tie(*temporal_layer_id, *ref_frames_used) = layer_info;
|
||||
}
|
||||
break;
|
||||
@ -102,7 +138,7 @@ void GetTemporalLayer(bool keyframe,
|
||||
{1, {true, true, false}}, {2, {true, true, false}},
|
||||
};
|
||||
const auto& layer_info = kThreeTemporalLayersDescription
|
||||
[index % base::size(kThreeTemporalLayersDescription)];
|
||||
[frame_num % base::size(kThreeTemporalLayersDescription)];
|
||||
std::tie(*temporal_layer_id, *ref_frames_used) = layer_info;
|
||||
}
|
||||
break;
|
||||
@ -110,34 +146,37 @@ void GetTemporalLayer(bool keyframe,
|
||||
}
|
||||
|
||||
VideoBitrateAllocation GetDefaultVideoBitrateAllocation(
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers,
|
||||
uint32_t bitrate) {
|
||||
VideoBitrateAllocation bitrate_allocation;
|
||||
if (num_temporal_layers == 1u) {
|
||||
DCHECK_LE(num_spatial_layers, 3u);
|
||||
DCHECK_LE(num_temporal_layers, 3u);
|
||||
if (num_spatial_layers == 1u && num_temporal_layers == 1u) {
|
||||
bitrate_allocation.SetBitrate(0, 0, bitrate);
|
||||
return bitrate_allocation;
|
||||
}
|
||||
|
||||
LOG_ASSERT(num_temporal_layers <= VP9SVCLayers::kMaxSupportedTemporalLayers);
|
||||
constexpr double kTemporalLayersBitrateScaleFactors
|
||||
[][VP9SVCLayers::kMaxSupportedTemporalLayers] = {
|
||||
{0.50, 0.50, 0.00}, // For two temporal layers.
|
||||
{0.25, 0.25, 0.50}, // For three temporal layers.
|
||||
};
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
const double bitrate_factor =
|
||||
kSpatialLayersBitrateScaleFactors[num_spatial_layers - 1][sid];
|
||||
uint32_t sl_bitrate = bitrate * bitrate_factor;
|
||||
|
||||
for (size_t i = 0; i < num_temporal_layers; i++) {
|
||||
const double factor =
|
||||
kTemporalLayersBitrateScaleFactors[num_temporal_layers - 2][i];
|
||||
bitrate_allocation.SetBitrate(0 /* spatial_index */, i,
|
||||
base::checked_cast<int>(bitrate * factor));
|
||||
for (size_t tl_idx = 0; tl_idx < num_temporal_layers; ++tl_idx) {
|
||||
const double tl_factor =
|
||||
kTemporalLayersBitrateScaleFactors[num_temporal_layers - 1][tl_idx];
|
||||
bitrate_allocation.SetBitrate(
|
||||
sid, tl_idx, base::checked_cast<int>(sl_bitrate * tl_factor));
|
||||
}
|
||||
}
|
||||
return bitrate_allocation;
|
||||
}
|
||||
|
||||
MATCHER_P4(MatchRtcConfigWithRates,
|
||||
MATCHER_P5(MatchRtcConfigWithRates,
|
||||
size,
|
||||
bitrate_allocation,
|
||||
framerate,
|
||||
num_spatial_layers,
|
||||
num_temporal_layers,
|
||||
"") {
|
||||
if (arg.target_bandwidth != bitrate_allocation.GetSumBps() / 1000)
|
||||
@ -146,25 +185,39 @@ MATCHER_P4(MatchRtcConfigWithRates,
|
||||
if (arg.framerate != static_cast<double>(framerate))
|
||||
return false;
|
||||
|
||||
int bitrate_sum = 0;
|
||||
for (size_t i = 0; i < num_temporal_layers; i++) {
|
||||
bitrate_sum += bitrate_allocation.GetBitrateBps(0, i);
|
||||
if (arg.layer_target_bitrate[i] != bitrate_sum / 1000)
|
||||
return false;
|
||||
if (arg.ts_rate_decimator[i] != (1 << (num_temporal_layers - i - 1)))
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
int bitrate_sum = 0;
|
||||
for (size_t tid = 0; tid < num_temporal_layers; tid++) {
|
||||
size_t idx = sid * num_temporal_layers + tid;
|
||||
bitrate_sum += bitrate_allocation.GetBitrateBps(sid, tid);
|
||||
if (arg.layer_target_bitrate[idx] != bitrate_sum / 1000)
|
||||
return false;
|
||||
if (arg.ts_rate_decimator[tid] != (1 << (num_temporal_layers - tid - 1)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg.scaling_factor_num[sid] != 1 ||
|
||||
arg.scaling_factor_den[sid] !=
|
||||
kSpatialLayersResolutionScaleDenom[num_spatial_layers - 1][sid]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return arg.width == size.width() && arg.height == size.height() &&
|
||||
base::checked_cast<size_t>(arg.ss_number_layers) ==
|
||||
num_spatial_layers &&
|
||||
base::checked_cast<size_t>(arg.ts_number_layers) ==
|
||||
num_temporal_layers &&
|
||||
arg.ss_number_layers == 1 && arg.scaling_factor_num[0] == 1 &&
|
||||
arg.scaling_factor_den[0] == 1;
|
||||
num_temporal_layers;
|
||||
}
|
||||
|
||||
MATCHER_P2(MatchFrameParam, frame_type, temporal_layer_id, "") {
|
||||
MATCHER_P3(MatchFrameParam,
|
||||
frame_type,
|
||||
temporal_layer_id,
|
||||
spatial_layer_id,
|
||||
"") {
|
||||
return arg.frame_type == frame_type &&
|
||||
(!temporal_layer_id || arg.temporal_layer_id == *temporal_layer_id);
|
||||
(!temporal_layer_id || arg.temporal_layer_id == *temporal_layer_id) &&
|
||||
(!spatial_layer_id || arg.spatial_layer_id == *spatial_layer_id);
|
||||
}
|
||||
|
||||
class MockVaapiWrapper : public VaapiWrapper {
|
||||
@ -204,13 +257,17 @@ class VP9VaapiVideoEncoderDelegateTest
|
||||
|
||||
protected:
|
||||
void InitializeVP9VaapiVideoEncoderDelegate(BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers);
|
||||
void EncodeConstantQuantizationParameterSequence(
|
||||
bool is_keyframe,
|
||||
size_t num_spatial_layers,
|
||||
absl::optional<std::array<bool, kVp9NumRefsPerFrame>>
|
||||
expected_ref_frames_used,
|
||||
absl::optional<uint8_t> expected_temporal_layer_id = absl::nullopt);
|
||||
absl::optional<uint8_t> expected_temporal_layer_id = absl::nullopt,
|
||||
absl::optional<uint8_t> expected_spatial_layer_id = absl::nullopt);
|
||||
void UpdateRatesTest(BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers);
|
||||
|
||||
private:
|
||||
@ -221,6 +278,7 @@ class VP9VaapiVideoEncoderDelegateTest
|
||||
void UpdateRatesSequence(const VideoBitrateAllocation& bitrate_allocation,
|
||||
uint32_t framerate,
|
||||
BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers);
|
||||
|
||||
std::unique_ptr<VP9VaapiVideoEncoderDelegate> encoder_;
|
||||
@ -264,6 +322,7 @@ VP9VaapiVideoEncoderDelegateTest::CreateEncodeJob(
|
||||
|
||||
void VP9VaapiVideoEncoderDelegateTest::InitializeVP9VaapiVideoEncoderDelegate(
|
||||
BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers) {
|
||||
auto config = kDefaultVideoEncodeAcceleratorConfig;
|
||||
auto ave_config = kDefaultVaapiVideoEncoderDelegateConfig;
|
||||
@ -277,44 +336,63 @@ void VP9VaapiVideoEncoderDelegateTest::InitializeVP9VaapiVideoEncoderDelegate(
|
||||
VideoBitrateAllocation initial_bitrate_allocation;
|
||||
initial_bitrate_allocation.SetBitrate(
|
||||
0, 0, kDefaultVideoEncodeAcceleratorConfig.initial_bitrate);
|
||||
if (num_temporal_layers > 1u) {
|
||||
VideoEncodeAccelerator::Config::SpatialLayer spatial_layer;
|
||||
spatial_layer.width = config.input_visible_size.width();
|
||||
spatial_layer.height = config.input_visible_size.height();
|
||||
spatial_layer.bitrate_bps = config.initial_bitrate;
|
||||
spatial_layer.framerate = *config.initial_framerate;
|
||||
spatial_layer.max_qp = 30;
|
||||
spatial_layer.num_of_temporal_layers = num_temporal_layers;
|
||||
config.spatial_layers.push_back(spatial_layer);
|
||||
if (num_spatial_layers > 1u || num_temporal_layers > 1u) {
|
||||
DCHECK_GT(num_spatial_layers, 0u);
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
const double bitrate_factor =
|
||||
kSpatialLayersBitrateScaleFactors[num_spatial_layers - 1][sid];
|
||||
const double resolution_denom =
|
||||
kSpatialLayersResolutionScaleDenom[num_spatial_layers - 1][sid];
|
||||
VideoEncodeAccelerator::Config::SpatialLayer spatial_layer;
|
||||
spatial_layer.width =
|
||||
config.input_visible_size.width() / resolution_denom;
|
||||
spatial_layer.height =
|
||||
config.input_visible_size.height() / resolution_denom;
|
||||
spatial_layer.bitrate_bps = config.initial_bitrate * bitrate_factor;
|
||||
spatial_layer.framerate = *config.initial_framerate;
|
||||
spatial_layer.num_of_temporal_layers = num_temporal_layers;
|
||||
spatial_layer.max_qp = 30u;
|
||||
config.spatial_layers.push_back(spatial_layer);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_CALL(
|
||||
*mock_rate_ctrl_,
|
||||
UpdateRateControl(MatchRtcConfigWithRates(
|
||||
kDefaultVideoEncodeAcceleratorConfig.input_visible_size,
|
||||
GetDefaultVideoBitrateAllocation(num_temporal_layers,
|
||||
config.initial_bitrate),
|
||||
VideoEncodeAccelerator::kDefaultFramerate, num_temporal_layers)))
|
||||
GetDefaultVideoBitrateAllocation(
|
||||
num_spatial_layers, num_temporal_layers, config.initial_bitrate),
|
||||
VideoEncodeAccelerator::kDefaultFramerate, num_spatial_layers,
|
||||
num_temporal_layers)))
|
||||
.Times(1)
|
||||
.WillOnce(Return());
|
||||
|
||||
EXPECT_TRUE(encoder_->Initialize(config, ave_config));
|
||||
EXPECT_EQ(num_temporal_layers > 1u, !!encoder_->svc_layers_);
|
||||
EXPECT_EQ(num_temporal_layers > 1u || num_spatial_layers > 1u,
|
||||
!!encoder_->svc_layers_);
|
||||
}
|
||||
|
||||
void VP9VaapiVideoEncoderDelegateTest::
|
||||
EncodeConstantQuantizationParameterSequence(
|
||||
bool is_keyframe,
|
||||
size_t num_spatial_layers,
|
||||
absl::optional<std::array<bool, kVp9NumRefsPerFrame>>
|
||||
expected_ref_frames_used,
|
||||
absl::optional<uint8_t> expected_temporal_layer_id) {
|
||||
absl::optional<uint8_t> expected_temporal_layer_id,
|
||||
absl::optional<uint8_t> expected_spatial_layer_id) {
|
||||
InSequence seq;
|
||||
|
||||
constexpr VASurfaceID kDummyVASurfaceID = 123;
|
||||
const double resolution_denom =
|
||||
kSpatialLayersResolutionScaleDenom[num_spatial_layers - 1]
|
||||
[expected_spatial_layer_id.value_or(0)];
|
||||
gfx::Size layer_size = gfx::Size(
|
||||
kDefaultVideoEncodeAcceleratorConfig.input_visible_size.width() /
|
||||
resolution_denom,
|
||||
kDefaultVideoEncodeAcceleratorConfig.input_visible_size.height() /
|
||||
resolution_denom);
|
||||
auto va_surface = base::MakeRefCounted<VASurface>(
|
||||
kDummyVASurfaceID,
|
||||
kDefaultVideoEncodeAcceleratorConfig.input_visible_size,
|
||||
VA_RT_FORMAT_YUV420, base::DoNothing());
|
||||
kDummyVASurfaceID, layer_size, VA_RT_FORMAT_YUV420, base::DoNothing());
|
||||
scoped_refptr<VP9Picture> picture = new VaapiVP9Picture(va_surface);
|
||||
|
||||
auto encode_job = CreateEncodeJob(is_keyframe, va_surface, picture);
|
||||
@ -323,7 +401,8 @@ void VP9VaapiVideoEncoderDelegateTest::
|
||||
is_keyframe ? FRAME_TYPE::KEY_FRAME : FRAME_TYPE::INTER_FRAME;
|
||||
EXPECT_CALL(
|
||||
*mock_rate_ctrl_,
|
||||
ComputeQP(MatchFrameParam(libvpx_frame_type, expected_temporal_layer_id)))
|
||||
ComputeQP(MatchFrameParam(libvpx_frame_type, expected_temporal_layer_id,
|
||||
expected_spatial_layer_id)))
|
||||
.WillOnce(Return());
|
||||
constexpr int kDefaultQP = 34;
|
||||
constexpr int kDefaultLoopFilterLevel = 8;
|
||||
@ -348,38 +427,48 @@ void VP9VaapiVideoEncoderDelegateTest::UpdateRatesSequence(
|
||||
const VideoBitrateAllocation& bitrate_allocation,
|
||||
uint32_t framerate,
|
||||
BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers) {
|
||||
ASSERT_TRUE(encoder_->current_params_.bitrate_allocation !=
|
||||
bitrate_allocation ||
|
||||
encoder_->current_params_.framerate != framerate);
|
||||
|
||||
ASSERT_EQ(bitrate_control, BitrateControl::kConstantQuantizationParameter);
|
||||
EXPECT_CALL(*mock_rate_ctrl_, UpdateRateControl(MatchRtcConfigWithRates(
|
||||
encoder_->visible_size_, bitrate_allocation,
|
||||
framerate, num_temporal_layers)))
|
||||
EXPECT_CALL(*mock_rate_ctrl_,
|
||||
UpdateRateControl(MatchRtcConfigWithRates(
|
||||
encoder_->visible_size_, bitrate_allocation, framerate,
|
||||
num_spatial_layers, num_temporal_layers)))
|
||||
.Times(1)
|
||||
.WillOnce(Return());
|
||||
|
||||
EXPECT_TRUE(encoder_->UpdateRates(bitrate_allocation, framerate));
|
||||
EXPECT_EQ(encoder_->current_params_.bitrate_allocation, bitrate_allocation);
|
||||
EXPECT_EQ(encoder_->current_params_.framerate, framerate);
|
||||
}
|
||||
|
||||
void VP9VaapiVideoEncoderDelegateTest::UpdateRatesTest(
|
||||
BitrateControl bitrate_control,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers) {
|
||||
ASSERT_TRUE(num_temporal_layers <= VP9SVCLayers::kMaxSupportedTemporalLayers);
|
||||
const auto update_rates_and_encode =
|
||||
[this, bitrate_control, num_temporal_layers](
|
||||
bool is_keyframe, const VideoBitrateAllocation& bitrate_allocation,
|
||||
[this, bitrate_control, num_spatial_layers, num_temporal_layers](
|
||||
bool is_key_pic, const VideoBitrateAllocation& bitrate_allocation,
|
||||
uint32_t framerate) {
|
||||
UpdateRatesSequence(bitrate_allocation, framerate, bitrate_control,
|
||||
num_temporal_layers);
|
||||
num_spatial_layers, num_temporal_layers);
|
||||
ASSERT_EQ(bitrate_control,
|
||||
BitrateControl::kConstantQuantizationParameter);
|
||||
|
||||
EncodeConstantQuantizationParameterSequence(is_keyframe, {},
|
||||
absl::nullopt);
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
const bool is_keyframe = is_key_pic && sid == 0;
|
||||
EncodeConstantQuantizationParameterSequence(
|
||||
is_keyframe, num_spatial_layers, {}, absl::nullopt,
|
||||
absl::nullopt);
|
||||
// Check if a rate change request is applied because the request is
|
||||
// applied during PrepareEncodeJob().
|
||||
EXPECT_EQ(encoder_->current_params_.bitrate_allocation,
|
||||
bitrate_allocation);
|
||||
EXPECT_EQ(encoder_->current_params_.framerate, framerate);
|
||||
}
|
||||
};
|
||||
|
||||
const uint32_t kBitrate =
|
||||
@ -388,40 +477,69 @@ void VP9VaapiVideoEncoderDelegateTest::UpdateRatesTest(
|
||||
*kDefaultVideoEncodeAcceleratorConfig.initial_framerate;
|
||||
// Call UpdateRates before Encode.
|
||||
update_rates_and_encode(
|
||||
true, GetDefaultVideoBitrateAllocation(num_temporal_layers, kBitrate / 2),
|
||||
true,
|
||||
GetDefaultVideoBitrateAllocation(num_spatial_layers, num_temporal_layers,
|
||||
kBitrate / 2),
|
||||
kFramerate);
|
||||
// Bitrate change only.
|
||||
update_rates_and_encode(
|
||||
false, GetDefaultVideoBitrateAllocation(num_temporal_layers, kBitrate),
|
||||
false,
|
||||
GetDefaultVideoBitrateAllocation(num_spatial_layers, num_temporal_layers,
|
||||
kBitrate),
|
||||
kFramerate);
|
||||
// Framerate change only.
|
||||
update_rates_and_encode(
|
||||
false, GetDefaultVideoBitrateAllocation(num_temporal_layers, kBitrate),
|
||||
false,
|
||||
GetDefaultVideoBitrateAllocation(num_spatial_layers, num_temporal_layers,
|
||||
kBitrate),
|
||||
kFramerate + 2);
|
||||
// Bitrate + Frame changes.
|
||||
update_rates_and_encode(
|
||||
false,
|
||||
GetDefaultVideoBitrateAllocation(num_temporal_layers, kBitrate * 3 / 4),
|
||||
GetDefaultVideoBitrateAllocation(num_spatial_layers, num_temporal_layers,
|
||||
kBitrate * 3 / 4),
|
||||
kFramerate - 5);
|
||||
}
|
||||
|
||||
struct VP9VaapiVideoEncoderDelegateTestParam {
|
||||
VP9VaapiVideoEncoderDelegateTest::BitrateControl bitrate_control;
|
||||
size_t num_spatial_layers;
|
||||
size_t num_temporal_layers;
|
||||
} kTestCasesForVP9VaapiVideoEncoderDelegateTest[] = {
|
||||
// {VP9VaapiVideoEncoderDelegateTest::BitrateControl, num_spatial_layers,
|
||||
// num_temporal_layers}
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
1u},
|
||||
1u, 1u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
2u},
|
||||
1u, 2u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
3u},
|
||||
1u, 3u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
2u, 1u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
2u, 2u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
2u, 3u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
3u, 1u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
3u, 2u},
|
||||
{VP9VaapiVideoEncoderDelegateTest::BitrateControl::
|
||||
kConstantQuantizationParameter,
|
||||
3u, 3u},
|
||||
};
|
||||
|
||||
TEST_P(VP9VaapiVideoEncoderDelegateTest, Initialize) {
|
||||
InitializeVP9VaapiVideoEncoderDelegate(GetParam().bitrate_control,
|
||||
GetParam().num_spatial_layers,
|
||||
GetParam().num_temporal_layers);
|
||||
}
|
||||
|
||||
@ -430,18 +548,24 @@ TEST_P(VP9VaapiVideoEncoderDelegateTest, EncodeWithSoftwareBitrateControl) {
|
||||
if (bitrate_control != BitrateControl::kConstantQuantizationParameter)
|
||||
GTEST_SKIP() << "Test only for with software bitrate control";
|
||||
|
||||
const size_t num_spatial_layers = GetParam().num_spatial_layers;
|
||||
const size_t num_temporal_layers = GetParam().num_temporal_layers;
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_temporal_layers);
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_spatial_layers,
|
||||
num_temporal_layers);
|
||||
|
||||
constexpr size_t kEncodeFrames = 20;
|
||||
for (size_t i = 0; i < kEncodeFrames; i++) {
|
||||
const bool is_keyframe = i == 0;
|
||||
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used;
|
||||
absl::optional<uint8_t> temporal_layer_id;
|
||||
GetTemporalLayer(is_keyframe, i, num_temporal_layers, &ref_frames_used,
|
||||
&temporal_layer_id);
|
||||
EncodeConstantQuantizationParameterSequence(is_keyframe, ref_frames_used,
|
||||
temporal_layer_id);
|
||||
for (size_t frame_num = 0; frame_num < kEncodeFrames; ++frame_num) {
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
const bool is_keyframe = (frame_num == 0 && sid == 0);
|
||||
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used;
|
||||
absl::optional<uint8_t> temporal_layer_id;
|
||||
GetTemporalLayer(is_keyframe, frame_num, num_spatial_layers,
|
||||
num_temporal_layers, &ref_frames_used,
|
||||
&temporal_layer_id);
|
||||
EncodeConstantQuantizationParameterSequence(
|
||||
is_keyframe, num_spatial_layers, ref_frames_used, temporal_layer_id,
|
||||
sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,30 +575,40 @@ TEST_P(VP9VaapiVideoEncoderDelegateTest,
|
||||
if (bitrate_control != BitrateControl::kConstantQuantizationParameter)
|
||||
GTEST_SKIP() << "Test only for with software bitrate control";
|
||||
|
||||
const size_t num_spatial_layers = GetParam().num_spatial_layers;
|
||||
const size_t num_temporal_layers = GetParam().num_temporal_layers;
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_temporal_layers);
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_spatial_layers,
|
||||
num_temporal_layers);
|
||||
constexpr size_t kNumKeyFrames = 3;
|
||||
constexpr size_t kKeyFrameInterval = 20;
|
||||
for (size_t j = 0; j < kNumKeyFrames; j++) {
|
||||
for (size_t i = 0; i < kKeyFrameInterval; i++) {
|
||||
const bool is_keyframe = i == 0;
|
||||
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used;
|
||||
absl::optional<uint8_t> temporal_layer_id;
|
||||
GetTemporalLayer(is_keyframe, i, num_temporal_layers, &ref_frames_used,
|
||||
&temporal_layer_id);
|
||||
EncodeConstantQuantizationParameterSequence(is_keyframe, ref_frames_used,
|
||||
temporal_layer_id);
|
||||
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
|
||||
const bool keyframe = (i == 0 && sid == 0);
|
||||
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used;
|
||||
absl::optional<uint8_t> temporal_layer_id;
|
||||
GetTemporalLayer(keyframe, i, num_spatial_layers, num_temporal_layers,
|
||||
&ref_frames_used, &temporal_layer_id);
|
||||
EncodeConstantQuantizationParameterSequence(
|
||||
keyframe, num_spatial_layers, ref_frames_used, temporal_layer_id,
|
||||
sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(VP9VaapiVideoEncoderDelegateTest, UpdateRates) {
|
||||
const auto& bitrate_control = GetParam().bitrate_control;
|
||||
const size_t num_spatial_layers = GetParam().num_spatial_layers;
|
||||
const size_t num_temporal_layers = GetParam().num_temporal_layers;
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_temporal_layers);
|
||||
UpdateRatesTest(bitrate_control, num_temporal_layers);
|
||||
InitializeVP9VaapiVideoEncoderDelegate(bitrate_control, num_spatial_layers,
|
||||
num_temporal_layers);
|
||||
UpdateRatesTest(bitrate_control, num_spatial_layers, num_temporal_layers);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/1186051): Add the test case to activate and deactivate spatial
|
||||
// layers.
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
,
|
||||
VP9VaapiVideoEncoderDelegateTest,
|
||||
|
Reference in New Issue
Block a user