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 // Tests for https://crbug.com/aomedia/3326.
13 //
14 // Set cfg.g_forced_max_frame_width and cfg.g_forced_max_frame_height and
15 // encode two frames of increasing sizes. The second aom_codec_encode() should
16 // not crash or have memory errors.
17
18 #include <algorithm>
19 #include <memory>
20 #include <vector>
21
22 #include "aom/aomcx.h"
23 #include "aom/aom_encoder.h"
24 #include "config/aom_config.h"
25 #include "gtest/gtest.h"
26
27 namespace {
28
29 // cfg.g_lag_in_frames must be set to 0 or 1 to allow the frame size to change,
30 // as required by the following check in encoder_set_config() in
31 // av1/av1_cx_iface.c:
32 //
33 // if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) {
34 // if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS)
35 // ERROR("Cannot change width or height after initialization");
36 // ...
37 // }
38
RunTest(unsigned int usage,unsigned int lag_in_frames,const char * tune_metric)39 void RunTest(unsigned int usage, unsigned int lag_in_frames,
40 const char *tune_metric) {
41 // A buffer of gray samples. Large enough for 128x128 and 256x256, YUV 4:2:0.
42 constexpr size_t kImageDataSize = 256 * 256 + 2 * 128 * 128;
43 std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
44 ASSERT_NE(img_data, nullptr);
45 memset(img_data.get(), 128, kImageDataSize);
46
47 aom_codec_iface_t *iface = aom_codec_av1_cx();
48 aom_codec_enc_cfg_t cfg;
49 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_default(iface, &cfg, usage));
50 cfg.g_w = 128;
51 cfg.g_h = 128;
52 cfg.g_forced_max_frame_width = 256;
53 cfg.g_forced_max_frame_height = 256;
54 cfg.g_lag_in_frames = lag_in_frames;
55 aom_codec_ctx_t enc;
56 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
57 EXPECT_EQ(AOM_CODEC_OK, aom_codec_set_option(&enc, "tune", tune_metric));
58
59 aom_image_t img;
60 EXPECT_EQ(&img,
61 aom_img_wrap(&img, AOM_IMG_FMT_I420, 128, 128, 1, img_data.get()));
62 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
63
64 cfg.g_w = 256;
65 cfg.g_h = 256;
66 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
67
68 EXPECT_EQ(&img,
69 aom_img_wrap(&img, AOM_IMG_FMT_I420, 256, 256, 1, img_data.get()));
70 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
71
72 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
73 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
74 }
75
76 #if !CONFIG_REALTIME_ONLY
77
TEST(EncodeForcedMaxFrameWidthHeight,GoodQualityLag0TunePSNR)78 TEST(EncodeForcedMaxFrameWidthHeight, GoodQualityLag0TunePSNR) {
79 RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/0, "psnr");
80 }
81
TEST(EncodeForcedMaxFrameWidthHeight,GoodQualityLag0TuneSSIM)82 TEST(EncodeForcedMaxFrameWidthHeight, GoodQualityLag0TuneSSIM) {
83 RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/0, "ssim");
84 }
85
TEST(EncodeForcedMaxFrameWidthHeight,GoodQualityLag1TunePSNR)86 TEST(EncodeForcedMaxFrameWidthHeight, GoodQualityLag1TunePSNR) {
87 RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/1, "psnr");
88 }
89
TEST(EncodeForcedMaxFrameWidthHeight,GoodQualityLag1TuneSSIM)90 TEST(EncodeForcedMaxFrameWidthHeight, GoodQualityLag1TuneSSIM) {
91 RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/1, "ssim");
92 }
93
FillImageGradient(aom_image_t * image,int bit_depth)94 void FillImageGradient(aom_image_t *image, int bit_depth) {
95 assert(image->range == AOM_CR_FULL_RANGE);
96 for (int plane = 0; plane < 3; plane++) {
97 const int plane_width = aom_img_plane_width(image, plane);
98 const int plane_height = aom_img_plane_height(image, plane);
99 unsigned char *row = image->planes[plane];
100 const int stride = image->stride[plane];
101 for (int y = 0; y < plane_height; ++y) {
102 for (int x = 0; x < plane_width; ++x) {
103 const int value = (x + y) * ((1 << bit_depth) - 1) /
104 std::max(1, plane_width + plane_height - 2);
105 assert(value >= 0 && value <= (1 << bit_depth) - 1);
106 if (bit_depth > 8) {
107 reinterpret_cast<uint16_t *>(row)[x] = static_cast<uint16_t>(value);
108 } else {
109 row[x] = static_cast<unsigned char>(value);
110 }
111 }
112 row += stride;
113 }
114 }
115 }
116
TEST(EncodeForcedMaxFrameWidthHeight,DimensionDecreasing)117 TEST(EncodeForcedMaxFrameWidthHeight, DimensionDecreasing) {
118 constexpr int kWidth = 128;
119 constexpr int kHeight = 128;
120 constexpr size_t kBufferSize = 3 * kWidth * kHeight;
121 std::vector<unsigned char> buffer(kBufferSize);
122
123 aom_image_t img;
124 EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, kWidth, kHeight, 1,
125 buffer.data()));
126 img.cp = AOM_CICP_CP_UNSPECIFIED;
127 img.tc = AOM_CICP_TC_UNSPECIFIED;
128 img.mc = AOM_CICP_MC_UNSPECIFIED;
129 img.range = AOM_CR_FULL_RANGE;
130 FillImageGradient(&img, 8);
131
132 aom_codec_iface_t *iface = aom_codec_av1_cx();
133 aom_codec_enc_cfg_t cfg;
134 EXPECT_EQ(AOM_CODEC_OK,
135 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_GOOD_QUALITY));
136 cfg.rc_end_usage = AOM_Q;
137 cfg.g_profile = 0;
138 cfg.g_bit_depth = AOM_BITS_8;
139 cfg.g_input_bit_depth = 8;
140 cfg.g_w = kWidth;
141 cfg.g_h = kHeight;
142 cfg.g_forced_max_frame_width = kWidth;
143 cfg.g_forced_max_frame_height = kHeight;
144 cfg.g_lag_in_frames = 1;
145 cfg.rc_min_quantizer = 20;
146 cfg.rc_max_quantizer = 40;
147 aom_codec_ctx_t enc;
148 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
149 EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AOME_SET_CQ_LEVEL, 30));
150 EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AOME_SET_CPUUSED, 6));
151 EXPECT_EQ(AOM_CODEC_OK,
152 aom_codec_control(&enc, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE));
153 EXPECT_EQ(AOM_CODEC_OK,
154 aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIM));
155
156 // First frame
157 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
158 aom_codec_iter_t iter = nullptr;
159 const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&enc, &iter);
160 ASSERT_NE(pkt, nullptr);
161 EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
162 // pkt->data.frame.flags is 0x1f0011.
163 EXPECT_NE(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u);
164 pkt = aom_codec_get_cx_data(&enc, &iter);
165 EXPECT_EQ(pkt, nullptr);
166
167 // Second frame
168 constexpr int kWidthSmall = 64;
169 constexpr int kHeightSmall = 64;
170 EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, kWidthSmall,
171 kHeightSmall, 1, buffer.data()));
172 img.cp = AOM_CICP_CP_UNSPECIFIED;
173 img.tc = AOM_CICP_TC_UNSPECIFIED;
174 img.mc = AOM_CICP_MC_UNSPECIFIED;
175 img.range = AOM_CR_FULL_RANGE;
176 FillImageGradient(&img, 8);
177 cfg.g_w = kWidthSmall;
178 cfg.g_h = kHeightSmall;
179 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
180 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
181 iter = nullptr;
182 pkt = aom_codec_get_cx_data(&enc, &iter);
183 ASSERT_NE(pkt, nullptr);
184 EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
185 // pkt->data.frame.flags is 0.
186 EXPECT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u);
187 pkt = aom_codec_get_cx_data(&enc, &iter);
188 EXPECT_EQ(pkt, nullptr);
189
190 // Flush encoder
191 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 1, 0));
192 iter = nullptr;
193 pkt = aom_codec_get_cx_data(&enc, &iter);
194 EXPECT_EQ(pkt, nullptr);
195
196 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
197 }
198
199 #endif // !CONFIG_REALTIME_ONLY
200
TEST(EncodeForcedMaxFrameWidthHeight,RealtimeLag0TunePSNR)201 TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag0TunePSNR) {
202 RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/0, "psnr");
203 }
204
TEST(EncodeForcedMaxFrameWidthHeight,RealtimeLag0TuneSSIM)205 TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag0TuneSSIM) {
206 RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/0, "ssim");
207 }
208
TEST(EncodeForcedMaxFrameWidthHeight,RealtimeLag1TunePSNR)209 TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag1TunePSNR) {
210 RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/1, "psnr");
211 }
212
TEST(EncodeForcedMaxFrameWidthHeight,RealtimeLag1TuneSSIM)213 TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag1TuneSSIM) {
214 RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/1, "ssim");
215 }
216
TEST(EncodeForcedMaxFrameWidthHeight,MaxFrameSizeTooBig)217 TEST(EncodeForcedMaxFrameWidthHeight, MaxFrameSizeTooBig) {
218 aom_codec_iface_t *iface = aom_codec_av1_cx();
219 aom_codec_enc_cfg_t cfg;
220 EXPECT_EQ(AOM_CODEC_OK,
221 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
222 cfg.g_w = 256;
223 cfg.g_h = 256;
224 cfg.g_forced_max_frame_width = 131072;
225 cfg.g_forced_max_frame_height = 131072;
226 aom_codec_ctx_t enc;
227 EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_init(&enc, iface, &cfg, 0));
228 }
229
TEST(EncodeForcedMaxFrameWidthHeight,FirstFrameTooBig)230 TEST(EncodeForcedMaxFrameWidthHeight, FirstFrameTooBig) {
231 aom_codec_iface_t *iface = aom_codec_av1_cx();
232 aom_codec_enc_cfg_t cfg;
233 EXPECT_EQ(AOM_CODEC_OK,
234 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
235 cfg.g_w = 258;
236 cfg.g_h = 256;
237 cfg.g_forced_max_frame_width = 256;
238 cfg.g_forced_max_frame_height = 256;
239 aom_codec_ctx_t enc;
240 EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_init(&enc, iface, &cfg, 0));
241 cfg.g_w = 256;
242 cfg.g_h = 258;
243 EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_init(&enc, iface, &cfg, 0));
244 cfg.g_w = 256;
245 cfg.g_h = 256;
246 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
247 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
248 }
249
TEST(EncodeForcedMaxFrameWidthHeight,SecondFrameTooBig)250 TEST(EncodeForcedMaxFrameWidthHeight, SecondFrameTooBig) {
251 // A buffer of gray samples. Large enough for 128x128 and 256x256, YUV 4:2:0.
252 constexpr size_t kImageDataSize = 256 * 256 + 2 * 128 * 128;
253 std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
254 ASSERT_NE(img_data, nullptr);
255 memset(img_data.get(), 128, kImageDataSize);
256
257 aom_codec_iface_t *iface = aom_codec_av1_cx();
258 aom_codec_enc_cfg_t cfg;
259 EXPECT_EQ(AOM_CODEC_OK,
260 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
261 cfg.g_w = 128;
262 cfg.g_h = 128;
263 cfg.g_forced_max_frame_width = 255;
264 cfg.g_forced_max_frame_height = 256;
265 aom_codec_ctx_t enc;
266 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
267
268 aom_image_t img;
269 EXPECT_EQ(&img,
270 aom_img_wrap(&img, AOM_IMG_FMT_I420, 128, 128, 1, img_data.get()));
271 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
272
273 cfg.g_w = 256;
274 cfg.g_h = 256;
275 EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_config_set(&enc, &cfg));
276
277 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
278 }
279
280 } // namespace
281