Merge "MediaTesting: Add FLAC Decoder Test" am: 09644d9b27 am: dbf34ddad2
Change-Id: I21ce63f0df776f137f9ccd14912f6bc3006fc9d2
diff --git a/media/libstagefright/flac/dec/test/Android.bp b/media/libstagefright/flac/dec/test/Android.bp
new file mode 100644
index 0000000..70ca80a
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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: "FlacDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "FlacDecoderTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_flacdec",
+ "libFLAC",
+ ],
+
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/flac/dec/test/AndroidTest.xml b/media/libstagefright/flac/dec/test/AndroidTest.xml
new file mode 100644
index 0000000..bebba8e
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="Test module config for flac decoder unit tests">
+ <option name="test-suite-tag" value="FlacDecoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="FlacDecoderTest->/data/local/tmp/FlacDecoderTest/" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip?unzip=true"
+ value="/data/local/tmp/FlacDecoderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="FlacDecoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/FlacDecoderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
new file mode 100644
index 0000000..34f12db
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "FlacDecoderTest"
+
+#include <utils/Log.h>
+#include <fstream>
+
+#include "FLACDecoder.h"
+
+#include "FlacDecoderTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/FlacDecoderOutput.raw"
+#define CODEC_CONFIG_FLAG 32
+
+constexpr uint32_t kMaxCount = 10;
+constexpr int32_t kMaxBlockSize = 4096;
+
+using namespace android;
+
+struct FrameInfo {
+ int32_t bytesCount;
+ uint32_t flags;
+ int64_t timestamp;
+};
+
+static FlacDecoderTestEnvironment *gEnv = nullptr;
+
+class FLACDecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {
+ public:
+ FLACDecoderTest() : mFLACDecoder(nullptr), mHasStreamInfo(false), mInputBufferCount(0) {}
+
+ ~FLACDecoderTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+ }
+
+ virtual void SetUp() override {
+ mFLACDecoder = FLACDecoder::Create();
+ ASSERT_NE(mFLACDecoder, nullptr) << "initDecoder: failed to create FLACDecoder";
+ }
+
+ int32_t processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ bool outputFloat, ofstream &ostrm);
+
+ FLACDecoder *mFLACDecoder;
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+
+ bool mHasStreamInfo;
+ int32_t mInputBufferCount;
+ ifstream mEleStream;
+};
+
+void getInfo(string infoFileName, vector<FrameInfo> &Info) {
+ ifstream eleInfo;
+ eleInfo.open(infoFileName);
+ ASSERT_EQ(eleInfo.is_open(), true);
+ int32_t bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+ while (1) {
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ if (eleInfo.is_open()) eleInfo.close();
+}
+
+int32_t FLACDecoderTest::processFlacDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ bool outputFloat, ofstream &ostrm) {
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+
+ int32_t frameID = offset;
+ if (range + offset > Info.size() || range < 0 || offset > Info.size() - 1 || offset < 0) {
+ ALOGE("Invalid offset or range or both passed for decoding");
+ ALOGE("offset = %d \t range = %d \t Info.size() = %zu", offset, range, Info.size());
+ return -1;
+ }
+
+ while (1) {
+ if (frameID == Info.size() || frameID == (offset + range)) break;
+ int64_t flags = (Info)[frameID].flags;
+ int32_t size = (Info)[frameID].bytesCount;
+ if (size < 0) {
+ ALOGE("Size for the memory allocation is negative");
+ return -1;
+ }
+ char *data = (char *)malloc(size);
+ if (!data) {
+ ALOGE("Insufficient memory to read frame");
+ return -1;
+ }
+
+ mEleStream.read(data, size);
+ if (mEleStream.gcount() != size) {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ ALOGE("Invalid size read, requested: %d and read: %zu", size, mEleStream.gcount());
+ return -1;
+ }
+
+ if (flags == CODEC_CONFIG_FLAG && mInputBufferCount == 0) {
+ status_t decoderErr = mFLACDecoder->parseMetadata((uint8_t *)data, size);
+ if (decoderErr == WOULD_BLOCK) {
+ ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+ } else if (decoderErr == OK) {
+ mStreamInfo = mFLACDecoder->getStreamInfo();
+ if (mStreamInfo.sample_rate && mStreamInfo.max_blocksize && mStreamInfo.channels) {
+ mHasStreamInfo = true;
+ }
+ ALOGV("decoder configuration : %d Hz, %d channels, %d samples,"
+ " %d block size",
+ mStreamInfo.sample_rate, mStreamInfo.channels,
+ (int32_t)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+ } else {
+ ALOGE("FLACDecoder parseMetaData returns error %d", decoderErr);
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ return decoderErr;
+ }
+ } else {
+ const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(int16_t);
+ size_t outSize = mHasStreamInfo
+ ? mStreamInfo.max_blocksize * mStreamInfo.channels * sampleSize
+ : kMaxBlockSize * FLACDecoder::kMaxChannels * sampleSize;
+
+ void *out_buf = malloc(outSize);
+ if (!out_buf) {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ ALOGE("Output buffer allocation failed");
+ return -1;
+ }
+ status_t decoderErr = mFLACDecoder->decodeOneFrame((uint8_t *)data, size, out_buf,
+ &outSize, outputFloat);
+ if (decoderErr != OK) {
+ ALOGE("decodeOneFrame returns error %d", decoderErr);
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ if (out_buf) {
+ free(out_buf);
+ out_buf = nullptr;
+ }
+ return decoderErr;
+ }
+ ostrm.write(reinterpret_cast<char *>(out_buf), outSize);
+ free(out_buf);
+ out_buf = nullptr;
+ }
+ mInputBufferCount++;
+ frameID++;
+ free(data);
+ data = nullptr;
+ }
+ ALOGV("frameID=%d", frameID);
+ return 0;
+}
+
+TEST_F(FLACDecoderTest, CreateDeleteTest) {
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+
+ for (int32_t i = 0; i < kMaxCount; i++) {
+ mFLACDecoder = FLACDecoder::Create();
+ ASSERT_NE(mFLACDecoder, nullptr) << "FLACDecoder Creation Failed";
+ if (mFLACDecoder) delete mFLACDecoder;
+ mFLACDecoder = nullptr;
+ }
+}
+
+TEST_P(FLACDecoderTest, FlushTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ bool outputFloat = get<2>(params);
+
+ vector<FrameInfo> Info;
+ getInfo(infoFileName, Info);
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true);
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true);
+
+ int32_t status = processFlacDecoder(Info, 0, Info.size() / 3, outputFloat, ostrm);
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+ mFLACDecoder->flush();
+ mHasStreamInfo = false;
+ status = processFlacDecoder(Info, (Info.size() / 3), Info.size() - (Info.size() / 3),
+ outputFloat, ostrm);
+ ostrm.close();
+ Info.clear();
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+TEST_P(FLACDecoderTest, DecodeTest) {
+ tuple<string /* InputFileName */, string /* InfoFileName */, bool /* outputfloat */> params =
+ GetParam();
+
+ string inputFileName = gEnv->getRes() + get<0>(params);
+ string infoFileName = gEnv->getRes() + get<1>(params);
+ bool outputFloat = get<2>(params);
+
+ vector<FrameInfo> Info;
+ getInfo(infoFileName, Info);
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true);
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true);
+
+ int32_t status = processFlacDecoder(Info, 0, Info.size(), outputFloat, ostrm);
+ ostrm.close();
+ Info.clear();
+ ASSERT_EQ(status, 0) << "Test Failed. Decode returned error = " << status << endl;
+}
+
+// TODO: Add remaining tests
+INSTANTIATE_TEST_SUITE_P(
+ FLACDecoderTestAll, FLACDecoderTest,
+ ::testing::Values(make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+ "bbb_flac_stereo_680kbps_48000hz.info", true),
+ make_tuple("bbb_flac_stereo_680kbps_48000hz.flac",
+ "bbb_flac_stereo_680kbps_48000hz.info", false),
+ make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+ "bbb_flac_stereo_600kbps_44100hz.info", true),
+ make_tuple("bbb_flac_stereo_600kbps_44100hz.flac",
+ "bbb_flac_stereo_600kbps_44100hz.info", false)));
+
+int main(int argc, char **argv) {
+ gEnv = new FlacDecoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Flac Decoder Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.h
new file mode 100644
index 0000000..1334bba
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/FlacDecoderTestEnvironment.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 __FLAC_DECODER_TEST_ENVIRONMENT_H__
+#define __FLAC_DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class FlacDecoderTestEnvironment : public ::testing::Environment {
+ public:
+ FlacDecoderTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int FlacDecoderTestEnvironment::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 <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __FLAC_DECODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/flac/dec/test/README.md b/media/libstagefright/flac/dec/test/README.md
new file mode 100644
index 0000000..4d194cd
--- /dev/null
+++ b/media/libstagefright/flac/dec/test/README.md
@@ -0,0 +1,40 @@
+## Media Testing ##
+---
+#### FlacDecoder :
+The FlacDecoder Test Suite validates the FlacDecoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m FlacDecoderTest
+```
+
+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/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/FlacDecoderTest/FlacDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/flac/dec/test/FlacDecoder.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push FlacDecoder /data/local/tmp/
+```
+
+usage: FlacDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/FlacDecoderTest -P /data/local/tmp/FlacDecoder/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest FlacDecoderTest -- --enable-module-dynamic-download=true
+```