Add unit test to check if kf interval is respected
KeyFrameIntervalTestLarge checks whether key frames are placed
at a distance <= max_kf_interval and >= min_kf_interval.
Change-Id: Ib72ed27c192dd01c618b99390ade8a4ed3183a52
diff --git a/aom/aom_codec.h b/aom/aom_codec.h
index 057c0f5..6e4208d 100644
--- a/aom/aom_codec.h
+++ b/aom/aom_codec.h
@@ -260,6 +260,27 @@
*/
typedef struct aom_codec_priv aom_codec_priv_t;
+/*!\brief Compressed Frame Flags
+ *
+ * This type represents a bitfield containing information about a compressed
+ * frame that may be useful to an application. The most significant 16 bits
+ * can be used by an algorithm to provide additional detail, for example to
+ * support frame types that are codec specific (MPEG-1 D-frames for example)
+ */
+typedef uint32_t aom_codec_frame_flags_t;
+#define AOM_FRAME_IS_KEY 0x1 /**< frame is the start of a GOP */
+/*!\brief frame can be dropped without affecting the stream (no future frame
+ * depends on this one) */
+#define AOM_FRAME_IS_DROPPABLE 0x2
+/*!\brief this is an INTRA_ONLY frame */
+#define AOM_FRAME_IS_INTRAONLY 0x10
+/*!\brief this is an S-frame */
+#define AOM_FRAME_IS_SWITCH 0x20
+/*!\brief this is an error-resilient frame */
+#define AOM_FRAME_IS_ERROR_RESILIENT 0x40
+/*!\brief this is a key-frame dependent recovery-point frame */
+#define AOM_FRAME_IS_DELAYED_RANDOM_ACCESS_POINT 0x80
+
/*!\brief Iterator
*
* Opaque storage used for iterating over lists.
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index 591d9ca..f36dc4e 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -78,27 +78,6 @@
size_t sz; /**< Length of the buffer, in chars */
} aom_fixed_buf_t; /**< alias for struct aom_fixed_buf */
-/*!\brief Compressed Frame Flags
- *
- * This type represents a bitfield containing information about a compressed
- * frame that may be useful to an application. The most significant 16 bits
- * can be used by an algorithm to provide additional detail, for example to
- * support frame types that are codec specific (MPEG-1 D-frames for example)
- */
-typedef uint32_t aom_codec_frame_flags_t;
-#define AOM_FRAME_IS_KEY 0x1 /**< frame is the start of a GOP */
-/*!\brief frame can be dropped without affecting the stream (no future frame
- * depends on this one) */
-#define AOM_FRAME_IS_DROPPABLE 0x2
-/*!\brief this is an INTRA_ONLY frame */
-#define AOM_FRAME_IS_INTRAONLY 0x10
-/*!\brief this is an S-frame */
-#define AOM_FRAME_IS_SWITCH 0x20
-/*!\brief this is an error-resilient frame */
-#define AOM_FRAME_IS_ERROR_RESILIENT 0x40
-/*!\brief this is a key-frame dependent recovery-point frame */
-#define AOM_FRAME_IS_DELAYED_RANDOM_ACCESS_POINT 0x80
-
/*!\brief Error Resilient flags
*
* These flags define which error resilient features to enable in the
diff --git a/aom/aomdx.h b/aom/aomdx.h
index 43fa85f..8874eea 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -309,6 +309,11 @@
AOM_DECODER_CTRL_ID_MAX,
AOMD_GET_FWD_KF_PRESENT,
+
+ /*!\brief Codec control function to get the frame flags of the previous frame
+ * decoded. This will return a flag of type aom_codec_frame_flags_t.
+ */
+ AOMD_GET_FRAME_FLAGS,
};
/*!\cond */
@@ -338,6 +343,9 @@
AOM_CTRL_USE_TYPE(AOMD_GET_FWD_KF_PRESENT, int *)
#define AOM_CTRL_AOMD_GET_FWD_KF_PRESENT
+AOM_CTRL_USE_TYPE(AOMD_GET_FRAME_FLAGS, int *)
+#define AOM_CTRL_AOMD_GET_FRAME_FLAGS
+
AOM_CTRL_USE_TYPE(AV1D_GET_DISPLAY_SIZE, int *)
#define AOM_CTRL_AV1D_GET_DISPLAY_SIZE
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index b3a09d5..a481895 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -977,6 +977,29 @@
return AOM_CODEC_OK;
}
+static aom_codec_err_t ctrl_get_frame_flags(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+ int *const arg = va_arg(args, int *);
+ if (arg == NULL) return AOM_CODEC_INVALID_PARAM;
+ AV1Decoder *pbi = ((FrameWorkerData *)ctx->frame_worker->data1)->pbi;
+ *arg = 0;
+ switch (pbi->common.current_frame.frame_type) {
+ case KEY_FRAME:
+ *arg |= AOM_FRAME_IS_KEY;
+ *arg |= AOM_FRAME_IS_INTRAONLY;
+ if (!pbi->common.show_frame) {
+ *arg |= AOM_FRAME_IS_DELAYED_RANDOM_ACCESS_POINT;
+ }
+ break;
+ case INTRA_ONLY_FRAME: *arg |= AOM_FRAME_IS_INTRAONLY; break;
+ case S_FRAME: *arg |= AOM_FRAME_IS_SWITCH; break;
+ }
+ if (pbi->common.features.error_resilient_mode) {
+ *arg |= AOM_FRAME_IS_ERROR_RESILIENT;
+ }
+ return AOM_CODEC_OK;
+}
+
static aom_codec_err_t ctrl_get_frame_corrupted(aom_codec_alg_priv_t *ctx,
va_list args) {
int *corrupted = va_arg(args, int *);
@@ -1370,6 +1393,7 @@
{ AV1D_GET_FRAME_HEADER_INFO, ctrl_get_frame_header_info },
{ AV1D_GET_TILE_DATA, ctrl_get_tile_data },
{ AOMD_GET_FWD_KF_PRESENT, ctrl_get_fwd_kf_value },
+ { AOMD_GET_FRAME_FLAGS, ctrl_get_frame_flags },
CTRL_MAP_END,
};
diff --git a/test/kf_test.cc b/test/kf_test.cc
new file mode 100644
index 0000000..9f8f52a
--- /dev/null
+++ b/test/kf_test.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
+ */
+
+#include <ostream>
+
+#include "aom/aom_codec.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"
+
+namespace {
+typedef struct {
+ const unsigned int min_kf_dist;
+ const unsigned int max_kf_dist;
+} kfIntervalParam;
+
+const kfIntervalParam kfTestParams[] = {
+ { 1, 1 }, { 0, 10 }, { 10, 10 }, { 0, 30 }, { 30, 30 }
+};
+
+std::ostream &operator<<(std::ostream &os, const kfIntervalParam &test_arg) {
+ return os << "kfIntervalParam { min_kf_dist:" << test_arg.min_kf_dist
+ << " max_kf_dist:" << test_arg.max_kf_dist << " }";
+}
+
+// This class is used to test the presence of forward key frame.
+class KeyFrameIntervalTestLarge
+ : public ::libaom_test::CodecTestWith3Params<libaom_test::TestMode,
+ kfIntervalParam, aom_rc_mode>,
+ public ::libaom_test::EncoderTest {
+ protected:
+ KeyFrameIntervalTestLarge()
+ : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
+ kf_dist_param_(GET_PARAM(2)), end_usage_check_(GET_PARAM(3)) {
+ kf_dist_ = -1;
+ is_kf_interval_violated_ = false;
+ }
+ virtual ~KeyFrameIntervalTestLarge() {}
+
+ virtual void SetUp() {
+ InitializeConfig();
+ SetMode(encoding_mode_);
+ const aom_rational timebase = { 1, 30 };
+ cfg_.g_timebase = timebase;
+ cfg_.rc_end_usage = end_usage_check_;
+ cfg_.g_threads = 1;
+ cfg_.kf_min_dist = kf_dist_param_.min_kf_dist;
+ cfg_.kf_max_dist = kf_dist_param_.max_kf_dist;
+ cfg_.g_lag_in_frames = 19;
+ }
+
+ 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);
+ encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1);
+ }
+ }
+
+ 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();
+ int frame_flags = 0;
+ AOM_CODEC_CONTROL_TYPECHECKED(ctx_dec, AOMD_GET_FRAME_FLAGS,
+ &frame_flags);
+ if (kf_dist_ != -1) {
+ kf_dist_++;
+ if (kf_dist_ > (int)kf_dist_param_.max_kf_dist) {
+ is_kf_interval_violated_ = true;
+ }
+ }
+ if ((frame_flags & AOM_FRAME_IS_KEY) ==
+ static_cast<aom_codec_frame_flags_t>(AOM_FRAME_IS_KEY)) {
+ if (kf_dist_ != -1 && kf_dist_ < (int)kf_dist_param_.min_kf_dist) {
+ is_kf_interval_violated_ = true;
+ }
+ kf_dist_ = 0;
+ }
+ }
+ return AOM_CODEC_OK == res_dec;
+ }
+
+ ::libaom_test::TestMode encoding_mode_;
+ const kfIntervalParam kf_dist_param_;
+ int kf_dist_;
+ bool is_kf_interval_violated_;
+ aom_rc_mode end_usage_check_;
+};
+
+TEST_P(KeyFrameIntervalTestLarge, KeyFrameIntervalTest) {
+ libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+ cfg_.g_timebase.den, cfg_.g_timebase.num,
+ 0, 150);
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ ASSERT_EQ(is_kf_interval_violated_, false) << kf_dist_param_;
+}
+
+AV1_INSTANTIATE_TEST_CASE(KeyFrameIntervalTestLarge,
+ ::testing::Values(::libaom_test::kOnePassGood,
+ ::libaom_test::kTwoPassGood),
+ ::testing::ValuesIn(kfTestParams),
+ ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
+} // namespace
diff --git a/test/test.cmake b/test/test.cmake
index 6c7d281..cbd5288 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -127,6 +127,7 @@
"${AOM_ROOT}/test/ethread_test.cc"
"${AOM_ROOT}/test/film_grain_table_test.cc"
"${AOM_ROOT}/test/fwd_kf_test.cc"
+ "${AOM_ROOT}/test/kf_test.cc"
"${AOM_ROOT}/test/sb_multipass_test.cc"
"${AOM_ROOT}/test/segment_binarization_sync.cc"
"${AOM_ROOT}/test/superframe_test.cc"