diff options
author | Zhuoyao Zhang <zhuoyao@google.com> | 2017-04-13 19:24:28 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-04-13 19:24:29 +0000 |
commit | 9eeda9561f18b253571519b20700b7d0d68b6370 (patch) | |
tree | 5a859966a575171b753485445c4e67334e299ac5 | |
parent | 61d3d5ee851453a6a4b4470e7c18eab1c8cc733e (diff) | |
parent | 527cf971592a0e06f30b7056374beb1a5257d2f7 (diff) | |
download | android_hardware_interfaces-9eeda9561f18b253571519b20700b7d0d68b6370.tar.gz android_hardware_interfaces-9eeda9561f18b253571519b20700b7d0d68b6370.tar.bz2 android_hardware_interfaces-9eeda9561f18b253571519b20700b7d0d68b6370.zip |
Merge "media.omx VTS test baseline" into oc-dev
11 files changed, 3048 insertions, 0 deletions
diff --git a/media/Android.bp b/media/Android.bp index f2abc678a..34ecf17e1 100644..100755 --- a/media/Android.bp +++ b/media/Android.bp @@ -2,4 +2,7 @@ subdirs = [ "1.0", "omx/1.0", + "omx/1.0/vts/functional/master", + "omx/1.0/vts/functional/component", + "omx/1.0/vts/functional/audio", ] diff --git a/media/omx/1.0/vts/functional/audio/Android.bp b/media/omx/1.0/vts/functional/audio/Android.bp new file mode 100644 index 000000000..d6c73ced7 --- /dev/null +++ b/media/omx/1.0/vts/functional/audio/Android.bp @@ -0,0 +1,79 @@ +// +// 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. +// + +cc_test { + name: "VtsHalMediaOmxV1_0TargetAudioEncTest", + defaults: ["hidl_defaults"], + srcs: ["VtsHalMediaOmxV1_0TargetAudioEncTest.cpp", + "media_audio_hidl_test_common.cpp"], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "libhwbinder", + "libnativehelper", + "libutils", + "libstagefright_foundation", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "android.hardware.media.omx@1.0", + ], + static_libs: ["VtsHalHidlTargetTestBase"], + cflags: [ + "-O0", + "-g", + ], + include_dirs: [ + "frameworks/native/include/media/openmax/", + "hardware/interfaces/media/omx/1.0/vts/functional/common", + ], +} + +cc_test { + name: "VtsHalMediaOmxV1_0TargetAudioDecTest", + defaults: ["hidl_defaults"], + srcs: ["VtsHalMediaOmxV1_0TargetAudioDecTest.cpp", + "media_audio_hidl_test_common.cpp"], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "libhwbinder", + "libnativehelper", + "libutils", + "libstagefright_foundation", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "android.hardware.media.omx@1.0", + ], + static_libs: ["VtsHalHidlTargetTestBase"], + cflags: [ + "-O0", + "-g", + ], + include_dirs: [ + "frameworks/native/include/media/openmax/", + "hardware/interfaces/media/omx/1.0/vts/functional/common", + ], +} + + diff --git a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp new file mode 100644 index 000000000..7c2175351 --- /dev/null +++ b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp @@ -0,0 +1,687 @@ +/* + * 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_audio_dec_test" +#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::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_audio_hidl_test_common.h> +#include <media_hidl_test_common.h> +#include <fstream> + +// A class for test environment setup +class ComponentTestEnvironment : public ::testing::Environment { + public: + virtual void SetUp() {} + virtual void TearDown() {} + + ComponentTestEnvironment() : instance("default") {} + + void setInstance(const char* _instance) { instance = _instance; } + + void setComponent(const char* _component) { component = _component; } + + void setRole(const char* _role) { role = _role; } + + void setQuirks(int _quirks) { quirks = _quirks; } + + const hidl_string getInstance() const { return instance; } + + const hidl_string getComponent() const { return component; } + + const hidl_string getRole() const { return role; } + + int getQuirks() const { return quirks; } + + int initFromOptions(int argc, char** argv) { + static struct option options[] = { + {"instance", required_argument, 0, 'I'}, + {"component", required_argument, 0, 'C'}, + {"role", required_argument, 0, 'R'}, + {"quirks", required_argument, 0, 'Q'}, + {0, 0, 0, 0}}; + + while (true) { + int index = 0; + int c = getopt_long(argc, argv, "I:C:Q:R:", options, &index); + if (c == -1) { + break; + } + + switch (c) { + case 'I': + setInstance(optarg); + break; + case 'C': + setComponent(optarg); + break; + case 'Q': + setQuirks(atoi(optarg)); + break; + case 'R': + setRole(optarg); + break; + case '?': + break; + } + } + + if (optind < argc) { + fprintf(stderr, + "unrecognized option: %s\n\n" + "usage: %s <gtest options> <test options>\n\n" + "test options are:\n\n" + "-I, --instance: HAL instance to test\n" + "-C, --component: OMX component to test\n" + "-R, --Role: OMX component Role\n" + "-Q, --quirks: Component quirks\n", + argv[optind ?: 1], argv[0]); + return 2; + } + return 0; + } + + private: + hidl_string instance; + hidl_string component; + hidl_string role; + // to be removed when IOmxNode::setQuirks is removed + int quirks; +}; + +static ComponentTestEnvironment* gEnv = nullptr; + +class AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + android::hardware::media::omx::V1_0::Status status; + omx = ::testing::VtsHalHidlTargetTestBase::getService<IOmx>( + gEnv->getInstance()); + ASSERT_NE(omx, nullptr); + observer = new CodecObserver(); + ASSERT_NE(observer, nullptr); + ASSERT_EQ(strncmp(gEnv->getComponent().c_str(), "OMX.", 4), 0) + << "Invalid Component Name"; + 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()); + ASSERT_NE(omxNode, nullptr); + ASSERT_NE(gEnv->getRole().empty(), true) << "Invalid Component Role"; + struct StringToName { + const char* Name; + standardComp CompName; + }; + const StringToName kStringToName[] = { + {"mp3", mp3}, {"amrnb", amrnb}, {"amrwb", amrwb}, + {"aac", aac}, {"vorbis", vorbis}, {"opus", opus}, + {"pcm", pcm}, {"flac", flac}, + }; + 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; + } + } + ASSERT_NE(compName, unknown_comp); + struct CompToCoding { + standardComp CompName; + OMX_AUDIO_CODINGTYPE eEncoding; + }; + static const CompToCoding kCompToCoding[] = { + {mp3, OMX_AUDIO_CodingMP3}, + {amrnb, OMX_AUDIO_CodingAMR}, + {amrwb, OMX_AUDIO_CodingAMR}, + {aac, OMX_AUDIO_CodingAAC}, + {vorbis, OMX_AUDIO_CodingVORBIS}, + {pcm, OMX_AUDIO_CodingPCM}, + {opus, (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS}, + {flac, OMX_AUDIO_CodingFLAC}, + }; + static const size_t kNumCompToCoding = + sizeof(kCompToCoding) / sizeof(kCompToCoding[0]); + size_t i; + for (i = 0; i < kNumCompToCoding; ++i) { + if (kCompToCoding[i].CompName == compName) { + eEncoding = kCompToCoding[i].eEncoding; + break; + } + } + ASSERT_NE(i, kNumCompToCoding); + } + + virtual void TearDown() override { + if (omxNode != nullptr) { + EXPECT_TRUE((omxNode->freeNode()).isOk()); + omxNode = nullptr; + } + } + + enum standardComp { + mp3, + amrnb, + amrwb, + aac, + vorbis, + opus, + pcm, + flac, + unknown_comp, + }; + + sp<IOmx> omx; + sp<CodecObserver> observer; + sp<IOmxNode> omxNode; + standardComp compName; + OMX_AUDIO_CODINGTYPE eEncoding; +}; + +void setDefaultPortParam( + sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE eEncoding, + int32_t nChannels = 2, int32_t nSampleRate = 44100, + OMX_NUMERICALDATATYPE eNumData = OMX_NumericalDataSigned, + int32_t nBitPerSample = 16) { + android::hardware::media::omx::V1_0::Status status; + OMX_PARAM_PORTDEFINITIONTYPE portDef; + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex, + &portDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + + portDef.format.audio.bFlagErrorConcealment = OMX_TRUE; + portDef.format.audio.eEncoding = eEncoding; + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex, + &portDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + + switch ((int)eEncoding) { + case OMX_AUDIO_CodingPCM: + setupPCMPort(omxNode, portIndex, nChannels, eNumData, nBitPerSample, + nSampleRate); + break; + default: + ASSERT_TRUE(false); + break; + } +} + +void getInputChannelInfo(sp<IOmxNode> omxNode, OMX_U32 kPortIndexInput, + OMX_AUDIO_CODINGTYPE eEncoding, int32_t* nChannels, + int32_t* nSampleRate) { + *nChannels = 0; + *nSampleRate = 0; + android::hardware::media::omx::V1_0::Status status; + + switch ((int)eEncoding) { + case OMX_AUDIO_CodingPCM: { + OMX_AUDIO_PARAM_PCMMODETYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioPcm, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSamplingRate; + break; + } + case OMX_AUDIO_CodingMP3: { + OMX_AUDIO_PARAM_MP3TYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioMp3, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSampleRate; + break; + } + case OMX_AUDIO_CodingFLAC: { + OMX_AUDIO_PARAM_FLACTYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioFlac, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSampleRate; + break; + } + case OMX_AUDIO_CodingAndroidOPUS: { + OMX_AUDIO_PARAM_ANDROID_OPUSTYPE param; + status = getPortParam(omxNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSampleRate; + break; + } + case OMX_AUDIO_CodingVORBIS: { + OMX_AUDIO_PARAM_VORBISTYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioVorbis, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSampleRate; + break; + } + case OMX_AUDIO_CodingAMR: { + OMX_AUDIO_PARAM_AMRTYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioAmr, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = 8000; + break; + } + case OMX_AUDIO_CodingAAC: { + OMX_AUDIO_PARAM_AACPROFILETYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioAac, + kPortIndexInput, ¶m); + ASSERT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + *nChannels = param.nChannels; + *nSampleRate = param.nSampleRate; + break; + } + default: + ASSERT_TRUE(false); + break; + } +} + +void GetURLForComponent(AudioDecHidlTest::standardComp comp, const char** mURL, + const char** info) { + struct CompToURL { + AudioDecHidlTest::standardComp comp; + const char* mURL; + const char* info; + }; + static const CompToURL kCompToURL[] = { + {AudioDecHidlTest::standardComp::mp3, + "/sdcard/raw/MP3_48KHz_128kbps_s_1_17_CBR.audio.mp3", + "/sdcard/raw/MP3_48KHz_128kbps_s_1_17_CBR.audio.info"}, + {AudioDecHidlTest::standardComp::aac, + "/sdcard/raw/H264_500_AAC_128.audio.aac", + "/sdcard/raw/H264_500_AAC_128.audio.info"}, + {AudioDecHidlTest::standardComp::amrnb, + "/sdcard/raw/H264_320_AMRNB_6.audio.amr", + "/sdcard/raw/H264_320_AMRNB_6.audio.info"}, + {AudioDecHidlTest::standardComp::amrwb, "", ""}, + {AudioDecHidlTest::standardComp::vorbis, "", ""}, + {AudioDecHidlTest::standardComp::opus, "", ""}, + {AudioDecHidlTest::standardComp::flac, "", ""}, + }; + + for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) { + if (kCompToURL[i].comp == comp) { + *mURL = kCompToURL[i].mURL; + *info = kCompToURL[i].info; + return; + } + } +} + +void flushAllPorts(sp<IOmxNode> omxNode, sp<CodecObserver> observer, + android::Vector<BufferInfo>* iBuffer, + android::Vector<BufferInfo>* oBuffer, + OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput) { + android::hardware::media::omx::V1_0::Status status; + Message msg; + // Flush + status = omxNode->sendCommand(toRawCommandType(OMX_CommandFlush), + kPortIndexInput); + 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_CommandFlush); + ASSERT_EQ(msg.data.eventData.data2, kPortIndexInput); + // test if client got all its buffers back + for (size_t i = 0; i < iBuffer->size(); ++i) { + EXPECT_EQ((*iBuffer)[i].owner, client); + } + + status = omxNode->sendCommand(toRawCommandType(OMX_CommandFlush), + kPortIndexOutput); + 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_CommandFlush); + ASSERT_EQ(msg.data.eventData.data2, kPortIndexOutput); + // test if client got all its buffers back + for (size_t i = 0; i < oBuffer->size(); ++i) { + EXPECT_EQ((*oBuffer)[i].owner, client); + } +} + +void decodeNFrames(sp<IOmxNode> omxNode, sp<CodecObserver> observer, + android::Vector<BufferInfo>* iBuffer, + android::Vector<BufferInfo>* oBuffer, + OMX_AUDIO_CODINGTYPE eEncoding, OMX_U32 kPortIndexInput, + OMX_U32 kPortIndexOutput, uint32_t nFrames, + std::ifstream& eleStream, std::ifstream& eleInfo) { + android::hardware::media::omx::V1_0::Status status; + Message msg; + + // dispatch output buffers + for (size_t i = 0; i < oBuffer->size(); i++) { + dispatchOutputBuffer(omxNode, oBuffer, i); + } + // dispatch input buffers + int bytesCount = 0; + for (size_t i = 0; i < iBuffer->size(); i++) { + char* ipBuffer = static_cast<char*>( + static_cast<void*>((*iBuffer)[i].mMemory->getPointer())); + if (!(eleInfo >> bytesCount)) break; + eleStream.read(ipBuffer, bytesCount); + ASSERT_EQ(eleStream.gcount(), bytesCount); + dispatchInputBuffer(omxNode, iBuffer, i, bytesCount, 0, 0); + } + + while (nFrames != 0) { + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer); + if (status == android::hardware::media::omx::V1_0::Status::OK && + msg.type == Message::Type::EVENT && + msg.data.eventData.event == OMX_EventPortSettingsChanged) { + ASSERT_EQ(msg.data.eventData.data1, kPortIndexOutput); + + 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); + + // Port Reconfigurations + int32_t nChannels; + int32_t nSampleRate; + getInputChannelInfo(omxNode, kPortIndexInput, eEncoding, + &nChannels, &nSampleRate); + setDefaultPortParam(omxNode, kPortIndexOutput, + OMX_AUDIO_CodingPCM, nChannels, + nSampleRate); + + // 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); + + allocatePortBuffers(omxNode, oBuffer, kPortIndexOutput); + 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); + + // dispatch output buffers + for (size_t i = 0; i < oBuffer->size(); i++) { + dispatchOutputBuffer(omxNode, oBuffer, i); + } + } else { + ASSERT_TRUE(false); + } + continue; + } + size_t index = 0; + if ((index = getEmptyBufferID(iBuffer)) < iBuffer->size()) { + char* ipBuffer = static_cast<char*>( + static_cast<void*>((*iBuffer)[index].mMemory->getPointer())); + if (!(eleInfo >> bytesCount)) break; + eleStream.read(ipBuffer, bytesCount); + ASSERT_EQ(eleStream.gcount(), bytesCount); + dispatchInputBuffer(omxNode, iBuffer, index, bytesCount, 0, 0); + } + if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) { + dispatchOutputBuffer(omxNode, oBuffer, index); + } + nFrames--; + } +} + +// Set Component Role +TEST_F(AudioDecHidlTest, SetRole) { + 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); +} + +// Enumerate Port Format +TEST_F(AudioDecHidlTest, EnumeratePortFormat) { + 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_IndexParamAudioInit, ¶ms); + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + kPortIndexInput = params.nStartPortNumber; + kPortIndexOutput = kPortIndexInput + 1; + } + status = setAudioPortFormat(omxNode, kPortIndexInput, eEncoding); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = setAudioPortFormat(omxNode, kPortIndexOutput, OMX_AUDIO_CodingPCM); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +// Decode Test +TEST_F(AudioDecHidlTest, DecodeTest) { + 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_IndexParamAudioInit, ¶ms); + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + kPortIndexInput = params.nStartPortNumber; + kPortIndexOutput = kPortIndexInput + 1; + } + const char *mURL = nullptr, *info = nullptr; + GetURLForComponent(compName, &mURL, &info); + EXPECT_NE(mURL, nullptr); + EXPECT_NE(info, nullptr); + + std::ifstream eleStream, eleInfo; + eleStream.open(mURL, std::ifstream::binary); + ASSERT_EQ(eleStream.is_open(), true); + eleInfo.open(info); + ASSERT_EQ(eleInfo.is_open(), true); + + if (eEncoding == OMX_AUDIO_CodingPCM) + setDefaultPortParam(omxNode, kPortIndexInput, eEncoding); + int32_t nChannels; + int32_t nSampleRate; + getInputChannelInfo(omxNode, kPortIndexInput, eEncoding, &nChannels, + &nSampleRate); + setDefaultPortParam(omxNode, kPortIndexOutput, OMX_AUDIO_CodingPCM, + nChannels, nSampleRate); + + Message msg; + android::Vector<BufferInfo> iBuffer, oBuffer; + + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + 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::TIMED_OUT); + + allocatePortBuffers(omxNode, &iBuffer, kPortIndexInput); + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + allocatePortBuffers(omxNode, &oBuffer, kPortIndexOutput); + + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting); + + // Port Reconfiguration + decodeNFrames(omxNode, observer, &iBuffer, &oBuffer, eEncoding, + kPortIndexInput, kPortIndexOutput, (1 << 12), eleStream, + eleInfo); + + // flush + flushAllPorts(omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput, + kPortIndexOutput); + + // set state to Idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // set state to Loaded + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateLoaded); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // dont change state until all buffers are freed + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + for (size_t i = 0; i < iBuffer.size(); ++i) { + status = omxNode->freeBuffer(kPortIndexInput, iBuffer[i].id); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + } + + // dont change state until all buffers are freed + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + for (size_t i = 0; i < oBuffer.size(); ++i) { + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateLoaded); + + eleInfo.close(); + eleStream.close(); +} + +int main(int argc, char** argv) { + gEnv = new ComponentTestEnvironment(); + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + } + return status; +} diff --git a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp new file mode 100644 index 000000000..6a88b1a7b --- /dev/null +++ b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp @@ -0,0 +1,263 @@ +/* + * 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_audio_enc_test" +#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::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_audio_hidl_test_common.h> +#include <media_hidl_test_common.h> +#include <fstream> + +// A class for test environment setup +class ComponentTestEnvironment : public ::testing::Environment { + public: + virtual void SetUp() {} + virtual void TearDown() {} + + ComponentTestEnvironment() : instance("default") {} + + void setInstance(const char* _instance) { instance = _instance; } + + void setComponent(const char* _component) { component = _component; } + + void setRole(const char* _role) { role = _role; } + + void setQuirks(int _quirks) { quirks = _quirks; } + + const hidl_string getInstance() const { return instance; } + + const hidl_string getComponent() const { return component; } + + const hidl_string getRole() const { return role; } + + int getQuirks() const { return quirks; } + + int initFromOptions(int argc, char** argv) { + static struct option options[] = { + {"instance", required_argument, 0, 'I'}, + {"component", required_argument, 0, 'C'}, + {"role", required_argument, 0, 'R'}, + {"quirks", required_argument, 0, 'Q'}, + {0, 0, 0, 0}}; + + while (true) { + int index = 0; + int c = getopt_long(argc, argv, "I:C:Q:R:", options, &index); + if (c == -1) { + break; + } + + switch (c) { + case 'I': + setInstance(optarg); + break; + case 'C': + setComponent(optarg); + break; + case 'Q': + setQuirks(atoi(optarg)); + break; + case 'R': + setRole(optarg); + break; + case '?': + break; + } + } + + if (optind < argc) { + fprintf(stderr, + "unrecognized option: %s\n\n" + "usage: %s <gtest options> <test options>\n\n" + "test options are:\n\n" + "-I, --instance: HAL instance to test\n" + "-C, --component: OMX component to test\n" + "-R, --Role: OMX component Role\n" + "-Q, --quirks: Component quirks\n", + argv[optind ?: 1], argv[0]); + return 2; + } + return 0; + } + + private: + hidl_string instance; + hidl_string component; + hidl_string role; + // to be removed when IOmxNode::setQuirks is removed + int quirks; +}; + +static ComponentTestEnvironment* gEnv = nullptr; + +class AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + android::hardware::media::omx::V1_0::Status status; + omx = ::testing::VtsHalHidlTargetTestBase::getService<IOmx>( + gEnv->getInstance()); + ASSERT_NE(omx, nullptr); + observer = new CodecObserver(); + ASSERT_NE(observer, nullptr); + ASSERT_EQ(strncmp(gEnv->getComponent().c_str(), "OMX.", 4), 0) + << "Invalid Component Name"; + 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()); + ASSERT_NE(omxNode, nullptr); + ASSERT_NE(gEnv->getRole().empty(), true) << "Invalid Component Role"; + struct StringToName { + const char* Name; + standardComp CompName; + }; + const StringToName kStringToName[] = { + {"mp3", mp3}, {"amrnb", amrnb}, {"amrwb", amrwb}, + {"aac", aac}, {"vorbis", vorbis}, {"opus", opus}, + {"pcm", pcm}, {"flac", flac}, + }; + 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; + } + } + ASSERT_NE(compName, unknown_comp); + struct CompToCoding { + standardComp CompName; + OMX_AUDIO_CODINGTYPE eEncoding; + }; + static const CompToCoding kCompToCoding[] = { + {mp3, OMX_AUDIO_CodingMP3}, + {amrnb, OMX_AUDIO_CodingAMR}, + {amrwb, OMX_AUDIO_CodingAMR}, + {aac, OMX_AUDIO_CodingAAC}, + {vorbis, OMX_AUDIO_CodingVORBIS}, + {pcm, OMX_AUDIO_CodingPCM}, + {opus, (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS}, + {flac, OMX_AUDIO_CodingFLAC}, + }; + static const size_t kNumCompToCoding = + sizeof(kCompToCoding) / sizeof(kCompToCoding[0]); + size_t i; + for (i = 0; i < kNumCompToCoding; ++i) { + if (kCompToCoding[i].CompName == compName) { + eEncoding = kCompToCoding[i].eEncoding; + break; + } + } + ASSERT_NE(i, kNumCompToCoding); + } + + virtual void TearDown() override { + if (omxNode != nullptr) { + EXPECT_TRUE((omxNode->freeNode()).isOk()); + omxNode = nullptr; + } + } + + enum standardComp { + mp3, + amrnb, + amrwb, + aac, + vorbis, + opus, + pcm, + flac, + unknown_comp, + }; + + sp<IOmx> omx; + sp<CodecObserver> observer; + sp<IOmxNode> omxNode; + standardComp compName; + OMX_AUDIO_CODINGTYPE eEncoding; +}; + +// Set Component Role +TEST_F(AudioEncHidlTest, SetRole) { + 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); +} + +// Enumerate Port Format +TEST_F(AudioEncHidlTest, EnumeratePortFormat) { + 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_IndexParamAudioInit, ¶ms); + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + kPortIndexInput = params.nStartPortNumber; + kPortIndexOutput = kPortIndexInput + 1; + } + status = setAudioPortFormat(omxNode, kPortIndexInput, OMX_AUDIO_CodingPCM); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = setAudioPortFormat(omxNode, kPortIndexOutput, eEncoding); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +int main(int argc, char** argv) { + gEnv = new ComponentTestEnvironment(); + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + } + return status; +} diff --git a/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.cpp b/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.cpp new file mode 100644 index 000000000..e01e9aaba --- /dev/null +++ b/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.cpp @@ -0,0 +1,316 @@ +/* + * 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_audio_test_common" +#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::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 <hidlmemory/mapping.h> +#include <media_audio_hidl_test_common.h> +#include <media_hidl_test_common.h> +#include <memory> + +void allocatePortBuffers(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + OMX_U32 portIndex) { + android::hardware::media::omx::V1_0::Status status; + OMX_PARAM_PORTDEFINITIONTYPE portDef; + + buffArray->clear(); + + sp<IAllocator> allocator = IAllocator::getService("ashmem"); + EXPECT_NE(allocator, nullptr); + + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex, + &portDef); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + for (size_t i = 0; i < portDef.nBufferCountActual; i++) { + BufferInfo buffer; + buffer.owner = client; + buffer.omxBuffer.type = CodecBuffer::Type::SHARED_MEM; + buffer.omxBuffer.attr.preset.rangeOffset = 0; + buffer.omxBuffer.attr.preset.rangeLength = 0; + bool success; + allocator->allocate( + portDef.nBufferSize, + [&success, &buffer](bool _s, + ::android::hardware::hidl_memory const& mem) { + success = _s; + buffer.omxBuffer.sharedMemory = mem; + }); + ASSERT_EQ(success, true); + buffer.mMemory = mapMemory(buffer.omxBuffer.sharedMemory); + ASSERT_NE(buffer.mMemory, nullptr); + omxNode->useBuffer( + portIndex, buffer.omxBuffer, + [&status, &buffer](android::hardware::media::omx::V1_0::Status _s, + uint32_t id) { + status = _s; + buffer.id = id; + }); + buffArray->push(buffer); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } +} + +size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray) { + for (size_t i = 0; i < buffArray->size(); i++) { + if ((*buffArray)[i].owner == client) return i; + } + return buffArray->size(); +} + +void dispatchInputBuffer(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + size_t bufferIndex, int bytesCount, uint32_t flags, + uint64_t timestamp) { + android::hardware::media::omx::V1_0::Status status; + CodecBuffer t; + t.sharedMemory = android::hardware::hidl_memory(); + t.nativeHandle = android::hardware::hidl_handle(); + t.type = CodecBuffer::Type::PRESET; + t.attr.preset.rangeOffset = 0; + t.attr.preset.rangeLength = bytesCount; + native_handle_t* fenceNh = native_handle_create(0, 0); + ASSERT_NE(fenceNh, nullptr); + status = omxNode->emptyBuffer((*buffArray)[bufferIndex].id, t, flags, + timestamp, fenceNh); + native_handle_close(fenceNh); + native_handle_delete(fenceNh); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + buffArray->editItemAt(bufferIndex).owner = component; +} + +void dispatchOutputBuffer(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + size_t bufferIndex) { + android::hardware::media::omx::V1_0::Status status; + CodecBuffer t; + t.sharedMemory = android::hardware::hidl_memory(); + t.nativeHandle = android::hardware::hidl_handle(); + t.type = CodecBuffer::Type::PRESET; + t.attr.preset.rangeOffset = 0; + t.attr.preset.rangeLength = 0; + native_handle_t* fenceNh = native_handle_create(0, 0); + ASSERT_NE(fenceNh, nullptr); + status = omxNode->fillBuffer((*buffArray)[bufferIndex].id, t, fenceNh); + native_handle_close(fenceNh); + native_handle_delete(fenceNh); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + buffArray->editItemAt(bufferIndex).owner = component; +} + +Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat( + sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE encoding) { + OMX_U32 index = 0; + OMX_AUDIO_PARAM_PORTFORMATTYPE portFormat; + std::vector<OMX_AUDIO_CODINGTYPE> eEncoding; + android::hardware::media::omx::V1_0::Status status; + + while (1) { + portFormat.nIndex = index; + status = getPortParam(omxNode, OMX_IndexParamAudioPortFormat, portIndex, + &portFormat); + if (status != ::android::hardware::media::omx::V1_0::Status::OK) break; + eEncoding.push_back(portFormat.eEncoding); + index++; + if (index == 512) { + EXPECT_LE(index, 512U) + << "Expecting OMX_ErrorNoMore but not received"; + break; + } + } + if (!index) return status; + for (index = 0; index < eEncoding.size(); index++) { + if (eEncoding[index] == encoding) { + portFormat.eEncoding = eEncoding[index]; + break; + } + } + if (index == eEncoding.size()) { + ALOGI("setting default Port format"); + portFormat.eEncoding = eEncoding[0]; + } + // In setParam call nIndex shall be ignored as per omx-il specification. + // see how this holds up by corrupting nIndex + portFormat.nIndex = RANDOM_INDEX; + status = setPortParam(omxNode, OMX_IndexParamAudioPortFormat, portIndex, + &portFormat); + return status; +} + +Return<android::hardware::media::omx::V1_0::Status> setRole( + sp<IOmxNode> omxNode, const char* role) { + OMX_PARAM_COMPONENTROLETYPE params; + strcpy((char*)params.cRole, role); + return setParam(omxNode, OMX_IndexParamStandardComponentRole, ¶ms); +} + +void setupPCMPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + OMX_NUMERICALDATATYPE eNumData, int32_t nBitPerSample, + int32_t nSamplingRate) { + OMX_AUDIO_PARAM_PCMMODETYPE param; + android::hardware::media::omx::V1_0::Status status; + status = getPortParam(omxNode, OMX_IndexParamAudioPcm, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.eNumData = eNumData; + param.eEndian = OMX_EndianBig; + param.bInterleaved = OMX_TRUE; + param.nBitPerSample = nBitPerSample; + param.nSamplingRate = nSamplingRate; + param.ePCMMode = OMX_AUDIO_PCMModeLinear; + switch (nChannels) { + case 1: + param.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 2: + param.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + param.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + break; + default: + EXPECT_TRUE(false); + } + status = setPortParam(omxNode, OMX_IndexParamAudioPcm, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupMP3Port(sp<IOmxNode> omxNode, OMX_U32 portIndex, + OMX_AUDIO_MP3STREAMFORMATTYPE eFormat, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, bool isEncoder) { + if (isEncoder == false) return; + OMX_AUDIO_PARAM_MP3TYPE param; + android::hardware::media::omx::V1_0::Status status; + status = getPortParam(omxNode, OMX_IndexParamAudioMp3, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.nBitRate = nBitRate; + param.nSampleRate = nSampleRate; + param.nAudioBandWidth = 0; + param.eChannelMode = (nChannels == 1) ? OMX_AUDIO_ChannelModeMono + : OMX_AUDIO_ChannelModeStereo; + param.eFormat = eFormat; + status = setPortParam(omxNode, OMX_IndexParamAudioMp3, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupFLACPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nSampleRate, int32_t nCompressionLevel, + bool isEncoder) { + if (isEncoder == false) return; + android::hardware::media::omx::V1_0::Status status; + OMX_AUDIO_PARAM_FLACTYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioFlac, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.nSampleRate = nSampleRate; + param.nCompressionLevel = nCompressionLevel; + status = setPortParam(omxNode, OMX_IndexParamAudioFlac, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupOPUSPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, bool isEncoder) { + if (isEncoder == false) return; + android::hardware::media::omx::V1_0::Status status; + OMX_AUDIO_PARAM_ANDROID_OPUSTYPE param; + status = + getPortParam(omxNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus, + portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.nBitRate = nBitRate; + param.nSampleRate = nSampleRate; + status = + setPortParam(omxNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus, + portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupAMRPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nBitRate, + OMX_AUDIO_AMRBANDMODETYPE eAMRBandMode, bool isEncoder) { + if (isEncoder == false) return; + android::hardware::media::omx::V1_0::Status status; + OMX_AUDIO_PARAM_AMRTYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioAmr, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = 1; + param.nBitRate = nBitRate; + param.eAMRBandMode = eAMRBandMode; + status = setPortParam(omxNode, OMX_IndexParamAudioAmr, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupVORBISPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, int32_t nQuality, + bool isEncoder) { + if (isEncoder == false) return; + android::hardware::media::omx::V1_0::Status status; + OMX_AUDIO_PARAM_VORBISTYPE param; + status = + getPortParam(omxNode, OMX_IndexParamAudioVorbis, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.nBitRate = nBitRate; + param.nSampleRate = nSampleRate; + param.nQuality = nQuality; + status = + setPortParam(omxNode, OMX_IndexParamAudioVorbis, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +void setupAACPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, + OMX_AUDIO_AACPROFILETYPE eAACProfile, + OMX_AUDIO_AACSTREAMFORMATTYPE eAACStreamFormat, + int32_t nChannels, int32_t nBitRate, int32_t nSampleRate, + bool isEncoder) { + if (isEncoder == false) return; + android::hardware::media::omx::V1_0::Status status; + OMX_AUDIO_PARAM_AACPROFILETYPE param; + status = getPortParam(omxNode, OMX_IndexParamAudioAac, portIndex, ¶m); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + param.nChannels = nChannels; + param.nSampleRate = nSampleRate; + param.nBitRate = nBitRate; + param.eAACProfile = eAACProfile; + param.eAACStreamFormat = eAACStreamFormat; + param.eChannelMode = (nChannels == 1) ? OMX_AUDIO_ChannelModeMono + : OMX_AUDIO_ChannelModeStereo; + status = setPortParam(omxNode, OMX_IndexParamAudioAac, portIndex, ¶m); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} diff --git a/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.h b/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.h new file mode 100644 index 000000000..bdf5d52d4 --- /dev/null +++ b/media/omx/1.0/vts/functional/audio/media_audio_hidl_test_common.h @@ -0,0 +1,78 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_AUDIO_HIDL_TEST_COMMON_H +#define MEDIA_AUDIO_HIDL_TEST_COMMON_H + +#include <media_hidl_test_common.h> +/* + * Random Index used for monkey testing while get/set parameters + */ +#define RANDOM_INDEX 1729 + +/* + * Common audio utils + */ +void allocatePortBuffers(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + OMX_U32 portIndex); + +size_t getEmptyBufferID(android::Vector<BufferInfo>* buffArray); + +void dispatchInputBuffer(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + size_t bufferIndex, int bytesCount, uint32_t flags, + uint64_t timestamp); + +void dispatchOutputBuffer(sp<IOmxNode> omxNode, + android::Vector<BufferInfo>* buffArray, + size_t bufferIndex); + +Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat( + sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE encoding); + +Return<android::hardware::media::omx::V1_0::Status> setRole( + sp<IOmxNode> omxNode, const char* role); + +void setupPCMPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + OMX_NUMERICALDATATYPE eNumData, int32_t nBitPerSample, + int32_t nSamplingRate); + +void setupMP3Port(sp<IOmxNode> omxNode, OMX_U32 portIndex, + OMX_AUDIO_MP3STREAMFORMATTYPE eFormat, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, bool isEncoder); + +void setupFLACPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nSampleRate, int32_t nCompressionLevel, + bool isEncoder); + +void setupOPUSPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, bool isEncoder); + +void setupAMRPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nBitRate, + OMX_AUDIO_AMRBANDMODETYPE eAMRBandMode, bool isEncoder); + +void setupVORBISPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, int32_t nChannels, + int32_t nBitRate, int32_t nSampleRate, int32_t nQuality, + bool isEncoder); + +void setupAACPort(sp<IOmxNode> omxNode, OMX_U32 portIndex, + OMX_AUDIO_AACPROFILETYPE eAACProfile, + OMX_AUDIO_AACSTREAMFORMATTYPE eAACStreamFormat, + int32_t nChannels, int32_t nBitRate, int32_t nSampleRate, + bool isEncoder); + +#endif // MEDIA_AUDIO_HIDL_TEST_COMMON_H diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.h b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h new file mode 100644 index 000000000..942146396 --- /dev/null +++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h @@ -0,0 +1,206 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_HIDL_TEST_COMMON_H +#define MEDIA_HIDL_TEST_COMMON_H + +#include <media/stagefright/foundation/ALooper.h> +#include <utils/Condition.h> +#include <utils/List.h> +#include <utils/Mutex.h> + +#include <media/openmax/OMX_Index.h> +#include <media/openmax/OMX_Core.h> +#include <media/openmax/OMX_Component.h> +#include <media/openmax/OMX_IndexExt.h> +#include <media/openmax/OMX_AudioExt.h> + +/* + * TODO: Borrowed from Conversion.h. This is not the ideal way to do it. + * Loose these definitions once you include Conversion.h + */ +inline uint32_t toRawIndexType(OMX_INDEXTYPE l) { + return static_cast<uint32_t>(l); +} + +inline android::hardware::media::omx::V1_0::Status toStatus( + android::status_t l) { + return static_cast<android::hardware::media::omx::V1_0::Status>(l); +} + +inline hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) { + hidl_vec<uint8_t> t; + t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false); + return t; +} + +inline uint32_t toRawCommandType(OMX_COMMANDTYPE l) { + return static_cast<uint32_t>(l); +} + +/* + * Handle Callback functions EmptythisBuffer(), FillthisBuffer(), + * EventHandler() + */ +#define DEFAULT_TIMEOUT 40000 + +enum bufferOwner { + client, + component, + unknown, +}; + +struct BufferInfo { + uint32_t id; + bufferOwner owner; + android::hardware::media::omx::V1_0::CodecBuffer omxBuffer; + ::android::sp<IMemory> mMemory; +}; + +struct CodecObserver : public IOmxObserver { + public: + Return<void> onMessages(const hidl_vec<Message>& messages) override { + android::Mutex::Autolock autoLock(msgLock); + for (hidl_vec<Message>::const_iterator it = messages.begin(); + it != messages.end(); ++it) { + msgQueue.push_back(*it); + } + msgCondition.signal(); + return Void(); + } + android::hardware::media::omx::V1_0::Status dequeueMessage( + Message* msg, int64_t timeoutUs, + android::Vector<BufferInfo>* iBuffers = nullptr, + android::Vector<BufferInfo>* oBuffers = nullptr) { + int64_t finishBy = android::ALooper::GetNowUs() + timeoutUs; + for (;;) { + android::Mutex::Autolock autoLock(msgLock); + android::List<Message>::iterator it = msgQueue.begin(); + while (it != msgQueue.end()) { + if (it->type == + android::hardware::media::omx::V1_0::Message::Type::EVENT) { + *msg = *it; + msgQueue.erase(it); + return ::android::hardware::media::omx::V1_0::Status::OK; + } else if (it->type == android::hardware::media::omx::V1_0:: + Message::Type::FILL_BUFFER_DONE) { + if (oBuffers) { + size_t i; + for (i = 0; i < oBuffers->size(); ++i) { + if ((*oBuffers)[i].id == + it->data.bufferData.buffer) { + oBuffers->editItemAt(i).owner = client; + msgQueue.erase(it); + break; + } + } + EXPECT_LE(i, oBuffers->size()); + } + } else if (it->type == android::hardware::media::omx::V1_0:: + Message::Type::EMPTY_BUFFER_DONE) { + if (iBuffers) { + size_t i; + for (i = 0; i < iBuffers->size(); ++i) { + if ((*iBuffers)[i].id == + it->data.bufferData.buffer) { + iBuffers->editItemAt(i).owner = client; + msgQueue.erase(it); + break; + } + } + EXPECT_LE(i, iBuffers->size()); + } + } + ++it; + } + android::status_t err = + (timeoutUs < 0) + ? msgCondition.wait(msgLock) + : msgCondition.waitRelative( + msgLock, + (finishBy - android::ALooper::GetNowUs()) * 1000); + if (err == android::TIMED_OUT) return toStatus(err); + } + } + + android::List<Message> msgQueue; + android::Mutex msgLock; + android::Condition msgCondition; +}; + +/* + * Useful Wrapper utilities + */ +template <class T> +void InitOMXParams(T* params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +template <class T> +Return<android::hardware::media::omx::V1_0::Status> getParam( + sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) { + android::hardware::media::omx::V1_0::Status status; + InitOMXParams(params); + omxNode->getParameter( + toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)), + [&status, ¶ms](android::hardware::media::omx::V1_0::Status _s, + hidl_vec<uint8_t> const& outParams) { + status = _s; + std::copy(outParams.data(), outParams.data() + outParams.size(), + static_cast<uint8_t*>(static_cast<void*>(params))); + }); + return status; +} + +template <class T> +Return<android::hardware::media::omx::V1_0::Status> setParam( + sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, T* params) { + InitOMXParams(params); + return omxNode->setParameter(toRawIndexType(omxIdx), + inHidlBytes(params, sizeof(*params))); +} + +template <class T> +Return<android::hardware::media::omx::V1_0::Status> getPortParam( + sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { + android::hardware::media::omx::V1_0::Status status; + InitOMXParams(params); + params->nPortIndex = nPortIndex; + omxNode->getParameter( + toRawIndexType(omxIdx), inHidlBytes(params, sizeof(*params)), + [&status, ¶ms](android::hardware::media::omx::V1_0::Status _s, + hidl_vec<uint8_t> const& outParams) { + status = _s; + std::copy(outParams.data(), outParams.data() + outParams.size(), + static_cast<uint8_t*>(static_cast<void*>(params))); + }); + return status; +} + +template <class T> +Return<android::hardware::media::omx::V1_0::Status> setPortParam( + sp<IOmxNode> omxNode, OMX_INDEXTYPE omxIdx, OMX_U32 nPortIndex, T* params) { + InitOMXParams(params); + params->nPortIndex = nPortIndex; + return omxNode->setParameter(toRawIndexType(omxIdx), + inHidlBytes(params, sizeof(*params))); +} + +#endif // MEDIA_HIDL_TEST_COMMON_H diff --git a/media/omx/1.0/vts/functional/component/Android.bp b/media/omx/1.0/vts/functional/component/Android.bp new file mode 100644 index 000000000..02fe18283 --- /dev/null +++ b/media/omx/1.0/vts/functional/component/Android.bp @@ -0,0 +1,45 @@ +// +// 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. +// + +cc_test { + name: "VtsHalMediaOmxV1_0TargetComponentTest", + defaults: ["hidl_defaults"], + srcs: ["VtsHalMediaOmxV1_0TargetComponentTest.cpp"], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "libnativehelper", + "libutils", + "libstagefright_foundation", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "android.hardware.media.omx@1.0", + ], + static_libs: ["VtsHalHidlTargetTestBase"], + cflags: [ + "-O0", + "-g", + ], + include_dirs: [ + "frameworks/native/include/media/openmax/", + "hardware/interfaces/media/omx/1.0/vts/functional/common", + ], +} + diff --git a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp new file mode 100644 index 000000000..fdffd661d --- /dev/null +++ b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp @@ -0,0 +1,1213 @@ +/* + * 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_component_test" +#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::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_hidl_test_common.h> + +// A class for test environment setup +class ComponentTestEnvironment : public ::testing::Environment { + public: + virtual void SetUp() {} + virtual void TearDown() {} + + ComponentTestEnvironment() : instance("default") {} + + void setInstance(const char* _instance) { instance = _instance; } + + void setComponent(const char* _component) { component = _component; } + + void setRole(const char* _role) { role = _role; } + + void setQuirks(int _quirks) { quirks = _quirks; } + + const hidl_string getInstance() const { return instance; } + + const hidl_string getComponent() const { return component; } + + const hidl_string getRole() const { return role; } + + int getQuirks() const { return quirks; } + + int initFromOptions(int argc, char** argv) { + static struct option options[] = { + {"instance", required_argument, 0, 'I'}, + {"component", required_argument, 0, 'C'}, + {"role", required_argument, 0, 'R'}, + {"quirks", required_argument, 0, 'Q'}, + {0, 0, 0, 0}}; + + while (true) { + int index = 0; + int c = getopt_long(argc, argv, "I:C:Q:R:", options, &index); + if (c == -1) { + break; + } + + switch (c) { + case 'I': + setInstance(optarg); + break; + case 'C': + setComponent(optarg); + break; + case 'Q': + setQuirks(atoi(optarg)); + break; + case 'R': + setRole(optarg); + break; + case '?': + break; + } + } + + if (optind < argc) { + fprintf(stderr, + "unrecognized option: %s\n\n" + "usage: %s <gtest options> <test options>\n\n" + "test options are:\n\n" + "-I, --instance: HAL instance to test\n" + "-C, --component: OMX component to test\n" + "-R, --Role: OMX component Role\n" + "-Q, --quirks: Component quirks\n", + argv[optind ?: 1], argv[0]); + return 2; + } + return 0; + } + + private: + hidl_string instance; + hidl_string component; + hidl_string role; + // to be removed when IOmxNode::setQuirks is removed + int quirks; +}; + +static ComponentTestEnvironment* gEnv = nullptr; + +class ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + android::hardware::media::omx::V1_0::Status status; + omx = ::testing::VtsHalHidlTargetTestBase::getService<IOmx>( + gEnv->getInstance()); + ASSERT_NE(omx, nullptr); + observer = new CodecObserver(); + ASSERT_NE(observer, nullptr); + ASSERT_EQ(strncmp(gEnv->getComponent().c_str(), "OMX.", 4), 0) + << "Invalid Component Name"; + 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()); + ASSERT_NE(omxNode, nullptr); + ASSERT_NE(gEnv->getRole().empty(), true) << "Invalid Component Role"; + struct StringToClass { + const char* Class; + standardCompClass CompClass; + }; + const StringToClass kStringToClass[] = { + {"audio_decoder", audio_decoder}, + {"audio_encoder", audio_encoder}, + {"video_decoder", video_decoder}, + {"video_encoder", video_encoder}, + }; + const size_t kNumStringToClass = + sizeof(kStringToClass) / sizeof(kStringToClass[0]); + const char* pch; + char substring[OMX_MAX_STRINGNAME_SIZE]; + strcpy(substring, gEnv->getRole().c_str()); + pch = strchr(substring, '.'); + ASSERT_NE(pch, nullptr) << "Invalid Component Role"; + substring[pch - substring] = '\0'; + compClass = unknown_class; + for (size_t i = 0; i < kNumStringToClass; ++i) { + if (!strcasecmp(substring, kStringToClass[i].Class)) { + compClass = kStringToClass[i].CompClass; + break; + } + } + ASSERT_NE(compClass, unknown_class) << "Invalid Component Class"; + + allocator = ::testing::VtsHalHidlTargetTestBase::getService<IAllocator>( + "ashmem"); + ASSERT_NE(allocator, nullptr); + } + + virtual void TearDown() override { + if (omxNode != nullptr) { + EXPECT_TRUE((omxNode->freeNode()).isOk()); + omxNode = nullptr; + } + } + + enum standardCompClass { + audio_decoder, + audio_encoder, + video_decoder, + video_encoder, + unknown_class, + }; + + sp<IOmx> omx; + sp<CodecObserver> observer; + sp<IOmxNode> omxNode; + sp<IAllocator> allocator; + standardCompClass compClass; +}; + +#define RANDOM_INDEX 1729 + +void allocatePortBuffers(sp<IOmxNode> omxNode, sp<IAllocator> allocator, + android::Vector<BufferInfo>* buffArray, + OMX_U32 portIndex) { + android::hardware::media::omx::V1_0::Status status; + OMX_PARAM_PORTDEFINITIONTYPE portDef; + + buffArray->clear(); + + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex, + &portDef); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + for (size_t i = 0; i < portDef.nBufferCountActual; i++) { + BufferInfo buffer; + buffer.owner = client; + buffer.omxBuffer.type = CodecBuffer::Type::SHARED_MEM; + buffer.omxBuffer.attr.preset.rangeOffset = 0; + buffer.omxBuffer.attr.preset.rangeLength = 0; + bool success; + allocator->allocate( + portDef.nBufferSize, + [&success, &buffer](bool _s, + ::android::hardware::hidl_memory const& mem) { + success = _s; + buffer.omxBuffer.sharedMemory = mem; + }); + ASSERT_EQ(success, true); + + omxNode->useBuffer( + portIndex, buffer.omxBuffer, + [&status, &buffer](android::hardware::media::omx::V1_0::Status _s, + uint32_t id) { + status = _s; + buffer.id = id; + }); + buffArray->push(buffer); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } +} + +Return<android::hardware::media::omx::V1_0::Status> setVideoPortFormat( + sp<IOmxNode> omxNode, OMX_U32 portIndex, + OMX_VIDEO_CODINGTYPE compressionFormat, OMX_COLOR_FORMATTYPE colorFormat, + OMX_U32 frameRate) { + OMX_U32 index = 0; + OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; + std::vector<OMX_COLOR_FORMATTYPE> eColorFormat; + std::vector<OMX_VIDEO_CODINGTYPE> eCompressionFormat; + android::hardware::media::omx::V1_0::Status status; + + while (1) { + portFormat.nIndex = index; + status = getPortParam(omxNode, OMX_IndexParamVideoPortFormat, portIndex, + &portFormat); + if (status != ::android::hardware::media::omx::V1_0::Status::OK) break; + if (compressionFormat == OMX_VIDEO_CodingUnused) + eColorFormat.push_back(portFormat.eColorFormat); + else + eCompressionFormat.push_back(portFormat.eCompressionFormat); + index++; + if (index == 512) { + EXPECT_LE(index, 512U) + << "Expecting OMX_ErrorNoMore but not received"; + break; + } + } + if (!index) return status; + if (compressionFormat == OMX_VIDEO_CodingUnused) { + for (index = 0; index < eColorFormat.size(); index++) { + if (eColorFormat[index] == colorFormat) { + portFormat.eColorFormat = eColorFormat[index]; + break; + } + } + if (index == eColorFormat.size()) { + ALOGI("setting default color format"); + portFormat.eColorFormat = eColorFormat[0]; + } + portFormat.eCompressionFormat = OMX_VIDEO_CodingUnused; + } else { + for (index = 0; index < eCompressionFormat.size(); index++) { + if (eCompressionFormat[index] == compressionFormat) { + portFormat.eCompressionFormat = eCompressionFormat[index]; + break; + } + } + if (index == eCompressionFormat.size()) { + ALOGI("setting default compression format"); + portFormat.eCompressionFormat = eCompressionFormat[0]; + } + portFormat.eColorFormat = OMX_COLOR_FormatUnused; + } + // In setParam call nIndex shall be ignored as per omx-il specification. + // see how this holds up by corrupting nIndex + portFormat.nIndex = RANDOM_INDEX; + portFormat.xFramerate = frameRate; + status = setPortParam(omxNode, OMX_IndexParamVideoPortFormat, portIndex, + &portFormat); + return status; +} + +Return<android::hardware::media::omx::V1_0::Status> setAudioPortFormat( + sp<IOmxNode> omxNode, OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE encoding) { + OMX_U32 index = 0; + OMX_AUDIO_PARAM_PORTFORMATTYPE portFormat; + std::vector<OMX_AUDIO_CODINGTYPE> eEncoding; + android::hardware::media::omx::V1_0::Status status; + + while (1) { + portFormat.nIndex = index; + status = getPortParam(omxNode, OMX_IndexParamAudioPortFormat, portIndex, + &portFormat); + if (status != ::android::hardware::media::omx::V1_0::Status::OK) break; + eEncoding.push_back(portFormat.eEncoding); + index++; + if (index == 512) { + EXPECT_LE(index, 512U) + << "Expecting OMX_ErrorNoMore but not received"; + break; + } + } + if (!index) return status; + for (index = 0; index < eEncoding.size(); index++) { + if (eEncoding[index] == encoding) { + portFormat.eEncoding = eEncoding[index]; + break; + } + } + if (index == eEncoding.size()) { + ALOGI("setting default Port format"); + portFormat.eEncoding = eEncoding[0]; + } + // In setParam call nIndex shall be ignored as per omx-il specification. + // see how this holds up by corrupting nIndex + portFormat.nIndex = RANDOM_INDEX; + status = setPortParam(omxNode, OMX_IndexParamAudioPortFormat, portIndex, + &portFormat); + return status; +} + +Return<android::hardware::media::omx::V1_0::Status> setRole( + sp<IOmxNode> omxNode, const char* role) { + OMX_PARAM_COMPONENTROLETYPE params; + strcpy((char*)params.cRole, role); + return setParam(omxNode, OMX_IndexParamStandardComponentRole, ¶ms); +} + +// Set Component Role +TEST_F(ComponentHidlTest, SetRole) { + android::hardware::media::omx::V1_0::Status status; + status = setRole(omxNode, gEnv->getRole().c_str()); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +// Get Number of Ports and their Indices for all Domains +// (Audio/Video/Image/Other) +TEST_F(ComponentHidlTest, GetPortIndices) { + android::hardware::media::omx::V1_0::Status status; + OMX_PORT_PARAM_TYPE params; + + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + + // All standard OMX components shall support following OMX Index types + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = getParam(omxNode, OMX_IndexParamImageInit, ¶ms); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = getParam(omxNode, OMX_IndexParamOtherInit, ¶ms); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); +} + +// Enumerate Port Format +TEST_F(ComponentHidlTest, EnumeratePortFormat) { + 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; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + 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; + } + + OMX_COLOR_FORMATTYPE colorFormat = OMX_COLOR_FormatYUV420Planar; + OMX_U32 frameRate = 24 << 16; + + if (compClass == audio_encoder) { + status = + setAudioPortFormat(omxNode, kPortIndexInput, OMX_AUDIO_CodingPCM); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = setAudioPortFormat(omxNode, kPortIndexOutput, + OMX_AUDIO_CodingAutoDetect); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } else if (compClass == audio_decoder) { + status = setAudioPortFormat(omxNode, kPortIndexInput, + OMX_AUDIO_CodingAutoDetect); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = + setAudioPortFormat(omxNode, kPortIndexOutput, OMX_AUDIO_CodingPCM); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } else if (compClass == video_encoder) { + status = + setVideoPortFormat(omxNode, kPortIndexInput, OMX_VIDEO_CodingUnused, + colorFormat, frameRate); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = setVideoPortFormat(omxNode, kPortIndexOutput, + OMX_VIDEO_CodingAutoDetect, + OMX_COLOR_FormatUnused, 0U); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } else { + status = setVideoPortFormat(omxNode, kPortIndexInput, + OMX_VIDEO_CodingAutoDetect, + OMX_COLOR_FormatUnused, 0U); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = + setVideoPortFormat(omxNode, kPortIndexOutput, + OMX_VIDEO_CodingUnused, colorFormat, frameRate); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + } +} + +// r/w default i/o port parameters +TEST_F(ComponentHidlTest, SetDefaultPortParams) { + 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; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + 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; + } + + OMX_PARAM_PORTDEFINITIONTYPE iPortDef; + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexInput, &iPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + + if (status == android::hardware::media::omx::V1_0::Status::OK) { + EXPECT_EQ(iPortDef.eDir, OMX_DirInput); + EXPECT_EQ(iPortDef.bEnabled, OMX_TRUE); + EXPECT_EQ(iPortDef.bPopulated, 0); + EXPECT_GE(iPortDef.nBufferCountMin, 1U); + EXPECT_GE(iPortDef.nBufferCountActual, iPortDef.nBufferCountMin); + if (compClass == audio_encoder || compClass == audio_decoder) { + EXPECT_EQ(iPortDef.eDomain, OMX_PortDomainAudio); + if (compClass == audio_decoder) { + iPortDef.format.audio.bFlagErrorConcealment = OMX_TRUE; + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexInput, &iPortDef); + } + EXPECT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + } else if (compClass == video_encoder || compClass == video_decoder) { + EXPECT_EQ(iPortDef.eDomain, OMX_PortDomainVideo); + } + OMX_PARAM_PORTDEFINITIONTYPE dummy = iPortDef; + iPortDef.nBufferCountActual = iPortDef.nBufferCountMin - 1; + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexInput, &iPortDef); + EXPECT_NE(status, ::android::hardware::media::omx::V1_0::Status::OK); + // Edit Read-Only fields. + iPortDef.eDir = OMX_DirOutput; // Read Only field + iPortDef.nBufferCountActual = dummy.nBufferCountActual << 1; + iPortDef.nBufferCountMin = dummy.nBufferCountMin + << 1; // Read Only field + iPortDef.nBufferSize = dummy.nBufferSize << 1; // Read Only field + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexInput, &iPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexInput, &iPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + EXPECT_EQ(iPortDef.nBufferCountActual, dummy.nBufferCountActual << 1); + if ((iPortDef.eDir != OMX_DirInput) || + (iPortDef.nBufferCountMin != dummy.nBufferCountMin) || + (iPortDef.nBufferSize != dummy.nBufferSize)) { + ALOGI( + "Warning, Component does not seem to preserve Read-Only " + "fields"); + printf( + "Warning, Component does not seem to preserve Read-Only fields " + "\n"); + } + } + + OMX_PARAM_PORTDEFINITIONTYPE oPortDef; + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexOutput, &oPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + EXPECT_EQ(oPortDef.eDir, OMX_DirOutput); + EXPECT_EQ(oPortDef.bEnabled, OMX_TRUE); + EXPECT_EQ(oPortDef.bPopulated, 0); + EXPECT_GE(oPortDef.nBufferCountMin, 1U); + EXPECT_GE(oPortDef.nBufferCountActual, oPortDef.nBufferCountMin); + if (compClass == audio_encoder || compClass == audio_decoder) { + EXPECT_EQ(oPortDef.eDomain, OMX_PortDomainAudio); + if (compClass == audio_encoder) { + oPortDef.format.audio.bFlagErrorConcealment = OMX_TRUE; + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexOutput, &oPortDef); + } + EXPECT_EQ(status, + ::android::hardware::media::omx::V1_0::Status::OK); + } else if (compClass == video_encoder || compClass == video_decoder) { + EXPECT_EQ(oPortDef.eDomain, OMX_PortDomainVideo); + } + OMX_PARAM_PORTDEFINITIONTYPE dummy = oPortDef; + oPortDef.nBufferCountActual = oPortDef.nBufferCountMin - 1; + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexOutput, &oPortDef); + EXPECT_NE(status, ::android::hardware::media::omx::V1_0::Status::OK); + // Edit Read-Only fields. + oPortDef.eDir = OMX_DirInput; // Read Only field + oPortDef.nBufferCountActual = dummy.nBufferCountActual << 1; + oPortDef.nBufferCountMin = dummy.nBufferCountMin + << 1; // Read Only field + oPortDef.nBufferSize = dummy.nBufferSize << 1; // Read Only field + status = setPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexOutput, &oPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + status = getPortParam(omxNode, OMX_IndexParamPortDefinition, + kPortIndexOutput, &oPortDef); + EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + EXPECT_EQ(oPortDef.nBufferCountActual, dummy.nBufferCountActual << 1); + if ((oPortDef.eDir != OMX_DirOutput) || + (oPortDef.nBufferCountMin != dummy.nBufferCountMin) || + (oPortDef.nBufferSize != dummy.nBufferSize)) { + ALOGI( + "Warning, Component does not seem to preserve Read-Only " + "fields"); + printf( + "Warning, Component does not seem to preserve Read-Only fields " + "\n"); + } + } +} + +// Test State Transitions & Flush +TEST_F(ComponentHidlTest, StateTransitions_Flush) { + android::hardware::media::omx::V1_0::Status status; + uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; + Message msg; + + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + 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; + } + + android::Vector<BufferInfo> iBuffer, oBuffer; + + // set state to idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // Dont switch states until the ports got their buffers + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + // allocate buffers on input port + allocatePortBuffers(omxNode, allocator, &iBuffer, kPortIndexInput); + + // Dont switch states until the ports got their buffers + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + // allocate buffers on output port + allocatePortBuffers(omxNode, allocator, &oBuffer, kPortIndexOutput); + + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // set state to executing + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting); + + CodecBuffer t; + t.sharedMemory = android::hardware::hidl_memory(); + t.nativeHandle = android::hardware::hidl_handle(); + t.type = CodecBuffer::Type::PRESET; + t.attr.preset.rangeOffset = 0; + t.attr.preset.rangeLength = 0; + for (size_t i = 0; i < oBuffer.size(); ++i) { + native_handle_t* fenceNh = native_handle_create(0, 0); + status = omxNode->fillBuffer(oBuffer[i].id, t, fenceNh); + native_handle_close(fenceNh); + native_handle_delete(fenceNh); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + oBuffer.editItemAt(i).owner = component; + } + + // set state to Idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // test if client got all its buffers back + for (size_t i = 0; i < oBuffer.size(); ++i) { + EXPECT_EQ(oBuffer[i].owner, client); + } + + // set state to executing + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting); + + for (size_t i = 0; i < oBuffer.size(); i += 2) { + native_handle_t* fenceNh = native_handle_create(0, 0); + status = omxNode->fillBuffer(oBuffer[i].id, t, fenceNh); + native_handle_close(fenceNh); + native_handle_delete(fenceNh); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + oBuffer.editItemAt(i).owner = component; + } + + status = omxNode->sendCommand(toRawCommandType(OMX_CommandFlush), + kPortIndexOutput); + 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_CommandFlush); + ASSERT_EQ(msg.data.eventData.data2, kPortIndexOutput); + + // test if client got all its buffers back + for (size_t i = 0; i < oBuffer.size(); ++i) { + EXPECT_EQ(oBuffer[i].owner, client); + } + + // set state to Idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // set state to Loaded + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateLoaded); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // dont change state until all buffers are freed + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + for (size_t i = 0; i < iBuffer.size(); ++i) { + status = omxNode->freeBuffer(kPortIndexInput, iBuffer[i].id); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + } + + // dont change state until all buffers are freed + status = + observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &iBuffer, &oBuffer); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + for (size_t i = 0; i < oBuffer.size(); ++i) { + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateLoaded); +} + +// Test State Transitions +TEST_F(ComponentHidlTest, StateTransitions_M) { + android::hardware::media::omx::V1_0::Status status; + uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; + Message msg; + + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + 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; + } + + android::Vector<BufferInfo> iBuffer, oBuffer; + + // set state to loaded ; receive error OMX_ErrorSameState + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateLoaded); + EXPECT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // set state to executing ; receive error OMX_ErrorIncorrectStateTransition + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + EXPECT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // set state to idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // allocate buffers on i/o port + allocatePortBuffers(omxNode, allocator, &iBuffer, kPortIndexInput); + allocatePortBuffers(omxNode, allocator, &oBuffer, kPortIndexOutput); + + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // set state to idle ; receive error OMX_ErrorSameState + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + EXPECT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // set state to executing + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting); + + // set state to executing ; receive error OMX_ErrorSameState + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + EXPECT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // set state to Loaded ; receive error OMX_ErrorIncorrectStateTransition + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateLoaded); + EXPECT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // set state to Idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // set state to Loaded + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateLoaded); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + for (size_t i = 0; i < iBuffer.size(); ++i) { + status = omxNode->freeBuffer(kPortIndexInput, iBuffer[i].id); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + } + for (size_t i = 0; i < oBuffer.size(); ++i) { + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateLoaded); +} + +// Enable and Disable Input and Output ports when the component is in Loaded +// State +TEST_F(ComponentHidlTest, PortEnableDisable_Loaded) { + android::hardware::media::omx::V1_0::Status status; + OMX_U32 portBase = 0; + Message msg; + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); + } + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + portBase = params.nStartPortNumber; + } + + for (size_t i = portBase; i < portBase + 2; i++) { + status = + omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), i); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + ASSERT_EQ(msg.type, Message::Type::EVENT); + if (msg.data.eventData.event == OMX_EventCmdComplete) { + ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortDisable); + ASSERT_EQ(msg.data.eventData.data2, i); + // If you can disable a port, then you should be able to enable it + // as well + status = omxNode->sendCommand( + toRawCommandType(OMX_CommandPortEnable), i); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT); + 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, i); + } else if (msg.data.eventData.event == OMX_EventError) { + ALOGI("Port %d Disabling failed with error %d", (int)i, + (int)msg.data.eventData.event); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } +} + +// Enable and Disable Input and Output ports when the component is in Idle State +TEST_F(ComponentHidlTest, PortEnableDisable_Idle) { + android::hardware::media::omx::V1_0::Status status; + uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; + OMX_U32 portBase = 0; + Message msg; + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); + } + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + portBase = params.nStartPortNumber; + } + kPortIndexInput = portBase; + kPortIndexOutput = portBase + 1; + + // Component State :: Idle + android::Vector<BufferInfo> pBuffer[2]; + // set state to idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // allocate buffers on input port + allocatePortBuffers(omxNode, allocator, &pBuffer[0], kPortIndexInput); + allocatePortBuffers(omxNode, allocator, &pBuffer[1], kPortIndexOutput); + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &pBuffer[0], + &pBuffer[1]); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + for (size_t i = portBase; i < portBase + 2; i++) { + status = + omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), i); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &pBuffer[0], + &pBuffer[1]); + if (status == android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(msg.type, Message::Type::EVENT); + if (msg.data.eventData.event == OMX_EventCmdComplete) { + // do not disable the port until all the buffers are freed + ASSERT_TRUE(false); + } else if (msg.data.eventData.event == OMX_EventError) { + ALOGI("Port %d Disabling failed with error %d", (int)i, + (int)msg.data.eventData.event); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } else if (status == + android::hardware::media::omx::V1_0::Status::TIMED_OUT) { + for (size_t j = 0; j < pBuffer[i - portBase].size(); ++j) { + status = omxNode->freeBuffer(i, pBuffer[i - portBase][j].id); + ASSERT_EQ(status, + android::hardware::media::omx::V1_0::Status::OK); + } + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, + &pBuffer[0], &pBuffer[1]); + 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, i); + + // If you can disable a port, then you should be able to enable it + // as well + status = omxNode->sendCommand( + toRawCommandType(OMX_CommandPortEnable), i); + 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, + &pBuffer[0], &pBuffer[1]); + ASSERT_EQ(status, + android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + allocatePortBuffers(omxNode, allocator, &pBuffer[i - portBase], i); + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, + &pBuffer[0], &pBuffer[1]); + 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, i); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } +} + +// Enable and Disable Input and Output ports when the component is in execute +TEST_F(ComponentHidlTest, PortEnableDisable_Execute) { + android::hardware::media::omx::V1_0::Status status; + uint32_t kPortIndexInput = 0, kPortIndexOutput = 1; + OMX_U32 portBase = 0; + Message msg; + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); + } + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + portBase = params.nStartPortNumber; + } + kPortIndexInput = portBase; + kPortIndexOutput = portBase + 1; + + // Component State :: Idle + android::Vector<BufferInfo> pBuffer[2]; + // set state to idle + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateIdle); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + // allocate buffers on input port + allocatePortBuffers(omxNode, allocator, &pBuffer[0], kPortIndexInput); + allocatePortBuffers(omxNode, allocator, &pBuffer[1], kPortIndexOutput); + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &pBuffer[0], + &pBuffer[1]); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateIdle); + + // Component State :: Execute + status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet), + OMX_StateExecuting); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &pBuffer[0], + &pBuffer[1]); + 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_CommandStateSet); + ASSERT_EQ(msg.data.eventData.data2, OMX_StateExecuting); + + CodecBuffer t; + t.sharedMemory = android::hardware::hidl_memory(); + t.nativeHandle = android::hardware::hidl_handle(); + t.type = CodecBuffer::Type::PRESET; + t.attr.preset.rangeOffset = 0; + t.attr.preset.rangeLength = 0; + for (size_t i = 0; i < pBuffer[1].size(); ++i) { + native_handle_t* fenceNh = native_handle_create(0, 0); + status = omxNode->fillBuffer(pBuffer[1][i].id, t, fenceNh); + native_handle_close(fenceNh); + native_handle_delete(fenceNh); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + pBuffer[1].editItemAt(i).owner = component; + } + + for (size_t i = portBase; i < portBase + 2; i++) { + status = + omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), i); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, &pBuffer[0], + &pBuffer[1]); + if (status == android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(msg.type, Message::Type::EVENT); + if (msg.data.eventData.event == OMX_EventCmdComplete) { + // do not disable the port until all the buffers are freed + ASSERT_TRUE(false); + } else if (msg.data.eventData.event == OMX_EventError) { + ALOGI("Port %d Disabling failed with error %d", (int)i, + (int)msg.data.eventData.event); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } else if (status == + android::hardware::media::omx::V1_0::Status::TIMED_OUT) { + for (size_t j = 0; j < pBuffer[i - portBase].size(); ++j) { + // test if client got all its buffers back + EXPECT_EQ(pBuffer[i - portBase][j].owner, client); + // free the buffers + status = omxNode->freeBuffer(i, pBuffer[i - portBase][j].id); + ASSERT_EQ(status, + android::hardware::media::omx::V1_0::Status::OK); + } + + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, + &pBuffer[0], &pBuffer[1]); + 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, i); + + // If you can disable a port, then you should be able to enable it + // as well + status = omxNode->sendCommand( + toRawCommandType(OMX_CommandPortEnable), i); + 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, + &pBuffer[0], &pBuffer[1]); + ASSERT_EQ(status, + android::hardware::media::omx::V1_0::Status::TIMED_OUT); + + allocatePortBuffers(omxNode, allocator, &pBuffer[i - portBase], i); + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, + &pBuffer[0], &pBuffer[1]); + 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, i); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } +} + +// Enable and Disable Input and Output ports, (in loaded state) +TEST_F(ComponentHidlTest, PortEnableDisable_M) { + android::hardware::media::omx::V1_0::Status status; + OMX_U32 portBase = 0; + Message msg; + status = setRole(omxNode, gEnv->getRole().c_str()); + ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK); + OMX_PORT_PARAM_TYPE params; + if (compClass == audio_decoder || compClass == audio_encoder) { + status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms); + } else { + status = getParam(omxNode, OMX_IndexParamVideoInit, ¶ms); + } + if (status == ::android::hardware::media::omx::V1_0::Status::OK) { + ASSERT_EQ(params.nPorts, 2U); + portBase = params.nStartPortNumber; + } + + // disable invalid port, expecting OMX_ErrorBadPortIndex + status = omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), + RANDOM_INDEX); + ASSERT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // enable invalid port, expecting OMX_ErrorBadPortIndex + status = omxNode->sendCommand(toRawCommandType(OMX_CommandPortEnable), + RANDOM_INDEX); + ASSERT_NE(status, android::hardware::media::omx::V1_0::Status::OK); + + // disable all ports + status = + omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), OMX_ALL); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + for (size_t i = 0; i < 2; i++) { + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + ASSERT_EQ(msg.type, Message::Type::EVENT); + if (msg.data.eventData.event == OMX_EventCmdComplete) { + ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortDisable); + if (msg.data.eventData.data2 != portBase || + msg.data.eventData.data2 != portBase + 1) + EXPECT_TRUE(false); + } else if (msg.data.eventData.event == OMX_EventError) { + ALOGI("Port %d Disabling failed with error %d", (int)i, + (int)msg.data.eventData.event); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } + + // enable all ports + status = + omxNode->sendCommand(toRawCommandType(OMX_CommandPortEnable), OMX_ALL); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + for (size_t i = 0; i < 2; i++) { + status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT); + ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK); + ASSERT_EQ(msg.type, Message::Type::EVENT); + if (msg.data.eventData.event == OMX_EventCmdComplete) { + ASSERT_EQ(msg.data.eventData.data1, OMX_CommandPortEnable); + if (msg.data.eventData.data2 != portBase || + msg.data.eventData.data2 != portBase + 1) + EXPECT_TRUE(false); + } else if (msg.data.eventData.event == OMX_EventError) { + ALOGI("Port %d Enabling failed with error %d", (int)i, + (int)msg.data.eventData.event); + } else { + // something unexpected happened + ASSERT_TRUE(false); + } + } +} + +int main(int argc, char** argv) { + gEnv = new ComponentTestEnvironment(); + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + } + return status; +} diff --git a/media/omx/1.0/vts/functional/master/Android.bp b/media/omx/1.0/vts/functional/master/Android.bp new file mode 100644 index 000000000..6edbf4a6f --- /dev/null +++ b/media/omx/1.0/vts/functional/master/Android.bp @@ -0,0 +1,38 @@ +// +// 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. +// + +cc_test { + name: "VtsHalMediaOmxV1_0TargetMasterTest", + defaults: ["hidl_defaults"], + srcs: ["VtsHalMediaOmxV1_0TargetMasterTest.cpp"], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "libnativehelper", + "libutils", + "android.hardware.media.omx@1.0", + ], + static_libs: ["VtsHalHidlTargetTestBase"], + cflags: [ + "-O0", + "-g", + ], +} + diff --git a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp new file mode 100644 index 000000000..65b999e88 --- /dev/null +++ b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp @@ -0,0 +1,120 @@ +/* + * 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_master_test" +#include <android-base/logging.h> + +#include <android/hardware/media/omx/1.0/IOmx.h> +#include <android/hardware/media/omx/1.0/types.h> +#include <getopt.h> +#include <log/log.h> +#include <VtsHalHidlTargetTestBase.h> + +using ::android::hardware::media::omx::V1_0::IOmx; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; +using ::android::sp; + +// A class for test environment setup +class ComponentTestEnvironment : public ::testing::Environment { + public: + virtual void SetUp() {} + virtual void TearDown() {} + + ComponentTestEnvironment() : instance("default") {} + + void setInstance(const char* _instance) { instance = _instance; } + + const hidl_string getInstance() const { return instance; } + + int initFromOptions(int argc, char** argv) { + static struct option options[] = { + {"instance", required_argument, 0, 'I'}, {0, 0, 0, 0}}; + + while (true) { + int index = 0; + int c = getopt_long(argc, argv, "I:", options, &index); + if (c == -1) { + break; + } + + switch (c) { + case 'I': + setInstance(optarg); + break; + case '?': + break; + } + } + + if (optind < argc) { + fprintf(stderr, + "unrecognized option: %s\n\n" + "usage: %s <gtest options> <test options>\n\n" + "test options are:\n\n" + "-I, --instance: HAL instance to test\n", + argv[optind ?: 1], argv[0]); + return 2; + } + return 0; + } + + private: + hidl_string instance; +}; + +static ComponentTestEnvironment* gEnv = nullptr; + +class MasterHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + omx = ::testing::VtsHalHidlTargetTestBase::getService<IOmx>( + gEnv->getInstance()); + ASSERT_NE(omx, nullptr); + } + + virtual void TearDown() override {} + + sp<IOmx> omx; +}; + +// enumerate list of components and roles +TEST_F(MasterHidlTest, ListNodes) { + android::hardware::media::omx::V1_0::Status status; + hidl_vec<IOmx::ComponentInfo> nodeList; + EXPECT_TRUE( + omx->listNodes([&status, &nodeList]( + android::hardware::media::omx::V1_0::Status _s, + hidl_vec<IOmx::ComponentInfo> const& _nl) { + status = _s; + nodeList = _nl; + }) + .isOk()); +} + +int main(int argc, char** argv) { + gEnv = new ComponentTestEnvironment(); + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + } + return status; +} |