-
Android-StageFright之OpenMAX的实现
2012-06-11 21:54:57Android-StageFright之OpenMAX的实现 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 学习记录------1
2013-09-21 21:20:16android_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> ¶ms, 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> ¶ms, 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_ics openmax in stagefright 学习记录------2
2013-09-21 21:22:12ANDROID_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:10android 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中stagefright和openmax简述
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:00OpenMax是一个多媒体应用程序的框架标准...在Android中,OpenMax IL层(集成层)通常可以用于多媒体引擎的插件,Android多媒体引擎OpenCore和StageFright都可以使用OpenMax做为插件,主要用于编解码处理。 在And -
stagefright框架
2013-05-02 11:07:11stagefright是目前android主流的多媒体框架层,网上收集了一遍关于stagefright框架的介绍,比较全面,可以作为学习stagefrigth的入门资料。 stagefright框架(一)-Video Playback的流程 stagefright框架(二... -
音视频框架Opencore(PacketVideo) /Stagefright-【OpenMAX】;DRM,TinyAlsa
2017-05-28 20:46:29音视频编码/解码过程及API. > 开源音频库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:59OpenMax是一个多媒体应用程序的框架... 在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解码(Codec)处理。 在Androi -
OpenMax在Android上的实现
2015-05-22 14:06:26关键字:OMX, 多媒体框架, IL, Android, Stagefright 1、OpenMax 集成层介绍 OpenMax是一个多媒体应用程序的框架标准。它自上而下分为三层,Application Layer, Integration Layer和Development Layer。应用层... -
Android 系统多媒体(一)- OpenMax 认识
2019-01-09 16:17:49在 Android 结构中,OpenMax IL 通常被当做多媒体引擎插件来使用,Android 最早的多媒体引擎是 OpenCore,后续版本逐渐使用 StageFright 来代替,这两种引擎可以使用 OpenMax 作为插件,主要实现编码/解码(Codec... -
OpenMax多媒体引擎
2013-12-19 14:40:5218.1 OpenMax系统结构和移植内容 OpenMax是一个多媒体应用程序的...在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解
-
元素周期表-three.js实战详解
-
Flutter布局详解
-
无线计算卸载的能量和时间优化
-
通过新颖的二元君主蝶优化算法解决0-1背包问题
-
使用Dom4j操作XML
-
2021-02-25
-
Go-SpeedTest-Bot:帮助您使用手机管理所有节点的机器人-源码
-
使用内置传感器的LED进行LED热阻和TIM评估的研究
-
2019年上半年 网络工程师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
响应式编程入门与实战(Reactor、WebFlux、R2DBC)
-
bert_sentiment_analysis_finetuning-源码
-
docker基本使用教程, 以及docker部署flask框架示例
-
解决问题:这是解决问题的仓库-源码
-
spark大数据分析与实战
-
leetcode算法第5题
-
生成、添加用于操作多个 Git 账户的 SSH 公钥配置
-
2019年下半年 信息系统监理师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
第1关上 将错就错.mp4
-
NearFi应用-源码
-
2015年上半年 信息系统管理工程师 上午试卷 综合知识 软考真题【含答案和答案解析】