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