Add test for qmode deltaq for ducky encode

This new test runs end to end encoding and verifies the psnr of each
encoded frame to be not too low.

Also:
 - Fix a bug of delta-q flag being persistent.
 - Populate encode results from ducky encode

Bug: b/256852795
Change-Id: I022fcbf785b7eab621262881095683f9606a48ef
diff --git a/av1/qmode_rc/ducky_encode.cc b/av1/qmode_rc/ducky_encode.cc
index dc71b6b..74bbf8a 100644
--- a/av1/qmode_rc/ducky_encode.cc
+++ b/av1/qmode_rc/ducky_encode.cc
@@ -399,12 +399,14 @@
 static void DuckyEncodeInfoSetEncodeFrameDecision(
     DuckyEncodeInfo *ducky_encode_info, const EncodeFrameDecision &decision) {
   DuckyEncodeFrameInfo *frame_info = &ducky_encode_info->frame_info;
+  *frame_info = {};
   frame_info->qp_mode = static_cast<DUCKY_ENCODE_FRAME_MODE>(decision.qp_mode);
   frame_info->gop_mode = static_cast<DUCKY_ENCODE_GOP_MODE>(decision.gop_mode);
   frame_info->q_index = decision.parameters.q_index;
   frame_info->rdmult = decision.parameters.rdmult;
   const size_t num_superblocks =
       decision.parameters.superblock_encode_params.size();
+  frame_info->delta_q_enabled = 0;
   if (num_superblocks > 1) {
     frame_info->delta_q_enabled = 1;
     frame_info->superblock_encode_qindex = new int[num_superblocks];
@@ -591,10 +593,10 @@
       aom::EncodeFrameDecision frame_decision = { aom::EncodeFrameMode::kQindex,
                                                   aom::EncodeGopMode::kGopRcl,
                                                   frame_param };
-      EncodeFrame(frame_decision);
+      EncodeFrameResult temp_result = EncodeFrame(frame_decision);
       if (ppi->cpi->common.show_frame) {
         bitstream_buf_.resize(pending_ctx_size_);
-        EncodeFrameResult encode_frame_result = {};
+        EncodeFrameResult encode_frame_result = temp_result;
         encode_frame_result.bitstream_buf = bitstream_buf_;
         encoded_frame_list.push_back(encode_frame_result);
 
diff --git a/test/ducky_encode_test.cc b/test/ducky_encode_test.cc
index 4dcb3dc..ff6bc2f 100644
--- a/test/ducky_encode_test.cc
+++ b/test/ducky_encode_test.cc
@@ -21,7 +21,10 @@
 
 #include "av1/encoder/encoder.h"
 #include "av1/qmode_rc/ducky_encode.h"
+#include "av1/qmode_rc/ratectrl_qmode.h"
+#include "av1/qmode_rc/ratectrl_qmode_interface.h"
 #include "test/video_source.h"
+#include "third_party/googletest/src/googlemock/include/gmock/gmock.h"
 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
 
 namespace aom {
@@ -94,12 +97,88 @@
   for (int i = 0; i < coding_frame_count; ++i) {
     ducky_encode.AllocateBitstreamBuffer(video_info);
     EncodeFrameResult encode_frame_result = ducky_encode.EncodeFrame(decision);
-    // TODO(angiebird): Check why distortion is not zero when q_index = 0
     EXPECT_EQ(encode_frame_result.dist, 0);
   }
   ducky_encode.EndEncode();
 }
 
+TEST(DuckyEncodeRCTest, EncodeVideoWithRC) {
+  aom_rational_t frame_rate = { 30, 1 };
+  const int frame_number = 35;
+  const int frame_width = 352;
+  const int frame_height = 288;
+  VideoInfo video_info = { frame_width,  frame_height,
+                           frame_rate,   AOM_IMG_FMT_I420,
+                           frame_number, "bus_352x288_420_f20_b8.yuv" };
+  video_info.file_path =
+      libaom_test::GetDataPath() + "/" + video_info.file_path;
+  DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
+
+  AV1RateControlQMode qmode_rc;
+  RateControlParam rc_param = {};
+  rc_param.max_gop_show_frame_count = 16;
+  rc_param.min_gop_show_frame_count = 4;
+  rc_param.ref_frame_table_size = 5;
+  rc_param.max_ref_frames = 3;
+  rc_param.base_q_index = 45;
+  rc_param.max_distinct_q_indices_per_frame = 8;
+  rc_param.max_distinct_lambda_scales_per_frame = 1;
+  rc_param.frame_width = frame_width;
+  rc_param.frame_height = frame_height;
+  rc_param.tpl_pass_count = TplPassCount::kOneTplPass;
+  rc_param.tpl_pass_index = 0;
+  const Status status = qmode_rc.SetRcParam(rc_param);
+  ASSERT_TRUE(status.ok());
+  FirstpassInfo firstpass_info;
+  firstpass_info.stats_list = ducky_encode.ComputeFirstPassStats();
+  constexpr int kBlockSize = 16;
+  firstpass_info.num_mbs_16x16 = ((frame_width + kBlockSize - 1) / kBlockSize) *
+                                 ((frame_height + kBlockSize - 1) / kBlockSize);
+  const auto gop_info = qmode_rc.DetermineGopInfo(firstpass_info);
+  ASSERT_TRUE(gop_info.status().ok());
+  const GopStructList &gop_list = gop_info.value();
+
+  std::vector<aom::GopEncodeInfo> tpl_pass_gop_encode_info_list;
+  std::vector<aom::TplGopStats> tpl_gop_stats_list;
+  for (const auto &gop_struct : gop_list) {
+    const auto gop_encode_info = qmode_rc.GetTplPassGopEncodeInfo(gop_struct);
+    ASSERT_TRUE(gop_encode_info.status().ok());
+    tpl_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
+  }
+  ducky_encode.StartEncode(firstpass_info.stats_list);
+  tpl_gop_stats_list =
+      ducky_encode.ComputeTplStats(gop_list, tpl_pass_gop_encode_info_list);
+  ducky_encode.EndEncode();
+
+  std::vector<aom::GopEncodeInfo> final_pass_gop_encode_info_list;
+  aom::RefFrameTable ref_frame_table;
+  for (size_t i = 0; i < gop_list.size(); ++i) {
+    const aom::GopStruct &gop_struct = gop_list[i];
+    const aom::TplGopStats &tpl_gop_stats = tpl_gop_stats_list[i];
+    std::vector<aom::LookaheadStats> lookahead_stats = {};
+    for (size_t lookahead_index = 1;
+         lookahead_index <= 1 && i + lookahead_index < gop_list.size();
+         ++lookahead_index) {
+      lookahead_stats.push_back({ &gop_list[i + lookahead_index],
+                                  &tpl_gop_stats_list[i + lookahead_index] });
+    }
+    const auto gop_encode_info = qmode_rc.GetGopEncodeInfo(
+        gop_struct, tpl_gop_stats, lookahead_stats, ref_frame_table);
+    ASSERT_TRUE(gop_encode_info.status().ok());
+    ref_frame_table = gop_encode_info.value().final_snapshot;
+    final_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
+  }
+
+  ducky_encode.StartEncode(firstpass_info.stats_list);
+  std::vector<aom::EncodeFrameResult> encoded_frames_list =
+      ducky_encode.EncodeVideo(gop_list, final_pass_gop_encode_info_list);
+  ducky_encode.EndEncode();
+
+  EXPECT_THAT(encoded_frames_list,
+              testing::Each(testing::Field(
+                  "psnr", &aom::EncodeFrameResult::psnr, testing::Gt(37))));
+}
+
 TEST(DuckyEncodeTest, EncodeFrameMode) {
   EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_NONE,
             static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kNone));