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_video_dec_test"
19
20 #include <android-base/logging.h>
21 #include <gtest/gtest.h>
22 #include <stdio.h>
23 #include <fstream>
24
25 #include <codec2/hidl/client.h>
26 #include <C2AllocatorIon.h>
27 #include <C2Config.h>
28 #include <C2Debug.h>
29 #include <C2Buffer.h>
30 #include <C2BufferPriv.h>
31
32 using android::C2AllocatorIon;
33
34 #include <VtsHalHidlTargetTestBase.h>
35 #include "media_c2_video_hidl_test_common.h"
36 #include "media_c2_hidl_test_common.h"
37
38 struct FrameInfo {
39 int bytesCount;
40 uint32_t flags;
41 int64_t timestamp;
42 };
43
44 class LinearBuffer : public C2Buffer {
45 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)46 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
47 : C2Buffer(
48 {block->share(block->offset(), block->size(), ::C2Fence())}) {}
49
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block,size_t size)50 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
51 : C2Buffer(
52 {block->share(block->offset(), size, ::C2Fence())}) {}
53 };
54
55 static ComponentTestEnvironment* gEnv = nullptr;
56
57 namespace {
58
59 class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
60 private:
61 typedef ::testing::VtsHalHidlTargetTestBase Super;
62
63 public:
getTestCaseInfo() const64 ::std::string getTestCaseInfo() const override {
65 return ::std::string() +
66 "Component: " + gEnv->getComponent().c_str() + " | " +
67 "Instance: " + gEnv->getInstance().c_str() + " | " +
68 "Res: " + gEnv->getRes().c_str();
69 }
70
71 // google.codec2 Video test setup
SetUp()72 virtual void SetUp() override {
73 Super::SetUp();
74 mDisableTest = false;
75 ALOGV("Codec2VideoDecHidlTest SetUp");
76 mClient = android::Codec2Client::CreateFromService(
77 gEnv->getInstance().c_str());
78 ASSERT_NE(mClient, nullptr);
79 mListener.reset(new CodecListener(
80 [this](std::list<std::unique_ptr<C2Work>>& workItems) {
81 handleWorkDone(workItems);
82 }));
83 ASSERT_NE(mListener, nullptr);
84 for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
85 mWorkQueue.emplace_back(new C2Work);
86 }
87 mClient->createComponent(gEnv->getComponent().c_str(), mListener,
88 &mComponent);
89 ASSERT_NE(mComponent, nullptr);
90
91 std::shared_ptr<C2AllocatorStore> store =
92 android::GetCodec2PlatformAllocatorStore();
93 CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
94 &mLinearAllocator),
95 C2_OK);
96 mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator,
97 mBlockPoolId++);
98 ASSERT_NE(mLinearPool, nullptr);
99
100 mCompName = unknown_comp;
101 struct StringToName {
102 const char* Name;
103 standardComp CompName;
104 };
105
106 const StringToName kStringToName[] = {
107 {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
108 {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
109 };
110
111 const size_t kNumStringToName =
112 sizeof(kStringToName) / sizeof(kStringToName[0]);
113
114 // Find the component type
115 std::string comp = std::string(gEnv->getComponent());
116 for (size_t i = 0; i < kNumStringToName; ++i) {
117 if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
118 mCompName = kStringToName[i].CompName;
119 break;
120 }
121 }
122 mEos = false;
123 mFramesReceived = 0;
124 mTimestampUs = 0u;
125 mTimestampDevTest = false;
126 if (mCompName == unknown_comp) mDisableTest = true;
127
128 C2SecureModeTuning secureModeTuning{};
129 mComponent->query({ &secureModeTuning }, {}, C2_MAY_BLOCK, nullptr);
130 if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
131 mDisableTest = true;
132 }
133
134 if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
135 }
136
TearDown()137 virtual void TearDown() override {
138 if (mComponent != nullptr) {
139 if (::testing::Test::HasFatalFailure()) return;
140 mComponent->release();
141 mComponent = nullptr;
142 }
143 Super::TearDown();
144 }
145
146 // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)147 void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
148 for (std::unique_ptr<C2Work>& work : workItems) {
149 if (!work->worklets.empty()) {
150 // For decoder components current timestamp always exceeds
151 // previous timestamp
152 typedef std::unique_lock<std::mutex> ULock;
153 bool codecConfig = ((work->worklets.front()->output.flags &
154 C2FrameData::FLAG_CODEC_CONFIG) != 0);
155 if (!codecConfig &&
156 !work->worklets.front()->output.buffers.empty()) {
157 EXPECT_GE(
158 (work->worklets.front()->output.ordinal.timestamp.peeku()),
159 mTimestampUs);
160 mTimestampUs =
161 work->worklets.front()->output.ordinal.timestamp.peeku();
162
163 ULock l(mQueueLock);
164 if (mTimestampDevTest) {
165 bool tsHit = false;
166 std::list<uint64_t>::iterator it = mTimestampUslist.begin();
167 while (it != mTimestampUslist.end()) {
168 if (*it == mTimestampUs) {
169 mTimestampUslist.erase(it);
170 tsHit = true;
171 break;
172 }
173 it++;
174 }
175 if (tsHit == false) {
176 if (mTimestampUslist.empty() == false) {
177 EXPECT_EQ(tsHit, true)
178 << "TimeStamp not recognized";
179 } else {
180 std::cout << "[ INFO ] Received non-zero "
181 "output / TimeStamp not recognized \n";
182 }
183 }
184 }
185 }
186 bool mCsd;
187 workDone(mComponent, work, mFlushedIndices, mQueueLock,
188 mQueueCondition, mWorkQueue, mEos, mCsd,
189 mFramesReceived);
190 (void)mCsd;
191 }
192 }
193 }
194
195 enum standardComp {
196 h263,
197 avc,
198 mpeg2,
199 mpeg4,
200 hevc,
201 vp8,
202 vp9,
203 av1,
204 unknown_comp,
205 };
206
207 bool mEos;
208 bool mDisableTest;
209 bool mTimestampDevTest;
210 uint64_t mTimestampUs;
211 std::list<uint64_t> mTimestampUslist;
212 std::list<uint64_t> mFlushedIndices;
213 standardComp mCompName;
214 uint32_t mFramesReceived;
215 C2BlockPool::local_id_t mBlockPoolId;
216 std::shared_ptr<C2BlockPool> mLinearPool;
217 std::shared_ptr<C2Allocator> mLinearAllocator;
218
219 std::mutex mQueueLock;
220 std::condition_variable mQueueCondition;
221 std::list<std::unique_ptr<C2Work>> mWorkQueue;
222
223 std::shared_ptr<android::Codec2Client> mClient;
224 std::shared_ptr<android::Codec2Client::Listener> mListener;
225 std::shared_ptr<android::Codec2Client::Component> mComponent;
226
227 protected:
description(const std::string & description)228 static void description(const std::string& description) {
229 RecordProperty("description", description);
230 }
231 };
232
validateComponent(const std::shared_ptr<android::Codec2Client::Component> & component,Codec2VideoDecHidlTest::standardComp compName,bool & disableTest)233 void validateComponent(
234 const std::shared_ptr<android::Codec2Client::Component>& component,
235 Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
236 // Validate its a C2 Component
237 if (component->getName().find("c2") == std::string::npos) {
238 ALOGE("Not a c2 component");
239 disableTest = true;
240 return;
241 }
242
243 // Validate its not an encoder and the component to be tested is video
244 if (component->getName().find("encoder") != std::string::npos) {
245 ALOGE("Expected Decoder, given Encoder");
246 disableTest = true;
247 return;
248 }
249 std::vector<std::unique_ptr<C2Param>> queried;
250 c2_status_t c2err =
251 component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
252 C2_DONT_BLOCK, &queried);
253 if (c2err != C2_OK && queried.size() == 0) {
254 ALOGE("Query media type failed => %d", c2err);
255 } else {
256 std::string inputDomain =
257 ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
258 if (inputDomain.find("video/") == std::string::npos) {
259 ALOGE("Expected Video Component");
260 disableTest = true;
261 return;
262 }
263 }
264
265 // Validates component name
266 if (compName == Codec2VideoDecHidlTest::unknown_comp) {
267 ALOGE("Component InValid");
268 disableTest = true;
269 return;
270 }
271 ALOGV("Component Valid");
272 }
273
274 // number of elementary streams per component
275 #define STREAM_COUNT 2
276 // LookUpTable of clips and metadata for component testing
GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp,char * mURL,char * info,size_t streamIndex=1)277 void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL,
278 char* info, size_t streamIndex = 1) {
279 struct CompToURL {
280 Codec2VideoDecHidlTest::standardComp comp;
281 const char mURL[STREAM_COUNT][512];
282 const char info[STREAM_COUNT][512];
283 };
284 ASSERT_TRUE(streamIndex < STREAM_COUNT);
285
286 static const CompToURL kCompToURL[] = {
287 {Codec2VideoDecHidlTest::standardComp::avc,
288 {"bbb_avc_176x144_300kbps_60fps.h264",
289 "bbb_avc_640x360_768kbps_30fps.h264"},
290 {"bbb_avc_176x144_300kbps_60fps.info",
291 "bbb_avc_640x360_768kbps_30fps.info"}},
292 {Codec2VideoDecHidlTest::standardComp::hevc,
293 {"bbb_hevc_176x144_176kbps_60fps.hevc",
294 "bbb_hevc_640x360_1600kbps_30fps.hevc"},
295 {"bbb_hevc_176x144_176kbps_60fps.info",
296 "bbb_hevc_640x360_1600kbps_30fps.info"}},
297 {Codec2VideoDecHidlTest::standardComp::mpeg2,
298 {"bbb_mpeg2_176x144_105kbps_25fps.m2v",
299 "bbb_mpeg2_352x288_1mbps_60fps.m2v"},
300 {"bbb_mpeg2_176x144_105kbps_25fps.info",
301 "bbb_mpeg2_352x288_1mbps_60fps.info"}},
302 {Codec2VideoDecHidlTest::standardComp::h263,
303 {"", "bbb_h263_352x288_300kbps_12fps.h263"},
304 {"", "bbb_h263_352x288_300kbps_12fps.info"}},
305 {Codec2VideoDecHidlTest::standardComp::mpeg4,
306 {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v"},
307 {"", "bbb_mpeg4_352x288_512kbps_30fps.info"}},
308 {Codec2VideoDecHidlTest::standardComp::vp8,
309 {"bbb_vp8_176x144_240kbps_60fps.vp8",
310 "bbb_vp8_640x360_2mbps_30fps.vp8"},
311 {"bbb_vp8_176x144_240kbps_60fps.info",
312 "bbb_vp8_640x360_2mbps_30fps.info"}},
313 {Codec2VideoDecHidlTest::standardComp::vp9,
314 {"bbb_vp9_176x144_285kbps_60fps.vp9",
315 "bbb_vp9_640x360_1600kbps_30fps.vp9"},
316 {"bbb_vp9_176x144_285kbps_60fps.info",
317 "bbb_vp9_640x360_1600kbps_30fps.info"}},
318 {Codec2VideoDecHidlTest::standardComp::av1,
319 {"bbb_av1_640_360.av1",
320 "bbb_av1_176_144.av1"},
321 {"bbb_av1_640_360.info",
322 "bbb_av1_176_144.info"}},
323 };
324
325 for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
326 if (kCompToURL[i].comp == comp) {
327 strcat(mURL, kCompToURL[i].mURL[streamIndex]);
328 strcat(info, kCompToURL[i].info[streamIndex]);
329 return;
330 }
331 }
332 }
333
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)334 void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
335 std::mutex &queueLock, std::condition_variable& queueCondition,
336 std::list<std::unique_ptr<C2Work>>& workQueue,
337 std::list<uint64_t>& flushedIndices,
338 std::shared_ptr<C2BlockPool>& linearPool,
339 std::ifstream& eleStream,
340 android::Vector<FrameInfo>* Info,
341 int offset, int range, bool signalEOS = true) {
342 typedef std::unique_lock<std::mutex> ULock;
343 int frameID = offset;
344 int maxRetry = 0;
345 while (1) {
346 if (frameID == (int)Info->size() || frameID == (offset + range)) break;
347 uint32_t flags = 0;
348 std::unique_ptr<C2Work> work;
349 // Prepare C2Work
350 while (!work && (maxRetry < MAX_RETRY)) {
351 ULock l(queueLock);
352 if (!workQueue.empty()) {
353 work.swap(workQueue.front());
354 workQueue.pop_front();
355 } else {
356 queueCondition.wait_for(l, TIME_OUT);
357 maxRetry++;
358 }
359 }
360 if (!work && (maxRetry >= MAX_RETRY)) {
361 ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
362 }
363 int64_t timestamp = (*Info)[frameID].timestamp;
364 if ((*Info)[frameID].flags) flags = (1 << ((*Info)[frameID].flags - 1));
365 if (signalEOS && ((frameID == (int)Info->size() - 1) ||
366 (frameID == (offset + range - 1))))
367 flags |= C2FrameData::FLAG_END_OF_STREAM;
368
369 work->input.flags = (C2FrameData::flags_t)flags;
370 work->input.ordinal.timestamp = timestamp;
371 work->input.ordinal.frameIndex = frameID;
372 {
373 ULock l(queueLock);
374 flushedIndices.emplace_back(frameID);
375 }
376
377 int size = (*Info)[frameID].bytesCount;
378 char* data = (char*)malloc(size);
379 ASSERT_NE(data, nullptr);
380
381 eleStream.read(data, size);
382 ASSERT_EQ(eleStream.gcount(), size);
383
384 work->input.buffers.clear();
385 auto alignedSize = ALIGN(size, PAGE_SIZE);
386 if (size) {
387 std::shared_ptr<C2LinearBlock> block;
388 ASSERT_EQ(C2_OK,
389 linearPool->fetchLinearBlock(
390 alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
391 &block));
392 ASSERT_TRUE(block);
393
394 // Write View
395 C2WriteView view = block->map().get();
396 if (view.error() != C2_OK) {
397 fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
398 break;
399 }
400 ASSERT_EQ((size_t)alignedSize, view.capacity());
401 ASSERT_EQ(0u, view.offset());
402 ASSERT_EQ((size_t)alignedSize, view.size());
403
404 memcpy(view.base(), data, size);
405
406 work->input.buffers.emplace_back(new LinearBuffer(block, size));
407 free(data);
408 }
409 work->worklets.clear();
410 work->worklets.emplace_back(new C2Worklet);
411 std::list<std::unique_ptr<C2Work>> items;
412 items.push_back(std::move(work));
413
414 // DO THE DECODING
415 ASSERT_EQ(component->queue(&items), C2_OK);
416 ALOGV("Frame #%d size = %d queued", frameID, size);
417 frameID++;
418 maxRetry = 0;
419 }
420 }
421
TEST_F(Codec2VideoDecHidlTest,validateCompName)422 TEST_F(Codec2VideoDecHidlTest, validateCompName) {
423 if (mDisableTest) return;
424 ALOGV("Checks if the given component is a valid video component");
425 validateComponent(mComponent, mCompName, mDisableTest);
426 ASSERT_EQ(mDisableTest, false);
427 }
428
429 class Codec2VideoDecDecodeTest
430 : public Codec2VideoDecHidlTest,
431 public ::testing::WithParamInterface<std::pair<int32_t, bool>> {
432 };
433
434 // Bitstream Test
TEST_P(Codec2VideoDecDecodeTest,DecodeTest)435 TEST_P(Codec2VideoDecDecodeTest, DecodeTest) {
436 description("Decodes input file");
437 if (mDisableTest) return;
438
439 uint32_t streamIndex = GetParam().first;
440 bool signalEOS = GetParam().second;
441 char mURL[512], info[512];
442 std::ifstream eleStream, eleInfo;
443 strcpy(mURL, gEnv->getRes().c_str());
444 strcpy(info, gEnv->getRes().c_str());
445 GetURLForComponent(mCompName, mURL, info, streamIndex);
446
447 eleInfo.open(info);
448 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
449 android::Vector<FrameInfo> Info;
450 int bytesCount = 0;
451 uint32_t flags = 0;
452 uint32_t timestamp = 0;
453 mTimestampDevTest = true;
454 mFlushedIndices.clear();
455 mTimestampUslist.clear();
456 while (1) {
457 if (!(eleInfo >> bytesCount)) break;
458 eleInfo >> flags;
459 eleInfo >> timestamp;
460 bool codecConfig = flags ?
461 ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
462 if (mTimestampDevTest && !codecConfig)
463 mTimestampUslist.push_back(timestamp);
464 Info.push_back({bytesCount, flags, timestamp});
465 }
466 eleInfo.close();
467
468 ASSERT_EQ(mComponent->start(), C2_OK);
469 // Reset total no of frames received
470 mFramesReceived = 0;
471 mTimestampUs = 0;
472 ALOGV("mURL : %s", mURL);
473 eleStream.open(mURL, std::ifstream::binary);
474 ASSERT_EQ(eleStream.is_open(), true);
475 ASSERT_NO_FATAL_FAILURE(decodeNFrames(
476 mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
477 mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS));
478
479 // If EOS is not sent, sending empty input with EOS flag
480 size_t infoSize = Info.size();
481 if (!signalEOS) {
482 ASSERT_NO_FATAL_FAILURE(
483 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
484 ASSERT_NO_FATAL_FAILURE(
485 testInputBuffer(mComponent, mQueueLock, mWorkQueue,
486 C2FrameData::FLAG_END_OF_STREAM, false));
487 infoSize += 1;
488 }
489 // blocking call to ensures application to Wait till all the inputs are
490 // consumed
491 if (!mEos) {
492 ALOGV("Waiting for input consumption");
493 ASSERT_NO_FATAL_FAILURE(
494 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
495 }
496
497 eleStream.close();
498 if (mFramesReceived != infoSize) {
499 ALOGE("Input buffer count and Output buffer count mismatch");
500 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
501 infoSize);
502 ASSERT_TRUE(false);
503 }
504
505 if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
506 ASSERT_EQ(mComponent->stop(), C2_OK);
507 }
508 // DecodeTest with StreamIndex and EOS / No EOS
509 INSTANTIATE_TEST_CASE_P(StreamIndexAndEOS, Codec2VideoDecDecodeTest,
510 ::testing::Values(std::make_pair(0, false),
511 std::make_pair(0, true),
512 std::make_pair(1, false),
513 std::make_pair(1, true)));
514
515 // Adaptive Test
TEST_F(Codec2VideoDecHidlTest,AdaptiveDecodeTest)516 TEST_F(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
517 description("Adaptive Decode Test");
518 if (mDisableTest) return;
519 if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 ||
520 mCompName == vp9 || mCompName == mpeg2))
521 return;
522
523 typedef std::unique_lock<std::mutex> ULock;
524 ASSERT_EQ(mComponent->start(), C2_OK);
525
526 mTimestampDevTest = true;
527 uint32_t timestampOffset = 0;
528 uint32_t offset = 0;
529 android::Vector<FrameInfo> Info;
530 for (uint32_t i = 0; i < STREAM_COUNT * 2; i++) {
531 char mURL[512], info[512];
532 std::ifstream eleStream, eleInfo;
533
534 strcpy(mURL, gEnv->getRes().c_str());
535 strcpy(info, gEnv->getRes().c_str());
536 GetURLForComponent(mCompName, mURL, info, i % STREAM_COUNT);
537
538 eleInfo.open(info);
539 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
540 int bytesCount = 0;
541 uint32_t flags = 0;
542 uint32_t timestamp = 0;
543 uint32_t timestampMax = 0;
544 while (1) {
545 if (!(eleInfo >> bytesCount)) break;
546 eleInfo >> flags;
547 eleInfo >> timestamp;
548 timestamp += timestampOffset;
549 Info.push_back({bytesCount, flags, timestamp});
550 bool codecConfig = flags ?
551 ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
552
553 {
554 ULock l(mQueueLock);
555 if (mTimestampDevTest && !codecConfig)
556 mTimestampUslist.push_back(timestamp);
557 }
558 if (timestampMax < timestamp) timestampMax = timestamp;
559 }
560 timestampOffset = timestampMax;
561 eleInfo.close();
562
563 // Reset Total frames before second decode loop
564 // mFramesReceived = 0;
565 ALOGV("mURL : %s", mURL);
566 eleStream.open(mURL, std::ifstream::binary);
567 ASSERT_EQ(eleStream.is_open(), true);
568 ASSERT_NO_FATAL_FAILURE(
569 decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
570 mFlushedIndices, mLinearPool, eleStream, &Info,
571 offset, (int)(Info.size() - offset), false));
572
573 eleStream.close();
574 offset = (int)Info.size();
575 }
576
577 // Send EOS
578 // TODO Add function to send EOS work item
579 int maxRetry = 0;
580 std::unique_ptr<C2Work> work;
581 while (!work && (maxRetry < MAX_RETRY)) {
582 ULock l(mQueueLock);
583 if (!mWorkQueue.empty()) {
584 work.swap(mWorkQueue.front());
585 mWorkQueue.pop_front();
586 } else {
587 mQueueCondition.wait_for(l, TIME_OUT);
588 maxRetry++;
589 }
590 }
591 ASSERT_NE(work, nullptr);
592 work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
593 work->input.ordinal.timestamp = 0;
594 work->input.ordinal.frameIndex = 0;
595 work->input.buffers.clear();
596 work->worklets.clear();
597 work->worklets.emplace_back(new C2Worklet);
598
599 std::list<std::unique_ptr<C2Work>> items;
600 items.push_back(std::move(work));
601 ASSERT_EQ(mComponent->queue(&items), C2_OK);
602
603 // blocking call to ensures application to Wait till all the inputs are
604 // consumed
605 ALOGV("Waiting for input consumption");
606 ASSERT_NO_FATAL_FAILURE(
607 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
608
609 if (mFramesReceived != ((Info.size()) + 1)) {
610 ALOGE("Input buffer count and Output buffer count mismatch");
611 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
612 Info.size() + 1);
613 ASSERT_TRUE(false);
614 }
615
616 if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
617 }
618
619 // thumbnail test
TEST_F(Codec2VideoDecHidlTest,ThumbnailTest)620 TEST_F(Codec2VideoDecHidlTest, ThumbnailTest) {
621 description("Test Request for thumbnail");
622 if (mDisableTest) return;
623
624 char mURL[512], info[512];
625 std::ifstream eleStream, eleInfo;
626
627 strcpy(mURL, gEnv->getRes().c_str());
628 strcpy(info, gEnv->getRes().c_str());
629 GetURLForComponent(mCompName, mURL, info);
630
631 eleInfo.open(info);
632 ASSERT_EQ(eleInfo.is_open(), true);
633 android::Vector<FrameInfo> Info;
634 int bytesCount = 0;
635 uint32_t flags = 0;
636 uint32_t timestamp = 0;
637 while (1) {
638 if (!(eleInfo >> bytesCount)) break;
639 eleInfo >> flags;
640 eleInfo >> timestamp;
641 Info.push_back({bytesCount, flags, timestamp});
642 }
643 eleInfo.close();
644
645 for (size_t i = 0; i < MAX_ITERATIONS; i++) {
646 ASSERT_EQ(mComponent->start(), C2_OK);
647
648 // request EOS for thumbnail
649 // signal EOS flag with last frame
650 size_t j = -1;
651 do {
652 j++;
653 flags = 0;
654 if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
655
656 } while (!(flags & SYNC_FRAME));
657 eleStream.open(mURL, std::ifstream::binary);
658 ASSERT_EQ(eleStream.is_open(), true);
659 ASSERT_NO_FATAL_FAILURE(decodeNFrames(
660 mComponent, mQueueLock, mQueueCondition, mWorkQueue,
661 mFlushedIndices, mLinearPool, eleStream, &Info, 0, j + 1));
662 ASSERT_NO_FATAL_FAILURE(
663 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
664 eleStream.close();
665 EXPECT_GE(mFramesReceived, 1U);
666 ASSERT_EQ(mEos, true);
667 ASSERT_EQ(mComponent->stop(), C2_OK);
668 }
669 ASSERT_EQ(mComponent->release(), C2_OK);
670 }
671
TEST_F(Codec2VideoDecHidlTest,EOSTest)672 TEST_F(Codec2VideoDecHidlTest, EOSTest) {
673 description("Test empty input buffer with EOS flag");
674 if (mDisableTest) return;
675 typedef std::unique_lock<std::mutex> ULock;
676 ASSERT_EQ(mComponent->start(), C2_OK);
677 std::unique_ptr<C2Work> work;
678 // Prepare C2Work
679 {
680 ULock l(mQueueLock);
681 if (!mWorkQueue.empty()) {
682 work.swap(mWorkQueue.front());
683 mWorkQueue.pop_front();
684 } else {
685 ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test";
686 }
687 }
688 ASSERT_NE(work, nullptr);
689
690 work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
691 work->input.ordinal.timestamp = 0;
692 work->input.ordinal.frameIndex = 0;
693 work->input.buffers.clear();
694 work->worklets.clear();
695 work->worklets.emplace_back(new C2Worklet);
696
697 std::list<std::unique_ptr<C2Work>> items;
698 items.push_back(std::move(work));
699 ASSERT_EQ(mComponent->queue(&items), C2_OK);
700
701 {
702 ULock l(mQueueLock);
703 if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
704 mQueueCondition.wait_for(l, TIME_OUT);
705 }
706 }
707 ASSERT_EQ(mEos, true);
708 ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
709 ASSERT_EQ(mComponent->stop(), C2_OK);
710 }
711
TEST_F(Codec2VideoDecHidlTest,FlushTest)712 TEST_F(Codec2VideoDecHidlTest, FlushTest) {
713 description("Tests Flush calls");
714 if (mDisableTest) return;
715 typedef std::unique_lock<std::mutex> ULock;
716 ASSERT_EQ(mComponent->start(), C2_OK);
717 char mURL[512], info[512];
718 std::ifstream eleStream, eleInfo;
719
720 strcpy(mURL, gEnv->getRes().c_str());
721 strcpy(info, gEnv->getRes().c_str());
722 GetURLForComponent(mCompName, mURL, info);
723
724 eleInfo.open(info);
725 ASSERT_EQ(eleInfo.is_open(), true);
726 android::Vector<FrameInfo> Info;
727 int bytesCount = 0;
728 uint32_t flags = 0;
729 uint32_t timestamp = 0;
730 mFlushedIndices.clear();
731 while (1) {
732 if (!(eleInfo >> bytesCount)) break;
733 eleInfo >> flags;
734 eleInfo >> timestamp;
735 Info.push_back({bytesCount, flags, timestamp});
736 }
737 eleInfo.close();
738
739 ALOGV("mURL : %s", mURL);
740 eleStream.open(mURL, std::ifstream::binary);
741 ASSERT_EQ(eleStream.is_open(), true);
742 // Decode 128 frames and flush. here 128 is chosen to ensure there is a key
743 // frame after this so that the below section can be covered for all
744 // components
745 uint32_t numFramesFlushed = 128;
746 ASSERT_NO_FATAL_FAILURE(decodeNFrames(
747 mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
748 mLinearPool, eleStream, &Info, 0, numFramesFlushed, false));
749 // flush
750 std::list<std::unique_ptr<C2Work>> flushedWork;
751 c2_status_t err =
752 mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
753 ASSERT_EQ(err, C2_OK);
754 ASSERT_NO_FATAL_FAILURE(
755 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
756 (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
757
758 {
759 // Update mFlushedIndices based on the index received from flush()
760 ULock l(mQueueLock);
761 for (std::unique_ptr<C2Work>& work : flushedWork) {
762 ASSERT_NE(work, nullptr);
763 auto frameIndexIt =
764 std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
765 work->input.ordinal.frameIndex.peeku());
766 if (!mFlushedIndices.empty() &&
767 (frameIndexIt != mFlushedIndices.end())) {
768 mFlushedIndices.erase(frameIndexIt);
769 work->input.buffers.clear();
770 work->worklets.clear();
771 mWorkQueue.push_back(std::move(work));
772 }
773 }
774 }
775 // Seek to next key frame and start decoding till the end
776 mFlushedIndices.clear();
777 int index = numFramesFlushed;
778 bool keyFrame = false;
779 flags = 0;
780 while (index < (int)Info.size()) {
781 if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
782 if ((flags & SYNC_FRAME) == SYNC_FRAME) {
783 keyFrame = true;
784 break;
785 }
786 flags = 0;
787 eleStream.ignore(Info[index].bytesCount);
788 index++;
789 }
790 if (keyFrame) {
791 ASSERT_NO_FATAL_FAILURE(
792 decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
793 mFlushedIndices, mLinearPool, eleStream, &Info, index,
794 (int)Info.size() - index));
795 }
796 eleStream.close();
797 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
798 ASSERT_EQ(err, C2_OK);
799 ASSERT_NO_FATAL_FAILURE(
800 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
801 (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
802 {
803 // Update mFlushedIndices based on the index received from flush()
804 ULock l(mQueueLock);
805 for (std::unique_ptr<C2Work>& work : flushedWork) {
806 ASSERT_NE(work, nullptr);
807 uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
808 std::list<uint64_t>::iterator frameIndexIt = std::find(
809 mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
810 if (!mFlushedIndices.empty() &&
811 (frameIndexIt != mFlushedIndices.end())) {
812 mFlushedIndices.erase(frameIndexIt);
813 work->input.buffers.clear();
814 work->worklets.clear();
815 mWorkQueue.push_back(std::move(work));
816 }
817 }
818 }
819 ASSERT_EQ(mFlushedIndices.empty(), true);
820 ASSERT_EQ(mComponent->stop(), C2_OK);
821 }
822
TEST_F(Codec2VideoDecHidlTest,DecodeTestEmptyBuffersInserted)823 TEST_F(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) {
824 description("Decode with multiple empty input frames");
825 if (mDisableTest) return;
826
827 char mURL[512], info[512];
828 std::ifstream eleStream, eleInfo;
829
830 strcpy(mURL, gEnv->getRes().c_str());
831 strcpy(info, gEnv->getRes().c_str());
832 GetURLForComponent(mCompName, mURL, info);
833
834 eleInfo.open(info);
835 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
836 android::Vector<FrameInfo> Info;
837 int bytesCount = 0;
838 uint32_t frameId = 0;
839 uint32_t flags = 0;
840 uint32_t timestamp = 0;
841 bool codecConfig = false;
842 // This test introduces empty CSD after every 20th frame
843 // and empty input frames at an interval of 5 frames.
844 while (1) {
845 if (!(frameId % 5)) {
846 if (!(frameId % 20)) flags = 32;
847 else flags = 0;
848 bytesCount = 0;
849 } else {
850 if (!(eleInfo >> bytesCount)) break;
851 eleInfo >> flags;
852 eleInfo >> timestamp;
853 codecConfig = flags ?
854 ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
855 }
856 Info.push_back({bytesCount, flags, timestamp});
857 frameId++;
858 }
859 eleInfo.close();
860
861 ASSERT_EQ(mComponent->start(), C2_OK);
862 ALOGV("mURL : %s", mURL);
863 eleStream.open(mURL, std::ifstream::binary);
864 ASSERT_EQ(eleStream.is_open(), true);
865 ASSERT_NO_FATAL_FAILURE(decodeNFrames(
866 mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
867 mLinearPool, eleStream, &Info, 0, (int)Info.size()));
868
869 // blocking call to ensures application to Wait till all the inputs are
870 // consumed
871 if (!mEos) {
872 ALOGV("Waiting for input consumption");
873 ASSERT_NO_FATAL_FAILURE(
874 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
875 }
876
877 eleStream.close();
878 if (mFramesReceived != Info.size()) {
879 ALOGE("Input buffer count and Output buffer count mismatch");
880 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
881 Info.size());
882 ASSERT_TRUE(false);
883 }
884 }
885
886 } // anonymous namespace
887
888 // TODO : Video specific configuration Test
main(int argc,char ** argv)889 int main(int argc, char** argv) {
890 gEnv = new ComponentTestEnvironment();
891 ::testing::AddGlobalTestEnvironment(gEnv);
892 ::testing::InitGoogleTest(&argc, argv);
893 gEnv->init(&argc, argv);
894 int status = gEnv->initFromOptions(argc, argv);
895 if (status == 0) {
896 int status = RUN_ALL_TESTS();
897 LOG(INFO) << "C2 Test result = " << status;
898 }
899 return status;
900 }
901