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