• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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], &timestamp, 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 
RecordFrameTimeDiff()174 bool FPSCalculator::RecordFrameTimeDiff() {
175     int64_t now_us = GetNowUs();
176     if (last_frame_time_us_ != 0) {
177         int64_t frame_diff_us = now_us - last_frame_time_us_;
178         if (frame_diff_us <= 0) return false;
179         frame_time_diffs_us_.push_back(static_cast<double>(frame_diff_us));
180     }
181     last_frame_time_us_ = now_us;
182     return true;
183 }
184 
185 // Reference: (https://cs.corp.google.com/android/cts/common/device-side/util/
186 //             src/com/android/compatibility/common/util/MediaPerfUtils.java)
187 //            addPerformanceStatsToLog
CalculateFPS() const188 double FPSCalculator::CalculateFPS() const {
189     std::vector<double> moving_avgs = MovingAvgOverSum();
190     std::sort(moving_avgs.begin(), moving_avgs.end());
191 
192     int index = static_cast<int>(std::round(kRegardedPercentile * (moving_avgs.size() - 1) / 100));
193     ALOGD("Frame decode time stats (us): { min=%.4f, regarded=%.4f, "
194           "max=%.4f}, window=%.0f",
195           moving_avgs[0], moving_avgs[index], moving_avgs[moving_avgs.size() - 1],
196           kMovingAvgWindowUs);
197 
198     return 1E6 / moving_avgs[index];
199 }
200 
201 // Reference: (https://cs.corp.google.com/android/cts/common/device-side/util/
202 //             src/com/android/compatibility/common/util/MediaUtils.java)
203 //            movingAverageOverSum
MovingAvgOverSum() const204 std::vector<double> FPSCalculator::MovingAvgOverSum() const {
205     std::vector<double> moving_avgs;
206 
207     double sum = std::accumulate(frame_time_diffs_us_.begin(), frame_time_diffs_us_.end(), 0.0);
208     int data_size = static_cast<int>(frame_time_diffs_us_.size());
209     double avg = sum / data_size;
210     if (kMovingAvgWindowUs >= sum) {
211         moving_avgs.push_back(avg);
212         return moving_avgs;
213     }
214 
215     int samples = static_cast<int>(std::ceil((sum - kMovingAvgWindowUs) / avg));
216     double cumulative_sum = 0;
217     int num = 0;
218     int bi = 0;
219     int ei = 0;
220     double space = kMovingAvgWindowUs;
221     double foot = 0;
222 
223     int ix = 0;
224     while (ix < samples) {
225         while (ei < data_size && frame_time_diffs_us_[ei] <= space) {
226             space -= frame_time_diffs_us_[ei];
227             cumulative_sum += frame_time_diffs_us_[ei];
228             num++;
229             ei++;
230         }
231 
232         if (num > 0) {
233             moving_avgs.push_back(cumulative_sum / num);
234         } else if (bi > 0 && foot > space) {
235             moving_avgs.push_back(frame_time_diffs_us_[bi - 1]);
236         } else if (ei == data_size) {
237             break;
238         } else {
239             moving_avgs.push_back(frame_time_diffs_us_[ei]);
240         }
241 
242         ix++;
243         foot -= avg;
244         space += avg;
245 
246         while (bi < ei && foot < 0) {
247             foot += frame_time_diffs_us_[bi];
248             cumulative_sum -= frame_time_diffs_us_[bi];
249             num--;
250             bi++;
251         }
252     }
253     return moving_avgs;
254 }
255 
VideoCodecProfileToType(VideoCodecProfile profile)256 VideoCodecType VideoCodecProfileToType(VideoCodecProfile profile) {
257     if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) return VideoCodecType::H264;
258     if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) return VideoCodecType::VP8;
259     if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) return VideoCodecType::VP9;
260     return VideoCodecType::UNKNOWN;
261 }
262 
SplitString(const std::string & src,char delim)263 std::vector<std::string> SplitString(const std::string& src, char delim) {
264     std::stringstream ss(src);
265     std::string item;
266     std::vector<std::string> ret;
267     while (std::getline(ss, item, delim)) {
268         ret.push_back(item);
269     }
270     return ret;
271 }
272 
GetNowUs()273 int64_t GetNowUs() {
274     struct timespec t;
275     t.tv_sec = t.tv_nsec = 0;
276     clock_gettime(CLOCK_MONOTONIC, &t);
277     int64_t nsecs = static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
278     return nsecs / 1000ll;
279 }
280 
GetMimeType(VideoCodecType type)281 const char* GetMimeType(VideoCodecType type) {
282     switch (type) {
283     case VideoCodecType::H264:
284         return "video/avc";
285     case VideoCodecType::VP8:
286         return "video/x-vnd.on2.vp8";
287     case VideoCodecType::VP9:
288         return "video/x-vnd.on2.vp9";
289     default:  // unknown type
290         return nullptr;
291     }
292 }
293 
294 }  // namespace android
295