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