RC: implement updating encodeframe result

Bug: aomedia:450252793
Change-Id: I1316d8d3f2ce5f670876c0204f568563621a0cf5
diff --git a/av1/encoder/av1_ext_ratectrl.c b/av1/encoder/av1_ext_ratectrl.c
index 417c87e..f7187a4 100644
--- a/av1/encoder/av1_ext_ratectrl.c
+++ b/av1/encoder/av1_ext_ratectrl.c
@@ -149,6 +149,24 @@
   return AOM_CODEC_OK;
 }
 
+aom_codec_err_t av1_extrc_update_encodeframe_result(
+    AOM_EXT_RATECTRL *ext_ratectrl, int64_t bit_count,
+    int actual_encoding_qindex) {
+  assert(ext_ratectrl != NULL);
+  if (ext_ratectrl->ready) {
+    aom_rc_status_t rc_status;
+    aom_rc_encodeframe_result_t encode_frame_result;
+    encode_frame_result.bit_count = bit_count;
+    encode_frame_result.actual_encoding_qindex = actual_encoding_qindex;
+    rc_status = ext_ratectrl->funcs.update_encodeframe_result(
+        ext_ratectrl->model, &encode_frame_result);
+    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 f6b4fbe..54e4214 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -4603,6 +4603,16 @@
     return AOM_CODEC_ERROR;
   }
 
+  if (cpi->ext_ratectrl.ready &&
+      cpi->ext_ratectrl.funcs.update_encodeframe_result != NULL) {
+    aom_codec_err_t codec_status = av1_extrc_update_encodeframe_result(
+        &cpi->ext_ratectrl, (*frame_size) << 3, cm->quant_params.base_qindex);
+    if (codec_status != AOM_CODEC_OK) {
+      aom_internal_error(cm->error, codec_status,
+                         "av1_extrc_update_encodeframe_result() failed");
+    }
+  }
+
   return AOM_CODEC_OK;
 }
 
diff --git a/test/ext_ratectrl_test.cc b/test/ext_ratectrl_test.cc
index c031f4a..269de04 100644
--- a/test/ext_ratectrl_test.cc
+++ b/test/ext_ratectrl_test.cc
@@ -42,6 +42,13 @@
 // A flag to indicate if get_gop_decision() is called.
 bool is_get_gop_decision_called = false;
 
+// A flag to indicate if update_encodeframe_result() is called.
+bool is_update_encodeframe_result_called = false;
+
+// Variables to store the parameters passed to update_encodeframe_result().
+int64_t bit_count = 0;
+int actual_encoding_qindex = 0;
+
 // Construct a single ALTREF GOP.
 const int kGopFrameCount = kFrameNum + 1;
 aom_rc_gop_frame_t gop_frame_list[kGopFrameCount];
@@ -123,6 +130,16 @@
   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) {
+  EXPECT_NE(encode_frame_result, nullptr);
+  bit_count = encode_frame_result->bit_count;
+  actual_encoding_qindex = encode_frame_result->actual_encoding_qindex;
+  is_update_encodeframe_result_called = true;
+  return AOM_RC_OK;
+}
+
 class ExtRateCtrlTest : public ::libaom_test::EncoderTest,
                         public ::libaom_test::CodecTestWith2Params<int, int> {
  protected:
@@ -149,6 +166,7 @@
     is_send_firstpass_stats_called = false;
     is_send_extrc_tpl_gop_stats_called = false;
     is_get_gop_decision_called = false;
+    is_update_encodeframe_result_called = false;
   }
 
   void PreEncodeFrameHook(::libaom_test::VideoSource *video,
@@ -182,10 +200,8 @@
 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_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
@@ -211,7 +227,6 @@
     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);
   }
 };
@@ -227,6 +242,56 @@
                            ::testing::Values(::libaom_test::kTwoPassGood),
                            ::testing::Values(3));
 
+class ExtRateCtrlUpdateEncodeFrameResultTest : public ExtRateCtrlTest {
+ protected:
+  ExtRateCtrlUpdateEncodeFrameResultTest() {
+    rc_funcs_.rc_type = AOM_RC_QP;
+    rc_funcs_.get_encodeframe_decision = mock_get_encodeframe_decision_const_q;
+    rc_funcs_.update_encodeframe_result = mock_update_encodeframe_result;
+  }
+  ~ExtRateCtrlUpdateEncodeFrameResultTest() override = default;
+
+  void SetUp() override {
+    ExtRateCtrlTest::SetUp();
+    is_update_encodeframe_result_called = false;
+    bit_count = 0;
+    actual_encoding_qindex = 0;
+  }
+
+  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);
+    EXPECT_EQ(q_index, kFrameQindex);
+    EXPECT_EQ(actual_encoding_qindex, kFrameQindex);
+    // The second packet includes both invisible and visible frames. To verify
+    // frame size, there's no move_offset (obu_header_size + obu_payload_size)
+    // for vis_frame_size.
+    const int pkt_size_padding = pkt_count_ == 1 ? 0 : 2;
+    // Due to av1 packing invisible and visible frames together, we can only
+    // verify the size of the visible frame in the packet using vis_frame_size.
+    EXPECT_EQ((bit_count >> 3) + pkt_size_padding,
+              pkt->data.frame.vis_frame_size);
+    pkt_count_++;
+  }
+
+ private:
+  int pkt_count_ = 0;
+};
+
+TEST_P(ExtRateCtrlUpdateEncodeFrameResultTest,
+       TestExternalRateCtrlUpdateEncodeFrameResult) {
+  ::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);
+  EXPECT_TRUE(is_update_encodeframe_result_called);
+}
+
+AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlUpdateEncodeFrameResultTest,
+                           ::testing::Values(::libaom_test::kTwoPassGood),
+                           ::testing::Values(3));
+
 class ExtRateCtrlGopTest : public ExtRateCtrlTest {
  protected:
   ExtRateCtrlGopTest() {