RC: Implement using QP from external RC Bug: b:450252793 Change-Id: I900d66431075c1ba5623d8f6ac33ecbcee669fd3
diff --git a/av1/encoder/av1_ext_ratectrl.c b/av1/encoder/av1_ext_ratectrl.c index 041a0a5..2b048c8 100644 --- a/av1/encoder/av1_ext_ratectrl.c +++ b/av1/encoder/av1_ext_ratectrl.c
@@ -119,6 +119,20 @@ return AOM_CODEC_OK; } +aom_codec_err_t av1_extrc_get_encodeframe_decision( + AOM_EXT_RATECTRL *ext_ratectrl, int gop_index, + aom_rc_encodeframe_decision_t *encode_frame_decision) { + assert(ext_ratectrl != NULL); + assert(ext_ratectrl->ready && (ext_ratectrl->funcs.rc_type & AOM_RC_QP) != 0); + assert(encode_frame_decision != NULL); + aom_rc_status_t rc_status = ext_ratectrl->funcs.get_encodeframe_decision( + ext_ratectrl->model, gop_index, encode_frame_decision); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + return AOM_CODEC_OK; +} + aom_codec_err_t av1_extrc_delete(AOM_EXT_RATECTRL *ext_ratectrl) { if (ext_ratectrl == NULL) { return AOM_CODEC_INVALID_PARAM;
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 5d47c1c..ed4cdb4 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -3427,6 +3427,24 @@ } } + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_QP) != 0 && + cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { + aom_codec_err_t codec_status; + aom_rc_encodeframe_decision_t encode_frame_decision; + codec_status = av1_extrc_get_encodeframe_decision( + &cpi->ext_ratectrl, cpi->gf_frame_index, &encode_frame_decision); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cm->error, codec_status, + "av1_extrc_get_encodeframe_decision() failed"); + } + // If the external model recommends a reserved value, we use the default + // q. + if (encode_frame_decision.q_index != AOM_DEFAULT_Q) { + q = encode_frame_decision.q_index; + } + } + av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, oxcf->mode == ALLINTRA, oxcf->tune_cfg.tuning); @@ -3568,6 +3586,13 @@ // Ducky encode currently does not support recode loop. loop = 0; } + + // Do not recode if external rate control is used. + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_QP) != 0 && + cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { + loop = 0; + } #if CONFIG_BITRATE_ACCURACY || CONFIG_RD_COMMAND loop = 0; // turn off recode loop when CONFIG_BITRATE_ACCURACY is on #endif // CONFIG_BITRATE_ACCURACY || CONFIG_RD_COMMAND
diff --git a/test/ext_ratectrl_test.cc b/test/ext_ratectrl_test.cc index c9a5b95..fac4077 100644 --- a/test/ext_ratectrl_test.cc +++ b/test/ext_ratectrl_test.cc
@@ -78,35 +78,19 @@ return AOM_RC_OK; } -aom_rc_status_t mock_get_encodeframe_decision( - aom_rc_model_t ratectrl_model, const int frame_gop_index, - aom_rc_encodeframe_decision_t *frame_decision) { - (void)ratectrl_model; - (void)frame_gop_index; - (void)frame_decision; - return AOM_RC_OK; -} - -aom_rc_status_t mock_update_encodeframe_result( - aom_rc_model_t ratectrl_model, - const aom_rc_encodeframe_result_t *encode_frame_result) { - (void)ratectrl_model; - (void)encode_frame_result; - return AOM_RC_OK; -} - class ExtRateCtrlTest : public ::libaom_test::EncoderTest, public ::libaom_test::CodecTestWith2Params<int, int> { protected: ExtRateCtrlTest() : EncoderTest(GET_PARAM(0)), cpu_used_(GET_PARAM(2)) { aom_rc_funcs_t *rc_funcs = &rc_funcs_; rc_funcs->priv = &g_priv; + rc_funcs->rc_type = AOM_RC_QP; rc_funcs->create_model = mock_create_model; rc_funcs->delete_model = mock_delete_model; rc_funcs->send_firstpass_stats = mock_send_firstpass_stats; rc_funcs->send_tpl_gop_stats = mock_send_extrc_tpl_gop_stats; - rc_funcs->get_encodeframe_decision = mock_get_encodeframe_decision; - rc_funcs->update_encodeframe_result = mock_update_encodeframe_result; + rc_funcs->get_encodeframe_decision = nullptr; + rc_funcs->update_encodeframe_result = nullptr; } ~ExtRateCtrlTest() override = default; @@ -126,8 +110,10 @@ encoder->Control(AOME_SET_CPUUSED, cpu_used_); encoder->Control(AV1E_SET_EXTERNAL_RATE_CONTROL, &rc_funcs_); } + current_encoder_ = encoder; } + ::libaom_test::Encoder *current_encoder_; aom_rc_funcs_t rc_funcs_; int cpu_used_; }; @@ -144,4 +130,53 @@ AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlTest, ::testing::Values(::libaom_test::kTwoPassGood), ::testing::Values(0)); + +// Corresponds to QP 43 for 8-bit video. +const int kFrameQindex = 172; + +aom_rc_status_t mock_get_encodeframe_decision_const_q( + aom_rc_model_t ratectrl_model, int frame_gop_index, + aom_rc_encodeframe_decision_t *frame_decision) { + (void)ratectrl_model; + (void)frame_gop_index; + // Set constant QP. + frame_decision->q_index = kFrameQindex; + frame_decision->sb_params_list = NULL; // Initialize to NULL + return AOM_RC_OK; +} + +class ExtRateCtrlQpTest : public ExtRateCtrlTest { + protected: + ExtRateCtrlQpTest() { + rc_funcs_.get_encodeframe_decision = mock_get_encodeframe_decision_const_q; + } + ~ExtRateCtrlQpTest() override = default; + + void SetUp() override { + InitializeConfig(static_cast<libaom_test::TestMode>(GET_PARAM(1))); + cfg_.g_threads = 1; + cfg_.g_limit = kFrameNum; + is_create_model_called = false; + is_delete_model_called = false; + } + + void FramePktHook(const aom_codec_cx_pkt_t *pkt) override { + if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return; + int q_index = -1; + current_encoder_->Control(AOME_GET_LAST_QUANTIZER, &q_index); + std::cout << q_index << std::endl; + EXPECT_EQ(q_index, kFrameQindex); + } +}; + +TEST_P(ExtRateCtrlQpTest, TestExternalRateCtrlConstQp) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_delete_model_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlQpTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(0)); } // namespace