精华内容
下载资源
问答
  • Android-StageFrightOpenMAX的实现

    千次阅读 2012-06-11 21:54:57
    Android-StageFrightOpenMAX的实现 OMXCodec是一个MediaSource,完成数据的parse和decode。而OMXCodec则主要通过IOMX跨越OpenBinder机制操作OMX来实现。 重点介绍一下OMX。OMX主要完成三个任务,NodeInstance...

    Android-StageFright之OpenMAX的实现

    OMXCodec是一个MediaSource,完成数据的parse和decode。而OMXCodec则主要通过IOMX跨越OpenBinder机制操作OMX来实现。

    重点介绍一下OMX。OMX主要完成三个任务,NodeInstance列表的管理,针对一个NodeInstance的操作以及事件的处理。

    一、NodeInstance列表的管理。

    这个主要包括NodeInstance的生成(allocateNode)和删除(freeNode)。其实就是对mDispatchers和 mNodeIDToInstance进行添加和删除。mNodeIDToInstance就是一个key为node_id,value为 NodeInstance的名值对列表。而mDispatchers就是一个key为node_id,value为 OMX::CallbackDispatcher的名值对列表。并且,一个NodeInstance都拥有一个 OMX::CallbackDispatcher。

    二、NodeInstance节点的操作。

    主要成员函数如下:

    sendCommand

    getParameter

    setParameter

    … …

    fillBuffer

    emptyBuffer

    getExtensionIndex

    这些方法执行时,都是先通过findInstance在mNodeIDToInstance列表中找到对应的NodeInstance,然后调用NodeInstance对应的方法。

    三、事件处理

    先看一下OMXNodeInstance.cpp中的这样一段代码:

    1
    2
    3
    4
    5
    
    OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {
     
        &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
     
    };

    它把三个OMXNodeInstance类的静态方法注册给了kCallbacks。而kCallbacks在哪里使用呢?看一下OMX.cpp中的allocateNode方法中的代码:

    1
    2
    3
    4
    5
    
    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
     
                name, &OMXNodeInstance::kCallbacks,
     
                instance, &handle);

    事件处理函数传给了组件ComponentInstance。当组件有事件发生时,就会调用OMXNodeInstance中这几个注册过的事件处理函数,而这几个函数又会去调用OMX中对应的函数,也就是下面这三个:

    OnEvent、OnEmptyBufferDone、OnFillBufferDone。

    这几个方法都采用相同的路子:根据node_id找到CallbackDispatcher,并把事件信息post过去。具体点儿,就是调用findDispatcher(node)->post(msg)。

    这里不得不提一下CallbackDispatcher的实现机制。它内部开启了一个线程,使用了信号量机制。可以看一下OMX::CallbackDispatcher的属性:Condition mQueueChanged;

    可以看出findDispatcher(node)->post(msg)是一个异步操作,只把msg给POST过去,不会等待事件处理完毕就返回了。那么CallbackDispatcher是怎么处理接收到的msg呢?看以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    
    OMX::CallbackDispatcher::threadEntry()
     
              dispatch(msg);
     
     
         mOwner->onMessage(msg);
     
    mObserver->onMessage(msg);

    这个mObserver是哪来的?OMXCodec::Create中初始化IOMX时传入的。

    1
    2
    3
    4
    5
    
    sp<OMXCodecObserver> observer = new OMXCodecObserver;
     
    ... ...
     
    omx->allocateNode(componentName, observer, &node);

    这样算下来,事件最终还是跨越OpenBinder又传到了OMXCodec里面去,交给OMXCodecObserver了。

    对节点的操作

    NodeInstance的大部分方法的实现,如sendCommand等,都是通过OMX_Core.h中的宏定义间接调用 OMX_Component.h中的OMX_COMPONENTTYPE这个struct中的相应函数指针来完成。在这里提到的OMX_Core.h和 OMX_Component.h都是OpenMAX标准头文件。
    展开全文
  • android_ics openmax_in_stagefright 再次学习 /* *在学习android源代码的工程中,一点要时刻牢记C/S架构 *任何时刻都要搞清除,这个时候的代码是运行在客户端, *还是服务端,这个对象来之,客户端还是服务端的代理...

    这几篇文章是之前学习openmax的输出,记录在这里,希望不要误导菜鸟的同时又能得到牛牛们的指导。


    android_ics openmax_in_stagefright 再次学习
    
    /*
    *在学习android源代码的工程中,一点要时刻牢记C/S架构
    *任何时刻都要搞清除,这个时候的代码是运行在客户端,
    *还是服务端,这个对象来之,客户端还是服务端的代理。
    */
    
    
    <---以下的讨论,目的都在于弄清楚,stagefright框架内,OpenMaXIL和各个编解码的组件是如何通信的--->
    
    At first:
    
    	OpenMax是事实上的标准,也是android上多媒体编解码框架未来的趋势。
    	
    
    分析的比较凌乱,后面在做整理。
    
    /
    //1,OMX 从何开始?
    
    
    	看下awesomeplayer的构造函数
    	
    	AwesomePlayer::AwesomePlayer()
        : mQueueStarted(false),
    	  ...
          mTextPlayer(NULL) {
        /*mClient是一个OMXClient类型的成员变量*/
        CHECK_EQ(mClient.connect(), (status_t)OK);
        DataSource::RegisterDefaultSniffers();
        mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
    	...
        mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate);
        mVideoEventPending = false;
        mCheckAudioStatusEvent = new AwesomeEvent(
                this, &AwesomePlayer::onCheckAudioStatus);
        ...
    }
    
    
        /*connect函数的定义*/
        
    	status_t OMXClient::connect() {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder = sm->getService(String16("media.player"));
        sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
    
        CHECK(service.get() != NULL);
    
        mOMX = service->getOMX();
        CHECK(mOMX.get() != NULL);
    
        return OK;
    }
    	
    	上面的函数,通过binder取请求MediaPlayerService的getOmx然后反回一个OMX实例,事实上这个时候在awesomePlayer
    中的mOMX是一个来之服务端的实例。打通了一条Client->Service的通道。个人认为这就是OpenMax框架的入口。
         
         /*OMX的构造函数如下,@OMX.cpp*/
         OMX::OMX():mMaster(new OMXMaster),mNodeCounter(0) {
    	}
    	
    -------------------------以下是插曲,首次看请忽略--------------------------------------------------------->	
    	/*下面完整的贴出OMX的结构,其中有些成员的作用,在后面的学习中,会慢慢的揭开其真面目*/
    	class OMX : public BnOMX,
                public IBinder::DeathRecipient {
    public:
        OMX();
    
        virtual bool livesLocally(pid_t pid);
    
        virtual status_t listNodes(List<ComponentInfo> *list);
    
        virtual status_t allocateNode(
                const char *name, const sp<IOMXObserver> &observer, 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 enableGraphicBuffers(
                node_id node, OMX_U32 port_index, OMX_BOOL enable);
    
        virtual status_t getGraphicBufferUsage(
                node_id node, OMX_U32 port_index, OMX_U32* usage);
    
        virtual status_t storeMetaDataInBuffers(
                node_id node, OMX_U32 port_index, OMX_BOOL enable);
    
        virtual status_t useBuffer(
                node_id node, OMX_U32 port_index, const sp<IMemory> &params,
                buffer_id *buffer);
    
        virtual status_t useGraphicBuffer(
                node_id node, OMX_U32 port_index,
                const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
    
        virtual status_t allocateBuffer(
                node_id node, OMX_U32 port_index, size_t size,
                buffer_id *buffer, void **buffer_data);
    
        virtual status_t allocateBufferWithBackup(
                node_id node, OMX_U32 port_index, const sp<IMemory> &params,
                buffer_id *buffer);
    
        virtual status_t freeBuffer(
                node_id node, OMX_U32 port_index, buffer_id buffer);
    
        virtual status_t fillBuffer(node_id node, buffer_id buffer);
    
        virtual status_t emptyBuffer(
                node_id node,
                buffer_id buffer,
                OMX_U32 range_offset, OMX_U32 range_length,
                OMX_U32 flags, OMX_TICKS timestamp);
    
        virtual status_t getExtensionIndex(
                node_id node,
                const char *parameter_name,
                OMX_INDEXTYPE *index);
    
        virtual void binderDied(const wp<IBinder> &the_late_who);
    
        OMX_ERRORTYPE OnEvent(
                node_id node,
                OMX_IN OMX_EVENTTYPE eEvent,
                OMX_IN OMX_U32 nData1,
                OMX_IN OMX_U32 nData2,
                OMX_IN OMX_PTR pEventData);
    
        OMX_ERRORTYPE OnEmptyBufferDone(
                node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
    
        OMX_ERRORTYPE OnFillBufferDone(
                node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
    
        void invalidateNodeID(node_id node);
    
    protected:
        virtual ~OMX();
    
    private:
        struct CallbackDispatcherThread;  //关注下
        struct CallbackDispatcher;		  //关注下
    
        Mutex mLock;
        OMXMaster *mMaster;
        int32_t mNodeCounter;
    
        KeyedVector<wp<IBinder>, OMXNodeInstance *> mLiveNodes;
        KeyedVector<node_id, OMXNodeInstance *> mNodeIDToInstance;
        KeyedVector<node_id, sp<CallbackDispatcher> > mDispatchers;
    
        node_id makeNodeID(OMXNodeInstance *instance);
        OMXNodeInstance *findInstance(node_id node);
        sp<CallbackDispatcher> findDispatcher(node_id node);
    
        void invalidateNodeID_l(node_id node);
    
        OMX(const OMX &);
        OMX &operator=(const OMX &);
    };
    
    }  // namespace android
    
    #endif  // ANDROID_OMX_H_
    <-------------------------以上是插曲,首次看请忽略---------------------------------------------------------
    
        可以看到,在构造OMX的时候,会new一个OMXMaster给成员变量mMaster,这个mMaster是对OMXPluginBase(基类)
    类型的插件的管理,其中各个插件在ICS的框架中就是软硬件编解码的插件。
     
    #ifndef OMX_MASTER_H_
    
    #define OMX_MASTER_H_
    
    #include <media/stagefright/OMXPluginBase.h>
    
    #include <utils/threads.h>
    #include <utils/KeyedVector.h>
    #include <utils/List.h>
    #include <utils/String8.h>
    
    namespace android {
    
    struct OMXMaster : public OMXPluginBase {
        OMXMaster();
        virtual ~OMXMaster();
    
        virtual OMX_ERRORTYPE makeComponentInstance(     //符合OpenMAX标准的接口
                const char *name,
                const OMX_CALLBACKTYPE *callbacks,
                OMX_PTR appData,
                OMX_COMPONENTTYPE **component);
    
        virtual OMX_ERRORTYPE destroyComponentInstance(  //符合OpenMAX标准的接口
                OMX_COMPONENTTYPE *component);
    
        virtual OMX_ERRORTYPE enumerateComponents(       //符合OpenMAX标准的接口
                OMX_STRING name,
                size_t size,
                OMX_U32 index);
    
        virtual OMX_ERRORTYPE getRolesOfComponent(		 //符合OpenMAX标准的接口
                const char *name,
                Vector<String8> *roles);
    
    private:
        Mutex mLock;
        List<OMXPluginBase *> mPlugins;
        KeyedVector<String8, OMXPluginBase *> mPluginByComponentName;
        KeyedVector<OMX_COMPONENTTYPE *, OMXPluginBase *> mPluginByInstance;
    
        void *mVendorLibHandle;
    
        void addVendorPlugin();
        void addPlugin(const char *libname);
        void addPlugin(OMXPluginBase *plugin);
        void clearPlugins();
    
        OMXMaster(const OMXMaster &);
        OMXMaster &operator=(const OMXMaster &);
    };
    
    }  // namespace android
    
    #endif  // OMX_MASTER_H_
    
    -----到目前为止,android2.3和ICS在该部分没有明显区别-----
    
    在OMXMaster的构造函数中,就开始不同了。
    
    
    2.3
    OMXMaster::OMXMaster()
        : mVendorLibHandle(NULL) {
        addVendorPlugin();
    
    #ifndef NO_OPENCORE
        addPlugin(new OMXPVCodecsPlugin);
    #endif
    }
    2.3中,硬解部分还是使用原来OMX的标准来进行即addVendorPlugin(),然后软件部分的话,在这里没有说明,
    事实上软件部分,2.3之间返回了XXXDecoder
    
    4.0
    OMXMaster::OMXMaster()
        : mVendorLibHandle(NULL) {
        addVendorPlugin();
        addPlugin(new SoftOMXPlugin);
    }
    
    4.0上对于软硬编解码,都使用OMX标准,挂载plugins的方式来进行。软解部分使用addPlugin(new SoftOMXPlugin)。

    /
    //2,一个重要的类OMXCodecObserver
    /
    
    OMXCodecObserver是一个OMXCodec的内部类,它在OMXCodec的Create函数中会实例化一个,并且使用observer->setCodec(codec)
    接口,对一个OMXCodec进行管理。OMXCodecObserver还有一个接口onMessage,其作用是对由observer管理的codec进行消息处理,
    这个位于OMXCodecObserver的onMessage函数是一个统一的入口,具体的消息处理,由被observer管理的codec自己实现。
    
    
    /
    //3,在OMXCodec的create中还做了些什么
    /
    现在看看相关的代码片段
    
    sp<MediaSource> OMXCodec::Create(
            const sp<IOMX> &omx,
            const sp<MetaData> &meta, bool createEncoder,
            const sp<MediaSource> &source,
            const char *matchComponentName,
            uint32_t flags,
            const sp<ANativeWindow> &nativeWindow) {
        int32_t requiresSecureBuffers;
        if (source->getFormat()->findInt32(
                    kKeyRequiresSecureBuffers,
                    &requiresSecureBuffers)
                && requiresSecureBuffers) {
            flags |= kIgnoreCodecSpecificData;
            flags |= kUseSecureInputBuffers;
            flags |= kEnableGrallocUsageProtected;
        }
        else
        {
            flags &= ~kEnableGrallocUsageProtected;
        }
    
        const char *mime;
        bool success = meta->findCString(kKeyMIMEType, &mime);
        CHECK(success);
    
        Vector<String8> matchingCodecs;
        findMatchingCodecs(
                mime, createEncoder, matchComponentName, flags, &matchingCodecs);
        if (matchingCodecs.isEmpty()) {
            return NULL;
        }
        sp<OMXCodecObserver> observer = new OMXCodecObserver;
        IOMX::node_id node = 0;
    
        for (size_t i = 0; i < matchingCodecs.size(); ++i) {
            const char *componentNameBase = matchingCodecs[i].string();
            const char *componentName = componentNameBase;
    
            AString tmp;
            if (flags & kUseSecureInputBuffers) {
                tmp = componentNameBase;
                tmp.append(".secure");
    
                componentName = tmp.c_str();
            }
    		...
    
            status_t err = omx->allocateNode(componentName, observer, &node);
    
            ...
            
                sp<OMXCodec> codec = new OMXCodec(
                        omx, node, quirks, flags,
                        createEncoder, mime, componentName,
                        source, nativeWindow);
    
                observer->setCodec(codec);
    
                err = codec->configureCodec(meta);
    
                if (err == OK) {
                    if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) {
                        codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime;
                    }
    
                    return codec;
    			...
    
        return NULL;
    }
    
    从OMXCodec::Create的代码中可以看到,这个Create函数主要做了一下几件事:
    
    //这里,根据mime的类型,先找到对应的容器格式,然后把这个ComponentName放到matchingCodecs中。如果有指定的容器话,作为实参传递给matchComponentName。
    
    1,findMatchingCodecs(mime, createEncoder, matchComponentName, flags, &matchingCodecs);
    
    //实例化一个OMXCodecObserver,作用上面有提到,然后给即将创建的node分配一个node_id,id号被初始化为0
    2,sp<OMXCodecObserver> observer = new OMXCodecObserver;
    IOMX::node_id node = 0;
    
    //allocateNode函数会通过binder调用到服务端的OMX::allocateNode,函数的具体实现如下
    3,status_t err = omx->allocateNode(componentName, observer, &node);
    
    status_t OMX::allocateNode(
            const char *name, const sp<IOMXObserver> &observer, node_id *node) {
        Mutex::Autolock autoLock(mLock);
    
        *node = 0;
        //OMXNodeInstance,与node_id一一对应,指的是OMX标准下node的实例,这个OMXNodeInstance内部封装了一下对不同实例的操作,以及回调。
        OMXNodeInstance *instance = new OMXNodeInstance(this, observer);
    
        OMX_COMPONENTTYPE *handle;
        
        //这里会一直调用具体的OMXPlugins的的的makeComponentInstance函数,假设这里的具体OMX plugin是SoftOMXPlugin,那么
        //SoftOMXPlugin的makeComponentInstance函数会根据容器名来动态的打开一个名为llibstagefright_soft_XXX.so的的动态库
        //然后使用ddlopen和dlsym来来调用这个so中的函数。这个操作完成之后,会在底层对应一个具体的编解码组件,例如SoftMPEG4,并对
        //这个编解码组件进行初始化,例如initPorts(),initDecoder()......
        OMX_ERRORTYPE err = mMaster->makeComponentInstance(
                name, &OMXNodeInstance::kCallbacks,
                instance, &handle);
    
        if (err != OMX_ErrorNone) {
            LOGV("FAILED to allocate omx component '%s'", name);
    
            instance->onGetHandleFailed();
    
            return UNKNOWN_ERROR;
        }
    
        *node = makeNodeID(instance);
        //首先由node_id来区分不同的node,然后给各个不同的node实例化一个CallbackDispatcher来与之对应,这个CallbackDispatcher
        //靠OMXNodeInstance instance来区分。CallbackDispatcher在实例化之后,内部会开启一个线程,这个线程循环的监听List<omx_message> mQueue;
        //看看是否有新的omx_message被分派过来。如果有就dispatch(msg)。
        mDispatchers.add(*node, new CallbackDispatcher(instance));
    
    //给这个OMXNodeInstance绑定一个node_id,和handler,这个handler是上面mMaster->makeComponentInstance传出的。
        instance->setHandle(*node, handle);
    
        mLiveNodes.add(observer->asBinder(), instance);
        observer->asBinder()->linkToDeath(this);
    
        return OK;
    }
    
       //在框架层实例化一个OOMXCodec
    4,sp<OMXCodec> codec = new OMXCodec(
                        omx, node, quirks, flags,
                        createEncoder, mime, componentName,
                        source, nativeWindow);
       //将前面的codec拉入observer的管理
       observer->setCodec(codec);
       //根据媒体流的format对codec进行一些配置
       err = codec->configureCodec(meta);
       
    下面来看张图:
    
    
    
    
    从上面的时序图看一看出omx在stagefright框架中的条用关系,
    1,在awesomeplayer中,有一个OMXClient成员变量mClient,作用与服务端OMX交互的代理。
    2,stagefright框架通过OMXCodec进入到OMX(即OMX的服务端)
    3,通过服务端的OMX去和具体的OMXNodeInstance还有编解码Components打交道。
    4,回调机制可以从图中看出来,是通过CallbackDispatcher分派消息,然后一层一层的往上调用onMessage().


    节约篇幅再开一篇

    展开全文
  • ANDROID_PRIORITY_FOREGROUND); } //在 SimpleSoftOMXComponent中有一个mHandler(new AHandlerReflector(this)),---注意这个mHandle的参数,其中 SimpleSoftOMXComponent做为这个mHandle的投递目标,也就是消息...
    
    
    /
    //4,回到awesomeplayer initVideoDecoder()中
    /
    
        mVideoSource = OMXCodec::Create(
                mClient.interface(), mVideoTrack->getFormat(),
                false, // createEncoder
                mVideoTrack,
                NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
    
        if (mVideoSource != NULL) {
            int64_t durationUs;
            if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
                Mutex::Autolock autoLock(mMiscStateLock);
                if (mDurationUs < 0 || durationUs > mDurationUs) {
                    mDurationUs = durationUs;
                }
            }
           //这里的mVideoSource就是上面返回的OMXCodec类型,所以mVideoSource->start()的函数定义如下,但是这里的OMXCodec类型靠node_id来标示,而且OMXCodec(mVideoSource)所扮演的角色类型也不同(setComponentRole())。
          status_t err = mVideoSource->start();
    
    status_t OMXCodec::start(MetaData *meta) {
        CODEC_LOGV("OMXCodec::start ");
        Mutex::Autolock autoLock(mLock);
    
        if(mPaused) {
            if (!strncmp(mComponentName, "OMX.qcom.", 9)) {
                while (isIntermediateState(mState)) {
                    mAsyncCompletion.wait(mLock);
                }
                CHECK_EQ(mState, (status_t)PAUSED);
                status_t err = mOMX->sendCommand(mNode,
                OMX_CommandStateSet, OMX_StateExecuting);
                CHECK_EQ(err, (status_t)OK);
                setState(IDLE_TO_EXECUTING);
                mPaused = false;
                while (mState != EXECUTING && mState != ERROR) {
                    mAsyncCompletion.wait(mLock);
                }
                drainInputBuffers();
                return mState == ERROR ? UNKNOWN_ERROR : OK;
            } else {   // SW Codec
                mPaused = false;
                return OK;
            }
        }
    
        if (mState != LOADED) {
            return UNKNOWN_ERROR;
        }
    
        sp<MetaData> params = new MetaData;
        if (mQuirks & kWantsNALFragments) {
            params->setInt32(kKeyWantsNALFragments, true);
        }
        if (meta) {
            int64_t startTimeUs = 0;
            int64_t timeUs;
            if (meta->findInt64(kKeyTime, &timeUs)) {
                startTimeUs = timeUs;
            }
            params->setInt64(kKeyTime, startTimeUs);
        }
        //第一次跳过前面的代码,走到这里,注意这里的mSource是OMXCodec构造函数中传进来的。追根溯源会发 现这里的mSource其实是mVideoTrack(awesomeplayer的成员变量),而mVideoTrack又是在AwesomePlayer::setVideoSource中被赋值的,在awesomeplayer的setdatasource()函数中setVideoSource(extractor->getTrack(i));所以这里的mSource应该是一个具体的XXXExtractor.getTrack(i)之后得到的。最终发现,mSource实际上是某种格式的一段媒体流。例如MPEG4Source等等。所以这个start函数的做用是根据这段媒体流的实际需要,分配一个合适大小的buf供以后使用。
    
        status_t err = mSource->start(params.get());
    
        if (err != OK) {
            return err;
        }
    
        mCodecSpecificDataIndex = 0;
        mInitialBufferSubmit = true;
        mSignalledEOS = false;
        mNoMoreOutputData = false;
        mOutputPortSettingsHaveChanged = false;
        mSeekTimeUs = -1;
        mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
        mTargetTimeUs = -1;
        mFilledBuffers.clear();
        mPaused = false;
        //从这个函数开始通过OMX和OMX的components通信了
        return init();
    }
    
    status_t OMXCodec::init() {
        // mLock is held.
    
        CHECK_EQ((int)mState, (int)LOADED);
    
        status_t err;
        //mQuirks是一些和具体编解码组件相关的特性参数,他描述了该组件在工作时候需要的一些注意事项
        //mQuirks主要很硬件编解码组件有关
        if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
            //向特定的node放送Command,这个node有node_id即mNode表示,后面会详细介绍-------------    1 
            err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
            CHECK_EQ(err, (status_t)OK);
            setState(LOADED_TO_IDLE);
        }
        
       //为不同的nodeInstance分配其输入和输出端口上的buffer,并确定分配大小和分配策略
        err = allocateBuffers();
        if (err != (status_t)OK) {
            CODEC_LOGE("Allocate Buffer failed - error = %d", err);
            setState(ERROR);
            return err;
        }
    
        if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
            err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
            CHECK_EQ(err, (status_t)OK);
    
            setState(LOADED_TO_IDLE);
        }
    
        while (mState != EXECUTING && mState != ERROR) {
            mAsyncCompletion.wait(mLock);
        }
    
        return mState == ERROR ? UNKNOWN_ERROR : OK;
    }
    
    
    
    
    ///
    //5,看看mOMX->sendCommand(mNode,...,...)做了什么
    ///
    在OMXCodec会通过OMX服务去调用:
    
    //该函数会查找node_id号所对应的nodeInstance,然后去调用这个nodeInstance的sendCommand函数。
    status_t OMX::sendCommand(
            node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
        return findInstance(node)->sendCommand(cmd, param);
    }
    	|
    	|
    	|
    	V
    //注意这里这个OMXNodeInstance对应就是具体node了,OMX_SendCommand函数中的第一个参数就是在instance->setHandle(*node, handle)传进出的,而且这个handle是由mMaster->makeComponentInstance传出的,上面有提到。
    status_t OMXNodeInstance::sendCommand(
            OMX_COMMANDTYPE cmd, OMX_S32 param) {
        Mutex::Autolock autoLock(mLock);
    
        OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL);
        return StatusFromOMXError(err);
    }
    
    //OMX_SendCommand由下面的宏定义,可以看到最后调用的就是那个mHandle的SendCommand方法
    #define OMX_SendCommand(                                    \
             hComponent,                                        \
             Cmd,                                               \
             nParam,                                            \
             pCmdData)                                          \
         ((OMX_COMPONENTTYPE*)hComponent)->SendCommand(         \
             hComponent,                                        \
             Cmd,                                               \
             nParam,                                            \
             pCmdData)                          /* Macro End */
    
    
    
    
    
    
    
    
    //特别要注意的地方是上面((OMX_COMPONENTTYPE*)hComponent)->SendCommand函数实际上是调用的过程是,首先通过
    SoftOMXComponent::SendCommandWrapper----->SoftOMXComponent *me =(SoftOMXComponent *)((OMX_COMPONENTTYPE *)component)->pComponentPrivate;(me被转换成一个this指针)---->me->sendCommand(cmd, param, data);(这个me->sendCommand调用的肯定是SimpleSoftOMXComponent::sendCommand) 
    注意一个继承关系   SoftMPEG4--->SimpleSoftOMXComponent--->SoftOMXComponent  (箭头理解为继承于)
    
    //sendCommand的函数实现如下:
    OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand(
            OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
        CHECK(data == NULL);
    
        sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id());
        msg->setInt32("cmd", cmd);
        msg->setInt32("param", param);
        //消息被投递出去了
        msg->post();------------------------------------------------------- 1
    
        return OMX_ErrorNone;
    }
    
    //消息投递出去之后,什么时候得到处理呢?看下 SimpleSoftOMXComponent的构造函数就明白了
    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<SimpleSoftOMXComponent>(this)),
          mState(OMX_StateLoaded),
          mTargetState(OMX_StateLoaded) {
        mLooper->setName(name);
        mLooper->registerHandler(mHandler);
    
        mLooper->start(
                false, // runOnCallingThread
                false, // canCallJava
                ANDROID_PRIORITY_FOREGROUND);
    }
    
    
    
    //在 SimpleSoftOMXComponent中有一个mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),---注意这个mHandle的参数,其中 SimpleSoftOMXComponent做为这个mHandle的投递目标,也就是消息将有 SimpleSoftOMXComponent处理。
    最终,消息会被投递到mLooper中的mEventQueue,并在消息循环(也就是mLooper.loop()函数中,被deliverMessage,然后被相应的hanler处理,也就是SimpleSoftOMXComponent::onMessageReceived函数)。


    在来看一个继承关系图。


    这个继承关系图,也许能说明一些问题。最右边的是具体的软件编/解码模块的实现的代码,在openmanx框架中,为了实现动态绑定,这些moudle最终会被编译成libstagefright_soft_XXXX.so。具体的编译步骤,可以去看相关路径下的Android.mk文件。可以说明一点的是,这些libstagefright_soft_XXXX.so是首先由一些编解码的srcfile编译出一个类似于libstagefright_m4vh263dec的静态库,然后这个静态库再被编译进一个.so里。

            在这个继承关系图上,位于中间的SimpleSoftOMXComponent,完成了大部分的工作,它负责sendCommand的同时又要将一些反馈信息notify到其父类SoftOMXCodec来处理。这些反馈的信息包括,组件状态的变化,ports的状态的变化等等。父类SoftOMXComponent来处理的notify的信息,其主要的工作是,找到具体的handler然后调用适当的CallBacks。


    关于组建的结构再来看一张图,下面是一张组件的模型图:


    说的简单一点,command从组件的外部进来,在组件内进行一系列的传递和处理(组件内部自己处理)之后,再将反馈信息传回组件外部,传递给OMXCodec框架层。


    说点废话,把时序图再贴一次:




    首先,可以明确一点的是,整个stagefright框架都是事件驱动模式的。上面的时序图主要描述了,收到onPrepareAsyncEvent时间后,OMX框架是如何是如何建立自己的编解码组件。
    
    上面的时序图中,要明确两点 1,事件驱动模式是异步的 2,时序图的末端 CallbackDispatcher也是异步分派的
    
    从上面的时序图中还有一点没有办法看出来。就是对应于具体的编解码组件,编解码的过程时候时候发生。下面就这个问题,展开讨论。
    
    
    
    //
    //下面的研究将围绕三个问题
    /
    1,待解码的原始数据问题  
    2,具体编解码组件是如何完成编解码工作的  
    3,解码之后的数据处理问题
    


    以上的三个问题,在我的《数据流向分析》中有介绍。


    OK,备忘做完了,还是那句话,菜鸟们请批判性的看,comments are very welcome.

     

     

     

    展开全文
  • android StageFright框架解读

    千次阅读 2017-06-27 18:52:10
    android StageFright框架解读 android多媒体框架 MediaPlayerService Stagefright OpenCore 底层openmax 1、JAVA类的路径: frameworks/base/media/java/android/media/MediaPlayer.java JAVA本地调用部分...

    android StageFright框架解读

    android多媒体框架
    这里写图片描述

    MediaPlayerService Stagefright OpenCore
    这里写图片描述

    底层openmax
    这里写图片描述

    1、JAVA类的路径:
    frameworks/base/media/java/android/media/MediaPlayer.java
    JAVA本地调用部分(JNI):
    frameworks/base/media/jni/android_media_MediaPlayer.cpp
    内容将被编译成为目标库libmedia_jni.so。
    2、多媒体客户端部分:
    一个服务端的代理,对应用层提供相关的接口,和服务端交互
    frameworks/base/media/libmedia
    将被编译成库libmedia.so
    3、多媒体服务端部分:
    //服务端库
    frameworks/base/media/libmediaplayerservice
    文件为mediaplayerservice.h和mediaplayerservice.cpp
    将被编译成库libmediaplayerservice.so。

    MediaPlayerService底层框架:
    1、opencore
    2、stagefright在MediaPlayerService这一层加入的(与opencore并列),android默认选择stagefright。
    Stagefright在 Android中是以shared library的形式存在(libstagefright.so),AwesomePlayer可用来播放video/audio。 AwesomePlayer提供许多API,可以让上层的应用程序(Java/JNI)来调用。
    二者都基于openmax框架

    MediaPlayerService底层框架演变:
    最早期OpenCore => StageFright => 现在NuPlayerDriver
    现在Android中使用的是Stagefright + NuPlayer并存的方式:
    1、Stagefright负责播放本地的媒体文件
    2、NuPlayer用于播放网络流媒体文件
    从Android L开始NuPlayeri渐渐开始替代了Stagefright,目前本地播放已切换到NuPlayer上了(Android N已移除了Stagefright)

    Stagefight的MediaPLayer框架
    这里写图片描述

    MediaPlayerService基本调用流程:
    1、Java层通过JNI调用Native侧的mediaplayer相关接口
    2、mediaPlayerService中创建服务器端客户端,分别实例化相关播放器,把接口事件调用应 用到具体的播放器:
    本地媒体调用stagefright,
    流媒体调用NuPlayerDriver
    3、stagefright会调用AwesomePlayer相应接口, 其实流媒体侧和本地媒体类似的,最后会调用Nuplayer.
    AwesomePlayer的几个成员:
    mVideoTrack(从多媒体文件中读取视频数据)
    mVideoSource(解码视频)
    mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
    mISurface(重绘图层)
    mQueue(event事件队列)

    AwesomePlyaer调用其它组件(如MPEG4Extractor)完成,参数mVideoBuffer即为解码后的帧图像,decode则是调用OMXCodec的服务接口. 也就是解码时又通过Binder做了一次跨进程通信. OMXCodec Service文件:
    接口定义:
    IOMX.h
    客户端类:
    OMXCodec.cpp
    OMXClient.cpp
    IOMX.cpp (BpOMX类/BnOMX类)
    服务端类:
    OMX.cpp
    OMXNodeInstance.cpp
    说明:
    1、 android系统中只用openmax来做code,所以android向上抽象了一层OMXCodec,提供给上层播放器用。 播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。
    2、OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中 实现。
    3、 OMX把软编解码和硬件编解码统一看作插件的形式管理起来

    StageFrightPlayer.cpp

    StagefrightPlayer::StagefrightPlayer()  
        : mPlayer(new AwesomePlayer) {  
        ALOGV("StagefrightPlayer");  
        mPlayer->setListener(this);  
    }  

    数据读取、解码流程:
    这里写图片描述
    数据由源到最终解码的流程:
    DataSource(URI,FD) => MediaExtractor => mVideoTrack => mVideoSource => mVideoBuffer
    DataSource(URI,FD) => MediaExtractor => mAudioTrack => mAudioSource => mAudioPlayer
    1、MediaPlayerService调用Stagefright相应的接口
    2、Stagefright调用AwesomePlayer相应的接口
    3、AwesomePlayer调用OMXCode读取ES数据,并且进行解码的处理
    4、OMXCodec调用MediaSource的read函数来获取音视频的数据
    5、OMXCodec调用Android的IOMX接口,其实就是Stagefrightde中的 OMX实现
    6、OMX调用OMXMaster,而OMXMaster调用OMXPluginBase的接口,这里也可以获取外部的Codec的插件,最终调用对应的解码组建来完成解码,不同解码组件不太相同,后面会做介绍
    7、解码完成后,通过OMXcodec返回的裸码流数据会在Awesomeplayer中调用Render模块,实现渲染,从而给用户提供了画面

    具体操作步骤:
    1、设置DataSource,数据源可以两种URI和FD。
    URI可以http://,rtsp://等。
    FD是一个本地文件描述符,能过FD,可以找到对应的文件。
    2、由DataSource生成MediaExtractor。通过sp extractor = MediaExtractor::Create(dataSource)来实现。
    MediaExtractor::Create(dataSource)会根据不同的数据内容创建不同的数据读取对象。
    3、通过调用setVideoSource由MediaExtractor分解生成音频数据流(mAudioTrack)和视频数据流(mVideoTrack)。
    onPrepareAsyncEvent()如果DataSource是URL的话,根据地址获取数据,并开始缓冲,直到获取到mVideoTrack和mAudioTrack。
    mVideoTrack和mAudioTrack通过调用initVideoDecoder()和initAudioDecoder()来生成 mVideoSource和mAudioSource这两个音视频解码器。
    然后调用postBufferingEvent_l()提交事件开启缓冲。
    4、数据缓冲的执行函数是onBufferingUpdate()。缓冲区有足够的数据可以播放时,调用play_l()开始播放。play_l()中关键是调用了postVideoEvent_l(),提交了 mVideoEvent。
    这个事件执行时会调用函数onVideoEvent(),内部执行mVideoSource->read(&mVideoBuffer, &options)进行视频解码。
    音频解码通过mAudioPlayer实现。
    5、视频解码器解码后通过mVideoSource->read读取一帧帧的数据,放到mVideoBuffer中,mVideoRenderer->render(mVideoBuffer)把视频数据发送到显示模块。
    当需要暂停或停止时,调用cancelPlayerEvents来提交事件用来停止解码,还可以选择是否继续缓冲数据。

    步骤解读:
    1、由数据源DataSource生成MediaExtractor。
    通过MediaExtractor::Create(dataSource)来实现。Create方法通过两步来生成相应的 MediaExtractor(MediaExtractor.cpp):

        通过dataSource->sniff来探测数据类型
        生成相应的Extractor:
        if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
                || !strcasecmp(mime, "audio/mp4")) {
            return new MPEG4Extractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
            return new MP3Extractor(source, meta);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
                || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
            return new AMRExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
            return new WAVExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
            return new OggExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
            return new MatroskaExtractor(source);
        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
            return new MPEG2TSExtractor(source);
        }

    2、音视频轨道分离,生成mVideoTrack和mAudioTrack两个MediaSource。代码如下(AwesomePlayer.cpp):

    if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
        setVideoSource(extractor->getTrack(i));
        haveVideo = true;
    } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
        setAudioSource(extractor->getTrack(i));
        haveAudio = true;
    }

    得到的这两个MediaSource,只具有parser功能,没有decode功能。还需要对这两个MediaSource做进一步的包装,获取了两个MediaSource(具有parse和decode功能):

    mVideoSource = OMXCodec::Create(
                mClient.interface(), mVideoTrack->getFormat(),
                false, // createEncoder
                mVideoTrack,
                NULL, flags);
    mAudioSource = OMXCodec::Create(
                    mClient.interface(), mAudioTrack->getFormat(),
                    false, // createEncoder
                    mAudioTrack);

    3、StageFright的Decode
    这里写图片描述
    从流中read packet,parse,decode .都在mVideoSource->read(&mVideoBuffer, &options)函数完成

    当调用MediaSource.start()方法后,它的内部就会开始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就可以调用MediaSource的read方法读取解码后的数据。
    a: 对于mVideoSource来说,读取的数据:
    mVideoSource->read(&mVideoBuffer, &options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBuffer);
    b: 对mAudioSource来说,用mAudioPlayer对mAudioSource进行封装,然后由mAudioPlayer负责读取数据和播放控制。

    经过“数据流的封装”得到的两个MediaSource,其实是两个OMXCodec。AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据进行播放。AwesomePlayer得到的是最终需要渲染的原始视频数据, 而mAudioPlayer读取的是最终需要播放的原始音频数据。也就是说,从OMXCodec中读到的数据已经是原始数据了。
    (1)、
    OMXCodec是怎么把数据源经过parse、decode两步以后转化成原始数据的。从OMXCodec::Create这个构造方法开始,它的参数:
    IOMX &omx //指的是一个OMXNodeInstance对象的实例。
    MetaData &meta //由MediaSource.getFormat获取得到。这个对象的主要成员就是一个KeyedVector<uint32_t, typed_data> mItems,里面存放了一些代表MediaSource格式信息的名值对。
    bool createEncoder //指明OMXCodec是编码还是解码。
    MediaSource &source //一个MediaExtractor。
    char *matchComponentName //指定一种Codec用于生成这个OMXCodec。

    a: findMatchingCodecs寻找对应的Codec,找到以后为当前IOMX分配节点并注册事件监听器:omx->allocateNode(componentName, observer, &node)。
    b: 把IOMX封装进一个OMXCodec:

    sp<OMXCodec> codec = new OMXCodec(
                        omx, node, quirks,
                        createEncoder, mime, componentName,
                        source);

    AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
    a: 向OpenMAX发送开始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
    b: 调用allocateBuffers()分配两个缓冲区,存放在Vector mPortBuffers[2]中,分别用于输入和输出。
    (2)、
    AwesomePlayer开始播放后,通过mVideoSource->read(&mVideoBuffer, &options)读取数据,实际调用OMXCodec.read来读取数据。而OMXCodec.read主要分两步来实现数据的读取:
    a:通过调用drainInputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完成 parse。由OpenMAX从数据源把demux后的数据读取到输入缓冲区,作为OpenMAX的输入。
    b:通过fillOutputBuffers()对mPortBuffers[kPortIndexOutput]进行填充,这一步完成 decode。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后可以显示的视频数据输出到输出缓冲区。
    (3)、
    AwesomePlayer通过mVideoRenderer->render(mVideoBuffer)对经过parse和decode 处理的数据进行渲染。一个mVideoRenderer其实就是一个包装了IOMXRenderer的AwesomeRemoteRenderer:

    mVideoRenderer = new AwesomeRemoteRenderer(
                    mClient.interface()->createRenderer(
                            mISurface, component,
                            (OMX_COLOR_FORMATTYPE)format,
                            decodedWidth, decodedHeight,
                                        mVideoWidth, mVideoHeight,
                            rotationDegrees));
    展开全文
  • android 4.0.3中stagefrightopenmax简述

    千次阅读 2012-03-01 17:49:31
    android 4.0.3 SF和Openmax关系  awesomplayer.cpp 中 awesomeplay有一个成员 OMXClient mClient;  这个mClient是通过MediaPlayerService的getOmx()方法创建(new OMX),代码如下 sp MediaPlayerService:...
  • stagefright,OpenMax框架

    千次阅读 2013-04-10 11:09:16
    转自:... stagefright框架(一)Video Playback的流程 在Android上,預設的多媒體框架(multimedia framework)是OpenCORE。OpenCORE的優點是兼顧了跨平台的移植性,而且已經過
  • Android OpenMax

    2014-03-08 21:28:00
    OpenMax是一个多媒体应用程序的框架标准...在Android中,OpenMax IL层(集成层)通常可以用于多媒体引擎的插件,Android多媒体引擎OpenCore和StageFright都可以使用OpenMax做为插件,主要用于编解码处理。 在And
  • stagefright框架

    2013-05-02 11:07:11
    stagefright是目前android主流的多媒体框架层,网上收集了一遍关于stagefright框架的介绍,比较全面,可以作为学习stagefrigth的入门资料。 stagefright框架(一)-Video Playback的流程 stagefright框架(二...
  • 音视频编码/解码过程及API. &gt; 开源音频库OpenCore和Stagefright android媒体开发,OpenCore和Stagefright(一)- ...Android Multimedia框架总结(九)Stagefright- https://www.jian...
  • android openmax AL/IL

    2012-12-29 09:40:18
    openmax IL/AL框架的分析,android平台openmax多媒体引擎分析,一个很好的在android上移植openmax时的参考资料,同时包含介绍了Android_系统下Stagefright_Player框架,对我开发起到了一定的帮助!
  • android openmax1

    2013-06-25 15:29:00
    Android的框架层,也定义了由...Android封装OpenMax的接口被StageFright使用,OpenCore没有使用这个接口,而是使用其他形式对OpenMax IL层接口进行封装。 对于我们使用硬件来解码来说,主要是使用了openmax的il...
  • Android平台OpenMax多媒体引擎介绍

    千次阅读 2013-07-08 09:56:59
    OpenMax是一个多媒体应用程序的框架... 在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解码(Codec)处理。  在Androi
  • 关键字:OMX, 多媒体框架, IL, Android, Stagefright 1、OpenMax 集成层介绍 OpenMax是一个多媒体应用程序的框架标准。它自上而下分为三层,Application Layer, Integration Layer和Development Layer。应用层...
  • Android 结构中,OpenMax IL 通常被当做多媒体引擎插件来使用,Android 最早的多媒体引擎是 OpenCore,后续版本逐渐使用 StageFright 来代替,这两种引擎可以使用 OpenMax 作为插件,主要实现编码/解码(Codec...
  • OpenMax多媒体引擎

    千次阅读 2013-12-19 14:40:52
    18.1 OpenMax系统结构和移植内容 OpenMax是一个多媒体应用程序的...在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解

空空如也

空空如也

1 2 3
收藏数 51
精华内容 20
关键字:

androidopenmaxstagefright