1 /*
2 * Copyright (C) 2018 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 "codec2_hidl_hal_audio_dec_test"
19
20 #include <android-base/logging.h>
21 #include <gtest/gtest.h>
22 #include <hidl/GtestPrinter.h>
23 #include <stdio.h>
24 #include <algorithm>
25
26 #include <C2AllocatorIon.h>
27 #include <C2Buffer.h>
28 #include <C2BufferPriv.h>
29 #include <C2Config.h>
30 #include <C2Debug.h>
31 #include <codec2/hidl/client.h>
32
33 using android::C2AllocatorIon;
34
35 #include "media_c2_hidl_test_common.h"
36
37 static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
38 kDecodeTestParameters;
39
40 static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
41
42 // Resource directory
43 static std::string sResourceDir = "";
44
45 class LinearBuffer : public C2Buffer {
46 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)47 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
48 : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
49 };
50
51 namespace {
52
53 class Codec2AudioDecHidlTestBase : public ::testing::Test {
54 public:
55 // google.codec2 Audio test setup
SetUp()56 virtual void SetUp() override {
57 getParams();
58 mDisableTest = false;
59 ALOGV("Codec2AudioDecHidlTest SetUp");
60 mClient = android::Codec2Client::CreateFromService(
61 mInstanceName.c_str(),
62 !bool(android::Codec2Client::CreateFromService("default", true)));
63 ASSERT_NE(mClient, nullptr);
64 mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
65 handleWorkDone(workItems);
66 }));
67 ASSERT_NE(mListener, nullptr);
68 for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
69 mWorkQueue.emplace_back(new C2Work);
70 }
71 mClient->createComponent(mComponentName, mListener, &mComponent);
72 ASSERT_NE(mComponent, nullptr);
73
74 std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
75 CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
76 mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
77 ASSERT_NE(mLinearPool, nullptr);
78
79 mCompName = unknown_comp;
80 struct StringToName {
81 const char* Name;
82 standardComp CompName;
83 };
84 const StringToName kStringToName[] = {
85 {"xaac", xaac}, {"mp3", mp3}, {"amrnb", amrnb},
86 {"amrwb", amrwb}, {"aac", aac}, {"vorbis", vorbis},
87 {"opus", opus}, {"pcm", pcm}, {"g711.alaw", g711alaw},
88 {"g711.mlaw", g711mlaw}, {"gsm", gsm}, {"raw", raw},
89 {"flac", flac},
90 };
91 const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
92
93 // Find the component type
94 for (size_t i = 0; i < kNumStringToName; ++i) {
95 if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
96 mCompName = kStringToName[i].CompName;
97 break;
98 }
99 }
100 mEos = false;
101 mFramesReceived = 0;
102 mTimestampUs = 0u;
103 mWorkResult = C2_OK;
104 mTimestampDevTest = false;
105 if (mCompName == unknown_comp) mDisableTest = true;
106 if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
107 }
108
TearDown()109 virtual void TearDown() override {
110 if (mComponent != nullptr) {
111 if (::testing::Test::HasFatalFailure()) return;
112 mComponent->release();
113 mComponent = nullptr;
114 }
115 }
116
117 // Get the test parameters from GetParam call.
getParams()118 virtual void getParams() {}
119
120 virtual void validateTimestampList(int32_t* bitStreamInfo);
121
122 struct outputMetaData {
123 uint64_t timestampUs;
124 uint32_t rangeLength;
125 };
126 // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)127 void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
128 for (std::unique_ptr<C2Work>& work : workItems) {
129 if (!work->worklets.empty()) {
130 // For decoder components current timestamp always exceeds
131 // previous timestamp
132 mWorkResult |= work->result;
133 bool codecConfig = ((work->worklets.front()->output.flags &
134 C2FrameData::FLAG_CODEC_CONFIG) != 0);
135 if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
136 EXPECT_GE(work->worklets.front()->output.ordinal.timestamp.peeku(),
137 mTimestampUs);
138 mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
139 uint32_t rangeLength = work->worklets.front()
140 ->output.buffers[0]
141 ->data()
142 .linearBlocks()
143 .front()
144 .map()
145 .get()
146 .capacity();
147 // List of timestamp values and output size to calculate timestamp
148 if (mTimestampDevTest) {
149 outputMetaData meta = {mTimestampUs, rangeLength};
150 oBufferMetaData.push_back(meta);
151 }
152 }
153 bool mCsd = false;
154 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
155 mEos, mCsd, mFramesReceived);
156 (void)mCsd;
157 }
158 }
159 }
160
161 enum standardComp {
162 xaac,
163 mp3,
164 amrnb,
165 amrwb,
166 aac,
167 vorbis,
168 opus,
169 pcm,
170 g711alaw,
171 g711mlaw,
172 gsm,
173 raw,
174 flac,
175 unknown_comp,
176 };
177
178 std::string mInstanceName;
179 std::string mComponentName;
180 bool mEos;
181 bool mDisableTest;
182 bool mTimestampDevTest;
183 standardComp mCompName;
184
185 int32_t mWorkResult;
186 uint64_t mTimestampUs;
187 uint32_t mFramesReceived;
188 std::list<uint64_t> mFlushedIndices;
189 std::list<uint64_t> mTimestampUslist;
190 std::list<outputMetaData> oBufferMetaData;
191
192 C2BlockPool::local_id_t mBlockPoolId;
193 std::shared_ptr<C2BlockPool> mLinearPool;
194 std::shared_ptr<C2Allocator> mLinearAllocator;
195
196 std::mutex mQueueLock;
197 std::condition_variable mQueueCondition;
198 std::list<std::unique_ptr<C2Work>> mWorkQueue;
199
200 std::shared_ptr<android::Codec2Client> mClient;
201 std::shared_ptr<android::Codec2Client::Listener> mListener;
202 std::shared_ptr<android::Codec2Client::Component> mComponent;
203
204 protected:
description(const std::string & description)205 static void description(const std::string& description) {
206 RecordProperty("description", description);
207 }
208 };
209
210 class Codec2AudioDecHidlTest
211 : public Codec2AudioDecHidlTestBase,
212 public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
getParams()213 void getParams() {
214 mInstanceName = std::get<0>(GetParam());
215 mComponentName = std::get<1>(GetParam());
216 }
217 };
218
validateComponent(const std::shared_ptr<android::Codec2Client::Component> & component,Codec2AudioDecHidlTest::standardComp compName,bool & disableTest)219 void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
220 Codec2AudioDecHidlTest::standardComp compName, bool& disableTest) {
221 // Validate its a C2 Component
222 if (component->getName().find("c2") == std::string::npos) {
223 ALOGE("Not a c2 component");
224 disableTest = true;
225 return;
226 }
227
228 // Validate its not an encoder and the component to be tested is audio
229 if (component->getName().find("encoder") != std::string::npos) {
230 ALOGE("Expected Decoder, given Encoder");
231 disableTest = true;
232 return;
233 }
234 std::vector<std::unique_ptr<C2Param>> queried;
235 c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
236 C2_DONT_BLOCK, &queried);
237 if (c2err != C2_OK && queried.size() == 0) {
238 ALOGE("Query media type failed => %d", c2err);
239 } else {
240 std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
241 if (inputDomain.find("audio/") == std::string::npos) {
242 ALOGE("Expected Audio Component");
243 disableTest = true;
244 return;
245 }
246 }
247
248 // Validates component name
249 if (compName == Codec2AudioDecHidlTest::unknown_comp) {
250 ALOGE("Component InValid");
251 disableTest = true;
252 return;
253 }
254 ALOGV("Component Valid");
255 }
256
257 // Set Default config param.
setupConfigParam(const std::shared_ptr<android::Codec2Client::Component> & component,int32_t * bitStreamInfo)258 bool setupConfigParam(const std::shared_ptr<android::Codec2Client::Component>& component,
259 int32_t* bitStreamInfo) {
260 std::vector<std::unique_ptr<C2SettingResult>> failures;
261 C2StreamSampleRateInfo::output sampleRateInfo(0u, bitStreamInfo[0]);
262 C2StreamChannelCountInfo::output channelCountInfo(0u, bitStreamInfo[1]);
263
264 std::vector<C2Param*> configParam{&sampleRateInfo, &channelCountInfo};
265 c2_status_t status = component->config(configParam, C2_DONT_BLOCK, &failures);
266 if (status == C2_OK && failures.size() == 0u) return true;
267 return false;
268 }
269
270 // In decoder components, often the input parameters get updated upon
271 // parsing the header of elementary stream. Client needs to collect this
272 // information and reconfigure
getInputChannelInfo(const std::shared_ptr<android::Codec2Client::Component> & component,Codec2AudioDecHidlTest::standardComp compName,int32_t * bitStreamInfo)273 void getInputChannelInfo(const std::shared_ptr<android::Codec2Client::Component>& component,
274 Codec2AudioDecHidlTest::standardComp compName, int32_t* bitStreamInfo) {
275 // query nSampleRate and nChannels
276 std::initializer_list<C2Param::Index> indices{
277 C2StreamSampleRateInfo::output::PARAM_TYPE,
278 C2StreamChannelCountInfo::output::PARAM_TYPE,
279 };
280 std::vector<std::unique_ptr<C2Param>> inParams;
281 c2_status_t status = component->query({}, indices, C2_DONT_BLOCK, &inParams);
282 if (status != C2_OK && inParams.size() == 0) {
283 ALOGE("Query media type failed => %d", status);
284 ASSERT_TRUE(false);
285 } else {
286 size_t offset = sizeof(C2Param);
287 for (size_t i = 0; i < inParams.size(); ++i) {
288 C2Param* param = inParams[i].get();
289 bitStreamInfo[i] = *(int32_t*)((uint8_t*)param + offset);
290 }
291 switch (compName) {
292 case Codec2AudioDecHidlTest::amrnb: {
293 ASSERT_EQ(bitStreamInfo[0], 8000);
294 ASSERT_EQ(bitStreamInfo[1], 1);
295 break;
296 }
297 case Codec2AudioDecHidlTest::amrwb: {
298 ASSERT_EQ(bitStreamInfo[0], 16000);
299 ASSERT_EQ(bitStreamInfo[1], 1);
300 break;
301 }
302 case Codec2AudioDecHidlTest::gsm: {
303 ASSERT_EQ(bitStreamInfo[0], 8000);
304 break;
305 }
306 default:
307 break;
308 }
309 }
310 }
311
312 // number of elementary streams per component
313 #define STREAM_COUNT 2
314
315 // LookUpTable of clips and metadata for component testing
GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp,char * mURL,char * info,size_t streamIndex=0)316 void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, char* info,
317 size_t streamIndex = 0) {
318 struct CompToURL {
319 Codec2AudioDecHidlTest::standardComp comp;
320 const char mURL[STREAM_COUNT][512];
321 const char info[STREAM_COUNT][512];
322 };
323 ASSERT_TRUE(streamIndex < STREAM_COUNT);
324
325 static const CompToURL kCompToURL[] = {
326 {Codec2AudioDecHidlTest::standardComp::xaac,
327 {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
328 {"bbb_aac_stereo_128kbps_48000hz.info",
329 "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
330 {Codec2AudioDecHidlTest::standardComp::mp3,
331 {"bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.mp3"},
332 {"bbb_mp3_stereo_192kbps_48000hz.info",
333 "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}},
334 {Codec2AudioDecHidlTest::standardComp::aac,
335 {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
336 {"bbb_aac_stereo_128kbps_48000hz.info",
337 "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
338 {Codec2AudioDecHidlTest::standardComp::amrnb,
339 {"sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.amrnb"},
340 {"sine_amrnb_1ch_12kbps_8000hz.info",
341 "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}},
342 {Codec2AudioDecHidlTest::standardComp::amrwb,
343 {"bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.amrwb"},
344 {"bbb_amrwb_1ch_14kbps_16000hz.info",
345 "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}},
346 {Codec2AudioDecHidlTest::standardComp::vorbis,
347 {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""},
348 {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}},
349 {Codec2AudioDecHidlTest::standardComp::opus,
350 {"bbb_opus_stereo_128kbps_48000hz.opus", ""},
351 {"bbb_opus_stereo_128kbps_48000hz.info", ""}},
352 {Codec2AudioDecHidlTest::standardComp::g711alaw,
353 {"bbb_g711alaw_1ch_8khz.raw", ""},
354 {"bbb_g711alaw_1ch_8khz.info", ""}},
355 {Codec2AudioDecHidlTest::standardComp::g711mlaw,
356 {"bbb_g711mulaw_1ch_8khz.raw", ""},
357 {"bbb_g711mulaw_1ch_8khz.info", ""}},
358 {Codec2AudioDecHidlTest::standardComp::gsm,
359 {"bbb_gsm_1ch_8khz_13kbps.raw", ""},
360 {"bbb_gsm_1ch_8khz_13kbps.info", ""}},
361 {Codec2AudioDecHidlTest::standardComp::raw,
362 {"bbb_raw_1ch_8khz_s32le.raw", ""},
363 {"bbb_raw_1ch_8khz_s32le.info", ""}},
364 {Codec2AudioDecHidlTest::standardComp::flac,
365 {"bbb_flac_stereo_680kbps_48000hz.flac", ""},
366 {"bbb_flac_stereo_680kbps_48000hz.info", ""}},
367 };
368
369 for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
370 if (kCompToURL[i].comp == comp) {
371 strcat(mURL, kCompToURL[i].mURL[streamIndex]);
372 strcat(info, kCompToURL[i].info[streamIndex]);
373 return;
374 }
375 }
376 }
377
decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> & component,std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,std::list<uint64_t> & flushedIndices,std::shared_ptr<C2BlockPool> & linearPool,std::ifstream & eleStream,android::Vector<FrameInfo> * Info,int offset,int range,bool signalEOS=true)378 void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
379 std::mutex& queueLock, std::condition_variable& queueCondition,
380 std::list<std::unique_ptr<C2Work>>& workQueue,
381 std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& linearPool,
382 std::ifstream& eleStream, android::Vector<FrameInfo>* Info, int offset,
383 int range, bool signalEOS = true) {
384 typedef std::unique_lock<std::mutex> ULock;
385 int frameID = offset;
386 int maxRetry = 0;
387 while (1) {
388 if (frameID == (int)Info->size() || frameID == (offset + range)) break;
389 uint32_t flags = 0;
390 std::unique_ptr<C2Work> work;
391 // Prepare C2Work
392 while (!work && (maxRetry < MAX_RETRY)) {
393 ULock l(queueLock);
394 if (!workQueue.empty()) {
395 work.swap(workQueue.front());
396 workQueue.pop_front();
397 } else {
398 queueCondition.wait_for(l, TIME_OUT);
399 maxRetry++;
400 }
401 }
402 if (!work && (maxRetry >= MAX_RETRY)) {
403 ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
404 }
405 int64_t timestamp = (*Info)[frameID].timestamp;
406 if ((*Info)[frameID].flags) flags = 1u << ((*Info)[frameID].flags - 1);
407 if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
408 flags |= C2FrameData::FLAG_END_OF_STREAM;
409
410 work->input.flags = (C2FrameData::flags_t)flags;
411 work->input.ordinal.timestamp = timestamp;
412 work->input.ordinal.frameIndex = frameID;
413 {
414 ULock l(queueLock);
415 flushedIndices.emplace_back(frameID);
416 }
417 int size = (*Info)[frameID].bytesCount;
418 char* data = (char*)malloc(size);
419 ASSERT_NE(data, nullptr);
420
421 eleStream.read(data, size);
422 ASSERT_EQ(eleStream.gcount(), size);
423
424 work->input.buffers.clear();
425 if (size) {
426 std::shared_ptr<C2LinearBlock> block;
427 ASSERT_EQ(C2_OK,
428 linearPool->fetchLinearBlock(
429 size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
430 ASSERT_TRUE(block);
431
432 // Write View
433 C2WriteView view = block->map().get();
434 if (view.error() != C2_OK) {
435 fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
436 break;
437 }
438 ASSERT_EQ((size_t)size, view.capacity());
439 ASSERT_EQ(0u, view.offset());
440 ASSERT_EQ((size_t)size, view.size());
441
442 memcpy(view.base(), data, size);
443
444 work->input.buffers.emplace_back(new LinearBuffer(block));
445 free(data);
446 }
447 work->worklets.clear();
448 work->worklets.emplace_back(new C2Worklet);
449
450 std::list<std::unique_ptr<C2Work>> items;
451 items.push_back(std::move(work));
452
453 // DO THE DECODING
454 ASSERT_EQ(component->queue(&items), C2_OK);
455 ALOGV("Frame #%d size = %d queued", frameID, size);
456 frameID++;
457 maxRetry = 0;
458 }
459 }
460
validateTimestampList(int32_t * bitStreamInfo)461 void Codec2AudioDecHidlTestBase::validateTimestampList(int32_t* bitStreamInfo) {
462 uint32_t samplesReceived = 0;
463 // Update SampleRate and ChannelCount
464 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
465 int32_t nSampleRate = bitStreamInfo[0];
466 int32_t nChannels = bitStreamInfo[1];
467 std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
468 auto itOut = oBufferMetaData.begin();
469 EXPECT_EQ(*itIn, itOut->timestampUs);
470 uint64_t expectedTimeStamp = *itIn;
471 while (itOut != oBufferMetaData.end()) {
472 EXPECT_EQ(expectedTimeStamp, itOut->timestampUs);
473 if (expectedTimeStamp != itOut->timestampUs) break;
474 // buffer samples = ((total bytes) / (ac * (bits per sample / 8))
475 samplesReceived += ((itOut->rangeLength) / (nChannels * 2));
476 expectedTimeStamp = samplesReceived * 1000000ll / nSampleRate;
477 itOut++;
478 }
479 itIn = mTimestampUslist.end();
480 --itIn;
481 EXPECT_GT(expectedTimeStamp, *itIn);
482 oBufferMetaData.clear();
483 mTimestampUslist.clear();
484 }
485
TEST_P(Codec2AudioDecHidlTest,validateCompName)486 TEST_P(Codec2AudioDecHidlTest, validateCompName) {
487 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
488 ALOGV("Checks if the given component is a valid audio component");
489 validateComponent(mComponent, mCompName, mDisableTest);
490 ASSERT_EQ(mDisableTest, false);
491 }
492
TEST_P(Codec2AudioDecHidlTest,configComp)493 TEST_P(Codec2AudioDecHidlTest, configComp) {
494 description("Tests component specific configuration");
495 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
496 ASSERT_EQ(mComponent->start(), C2_OK);
497 int32_t bitStreamInfo[2] = {0};
498 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
499 setupConfigParam(mComponent, bitStreamInfo);
500 ASSERT_EQ(mComponent->stop(), C2_OK);
501 }
502
503 class Codec2AudioDecDecodeTest
504 : public Codec2AudioDecHidlTestBase,
505 public ::testing::WithParamInterface<
506 std::tuple<std::string, std::string, std::string, std::string>> {
getParams()507 void getParams() {
508 mInstanceName = std::get<0>(GetParam());
509 mComponentName = std::get<1>(GetParam());
510 }
511 };
512
TEST_P(Codec2AudioDecDecodeTest,DecodeTest)513 TEST_P(Codec2AudioDecDecodeTest, DecodeTest) {
514 description("Decodes input file");
515 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
516
517 uint32_t streamIndex = std::stoi(std::get<2>(GetParam()));
518 ;
519 bool signalEOS = !std::get<3>(GetParam()).compare("true");
520 mTimestampDevTest = true;
521 char mURL[512], info[512];
522 android::Vector<FrameInfo> Info;
523
524 strcpy(mURL, sResourceDir.c_str());
525 strcpy(info, sResourceDir.c_str());
526 GetURLForComponent(mCompName, mURL, info, streamIndex);
527 if (!strcmp(mURL, sResourceDir.c_str())) {
528 ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
529 return;
530 }
531
532 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
533 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
534
535 // Reset total no of frames received
536 mFramesReceived = 0;
537 mTimestampUs = 0;
538 int32_t bitStreamInfo[2] = {0};
539 if (mCompName == raw) {
540 bitStreamInfo[0] = 8000;
541 bitStreamInfo[1] = 1;
542 } else {
543 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
544 }
545 if (!setupConfigParam(mComponent, bitStreamInfo)) {
546 std::cout << "[ WARN ] Test Skipped \n";
547 return;
548 }
549 ASSERT_EQ(mComponent->start(), C2_OK);
550 ALOGV("mURL : %s", mURL);
551 std::ifstream eleStream;
552 eleStream.open(mURL, std::ifstream::binary);
553 ASSERT_EQ(eleStream.is_open(), true);
554 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
555 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
556 (int)Info.size(), signalEOS));
557
558 // If EOS is not sent, sending empty input with EOS flag
559 size_t infoSize = Info.size();
560 if (!signalEOS) {
561 ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
562 ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
563 C2FrameData::FLAG_END_OF_STREAM, false));
564 infoSize += 1;
565 }
566 // blocking call to ensures application to Wait till all the inputs are
567 // consumed
568 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
569 eleStream.close();
570 if (mFramesReceived != infoSize) {
571 ALOGE("Input buffer count and Output buffer count mismatch");
572 ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived, infoSize);
573 ASSERT_TRUE(false);
574 }
575 ASSERT_EQ(mEos, true);
576
577 if (mTimestampDevTest) {
578 validateTimestampList(bitStreamInfo);
579 }
580 ASSERT_EQ(mComponent->stop(), C2_OK);
581 ASSERT_EQ(mWorkResult, C2_OK);
582 }
583
584 // thumbnail test
TEST_P(Codec2AudioDecHidlTest,ThumbnailTest)585 TEST_P(Codec2AudioDecHidlTest, ThumbnailTest) {
586 description("Test Request for thumbnail");
587 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
588
589 char mURL[512], info[512];
590 android::Vector<FrameInfo> Info;
591
592 strcpy(mURL, sResourceDir.c_str());
593 strcpy(info, sResourceDir.c_str());
594 GetURLForComponent(mCompName, mURL, info);
595
596 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
597 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
598
599 int32_t bitStreamInfo[2] = {0};
600 if (mCompName == raw) {
601 bitStreamInfo[0] = 8000;
602 bitStreamInfo[1] = 1;
603 } else {
604 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
605 }
606 if (!setupConfigParam(mComponent, bitStreamInfo)) {
607 std::cout << "[ WARN ] Test Skipped \n";
608 return;
609 }
610 ASSERT_EQ(mComponent->start(), C2_OK);
611 ALOGV("mURL : %s", mURL);
612
613 // request EOS for thumbnail
614 // signal EOS flag with last frame
615 size_t i = -1;
616 uint32_t flags;
617 do {
618 i++;
619 flags = 0;
620 if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
621
622 } while (!(flags & SYNC_FRAME));
623 std::ifstream eleStream;
624 eleStream.open(mURL, std::ifstream::binary);
625 ASSERT_EQ(eleStream.is_open(), true);
626 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
627 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
628 i + 1));
629 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
630 eleStream.close();
631 EXPECT_GE(mFramesReceived, 1U);
632 ASSERT_EQ(mEos, true);
633 ASSERT_EQ(mComponent->stop(), C2_OK);
634 ASSERT_EQ(mWorkResult, C2_OK);
635 }
636
TEST_P(Codec2AudioDecHidlTest,EOSTest)637 TEST_P(Codec2AudioDecHidlTest, EOSTest) {
638 description("Test empty input buffer with EOS flag");
639 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
640 typedef std::unique_lock<std::mutex> ULock;
641 ASSERT_EQ(mComponent->start(), C2_OK);
642 std::unique_ptr<C2Work> work;
643 // Prepare C2Work
644 {
645 ULock l(mQueueLock);
646 if (!mWorkQueue.empty()) {
647 work.swap(mWorkQueue.front());
648 mWorkQueue.pop_front();
649 } else {
650 ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test";
651 }
652 }
653 ASSERT_NE(work, nullptr);
654
655 work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
656 work->input.ordinal.timestamp = 0;
657 work->input.ordinal.frameIndex = 0;
658 work->input.buffers.clear();
659 work->worklets.clear();
660 work->worklets.emplace_back(new C2Worklet);
661
662 std::list<std::unique_ptr<C2Work>> items;
663 items.push_back(std::move(work));
664 ASSERT_EQ(mComponent->queue(&items), C2_OK);
665
666 {
667 ULock l(mQueueLock);
668 if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
669 mQueueCondition.wait_for(l, TIME_OUT);
670 }
671 }
672 ASSERT_EQ(mEos, true);
673 ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
674 ASSERT_EQ(mComponent->stop(), C2_OK);
675 ASSERT_EQ(mWorkResult, C2_OK);
676 }
677
TEST_P(Codec2AudioDecHidlTest,FlushTest)678 TEST_P(Codec2AudioDecHidlTest, FlushTest) {
679 description("Tests Flush calls");
680 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
681 char mURL[512], info[512];
682 android::Vector<FrameInfo> Info;
683
684 strcpy(mURL, sResourceDir.c_str());
685 strcpy(info, sResourceDir.c_str());
686 GetURLForComponent(mCompName, mURL, info);
687
688 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
689 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
690
691 int32_t bitStreamInfo[2] = {0};
692 if (mCompName == raw) {
693 bitStreamInfo[0] = 8000;
694 bitStreamInfo[1] = 1;
695 } else {
696 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
697 }
698 if (!setupConfigParam(mComponent, bitStreamInfo)) {
699 std::cout << "[ WARN ] Test Skipped \n";
700 return;
701 }
702 ASSERT_EQ(mComponent->start(), C2_OK);
703 // flush
704 std::list<std::unique_ptr<C2Work>> flushedWork;
705 c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
706 ASSERT_EQ(err, C2_OK);
707 ASSERT_NO_FATAL_FAILURE(
708 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
709 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
710
711 ALOGV("mURL : %s", mURL);
712 std::ifstream eleStream;
713 eleStream.open(mURL, std::ifstream::binary);
714 ASSERT_EQ(eleStream.is_open(), true);
715 // Decode 30 frames and flush.
716 uint32_t numFramesFlushed = FLUSH_INTERVAL;
717 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
718 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
719 numFramesFlushed, false));
720 // flush
721 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
722 ASSERT_EQ(err, C2_OK);
723 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
724 (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
725 ASSERT_NO_FATAL_FAILURE(
726 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
727 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
728
729 // Seek to next key frame and start decoding till the end
730 mFlushedIndices.clear();
731 int index = numFramesFlushed;
732 bool keyFrame = false;
733 uint32_t flags = 0;
734 while (index < (int)Info.size()) {
735 if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
736 if ((flags & SYNC_FRAME) == SYNC_FRAME) {
737 keyFrame = true;
738 break;
739 }
740 flags = 0;
741 eleStream.ignore(Info[index].bytesCount);
742 index++;
743 }
744 if (keyFrame) {
745 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
746 mFlushedIndices, mLinearPool, eleStream, &Info, index,
747 (int)Info.size() - index));
748 }
749 eleStream.close();
750 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
751 ASSERT_EQ(err, C2_OK);
752 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
753 (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
754 ASSERT_NO_FATAL_FAILURE(
755 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
756 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
757 // TODO: (b/154671521)
758 // Add assert for mWorkResult
759 ASSERT_EQ(mComponent->stop(), C2_OK);
760 }
761
TEST_P(Codec2AudioDecHidlTest,DecodeTestEmptyBuffersInserted)762 TEST_P(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) {
763 description("Decode with multiple empty input frames");
764 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
765
766 char mURL[512], info[512];
767 std::ifstream eleStream, eleInfo;
768
769 strcpy(mURL, sResourceDir.c_str());
770 strcpy(info, sResourceDir.c_str());
771 GetURLForComponent(mCompName, mURL, info);
772
773 eleInfo.open(info);
774 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
775 android::Vector<FrameInfo> Info;
776 int bytesCount = 0;
777 uint32_t frameId = 0;
778 uint32_t flags = 0;
779 uint32_t timestamp = 0;
780 bool codecConfig = false;
781 // This test introduces empty CSD after every 20th frame
782 // and empty input frames at an interval of 5 frames.
783 while (1) {
784 if (!(frameId % 5)) {
785 if (!(frameId % 20))
786 flags = 32;
787 else
788 flags = 0;
789 bytesCount = 0;
790 } else {
791 if (!(eleInfo >> bytesCount)) break;
792 eleInfo >> flags;
793 eleInfo >> timestamp;
794 codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
795 }
796 Info.push_back({bytesCount, flags, timestamp});
797 frameId++;
798 }
799 eleInfo.close();
800 int32_t bitStreamInfo[2] = {0};
801 if (mCompName == raw) {
802 bitStreamInfo[0] = 8000;
803 bitStreamInfo[1] = 1;
804 } else {
805 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
806 }
807 if (!setupConfigParam(mComponent, bitStreamInfo)) {
808 std::cout << "[ WARN ] Test Skipped \n";
809 return;
810 }
811 ASSERT_EQ(mComponent->start(), C2_OK);
812 ALOGV("mURL : %s", mURL);
813 eleStream.open(mURL, std::ifstream::binary);
814 ASSERT_EQ(eleStream.is_open(), true);
815 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
816 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
817 (int)Info.size()));
818
819 // blocking call to ensures application to Wait till all the inputs are
820 // consumed
821 if (!mEos) {
822 ALOGV("Waiting for input consumption");
823 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
824 }
825
826 eleStream.close();
827 if (mFramesReceived != Info.size()) {
828 ALOGE("Input buffer count and Output buffer count mismatch");
829 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size());
830 ASSERT_TRUE(false);
831 }
832
833 ASSERT_EQ(mComponent->stop(), C2_OK);
834 }
835
836 class Codec2AudioDecCsdInputTests
837 : public Codec2AudioDecHidlTestBase,
838 public ::testing::WithParamInterface<std::tuple<std::string, std::string, std::string>> {
getParams()839 void getParams() {
840 mInstanceName = std::get<0>(GetParam());
841 mComponentName = std::get<1>(GetParam());
842 }
843 };
844
845 // Test the codecs for the following
846 // start - csd - data… - (with/without)flush - data… - flush - data…
TEST_P(Codec2AudioDecCsdInputTests,CSDFlushTest)847 TEST_P(Codec2AudioDecCsdInputTests, CSDFlushTest) {
848 description("Tests codecs for flush at different states");
849 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
850
851 char mURL[512], info[512];
852 android::Vector<FrameInfo> Info;
853
854 strcpy(mURL, sResourceDir.c_str());
855 strcpy(info, sResourceDir.c_str());
856 GetURLForComponent(mCompName, mURL, info);
857 if (!strcmp(mURL, sResourceDir.c_str())) {
858 ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
859 return;
860 }
861 ALOGV("mURL : %s", mURL);
862
863 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
864 ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
865
866 int32_t bitStreamInfo[2] = {0};
867 if (mCompName == raw) {
868 bitStreamInfo[0] = 8000;
869 bitStreamInfo[1] = 1;
870 } else {
871 ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
872 }
873 if (!setupConfigParam(mComponent, bitStreamInfo)) {
874 std::cout << "[ WARN ] Test Skipped \n";
875 return;
876 }
877
878 ASSERT_EQ(mComponent->start(), C2_OK);
879 std::ifstream eleStream;
880 eleStream.open(mURL, std::ifstream::binary);
881 ASSERT_EQ(eleStream.is_open(), true);
882
883 bool signalEOS = false;
884 bool flushCsd = !std::get<2>(GetParam()).compare("true");
885 ALOGV("sending %d csd data ", numCsds);
886 int framesToDecode = numCsds;
887 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
888 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
889 framesToDecode, false));
890
891 c2_status_t err = C2_OK;
892 std::list<std::unique_ptr<C2Work>> flushedWork;
893 if (numCsds && flushCsd) {
894 // We wait for all the CSD buffers to get consumed.
895 // Once we have received all CSD work back, we call flush
896 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
897
898 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
899 ASSERT_EQ(err, C2_OK);
900 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
901 MAX_INPUT_BUFFERS - flushedWork.size());
902 ASSERT_NO_FATAL_FAILURE(
903 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
904 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
905 oBufferMetaData.clear();
906 }
907
908 int offset = framesToDecode;
909 while (1) {
910 framesToDecode = c2_min(FLUSH_INTERVAL, (int)Info.size() - offset);
911 if (framesToDecode < FLUSH_INTERVAL) signalEOS = true;
912 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
913 mFlushedIndices, mLinearPool, eleStream, &Info,
914 offset, framesToDecode, signalEOS));
915 offset += framesToDecode;
916 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
917 ASSERT_EQ(err, C2_OK);
918 // blocking call to ensures application to Wait till remaining
919 // 'non-flushed' inputs are consumed
920 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
921 MAX_INPUT_BUFFERS - flushedWork.size());
922 ASSERT_NO_FATAL_FAILURE(
923 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
924 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
925 if (signalEOS || offset >= (int)Info.size()) {
926 break;
927 }
928 }
929 if (!signalEOS) {
930 ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
931 C2FrameData::FLAG_END_OF_STREAM, false));
932 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
933 }
934 eleStream.close();
935 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
936 ASSERT_EQ(mComponent->stop(), C2_OK);
937 }
938
939 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2AudioDecHidlTest, testing::ValuesIn(kTestParameters),
940 android::hardware::PrintInstanceTupleNameToString<>);
941
942 // DecodeTest with StreamIndex and EOS / No EOS
943 INSTANTIATE_TEST_SUITE_P(StreamIndexAndEOS, Codec2AudioDecDecodeTest,
944 testing::ValuesIn(kDecodeTestParameters),
945 android::hardware::PrintInstanceTupleNameToString<>);
946
947 INSTANTIATE_TEST_SUITE_P(CsdInputs, Codec2AudioDecCsdInputTests,
948 testing::ValuesIn(kCsdFlushTestParameters),
949 android::hardware::PrintInstanceTupleNameToString<>);
950
951 } // anonymous namespace
952
main(int argc,char ** argv)953 int main(int argc, char** argv) {
954 kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER);
955 for (auto params : kTestParameters) {
956 kDecodeTestParameters.push_back(
957 std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "false"));
958 kDecodeTestParameters.push_back(
959 std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "true"));
960 kDecodeTestParameters.push_back(
961 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "false"));
962 kDecodeTestParameters.push_back(
963 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "true"));
964
965 kCsdFlushTestParameters.push_back(
966 std::make_tuple(std::get<0>(params), std::get<1>(params), "true"));
967 kCsdFlushTestParameters.push_back(
968 std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
969 }
970
971 // Set the resource directory based on command line args.
972 // Test will fail to set up if the argument is not set.
973 for (int i = 1; i < argc; i++) {
974 if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
975 sResourceDir = argv[i + 1];
976 break;
977 }
978 }
979
980 ::testing::InitGoogleTest(&argc, argv);
981 return RUN_ALL_TESTS();
982 }
983