1 /******************************************************************************
2 *
3 * Copyright (C) 2024 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************
18 */
19
20 #include <benchmark/benchmark.h>
21 #include <log/log.h>
22
23 #include <iostream>
24 #include <sys/stat.h>
25 #include <vector>
26
27 #include "aacenc_lib.h"
28
29 class AACEncoder {
30 private:
31 HANDLE_AACENCODER mAACEncoder;
32 AACENC_InfoStruct mEncInfo;
33 const AUDIO_OBJECT_TYPE mProfile;
34 const CHANNEL_MODE mChannelCount;
35 const int mSampleRate;
36 const int mBitRate;
37
38 public:
AACEncoder(int sampleRate,int bitRate,AUDIO_OBJECT_TYPE profile,CHANNEL_MODE channelCount)39 AACEncoder(int sampleRate, int bitRate, AUDIO_OBJECT_TYPE profile, CHANNEL_MODE channelCount)
40 : mAACEncoder(nullptr), mProfile(profile), mChannelCount(channelCount),
41 mSampleRate(sampleRate), mBitRate(bitRate) {}
42
initialize()43 bool initialize() {
44 if (aacEncOpen(&mAACEncoder, 0, 0) != AACENC_OK) {
45 ALOGE("Failed to initialize AAC encoder");
46 return false;
47 }
48
49 if (aacEncoder_SetParam(mAACEncoder, AACENC_AOT, mProfile) != AACENC_OK
50 || aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate) != AACENC_OK
51 || aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, mChannelCount) != AACENC_OK
52 || aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate) != AACENC_OK
53 || aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW) != AACENC_OK) {
54 ALOGE("Failed to set AAC encoder parameters");
55 return false;
56 }
57
58 if (aacEncEncode(mAACEncoder, nullptr, nullptr, nullptr, nullptr) != AACENC_OK) {
59 ALOGE("Unable to initialize encoder for profile:%d, sample-rate: %d, bit-rate: %d, "
60 "channels: %d", mProfile, mSampleRate, mBitRate, mChannelCount);
61 return false;
62 }
63
64 if (aacEncInfo(mAACEncoder, &mEncInfo) != AACENC_OK) {
65 ALOGE("Failed to get AAC encoder info");
66 return false;
67 }
68 return true;
69 }
70
~AACEncoder()71 ~AACEncoder() {
72 if (mAACEncoder) {
73 aacEncClose(&mAACEncoder);
74 }
75 }
76
getChannels() const77 int getChannels() const { return aacEncoder_GetParam(mAACEncoder, AACENC_CHANNELMODE); }
getSampleRate() const78 int getSampleRate() const { return aacEncoder_GetParam(mAACEncoder, AACENC_SAMPLERATE); }
getBitRate() const79 int getBitRate() const { return aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE); }
getProfile() const80 int getProfile() const { return aacEncoder_GetParam(mAACEncoder, AACENC_AOT); }
81
encode(const std::vector<uint8_t> & pcmFrames)82 bool encode(const std::vector<uint8_t>& pcmFrames) {
83 size_t frameSize = mEncInfo.frameLength * mChannelCount * sizeof(uint16_t);
84 std::vector<uint8_t> encodedBuffer(frameSize);
85 AACENC_BufDesc inBufDesc, outBufDesc;
86 AACENC_InArgs inArgs;
87 AACENC_OutArgs outArgs;
88
89 void* outBuffer[] = {encodedBuffer.data()};
90 int outBufferIds[] = {OUT_BITSTREAM_DATA};
91 int outBufferSize[] = {static_cast<int>(encodedBuffer.size())};
92 int outBufferElSize[] = {sizeof(UCHAR)};
93
94 outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*);
95 outBufDesc.bufs = (void**)&outBuffer;
96 outBufDesc.bufferIdentifiers = outBufferIds;
97 outBufDesc.bufSizes = outBufferSize;
98 outBufDesc.bufElSizes = outBufferElSize;
99
100 size_t numFrames = pcmFrames.size() / frameSize;
101
102 for (size_t frameIdx = 0; ; ++frameIdx) {
103
104 const uint8_t* frameData = nullptr;
105 void* inBuffer[1];
106 int inBufferSize[1];
107 if (frameIdx < numFrames) {
108 frameData = pcmFrames.data() + frameIdx * frameSize;
109 }
110
111 if (frameData != nullptr) {
112 inBuffer[0] = const_cast<uint8_t*>(frameData);
113 inBufferSize[0] = static_cast<int>(frameSize);
114 inArgs.numInSamples = frameSize / sizeof(uint16_t);
115 } else {
116 inBuffer[0] = nullptr;
117 inBufferSize[0] = 0;
118 inArgs.numInSamples = -1;
119 }
120
121 int inBufferIds[] = {IN_AUDIO_DATA};
122 int inBufferElSize[] = {sizeof(int16_t)};
123
124 inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*);
125 inBufDesc.bufs = (void**)&inBuffer;
126 inBufDesc.bufferIdentifiers = inBufferIds;
127 inBufDesc.bufSizes = inBufferSize;
128 inBufDesc.bufElSizes = inBufferElSize;
129 AACENC_ERROR err =
130 aacEncEncode(mAACEncoder, &inBufDesc, &outBufDesc, &inArgs, &outArgs);
131 if (err != AACENC_OK) {
132 if (err == AACENC_ENCODE_EOF) {
133 break;
134 }
135 ALOGE("Failed to encode AAC frame");
136 return false;
137 }
138 }
139 return true;
140 }
141 };
142
readInputFile(const std::string & folderPath,const std::string & pcmFile)143 std::vector<uint8_t> readInputFile(const std::string& folderPath, const std::string& pcmFile) {
144 std::string fullPcmPath = folderPath + "/" + pcmFile;
145 std::vector<uint8_t> inputBuffer;
146
147 FILE* pcmFilePtr = fopen(fullPcmPath.c_str(), "rb");
148 if (!pcmFilePtr) {
149 ALOGE("Failed to open pcm file %s", fullPcmPath.c_str());
150 return inputBuffer;
151 }
152
153 struct stat fileStat;
154 int fd = fileno(pcmFilePtr);
155
156 if (fstat(fd, &fileStat) == -1) {
157 ALOGE("Error occured while accessing the pcm file");
158 return inputBuffer;
159 }
160 size_t fileSize = fileStat.st_size;
161 inputBuffer.resize(fileSize);
162 size_t bytesRead = fread(inputBuffer.data(), sizeof(uint8_t), inputBuffer.size(), pcmFilePtr);
163 if (bytesRead != fileSize) {
164 ALOGE("Failed to read the complete pcm data");
165 return std::vector<uint8_t>();
166 }
167
168 fclose(pcmFilePtr);
169 return inputBuffer;
170 }
171
BM_EncodeAAC(benchmark::State & state,const std::string & inpFolderPath,const std::string & pcmFile,const int sampleRate,const int bitRate,const AUDIO_OBJECT_TYPE profile,const CHANNEL_MODE channelCount)172 static void BM_EncodeAAC(benchmark::State& state, const std::string& inpFolderPath,
173 const std::string& pcmFile, const int sampleRate, const int bitRate,
174 const AUDIO_OBJECT_TYPE profile, const CHANNEL_MODE channelCount) {
175 auto inputBuffer = readInputFile(inpFolderPath, pcmFile);
176 if (inputBuffer.empty()) {
177 state.SkipWithError("Failed to read input from pcm file");
178 return;
179 }
180 AACEncoder encoder(sampleRate, bitRate, profile, channelCount);
181
182 if (!encoder.initialize()) {
183 state.SkipWithError("Unable to initialize encoder");
184 return;
185 }
186
187 for (auto _ : state) {
188 if (!encoder.encode(inputBuffer)) {
189 state.SkipWithError("Unable to encode the Stream");
190 return;
191 }
192 }
193
194 state.SetLabel(pcmFile + ", " + std::to_string(encoder.getChannels()) + ", "
195 + std::to_string(encoder.getSampleRate()) + ", "
196 + std::to_string(encoder.getBitRate()) + ", "
197 + std::to_string(encoder.getProfile()));
198 }
199
RegisterBenchmarks(const std::string & folderPath)200 void RegisterBenchmarks(const std::string& folderPath) {
201 // testlabel, BM function, folderpath, pcm file, sampleRate, bitRate, profile, ChannelCount
202 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_8kHz_48kbps_lc", BM_EncodeAAC,
203 folderPath, "bbb_1ch_8kHz.pcm", 8000, 48000, AOT_AAC_LC,
204 MODE_1);
205 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_lc", BM_EncodeAAC,
206 folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC,
207 MODE_1);
208 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_lc", BM_EncodeAAC,
209 folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC,
210 MODE_2);
211 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_lc", BM_EncodeAAC,
212 folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC,
213 MODE_1_2_2_1);
214 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_16kHz_48kbps_he", BM_EncodeAAC,
215 folderPath, "bbb_1ch_16kHz.pcm", 16000, 48000, AOT_SBR,
216 MODE_1);
217 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_he", BM_EncodeAAC,
218 folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_SBR,
219 MODE_1);
220 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_he", BM_EncodeAAC,
221 folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_SBR,
222 MODE_2);
223 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_16kHz_48kbps_hev2", BM_EncodeAAC,
224 folderPath, "bbb_2ch_16kHz.pcm", 16000, 48000, AOT_PS,
225 MODE_2);
226 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_hev2", BM_EncodeAAC,
227 folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_PS,
228 MODE_2);
229 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_ld", BM_EncodeAAC,
230 folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD,
231 MODE_1);
232 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_ld", BM_EncodeAAC,
233 folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD,
234 MODE_2);
235 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_ld", BM_EncodeAAC,
236 folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD,
237 MODE_1_2_2_1);
238 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_16kHz_64kbps_eld", BM_EncodeAAC,
239 folderPath, "bbb_1ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD,
240 MODE_1);
241 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_eld", BM_EncodeAAC,
242 folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD,
243 MODE_1);
244 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_16kHz_64kbps_eld", BM_EncodeAAC,
245 folderPath, "bbb_2ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD,
246 MODE_2);
247 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_eld", BM_EncodeAAC,
248 folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD,
249 MODE_2);
250 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_16kHz_64kbps_eld", BM_EncodeAAC,
251 folderPath, "bbb_6ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD,
252 MODE_1_2_2_1);
253 benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_eld", BM_EncodeAAC,
254 folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD,
255 MODE_1_2_2_1);
256 }
257
258 class CustomCsvReporter : public benchmark::BenchmarkReporter {
259 public:
CustomCsvReporter()260 CustomCsvReporter() : mPrintedHeader(false) {};
261 virtual bool ReportContext(const Context& context);
262 virtual void ReportRuns(const std::vector<Run>& reports);
263
264 private:
265 void PrintRunData(const Run& report);
266 bool mPrintedHeader;
267 std::vector<std::string> mHeaders = {"File", "Channels", "SampleRate", "BitRate",
268 "profile", "real_time(ns)", "cpu_time(ns)"};
269 };
270
ReportContext(const Context & context)271 bool CustomCsvReporter::ReportContext(const Context& context /* __unused */) { return true; }
272
ReportRuns(const std::vector<Run> & reports)273 void CustomCsvReporter::ReportRuns(const std::vector<Run>& reports) {
274 std::ostream& Out = GetOutputStream();
275
276 if (!mPrintedHeader) {
277 // print the header
278 for (auto header = mHeaders.begin(); header != mHeaders.end();) {
279 Out << *header++;
280 if (header != mHeaders.end()) Out << ",";
281 }
282 Out << "\n";
283 mPrintedHeader = true;
284 }
285
286 // print results for each run
287 for (const auto& run : reports) {
288 PrintRunData(run);
289 }
290 }
291
PrintRunData(const Run & run)292 void CustomCsvReporter::PrintRunData(const Run& run) {
293 if (run.skipped) {
294 return;
295 }
296 std::ostream& Out = GetOutputStream();
297 Out << run.report_label << ",";
298 Out << run.GetAdjustedRealTime() << ",";
299 Out << run.GetAdjustedCPUTime() << ",";
300 Out << '\n';
301 }
302
main(int argc,char ** argv)303 int main(int argc, char** argv) {
304 std::unique_ptr<benchmark::BenchmarkReporter> csvReporter;
305 std::string pathArg, inpFolderPath;
306
307 for (int i = 1; i < argc; ++i) {
308 // pass --path=/path/to/resourcefolder in command line while running without atest
309 // to specify where resources are present
310 if (std::string(argv[i]).find("--path") != std::string ::npos) {
311 pathArg = argv[i];
312 auto separator = pathArg.find('=');
313 if (separator != std::string::npos) {
314 inpFolderPath = pathArg.substr(separator + 1);
315 }
316 }
317 // pass --benchmark_out=/path/to/.csv in command line to generate csv report
318 if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) {
319 csvReporter.reset(new CustomCsvReporter);
320 break;
321 }
322 }
323
324 if (inpFolderPath.empty()) {
325 inpFolderPath = "/sdcard/test/AacEncBenchmark-1.0";
326 }
327
328 FILE* pcmFilePath = fopen(inpFolderPath.c_str(), "r");
329 if (!pcmFilePath) {
330 std::cerr << "Error: Invalid path provided: " << inpFolderPath << std::endl;
331 return -1;
332 }
333 fclose(pcmFilePath);
334
335 RegisterBenchmarks(inpFolderPath);
336 benchmark::Initialize(&argc, argv);
337 benchmark::RunSpecifiedBenchmarks(nullptr, csvReporter.get());
338 benchmark::Shutdown();
339 return 0;
340 }
341