1
0
mirror of https://github.com/mstorsjo/fdk-aac.git synced 2025-06-05 22:39:13 +02:00

Merge remote-tracking branch 'aosp/main'

This commit is contained in:
Martin Storsjo
2025-03-01 00:02:50 +02:00
13 changed files with 1098 additions and 5 deletions

View File

@@ -94,7 +94,7 @@ cc_library_static {
apex_available: [
"//apex_available:platform",
"com.android.btservices",
"com.android.bt",
"com.android.media.swcodec",
],
min_sdk_version: "29",

2
OWNERS
View File

@@ -1,2 +1,2 @@
jmtrivi@google.com
gkasten@android.com
include platform/system/core:/janitors/OWNERS #{LAST_RESORT_SUGGESTION}

View File

@@ -118,7 +118,11 @@ amm-info@iis.fraunhofer.de
\sa lppTransposer(), main_audio.cpp, sbr_scale.h, \ref documentationOverview
*/
#ifdef __ANDROID__
#if __has_include(<android/ndk-version.h>)
#include <android/ndk-version.h>
#endif
#if defined __ANDROID__ && !defined __ANDROID_NDK__
#include "log/log.h"
#endif
@@ -334,7 +338,7 @@ void lppTransposer(
}
}
}
#ifdef __ANDROID__
#if defined __ANDROID__ && !defined __ANDROID_NDK__
else {
// Safetynet logging
android_errorWriteLog(0x534e4554, "112160868");
@@ -930,7 +934,7 @@ void lppTransposerHBE(
FDKmemclear(&qmfBufferImag[i][targetStopBand], memSize);
}
}
#ifdef __ANDROID__
#if defined __ANDROID__ && !defined __ANDROID_NDK__
else {
// Safetynet logging
android_errorWriteLog(0x534e4554, "112160868");

View File

@@ -0,0 +1,320 @@
/******************************************************************************
*
* 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 <benchmark/benchmark.h>
#include <log/log.h>
#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "aacdecoder_lib.h"
class AACDecoder {
private:
HANDLE_AACDECODER mAACDecoder;
CStreamInfo* mStreamInfo;
public:
AACDecoder() : mAACDecoder(nullptr), mStreamInfo(nullptr) {}
bool initialize() {
mAACDecoder = aacDecoder_Open(TT_MP4_RAW, 1);
if (!mAACDecoder) {
ALOGE("Failed to initialize AAC decoder");
return false;
}
mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder);
if (!mStreamInfo) {
ALOGE("Failed to get stream info after initialization");
return false;
}
return true;
}
~AACDecoder() {
if (mAACDecoder) {
aacDecoder_Close(mAACDecoder);
}
}
int getChannels() const { return mStreamInfo ? mStreamInfo->numChannels : 0; }
int getFrameSize() const { return mStreamInfo ? mStreamInfo->frameSize : 0; }
int getSampleRate() const { return mStreamInfo ? mStreamInfo->sampleRate : 0; }
bool decode(const std::vector<std::pair<std::vector<uint8_t>, int>>& inputBuffers) {
for (const auto& [buffer, flag] : inputBuffers) {
std::vector<INT_PCM> frameOutput;
if (flag == 2) {
if (!configureDecoder(buffer)) {
return false;
}
} else {
if (!decodeFrame(buffer, frameOutput)) {
return false;
}
}
}
return true;
}
private:
bool configureDecoder(const std::vector<uint8_t>& configBuffer) {
UINT bytesRead = configBuffer.size();
UCHAR* configData = const_cast<UCHAR*>(configBuffer.data());
UCHAR* configArray[1] = {configData};
AAC_DECODER_ERROR err = aacDecoder_ConfigRaw(mAACDecoder, configArray, &bytesRead);
if (err != AAC_DEC_OK) {
ALOGE("Failed to configure decoder: error %d", err);
return false;
}
return true;
}
bool decodeFrame(const std::vector<uint8_t>& inputBuffer, std::vector<INT_PCM>& outputBuffer) {
constexpr size_t kOutputBufferSize = 10240;
UINT bytesRead = inputBuffer.size();
UINT validBytes = bytesRead;
UCHAR* inputPtr = const_cast<UCHAR*>(inputBuffer.data());
UCHAR* bufferArray[1] = {inputPtr};
AAC_DECODER_ERROR err = aacDecoder_Fill(mAACDecoder, bufferArray, &bytesRead, &validBytes);
if (err != AAC_DEC_OK) {
ALOGE("Failed to fill decoder buffer: error %d", err);
return false;
}
outputBuffer.resize(kOutputBufferSize); // Ensure buffer is large enough
err = aacDecoder_DecodeFrame(mAACDecoder, outputBuffer.data(), outputBuffer.size(), 0);
if (err != AAC_DEC_OK) {
ALOGE("Failed to decode frame: error %d", err);
return false;
}
outputBuffer.resize(mStreamInfo->numChannels * mStreamInfo->frameSize);
return true;
}
};
std::vector<std::pair<std::vector<uint8_t>, int>> readInputFiles(const std::string& folderPath,
const std::string& bitstreamFile,
const std::string& infoFile) {
std::string fullBitstreamPath = folderPath + "/" + bitstreamFile;
std::string fullInfoPath = folderPath + "/" + infoFile;
std::vector<std::pair<std::vector<uint8_t>, int>> inputBuffers;
FILE* bitStreamFilePtr = fopen(fullBitstreamPath.c_str(), "rb");
if (!bitStreamFilePtr) {
ALOGE("Failed to open bitstream file %s", fullBitstreamPath.c_str());
return inputBuffers;
}
FILE* infoFilePtr = fopen(fullInfoPath.c_str(), "r");
if (!infoFilePtr) {
ALOGE("Failed to open info file %s", fullInfoPath.c_str());
return inputBuffers;
}
int bufferSize, flag;
long pts;
while (fscanf(infoFilePtr, "%d %d %ld", &bufferSize, &flag, &pts) == 3) {
std::vector<uint8_t> buffer(bufferSize);
size_t bytesRead = fread(buffer.data(), 1, bufferSize, bitStreamFilePtr);
if (bytesRead != bufferSize) {
ALOGE("Failed to read input data");
return std::vector<std::pair<std::vector<uint8_t>, int>>();
}
inputBuffers.emplace_back(std::move(buffer), flag);
}
fclose(bitStreamFilePtr);
fclose(infoFilePtr);
return inputBuffers;
}
static void BM_DecodeAAC(benchmark::State& state, const std::string& inpFolderPath,
const std::string& bitstreamFile, const std::string& infoFile) {
auto inputBuffers = readInputFiles(inpFolderPath, bitstreamFile, infoFile);
if(inputBuffers.empty()) {
state.SkipWithError("Failed to read input data completely");
}
AACDecoder decoder;
if (!decoder.initialize()) {
state.SkipWithError("Unable to initialize decoder");
}
for (auto _ : state) {
if(!decoder.decode(inputBuffers)) {
state.SkipWithError("Unable to decode the Stream");
}
}
state.SetLabel(bitstreamFile + ", " + std::to_string(decoder.getChannels()) + ", "
+ std::to_string(decoder.getSampleRate()) + ", "
+ std::to_string(decoder.getFrameSize()));
}
// Function to register benchmarks
void RegisterBenchmarks(const std::string& folderPath) {
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_8kHz_64kbps_lc", BM_DecodeAAC,
folderPath, "bbb_1ch_8kHz_64kbps_lc.bin",
"bbb_1ch_8kHz_64kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_48kHz_128kbps_lc", BM_DecodeAAC,
folderPath, "bbb_1ch_48kHz_128kbps_lc.bin",
"bbb_1ch_48kHz_128kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_8kHz_64kbps_lc", BM_DecodeAAC,
folderPath, "bbb_2ch_8kHz_64kbps_lc.bin",
"bbb_2ch_8kHz_64kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_48kHz_128kbps_lc", BM_DecodeAAC,
folderPath, "bbb_2ch_48kHz_128kbps_lc.bin",
"bbb_2ch_48kHz_128kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_6ch_8kHz_64kbps_lc", BM_DecodeAAC,
folderPath, "bbb_6ch_8kHz_64kbps_lc.bin",
"bbb_6ch_8kHz_64kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_6ch_48kHz_128kbps_lc", BM_DecodeAAC,
folderPath, "bbb_6ch_48kHz_128kbps_lc.bin",
"bbb_6ch_48kHz_128kbps_lc.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_16kHz_64kbps_he", BM_DecodeAAC,
folderPath, "bbb_1ch_16kHz_64kbps_he.bin",
"bbb_1ch_16kHz_64kbps_he.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_48kHz_128kbps_he", BM_DecodeAAC,
folderPath, "bbb_1ch_48kHz_128kbps_he.bin",
"bbb_1ch_48kHz_128kbps_he.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_16kHz_64kbps_he", BM_DecodeAAC,
folderPath, "bbb_2ch_16kHz_64kbps_he.bin",
"bbb_2ch_16kHz_64kbps_he.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_48kHz_128kbps_he", BM_DecodeAAC,
folderPath, "bbb_2ch_48kHz_128kbps_he.bin",
"bbb_2ch_48kHz_128kbps_he.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_16kHz_64kbps_hev2", BM_DecodeAAC,
folderPath, "bbb_2ch_16kHz_64kbps_hev2.bin",
"bbb_2ch_16kHz_64kbps_hev2.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_48kHz_128kbps_hev2", BM_DecodeAAC,
folderPath, "bbb_2ch_48kHz_128kbps_hev2.bin",
"bbb_2ch_48kHz_128kbps_hev2.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_48kHz_128kbps_ld", BM_DecodeAAC,
folderPath, "bbb_1ch_48kHz_128kbps_ld.bin",
"bbb_1ch_48kHz_128kbps_ld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_48kHz_128kbps_ld", BM_DecodeAAC,
folderPath, "bbb_2ch_48kHz_128kbps_ld.bin",
"bbb_2ch_48kHz_128kbps_ld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_6ch_48kHz_128kbps_ld", BM_DecodeAAC,
folderPath, "bbb_6ch_48kHz_128kbps_ld.bin",
"bbb_6ch_48kHz_128kbps_ld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_16kHz_64kbps_eld", BM_DecodeAAC,
folderPath, "bbb_1ch_16kHz_64kbps_eld.bin",
"bbb_1ch_16kHz_64kbps_eld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_1ch_48kHz_128kbps_eld", BM_DecodeAAC,
folderPath, "bbb_1ch_48kHz_128kbps_eld.bin",
"bbb_1ch_48kHz_128kbps_eld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_16kHz_64kbps_eld", BM_DecodeAAC,
folderPath, "bbb_2ch_16kHz_64kbps_eld.bin",
"bbb_2ch_16kHz_64kbps_eld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_2ch_48kHz_128kbps_eld", BM_DecodeAAC,
folderPath, "bbb_2ch_48kHz_128kbps_eld.bin",
"bbb_2ch_48kHz_128kbps_eld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_6ch_16kHz_64kbps_eld", BM_DecodeAAC,
folderPath, "bbb_6ch_16kHz_64kbps_eld.bin",
"bbb_6ch_16kHz_64kbps_eld.info");
benchmark::RegisterBenchmark("BM_DecodeAAC/bbb_6ch_48kHz_128kbps_eld", BM_DecodeAAC,
folderPath, "bbb_6ch_48kHz_128kbps_eld.bin",
"bbb_6ch_48kHz_128kbps_eld.info");
}
class CustomCsvReporter : public benchmark::BenchmarkReporter {
public:
CustomCsvReporter() : mPrintedHeader(false) {}
virtual bool ReportContext(const Context& context);
virtual void ReportRuns(const std::vector<Run>& reports);
private:
void PrintRunData(const Run& report);
bool mPrintedHeader;
std::vector<std::string> mHeaders = {"File", "Channels", "SampleRate",
"FrameSize", "real_time(ns)", "cpu_time(ns)"};
};
bool CustomCsvReporter::ReportContext(const Context& context /* __unused */) { return true; }
void CustomCsvReporter::ReportRuns(const std::vector<Run>& 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<benchmark::BenchmarkReporter> 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/AacDecBenchmark-1.0";
}
RegisterBenchmarks(inpFolderPath);
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks(nullptr, csvReporter.get());
benchmark::Shutdown();
return 0;
}

