blob: 9ee55e538db059aef7f9e71877fd1b267ef2b665 [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>
33#include <fcntl.h>
34#include <media/MediaTranscoder.h>
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053035#include <iostream>
Linus Nilsson8f0b8762020-07-23 17:12:45 -070036
37using namespace android;
38
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +053039const std::string PARAM_VIDEO_FRAME_RATE = "VideoFrameRate";
40
Linus Nilsson8f0b8762020-07-23 17:12:45 -070041class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
42public:
43 virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
44 std::unique_lock<std::mutex> lock(mMutex);
45 mFinished = true;
46 mCondition.notify_all();
47 }
48
49 virtual void onError(const MediaTranscoder* transcoder __unused,
50 media_status_t error) override {
51 std::unique_lock<std::mutex> lock(mMutex);
52 mFinished = true;
53 mStatus = error;
54 mCondition.notify_all();
55 }
56
57 virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
58 int32_t progress __unused) override {}
59
60 virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
Chong Zhange4e088f2020-10-21 19:10:42 -070061 const std::shared_ptr<ndk::ScopedAParcel>& pausedState
Linus Nilsson8f0b8762020-07-23 17:12:45 -070062 __unused) override {}
63
64 bool waitForTranscodingFinished() {
65 std::unique_lock<std::mutex> lock(mMutex);
66 while (!mFinished) {
67 if (mCondition.wait_for(lock, std::chrono::minutes(5)) == std::cv_status::timeout) {
68 return false;
69 }
70 }
71 return true;
72 }
73
74 media_status_t mStatus = AMEDIA_OK;
75
76private:
77 std::mutex mMutex;
78 std::condition_variable mCondition;
79 bool mFinished = false;
80};
81
Linus Nilsson16d772b2020-09-29 19:21:11 -070082static AMediaFormat* CreateDefaultVideoFormat() {
Linus Nilsson8f0b8762020-07-23 17:12:45 -070083 // Default bitrate
84 static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20Mbs
Linus Nilsson16d772b2020-09-29 19:21:11 -070085
86 AMediaFormat* videoFormat = AMediaFormat_new();
87 AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
88 return videoFormat;
89}
90
91/**
92 * Callback to configure tracks for transcoding.
93 * @param mime The source track mime type.
94 * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
95 * should be passed through.
96 * @return True if the track should be included in the output file.
97 */
98using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;
99
100static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
101 const std::string& dstFileName,
102 TrackSelectionCallback trackSelectionCallback) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700103 // Write-only, create file if non-existent.
104 static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
105 // User R+W permission.
106 static constexpr int kDstFileMode = S_IRUSR | S_IWUSR;
107 // Asset directory
108 static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
109
110 int srcFd = 0;
111 int dstFd = 0;
112
113 std::string srcPath = kAssetDirectory + srcFileName;
114 std::string dstPath = kAssetDirectory + dstFileName;
115
116 auto callbacks = std::make_shared<TranscoderCallbacks>();
117 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) {
Chong Zhangbbb4eac2020-11-18 11:12:06 -0800129 auto transcoder = MediaTranscoder::create(callbacks);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700130
131 status = transcoder->configureSource(srcFd);
132 if (status != AMEDIA_OK) {
133 state.SkipWithError("Unable to configure transcoder source");
134 goto exit;
135 }
136
137 status = transcoder->configureDestination(dstFd);
138 if (status != AMEDIA_OK) {
139 state.SkipWithError("Unable to configure transcoder destination");
140 goto exit;
141 }
142
143 std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
144 for (int i = 0; i < trackFormats.size(); ++i) {
145 AMediaFormat* srcFormat = trackFormats[i].get();
146 AMediaFormat* dstFormat = nullptr;
147
148 const char* mime = nullptr;
149 if (!AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
150 state.SkipWithError("Source track format does not have MIME type");
151 goto exit;
152 }
153
154 if (strncmp(mime, "video/", 6) == 0) {
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700155 int32_t frameCount;
156 if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530157 state.counters[PARAM_VIDEO_FRAME_RATE] =
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700158 benchmark::Counter(frameCount, benchmark::Counter::kIsRate);
159 }
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700160 }
161
Linus Nilsson16d772b2020-09-29 19:21:11 -0700162 if (trackSelectionCallback(mime, &dstFormat)) {
163 status = transcoder->configureTrackFormat(i, dstFormat);
164 }
165
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700166 if (dstFormat != nullptr) {
167 AMediaFormat_delete(dstFormat);
168 }
169 if (status != AMEDIA_OK) {
170 state.SkipWithError("Unable to configure track");
171 goto exit;
172 }
173 }
174
175 status = transcoder->start();
176 if (status != AMEDIA_OK) {
177 state.SkipWithError("Unable to start transcoder");
178 goto exit;
179 }
180
181 if (!callbacks->waitForTranscodingFinished()) {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700182 transcoder->cancel();
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700183 state.SkipWithError("Transcoder timed out");
184 goto exit;
185 }
186 if (callbacks->mStatus != AMEDIA_OK) {
187 state.SkipWithError("Transcoder error when running");
188 goto exit;
189 }
190 }
191
192exit:
193 if (srcFd > 0) close(srcFd);
194 if (dstFd > 0) close(dstFd);
195}
196
Linus Nilsson6bf46532020-10-07 11:58:02 -0700197/**
198 * Callback to edit track format for transcoding.
199 * @param dstFormat The default track format for the track type.
200 */
201using TrackFormatEditCallback = std::function<void(AMediaFormat* dstFormat)>;
202
Linus Nilsson16d772b2020-09-29 19:21:11 -0700203static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
204 const std::string& dstFileName, bool includeAudio,
Linus Nilsson6bf46532020-10-07 11:58:02 -0700205 bool transcodeVideo,
206 const TrackFormatEditCallback& videoFormatEditor = nullptr) {
Linus Nilsson16d772b2020-09-29 19:21:11 -0700207 TranscodeMediaFile(state, srcFileName, dstFileName,
208 [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
209 *dstFormatOut = nullptr;
210 if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
211 *dstFormatOut = CreateDefaultVideoFormat();
Linus Nilsson6bf46532020-10-07 11:58:02 -0700212 if (videoFormatEditor != nullptr) {
213 videoFormatEditor(*dstFormatOut);
214 }
Linus Nilsson16d772b2020-09-29 19:21:11 -0700215 } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
216 return false;
217 }
218 return true;
219 });
220}
221
Linus Nilsson6bf46532020-10-07 11:58:02 -0700222static void SetMaxOperatingRate(AMediaFormat* format) {
223 AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
224 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
225}
226
227//-------------------------------- AVC to AVC Benchmarks -------------------------------------------
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700228
229static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
230 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
231 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700232 true /* includeAudio */, true /* transcodeVideo */);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700233}
234
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700235static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
236 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
237 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson16d772b2020-09-29 19:21:11 -0700238 false /* includeAudio */, true /* transcodeVideo */);
239}
240
241static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
242 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
243 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700244 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700245}
246
247static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
248 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
249 "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
Linus Nilsson6bf46532020-10-07 11:58:02 -0700250 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700251}
252
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700253static void BM_TranscodeAvc2AvcAV2AV720P(benchmark::State& state) {
254 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
255 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
256 true /* includeAudio */, true /* transcodeVideo */);
257}
258
259static void BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
260 TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
261 "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
262 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
263}
Linus Nilsson6bf46532020-10-07 11:58:02 -0700264//-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
265
266static void BM_TranscodeHevc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
267 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
268 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
269 true /* includeAudio */, true /* transcodeVideo */);
270}
271
272static void BM_TranscodeHevc2AvcVideo2Video(benchmark::State& state) {
273 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
274 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
275 false /* includeAudio */, true /* transcodeVideo */);
276}
277
278static void BM_TranscodeHevc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
279 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
280 "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
281 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
282}
283
284static void BM_TranscodeHevc2AvcV2VMaxOperatingRate(benchmark::State& state) {
285 TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
286 "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
287 false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
288}
289
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700290static void BM_TranscodeHevc2AvcAV2AV720P(benchmark::State& state) {
291 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
292 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
293 true /* includeAudio */, true /* transcodeVideo */);
294}
295
296static void BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
297 TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
298 "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
299 true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
300}
301
Linus Nilsson6bf46532020-10-07 11:58:02 -0700302//-------------------------------- Passthrough Benchmarks ------------------------------------------
303
Linus Nilsson6233fed2020-08-13 15:15:14 -0700304static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
305 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
306 "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_passthrough_AV.mp4",
307 true /* includeAudio */, false /* transcodeVideo */);
308}
309static void BM_TranscodeVideoPassthrough(benchmark::State& state) {
310 TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
311 "video_1920x1080_3648frame_h264_22Mbps_30fps_passthrough_AV.mp4",
312 false /* includeAudio */, false /* transcodeVideo */);
313}
314
Linus Nilsson6bf46532020-10-07 11:58:02 -0700315//-------------------------------- Benchmark Registration ------------------------------------------
316
317// Benchmark registration wrapper for transcoding.
318#define TRANSCODER_BENCHMARK(func) \
319 BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
320
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700321TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700322TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
Linus Nilsson16d772b2020-09-29 19:21:11 -0700323TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
324TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700325TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720P);
326TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700327
328TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAudioVideo2AudioVideo);
329TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcVideo2Video);
330TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AVMaxOperatingRate);
331TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcV2VMaxOperatingRate);
Linus Nilssona7aa2f62020-10-29 13:19:35 -0700332TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720P);
333TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate);
Linus Nilsson6bf46532020-10-07 11:58:02 -0700334
Linus Nilsson6233fed2020-08-13 15:15:14 -0700335TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
336TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
Linus Nilsson8f0b8762020-07-23 17:12:45 -0700337
Naveen Kumar Ponnusamy8b8e7002020-12-02 12:01:32 +0530338class CustomCsvReporter : public benchmark::BenchmarkReporter {
339public:
340 CustomCsvReporter() : mPrintedHeader(false) {}
341 virtual bool ReportContext(const Context& context);
342 virtual void ReportRuns(const std::vector<Run>& reports);
343
344private:
345 void PrintRunData(const Run& report);
346
347 bool mPrintedHeader;
348 std::vector<std::string> mHeaders = {"name", "real_time", "cpu_time", PARAM_VIDEO_FRAME_RATE};
349};
350
351bool CustomCsvReporter::ReportContext(const Context& context __unused) {
352 return true;
353}
354
355void CustomCsvReporter::ReportRuns(const std::vector<Run>& reports) {
356 std::ostream& Out = GetOutputStream();
357
358 if (!mPrintedHeader) {
359 // print the header
360 for (auto header = mHeaders.begin(); header != mHeaders.end();) {
361 Out << *header++;
362 if (header != mHeaders.end()) Out << ",";
363 }
364 Out << "\n";
365 mPrintedHeader = true;
366 }
367
368 // print results for each run
369 for (const auto& run : reports) {
370 PrintRunData(run);
371 }
372}
373
374void CustomCsvReporter::PrintRunData(const Run& run) {
375 if (run.error_occurred) {
376 return;
377 }
378 std::ostream& Out = GetOutputStream();
379 Out << run.benchmark_name() << ",";
380 Out << run.GetAdjustedRealTime() << ",";
381 Out << run.GetAdjustedCPUTime() << ",";
382 auto frameRate = run.counters.find(PARAM_VIDEO_FRAME_RATE);
383 if (frameRate == run.counters.end()) {
384 Out << "NA"
385 << ",";
386 } else {
387 Out << frameRate->second << ",";
388 }
389 Out << '\n';
390}
391
392int main(int argc, char** argv) {
393 std::unique_ptr<benchmark::BenchmarkReporter> fileReporter;
394 for (int i = 1; i < argc; ++i) {
395 if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) {
396 fileReporter.reset(new CustomCsvReporter);
397 break;
398 }
399 }
400 ::benchmark::Initialize(&argc, argv);
401 if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
402 ::benchmark::RunSpecifiedBenchmarks(nullptr, fileReporter.get());
403}