1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // #define LOG_NDEBUG 0
6 #define LOG_TAG "Common"
7
8 #include "common.h"
9
10 #include <strings.h>
11 #include <time.h>
12
13 #include <algorithm>
14 #include <cmath>
15 #include <numeric>
16 #include <sstream>
17
18 #include <log/log.h>
19
20 namespace android {
21
InputFile(std::string file_path)22 InputFile::InputFile(std::string file_path) {
23 file_ = std::ifstream(file_path);
24 }
25
InputFile(std::string file_path,std::ios_base::openmode openmode)26 InputFile::InputFile(std::string file_path, std::ios_base::openmode openmode) {
27 file_ = std::ifstream(file_path, openmode);
28 }
29
IsValid() const30 bool InputFile::IsValid() const {
31 return file_.is_open();
32 }
33
GetLength()34 size_t InputFile::GetLength() {
35 int current_pos = file_.tellg();
36
37 file_.seekg(0, file_.end);
38 size_t ret = file_.tellg();
39
40 file_.seekg(current_pos, file_.beg);
41 return ret;
42 }
43
Rewind()44 void InputFile::Rewind() {
45 file_.clear();
46 file_.seekg(0);
47 }
48
CachedInputFileStream(std::string file_path)49 CachedInputFileStream::CachedInputFileStream(std::string file_path)
50 : InputFile(file_path, std::ifstream::binary) {
51 if (IsValid()) {
52 data_.resize(GetLength());
53 file_.read(data_.data(), GetLength());
54 }
55 }
56
Read(char * buffer,size_t size)57 size_t CachedInputFileStream::Read(char* buffer, size_t size) {
58 memcpy(buffer, data_.data() + position_, size);
59 position_ += size;
60 return size;
61 }
62
Rewind()63 void CachedInputFileStream::Rewind() {
64 position_ = 0;
65 }
66
InputFileASCII(std::string file_path)67 InputFileASCII::InputFileASCII(std::string file_path) : InputFile(file_path) {}
68
ReadLine(std::string * line)69 bool InputFileASCII::ReadLine(std::string* line) {
70 std::string read_line;
71 while (std::getline(file_, read_line)) {
72 if (read_line.empty()) // be careful: an empty line might be read
73 continue; // even if none exist.
74 *line = read_line;
75 return true;
76 }
77 return false; // no more lines
78 }
79
IVFWriter(std::ofstream * output_file,VideoCodecType codec)80 IVFWriter::IVFWriter(std::ofstream* output_file, VideoCodecType codec)
81 : output_file_(output_file), codec_(codec) {}
82
WriteHeader(const Size & resolution,uint32_t frame_rate,uint32_t num_frames)83 bool IVFWriter::WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames) {
84 constexpr uint16_t kIVFHeaderSize = 32;
85 char header[kIVFHeaderSize];
86
87 // Helper lambdas to write 16bit and 32bit data, expects the device to use little endian.
88 auto write16 = [&header](int i, uint16_t data) { memcpy(&header[i], &data, sizeof(data)); };
89 auto write32 = [&header](int i, uint32_t data) { memcpy(&header[i], &data, sizeof(data)); };
90
91 const char* codec_str;
92 switch (codec_) {
93 case VideoCodecType::VP8:
94 codec_str = "VP80";
95 break;
96 case VideoCodecType::VP9:
97 codec_str = "VP90";
98 break;
99 default:
100 printf("[ERR] Unknown codec: \n");
101 return false;
102 }
103
104 strcpy(&header[0], "DKIF"); // Bytes 0-3 of an IVF file header always contain 'DKIF' signature.
105 constexpr uint16_t kVersion = 0;
106 write16(4, kVersion);
107 write16(6, kIVFHeaderSize);
108 strcpy(&header[8], codec_str);
109 write16(12, resolution.width);
110 write16(14, resolution.height);
111 write32(16, frame_rate);
112 write32(20, 1);
113 write32(24, num_frames);
114 write32(28, 0); // Reserved.
115
116 output_file_->write(header, kIVFHeaderSize);
117 return !output_file_->bad();
118 }
119
WriteFrame(const uint8_t * data,uint32_t data_size,uint64_t timestamp)120 bool IVFWriter::WriteFrame(const uint8_t* data, uint32_t data_size, uint64_t timestamp) {
121 constexpr size_t kIVFFrameHeaderSize = 12;
122 char frame_header[kIVFFrameHeaderSize];
123 memcpy(&frame_header[0], &data_size, sizeof(data_size));
124 memcpy(&frame_header[4], ×tamp, sizeof(timestamp));
125 output_file_->write(frame_header, kIVFFrameHeaderSize);
126 output_file_->write(reinterpret_cast<const char*>(data), data_size);
127 return !output_file_->bad();
128 }
129
SetNumFrames(uint32_t num_frames)130 bool IVFWriter::SetNumFrames(uint32_t num_frames) {
131 output_file_->seekp(24);
132 output_file_->write(reinterpret_cast<const char*>(&num_frames), sizeof(num_frames));
133 return !output_file_->bad();
134 }
135
Open(const std::string & file_path,VideoCodecType codec)136 bool OutputFile::Open(const std::string& file_path, VideoCodecType codec) {
137 output_file_.open(file_path, std::ofstream::binary);
138 if (!output_file_.is_open()) {
139 return false;
140 }
141
142 if ((codec == VideoCodecType::VP8) || (codec == VideoCodecType::VP9)) {
143 ivf_writer_ = std::make_unique<IVFWriter>(&output_file_, codec);
144 }
145 return true;
146 }
147
Close()148 void OutputFile::Close() {
149 if (ivf_writer_) {
150 ivf_writer_->SetNumFrames(frame_index_);
151 ivf_writer_.reset();
152 }
153 output_file_.close();
154 }
155
IsOpen()156 bool OutputFile::IsOpen() {
157 return output_file_.is_open();
158 }
159
160 // Write the file header.
WriteHeader(const Size & resolution,uint32_t frame_rate,uint32_t num_frames)161 bool OutputFile::WriteHeader(const Size& resolution, uint32_t frame_rate, uint32_t num_frames) {
162 return !ivf_writer_ || ivf_writer_->WriteHeader(resolution, frame_rate, num_frames);
163 }
164
WriteFrame(uint32_t data_size,const uint8_t * data)165 bool OutputFile::WriteFrame(uint32_t data_size, const uint8_t* data) {
166 if (ivf_writer_) {
167 return (ivf_writer_->WriteFrame(data, data_size, frame_index_++));
168 } else {
169 output_file_.write(reinterpret_cast<const char*>(data), data_size);
170 return (output_file_.fail());
171 }
172 }
173
174 // Reference: (https://source.chromium.org/chromium/chromium/src/+/main:
175 // media/gpu/video_decode_accelerator_perf_tests.cc
PerformanceTimeStats(const std::vector<double> & times)176 PerformanceTimeStats::PerformanceTimeStats(const std::vector<double>& times) {
177 avg_us_ = std::accumulate(times.begin(), times.end(), 0.0) / times.size();
178 std::vector<double> sorted_times = times;
179 std::sort(sorted_times.begin(), sorted_times.end());
180 percentile_25_us_ = sorted_times[sorted_times.size() / 4];
181 percentile_50_us_ = sorted_times[sorted_times.size() / 2];
182 percentile_75_us_ = sorted_times[(sorted_times.size() * 3) / 4];
183 }
184
RecordFrameTimeDiff()185 bool FPSCalculator::RecordFrameTimeDiff() {
186 int64_t now_us = GetNowUs();
187 if (last_frame_time_us_ != 0) {
188 int64_t frame_diff_us = now_us - last_frame_time_us_;
189 if (frame_diff_us <= 0) return false;
190 frame_time_diffs_us_.push_back(static_cast<double>(frame_diff_us));
191 }
192 last_frame_time_us_ = now_us;
193 return true;
194 }
195
196 // Reference: (https://cs.corp.google.com/android/cts/common/device-side/util/
197 // src/com/android/compatibility/common/util/MediaPerfUtils.java)
198 // addPerformanceStatsToLog
CalculateFPS() const199 double FPSCalculator::CalculateFPS() const {
200 std::vector<double> moving_avgs = MovingAvgOverSum();
201 std::sort(moving_avgs.begin(), moving_avgs.end());
202
203 int index = static_cast<int>(std::round(kRegardedPercentile * (moving_avgs.size() - 1) / 100));
204 ALOGD("Frame decode time stats (us): { min=%.4f, regarded=%.4f, "
205 "max=%.4f}, window=%.0f",
206 moving_avgs[0], moving_avgs[index], moving_avgs[moving_avgs.size() - 1],
207 kMovingAvgWindowUs);
208
209 return 1E6 / moving_avgs[index];
210 }
211
CalucalateDeliveryTimeStats() const212 PerformanceTimeStats FPSCalculator::CalucalateDeliveryTimeStats() const {
213 return PerformanceTimeStats(frame_time_diffs_us_);
214 }
215
216 // Reference: (https://cs.corp.google.com/android/cts/common/device-side/util/
217 // src/com/android/compatibility/common/util/MediaUtils.java)
218 // movingAverageOverSum
MovingAvgOverSum() const219 std::vector<double> FPSCalculator::MovingAvgOverSum() const {
220 std::vector<double> moving_avgs;
221
222 double sum = std::accumulate(frame_time_diffs_us_.begin(), frame_time_diffs_us_.end(), 0.0);
223 int data_size = static_cast<int>(frame_time_diffs_us_.size());
224 double avg = sum / data_size;
225 if (kMovingAvgWindowUs >= sum) {
226 moving_avgs.push_back(avg);
227 return moving_avgs;
228 }
229
230 int samples = static_cast<int>(std::ceil((sum - kMovingAvgWindowUs) / avg));
231 double cumulative_sum = 0;
232 int num = 0;
233 int bi = 0;
234 int ei = 0;
235 double space = kMovingAvgWindowUs;
236 double foot = 0;
237
238 int ix = 0;
239 while (ix < samples) {
240 while (ei < data_size && frame_time_diffs_us_[ei] <= space) {
241 space -= frame_time_diffs_us_[ei];
242 cumulative_sum += frame_time_diffs_us_[ei];
243 num++;
244 ei++;
245 }
246
247 if (num > 0) {
248 moving_avgs.push_back(cumulative_sum / num);
249 } else if (bi > 0 && foot > space) {
250 moving_avgs.push_back(frame_time_diffs_us_[bi - 1]);
251 } else if (ei == data_size) {
252 break;
253 } else {
254 moving_avgs.push_back(frame_time_diffs_us_[ei]);
255 }
256
257 ix++;
258 foot -= avg;
259 space += avg;
260
261 while (bi < ei && foot < 0) {
262 foot += frame_time_diffs_us_[bi];
263 cumulative_sum -= frame_time_diffs_us_[bi];
264 num--;
265 bi++;
266 }
267 }
268 return moving_avgs;
269 }
270
VideoCodecProfileToType(VideoCodecProfile profile)271 VideoCodecType VideoCodecProfileToType(VideoCodecProfile profile) {
272 if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) return VideoCodecType::H264;
273 if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) return VideoCodecType::VP8;
274 if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) return VideoCodecType::VP9;
275 if (profile >= HEVCPROFILE_MIN && profile <= HEVCPROFILE_MAX) return VideoCodecType::HEVC;
276 return VideoCodecType::UNKNOWN;
277 }
278
SplitString(const std::string & src,char delim)279 std::vector<std::string> SplitString(const std::string& src, char delim) {
280 std::stringstream ss(src);
281 std::string item;
282 std::vector<std::string> ret;
283 while (std::getline(ss, item, delim)) {
284 ret.push_back(item);
285 }
286 return ret;
287 }
288
GetNowUs()289 int64_t GetNowUs() {
290 struct timespec t;
291 t.tv_sec = t.tv_nsec = 0;
292 clock_gettime(CLOCK_MONOTONIC, &t);
293 int64_t nsecs = static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
294 return nsecs / 1000ll;
295 }
296
GetMimeType(VideoCodecType type)297 const char* GetMimeType(VideoCodecType type) {
298 switch (type) {
299 case VideoCodecType::H264:
300 return "video/avc";
301 case VideoCodecType::VP8:
302 return "video/x-vnd.on2.vp8";
303 case VideoCodecType::VP9:
304 return "video/x-vnd.on2.vp9";
305 case VideoCodecType::HEVC:
306 return "video/hevc";
307 default: // unknown type
308 return nullptr;
309 }
310 }
311
312 } // namespace android
313