/****************************************************************************** * * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ***************************************************************************** */ #include #include #include #include #include #include "aacenc_lib.h" class AACEncoder { private: HANDLE_AACENCODER mAACEncoder; AACENC_InfoStruct mEncInfo; const AUDIO_OBJECT_TYPE mProfile; const CHANNEL_MODE mChannelCount; const int mSampleRate; const int mBitRate; public: AACEncoder(int sampleRate, int bitRate, AUDIO_OBJECT_TYPE profile, CHANNEL_MODE channelCount) : mAACEncoder(nullptr), mProfile(profile), mChannelCount(channelCount), mSampleRate(sampleRate), mBitRate(bitRate) {} bool initialize() { if (aacEncOpen(&mAACEncoder, 0, 0) != AACENC_OK) { ALOGE("Failed to initialize AAC encoder"); return false; } if (aacEncoder_SetParam(mAACEncoder, AACENC_AOT, mProfile) != AACENC_OK || aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate) != AACENC_OK || aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, mChannelCount) != AACENC_OK || aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate) != AACENC_OK || aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW) != AACENC_OK) { ALOGE("Failed to set AAC encoder parameters"); return false; } if (aacEncEncode(mAACEncoder, nullptr, nullptr, nullptr, nullptr) != AACENC_OK) { ALOGE("Unable to initialize encoder for profile:%d, sample-rate: %d, bit-rate: %d, " "channels: %d", mProfile, mSampleRate, mBitRate, mChannelCount); return false; } if (aacEncInfo(mAACEncoder, &mEncInfo) != AACENC_OK) { ALOGE("Failed to get AAC encoder info"); return false; } return true; } ~AACEncoder() { if (mAACEncoder) { aacEncClose(&mAACEncoder); } } int getChannels() const { return aacEncoder_GetParam(mAACEncoder, AACENC_CHANNELMODE); } int getSampleRate() const { return aacEncoder_GetParam(mAACEncoder, AACENC_SAMPLERATE); } int getBitRate() const { return aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE); } int getProfile() const { return aacEncoder_GetParam(mAACEncoder, AACENC_AOT); } bool encode(const std::vector& pcmFrames) { size_t frameSize = mEncInfo.frameLength * mChannelCount * sizeof(uint16_t); std::vector encodedBuffer(frameSize); AACENC_BufDesc inBufDesc, outBufDesc; AACENC_InArgs inArgs; AACENC_OutArgs outArgs; void* outBuffer[] = {encodedBuffer.data()}; int outBufferIds[] = {OUT_BITSTREAM_DATA}; int outBufferSize[] = {static_cast(encodedBuffer.size())}; int outBufferElSize[] = {sizeof(UCHAR)}; outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*); outBufDesc.bufs = (void**)&outBuffer; outBufDesc.bufferIdentifiers = outBufferIds; outBufDesc.bufSizes = outBufferSize; outBufDesc.bufElSizes = outBufferElSize; size_t numFrames = pcmFrames.size() / frameSize; for (size_t frameIdx = 0; ; ++frameIdx) { const uint8_t* frameData = nullptr; void* inBuffer[1]; int inBufferSize[1]; if (frameIdx < numFrames) { frameData = pcmFrames.data() + frameIdx * frameSize; } if (frameData != nullptr) { inBuffer[0] = const_cast(frameData); inBufferSize[0] = static_cast(frameSize); inArgs.numInSamples = frameSize / sizeof(uint16_t); } else { inBuffer[0] = nullptr; inBufferSize[0] = 0; inArgs.numInSamples = -1; } int inBufferIds[] = {IN_AUDIO_DATA}; int inBufferElSize[] = {sizeof(int16_t)}; inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*); inBufDesc.bufs = (void**)&inBuffer; inBufDesc.bufferIdentifiers = inBufferIds; inBufDesc.bufSizes = inBufferSize; inBufDesc.bufElSizes = inBufferElSize; AACENC_ERROR err = aacEncEncode(mAACEncoder, &inBufDesc, &outBufDesc, &inArgs, &outArgs); if (err != AACENC_OK) { if (err == AACENC_ENCODE_EOF) { break; } ALOGE("Failed to encode AAC frame"); return false; } } return true; } }; std::vector readInputFile(const std::string& folderPath, const std::string& pcmFile) { std::string fullPcmPath = folderPath + "/" + pcmFile; std::vector inputBuffer; FILE* pcmFilePtr = fopen(fullPcmPath.c_str(), "rb"); if (!pcmFilePtr) { ALOGE("Failed to open pcm file %s", fullPcmPath.c_str()); return inputBuffer; } struct stat fileStat; int fd = fileno(pcmFilePtr); if (fstat(fd, &fileStat) == -1) { ALOGE("Error occured while accessing the pcm file"); return inputBuffer; } size_t fileSize = fileStat.st_size; inputBuffer.resize(fileSize); size_t bytesRead = fread(inputBuffer.data(), sizeof(uint8_t), inputBuffer.size(), pcmFilePtr); if (bytesRead != fileSize) { ALOGE("Failed to read the complete pcm data"); return std::vector(); } fclose(pcmFilePtr); return inputBuffer; } static void 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) { auto inputBuffer = readInputFile(inpFolderPath, pcmFile); if (inputBuffer.empty()) { state.SkipWithError("Failed to read input from pcm file"); return; } AACEncoder encoder(sampleRate, bitRate, profile, channelCount); if (!encoder.initialize()) { state.SkipWithError("Unable to initialize encoder"); return; } for (auto _ : state) { if (!encoder.encode(inputBuffer)) { state.SkipWithError("Unable to encode the Stream"); return; } } state.SetLabel(pcmFile + ", " + std::to_string(encoder.getChannels()) + ", " + std::to_string(encoder.getSampleRate()) + ", " + std::to_string(encoder.getBitRate()) + ", " + std::to_string(encoder.getProfile())); } void RegisterBenchmarks(const std::string& folderPath) { // testlabel, BM function, folderpath, pcm file, sampleRate, bitRate, profile, ChannelCount benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_8kHz_48kbps_lc", BM_EncodeAAC, folderPath, "bbb_1ch_8kHz.pcm", 8000, 48000, AOT_AAC_LC, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_lc", BM_EncodeAAC, folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_lc", BM_EncodeAAC, folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_lc", BM_EncodeAAC, folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_AAC_LC, MODE_1_2_2_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_16kHz_48kbps_he", BM_EncodeAAC, folderPath, "bbb_1ch_16kHz.pcm", 16000, 48000, AOT_SBR, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_he", BM_EncodeAAC, folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_SBR, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_he", BM_EncodeAAC, folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_SBR, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_16kHz_48kbps_hev2", BM_EncodeAAC, folderPath, "bbb_2ch_16kHz.pcm", 16000, 48000, AOT_PS, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_hev2", BM_EncodeAAC, folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_PS, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_ld", BM_EncodeAAC, folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_ld", BM_EncodeAAC, folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_ld", BM_EncodeAAC, folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_LD, MODE_1_2_2_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_16kHz_64kbps_eld", BM_EncodeAAC, folderPath, "bbb_1ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_1ch_48kHz_128kbps_eld", BM_EncodeAAC, folderPath, "bbb_1ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD, MODE_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_16kHz_64kbps_eld", BM_EncodeAAC, folderPath, "bbb_2ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_2ch_48kHz_128kbps_eld", BM_EncodeAAC, folderPath, "bbb_2ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD, MODE_2); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_16kHz_64kbps_eld", BM_EncodeAAC, folderPath, "bbb_6ch_16kHz.pcm", 16000, 64000, AOT_ER_AAC_ELD, MODE_1_2_2_1); benchmark::RegisterBenchmark("BM_EncodeAAC/bbb_6ch_48kHz_128kbps_eld", BM_EncodeAAC, folderPath, "bbb_6ch_48kHz.pcm", 48000, 128000, AOT_ER_AAC_ELD, MODE_1_2_2_1); } class CustomCsvReporter : public benchmark::BenchmarkReporter { public: CustomCsvReporter() : mPrintedHeader(false) {}; virtual bool ReportContext(const Context& context); virtual void ReportRuns(const std::vector& reports); private: void PrintRunData(const Run& report); bool mPrintedHeader; std::vector mHeaders = {"File", "Channels", "SampleRate", "BitRate", "profile", "real_time(ns)", "cpu_time(ns)"}; }; bool CustomCsvReporter::ReportContext(const Context& context /* __unused */) { return true; } void CustomCsvReporter::ReportRuns(const std::vector& reports) { std::ostream& Out = GetOutputStream(); if (!mPrintedHeader) { // print the header for (auto header = mHeaders.begin(); header != mHeaders.end();) { Out << *header++; if (header != mHeaders.end()) Out << ","; } Out << "\n"; mPrintedHeader = true; } // print results for each run for (const auto& run : reports) { PrintRunData(run); } } void CustomCsvReporter::PrintRunData(const Run& run) { if (run.skipped) { return; } std::ostream& Out = GetOutputStream(); Out << run.report_label << ","; Out << run.GetAdjustedRealTime() << ","; Out << run.GetAdjustedCPUTime() << ","; Out << '\n'; } int main(int argc, char** argv) { std::unique_ptr csvReporter; std::string pathArg, inpFolderPath; for (int i = 1; i < argc; ++i) { // pass --path=/path/to/resourcefolder in command line while running without atest // to specify where resources are present if (std::string(argv[i]).find("--path") != std::string ::npos) { pathArg = argv[i]; auto separator = pathArg.find('='); if (separator != std::string::npos) { inpFolderPath = pathArg.substr(separator + 1); } } // pass --benchmark_out=/path/to/.csv in command line to generate csv report if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) { csvReporter.reset(new CustomCsvReporter); break; } } if (inpFolderPath.empty()) { inpFolderPath = "/sdcard/test/AacEncBenchmark-1.0"; } FILE* pcmFilePath = fopen(inpFolderPath.c_str(), "r"); if (!pcmFilePath) { std::cerr << "Error: Invalid path provided: " << inpFolderPath << std::endl; return -1; } fclose(pcmFilePath); RegisterBenchmarks(inpFolderPath); benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(nullptr, csvReporter.get()); benchmark::Shutdown(); return 0; }