1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "VorbisDecoderTest"
19 #include <utils/Log.h>
20
21 #include <fstream>
22
23 #include "VorbisDecoderTestEnvironment.h"
24
25 #define OUTPUT_FILE_NAME "/data/local/tmp/VorbisDecoderOutput.raw"
26
27 constexpr uint32_t kMaxChannels = 255;
28 constexpr uint32_t kMaxNumSamplesPerChannel = 8192;
29
30 struct vorbis_dsp_state;
31 struct vorbis_info;
32 struct FrameInfo {
33 int32_t bytesCount;
34 uint32_t flags;
35 int64_t timestamp;
36 };
37
38 extern "C" {
39 #include <Tremolo/codec_internal.h>
40
41 int _vorbis_unpack_books(vorbis_info* vi, oggpack_buffer* opb);
42 int _vorbis_unpack_info(vorbis_info* vi, oggpack_buffer* opb);
43 int _vorbis_unpack_comment(vorbis_comment* vc, oggpack_buffer* opb);
44 }
45
46 static VorbisDecoderTestEnvironment* gEnv = nullptr;
47
48 class VorbisDecoderTest : public ::testing::TestWithParam<pair<string, string>> {
49 public:
VorbisDecoderTest()50 VorbisDecoderTest()
51 : mNumFramesLeftOnPage(-1),
52 mInfoUnpacked(false),
53 mBooksUnpacked(false),
54 mInputBuffer(nullptr),
55 mOutputBuffer(nullptr),
56 mState(nullptr),
57 mVi(nullptr) {}
58
~VorbisDecoderTest()59 ~VorbisDecoderTest() {
60 if (mInputBuffer) free(mInputBuffer);
61 if (mOutputBuffer) free(mOutputBuffer);
62 if (mEleStream.is_open()) mEleStream.close();
63 if (mState) {
64 vorbis_dsp_clear(mState);
65 delete mState;
66 mState = nullptr;
67 }
68
69 if (mVi) {
70 vorbis_info_clear(mVi);
71 delete mVi;
72 mVi = nullptr;
73 }
74 mNumFramesLeftOnPage = -1;
75 if (gEnv->cleanUp()) remove(OUTPUT_FILE_NAME);
76 }
77
78 int32_t initVorbisDecoder();
79 void processVorbisDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
80 ofstream& ostrm);
81
82 ifstream mEleStream;
83 int32_t mNumFramesLeftOnPage;
84 bool mInfoUnpacked;
85 bool mBooksUnpacked;
86 char* mInputBuffer;
87 int16_t* mOutputBuffer;
88 vorbis_dsp_state* mState;
89 vorbis_info* mVi;
90 };
91
getInfo(string infoFileName,vector<FrameInfo> & Info)92 void getInfo(string infoFileName, vector<FrameInfo>& Info) {
93 ifstream eleInfo;
94 eleInfo.open(infoFileName);
95 ASSERT_EQ(eleInfo.is_open(), true) << "Failed to open " << infoFileName;
96 while (1) {
97 int32_t bytesCount = 0;
98 uint32_t flags = 0;
99 uint32_t timestamp = 0;
100
101 if (!(eleInfo >> bytesCount)) break;
102 eleInfo >> flags;
103 eleInfo >> timestamp;
104 Info.push_back({bytesCount, flags, timestamp});
105 }
106 if (eleInfo.is_open()) eleInfo.close();
107 }
108
initVorbisDecoder()109 int32_t VorbisDecoderTest::initVorbisDecoder() {
110 if (!mVi) {
111 mVi = new vorbis_info{};
112 if (!mVi) return -1;
113 }
114 vorbis_info_clear(mVi);
115
116 if (!mState) {
117 mState = new vorbis_dsp_state{};
118 if (!mState) return -1;
119 }
120 vorbis_dsp_clear(mState);
121
122 return 0;
123 }
124
makeBitReader(const void * inputBuffer,size_t size,ogg_buffer * buf,ogg_reference * ref,oggpack_buffer * bits)125 static void makeBitReader(const void* inputBuffer, size_t size, ogg_buffer* buf, ogg_reference* ref,
126 oggpack_buffer* bits) {
127 buf->data = (uint8_t*)inputBuffer;
128 buf->size = size;
129 buf->refcount = 1;
130 buf->ptr.owner = nullptr;
131
132 ref->buffer = buf;
133 ref->begin = 0;
134 ref->length = size;
135 ref->next = nullptr;
136
137 oggpack_readinit(bits, ref);
138 }
139
processVorbisDecoder(vector<FrameInfo> Info,int32_t offset,int32_t range,ofstream & ostrm)140 void VorbisDecoderTest::processVorbisDecoder(vector<FrameInfo> Info, int32_t offset, int32_t range,
141 ofstream& ostrm) {
142 int32_t frameID = offset;
143 ASSERT_GE(range, 0) << "Invalid Range";
144 ASSERT_GE(offset, 0) << "Invalid Offset";
145 ASSERT_LT(offset, Info.size()) << "Offset must be less than ";
146
147 while (1) {
148 if (frameID == Info.size() || frameID == (offset + range)) break;
149 int32_t size = (Info)[frameID].bytesCount;
150 ASSERT_GE(size, 0) << "Size for the memory allocation is negative" << size;
151
152 if (!mInputBuffer) {
153 mInputBuffer = (char*)malloc(size);
154 ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory to read frame";
155 }
156
157 mEleStream.read(mInputBuffer, size);
158 ASSERT_EQ(mEleStream.gcount(), size)
159 << "Invalid size read. Requested: " << size << " and read: " << mEleStream.gcount();
160
161 int32_t numChannels = mVi->channels;
162 /* Decode vorbis headers only once */
163 if (size > 7 && !memcmp(&mInputBuffer[1], "vorbis", 6) &&
164 (!mInfoUnpacked || !mBooksUnpacked)) {
165 ASSERT_TRUE((mInputBuffer[0] == 1) || (mInputBuffer[0] == 5))
166 << "unexpected type received " << mInputBuffer[0];
167 ogg_buffer buf;
168 ogg_reference ref;
169 oggpack_buffer bits;
170
171 // skip 7 <type + "vorbis"> bytes
172 makeBitReader((const uint8_t*)mInputBuffer + 7, size - 7, &buf, &ref, &bits);
173 if (mInputBuffer[0] == 1) {
174 vorbis_info_init(mVi);
175 int32_t status = _vorbis_unpack_info(mVi, &bits);
176 ASSERT_EQ(status, 0) << "Encountered error while unpacking info";
177 if (mVi->channels != numChannels) {
178 ALOGV("num channels changed: %d, sample rate: %ld", mVi->channels, mVi->rate);
179 numChannels = mVi->channels;
180 }
181 ASSERT_FALSE(numChannels < 1 || numChannels > kMaxChannels)
182 << "Invalid number of channels: " << numChannels;
183 mInfoUnpacked = true;
184 } else {
185 ASSERT_TRUE(mInfoUnpacked) << "Data with type:5 sent before sending type:1";
186 int32_t status = _vorbis_unpack_books(mVi, &bits);
187 ASSERT_EQ(status, 0) << "Encountered error while unpacking books";
188 status = vorbis_dsp_init(mState, mVi);
189 ASSERT_EQ(status, 0) << "Encountered error while dsp init";
190 mBooksUnpacked = true;
191 }
192 ALOGV("frameID= %d", frameID);
193 frameID++;
194 free(mInputBuffer);
195 mInputBuffer = nullptr;
196 continue;
197 }
198
199 ASSERT_TRUE(mInfoUnpacked && mBooksUnpacked)
200 << "Missing CODEC_CONFIG data mInfoUnpacked: " << mInfoUnpacked
201 << " mBooksUnpack: " << mBooksUnpacked;
202
203 int32_t numPageFrames = 0;
204 ASSERT_GE(size, sizeof(numPageFrames))
205 << "input header has size: " << size << " expected: " << sizeof(numPageFrames);
206 memcpy(&numPageFrames, mInputBuffer + size - sizeof(numPageFrames), sizeof(numPageFrames));
207 size -= sizeof(numPageFrames);
208 if (numPageFrames >= 0) {
209 mNumFramesLeftOnPage = numPageFrames;
210 }
211
212 ogg_buffer buf;
213 buf.data = reinterpret_cast<unsigned char*>(mInputBuffer);
214 buf.size = size;
215 buf.refcount = 1;
216 buf.ptr.owner = nullptr;
217
218 ogg_reference ref;
219 ref.buffer = &buf;
220 ref.begin = 0;
221 ref.length = buf.size;
222 ref.next = nullptr;
223
224 ogg_packet pack;
225 pack.packet = &ref;
226 pack.bytes = ref.length;
227 pack.b_o_s = 0;
228 pack.e_o_s = 0;
229 pack.granulepos = 0;
230 pack.packetno = 0;
231
232 size_t outCapacity = kMaxNumSamplesPerChannel * numChannels * sizeof(int16_t);
233 if (!mOutputBuffer) {
234 mOutputBuffer = (int16_t*)malloc(outCapacity);
235 ASSERT_NE(mOutputBuffer, nullptr) << "Insufficient memory";
236 }
237
238 int32_t numFrames = 0;
239 int32_t ret = vorbis_dsp_synthesis(mState, &pack, 1);
240 if (0 != ret) {
241 ALOGV("vorbis_dsp_synthesis returned %d; ignored", ret);
242 } else {
243 numFrames = vorbis_dsp_pcmout(mState, mOutputBuffer, kMaxNumSamplesPerChannel);
244 if (numFrames < 0) {
245 ALOGV("vorbis_dsp_pcmout returned %d", numFrames);
246 numFrames = 0;
247 }
248 }
249
250 if (mNumFramesLeftOnPage >= 0) {
251 if (numFrames > mNumFramesLeftOnPage) {
252 ALOGV("discarding %d frames at end of page", numFrames - mNumFramesLeftOnPage);
253 numFrames = mNumFramesLeftOnPage;
254 }
255 mNumFramesLeftOnPage -= numFrames;
256 }
257 if (numFrames) {
258 int32_t outSize = numFrames * sizeof(int16_t) * numChannels;
259 ostrm.write(reinterpret_cast<char*>(mOutputBuffer), outSize);
260 }
261 frameID++;
262 free(mInputBuffer);
263 mInputBuffer = nullptr;
264 }
265 ALOGV("Last frame decoded = %d", frameID);
266 }
267
TEST_P(VorbisDecoderTest,FlushTest)268 TEST_P(VorbisDecoderTest, FlushTest) {
269 string inputFileName = gEnv->getRes() + GetParam().first;
270 string infoFileName = gEnv->getRes() + GetParam().second;
271
272 vector<FrameInfo> Info;
273 ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
274
275 mEleStream.open(inputFileName, ifstream::binary);
276 ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << inputFileName;
277
278 ofstream ostrm;
279 ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
280 ASSERT_EQ(ostrm.is_open(), true) << "Failed to open " << OUTPUT_FILE_NAME;
281
282 int32_t err = initVorbisDecoder();
283 ASSERT_EQ(err, 0) << "initVorbisDecoder: failed to create decoder " << err;
284
285 ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, 0, Info.size() / 3, ostrm));
286
287 // flushing the decoder
288 mNumFramesLeftOnPage = -1;
289 int32_t status = vorbis_dsp_restart(mState);
290 ASSERT_EQ(status, 0) << "Encountered error while restarting";
291
292 ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, (Info.size() / 3), Info.size(), ostrm));
293
294 ostrm.close();
295 Info.clear();
296 }
297
TEST_P(VorbisDecoderTest,DecodeTest)298 TEST_P(VorbisDecoderTest, DecodeTest) {
299 string inputFileName = gEnv->getRes() + GetParam().first;
300 string infoFileName = gEnv->getRes() + GetParam().second;
301
302 vector<FrameInfo> Info;
303 ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info));
304
305 mEleStream.open(inputFileName, ifstream::binary);
306 ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << inputFileName;
307
308 ofstream ostrm;
309 ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary);
310 ASSERT_EQ(ostrm.is_open(), true) << "Failed to open " << OUTPUT_FILE_NAME;
311
312 int32_t err = initVorbisDecoder();
313 ASSERT_EQ(err, 0) << "initVorbisDecoder: failed to create decoder " << err;
314
315 ASSERT_NO_FATAL_FAILURE(processVorbisDecoder(Info, 0, Info.size(), ostrm));
316 ostrm.close();
317 Info.clear();
318 }
319
320 INSTANTIATE_TEST_SUITE_P(
321 VorbisDecoderTestAll, VorbisDecoderTest,
322 ::testing::Values(make_pair("bbb_vorbis_mono_64kbps_48000hz.vorbis",
323 "bbb_vorbis_mono_64kbps_48000hz.info"),
324 make_pair("bbb_vorbis_stereo_128kbps_44100hz_crypt.vorbis",
325 "bbb_vorbis_stereo_128kbps_44100hz_crypt.info"),
326 make_pair("bbb_vorbis_stereo_128kbps_48000hz.vorbis",
327 "bbb_vorbis_stereo_128kbps_48000hz.info"),
328 make_pair("bbb_vorbis_5ch_320kbps_48000hz.vorbis",
329 "bbb_vorbis_5ch_320kbps_48000hz.info"),
330 make_pair("bbb_vorbis_6ch_384kbps_24000hz.vorbis",
331 "bbb_vorbis_6ch_384kbps_24000hz.info")));
332
main(int argc,char ** argv)333 int main(int argc, char** argv) {
334 gEnv = new VorbisDecoderTestEnvironment();
335 ::testing::AddGlobalTestEnvironment(gEnv);
336 ::testing::InitGoogleTest(&argc, argv);
337 int status = gEnv->initFromOptions(argc, argv);
338 if (status == 0) {
339 status = RUN_ALL_TESTS();
340 ALOGV("Vorbis Decoder Test Result = %d", status);
341 }
342 return status;
343 }
344