View File

@@ -0,0 +1,33 @@
// 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.
package {
// See: http://go/android-license-faq
default_team: "trendy_team_android_media_codec_framework",
default_applicable_licenses: ["external_aac_license"],
}
cc_benchmark {
name: "AacDecBenchmark",
host_supported: true,
srcs: ["AacDecBenchmark.cpp"],
shared_libs: [
"liblog",
],
static_libs: [
"libFraunhoferAAC",
"libgoogle-benchmark",
],
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<configuration description="Unit test configuration for AacDecBenchmark">
<option name="test-suite-tag" value="device-tests" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push-file" key="AacDecBenchmark" value="/data/local/tmp/AacDecBenchmark" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="host" />
<option name="config-filename" value="AacDecBenchmark" />
<option name="version" value="1.0"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="device" />
<option name="config-filename" value="AacDecBenchmark" />
<option name="version" value="1.0"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="AacDecBenchmark-1.0" />
<option name="dynamic-config-module" value="AacDecBenchmark" />
</target_preparer>
<test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
<option name="native-benchmark-device-path" value="/data/local/tmp" />
<option name="benchmark-module-name" value="AacDecBenchmark" />
</test>
</configuration>

View File

@@ -0,0 +1,20 @@
<!-- 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.
-->
<dynamicConfig>
<entry key="media_files_url">
<value>https://dl.google.com/android-unittest/media/external/aac/tests/AacDecBenchmark/AacDecBenchmark-1.0.zip</value>
</entry>
</dynamicConfig>

View File

@@ -0,0 +1,120 @@
# Benchmark tests
This Benchmark app analyses the time taken by AAC Decoder for given set of inputs. It is used to benchmark decoder module on android devices.
This page describes steps to run the AAC decoder Benchmark test.
Run the following steps to build the test suite:
```
mmm external/aac/tests/AacDecBenchmark/
```
# Resources
The resource folder for the tests is taken from [here](https://dl.google.com/android-unittest/media/external/aac/tests/AacDecBenchmark/AacDecBenchmark-1.0.zip)
Download the AacDecBenchmark-1.0.zip folder, unzip and push it to any path on the device, Let's say the path be /sdcard/test. You can give the path wherever you chose to put the files.
```
unzip AacDecBenchmark-1.0.zip
adb push AacDecBenchmark-1.0 /sdcard/test
```
# <a name="BenchmarkApplication"></a> Benchmark Application
To run the test suite for measuring performance, follow the following steps:
Benchmark Application can be run in two ways.
## Steps to run with atest
Note that atest command will install Benchmark application and push the required test files to the device as well.
For running the benchmark test, run the following command
```
atest AacDecBenchmark
```
## Steps to run without atest (push binary to the device and run)
To run the test suite for measuring performance of the decoder, follow the following steps:
The 64-bit binaries will be created in the following path : ${OUT}/data/benchmarktest64/
The 32-bit binaries will be created in the following path : ${OUT}/data/benchmarktest/
To test 64-bit binary push binaries from benchmarktest64.
```
adb push $(OUT)/data/benchmarktest64/AacDecBenchmark/AacDecBenchmark /data/local/tmp/
```
To test 32-bit binary push binaries from benchmarktest.
```
adb push $(OUT)/data/benchmarktest/AacDecBenchmark/AacDecBenchmark /data/local/tmp/
```
To get the resource files for the test follow instructions given in [Resources](#Resources)
After running the above steps, /sdcard/test should contain AacDecBenchmark-1.0 folder and /data/local/tmp should contain benchmark binary to be executed.
Run the following commands to see the benchmark results
```
adb shell
cd /data/local/tmp/
chmod a+x AacDecBenchmark
./AacDecBenchmark
```
Run the below commands to generate a csv report and see the benchmark results
```
adb shell
cd /data/local/tmp/
chmod a+x AacDecBenchmark
./AacDecBenchmark --benchmark_out=output.csv
```
if the folder path where the resource files are pushed is different from /sdcard/test/ , pass the actual folder path as an argument as shown below and run the following commands to see the benchmark results. Here let's say the path be /sdcard/test/AacDecBenchmark-1.0
```
adb shell
cd /data/local/tmp/
chmod a+x AacDecBenchmark
./AacDecBenchmark --path=/sdcard/test/AacDecBenchmark-1.0
```
Run the below commands to store the benchmark results in an output.csv file which will be generated in the same path on the device.
You can modify the output csv filename to any name and can be generated in any given absolute path.
```
adb shell
cd /data/local/tmp/
chmod a+x AacDecBenchmark
./AacDecBenchmark --path=/sdcard/test/AacDecBenchmark-1.0 --benchmark_out=output.csv
```
# Analysis
The benchmark results are stored in a CSV file if opted, which can be used for analysis.
Note: This timestamp is in nano seconds and will change based on current system time.
This csv file can be pulled from the device using "adb pull" command.
```
adb pull /data/local/tmp/output.csv ./output.csv
```
## CSV Columns
Following columns are available in CSV.
Note: All time values are in nano seconds
1. **fileName**: The file being used as an input for the benchmark test.
2. **Channels**: Number of channels does the input audio bitstream contain.
3. **SampleRate**: SampleRate of the input audio bitstream.
4. **FrameSize**: FrameSize of the input audio bitstream.
5. **real_time**: Measures total elapsed time from start to end of process, including wait times and delays.
6. **cpu_time**: Measures total time spent by cpu actively executing instructions for a process.

View File

@@ -0,0 +1,340 @@
/******************************************************************************
*
* 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 <benchmark/benchmark.h>
#include <log/log.h>
#include <iostream>
#include <sys/stat.h>
#include <vector>
#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<uint8_t>& pcmFrames) {
size_t frameSize = mEncInfo.frameLength * mChannelCount * sizeof(uint16_t);
std::vector<uint8_t> 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<int>(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<uint8_t*>(frameData);
inBufferSize[0] = static_cast<int>(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<uint8_t> readInputFile(const std::string& folderPath, const std::string& pcmFile) {
std::string fullPcmPath = folderPath + "/" + pcmFile;
std::vector<uint8_t> 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<uint8_t>();
}
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<Run>& reports);
private:
void PrintRunData(const Run& report);
bool mPrintedHeader;
std::vector<std::string> 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<Run>& 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<benchmark::BenchmarkReporter> 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;
}

View File

@@ -0,0 +1,33 @@
// 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.
package {
// See: http://go/android-license-faq
default_team: "trendy_team_android_media_codec_framework",
default_applicable_licenses: ["external_aac_license"],
}
cc_benchmark {
name: "AacEncBenchmark",
host_supported: true,
srcs: ["AacEncBenchmark.cpp"],
shared_libs: [
"liblog",
],
static_libs: [
"libFraunhoferAAC",
"libgoogle-benchmark",
],
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<configuration description="Unit test configuration for AacEncBenchmark">
<option name="test-suite-tag" value="device-tests" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push-file" key="AacEncBenchmark" value="/data/local/tmp/AacEncBenchmark" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="host" />
<option name="config-filename" value="AacEncBenchmark" />
<option name="version" value="1.0"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="device" />
<option name="config-filename" value="AacEncBenchmark" />
<option name="version" value="1.0"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="AacEncBenchmark-1.0" />
<option name="dynamic-config-module" value="AacEncBenchmark" />
</target_preparer>
<test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
<option name="native-benchmark-device-path" value="/data/local/tmp" />
<option name="benchmark-module-name" value="AacEncBenchmark" />
</test>
</configuration>

View File

@@ -0,0 +1,20 @@
<!-- 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.
-->
<dynamicConfig>
<entry key="media_files_url">
<value>https://dl.google.com/android-unittest/media/external/aac/tests/AacEncBenchmark/AacEncBenchmark-1.0.zip</value>
</entry>
</dynamicConfig>

View File

@@ -0,0 +1,121 @@
# Benchmark tests
This Benchmark app analyses the time taken by AAC Encoder for given set of inputs. It is used to benchmark encoder module on android devices.
This page describes steps to run the AAC encoder Benchmark test.
Run the following steps to build the test suite:
```
mmm external/aac/tests/AacEncBenchmark/
```
# Resources
The resource folder for the tests is taken from [here](https://dl.google.com/android-unittest/media/external/aac/tests/AacEncBenchmark/AacEncBenchmark-1.0.zip)
Download the AacEncBenchmark-1.0.zip folder, unzip and push it to any path on the device, Let's say the path be /sdcard/test. You can give the path wherever you chose to put the files.
```
unzip AacEncBenchmark-1.0.zip
adb push AacEncBenchmark-1.0 /sdcard/test
```
# <a name="BenchmarkApplication"></a> Benchmark Application
To run the test suite for measuring performance, follow the following steps:
Benchmark Application can be run in two ways.
## Steps to run with atest
Note that atest command will install Benchmark application and push the required test files to the device as well.
For running the benchmark test, run the following command
```
atest AacEncBenchmark
```
## Steps to run without atest (push binary to the device and run)
To run the test suite for measuring performance of the encoder, follow the following steps:
The 64-bit binaries will be created in the following path : ${OUT}/data/benchmarktest64/AacEncBenchmark/
The 32-bit binaries will be created in the following path : ${OUT}/data/benchmarktest/AacEncBenchmark/
To test 64-bit binary push binaries from benchmarktest64.
```
adb push $(OUT)/data/benchmarktest64/AacEncBenchmark/AacEncBenchmark /data/local/tmp/
```
To test 32-bit binary push binaries from benchmarktest.
```
adb push $(OUT)/data/benchmarktest/AacEncBenchmark/AacEncBenchmark /data/local/tmp/
```
To get the resource files for the test follow instructions given in [Resources](#Resources)
After running the above steps, /sdcard/test should contain AacEncBenchmark-1.0 folder and /data/local/tmp should contain benchmark binary to be executed.
Run the following commands to see the benchmark results
```
adb shell
cd /data/local/tmp/
chmod a+x AacEncBenchmark
./AacEncBenchmark
```
Run the below commands to generate a csv report and see the benchmark results
```
adb shell
cd /data/local/tmp/
chmod a+x AacEncBenchmark
./AacEncBenchmark --benchmark_out=output.csv
```
if the folder path where the resource files are pushed is different from /sdcard/test/ , pass the actual folder path as an argument as shown below and run the following commands to see the benchmark results. Here let's say the path be /sdcard/test/AacEncBenchmark-1.0
```
adb shell
cd /data/local/tmp/
chmod a+x AacEncBenchmark
./AacEncBenchmark --path=/sdcard/test/AacEncBenchmark-1.0
```
Run the below commands to store the benchmark results in an output.csv file which will be generated in the same path on the device.
You can modify the output csv filename to any name and can be generated in any given absolute path.
```
adb shell
cd /data/local/tmp/
chmod a+x AacEncBenchmark
./AacEncBenchmark --path=/sdcard/test/AacEncBenchmark-1.0/ --benchmark_out=output.csv
```
# Analysis
The benchmark results are stored in a CSV file if opted, which can be used for analysis.
Note: This timestamp is in nano seconds and will change based on current system time.
This csv file can be pulled from the device using "adb pull" command.
```
adb pull /data/local/tmp/output.csv ./output.csv
```
## CSV Columns
Following columns are available in CSV.
Note: All time values are in nano seconds
1. **fileName**: The file being used as an input for the benchmark test.
2. **Channels**: Number of channels does the input audio bitstream contain.
3. **SampleRate**: SampleRate of the input audio bitstream.
4. **bitRate**: bitRate of the input audio bitstream.
5. **profile**: profile of the input audio bitstream.
6. **real_time**: Measures total elapsed time from start to end of process, including wait times and delays.
7. **cpu_time**: Measures total time spent by cpu actively executing instructions for a process.