blob: d47a30c8a05331a6312d0216aa7dfb9d076f35d8 [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>
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053036#include <iostream>
Linus Nilsson8f0b8762020-07-23 17:12:45 -070037
38using namespace android;
39
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053040const std::string PARAM_VIDEO_FRAME_RATE = "VideoFrameRate";
41
Linus Nilsson8f0b8762020-07-23 17:12:45 -070042class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
43public:
44 virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
45 std::unique_lock<std::mutex> lock(mMutex);
46 mFinished = true;
47 mCondition.notify_all();
48 }
49
50 virtual void onError(const MediaTranscoder* transcoder __unused,
51 media_status_t error) override {
52 std::unique_lock<std::mutex> lock(mMutex);
53 mFinished = true;
54 mStatus = error;
55 mCondition.notify_all();
56 }
57
58 virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
59 int32_t progress __unused) override {}
60
61 virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
Chong Zhange4e088f2020-10-21 19:10:42 -070062 const std::shared_ptr<ndk::ScopedAParcel>& pausedState
Linus Nilsson8f0b8762020-07-23 17:12:45 -070063 __unused) override {}
64
65 bool waitForTranscodingFinished() {
66 std::unique_lock<std::mutex> lock(mMutex);
67 while (!mFinished) {
68 if (mCondition.wait_for(lock, std::chrono::minutes(5)) == std::cv_status::timeout) {
69 return false;
70 }
71 }
72 return true;
73 }
74
75 media_status_t mStatus = AMEDIA_OK;
76
77private:
78 std::mutex mMutex;
79 std::condition_variable mCondition;
80 bool mFinished = false;
81};
82
Linus Nilsson16d772b2020-09-29 19:21:11 -070083static AMediaFormat* CreateDefaultVideoFormat() {
Linus Nilsson8f0b8762020-07-23 17:12:45 -070084 // Default bitrate
85 static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20Mbs
Linus Nilsson16d772b2020-09-29 19:21:11 -070086
87 AMediaFormat* videoFormat = AMediaFormat_new();
88 AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
89 return videoFormat;
90}
91
92/**
93 * Callback to configure tracks for transcoding.
94 * @param mime The source track mime type.
95 * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
96 * should be passed through.
97 * @return True if the track should be included in the output file.
98 */
99using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;
100
101static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
102 const std::string& dstFileName,
103 TrackSelectionCallback trackSelectionCallback) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700104 // Write-only, create file if non-existent.
105 static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
106 // User R+W permission.
107 static constexpr int kDstFileMode = S_IRUSR | S_IWUSR;
108 // Asset directory
109 static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
110
111 int srcFd = 0;
112 int dstFd = 0;
113
114 std::string srcPath = kAssetDirectory + srcFileName;
115 std::string dstPath = kAssetDirectory + dstFileName;
116
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700117 media_status_t status = AMEDIA_OK;
118
119 if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
120 state.SkipWithError("Unable to open source file");
121 goto exit;
122 }
123 if ((dstFd = open(dstPath.c_str(), kDstOpenFlags, kDstFileMode)) < 0) {
124 state.SkipWithError("Unable to open destination file");
125 goto exit;
126 }
127
128 for (auto _ : state) {
Naveen Kumar Ponnusamy78d362a2020-12-16 18:10:47 -0800129 auto callbacks = std::make_shared<TranscoderCallbacks>();
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800130 auto transcoder = MediaTranscoder::create(callbacks);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700131
132 status = transcoder->configureSource(srcFd);
133 if (status != AMEDIA_OK) {
134 state.SkipWithError("Unable to configure transcoder source");
135 goto exit;
136 }
137
138 status = transcoder->configureDestination(dstFd);
139 if (status != AMEDIA_OK) {
140 state.SkipWithError("Unable to configure transcoder destination");
141 goto exit;
142 }
143
144 std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
145 for (int i = 0; i < trackFormats.size(); ++i) {
146 AMediaFormat* srcFormat = trackFormats[i].get();
147 AMediaFormat* dstFormat = nullptr;
148
149 const char* mime = nullptr;
150 if (!AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
151 state.SkipWithError("Source track format does not have MIME type");
152 goto exit;
153 }
154
155 if (strncmp(mime, "video/", 6) == 0) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700156 int32_t frameCount;
157 if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
Naveen Kumar Ponnusamy0dbe8712020-12-16 23:31:38 -0800158 state.counters[PARAM_VIDEO_FRAME_RATE] = benchmark::Counter(
159 frameCount, benchmark::Counter::kIsIterationInvariantRate);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700160 }
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700161 }
162
Linus Nilsson16d772b2020-09-29 19:21:11 -0700163 if (trackSelectionCallback(mime, &dstFormat)) {
164 status = transcoder->configureTrackFormat(i, dstFormat);
165 }
166
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700167 if (dstFormat != nullptr) {
168 AMediaFormat_delete(dstFormat);
169 }
170 if (status != AMEDIA_OK) {
171 state.SkipWithError("Unable to configure track");
172 goto exit;
173 }
174 }
175
176 status = transcoder->start();
177 if (status != AMEDIA_OK) {
178 state.SkipWithError("Unable to start transcoder");
179 goto exit;
180 }
181
182 if (!callbacks->waitForTranscodingFinished()) {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700183 transcoder->cancel();
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700184 state.SkipWithError("Transcoder timed out");
185 goto exit;
186 }
187 if (callbacks->mStatus != AMEDIA_OK) {
188 state.SkipWithError("Transcoder error when running");
189 goto exit;
190 }
191 }
192
193exit:
194 if (srcFd > 0) close(srcFd);
195 if (dstFd > 0) close(dstFd);
196}
197
Linus Nilsson6bf46532020-10-07 11:58:02 -0700198/**
199 * Callback to edit track format for transcoding.
200 * @param dstFormat The default track format for the track type.
201 */
202using TrackFormatEditCallback = std::function<void(AMediaFormat* dstFormat)>;
203
Linus Nilsson16d772b2020-09-29 19:21:11 -0700204static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
205 const std::string& dstFileName, bool includeAudio,
Linus Nilsson6bf46532020-10-07 11:58:02 -0700206 bool transcodeVideo,
207 const TrackFormatEditCallback& videoFormatEditor = nullptr) {
Linus Nilsson16d772b2020-09-29 19:21:11 -0700208 TranscodeMediaFile(state, srcFileName, dstFileName,
209 [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
210 *dstFormatOut = nullptr;
211 if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
212 *dstFormatOut = CreateDefaultVideoFormat();
Linus Nilsson6bf46532020-10-07 11:58:02 -0700213 if (videoFormatEditor != nullptr) {
214 videoFormatEditor(*dstFormatOut);
215 }
Linus Nilsson16d772b2020-09-29 19:21:11 -0700216 } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
217 return false;
218 }
219 return true;
220 });
221}
222
Linus Nilsson6bf46532020-10-07 11:58:02 -0700223static void SetMaxOperatingRate(AMediaFormat* format) {
224 AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
225 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
226}
227
228//-------------------------------- AVC to AVC Benchmarks -------------------------------------------
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700229
230static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
231 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
232 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700233 true /* includeAudio */, true /* transcodeVideo */);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700234}
235
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700236static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
237 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
238 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700239 false /* includeAudio */, true /* transcodeVideo */);
240}
241
242static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
243 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
244 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700245 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700246}
247
248static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
249 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
250 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700251 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700252}
253
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700254static void BM_TranscodeAvc2AvcAV2AV720P(benchmark::State& state) {
255 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
256 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
257 true /* includeAudio */, true /* transcodeVideo */);
258}
259
260static void BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
261 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
262 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
263 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
264}
Linus Nilsson6bf46532020-10-07 11:58:02 -0700265//-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
266
267static void BM_TranscodeHevc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
268 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
269 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
270 true /* includeAudio */, true /* transcodeVideo */);
271}
272
273static void BM_TranscodeHevc2AvcVideo2Video(benchmark::State& state) {
274 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
275 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
276 false /* includeAudio */, true /* transcodeVideo */);
277}
278
279static void BM_TranscodeHevc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
280 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
281 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
282 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
283}
284
285static void BM_TranscodeHevc2AvcV2VMaxOperatingRate(benchmark::State& state) {
286 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
287 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
288 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
289}
290
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700291static void BM_TranscodeHevc2AvcAV2AV720P(benchmark::State& state) {
292 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
293 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
294 true /* includeAudio */, true /* transcodeVideo */);
295}
296
297static void BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
298 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
299 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
300 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
301}
302
Linus Nilsson6bf46532020-10-07 11:58:02 -0700303//-------------------------------- Passthrough Benchmarks ------------------------------------------
304
Linus Nilsson6233fed2020-08-13 15:15:14 -0700305static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
306 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
307 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_passthrough_AV.mp4",
308 true /* includeAudio */, false /* transcodeVideo */);
309}
310static void BM_TranscodeVideoPassthrough(benchmark::State& state) {
311 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
312 "video_1920x1080_3648frame_h264_22Mbps_30fps_passthrough_AV.mp4",
313 false /* includeAudio */, false /* transcodeVideo */);
314}
315
Linus Nilsson6bf46532020-10-07 11:58:02 -0700316//-------------------------------- Benchmark Registration ------------------------------------------
317
318// Benchmark registration wrapper for transcoding.
319#define TRANSCODER_BENCHMARK(func) \
320 BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
321
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700322TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700323TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700324TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
325TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700326TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720P);
327TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700328
329TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAudioVideo2AudioVideo);
330TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcVideo2Video);
331TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AVMaxOperatingRate);
332TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700333TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720P);
334TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700335
Linus Nilsson6233fed2020-08-13 15:15:14 -0700336TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
337TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700338
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530339class CustomCsvReporter : public benchmark::BenchmarkReporter {
340public:
341 CustomCsvReporter() : mPrintedHeader(false) {}
342 virtual bool ReportContext(const Context& context);
343 virtual void ReportRuns(const std::vector<Run>& reports);
344
345private:
346 void PrintRunData(const Run& report);
347
348 bool mPrintedHeader;
349 std::vector<std::string> mHeaders = {"name", "real_time", "cpu_time", PARAM_VIDEO_FRAME_RATE};
350};
351
352bool CustomCsvReporter::ReportContext(const Context& context __unused) {
353 return true;
354}
355
356void CustomCsvReporter::ReportRuns(const std::vector<Run>& reports) {
357 std::ostream& Out = GetOutputStream();
358
359 if (!mPrintedHeader) {
360 // print the header
361 for (auto header = mHeaders.begin(); header != mHeaders.end();) {
362 Out << *header++;
363 if (header != mHeaders.end()) Out << ",";
364 }
365 Out << "\n";
366 mPrintedHeader = true;
367 }
368
369 // print results for each run
370 for (const auto& run : reports) {
371 PrintRunData(run);
372 }
373}
374
375void CustomCsvReporter::PrintRunData(const Run& run) {
376 if (run.error_occurred) {
377 return;
378 }
379 std::ostream& Out = GetOutputStream();
380 Out << run.benchmark_name() << ",";
381 Out << run.GetAdjustedRealTime() << ",";
382 Out << run.GetAdjustedCPUTime() << ",";
383 auto frameRate = run.counters.find(PARAM_VIDEO_FRAME_RATE);
384 if (frameRate == run.counters.end()) {
385 Out << "NA"
386 << ",";
387 } else {
388 Out << frameRate->second << ",";
389 }
390 Out << '\n';
391}
392
393int main(int argc, char** argv) {
Naveen Kumar Ponnusamy2d074ba2020-12-17 17:13:45 -0800394 android::ProcessState::self()->startThreadPool();
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530395 std::unique_ptr<benchmark::BenchmarkReporter> fileReporter;
396 for (int i = 1; i < argc; ++i) {
397 if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) {
398 fileReporter.reset(new CustomCsvReporter);
399 break;
400 }
401 }
402 ::benchmark::Initialize(&argc, argv);
403 if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
404 ::benchmark::RunSpecifiedBenchmarks(nullptr, fileReporter.get());
405}