1 /*
2 * Copyright (c) 2022, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #include <array>
13 #include <algorithm>
14 #include <cerrno>
15 #include <cstring>
16 #include <fstream>
17 #include <memory>
18 #include <numeric>
19 #include <string>
20 #include <vector>
21
22 #include "av1/encoder/encoder.h"
23 #include "av1/qmode_rc/ducky_encode.h"
24 #include "av1/qmode_rc/ratectrl_qmode.h"
25 #include "av1/qmode_rc/ratectrl_qmode_interface.h"
26 #include "test/video_source.h"
27 #include "third_party/googletest/src/googlemock/include/gmock/gmock.h"
28 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
29
30 namespace aom {
31
32 constexpr int kMaxRefFrames = 7;
33
TEST(DuckyEncodeTest,ComputeFirstPassStats)34 TEST(DuckyEncodeTest, ComputeFirstPassStats) {
35 aom_rational_t frame_rate = { 30, 1 };
36 VideoInfo video_info = { 352, 288,
37 frame_rate, AOM_IMG_FMT_I420,
38 1, "bus_352x288_420_f20_b8.yuv" };
39 video_info.file_path =
40 libaom_test::GetDataPath() + "/" + video_info.file_path;
41 DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
42 std::vector<FIRSTPASS_STATS> frame_stats =
43 ducky_encode.ComputeFirstPassStats();
44 EXPECT_EQ(frame_stats.size(), static_cast<size_t>(video_info.frame_count));
45 for (size_t i = 0; i < frame_stats.size(); ++i) {
46 // FIRSTPASS_STATS's first element is frame
47 EXPECT_EQ(frame_stats[i].frame, i);
48 }
49 }
50
TEST(DuckyEncodeTest,EncodeFrame)51 TEST(DuckyEncodeTest, EncodeFrame) {
52 aom_rational_t frame_rate = { 30, 1 };
53 VideoInfo video_info = { 352, 288,
54 frame_rate, AOM_IMG_FMT_I420,
55 17, "bus_352x288_420_f20_b8.yuv" };
56 video_info.file_path =
57 libaom_test::GetDataPath() + "/" + video_info.file_path;
58 DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
59 std::vector<FIRSTPASS_STATS> frame_stats =
60 ducky_encode.ComputeFirstPassStats();
61 ducky_encode.StartEncode(frame_stats);
62 // We set coding_frame_count to a arbitrary number that smaller than
63 // 17 here.
64 // TODO(angiebird): Set coding_frame_count properly, once the DuckyEncode can
65 // provide proper information.
66 int coding_frame_count = 5;
67 EncodeFrameDecision decision = { aom::EncodeFrameMode::kNone,
68 aom::EncodeGopMode::kNone,
69 {} };
70 for (int i = 0; i < coding_frame_count; ++i) {
71 ducky_encode.AllocateBitstreamBuffer(video_info);
72 EncodeFrameResult encode_frame_result = ducky_encode.EncodeFrame(decision);
73 }
74 ducky_encode.EndEncode();
75 }
76
TEST(DuckyEncodeTest,EncodeFrameWithQindex)77 TEST(DuckyEncodeTest, EncodeFrameWithQindex) {
78 aom_rational_t frame_rate = { 30, 1 };
79 VideoInfo video_info = { 352, 288,
80 frame_rate, AOM_IMG_FMT_I420,
81 17, "bus_352x288_420_f20_b8.yuv" };
82 video_info.file_path =
83 libaom_test::GetDataPath() + "/" + video_info.file_path;
84 DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
85 std::vector<FIRSTPASS_STATS> frame_stats =
86 ducky_encode.ComputeFirstPassStats();
87 ducky_encode.StartEncode(frame_stats);
88 // We set coding_frame_count to a arbitrary number that smaller than
89 // 17 here.
90 // TODO(angiebird): Set coding_frame_count properly, once the DuckyEncode can
91 // provide proper information.
92 int coding_frame_count = 5;
93 int q_index = 0;
94 EncodeFrameDecision decision = { aom::EncodeFrameMode::kQindex,
95 aom::EncodeGopMode::kNone,
96 { q_index, -1, {}, {} } };
97 for (int i = 0; i < coding_frame_count; ++i) {
98 ducky_encode.AllocateBitstreamBuffer(video_info);
99 EncodeFrameResult encode_frame_result = ducky_encode.EncodeFrame(decision);
100 EXPECT_EQ(encode_frame_result.dist, 0);
101 }
102 ducky_encode.EndEncode();
103 }
104
TEST(DuckyEncodeRCTest,EncodeVideoWithRC)105 TEST(DuckyEncodeRCTest, EncodeVideoWithRC) {
106 aom_rational_t frame_rate = { 30, 1 };
107 const int frame_number = 35;
108 const int frame_width = 352;
109 const int frame_height = 288;
110 VideoInfo video_info = { frame_width, frame_height,
111 frame_rate, AOM_IMG_FMT_I420,
112 frame_number, "bus_352x288_420_f20_b8.yuv" };
113 video_info.file_path =
114 libaom_test::GetDataPath() + "/" + video_info.file_path;
115 DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
116
117 AV1RateControlQMode qmode_rc;
118 RateControlParam rc_param = {};
119 rc_param.max_gop_show_frame_count = 16;
120 rc_param.min_gop_show_frame_count = 4;
121 rc_param.ref_frame_table_size = 5;
122 rc_param.max_ref_frames = 3;
123 rc_param.base_q_index = 45;
124 rc_param.max_distinct_q_indices_per_frame = 8;
125 rc_param.max_distinct_lambda_scales_per_frame = 1;
126 rc_param.frame_width = frame_width;
127 rc_param.frame_height = frame_height;
128 rc_param.tpl_pass_count = TplPassCount::kOneTplPass;
129 rc_param.tpl_pass_index = 0;
130 const Status status = qmode_rc.SetRcParam(rc_param);
131 ASSERT_TRUE(status.ok());
132 FirstpassInfo firstpass_info;
133 firstpass_info.stats_list = ducky_encode.ComputeFirstPassStats();
134 constexpr int kBlockSize = 16;
135 firstpass_info.num_mbs_16x16 = ((frame_width + kBlockSize - 1) / kBlockSize) *
136 ((frame_height + kBlockSize - 1) / kBlockSize);
137 const auto gop_info = qmode_rc.DetermineGopInfo(firstpass_info);
138 ASSERT_TRUE(gop_info.status().ok());
139 const GopStructList &gop_list = gop_info.value();
140
141 std::vector<aom::GopEncodeInfo> tpl_pass_gop_encode_info_list;
142 std::vector<aom::TplGopStats> tpl_gop_stats_list;
143 for (const auto &gop_struct : gop_list) {
144 const auto gop_encode_info =
145 qmode_rc.GetTplPassGopEncodeInfo(gop_struct, firstpass_info);
146 ASSERT_TRUE(gop_encode_info.status().ok());
147 tpl_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
148 }
149
150 tpl_gop_stats_list = ducky_encode.ComputeTplStats(
151 firstpass_info.stats_list, gop_list, tpl_pass_gop_encode_info_list);
152
153 std::vector<aom::GopEncodeInfo> final_pass_gop_encode_info_list;
154 aom::RefFrameTable ref_frame_table;
155 for (size_t i = 0; i < gop_list.size(); ++i) {
156 const aom::GopStruct &gop_struct = gop_list[i];
157 const aom::TplGopStats &tpl_gop_stats = tpl_gop_stats_list[i];
158 std::vector<aom::LookaheadStats> lookahead_stats = {};
159 for (size_t lookahead_index = 1;
160 lookahead_index <= 1 && i + lookahead_index < gop_list.size();
161 ++lookahead_index) {
162 lookahead_stats.push_back({ &gop_list[i + lookahead_index],
163 &tpl_gop_stats_list[i + lookahead_index] });
164 }
165 const auto gop_encode_info =
166 qmode_rc.GetGopEncodeInfo(gop_struct, tpl_gop_stats, lookahead_stats,
167 firstpass_info, ref_frame_table);
168 ASSERT_TRUE(gop_encode_info.status().ok());
169 ref_frame_table = gop_encode_info.value().final_snapshot;
170 final_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
171 }
172
173 ducky_encode.StartEncode(firstpass_info.stats_list);
174 std::vector<aom::EncodeFrameResult> encoded_frames_list =
175 ducky_encode.EncodeVideo(gop_list, final_pass_gop_encode_info_list);
176 ducky_encode.EndEncode();
177
178 EXPECT_THAT(encoded_frames_list,
179 testing::Each(testing::Field(
180 "psnr", &aom::EncodeFrameResult::psnr, testing::Gt(37))));
181 }
182
TEST(DuckyEncodeTest,EncodeFrameMode)183 TEST(DuckyEncodeTest, EncodeFrameMode) {
184 EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_NONE,
185 static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kNone));
186 EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_QINDEX,
187 static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kQindex));
188 EXPECT_EQ(
189 DUCKY_ENCODE_FRAME_MODE_QINDEX_RDMULT,
190 static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kQindexRdmult));
191 }
192
193 } // namespace aom
194