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 <string>
13 #include <tuple>
14 
15 #include "config/aom_version.h"
16 
17 #include "aom_ports/aom_timer.h"
18 #include "common/ivfenc.h"
19 #include "test/codec_factory.h"
20 #include "test/decode_test_driver.h"
21 #include "test/encode_test_driver.h"
22 #include "test/i420_video_source.h"
23 #include "test/ivf_video_source.h"
24 #include "test/md5_helper.h"
25 #include "test/util.h"
26 #include "test/webm_video_source.h"
27 
28 using std::make_tuple;
29 
30 namespace {
31 
32 #define VIDEO_NAME 0
33 #define THREADS 1
34 
35 const double kUsecsInSec = 1000000.0;
36 const char kNewEncodeOutputFile[] = "new_encode.ivf";
37 
38 /*
39  DecodePerfTest takes a tuple of filename + number of threads to decode with
40  */
41 typedef std::tuple<const char *, unsigned> DecodePerfParam;
42 
43 // TODO(jimbankoski): Add actual test vectors here when available.
44 // const DecodePerfParam kAV1DecodePerfVectors[] = {};
45 
46 /*
47  In order to reflect real world performance as much as possible, Perf tests
48  *DO NOT* do any correctness checks. Please run them alongside correctness
49  tests to ensure proper codec integrity. Furthermore, in this test we
50  deliberately limit the amount of system calls we make to avoid OS
51  preemption.
52 
53  TODO(joshualitt) create a more detailed perf measurement test to collect
54    power/temp/min max frame decode times/etc
55  */
56 
57 class DecodePerfTest : public ::testing::TestWithParam<DecodePerfParam> {};
58 
TEST_P(DecodePerfTest,PerfTest)59 TEST_P(DecodePerfTest, PerfTest) {
60   const char *const video_name = GET_PARAM(VIDEO_NAME);
61   const unsigned threads = GET_PARAM(THREADS);
62 
63   libaom_test::WebMVideoSource video(video_name);
64   video.Init();
65 
66   aom_codec_dec_cfg_t cfg = aom_codec_dec_cfg_t();
67   cfg.threads = threads;
68   cfg.allow_lowbitdepth = 1;
69   libaom_test::AV1Decoder decoder(cfg, 0);
70 
71   aom_usec_timer t;
72   aom_usec_timer_start(&t);
73 
74   for (video.Begin(); video.cxdata() != NULL; video.Next()) {
75     decoder.DecodeFrame(video.cxdata(), video.frame_size());
76   }
77 
78   aom_usec_timer_mark(&t);
79   const double elapsed_secs = double(aom_usec_timer_elapsed(&t)) / kUsecsInSec;
80   const unsigned frames = video.frame_number();
81   const double fps = double(frames) / elapsed_secs;
82 
83   printf("{\n");
84   printf("\t\"type\" : \"decode_perf_test\",\n");
85   printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
86   printf("\t\"videoName\" : \"%s\",\n", video_name);
87   printf("\t\"threadCount\" : %u,\n", threads);
88   printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
89   printf("\t\"totalFrames\" : %u,\n", frames);
90   printf("\t\"framesPerSecond\" : %f\n", fps);
91   printf("}\n");
92 }
93 
94 // TODO(jimbankoski): Enabled when we have actual AV1 Decode vectors.
95 // INSTANTIATE_TEST_SUITE_P(AV1, DecodePerfTest,
96 //                        ::testing::ValuesIn(kAV1DecodePerfVectors));
97 
98 class AV1NewEncodeDecodePerfTest
99     : public ::libaom_test::CodecTestWithParam<libaom_test::TestMode>,
100       public ::libaom_test::EncoderTest {
101  protected:
AV1NewEncodeDecodePerfTest()102   AV1NewEncodeDecodePerfTest()
103       : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)), speed_(0),
104         outfile_(0), out_frames_(0) {}
105 
~AV1NewEncodeDecodePerfTest()106   virtual ~AV1NewEncodeDecodePerfTest() {}
107 
SetUp()108   virtual void SetUp() {
109     InitializeConfig();
110     SetMode(encoding_mode_);
111 
112     cfg_.g_lag_in_frames = 25;
113     cfg_.rc_min_quantizer = 2;
114     cfg_.rc_max_quantizer = 56;
115     cfg_.rc_dropframe_thresh = 0;
116     cfg_.rc_undershoot_pct = 50;
117     cfg_.rc_overshoot_pct = 50;
118     cfg_.rc_buf_sz = 1000;
119     cfg_.rc_buf_initial_sz = 500;
120     cfg_.rc_buf_optimal_sz = 600;
121     cfg_.rc_end_usage = AOM_VBR;
122   }
123 
PreEncodeFrameHook(::libaom_test::VideoSource * video,::libaom_test::Encoder * encoder)124   virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video,
125                                   ::libaom_test::Encoder *encoder) {
126     if (video->frame() == 0) {
127       encoder->Control(AOME_SET_CPUUSED, speed_);
128       encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
129       encoder->Control(AV1E_SET_TILE_COLUMNS, 2);
130     }
131   }
132 
BeginPassHook(unsigned int)133   virtual void BeginPassHook(unsigned int /*pass*/) {
134     const char *const env = getenv("LIBAOM_TEST_DATA_PATH");
135     const std::string data_path(env ? env : ".");
136     const std::string path_to_source = data_path + "/" + kNewEncodeOutputFile;
137     outfile_ = fopen(path_to_source.c_str(), "wb");
138     ASSERT_TRUE(outfile_ != NULL);
139   }
140 
EndPassHook()141   virtual void EndPassHook() {
142     if (outfile_ != NULL) {
143       if (!fseek(outfile_, 0, SEEK_SET))
144         ivf_write_file_header(outfile_, &cfg_, AV1_FOURCC, out_frames_);
145       fclose(outfile_);
146       outfile_ = NULL;
147     }
148   }
149 
FramePktHook(const aom_codec_cx_pkt_t * pkt)150   virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) {
151     ++out_frames_;
152 
153     // Write initial file header if first frame.
154     if (pkt->data.frame.pts == 0)
155       ivf_write_file_header(outfile_, &cfg_, AV1_FOURCC, out_frames_);
156 
157     // Write frame header and data.
158     ivf_write_frame_header(outfile_, out_frames_, pkt->data.frame.sz);
159     ASSERT_EQ(fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_),
160               pkt->data.frame.sz);
161   }
162 
DoDecode() const163   virtual bool DoDecode() const { return false; }
164 
set_speed(unsigned int speed)165   void set_speed(unsigned int speed) { speed_ = speed; }
166 
167  private:
168   libaom_test::TestMode encoding_mode_;
169   uint32_t speed_;
170   FILE *outfile_;
171   uint32_t out_frames_;
172 };
173 
174 struct EncodePerfTestVideo {
EncodePerfTestVideo__anon06853f160111::EncodePerfTestVideo175   EncodePerfTestVideo(const char *name_, uint32_t width_, uint32_t height_,
176                       uint32_t bitrate_, int frames_)
177       : name(name_), width(width_), height(height_), bitrate(bitrate_),
178         frames(frames_) {}
179   const char *name;
180   uint32_t width;
181   uint32_t height;
182   uint32_t bitrate;
183   int frames;
184 };
185 
186 const EncodePerfTestVideo kAV1EncodePerfTestVectors[] = {
187   EncodePerfTestVideo("niklas_1280_720_30.yuv", 1280, 720, 600, 470),
188 };
189 
TEST_P(AV1NewEncodeDecodePerfTest,PerfTest)190 TEST_P(AV1NewEncodeDecodePerfTest, PerfTest) {
191   SetUp();
192 
193   // TODO(JBB): Make this work by going through the set of given files.
194   const int i = 0;
195   const aom_rational timebase = { 33333333, 1000000000 };
196   cfg_.g_timebase = timebase;
197   cfg_.rc_target_bitrate = kAV1EncodePerfTestVectors[i].bitrate;
198 
199   init_flags_ = AOM_CODEC_USE_PSNR;
200 
201   const char *video_name = kAV1EncodePerfTestVectors[i].name;
202   libaom_test::I420VideoSource video(
203       video_name, kAV1EncodePerfTestVectors[i].width,
204       kAV1EncodePerfTestVectors[i].height, timebase.den, timebase.num, 0,
205       kAV1EncodePerfTestVectors[i].frames);
206   set_speed(2);
207 
208   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
209 
210   const uint32_t threads = 4;
211 
212   libaom_test::IVFVideoSource decode_video(kNewEncodeOutputFile);
213   decode_video.Init();
214 
215   aom_codec_dec_cfg_t cfg = aom_codec_dec_cfg_t();
216   cfg.threads = threads;
217   cfg.allow_lowbitdepth = 1;
218   libaom_test::AV1Decoder decoder(cfg, 0);
219 
220   aom_usec_timer t;
221   aom_usec_timer_start(&t);
222 
223   for (decode_video.Begin(); decode_video.cxdata() != NULL;
224        decode_video.Next()) {
225     decoder.DecodeFrame(decode_video.cxdata(), decode_video.frame_size());
226   }
227 
228   aom_usec_timer_mark(&t);
229   const double elapsed_secs =
230       static_cast<double>(aom_usec_timer_elapsed(&t)) / kUsecsInSec;
231   const unsigned decode_frames = decode_video.frame_number();
232   const double fps = static_cast<double>(decode_frames) / elapsed_secs;
233 
234   printf("{\n");
235   printf("\t\"type\" : \"decode_perf_test\",\n");
236   printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
237   printf("\t\"videoName\" : \"%s\",\n", kNewEncodeOutputFile);
238   printf("\t\"threadCount\" : %u,\n", threads);
239   printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
240   printf("\t\"totalFrames\" : %u,\n", decode_frames);
241   printf("\t\"framesPerSecond\" : %f\n", fps);
242   printf("}\n");
243 }
244 
245 AV1_INSTANTIATE_TEST_SUITE(AV1NewEncodeDecodePerfTest,
246                            ::testing::Values(::libaom_test::kTwoPassGood));
247 }  // namespace
248