Benchmark: Add test for Codec2 decoders
Test: C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
Bug: 140051680
Change-Id: Ie70e8ab18d067cc6b7cf852fb85dd09ed8640351
diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md
index 487ddb8..520a2cf 100644
--- a/media/tests/benchmark/README.md
+++ b/media/tests/benchmark/README.md
@@ -118,5 +118,30 @@
The test encodes input stream and benchmarks the encoders available in SDK.
```
adb shell am instrument -w -r -e class 'com.android.media.benchmark.tests.EncoderTest' com.android.media.benchmark/androidx.test.runner.AndroidJUnitRunner
+```
+# Codec2
+To run the test suite for measuring performance of the codec2 layer, follow the following steps:
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+adb push $(OUT)/data/nativetest64/* /data/local/tmp/
+Eg. adb push $(OUT)/data/nativetest64/C2DecoderTest/C2DecoderTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+adb push $(OUT)/data/nativetest/* /data/local/tmp/
+Eg. adb push $(OUT)/data/nativetest/C2DecoderTest/C2DecoderTest /data/local/tmp/
+
+To get the resource files for the test follow instructions given in [NDK](#NDK)
+
+## C2 Decoder
+
+The test decodes input stream and benchmarks the codec2 decoders available in device.
+
+Setup steps are same as [extractor](#extractor).
+
+```
+adb shell /data/local/tmp/C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
```
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index 1da0102..08ef2d1 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -58,6 +58,39 @@
]
}
+cc_library_static {
+ name: "libmediabenchmark_codec2_common",
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: [
+ "BenchmarkC2Common.cpp",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
+
+cc_defaults {
+ name: "libmediabenchmark_codec2_common-defaults",
+
+ defaults: [
+ "libmediabenchmark_common-defaults",
+ "libcodec2-hidl-client-defaults",
+ "libmediabenchmark_soft_sanitize_all-defaults",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/codec2/hidl/client/include",
+ ],
+
+ shared_libs: [
+ "libcodec2_client",
+ ]
+}
+
// public dependency for native implementation
// to be used by code under media/benchmark/* only
cc_defaults {
diff --git a/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp b/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp
new file mode 100644
index 0000000..622a0e1
--- /dev/null
+++ b/media/tests/benchmark/src/native/common/BenchmarkC2Common.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "BenchmarkC2Common"
+
+#include "BenchmarkC2Common.h"
+
+int32_t BenchmarkC2Common::setupCodec2() {
+ ALOGV("In %s", __func__);
+ mClient = android::Codec2Client::CreateFromService("default");
+ if (!mClient) return -1;
+
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ if (!store) return -1;
+
+ c2_status_t status = store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator);
+ if (status != C2_OK) return status;
+
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
+ if (!mLinearPool) return -1;
+
+ status = store->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &mGraphicAllocator);
+ if (status != C2_OK) return status;
+
+ mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator, mBlockPoolId++);
+ if (!mGraphicPool) return -1;
+
+ for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
+ mWorkQueue.emplace_back(new C2Work);
+ }
+ if (!mStats) mStats = new Stats();
+
+ return status;
+}
+
+vector<string> BenchmarkC2Common::getSupportedComponentList(bool isEncoder) {
+ // Get List of components from all known services
+ vector<string> codecList;
+ const std::vector<C2Component::Traits> listTraits = mClient->ListComponents();
+ if (listTraits.size() == 0)
+ ALOGE("ComponentInfo list empty.");
+ else {
+ for (size_t i = 0; i < listTraits.size(); i++) {
+ if (isEncoder && C2Component::KIND_ENCODER == listTraits[i].kind) {
+ codecList.push_back(listTraits[i].name);
+ } else if (!isEncoder && C2Component::KIND_DECODER == listTraits[i].kind) {
+ codecList.push_back(listTraits[i].name);
+ }
+ }
+ }
+ return codecList;
+}
+
+void BenchmarkC2Common::waitOnInputConsumption() {
+ typedef std::unique_lock<std::mutex> ULock;
+ uint32_t queueSize;
+ uint32_t maxRetry = 0;
+ {
+ ULock l(mQueueLock);
+ queueSize = mWorkQueue.size();
+ }
+ while ((maxRetry < MAX_RETRY) && (queueSize < MAX_INPUT_BUFFERS)) {
+ ULock l(mQueueLock);
+ if (queueSize != mWorkQueue.size()) {
+ queueSize = mWorkQueue.size();
+ maxRetry = 0;
+ } else {
+ mQueueCondition.wait_for(l, TIME_OUT);
+ maxRetry++;
+ }
+ }
+}
+
+void BenchmarkC2Common::handleWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
+ ALOGV("In %s", __func__);
+ mStats->addOutputTime();
+ for (std::unique_ptr<C2Work> &work : workItems) {
+ if (!work->worklets.empty()) {
+ if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
+ mEos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) !=
+ 0;
+ ALOGV("WorkDone: frameID received %d , mEos : %d",
+ (int)work->worklets.front()->output.ordinal.frameIndex.peeku(), mEos);
+ work->input.buffers.clear();
+ work->worklets.clear();
+ {
+ typedef std::unique_lock<std::mutex> ULock;
+ ULock l(mQueueLock);
+ mWorkQueue.push_back(std::move(work));
+ mQueueCondition.notify_all();
+ }
+ }
+ }
+ }
+}
+
diff --git a/media/tests/benchmark/src/native/common/BenchmarkC2Common.h b/media/tests/benchmark/src/native/common/BenchmarkC2Common.h
new file mode 100644
index 0000000..d67758a
--- /dev/null
+++ b/media/tests/benchmark/src/native/common/BenchmarkC2Common.h
@@ -0,0 +1,141 @@
+/*
+ * 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_C2_COMMON_H__
+#define __BENCHMARK_C2_COMMON_H__
+
+#include "codec2/hidl/client.h"
+
+#include <C2Component.h>
+#include <C2Config.h>
+
+#include <hidl/HidlSupport.h>
+
+#include <C2AllocatorIon.h>
+#include <C2Buffer.h>
+#include <C2BufferPriv.h>
+
+#include "BenchmarkCommon.h"
+
+#define MAX_RETRY 20
+#define TIME_OUT 400ms
+#define MAX_INPUT_BUFFERS 8
+
+using android::C2AllocatorIon;
+
+class LinearBuffer : public C2Buffer {
+ public:
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
+ : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
+
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block, size_t size)
+ : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
+};
+
+class GraphicBuffer : public C2Buffer {
+ public:
+ explicit GraphicBuffer(const std::shared_ptr<C2GraphicBlock> &block)
+ : C2Buffer({block->share(C2Rect(block->width(), block->height()), ::C2Fence())}) {}
+};
+
+/**
+ * Handle Callback functions onWorkDone(), onTripped(),
+ * onError(), onDeath(), onFramesRendered() for C2 Components
+ */
+struct CodecListener : public android::Codec2Client::Listener {
+ public:
+ CodecListener(
+ const std::function<void(std::list<std::unique_ptr<C2Work>> &workItems)> fn = nullptr)
+ : callBack(fn) {}
+ virtual void onWorkDone(const std::weak_ptr<android::Codec2Client::Component> &comp,
+ std::list<std::unique_ptr<C2Work>> &workItems) override {
+ ALOGV("onWorkDone called");
+ (void)comp;
+ if (callBack) callBack(workItems);
+ }
+
+ virtual void onTripped(
+ const std::weak_ptr<android::Codec2Client::Component> &comp,
+ const std::vector<std::shared_ptr<C2SettingResult>> &settingResults) override {
+ (void)comp;
+ (void)settingResults;
+ }
+
+ virtual void onError(const std::weak_ptr<android::Codec2Client::Component> &comp,
+ uint32_t errorCode) override {
+ (void)comp;
+ ALOGV("onError called");
+ if (errorCode != 0) ALOGE("Error : %u", errorCode);
+ }
+
+ virtual void onDeath(const std::weak_ptr<android::Codec2Client::Component> &comp) override {
+ (void)comp;
+ }
+
+ virtual void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) override {
+ (void)frameIndex;
+ (void)arrayIndex;
+ }
+
+ virtual void onFrameRendered(uint64_t bufferQueueId, int32_t slotId,
+ int64_t timestampNs) override {
+ (void)bufferQueueId;
+ (void)slotId;
+ (void)timestampNs;
+ }
+
+ std::function<void(std::list<std::unique_ptr<C2Work>> &workItems)> callBack;
+};
+
+class BenchmarkC2Common {
+ public:
+ BenchmarkC2Common()
+ : mEos(false),
+ mStats(nullptr),
+ mClient(nullptr),
+ mBlockPoolId(0),
+ mLinearPool(nullptr),
+ mGraphicPool(nullptr),
+ mLinearAllocator(nullptr),
+ mGraphicAllocator(nullptr) {}
+
+ int32_t setupCodec2();
+
+ vector<string> getSupportedComponentList(bool isEncoder);
+
+ void waitOnInputConsumption();
+
+ // callback function to process onWorkDone received by Listener
+ void handleWorkDone(std::list<std::unique_ptr<C2Work>> &workItems);
+
+ bool mEos;
+ protected:
+ Stats *mStats;
+
+ std::shared_ptr<android::Codec2Client> mClient;
+
+ C2BlockPool::local_id_t mBlockPoolId;
+ std::shared_ptr<C2BlockPool> mLinearPool;
+ std::shared_ptr<C2BlockPool> mGraphicPool;
+ std::shared_ptr<C2Allocator> mLinearAllocator;
+ std::shared_ptr<C2Allocator> mGraphicAllocator;
+
+ std::mutex mQueueLock;
+ std::condition_variable mQueueCondition;
+ std::list<std::unique_ptr<C2Work>> mWorkQueue;
+};
+
+#endif // __BENCHMARK_C2_COMMON_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
index b6286d4..b5072ab 100644
--- a/media/tests/benchmark/src/native/decoder/Android.bp
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -29,3 +29,22 @@
ldflags: ["-Wl,-Bsymbolic"]
}
+
+cc_library_static {
+ name: "libmediabenchmark_codec2_decoder",
+ defaults: [
+ "libmediabenchmark_common-defaults",
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["C2Decoder.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_common",
+ "libmediabenchmark_extractor",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
new file mode 100644
index 0000000..e88d011
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 "C2Decoder"
+
+#include "C2Decoder.h"
+
+int32_t C2Decoder::createCodec2Component(string compName, AMediaFormat *format) {
+ ALOGV("In %s", __func__);
+ mListener.reset(new CodecListener(
+ [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
+ if (!mListener) return -1;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("Error in AMediaFormat_getString");
+ return -1;
+ }
+ // Configure the plugin with Input properties
+ std::vector<C2Param *> configParam;
+ if (!strncmp(mime, "audio/", 6)) {
+ int32_t sampleRate, numChannels;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels);
+ C2StreamSampleRateInfo::output sampleRateInfo(0u, sampleRate);
+ C2StreamChannelCountInfo::output channelCountInfo(0u, numChannels);
+ configParam.push_back(&sampleRateInfo);
+ configParam.push_back(&channelCountInfo);
+
+ } else {
+ int32_t width, height;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
+ C2StreamPictureSizeInfo::input inputSize(0u, width, height);
+ configParam.push_back(&inputSize);
+ }
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
+ if (mComponent == nullptr) {
+ ALOGE("Create component failed for %s", compName.c_str());
+ return -1;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+ if (failures.size() != 0) {
+ ALOGE("Invalid Configuration");
+ return -1;
+ }
+
+ status |= mComponent->start();
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setInitTime(timeTaken);
+ return status;
+}
+
+int32_t C2Decoder::decodeFrames(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo) {
+ ALOGV("In %s", __func__);
+ typedef std::unique_lock<std::mutex> ULock;
+ c2_status_t status = C2_OK;
+ mStats->setStartTime();
+ while (1) {
+ if (mNumInputFrame == frameInfo.size()) break;
+ std::unique_ptr<C2Work> work;
+ // Prepare C2Work
+ {
+ ULock l(mQueueLock);
+ if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
+ if (!mWorkQueue.empty()) {
+ mStats->addInputTime();
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ cout << "Wait for generating C2Work exceeded timeout" << endl;
+ return -1;
+ }
+ }
+
+ uint32_t flags = frameInfo[mNumInputFrame].flags;
+ if (flags == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) {
+ flags = C2FrameData::FLAG_CODEC_CONFIG;
+ }
+ if (mNumInputFrame == (frameInfo.size() - 1)) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ }
+ work->input.flags = (C2FrameData::flags_t)flags;
+ work->input.ordinal.timestamp = frameInfo[mNumInputFrame].presentationTimeUs;
+ work->input.ordinal.frameIndex = mNumInputFrame;
+ work->input.buffers.clear();
+ int size = frameInfo[mNumInputFrame].size;
+ int alignedSize = ALIGN(size, PAGE_SIZE);
+ if (size) {
+ std::shared_ptr<C2LinearBlock> block;
+ status = mLinearPool->fetchLinearBlock(
+ alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (status != C2_OK || block == nullptr) {
+ cout << "C2LinearBlock::map() failed : " << status << endl;
+ return status;
+ }
+
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ cout << "C2LinearBlock::map() failed : " << view.error() << endl;
+ return view.error();
+ }
+ memcpy(view.base(), inputBuffer + mOffset, size);
+ work->input.buffers.emplace_back(new LinearBuffer(block, size));
+ mStats->addFrameSize(size);
+ }
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ // queue() invokes process() function of C2 Plugin.
+ status = mComponent->queue(&items);
+ if (status != C2_OK) {
+ ALOGE("queue failed");
+ return status;
+ }
+ ALOGV("Frame #%d size = %d queued", mNumInputFrame, size);
+ mNumInputFrame++;
+ mOffset += size;
+ }
+ return status;
+}
+
+void C2Decoder::deInitCodec() {
+ ALOGV("In %s", __func__);
+ if (!mComponent) return;
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent->stop();
+ mComponent->release();
+ mComponent = nullptr;
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setDeInitTime(timeTaken);
+}
+
+void C2Decoder::dumpStatistics(string inputReference, int64_t durationUs) {
+ string operation = "c2decode";
+ mStats->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void C2Decoder::resetDecoder() {
+ mOffset = 0;
+ mNumInputFrame = 0;
+ if (mStats) mStats->reset();
+}
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.h b/media/tests/benchmark/src/native/decoder/C2Decoder.h
new file mode 100644
index 0000000..0e79d51
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __C2_DECODER_H__
+#define __C2_DECODER_H__
+
+#include <stdio.h>
+#include <algorithm>
+#include <fstream>
+
+#include "BenchmarkC2Common.h"
+
+#define ALIGN(_sz, _align) (((_sz) + ((_align) - 1)) & ~((_align) - 1))
+
+class C2Decoder : public BenchmarkC2Common {
+ public:
+ C2Decoder() : mOffset(0), mNumInputFrame(0), mComponent(nullptr) {}
+
+ int32_t createCodec2Component(string codecName, AMediaFormat *format);
+
+ int32_t decodeFrames(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo);
+
+ void deInitCodec();
+
+ void dumpStatistics(string inputReference, int64_t durationUs);
+
+ void resetDecoder();
+
+ private:
+ int32_t mOffset;
+ int32_t mNumInputFrame;
+ vector<AMediaCodecBufferInfo> mFrameMetaData;
+
+ std::shared_ptr<android::Codec2Client::Listener> mListener;
+ std::shared_ptr<android::Codec2Client::Component> mComponent;
+};
+
+#endif // __C2_DECODER_H__
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
index 24fd68c..128d055 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -75,3 +75,20 @@
"libmediabenchmark_encoder",
],
}
+
+cc_test {
+ name: "C2DecoderTest",
+ gtest: true,
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ "libmediabenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["C2DecoderTest.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_common",
+ "libmediabenchmark_codec2_decoder",
+ ],
+}
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
new file mode 100644
index 0000000..3531d8a
--- /dev/null
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "C2DecoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "BenchmarkTestEnvironment.h"
+#include "C2Decoder.h"
+#include "Extractor.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class C2DecoderTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ C2DecoderTest() : mDecoder(nullptr), disableTest(false) { setupC2DecoderTest(); }
+
+ void setupC2DecoderTest();
+
+ vector<string> mCodecList;
+ C2Decoder *mDecoder;
+ bool disableTest;
+};
+
+void C2DecoderTest::setupC2DecoderTest() {
+ mDecoder = new C2Decoder();
+ if (!mDecoder) {
+ cout << "[ WARN ] Test Skipped. C2Decoder creation failed\n";
+ disableTest = true;
+ return;
+ }
+ int32_t status = mDecoder->setupCodec2();
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Codec2 setup failed \n";
+ disableTest = true;
+ return;
+ }
+ mCodecList = mDecoder->getSupportedComponentList(false /* isEncoder*/);
+ if (!mCodecList.size()) {
+ cout << "[ WARN ] Test Skipped. Codec2 client didn't recognise any component \n";
+ disableTest = true;
+ return;
+ }
+}
+
+TEST_P(C2DecoderTest, Codec2Decode) {
+ if (disableTest) return;
+
+ ALOGV("Decode the samples given by extractor using codec2");
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ if (!inputFp) {
+ cout << "[ WARN ] Test Skipped. Unable to open input file" << inputFile
+ << " for reading \n";
+ return;
+ }
+
+ Extractor *extractor = new Extractor();
+ if (!extractor) {
+ cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
+ return;
+ }
+
+ // Read file properties
+ fseek(inputFp, 0, SEEK_END);
+ size_t fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ if (fileSize > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Input file size is greater than the threshold memory "
+ "dedicated to the test \n";
+ }
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+ for (int32_t curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Track Format invalid \n";
+ return;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
+ if (!inputBuffer) {
+ cout << "[ WARN ] Test Skipped. Insufficient memory \n";
+ return;
+ }
+
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+ int32_t idx = 0;
+
+ // Get CSD data
+ while (1) {
+ void *csdBuffer = extractor->getCSDSample(info, idx);
+ if (!csdBuffer || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > fileSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ idx++;
+ }
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > fileSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ AMediaFormat *format = extractor->getFormat();
+ // Decode the given input stream for all C2 codecs supported by device
+ for (string codecName : mCodecList) {
+ if (codecName.find(GetParam().second) != string::npos &&
+ codecName.find("secure") == string::npos) {
+ status = mDecoder->createCodec2Component(codecName, format);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Create component failed for " << codecName
+ << "\n";
+ continue;
+ }
+
+ // Send the inputs to C2 Decoder and wait till all buffers are returned.
+ mDecoder->decodeFrames(inputBuffer, frameInfo);
+ mDecoder->waitOnInputConsumption();
+ if (!mDecoder->mEos) {
+ cout << "[ WARN ] Test Failed. Didn't receive EOS \n";
+ }
+ mDecoder->deInitCodec();
+ int64_t durationUs = extractor->getClipDuration();
+ cout << "codec: " << codecName << endl;
+ mDecoder->dumpStatistics(GetParam().first, durationUs);
+ mDecoder->resetDecoder();
+ }
+ }
+ free(inputBuffer);
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete extractor;
+ delete mDecoder;
+ }
+}
+
+// TODO: (b/140549596)
+// Add wav files
+INSTANTIATE_TEST_SUITE_P(
+ AudioDecoderTest, C2DecoderTest,
+ ::testing::Values(
+ make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+
+INSTANTIATE_TEST_SUITE_P(
+ VideoDecoderTest, C2DecoderTest,
+ ::testing::Values(
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
+ make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+
+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();
+ ALOGV("C2 Decoder Test result = %d\n", status);
+ }
+ return status;
+}