1/*
2 * Copyright 2017, 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#ifndef MEDIA_HIDL_TEST_COMMON_H
18#define MEDIA_HIDL_TEST_COMMON_H
19
20#ifdef __LP64__
21#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
22#endif
23
24#include <getopt.h>
25#include <media/stagefright/foundation/ALooper.h>
26#include <utils/Condition.h>
27#include <utils/List.h>
28#include <utils/Mutex.h>
29
30#include <media/openmax/OMX_Index.h>
31#include <media/openmax/OMX_Core.h>
32#include <media/openmax/OMX_Component.h>
33#include <media/openmax/OMX_IndexExt.h>
34#include <media/openmax/OMX_AudioExt.h>
35#include <media/openmax/OMX_VideoExt.h>
36
37#include <VtsHalHidlTargetTestEnvBase.h>
38
39/* TIME OUTS (Wait time in dequeueMessage()) */
40
41/* As component is switching states (loaded<->idle<->execute), dequeueMessage()
42 * expects the events to be received within this duration */
43#define DEFAULT_TIMEOUT 100000
44/* Time interval between successive Input/Output enqueues */
45#define DEFAULT_TIMEOUT_Q 2000
46/* While the component is amidst a process call, asynchronous commands like
47 * flush, change states can get delayed (at max by process call time). Instead
48 * of waiting on DEFAULT_TIMEOUT, we give an additional leeway. */
49#define DEFAULT_TIMEOUT_PE 500000
50
51/* Breakout Timeout :: 5 sec*/
52#define TIMEOUT_COUNTER_Q (5000000 / DEFAULT_TIMEOUT_Q)
53#define TIMEOUT_COUNTER_PE (5000000 / DEFAULT_TIMEOUT_PE)
54
55/*
56 * Random Index used for monkey testing while get/set parameters
57 */
58#define RANDOM_INDEX 1729
59
60#define ALIGN_POWER_OF_TWO(value, n) \
61    (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1))
62
63enum bufferOwner {
64    client,
65    component,
66    unknown,
67};
68
69/*
70 * TODO: below definitions are borrowed from Conversion.h.
71 * This is not the ideal way to do it. Loose these definitions once you
72 * include Conversion.h
73 */
74inline uint32_t toRawIndexType(OMX_INDEXTYPE l) {
75    return static_cast<uint32_t>(l);
76}
77
78inline android::hardware::media::omx::V1_0::Status toStatus(
79    android::status_t l) {
80    return static_cast<android::hardware::media::omx::V1_0::Status>(l);
81}
82
83inline hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) {
84    hidl_vec<uint8_t> t;
85    t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false);
86    return t;
87}
88
89inline uint32_t toRawCommandType(OMX_COMMANDTYPE l) {
90    return static_cast<uint32_t>(l);
91}
92
93/*
94 * struct definitions
95 */
96struct BufferInfo {
97    uint32_t id;
98    bufferOwner owner;
99    android::hardware::media::omx::V1_0::CodecBuffer omxBuffer;
100    ::android::sp<IMemory> mMemory;
101    int32_t slot;
102};
103
104struct FrameData {
105    int bytesCount;
106    uint32_t flags;
107    uint32_t timestamp;
108};
109
110/*
111 * Handle Callback functions EmptythisBuffer(), FillthisBuffer(),
112 * EventHandler()
113 */
114struct CodecObserver : public IOmxObserver {
115   public:
116    CodecObserver(std::function<void(Message, const BufferInfo*)> fn)
117        : callBack(fn) {}
118    Return<void> onMessages(const hidl_vec<Message>& messages) override {
119        android::Mutex::Autolock autoLock(msgLock);
120        for (hidl_vec<Message>::const_iterator it = messages.begin();
121             it != messages.end(); ++it) {
122            msgQueue.push_back(*it);
123        }
124        msgCondition.signal();
125        return Void();
126    }
127    android::hardware::media::omx::V1_0::Status dequeueMessage(
128        Message* msg, int64_t timeoutUs,
129        android::Vector<BufferInfo>* iBuffers = nullptr,
130        android::Vector<BufferInfo>* oBuffers = nullptr) {
131        int64_t finishBy = android::ALooper::GetNowUs() + timeoutUs;
132        for (;;) {
133            android::Mutex::Autolock autoLock(msgLock);
134            android::List<Message>::iterator it = msgQueue.begin();
135            while (it != msgQueue.end()) {
136                if (it->type ==
137                    android::hardware::media::omx::V1_0::Message::Type::EVENT) {
138                    *msg = *it;
139                    if (callBack) callBack(*it, nullptr);
140                    it = msgQueue.erase(it);
141                    // OMX_EventBufferFlag event is sent when the component has
142                    // processed a buffer with its EOS flag set. This event is
143                    // not sent by soft omx components. Vendor components can
144                    // send this. From IOMX point of view, we will ignore this
145                    // event.
146                    if (msg->data.eventData.event == OMX_EventBufferFlag)
147                        continue;
148                    return ::android::hardware::media::omx::V1_0::Status::OK;
149                } else if (it->type == android::hardware::media::omx::V1_0::
150                                           Message::Type::FILL_BUFFER_DONE) {
151                    if (oBuffers) {
152                        size_t i;
153                        for (i = 0; i < oBuffers->size(); ++i) {
154                            if ((*oBuffers)[i].id ==
155                                it->data.bufferData.buffer) {
156                                if (callBack) callBack(*it, &(*oBuffers)[i]);
157                                oBuffers->editItemAt(i).owner = client;
158                                it = msgQueue.erase(it);
159                                break;
160                            }
161                        }
162                        EXPECT_LE(i, oBuffers->size());
163                    }
164                } else if (it->type == android::hardware::media::omx::V1_0::
165                                           Message::Type::EMPTY_BUFFER_DONE) {
166                    if (iBuffers) {
167                        size_t i;
168                        for (i = 0; i < iBuffers->size(); ++i) {
169                            if ((*iBuffers)[i].id ==
170                                it->data.bufferData.buffer) {
171                                if (callBack) callBack(*it, &(*iBuffers)[i]);
172                                iBuffers->editItemAt(i).owner = client;
173                                it = msgQueue.erase(it);
174                                break;
175                            }
176                        }
177                        EXPECT_LE(i, iBuffers->size());
178                    }
179                } else {
180                    EXPECT_TRUE(false) << "Received unexpected message";
181                    ++it;
182                }
183            }
184            int64_t delayUs = finishBy - android::ALooper::GetNowUs();
185            if (delayUs < 0) return toStatus(android::TIMED_OUT);
186            (timeoutUs < 0)
187                ? msgCondition.wait(msgLock)
188                : msgCondition.waitRelative(msgLock, delayUs * 1000ll);
189        }
190    }
191
192    android::List<Message> msgQueue;
193    android::Mutex msgLock;
194    android::Condition msgCondition;
195    std::function<void(Message, const BufferInfo*)> callBack;
196};
197
198/*
199 * Useful Wrapper utilities
200 */
201template <class T>
202void InitOMXParams(T* params) {
203    params->nSize = sizeof(T);
204    params->nVersion.s.nVersionMajor = 1;
205    params->nVersion.s.nVersionMinor = 0;
206    params->nVersion.s.nRevision = 0;
207    params->nVersion.s.nStep = 0;
208}
209
210template <class T>
211Return<android::hardware::media::omx::V1_0::Status> getParam(
212    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) {
213    android::hardware::media::omx::V1_0::Status status;
214    InitOMXParams(params);
215    omxNode->getParameter(
216        toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)),
217        [&status, &params](android::hardware::media::omx::V1_0::Status _s,
218                           hidl_vec<uint8_t> const& outParams) {
219            status = _s;
220            std::copy(outParams.data(), outParams.data() + outParams.size(),
221                      static_cast<uint8_t*>(static_cast<void*>(params)));
222        });
223    return status;
224}
225
226template <class T>
227Return<android::hardware::media::omx::V1_0::Status> setParam(
228    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) {
229    InitOMXParams(params);
230    return omxNode->setParameter(toRawIndexType(omxIdx),
231                                 inHidlBytes(params, sizeof(*params)));
232}
233
234template <class T>
235Return<android::hardware::media::omx::V1_0::Status> getPortParam(
236    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) {
237    android::hardware::media::omx::V1_0::Status status;
238    InitOMXParams(params);
239    params->nPortIndex = nPortIndex;
240    omxNode->getParameter(
241        toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)),
242        [&status, &params](android::hardware::media::omx::V1_0::Status _s,
243                           hidl_vec<uint8_t> const& outParams) {
244            status = _s;
245            std::copy(outParams.data(), outParams.data() + outParams.size(),
246                      static_cast<uint8_t*>(static_cast<void*>(params)));
247        });
248    return status;
249}
250
251template <class T>
252Return<android::hardware::media::omx::V1_0::Status> setPortParam(
253    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) {
254    InitOMXParams(params);
255    params->nPortIndex = nPortIndex;
256    return omxNode->setParameter(toRawIndexType(omxIdx),
257                                 inHidlBytes(params, sizeof(*params)));
258}
259
260template <class T>
261Return<android::hardware::media::omx::V1_0::Status> getPortConfig(
262    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) {
263    android::hardware::media::omx::V1_0::Status status;
264    InitOMXParams(params);
265    params->nPortIndex = nPortIndex;
266    omxNode->getConfig(
267        toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)),
268        [&status, &params](android::hardware::media::omx::V1_0::Status _s,
269                           hidl_vec<uint8_t> const& outParams) {
270            status = _s;
271            std::copy(outParams.data(), outParams.data() + outParams.size(),
272                      static_cast<uint8_t*>(static_cast<void*>(params)));
273        });
274    return status;
275}
276
277template <class T>
278Return<android::hardware::media::omx::V1_0::Status> setPortConfig(
279    sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) {
280    InitOMXParams(params);
281    params->nPortIndex = nPortIndex;
282    return omxNode->setConfig(toRawIndexType(omxIdx),
283                              inHidlBytes(params, sizeof(*params)));
284}
285
286/*
287 * common functions declarations
288 */
289Return<android::hardware::media::omx::V1_0::Status> setRole(
290    sp<IOmxNode> omxNode, const char* role);
291
292Return<android::hardware::media::omx::V1_0::Status> setPortBufferSize(
293    sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_U32 size);
294
295Return<android::hardware::media::omx::V1_0::Status> setVideoPortFormat(
296    sp<IOmxNode> omxNode, OMX_U32 portIndex,
297    OMX_VIDEO_CODINGTYPE eCompressionFormat, OMX_COLOR_FORMATTYPE eColorFormat,
298    OMX_U32 xFramerate);
299
300Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat(
301    sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE eEncoding);
302
303void allocateBuffer(sp<IOmxNode> omxNode, BufferInfo* buffer, OMX_U32 portIndex,
304                    OMX_U32 nBufferSize, PortMode portMode);
305
306void allocatePortBuffers(sp<IOmxNode> omxNode,
307                         android::Vector<BufferInfo>* buffArray,
308                         OMX_U32 portIndex,
309                         PortMode portMode = PortMode::PRESET_BYTE_BUFFER,
310                         bool allocGrap = false);
311
312void changeStateLoadedtoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
313                             android::Vector<BufferInfo>* iBuffer,
314                             android::Vector<BufferInfo>* oBuffer,
315                             OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput,
316                             PortMode* portMode = nullptr,
317                             bool allocGrap = false);
318
319void changeStateIdletoLoaded(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
320                             android::Vector<BufferInfo>* iBuffer,
321                             android::Vector<BufferInfo>* oBuffer,
322                             OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput);
323
324void changeStateIdletoExecute(sp<IOmxNode> omxNode, sp<CodecObserver> observer);
325
326void changeStateExecutetoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
327                              android::Vector<BufferInfo>* iBuffer,
328                              android::Vector<BufferInfo>* oBuffer);
329
330size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray);
331
332void dispatchOutputBuffer(sp<IOmxNode> omxNode,
333                          android::Vector<BufferInfo>* buffArray,
334                          size_t bufferIndex,
335                          PortMode portMode = PortMode::PRESET_BYTE_BUFFER);
336
337void dispatchInputBuffer(sp<IOmxNode> omxNode,
338                         android::Vector<BufferInfo>* buffArray,
339                         size_t bufferIndex, int bytesCount, uint32_t flags,
340                         uint64_t timestamp,
341                         PortMode portMode = PortMode::PRESET_BYTE_BUFFER);
342
343void flushPorts(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
344                android::Vector<BufferInfo>* iBuffer,
345                android::Vector<BufferInfo>* oBuffer, OMX_U32 kPortIndexInput,
346                OMX_U32 kPortIndexOutput,
347                int64_t timeoutUs = DEFAULT_TIMEOUT_PE);
348
349typedef void (*portreconfig)(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
350                             android::Vector<BufferInfo>* iBuffer,
351                             android::Vector<BufferInfo>* oBuffer,
352                             OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput,
353                             Message msg, PortMode oPortMode, void* args);
354void testEOS(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
355             android::Vector<BufferInfo>* iBuffer,
356             android::Vector<BufferInfo>* oBuffer, bool signalEOS,
357             bool& eosFlag, PortMode* portMode = nullptr,
358             portreconfig fptr = nullptr, OMX_U32 kPortIndexInput = 0,
359             OMX_U32 kPortIndexOutput = 1, void* args = nullptr);
360
361// A class for test environment setup
362class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
363   private:
364    typedef ::testing::VtsHalHidlTargetTestEnvBase Super;
365
366   public:
367    virtual void registerTestServices() override { registerTestService<IOmx>(); }
368
369    ComponentTestEnvironment() : res("/sdcard/media/") {}
370
371    void setComponent(const char* _component) { component = _component; }
372
373    void setRole(const char* _role) { role = _role; }
374
375    void setRes(const char* _res) { res = _res; }
376
377    const hidl_string getInstance() { return Super::getServiceName<IOmx>(); }
378
379    const hidl_string getComponent() const { return component; }
380
381    const hidl_string getRole() const { return role; }
382
383    const hidl_string getRes() const { return res; }
384
385    int initFromOptions(int argc, char** argv) {
386        static struct option options[] = {{"component", required_argument, 0, 'C'},
387                                          {"role", required_argument, 0, 'R'},
388                                          {"res", required_argument, 0, 'P'},
389                                          {0, 0, 0, 0}};
390
391        while (true) {
392            int index = 0;
393            int c = getopt_long(argc, argv, "C:R:P:", options, &index);
394            if (c == -1) {
395                break;
396            }
397
398            switch (c) {
399                case 'C':
400                    setComponent(optarg);
401                    break;
402                case 'R':
403                    setRole(optarg);
404                    break;
405                case 'P':
406                    setRes(optarg);
407                    break;
408                case '?':
409                    break;
410            }
411        }
412
413        if (optind < argc) {
414            fprintf(stderr,
415                    "unrecognized option: %s\n\n"
416                    "usage: %s <gtest options> <test options>\n\n"
417                    "test options are:\n\n"
418                    "-C, --component: OMX component to test\n"
419                    "-R, --role: OMX component Role\n"
420                    "-P, --res: Resource files directory location\n",
421                    argv[optind ?: 1], argv[0]);
422            return 2;
423        }
424        return 0;
425    }
426
427   private:
428    hidl_string instance;
429    hidl_string component;
430    hidl_string role;
431    hidl_string res;
432};
433
434#endif  // MEDIA_HIDL_TEST_COMMON_H
435