| /* |
| * Copyright (C) 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. |
| */ |
| |
| #define LOG_TAG "media_omx_hidl_video_dec_test" |
| #ifdef __LP64__ |
| #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS |
| #endif |
| |
| #include <android-base/logging.h> |
| |
| #include <android/hardware/media/omx/1.0/IOmx.h> |
| #include <android/hardware/media/omx/1.0/IOmxNode.h> |
| #include <android/hardware/media/omx/1.0/IOmxObserver.h> |
| #include <android/hardware/media/omx/1.0/types.h> |
| #include <android/hidl/allocator/1.0/IAllocator.h> |
| #include <android/hidl/memory/1.0/IMapper.h> |
| #include <android/hidl/memory/1.0/IMemory.h> |
| |
| using ::android::hardware::media::omx::V1_0::IOmx; |
| using ::android::hardware::media::omx::V1_0::IOmxObserver; |
| using ::android::hardware::media::omx::V1_0::IOmxNode; |
| using ::android::hardware::media::omx::V1_0::Message; |
| using ::android::hardware::media::omx::V1_0::CodecBuffer; |
| using ::android::hardware::media::omx::V1_0::PortMode; |
| using ::android::hidl::allocator::V1_0::IAllocator; |
| using ::android::hidl::memory::V1_0::IMemory; |
| using ::android::hidl::memory::V1_0::IMapper; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::hidl_string; |
| using ::android::sp; |
| |
| #include <VtsHalHidlTargetTestBase.h> |
| #include <getopt.h> |
| #include <media/hardware/HardwareAPI.h> |
| #include <media_hidl_test_common.h> |
| #include <media_video_hidl_test_common.h> |
| #include <fstream> |
| |
| static ComponentTestEnvironment* gEnv = nullptr; |
| |
| // video decoder test fixture class |
| class VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { |
| private: |
| typedef ::testing::VtsHalHidlTargetTestBase Super; |
| public: |
| ::std::string getTestCaseInfo() const override { |
| return ::std::string() + |
| "Component: " + gEnv->getComponent().c_str() + " | " + |
| "Role: " + gEnv->getRole().c_str() + " | " + |
| "Instance: " + gEnv->getInstance().c_str() + " | " + |
| "Res: " + gEnv->getRes().c_str(); |
| } |
| |
| virtual void SetUp() override { |
| Super::SetUp(); |
| disableTest = false; |
| android::hardware::media::omx::V1_0::Status status; |
| omx = Super::getService<IOmx>(gEnv->getInstance()); |
| ASSERT_NE(omx, nullptr); |
| observer = |
| new CodecObserver([this](Message msg, const BufferInfo* buffer) { |
| handleMessage(msg, buffer); |
| }); |
| ASSERT_NE(observer, nullptr); |
| if (strncmp(gEnv->getComponent().c_str(), "OMX.", 4) != 0) |
| disableTest = true; |
| EXPECT_TRUE(omx->allocateNode( |
| gEnv->getComponent(), observer, |
| [&](android::hardware::media::omx::V1_0::Status _s, |
| sp<IOmxNode> const& _nl) { |
| status = _s; |
| this->omxNode = _nl; |
| }) |
| .isOk()); |
| if (status == android::hardware::media::omx::V1_0::Status::NAME_NOT_FOUND) { |
| disableTest = true; |
| std::cout << "[ WARN ] Test Disabled, component not present\n"; |
| return; |
| } |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| ASSERT_NE(omxNode, nullptr); |
| ASSERT_NE(gEnv->getRole().empty(), true) << "Invalid Component Role"; |
| struct StringToName { |
| const char* Name; |
| standardComp CompName; |
| }; |
| const StringToName kStringToName[] = { |
| {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4}, |
| {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, |
| }; |
| const size_t kNumStringToName = |
| sizeof(kStringToName) / sizeof(kStringToName[0]); |
| const char* pch; |
| char substring[OMX_MAX_STRINGNAME_SIZE]; |
| strcpy(substring, gEnv->getRole().c_str()); |
| pch = strchr(substring, '.'); |
| ASSERT_NE(pch, nullptr); |
| compName = unknown_comp; |
| for (size_t i = 0; i < kNumStringToName; ++i) { |
| if (!strcasecmp(pch + 1, kStringToName[i].Name)) { |
| compName = kStringToName[i].CompName; |
| break; |
| } |
| } |
| if (compName == unknown_comp) disableTest = true; |
| struct CompToCompression { |
| standardComp CompName; |
| OMX_VIDEO_CODINGTYPE eCompressionFormat; |
| }; |
| static const CompToCompression kCompToCompression[] = { |
| {h263, OMX_VIDEO_CodingH263}, {avc, OMX_VIDEO_CodingAVC}, |
| {mpeg2, OMX_VIDEO_CodingMPEG2}, {mpeg4, OMX_VIDEO_CodingMPEG4}, |
| {hevc, OMX_VIDEO_CodingHEVC}, {vp8, OMX_VIDEO_CodingVP8}, |
| {vp9, OMX_VIDEO_CodingVP9}, |
| }; |
| static const size_t kNumCompToCompression = |
| sizeof(kCompToCompression) / sizeof(kCompToCompression[0]); |
| size_t i; |
| for (i = 0; i < kNumCompToCompression; ++i) { |
| if (kCompToCompression[i].CompName == compName) { |
| eCompressionFormat = kCompToCompression[i].eCompressionFormat; |
| break; |
| } |
| } |
| if (i == kNumCompToCompression) disableTest = true; |
| portMode[0] = portMode[1] = PortMode::PRESET_BYTE_BUFFER; |
| eosFlag = false; |
| framesReceived = 0; |
| timestampUs = 0; |
| timestampDevTest = false; |
| isSecure = false; |
| portSettingsChange = false; |
| size_t suffixLen = strlen(".secure"); |
| if (strlen(gEnv->getComponent().c_str()) >= suffixLen) { |
| isSecure = |
| !strcmp(gEnv->getComponent().c_str() + |
| strlen(gEnv->getComponent().c_str()) - suffixLen, |
| ".secure"); |
| } |
| if (isSecure) disableTest = true; |
| omxNode->configureVideoTunnelMode( |
| 1, OMX_TRUE, 0, |
| [&](android::hardware::media::omx::V1_0::Status _s, |
| const ::android::hardware::hidl_handle& sidebandHandle) { |
| (void)sidebandHandle; |
| if (_s == android::hardware::media::omx::V1_0::Status::OK) |
| this->disableTest = true; |
| }); |
| if (disableTest) std::cout << "[ WARN ] Test Disabled \n"; |
| // NOTES: secure and tunneled components are not covered in these tests. |
| // we are disabling tests for them |
| } |
| |
| virtual void TearDown() override { |
| if (omxNode != nullptr) { |
| // If you have encountered a fatal failure, it is possible that |
| // freeNode() will not go through. Instead of hanging the app. |
| // let it pass through and report errors |
| if (::testing::Test::HasFatalFailure()) return; |
| EXPECT_TRUE((omxNode->freeNode()).isOk()); |
| omxNode = nullptr; |
| } |
| Super::TearDown(); |
| } |
| |
| // callback function to process messages received by onMessages() from IL |
| // client. |
| void handleMessage(Message msg, const BufferInfo* buffer) { |
| (void)buffer; |
| if (msg.type == Message::Type::FILL_BUFFER_DONE) { |
| if (msg.data.extendedBufferData.flags & OMX_BUFFERFLAG_EOS) { |
| eosFlag = true; |
| } |
| if (msg.data.extendedBufferData.rangeLength != 0) { |
| framesReceived += 1; |
| // For decoder components current timestamp always exceeds |
| // previous timestamp |
| EXPECT_GE(msg.data.extendedBufferData.timestampUs, timestampUs); |
| timestampUs = msg.data.extendedBufferData.timestampUs; |
| // Test if current timestamp is among the list of queued |
| // timestamps |
| if (timestampDevTest) { |
| bool tsHit = false; |
| android::List<uint64_t>::iterator it = |
| timestampUslist.begin(); |
| while (it != timestampUslist.end()) { |
| if (*it == timestampUs) { |
| timestampUslist.erase(it); |
| tsHit = true; |
| break; |
| } |
| it++; |
| } |
| if (tsHit == false) { |
| if (timestampUslist.empty() == false) { |
| EXPECT_EQ(tsHit, true) |
| << "TimeStamp not recognized"; |
| } else { |
| std::cout << "[ INFO ] Received non-zero " |
| "output / TimeStamp not recognized \n"; |
| } |
| } |
| } |
| #define WRITE_OUTPUT 0 |
| #if WRITE_OUTPUT |
| static int count = 0; |
| FILE* ofp = nullptr; |
| if (count) |
| ofp = fopen("out.bin", "ab"); |
| else |
| ofp = fopen("out.bin", "wb"); |
| if (ofp != nullptr && |
| portMode[1] == PortMode::PRESET_BYTE_BUFFER) { |
| fwrite(static_cast<void*>(buffer->mMemory->getPointer()), |
| sizeof(char), |
| msg.data.extendedBufferData.rangeLength, ofp); |
| fclose(ofp); |
| count++; |
| } |
| #endif |
| } |
| } else if (msg.type == Message::Type::EVENT) { |
| if (msg.data.eventData.event == OMX_EventPortSettingsChanged) { |
| if ((msg.data.eventData.data2 == OMX_IndexParamPortDefinition || |
| msg.data.eventData.data2 == 0)) { |
| portSettingsChange = true; |
| } |
| } |
| } |
| } |
| |
| enum standardComp { |
| h263, |
| avc, |
| mpeg2, |
| mpeg4, |
| hevc, |
| vp8, |
| vp9, |
| unknown_comp, |
| }; |
| |
| sp<IOmx> omx; |
| sp<CodecObserver> observer; |
| sp<IOmxNode> omxNode; |
| standardComp compName; |
| OMX_VIDEO_CODINGTYPE eCompressionFormat; |
| bool disableTest; |
| PortMode portMode[2]; |
| bool eosFlag; |
| uint32_t framesReceived; |
| uint64_t timestampUs; |
| ::android::List<uint64_t> timestampUslist; |
| bool timestampDevTest; |
| bool isSecure; |
| bool portSettingsChange; |
| |
| protected: |
| static void description(const std::string& description) { |
| RecordProperty("description", description); |
| } |
| }; |
| |
| // Set Default port param. |
| void setDefaultPortParam(sp<IOmxNode> omxNode, OMX_U32 portIndex, |
| OMX_VIDEO_CODINGTYPE eCompressionFormat, |
| OMX_COLOR_FORMATTYPE eColorFormat, |
| OMX_U32 nFrameWidth = 352, OMX_U32 nFrameHeight = 288, |
| OMX_U32 nBitrate = 0, |
| OMX_U32 xFramerate = (24U << 16)) { |
| switch ((int)eCompressionFormat) { |
| case OMX_VIDEO_CodingUnused: |
| setupRAWPort(omxNode, portIndex, nFrameWidth, nFrameHeight, |
| nBitrate, xFramerate, eColorFormat); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // In decoder components, often the input port parameters get updated upon |
| // parsing the header of elementary stream. Client needs to collect this |
| // information to reconfigure other ports that share data with this input |
| // port. |
| void getInputChannelInfo(sp<IOmxNode> omxNode, OMX_U32 kPortIndexInput, |
| uint32_t* nFrameWidth, uint32_t* nFrameHeight, |
| uint32_t* xFramerate) { |
| android::hardware::media::omx::V1_0::Status status; |
| *nFrameWidth = 352; |
| *nFrameHeight = 288; |
| *xFramerate = (24U << 16); |
| |
| OMX_PARAM_PORTDEFINITIONTYPE portDef; |
| status = getPortParam(omxNode, OMX_IndexParamPortDefinition, |
| kPortIndexInput, &portDef); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| *nFrameWidth = portDef.format.video.nFrameWidth; |
| *nFrameHeight = portDef.format.video.nFrameHeight; |
| *xFramerate = portDef.format.video.xFramerate; |
| } |
| } |
| |
| // number of elementary streams per component |
| #define STREAM_COUNT 2 |
| // LookUpTable of clips and metadata for component testing |
| void GetURLForComponent(VideoDecHidlTest::standardComp comp, char* mURL, |
| char* info, size_t streamIndex = 1) { |
| struct CompToURL { |
| VideoDecHidlTest::standardComp comp; |
| const char mURL[STREAM_COUNT][512]; |
| const char info[STREAM_COUNT][512]; |
| }; |
| ASSERT_TRUE(streamIndex < STREAM_COUNT); |
| |
| static const CompToURL kCompToURL[] = { |
| {VideoDecHidlTest::standardComp::avc, |
| {"bbb_avc_176x144_300kbps_60fps.h264", |
| "bbb_avc_640x360_768kbps_30fps.h264"}, |
| {"bbb_avc_176x144_300kbps_60fps.info", |
| "bbb_avc_640x360_768kbps_30fps.info"}}, |
| {VideoDecHidlTest::standardComp::hevc, |
| {"bbb_hevc_176x144_176kbps_60fps.hevc", |
| "bbb_hevc_640x360_1600kbps_30fps.hevc"}, |
| {"bbb_hevc_176x144_176kbps_60fps.info", |
| "bbb_hevc_640x360_1600kbps_30fps.info"}}, |
| {VideoDecHidlTest::standardComp::mpeg2, |
| {"bbb_mpeg2_176x144_105kbps_25fps.m2v", |
| "bbb_mpeg2_352x288_1mbps_60fps.m2v"}, |
| {"bbb_mpeg2_176x144_105kbps_25fps.info", |
| "bbb_mpeg2_352x288_1mbps_60fps.info"}}, |
| {VideoDecHidlTest::standardComp::h263, |
| {"", "bbb_h263_352x288_300kbps_12fps.h263"}, |
| {"", "bbb_h263_352x288_300kbps_12fps.info"}}, |
| {VideoDecHidlTest::standardComp::mpeg4, |
| {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v"}, |
| {"", "bbb_mpeg4_352x288_512kbps_30fps.info"}}, |
| {VideoDecHidlTest::standardComp::vp8, |
| {"bbb_vp8_176x144_240kbps_60fps.vp8", |
| "bbb_vp8_640x360_2mbps_30fps.vp8"}, |
| {"bbb_vp8_176x144_240kbps_60fps.info", |
| "bbb_vp8_640x360_2mbps_30fps.info"}}, |
| {VideoDecHidlTest::standardComp::vp9, |
| {"bbb_vp9_176x144_285kbps_60fps.vp9", |
| "bbb_vp9_640x360_1600kbps_30fps.vp9"}, |
| {"bbb_vp9_176x144_285kbps_60fps.info", |
| "bbb_vp9_640x360_1600kbps_30fps.info"}}, |
| }; |
| |
| for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) { |
| if (kCompToURL[i].comp == comp) { |
| strcat(mURL, kCompToURL[i].mURL[streamIndex]); |
| strcat(info, kCompToURL[i].info[streamIndex]); |
| return; |
| } |
| } |
| } |
| |
| // port settings reconfiguration during runtime. reconfigures frame dimensions |
| void portReconfiguration(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) { |
| android::hardware::media::omx::V1_0::Status status; |
| (void)args; |
| |
| if (msg.data.eventData.event == OMX_EventPortSettingsChanged) { |
| ASSERT_EQ(msg.data.eventData.data1, kPortIndexOutput); |
| if (msg.data.eventData.data2 == OMX_IndexParamPortDefinition || |
| msg.data.eventData.data2 == 0) { |
| // Components can send various kinds of port settings changed events |
| // all at once. Before committing to a full port reconfiguration, |
| // defer any events waiting in the queue to be addressed to a later |
| // point. |
| android::List<Message> msgQueueDefer; |
| while (1) { |
| status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, |
| iBuffer, oBuffer); |
| if (status != |
| android::hardware::media::omx::V1_0::Status::TIMED_OUT) { |
| msgQueueDefer.push_back(msg); |
| continue; |
| } else |
| break; |
| } |
| status = omxNode->sendCommand( |
| toRawCommandType(OMX_CommandPortDisable), kPortIndexOutput); |
| ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); |
| |
| status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, |
| oBuffer); |
| if (status == |
| android::hardware::media::omx::V1_0::Status::TIMED_OUT) { |
| for (size_t i = 0; i < oBuffer->size(); ++i) { |
| // test if client got all its buffers back |
| EXPECT_EQ((*oBuffer)[i].owner, client); |
| // free the buffers |
| status = |
| omxNode->freeBuffer(kPortIndexOutput, (*oBuffer)[i].id); |
| ASSERT_EQ(status, |
| android::hardware::media::omx::V1_0::Status::OK); |
| } |
| status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, |
| iBuffer, oBuffer); |
| ASSERT_EQ(status, |
| android::hardware::media::omx::V1_0::Status::OK); |
| ASSERT_EQ(msg.type, Message::Type::EVENT); |
| ASSERT_EQ(msg.data.eventData.event, OMX_EventCmdComplete); |
| ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortDisable); |
| ASSERT_EQ(msg.data.eventData.data2, kPortIndexOutput); |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, |
| &nFrameHeight, &xFramerate); |
| // get configured color format |
| OMX_PARAM_PORTDEFINITIONTYPE portDef; |
| status = getPortParam(omxNode, OMX_IndexParamPortDefinition, |
| kPortIndexOutput, &portDef); |
| setDefaultPortParam(omxNode, kPortIndexOutput, |
| OMX_VIDEO_CodingUnused, |
| portDef.format.video.eColorFormat, |
| nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| // If you can disable a port, then you should be able to |
| // enable it as well |
| status = omxNode->sendCommand( |
| toRawCommandType(OMX_CommandPortEnable), kPortIndexOutput); |
| ASSERT_EQ(status, |
| android::hardware::media::omx::V1_0::Status::OK); |
| |
| // do not enable the port until all the buffers are supplied |
| status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, |
| iBuffer, oBuffer); |
| ASSERT_EQ( |
| status, |
| android::hardware::media::omx::V1_0::Status::TIMED_OUT); |
| |
| ASSERT_NO_FATAL_FAILURE(allocatePortBuffers( |
| omxNode, oBuffer, kPortIndexOutput, oPortMode, true)); |
| status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, |
| iBuffer, oBuffer); |
| ASSERT_EQ(status, |
| android::hardware::media::omx::V1_0::Status::OK); |
| ASSERT_EQ(msg.type, Message::Type::EVENT); |
| ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortEnable); |
| ASSERT_EQ(msg.data.eventData.data2, kPortIndexOutput); |
| |
| // Push back deferred messages to the list |
| android::List<Message>::iterator it = msgQueueDefer.begin(); |
| while (it != msgQueueDefer.end()) { |
| status = omxNode->dispatchMessage(*it); |
| ASSERT_EQ( |
| status, |
| ::android::hardware::media::omx::V1_0::Status::OK); |
| it++; |
| } |
| |
| // dispatch output buffers |
| for (size_t i = 0; i < oBuffer->size(); i++) { |
| ASSERT_NO_FATAL_FAILURE( |
| dispatchOutputBuffer(omxNode, oBuffer, i, oPortMode)); |
| } |
| } else { |
| ASSERT_TRUE(false); |
| } |
| } else if (msg.data.eventData.data2 == |
| OMX_IndexConfigCommonOutputCrop) { |
| std::cout << "[ INFO ] OMX_EventPortSettingsChanged/ " |
| "OMX_IndexConfigCommonOutputCrop not handled \n"; |
| } else if (msg.data.eventData.data2 == OMX_IndexVendorStartUnused + 3) { |
| std::cout << "[ INFO ] OMX_EventPortSettingsChanged/ " |
| "kDescribeColorAspectsIndex not handled \n"; |
| } |
| } else if (msg.data.eventData.event == OMX_EventError) { |
| std::cerr << "[ ERROR ] OMX_EventError/ " |
| "Decode Frame Call might be failed \n"; |
| ASSERT_TRUE(false); |
| } else { |
| // something unexpected happened |
| ASSERT_TRUE(false); |
| } |
| } |
| |
| // blocking call to ensures application to Wait till all the inputs are consumed |
| void waitOnInputConsumption(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, |
| OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput, |
| PortMode oPortMode) { |
| android::hardware::media::omx::V1_0::Status status; |
| Message msg; |
| int timeOut = TIMEOUT_COUNTER_Q; |
| |
| while (timeOut--) { |
| size_t i = 0; |
| status = |
| observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer); |
| if (status == android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(msg.type, Message::Type::EVENT); |
| ASSERT_NO_FATAL_FAILURE(portReconfiguration( |
| omxNode, observer, iBuffer, oBuffer, kPortIndexInput, |
| kPortIndexOutput, msg, oPortMode, nullptr)); |
| } |
| // status == TIMED_OUT, it could be due to process time being large |
| // than DEFAULT_TIMEOUT or component needs output buffers to start |
| // processing. |
| for (; i < iBuffer->size(); i++) { |
| if ((*iBuffer)[i].owner != client) break; |
| } |
| if (i == iBuffer->size()) break; |
| |
| // Dispatch an output buffer assuming outQueue.empty() is true |
| size_t index; |
| if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) { |
| ASSERT_NO_FATAL_FAILURE( |
| dispatchOutputBuffer(omxNode, oBuffer, index, oPortMode)); |
| timeOut = TIMEOUT_COUNTER_Q; |
| } |
| } |
| } |
| |
| // Decode N Frames |
| void decodeNFrames(sp<IOmxNode> omxNode, sp<CodecObserver> observer, |
| android::Vector<BufferInfo>* iBuffer, |
| android::Vector<BufferInfo>* oBuffer, |
| OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput, |
| std::ifstream& eleStream, android::Vector<FrameData>* Info, |
| int offset, int range, PortMode oPortMode, |
| bool signalEOS = true) { |
| android::hardware::media::omx::V1_0::Status status; |
| Message msg; |
| size_t index; |
| uint32_t flags = 0; |
| int frameID = offset; |
| int timeOut = TIMEOUT_COUNTER_Q; |
| bool iQueued, oQueued; |
| |
| while (1) { |
| iQueued = oQueued = false; |
| status = |
| observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer); |
| // Port Reconfiguration |
| if (status == android::hardware::media::omx::V1_0::Status::OK && |
| msg.type == Message::Type::EVENT) { |
| ASSERT_NO_FATAL_FAILURE(portReconfiguration( |
| omxNode, observer, iBuffer, oBuffer, kPortIndexInput, |
| kPortIndexOutput, msg, oPortMode, nullptr)); |
| } |
| |
| if (frameID == (int)Info->size() || frameID == (offset + range)) break; |
| |
| // Dispatch input buffer |
| if ((index = getEmptyBufferID(iBuffer)) < iBuffer->size()) { |
| char* ipBuffer = static_cast<char*>( |
| static_cast<void*>((*iBuffer)[index].mMemory->getPointer())); |
| ASSERT_LE((*Info)[frameID].bytesCount, |
| static_cast<int>((*iBuffer)[index].mMemory->getSize())); |
| eleStream.read(ipBuffer, (*Info)[frameID].bytesCount); |
| ASSERT_EQ(eleStream.gcount(), (*Info)[frameID].bytesCount); |
| flags = (*Info)[frameID].flags; |
| // Indicate to omx core that the buffer contains a full frame worth |
| // of data |
| flags |= OMX_BUFFERFLAG_ENDOFFRAME; |
| // Indicate the omx core that this is the last buffer it needs to |
| // process |
| if (signalEOS && ((frameID == (int)Info->size() - 1) || |
| (frameID == (offset + range - 1)))) |
| flags |= OMX_BUFFERFLAG_EOS; |
| ASSERT_NO_FATAL_FAILURE(dispatchInputBuffer( |
| omxNode, iBuffer, index, (*Info)[frameID].bytesCount, flags, |
| (*Info)[frameID].timestamp)); |
| frameID++; |
| iQueued = true; |
| } |
| // Dispatch output buffer |
| if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) { |
| ASSERT_NO_FATAL_FAILURE( |
| dispatchOutputBuffer(omxNode, oBuffer, index, oPortMode)); |
| oQueued = true; |
| } |
| // Reset Counters when either input or output buffer is dispatched |
| if (iQueued || oQueued) |
| timeOut = TIMEOUT_COUNTER_Q; |
| else |
| timeOut--; |
| if (timeOut == 0) { |
| ASSERT_TRUE(false) << "Wait on Input/Output is found indefinite"; |
| } |
| } |
| } |
| |
| // DescribeColorFormatParams Copy Constructor (Borrowed from OMXUtils.cpp) |
| android::DescribeColorFormatParams::DescribeColorFormatParams( |
| const android::DescribeColorFormat2Params& params) { |
| eColorFormat = params.eColorFormat; |
| nFrameWidth = params.nFrameWidth; |
| nFrameHeight = params.nFrameHeight; |
| nStride = params.nStride; |
| nSliceHeight = params.nSliceHeight; |
| bUsingNativeBuffers = params.bUsingNativeBuffers; |
| }; |
| |
| bool isColorFormatFlexibleYUV(sp<IOmxNode> omxNode, |
| OMX_COLOR_FORMATTYPE eColorFormat) { |
| android::hardware::media::omx::V1_0::Status status; |
| unsigned int index = OMX_IndexMax, index2 = OMX_IndexMax; |
| omxNode->getExtensionIndex( |
| "OMX.google.android.index.describeColorFormat", |
| [&index](android::hardware::media::omx::V1_0::Status _s, |
| unsigned int _nl) { |
| if (_s == ::android::hardware::media::omx::V1_0::Status::OK) |
| index = _nl; |
| }); |
| omxNode->getExtensionIndex( |
| "OMX.google.android.index.describeColorFormat2", |
| [&index2](android::hardware::media::omx::V1_0::Status _s, |
| unsigned int _nl) { |
| if (_s == ::android::hardware::media::omx::V1_0::Status::OK) |
| index2 = _nl; |
| }); |
| |
| android::DescribeColorFormat2Params describeParams; |
| describeParams.eColorFormat = eColorFormat; |
| describeParams.nFrameWidth = 128; |
| describeParams.nFrameHeight = 128; |
| describeParams.nStride = 128; |
| describeParams.nSliceHeight = 128; |
| describeParams.bUsingNativeBuffers = OMX_FALSE; |
| if (index != OMX_IndexMax) { |
| android::DescribeColorFormatParams describeParamsV1(describeParams); |
| status = getParam(omxNode, static_cast<OMX_INDEXTYPE>(index), |
| &describeParamsV1); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| android::MediaImage& img = describeParamsV1.sMediaImage; |
| if (img.mType == android::MediaImage::MEDIA_IMAGE_TYPE_YUV) { |
| if (img.mNumPlanes == 3 && |
| img.mPlane[img.Y].mHorizSubsampling == 1 && |
| img.mPlane[img.Y].mVertSubsampling == 1) { |
| if (img.mPlane[img.U].mHorizSubsampling == 2 && |
| img.mPlane[img.U].mVertSubsampling == 2 && |
| img.mPlane[img.V].mHorizSubsampling == 2 && |
| img.mPlane[img.V].mVertSubsampling == 2) { |
| if (img.mBitDepth <= 8) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } else if (index2 != OMX_IndexMax) { |
| status = getParam(omxNode, static_cast<OMX_INDEXTYPE>(index2), |
| &describeParams); |
| android::MediaImage2& img = describeParams.sMediaImage; |
| if (img.mType == android::MediaImage2::MEDIA_IMAGE_TYPE_YUV) { |
| if (img.mNumPlanes == 3 && |
| img.mPlane[img.Y].mHorizSubsampling == 1 && |
| img.mPlane[img.Y].mVertSubsampling == 1) { |
| if (img.mPlane[img.U].mHorizSubsampling == 2 && |
| img.mPlane[img.U].mVertSubsampling == 2 && |
| img.mPlane[img.V].mHorizSubsampling == 2 && |
| img.mPlane[img.V].mVertSubsampling == 2) { |
| if (img.mBitDepth <= 8) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| // get default color format for output port |
| void getDefaultColorFormat(sp<IOmxNode> omxNode, OMX_U32 kPortIndexOutput, |
| PortMode oPortMode, |
| OMX_COLOR_FORMATTYPE* eColorFormat) { |
| android::hardware::media::omx::V1_0::Status status; |
| OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; |
| *eColorFormat = OMX_COLOR_FormatUnused; |
| portFormat.nIndex = 0; |
| while (portFormat.nIndex < 512) { |
| status = getPortParam(omxNode, OMX_IndexParamVideoPortFormat, |
| kPortIndexOutput, &portFormat); |
| if (status != ::android::hardware::media::omx::V1_0::Status::OK) break; |
| EXPECT_EQ(portFormat.eCompressionFormat, OMX_VIDEO_CodingUnused); |
| if (oPortMode != PortMode::PRESET_BYTE_BUFFER) { |
| *eColorFormat = portFormat.eColorFormat; |
| break; |
| } |
| if (isColorFormatFlexibleYUV(omxNode, portFormat.eColorFormat)) { |
| *eColorFormat = portFormat.eColorFormat; |
| break; |
| } |
| if (OMX_COLOR_FormatYUV420SemiPlanar == portFormat.eColorFormat || |
| OMX_COLOR_FormatYUV420Planar == portFormat.eColorFormat || |
| OMX_COLOR_FormatYUV420PackedPlanar == portFormat.eColorFormat || |
| OMX_COLOR_FormatYUV420PackedSemiPlanar == portFormat.eColorFormat) { |
| *eColorFormat = portFormat.eColorFormat; |
| break; |
| } |
| portFormat.nIndex++; |
| } |
| } |
| |
| // set component role |
| TEST_F(VideoDecHidlTest, SetRole) { |
| description("Test Set Component Role"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } |
| |
| // port format enumeration |
| TEST_F(VideoDecHidlTest, EnumeratePortFormat) { |
| description("Test Component on Mandatory Port Parameters (Port Format)"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatYUV420Planar; |
| OMX_U32 xFramerate = (24U << 16); |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| status = setVideoPortFormat(omxNode, kPortIndexInput, eCompressionFormat, |
| OMX_COLOR_FormatUnused, 0U); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } |
| |
| // test port settings reconfiguration, elementary stream decode and timestamp |
| // deviation |
| TEST_F(VideoDecHidlTest, DecodeTest) { |
| description("Tests Port Reconfiguration, Decode and timestamp deviation"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| char mURL[512], info[512]; |
| strcpy(mURL, gEnv->getRes().c_str()); |
| strcpy(info, gEnv->getRes().c_str()); |
| GetURLForComponent(compName, mURL, info); |
| |
| std::ifstream eleStream, eleInfo; |
| |
| eleInfo.open(info); |
| ASSERT_EQ(eleInfo.is_open(), true); |
| android::Vector<FrameData> Info; |
| int bytesCount = 0, maxBytesCount = 0; |
| uint32_t flags = 0; |
| uint32_t timestamp = 0; |
| timestampDevTest = true; |
| while (1) { |
| if (!(eleInfo >> bytesCount)) break; |
| eleInfo >> flags; |
| eleInfo >> timestamp; |
| Info.push_back({bytesCount, flags, timestamp}); |
| if (timestampDevTest && (flags != OMX_BUFFERFLAG_CODECCONFIG)) |
| timestampUslist.push_back(timestamp); |
| if (maxBytesCount < bytesCount) maxBytesCount = bytesCount; |
| } |
| eleInfo.close(); |
| |
| // As the frame sizes are known ahead, use it to configure i/p buffer size |
| maxBytesCount = ALIGN_POWER_OF_TWO(maxBytesCount, 10); |
| status = setPortBufferSize(omxNode, kPortIndexInput, maxBytesCount); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set port mode |
| portMode[0] = PortMode::PRESET_BYTE_BUFFER; |
| portMode[1] = PortMode::DYNAMIC_ANW_BUFFER; |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| if (status != ::android::hardware::media::omx::V1_0::Status::OK) { |
| portMode[1] = PortMode::PRESET_BYTE_BUFFER; |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| // Port Reconfiguration |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE(decodeNFrames( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, eleStream, &Info, 0, (int)Info.size(), portMode[1])); |
| eleStream.close(); |
| ASSERT_NO_FATAL_FAILURE( |
| waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, portMode[1])); |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, false, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| if (timestampDevTest) EXPECT_EQ(timestampUslist.empty(), true); |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| // Test for adaptive playback support |
| TEST_F(VideoDecHidlTest, AdaptivePlaybackTest) { |
| description("Tests for Adaptive Playback support"); |
| if (disableTest) return; |
| if (!(compName == avc || compName == hevc || compName == vp8 || |
| compName == vp9 || compName == mpeg2)) |
| return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| |
| // set port mode |
| portMode[0] = PortMode::PRESET_BYTE_BUFFER; |
| portMode[1] = PortMode::DYNAMIC_ANW_BUFFER; |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| if (status != ::android::hardware::media::omx::V1_0::Status::OK) { |
| portMode[1] = PortMode::PRESET_BYTE_BUFFER; |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } |
| |
| // prepare for adaptive playback |
| uint32_t adaptiveMaxWidth = 320; |
| uint32_t adaptiveMaxHeight = 240; |
| status = omxNode->prepareForAdaptivePlayback( |
| kPortIndexOutput, true, adaptiveMaxWidth, adaptiveMaxHeight); |
| if (strncmp(gEnv->getComponent().c_str(), "OMX.google.", 11) == 0) { |
| // SoftOMX Decoders donot support graphic buffer modes. So for them |
| // support for adaptive play back is mandatory in Byte Buffer mode |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } else { |
| // for vendor codecs, support for adaptive play back is optional |
| // in byte buffer mode. |
| if (portMode[1] == PortMode::PRESET_BYTE_BUFFER) return; |
| if (status != ::android::hardware::media::omx::V1_0::Status::OK) return; |
| } |
| |
| // TODO: Handle this better !!! |
| // Without the knowledge of the maximum resolution of the frame to be |
| // decoded it is not possible to choose the size of the input buffer. |
| // The value below is based on the info. files of clips in res folder. |
| status = setPortBufferSize(omxNode, kPortIndexInput, 482304); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| timestampDevTest = true; |
| uint32_t timestampOffset = 0; |
| for (uint32_t i = 0; i < STREAM_COUNT * 2; i++) { |
| std::ifstream eleStream, eleInfo; |
| char mURL[512], info[512]; |
| android::Vector<FrameData> Info; |
| strcpy(mURL, gEnv->getRes().c_str()); |
| strcpy(info, gEnv->getRes().c_str()); |
| GetURLForComponent(compName, mURL, info, i % STREAM_COUNT); |
| eleInfo.open(info); |
| ASSERT_EQ(eleInfo.is_open(), true); |
| int bytesCount = 0; |
| uint32_t flags = 0; |
| uint32_t timestamp = 0; |
| uint32_t timestampMax = 0; |
| while (1) { |
| if (!(eleInfo >> bytesCount)) break; |
| eleInfo >> flags; |
| eleInfo >> timestamp; |
| timestamp += timestampOffset; |
| Info.push_back({bytesCount, flags, timestamp}); |
| if (timestampDevTest && (flags != OMX_BUFFERFLAG_CODECCONFIG)) |
| timestampUslist.push_back(timestamp); |
| if (timestampMax < timestamp) timestampMax = timestamp; |
| } |
| timestampOffset = timestampMax; |
| eleInfo.close(); |
| |
| // Port Reconfiguration |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE( |
| decodeNFrames(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, eleStream, &Info, |
| 0, (int)Info.size(), portMode[1], false)); |
| eleStream.close(); |
| |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, |
| &nFrameHeight, &xFramerate); |
| if ((nFrameWidth > adaptiveMaxWidth) || |
| (nFrameHeight > adaptiveMaxHeight)) { |
| if (nFrameWidth > adaptiveMaxWidth) adaptiveMaxWidth = nFrameWidth; |
| if (nFrameHeight > adaptiveMaxHeight) |
| adaptiveMaxHeight = nFrameHeight; |
| EXPECT_TRUE(portSettingsChange); |
| } else { |
| // In DynamicANW Buffer mode, its ok to do a complete |
| // reconfiguration even if a partial reconfiguration is sufficient. |
| if (portMode[1] != PortMode::DYNAMIC_ANW_BUFFER) |
| EXPECT_FALSE(portSettingsChange); |
| } |
| portSettingsChange = false; |
| } |
| ASSERT_NO_FATAL_FAILURE( |
| waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, portMode[1])); |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, true, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| if (timestampDevTest) EXPECT_EQ(timestampUslist.empty(), true); |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| // end of sequence test |
| TEST_F(VideoDecHidlTest, EOSTest_M) { |
| description("Test End of stream monkeying"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| |
| // set port mode |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| // request EOS at the start |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, true, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| EXPECT_GE(framesReceived, 0U); |
| framesReceived = 0; |
| timestampUs = 0; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| // end of sequence test |
| TEST_F(VideoDecHidlTest, ThumbnailTest) { |
| description("Test Request for thumbnail"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| char mURL[512], info[512]; |
| strcpy(mURL, gEnv->getRes().c_str()); |
| strcpy(info, gEnv->getRes().c_str()); |
| GetURLForComponent(compName, mURL, info); |
| |
| std::ifstream eleStream, eleInfo; |
| |
| eleInfo.open(info); |
| ASSERT_EQ(eleInfo.is_open(), true); |
| android::Vector<FrameData> Info; |
| int bytesCount = 0, maxBytesCount = 0; |
| uint32_t flags = 0; |
| uint32_t timestamp = 0; |
| while (1) { |
| if (!(eleInfo >> bytesCount)) break; |
| eleInfo >> flags; |
| eleInfo >> timestamp; |
| Info.push_back({bytesCount, flags, timestamp}); |
| if (maxBytesCount < bytesCount) maxBytesCount = bytesCount; |
| } |
| eleInfo.close(); |
| |
| // As the frame sizes are known ahead, use it to configure i/p buffer size |
| maxBytesCount = ALIGN_POWER_OF_TWO(maxBytesCount, 10); |
| status = setPortBufferSize(omxNode, kPortIndexInput, maxBytesCount); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set port mode |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| // request EOS for thumbnail |
| size_t i = 0; |
| while (!(Info[i].flags & OMX_BUFFERFLAG_SYNCFRAME)) i++; |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE(decodeNFrames( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, eleStream, &Info, 0, i + 1, portMode[1])); |
| eleStream.close(); |
| ASSERT_NO_FATAL_FAILURE( |
| waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, portMode[1])); |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, false, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| EXPECT_GE(framesReceived, 1U); |
| framesReceived = 0; |
| timestampUs = 0; |
| |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE(decodeNFrames( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, eleStream, &Info, 0, i + 1, portMode[1], false)); |
| eleStream.close(); |
| ASSERT_NO_FATAL_FAILURE( |
| waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, portMode[1])); |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, true, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| EXPECT_GE(framesReceived, 1U); |
| framesReceived = 0; |
| timestampUs = 0; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| // end of sequence test |
| TEST_F(VideoDecHidlTest, SimpleEOSTest) { |
| description("Test End of stream"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| char mURL[512], info[512]; |
| strcpy(mURL, gEnv->getRes().c_str()); |
| strcpy(info, gEnv->getRes().c_str()); |
| GetURLForComponent(compName, mURL, info); |
| |
| std::ifstream eleStream, eleInfo; |
| |
| eleInfo.open(info); |
| ASSERT_EQ(eleInfo.is_open(), true); |
| android::Vector<FrameData> Info; |
| int bytesCount = 0, maxBytesCount = 0; |
| uint32_t flags = 0; |
| uint32_t timestamp = 0; |
| while (1) { |
| if (!(eleInfo >> bytesCount)) break; |
| eleInfo >> flags; |
| eleInfo >> timestamp; |
| Info.push_back({bytesCount, flags, timestamp}); |
| if (maxBytesCount < bytesCount) maxBytesCount = bytesCount; |
| } |
| eleInfo.close(); |
| |
| // As the frame sizes are known ahead, use it to configure i/p buffer size |
| maxBytesCount = ALIGN_POWER_OF_TWO(maxBytesCount, 10); |
| status = setPortBufferSize(omxNode, kPortIndexInput, maxBytesCount); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set port mode |
| portMode[0] = PortMode::PRESET_BYTE_BUFFER; |
| portMode[1] = PortMode::PRESET_ANW_BUFFER; |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| if (status != ::android::hardware::media::omx::V1_0::Status::OK) { |
| portMode[1] = PortMode::PRESET_BYTE_BUFFER; |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| } |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| // request EOS at the end |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE(decodeNFrames(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, |
| eleStream, &Info, 0, (int)Info.size(), |
| portMode[1], false)); |
| eleStream.close(); |
| ASSERT_NO_FATAL_FAILURE( |
| waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, portMode[1])); |
| ASSERT_NO_FATAL_FAILURE(testEOS( |
| omxNode, observer, &iBuffer, &oBuffer, true, eosFlag, portMode, |
| portReconfiguration, kPortIndexInput, kPortIndexOutput, nullptr)); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| framesReceived = 0; |
| timestampUs = 0; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| // test input/output port flush |
| TEST_F(VideoDecHidlTest, FlushTest) { |
| description("Test Flush"); |
| if (disableTest) return; |
| android::hardware::media::omx::V1_0::Status status; |
| uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; |
| status = setRole(omxNode, gEnv->getRole().c_str()); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| OMX_PORT_PARAM_TYPE params; |
| status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); |
| if (status == ::android::hardware::media::omx::V1_0::Status::OK) { |
| ASSERT_EQ(params.nPorts, 2U); |
| kPortIndexInput = params.nStartPortNumber; |
| kPortIndexOutput = kPortIndexInput + 1; |
| } |
| char mURL[512], info[512]; |
| strcpy(mURL, gEnv->getRes().c_str()); |
| strcpy(info, gEnv->getRes().c_str()); |
| GetURLForComponent(compName, mURL, info); |
| |
| std::ifstream eleStream, eleInfo; |
| |
| eleInfo.open(info); |
| ASSERT_EQ(eleInfo.is_open(), true); |
| android::Vector<FrameData> Info; |
| int bytesCount = 0, maxBytesCount = 0; |
| uint32_t flags = 0; |
| uint32_t timestamp = 0; |
| while (1) { |
| if (!(eleInfo >> bytesCount)) break; |
| eleInfo >> flags; |
| eleInfo >> timestamp; |
| Info.push_back({bytesCount, flags, timestamp}); |
| if (maxBytesCount < bytesCount) maxBytesCount = bytesCount; |
| } |
| eleInfo.close(); |
| |
| // As the frame sizes are known ahead, use it to configure i/p buffer size |
| maxBytesCount = ALIGN_POWER_OF_TWO(maxBytesCount, 10); |
| status = setPortBufferSize(omxNode, kPortIndexInput, maxBytesCount); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set port mode |
| status = omxNode->setPortMode(kPortIndexInput, portMode[0]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| status = omxNode->setPortMode(kPortIndexOutput, portMode[1]); |
| ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| |
| // set Port Params |
| uint32_t nFrameWidth, nFrameHeight, xFramerate; |
| getInputChannelInfo(omxNode, kPortIndexInput, &nFrameWidth, &nFrameHeight, |
| &xFramerate); |
| // get default color format |
| OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused; |
| getDefaultColorFormat(omxNode, kPortIndexOutput, portMode[1], |
| &eColorFormat); |
| ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused); |
| status = |
| setVideoPortFormat(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, xFramerate); |
| EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); |
| setDefaultPortParam(omxNode, kPortIndexOutput, OMX_VIDEO_CodingUnused, |
| eColorFormat, nFrameWidth, nFrameHeight, 0, xFramerate); |
| |
| android::Vector<BufferInfo> iBuffer, oBuffer; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, portMode, true)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer)); |
| |
| // Decode 128 frames and flush. here 128 is chosen to ensure there is a key |
| // frame after this so that the below section can be convered for all |
| // components |
| int nFrames = 128; |
| eleStream.open(mURL, std::ifstream::binary); |
| ASSERT_EQ(eleStream.is_open(), true); |
| ASSERT_NO_FATAL_FAILURE(decodeNFrames( |
| omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, |
| kPortIndexOutput, eleStream, &Info, 0, nFrames, portMode[1], false)); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| framesReceived = 0; |
| |
| // Seek to next key frame and start decoding till the end |
| int index = nFrames; |
| bool keyFrame = false; |
| while (index < (int)Info.size()) { |
| if ((Info[index].flags & OMX_BUFFERFLAG_SYNCFRAME) == |
| OMX_BUFFERFLAG_SYNCFRAME) { |
| timestampUs = Info[index - 1].timestamp; |
| keyFrame = true; |
| break; |
| } |
| eleStream.ignore(Info[index].bytesCount); |
| index++; |
| } |
| if (keyFrame) { |
| ASSERT_NO_FATAL_FAILURE( |
| decodeNFrames(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput, eleStream, &Info, |
| index, Info.size() - index, portMode[1], false)); |
| } |
| eleStream.close(); |
| ASSERT_NO_FATAL_FAILURE(flushPorts(omxNode, observer, &iBuffer, &oBuffer, |
| kPortIndexInput, kPortIndexOutput)); |
| framesReceived = 0; |
| |
| // set state to idle |
| ASSERT_NO_FATAL_FAILURE( |
| changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer)); |
| // set state to executing |
| ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, |
| &oBuffer, kPortIndexInput, |
| kPortIndexOutput)); |
| } |
| |
| int main(int argc, char** argv) { |
| gEnv = new ComponentTestEnvironment(); |
| ::testing::AddGlobalTestEnvironment(gEnv); |
| ::testing::InitGoogleTest(&argc, argv); |
| gEnv->init(&argc, argv); |
| int status = gEnv->initFromOptions(argc, argv); |
| if (status == 0) { |
| status = RUN_ALL_TESTS(); |
| ALOGI("Test result = %d", status); |
| } |
| return status; |
| } |