/* * Copyright (C) 2011 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_NDEBUG 0 #define LOG_TAG "SimpleSoftOMXComponent" #include #include "include/SimpleSoftOMXComponent.h" #include #include #include namespace android { SimpleSoftOMXComponent::SimpleSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) : SoftOMXComponent(name, callbacks, appData, component), mLooper(new ALooper), mHandler(new AHandlerReflector(this)), mState(OMX_StateLoaded), mTargetState(OMX_StateLoaded) { mLooper->setName(name); mLooper->registerHandler(mHandler); mLooper->start( false, // runOnCallingThread false, // canCallJava ANDROID_PRIORITY_FOREGROUND); } void SimpleSoftOMXComponent::prepareForDestruction() { // The looper's queue may still contain messages referencing this // object. Make sure those are flushed before returning so that // a subsequent dlunload() does not pull out the rug from under us. mLooper->unregisterHandler(mHandler->id()); mLooper->stop(); } OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { CHECK(data == NULL); sp msg = new AMessage(kWhatSendCommand, mHandler->id()); msg->setInt32("cmd", cmd); msg->setInt32("param", param); msg->post(); return OMX_ErrorNone; } bool SimpleSoftOMXComponent::isSetParameterAllowed( OMX_INDEXTYPE index, const OMX_PTR params) const { if (mState == OMX_StateLoaded) { return true; } OMX_U32 portIndex; switch (index) { case OMX_IndexParamPortDefinition: { portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex; break; } case OMX_IndexParamAudioPcm: { portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex; break; } case OMX_IndexParamAudioAac: { portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex; break; } default: return false; } CHECK(portIndex < mPorts.size()); return !mPorts.itemAt(portIndex).mDef.bEnabled; } OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter( OMX_INDEXTYPE index, OMX_PTR params) { Mutex::Autolock autoLock(mLock); return internalGetParameter(index, params); } OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter( OMX_INDEXTYPE index, const OMX_PTR params) { Mutex::Autolock autoLock(mLock); CHECK(isSetParameterAllowed(index, params)); return internalSetParameter(index, params); } OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter( OMX_INDEXTYPE index, OMX_PTR params) { switch (index) { case OMX_IndexParamPortDefinition: { OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params; if (defParams->nPortIndex >= mPorts.size() || defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { return OMX_ErrorUndefined; } const PortInfo *port = &mPorts.itemAt(defParams->nPortIndex); memcpy(defParams, &port->mDef, sizeof(port->mDef)); return OMX_ErrorNone; } default: return OMX_ErrorUnsupportedIndex; } } OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR params) { switch (index) { case OMX_IndexParamPortDefinition: { OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params; if (defParams->nPortIndex >= mPorts.size() || defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { return OMX_ErrorUndefined; } PortInfo *port = &mPorts.editItemAt(defParams->nPortIndex); if (defParams->nBufferSize != port->mDef.nBufferSize) { CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize); port->mDef.nBufferSize = defParams->nBufferSize; } if (defParams->nBufferCountActual != port->mDef.nBufferCountActual) { CHECK_GE(defParams->nBufferCountActual, port->mDef.nBufferCountMin); port->mDef.nBufferCountActual = defParams->nBufferCountActual; } return OMX_ErrorNone; } default: return OMX_ErrorUnsupportedIndex; } } OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( OMX_BUFFERHEADERTYPE **header, OMX_U32 portIndex, OMX_PTR appPrivate, OMX_U32 size, OMX_U8 *ptr) { Mutex::Autolock autoLock(mLock); CHECK_LT(portIndex, mPorts.size()); *header = new OMX_BUFFERHEADERTYPE; (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE); (*header)->nVersion.s.nVersionMajor = 1; (*header)->nVersion.s.nVersionMinor = 0; (*header)->nVersion.s.nRevision = 0; (*header)->nVersion.s.nStep = 0; (*header)->pBuffer = ptr; (*header)->nAllocLen = size; (*header)->nFilledLen = 0; (*header)->nOffset = 0; (*header)->pAppPrivate = appPrivate; (*header)->pPlatformPrivate = NULL; (*header)->pInputPortPrivate = NULL; (*header)->pOutputPortPrivate = NULL; (*header)->hMarkTargetComponent = NULL; (*header)->pMarkData = NULL; (*header)->nTickCount = 0; (*header)->nTimeStamp = 0; (*header)->nFlags = 0; (*header)->nOutputPortIndex = portIndex; (*header)->nInputPortIndex = portIndex; PortInfo *port = &mPorts.editItemAt(portIndex); CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE); CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual); port->mBuffers.push(); BufferInfo *buffer = &port->mBuffers.editItemAt(port->mBuffers.size() - 1); buffer->mHeader = *header; buffer->mOwnedByUs = false; if (port->mBuffers.size() == port->mDef.nBufferCountActual) { port->mDef.bPopulated = OMX_TRUE; checkTransitions(); } return OMX_ErrorNone; } OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer( OMX_BUFFERHEADERTYPE **header, OMX_U32 portIndex, OMX_PTR appPrivate, OMX_U32 size) { OMX_U8 *ptr = new OMX_U8[size]; OMX_ERRORTYPE err = useBuffer(header, portIndex, appPrivate, size, ptr); if (err != OMX_ErrorNone) { delete[] ptr; ptr = NULL; return err; } CHECK((*header)->pPlatformPrivate == NULL); (*header)->pPlatformPrivate = ptr; return OMX_ErrorNone; } OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( OMX_U32 portIndex, OMX_BUFFERHEADERTYPE *header) { Mutex::Autolock autoLock(mLock); CHECK_LT(portIndex, mPorts.size()); PortInfo *port = &mPorts.editItemAt(portIndex); #if 0 // XXX CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded) || port->mDef.bEnabled == OMX_FALSE); #endif bool found = false; for (size_t i = 0; i < port->mBuffers.size(); ++i) { BufferInfo *buffer = &port->mBuffers.editItemAt(i); if (buffer->mHeader == header) { CHECK(!buffer->mOwnedByUs); if (header->pPlatformPrivate != NULL) { // This buffer's data was allocated by us. CHECK(header->pPlatformPrivate == header->pBuffer); delete[] header->pBuffer; header->pBuffer = NULL; } delete header; header = NULL; port->mBuffers.removeAt(i); port->mDef.bPopulated = OMX_FALSE; checkTransitions(); found = true; break; } } CHECK(found); return OMX_ErrorNone; } OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { sp msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id()); msg->setPointer("header", buffer); msg->post(); return OMX_ErrorNone; } OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { sp msg = new AMessage(kWhatFillThisBuffer, mHandler->id()); msg->setPointer("header", buffer); msg->post(); return OMX_ErrorNone; } OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) { Mutex::Autolock autoLock(mLock); *state = mState; return OMX_ErrorNone; } void SimpleSoftOMXComponent::onMessageReceived(const sp &msg) { Mutex::Autolock autoLock(mLock); uint32_t msgType = msg->what(); ALOGV("msgType = %d", msgType); switch (msgType) { case kWhatSendCommand: { int32_t cmd, param; CHECK(msg->findInt32("cmd", &cmd)); CHECK(msg->findInt32("param", ¶m)); onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param); break; } case kWhatEmptyThisBuffer: case kWhatFillThisBuffer: { OMX_BUFFERHEADERTYPE *header; CHECK(msg->findPointer("header", (void **)&header)); CHECK(mState == OMX_StateExecuting && mTargetState == mState); bool found = false; size_t portIndex = (kWhatEmptyThisBuffer == msgType)? header->nInputPortIndex: header->nOutputPortIndex; PortInfo *port = &mPorts.editItemAt(portIndex); for (size_t j = 0; j < port->mBuffers.size(); ++j) { BufferInfo *buffer = &port->mBuffers.editItemAt(j); if (buffer->mHeader == header) { CHECK(!buffer->mOwnedByUs); buffer->mOwnedByUs = true; CHECK((msgType == kWhatEmptyThisBuffer && port->mDef.eDir == OMX_DirInput) || (port->mDef.eDir == OMX_DirOutput)); port->mQueue.push_back(buffer); onQueueFilled(portIndex); found = true; break; } } CHECK(found); break; } default: TRESPASS(); break; } } void SimpleSoftOMXComponent::onSendCommand( OMX_COMMANDTYPE cmd, OMX_U32 param) { switch (cmd) { case OMX_CommandStateSet: { onChangeState((OMX_STATETYPE)param); break; } case OMX_CommandPortEnable: case OMX_CommandPortDisable: { onPortEnable(param, cmd == OMX_CommandPortEnable); break; } case OMX_CommandFlush: { onPortFlush(param, true /* sendFlushComplete */); break; } default: TRESPASS(); break; } } void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { // We shouldn't be in a state transition already. CHECK_EQ((int)mState, (int)mTargetState); switch (mState) { case OMX_StateLoaded: CHECK_EQ((int)state, (int)OMX_StateIdle); break; case OMX_StateIdle: CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting); break; case OMX_StateExecuting: { CHECK_EQ((int)state, (int)OMX_StateIdle); for (size_t i = 0; i < mPorts.size(); ++i) { onPortFlush(i, false /* sendFlushComplete */); } mState = OMX_StateIdle; notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL); break; } default: TRESPASS(); } mTargetState = state; checkTransitions(); } void SimpleSoftOMXComponent::onReset() { // no-op } void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { CHECK_LT(portIndex, mPorts.size()); PortInfo *port = &mPorts.editItemAt(portIndex); CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); CHECK(port->mDef.bEnabled == !enable); if (!enable) { port->mDef.bEnabled = OMX_FALSE; port->mTransition = PortInfo::DISABLING; for (size_t i = 0; i < port->mBuffers.size(); ++i) { BufferInfo *buffer = &port->mBuffers.editItemAt(i); if (buffer->mOwnedByUs) { buffer->mOwnedByUs = false; if (port->mDef.eDir == OMX_DirInput) { notifyEmptyBufferDone(buffer->mHeader); } else { CHECK_EQ(port->mDef.eDir, OMX_DirOutput); notifyFillBufferDone(buffer->mHeader); } } } port->mQueue.clear(); } else { port->mTransition = PortInfo::ENABLING; } checkTransitions(); } void SimpleSoftOMXComponent::onPortFlush( OMX_U32 portIndex, bool sendFlushComplete) { if (portIndex == OMX_ALL) { for (size_t i = 0; i < mPorts.size(); ++i) { onPortFlush(i, sendFlushComplete); } if (sendFlushComplete) { notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL); } return; } CHECK_LT(portIndex, mPorts.size()); PortInfo *port = &mPorts.editItemAt(portIndex); CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); for (size_t i = 0; i < port->mBuffers.size(); ++i) { BufferInfo *buffer = &port->mBuffers.editItemAt(i); if (!buffer->mOwnedByUs) { continue; } buffer->mHeader->nFilledLen = 0; buffer->mHeader->nOffset = 0; buffer->mHeader->nFlags = 0; buffer->mOwnedByUs = false; if (port->mDef.eDir == OMX_DirInput) { notifyEmptyBufferDone(buffer->mHeader); } else { CHECK_EQ(port->mDef.eDir, OMX_DirOutput); notifyFillBufferDone(buffer->mHeader); } } port->mQueue.clear(); if (sendFlushComplete) { notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL); onPortFlushCompleted(portIndex); } } void SimpleSoftOMXComponent::checkTransitions() { if (mState != mTargetState) { bool transitionComplete = true; if (mState == OMX_StateLoaded) { CHECK_EQ((int)mTargetState, (int)OMX_StateIdle); for (size_t i = 0; i < mPorts.size(); ++i) { const PortInfo &port = mPorts.itemAt(i); if (port.mDef.bEnabled == OMX_FALSE) { continue; } if (port.mDef.bPopulated == OMX_FALSE) { transitionComplete = false; break; } } } else if (mTargetState == OMX_StateLoaded) { CHECK_EQ((int)mState, (int)OMX_StateIdle); for (size_t i = 0; i < mPorts.size(); ++i) { const PortInfo &port = mPorts.itemAt(i); if (port.mDef.bEnabled == OMX_FALSE) { continue; } size_t n = port.mBuffers.size(); if (n > 0) { CHECK_LE(n, port.mDef.nBufferCountActual); if (n == port.mDef.nBufferCountActual) { CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE); } else { CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE); } transitionComplete = false; break; } } } if (transitionComplete) { mState = mTargetState; if (mState == OMX_StateLoaded) { onReset(); } notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); } } for (size_t i = 0; i < mPorts.size(); ++i) { PortInfo *port = &mPorts.editItemAt(i); if (port->mTransition == PortInfo::DISABLING) { if (port->mBuffers.empty()) { ALOGV("Port %d now disabled.", i); port->mTransition = PortInfo::NONE; notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL); onPortEnableCompleted(i, false /* enabled */); } } else if (port->mTransition == PortInfo::ENABLING) { if (port->mDef.bPopulated == OMX_TRUE) { ALOGV("Port %d now enabled.", i); port->mTransition = PortInfo::NONE; port->mDef.bEnabled = OMX_TRUE; notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL); onPortEnableCompleted(i, true /* enabled */); } } } } void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) { CHECK_EQ(def.nPortIndex, mPorts.size()); mPorts.push(); PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1); info->mDef = def; info->mTransition = PortInfo::NONE; } void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) { } void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) { } void SimpleSoftOMXComponent::onPortEnableCompleted( OMX_U32 portIndex, bool enabled) { } List & SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) { CHECK_LT(portIndex, mPorts.size()); return mPorts.editItemAt(portIndex).mQueue; } SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo( OMX_U32 portIndex) { CHECK_LT(portIndex, mPorts.size()); return &mPorts.editItemAt(portIndex); } } // namespace android