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 <hidl/GtestPrinter.h>
23 #include <stdio.h>
24
25 #include <openssl/md5.h>
26
27 #include <C2AllocatorIon.h>
28 #include <C2Buffer.h>
29 #include <C2BufferPriv.h>
30 #include <C2Config.h>
31 #include <C2Debug.h>
32 #include <codec2/hidl/client.h>
33 #include <gui/BufferQueue.h>
34 #include <gui/IConsumerListener.h>
35 #include <gui/IProducerListener.h>
36 #include <system/window.h>
37
38 using android::C2AllocatorIon;
39
40 #include "media_c2_hidl_test_common.h"
41 #include "media_c2_video_hidl_test_common.h"
42
43 static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
44 kDecodeTestParameters;
45
46 static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
47
48 // Resource directory
49 static std::string sResourceDir = "";
50
51 class LinearBuffer : public C2Buffer {
52 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)53 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
54 : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
55
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block,size_t size)56 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
57 : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
58 };
59
60 namespace {
61
62 class Codec2VideoDecHidlTestBase : public ::testing::Test {
63 public:
64 // google.codec2 Video test setup
SetUp()65 virtual void SetUp() override {
66 getParams();
67 mDisableTest = false;
68 ALOGV("Codec2VideoDecHidlTest SetUp");
69 mClient = android::Codec2Client::CreateFromService(
70 mInstanceName.c_str(),
71 !bool(android::Codec2Client::CreateFromService("default", true)));
72 ASSERT_NE(mClient, nullptr);
73 mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
74 handleWorkDone(workItems);
75 }));
76 ASSERT_NE(mListener, nullptr);
77 for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
78 mWorkQueue.emplace_back(new C2Work);
79 }
80 mClient->createComponent(mComponentName, mListener, &mComponent);
81 ASSERT_NE(mComponent, nullptr);
82
83 std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
84 CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
85 mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
86 ASSERT_NE(mLinearPool, nullptr);
87
88 mCompName = unknown_comp;
89 struct StringToName {
90 const char* Name;
91 standardComp CompName;
92 };
93
94 const StringToName kStringToName[] = {
95 {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
96 {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
97 };
98
99 const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
100
101 // Find the component type
102 for (size_t i = 0; i < kNumStringToName; ++i) {
103 if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
104 mCompName = kStringToName[i].CompName;
105 break;
106 }
107 }
108 mEos = false;
109 mFramesReceived = 0;
110 mTimestampUs = 0u;
111 mWorkResult = C2_OK;
112 mReorderDepth = -1;
113 mTimestampDevTest = false;
114 mMd5Offset = 0;
115 mMd5Enable = false;
116 mRefMd5 = nullptr;
117 if (mCompName == unknown_comp) mDisableTest = true;
118
119 C2SecureModeTuning secureModeTuning{};
120 mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
121 if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
122 mDisableTest = true;
123 }
124
125 if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
126 }
127
TearDown()128 virtual void TearDown() override {
129 if (mComponent != nullptr) {
130 if (::testing::Test::HasFatalFailure()) return;
131 mComponent->release();
132 mComponent = nullptr;
133 }
134 }
135
136 // Get the test parameters from GetParam call.
getParams()137 virtual void getParams() {}
138
139 /* Calculate the CKSUM for the data in inbuf */
calc_md5_cksum(uint8_t * pu1_inbuf,uint32_t u4_stride,uint32_t u4_width,uint32_t u4_height,uint8_t * pu1_cksum_p)140 void calc_md5_cksum(uint8_t* pu1_inbuf, uint32_t u4_stride, uint32_t u4_width,
141 uint32_t u4_height, uint8_t* pu1_cksum_p) {
142 int32_t row;
143 MD5_CTX s_md5_context;
144 MD5_Init(&s_md5_context);
145 for (row = 0; row < u4_height; row++) {
146 MD5_Update(&s_md5_context, pu1_inbuf, u4_width);
147 pu1_inbuf += u4_stride;
148 }
149 MD5_Final(pu1_cksum_p, &s_md5_context);
150 }
151
compareMd5Chksm(std::unique_ptr<C2Work> & work)152 void compareMd5Chksm(std::unique_ptr<C2Work>& work) {
153 uint8_t chksum[48];
154 uint8_t* au1_y_chksum = chksum;
155 uint8_t* au1_u_chksum = chksum + 16;
156 uint8_t* au1_v_chksum = chksum + 32;
157 const C2GraphicView output = work->worklets.front()
158 ->output.buffers[0]
159 ->data()
160 .graphicBlocks()
161 .front()
162 .map()
163 .get();
164 uint8_t* yPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_Y]);
165 uint8_t* uPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_U]);
166 uint8_t* vPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_V]);
167 C2PlanarLayout layout = output.layout();
168
169 size_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
170 size_t uvStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
171 size_t colInc = layout.planes[C2PlanarLayout::PLANE_U].colInc;
172 size_t bitDepth = layout.planes[C2PlanarLayout::PLANE_Y].bitDepth;
173 uint32_t layoutType = layout.type;
174 size_t cropWidth = output.crop().width;
175 size_t cropHeight = output.crop().height;
176
177 if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 1) {
178 calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
179 calc_md5_cksum(uPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_u_chksum);
180 calc_md5_cksum(vPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_v_chksum);
181 } else if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 2) {
182 uint8_t* cbPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
183 uint8_t* crPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
184 ASSERT_NE(cbPlane, nullptr);
185 ASSERT_NE(crPlane, nullptr);
186 size_t count = 0;
187 for (size_t k = 0; k < (cropHeight / 2); k++) {
188 for (size_t l = 0; l < (cropWidth); l = l + 2) {
189 cbPlane[count] = uPlane[k * uvStride + l];
190 crPlane[count] = vPlane[k * uvStride + l];
191 count++;
192 }
193 }
194 calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
195 calc_md5_cksum(cbPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_u_chksum);
196 calc_md5_cksum(crPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_v_chksum);
197 free(cbPlane);
198 free(crPlane);
199 } else {
200 mMd5Enable = false;
201 ALOGV("Disabling MD5 chksm flag");
202 return;
203 }
204 if (memcmp(mRefMd5 + mMd5Offset, chksum, 48)) ASSERT_TRUE(false);
205 mMd5Offset += 48;
206 return;
207 }
208 bool configPixelFormat(uint32_t format);
209
210 // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)211 void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
212 for (std::unique_ptr<C2Work>& work : workItems) {
213 if (!work->worklets.empty()) {
214 // For decoder components current timestamp always exceeds
215 // previous timestamp if output is in display order
216 typedef std::unique_lock<std::mutex> ULock;
217 mWorkResult |= work->result;
218 bool codecConfig = ((work->worklets.front()->output.flags &
219 C2FrameData::FLAG_CODEC_CONFIG) != 0);
220 if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
221 if (mReorderDepth < 0) {
222 C2PortReorderBufferDepthTuning::output reorderBufferDepth;
223 mComponent->query({&reorderBufferDepth}, {}, C2_MAY_BLOCK,
224 nullptr);
225 mReorderDepth = reorderBufferDepth.value;
226 if (mReorderDepth > 0) {
227 // TODO: Add validation for reordered output
228 mTimestampDevTest = false;
229 }
230 }
231 if (mTimestampDevTest) {
232 EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
233 mTimestampUs);
234 mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
235
236 ULock l(mQueueLock);
237 {
238 bool tsHit = false;
239 std::list<uint64_t>::iterator it = mTimestampUslist.begin();
240 while (it != mTimestampUslist.end()) {
241 if (*it == mTimestampUs) {
242 mTimestampUslist.erase(it);
243 tsHit = true;
244 break;
245 }
246 it++;
247 }
248 if (tsHit == false) {
249 if (mTimestampUslist.empty() == false) {
250 EXPECT_EQ(tsHit, true) << "TimeStamp not recognized";
251 } else {
252 std::cout << "[ INFO ] Received non-zero "
253 "output / TimeStamp not recognized \n";
254 }
255 }
256 }
257 }
258 if (mMd5Enable) {
259 compareMd5Chksm(work);
260 }
261 }
262 bool mCsd = false;
263 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
264 mEos, mCsd, mFramesReceived);
265 (void)mCsd;
266 }
267 }
268 }
269
270 enum standardComp {
271 h263,
272 avc,
273 mpeg2,
274 mpeg4,
275 hevc,
276 vp8,
277 vp9,
278 av1,
279 unknown_comp,
280 };
281
282 std::string mInstanceName;
283 std::string mComponentName;
284
285 bool mEos;
286 bool mDisableTest;
287 bool mMd5Enable;
288 bool mTimestampDevTest;
289 uint64_t mTimestampUs;
290 uint64_t mMd5Offset;
291 char* mRefMd5;
292 std::list<uint64_t> mTimestampUslist;
293 std::list<uint64_t> mFlushedIndices;
294 standardComp mCompName;
295
296 int32_t mWorkResult;
297 int32_t mReorderDepth;
298 uint32_t mFramesReceived;
299 C2BlockPool::local_id_t mBlockPoolId;
300 std::shared_ptr<C2BlockPool> mLinearPool;
301 std::shared_ptr<C2Allocator> mLinearAllocator;
302
303 std::mutex mQueueLock;
304 std::condition_variable mQueueCondition;
305 std::list<std::unique_ptr<C2Work>> mWorkQueue;
306
307 std::shared_ptr<android::Codec2Client> mClient;
308 std::shared_ptr<android::Codec2Client::Listener> mListener;
309 std::shared_ptr<android::Codec2Client::Component> mComponent;
310
311 protected:
description(const std::string & description)312 static void description(const std::string& description) {
313 RecordProperty("description", description);
314 }
315 };
316
317 class Codec2VideoDecHidlTest
318 : public Codec2VideoDecHidlTestBase,
319 public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
getParams()320 void getParams() {
321 mInstanceName = std::get<0>(GetParam());
322 mComponentName = std::get<1>(GetParam());
323 }
324 };
325
validateComponent(const std::shared_ptr<android::Codec2Client::Component> & component,Codec2VideoDecHidlTest::standardComp compName,bool & disableTest)326 void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
327 Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
328 // Validate its a C2 Component
329 if (component->getName().find("c2") == std::string::npos) {
330 ALOGE("Not a c2 component");
331 disableTest = true;
332 return;
333 }
334
335 // Validate its not an encoder and the component to be tested is video
336 if (component->getName().find("encoder") != std::string::npos) {
337 ALOGE("Expected Decoder, given Encoder");
338 disableTest = true;
339 return;
340 }
341 std::vector<std::unique_ptr<C2Param>> queried;
342 c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
343 C2_DONT_BLOCK, &queried);
344 if (c2err != C2_OK && queried.size() == 0) {
345 ALOGE("Query media type failed => %d", c2err);
346 } else {
347 std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
348 if (inputDomain.find("video/") == std::string::npos) {
349 ALOGE("Expected Video Component");
350 disableTest = true;
351 return;
352 }
353 }
354
355 // Validates component name
356 if (compName == Codec2VideoDecHidlTest::unknown_comp) {
357 ALOGE("Component InValid");
358 disableTest = true;
359 return;
360 }
361 ALOGV("Component Valid");
362 }
363
364 // number of elementary streams per component
365 #define STREAM_COUNT 3
366 // LookUpTable of clips, metadata and chksum for component testing
GetURLChksmForComponent(Codec2VideoDecHidlTest::standardComp comp,char * mURL,char * info,char * chksum,size_t streamIndex=1)367 void GetURLChksmForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
368 char* chksum, size_t streamIndex = 1) {
369 struct CompToURL {
370 Codec2VideoDecHidlTest::standardComp comp;
371 const char mURL[STREAM_COUNT][512];
372 const char info[STREAM_COUNT][512];
373 const char chksum[STREAM_COUNT][512];
374 };
375 ASSERT_TRUE(streamIndex < STREAM_COUNT);
376
377 static const CompToURL kCompToURL[] = {
378 {Codec2VideoDecHidlTest::standardComp::avc,
379 {"bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_640x360_768kbps_30fps.h264", ""},
380 {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info", ""},
381 {"bbb_avc_176x144_300kbps_60fps_chksum.md5",
382 "bbb_avc_640x360_768kbps_30fps_chksum.md5", ""}},
383 {Codec2VideoDecHidlTest::standardComp::hevc,
384 {"bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.hevc", ""},
385 {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info", ""},
386 {"bbb_hevc_176x144_176kbps_60fps_chksum.md5",
387 "bbb_hevc_640x360_1600kbps_30fps_chksum.md5", ""}},
388 {Codec2VideoDecHidlTest::standardComp::mpeg2,
389 {"bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_352x288_1mbps_60fps.m2v", ""},
390 {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info", ""},
391 {"", "", ""}},
392 {Codec2VideoDecHidlTest::standardComp::h263,
393 {"", "bbb_h263_352x288_300kbps_12fps.h263", ""},
394 {"", "bbb_h263_352x288_300kbps_12fps.info", ""},
395 {"", "", ""}},
396 {Codec2VideoDecHidlTest::standardComp::mpeg4,
397 {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v", ""},
398 {"", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
399 {"", "", ""}},
400 {Codec2VideoDecHidlTest::standardComp::vp8,
401 {"bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_640x360_2mbps_30fps.vp8", ""},
402 {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info", ""},
403 {"", "bbb_vp8_640x360_2mbps_30fps_chksm.md5", ""}},
404 {Codec2VideoDecHidlTest::standardComp::vp9,
405 {"bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.vp9",
406 "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9"},
407 {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info",
408 "bbb_vp9_704x480_280kbps_24fps_altref_2.info"},
409 {"", "bbb_vp9_640x360_1600kbps_30fps_chksm.md5", ""}},
410 {Codec2VideoDecHidlTest::standardComp::av1,
411 {"bbb_av1_640_360.av1", "bbb_av1_176_144.av1", ""},
412 {"bbb_av1_640_360.info", "bbb_av1_176_144.info", ""},
413 {"bbb_av1_640_360_chksum.md5", "bbb_av1_176_144_chksm.md5", ""}},
414 };
415
416 for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
417 if (kCompToURL[i].comp == comp) {
418 strcat(mURL, kCompToURL[i].mURL[streamIndex]);
419 strcat(info, kCompToURL[i].info[streamIndex]);
420 strcat(chksum, kCompToURL[i].chksum[streamIndex]);
421 return;
422 }
423 }
424 }
425
GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp,char * mURL,char * info,size_t streamIndex=1)426 void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
427 size_t streamIndex = 1) {
428 char chksum[512];
429 strcpy(chksum, sResourceDir.c_str());
430 GetURLChksmForComponent(comp, mURL, info, chksum, streamIndex);
431 }
432
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)433 void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
434 std::mutex& queueLock, std::condition_variable& queueCondition,
435 std::list<std::unique_ptr<C2Work>>& workQueue,
436 std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& linearPool,
437 std::ifstream& eleStream, android::Vector<FrameInfo>* Info, int offset,
438 int range, bool signalEOS = true) {
439 typedef std::unique_lock<std::mutex> ULock;
440 int frameID = offset;
441 int maxRetry = 0;
442 while (1) {
443 if (frameID == (int)Info->size() || frameID == (offset + range)) break;
444 uint32_t flags = 0;
445 std::unique_ptr<C2Work> work;
446 // Prepare C2Work
447 while (!work && (maxRetry < MAX_RETRY)) {
448 ULock l(queueLock);
449 if (!workQueue.empty()) {
450 work.swap(workQueue.front());
451 workQueue.pop_front();
452 } else {
453 queueCondition.wait_for(l, TIME_OUT);
454 maxRetry++;
455 }
456 }
457 if (!work && (maxRetry >= MAX_RETRY)) {
458 ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
459 }
460 int64_t timestamp = (*Info)[frameID].timestamp;
461 if ((*Info)[frameID].flags) flags = (1 << ((*Info)[frameID].flags - 1));
462 if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
463 flags |= C2FrameData::FLAG_END_OF_STREAM;
464
465 work->input.flags = (C2FrameData::flags_t)flags;
466 work->input.ordinal.timestamp = timestamp;
467 work->input.ordinal.frameIndex = frameID;
468 {
469 ULock l(queueLock);
470 flushedIndices.emplace_back(frameID);
471 }
472
473 int size = (*Info)[frameID].bytesCount;
474 char* data = (char*)malloc(size);
475 ASSERT_NE(data, nullptr);
476
477 eleStream.read(data, size);
478 ASSERT_EQ(eleStream.gcount(), size);
479
480 work->input.buffers.clear();
481 auto alignedSize = ALIGN(size, PAGE_SIZE);
482 if (size) {
483 std::shared_ptr<C2LinearBlock> block;
484 ASSERT_EQ(C2_OK, linearPool->fetchLinearBlock(
485 alignedSize,
486 {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
487 ASSERT_TRUE(block);
488
489 // Write View
490 C2WriteView view = block->map().get();
491 if (view.error() != C2_OK) {
492 fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
493 break;
494 }
495 ASSERT_EQ((size_t)alignedSize, view.capacity());
496 ASSERT_EQ(0u, view.offset());
497 ASSERT_EQ((size_t)alignedSize, view.size());
498
499 memcpy(view.base(), data, size);
500
501 work->input.buffers.emplace_back(new LinearBuffer(block, size));
502 free(data);
503 }
504 work->worklets.clear();
505 work->worklets.emplace_back(new C2Worklet);
506 std::list<std::unique_ptr<C2Work>> items;
507 items.push_back(std::move(work));
508
509 // DO THE DECODING
510 ASSERT_EQ(component->queue(&items), C2_OK);
511 ALOGV("Frame #%d size = %d queued", frameID, size);
512 frameID++;
513 maxRetry = 0;
514 }
515 }
516
TEST_P(Codec2VideoDecHidlTest,validateCompName)517 TEST_P(Codec2VideoDecHidlTest, validateCompName) {
518 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
519 ALOGV("Checks if the given component is a valid video component");
520 validateComponent(mComponent, mCompName, mDisableTest);
521 ASSERT_EQ(mDisableTest, false);
522 }
523
TEST_P(Codec2VideoDecHidlTest,configureTunnel)524 TEST_P(Codec2VideoDecHidlTest, configureTunnel) {
525 description("Attempts to configure tunneling");
526 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
527 ALOGV("Checks if the component can be configured for tunneling");
528 native_handle_t* sidebandStream{};
529 c2_status_t err = mComponent->configureVideoTunnel(0, &sidebandStream);
530 if (err == C2_OMITTED) {
531 return;
532 }
533
534 using namespace android;
535 sp<NativeHandle> nativeHandle = NativeHandle::create(sidebandStream, true);
536
537 sp<IGraphicBufferProducer> producer;
538 sp<IGraphicBufferConsumer> consumer;
539 BufferQueue::createBufferQueue(&producer, &consumer);
540
541 class DummyConsumerListener : public BnConsumerListener {
542 public:
543 DummyConsumerListener() : BnConsumerListener() {}
544 void onFrameAvailable(const BufferItem&) override {}
545 void onBuffersReleased() override {}
546 void onSidebandStreamChanged() override {}
547 };
548 consumer->consumerConnect(new DummyConsumerListener(), false);
549
550 class DummyProducerListener : public BnProducerListener {
551 public:
552 DummyProducerListener() : BnProducerListener() {}
553 virtual void onBufferReleased() override {}
554 virtual bool needsReleaseNotify() override { return false; }
555 virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
556 };
557 IGraphicBufferProducer::QueueBufferOutput qbo{};
558 producer->connect(new DummyProducerListener(), NATIVE_WINDOW_API_MEDIA, false, &qbo);
559
560 ASSERT_EQ(producer->setSidebandStream(nativeHandle), NO_ERROR);
561 }
562
563 // Config output pixel format
configPixelFormat(uint32_t format)564 bool Codec2VideoDecHidlTestBase::configPixelFormat(uint32_t format) {
565 std::vector<std::unique_ptr<C2SettingResult>> failures;
566 C2StreamPixelFormatInfo::output pixelformat(0u, format);
567
568 std::vector<C2Param*> configParam{&pixelformat};
569 c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
570 if (status == C2_OK && failures.size() == 0u) {
571 return true;
572 }
573 return false;
574 }
575
576 class Codec2VideoDecDecodeTest
577 : public Codec2VideoDecHidlTestBase,
578 public ::testing::WithParamInterface<
579 std::tuple<std::string, std::string, std::string, std::string>> {
getParams()580 void getParams() {
581 mInstanceName = std::get<0>(GetParam());
582 mComponentName = std::get<1>(GetParam());
583 }
584 };
585
586 // Bitstream Test
TEST_P(Codec2VideoDecDecodeTest,DecodeTest)587 TEST_P(Codec2VideoDecDecodeTest, DecodeTest) {
588 description("Decodes input file");
589 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
590
591 uint32_t streamIndex = std::stoi(std::get<2>(GetParam()));
592 bool signalEOS = !std::get<2>(GetParam()).compare("true");
593 mTimestampDevTest = true;
594
595 char mURL[512], info[512], chksum[512];
596 android::Vector<FrameInfo> Info;
597
598 strcpy(mURL, sResourceDir.c_str());
599 strcpy(info, sResourceDir.c_str());
600 strcpy(chksum, sResourceDir.c_str());
601
602 GetURLChksmForComponent(mCompName, mURL, info, chksum, streamIndex);
603 if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
604 ALOGV("Skipping Test, Stream not available");
605 return;
606 }
607 mMd5Enable = true;
608 if (!strcmp(chksum, sResourceDir.c_str())) mMd5Enable = false;
609
610 uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
611 if (!configPixelFormat(format)) {
612 std::cout << "[ WARN ] Test Skipped PixelFormat not configured\n";
613 return;
614 }
615
616 mFlushedIndices.clear();
617 mTimestampUslist.clear();
618
619 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
620 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
621
622 ASSERT_EQ(mComponent->start(), C2_OK);
623 // Reset total no of frames received
624 mFramesReceived = 0;
625 mTimestampUs = 0;
626 ALOGV("mURL : %s", mURL);
627 std::ifstream eleStream;
628 eleStream.open(mURL, std::ifstream::binary);
629 ASSERT_EQ(eleStream.is_open(), true);
630
631 size_t refChksmSize = 0;
632 std::ifstream refChksum;
633 if (mMd5Enable) {
634 ALOGV("chksum file name: %s", chksum);
635 refChksum.open(chksum, std::ifstream::binary | std::ifstream::ate);
636 ASSERT_EQ(refChksum.is_open(), true);
637 refChksmSize = refChksum.tellg();
638 refChksum.seekg(0, std::ifstream::beg);
639
640 ALOGV("chksum Size %zu ", refChksmSize);
641 mRefMd5 = (char*)malloc(refChksmSize);
642 ASSERT_NE(mRefMd5, nullptr);
643 refChksum.read(mRefMd5, refChksmSize);
644 ASSERT_EQ(refChksum.gcount(), refChksmSize);
645 refChksum.close();
646 }
647
648 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
649 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
650 (int)Info.size(), signalEOS));
651
652 // If EOS is not sent, sending empty input with EOS flag
653 size_t infoSize = Info.size();
654 if (!signalEOS) {
655 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1);
656 ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
657 C2FrameData::FLAG_END_OF_STREAM, false));
658 infoSize += 1;
659 }
660 // blocking call to ensures application to Wait till all the inputs are
661 // consumed
662 if (!mEos) {
663 ALOGV("Waiting for input consumption");
664 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
665 }
666
667 eleStream.close();
668 if (mFramesReceived != infoSize) {
669 ALOGE("Input buffer count and Output buffer count mismatch");
670 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, infoSize);
671 ASSERT_TRUE(false);
672 }
673
674 if (mRefMd5 != nullptr) free(mRefMd5);
675 if (mMd5Enable && refChksmSize != mMd5Offset) {
676 ALOGE("refChksum size and generated chksum size mismatch refChksum size %zu generated "
677 "chksum size %" PRId64 "",
678 refChksmSize, mMd5Offset);
679 ASSERT_TRUE(false);
680 }
681
682 if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
683 ASSERT_EQ(mComponent->stop(), C2_OK);
684 ASSERT_EQ(mWorkResult, C2_OK);
685 }
686
687 // Adaptive Test
TEST_P(Codec2VideoDecHidlTest,AdaptiveDecodeTest)688 TEST_P(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
689 description("Adaptive Decode Test");
690 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
691 if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 || mCompName == vp9 ||
692 mCompName == mpeg2))
693 return;
694
695 typedef std::unique_lock<std::mutex> ULock;
696 ASSERT_EQ(mComponent->start(), C2_OK);
697
698 mTimestampDevTest = true;
699 uint32_t timestampOffset = 0;
700 uint32_t offset = 0;
701 android::Vector<FrameInfo> Info;
702 for (uint32_t i = 0; i < STREAM_COUNT * 2; i++) {
703 char mURL[512], info[512];
704 std::ifstream eleStream, eleInfo;
705
706 strcpy(mURL, sResourceDir.c_str());
707 strcpy(info, sResourceDir.c_str());
708 GetURLForComponent(mCompName, mURL, info, i % STREAM_COUNT);
709 if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
710 ALOGV("Stream not available, skipping this index");
711 continue;
712 }
713
714 eleInfo.open(info);
715 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
716 int bytesCount = 0;
717 uint32_t flags = 0;
718 uint32_t timestamp = 0;
719 uint32_t timestampMax = 0;
720 while (1) {
721 if (!(eleInfo >> bytesCount)) break;
722 eleInfo >> flags;
723 eleInfo >> timestamp;
724 timestamp += timestampOffset;
725 Info.push_back({bytesCount, flags, timestamp});
726 bool codecConfig =
727 flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
728 bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
729
730 {
731 ULock l(mQueueLock);
732 if (mTimestampDevTest && !codecConfig && !nonDisplayFrame)
733 mTimestampUslist.push_back(timestamp);
734 }
735 if (timestampMax < timestamp) timestampMax = timestamp;
736 }
737 timestampOffset = timestampMax;
738 eleInfo.close();
739
740 // Reset Total frames before second decode loop
741 // mFramesReceived = 0;
742 ALOGV("mURL : %s", mURL);
743 eleStream.open(mURL, std::ifstream::binary);
744 ASSERT_EQ(eleStream.is_open(), true);
745 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
746 mFlushedIndices, mLinearPool, eleStream, &Info,
747 offset, (int)(Info.size() - offset), false));
748
749 eleStream.close();
750 offset = (int)Info.size();
751 }
752
753 // Send EOS
754 // TODO Add function to send EOS work item
755 int maxRetry = 0;
756 std::unique_ptr<C2Work> work;
757 while (!work && (maxRetry < MAX_RETRY)) {
758 ULock l(mQueueLock);
759 if (!mWorkQueue.empty()) {
760 work.swap(mWorkQueue.front());
761 mWorkQueue.pop_front();
762 } else {
763 mQueueCondition.wait_for(l, TIME_OUT);
764 maxRetry++;
765 }
766 }
767 ASSERT_NE(work, nullptr);
768 work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
769 work->input.ordinal.timestamp = 0;
770 work->input.ordinal.frameIndex = 0;
771 work->input.buffers.clear();
772 work->worklets.clear();
773 work->worklets.emplace_back(new C2Worklet);
774
775 std::list<std::unique_ptr<C2Work>> items;
776 items.push_back(std::move(work));
777 ASSERT_EQ(mComponent->queue(&items), C2_OK);
778
779 // blocking call to ensures application to Wait till all the inputs are
780 // consumed
781 ALOGV("Waiting for input consumption");
782 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
783
784 if (mFramesReceived != ((Info.size()) + 1)) {
785 ALOGE("Input buffer count and Output buffer count mismatch");
786 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size() + 1);
787 ASSERT_TRUE(false);
788 }
789
790 if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
791 ASSERT_EQ(mWorkResult, C2_OK);
792 }
793
794 // thumbnail test
TEST_P(Codec2VideoDecHidlTest,ThumbnailTest)795 TEST_P(Codec2VideoDecHidlTest, ThumbnailTest) {
796 description("Test Request for thumbnail");
797 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
798
799 char mURL[512], info[512];
800 android::Vector<FrameInfo> Info;
801
802 strcpy(mURL, sResourceDir.c_str());
803 strcpy(info, sResourceDir.c_str());
804 GetURLForComponent(mCompName, mURL, info);
805
806 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
807 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
808
809 uint32_t flags = 0;
810 for (size_t i = 0; i < MAX_ITERATIONS; i++) {
811 ASSERT_EQ(mComponent->start(), C2_OK);
812
813 // request EOS for thumbnail
814 // signal EOS flag with last frame
815 size_t j = -1;
816 do {
817 j++;
818 flags = 0;
819 if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
820
821 } while (!(flags & SYNC_FRAME));
822
823 std::ifstream eleStream;
824 eleStream.open(mURL, std::ifstream::binary);
825 ASSERT_EQ(eleStream.is_open(), true);
826 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
827 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
828 j + 1));
829 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
830 eleStream.close();
831 EXPECT_GE(mFramesReceived, 1U);
832 ASSERT_EQ(mEos, true);
833 ASSERT_EQ(mComponent->stop(), C2_OK);
834 }
835 ASSERT_EQ(mComponent->release(), C2_OK);
836 ASSERT_EQ(mWorkResult, C2_OK);
837 }
838
TEST_P(Codec2VideoDecHidlTest,EOSTest)839 TEST_P(Codec2VideoDecHidlTest, EOSTest) {
840 description("Test empty input buffer with EOS flag");
841 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
842 typedef std::unique_lock<std::mutex> ULock;
843 ASSERT_EQ(mComponent->start(), C2_OK);
844 std::unique_ptr<C2Work> work;
845 // Prepare C2Work
846 {
847 ULock l(mQueueLock);
848 if (!mWorkQueue.empty()) {
849 work.swap(mWorkQueue.front());
850 mWorkQueue.pop_front();
851 } else {
852 ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test";
853 }
854 }
855 ASSERT_NE(work, nullptr);
856
857 work->input.flags = (C2FrameData::flags_t)C2FrameData::FLAG_END_OF_STREAM;
858 work->input.ordinal.timestamp = 0;
859 work->input.ordinal.frameIndex = 0;
860 work->input.buffers.clear();
861 work->worklets.clear();
862 work->worklets.emplace_back(new C2Worklet);
863
864 std::list<std::unique_ptr<C2Work>> items;
865 items.push_back(std::move(work));
866 ASSERT_EQ(mComponent->queue(&items), C2_OK);
867
868 {
869 ULock l(mQueueLock);
870 if (mWorkQueue.size() != MAX_INPUT_BUFFERS) {
871 mQueueCondition.wait_for(l, TIME_OUT);
872 }
873 }
874 ASSERT_EQ(mEos, true);
875 ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
876 ASSERT_EQ(mComponent->stop(), C2_OK);
877 ASSERT_EQ(mWorkResult, C2_OK);
878 }
879
TEST_P(Codec2VideoDecHidlTest,FlushTest)880 TEST_P(Codec2VideoDecHidlTest, FlushTest) {
881 description("Tests Flush calls");
882 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
883
884 ASSERT_EQ(mComponent->start(), C2_OK);
885
886 char mURL[512], info[512];
887 android::Vector<FrameInfo> Info;
888
889 strcpy(mURL, sResourceDir.c_str());
890 strcpy(info, sResourceDir.c_str());
891 GetURLForComponent(mCompName, mURL, info);
892
893 mFlushedIndices.clear();
894
895 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
896 ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
897
898 ALOGV("mURL : %s", mURL);
899
900 // flush
901 std::list<std::unique_ptr<C2Work>> flushedWork;
902 c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
903 ASSERT_EQ(err, C2_OK);
904 ASSERT_NO_FATAL_FAILURE(
905 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
906 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
907
908 std::ifstream eleStream;
909 eleStream.open(mURL, std::ifstream::binary);
910 ASSERT_EQ(eleStream.is_open(), true);
911 // Decode 30 frames and flush. here 30 is chosen to ensure there is a key
912 // frame after this so that the below section can be covered for all
913 // components
914 uint32_t numFramesFlushed = FLUSH_INTERVAL;
915 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
916 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
917 numFramesFlushed, false));
918 // flush
919 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
920 ASSERT_EQ(err, C2_OK);
921 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
922 (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
923 ASSERT_NO_FATAL_FAILURE(
924 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
925 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
926 // Seek to next key frame and start decoding till the end
927 int index = numFramesFlushed;
928 bool keyFrame = false;
929 uint32_t flags = 0;
930 while (index < (int)Info.size()) {
931 if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
932 if ((flags & SYNC_FRAME) == SYNC_FRAME) {
933 keyFrame = true;
934 break;
935 }
936 flags = 0;
937 eleStream.ignore(Info[index].bytesCount);
938 index++;
939 }
940 if (keyFrame) {
941 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
942 mFlushedIndices, mLinearPool, eleStream, &Info, index,
943 (int)Info.size() - index));
944 }
945 eleStream.close();
946 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
947 ASSERT_EQ(err, C2_OK);
948 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
949 (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
950 ASSERT_NO_FATAL_FAILURE(
951 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
952 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
953 // TODO: (b/154671521)
954 // Add assert for mWorkResult
955 ASSERT_EQ(mComponent->stop(), C2_OK);
956 }
957
TEST_P(Codec2VideoDecHidlTest,DecodeTestEmptyBuffersInserted)958 TEST_P(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) {
959 description("Decode with multiple empty input frames");
960 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
961
962 char mURL[512], info[512];
963 std::ifstream eleStream, eleInfo;
964
965 strcpy(mURL, sResourceDir.c_str());
966 strcpy(info, sResourceDir.c_str());
967 GetURLForComponent(mCompName, mURL, info);
968
969 eleInfo.open(info);
970 ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
971 android::Vector<FrameInfo> Info;
972 int bytesCount = 0;
973 uint32_t frameId = 0;
974 uint32_t flags = 0;
975 uint32_t timestamp = 0;
976 bool codecConfig = false;
977 // This test introduces empty CSD after every 20th frame
978 // and empty input frames at an interval of 5 frames.
979 while (1) {
980 if (!(frameId % 5)) {
981 if (!(frameId % 20))
982 flags = 32;
983 else
984 flags = 0;
985 bytesCount = 0;
986 } else {
987 if (!(eleInfo >> bytesCount)) break;
988 eleInfo >> flags;
989 eleInfo >> timestamp;
990 codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
991 }
992 Info.push_back({bytesCount, flags, timestamp});
993 frameId++;
994 }
995 eleInfo.close();
996
997 ASSERT_EQ(mComponent->start(), C2_OK);
998 ALOGV("mURL : %s", mURL);
999 eleStream.open(mURL, std::ifstream::binary);
1000 ASSERT_EQ(eleStream.is_open(), true);
1001 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
1002 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
1003 (int)Info.size()));
1004
1005 // blocking call to ensures application to Wait till all the inputs are
1006 // consumed
1007 if (!mEos) {
1008 ALOGV("Waiting for input consumption");
1009 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
1010 }
1011
1012 eleStream.close();
1013 if (mFramesReceived != Info.size()) {
1014 ALOGE("Input buffer count and Output buffer count mismatch");
1015 ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size());
1016 ASSERT_TRUE(false);
1017 }
1018 }
1019
1020 class Codec2VideoDecCsdInputTests
1021 : public Codec2VideoDecHidlTestBase,
1022 public ::testing::WithParamInterface<std::tuple<std::string, std::string, std::string>> {
getParams()1023 void getParams() {
1024 mInstanceName = std::get<0>(GetParam());
1025 mComponentName = std::get<1>(GetParam());
1026 }
1027 };
1028
1029 // Test the codecs for the following
1030 // start - csd - data… - (with/without)flush - data… - flush - data…
TEST_P(Codec2VideoDecCsdInputTests,CSDFlushTest)1031 TEST_P(Codec2VideoDecCsdInputTests, CSDFlushTest) {
1032 description("Tests codecs for flush at different states");
1033 if (mDisableTest) GTEST_SKIP() << "Test is disabled";
1034
1035 char mURL[512], info[512];
1036
1037 android::Vector<FrameInfo> Info;
1038
1039 strcpy(mURL, sResourceDir.c_str());
1040 strcpy(info, sResourceDir.c_str());
1041 GetURLForComponent(mCompName, mURL, info);
1042
1043 int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
1044 ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
1045
1046 ASSERT_EQ(mComponent->start(), C2_OK);
1047
1048 ALOGV("mURL : %s", mURL);
1049 std::ifstream eleStream;
1050 eleStream.open(mURL, std::ifstream::binary);
1051 ASSERT_EQ(eleStream.is_open(), true);
1052 bool flushedDecoder = false;
1053 bool signalEOS = false;
1054 bool keyFrame = false;
1055 bool flushCsd = !std::get<2>(GetParam()).compare("true");
1056
1057 ALOGV("sending %d csd data ", numCsds);
1058 int framesToDecode = numCsds;
1059 ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
1060 mFlushedIndices, mLinearPool, eleStream, &Info, 0,
1061 framesToDecode, false));
1062 c2_status_t err = C2_OK;
1063 std::list<std::unique_ptr<C2Work>> flushedWork;
1064 if (numCsds && flushCsd) {
1065 // We wait for all the CSD buffers to get consumed.
1066 // Once we have received all CSD work back, we call flush
1067 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
1068
1069 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
1070 ASSERT_EQ(err, C2_OK);
1071 flushedDecoder = true;
1072 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
1073 MAX_INPUT_BUFFERS - flushedWork.size());
1074 ASSERT_NO_FATAL_FAILURE(
1075 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
1076 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
1077 }
1078
1079 int offset = framesToDecode;
1080 uint32_t flags = 0;
1081 while (1) {
1082 while (offset < (int)Info.size()) {
1083 flags = 0;
1084 if (Info[offset].flags) flags = 1u << (Info[offset].flags - 1);
1085 if (flags & SYNC_FRAME) {
1086 keyFrame = true;
1087 break;
1088 }
1089 eleStream.ignore(Info[offset].bytesCount);
1090 offset++;
1091 }
1092 if (keyFrame) {
1093 framesToDecode = c2_min(FLUSH_INTERVAL, (int)Info.size() - offset);
1094 if (framesToDecode < FLUSH_INTERVAL) signalEOS = true;
1095 ASSERT_NO_FATAL_FAILURE(decodeNFrames(
1096 mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
1097 mLinearPool, eleStream, &Info, offset, framesToDecode, signalEOS));
1098 offset += framesToDecode;
1099 }
1100 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
1101 ASSERT_EQ(err, C2_OK);
1102 keyFrame = false;
1103 // blocking call to ensures application to Wait till remaining
1104 // 'non-flushed' inputs are consumed
1105 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
1106 MAX_INPUT_BUFFERS - flushedWork.size());
1107 ASSERT_NO_FATAL_FAILURE(
1108 verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
1109 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
1110 if (signalEOS || offset >= (int)Info.size()) {
1111 break;
1112 }
1113 }
1114 if (!signalEOS) {
1115 ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
1116 C2FrameData::FLAG_END_OF_STREAM, false));
1117 waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
1118 }
1119 eleStream.close();
1120 ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
1121 ASSERT_EQ(mComponent->stop(), C2_OK);
1122 }
1123
1124 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2VideoDecHidlTest, testing::ValuesIn(kTestParameters),
1125 android::hardware::PrintInstanceTupleNameToString<>);
1126
1127 // DecodeTest with StreamIndex and EOS / No EOS
1128 INSTANTIATE_TEST_SUITE_P(StreamIndexAndEOS, Codec2VideoDecDecodeTest,
1129 testing::ValuesIn(kDecodeTestParameters),
1130 android::hardware::PrintInstanceTupleNameToString<>);
1131
1132 INSTANTIATE_TEST_SUITE_P(CsdInputs, Codec2VideoDecCsdInputTests,
1133 testing::ValuesIn(kCsdFlushTestParameters),
1134 android::hardware::PrintInstanceTupleNameToString<>);
1135
1136 } // anonymous namespace
1137
1138 // TODO : Video specific configuration Test
main(int argc,char ** argv)1139 int main(int argc, char** argv) {
1140 kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER);
1141 for (auto params : kTestParameters) {
1142 kDecodeTestParameters.push_back(
1143 std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "false"));
1144 kDecodeTestParameters.push_back(
1145 std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "true"));
1146 kDecodeTestParameters.push_back(
1147 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "false"));
1148 kDecodeTestParameters.push_back(
1149 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "true"));
1150 kDecodeTestParameters.push_back(
1151 std::make_tuple(std::get<0>(params), std::get<1>(params), "2", "false"));
1152 kDecodeTestParameters.push_back(
1153 std::make_tuple(std::get<0>(params), std::get<1>(params), "2", "true"));
1154
1155 kCsdFlushTestParameters.push_back(
1156 std::make_tuple(std::get<0>(params), std::get<1>(params), "true"));
1157 kCsdFlushTestParameters.push_back(
1158 std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
1159 }
1160
1161 // Set the resource directory based on command line args.
1162 // Test will fail to set up if the argument is not set.
1163 for (int i = 1; i < argc; i++) {
1164 if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
1165 sResourceDir = argv[i + 1];
1166 break;
1167 }
1168 }
1169
1170 ::testing::InitGoogleTest(&argc, argv);
1171 return RUN_ALL_TESTS();
1172 }
1173