android的多媒体部分采用的编解码标准是OMX,当然这个标准是用于硬件编解码的,软件编解码在这里我就不说了。
直接从stagefright的awesomeplayer开始说起吧,如果看过我前面博客的人知道stagefright使用的三个步骤:

setdatasoure
 prepare
 start


至于它们的作用在这里就不多说了。
在prepare里面,当MediaExtractor解析文件后会产生一个音频流和一个视频流(可能还有字幕流)对应到stagefright里面就是一个MediaSource的数据结构。
也就是awesomeplayer里面的mVideoTrack和mAudioTrack两个数据成员。
得到音视频流后就要开始构造解码器了(暂且只说解码,编码类似)。请看initVideoDecoder或initAudioDecoder。

status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
 mVideoSource = OMXCodec::Create(
 mClient.interface(), mVideoTrack->getFormat(),//mClient.interface() is BnOMX
 false, // createEncoder
 mVideoTrack,
 NULL, flags);
 ...
 ...
 ...
 status_t err = mVideoSource->start(); if (err != OK) {
 mVideoSource.clear();
 return err;
 }
 }
 return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
 }


函数一开始就创建了一个OMXCodec,下面我们看下传进来的几个参数的意思:
===========================================================================
mClient.interface()
在awesomeplayer的构造函数里面有这么一句话 CHECK_EQ(mClient.connect(), OK);可以到OMXClient里面去看实际是通过MediaPlayerService创建了一个BnOMX(bnOMX会在后面讲到)然后作为自己的成员变量保存下来,这里我们可以将OMXClient看作OMX的客户端,BnOMX则是OMX的具体实现。
再回到mClient.interface(),就知道它返回的就是前面创建的BnOMX。
mVideoTrack->getFormat()
这个很显然是格式信息,但是这个里面不仅仅是编码格式,还有宽高,是否旋转等等,通称MetaData
false
指的是创建解码器,true则是编码器
mVideoTrack
视频流(解码前的压缩数据)
NULL
不指定解码器,如果不指定就会到现有的解码器中去找,选择第一个找到的
flags
解码器的类型,你可以在这里将解码器指定为软解码
===========================================================================
进入OMXCodec::Create函数

sp<MediaSource> OMXCodec::Create(
 const sp<IOMX> &omx,//BnOMX
 const sp<MetaData> &meta, bool createEncoder,
 const sp<MediaSource> &source,
 const char *matchComponentName,//NULL
 uint32_t flags) {
 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; const char *componentName;
 for (size_t i = 0; i < matchingCodecs.size(); ++i) {
 componentName = matchingCodecs[i].string();
 LOGV("componentName is %s",componentName);
 sp<MediaSource> softwareCodec = createEncoder?
 InstantiateSoftwareEncoder(componentName, source, meta):
 InstantiateSoftwareCodec(componentName, source); if (softwareCodec != NULL) {
 LOGV("Successfully allocated software codec '%s'", componentName); return softwareCodec;
 } LOGV("Attempting to allocate OMX node '%s'", componentName);
 uint32_t quirks = getComponentQuirks(componentName, createEncoder);
 if (!createEncoder
 && (quirks & kOutputBuffersAreUnreadable)
 && (flags & kClientNeedsFramebuffer)) {
 if (strncmp(componentName, "OMX.SEC.", 8)) {
 // For OMX.SEC.* decoders we can enable a special mode that
 // gives the client access to the framebuffer contents. LOGW("Component '%s' does not give the client access to "
 "the framebuffer contents. Skipping.",
 componentName); continue;
 }
 } status_t err = omx->allocateNode(componentName, observer, &node);
 if (err == OK) {
 LOGV("Successfully allocated OMX node '%s'", componentName); sp<OMXCodec> codec = new OMXCodec(
 omx, node, quirks,
 createEncoder, mime, componentName,
 source); observer->setCodec(codec);
 err = codec->configureCodec(meta, flags);
 if (err == OK) {
 return codec;
 } LOGV("Failed to configure codec '%s'", componentName);
 }
 } return NULL;


}

这里面有几个关键的函数
 findMatchingCodecs
 InstantiateSoftwareCodec
 omx->allocateNode


下面一一进行说明:
=========================================================================
findMatchingCodecs
这个函数里面实际是会到一个CodecInfo的数组里面去找到符合条件的解码器,这个数组在OMXCodec里面定义的。当然放在前面就被放到数组的前面保存在matchingCodecs里面,
然后通过for循环来遍历这个matchingCodecs数组。

