1/*
2 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
3 *
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.
9 */
10
11#include <string>
12#include "test/codec_factory.h"
13#include "test/decode_test_driver.h"
14#include "test/encode_test_driver.h"
15#include "test/i420_video_source.h"
16#include "test/ivf_video_source.h"
17#include "test/md5_helper.h"
18#include "test/util.h"
19#include "test/webm_video_source.h"
20#include "vpx_ports/vpx_timer.h"
21#include "./ivfenc.h"
22#include "./vpx_version.h"
23
24using std::tr1::make_tuple;
25
26namespace {
27
28#define VIDEO_NAME 0
29#define THREADS 1
30
31const int kMaxPsnr = 100;
32const double kUsecsInSec = 1000000.0;
33const char kNewEncodeOutputFile[] = "new_encode.ivf";
34
35/*
36 DecodePerfTest takes a tuple of filename + number of threads to decode with
37 */
38typedef std::tr1::tuple<const char *, unsigned> DecodePerfParam;
39
40const DecodePerfParam kVP9DecodePerfVectors[] = {
41  make_tuple("vp90-2-bbb_426x240_tile_1x1_180kbps.webm", 1),
42  make_tuple("vp90-2-bbb_640x360_tile_1x2_337kbps.webm", 2),
43  make_tuple("vp90-2-bbb_854x480_tile_1x2_651kbps.webm", 2),
44  make_tuple("vp90-2-bbb_1280x720_tile_1x4_1310kbps.webm", 4),
45  make_tuple("vp90-2-bbb_1920x1080_tile_1x1_2581kbps.webm", 1),
46  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_2586kbps.webm", 4),
47  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_fpm_2304kbps.webm", 4),
48  make_tuple("vp90-2-sintel_426x182_tile_1x1_171kbps.webm", 1),
49  make_tuple("vp90-2-sintel_640x272_tile_1x2_318kbps.webm", 2),
50  make_tuple("vp90-2-sintel_854x364_tile_1x2_621kbps.webm", 2),
51  make_tuple("vp90-2-sintel_1280x546_tile_1x4_1257kbps.webm", 4),
52  make_tuple("vp90-2-sintel_1920x818_tile_1x4_fpm_2279kbps.webm", 4),
53  make_tuple("vp90-2-tos_426x178_tile_1x1_181kbps.webm", 1),
54  make_tuple("vp90-2-tos_640x266_tile_1x2_336kbps.webm", 2),
55  make_tuple("vp90-2-tos_854x356_tile_1x2_656kbps.webm", 2),
56  make_tuple("vp90-2-tos_854x356_tile_1x2_fpm_546kbps.webm", 2),
57  make_tuple("vp90-2-tos_1280x534_tile_1x4_1306kbps.webm", 4),
58  make_tuple("vp90-2-tos_1280x534_tile_1x4_fpm_952kbps.webm", 4),
59  make_tuple("vp90-2-tos_1920x800_tile_1x4_fpm_2335kbps.webm", 4),
60};
61
62/*
63 In order to reflect real world performance as much as possible, Perf tests
64 *DO NOT* do any correctness checks. Please run them alongside correctness
65 tests to ensure proper codec integrity. Furthermore, in this test we
66 deliberately limit the amount of system calls we make to avoid OS
67 preemption.
68
69 TODO(joshualitt) create a more detailed perf measurement test to collect
70   power/temp/min max frame decode times/etc
71 */
72
73class DecodePerfTest : public ::testing::TestWithParam<DecodePerfParam> {
74};
75
76TEST_P(DecodePerfTest, PerfTest) {
77  const char *const video_name = GET_PARAM(VIDEO_NAME);
78  const unsigned threads = GET_PARAM(THREADS);
79
80  libvpx_test::WebMVideoSource video(video_name);
81  video.Init();
82
83  vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
84  cfg.threads = threads;
85  libvpx_test::VP9Decoder decoder(cfg, 0);
86
87  vpx_usec_timer t;
88  vpx_usec_timer_start(&t);
89
90  for (video.Begin(); video.cxdata() != NULL; video.Next()) {
91    decoder.DecodeFrame(video.cxdata(), video.frame_size());
92  }
93
94  vpx_usec_timer_mark(&t);
95  const double elapsed_secs = double(vpx_usec_timer_elapsed(&t))
96                              / kUsecsInSec;
97  const unsigned frames = video.frame_number();
98  const double fps = double(frames) / elapsed_secs;
99
100  printf("{\n");
101  printf("\t\"type\" : \"decode_perf_test\",\n");
102  printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
103  printf("\t\"videoName\" : \"%s\",\n", video_name);
104  printf("\t\"threadCount\" : %u,\n", threads);
105  printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
106  printf("\t\"totalFrames\" : %u,\n", frames);
107  printf("\t\"framesPerSecond\" : %f\n", fps);
108  printf("}\n");
109}
110
111INSTANTIATE_TEST_CASE_P(VP9, DecodePerfTest,
112                        ::testing::ValuesIn(kVP9DecodePerfVectors));
113
114class VP9NewEncodeDecodePerfTest :
115    public ::libvpx_test::EncoderTest,
116    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
117 protected:
118  VP9NewEncodeDecodePerfTest()
119      : EncoderTest(GET_PARAM(0)),
120        encoding_mode_(GET_PARAM(1)),
121        speed_(0),
122        outfile_(0),
123        out_frames_(0) {
124  }
125
126  virtual ~VP9NewEncodeDecodePerfTest() {}
127
128  virtual void SetUp() {
129    InitializeConfig();
130    SetMode(encoding_mode_);
131
132    cfg_.g_lag_in_frames = 25;
133    cfg_.rc_min_quantizer = 2;
134    cfg_.rc_max_quantizer = 56;
135    cfg_.rc_dropframe_thresh = 0;
136    cfg_.rc_undershoot_pct = 50;
137    cfg_.rc_overshoot_pct = 50;
138    cfg_.rc_buf_sz = 1000;
139    cfg_.rc_buf_initial_sz = 500;
140    cfg_.rc_buf_optimal_sz = 600;
141    cfg_.rc_resize_allowed = 0;
142    cfg_.rc_end_usage = VPX_VBR;
143  }
144
145  virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
146                                  ::libvpx_test::Encoder *encoder) {
147    if (video->frame() == 1) {
148      encoder->Control(VP8E_SET_CPUUSED, speed_);
149      encoder->Control(VP9E_SET_FRAME_PARALLEL_DECODING, 1);
150      encoder->Control(VP9E_SET_TILE_COLUMNS, 2);
151    }
152  }
153
154  virtual void BeginPassHook(unsigned int /*pass*/) {
155    const std::string data_path = getenv("LIBVPX_TEST_DATA_PATH");
156    const std::string path_to_source = data_path + "/" + kNewEncodeOutputFile;
157    outfile_ = fopen(path_to_source.c_str(), "wb");
158    ASSERT_TRUE(outfile_ != NULL);
159  }
160
161  virtual void EndPassHook() {
162    if (outfile_ != NULL) {
163      if (!fseek(outfile_, 0, SEEK_SET))
164        ivf_write_file_header(outfile_, &cfg_, VP9_FOURCC, out_frames_);
165      fclose(outfile_);
166      outfile_ = NULL;
167    }
168  }
169
170  virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
171    ++out_frames_;
172
173    // Write initial file header if first frame.
174    if (pkt->data.frame.pts == 0)
175      ivf_write_file_header(outfile_, &cfg_, VP9_FOURCC, out_frames_);
176
177    // Write frame header and data.
178    ivf_write_frame_header(outfile_, out_frames_, pkt->data.frame.sz);
179    ASSERT_EQ(fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_),
180              pkt->data.frame.sz);
181  }
182
183  virtual bool DoDecode() { return false; }
184
185  void set_speed(unsigned int speed) {
186    speed_ = speed;
187  }
188
189 private:
190  libvpx_test::TestMode encoding_mode_;
191  uint32_t speed_;
192  FILE *outfile_;
193  uint32_t out_frames_;
194};
195
196struct EncodePerfTestVideo {
197  EncodePerfTestVideo(const char *name_, uint32_t width_, uint32_t height_,
198                      uint32_t bitrate_, int frames_)
199      : name(name_),
200        width(width_),
201        height(height_),
202        bitrate(bitrate_),
203        frames(frames_) {}
204  const char *name;
205  uint32_t width;
206  uint32_t height;
207  uint32_t bitrate;
208  int frames;
209};
210
211const EncodePerfTestVideo kVP9EncodePerfTestVectors[] = {
212  EncodePerfTestVideo("niklas_1280_720_30.yuv", 1280, 720, 600, 470),
213};
214
215TEST_P(VP9NewEncodeDecodePerfTest, PerfTest) {
216  SetUp();
217
218  // TODO(JBB): Make this work by going through the set of given files.
219  const int i = 0;
220  const vpx_rational timebase = { 33333333, 1000000000 };
221  cfg_.g_timebase = timebase;
222  cfg_.rc_target_bitrate = kVP9EncodePerfTestVectors[i].bitrate;
223
224  init_flags_ = VPX_CODEC_USE_PSNR;
225
226  const char *video_name = kVP9EncodePerfTestVectors[i].name;
227  libvpx_test::I420VideoSource video(
228      video_name,
229      kVP9EncodePerfTestVectors[i].width,
230      kVP9EncodePerfTestVectors[i].height,
231      timebase.den, timebase.num, 0,
232      kVP9EncodePerfTestVectors[i].frames);
233  set_speed(2);
234
235  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
236
237  const uint32_t threads = 4;
238
239  libvpx_test::IVFVideoSource decode_video(kNewEncodeOutputFile);
240  decode_video.Init();
241
242  vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
243  cfg.threads = threads;
244  libvpx_test::VP9Decoder decoder(cfg, 0);
245
246  vpx_usec_timer t;
247  vpx_usec_timer_start(&t);
248
249  for (decode_video.Begin(); decode_video.cxdata() != NULL;
250       decode_video.Next()) {
251    decoder.DecodeFrame(decode_video.cxdata(), decode_video.frame_size());
252  }
253
254  vpx_usec_timer_mark(&t);
255  const double elapsed_secs =
256      static_cast<double>(vpx_usec_timer_elapsed(&t)) / kUsecsInSec;
257  const unsigned decode_frames = decode_video.frame_number();
258  const double fps = static_cast<double>(decode_frames) / elapsed_secs;
259
260  printf("{\n");
261  printf("\t\"type\" : \"decode_perf_test\",\n");
262  printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
263  printf("\t\"videoName\" : \"%s\",\n", kNewEncodeOutputFile);
264  printf("\t\"threadCount\" : %u,\n", threads);
265  printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
266  printf("\t\"totalFrames\" : %u,\n", decode_frames);
267  printf("\t\"framesPerSecond\" : %f\n", fps);
268  printf("}\n");
269}
270
271VP9_INSTANTIATE_TEST_CASE(
272  VP9NewEncodeDecodePerfTest, ::testing::Values(::libvpx_test::kTwoPassGood));
273}  // namespace
274