1 /*
2 * Copyright (c) 2016, 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 <climits>
13 #include <vector>
14
15 #include "aom/aomcx.h"
16 #include "aom_dsp/aom_dsp_common.h"
17 #include "av1/encoder/encoder.h"
18 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
19 #include "test/codec_factory.h"
20 #include "test/encode_test_driver.h"
21 #include "test/i420_video_source.h"
22 #include "test/util.h"
23 #include "test/video_source.h"
24 #include "test/y4m_video_source.h"
25
26 // Enable(1) or Disable(0) writing of the compressed bitstream.
27 #define WRITE_COMPRESSED_STREAM 0
28
29 namespace {
30
31 #if WRITE_COMPRESSED_STREAM
mem_put_le16(char * const mem,unsigned int val)32 static void mem_put_le16(char *const mem, unsigned int val) {
33 mem[0] = val;
34 mem[1] = val >> 8;
35 }
36
mem_put_le32(char * const mem,unsigned int val)37 static void mem_put_le32(char *const mem, unsigned int val) {
38 mem[0] = val;
39 mem[1] = val >> 8;
40 mem[2] = val >> 16;
41 mem[3] = val >> 24;
42 }
43
write_ivf_file_header(const aom_codec_enc_cfg_t * const cfg,int frame_cnt,FILE * const outfile)44 static void write_ivf_file_header(const aom_codec_enc_cfg_t *const cfg,
45 int frame_cnt, FILE *const outfile) {
46 char header[32];
47
48 header[0] = 'D';
49 header[1] = 'K';
50 header[2] = 'I';
51 header[3] = 'F';
52 mem_put_le16(header + 4, 0); /* version */
53 mem_put_le16(header + 6, 32); /* headersize */
54 mem_put_le32(header + 8, AV1_FOURCC); /* fourcc (av1) */
55 mem_put_le16(header + 12, cfg->g_w); /* width */
56 mem_put_le16(header + 14, cfg->g_h); /* height */
57 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */
58 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */
59 mem_put_le32(header + 24, frame_cnt); /* length */
60 mem_put_le32(header + 28, 0); /* unused */
61
62 (void)fwrite(header, 1, 32, outfile);
63 }
64
write_ivf_frame_size(FILE * const outfile,const size_t size)65 static void write_ivf_frame_size(FILE *const outfile, const size_t size) {
66 char header[4];
67 mem_put_le32(header, static_cast<unsigned int>(size));
68 (void)fwrite(header, 1, 4, outfile);
69 }
70
write_ivf_frame_header(const aom_codec_cx_pkt_t * const pkt,FILE * const outfile)71 static void write_ivf_frame_header(const aom_codec_cx_pkt_t *const pkt,
72 FILE *const outfile) {
73 char header[12];
74 aom_codec_pts_t pts;
75
76 if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return;
77
78 pts = pkt->data.frame.pts;
79 mem_put_le32(header, static_cast<unsigned int>(pkt->data.frame.sz));
80 mem_put_le32(header + 4, pts & 0xFFFFFFFF);
81 mem_put_le32(header + 8, pts >> 32);
82
83 (void)fwrite(header, 1, 12, outfile);
84 }
85 #endif // WRITE_COMPRESSED_STREAM
86
87 const unsigned int kInitialWidth = 320;
88 const unsigned int kInitialHeight = 240;
89
90 struct FrameInfo {
FrameInfo__anon433622bc0111::FrameInfo91 FrameInfo(aom_codec_pts_t _pts, unsigned int _w, unsigned int _h)
92 : pts(_pts), w(_w), h(_h) {}
93
94 aom_codec_pts_t pts;
95 unsigned int w;
96 unsigned int h;
97 };
98
ScaleForFrameNumber(unsigned int frame,unsigned int initial_w,unsigned int initial_h,int flag_codec,bool change_start_resln,unsigned int * w,unsigned int * h)99 void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w,
100 unsigned int initial_h, int flag_codec,
101 bool change_start_resln, unsigned int *w,
102 unsigned int *h) {
103 if (frame < 10) {
104 if (change_start_resln) {
105 *w = initial_w / 4;
106 *h = initial_h / 4;
107 } else {
108 *w = initial_w;
109 *h = initial_h;
110 }
111 return;
112 }
113 if (frame < 20) {
114 *w = initial_w * 3 / 4;
115 *h = initial_h * 3 / 4;
116 return;
117 }
118 if (frame < 30) {
119 *w = initial_w / 2;
120 *h = initial_h / 2;
121 return;
122 }
123 if (frame < 40) {
124 *w = initial_w;
125 *h = initial_h;
126 return;
127 }
128 if (frame < 50) {
129 *w = initial_w * 3 / 4;
130 *h = initial_h * 3 / 4;
131 return;
132 }
133 if (frame < 60) {
134 *w = initial_w / 2;
135 *h = initial_h / 2;
136 return;
137 }
138 if (frame < 70) {
139 *w = initial_w;
140 *h = initial_h;
141 return;
142 }
143 if (frame < 80) {
144 *w = initial_w * 3 / 4;
145 *h = initial_h * 3 / 4;
146 return;
147 }
148 if (frame < 90) {
149 *w = initial_w / 2;
150 *h = initial_h / 2;
151 return;
152 }
153 if (frame < 100) {
154 *w = initial_w * 3 / 4;
155 *h = initial_h * 3 / 4;
156 return;
157 }
158 if (frame < 110) {
159 *w = initial_w;
160 *h = initial_h;
161 return;
162 }
163 // Go down very low
164 if (frame < 120) {
165 *w = initial_w / 4;
166 *h = initial_h / 4;
167 return;
168 }
169 if (flag_codec == 1) {
170 // Cases that only works for AV1.
171 // For AV1: Swap width and height of original.
172 if (frame < 140) {
173 *w = initial_h;
174 *h = initial_w;
175 return;
176 }
177 }
178 *w = initial_w;
179 *h = initial_h;
180 }
181
182 class ResizingVideoSource : public ::libaom_test::DummyVideoSource {
183 public:
ResizingVideoSource()184 ResizingVideoSource() {
185 SetSize(kInitialWidth, kInitialHeight);
186 limit_ = 150;
187 }
188 int flag_codec_;
189 bool change_start_resln_;
190 ~ResizingVideoSource() override = default;
191
192 protected:
Begin()193 void Begin() override {
194 frame_ = 0;
195 unsigned int width;
196 unsigned int height;
197 ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, flag_codec_,
198 change_start_resln_, &width, &height);
199 SetSize(width, height);
200 FillFrame();
201 }
Next()202 void Next() override {
203 ++frame_;
204 unsigned int width;
205 unsigned int height;
206 ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, flag_codec_,
207 change_start_resln_, &width, &height);
208 SetSize(width, height);
209 FillFrame();
210 }
211 };
212
213 class ResizeTest
214 : public ::libaom_test::CodecTestWithParam<libaom_test::TestMode>,
215 public ::libaom_test::EncoderTest {
216 protected:
ResizeTest()217 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
218
219 ~ResizeTest() override = default;
220
SetUp()221 void SetUp() override { InitializeConfig(GET_PARAM(1)); }
222
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)223 void PreEncodeFrameHook(libaom_test::VideoSource *video,
224 libaom_test::Encoder *encoder) override {
225 if (video->frame() == 0) {
226 if (GET_PARAM(1) == ::libaom_test::kRealTime) {
227 encoder->Control(AV1E_SET_AQ_MODE, 3);
228 encoder->Control(AOME_SET_CPUUSED, 5);
229 encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
230 }
231 }
232 }
233
DecompressedFrameHook(const aom_image_t & img,aom_codec_pts_t pts)234 void DecompressedFrameHook(const aom_image_t &img,
235 aom_codec_pts_t pts) override {
236 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
237 }
238
239 std::vector<FrameInfo> frame_info_list_;
240 };
241
TEST_P(ResizeTest,TestExternalResizeWorks)242 TEST_P(ResizeTest, TestExternalResizeWorks) {
243 ResizingVideoSource video;
244 video.flag_codec_ = 0;
245 video.change_start_resln_ = false;
246 cfg_.g_lag_in_frames = 0;
247 // We use max(kInitialWidth, kInitialHeight) because during the test
248 // the width and height of the frame are swapped
249 cfg_.g_forced_max_frame_width = cfg_.g_forced_max_frame_height =
250 AOMMAX(kInitialWidth, kInitialHeight);
251 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
252
253 // Check we decoded the same number of frames as we attempted to encode
254 ASSERT_EQ(frame_info_list_.size(), video.limit());
255
256 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
257 info != frame_info_list_.end(); ++info) {
258 const unsigned int frame = static_cast<unsigned>(info->pts);
259 unsigned int expected_w;
260 unsigned int expected_h;
261 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, video.flag_codec_,
262 video.change_start_resln_, &expected_w, &expected_h);
263 EXPECT_EQ(expected_w, info->w)
264 << "Frame " << frame << " had unexpected width";
265 EXPECT_EQ(expected_h, info->h)
266 << "Frame " << frame << " had unexpected height";
267 }
268 }
269
270 #if !CONFIG_REALTIME_ONLY
271 const unsigned int kStepDownFrame = 3;
272 const unsigned int kStepUpFrame = 6;
273
274 class ResizeInternalTestLarge : public ResizeTest {
275 protected:
276 #if WRITE_COMPRESSED_STREAM
ResizeInternalTestLarge()277 ResizeInternalTestLarge()
278 : ResizeTest(), frame0_psnr_(0.0), outfile_(nullptr), out_frames_(0) {}
279 #else
280 ResizeInternalTestLarge() : ResizeTest(), frame0_psnr_(0.0) {}
281 #endif
282
283 ~ResizeInternalTestLarge() override = default;
284
BeginPassHook(unsigned int)285 void BeginPassHook(unsigned int /*pass*/) override {
286 #if WRITE_COMPRESSED_STREAM
287 outfile_ = fopen("av10-2-05-resize.ivf", "wb");
288 #endif
289 }
290
EndPassHook()291 void EndPassHook() override {
292 #if WRITE_COMPRESSED_STREAM
293 if (outfile_) {
294 if (!fseek(outfile_, 0, SEEK_SET))
295 write_ivf_file_header(&cfg_, out_frames_, outfile_);
296 fclose(outfile_);
297 outfile_ = nullptr;
298 }
299 #endif
300 }
301
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)302 void PreEncodeFrameHook(libaom_test::VideoSource *video,
303 libaom_test::Encoder *encoder) override {
304 if (change_config_) {
305 int new_q = 60;
306 if (video->frame() == 0) {
307 struct aom_scaling_mode mode = { AOME_ONETWO, AOME_ONETWO };
308 encoder->Control(AOME_SET_SCALEMODE, &mode);
309 } else if (video->frame() == 1) {
310 struct aom_scaling_mode mode = { AOME_NORMAL, AOME_NORMAL };
311 encoder->Control(AOME_SET_SCALEMODE, &mode);
312 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = new_q;
313 encoder->Config(&cfg_);
314 }
315 } else {
316 if (video->frame() >= kStepDownFrame && video->frame() < kStepUpFrame) {
317 struct aom_scaling_mode mode = { AOME_FOURFIVE, AOME_THREEFIVE };
318 encoder->Control(AOME_SET_SCALEMODE, &mode);
319 }
320 if (video->frame() >= kStepUpFrame) {
321 struct aom_scaling_mode mode = { AOME_NORMAL, AOME_NORMAL };
322 encoder->Control(AOME_SET_SCALEMODE, &mode);
323 }
324 }
325 }
326
PSNRPktHook(const aom_codec_cx_pkt_t * pkt)327 void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) override {
328 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
329 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 4.1);
330 }
331
332 #if WRITE_COMPRESSED_STREAM
FramePktHook(const aom_codec_cx_pkt_t * pkt)333 void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
334 ++out_frames_;
335
336 // Write initial file header if first frame.
337 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
338
339 // Write frame header and data.
340 write_ivf_frame_header(pkt, outfile_);
341 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
342 }
343 #endif
344
345 double frame0_psnr_;
346 bool change_config_;
347 #if WRITE_COMPRESSED_STREAM
348 FILE *outfile_;
349 unsigned int out_frames_;
350 #endif
351 };
352
TEST_P(ResizeInternalTestLarge,TestInternalResizeWorks)353 TEST_P(ResizeInternalTestLarge, TestInternalResizeWorks) {
354 ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
355 30, 1, 0, 10);
356 init_flags_ = AOM_CODEC_USE_PSNR;
357 change_config_ = false;
358
359 // q picked such that initial keyframe on this clip is ~30dB PSNR
360 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
361
362 // If the number of frames being encoded is smaller than g_lag_in_frames
363 // the encoded frame is unavailable using the current API. Comparing
364 // frames to detect mismatch would then not be possible. Set
365 // g_lag_in_frames = 0 to get around this.
366 cfg_.g_lag_in_frames = 0;
367 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
368
369 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
370 info != frame_info_list_.end(); ++info) {
371 }
372 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
373 info != frame_info_list_.end(); ++info) {
374 const aom_codec_pts_t pts = info->pts;
375 if (pts >= kStepDownFrame && pts < kStepUpFrame) {
376 ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width";
377 ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height";
378 } else {
379 EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width";
380 EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height";
381 }
382 }
383 }
384
TEST_P(ResizeInternalTestLarge,TestInternalResizeChangeConfig)385 TEST_P(ResizeInternalTestLarge, TestInternalResizeChangeConfig) {
386 ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
387 30, 1, 0, 10);
388 cfg_.g_w = 352;
389 cfg_.g_h = 288;
390 change_config_ = true;
391 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
392 }
393
394 AV1_INSTANTIATE_TEST_SUITE(ResizeInternalTestLarge,
395 ::testing::Values(::libaom_test::kOnePassGood));
396 #endif
397
398 // Parameters: test mode, speed, threads
399 class ResizeRealtimeTest
400 : public ::libaom_test::CodecTestWith3Params<libaom_test::TestMode, int,
401 int>,
402 public ::libaom_test::EncoderTest {
403 protected:
ResizeRealtimeTest()404 ResizeRealtimeTest()
405 : EncoderTest(GET_PARAM(0)), num_threads_(GET_PARAM(3)),
406 set_scale_mode_(false), set_scale_mode2_(false),
407 set_scale_mode3_(false), is_screen_(false) {}
408 ~ResizeRealtimeTest() override = default;
409
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)410 void PreEncodeFrameHook(libaom_test::VideoSource *video,
411 libaom_test::Encoder *encoder) override {
412 if (video->frame() == 0) {
413 encoder->Control(AV1E_SET_AQ_MODE, 3);
414 encoder->Control(AV1E_SET_ALLOW_WARPED_MOTION, 0);
415 encoder->Control(AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
416 encoder->Control(AV1E_SET_ENABLE_OBMC, 0);
417 encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
418 encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
419 if (is_screen_)
420 encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN);
421 }
422 if (set_scale_mode_) {
423 struct aom_scaling_mode mode;
424 if (video->frame() <= 20)
425 mode = { AOME_ONETWO, AOME_ONETWO };
426 else if (video->frame() <= 40)
427 mode = { AOME_ONEFOUR, AOME_ONEFOUR };
428 else if (video->frame() > 40)
429 mode = { AOME_NORMAL, AOME_NORMAL };
430 encoder->Control(AOME_SET_SCALEMODE, &mode);
431 } else if (set_scale_mode2_) {
432 struct aom_scaling_mode mode;
433 if (video->frame() <= 20)
434 mode = { AOME_ONEFOUR, AOME_ONEFOUR };
435 else if (video->frame() <= 40)
436 mode = { AOME_ONETWO, AOME_ONETWO };
437 else if (video->frame() > 40)
438 mode = { AOME_THREEFOUR, AOME_THREEFOUR };
439 encoder->Control(AOME_SET_SCALEMODE, &mode);
440 } else if (set_scale_mode3_) {
441 struct aom_scaling_mode mode;
442 if (video->frame() <= 30)
443 mode = { AOME_ONETWO, AOME_NORMAL };
444 else
445 mode = { AOME_NORMAL, AOME_NORMAL };
446 encoder->Control(AOME_SET_SCALEMODE, &mode);
447 }
448
449 if (change_bitrate_ && video->frame() == frame_change_bitrate_) {
450 change_bitrate_ = false;
451 cfg_.rc_target_bitrate = 500;
452 encoder->Config(&cfg_);
453 }
454 }
455
SetUp()456 void SetUp() override {
457 InitializeConfig(GET_PARAM(1));
458 set_cpu_used_ = GET_PARAM(2);
459 }
460
DecompressedFrameHook(const aom_image_t & img,aom_codec_pts_t pts)461 void DecompressedFrameHook(const aom_image_t &img,
462 aom_codec_pts_t pts) override {
463 frame_info_list_.push_back(FrameInfo(pts, img.d_w, img.d_h));
464 }
465
MismatchHook(const aom_image_t * img1,const aom_image_t * img2)466 void MismatchHook(const aom_image_t *img1, const aom_image_t *img2) override {
467 double mismatch_psnr = compute_psnr(img1, img2);
468 mismatch_psnr_ += mismatch_psnr;
469 ++mismatch_nframes_;
470 }
471
GetMismatchFrames()472 unsigned int GetMismatchFrames() { return mismatch_nframes_; }
473
DefaultConfig()474 void DefaultConfig() {
475 cfg_.rc_buf_initial_sz = 500;
476 cfg_.rc_buf_optimal_sz = 600;
477 cfg_.rc_buf_sz = 1000;
478 cfg_.rc_min_quantizer = 2;
479 cfg_.rc_max_quantizer = 56;
480 cfg_.rc_undershoot_pct = 50;
481 cfg_.rc_overshoot_pct = 50;
482 cfg_.rc_end_usage = AOM_CBR;
483 cfg_.kf_mode = AOM_KF_AUTO;
484 cfg_.g_lag_in_frames = 0;
485 cfg_.kf_min_dist = cfg_.kf_max_dist = 3000;
486 // Enable dropped frames.
487 cfg_.rc_dropframe_thresh = 1;
488 // Disable error_resilience mode.
489 cfg_.g_error_resilient = 0;
490 cfg_.g_threads = num_threads_;
491 // Run at low bitrate.
492 cfg_.rc_target_bitrate = 200;
493 // We use max(kInitialWidth, kInitialHeight) because during the test
494 // the width and height of the frame are swapped
495 cfg_.g_forced_max_frame_width = cfg_.g_forced_max_frame_height =
496 AOMMAX(kInitialWidth, kInitialHeight);
497 if (set_scale_mode_ || set_scale_mode2_ || set_scale_mode3_) {
498 cfg_.rc_dropframe_thresh = 0;
499 cfg_.g_forced_max_frame_width = 1280;
500 cfg_.g_forced_max_frame_height = 1280;
501 }
502 }
503
504 std::vector<FrameInfo> frame_info_list_;
505 int set_cpu_used_;
506 int num_threads_;
507 bool change_bitrate_;
508 unsigned int frame_change_bitrate_;
509 double mismatch_psnr_;
510 int mismatch_nframes_;
511 bool set_scale_mode_;
512 bool set_scale_mode2_;
513 bool set_scale_mode3_;
514 bool is_screen_;
515 };
516
517 // Check the AOME_SET_SCALEMODE control by downsizing to
518 // 1/2, then 1/4, and then back up to originsal.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode1)519 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode1) {
520 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
521 cfg_.g_w = 1280;
522 cfg_.g_h = 720;
523 set_scale_mode_ = true;
524 set_scale_mode2_ = false;
525 set_scale_mode3_ = false;
526 DefaultConfig();
527 change_bitrate_ = false;
528 mismatch_nframes_ = 0;
529 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
530 // Check we decoded the same number of frames as we attempted to encode
531 ASSERT_EQ(frame_info_list_.size(), video.limit());
532 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
533 info != frame_info_list_.end(); ++info) {
534 const auto frame = static_cast<unsigned>(info->pts);
535 unsigned int expected_w = 1280 >> 1;
536 unsigned int expected_h = 720 >> 1;
537 if (frame > 40) {
538 expected_w = 1280;
539 expected_h = 720;
540 } else if (frame > 20 && frame <= 40) {
541 expected_w = 1280 >> 2;
542 expected_h = 720 >> 2;
543 }
544 EXPECT_EQ(expected_w, info->w)
545 << "Frame " << frame << " had unexpected width";
546 EXPECT_EQ(expected_h, info->h)
547 << "Frame " << frame << " had unexpected height";
548 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
549 }
550 }
551
552 // Check the AOME_SET_SCALEMODE control by downsizing to
553 // 1/2, then 1/4, and then back up to originsal.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode1QVGA)554 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode1QVGA) {
555 ::libaom_test::I420VideoSource video("desktop1.320_180.yuv", 320, 180, 30, 1,
556 0, 80);
557 cfg_.g_w = 320;
558 cfg_.g_h = 180;
559 set_scale_mode_ = true;
560 set_scale_mode2_ = false;
561 set_scale_mode3_ = false;
562 DefaultConfig();
563 change_bitrate_ = false;
564 mismatch_nframes_ = 0;
565 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
566 // Check we decoded the same number of frames as we attempted to encode
567 ASSERT_EQ(frame_info_list_.size(), video.limit());
568 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
569 info != frame_info_list_.end(); ++info) {
570 const auto frame = static_cast<unsigned>(info->pts);
571 unsigned int expected_w = 320 >> 1;
572 unsigned int expected_h = 180 >> 1;
573 if (frame > 40) {
574 expected_w = 320;
575 expected_h = 180;
576 } else if (frame > 20 && frame <= 40) {
577 expected_w = 320 >> 2;
578 expected_h = 180 >> 2;
579 }
580 EXPECT_EQ(expected_w, info->w)
581 << "Frame " << frame << " had unexpected width";
582 EXPECT_EQ(expected_h, info->h)
583 << "Frame " << frame << " had unexpected height";
584 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
585 }
586 }
587
588 // Check the AOME_SET_SCALEMODE control by downsizing to
589 // 1/4, then 1/2, and then up to 3/4.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode2)590 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode2) {
591 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
592 cfg_.g_w = 1280;
593 cfg_.g_h = 720;
594 set_scale_mode_ = false;
595 set_scale_mode2_ = true;
596 set_scale_mode3_ = false;
597 DefaultConfig();
598 change_bitrate_ = false;
599 mismatch_nframes_ = 0;
600 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
601 // Check we decoded the same number of frames as we attempted to encode
602 ASSERT_EQ(frame_info_list_.size(), video.limit());
603 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
604 info != frame_info_list_.end(); ++info) {
605 const auto frame = static_cast<unsigned>(info->pts);
606 unsigned int expected_w = 1280 >> 2;
607 unsigned int expected_h = 720 >> 2;
608 if (frame > 40) {
609 expected_w = (3 * 1280) >> 2;
610 expected_h = (3 * 720) >> 2;
611 } else if (frame > 20 && frame <= 40) {
612 expected_w = 1280 >> 1;
613 expected_h = 720 >> 1;
614 }
615 EXPECT_EQ(expected_w, info->w)
616 << "Frame " << frame << " had unexpected width";
617 EXPECT_EQ(expected_h, info->h)
618 << "Frame " << frame << " had unexpected height";
619 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
620 }
621 }
622
623 // Check the AOME_SET_SCALEMODE control by downsizing to
624 // 1/2 horizontally only and then back up to original.
TEST_P(ResizeRealtimeTest,TestInternalResizeSetScaleMode3)625 TEST_P(ResizeRealtimeTest, TestInternalResizeSetScaleMode3) {
626 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
627 cfg_.g_w = 1280;
628 cfg_.g_h = 720;
629 set_scale_mode_ = false;
630 set_scale_mode2_ = false;
631 set_scale_mode3_ = true;
632 DefaultConfig();
633 change_bitrate_ = false;
634 mismatch_nframes_ = 0;
635 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
636 // Check we decoded the same number of frames as we attempted to encode
637 ASSERT_EQ(frame_info_list_.size(), video.limit());
638 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
639 info != frame_info_list_.end(); ++info) {
640 const auto frame = static_cast<unsigned>(info->pts);
641 unsigned int expected_w = 640;
642 unsigned int expected_h = 720;
643 if (frame > 30) {
644 expected_w = 1280;
645 expected_h = 720;
646 }
647 EXPECT_EQ(expected_w, info->w)
648 << "Frame " << frame << " had unexpected width";
649 EXPECT_EQ(expected_h, info->h)
650 << "Frame " << frame << " had unexpected height";
651 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
652 }
653 }
654
TEST_P(ResizeRealtimeTest,TestExternalResizeWorks)655 TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) {
656 ResizingVideoSource video;
657 video.flag_codec_ = 1;
658 change_bitrate_ = false;
659 set_scale_mode_ = false;
660 set_scale_mode2_ = false;
661 set_scale_mode3_ = false;
662 mismatch_psnr_ = 0.0;
663 mismatch_nframes_ = 0;
664 DefaultConfig();
665 // Test external resizing with start resolution equal to
666 // 1. kInitialWidth and kInitialHeight
667 // 2. down-scaled kInitialWidth and kInitialHeight
668 for (int i = 0; i < 2; i++) {
669 video.change_start_resln_ = static_cast<bool>(i);
670
671 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
672
673 // Check we decoded the same number of frames as we attempted to encode
674 ASSERT_EQ(frame_info_list_.size(), video.limit());
675 for (const auto &info : frame_info_list_) {
676 const unsigned int frame = static_cast<unsigned>(info.pts);
677 unsigned int expected_w;
678 unsigned int expected_h;
679 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight,
680 video.flag_codec_, video.change_start_resln_,
681 &expected_w, &expected_h);
682 EXPECT_EQ(expected_w, info.w)
683 << "Frame " << frame << " had unexpected width";
684 EXPECT_EQ(expected_h, info.h)
685 << "Frame " << frame << " had unexpected height";
686 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
687 }
688 frame_info_list_.clear();
689 }
690 }
691
TEST_P(ResizeRealtimeTest,TestExternalResizeWorksUsePSNR)692 TEST_P(ResizeRealtimeTest, TestExternalResizeWorksUsePSNR) {
693 ResizingVideoSource video;
694 video.flag_codec_ = 1;
695 change_bitrate_ = false;
696 set_scale_mode_ = false;
697 set_scale_mode2_ = false;
698 set_scale_mode3_ = false;
699 mismatch_psnr_ = 0.0;
700 mismatch_nframes_ = 0;
701 init_flags_ = AOM_CODEC_USE_PSNR;
702 cfg_.rc_dropframe_thresh = 30;
703 DefaultConfig();
704 // Test external resizing with start resolution equal to
705 // 1. kInitialWidth and kInitialHeight
706 // 2. down-scaled kInitialWidth and kInitialHeight
707 for (int i = 0; i < 2; i++) {
708 video.change_start_resln_ = static_cast<bool>(i);
709
710 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
711
712 // Check we decoded the same number of frames as we attempted to encode
713 ASSERT_EQ(frame_info_list_.size(), video.limit());
714 for (const auto &info : frame_info_list_) {
715 const unsigned int frame = static_cast<unsigned>(info.pts);
716 unsigned int expected_w;
717 unsigned int expected_h;
718 ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight,
719 video.flag_codec_, video.change_start_resln_,
720 &expected_w, &expected_h);
721 EXPECT_EQ(expected_w, info.w)
722 << "Frame " << frame << " had unexpected width";
723 EXPECT_EQ(expected_h, info.h)
724 << "Frame " << frame << " had unexpected height";
725 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
726 }
727 frame_info_list_.clear();
728 }
729 }
730
731 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
732 // Run at low bitrate, with resize_allowed = 1, and verify that we get
733 // one resize down event.
TEST_P(ResizeRealtimeTest,TestInternalResizeDown)734 TEST_P(ResizeRealtimeTest, TestInternalResizeDown) {
735 ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
736 0, 400);
737 cfg_.g_w = 640;
738 cfg_.g_h = 480;
739 change_bitrate_ = false;
740 set_scale_mode_ = false;
741 set_scale_mode2_ = false;
742 set_scale_mode3_ = false;
743 mismatch_psnr_ = 0.0;
744 mismatch_nframes_ = 0;
745 DefaultConfig();
746 // Disable dropped frames.
747 cfg_.rc_dropframe_thresh = 0;
748 // Starting bitrate low.
749 cfg_.rc_target_bitrate = 150;
750 cfg_.rc_resize_mode = RESIZE_DYNAMIC;
751 cfg_.g_forced_max_frame_width = 1280;
752 cfg_.g_forced_max_frame_height = 1280;
753 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
754
755 unsigned int last_w = cfg_.g_w;
756 unsigned int last_h = cfg_.g_h;
757 int resize_down_count = 0;
758 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
759 info != frame_info_list_.end(); ++info) {
760 if (info->w != last_w || info->h != last_h) {
761 // Verify that resize down occurs.
762 if (info->w < last_w && info->h < last_h) {
763 resize_down_count++;
764 }
765 last_w = info->w;
766 last_h = info->h;
767 }
768 }
769
770 #if CONFIG_AV1_DECODER
771 // Verify that we get at lease 1 resize down event in this test.
772 ASSERT_GE(resize_down_count, 1) << "Resizing should occur.";
773 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
774 #else
775 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
776 #endif
777 }
778
779 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
780 // Start at low target bitrate, raise the bitrate in the middle of the clip
781 // (at frame# = frame_change_bitrate_), scaling-up should occur after bitrate
782 // is increased.
TEST_P(ResizeRealtimeTest,TestInternalResizeDownUpChangeBitRate)783 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRate) {
784 ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
785 0, 400);
786 init_flags_ = AOM_CODEC_USE_PSNR;
787 cfg_.g_w = 640;
788 cfg_.g_h = 480;
789 change_bitrate_ = true;
790 frame_change_bitrate_ = 120;
791 set_scale_mode_ = false;
792 set_scale_mode2_ = false;
793 set_scale_mode3_ = false;
794 mismatch_psnr_ = 0.0;
795 mismatch_nframes_ = 0;
796 DefaultConfig();
797 // Disable dropped frames.
798 cfg_.rc_dropframe_thresh = 0;
799 // Starting bitrate low.
800 cfg_.rc_target_bitrate = 150;
801 cfg_.rc_resize_mode = RESIZE_DYNAMIC;
802 cfg_.g_forced_max_frame_width = 1280;
803 cfg_.g_forced_max_frame_height = 1280;
804 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
805
806 unsigned int last_w = cfg_.g_w;
807 unsigned int last_h = cfg_.g_h;
808 unsigned int frame_number = 0;
809 int resize_down_count = 0;
810 int resize_up_count = 0;
811 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
812 info != frame_info_list_.end(); ++info) {
813 if (info->w != last_w || info->h != last_h) {
814 if (frame_number < frame_change_bitrate_) {
815 // Verify that resize down occurs, before bitrate is increased.
816 ASSERT_LT(info->w, last_w);
817 ASSERT_LT(info->h, last_h);
818 resize_down_count++;
819 } else {
820 // Verify that resize up occurs, after bitrate is increased.
821 ASSERT_GT(info->w, last_w);
822 ASSERT_GT(info->h, last_h);
823 resize_up_count++;
824 }
825 last_w = info->w;
826 last_h = info->h;
827 }
828 frame_number++;
829 }
830
831 #if CONFIG_AV1_DECODER
832 // Verify that we get at least 2 resize events in this test.
833 ASSERT_GE(resize_up_count, 1) << "Resizing up should occur at lease once.";
834 ASSERT_GE(resize_down_count, 1)
835 << "Resizing down should occur at lease once.";
836 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
837 #else
838 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
839 #endif
840 }
841
842 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode for
843 // screen content mode. Start at low target bitrate, raise the bitrate in the
844 // middle of the clip (at frame# = frame_change_bitrate_), scaling-up should
845 // occur after bitrate is increased.
TEST_P(ResizeRealtimeTest,TestInternalResizeDownUpChangeBitRateScreen)846 TEST_P(ResizeRealtimeTest, TestInternalResizeDownUpChangeBitRateScreen) {
847 ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
848 30, 1, 0, 300);
849 init_flags_ = AOM_CODEC_USE_PSNR;
850 cfg_.g_w = 352;
851 cfg_.g_h = 288;
852 change_bitrate_ = true;
853 frame_change_bitrate_ = 120;
854 set_scale_mode_ = false;
855 set_scale_mode2_ = false;
856 set_scale_mode3_ = false;
857 mismatch_psnr_ = 0.0;
858 mismatch_nframes_ = 0;
859 is_screen_ = true;
860 DefaultConfig();
861 // Disable dropped frames.
862 cfg_.rc_dropframe_thresh = 0;
863 // Starting bitrate low.
864 cfg_.rc_target_bitrate = 100;
865 cfg_.rc_resize_mode = RESIZE_DYNAMIC;
866 cfg_.g_forced_max_frame_width = 1280;
867 cfg_.g_forced_max_frame_height = 1280;
868 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
869
870 unsigned int last_w = cfg_.g_w;
871 unsigned int last_h = cfg_.g_h;
872 unsigned int frame_number = 0;
873 int resize_down_count = 0;
874 for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin();
875 info != frame_info_list_.end(); ++info) {
876 if (info->w != last_w || info->h != last_h) {
877 if (frame_number < frame_change_bitrate_) {
878 // Verify that resize down occurs, before bitrate is increased.
879 ASSERT_LT(info->w, last_w);
880 ASSERT_LT(info->h, last_h);
881 resize_down_count++;
882 }
883 last_w = info->w;
884 last_h = info->h;
885 }
886 frame_number++;
887 }
888
889 #if CONFIG_AV1_DECODER
890 // Verify that we get at least 1 resize event in this test.
891 ASSERT_GE(resize_down_count, 1)
892 << "Resizing down should occur at lease once.";
893 EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames());
894 #else
895 printf("Warning: AV1 decoder unavailable, unable to check resize count!\n");
896 #endif
897 }
898
899 class ResizeCspTest : public ResizeTest {
900 protected:
901 #if WRITE_COMPRESSED_STREAM
ResizeCspTest()902 ResizeCspTest()
903 : ResizeTest(), frame0_psnr_(0.0), outfile_(nullptr), out_frames_(0) {}
904 #else
905 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
906 #endif
907
908 ~ResizeCspTest() override = default;
909
BeginPassHook(unsigned int)910 void BeginPassHook(unsigned int /*pass*/) override {
911 #if WRITE_COMPRESSED_STREAM
912 outfile_ = fopen("av11-2-05-cspchape.ivf", "wb");
913 #endif
914 }
915
EndPassHook()916 void EndPassHook() override {
917 #if WRITE_COMPRESSED_STREAM
918 if (outfile_) {
919 if (!fseek(outfile_, 0, SEEK_SET))
920 write_ivf_file_header(&cfg_, out_frames_, outfile_);
921 fclose(outfile_);
922 outfile_ = nullptr;
923 }
924 #endif
925 }
926
PSNRPktHook(const aom_codec_cx_pkt_t * pkt)927 void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) override {
928 if (frame0_psnr_ == 0.) frame0_psnr_ = pkt->data.psnr.psnr[0];
929 EXPECT_NEAR(pkt->data.psnr.psnr[0], frame0_psnr_, 2.0);
930 }
931
932 #if WRITE_COMPRESSED_STREAM
FramePktHook(const aom_codec_cx_pkt_t * pkt)933 void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
934 ++out_frames_;
935
936 // Write initial file header if first frame.
937 if (pkt->data.frame.pts == 0) write_ivf_file_header(&cfg_, 0, outfile_);
938
939 // Write frame header and data.
940 write_ivf_frame_header(pkt, outfile_);
941 (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_);
942 }
943 #endif
944
945 double frame0_psnr_;
946 #if WRITE_COMPRESSED_STREAM
947 FILE *outfile_;
948 unsigned int out_frames_;
949 #endif
950 };
951
952 class ResizingCspVideoSource : public ::libaom_test::DummyVideoSource {
953 public:
ResizingCspVideoSource(aom_img_fmt_t image_format)954 explicit ResizingCspVideoSource(aom_img_fmt_t image_format) {
955 SetSize(kInitialWidth, kInitialHeight);
956 SetImageFormat(image_format);
957 limit_ = 30;
958 }
959
960 ~ResizingCspVideoSource() override = default;
961 };
962
963 #if (defined(DISABLE_TRELLISQ_SEARCH) && DISABLE_TRELLISQ_SEARCH) || \
964 (defined(CONFIG_MAX_DECODE_PROFILE) && CONFIG_MAX_DECODE_PROFILE < 1)
TEST_P(ResizeCspTest,DISABLED_TestResizeCspWorks)965 TEST_P(ResizeCspTest, DISABLED_TestResizeCspWorks) {
966 #else
967 TEST_P(ResizeCspTest, TestResizeCspWorks) {
968 #endif
969 const aom_img_fmt_t image_formats[] = { AOM_IMG_FMT_I420, AOM_IMG_FMT_I444 };
970 for (const aom_img_fmt_t &img_format : image_formats) {
971 ResizingCspVideoSource video(img_format);
972 init_flags_ = AOM_CODEC_USE_PSNR;
973 cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
974 cfg_.g_lag_in_frames = 0;
975 cfg_.g_profile = (img_format == AOM_IMG_FMT_I420) ? 0 : 1;
976 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
977
978 // Check we decoded the same number of frames as we attempted to encode
979 ASSERT_EQ(frame_info_list_.size(), video.limit());
980 frame_info_list_.clear();
981 }
982 }
983
984 #if !CONFIG_REALTIME_ONLY
985 // This class is used to check if there are any fatal
986 // failures while encoding with resize-mode > 0
987 class ResizeModeTestLarge
988 : public ::libaom_test::CodecTestWith5Params<libaom_test::TestMode, int,
989 int, int, int>,
990 public ::libaom_test::EncoderTest {
991 protected:
992 ResizeModeTestLarge()
993 : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
994 resize_mode_(GET_PARAM(2)), resize_denominator_(GET_PARAM(3)),
995 resize_kf_denominator_(GET_PARAM(4)), cpu_used_(GET_PARAM(5)) {}
996 ~ResizeModeTestLarge() override = default;
997
998 void SetUp() override {
999 InitializeConfig(encoding_mode_);
1000 const aom_rational timebase = { 1, 30 };
1001 cfg_.g_timebase = timebase;
1002 cfg_.rc_end_usage = AOM_VBR;
1003 cfg_.g_threads = 1;
1004 cfg_.g_lag_in_frames = 35;
1005 cfg_.rc_target_bitrate = 1000;
1006 cfg_.rc_resize_mode = resize_mode_;
1007 cfg_.rc_resize_denominator = resize_denominator_;
1008 cfg_.rc_resize_kf_denominator = resize_kf_denominator_;
1009 init_flags_ = AOM_CODEC_USE_PSNR;
1010 }
1011
1012 void PreEncodeFrameHook(::libaom_test::VideoSource *video,
1013 ::libaom_test::Encoder *encoder) override {
1014 if (video->frame() == 0) {
1015 encoder->Control(AOME_SET_CPUUSED, cpu_used_);
1016 encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1);
1017 }
1018 }
1019
1020 ::libaom_test::TestMode encoding_mode_;
1021 int resize_mode_;
1022 int resize_denominator_;
1023 int resize_kf_denominator_;
1024 int cpu_used_;
1025 };
1026
1027 TEST_P(ResizeModeTestLarge, ResizeModeTest) {
1028 ::libaom_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 30);
1029 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
1030 }
1031
1032 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ResizeModeTestLarge);
1033 AV1_INSTANTIATE_TEST_SUITE(ResizeModeTestLarge,
1034 ::testing::Values(::libaom_test::kOnePassGood,
1035 ::libaom_test::kTwoPassGood),
1036 ::testing::Values(1, 2), ::testing::Values(8, 12),
1037 ::testing::Values(10, 14), ::testing::Values(3, 6));
1038 #endif // !CONFIG_REALTIME_ONLY
1039
1040 AV1_INSTANTIATE_TEST_SUITE(ResizeTest,
1041 ::testing::Values(::libaom_test::kRealTime));
1042 AV1_INSTANTIATE_TEST_SUITE(ResizeRealtimeTest,
1043 ::testing::Values(::libaom_test::kRealTime),
1044 ::testing::Range(6, 10), ::testing::Values(1, 2, 4));
1045 AV1_INSTANTIATE_TEST_SUITE(ResizeCspTest,
1046 ::testing::Values(::libaom_test::kRealTime));
1047
1048 // A test that reproduces crbug.com/1393384. In realtime usage mode, encode
1049 // frames of sizes 202x202, 1x202, and 202x202. ASan should report no memory
1050 // errors.
1051 TEST(ResizeSimpleTest, TemporarySmallerFrameSize) {
1052 constexpr int kWidth = 202;
1053 constexpr int kHeight = 202;
1054 // Dummy buffer of zero samples.
1055 constexpr size_t kBufferSize =
1056 kWidth * kHeight + 2 * (kWidth + 1) / 2 * (kHeight + 1) / 2;
1057 std::vector<unsigned char> buffer(kBufferSize);
1058
1059 aom_image_t img;
1060 EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, kWidth, kHeight, 1,
1061 buffer.data()));
1062 aom_image_t img2;
1063 EXPECT_EQ(&img2, aom_img_wrap(&img2, AOM_IMG_FMT_I420, 1, kHeight, 1,
1064 buffer.data()));
1065
1066 aom_codec_iface_t *iface = aom_codec_av1_cx();
1067 aom_codec_enc_cfg_t cfg;
1068 EXPECT_EQ(AOM_CODEC_OK,
1069 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
1070 cfg.g_w = kWidth;
1071 cfg.g_h = kHeight;
1072 aom_codec_ctx_t enc;
1073 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
1074 EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AOME_SET_CPUUSED, 5));
1075
1076 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
1077
1078 cfg.g_w = 1;
1079 cfg.g_h = kHeight;
1080 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
1081 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img2, 1, 1, 0));
1082
1083 cfg.g_w = kWidth;
1084 cfg.g_h = kHeight;
1085 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
1086 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 2, 1, 0));
1087
1088 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
1089 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
1090 }
1091
1092 // A test that reproduces crbug.com/1410766. In realtime usage mode
1093 // for SVC with temporal layers, encode frames of sizes 600x600,
1094 // 600x600, and 100x480. ASan should report no memory errors.
1095 TEST(ResizeSimpleTest, SmallerFrameSizeSVC) {
1096 constexpr int kWidth = 600;
1097 constexpr int kHeight = 600;
1098 // Dummy buffer of zero samples.
1099 constexpr size_t kBufferSize =
1100 kWidth * kHeight + 2 * (kWidth + 1) / 2 * (kHeight + 1) / 2;
1101 std::vector<unsigned char> buffer(kBufferSize);
1102
1103 aom_image_t img;
1104 EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, kWidth, kHeight, 1,
1105 buffer.data()));
1106 aom_image_t img2;
1107 EXPECT_EQ(&img2,
1108 aom_img_wrap(&img2, AOM_IMG_FMT_I420, 100, 480, 1, buffer.data()));
1109
1110 aom_codec_iface_t *iface = aom_codec_av1_cx();
1111 aom_codec_enc_cfg_t cfg;
1112 EXPECT_EQ(AOM_CODEC_OK,
1113 aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
1114 cfg.g_w = kWidth;
1115 cfg.g_h = kHeight;
1116 aom_codec_ctx_t enc;
1117 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
1118 EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AOME_SET_CPUUSED, 5));
1119
1120 aom_svc_params_t svc_params = {};
1121 aom_svc_layer_id_t layer_id;
1122 svc_params.number_spatial_layers = 1;
1123 svc_params.framerate_factor[0] = 2;
1124 svc_params.framerate_factor[1] = 1;
1125 svc_params.number_temporal_layers = 2;
1126 // Bitrate allocation L0: 60% L1: 40%
1127 svc_params.layer_target_bitrate[0] = 60 * cfg.rc_target_bitrate / 100;
1128 svc_params.layer_target_bitrate[1] = cfg.rc_target_bitrate;
1129 EXPECT_EQ(AOM_CODEC_OK,
1130 aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params));
1131
1132 layer_id.spatial_layer_id = 0;
1133 layer_id.temporal_layer_id = 0;
1134 EXPECT_EQ(AOM_CODEC_OK,
1135 aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id));
1136 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
1137
1138 cfg.g_w = kWidth;
1139 cfg.g_h = kHeight;
1140 layer_id.temporal_layer_id = 1;
1141 EXPECT_EQ(AOM_CODEC_OK,
1142 aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id));
1143 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
1144 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 1, 1, 0));
1145
1146 cfg.g_w = 100;
1147 cfg.g_h = 480;
1148 layer_id.temporal_layer_id = 0;
1149 EXPECT_EQ(AOM_CODEC_OK,
1150 aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id));
1151 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
1152 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img2, 2, 1, 0));
1153
1154 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
1155 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
1156 }
1157
1158 const int kUsages[] =
1159 #if CONFIG_REALTIME_ONLY
1160 { AOM_USAGE_REALTIME };
1161 #else
1162 { AOM_USAGE_GOOD_QUALITY, AOM_USAGE_REALTIME, AOM_USAGE_ALL_INTRA };
1163 #endif
1164
1165 const int kNumThreads[] = { 2, 4, 8 };
1166
1167 class FrameSizeChangeTest
1168 : public ::libaom_test::CodecTestWith3Params<int, int, int> {
1169 protected:
1170 FrameSizeChangeTest() {}
1171 ~FrameSizeChangeTest() override = default;
1172
1173 void DoTest(int change_thread) {
1174 usage_ = GET_PARAM(1);
1175 cpu_used_ = GET_PARAM(2);
1176 threads_ = GET_PARAM(3);
1177 constexpr int kWidth = 512;
1178 constexpr int kHeight = 512;
1179 constexpr int kFirstWidth = 256;
1180 constexpr int kFirstHeight = 256;
1181 // Buffer of zero samples.
1182 constexpr size_t kBufferSize = 3 * kWidth * kHeight;
1183 std::vector<unsigned char> buffer(kBufferSize,
1184 static_cast<unsigned char>(0));
1185
1186 aom_image_t img1;
1187 EXPECT_EQ(&img1, aom_img_wrap(&img1, AOM_IMG_FMT_I420, kFirstWidth,
1188 kFirstHeight, 1, buffer.data()));
1189
1190 aom_image_t img2;
1191 EXPECT_EQ(&img2, aom_img_wrap(&img2, AOM_IMG_FMT_I420, kWidth, kHeight, 1,
1192 buffer.data()));
1193
1194 aom_codec_iface_t *iface = aom_codec_av1_cx();
1195 aom_codec_enc_cfg_t cfg;
1196 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_default(iface, &cfg, usage_));
1197 cfg.g_threads = threads_;
1198 cfg.g_lag_in_frames = usage_ == AOM_USAGE_ALL_INTRA ? 0 : 1;
1199 cfg.g_w = kFirstWidth;
1200 cfg.g_h = kFirstHeight;
1201 cfg.g_forced_max_frame_width = kWidth;
1202 cfg.g_forced_max_frame_height = kHeight;
1203 aom_codec_ctx_t enc;
1204 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
1205 EXPECT_EQ(AOM_CODEC_OK,
1206 aom_codec_control(&enc, AOME_SET_CPUUSED, cpu_used_));
1207
1208 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img1, 0, 1, 0));
1209
1210 if (change_thread == 1) {
1211 cfg.g_threads = AOMMAX(1, threads_ / 2);
1212 } else if (change_thread == 2) {
1213 cfg.g_threads = threads_ * 2;
1214 }
1215 cfg.g_w = kWidth;
1216 cfg.g_h = kHeight;
1217 EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
1218 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img2, 1, 1, 0));
1219
1220 EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
1221 EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
1222 }
1223
1224 int cpu_used_;
1225 int threads_;
1226 int usage_;
1227 };
1228
1229 TEST_P(FrameSizeChangeTest, FixedThreads) { DoTest(0); }
1230 TEST_P(FrameSizeChangeTest, DecreasingThreads) { DoTest(1); }
1231 TEST_P(FrameSizeChangeTest, IncreasingThreads) { DoTest(2); }
1232
1233 AV1_INSTANTIATE_TEST_SUITE(FrameSizeChangeTest, ::testing::ValuesIn(kUsages),
1234 ::testing::Range(6, 7),
1235 ::testing::ValuesIn(kNumThreads));
1236
1237 } // namespace
1238