InstantiateSoftwareCodec
在看这个函数前我声明一下,我分析的代码是2.3.x的在4.0的代码里面这里会有些区别,这个看我的另一篇blog stagefright之2.3和4.0的区别 就知道了。
在这个函数里面,会将matchingCodecs里面的解码器与一些软解码器进行比较(如果是硬解的话名字当然会不一样)。然而我们一般都是将硬解放在前面的,所以这个函数肯定会返回NULL
所以一般都会走到第三函数

omx->allocateNode
这个函数会调到BnOMX里面来。我们先看下它的三个参数componentName不多说,observer大家得留意了,它可是底层给我们上报消息的东西了,在后面会谈到,node显然是一个输出参数暂时我只能说它就是
一个区分不同解码器的标识。
下面看allocateNode的代码,这个比较重要

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);
 OMX_COMPONENTTYPE *handle;
 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);
 mDispatchers.add(*node, new CallbackDispatcher(instance));//CallbackDispatcher dispatch the callback message from omx hardware instance->setHandle(*node, handle);
 mLiveNodes.add(observer->asBinder(), instance);
 observer->asBinder()->linkToDeath(this); return OK;
 }


这里面先是创建了一个OMXNodeInstance,然后就是mMaster->makeComponentInstance再就是mDispatchers.add(*node, new CallbackDispatcher(instance))
最后instance->setHandle(*node, handle);
先简单介绍几个数据结构
OMXNodeInstance某一种类型的OMX,跟nodeid 一一对应
mMaste是OMXMaster这个说白了就是在本地OMX和硬件厂商的OMX之间做管理和协调工作的
mDispatchers是CallbackDispatcher类型的数组,它负责消息的分发(从硬件厂商获取消息分发到具体的某一种类型的解码器OMXNodeInstance,最后到前面讲的observer)
instance->setHandle(*node, handle)这里的这个handle就很关键了,所有的操作都必须通过它来完成
=========================================================================
细说一下OMXMaster
构造函数里面

OMXMaster::OMXMaster()
 : mVendorLibHandle(NULL) {
 addVendorPlugin();#ifndef NO_OPENCORE
 addPlugin(new OMXPVCodecsPlugin);
 #endif
 }
 在没有Opencore的情况下我们只看addVendorPlugin(),这从函数名就可以看出来就是将厂家的插件加入进来。
 void OMXMaster::addVendorPlugin() {
 mVendorLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW); if (mVendorLibHandle == NULL) {
 return;
 } typedef OMXPluginBase *(*CreateOMXPluginFunc)();
 CreateOMXPluginFunc createOMXPlugin =
 (CreateOMXPluginFunc)dlsym(
 mVendorLibHandle, "_ZN7android15createOMXPluginEv"); if (createOMXPlugin) {
 addPlugin((*createOMXPlugin)());
 }
 }


看到了吧,这里开始使用动态库来调用了,也就是说厂家自己替换这个库就行了。
至于要实现什么东西是有标准的,这里我就不多说了。
像CreateOMXPluginFunc这个函数是一定得有的,也就是所创建了一个厂家提供的OMX插件。
再看

void OMXMaster::addPlugin(OMXPluginBase *plugin) {
 Mutex::Autolock autoLock(mLock); mPlugins.push_back(plugin);
 OMX_U32 index = 0;
 char name[128];
 OMX_ERRORTYPE err;
 while ((err = plugin->enumerateComponents(
 name, sizeof(name), index++)) == OMX_ErrorNone) {
 String8 name8(name); if (mPluginByComponentName.indexOfKey(name8) >= 0) {
 LOGE("A component of name '%s' already exists, ignoring this one.",
 name8.string()); continue;
 } mPluginByComponentName.add(name8, plugin);
 }
 CHECK_EQ(err, OMX_ErrorNoMore);
 }


这里有一个数组保存这些插件,plugin->enumerateComponents这句话的意思是说把厂家提供的这个OMX插件支持的解码器格式一一放入到mPluginByComponentName里面,以后需要的这种格式的解码
器就到这里来找。
回到OMXMaster::makeComponentInstance这个函数,它在OMX::allocateNode中被调用的,

OMX_ERRORTYPE OMXMaster::makeComponentInstance(
 const char *name,//name is codec type 
 const OMX_CALLBACKTYPE *callbacks,
 OMX_PTR appData,
 OMX_COMPONENTTYPE **component) {
 Mutex::Autolock autoLock(mLock); *component = NULL;
 ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
 if (index < 0) {
 return OMX_ErrorInvalidComponentName;
 } OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
 OMX_ERRORTYPE err =
 plugin->makeComponentInstance(name, callbacks, appData, component); if (err != OMX_ErrorNone) {
 return err;
 } mPluginByInstance.add(*component, plugin);
 return err;
 }


