Add codec control to set per frame QP for one pass Change-Id: I22186d6625ff46e87bc892c5500ef736eb4f4ce3
diff --git a/aom/aomcx.h b/aom/aomcx.h index 4a78454..8b2722f 100644 --- a/aom/aomcx.h +++ b/aom/aomcx.h
@@ -1478,6 +1478,12 @@ */ AV1E_ENABLE_SB_QP_SWEEP = 158, + /*!\brief Codec control to set quantizer for the next frame. + * + * This will turn off cyclic refresh. Only applicable to 1-pass. + */ + AV1E_SET_QUANTIZER_ONE_PASS = 159, + // Any new encoder control IDs should be added above. // Maximum allowed encoder control ID is 229. // No encoder control ID should be added below. @@ -2102,6 +2108,9 @@ AOM_CTRL_USE_TYPE(AV1E_ENABLE_SB_QP_SWEEP, unsigned int) #define AOM_CTRL_AV1E_ENABLE_SB_QP_SWEEP +AOM_CTRL_USE_TYPE(AV1E_SET_QUANTIZER_ONE_PASS, int) +#define AOM_CTRL_AV1E_SET_QUANTIZER_ONE_PASS + /*!\endcond */ /*! @} - end defgroup aom_encoder */ #ifdef __cplusplus
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c index 669f845..aca0ab6 100644 --- a/av1/av1_cx_iface.c +++ b/av1/av1_cx_iface.c
@@ -2154,6 +2154,9 @@ va_list args) { struct av1_extracfg extra_cfg = ctx->extra_cfg; extra_cfg.aq_mode = CAST(AV1E_SET_AQ_MODE, args); + + // Skip AQ mode if using fixed QP for current frame. + if (ctx->ppi->cpi->rc.use_external_qp_one_pass) extra_cfg.aq_mode = 0; return update_extra_cfg(ctx, &extra_cfg); } @@ -2499,6 +2502,21 @@ return AOM_CODEC_OK; } +static aom_codec_err_t ctrl_set_quantizer_one_pass(aom_codec_alg_priv_t *ctx, + va_list args) { + const int qp = CAST(AV1E_SET_QUANTIZER_ONE_PASS, args); + + if (qp < 0 || qp > 63) return AOM_CODEC_INVALID_PARAM; + + aom_codec_enc_cfg_t *cfg = &ctx->cfg; + struct av1_extracfg extra_cfg = ctx->extra_cfg; + cfg->rc_min_quantizer = cfg->rc_max_quantizer = qp; + extra_cfg.aq_mode = 0; + ctx->ppi->cpi->rc.use_external_qp_one_pass = 1; + + return update_extra_cfg(ctx, &extra_cfg); +} + #if !CONFIG_REALTIME_ONLY aom_codec_err_t av1_create_stats_buffer(FIRSTPASS_STATS **frame_stats_buffer, STATS_BUFFER_CTX *stats_buf_context, @@ -4228,6 +4246,7 @@ { AV1E_SET_SKIP_POSTPROC_FILTERING, ctrl_set_skip_postproc_filtering }, { AV1E_SET_AUTO_INTRA_TOOLS_OFF, ctrl_set_auto_intra_tools_off }, { AV1E_SET_RTC_EXTERNAL_RC, ctrl_set_rtc_external_rc }, + { AV1E_SET_QUANTIZER_ONE_PASS, ctrl_set_quantizer_one_pass }, // Getters { AOME_GET_LAST_QUANTIZER, ctrl_get_quantizer },
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 0d5b695..4430a00 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -4732,6 +4732,9 @@ } #endif + // Reset the flag to 0 afer encoding. + cpi->rc.use_external_qp_one_pass = 0; + if (result == -1) { cm->error->setjmp = 0; // Returning -1 indicates no frame encoded; more input is required
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c index 3f04b07..4ea1c9a 100644 --- a/av1/encoder/ratectrl.c +++ b/av1/encoder/ratectrl.c
@@ -426,6 +426,7 @@ rc->resize_count = 0; rc->rtc_external_ratectrl = 0; rc->frame_level_fast_extra_bits = 0; + rc->use_external_qp_one_pass = 0; } int av1_rc_drop_frame(AV1_COMP *cpi) {
diff --git a/av1/encoder/ratectrl.h b/av1/encoder/ratectrl.h index c8963a4..14a96ff 100644 --- a/av1/encoder/ratectrl.h +++ b/av1/encoder/ratectrl.h
@@ -266,6 +266,10 @@ // TODO(yunqing): if golden frame is treated differently (e.g. gf_cbr_boost_ // pct > THR), consider to add bit_est_ratio_g for golden frames. int bit_est_ratio; + + // Whether use a fixed qp for the frame, bypassing internal rate control. + // This flag will reset to 0 after every frame. + int use_external_qp_one_pass; /*!\endcond */ } RATE_CONTROL;
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c index 6617f94..d5e0920 100644 --- a/av1/encoder/svc_layercontext.c +++ b/av1/encoder/svc_layercontext.c
@@ -139,6 +139,10 @@ lrc->rtc_external_ratectrl = rc->rtc_external_ratectrl; lrc->worst_quality = av1_quantizer_to_qindex(lc->max_q); lrc->best_quality = av1_quantizer_to_qindex(lc->min_q); + if (rc->use_external_qp_one_pass) { + lrc->worst_quality = rc->worst_quality; + lrc->best_quality = rc->best_quality; + } // Reset the cyclic refresh parameters, if needed (map is NULL), // or number of spatial layers has changed. // Cyclic refresh is only applied on base temporal layer.
diff --git a/test/datarate_test.cc b/test/datarate_test.cc index 8fdc662..6de6a71 100644 --- a/test/datarate_test.cc +++ b/test/datarate_test.cc
@@ -12,6 +12,7 @@ #include "config/aom_config.h" #include "third_party/googletest/src/googletest/include/gtest/gtest.h" +#include "test/acm_random.h" #include "test/codec_factory.h" #include "test/datarate_test.h" #include "test/encode_test_driver.h" @@ -524,6 +525,68 @@ ChangingSpeedTest(); } +class DatarateTestSetFrameQpRealtime + : public DatarateTest, + public ::testing::TestWithParam<const libaom_test::AV1CodecFactory *> { + public: + DatarateTestSetFrameQpRealtime() : DatarateTest(GetParam()), frame_(0) {} + + protected: + virtual ~DatarateTestSetFrameQpRealtime() {} + + virtual void SetUp() { + InitializeConfig(libaom_test::kRealTime); + ResetModel(); + } + + virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video, + ::libaom_test::Encoder *encoder) { + set_cpu_used_ = 7; + DatarateTest::PreEncodeFrameHook(video, encoder); + frame_qp_ = rnd_.PseudoUniform(63); + encoder->Control(AV1E_SET_QUANTIZER_ONE_PASS, frame_qp_); + frame_++; + } + + virtual void PostEncodeFrameHook(::libaom_test::Encoder *encoder) { + if (frame_ >= total_frames_) return; + int qp = 0; + encoder->Control(AOME_GET_LAST_QUANTIZER_64, &qp); + ASSERT_EQ(qp, frame_qp_); + } + + protected: + int total_frames_; + + private: + int frame_qp_; + int frame_; + libaom_test::ACMRandom rnd_; +}; + +TEST_P(DatarateTestSetFrameQpRealtime, SetFrameQpOnePass) { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_undershoot_pct = 20; + cfg_.rc_undershoot_pct = 20; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 50; + cfg_.rc_end_usage = AOM_CBR; + cfg_.rc_target_bitrate = 200; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 1; + cfg_.kf_max_dist = 9999; + cfg_.rc_dropframe_thresh = 0; + + total_frames_ = 100; + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, 100); + + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + AV1_INSTANTIATE_TEST_SUITE(DatarateTestLarge, ::testing::Values(::libaom_test::kRealTime), ::testing::Range(5, 7), ::testing::Values(0, 3), @@ -546,5 +609,10 @@ ::testing::Values(::libaom_test::kRealTime), ::testing::Values(0, 3)); +INSTANTIATE_TEST_SUITE_P( + AV1, DatarateTestSetFrameQpRealtime, + ::testing::Values( + static_cast<const libaom_test::CodecFactory *>(&libaom_test::kAV1))); + } // namespace } // namespace datarate_test
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc index a5c3840..60aa381 100644 --- a/test/svc_datarate_test.cc +++ b/test/svc_datarate_test.cc
@@ -92,6 +92,7 @@ screen_mode_ = 0; rps_mode_ = 0; rps_recovery_frame_ = 0; + user_define_frame_qp_ = 0; } virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video, @@ -193,6 +194,11 @@ } layer_frame_cnt_++; DatarateTest::PreEncodeFrameHook(video, encoder); + + if (user_define_frame_qp_) { + frame_qp_ = rnd_.PseudoUniform(63); + encoder->Control(AV1E_SET_QUANTIZER_ONE_PASS, frame_qp_); + } } virtual void PostEncodeFrameHook(::libaom_test::Encoder *encoder) { @@ -200,6 +206,14 @@ encoder->Control(AV1E_GET_NUM_OPERATING_POINTS, &num_operating_points); ASSERT_EQ(num_operating_points, number_temporal_layers_ * number_spatial_layers_); + + if (user_define_frame_qp_) { + if (current_video_frame_ >= static_cast<unsigned int>(total_frame_)) + return; + int qp; + encoder->Control(AOME_GET_LAST_QUANTIZER_64, &qp); + ASSERT_EQ(qp, frame_qp_); + } } virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) { @@ -637,6 +651,71 @@ EXPECT_EQ((int)GetMismatchFrames(), 150); } + virtual void SetFrameQpSVC3TL1SLTest() { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 1; + + user_define_frame_qp_ = 1; + total_frame_ = 300; + + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, + 288, 30, 1, 0, 300); + const int bitrate_array[2] = { 200, 550 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + number_temporal_layers_ = 3; + target_layer_bitrate_[0] = 50 * cfg_.rc_target_bitrate / 100; + target_layer_bitrate_[1] = 70 * cfg_.rc_target_bitrate / 100; + target_layer_bitrate_[2] = cfg_.rc_target_bitrate; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + + virtual void SetFrameQpSVC3TL3SLTest() { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 0; + + user_define_frame_qp_ = 1; + total_frame_ = 300; + + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, + 288, 30, 1, 0, 300); + const int bitrate_array[2] = { 600, 1200 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + number_temporal_layers_ = 3; + number_spatial_layers_ = 3; + // SL0 + const int bitrate_sl0 = 1 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[0] = 50 * bitrate_sl0 / 100; + target_layer_bitrate_[1] = 70 * bitrate_sl0 / 100; + target_layer_bitrate_[2] = bitrate_sl0; + // SL1 + const int bitrate_sl1 = 3 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[3] = 50 * bitrate_sl1 / 100; + target_layer_bitrate_[4] = 70 * bitrate_sl1 / 100; + target_layer_bitrate_[5] = bitrate_sl1; + // SL2 + const int bitrate_sl2 = 4 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[6] = 50 * bitrate_sl2 / 100; + target_layer_bitrate_[7] = 70 * bitrate_sl2 / 100; + target_layer_bitrate_[8] = bitrate_sl2; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + virtual void BasicRateTargetingSVC3TL1SLScreenTest() { cfg_.rc_buf_initial_sz = 500; cfg_.rc_buf_optimal_sz = 500; @@ -1861,6 +1940,11 @@ int screen_mode_; int rps_mode_; int rps_recovery_frame_; + + int user_define_frame_qp_; + int frame_qp_; + int total_frame_; + libaom_test::ACMRandom rnd_; }; // Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial. @@ -1868,6 +1952,10 @@ BasicRateTargetingSVC3TL1SLTest(); } +TEST_P(DatarateTestSVC, SetFrameQpSVC3TL1SL) { SetFrameQpSVC3TL1SLTest(); } + +TEST_P(DatarateTestSVC, SetFrameQpSVC3TL3SL) { SetFrameQpSVC3TL3SLTest(); } + // Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial // for screen mode. TEST_P(DatarateTestSVC, BasicRateTargetingSVC3TL1SLScreen) {