1 /*
2  * Copyright (C) 2020 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 #include <C2AllocatorIon.h>
18 #include <C2Buffer.h>
19 #include <C2BufferPriv.h>
20 #include <C2Config.h>
21 #include <C2Debug.h>
22 #include <codec2/hidl/client.h>
23 #include <gui/BufferQueue.h>
24 #include <gui/IConsumerListener.h>
25 #include <gui/IProducerListener.h>
26 
27 #include <chrono>
28 #include <condition_variable>
29 #include <fstream>
30 #include <iostream>
31 
32 #include "../includes/common.h"
33 
34 using android::C2AllocatorIon;
35 using std::chrono_literals::operator""ms;
36 
37 #define MAXIMUM_NUMBER_OF_RETRIES 20
38 #define QUEUE_TIMEOUT 400ms
39 #define MAXIMUM_NUMBER_OF_INPUT_BUFFERS 8
40 #define ALIGN(_sz, _align) ((_sz + (_align - 1)) & ~(_align - 1))
41 
42 void workDone(const std::shared_ptr<android::Codec2Client::Component> &component,
43               std::unique_ptr<C2Work> &work, std::list<uint64_t> &flushedIndices,
44               std::mutex &queueLock, std::condition_variable &queueCondition,
45               std::list<std::unique_ptr<C2Work>> &workQueue, bool &eos, bool &csd,
46               uint32_t &framesReceived);
47 
48 struct FrameInfo {
49     int bytesCount;
50     uint32_t flags;
51     int64_t timestamp;
52 };
53 
54 class LinearBuffer : public C2Buffer {
55 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)56     explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
57           : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
58 
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block,size_t size)59     explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block, size_t size)
60           : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
61 };
62 
63 /*
64  * Handle Callback functions onWorkDone(), onTripped(),
65  * onError(), onDeath(), onFramesRendered()
66  */
67 struct CodecListener : public android::Codec2Client::Listener {
68 public:
CodecListenerCodecListener69     CodecListener(
70             const std::function<void(std::list<std::unique_ptr<C2Work>> &workItems)> fn = nullptr)
71           : callBack(fn) {}
onWorkDoneCodecListener72     virtual void onWorkDone(const std::weak_ptr<android::Codec2Client::Component> &comp,
73                             std::list<std::unique_ptr<C2Work>> &workItems) override {
74         (void)comp;
75         if (callBack) {
76             callBack(workItems);
77         }
78     }
79 
onTrippedCodecListener80     virtual void onTripped(
81             const std::weak_ptr<android::Codec2Client::Component> &comp,
82             const std::vector<std::shared_ptr<C2SettingResult>> &settingResults) override {
83         (void)comp;
84         (void)settingResults;
85     }
86 
onErrorCodecListener87     virtual void onError(const std::weak_ptr<android::Codec2Client::Component> &comp,
88                          uint32_t errorCode) override {
89         (void)comp;
90         (void)errorCode;
91     }
92 
onDeathCodecListener93     virtual void onDeath(const std::weak_ptr<android::Codec2Client::Component> &comp) override {
94         (void)comp;
95     }
96 
onInputBufferDoneCodecListener97     virtual void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) override {
98         (void)frameIndex;
99         (void)arrayIndex;
100     }
101 
onFrameRenderedCodecListener102     virtual void onFrameRendered(uint64_t bufferQueueId, int32_t slotId,
103                                  int64_t timestampNs) override {
104         (void)bufferQueueId;
105         (void)slotId;
106         (void)timestampNs;
107     }
108     std::function<void(std::list<std::unique_ptr<C2Work>> &workItems)> callBack;
109 };
110 
111 class Codec2VideoDecHidlTestBase {
112 public:
SetUp()113     bool SetUp() {
114         mClient = getClient();
115         if (!mClient) {
116             return false;
117         }
118         mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>> &workItems) {
119             handleWorkDone(workItems);
120         }));
121         if (!mListener) {
122             return false;
123         }
124         if (android::Codec2Client::CreateComponentByName(mComponentName.c_str(), mListener,
125                                                          &mComponent, &mClient) != C2_OK) {
126             return false;
127         }
128         for (int i = 0; i < MAXIMUM_NUMBER_OF_INPUT_BUFFERS; ++i) {
129             mWorkQueue.emplace_back(new C2Work);
130         }
131         std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
132         if (store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator) != C2_OK) {
133             return false;
134         }
135         mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
136         if (!mLinearPool) {
137             return false;
138         }
139         mEos = false;
140         mHasVulnerability = false;
141         mTimestampUs = 0u;
142         mWorkResult = C2_OK;
143         mFramesReceived = 0;
144         return true;
145     }
146 
~Codec2VideoDecHidlTestBase()147     ~Codec2VideoDecHidlTestBase() {
148         if (mComponent != nullptr) {
149             mComponent->release();
150             mComponent = nullptr;
151         }
152     }
153 
getClient()154     std::shared_ptr<android::Codec2Client> getClient() {
155         auto instances = android::Codec2Client::GetServiceNames();
156         for (std::string instance : instances) {
157             std::shared_ptr<android::Codec2Client> client =
158                     android::Codec2Client::CreateFromService(instance.c_str());
159             std::vector<C2Component::Traits> components = client->listComponents();
160             for (C2Component::Traits traits : components) {
161                 if (instance.compare(traits.owner)) {
162                     continue;
163                 }
164                 if (traits.domain == DOMAIN_VIDEO && traits.kind == KIND_DECODER &&
165                     mComponentName.compare(traits.name)) {
166                     return android::Codec2Client::
167                             CreateFromService(instance.c_str(),
168                                               !bool(android::Codec2Client::
169                                                             CreateFromService("default", true)));
170                 }
171             }
172         }
173         return nullptr;
174     }
175 
checkBufferOK(std::unique_ptr<C2Work> & work)176     void checkBufferOK(std::unique_ptr<C2Work> &work) {
177         const C2GraphicView output = work->worklets.front()
178                                              ->output.buffers[0]
179                                              ->data()
180                                              .graphicBlocks()
181                                              .front()
182                                              .map()
183                                              .get();
184         uint8_t *uPlane = const_cast<uint8_t *>(output.data()[C2PlanarLayout::PLANE_U]);
185         uint8_t *vPlane = const_cast<uint8_t *>(output.data()[C2PlanarLayout::PLANE_V]);
186         const uint8_t ul[] = {109, 109, 109, 109, 109, 109, 109};
187         const uint8_t vl[] = {121, 121, 121, 121, 121, 121, 121};
188         const uint8_t ur[] = {114, 114, 120, 120, 122, 127, 127};
189         const uint8_t vr[] = {126, 121, 123, 121, 123, 126, 126};
190         if (!memcmp(uPlane - 7, ul, 7) && !memcmp(vPlane - 7, vl, 7) &&
191             !memcmp(uPlane + 1, ur, 7) && !memcmp(vPlane + 1, vr, 7)) {
192             mHasVulnerability |= true;
193         }
194     }
195 
196     // Config output pixel format
configPixelFormat(uint32_t format)197     bool configPixelFormat(uint32_t format) {
198         std::vector<std::unique_ptr<C2SettingResult>> failures;
199         C2StreamPixelFormatInfo::output pixelformat(0u, format);
200 
201         std::vector<C2Param *> configParam{&pixelformat};
202         c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
203         if (status == C2_OK && failures.size() == 0u) {
204             return true;
205         }
206         return false;
207     }
208 
209     // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)210     void handleWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
211         for (std::unique_ptr<C2Work> &work : workItems) {
212             if (!work->worklets.empty()) {
213                 // For decoder components current timestamp always exceeds
214                 // previous timestamp if output is in display order
215                 mWorkResult |= work->result;
216                 bool codecConfig = ((work->worklets.front()->output.flags &
217                                      C2FrameData::FLAG_CODEC_CONFIG) != 0);
218                 if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
219                     checkBufferOK(work);
220                 }
221                 bool mCsd = false;
222                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
223                          mEos, mCsd, mFramesReceived);
224                 (void)mCsd;
225             }
226         }
227     }
228 
229     const std::string mComponentName = "c2.android.hevc.decoder";
230     bool mEos;
231     bool mHasVulnerability;
232     uint64_t mTimestampUs;
233     int32_t mWorkResult;
234     uint32_t mFramesReceived;
235     std::list<uint64_t> mFlushedIndices;
236 
237     C2BlockPool::local_id_t mBlockPoolId;
238     std::shared_ptr<C2BlockPool> mLinearPool;
239     std::shared_ptr<C2Allocator> mLinearAllocator;
240 
241     std::mutex mQueueLock;
242     std::condition_variable mQueueCondition;
243     std::list<std::unique_ptr<C2Work>> mWorkQueue;
244 
245     std::shared_ptr<android::Codec2Client> mClient;
246     std::shared_ptr<android::Codec2Client::Listener> mListener;
247     std::shared_ptr<android::Codec2Client::Component> mComponent;
248 };
249 
250 // process onWorkDone received by Listener
workDone(const std::shared_ptr<android::Codec2Client::Component> & component,std::unique_ptr<C2Work> & work,std::list<uint64_t> & flushedIndices,std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,bool & eos,bool & csd,uint32_t & framesReceived)251 void workDone(const std::shared_ptr<android::Codec2Client::Component> &component,
252               std::unique_ptr<C2Work> &work, std::list<uint64_t> &flushedIndices,
253               std::mutex &queueLock, std::condition_variable &queueCondition,
254               std::list<std::unique_ptr<C2Work>> &workQueue, bool &eos, bool &csd,
255               uint32_t &framesReceived) {
256     // handle configuration changes in work done
257     if (work->worklets.front()->output.configUpdate.size() != 0) {
258         std::vector<std::unique_ptr<C2Param>> updates =
259                 std::move(work->worklets.front()->output.configUpdate);
260         std::vector<C2Param *> configParam;
261         std::vector<std::unique_ptr<C2SettingResult>> failures;
262         for (size_t i = 0; i < updates.size(); ++i) {
263             C2Param *param = updates[i].get();
264             if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
265                 C2StreamInitDataInfo::output *csdBuffer = (C2StreamInitDataInfo::output *)(param);
266                 size_t csdSize = csdBuffer->flexCount();
267                 if (csdSize > 0) {
268                     csd = true;
269                 }
270             } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
271                        (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) ||
272                        (param->index() == C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
273                 configParam.push_back(param);
274             }
275         }
276         component->config(configParam, C2_DONT_BLOCK, &failures);
277         assert(failures.size() == 0u);
278     }
279     if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
280         ++framesReceived;
281         eos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
282         auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
283                                       work->input.ordinal.frameIndex.peeku());
284         work->input.buffers.clear();
285         work->worklets.clear();
286         {
287             typedef std::unique_lock<std::mutex> ULock;
288             ULock l(queueLock);
289             workQueue.push_back(std::move(work));
290             if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
291                 flushedIndices.erase(frameIndexIt);
292             }
293             queueCondition.notify_all();
294         }
295     }
296 }
297 
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 & ifStream,android::Vector<FrameInfo> * Info)298 bool decodeNFrames(const std::shared_ptr<android::Codec2Client::Component> &component,
299                    std::mutex &queueLock, std::condition_variable &queueCondition,
300                    std::list<std::unique_ptr<C2Work>> &workQueue,
301                    std::list<uint64_t> &flushedIndices, std::shared_ptr<C2BlockPool> &linearPool,
302                    std::ifstream &ifStream, android::Vector<FrameInfo> *Info) {
303     typedef std::unique_lock<std::mutex> ULock;
304     int frameID = 0;
305     int retryCount = 0;
306     while (1) {
307         if (frameID == (int)Info->size()) {
308             break;
309         }
310         uint32_t flags = 0;
311         std::unique_ptr<C2Work> work;
312         // Prepare C2Work
313         while (!work && (retryCount < MAXIMUM_NUMBER_OF_RETRIES)) {
314             ULock l(queueLock);
315             if (!workQueue.empty()) {
316                 work.swap(workQueue.front());
317                 workQueue.pop_front();
318             } else {
319                 queueCondition.wait_for(l, QUEUE_TIMEOUT);
320                 ++retryCount;
321             }
322         }
323         if (!work && (retryCount >= MAXIMUM_NUMBER_OF_RETRIES)) {
324             return false; // "Wait for generating C2Work exceeded timeout"
325         }
326         int64_t timestamp = (*Info)[frameID].timestamp;
327         if ((*Info)[frameID].flags) {
328             flags = (1 << ((*Info)[frameID].flags - 1));
329         }
330         if (frameID == (int)Info->size() - 1) {
331             flags |= C2FrameData::FLAG_END_OF_STREAM;
332         }
333 
334         work->input.flags = (C2FrameData::flags_t)flags;
335         work->input.ordinal.timestamp = timestamp;
336         work->input.ordinal.frameIndex = frameID;
337         {
338             ULock l(queueLock);
339             flushedIndices.emplace_back(frameID);
340         }
341 
342         int size = (*Info)[frameID].bytesCount;
343         char *data = (char *)malloc(size);
344         if (!data) {
345             return false;
346         }
347 
348         ifStream.read(data, size);
349         if (ifStream.gcount() != size) {
350             return false;
351         }
352 
353         work->input.buffers.clear();
354         auto alignedSize = ALIGN(size, PAGE_SIZE);
355         if (size) {
356             std::shared_ptr<C2LinearBlock> block;
357             if (linearPool->fetchLinearBlock(alignedSize,
358                                              {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
359                                              &block) != C2_OK) {
360                 return false;
361             }
362             if (!block) {
363                 return false;
364             }
365 
366             // Write View
367             C2WriteView view = block->map().get();
368             if (view.error() != C2_OK) {
369                 return false;
370             }
371             if ((size_t)alignedSize != view.capacity()) {
372                 return false;
373             }
374             if (0u != view.offset()) {
375                 return false;
376             }
377             if ((size_t)alignedSize != view.size()) {
378                 return false;
379             }
380 
381             memcpy(view.base(), data, size);
382 
383             work->input.buffers.emplace_back(new LinearBuffer(block, size));
384             free(data);
385         }
386         work->worklets.clear();
387         work->worklets.emplace_back(new C2Worklet);
388         std::list<std::unique_ptr<C2Work>> items;
389         items.push_back(std::move(work));
390 
391         // DO THE DECODING
392         if (component->queue(&items) != C2_OK) {
393             return false;
394         }
395         ++frameID;
396         retryCount = 0;
397     }
398     return true;
399 }
400 
401 // Wait for all the inputs to be consumed by the plugin.
waitOnInputConsumption(std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,size_t bufferCount=MAXIMUM_NUMBER_OF_INPUT_BUFFERS)402 void waitOnInputConsumption(std::mutex &queueLock, std::condition_variable &queueCondition,
403                             std::list<std::unique_ptr<C2Work>> &workQueue,
404                             size_t bufferCount = MAXIMUM_NUMBER_OF_INPUT_BUFFERS) {
405     typedef std::unique_lock<std::mutex> ULock;
406     uint32_t queueSize;
407     uint32_t retryCount = 0;
408     {
409         ULock l(queueLock);
410         queueSize = workQueue.size();
411     }
412     while ((retryCount < MAXIMUM_NUMBER_OF_RETRIES) && (queueSize < bufferCount)) {
413         ULock l(queueLock);
414         if (queueSize != workQueue.size()) {
415             queueSize = workQueue.size();
416             retryCount = 0;
417         } else {
418             queueCondition.wait_for(l, QUEUE_TIMEOUT);
419             ++retryCount;
420         }
421     }
422 }
423 
424 // Populate Info vector and return number of CSDs
populateInfoVector(std::string info,android::Vector<FrameInfo> * frameInfo)425 int32_t populateInfoVector(std::string info, android::Vector<FrameInfo> *frameInfo) {
426     std::ifstream eleInfo;
427     eleInfo.open(info);
428     if (!eleInfo.is_open()) {
429         return -1;
430     }
431     int32_t numCsds = 0;
432     int32_t bytesCount = 0;
433     uint32_t flags = 0;
434     uint32_t timestamp = 0;
435     while (1) {
436         if (!(eleInfo >> bytesCount)) {
437             break;
438         }
439         eleInfo >> flags;
440         eleInfo >> timestamp;
441         bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
442         if (codecConfig) {
443             ++numCsds;
444         }
445         frameInfo->push_back({bytesCount, flags, timestamp});
446     }
447     eleInfo.close();
448     return numCsds;
449 }
450 
451 #define RETURN_FAILURE(condition) \
452     if ((condition)) {            \
453         return EXIT_FAILURE;      \
454     }
455 
main(int argc,char ** argv)456 int main(int argc, char **argv) {
457     RETURN_FAILURE(argc != 3);
458 
459     Codec2VideoDecHidlTestBase handle;
460     RETURN_FAILURE(!handle.SetUp());
461     RETURN_FAILURE(!handle.configPixelFormat(HAL_PIXEL_FORMAT_YCBCR_420_888));
462 
463     std::string eleStreamInfo{argv[2]};
464     android::Vector<FrameInfo> Info;
465     RETURN_FAILURE(populateInfoVector(eleStreamInfo, &Info) < 0);
466     RETURN_FAILURE(handle.mComponent->start() != C2_OK);
467 
468     std::string eleStream{argv[1]};
469     std::ifstream ifStream;
470     ifStream.open(eleStream, std::ifstream::binary);
471     RETURN_FAILURE(!ifStream.is_open());
472     RETURN_FAILURE(!decodeNFrames(handle.mComponent, handle.mQueueLock, handle.mQueueCondition,
473                                   handle.mWorkQueue, handle.mFlushedIndices, handle.mLinearPool,
474                                   ifStream, &Info));
475     // blocking call to ensures application to Wait till all the inputs are
476     // consumed
477     if (!handle.mEos) {
478         waitOnInputConsumption(handle.mQueueLock, handle.mQueueCondition, handle.mWorkQueue);
479     }
480     ifStream.close();
481     RETURN_FAILURE(handle.mFramesReceived != Info.size());
482     RETURN_FAILURE(handle.mComponent->stop() != C2_OK);
483     RETURN_FAILURE(handle.mWorkResult != C2_OK);
484     if (handle.mHasVulnerability) {
485         return EXIT_VULNERABLE;
486     }
487     return EXIT_SUCCESS;
488 }
489