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