aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-11-10 23:22:49 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-11-10 23:22:49 +0000
commit542b8104e59a57439a70b7cc456ffabe6226a927 (patch)
treed0d38ebabbba4d9ae5c8c67a77b4bf1b041e4480
parentd46c5d6226eead887ed7ceb9c7ee511c0725d481 (diff)
parente6c4e8739d899776f39d3667676e12162522209a (diff)
downloadtremolo-542b8104e59a57439a70b7cc456ffabe6226a927.tar.gz
Merge "MediaTesting: Add Vorbis Decoder Unit Test" am: 18ac1d4e55 am: e6c4e8739d
Original change: https://android-review.googlesource.com/c/platform/external/tremolo/+/1487218 Change-Id: Ic912e8b56993d22357a0dc65bb482dfd6fd4456c
-rw-r--r--tests/Android.bp31
-rw-r--r--tests/AndroidTest.xml28
-rw-r--r--tests/README.md39
-rw-r--r--tests/VorbisDecoderTest.cpp343
-rw-r--r--tests/VorbisDecoderTestEnvironment.h83
5 files changed, 524 insertions, 0 deletions
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..ea3dd9d
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,31 @@
+cc_test {
+ name: "VorbisDecoderTest",
+ gtest: true,
+ test_suites: ["device-tests"],
+
+ srcs: [
+ "VorbisDecoderTest.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libvorbisidec",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..cb81d5f
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 vorbis decoder unit test">
+ <option name="test-suite-tag" value="VorbisDecoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VorbisDecoderTest->/data/local/tmp/VorbisDecoderTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/external/tremolo/tests/VorbisDecoderRes-1.0.zip?unzip=true"
+ value="/data/local/tmp/VorbisDecoderTestRes/" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VorbisDecoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/VorbisDecoderTestRes/" />
+ <option name="native-test-flag" value="-C true" />
+ </test>
+</configuration>
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..8eb17cd
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### Vorbis Decoder
+The VorbisDecoder Test Suite validates the libvorbisidec library available in external/tremelo.
+
+Run the following steps to build the test suite:
+```
+m VorbisDecoderTest
+```
+
+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/VorbisDecoderTest/VorbisDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/VorbisDecoderTest/VorbisDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/external/tremolo/tests/VorbisDecoderRes-1.0.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push VorbisDecoderTestRes /data/local/tmp/
+```
+
+usage: VorbisDecoderTest -P \<path_to_folder\> -C <remove_output_file>
+```
+adb shell /data/local/tmp/VorbisDecoderTest -P /data/local/tmp/VorbisDecoderTestRes/ -C true
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest VorbisDecoderTest -- --enable-module-dynamic-download=true
+```
diff --git a/tests/VorbisDecoderTest.cpp b/tests/VorbisDecoderTest.cpp
new file mode 100644
index 0000000..22e19a6
--- /dev/null
+++ b/tests/VorbisDecoderTest.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisDecoderTest"
+#include <utils/Log.h>
+
+#include <fstream>
+
+#include "VorbisDecoderTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/VorbisDecoderOutput.raw"
+
+constexpr uint32_t kMaxChannels = 255;
+constexpr uint32_t kMaxNumSamplesPerChannel = 8192;
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+struct FrameInfo {
+ int32_t bytesCount;
+ uint32_t flags;
+ int64_t timestamp;
+};
+
+extern "C" {
+ #include <Tremolo/codec_internal.h>
+
+ int _vorbis_unpack_books(vorbis_info* vi, oggpack_buffer* opb);
+ int _vorbis_unpack_info(vorbis_info* vi, oggpack_buffer* opb);
+ int _vorbis_unpack_comment(vorbis_comment* vc, oggpack_buffer* opb);
+}
+
+static VorbisDecoderTestEnvironment* gEnv = nullptr;
+
+class VorbisDecoderTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ VorbisDecoderTest()
+ : mNumFramesLeftOnPage(-1),
+ mInfoUnpacked(false),
+ mBooksUnpacked(false),
+ mInputBuffer(nullptr),
+ mOutputBuffer(nullptr),
+ mState(nullptr),
+ mVi(nullptr) {}
+
+ ~VorbisDecoderTest() {
+ if (mInputBuffer) free(mInputBuffer);
+ if (mOutputBuffer) free(mOutputBuffer);
+ if (mEleStream.is_open()) mEleStream.close();
+ if (mState) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = nullptr;
+ }
+
+ if (mVi) {
+ vorbis_info_clear(mVi);
+ delete mVi;
+ mVi = nullptr;
+ }
+ mNumFramesLeftOnPage = -1;
+ if (gEnv->cleanUp()) remove(OUTPUT_FILE_NAME);
+ }
+
+ int32_t initVorbisDecoder();
+ void processVorbisDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ ofstream& ostrm);
+
+ ifstream mEleStream;
+ int32_t mNumFramesLeftOnPage;
+ bool mInfoUnpacked;
+ bool mBooksUnpacked;
+ char* mInputBuffer;
+ int16_t* mOutputBuffer;
+ vorbis_dsp_state* mState;
+ vorbis_info* mVi;
+};
+
+void getInfo(string infoFileName, vector<FrameInfo>& Info) {
+ ifstream eleInfo;
+ eleInfo.open(infoFileName);
+ ASSERT_EQ(eleInfo.is_open(), true) << "Failed to open " << infoFileName;
+ while (1) {
+ int32_t bytesCount = 0;
+ uint32_t flags = 0;
+ uint32_t timestamp = 0;
+
+ if (!(eleInfo >> bytesCount)) break;
+ eleInfo >> flags;
+ eleInfo >> timestamp;
+ Info.push_back({bytesCount, flags, timestamp});
+ }
+ if (eleInfo.is_open()) eleInfo.close();
+}
+
+int32_t VorbisDecoderTest::initVorbisDecoder() {
+ if (!mVi) {
+ mVi = new vorbis_info{};
+ if (!mVi) return -1;
+ }
+ vorbis_info_clear(mVi);
+
+ if (!mState) {
+ mState = new vorbis_dsp_state{};
+ if (!mState) return -1;
+ }
+ vorbis_dsp_clear(mState);
+
+ return 0;
+}
+
+static void makeBitReader(const void* inputBuffer, size_t size, ogg_buffer* buf, ogg_reference* ref,
+ oggpack_buffer* bits) {
+ buf->data = (uint8_t*)inputBuffer;
+ buf->size = size;
+ buf->refcount = 1;
+ buf->ptr.owner = nullptr;
+
+ ref->buffer = buf;
+ ref->begin = 0;
+ ref->length = size;
+ ref->next = nullptr;
+
+ oggpack_readinit(bits, ref);
+}
+
+void VorbisDecoderTest::processVorbisDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
+ ofstream& ostrm) {
+ int32_t frameID = offset;
+ ASSERT_GE(range, 0) << "Invalid Range";
+ ASSERT_GE(offset, 0) << "Invalid Offset";
+ ASSERT_LT(offset, Info.size()) << "Offset must be less than ";
+
+ while (1) {
+ if (frameID == Info.size() || frameID == (offset + range)) break;
+ int32_t size = (Info)[frameID].bytesCount;
+ ASSERT_GE(size, 0) << "Size for the memory allocation is negative" << size;
+
+ if (!mInputBuffer) {
+ mInputBuffer = (char*)malloc(size);
+ ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory to read frame";
+ }
+
+ mEleStream.read(mInputBuffer, size);
+ ASSERT_EQ(mEleStream.gcount(), size)
+ << "Invalid size read. Requested: " << size << " and read: " << mEleStream.gcount();
+
+ int32_t numChannels = mVi->channels;
+ /* Decode vorbis headers only once */
+ if (size > 7 && !memcmp(&mInputBuffer[1], "vorbis", 6) &&
+ (!mInfoUnpacked || !mBooksUnpacked)) {
+ ASSERT_TRUE((mInputBuffer[0] == 1) || (mInputBuffer[0] == 5))
+ << "unexpected type received " << mInputBuffer[0];
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ // skip 7 <type + "vorbis"> bytes
+ makeBitReader((const uint8_t*)mInputBuffer + 7, size - 7, &buf, &ref, &bits);
+ if (mInputBuffer[0] == 1) {
+ vorbis_info_init(mVi);
+ int32_t status = _vorbis_unpack_info(mVi, &bits);
+ ASSERT_EQ(status, 0) << "Encountered error while unpacking info";
+ if (mVi->channels != numChannels) {
+ ALOGV("num channels changed: %d, sample rate: %ld", mVi->channels, mVi->rate);
+ numChannels = mVi->channels;
+ }
+ ASSERT_FALSE(numChannels < 1 || numChannels > kMaxChannels)
+ << "Invalid number of channels: " << numChannels;
+ mInfoUnpacked = true;
+ } else {
+ ASSERT_TRUE(mInfoUnpacked) << "Data with type:5 sent before sending type:1";
+ int32_t status = _vorbis_unpack_books(mVi, &bits);
+ ASSERT_EQ(status, 0) << "Encountered error while unpacking books";
+ status = vorbis_dsp_init(mState, mVi);
+ ASSERT_EQ(status, 0) << "Encountered error while dsp init";
+ mBooksUnpacked = true;
+ }
+ ALOGV("frameID= %d", frameID);
+ frameID++;
+ free(mInputBuffer);
+ mInputBuffer = nullptr;
+ continue;
+ }
+
+ ASSERT_TRUE(mInfoUnpacked && mBooksUnpacked)
+ << "Missing CODEC_CONFIG data mInfoUnpacked: " << mInfoUnpacked
+ << " mBooksUnpack: " << mBooksUnpacked;
+
+ int32_t numPageFrames = 0;
+ ASSERT_GE(size, sizeof(numPageFrames))
+ << "input header has size: " << size << " expected: " << sizeof(numPageFrames);
+ memcpy(&numPageFrames, mInputBuffer + size - sizeof(numPageFrames), sizeof(numPageFrames));
+ size -= sizeof(numPageFrames);
+ if (numPageFrames >= 0) {
+ mNumFramesLeftOnPage = numPageFrames;
+ }
+
+ ogg_buffer buf;
+ buf.data = reinterpret_cast<unsigned char*>(mInputBuffer);
+ buf.size = size;
+ buf.refcount = 1;
+ buf.ptr.owner = nullptr;
+
+ ogg_reference ref;
+ ref.buffer = &buf;
+ ref.begin = 0;
+ ref.length = buf.size;
+ ref.next = nullptr;
+
+ ogg_packet pack;
+ pack.packet = &ref;
+ pack.bytes = ref.length;
+ pack.b_o_s = 0;
+ pack.e_o_s = 0;
+ pack.granulepos = 0;
+ pack.packetno = 0;
+
+ size_t outCapacity = kMaxNumSamplesPerChannel * numChannels * sizeof(int16_t);
+ if (!mOutputBuffer) {
+ mOutputBuffer = (int16_t*)malloc(outCapacity);
+ ASSERT_NE(mOutputBuffer, nullptr) << "Insufficient memory";
+ }
+
+ int32_t numFrames = 0;
+ int32_t ret = vorbis_dsp_synthesis(mState, &pack, 1);
+ if (0 != ret) {
+ ALOGV("vorbis_dsp_synthesis returned %d; ignored", ret);
+ } else {
+ numFrames = vorbis_dsp_pcmout(mState, mOutputBuffer, kMaxNumSamplesPerChannel);
+ if (numFrames < 0) {
+ ALOGV("vorbis_dsp_pcmout returned %d", numFrames);
+ numFrames = 0;
+ }
+ }
+
+ if (mNumFramesLeftOnPage >= 0) {
+ if (numFrames > mNumFramesLeftOnPage) {
+ ALOGV("discarding %d frames at end of page", numFrames - mNumFramesLeftOnPage);
+ numFrames = mNumFramesLeftOnPage;
+ }
+ mNumFramesLeftOnPage -= numFrames;
+ }
+ if (numFrames) {
+ int32_t outSize = numFrames * sizeof(int16_t) * numChannels;
+ ostrm.write(reinterpret_cast<char*>(mOutputBuffer), outSize);
+ }
+ frameID++;
+ free(mInputBuffer);
+ mInputBuffer = nullptr;
+ }
+ ALOGV("Last frame decoded = %d", frameID);
+}
+
+TEST_P(VorbisDecoderTest, FlushTest) {
+ string inputFileName = gEnv->getRes() + GetParam().first;
+ string infoFileName = gEnv->getRes() + GetParam().second;
+
+ vector<FrameInfo> Info;
+ ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << inputFileName;
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true) << "Failed to open " << OUTPUT_FILE_NAME;
+
+ int32_t err = initVorbisDecoder();
+ ASSERT_EQ(err, 0) << "initVorbisDecoder: failed to create decoder " << err;
+
+ ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, 0, Info.size() / 3, ostrm));
+
+ // flushing the decoder
+ mNumFramesLeftOnPage = -1;
+ int32_t status = vorbis_dsp_restart(mState);
+ ASSERT_EQ(status, 0) << "Encountered error while restarting";
+
+ ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, (Info.size() / 3), Info.size(), ostrm));
+
+ ostrm.close();
+ Info.clear();
+}
+
+TEST_P(VorbisDecoderTest, DecodeTest) {
+ string inputFileName = gEnv->getRes() + GetParam().first;
+ string infoFileName = gEnv->getRes() + GetParam().second;
+
+ vector<FrameInfo> Info;
+ ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
+
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << inputFileName;
+
+ ofstream ostrm;
+ ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
+ ASSERT_EQ(ostrm.is_open(), true) << "Failed to open " << OUTPUT_FILE_NAME;
+
+ int32_t err = initVorbisDecoder();
+ ASSERT_EQ(err, 0) << "initVorbisDecoder: failed to create decoder " << err;
+
+ ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, 0, Info.size(), ostrm));
+ ostrm.close();
+ Info.clear();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ VorbisDecoderTestAll, VorbisDecoderTest,
+ ::testing::Values(make_pair("bbb_vorbis_mono_64kbps_48000hz.vorbis",
+ "bbb_vorbis_mono_64kbps_48000hz.info"),
+ make_pair("bbb_vorbis_stereo_128kbps_44100hz_crypt.vorbis",
+ "bbb_vorbis_stereo_128kbps_44100hz_crypt.info"),
+ make_pair("bbb_vorbis_stereo_128kbps_48000hz.vorbis",
+ "bbb_vorbis_stereo_128kbps_48000hz.info"),
+ make_pair("bbb_vorbis_5ch_320kbps_48000hz.vorbis",
+ "bbb_vorbis_5ch_320kbps_48000hz.info"),
+ make_pair("bbb_vorbis_6ch_384kbps_24000hz.vorbis",
+ "bbb_vorbis_6ch_384kbps_24000hz.info")));
+
+int main(int argc, char** argv) {
+ gEnv = new VorbisDecoderTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Vorbis Decoder Test Result = %d", status);
+ }
+ return status;
+}
diff --git a/tests/VorbisDecoderTestEnvironment.h b/tests/VorbisDecoderTestEnvironment.h
new file mode 100644
index 0000000..8de7524
--- /dev/null
+++ b/tests/VorbisDecoderTestEnvironment.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef __VORBIS_DECODER_TEST_ENVIRONMENT_H__
+#define __VORBIS_DECODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class VorbisDecoderTestEnvironment : public ::testing::Environment {
+ public:
+ VorbisDecoderTestEnvironment() : res("/data/local/tmp/"), deleteOutput(true) {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char** argv);
+
+ void setRes(const char* _res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ bool cleanUp() const { return deleteOutput; }
+
+ private:
+ string res;
+ bool deleteOutput;
+};
+
+int VorbisDecoderTestEnvironment::initFromOptions(int argc, char** argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'},
+ {"cleanUp", optional_argument, 0, 'C'},
+ {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:C:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ case 'C':
+ if (!strcmp(optarg, "false")) {
+ deleteOutput = false;
+ }
+ 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 // __VORBIS_DECODER_TEST_ENVIRONMENT_H__