kf_test.cc (14421B)
1 /* 2 * Copyright (c) 2020, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include <string.h> 13 14 #include <ostream> 15 16 #include "aom/aom_codec.h" 17 #include "aom/aom_encoder.h" 18 #include "aom/aom_image.h" 19 #include "aom/aomcx.h" 20 #include "gtest/gtest.h" 21 #include "test/codec_factory.h" 22 #include "test/encode_test_driver.h" 23 #include "test/i420_video_source.h" 24 #include "test/util.h" 25 26 #define NUM_LAG_VALUES 3 27 28 namespace { 29 aom_image_t *CreateGrayImage(aom_img_fmt_t fmt, unsigned int w, 30 unsigned int h) { 31 aom_image_t *const image = aom_img_alloc(nullptr, fmt, w, h, 1); 32 if (!image) return image; 33 34 for (unsigned int i = 0; i < image->d_h; ++i) { 35 memset(image->planes[0] + i * image->stride[0], 128, image->d_w); 36 } 37 const unsigned int uv_h = (image->d_h + 1) / 2; 38 const unsigned int uv_w = (image->d_w + 1) / 2; 39 for (unsigned int i = 0; i < uv_h; ++i) { 40 memset(image->planes[1] + i * image->stride[1], 128, uv_w); 41 memset(image->planes[2] + i * image->stride[2], 128, uv_w); 42 } 43 return image; 44 } 45 46 // Tests kf_max_dist in one-pass encoding with zero lag. 47 void TestKeyFrameMaximumInterval(unsigned int usage, unsigned int kf_max_dist) { 48 aom_codec_iface_t *iface = aom_codec_av1_cx(); 49 aom_codec_enc_cfg_t cfg; 50 ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, usage), AOM_CODEC_OK); 51 cfg.g_w = 320; 52 cfg.g_h = 240; 53 cfg.g_pass = AOM_RC_ONE_PASS; 54 cfg.g_lag_in_frames = 0; 55 cfg.kf_mode = AOM_KF_AUTO; 56 cfg.kf_min_dist = 0; 57 cfg.kf_max_dist = kf_max_dist; 58 59 aom_codec_ctx_t enc; 60 ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); 61 62 ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 6), AOM_CODEC_OK); 63 64 aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); 65 ASSERT_NE(image, nullptr); 66 67 // Encode frames. 68 const aom_codec_cx_pkt_t *pkt; 69 const unsigned int num_frames = kf_max_dist == 0 ? 4 : 3 * kf_max_dist + 1; 70 for (unsigned int i = 0; i < num_frames; ++i) { 71 ASSERT_EQ(aom_codec_encode(&enc, image, i, 1, 0), AOM_CODEC_OK); 72 aom_codec_iter_t iter = nullptr; 73 while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { 74 ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); 75 if (kf_max_dist == 0 || i % kf_max_dist == 0) { 76 ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); 77 } else { 78 ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u); 79 } 80 } 81 } 82 83 // Flush the encoder. 84 bool got_data; 85 do { 86 ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 1, 0), AOM_CODEC_OK); 87 got_data = false; 88 aom_codec_iter_t iter = nullptr; 89 while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { 90 ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); 91 got_data = true; 92 } 93 } while (got_data); 94 95 aom_img_free(image); 96 ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); 97 } 98 99 TEST(KeyFrameIntervalTest, KeyFrameMaximumInterval) { 100 for (unsigned int usage : { AOM_USAGE_GOOD_QUALITY, AOM_USAGE_REALTIME }) { 101 // Test 0 and 1 (both mean all intra), some powers of 2, some multiples of 102 // 10, and some prime numbers. 103 for (unsigned int kf_max_dist : 104 { 0, 1, 2, 3, 4, 7, 10, 13, 16, 20, 23, 29, 32 }) { 105 TestKeyFrameMaximumInterval(usage, kf_max_dist); 106 } 107 } 108 } 109 110 struct kfIntervalParam { 111 const unsigned int min_kf_dist; 112 const unsigned int max_kf_dist; 113 }; 114 115 const kfIntervalParam kfTestParams[] = { 116 { 1, 1 }, { 0, 10 }, { 10, 10 }, { 0, 30 }, { 30, 30 } 117 }; 118 119 std::ostream &operator<<(std::ostream &os, const kfIntervalParam &test_arg) { 120 return os << "kfIntervalParam { min_kf_dist:" << test_arg.min_kf_dist 121 << " max_kf_dist:" << test_arg.max_kf_dist << " }"; 122 } 123 124 // This class is used to test the presence of forward key frame. 125 class KeyFrameIntervalTestLarge 126 : public ::libaom_test::CodecTestWith3Params<libaom_test::TestMode, 127 kfIntervalParam, aom_rc_mode>, 128 public ::libaom_test::EncoderTest { 129 protected: 130 KeyFrameIntervalTestLarge() 131 : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)), 132 kf_dist_param_(GET_PARAM(2)), end_usage_check_(GET_PARAM(3)) { 133 kf_dist_ = -1; 134 is_kf_interval_violated_ = false; 135 } 136 ~KeyFrameIntervalTestLarge() override = default; 137 138 void SetUp() override { 139 InitializeConfig(encoding_mode_); 140 const aom_rational timebase = { 1, 30 }; 141 cfg_.g_timebase = timebase; 142 cfg_.rc_end_usage = end_usage_check_; 143 cfg_.g_threads = 1; 144 cfg_.kf_min_dist = kf_dist_param_.min_kf_dist; 145 cfg_.kf_max_dist = kf_dist_param_.max_kf_dist; 146 cfg_.g_lag_in_frames = 19; 147 } 148 149 bool DoDecode() const override { return true; } 150 151 void PreEncodeFrameHook(::libaom_test::VideoSource *video, 152 ::libaom_test::Encoder *encoder) override { 153 if (video->frame() == 0) { 154 encoder->Control(AOME_SET_CPUUSED, 5); 155 encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1); 156 } 157 } 158 159 bool HandleDecodeResult(const aom_codec_err_t res_dec, 160 libaom_test::Decoder *decoder) override { 161 EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError(); 162 if (AOM_CODEC_OK == res_dec) { 163 aom_codec_ctx_t *ctx_dec = decoder->GetDecoder(); 164 int frame_flags = 0; 165 AOM_CODEC_CONTROL_TYPECHECKED(ctx_dec, AOMD_GET_FRAME_FLAGS, 166 &frame_flags); 167 if (kf_dist_ != -1) { 168 kf_dist_++; 169 if (kf_dist_ > (int)kf_dist_param_.max_kf_dist) { 170 is_kf_interval_violated_ = true; 171 } 172 } 173 if ((frame_flags & AOM_FRAME_IS_KEY) == AOM_FRAME_IS_KEY) { 174 if (kf_dist_ != -1 && kf_dist_ < (int)kf_dist_param_.min_kf_dist) { 175 is_kf_interval_violated_ = true; 176 } 177 kf_dist_ = 0; 178 } 179 } 180 return AOM_CODEC_OK == res_dec; 181 } 182 183 ::libaom_test::TestMode encoding_mode_; 184 const kfIntervalParam kf_dist_param_; 185 int kf_dist_; 186 bool is_kf_interval_violated_; 187 aom_rc_mode end_usage_check_; 188 }; 189 190 // Because valgrind builds take a very long time to run, use a lower 191 // resolution video for valgrind runs. 192 const char *TestFileName() { 193 #ifdef AOM_VALGRIND_BUILD 194 return "hantro_collage_w176h144.yuv"; 195 #else 196 return "hantro_collage_w352h288.yuv"; 197 #endif // AOM_VALGRIND_BUILD 198 } 199 200 int TestFileWidth() { 201 #ifdef AOM_VALGRIND_BUILD 202 return 176; 203 #else 204 return 352; 205 #endif // AOM_VALGRIND_BUILD 206 } 207 208 int TestFileHeight() { 209 #ifdef AOM_VALGRIND_BUILD 210 return 144; 211 #else 212 return 288; 213 #endif // AOM_VALGRIND_BUILD 214 } 215 216 TEST_P(KeyFrameIntervalTestLarge, KeyFrameIntervalTest) { 217 libaom_test::I420VideoSource video(TestFileName(), TestFileWidth(), 218 TestFileHeight(), cfg_.g_timebase.den, 219 cfg_.g_timebase.num, 0, 75); 220 ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); 221 ASSERT_EQ(is_kf_interval_violated_, false) << kf_dist_param_; 222 } 223 224 // This class tests for presence and placement of application forced key frames. 225 class ForcedKeyTestLarge 226 : public ::libaom_test::CodecTestWith5Params<libaom_test::TestMode, int, 227 int, int, aom_rc_mode>, 228 public ::libaom_test::EncoderTest { 229 protected: 230 ForcedKeyTestLarge() 231 : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)), 232 auto_alt_ref_(GET_PARAM(2)), fwd_kf_enabled_(GET_PARAM(3)), 233 cpu_used_(GET_PARAM(4)), rc_end_usage_(GET_PARAM(5)) { 234 forced_kf_frame_num_ = 1; 235 frame_num_ = 0; 236 is_kf_placement_violated_ = false; 237 } 238 ~ForcedKeyTestLarge() override = default; 239 240 void SetUp() override { 241 InitializeConfig(encoding_mode_); 242 cfg_.rc_end_usage = rc_end_usage_; 243 cfg_.g_threads = 0; 244 cfg_.kf_max_dist = 30; 245 cfg_.kf_min_dist = 0; 246 cfg_.fwd_kf_enabled = fwd_kf_enabled_; 247 } 248 249 void PreEncodeFrameHook(::libaom_test::VideoSource *video, 250 ::libaom_test::Encoder *encoder) override { 251 if (video->frame() == 0) { 252 encoder->Control(AOME_SET_CPUUSED, cpu_used_); 253 encoder->Control(AOME_SET_ENABLEAUTOALTREF, auto_alt_ref_); 254 #if CONFIG_AV1_ENCODER 255 // override test default for tile columns if necessary. 256 if (GET_PARAM(0) == &libaom_test::kAV1) { 257 encoder->Control(AV1E_SET_TILE_COLUMNS, 6); 258 } 259 #endif 260 } 261 frame_flags_ = 262 ((int)video->frame() == forced_kf_frame_num_) ? AOM_EFLAG_FORCE_KF : 0; 263 } 264 265 bool HandleDecodeResult(const aom_codec_err_t res_dec, 266 libaom_test::Decoder *decoder) override { 267 EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError(); 268 if (AOM_CODEC_OK == res_dec) { 269 if ((int)frame_num_ == forced_kf_frame_num_) { 270 aom_codec_ctx_t *ctx_dec = decoder->GetDecoder(); 271 int frame_flags = 0; 272 AOM_CODEC_CONTROL_TYPECHECKED(ctx_dec, AOMD_GET_FRAME_FLAGS, 273 &frame_flags); 274 if ((frame_flags & AOM_FRAME_IS_KEY) != AOM_FRAME_IS_KEY) { 275 is_kf_placement_violated_ = true; 276 } 277 } 278 ++frame_num_; 279 } 280 return AOM_CODEC_OK == res_dec; 281 } 282 283 void Frame1IsKey(); 284 void ForcedFrameIsKey(); 285 void ForcedFrameIsKeyCornerCases(); 286 287 ::libaom_test::TestMode encoding_mode_; 288 int auto_alt_ref_; 289 int fwd_kf_enabled_; 290 int cpu_used_; 291 aom_rc_mode rc_end_usage_; 292 int forced_kf_frame_num_; 293 unsigned int frame_num_; 294 bool is_kf_placement_violated_; 295 }; 296 297 void ForcedKeyTestLarge::Frame1IsKey() { 298 const aom_rational timebase = { 1, 30 }; 299 // 1st element of this 2D array is for good encoding mode and 2nd element 300 // is for RT encoding mode. 301 const int lag_values[2][NUM_LAG_VALUES] = { { 3, 15, 25 }, { 0, -1, -1 } }; 302 int is_realtime = (encoding_mode_ == ::libaom_test::kRealTime); 303 304 forced_kf_frame_num_ = 1; 305 for (int i = 0; i < NUM_LAG_VALUES; ++i) { 306 if (lag_values[is_realtime][i] == -1) continue; 307 frame_num_ = 0; 308 cfg_.g_lag_in_frames = lag_values[is_realtime][i]; 309 is_kf_placement_violated_ = false; 310 libaom_test::I420VideoSource video( 311 TestFileName(), TestFileWidth(), TestFileHeight(), timebase.den, 312 timebase.num, 0, fwd_kf_enabled_ ? 60 : 30); 313 ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); 314 ASSERT_EQ(is_kf_placement_violated_, false) 315 << "Frame #" << frame_num_ << " isn't a keyframe!"; 316 } 317 } 318 319 // This class checks the presence and placement of application 320 // forced key frames. 321 void ForcedKeyTestLarge::ForcedFrameIsKey() { 322 const aom_rational timebase = { 1, 30 }; 323 const int lag_values[] = { 3, 15, 25, -1 }; 324 325 for (int i = 0; lag_values[i] != -1; ++i) { 326 frame_num_ = 0; 327 forced_kf_frame_num_ = lag_values[i] - 1; 328 cfg_.g_lag_in_frames = lag_values[i]; 329 is_kf_placement_violated_ = false; 330 libaom_test::I420VideoSource video( 331 TestFileName(), TestFileWidth(), TestFileHeight(), timebase.den, 332 timebase.num, 0, fwd_kf_enabled_ ? 60 : 30); 333 ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); 334 ASSERT_EQ(is_kf_placement_violated_, false) 335 << "Frame #" << frame_num_ << " isn't a keyframe!"; 336 337 // Two pass and single pass CBR are currently segfaulting for the case when 338 // forced kf is placed after lag in frames. 339 // TODO(anyone): Enable(uncomment) below test once above bug is fixed. 340 // frame_num_ = 0; 341 // forced_kf_frame_num_ = lag_values[i] + 1; 342 // cfg_.g_lag_in_frames = lag_values[i]; 343 // is_kf_placement_violated_ = false; 344 // ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); 345 // ASSERT_EQ(is_kf_placement_violated_, false) 346 // << "Frame #" << frame_num_ << " isn't a keyframe!"; 347 } 348 } 349 350 void ForcedKeyTestLarge::ForcedFrameIsKeyCornerCases() { 351 const aom_rational timebase = { 1, 30 }; 352 const int kf_offsets[] = { -2, -1, 1, 2, 0 }; 353 cfg_.g_lag_in_frames = 35; 354 if (encoding_mode_ == ::libaom_test::kRealTime) cfg_.g_lag_in_frames = 0; 355 356 for (int i = 0; kf_offsets[i] != 0; ++i) { 357 frame_num_ = 0; 358 forced_kf_frame_num_ = (int)cfg_.kf_max_dist + kf_offsets[i]; 359 forced_kf_frame_num_ = forced_kf_frame_num_ > 0 ? forced_kf_frame_num_ : 1; 360 is_kf_placement_violated_ = false; 361 libaom_test::I420VideoSource video( 362 TestFileName(), TestFileWidth(), TestFileHeight(), timebase.den, 363 timebase.num, 0, fwd_kf_enabled_ ? 60 : 30); 364 ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); 365 ASSERT_EQ(is_kf_placement_violated_, false) 366 << "Frame #" << frame_num_ << " isn't a keyframe!"; 367 } 368 } 369 370 AV1_INSTANTIATE_TEST_SUITE(KeyFrameIntervalTestLarge, 371 testing::Values(::libaom_test::kOnePassGood, 372 ::libaom_test::kTwoPassGood), 373 ::testing::ValuesIn(kfTestParams), 374 ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ)); 375 376 TEST_P(ForcedKeyTestLarge, Frame1IsKey) { Frame1IsKey(); } 377 TEST_P(ForcedKeyTestLarge, ForcedFrameIsKey) { ForcedFrameIsKey(); } 378 TEST_P(ForcedKeyTestLarge, ForcedFrameIsKeyCornerCases) { 379 ForcedFrameIsKeyCornerCases(); 380 } 381 382 class ForcedKeyRTTestLarge : public ForcedKeyTestLarge {}; 383 384 TEST_P(ForcedKeyRTTestLarge, Frame1IsKey) { Frame1IsKey(); } 385 TEST_P(ForcedKeyRTTestLarge, ForcedFrameIsKeyCornerCases) { 386 ForcedFrameIsKeyCornerCases(); 387 } 388 // TODO(anyone): Add CBR to list of rc_modes once forced kf placement after 389 // lag in frames bug is fixed. 390 AV1_INSTANTIATE_TEST_SUITE(ForcedKeyTestLarge, 391 ::testing::Values(::libaom_test::kOnePassGood, 392 ::libaom_test::kTwoPassGood), 393 ::testing::Values(0, 1), ::testing::Values(0, 1), 394 ::testing::Values(2, 5), 395 ::testing::Values(AOM_Q, AOM_VBR, AOM_CQ)); 396 AV1_INSTANTIATE_TEST_SUITE(ForcedKeyRTTestLarge, 397 ::testing::Values(::libaom_test::kRealTime), 398 ::testing::Values(0), ::testing::Values(0), 399 ::testing::Values(7, 9), 400 ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR)); 401 } // namespace