xref: /aosp_15_r20/external/libaom/test/forced_max_frame_width_height_test.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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