blob: 56b60551fd7d2a0748f8a3a9baa56331a7eaa807 [file] [log] [blame]
/*
* 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 <inttypes.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "OMX"
#include <utils/Log.h>
#include <dlfcn.h>
#include "../include/OMX.h"
#include "../include/OMXNodeInstance.h"
#include <binder/IMemory.h>
#include <media/stagefright/foundation/ADebug.h>
#include <utils/threads.h>
#include "OMXMaster.h"
#include <OMX_AsString.h>
#include <OMX_Component.h>
#include <OMX_VideoExt.h>
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<omx_message> mQueue;
sp<CallbackDispatcherThread> mThread;
void dispatch(std::list<omx_message> &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<omx_message> &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<omx_message> 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<IBinder> &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<ComponentInfo> *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<String8> 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<IOMXObserver> &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<IMemory> &params,
buffer_id *buffer, OMX_U32 allottedSize) {
return findInstance(node)->useBuffer(
port_index, params, buffer, allottedSize);
}
status_t OMX::useGraphicBuffer(
node_id node, OMX_U32 port_index,
const sp<GraphicBuffer> &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> &graphicBuffer, buffer_id buffer) {
return findInstance(node)->updateGraphicBufferInMeta(
port_index, graphicBuffer, buffer);
}
status_t OMX::createInputSurface(
node_id node, OMX_U32 port_index,
sp<IGraphicBufferProducer> *bufferProducer, MetadataBufferType *type) {
return findInstance(node)->createInputSurface(
port_index, bufferProducer, type);
}
status_t OMX::createPersistentInputSurface(
sp<IGraphicBufferProducer> *bufferProducer,
sp<IGraphicBufferConsumer> *bufferConsumer) {
return OMXNodeInstance::createPersistentInputSurface(
bufferProducer, bufferConsumer);
}
status_t OMX::setInputSurface(
node_id node, OMX_U32 port_index,
const sp<IGraphicBufferConsumer> &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<IMemory> &params,
buffer_id *buffer, OMX_U32 allottedSize) {
return findInstance(node)->allocateBufferWithBackup(
port_index, params, buffer, allottedSize);
}
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<OMX::CallbackDispatcher> 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::CallbackDispatcher> 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