/* * 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 "OMXClient" #ifdef __LP64__ #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS #endif #include #include #include #include #include #include #include #include #include "include/OMX.h" namespace android { static bool sCodecProcessEnabled = true; struct MuxOMX : public IOMX { MuxOMX(const sp &mediaServerOMX, const sp &mediaCodecOMX); virtual ~MuxOMX(); // Nobody should be calling this. In case someone does anyway, just // return the media server IOMX. // TODO: return NULL virtual IBinder *onAsBinder() { ALOGE("MuxOMX::onAsBinder should not be called"); return IInterface::asBinder(mMediaServerOMX).get(); } virtual bool livesLocally(node_id node, pid_t pid); virtual status_t listNodes(List *list); virtual status_t allocateNode( const char *name, const sp &observer, sp *nodeBinder, node_id *node); virtual status_t freeNode(node_id node); virtual status_t sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param); virtual status_t getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size); virtual status_t setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size); virtual status_t getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size); virtual status_t setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size); virtual status_t getState( node_id node, OMX_STATETYPE* state); virtual status_t storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable, MetadataBufferType *type); virtual status_t prepareForAdaptivePlayback( node_id node, OMX_U32 port_index, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight); virtual status_t configureVideoTunnelMode( node_id node, OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle); virtual status_t enableNativeBuffers( node_id node, OMX_U32 port_index, OMX_BOOL graphic, OMX_BOOL enable); virtual status_t getGraphicBufferUsage( node_id node, OMX_U32 port_index, OMX_U32* usage); virtual status_t useBuffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize); virtual status_t useGraphicBuffer( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer); virtual status_t updateGraphicBufferInMeta( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id buffer); virtual status_t updateNativeHandleInMeta( node_id node, OMX_U32 port_index, const sp &nativeHandle, buffer_id buffer); virtual status_t createInputSurface( node_id node, OMX_U32 port_index, android_dataspace dataSpace, sp *bufferProducer, MetadataBufferType *type); virtual status_t createPersistentInputSurface( sp *bufferProducer, sp *bufferConsumer); virtual status_t setInputSurface( node_id node, OMX_U32 port_index, const sp &bufferConsumer, MetadataBufferType *type); virtual status_t signalEndOfInputStream(node_id node); virtual status_t allocateSecureBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data, sp *native_handle); virtual status_t allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize); virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd); virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); virtual status_t getExtensionIndex( node_id node, const char *parameter_name, OMX_INDEXTYPE *index); virtual status_t setInternalOption( node_id node, OMX_U32 port_index, InternalOptionType type, const void *data, size_t size); private: mutable Mutex mLock; sp mMediaServerOMX; sp mMediaCodecOMX; sp mLocalOMX; typedef enum { LOCAL, MEDIAPROCESS, CODECPROCESS } node_location; KeyedVector mNodeLocation; bool isLocalNode(node_id node) const; bool isLocalNode_l(node_id node) const; const sp &getOMX(node_id node) const; const sp &getOMX_l(node_id node) const; static node_location getPreferredCodecLocation(const char *name); DISALLOW_EVIL_CONSTRUCTORS(MuxOMX); }; MuxOMX::MuxOMX(const sp &mediaServerOMX, const sp &mediaCodecOMX) : mMediaServerOMX(mediaServerOMX), mMediaCodecOMX(mediaCodecOMX) { ALOGI("MuxOMX ctor"); } MuxOMX::~MuxOMX() { } bool MuxOMX::isLocalNode(node_id node) const { Mutex::Autolock autoLock(mLock); return isLocalNode_l(node); } bool MuxOMX::isLocalNode_l(node_id node) const { return mNodeLocation.valueFor(node) == LOCAL; } // static MuxOMX::node_location MuxOMX::getPreferredCodecLocation(const char *name) { if (sCodecProcessEnabled) { // all codecs go to codec process unless excluded using system property, in which case // all non-secure decoders, OMX.google.* codecs and encoders can go in the codec process // (non-OMX.google.* encoders can be excluded using system property.) if ((strcasestr(name, "decoder") && strcasestr(name, ".secure") != name + strlen(name) - 7) || (strcasestr(name, "encoder") && !property_get_bool("media.stagefright.legacyencoder", false)) || !property_get_bool("media.stagefright.less-secure", false) || !strncasecmp(name, "OMX.google.", 11)) { return CODECPROCESS; } // everything else runs in the media server return MEDIAPROCESS; } else { #ifdef __LP64__ // 64 bit processes always run OMX remote on MediaServer return MEDIAPROCESS; #else // 32 bit processes run only OMX.google.* components locally if (!strncasecmp(name, "OMX.google.", 11)) { return LOCAL; } return MEDIAPROCESS; #endif } } const sp &MuxOMX::getOMX(node_id node) const { Mutex::Autolock autoLock(mLock); return getOMX_l(node); } const sp &MuxOMX::getOMX_l(node_id node) const { node_location loc = mNodeLocation.valueFor(node); if (loc == LOCAL) { return mLocalOMX; } else if (loc == MEDIAPROCESS) { return mMediaServerOMX; } else if (loc == CODECPROCESS) { return mMediaCodecOMX; } ALOGE("Couldn't determine node location for node %d: %d, using local", node, loc); return mLocalOMX; } bool MuxOMX::livesLocally(node_id node, pid_t pid) { return getOMX(node)->livesLocally(node, pid); } status_t MuxOMX::listNodes(List *list) { Mutex::Autolock autoLock(mLock); if (mLocalOMX == NULL) { mLocalOMX = new OMX; } return mLocalOMX->listNodes(list); } status_t MuxOMX::allocateNode( const char *name, const sp &observer, sp *nodeBinder, node_id *node) { Mutex::Autolock autoLock(mLock); sp omx; node_location loc = getPreferredCodecLocation(name); if (loc == CODECPROCESS) { omx = mMediaCodecOMX; } else if (loc == MEDIAPROCESS) { omx = mMediaServerOMX; } else { if (mLocalOMX == NULL) { mLocalOMX = new OMX; } omx = mLocalOMX; } status_t err = omx->allocateNode(name, observer, nodeBinder, node); ALOGV("allocated node_id %x on %s OMX", *node, omx == mMediaCodecOMX ? "codecprocess" : omx == mMediaServerOMX ? "mediaserver" : "local"); if (err != OK) { return err; } mNodeLocation.add(*node, loc); return OK; } status_t MuxOMX::freeNode(node_id node) { Mutex::Autolock autoLock(mLock); status_t err = getOMX_l(node)->freeNode(node); if (err != OK) { return err; } mNodeLocation.removeItem(node); return OK; } status_t MuxOMX::sendCommand( node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) { return getOMX(node)->sendCommand(node, cmd, param); } status_t MuxOMX::getParameter( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { return getOMX(node)->getParameter(node, index, params, size); } status_t MuxOMX::setParameter( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { return getOMX(node)->setParameter(node, index, params, size); } status_t MuxOMX::getConfig( node_id node, OMX_INDEXTYPE index, void *params, size_t size) { return getOMX(node)->getConfig(node, index, params, size); } status_t MuxOMX::setConfig( node_id node, OMX_INDEXTYPE index, const void *params, size_t size) { return getOMX(node)->setConfig(node, index, params, size); } status_t MuxOMX::getState( node_id node, OMX_STATETYPE* state) { return getOMX(node)->getState(node, state); } status_t MuxOMX::storeMetaDataInBuffers( node_id node, OMX_U32 port_index, OMX_BOOL enable, MetadataBufferType *type) { return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable, type); } status_t MuxOMX::prepareForAdaptivePlayback( node_id node, OMX_U32 port_index, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) { return getOMX(node)->prepareForAdaptivePlayback( node, port_index, enable, maxFrameWidth, maxFrameHeight); } status_t MuxOMX::configureVideoTunnelMode( node_id node, OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) { return getOMX(node)->configureVideoTunnelMode( node, portIndex, enable, audioHwSync, sidebandHandle); } status_t MuxOMX::enableNativeBuffers( node_id node, OMX_U32 port_index, OMX_BOOL graphic, OMX_BOOL enable) { return getOMX(node)->enableNativeBuffers(node, port_index, graphic, enable); } status_t MuxOMX::getGraphicBufferUsage( node_id node, OMX_U32 port_index, OMX_U32* usage) { return getOMX(node)->getGraphicBufferUsage(node, port_index, usage); } status_t MuxOMX::useBuffer( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize) { return getOMX(node)->useBuffer(node, port_index, params, buffer, allottedSize); } status_t MuxOMX::useGraphicBuffer( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id *buffer) { return getOMX(node)->useGraphicBuffer( node, port_index, graphicBuffer, buffer); } status_t MuxOMX::updateGraphicBufferInMeta( node_id node, OMX_U32 port_index, const sp &graphicBuffer, buffer_id buffer) { return getOMX(node)->updateGraphicBufferInMeta( node, port_index, graphicBuffer, buffer); } status_t MuxOMX::updateNativeHandleInMeta( node_id node, OMX_U32 port_index, const sp &nativeHandle, buffer_id buffer) { return getOMX(node)->updateNativeHandleInMeta( node, port_index, nativeHandle, buffer); } status_t MuxOMX::createInputSurface( node_id node, OMX_U32 port_index, android_dataspace dataSpace, sp *bufferProducer, MetadataBufferType *type) { status_t err = getOMX(node)->createInputSurface( node, port_index, dataSpace, bufferProducer, type); return err; } status_t MuxOMX::createPersistentInputSurface( sp *bufferProducer, sp *bufferConsumer) { sp omx; { Mutex::Autolock autoLock(mLock); if (property_get_bool("media.stagefright.legacyencoder", false)) { omx = mMediaServerOMX; } else { omx = mMediaCodecOMX; } } return omx->createPersistentInputSurface( bufferProducer, bufferConsumer); } status_t MuxOMX::setInputSurface( node_id node, OMX_U32 port_index, const sp &bufferConsumer, MetadataBufferType *type) { return getOMX(node)->setInputSurface(node, port_index, bufferConsumer, type); } status_t MuxOMX::signalEndOfInputStream(node_id node) { return getOMX(node)->signalEndOfInputStream(node); } status_t MuxOMX::allocateSecureBuffer( node_id node, OMX_U32 port_index, size_t size, buffer_id *buffer, void **buffer_data, sp *native_handle) { return getOMX(node)->allocateSecureBuffer( node, port_index, size, buffer, buffer_data, native_handle); } status_t MuxOMX::allocateBufferWithBackup( node_id node, OMX_U32 port_index, const sp ¶ms, buffer_id *buffer, OMX_U32 allottedSize) { return getOMX(node)->allocateBufferWithBackup( node, port_index, params, buffer, allottedSize); } status_t MuxOMX::freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer) { return getOMX(node)->freeBuffer(node, port_index, buffer); } status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { return getOMX(node)->fillBuffer(node, buffer, fenceFd); } status_t MuxOMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return getOMX(node)->emptyBuffer( node, buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t MuxOMX::getExtensionIndex( node_id node, const char *parameter_name, OMX_INDEXTYPE *index) { return getOMX(node)->getExtensionIndex(node, parameter_name, index); } status_t MuxOMX::setInternalOption( node_id node, OMX_U32 port_index, InternalOptionType type, const void *data, size_t size) { return getOMX(node)->setInternalOption(node, port_index, type, data, size); } OMXClient::OMXClient() { char value[PROPERTY_VALUE_MAX]; if (property_get("media.stagefright.codecremote", value, NULL) && (!strcmp("0", value) || !strcasecmp("false", value))) { sCodecProcessEnabled = false; } } status_t OMXClient::connect() { sp sm = defaultServiceManager(); sp playerbinder = sm->getService(String16("media.player")); sp mediaservice = interface_cast(playerbinder); if (mediaservice.get() == NULL) { ALOGE("Cannot obtain IMediaPlayerService"); return NO_INIT; } sp mediaServerOMX = mediaservice->getOMX(); if (mediaServerOMX.get() == NULL) { ALOGE("Cannot obtain mediaserver IOMX"); return NO_INIT; } // If we don't want to use the codec process, and the media server OMX // is local, use it directly instead of going through MuxOMX if (!sCodecProcessEnabled && mediaServerOMX->livesLocally(0 /* node */, getpid())) { mOMX = mediaServerOMX; return OK; } sp codecbinder = sm->getService(String16("media.codec")); sp codecservice = interface_cast(codecbinder); if (codecservice.get() == NULL) { ALOGE("Cannot obtain IMediaCodecService"); return NO_INIT; } sp mediaCodecOMX = codecservice->getOMX(); if (mediaCodecOMX.get() == NULL) { ALOGE("Cannot obtain mediacodec IOMX"); return NO_INIT; } mOMX = new MuxOMX(mediaServerOMX, mediaCodecOMX); return OK; } void OMXClient::disconnect() { if (mOMX.get() != NULL) { mOMX.clear(); mOMX = NULL; } } } // namespace android