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, ¶ms);
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