/* * 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; }