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