1 /*
2  * Copyright (C) 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 #define LOG_TAG "media_omx_hidl_video_test_common"
18 
19 #ifdef __LP64__
20 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
21 #endif
22 
23 #include <android-base/logging.h>
24 
25 #include <android/hardware/media/omx/1.0/IOmx.h>
26 #include <android/hardware/media/omx/1.0/IOmxNode.h>
27 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
28 #include <android/hardware/media/omx/1.0/types.h>
29 #include <android/hidl/allocator/1.0/IAllocator.h>
30 #include <android/hidl/memory/1.0/IMapper.h>
31 #include <android/hidl/memory/1.0/IMemory.h>
32 
33 using ::android::hardware::media::omx::V1_0::IOmx;
34 using ::android::hardware::media::omx::V1_0::IOmxObserver;
35 using ::android::hardware::media::omx::V1_0::IOmxNode;
36 using ::android::hardware::media::omx::V1_0::Message;
37 using ::android::hardware::media::omx::V1_0::CodecBuffer;
38 using ::android::hardware::media::omx::V1_0::PortMode;
39 using ::android::hardware::media::omx::V1_0::Status;
40 using ::android::hidl::allocator::V1_0::IAllocator;
41 using ::android::hidl::memory::V1_0::IMemory;
42 using ::android::hidl::memory::V1_0::IMapper;
43 using ::android::hardware::Return;
44 using ::android::hardware::Void;
45 using ::android::hardware::hidl_vec;
46 using ::android::hardware::hidl_string;
47 using ::android::sp;
48 
49 #include <VtsHalHidlTargetTestBase.h>
50 #include <hidlmemory/mapping.h>
51 #include <media/hardware/HardwareAPI.h>
52 #include <media_hidl_test_common.h>
53 #include <memory>
54 
55 // set component role
setRole(sp<IOmxNode> omxNode,const char * role)56 Return<android::hardware::media::omx::V1_0::Status> setRole(
57     sp<IOmxNode> omxNode, const char* role) {
58     OMX_PARAM_COMPONENTROLETYPE params;
59     strcpy((char*)params.cRole, role);
60     return setParam(omxNode, OMX_IndexParamStandardComponentRole, &params);
61 }
62 
63 // allocate buffers needed on a component port
allocatePortBuffers(sp<IOmxNode> omxNode,android::Vector<BufferInfo> * buffArray,OMX_U32 portIndex,PortMode portMode)64 void allocatePortBuffers(sp<IOmxNode> omxNode,
65                          android::Vector<BufferInfo>* buffArray,
66                          OMX_U32 portIndex, PortMode portMode) {
67     android::hardware::media::omx::V1_0::Status status;
68     OMX_PARAM_PORTDEFINITIONTYPE portDef;
69 
70     buffArray->clear();
71 
72     status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
73                           &portDef);
74     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
75 
76     if (portMode == PortMode::PRESET_SECURE_BUFFER) {
77         for (size_t i = 0; i < portDef.nBufferCountActual; i++) {
78             BufferInfo buffer;
79             buffer.owner = client;
80             buffer.omxBuffer.type = CodecBuffer::Type::NATIVE_HANDLE;
81             omxNode->allocateSecureBuffer(
82                 portIndex, portDef.nBufferSize,
83                 [&status, &buffer](
84                     android::hardware::media::omx::V1_0::Status _s, uint32_t id,
85                     ::android::hardware::hidl_handle const& nativeHandle) {
86                     status = _s;
87                     buffer.id = id;
88                     buffer.omxBuffer.nativeHandle = nativeHandle;
89                 });
90             buffArray->push(buffer);
91             ASSERT_EQ(status,
92                       ::android::hardware::media::omx::V1_0::Status::OK);
93         }
94     } else if (portMode == PortMode::PRESET_BYTE_BUFFER ||
95                portMode == PortMode::DYNAMIC_ANW_BUFFER) {
96         sp<IAllocator> allocator = IAllocator::getService("ashmem");
97         EXPECT_NE(allocator.get(), nullptr);
98 
99         for (size_t i = 0; i < portDef.nBufferCountActual; i++) {
100             BufferInfo buffer;
101             buffer.owner = client;
102             buffer.omxBuffer.type = CodecBuffer::Type::SHARED_MEM;
103             buffer.omxBuffer.attr.preset.rangeOffset = 0;
104             buffer.omxBuffer.attr.preset.rangeLength = 0;
105             bool success = false;
106             if (portMode != PortMode::PRESET_BYTE_BUFFER) {
107                 portDef.nBufferSize = sizeof(android::VideoNativeMetadata);
108             }
109             allocator->allocate(
110                 portDef.nBufferSize,
111                 [&success, &buffer](
112                     bool _s, ::android::hardware::hidl_memory const& mem) {
113                     success = _s;
114                     buffer.omxBuffer.sharedMemory = mem;
115                 });
116             ASSERT_EQ(success, true);
117             ASSERT_EQ(buffer.omxBuffer.sharedMemory.size(),
118                       portDef.nBufferSize);
119             buffer.mMemory = mapMemory(buffer.omxBuffer.sharedMemory);
120             ASSERT_NE(buffer.mMemory, nullptr);
121             if (portMode == PortMode::DYNAMIC_ANW_BUFFER) {
122                 android::VideoNativeMetadata* metaData =
123                     static_cast<android::VideoNativeMetadata*>(
124                         static_cast<void*>(buffer.mMemory->getPointer()));
125                 metaData->nFenceFd = -1;
126                 buffer.slot = -1;
127             }
128             omxNode->useBuffer(
129                 portIndex, buffer.omxBuffer,
130                 [&status, &buffer](
131                     android::hardware::media::omx::V1_0::Status _s,
132                     uint32_t id) {
133                     status = _s;
134                     buffer.id = id;
135                 });
136             buffArray->push(buffer);
137             ASSERT_EQ(status,
138                       ::android::hardware::media::omx::V1_0::Status::OK);
139         }
140     }
141 }
142 
143 // State Transition : Loaded -> Idle
144 // Note: This function does not make any background checks for this transition.
145 // The callee holds the reponsibility to ensure the legality of the transition.
changeStateLoadedtoIdle(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,OMX_U32 kPortIndexInput,OMX_U32 kPortIndexOutput,PortMode * portMode)146 void changeStateLoadedtoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
147                              android::Vector<BufferInfo>* iBuffer,
148                              android::Vector<BufferInfo>* oBuffer,
149                              OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput,
150                              PortMode* portMode) {
151     android::hardware::media::omx::V1_0::Status status;
152     Message msg;
153     PortMode defaultPortMode[2], *pm;
154 
155     defaultPortMode[0] = PortMode::PRESET_BYTE_BUFFER;
156     defaultPortMode[1] = PortMode::PRESET_BYTE_BUFFER;
157     pm = portMode ? portMode : defaultPortMode;
158 
159     // set state to idle
160     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
161                                   OMX_StateIdle);
162     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
163 
164     // Dont switch states until the ports are populated
165     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
166     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
167 
168     // allocate buffers on input port
169     allocatePortBuffers(omxNode, iBuffer, kPortIndexInput, pm[0]);
170 
171     // Dont switch states until the ports are populated
172     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
173     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
174 
175     // allocate buffers on output port
176     allocatePortBuffers(omxNode, oBuffer, kPortIndexOutput, pm[1]);
177 
178     // As the ports are populated, check if the state transition is complete
179     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
180     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
181     ASSERT_EQ(msg.type, Message::Type::EVENT);
182     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
183     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandStateSet);
184     ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle);
185 
186     return;
187 }
188 
189 // State Transition : Idle -> Loaded
190 // Note: This function does not make any background checks for this transition.
191 // The callee holds the reponsibility to ensure the legality of the transition.
changeStateIdletoLoaded(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,OMX_U32 kPortIndexInput,OMX_U32 kPortIndexOutput)192 void changeStateIdletoLoaded(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
193                              android::Vector<BufferInfo>* iBuffer,
194                              android::Vector<BufferInfo>* oBuffer,
195                              OMX_U32 kPortIndexInput,
196                              OMX_U32 kPortIndexOutput) {
197     android::hardware::media::omx::V1_0::Status status;
198     Message msg;
199 
200     // set state to Loaded
201     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
202                                   OMX_StateLoaded);
203     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
204 
205     // dont change state until all buffers are freed
206     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
207     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
208 
209     for (size_t i = 0; i < iBuffer->size(); ++i) {
210         status = omxNode->freeBuffer(kPortIndexInput, (*iBuffer)[i].id);
211         ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
212     }
213 
214     // dont change state until all buffers are freed
215     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
216     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
217 
218     for (size_t i = 0; i < oBuffer->size(); ++i) {
219         status = omxNode->freeBuffer(kPortIndexOutput, (*oBuffer)[i].id);
220         ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
221     }
222 
223     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
224     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
225     ASSERT_EQ(msg.type, Message::Type::EVENT);
226     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
227     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandStateSet);
228     ASSERT_EQ(msg.data.eventData.data2, OMX_StateLoaded);
229 
230     return;
231 }
232 
233 // State Transition : Idle -> Execute
234 // Note: This function does not make any background checks for this transition.
235 // The callee holds the reponsibility to ensure the legality of the transition.
changeStateIdletoExecute(sp<IOmxNode> omxNode,sp<CodecObserver> observer)236 void changeStateIdletoExecute(sp<IOmxNode> omxNode,
237                               sp<CodecObserver> observer) {
238     android::hardware::media::omx::V1_0::Status status;
239     Message msg;
240 
241     // set state to execute
242     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
243                                   OMX_StateExecuting);
244     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
245     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT);
246     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
247     ASSERT_EQ(msg.type, Message::Type::EVENT);
248     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
249     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandStateSet);
250     ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting);
251 
252     return;
253 }
254 
255 // State Transition : Execute -> Idle
256 // Note: This function does not make any background checks for this transition.
257 // The callee holds the reponsibility to ensure the legality of the transition.
changeStateExecutetoIdle(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer)258 void changeStateExecutetoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
259                               android::Vector<BufferInfo>* iBuffer,
260                               android::Vector<BufferInfo>* oBuffer) {
261     android::hardware::media::omx::V1_0::Status status;
262     Message msg;
263 
264     // set state to Idle
265     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
266                                   OMX_StateIdle);
267     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
268     status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
269     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
270     ASSERT_EQ(msg.type, Message::Type::EVENT);
271     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
272     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandStateSet);
273     ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle);
274 
275     // test if client got all its buffers back
276     for (size_t i = 0; i < oBuffer->size(); ++i) {
277         EXPECT_EQ((*oBuffer)[i].owner, client);
278     }
279     for (size_t i = 0; i < iBuffer->size(); ++i) {
280         EXPECT_EQ((*iBuffer)[i].owner, client);
281     }
282 }
283 
284 // get empty buffer index
getEmptyBufferID(android::Vector<BufferInfo> * buffArray)285 size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray) {
286     android::Vector<BufferInfo>::iterator it = buffArray->begin();
287     while (it != buffArray->end()) {
288         if (it->owner == client) {
289             // This block of code ensures that all buffers allocated at init
290             // time are utilized
291             BufferInfo backup = *it;
292             buffArray->erase(it);
293             buffArray->push_back(backup);
294             return buffArray->size() - 1;
295         }
296         it++;
297     }
298     return buffArray->size();
299 }
300 
301 // dispatch buffer to output port
dispatchOutputBuffer(sp<IOmxNode> omxNode,android::Vector<BufferInfo> * buffArray,size_t bufferIndex,PortMode portMode)302 void dispatchOutputBuffer(sp<IOmxNode> omxNode,
303                           android::Vector<BufferInfo>* buffArray,
304                           size_t bufferIndex, PortMode portMode) {
305     android::hardware::media::omx::V1_0::Status status;
306     CodecBuffer t;
307     native_handle_t* fenceNh = native_handle_create(0, 0);
308     ASSERT_NE(fenceNh, nullptr);
309     switch (portMode) {
310         case PortMode::DYNAMIC_ANW_BUFFER:
311             t = (*buffArray)[bufferIndex].omxBuffer;
312             t.type = CodecBuffer::Type::ANW_BUFFER;
313             status =
314                 omxNode->fillBuffer((*buffArray)[bufferIndex].id, t, fenceNh);
315             break;
316         case PortMode::PRESET_SECURE_BUFFER:
317         case PortMode::PRESET_BYTE_BUFFER:
318             t.sharedMemory = android::hardware::hidl_memory();
319             t.nativeHandle = android::hardware::hidl_handle();
320             t.type = CodecBuffer::Type::PRESET;
321             t.attr.preset.rangeOffset = 0;
322             t.attr.preset.rangeLength = 0;
323             status =
324                 omxNode->fillBuffer((*buffArray)[bufferIndex].id, t, fenceNh);
325             break;
326         default:
327             status = Status::NAME_NOT_FOUND;
328     }
329     native_handle_close(fenceNh);
330     native_handle_delete(fenceNh);
331     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
332     buffArray->editItemAt(bufferIndex).owner = component;
333 }
334 
335 // dispatch buffer to input port
dispatchInputBuffer(sp<IOmxNode> omxNode,android::Vector<BufferInfo> * buffArray,size_t bufferIndex,int bytesCount,uint32_t flags,uint64_t timestamp,PortMode portMode)336 void dispatchInputBuffer(sp<IOmxNode> omxNode,
337                          android::Vector<BufferInfo>* buffArray,
338                          size_t bufferIndex, int bytesCount, uint32_t flags,
339                          uint64_t timestamp, PortMode portMode) {
340     android::hardware::media::omx::V1_0::Status status;
341     CodecBuffer t;
342     native_handle_t* fenceNh = native_handle_create(0, 0);
343     ASSERT_NE(fenceNh, nullptr);
344     switch (portMode) {
345         case PortMode::PRESET_SECURE_BUFFER:
346         case PortMode::PRESET_BYTE_BUFFER:
347             t.sharedMemory = android::hardware::hidl_memory();
348             t.nativeHandle = android::hardware::hidl_handle();
349             t.type = CodecBuffer::Type::PRESET;
350             t.attr.preset.rangeOffset = 0;
351             t.attr.preset.rangeLength = bytesCount;
352             status = omxNode->emptyBuffer((*buffArray)[bufferIndex].id, t,
353                                           flags, timestamp, fenceNh);
354             break;
355         default:
356             status = Status::NAME_NOT_FOUND;
357     }
358     native_handle_close(fenceNh);
359     native_handle_delete(fenceNh);
360     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
361     buffArray->editItemAt(bufferIndex).owner = component;
362 }
363 
364 // Flush input and output ports
flushPorts(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,OMX_U32 kPortIndexInput,OMX_U32 kPortIndexOutput,int64_t timeoutUs)365 void flushPorts(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
366                 android::Vector<BufferInfo>* iBuffer,
367                 android::Vector<BufferInfo>* oBuffer, OMX_U32 kPortIndexInput,
368                 OMX_U32 kPortIndexOutput, int64_t timeoutUs) {
369     android::hardware::media::omx::V1_0::Status status;
370     Message msg;
371 
372     // Flush input port
373     status = omxNode->sendCommand(toRawCommandType(OMX_CommandFlush),
374                                   kPortIndexInput);
375     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
376     status = observer->dequeueMessage(&msg, timeoutUs, iBuffer, oBuffer);
377     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
378     ASSERT_EQ(msg.type, Message::Type::EVENT);
379     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
380     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandFlush);
381     ASSERT_EQ(msg.data.eventData.data2, kPortIndexInput);
382     // test if client got all its buffers back
383     for (size_t i = 0; i < iBuffer->size(); ++i) {
384         EXPECT_EQ((*iBuffer)[i].owner, client);
385     }
386 
387     // Flush output port
388     status = omxNode->sendCommand(toRawCommandType(OMX_CommandFlush),
389                                   kPortIndexOutput);
390     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
391     status = observer->dequeueMessage(&msg, timeoutUs, iBuffer, oBuffer);
392     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
393     ASSERT_EQ(msg.type, Message::Type::EVENT);
394     ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete);
395     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandFlush);
396     ASSERT_EQ(msg.data.eventData.data2, kPortIndexOutput);
397     // test if client got all its buffers back
398     for (size_t i = 0; i < oBuffer->size(); ++i) {
399         EXPECT_EQ((*oBuffer)[i].owner, client);
400     }
401 }
402 
403 // dispatch an empty input buffer with eos flag set if requested.
404 // This call assumes that all input buffers are processed completely.
405 // feed output buffers till we receive a buffer with eos flag set
testEOS(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,bool signalEOS,bool & eosFlag,PortMode * portMode)406 void testEOS(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
407              android::Vector<BufferInfo>* iBuffer,
408              android::Vector<BufferInfo>* oBuffer, bool signalEOS,
409              bool& eosFlag, PortMode* portMode) {
410     android::hardware::media::omx::V1_0::Status status;
411     PortMode defaultPortMode[2], *pm;
412 
413     defaultPortMode[0] = PortMode::PRESET_BYTE_BUFFER;
414     defaultPortMode[1] = PortMode::PRESET_BYTE_BUFFER;
415     pm = portMode ? portMode : defaultPortMode;
416 
417     size_t i = 0;
418     if (signalEOS) {
419         if ((i = getEmptyBufferID(iBuffer)) < iBuffer->size()) {
420             // signal an empty buffer with flag set to EOS
421             dispatchInputBuffer(omxNode, iBuffer, i, 0, OMX_BUFFERFLAG_EOS, 0);
422         } else {
423             ASSERT_TRUE(false);
424         }
425     }
426 
427     int timeOut = TIMEOUT_COUNTER;
428     while (timeOut--) {
429         // Dispatch all client owned output buffers to recover remaining frames
430         while (1) {
431             if ((i = getEmptyBufferID(oBuffer)) < oBuffer->size()) {
432                 dispatchOutputBuffer(omxNode, oBuffer, i, pm[1]);
433                 // if dispatch is successful, perhaps there is a latency
434                 // in the component. Dont be in a haste to leave. reset timeout
435                 // counter
436                 timeOut = TIMEOUT_COUNTER;
437             } else {
438                 break;
439             }
440         }
441 
442         Message msg;
443         status =
444             observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
445         if (status == android::hardware::media::omx::V1_0::Status::OK) {
446             if (msg.data.eventData.event == OMX_EventBufferFlag) {
447                 // soft omx components donot send this, we will just ignore it
448                 // for now
449             } else {
450                 // something unexpected happened
451                 EXPECT_TRUE(false);
452             }
453         }
454         if (eosFlag == true) break;
455     }
456     // test for flag
457     EXPECT_EQ(eosFlag, true);
458     eosFlag = false;
459 }
460