blob: 34f12db9fe24f11836ac3df6001ea21a04ac5059 [file] [log] [blame]
/*
* 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;
}