You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
12 KiB
344 lines
12 KiB
7 months ago
|
/*
|
||
|
* 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;
|
||
|
}
|