| /* |
| * 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. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "OMX" |
| #include <utils/Log.h> |
| |
| #include "../include/OMX.h" |
| #include "OMXRenderer.h" |
| |
| #include "pv_omxcore.h" |
| |
| #include "../include/OMXNodeInstance.h" |
| |
| #include <binder/IMemory.h> |
| #include <media/stagefright/MediaDebug.h> |
| #include <media/stagefright/QComHardwareRenderer.h> |
| #include <media/stagefright/SoftwareRenderer.h> |
| #include <media/stagefright/TIHardwareRenderer.h> |
| #include <media/stagefright/VideoRenderer.h> |
| |
| #include <OMX_Component.h> |
| |
| namespace android { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| struct OMX::CallbackDispatcher : public RefBase { |
| CallbackDispatcher(OMX *owner); |
| |
| void post(const omx_message &msg); |
| |
| protected: |
| virtual ~CallbackDispatcher(); |
| |
| private: |
| Mutex mLock; |
| |
| OMX *mOwner; |
| bool mDone; |
| Condition mQueueChanged; |
| List<omx_message> mQueue; |
| |
| pthread_t mThread; |
| |
| void dispatch(const omx_message &msg); |
| |
| static void *ThreadWrapper(void *me); |
| void threadEntry(); |
| |
| CallbackDispatcher(const CallbackDispatcher &); |
| CallbackDispatcher &operator=(const CallbackDispatcher &); |
| }; |
| |
| OMX::CallbackDispatcher::CallbackDispatcher(OMX *owner) |
| : mOwner(owner), |
| mDone(false) { |
| pthread_attr_t attr; |
| pthread_attr_init(&attr); |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); |
| |
| pthread_create(&mThread, &attr, ThreadWrapper, this); |
| |
| pthread_attr_destroy(&attr); |
| } |
| |
| OMX::CallbackDispatcher::~CallbackDispatcher() { |
| { |
| Mutex::Autolock autoLock(mLock); |
| |
| mDone = true; |
| mQueueChanged.signal(); |
| } |
| |
| void *dummy; |
| pthread_join(mThread, &dummy); |
| } |
| |
| void OMX::CallbackDispatcher::post(const omx_message &msg) { |
| Mutex::Autolock autoLock(mLock); |
| mQueue.push_back(msg); |
| mQueueChanged.signal(); |
| } |
| |
| void OMX::CallbackDispatcher::dispatch(const omx_message &msg) { |
| OMXNodeInstance *instance = mOwner->findInstance(msg.node); |
| if (instance == NULL) { |
| LOGV("Would have dispatched a message to a node that's already gone."); |
| return; |
| } |
| instance->onMessage(msg); |
| } |
| |
| // static |
| void *OMX::CallbackDispatcher::ThreadWrapper(void *me) { |
| static_cast<CallbackDispatcher *>(me)->threadEntry(); |
| |
| return NULL; |
| } |
| |
| void OMX::CallbackDispatcher::threadEntry() { |
| for (;;) { |
| omx_message msg; |
| |
| { |
| Mutex::Autolock autoLock(mLock); |
| while (!mDone && mQueue.empty()) { |
| mQueueChanged.wait(mLock); |
| } |
| |
| if (mDone) { |
| break; |
| } |
| |
| msg = *mQueue.begin(); |
| mQueue.erase(mQueue.begin()); |
| } |
| |
| dispatch(msg); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| class BufferMeta { |
| public: |
| BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false) |
| : mOwner(owner), |
| mMem(mem), |
| mIsBackup(is_backup) { |
| } |
| |
| BufferMeta(OMX *owner, size_t size) |
| : mOwner(owner), |
| mSize(size), |
| mIsBackup(false) { |
| } |
| |
| void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) { |
| if (!mIsBackup) { |
| return; |
| } |
| |
| memcpy((OMX_U8 *)mMem->pointer() + header->nOffset, |
| header->pBuffer + header->nOffset, |
| header->nFilledLen); |
| } |
| |
| void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) { |
| if (!mIsBackup) { |
| return; |
| } |
| |
| memcpy(header->pBuffer + header->nOffset, |
| (const OMX_U8 *)mMem->pointer() + header->nOffset, |
| header->nFilledLen); |
| } |
| |
| private: |
| OMX *mOwner; |
| sp<IMemory> mMem; |
| size_t mSize; |
| bool mIsBackup; |
| |
| BufferMeta(const BufferMeta &); |
| BufferMeta &operator=(const BufferMeta &); |
| }; |
| |
| OMX::OMX() |
| : mDispatcher(new CallbackDispatcher(this)), |
| mNodeCounter(0) { |
| } |
| |
| void OMX::binderDied(const wp<IBinder> &the_late_who) { |
| OMXNodeInstance *instance; |
| |
| { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t index = mLiveNodes.indexOfKey(the_late_who); |
| CHECK(index >= 0); |
| |
| instance = mLiveNodes.editValueAt(index); |
| mLiveNodes.removeItemsAt(index); |
| |
| invalidateNodeID_l(instance->nodeID()); |
| } |
| |
| instance->onObserverDied(); |
| } |
| |
| status_t OMX::listNodes(List<String8> *list) { |
| OMX_MasterInit(); // XXX Put this somewhere else. |
| |
| list->clear(); |
| |
| OMX_U32 index = 0; |
| char componentName[256]; |
| while (OMX_MasterComponentNameEnum(componentName, sizeof(componentName), index) |
| == OMX_ErrorNone) { |
| list->push_back(String8(componentName)); |
| |
| ++index; |
| } |
| |
| return OK; |
| } |
| |
| status_t OMX::allocateNode( |
| const char *name, const sp<IOMXObserver> &observer, node_id *node) { |
| Mutex::Autolock autoLock(mLock); |
| |
| *node = 0; |
| |
| OMX_MasterInit(); // XXX Put this somewhere else. |
| |
| OMXNodeInstance *instance = new OMXNodeInstance(this, observer); |
| |
| OMX_HANDLETYPE handle; |
| OMX_ERRORTYPE err = OMX_MasterGetHandle( |
| &handle, const_cast<char *>(name), instance, |
| &OMXNodeInstance::kCallbacks); |
| |
| if (err != OMX_ErrorNone) { |
| LOGE("FAILED to allocate omx component '%s'", name); |
| |
| instance->onGetHandleFailed(); |
| |
| return UNKNOWN_ERROR; |
| } |
| |
| *node = makeNodeID(instance); |
| |
| instance->setHandle(*node, handle); |
| |
| mLiveNodes.add(observer->asBinder(), instance); |
| observer->asBinder()->linkToDeath(this); |
| |
| return OK; |
| } |
| |
| status_t OMX::freeNode(node_id node) { |
| OMXNodeInstance *instance = findInstance(node); |
| |
| ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder()); |
| CHECK(index >= 0); |
| mLiveNodes.removeItemsAt(index); |
| instance->observer()->asBinder()->unlinkToDeath(this); |
| |
| return instance->freeNode(); |
| } |
| |
| 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) { |
| return findInstance(node)->getParameter( |
| index, params, size); |
| } |
| |
| status_t OMX::setParameter( |
| node_id node, OMX_INDEXTYPE index, |
| const void *params, size_t 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::useBuffer( |
| node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, |
| buffer_id *buffer) { |
| return findInstance(node)->useBuffer( |
| port_index, params, buffer); |
| } |
| |
| status_t OMX::allocateBuffer( |
| node_id node, OMX_U32 port_index, size_t size, |
| buffer_id *buffer) { |
| return findInstance(node)->allocateBuffer( |
| port_index, size, buffer); |
| } |
| |
| status_t OMX::allocateBufferWithBackup( |
| node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, |
| buffer_id *buffer) { |
| return findInstance(node)->allocateBufferWithBackup( |
| port_index, params, buffer); |
| } |
| |
| 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) { |
| return findInstance(node)->fillBuffer(buffer); |
| } |
| |
| status_t OMX::emptyBuffer( |
| node_id node, |
| buffer_id buffer, |
| OMX_U32 range_offset, OMX_U32 range_length, |
| OMX_U32 flags, OMX_TICKS timestamp) { |
| return findInstance(node)->emptyBuffer( |
| buffer, range_offset, range_length, flags, timestamp); |
| } |
| |
| status_t OMX::getExtensionIndex( |
| node_id node, |
| const char *parameter_name, |
| OMX_INDEXTYPE *index) { |
| return findInstance(node)->getExtensionIndex( |
| parameter_name, index); |
| } |
| |
| 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) { |
| LOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2); |
| |
| omx_message msg; |
| msg.type = omx_message::EVENT; |
| msg.node = node; |
| msg.u.event_data.event = eEvent; |
| msg.u.event_data.data1 = nData1; |
| msg.u.event_data.data2 = nData2; |
| |
| mDispatcher->post(msg); |
| |
| return OMX_ErrorNone; |
| } |
| |
| OMX_ERRORTYPE OMX::OnEmptyBufferDone( |
| node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { |
| LOGV("OnEmptyBufferDone buffer=%p", pBuffer); |
| |
| omx_message msg; |
| msg.type = omx_message::EMPTY_BUFFER_DONE; |
| msg.node = node; |
| msg.u.buffer_data.buffer = pBuffer; |
| |
| mDispatcher->post(msg); |
| |
| return OMX_ErrorNone; |
| } |
| |
| OMX_ERRORTYPE OMX::OnFillBufferDone( |
| node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { |
| LOGV("OnFillBufferDone buffer=%p", pBuffer); |
| |
| omx_message msg; |
| msg.type = omx_message::FILL_BUFFER_DONE; |
| msg.node = node; |
| msg.u.extended_buffer_data.buffer = pBuffer; |
| 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; |
| msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate; |
| |
| mDispatcher->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); |
| } |
| |
| 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); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| sp<IOMXRenderer> OMX::createRenderer( |
| const sp<ISurface> &surface, |
| const char *componentName, |
| OMX_COLOR_FORMATTYPE colorFormat, |
| size_t encodedWidth, size_t encodedHeight, |
| size_t displayWidth, size_t displayHeight) { |
| VideoRenderer *impl = NULL; |
| |
| static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; |
| |
| if (colorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar |
| && !strncmp(componentName, "OMX.qcom.video.decoder.", 23)) { |
| LOGW("Using QComHardwareRenderer."); |
| impl = |
| new QComHardwareRenderer( |
| surface, |
| displayWidth, displayHeight, |
| encodedWidth, encodedHeight); |
| } else if (colorFormat == OMX_COLOR_FormatCbYCrY |
| && !strcmp(componentName, "OMX.TI.Video.Decoder")) { |
| LOGW("Using TIHardwareRenderer."); |
| impl = |
| new TIHardwareRenderer( |
| surface, |
| displayWidth, displayHeight, |
| encodedWidth, encodedHeight); |
| } else { |
| LOGW("Using software renderer."); |
| impl = new SoftwareRenderer( |
| colorFormat, |
| surface, |
| displayWidth, displayHeight, |
| encodedWidth, encodedHeight); |
| } |
| |
| return new OMXRenderer(impl); |
| } |
| |
| OMXRenderer::OMXRenderer(VideoRenderer *impl) |
| : mImpl(impl) { |
| } |
| |
| OMXRenderer::~OMXRenderer() { |
| delete mImpl; |
| mImpl = NULL; |
| } |
| |
| void OMXRenderer::render(IOMX::buffer_id buffer) { |
| OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer; |
| |
| mImpl->render( |
| header->pBuffer + header->nOffset, |
| header->nFilledLen, |
| header->pPlatformPrivate); |
| } |
| |
| } // namespace android |
| |