blob: 6179b108ab503ab0c381d931134c3233df094232 [file] [log] [blame] [edit]
/*
* Copyright (c) 2021, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 3-Clause Clear License
* and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
* License was not distributed with this source code in the LICENSE file, you
* can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. If the
* Alliance for Open Media Patent License 1.0 was not distributed with this
* source code in the PATENTS file, you can obtain it at
* aomedia.org/license/patent-license/.
*/
#include "config/aom_config.h"
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"
#include "av1/encoder/av1_quantize.h"
#include "test/y4m_video_source.h"
namespace {
// Level 6 of the default quantization matrices.
// clang-format off
constexpr uint8_t user_defined_qm_8x8[2][8 * 8] = {
{
/* Luma */
32, 31, 30, 32, 37, 44, 53, 65,
31, 28, 29, 31, 36, 44, 52, 62,
30, 29, 29, 35, 39, 45, 53, 63,
32, 31, 35, 37, 44, 51, 57, 66,
37, 36, 39, 44, 51, 57, 65, 74,
44, 44, 45, 51, 57, 65, 74, 85,
53, 52, 53, 57, 65, 74, 84, 97,
65, 62, 63, 66, 74, 85, 97, 112,
},
{
/* Chroma */
32, 30, 36, 40, 44, 47, 51, 53,
30, 32, 37, 40, 43, 45, 48, 49,
36, 37, 41, 43, 46, 47, 51, 52,
40, 40, 43, 45, 47, 49, 51, 53,
44, 43, 46, 47, 51, 52, 56, 58,
47, 45, 47, 49, 52, 53, 59, 62,
51, 48, 51, 51, 56, 59, 65, 71,
53, 49, 52, 53, 58, 62, 71, 79,
},
};
constexpr uint8_t user_defined_qm_8x4[2][8 * 4] = {
{
/* Luma */
32, 31, 31, 33, 38, 44, 54, 65,
30, 30, 32, 35, 40, 46, 54, 62,
38, 38, 41, 45, 53, 58, 67, 77,
57, 54, 56, 61, 68, 77, 89, 103,
},
{
/* Chroma */
32, 31, 37, 41, 45, 48, 52, 54,
37, 38, 42, 43, 46, 47, 51, 52,
45, 44, 47, 48, 52, 53, 57, 61,
51, 48, 51, 53, 57, 62, 70, 76,
},
};
constexpr uint8_t user_defined_qm_4x8[2][4 * 8] = {
{
/* Luma */
32, 30, 38, 57,
31, 30, 38, 54,
31, 32, 41, 56,
33, 35, 45, 61,
38, 40, 53, 68,
44, 46, 58, 77,
54, 54, 67, 89,
65, 62, 77, 103,
},
{
/* Chroma */
32, 37, 45, 51,
31, 38, 44, 48,
37, 42, 47, 51,
41, 43, 48, 53,
45, 46, 52, 57,
48, 47, 53, 62,
52, 51, 57, 70,
54, 52, 61, 76,
},
};
constexpr uint8_t flat_qm_8x8[8 * 8] = {
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
};
constexpr uint8_t flat_qm_8x4[8 * 4] = {
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
};
constexpr uint8_t flat_qm_4x8[4 * 8] = {
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
};
constexpr uint8_t jpeg_luma_qm_8x8[8 * 8] = {
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99,
};
constexpr uint8_t jpeg_chroma_qm_8x8[8 * 8] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
};
// This matrix is almost jpeg_chroma_qm_8x8 except that the (2, 2) coefficient
// is changed from 56 to 66.
constexpr uint8_t jpeg_chroma_qm_8x8_almost[8 * 8] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 66, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
};
// clang-format off
class QMTest
: public ::libaom_test::CodecTestWith2Params<libaom_test::TestMode, int>,
public ::libaom_test::EncoderTest {
protected:
QMTest() : EncoderTest(GET_PARAM(0)) {}
virtual ~QMTest() {}
virtual void SetUp() {
InitializeConfig();
SetMode(GET_PARAM(1));
set_cpu_used_ = GET_PARAM(2);
}
virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video,
::libaom_test::Encoder *encoder) {
if (video->frame() == 0) {
encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
encoder->Control(AV1E_SET_ENABLE_QM, 1);
encoder->Control(AV1E_SET_QM_MIN, qm_min_);
encoder->Control(AV1E_SET_QM_MAX, qm_max_);
if (user_defined_qmatrix_ == 1) {
ASSERT_EQ(qm_min_, qm_max_);
aom_user_defined_qm_t qm;
qm.level = qm_min_;
qm.num_planes = monochrome_ ? 1 : 3;
if (monochrome_) {
qm.qm_8x8[0] = user_defined_qm_8x8[0];
qm.qm_8x4[0] = user_defined_qm_8x4[0];
qm.qm_4x8[0] = user_defined_qm_4x8[0];
} else {
qm.qm_8x8[0] = user_defined_qm_8x8[0];
qm.qm_8x8[1] = user_defined_qm_8x8[1];
qm.qm_8x8[2] = user_defined_qm_8x8[1];
qm.qm_8x4[0] = user_defined_qm_8x4[0];
qm.qm_8x4[1] = user_defined_qm_8x4[1];
qm.qm_8x4[2] = user_defined_qm_8x4[1];
qm.qm_4x8[0] = user_defined_qm_4x8[0];
qm.qm_4x8[1] = user_defined_qm_4x8[1];
qm.qm_4x8[2] = user_defined_qm_4x8[1];
}
encoder->Control(AV1E_SET_USER_DEFINED_QMATRIX, &qm);
} else if (user_defined_qmatrix_ == 2) {
ASSERT_EQ(qm_min_, qm_max_);
aom_user_defined_qm_t qm;
qm.level = qm_min_;
qm.num_planes = monochrome_ ? 1 : 3;
if (monochrome_) {
qm.qm_8x8[0] = flat_qm_8x8;
qm.qm_8x4[0] = flat_qm_8x4;
qm.qm_4x8[0] = flat_qm_4x8;
} else {
qm.qm_8x8[0] = flat_qm_8x8;
qm.qm_8x8[1] = flat_qm_8x8;
qm.qm_8x8[2] = flat_qm_8x8;
qm.qm_8x4[0] = flat_qm_8x4;
qm.qm_8x4[1] = flat_qm_8x4;
qm.qm_8x4[2] = flat_qm_8x4;
qm.qm_4x8[0] = flat_qm_4x8;
qm.qm_4x8[1] = flat_qm_4x8;
qm.qm_4x8[2] = flat_qm_4x8;
}
encoder->Control(AV1E_SET_USER_DEFINED_QMATRIX, &qm);
} else if (user_defined_qmatrix_ == 3) {
ASSERT_EQ(qm_min_, qm_max_);
aom_user_defined_qm_t qm;
qm.level = qm_min_;
qm.num_planes = monochrome_ ? 1 : 3;
if (monochrome_) {
qm.qm_8x8[0] = jpeg_luma_qm_8x8;
qm.qm_8x4[0] = flat_qm_8x4;
qm.qm_4x8[0] = flat_qm_4x8;
} else {
qm.qm_8x8[0] = jpeg_luma_qm_8x8;
qm.qm_8x8[1] = jpeg_chroma_qm_8x8;
qm.qm_8x8[2] = jpeg_chroma_qm_8x8;
qm.qm_8x4[0] = flat_qm_8x4;
qm.qm_8x4[1] = flat_qm_8x4;
qm.qm_8x4[2] = flat_qm_8x4;
qm.qm_4x8[0] = flat_qm_4x8;
qm.qm_4x8[1] = flat_qm_4x8;
qm.qm_4x8[2] = flat_qm_4x8;
}
encoder->Control(AV1E_SET_USER_DEFINED_QMATRIX, &qm);
} else if (user_defined_qmatrix_ == 4) {
ASSERT_EQ(qm_min_, qm_max_);
aom_user_defined_qm_t qm;
qm.level = qm_min_;
ASSERT_FALSE(monochrome_);
qm.num_planes = 3;
qm.qm_8x8[0] = jpeg_luma_qm_8x8;
qm.qm_8x8[1] = jpeg_chroma_qm_8x8_almost;
qm.qm_8x8[2] = jpeg_chroma_qm_8x8_almost;
qm.qm_8x4[0] = flat_qm_8x4;
qm.qm_8x4[1] = flat_qm_8x4;
qm.qm_8x4[2] = flat_qm_8x4;
qm.qm_4x8[0] = flat_qm_4x8;
qm.qm_4x8[1] = flat_qm_4x8;
qm.qm_4x8[2] = flat_qm_4x8;
encoder->Control(AV1E_SET_USER_DEFINED_QMATRIX, &qm);
}
encoder->Control(AOME_SET_MAX_INTRA_BITRATE_PCT, 100);
}
}
void DoTest(int qm_min, int qm_max) {
qm_min_ = qm_min;
qm_max_ = qm_max;
if (monochrome_) {
cfg_.monochrome = 1;
}
cfg_.kf_max_dist = 12;
cfg_.rc_min_quantizer = 32;
cfg_.rc_max_quantizer = 224;
cfg_.rc_end_usage = AOM_CBR;
cfg_.g_lag_in_frames = 6;
cfg_.rc_buf_initial_sz = 500;
cfg_.rc_buf_optimal_sz = 500;
cfg_.rc_buf_sz = 1000;
cfg_.rc_target_bitrate = 300;
::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
288, 30, 1, 0, 15);
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
}
bool monochrome_ = false;
int set_cpu_used_;
int qm_min_;
int qm_max_;
int user_defined_qmatrix_ = 0;
};
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM1) { DoTest(5, 9); }
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM2) { DoTest(0, 8); }
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM3) { DoTest(9, 15); }
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM4) {
monochrome_ = true;
DoTest(5, 9);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM5) {
user_defined_qmatrix_ = 1;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM6) {
monochrome_ = true;
user_defined_qmatrix_ = 1;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM7) {
user_defined_qmatrix_ = 2;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM8) {
monochrome_ = true;
user_defined_qmatrix_ = 2;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM9) {
user_defined_qmatrix_ = 3;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM10) {
monochrome_ = true;
user_defined_qmatrix_ = 3;
DoTest(0, 0);
}
// encodes and decodes without a mismatch.
TEST_P(QMTest, TestNoMisMatchQM11) {
user_defined_qmatrix_ = 4;
DoTest(0, 0);
}
AV1_INSTANTIATE_TEST_SUITE(QMTest,
::testing::Values(::libaom_test::kOnePassGood),
::testing::Range(5, 9));
typedef struct {
const unsigned int min_q;
const unsigned int max_q;
} QuantParam;
const QuantParam QuantTestParams[] = {
{ 0, 50 }, { 0, 240 }, { 100, 150 }, { 150, 200 }, { 200, 255 }
};
std::ostream &operator<<(std::ostream &os, const QuantParam &test_arg) {
return os << "QuantParam { min_q:" << test_arg.min_q
<< " max_q:" << test_arg.max_q << " }";
}
/*
* This class is used to test whether base_qindex is within min
* and max quantizer range configured by user.
*/
class QuantizerBoundsCheckTestLarge
: public ::libaom_test::CodecTestWith3Params<libaom_test::TestMode,
QuantParam, aom_rc_mode>,
public ::libaom_test::EncoderTest {
protected:
QuantizerBoundsCheckTestLarge()
: EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
quant_param_(GET_PARAM(2)), rc_end_usage_(GET_PARAM(3)) {
quant_bound_violated_ = false;
}
virtual ~QuantizerBoundsCheckTestLarge() {}
virtual void SetUp() {
InitializeConfig();
SetMode(encoding_mode_);
const aom_rational timebase = { 1, 30 };
cfg_.g_timebase = timebase;
cfg_.rc_end_usage = rc_end_usage_;
cfg_.g_threads = 1;
cfg_.rc_min_quantizer = quant_param_.min_q;
cfg_.rc_max_quantizer = quant_param_.max_q;
cfg_.g_lag_in_frames = 35;
if (rc_end_usage_ != AOM_Q) {
cfg_.rc_target_bitrate = 400;
}
}
virtual bool DoDecode() const { return 1; }
virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video,
::libaom_test::Encoder *encoder) {
if (video->frame() == 0) {
encoder->Control(AOME_SET_CPUUSED, 5);
}
}
virtual bool HandleDecodeResult(const aom_codec_err_t res_dec,
libaom_test::Decoder *decoder) {
EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError();
if (AOM_CODEC_OK == res_dec) {
aom_codec_ctx_t *ctx_dec = decoder->GetDecoder();
AOM_CODEC_CONTROL_TYPECHECKED(ctx_dec, AOMD_GET_LAST_QUANTIZER,
&base_qindex_);
min_bound_qindex_ = cfg_.rc_min_quantizer;
max_bound_qindex_ = cfg_.rc_max_quantizer;
if ((base_qindex_ < min_bound_qindex_ ||
base_qindex_ > max_bound_qindex_) &&
quant_bound_violated_ == false) {
quant_bound_violated_ = true;
}
}
return AOM_CODEC_OK == res_dec;
}
::libaom_test::TestMode encoding_mode_;
const QuantParam quant_param_;
int base_qindex_;
int min_bound_qindex_;
int max_bound_qindex_;
bool quant_bound_violated_;
aom_rc_mode rc_end_usage_;
};
TEST_P(QuantizerBoundsCheckTestLarge, QuantizerBoundsCheckEncodeTest) {
libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
cfg_.g_timebase.den, cfg_.g_timebase.num,
0, 50);
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
ASSERT_EQ(quant_bound_violated_, false);
}
AV1_INSTANTIATE_TEST_SUITE(QuantizerBoundsCheckTestLarge,
GOODQUALITY_TEST_MODES,
::testing::ValuesIn(QuantTestParams),
::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
} // namespace