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
63 enum 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 */
toRawIndexType(OMX_INDEXTYPE l)74 inline uint32_t toRawIndexType(OMX_INDEXTYPE l) {
75 return static_cast<uint32_t>(l);
76 }
77
toStatus(android::status_t l)78 inline 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
inHidlBytes(void const * l,size_t size)83 inline 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
toRawCommandType(OMX_COMMANDTYPE l)89 inline uint32_t toRawCommandType(OMX_COMMANDTYPE l) {
90 return static_cast<uint32_t>(l);
91 }
92
93 /*
94 * struct definitions
95 */
96 struct 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
104 struct FrameData {
105 int bytesCount;
106 uint32_t flags;
107 uint32_t timestamp;
108 };
109
110 /*
111 * Handle Callback functions EmptythisBuffer(), FillthisBuffer(),
112 * EventHandler()
113 */
114 struct CodecObserver : public IOmxObserver {
115 public:
CodecObserverCodecObserver116 CodecObserver(std::function<void(Message, const BufferInfo*)> fn)
117 : callBack(fn) {}
onMessagesCodecObserver118 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 */
201 template <class T>
InitOMXParams(T * params)202 void 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
210 template <class T>
getParam(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,T * params)211 Return<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, ¶ms](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
226 template <class T>
setParam(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,T * params)227 Return<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
234 template <class T>
getPortParam(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,OMX_U32 nPortIndex,T * params)235 Return<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, ¶ms](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
251 template <class T>
setPortParam(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,OMX_U32 nPortIndex,T * params)252 Return<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
260 template <class T>
getPortConfig(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,OMX_U32 nPortIndex,T * params)261 Return<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, ¶ms](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
277 template <class T>
setPortConfig(sp<IOmxNode> omxNode,OMX_INDEXTYPE omxIdx,OMX_U32 nPortIndex,T * params)278 Return<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 */
289 Return<android::hardware::media::omx::V1_0::Status> setRole(
290 sp<IOmxNode> omxNode, const char* role);
291
292 Return<android::hardware::media::omx::V1_0::Status> setPortBufferSize(
293 sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_U32 size);
294
295 Return<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
300 Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat(
301 sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE eEncoding);
302
303 void allocateBuffer(sp<IOmxNode> omxNode, BufferInfo* buffer, OMX_U32 portIndex,
304 OMX_U32 nBufferSize, PortMode portMode);
305
306 void 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
312 void 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
319 void 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
324 void changeStateIdletoExecute(sp<IOmxNode> omxNode, sp<CodecObserver> observer);
325
326 void changeStateExecutetoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
327 android::Vector<BufferInfo>* iBuffer,
328 android::Vector<BufferInfo>* oBuffer);
329
330 size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray);
331
332 void dispatchOutputBuffer(sp<IOmxNode> omxNode,
333 android::Vector<BufferInfo>* buffArray,
334 size_t bufferIndex,
335 PortMode portMode = PortMode::PRESET_BYTE_BUFFER);
336
337 void 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
343 void 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
349 typedef 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);
354 void 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
362 class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
363 private:
364 typedef ::testing::VtsHalHidlTargetTestEnvBase Super;
365
366 public:
registerTestServices()367 virtual void registerTestServices() override { registerTestService<IOmx>(); }
368
ComponentTestEnvironment()369 ComponentTestEnvironment() : res("/sdcard/media/") {}
370
setComponent(const char * _component)371 void setComponent(const char* _component) { component = _component; }
372
setRole(const char * _role)373 void setRole(const char* _role) { role = _role; }
374
setRes(const char * _res)375 void setRes(const char* _res) { res = _res; }
376
getInstance()377 const hidl_string getInstance() { return Super::getServiceName<IOmx>(); }
378
getComponent()379 const hidl_string getComponent() const { return component; }
380
getRole()381 const hidl_string getRole() const { return role; }
382
getRes()383 const hidl_string getRes() const { return res; }
384
initFromOptions(int argc,char ** argv)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