2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
12 #include "third_party/googletest/src/include/gtest/gtest.h"
13 #include "test/codec_factory.h"
14 #include "test/encode_test_driver.h"
15 #include "test/i420_video_source.h"
16 #include "test/video_source.h"
17 #include "test/util.h"
19 // Enable(1) or Disable(0) writing of the compressed bitstream.
20 #define WRITE_COMPRESSED_STREAM 0
24 #if WRITE_COMPRESSED_STREAM
25 static void mem_put_le16(char *const mem
, const unsigned int val
) {
30 static void mem_put_le32(char *const mem
, const unsigned int val
) {
37 static void write_ivf_file_header(const vpx_codec_enc_cfg_t
*const cfg
,
38 int frame_cnt
, FILE *const outfile
) {
45 mem_put_le16(header
+ 4, 0); /* version */
46 mem_put_le16(header
+ 6, 32); /* headersize */
47 mem_put_le32(header
+ 8, 0x30395056); /* fourcc (vp9) */
48 mem_put_le16(header
+ 12, cfg
->g_w
); /* width */
49 mem_put_le16(header
+ 14, cfg
->g_h
); /* height */
50 mem_put_le32(header
+ 16, cfg
->g_timebase
.den
); /* rate */
51 mem_put_le32(header
+ 20, cfg
->g_timebase
.num
); /* scale */
52 mem_put_le32(header
+ 24, frame_cnt
); /* length */
53 mem_put_le32(header
+ 28, 0); /* unused */
55 (void)fwrite(header
, 1, 32, outfile
);
58 static void write_ivf_frame_size(FILE *const outfile
, const size_t size
) {
60 mem_put_le32(header
, static_cast<unsigned int>(size
));
61 (void)fwrite(header
, 1, 4, outfile
);
64 static void write_ivf_frame_header(const vpx_codec_cx_pkt_t
*const pkt
,
65 FILE *const outfile
) {
69 if (pkt
->kind
!= VPX_CODEC_CX_FRAME_PKT
)
72 pts
= pkt
->data
.frame
.pts
;
73 mem_put_le32(header
, static_cast<unsigned int>(pkt
->data
.frame
.sz
));
74 mem_put_le32(header
+ 4, pts
& 0xFFFFFFFF);
75 mem_put_le32(header
+ 8, pts
>> 32);
77 (void)fwrite(header
, 1, 12, outfile
);
79 #endif // WRITE_COMPRESSED_STREAM
81 const unsigned int kInitialWidth
= 320;
82 const unsigned int kInitialHeight
= 240;
85 FrameInfo(vpx_codec_pts_t _pts
, unsigned int _w
, unsigned int _h
)
86 : pts(_pts
), w(_w
), h(_h
) {}
93 unsigned int ScaleForFrameNumber(unsigned int frame
, unsigned int val
) {
107 class ResizingVideoSource
: public ::libvpx_test::DummyVideoSource
{
109 ResizingVideoSource() {
110 SetSize(kInitialWidth
, kInitialHeight
);
114 virtual ~ResizingVideoSource() {}
117 virtual void Next() {
119 SetSize(ScaleForFrameNumber(frame_
, kInitialWidth
),
120 ScaleForFrameNumber(frame_
, kInitialHeight
));
125 class ResizeTest
: public ::libvpx_test::EncoderTest
,
126 public ::libvpx_test::CodecTestWithParam
<libvpx_test::TestMode
> {
128 ResizeTest() : EncoderTest(GET_PARAM(0)) {}
130 virtual ~ResizeTest() {}
132 virtual void SetUp() {
134 SetMode(GET_PARAM(1));
137 virtual void DecompressedFrameHook(const vpx_image_t
&img
,
138 vpx_codec_pts_t pts
) {
139 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
142 std::vector
< FrameInfo
> frame_info_list_
;
145 TEST_P(ResizeTest
, TestExternalResizeWorks
) {
146 ResizingVideoSource video
;
147 cfg_
.g_lag_in_frames
= 0;
148 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
150 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
151 info
!= frame_info_list_
.end(); ++info
) {
152 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
153 const unsigned int expected_w
= ScaleForFrameNumber(frame
, kInitialWidth
);
154 const unsigned int expected_h
= ScaleForFrameNumber(frame
, kInitialHeight
);
156 EXPECT_EQ(expected_w
, info
->w
)
157 << "Frame " << frame
<< " had unexpected width";
158 EXPECT_EQ(expected_h
, info
->h
)
159 << "Frame " << frame
<< " had unexpected height";
163 const unsigned int kStepDownFrame
= 3;
164 const unsigned int kStepUpFrame
= 6;
166 class ResizeInternalTest
: public ResizeTest
{
168 #if WRITE_COMPRESSED_STREAM
175 ResizeInternalTest() : ResizeTest(), frame0_psnr_(0.0) {}
178 virtual ~ResizeInternalTest() {}
180 virtual void BeginPassHook(unsigned int /*pass*/) {
181 #if WRITE_COMPRESSED_STREAM
182 outfile_
= fopen("vp90-2-05-resize.ivf", "wb");
186 virtual void EndPassHook() {
187 #if WRITE_COMPRESSED_STREAM
189 if (!fseek(outfile_
, 0, SEEK_SET
))
190 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
197 virtual void PreEncodeFrameHook(libvpx_test::VideoSource
*video
,
198 libvpx_test::Encoder
*encoder
) {
199 if (change_config_
) {
201 if (video
->frame() == 0) {
202 struct vpx_scaling_mode mode
= {VP8E_ONETWO
, VP8E_ONETWO
};
203 encoder
->Control(VP8E_SET_SCALEMODE
, &mode
);
205 if (video
->frame() == 1) {
206 struct vpx_scaling_mode mode
= {VP8E_NORMAL
, VP8E_NORMAL
};
207 encoder
->Control(VP8E_SET_SCALEMODE
, &mode
);
208 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= new_q
;
209 encoder
->Config(&cfg_
);
212 if (video
->frame() == kStepDownFrame
) {
213 struct vpx_scaling_mode mode
= {VP8E_FOURFIVE
, VP8E_THREEFIVE
};
214 encoder
->Control(VP8E_SET_SCALEMODE
, &mode
);
216 if (video
->frame() == kStepUpFrame
) {
217 struct vpx_scaling_mode mode
= {VP8E_NORMAL
, VP8E_NORMAL
};
218 encoder
->Control(VP8E_SET_SCALEMODE
, &mode
);
223 virtual void PSNRPktHook(const vpx_codec_cx_pkt_t
*pkt
) {
225 frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
226 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.0);
229 #if WRITE_COMPRESSED_STREAM
230 virtual void FramePktHook(const vpx_codec_cx_pkt_t
*pkt
) {
233 // Write initial file header if first frame.
234 if (pkt
->data
.frame
.pts
== 0)
235 write_ivf_file_header(&cfg_
, 0, outfile_
);
237 // Write frame header and data.
238 write_ivf_frame_header(pkt
, outfile_
);
239 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
245 #if WRITE_COMPRESSED_STREAM
247 unsigned int out_frames_
;
251 TEST_P(ResizeInternalTest
, TestInternalResizeWorks
) {
252 ::libvpx_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
254 init_flags_
= VPX_CODEC_USE_PSNR
;
255 change_config_
= false;
257 // q picked such that initial keyframe on this clip is ~30dB PSNR
258 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
260 // If the number of frames being encoded is smaller than g_lag_in_frames
261 // the encoded frame is unavailable using the current API. Comparing
262 // frames to detect mismatch would then not be possible. Set
263 // g_lag_in_frames = 0 to get around this.
264 cfg_
.g_lag_in_frames
= 0;
265 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
267 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
268 info
!= frame_info_list_
.end(); ++info
) {
269 const vpx_codec_pts_t pts
= info
->pts
;
270 if (pts
>= kStepDownFrame
&& pts
< kStepUpFrame
) {
271 ASSERT_EQ(282U, info
->w
) << "Frame " << pts
<< " had unexpected width";
272 ASSERT_EQ(173U, info
->h
) << "Frame " << pts
<< " had unexpected height";
274 EXPECT_EQ(352U, info
->w
) << "Frame " << pts
<< " had unexpected width";
275 EXPECT_EQ(288U, info
->h
) << "Frame " << pts
<< " had unexpected height";
280 TEST_P(ResizeInternalTest
, TestInternalResizeChangeConfig
) {
281 ::libvpx_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
285 change_config_
= true;
286 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
289 class ResizeRealtimeTest
: public ::libvpx_test::EncoderTest
,
290 public ::libvpx_test::CodecTestWith2Params
<libvpx_test::TestMode
, int> {
292 ResizeRealtimeTest() : EncoderTest(GET_PARAM(0)) {}
293 virtual ~ResizeRealtimeTest() {}
295 virtual void PreEncodeFrameHook(libvpx_test::VideoSource
*video
,
296 libvpx_test::Encoder
*encoder
) {
297 if (video
->frame() == 0) {
298 encoder
->Control(VP9E_SET_AQ_MODE
, 3);
299 encoder
->Control(VP8E_SET_CPUUSED
, set_cpu_used_
);
302 if (change_bitrate_
&& video
->frame() == 120) {
303 change_bitrate_
= false;
304 cfg_
.rc_target_bitrate
= 500;
305 encoder
->Config(&cfg_
);
309 virtual void SetUp() {
311 SetMode(GET_PARAM(1));
312 set_cpu_used_
= GET_PARAM(2);
315 virtual void DecompressedFrameHook(const vpx_image_t
&img
,
316 vpx_codec_pts_t pts
) {
317 frame_info_list_
.push_back(FrameInfo(pts
, img
.d_w
, img
.d_h
));
320 void DefaultConfig() {
321 cfg_
.rc_buf_initial_sz
= 500;
322 cfg_
.rc_buf_optimal_sz
= 600;
323 cfg_
.rc_buf_sz
= 1000;
324 cfg_
.rc_min_quantizer
= 2;
325 cfg_
.rc_max_quantizer
= 56;
326 cfg_
.rc_undershoot_pct
= 50;
327 cfg_
.rc_overshoot_pct
= 50;
328 cfg_
.rc_end_usage
= VPX_CBR
;
329 cfg_
.kf_mode
= VPX_KF_AUTO
;
330 cfg_
.g_lag_in_frames
= 0;
331 cfg_
.kf_min_dist
= cfg_
.kf_max_dist
= 3000;
332 // Enable dropped frames.
333 cfg_
.rc_dropframe_thresh
= 1;
334 // Enable error_resilience mode.
335 cfg_
.g_error_resilient
= 1;
336 // Enable dynamic resizing.
337 cfg_
.rc_resize_allowed
= 1;
338 // Run at low bitrate.
339 cfg_
.rc_target_bitrate
= 200;
342 std::vector
< FrameInfo
> frame_info_list_
;
344 bool change_bitrate_
;
347 TEST_P(ResizeRealtimeTest
, TestExternalResizeWorks
) {
348 ResizingVideoSource video
;
350 change_bitrate_
= false;
351 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
353 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
354 info
!= frame_info_list_
.end(); ++info
) {
355 const unsigned int frame
= static_cast<unsigned>(info
->pts
);
356 const unsigned int expected_w
= ScaleForFrameNumber(frame
, kInitialWidth
);
357 const unsigned int expected_h
= ScaleForFrameNumber(frame
, kInitialHeight
);
359 EXPECT_EQ(expected_w
, info
->w
)
360 << "Frame " << frame
<< " had unexpected width";
361 EXPECT_EQ(expected_h
, info
->h
)
362 << "Frame " << frame
<< " had unexpected height";
366 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
367 // Run at low bitrate, with resize_allowed = 1, and verify that we get
368 // one resize down event.
369 TEST_P(ResizeRealtimeTest
, TestInternalResizeDown
) {
370 ::libvpx_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
375 change_bitrate_
= false;
376 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
378 unsigned int last_w
= cfg_
.g_w
;
379 unsigned int last_h
= cfg_
.g_h
;
380 int resize_count
= 0;
381 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
382 info
!= frame_info_list_
.end(); ++info
) {
383 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
384 // Verify that resize down occurs.
385 ASSERT_LT(info
->w
, last_w
);
386 ASSERT_LT(info
->h
, last_h
);
393 // Verify that we get 1 resize down event in this test.
394 ASSERT_EQ(1, resize_count
) << "Resizing should occur.";
397 // Verify the dynamic resizer behavior for real time, 1 pass CBR mode.
398 // Start at low target bitrate, raise the bitrate in the middle of the clip,
399 // scaling-up should occur after bitrate changed.
400 TEST_P(ResizeRealtimeTest
, TestInternalResizeDownUpChangeBitRate
) {
401 ::libvpx_test::I420VideoSource
video("hantro_collage_w352h288.yuv", 352, 288,
406 change_bitrate_
= true;
407 // Disable dropped frames.
408 cfg_
.rc_dropframe_thresh
= 0;
409 // Starting bitrate low.
410 cfg_
.rc_target_bitrate
= 80;
411 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
413 unsigned int last_w
= cfg_
.g_w
;
414 unsigned int last_h
= cfg_
.g_h
;
415 int resize_count
= 0;
416 for (std::vector
<FrameInfo
>::const_iterator info
= frame_info_list_
.begin();
417 info
!= frame_info_list_
.end(); ++info
) {
418 if (info
->w
!= last_w
|| info
->h
!= last_h
) {
420 if (resize_count
== 1) {
421 // Verify that resize down occurs.
422 ASSERT_LT(info
->w
, last_w
);
423 ASSERT_LT(info
->h
, last_h
);
424 } else if (resize_count
== 2) {
425 // Verify that resize up occurs.
426 ASSERT_GT(info
->w
, last_w
);
427 ASSERT_GT(info
->h
, last_h
);
434 // Verify that we get 2 resize events in this test.
435 ASSERT_EQ(resize_count
, 2) << "Resizing should occur twice.";
438 vpx_img_fmt_t
CspForFrameNumber(int frame
) {
440 return VPX_IMG_FMT_I420
;
442 return VPX_IMG_FMT_I444
;
443 return VPX_IMG_FMT_I420
;
446 class ResizeCspTest
: public ResizeTest
{
448 #if WRITE_COMPRESSED_STREAM
455 ResizeCspTest() : ResizeTest(), frame0_psnr_(0.0) {}
458 virtual ~ResizeCspTest() {}
460 virtual void BeginPassHook(unsigned int /*pass*/) {
461 #if WRITE_COMPRESSED_STREAM
462 outfile_
= fopen("vp91-2-05-cspchape.ivf", "wb");
466 virtual void EndPassHook() {
467 #if WRITE_COMPRESSED_STREAM
469 if (!fseek(outfile_
, 0, SEEK_SET
))
470 write_ivf_file_header(&cfg_
, out_frames_
, outfile_
);
477 virtual void PreEncodeFrameHook(libvpx_test::VideoSource
*video
,
478 libvpx_test::Encoder
*encoder
) {
479 if (CspForFrameNumber(video
->frame()) != VPX_IMG_FMT_I420
&&
480 cfg_
.g_profile
!= 1) {
482 encoder
->Config(&cfg_
);
484 if (CspForFrameNumber(video
->frame()) == VPX_IMG_FMT_I420
&&
485 cfg_
.g_profile
!= 0) {
487 encoder
->Config(&cfg_
);
491 virtual void PSNRPktHook(const vpx_codec_cx_pkt_t
*pkt
) {
493 frame0_psnr_
= pkt
->data
.psnr
.psnr
[0];
494 EXPECT_NEAR(pkt
->data
.psnr
.psnr
[0], frame0_psnr_
, 2.0);
497 #if WRITE_COMPRESSED_STREAM
498 virtual void FramePktHook(const vpx_codec_cx_pkt_t
*pkt
) {
501 // Write initial file header if first frame.
502 if (pkt
->data
.frame
.pts
== 0)
503 write_ivf_file_header(&cfg_
, 0, outfile_
);
505 // Write frame header and data.
506 write_ivf_frame_header(pkt
, outfile_
);
507 (void)fwrite(pkt
->data
.frame
.buf
, 1, pkt
->data
.frame
.sz
, outfile_
);
512 #if WRITE_COMPRESSED_STREAM
514 unsigned int out_frames_
;
518 class ResizingCspVideoSource
: public ::libvpx_test::DummyVideoSource
{
520 ResizingCspVideoSource() {
521 SetSize(kInitialWidth
, kInitialHeight
);
525 virtual ~ResizingCspVideoSource() {}
528 virtual void Next() {
530 SetImageFormat(CspForFrameNumber(frame_
));
535 TEST_P(ResizeCspTest
, TestResizeCspWorks
) {
536 ResizingCspVideoSource video
;
537 init_flags_
= VPX_CODEC_USE_PSNR
;
538 cfg_
.rc_min_quantizer
= cfg_
.rc_max_quantizer
= 48;
539 cfg_
.g_lag_in_frames
= 0;
540 ASSERT_NO_FATAL_FAILURE(RunLoop(&video
));
543 VP8_INSTANTIATE_TEST_CASE(ResizeTest
, ONE_PASS_TEST_MODES
);
544 VP9_INSTANTIATE_TEST_CASE(ResizeTest
,
545 ::testing::Values(::libvpx_test::kRealTime
));
546 VP9_INSTANTIATE_TEST_CASE(ResizeInternalTest
,
547 ::testing::Values(::libvpx_test::kOnePassBest
));
548 VP9_INSTANTIATE_TEST_CASE(ResizeRealtimeTest
,
549 ::testing::Values(::libvpx_test::kRealTime
),
550 ::testing::Range(5, 9));
551 VP9_INSTANTIATE_TEST_CASE(ResizeCspTest
,
552 ::testing::Values(::libvpx_test::kRealTime
));