diff --git a/media/tests/benchmark/.clang-format b/media/tests/benchmark/.clang-format new file mode 100644 index 0000000000..bf1e355f91 --- /dev/null +++ b/media/tests/benchmark/.clang-format @@ -0,0 +1,13 @@ +BasedOnStyle: Google +Standard: Cpp11 +AccessModifierOffset: -2 +AllowShortFunctionsOnASingleLine: Inline +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IncludeBlocks: Preserve +IndentWidth: 4 +ContinuationIndentWidth: 8 +PointerAlignment: Right +TabWidth: 4 +UseTab: Never diff --git a/media/tests/benchmark/Android.bp b/media/tests/benchmark/Android.bp new file mode 100644 index 0000000000..8a7a59f6cd --- /dev/null +++ b/media/tests/benchmark/Android.bp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 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. + */ + +subdirs = [ + "src", + "tests", +] \ No newline at end of file diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md new file mode 100644 index 0000000000..327f0e23ad --- /dev/null +++ b/media/tests/benchmark/README.md @@ -0,0 +1,30 @@ +# Benchmark tests + +Run the following steps to build the test suite: +``` +mmm frameworks/av/media/tests/benchmark/ +``` + +The binaries will be created in the following path : ${OUT}/data/nativetest64/ + +adb push $(OUT)/data/nativetest64/* /data/local/tmp/ + +Eg. adb push $(OUT)/data/nativetest64/extractorTest/extractorTest /data/local/tmp/ + +To run the binary, follow the commands mentioned below under each module. + +The resource files for the tests are taken from [here](https://drive.google.com/open?id=1ghMr17BBJ7n0pqbm7oREiTN_MNemJUqy) + +## Extractor + +The test extracts elementary stream and benchmarks the extractors available in NDK. + +Push the resource files to /sdcard/res on the device. + +You can use a different location, but you have to modify the rest of the instructions to replace /sdcard/res with wherever you chose to put the files. + +The path to these files on the device is required to be given for the test. + +``` +adb shell /data/local/tmp/extractorTest -P /sdcard/res/ +``` diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h index 3ef47c2a77..bad6346e1b 100644 --- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h +++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h @@ -20,6 +20,7 @@ #include #include +#include constexpr uint32_t kQueueDequeueTimeoutUs = 1000; constexpr uint32_t kMaxCSDStrlen = 16; diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp new file mode 100644 index 0000000000..2fbe4e8d0b --- /dev/null +++ b/media/tests/benchmark/src/native/extractor/Android.bp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 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. + */ + +cc_library_static { + name: "libbenchmark_extractor", + defaults: [ + "libbenchmark_common-defaults", + "libbenchmark_soft_sanitize_all-defaults", + ], + + srcs: ["Extractor.cpp"], + + export_include_dirs: ["."], + + ldflags: ["-Wl,-Bsymbolic"] +} diff --git a/media/tests/benchmark/src/native/extractor/Extractor.cpp b/media/tests/benchmark/src/native/extractor/Extractor.cpp new file mode 100644 index 0000000000..0726ae3602 --- /dev/null +++ b/media/tests/benchmark/src/native/extractor/Extractor.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "extractor" + +#include + +#include "Extractor.h" + +int32_t Extractor::initExtractor(int32_t fd, size_t fileSize) { + mTimer = new Timer(); + + mFrameBuf = (uint8_t *)calloc(kMaxBufferSize, sizeof(uint8_t)); + if (!mFrameBuf) return -1; + + int64_t sTime = mTimer->getCurTime(); + + mExtractor = AMediaExtractor_new(); + if (!mExtractor) return AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE; + media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, fd, 0, fileSize); + if (status != AMEDIA_OK) return status; + + int64_t eTime = mTimer->getCurTime(); + int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime); + mTimer->setInitTime(timeTaken); + + return AMediaExtractor_getTrackCount(mExtractor); +} + +void *Extractor::getCSDSample(AMediaCodecBufferInfo &frameInfo, int32_t csdIndex) { + char csdName[kMaxCSDStrlen]; + void *csdBuffer = nullptr; + frameInfo.presentationTimeUs = 0; + frameInfo.flags = AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG; + snprintf(csdName, sizeof(csdName), "csd-%d", csdIndex); + + size_t size; + bool csdFound = AMediaFormat_getBuffer(mFormat, csdName, &csdBuffer, &size); + if (!csdFound) return nullptr; + frameInfo.size = (int32_t)size; + + return csdBuffer; +} + +int32_t Extractor::getFrameSample(AMediaCodecBufferInfo &frameInfo) { + int32_t size = AMediaExtractor_readSampleData(mExtractor, mFrameBuf, kMaxBufferSize); + if (size < 0) return -1; + + frameInfo.flags = AMediaExtractor_getSampleFlags(mExtractor); + frameInfo.size = size; + frameInfo.presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor); + AMediaExtractor_advance(mExtractor); + + return 0; +} + +int32_t Extractor::setupTrackFormat(int32_t trackId) { + AMediaExtractor_selectTrack(mExtractor, trackId); + mFormat = AMediaExtractor_getTrackFormat(mExtractor, trackId); + if (!mFormat) return AMEDIA_ERROR_INVALID_OBJECT; + + bool durationFound = AMediaFormat_getInt64(mFormat, AMEDIAFORMAT_KEY_DURATION, &mDurationUs); + if (!durationFound) return AMEDIA_ERROR_INVALID_OBJECT; + + return AMEDIA_OK; +} + +int32_t Extractor::extract(int32_t trackId) { + int32_t status = setupTrackFormat(trackId); + if (status != AMEDIA_OK) return status; + + int32_t idx = 0; + AMediaCodecBufferInfo frameInfo; + while (1) { + memset(&frameInfo, 0, sizeof(AMediaCodecBufferInfo)); + void *csdBuffer = getCSDSample(frameInfo, idx); + if (!csdBuffer || !frameInfo.size) break; + idx++; + } + + mTimer->setStartTime(); + while (1) { + int32_t status = getFrameSample(frameInfo); + if (status || !frameInfo.size) break; + mTimer->addOutputTime(); + } + + if (mFormat) { + AMediaFormat_delete(mFormat); + mFormat = nullptr; + } + + AMediaExtractor_unselectTrack(mExtractor, trackId); + + return AMEDIA_OK; +} + +void Extractor::dumpStatistics(string inputReference) { + string operation = "extract"; + mTimer->dumpStatistics(operation, inputReference, mDurationUs); +} + +void Extractor::deInitExtractor() { + if (mFrameBuf) { + free(mFrameBuf); + mFrameBuf = nullptr; + } + + int64_t sTime = mTimer->getCurTime(); + if (mExtractor) { + // TODO: (b/140128505) Multiple calls result in DoS. + // Uncomment call to AMediaExtractor_delete() once this is resolved + // AMediaExtractor_delete(mExtractor); + mExtractor = nullptr; + } + int64_t eTime = mTimer->getCurTime(); + int64_t deInitTime = mTimer->getTimeDiff(sTime, eTime); + mTimer->setDeInitTime(deInitTime); +} diff --git a/media/tests/benchmark/src/native/extractor/Extractor.h b/media/tests/benchmark/src/native/extractor/Extractor.h new file mode 100644 index 0000000000..361bcd7e6c --- /dev/null +++ b/media/tests/benchmark/src/native/extractor/Extractor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef __EXTRACTOR_H__ +#define __EXTRACTOR_H__ + +#include + +#include "BenchmarkCommon.h" +#include "Timer.h" + +class Extractor { + public: + Extractor() + : mFormat(nullptr), + mExtractor(nullptr), + mTimer(nullptr), + mFrameBuf{nullptr}, + mDurationUs{0} {} + + ~Extractor() { + if (mTimer) delete mTimer; + } + + int32_t initExtractor(int32_t fd, size_t fileSize); + + int32_t setupTrackFormat(int32_t trackId); + + void *getCSDSample(AMediaCodecBufferInfo &frameInfo, int32_t csdIndex); + + int32_t getFrameSample(AMediaCodecBufferInfo &frameInfo); + + int32_t extract(int32_t trackId); + + void dumpStatistics(std::string inputReference); + + void deInitExtractor(); + + AMediaFormat *getFormat() { return mFormat; } + + uint8_t *getFrameBuf() { return mFrameBuf; } + + int64_t getClipDuration() { return mDurationUs; } + + private: + AMediaFormat *mFormat; + AMediaExtractor *mExtractor; + Timer *mTimer; + uint8_t *mFrameBuf; + int64_t mDurationUs; +}; + +#endif // __EXTRACTOR_H__ \ No newline at end of file diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp new file mode 100644 index 0000000000..30d6e203ce --- /dev/null +++ b/media/tests/benchmark/tests/Android.bp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 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. + */ + +cc_test { + name: "extractorTest", + gtest: true, + defaults: [ + "libbenchmark_common-defaults", + "libbenchmark_soft_sanitize_all-defaults", + ], + + srcs: ["ExtractorTest.cpp"], + + static_libs: ["libbenchmark_extractor"] +} diff --git a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h new file mode 100644 index 0000000000..ae2eee15dc --- /dev/null +++ b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef __BENCHMARK_TEST_ENVIRONMENT_H__ +#define __BENCHMARK_TEST_ENVIRONMENT_H__ + +#include + +#include + +using namespace std; + +class BenchmarkTestEnvironment : public ::testing::Environment { + public: + BenchmarkTestEnvironment() : res("/sdcard/media/") {} + + // Parses the command line argument + int initFromOptions(int argc, char **argv); + + void setRes(const char *_res) { res = _res; } + + const string getRes() const { return res; } + + private: + string res; +}; + +int BenchmarkTestEnvironment::initFromOptions(int argc, char **argv) { + static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}}; + + while (true) { + int index = 0; + int c = getopt_long(argc, argv, "P:", options, &index); + if (c == -1) { + break; + } + + switch (c) { + case 'P': { + setRes(optarg); + break; + } + default: + break; + } + } + + if (optind < argc) { + fprintf(stderr, + "unrecognized option: %s\n\n" + "usage: %s \n\n" + "test options are:\n\n" + "-P, --path: Resource files directory location\n", + argv[optind ?: 1], argv[0]); + return 2; + } + return 0; +} + +#endif // __BENCHMARK_TEST_ENVIRONMENT_H__ diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp new file mode 100644 index 0000000000..dd0d711d8b --- /dev/null +++ b/media/tests/benchmark/tests/ExtractorTest.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "extractorTest" + +#include + +#include "Extractor.h" +#include "BenchmarkTestEnvironment.h" + +static BenchmarkTestEnvironment *gEnv = nullptr; + +class ExtractorTest : public ::testing::TestWithParam> {}; + +TEST_P(ExtractorTest, Extract) { + Extractor *extractObj = new Extractor(); + + string inputFile = gEnv->getRes() + GetParam().first; + FILE *inputFp = fopen(inputFile.c_str(), "rb"); + if (!inputFp) { + cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n"; + return; + } + + // Read file properties + size_t fileSize = 0; + fseek(inputFp, 0, SEEK_END); + fileSize = ftell(inputFp); + fseek(inputFp, 0, SEEK_SET); + int32_t fd = fileno(inputFp); + + int32_t trackCount = extractObj->initExtractor(fd, fileSize); + if (trackCount <= 0) { + cout << "[ WARN ] Test Skipped. initExtractor failed\n"; + return; + } + + int32_t trackID = GetParam().second; + int32_t status = extractObj->extract(trackID); + if (status != AMEDIA_OK) { + cout << "[ WARN ] Test Skipped. Extraction failed \n"; + return; + } + + extractObj->deInitExtractor(); + + extractObj->dumpStatistics(GetParam().first); + + fclose(inputFp); + delete extractObj; +} + +INSTANTIATE_TEST_SUITE_P(ExtractorTestAll, ExtractorTest, + ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", 0), + make_pair("crowd_1920x1080_25fps_6000kbps_h263.3gp", 0), + make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", 0), + make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", 0), + make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", 0), + make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", 0), + make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", 0), + make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", 0), + make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", 0), + make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 0), + make_pair("bbb_44100hz_2ch_600kbps_flac_5mins.flac", 0), + make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", 0), + make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", 0), + make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", 0), + make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", 0))); + +int main(int argc, char **argv) { + gEnv = new BenchmarkTestEnvironment(); + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + status = RUN_ALL_TESTS(); + ALOGD(" Extractor Test result = %d\n", status); + } + return status; +}