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