blob: fa0f8b53d3c3969e1976a5050d203cc585b69579 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/vaapi/vp9_encoder.h"
#include <memory>
#include <numeric>
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/optional.h"
#include "media/filters/vp9_parser.h"
#include "media/gpu/vaapi/vp9_rate_control.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libvpx/source/libvpx/vp9/common/vp9_blockd.h"
#include "third_party/libvpx/source/libvpx/vp9/ratectrl_rtc.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
namespace media {
namespace {
constexpr size_t kDefaultMaxNumRefFrames = kVp9NumRefsPerFrame;
AcceleratedVideoEncoder::Config kDefaultAcceleratedVideoEncoderConfig{
kDefaultMaxNumRefFrames,
AcceleratedVideoEncoder::BitrateControl::kConstantBitrate};
VideoEncodeAccelerator::Config kDefaultVideoEncodeAcceleratorConfig(
PIXEL_FORMAT_I420,
gfx::Size(1280, 720),
VP9PROFILE_PROFILE0,
14000000 /* = maximum bitrate in bits per second for level 3.1 */,
VideoEncodeAccelerator::kDefaultFramerate,
base::nullopt /* gop_length */,
base::nullopt /* h264 output level*/,
VideoEncodeAccelerator::Config::StorageType::kShmem);
const std::vector<bool> kRefFramesUsedForKeyFrame = {false, false, false};
const std::vector<bool> kRefFramesUsedForInterFrame = {true, true, true};
MATCHER_P4(MatchRtcConfigWithRates,
size,
bitrate_allocation,
framerate,
num_temporal_layers,
"") {
if (arg.target_bandwidth !=
static_cast<int64_t>(bitrate_allocation.GetSumBps() / 1000.0)) {
return false;
}
if (arg.framerate != static_cast<double>(framerate))
return false;
for (size_t i = 0; i < num_temporal_layers; i++) {
if (arg.layer_target_bitrate[i] !=
static_cast<int>(bitrate_allocation.GetBitrateBps(0, i) / 1000.0)) {
return false;
}
if (arg.ts_rate_decimator[i] != (1 << i))
return false;
}
return arg.width == size.width() && arg.height == size.height() &&
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;
}
MATCHER_P2(MatchFrameParam, frame_type, temporal_idx, "") {
return arg.frame_type == frame_type &&
(!temporal_idx || arg.temporal_layer_id == *temporal_idx);
}
class MockVP9Accelerator : public VP9Encoder::Accelerator {
public:
MockVP9Accelerator() = default;
~MockVP9Accelerator() override = default;
MOCK_METHOD1(GetPicture,
scoped_refptr<VP9Picture>(AcceleratedVideoEncoder::EncodeJob*));
MOCK_METHOD5(SubmitFrameParameters,
bool(AcceleratedVideoEncoder::EncodeJob*,
const VP9Encoder::EncodeParams&,
scoped_refptr<VP9Picture>,
const Vp9ReferenceFrameVector&,
const std::array<bool, kVp9NumRefsPerFrame>&));
};
class MockVP9RateControl : public VP9RateControl {
public:
MockVP9RateControl() = default;
~MockVP9RateControl() override = default;
MOCK_METHOD1(UpdateRateControl, void(const libvpx::VP9RateControlRtcConfig&));
MOCK_CONST_METHOD0(GetQP, int());
MOCK_CONST_METHOD0(GetLoopfilterLevel, int());
MOCK_METHOD1(ComputeQP, void(const libvpx::VP9FrameParamsQpRTC&));
MOCK_METHOD1(PostEncodeUpdate, void(uint64_t));
};
} // namespace
struct VP9EncoderTestParam;
class VP9EncoderTest : public ::testing::TestWithParam<VP9EncoderTestParam> {
public:
using BitrateControl = AcceleratedVideoEncoder::BitrateControl;
VP9EncoderTest() = default;
~VP9EncoderTest() override = default;
void SetUp() override;
protected:
using FrameType = Vp9FrameHeader::FrameType;
void InitializeVP9Encoder(BitrateControl bitrate_control);
void EncodeSequence(FrameType frame_type);
void EncodeConstantQuantizationParameterSequence(
FrameType frame_type,
const std::vector<bool>& expected_ref_frames_used,
base::Optional<uint8_t> expected_temporal_idx = base::nullopt);
void UpdateRatesTest(BitrateControl bitrate_control,
size_t num_temporal_layers);
private:
std::unique_ptr<AcceleratedVideoEncoder::EncodeJob> CreateEncodeJob(
bool keyframe);
void UpdateRatesSequence(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate,
BitrateControl bitrate_control);
std::unique_ptr<VP9Encoder> encoder_;
MockVP9Accelerator* mock_accelerator_ = nullptr;
MockVP9RateControl* mock_rate_ctrl_ = nullptr;
};
void VP9EncoderTest::SetUp() {
auto mock_accelerator = std::make_unique<MockVP9Accelerator>();
mock_accelerator_ = mock_accelerator.get();
auto rate_ctrl = std::make_unique<MockVP9RateControl>();
mock_rate_ctrl_ = rate_ctrl.get();
encoder_ = std::make_unique<VP9Encoder>(std::move(mock_accelerator));
encoder_->set_rate_ctrl_for_testing(std::move(rate_ctrl));
}
std::unique_ptr<AcceleratedVideoEncoder::EncodeJob>
VP9EncoderTest::CreateEncodeJob(bool keyframe) {
auto input_frame = VideoFrame::CreateFrame(
kDefaultVideoEncodeAcceleratorConfig.input_format,
kDefaultVideoEncodeAcceleratorConfig.input_visible_size,
gfx::Rect(kDefaultVideoEncodeAcceleratorConfig.input_visible_size),
kDefaultVideoEncodeAcceleratorConfig.input_visible_size,
base::TimeDelta());
LOG_ASSERT(input_frame) << " Failed to create VideoFrame";
return std::make_unique<AcceleratedVideoEncoder::EncodeJob>(
input_frame, keyframe, base::DoNothing());
}
void VP9EncoderTest::InitializeVP9Encoder(BitrateControl bitrate_control) {
auto ave_config = kDefaultAcceleratedVideoEncoderConfig;
ave_config.bitrate_control = bitrate_control;
if (bitrate_control == BitrateControl::kConstantQuantizationParameter) {
constexpr size_t kNumTemporalLayers = 1u;
VideoBitrateAllocation initial_bitrate_allocation;
initial_bitrate_allocation.SetBitrate(
0, 0, kDefaultVideoEncodeAcceleratorConfig.initial_bitrate);
EXPECT_CALL(
*mock_rate_ctrl_,
UpdateRateControl(MatchRtcConfigWithRates(
kDefaultVideoEncodeAcceleratorConfig.input_visible_size,
initial_bitrate_allocation,
VideoEncodeAccelerator::kDefaultFramerate, kNumTemporalLayers)))
.Times(1)
.WillOnce(Return());
}
EXPECT_TRUE(
encoder_->Initialize(kDefaultVideoEncodeAcceleratorConfig, ave_config));
}
void VP9EncoderTest::EncodeSequence(FrameType frame_type) {
InSequence seq;
const bool keyframe = frame_type == FrameType::KEYFRAME;
auto encode_job = CreateEncodeJob(keyframe);
scoped_refptr<VP9Picture> picture(new VP9Picture);
EXPECT_CALL(*mock_accelerator_, GetPicture(encode_job.get()))
.WillOnce(Invoke(
[picture](AcceleratedVideoEncoder::EncodeJob*) { return picture; }));
const auto& expected_ref_frames_used =
keyframe ? kRefFramesUsedForKeyFrame : kRefFramesUsedForInterFrame;
EXPECT_CALL(*mock_accelerator_,
SubmitFrameParameters(
encode_job.get(), _, _, _,
::testing::ElementsAreArray(expected_ref_frames_used)))
.WillOnce(Return(true));
EXPECT_TRUE(encoder_->PrepareEncodeJob(encode_job.get()));
// TODO(hiroh): Test for encoder_->reference_frames_.
}
void VP9EncoderTest::EncodeConstantQuantizationParameterSequence(
FrameType frame_type,
const std::vector<bool>& expected_ref_frames_used,
base::Optional<uint8_t> expected_temporal_idx) {
const bool keyframe = frame_type == FrameType::KEYFRAME;
InSequence seq;
auto encode_job = CreateEncodeJob(keyframe);
scoped_refptr<VP9Picture> picture(new VP9Picture);
EXPECT_CALL(*mock_accelerator_, GetPicture(encode_job.get()))
.WillOnce(Invoke(
[picture](AcceleratedVideoEncoder::EncodeJob*) { return picture; }));
FRAME_TYPE libvpx_frame_type =
keyframe ? FRAME_TYPE::KEY_FRAME : FRAME_TYPE::INTER_FRAME;
EXPECT_CALL(*mock_rate_ctrl_, ComputeQP(MatchFrameParam(
libvpx_frame_type, expected_temporal_idx)))
.WillOnce(Return());
constexpr int kDefaultQP = 34;
constexpr int kDefaultLoopFilterLevel = 8;
EXPECT_CALL(*mock_rate_ctrl_, GetQP()).WillOnce(Return(kDefaultQP));
EXPECT_CALL(*mock_rate_ctrl_, GetLoopfilterLevel())
.WillOnce(Return(kDefaultLoopFilterLevel));
if (!expected_ref_frames_used.empty()) {
EXPECT_CALL(*mock_accelerator_,
SubmitFrameParameters(
encode_job.get(), _, _, _,
::testing::ElementsAreArray(expected_ref_frames_used)))
.WillOnce(Return(true));
} else {
EXPECT_CALL(*mock_accelerator_,
SubmitFrameParameters(encode_job.get(), _, _, _, _))
.WillOnce(Return(true));
}
EXPECT_TRUE(encoder_->PrepareEncodeJob(encode_job.get()));
// TODO(hiroh): Test for encoder_->reference_frames_.
constexpr size_t kDefaultEncodedFrameSize = 123456;
// For BitrateControlUpdate sequence.
EXPECT_CALL(*mock_rate_ctrl_, PostEncodeUpdate(kDefaultEncodedFrameSize))
.WillOnce(Return());
encoder_->BitrateControlUpdate(kDefaultEncodedFrameSize);
}
void VP9EncoderTest::UpdateRatesSequence(
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate,
BitrateControl bitrate_control) {
ASSERT_TRUE(encoder_->current_params_.bitrate_allocation !=
bitrate_allocation ||
encoder_->current_params_.framerate != framerate);
if (bitrate_control == BitrateControl::kConstantQuantizationParameter) {
constexpr size_t kNumTemporalLayers = 1u;
EXPECT_CALL(*mock_rate_ctrl_,
UpdateRateControl(MatchRtcConfigWithRates(
encoder_->visible_size_, bitrate_allocation, framerate,
kNumTemporalLayers)))
.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 VP9EncoderTest::UpdateRatesTest(BitrateControl bitrate_control,
size_t num_temporal_layers) {
ASSERT_LE(num_temporal_layers, 3u);
auto create_allocation =
[num_temporal_layers](uint32_t bitrate) -> VideoBitrateAllocation {
VideoBitrateAllocation bitrate_allocation;
constexpr int kTemporalLayerBitrateScaleFactor[] = {1, 2, 4};
const int kScaleFactors =
std::accumulate(std::cbegin(kTemporalLayerBitrateScaleFactor),
std::cend(kTemporalLayerBitrateScaleFactor), 0);
for (size_t ti = 0; ti < num_temporal_layers; ti++) {
bitrate_allocation.SetBitrate(
0, ti,
bitrate * kTemporalLayerBitrateScaleFactor[ti] / kScaleFactors);
}
return bitrate_allocation;
};
const auto update_rates_and_encode =
[this, bitrate_control](FrameType frame_type,
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) {
UpdateRatesSequence(bitrate_allocation, framerate, bitrate_control);
if (bitrate_control == BitrateControl::kConstantQuantizationParameter) {
EncodeConstantQuantizationParameterSequence(frame_type, {},
base::nullopt);
} else {
EncodeSequence(frame_type);
}
};
const uint32_t kBitrate =
kDefaultVideoEncodeAcceleratorConfig.initial_bitrate;
const uint32_t kFramerate =
*kDefaultVideoEncodeAcceleratorConfig.initial_framerate;
// Call UpdateRates before Encode.
update_rates_and_encode(FrameType::KEYFRAME, create_allocation(kBitrate / 2),
kFramerate);
// Bitrate change only.
update_rates_and_encode(FrameType::INTERFRAME, create_allocation(kBitrate),
kFramerate);
// Framerate change only.
update_rates_and_encode(FrameType::INTERFRAME, create_allocation(kBitrate),
kFramerate + 2);
// Bitrate + Frame changes.
update_rates_and_encode(FrameType::INTERFRAME,
create_allocation(kBitrate * 3 / 4), kFramerate - 5);
}
struct VP9EncoderTestParam {
VP9EncoderTest::BitrateControl bitrate_control;
} kTestCasesForVP9EncoderTest[] = {
{VP9EncoderTest::BitrateControl::kConstantBitrate},
{VP9EncoderTest::BitrateControl::kConstantQuantizationParameter},
};
TEST_P(VP9EncoderTest, Initialize) {
InitializeVP9Encoder(GetParam().bitrate_control);
}
TEST_P(VP9EncoderTest, Encode) {
const auto& bitrate_control = GetParam().bitrate_control;
InitializeVP9Encoder(bitrate_control);
if (bitrate_control == BitrateControl::kConstantBitrate) {
EncodeSequence(FrameType::KEYFRAME);
EncodeSequence(FrameType::INTERFRAME);
} else {
EncodeConstantQuantizationParameterSequence(FrameType::KEYFRAME,
kRefFramesUsedForKeyFrame);
EncodeConstantQuantizationParameterSequence(FrameType::INTERFRAME,
kRefFramesUsedForInterFrame);
}
}
TEST_P(VP9EncoderTest, UpdateRates) {
const auto& bitrate_control = GetParam().bitrate_control;
InitializeVP9Encoder(bitrate_control);
constexpr size_t kNumTemporalLayers = 1u;
UpdateRatesTest(bitrate_control, kNumTemporalLayers);
}
TEST_P(VP9EncoderTest, ForceKeyFrame) {
const auto& bitrate_control = GetParam().bitrate_control;
InitializeVP9Encoder(GetParam().bitrate_control);
if (bitrate_control == BitrateControl::kConstantBitrate) {
EncodeSequence(FrameType::KEYFRAME);
EncodeSequence(FrameType::INTERFRAME);
EncodeSequence(FrameType::KEYFRAME);
EncodeSequence(FrameType::INTERFRAME);
} else {
EncodeConstantQuantizationParameterSequence(FrameType::KEYFRAME,
kRefFramesUsedForKeyFrame);
EncodeConstantQuantizationParameterSequence(FrameType::INTERFRAME,
kRefFramesUsedForInterFrame);
EncodeConstantQuantizationParameterSequence(FrameType::KEYFRAME,
kRefFramesUsedForKeyFrame);
EncodeConstantQuantizationParameterSequence(FrameType::INTERFRAME,
kRefFramesUsedForInterFrame);
}
}
INSTANTIATE_TEST_SUITE_P(,
VP9EncoderTest,
::testing::ValuesIn(kTestCasesForVP9EncoderTest));
} // namespace media