/* * Copyright (C) 2009 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. */ #include //#define LOG_NDEBUG 0 #define LOG_TAG "OMX" #include #include #include "../include/OMX.h" #include "../include/OMXNodeInstance.h" #include #include #include #include "OMXMaster.h" #include #include #include namespace android { //////////////////////////////////////////////////////////////////////////////// // This provides the underlying Thread used by CallbackDispatcher. // Note that deriving CallbackDispatcher from Thread does not work. struct OMX::CallbackDispatcherThread : public Thread { CallbackDispatcherThread(CallbackDispatcher *dispatcher) : mDispatcher(dispatcher) { } private: CallbackDispatcher *mDispatcher; bool threadLoop(); CallbackDispatcherThread(const CallbackDispatcherThread &); CallbackDispatcherThread &operator=(const CallbackDispatcherThread &); }; //////////////////////////////////////////////////////////////////////////////// struct OMX::CallbackDispatcher : public RefBase { CallbackDispatcher(OMXNodeInstance *owner); // Posts |msg| to the listener's queue. If |realTime| is true, the listener thread is notified // that a new message is available on the queue. Otherwise, the message stays on the queue, but // the listener is not notified of it. It will process this message when a subsequent message // is posted with |realTime| set to true. void post(const omx_message &msg, bool realTime = true); bool loop(); protected: virtual ~CallbackDispatcher(); private: Mutex mLock; OMXNodeInstance *mOwner; bool mDone; Condition mQueueChanged; std::list mQueue; sp mThread; void dispatch(std::list &messages); CallbackDispatcher(const CallbackDispatcher &); CallbackDispatcher &operator=(const CallbackDispatcher &); }; OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner) : mOwner(owner), mDone(false) { mThread = new CallbackDispatcherThread(this); mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND); } OMX::CallbackDispatcher::~CallbackDispatcher() { { Mutex::Autolock autoLock(mLock); mDone = true; mQueueChanged.signal(); } // A join on self can happen if the last ref to CallbackDispatcher // is released within the CallbackDispatcherThread loop status_t status = mThread->join(); if (status != WOULD_BLOCK) { // Other than join to self, the only other error return codes are // whatever readyToRun() returns, and we don't override that CHECK_EQ(status, (status_t)NO_ERROR); } } void OMX::CallbackDispatcher::post(const omx_message &msg, bool realTime) { Mutex::Autolock autoLock(mLock); mQueue.push_back(msg); if (realTime) { mQueueChanged.signal(); } } void OMX::CallbackDispatcher::dispatch(std::list &messages) { if (mOwner == NULL) { ALOGV("Would have dispatched a message to a node that's already gone."); return; } mOwner->onMessages(messages); } bool OMX::CallbackDispatcher::loop() { for (;;) { std::list messages; { Mutex::Autolock autoLock(mLock); while (!mDone && mQueue.empty()) { mQueueChanged.wait(mLock); } if (mDone) { break; } messages.swap(mQueue); } dispatch(messages); } return false; } //////////////////////////////////////////////////////////////////////////////// bool OMX::CallbackDispatcherThread::threadLoop() { return mDispatcher->loop(); } //////////////////////////////////////////////////////////////////////////////// OMX::OMX() : mMaster(new OMXMaster), mNodeCounter(0) { } OMX::~OMX() { delete mMaster; mMaster = NULL; } void OMX::binderDied(const wp &the_late_who) { OMXNodeInstance *instance; { Mutex::Autolock autoLock(mLock); ssize_t index = mLiveNodes.indexOfKey(the_late_who); if (index < 0) { ALOGE("b/27597103, nonexistent observer on binderDied"); android_errorWriteLog(0x534e4554, "27597103"); return; } instance = mLiveNodes.editValueAt(index); mLiveNodes.removeItemsAt(index); index = mDispatchers.indexOfKey(instance->nodeID()); CHECK(index >= 0); mDispatchers.removeItemsAt(index); invalidateNodeID_l(instance->nodeID()); } instance->onObserverDied(mMaster); } bool OMX::isSecure(node_id node) { OMXNodeInstance *instance = findInstance(node); return (instance == NULL ? false : instance->isSecure()); } bool OMX::livesLocally(node_id /* node */, pid_t pid) { return pid == getpid(); } status_t OMX::listNodes(List *list) { list->clear(); OMX_U32 index = 0; char componentName[256]; while (mMaster->enumerateComponents( componentName, sizeof(componentName), index) == OMX_ErrorNone) { list->push_back(ComponentInfo()); ComponentInfo &info = *--list->end(); info.mName = componentName; Vector roles; OMX_ERRORTYPE err = mMaster->getRolesOfComponent(componentName, &roles); if (err == OMX_ErrorNone) { for (OMX_U32 i = 0; i < roles.size(); ++i) { info.mRoles.push_back(roles[i]); } } ++index; } return OK; } status_t OMX::allocateNode( const char *name, const sp &observer, node_id *node) { Mutex::Autolock autoLock(mLock); *node = 0; OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); OMX_COMPONENTTYPE *handle; OMX_ERRORTYPE err = mMaster->makeComponentInstance( name, &OMXNodeInstance::kCallbacks, instance, &handle); if (err != OMX_ErrorNone) { ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err); instance->onGetHandleFailed(); return StatusFromOMXError(err); } *node = makeNodeID(instance); mDispatchers.add(*node, new CallbackDispatcher(instance)); instance->setHandle(*node, handle); mLiveNodes.add(IInterface::asBinder(observer), instance); IInterface::asBinder(observer)->linkToDeath(this); return OK; } status_t OMX::freeNode(node_id node) { OMXNodeInstance *instance = findInstance(node); { Mutex::Autolock autoLock(mLock); ssize_t index = mLiveNodes.indexOfKey(IInterface::asBinder(instance->observer())); if (index < 0) { // This could conceivably happen if the observer dies at roughly the // same time that a client attempts to free the node explicitly. return OK; } mLiveNodes.removeItemsAt(index); } IInterface::asBinder(instance->observer())->unlinkToDeath(this); status_t err = instance->freeNode(mMaster); { Mutex::Autolock autoLock(mLock); ssize_t index = mDispatchers.indexOfKey(node); CHECK(index >= 0); mDispatchers.removeItemsAt(index); } return err; } status_t OMX::sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) { return findInstance(node)->sendCommand(cmd, param); } status_t OMX::getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { ALOGV("getParameter(%u %#x %p %zd)", node, index, params, size); return findInstance(node)->getParameter( index, params, size); } status_t OMX::setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { ALOGV("setParameter(%u %#x %p %zd)", node, index, params, size); return findInstance(node)->setParameter( index, params, size); } status_t OMX::getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { return findInstance(node)->getConfig( index, params, size); } status_t OMX::setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { return findInstance(node)->setConfig( index, params, size); } status_t OMX::getState( node_id node, OMX_STATETYPE* state) { return findInstance(node)->getState( state); } status_t OMX::enableGraphicBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable) { return findInstance(node)->enableGraphicBuffers(port_index, enable); } status_t OMX::getGraphicBufferUsage( node_id node, OMX_U32 port_index, OMX_U32* usage) { return findInstance(node)->getGraphicBufferUsage(port_index, usage); } status_t OMX::storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable, MetadataBufferType *type) { return findInstance(node)->storeMetaDataInBuffers(port_index, enable, type); } status_t OMX::prepareForAdaptivePlayback( node_id node, OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { return findInstance(node)->prepareForAdaptivePlayback( portIndex, enable, maxFrameWidth, maxFrameHeight); } status_t OMX::configureVideoTunnelMode( node_id node, OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) { return findInstance(node)->configureVideoTunnelMode( portIndex, tunneled, audioHwSync, sidebandHandle); } status_t OMX::useBuffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize, OMX_BOOL crossProcess) { return findInstance(node)->useBuffer( port_index, params, buffer, allottedSize, crossProcess); } status_t OMX::useGraphicBuffer( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer) { return findInstance(node)->useGraphicBuffer( port_index, graphicBuffer, buffer); } status_t OMX::updateGraphicBufferInMeta( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id buffer) { return findInstance(node)->updateGraphicBufferInMeta( port_index, graphicBuffer, buffer); } status_t OMX::createInputSurface( node_id node, OMX_U32 port_index, sp *bufferProducer, MetadataBufferType *type) { return findInstance(node)->createInputSurface( port_index, bufferProducer, type); } status_t OMX::createPersistentInputSurface( sp *bufferProducer, sp *bufferConsumer) { return OMXNodeInstance::createPersistentInputSurface( bufferProducer, bufferConsumer); } status_t OMX::setInputSurface( node_id node, OMX_U32 port_index, const sp &bufferConsumer, MetadataBufferType *type) { return findInstance(node)->setInputSurface(port_index, bufferConsumer, type); } status_t OMX::signalEndOfInputStream(node_id node) { return findInstance(node)->signalEndOfInputStream(); } status_t OMX::allocateBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data) { return findInstance(node)->allocateBuffer( port_index, size, buffer, buffer_data); } status_t OMX::allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize, OMX_BOOL crossProcess) { return findInstance(node)->allocateBufferWithBackup( port_index, params, buffer, allottedSize, crossProcess); } status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) { return findInstance(node)->freeBuffer( port_index, buffer); } status_t OMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { return findInstance(node)->fillBuffer(buffer, fenceFd); } status_t OMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return findInstance(node)->emptyBuffer( buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t OMX::getExtensionIndex( node_id node, const char *parameter_name, OMX_INDEXTYPE *index) { return findInstance(node)->getExtensionIndex( parameter_name, index); } status_t OMX::setInternalOption( node_id node, OMX_U32 port_index, InternalOptionType type, const void *data, size_t size) { return findInstance(node)->setInternalOption(port_index, type, data, size); } OMX_ERRORTYPE OMX::OnEvent( node_id node, OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData) { ALOGV("OnEvent(%d, %" PRIu32", %" PRIu32 ")", eEvent, nData1, nData2); // Forward to OMXNodeInstance. findInstance(node)->onEvent(eEvent, nData1, nData2); sp dispatcher = findDispatcher(node); // output rendered events are not processed as regular events until they hit the observer if (eEvent == OMX_EventOutputRendered) { if (pEventData == NULL) { return OMX_ErrorBadParameter; } // process data from array OMX_VIDEO_RENDEREVENTTYPE *renderData = (OMX_VIDEO_RENDEREVENTTYPE *)pEventData; for (size_t i = 0; i < nData1; ++i) { omx_message msg; msg.type = omx_message::FRAME_RENDERED; msg.node = node; msg.fenceFd = -1; msg.u.render_data.timestamp = renderData[i].nMediaTimeUs; msg.u.render_data.nanoTime = renderData[i].nSystemTimeNs; dispatcher->post(msg, false /* realTime */); } return OMX_ErrorNone; } omx_message msg; msg.type = omx_message::EVENT; msg.node = node; msg.fenceFd = -1; msg.u.event_data.event = eEvent; msg.u.event_data.data1 = nData1; msg.u.event_data.data2 = nData2; dispatcher->post(msg, true /* realTime */); return OMX_ErrorNone; } OMX_ERRORTYPE OMX::OnEmptyBufferDone( node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnEmptyBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::EMPTY_BUFFER_DONE; msg.node = node; msg.fenceFd = fenceFd; msg.u.buffer_data.buffer = buffer; findDispatcher(node)->post(msg); return OMX_ErrorNone; } OMX_ERRORTYPE OMX::OnFillBufferDone( node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnFillBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::FILL_BUFFER_DONE; msg.node = node; msg.fenceFd = fenceFd; msg.u.extended_buffer_data.buffer = buffer; msg.u.extended_buffer_data.range_offset = pBuffer->nOffset; msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; msg.u.extended_buffer_data.flags = pBuffer->nFlags; msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp; findDispatcher(node)->post(msg); return OMX_ErrorNone; } OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) { // mLock is already held. node_id node = (node_id)++mNodeCounter; mNodeIDToInstance.add(node, instance); return node; } OMXNodeInstance *OMX::findInstance(node_id node) { Mutex::Autolock autoLock(mLock); ssize_t index = mNodeIDToInstance.indexOfKey(node); return index < 0 ? NULL : mNodeIDToInstance.valueAt(index); } sp OMX::findDispatcher(node_id node) { Mutex::Autolock autoLock(mLock); ssize_t index = mDispatchers.indexOfKey(node); return index < 0 ? NULL : mDispatchers.valueAt(index); } void OMX::invalidateNodeID(node_id node) { Mutex::Autolock autoLock(mLock); invalidateNodeID_l(node); } void OMX::invalidateNodeID_l(node_id node) { // mLock is held. mNodeIDToInstance.removeItem(node); } } // namespace android