可以看到这里就通过插件创建了实例,这里的component就是handle,也是底下返回的。callbacks就是OMXNodeInstance里面的几个回调函数OnEvent,OnEmptyBufferDone,OnFillBufferDone

总结一下操作的流程:

OMXCodec ---> BnOMX ----> OMXNodeInstance -----> handle(看作是厂家OMX的句柄)

消息回调的流程就是

handle ---->OnEvent(OMXNodeInstance) ------->OnEvent(BnOMX)-------->post(CallbackDispatcher)-------->onMessage(OMXNodeInstance)
 ----->onMessage(OMXCodecObserver)------->on_message(OMXCodec)

操作的流程大家都清楚了,下面正式进入我们最关心的,OMX是怎么来实现解码,又是怎么把解码后的数据交给我们的:
上面的操作流程里面我们知道了解码器的创建,开始解码我们必须调用OMXCodec的start函数

status_t OMXCodec::start(MetaData *meta) {
 ...
 ...
 ...
 status_t err = mSource->start(params.get()); if (err != OK) {
 return err;
 }
 ...
 ...
 ...

 return init();
 }


我选择了两个比较重要的地方贴了出来。
mSource->start(params.get());
这句话实际是开启了音视频流Track,也就是说准备好压缩数据给解码器去取。
init()里面会调用一个非常重要的函数allocateBuffers();从字面上看是分配内存,没错它就是分配内存的。

status_t OMXCodec::allocateBuffers() {
 status_t err = allocateBuffersOnPort(kPortIndexInput); if (err != OK) {
 return err;
 } return allocateBuffersOnPort(kPortIndexOutput);
 }


它分配了两块内存,一块用于输入,一块用于输出。
至于这里面的实现不同的厂家又有所不同了,我之前做过的一个项目是从/dev/pmem_adsp这个设备中映射出来的一块内存。当然,内存的大小跟厂家提供的解码器的能力是相关的可以通过
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
来获取(前面我们讲到了调用的流程,这里就不再多说,总之看到调OMX的最终都会到厂家自定义里面去)。申请的内存在代码中被分为一块一块的(了解camera底层实现的应该知道,这个跟camera的风格很类似)
这些内存块会放到一个叫mPortBuffers[inPort/outPort]的容器中

在awesomeplayer里面开始进行播放后不管是视频还是音频都会调OMXCodec里面的read函数。

status_t OMXCodec::read(
 MediaBuffer **buffer, const ReadOptions *options) {
 *buffer = NULL; ...
 ...
 ... if (mInitialBufferSubmit) {
 mInitialBufferSubmit = false;...
 ...
 ...
 drainInputBuffers();//key word if (mState == EXECUTING) {
 // Otherwise mState == RECONFIGURING and this code will trigger
 // after the output port is reenabled.
 fillOutputBuffers();//key word
 }
 }
 ...
 ...
 ...

while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
 LOGV("NO MORE OUTPUT DATA=============");
 mBufferFilled.wait(mLock);//key word
 } if (mState == ERROR) {
 return UNKNOWN_ERROR;
 } if (mFilledBuffers.empty()) {
 return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;
 } if (mOutputPortSettingsHaveChanged) {
 mOutputPortSettingsHaveChanged = false; return INFO_FORMAT_CHANGED;
 } size_t index = *mFilledBuffers.begin();
 mFilledBuffers.erase(mFilledBuffers.begin()); BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);//key word ,we got original data
 info->mMediaBuffer->add_ref();
 *buffer = info->mMediaBuffer; return OK;
 }


read要做的事就是从Track里面读取数据给解码器解码后返回给awesomeplayer。而其中最关键的就是

mOMX->emptyBuffer(
 mNode, info->mBuffer, 0, offset,
 flags, timestampUs);//在drainInputBuffers中被调用
 和mOMX->fillBuffer(mNode, info->mBuffer);//在fillOutputBuffers中被调用


前面一个函数是将从Track读取来的数据交给OMX解码器解码,而后一个函数就是向OMX解码器请求获取解码后的数据。
这两个操作完成后都是有回调的,至于回调的地方大家自己找吧!上面流程里面已经提到过了。

由于时间的原因后面说的比较简洁,但是大家认真看代码再结合我说的看起来应该没问题的。
先就说这么多吧!希望如果有搞驱动或硬件的能贴出来一些厂家OMX具体实现的代码,并且做下讲解,大家一起学习,感激不尽!