Add range checks for max_ref_frames and max_depth.
Also:
- Added RateControlQModeTest fixture
- Added tests for each field of RateControlParam being invalid
- Rewrote GetGopEncodeInfoRefFrameMissingBlockStats to be clearer
Bug: b/240412144
Change-Id: I91e6c2ea6de4a205d29eb154e6603660e0917913
diff --git a/av1/ratectrl_qmode.cc b/av1/ratectrl_qmode.cc
index 0d9b43e..8edefd7 100644
--- a/av1/ratectrl_qmode.cc
+++ b/av1/ratectrl_qmode.cc
@@ -244,6 +244,16 @@
<< ") must be in the range [1, 8].";
return { AOM_CODEC_INVALID_PARAM, error_message.str() };
}
+ if (rc_param.max_ref_frames < 1 || rc_param.max_ref_frames > 7) {
+ error_message << "max_ref_frames (" << rc_param.max_ref_frames
+ << ") must be in the range [1, 7].";
+ return { AOM_CODEC_INVALID_PARAM, error_message.str() };
+ }
+ if (rc_param.max_depth < 1 || rc_param.max_depth > 5) {
+ error_message << "max_depth (" << rc_param.max_depth
+ << ") must be in the range [1, 5].";
+ return { AOM_CODEC_INVALID_PARAM, error_message.str() };
+ }
if (rc_param.base_q_index < 0 || rc_param.base_q_index > 255) {
error_message << "base_q_index (" << rc_param.base_q_index
<< ") must be in the range [0, 255].";
diff --git a/test/ratectrl_qmode_test.cc b/test/ratectrl_qmode_test.cc
index 60a230f..726099b 100644
--- a/test/ratectrl_qmode_test.cc
+++ b/test/ratectrl_qmode_test.cc
@@ -32,6 +32,8 @@
using ::testing::HasSubstr;
constexpr int kRefFrameTableSize = 7;
+constexpr int kFrameWidth = 352;
+constexpr int kFrameHeight = 288;
MATCHER(IsOkStatus, "") {
*result_listener << "with code " << arg.code
@@ -101,7 +103,8 @@
std::ifstream firstpass_stats_file(path);
ASSERT_TRUE(firstpass_stats_file.good())
<< "Error opening " << path << ": " << std::strerror(errno);
- firstpass_info->num_mbs_16x16 = (352 / 16 + 1) * (288 / 16 + 1);
+ firstpass_info->num_mbs_16x16 =
+ (kFrameWidth / 16 + 1) * (kFrameHeight / 16 + 1);
std::string newline;
while (std::getline(firstpass_stats_file, newline)) {
std::istringstream iss(newline);
@@ -148,8 +151,8 @@
std::ifstream tpl_stats_file(path);
ASSERT_TRUE(tpl_stats_file.good())
<< "Error opening " << path << ": " << std::strerror(errno);
- const int mi_rows = 352 / 16;
- const int mi_cols = 288 / 16;
+ const int mi_rows = kFrameHeight / 16;
+ const int mi_cols = kFrameWidth / 16;
for (auto &gop_struct : gop_list) {
int gop_interval = gop_struct.show_frame_count;
// One GOP in this test only has 2 frames, and TPL stats are not generated
@@ -158,8 +161,8 @@
aom::TplGopStats tpl_stats = {};
for (int frame_idx = 0; frame_idx < gop_interval; frame_idx++) {
aom::TplFrameStats tpl_frame_stats = {};
- tpl_frame_stats.frame_height = 288;
- tpl_frame_stats.frame_width = 352;
+ tpl_frame_stats.frame_height = kFrameHeight;
+ tpl_frame_stats.frame_width = kFrameWidth;
tpl_frame_stats.min_block_size = 4;
int display_index;
for (int mi_row = 0; mi_row < mi_rows; mi_row++) {
@@ -297,7 +300,23 @@
}
}
-TEST(RateControlQModeTest, ConstructGopARF) {
+class RateControlQModeTest : public ::testing::Test {
+ protected:
+ RateControlQModeTest() {
+ rc_param_.max_gop_show_frame_count = 32;
+ rc_param_.min_gop_show_frame_count = 4;
+ rc_param_.ref_frame_table_size = 7;
+ rc_param_.max_ref_frames = 7;
+ rc_param_.max_depth = 5;
+ rc_param_.base_q_index = 128;
+ rc_param_.frame_height = kFrameHeight;
+ rc_param_.frame_width = kFrameWidth;
+ }
+
+ RateControlParam rc_param_;
+};
+
+TEST_F(RateControlQModeTest, ConstructGopARF) {
int show_frame_count = 16;
const bool has_key_frame = false;
const int global_coding_idx_offset = 5;
@@ -317,7 +336,7 @@
TestArfInterval(gop_struct);
}
-TEST(RateControlQModeTest, ConstructGopKey) {
+TEST_F(RateControlQModeTest, ConstructGopKey) {
const int show_frame_count = 16;
const bool has_key_frame = true;
const int global_coding_idx_offset = 10;
@@ -337,7 +356,7 @@
TestArfInterval(gop_struct);
}
-TEST(RateControlQModeTest, ConstructShortGop) {
+TEST_F(RateControlQModeTest, ConstructShortGop) {
int show_frame_count = 2;
const bool has_key_frame = false;
const int global_coding_idx_offset = 5;
@@ -437,7 +456,7 @@
return sum;
}
-TEST(RateControlQModeTest, CreateTplFrameDepStats) {
+TEST_F(RateControlQModeTest, CreateTplFrameDepStats) {
TplFrameStats frame_stats = CreateToyTplFrameStatsWithDiffSizes(8, 16);
StatusOr<TplFrameDepStats> frame_dep_stats =
CreateTplFrameDepStatsWithoutPropagation(frame_stats);
@@ -455,7 +474,7 @@
EXPECT_NEAR(intra_cost_sum, expected_intra_cost_sum, kErrorEpsilon);
}
-TEST(RateControlQModeTest, BlockRowNotAMultipleOfMinBlockSizeError) {
+TEST_F(RateControlQModeTest, BlockRowNotAMultipleOfMinBlockSizeError) {
TplFrameStats frame_stats = CreateToyTplFrameStatsWithDiffSizes(8, 16);
frame_stats.block_stats_list.back().row = 1;
auto result = CreateTplFrameDepStatsWithoutPropagation(frame_stats);
@@ -463,7 +482,7 @@
EXPECT_THAT(result.status().message, HasSubstr("must be a multiple of 8"));
}
-TEST(RateControlQModeTest, BlockPositionOutOfRangeError) {
+TEST_F(RateControlQModeTest, BlockPositionOutOfRangeError) {
TplFrameStats frame_stats = CreateToyTplFrameStatsWithDiffSizes(8, 16);
frame_stats.block_stats_list.back().row += 8;
auto result = CreateTplFrameDepStatsWithoutPropagation(frame_stats);
@@ -471,7 +490,7 @@
EXPECT_THAT(result.status().message, HasSubstr("out of range"));
}
-TEST(RateControlQModeTest, GetBlockOverlapArea) {
+TEST_F(RateControlQModeTest, GetBlockOverlapArea) {
const int size = 8;
const int r0 = 8;
const int c0 = 9;
@@ -486,7 +505,7 @@
}
}
-TEST(RateControlQModeTest, TplBlockStatsToDepStats) {
+TEST_F(RateControlQModeTest, TplBlockStatsToDepStats) {
const int intra_cost = 100;
const int inter_cost = 120;
const int unit_count = 2;
@@ -500,7 +519,7 @@
EXPECT_LE(unit_stats.inter_cost, unit_stats.intra_cost);
}
-TEST(RateControlQModeTest, TplFrameDepStatsPropagateSingleZeroMotion) {
+TEST_F(RateControlQModeTest, TplFrameDepStatsPropagateSingleZeroMotion) {
// cur frame with coding_idx 1 use ref frame with coding_idx 0
const std::array<int, kBlockRefCount> ref_frame_index = { 0, -1 };
TplFrameStats frame_stats = CreateToyTplFrameStatsWithDiffSizes(8, 16);
@@ -537,7 +556,7 @@
EXPECT_NEAR(propagation_sum, expected_propagation_sum, kErrorEpsilon);
}
-TEST(RateControlQModeTest, TplFrameDepStatsPropagateCompoundZeroMotion) {
+TEST_F(RateControlQModeTest, TplFrameDepStatsPropagateCompoundZeroMotion) {
// cur frame with coding_idx 2 use two ref frames with coding_idx 0 and 1
const std::array<int, kBlockRefCount> ref_frame_index = { 0, 1 };
TplFrameStats frame_stats = CreateToyTplFrameStatsWithDiffSizes(8, 16);
@@ -580,7 +599,7 @@
EXPECT_NEAR(cost_sum1, expected_ref_sum * 0.5, kErrorEpsilon);
}
-TEST(RateControlQModeTest, TplFrameDepStatsPropagateSingleWithMotion) {
+TEST_F(RateControlQModeTest, TplFrameDepStatsPropagateSingleWithMotion) {
// cur frame with coding_idx 1 use ref frame with coding_idx 0
const std::array<int, kBlockRefCount> ref_frame_index = { 0, -1 };
const int min_block_size = 8;
@@ -640,7 +659,7 @@
}
}
-TEST(RateControlQModeTest, ComputeTplGopDepStats) {
+TEST_F(RateControlQModeTest, ComputeTplGopDepStats) {
TplGopStats tpl_gop_stats;
std::vector<RefFrameTable> ref_frame_table_list;
GopStruct gop_struct;
@@ -893,7 +912,7 @@
}
}
-TEST(RateControlQModeTest, TestKeyframeDetection) {
+TEST_F(RateControlQModeTest, TestKeyframeDetection) {
FirstpassInfo firstpass_info;
const std::string kFirstpassStatsFile = "firstpass_stats";
ASSERT_NO_FATAL_FAILURE(
@@ -934,27 +953,78 @@
GopFrame GopFrameUpdateRefIdx(int index, GopFrameType gop_frame_type,
int update_ref_idx) {
GopFrame frame =
- GopFrameBasic(index, index, index, index, /*depth=*/0, 0, gop_frame_type);
+ GopFrameBasic(0, 0, index, index, /*depth=*/0, 0, gop_frame_type);
frame.update_ref_idx = update_ref_idx;
return frame;
}
-TEST(RateControlQModeTest, TestSetRcParamErrorChecking) {
+TEST_F(RateControlQModeTest, TestInvalidRateControlParam) {
// Default constructed RateControlParam should not be valid.
RateControlParam rc_param = {};
EXPECT_NE(AV1RateControlQMode().SetRcParam(rc_param).code, AOM_CODEC_OK);
}
-TEST(RateControlQModeTest, TestGetRefFrameTableListFirstGop) {
+TEST_F(RateControlQModeTest, TestInvalidMaxGopShowFrameCount) {
+ rc_param_.min_gop_show_frame_count = 2;
+ rc_param_.max_gop_show_frame_count = 3;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("max_gop_show_frame_count (3) must be at least 4"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidMinGopShowFrameCount) {
+ rc_param_.min_gop_show_frame_count = 9;
+ rc_param_.max_gop_show_frame_count = 8;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("may not be less than min_gop_show_frame_count (9)"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidRefFrameTableSize) {
+ rc_param_.ref_frame_table_size = 9;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("ref_frame_table_size (9) must be in the range"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidMaxRefFrames) {
+ rc_param_.max_ref_frames = 8;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("max_ref_frames (8) must be in the range"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidMaxDepth) {
+ rc_param_.max_depth = 6;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message, HasSubstr("max_depth (6) must be in the range"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidBaseQIndex) {
+ rc_param_.base_q_index = 256;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("base_q_index (256) must be in the range"));
+}
+
+TEST_F(RateControlQModeTest, TestInvalidFrameHeight) {
+ rc_param_.frame_height = 15;
+ Status status = AV1RateControlQMode().SetRcParam(rc_param_);
+ EXPECT_EQ(status.code, AOM_CODEC_INVALID_PARAM);
+ EXPECT_THAT(status.message,
+ HasSubstr("frame_height (15) must be in the range"));
+}
+
+TEST_F(RateControlQModeTest, TestGetRefFrameTableListFirstGop) {
AV1RateControlQMode rc;
- RateControlParam rc_param = {};
- rc_param.max_gop_show_frame_count = 8;
- rc_param.ref_frame_table_size = 3;
- rc_param.max_ref_frames = 3;
- rc_param.max_depth = 3;
- rc_param.frame_width = 128;
- rc_param.frame_height = 128;
- ASSERT_THAT(rc.SetRcParam(rc_param), IsOkStatus());
+ rc_param_.ref_frame_table_size = 3;
+ ASSERT_THAT(rc.SetRcParam(rc_param_), IsOkStatus());
const auto invalid = GopFrameInvalid();
const auto frame0 = GopFrameUpdateRefIdx(0, GopFrameType::kRegularKey, -1);
@@ -981,16 +1051,10 @@
ElementsAre(matches_frame2, matches_frame0, matches_frame1)));
}
-TEST(RateControlQModeTest, TestGetRefFrameTableListNotFirstGop) {
+TEST_F(RateControlQModeTest, TestGetRefFrameTableListNotFirstGop) {
AV1RateControlQMode rc;
- RateControlParam rc_param = {};
- rc_param.max_gop_show_frame_count = 8;
- rc_param.ref_frame_table_size = 3;
- rc_param.max_ref_frames = 3;
- rc_param.max_depth = 3;
- rc_param.frame_height = 128;
- rc_param.frame_width = 128;
- ASSERT_THAT(rc.SetRcParam(rc_param), IsOkStatus());
+ rc_param_.ref_frame_table_size = 3;
+ ASSERT_THAT(rc.SetRcParam(rc_param_), IsOkStatus());
const auto previous = GopFrameUpdateRefIdx(0, GopFrameType::kRegularKey, -1);
const auto frame0 = GopFrameUpdateRefIdx(5, GopFrameType::kRegularLeaf, 2);
@@ -1017,20 +1081,12 @@
ElementsAre(matches_frame2, matches_previous, matches_frame0)));
}
-TEST(RateControlQModeTest, TestGopIntervals) {
+TEST_F(RateControlQModeTest, TestGopIntervals) {
FirstpassInfo firstpass_info;
ASSERT_NO_FATAL_FAILURE(
ReadFirstpassInfo("firstpass_stats", &firstpass_info));
AV1RateControlQMode rc;
- RateControlParam rc_param = {};
- rc_param.frame_height = 288;
- rc_param.frame_width = 352;
- rc_param.max_gop_show_frame_count = 32;
- rc_param.min_gop_show_frame_count = 4;
- rc_param.ref_frame_table_size = 7;
- rc_param.max_ref_frames = 7;
- rc_param.max_depth = 5;
- ASSERT_THAT(rc.SetRcParam(rc_param), IsOkStatus());
+ ASSERT_THAT(rc.SetRcParam(rc_param_), IsOkStatus());
const auto gop_info = rc.DetermineGopInfo(firstpass_info);
ASSERT_THAT(gop_info.status(), IsOkStatus());
@@ -1046,22 +1102,13 @@
// consistent with each other. With the existing files, the GOP structure
// resulting from the first pass stats doesn't match the TPL stats, resulting
// in an error from ValidateTplStats.
-TEST(RateControlQModeTest, DISABLED_TestGetGopEncodeInfo) {
+TEST_F(RateControlQModeTest, DISABLED_TestGetGopEncodeInfo) {
FirstpassInfo firstpass_info;
ASSERT_NO_FATAL_FAILURE(
ReadFirstpassInfo("firstpass_stats", &firstpass_info));
AV1RateControlQMode rc;
- RateControlParam rc_param = {};
- rc_param.frame_height = 288;
- rc_param.frame_width = 352;
- rc_param.max_gop_show_frame_count = 16;
- rc_param.min_gop_show_frame_count = 4;
- rc_param.ref_frame_table_size = 7;
- rc_param.max_ref_frames = 7;
- rc_param.max_depth = 5;
- // Just an arbitrary base qp for the test.
- rc_param.base_q_index = 128;
- ASSERT_THAT(rc.SetRcParam(rc_param), IsOkStatus());
+ rc_param_.max_gop_show_frame_count = 16;
+ ASSERT_THAT(rc.SetRcParam(rc_param_), IsOkStatus());
const auto gop_info = rc.DetermineGopInfo(firstpass_info);
ASSERT_THAT(gop_info.status(), IsOkStatus());
const GopStructList &gop_list = *gop_info;
@@ -1087,7 +1134,7 @@
}
}
-TEST(RateControlQModeTest, GetGopEncodeInfoWrongGopSize) {
+TEST_F(RateControlQModeTest, GetGopEncodeInfoWrongGopSize) {
GopStruct gop_struct;
gop_struct.gop_frame_list.assign(7, GopFrameInvalid());
TplGopStats tpl_gop_stats;
@@ -1102,18 +1149,19 @@
"count of TPL stats (5)"));
}
-TEST(RateControlQModeTest, GetGopEncodeInfoRefFrameMissingBlockStats) {
+TEST_F(RateControlQModeTest, GetGopEncodeInfoRefFrameMissingBlockStats) {
GopStruct gop_struct;
- for (int i = 0; i < 4; ++i) {
- gop_struct.gop_frame_list.push_back(
- GopFrameBasic(0, 0, i, i, 0, i, GopFrameType::kRegularLeaf));
- }
- // Only frame 2 is a reference frame.
- gop_struct.gop_frame_list[2].update_ref_idx = 1;
+ // Frames 0 and 2 are reference frames.
+ gop_struct.gop_frame_list = {
+ GopFrameUpdateRefIdx(0, GopFrameType::kRegularKey, 1),
+ GopFrameUpdateRefIdx(1, GopFrameType::kRegularLeaf, -1),
+ GopFrameUpdateRefIdx(2, GopFrameType::kRegularLeaf, 2),
+ };
- // None of the frames have TPL block stats.
+ // Only frame 0 has TPL block stats.
TplGopStats tpl_gop_stats;
- tpl_gop_stats.frame_stats_list.assign(4, { 8, 176, 144, {} });
+ tpl_gop_stats.frame_stats_list.assign(3, { 8, 176, 144, {} });
+ tpl_gop_stats.frame_stats_list[0] = CreateToyTplFrameStatsWithDiffSizes(8, 8);
AV1RateControlQMode rc;
const Status status =
@@ -1128,7 +1176,7 @@
// not expected that it will be used in any real libaom tests.
// This simple "toy" test exists solely to verify the integration of gmock into
// the aom build.
-TEST(RateControlQModeTest, TestMock) {
+TEST_F(RateControlQModeTest, TestMock) {
MockRateControlQMode mock_rc;
EXPECT_CALL(mock_rc,
DetermineGopInfo(Field(&FirstpassInfo::num_mbs_16x16, 1000)))