| /* |
| * Copyright 2017, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef MEDIA_HIDL_TEST_COMMON_H |
| #define MEDIA_HIDL_TEST_COMMON_H |
| |
| #ifdef __LP64__ |
| #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS |
| #endif |
| |
| #include <getopt.h> |
| |
| #include <android/hardware/graphics/allocator/2.0/IAllocator.h> |
| #include <android/hardware/graphics/allocator/3.0/IAllocator.h> |
| #include <android/hardware/graphics/common/1.0/types.h> |
| #include <android/hardware/graphics/common/1.1/types.h> |
| #include <android/hardware/graphics/common/1.2/types.h> |
| #include <android/hardware/graphics/mapper/2.0/IMapper.h> |
| #include <android/hardware/graphics/mapper/2.0/types.h> |
| #include <android/hardware/graphics/mapper/3.0/IMapper.h> |
| #include <android/hardware/graphics/mapper/3.0/types.h> |
| #include <media/stagefright/foundation/ALooper.h> |
| #include <utils/Condition.h> |
| #include <utils/List.h> |
| #include <utils/Mutex.h> |
| |
| #include <media/openmax/OMX_Index.h> |
| #include <media/openmax/OMX_Core.h> |
| #include <media/openmax/OMX_Component.h> |
| #include <media/openmax/OMX_IndexExt.h> |
| #include <media/openmax/OMX_AudioExt.h> |
| #include <media/openmax/OMX_VideoExt.h> |
| |
| #include <VtsHalHidlTargetTestEnvBase.h> |
| |
| /* TIME OUTS (Wait time in dequeueMessage()) */ |
| |
| /* As component is switching states (loaded<->idle<->execute), dequeueMessage() |
| * expects the events to be received within this duration */ |
| #define DEFAULT_TIMEOUT 100000 |
| // b/70933963 |
| #define RELAXED_TIMEOUT 400000 |
| /* Time interval between successive Input/Output enqueues */ |
| #define DEFAULT_TIMEOUT_Q 2000 |
| /* While the component is amidst a process call, asynchronous commands like |
| * flush, change states can get delayed (at max by process call time). Instead |
| * of waiting on DEFAULT_TIMEOUT, we give an additional leeway. */ |
| #define DEFAULT_TIMEOUT_PE 500000 |
| |
| /* Breakout Timeout :: 5 sec*/ |
| #define TIMEOUT_COUNTER_Q (5000000 / DEFAULT_TIMEOUT_Q) |
| #define TIMEOUT_COUNTER_PE (5000000 / DEFAULT_TIMEOUT_PE) |
| |
| /* |
| * Random Index used for monkey testing while get/set parameters |
| */ |
| #define RANDOM_INDEX 1729 |
| |
| #define ALIGN_POWER_OF_TWO(value, n) \ |
| (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1)) |
| |
| enum bufferOwner { |
| client, |
| component, |
| unknown, |
| }; |
| |
| /* |
| * TODO: below definitions are borrowed from Conversion.h. |
| * This is not the ideal way to do it. Loose these definitions once you |
| * include Conversion.h |
| */ |
| inline uint32_t toRawIndexType(OMX_INDEXTYPE l) { |
| return static_cast<uint32_t>(l); |
| } |
| |
| inline android::hardware::media::omx::V1_0::Status toStatus( |
| android::status_t l) { |
| return static_cast<android::hardware::media::omx::V1_0::Status>(l); |
| } |
| |
| inline hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) { |
| hidl_vec<uint8_t> t; |
| t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false); |
| return t; |
| } |
| |
| inline uint32_t toRawCommandType(OMX_COMMANDTYPE l) { |
| return static_cast<uint32_t>(l); |
| } |
| |
| /* |
| * struct definitions |
| */ |
| struct BufferInfo { |
| uint32_t id; |
| bufferOwner owner; |
| android::hardware::media::omx::V1_0::CodecBuffer omxBuffer; |
| ::android::sp<IMemory> mMemory; |
| int32_t slot; |
| }; |
| |
| struct FrameData { |
| int bytesCount; |
| uint32_t flags; |
| uint32_t timestamp; |
| }; |
| |
| /* |
| * Handle Callback functions EmptythisBuffer(), FillthisBuffer(), |
| * EventHandler() |
| */ |
| struct CodecObserver : public IOmxObserver { |
| public: |
| CodecObserver(std::function<void(Message, const BufferInfo*)> fn) |
| : callBack(fn) {} |
| Return<void> onMessages(const hidl_vec<Message>& messages) override { |
| android::Mutex::Autolock autoLock(msgLock); |
| for (hidl_vec<Message>::const_iterator it = messages.begin(); |
| it != messages.end(); ++it) { |
| msgQueue.push_back(*it); |
| } |
| msgCondition.signal(); |
| return Void(); |
| } |
| android::hardware::media::omx::V1_0::Status dequeueMessage( |
| Message* msg, int64_t timeoutUs, |
| android::Vector<BufferInfo>* iBuffers = nullptr, |
| android::Vector<BufferInfo>* oBuffers = nullptr) { |
| int64_t finishBy = android::ALooper::GetNowUs() + timeoutUs; |
| for (;;) { |
| android::Mutex::Autolock autoLock(msgLock); |
| android::List<Message>::iterator it = msgQueue.begin(); |
| while (it != msgQueue.end()) { |
| if (it->type == |
| android::hardware::media::omx::V1_0::Message::Type::EVENT) { |
| *msg = *it; |
| if (callBack) callBack(*it, nullptr); |
| it = msgQueue.erase(it); |
| // OMX_EventBufferFlag event is sent when the component has |
| // processed a buffer with its EOS flag set. This event is |
| // not sent by soft omx components. Vendor components can |
| // send this. From IOMX point of view, we will ignore this |
| // event. |
| if (msg->data.eventData.event == OMX_EventBufferFlag) |
| continue; |
| return ::android::hardware::media::omx::V1_0::Status::OK; |
| } else if (it->type == android::hardware::media::omx::V1_0:: |
| Message::Type::FILL_BUFFER_DONE) { |
| if (oBuffers) { |
| size_t i; |
| for (i = 0; i < oBuffers->size(); ++i) { |
| if ((*oBuffers)[i].id == |
| it->data.bufferData.buffer) { |
| if (callBack) callBack(*it, &(*oBuffers)[i]); |
| oBuffers->editItemAt(i).owner = client; |
| it = msgQueue.erase(it); |
| break; |
| } |
| } |
| EXPECT_LE(i, oBuffers->size()); |
| } |
| } else if (it->type == android::hardware::media::omx::V1_0:: |
| Message::Type::EMPTY_BUFFER_DONE) { |
| if (iBuffers) { |
| size_t i; |
| for (i = 0; i < iBuffers->size(); ++i) { |
| if ((*iBuffers)[i].id == |
| it->data.bufferData.buffer) { |
| if (callBack) callBack(*it, &(*iBuffers)[i]); |
| iBuffers->editItemAt(i).owner = client; |
| it = msgQueue.erase(it); |
| break; |
| } |
| } |
| EXPECT_LE(i, iBuffers->size()); |
| } |
| } else { |
| EXPECT_TRUE(false) << "Received unexpected message"; |
| ++it; |
| } |
| } |
| int64_t delayUs = finishBy - android::ALooper::GetNowUs(); |
| if (delayUs < 0) return toStatus(android::TIMED_OUT); |
| (timeoutUs < 0) |
| ? msgCondition.wait(msgLock) |
| : msgCondition.waitRelative(msgLock, delayUs * 1000ll); |
| } |
| } |
| |
| android::List<Message> msgQueue; |
| android::Mutex msgLock; |
| android::Condition msgCondition; |
| std::function<void(Message, const BufferInfo*)> callBack; |
| }; |
| |
| /* |
| * Useful Wrapper utilities |
| */ |
| template <class T> |
| void InitOMXParams(T* params) { |
| params->nSize = sizeof(T); |
| params->nVersion.s.nVersionMajor = 1; |
| params->nVersion.s.nVersionMinor = 0; |
| params->nVersion.s.nRevision = 0; |
| params->nVersion.s.nStep = 0; |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> getParam( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) { |
| android::hardware::media::omx::V1_0::Status status; |
| InitOMXParams(params); |
| omxNode->getParameter( |
| toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)), |
| [&status, ¶ms](android::hardware::media::omx::V1_0::Status _s, |
| hidl_vec<uint8_t> const& outParams) { |
| status = _s; |
| std::copy(outParams.data(), outParams.data() + outParams.size(), |
| static_cast<uint8_t*>(static_cast<void*>(params))); |
| }); |
| return status; |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> setParam( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) { |
| InitOMXParams(params); |
| return omxNode->setParameter(toRawIndexType(omxIdx), |
| inHidlBytes(params, sizeof(*params))); |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> getPortParam( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { |
| android::hardware::media::omx::V1_0::Status status; |
| InitOMXParams(params); |
| params->nPortIndex = nPortIndex; |
| omxNode->getParameter( |
| toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)), |
| [&status, ¶ms](android::hardware::media::omx::V1_0::Status _s, |
| hidl_vec<uint8_t> const& outParams) { |
| status = _s; |
| std::copy(outParams.data(), outParams.data() + outParams.size(), |
| static_cast<uint8_t*>(static_cast<void*>(params))); |
| }); |
| return status; |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> setPortParam( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { |
| InitOMXParams(params); |
| params->nPortIndex = nPortIndex; |
| return omxNode->setParameter(toRawIndexType(omxIdx), |
| inHidlBytes(params, sizeof(*params))); |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> getPortConfig( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { |
| android::hardware::media::omx::V1_0::Status status; |
| InitOMXParams(params); |
| params->nPortIndex = nPortIndex; |
| omxNode->getConfig( |
| toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)), |
| [&status, ¶ms](android::hardware::media::omx::V1_0::Status _s, |
| hidl_vec<uint8_t> const& outParams) { |
| status = _s; |
| std::copy(outParams.data(), outParams.data() + outParams.size(), |
| static_cast<uint8_t*>(static_cast<void*>(params))); |
| }); |
| return status; |
| } |
| |
| template <class T> |
| Return<android::hardware::media::omx::V1_0::Status> setPortConfig( |
| sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { |
| InitOMXParams(params); |
| params->nPortIndex = nPortIndex; |
| return omxNode->setConfig(toRawIndexType(omxIdx), |
| inHidlBytes(params, sizeof(*params))); |
| } |
| |
| /* |
| * common functions declarations |
| */ |
| struct GrallocV2 { |
| using Format = android::hardware::graphics::common::V1_0::PixelFormat; |
| using Usage = android::hardware::hidl_bitfield< |
| android::hardware::graphics::common::V1_0::BufferUsage>; |
| |
| using IAllocator = android::hardware::graphics::allocator::V2_0::IAllocator; |
| |
| using IMapper = android::hardware::graphics::mapper::V2_0::IMapper; |
| using Error = android::hardware::graphics::mapper::V2_0::Error; |
| using Descriptor = android::hardware::graphics::mapper::V2_0::BufferDescriptor; |
| using YCbCrLayout = android::hardware::graphics::mapper::V2_0::YCbCrLayout; |
| using DescriptorInfo = IMapper::BufferDescriptorInfo; |
| using Rect = IMapper::Rect; |
| }; |
| |
| struct GrallocV3 { |
| using Format = android::hardware::graphics::common::V1_2::PixelFormat; |
| using Usage = android::hardware::hidl_bitfield< |
| android::hardware::graphics::common::V1_2::BufferUsage>; |
| |
| using IAllocator = android::hardware::graphics::allocator::V3_0::IAllocator; |
| |
| using IMapper = android::hardware::graphics::mapper::V3_0::IMapper; |
| using Error = android::hardware::graphics::mapper::V3_0::Error; |
| using Descriptor = android::hardware::graphics::mapper::V3_0::BufferDescriptor; |
| using YCbCrLayout = android::hardware::graphics::mapper::V3_0::YCbCrLayout; |
| using DescriptorInfo = IMapper::BufferDescriptorInfo; |
| using Rect = IMapper::Rect; |
| }; |
| |
| Return<android::hardware::media::omx::V1_0::Status> setRole( |
| sp<IOmxNode> omxNode, const char* role); |
| |
| Return<android::hardware::media::omx::V1_0::Status> setPortBufferSize( |
| sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_U32 size); |
| |
| Return<android::hardware::media::omx::V1_0::Status> setVideoPortFormat( |
| sp<IOmxNode> omxNode, OMX_U32 portIndex, |
| OMX_VIDEO_CODINGTYPE eCompressionFormat, OMX_COLOR_FORMATTYPE eColorFormat, |
| OMX_U32 xFramerate); |
| |
| Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat( |
| sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE eEncoding); |
| |
| void allocateBuffer(sp<IOmxNode> omxNode, BufferInfo* buffer, OMX_U32 portIndex, |
| OMX_U32 nBufferSize, PortMode portMode); |
| |
| void allocatePortBuffers(sp<IOmxNode> omxNode, |
| android::Vector<BufferInfo>* buffArray, |
| OMX_U32 portIndex, |
| PortMode portMode = PortMode::PRESET_BYTE_BUFFER, |
| bool allocGrap = false); |
| |
| void changeStateLoadedtoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, |
| OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput, |
| PortMode* portMode = nullptr, |
| bool allocGrap = false); |
| |
| void changeStateIdletoLoaded(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, |
| OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput); |
| |
| void changeStateIdletoExecute(sp<IOmxNode> omxNode, sp<CodecObserver> observer); |
| |
| void changeStateExecutetoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer); |
| |
| size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray); |
| |
| void dispatchOutputBuffer(sp<IOmxNode> omxNode, |
| android::Vector<BufferInfo>* buffArray, |
| size_t bufferIndex, |
| PortMode portMode = PortMode::PRESET_BYTE_BUFFER); |
| |
| void dispatchInputBuffer(sp<IOmxNode> omxNode, |
| android::Vector<BufferInfo>* buffArray, |
| size_t bufferIndex, int bytesCount, uint32_t flags, |
| uint64_t timestamp, |
| PortMode portMode = PortMode::PRESET_BYTE_BUFFER); |
| |
| void flushPorts(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, OMX_U32 kPortIndexInput, |
| OMX_U32 kPortIndexOutput, |
| int64_t timeoutUs = DEFAULT_TIMEOUT_PE); |
| |
| typedef void (*portreconfig)(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, |
| OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput, |
| Message msg, PortMode oPortMode, void* args); |
| void testEOS(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, bool signalEOS, |
| bool& eosFlag, PortMode* portMode = nullptr, |
| portreconfig fptr = nullptr, OMX_U32 kPortIndexInput = 0, |
| OMX_U32 kPortIndexOutput = 1, void* args = nullptr); |
| |
| // A class for test environment setup |
| class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { |
| private: |
| typedef ::testing::VtsHalHidlTargetTestEnvBase Super; |
| |
| public: |
| virtual void registerTestServices() override { registerTestService<IOmx>(); } |
| |
| ComponentTestEnvironment() : res("/sdcard/media/") {} |
| |
| void setComponent(const char* _component) { component = _component; } |
| |
| void setRole(const char* _role) { role = _role; } |
| |
| void setRes(const char* _res) { res = _res; } |
| |
| const hidl_string getInstance() { return Super::getServiceName<IOmx>(); } |
| |
| const hidl_string getComponent() const { return component; } |
| |
| const hidl_string getRole() const { return role; } |
| |
| const hidl_string getRes() const { return res; } |
| |
| int initFromOptions(int argc, char** argv) { |
| static struct option options[] = {{"component", required_argument, 0, 'C'}, |
| {"role", required_argument, 0, 'R'}, |
| {"res", required_argument, 0, 'P'}, |
| {0, 0, 0, 0}}; |
| |
| while (true) { |
| int index = 0; |
| int c = getopt_long(argc, argv, "C:R:P:", options, &index); |
| if (c == -1) { |
| break; |
| } |
| |
| switch (c) { |
| case 'C': |
| setComponent(optarg); |
| break; |
| case 'R': |
| setRole(optarg); |
| break; |
| case 'P': |
| setRes(optarg); |
| break; |
| case '?': |
| break; |
| } |
| } |
| |
| if (optind < argc) { |
| fprintf(stderr, |
| "unrecognized option: %s\n\n" |
| "usage: %s <gtest options> <test options>\n\n" |
| "test options are:\n\n" |
| "-C, --component: OMX component to test\n" |
| "-R, --role: OMX component Role\n" |
| "-P, --res: Resource files directory location\n", |
| argv[optind ?: 1], argv[0]); |
| return 2; |
| } |
| return 0; |
| } |
| |
| private: |
| hidl_string instance; |
| hidl_string component; |
| hidl_string role; |
| hidl_string res; |
| }; |
| |
| #endif // MEDIA_HIDL_TEST_COMMON_H |