blob: e0b20504fd366b0dcc05f35b804e72ae653d5d7f [file] [log] [blame]
Linus Nilsson8f0b8762020-07-23 17:12:45 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * Native media transcoder library benchmark tests.
19 *
20 * How to run the benchmark:
21 *
22 * 1. Download the media assets from http://go/transcodingbenchmark and push the directory
23 * ("TranscodingBenchmark") to /data/local/tmp.
24 *
25 * 2. Compile the benchmark and sync to device:
26 * $ mm -j72 && adb sync
27 *
28 * 3. Run:
29 * $ adb shell /data/nativetest64/MediaTranscoderBenchmark/MediaTranscoderBenchmark
30 */
31
32#include <benchmark/benchmark.h>
Naveen Kumar Ponnusamy2d074ba2020-12-17 17:13:45 -080033#include <binder/ProcessState.h>
Linus Nilsson8f0b8762020-07-23 17:12:45 -070034#include <fcntl.h>
35#include <media/MediaTranscoder.h>
Linus Nilsson47352412020-12-16 12:21:26 -080036
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053037#include <iostream>
Linus Nilsson8f0b8762020-07-23 17:12:45 -070038
39using namespace android;
40
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053041const std::string PARAM_VIDEO_FRAME_RATE = "VideoFrameRate";
42
Linus Nilsson8f0b8762020-07-23 17:12:45 -070043class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
44public:
45 virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
46 std::unique_lock<std::mutex> lock(mMutex);
47 mFinished = true;
48 mCondition.notify_all();
49 }
50
51 virtual void onError(const MediaTranscoder* transcoder __unused,
52 media_status_t error) override {
53 std::unique_lock<std::mutex> lock(mMutex);
54 mFinished = true;
55 mStatus = error;
56 mCondition.notify_all();
57 }
58
59 virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
60 int32_t progress __unused) override {}
61
62 virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
Chong Zhange4e088f2020-10-21 19:10:42 -070063 const std::shared_ptr<ndk::ScopedAParcel>& pausedState
Linus Nilsson8f0b8762020-07-23 17:12:45 -070064 __unused) override {}
65
66 bool waitForTranscodingFinished() {
67 std::unique_lock<std::mutex> lock(mMutex);
68 while (!mFinished) {
69 if (mCondition.wait_for(lock, std::chrono::minutes(5)) == std::cv_status::timeout) {
70 return false;
71 }
72 }
73 return true;
74 }
75
76 media_status_t mStatus = AMEDIA_OK;
77
78private:
79 std::mutex mMutex;
80 std::condition_variable mCondition;
81 bool mFinished = false;
82};
83
Linus Nilsson16d772b2020-09-29 19:21:11 -070084static AMediaFormat* CreateDefaultVideoFormat() {
Linus Nilsson8f0b8762020-07-23 17:12:45 -070085 // Default bitrate
86 static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20Mbs
Linus Nilsson16d772b2020-09-29 19:21:11 -070087
88 AMediaFormat* videoFormat = AMediaFormat_new();
89 AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
90 return videoFormat;
91}
92
93/**
94 * Callback to configure tracks for transcoding.
95 * @param mime The source track mime type.
96 * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
97 * should be passed through.
98 * @return True if the track should be included in the output file.
99 */
100using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;
101
102static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
103 const std::string& dstFileName,
104 TrackSelectionCallback trackSelectionCallback) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700105 // Write-only, create file if non-existent.
106 static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
107 // User R+W permission.
108 static constexpr int kDstFileMode = S_IRUSR | S_IWUSR;
109 // Asset directory
110 static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
111
112 int srcFd = 0;
113 int dstFd = 0;
114
115 std::string srcPath = kAssetDirectory + srcFileName;
116 std::string dstPath = kAssetDirectory + dstFileName;
117
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700118 media_status_t status = AMEDIA_OK;
119
120 if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
121 state.SkipWithError("Unable to open source file");
122 goto exit;
123 }
124 if ((dstFd = open(dstPath.c_str(), kDstOpenFlags, kDstFileMode)) < 0) {
125 state.SkipWithError("Unable to open destination file");
126 goto exit;
127 }
128
129 for (auto _ : state) {
Naveen Kumar Ponnusamy78d362a2020-12-16 18:10:47 -0800130 auto callbacks = std::make_shared<TranscoderCallbacks>();
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800131 auto transcoder = MediaTranscoder::create(callbacks);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700132
133 status = transcoder->configureSource(srcFd);
134 if (status != AMEDIA_OK) {
135 state.SkipWithError("Unable to configure transcoder source");
136 goto exit;
137 }
138
139 status = transcoder->configureDestination(dstFd);
140 if (status != AMEDIA_OK) {
141 state.SkipWithError("Unable to configure transcoder destination");
142 goto exit;
143 }
144
145 std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
146 for (int i = 0; i < trackFormats.size(); ++i) {
147 AMediaFormat* srcFormat = trackFormats[i].get();
148 AMediaFormat* dstFormat = nullptr;
149
150 const char* mime = nullptr;
151 if (!AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
152 state.SkipWithError("Source track format does not have MIME type");
153 goto exit;
154 }
155
156 if (strncmp(mime, "video/", 6) == 0) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700157 int32_t frameCount;
158 if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
Naveen Kumar Ponnusamy0dbe8712020-12-16 23:31:38 -0800159 state.counters[PARAM_VIDEO_FRAME_RATE] = benchmark::Counter(
160 frameCount, benchmark::Counter::kIsIterationInvariantRate);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700161 }
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700162 }
163
Linus Nilsson16d772b2020-09-29 19:21:11 -0700164 if (trackSelectionCallback(mime, &dstFormat)) {
165 status = transcoder->configureTrackFormat(i, dstFormat);
166 }
167
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700168 if (dstFormat != nullptr) {
169 AMediaFormat_delete(dstFormat);
170 }
171 if (status != AMEDIA_OK) {
172 state.SkipWithError("Unable to configure track");
173 goto exit;
174 }
175 }
176
177 status = transcoder->start();
178 if (status != AMEDIA_OK) {
179 state.SkipWithError("Unable to start transcoder");
180 goto exit;
181 }
182
183 if (!callbacks->waitForTranscodingFinished()) {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700184 transcoder->cancel();
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700185 state.SkipWithError("Transcoder timed out");
186 goto exit;
187 }
188 if (callbacks->mStatus != AMEDIA_OK) {
189 state.SkipWithError("Transcoder error when running");
190 goto exit;
191 }
192 }
193
194exit:
195 if (srcFd > 0) close(srcFd);
196 if (dstFd > 0) close(dstFd);
197}
198
Linus Nilsson6bf46532020-10-07 11:58:02 -0700199/**
200 * Callback to edit track format for transcoding.
201 * @param dstFormat The default track format for the track type.
202 */
203using TrackFormatEditCallback = std::function<void(AMediaFormat* dstFormat)>;
204
Linus Nilsson16d772b2020-09-29 19:21:11 -0700205static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
206 const std::string& dstFileName, bool includeAudio,
Linus Nilsson6bf46532020-10-07 11:58:02 -0700207 bool transcodeVideo,
208 const TrackFormatEditCallback& videoFormatEditor = nullptr) {
Linus Nilsson16d772b2020-09-29 19:21:11 -0700209 TranscodeMediaFile(state, srcFileName, dstFileName,
210 [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
211 *dstFormatOut = nullptr;
212 if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
213 *dstFormatOut = CreateDefaultVideoFormat();
Linus Nilsson6bf46532020-10-07 11:58:02 -0700214 if (videoFormatEditor != nullptr) {
215 videoFormatEditor(*dstFormatOut);
216 }
Linus Nilsson16d772b2020-09-29 19:21:11 -0700217 } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
218 return false;
219 }
220 return true;
221 });
222}
223
Linus Nilsson6bf46532020-10-07 11:58:02 -0700224static void SetMaxOperatingRate(AMediaFormat* format) {
225 AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
226 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
227}
228
229//-------------------------------- AVC to AVC Benchmarks -------------------------------------------
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700230
231static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
232 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
233 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700234 true /* includeAudio */, true /* transcodeVideo */);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700235}
236
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700237static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
238 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
239 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700240 false /* includeAudio */, true /* transcodeVideo */);
241}
242
243static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
244 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
245 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700246 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700247}
248
249static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
250 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
251 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700252 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700253}
254
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700255static void BM_TranscodeAvc2AvcAV2AV720P(benchmark::State& state) {
256 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
257 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
258 true /* includeAudio */, true /* transcodeVideo */);
259}
260
261static void BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
262 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
263 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
264 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
265}
Linus Nilsson6bf46532020-10-07 11:58:02 -0700266//-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
267
268static void BM_TranscodeHevc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
269 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
270 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
271 true /* includeAudio */, true /* transcodeVideo */);
272}
273
274static void BM_TranscodeHevc2AvcVideo2Video(benchmark::State& state) {
275 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
276 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
277 false /* includeAudio */, true /* transcodeVideo */);
278}
279
280static void BM_TranscodeHevc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
281 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
282 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
283 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
284}
285
286static void BM_TranscodeHevc2AvcV2VMaxOperatingRate(benchmark::State& state) {
287 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
288 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
289 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
290}
291
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700292static void BM_TranscodeHevc2AvcAV2AV720P(benchmark::State& state) {
293 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
294 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
295 true /* includeAudio */, true /* transcodeVideo */);
296}
297
298static void BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
299 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
300 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
301 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
302}
303
Linus Nilsson6bf46532020-10-07 11:58:02 -0700304//-------------------------------- Passthrough Benchmarks ------------------------------------------
305
Linus Nilsson6233fed2020-08-13 15:15:14 -0700306static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
307 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
308 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_passthrough_AV.mp4",
309 true /* includeAudio */, false /* transcodeVideo */);
310}
311static void BM_TranscodeVideoPassthrough(benchmark::State& state) {
312 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
313 "video_1920x1080_3648frame_h264_22Mbps_30fps_passthrough_AV.mp4",
314 false /* includeAudio */, false /* transcodeVideo */);
315}
316
Linus Nilsson6bf46532020-10-07 11:58:02 -0700317//-------------------------------- Benchmark Registration ------------------------------------------
318
319// Benchmark registration wrapper for transcoding.
320#define TRANSCODER_BENCHMARK(func) \
321 BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
322
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700323TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700324TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700325TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
326TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700327TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720P);
328TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700329
330TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAudioVideo2AudioVideo);
331TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcVideo2Video);
332TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AVMaxOperatingRate);
333TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700334TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720P);
335TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700336
Linus Nilsson6233fed2020-08-13 15:15:14 -0700337TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
338TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700339
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530340class CustomCsvReporter : public benchmark::BenchmarkReporter {
341public:
342 CustomCsvReporter() : mPrintedHeader(false) {}
343 virtual bool ReportContext(const Context& context);
344 virtual void ReportRuns(const std::vector<Run>& reports);
345
346private:
347 void PrintRunData(const Run& report);
348
349 bool mPrintedHeader;
350 std::vector<std::string> mHeaders = {"name", "real_time", "cpu_time", PARAM_VIDEO_FRAME_RATE};
351};
352
353bool CustomCsvReporter::ReportContext(const Context& context __unused) {
354 return true;
355}
356
357void CustomCsvReporter::ReportRuns(const std::vector<Run>& reports) {
358 std::ostream& Out = GetOutputStream();
359
360 if (!mPrintedHeader) {
361 // print the header
362 for (auto header = mHeaders.begin(); header != mHeaders.end();) {
363 Out << *header++;
364 if (header != mHeaders.end()) Out << ",";
365 }
366 Out << "\n";
367 mPrintedHeader = true;
368 }
369
370 // print results for each run
371 for (const auto& run : reports) {
372 PrintRunData(run);
373 }
374}
375
376void CustomCsvReporter::PrintRunData(const Run& run) {
377 if (run.error_occurred) {
378 return;
379 }
380 std::ostream& Out = GetOutputStream();
381 Out << run.benchmark_name() << ",";
382 Out << run.GetAdjustedRealTime() << ",";
383 Out << run.GetAdjustedCPUTime() << ",";
384 auto frameRate = run.counters.find(PARAM_VIDEO_FRAME_RATE);
385 if (frameRate == run.counters.end()) {
386 Out << "NA"
387 << ",";
388 } else {
389 Out << frameRate->second << ",";
390 }
391 Out << '\n';
392}
393
394int main(int argc, char** argv) {
Naveen Kumar Ponnusamy2d074ba2020-12-17 17:13:45 -0800395 android::ProcessState::self()->startThreadPool();
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530396 std::unique_ptr<benchmark::BenchmarkReporter> fileReporter;
397 for (int i = 1; i < argc; ++i) {
398 if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) {
399 fileReporter.reset(new CustomCsvReporter);
400 break;
401 }
402 }
403 ::benchmark::Initialize(&argc, argv);
404 if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
405 ::benchmark::RunSpecifiedBenchmarks(nullptr, fileReporter.get());
406}