【stagefrightplayer】4 OMX Codec介绍

概述

OMX Codec是stagefrightplayer中负责解码的模块。

由于遵循openmax接口规范,因此结构稍微有点负责,这里就依照awesomeplayer中的调用顺序来介绍。

主要分如下几步:

1 mClient->connect

2 InitAudioDecoder & InitVideoDecoder

3 消息通信机制模型的介绍

4 解码过程介绍

先看下类图

 

 

这里OMX Codec是以service的方式提供服务的。Awesomeplayer中通过mOmx(IOMX) 作为客户端通过binder方式与OMX 通信完成解码的工作

下面一句具体代码分析

1 mClient->connect

在awesomeplayer的构造函数中调用,具体代码如下

[html] view plain copy print ?
  1. AwesomePlayer::AwesomePlayer()
  2. {
  3. ******
  4. CHECK_EQ(mClient.connect(), (status_t)OK);
  5. ******
  6. }

看下具体实现

[html] view plain copy print ?
  1. status_t OMXClient::connect() {
  2. sp<IServiceManager> sm = defaultServiceManager();
  3. sp<IBinder> binder = sm->getService(String16("media.player"));
  4. sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
  5. CHECK(service.get() != NULL);
  6. mOMX = service->getOMX();
  7. CHECK(mOMX.get() != NULL);
  8. if (!mOMX->livesLocally(NULL /* node */, getpid())) {
  9. ALOGI("Using client-side OMX mux.");
  10. mOMX = new MuxOMX(mOMX);
  11. }
  12. return OK;
  13. }

这里主要就是通过binder机制与mediaplayerservice通信来完成,具体实现看mediaplayerservice

[html] view plain copy print ?
  1. sp<IOMX> MediaPlayerService::getOMX() {
  2. Mutex::Autolock autoLock(mLock);
  3. if (mOMX.get() == NULL) {
  4. mOMX = new OMX;
  5. }
  6. return mOMX;
  7. }


 

主要就是构造一个OMX对象返回给上层保存在mClient的IOMX对象mOmx中

看下构造函数都做了啥

[html] view plain copy print ?
  1. OMX::OMX()
  2. : mMaster(new OMXMaster),
  3. mNodeCounter(0) {
  4. }

在构造函数中又调用了OMXMaster的构造函数,代码如下

[html] view plain copy print ?
  1. OMXMaster::OMXMaster()
  2. : mVendorLibHandle(NULL) {
  3. addVendorPlugin();
  4. addPlugin(new SoftOMXPlugin);
  5. }

这里OMXMaster可以看成是解码器的入口,通过makeComponentInstance建立解码器的实例,之后就可以进行解码操作了。

这里我们以软件解码器插件为例来看整个流程,主要是addPlugin(newSoftOMXPlugin);先看SoftOMXPlugin构造函数

[html] view plain copy print ?
  1. SoftOMXPlugin::SoftOMXPlugin() {
  2. }

是空的~~

再看下addPlugin代码

[html] view plain copy print ?
  1. void OMXMaster::addPlugin(OMXPluginBase *plugin) {
  2. Mutex::Autolock autoLock(mLock);
  3. mPlugins.push_back(plugin);
  4. OMX_U32 index = 0;
  5. char name[128];
  6. OMX_ERRORTYPE err;
  7. while ((err = plugin->enumerateComponents(
  8. name, sizeof(name), index++)) == OMX_ErrorNone) {
  9. String8 name8(name);
  10. if (mPluginByComponentName.indexOfKey(name8) >= 0) {
  11. ALOGE("A component of name '%s' already exists, ignoring this one.",
  12. name8.string());
  13. continue;
  14. }
  15. mPluginByComponentName.add(name8, plugin);
  16. }
  17. if (err != OMX_ErrorNoMore) {
  18. ALOGE("OMX plugin failed w/ error 0x%08x after registering %d "
  19. "components", err, mPluginByComponentName.size());
  20. }
  21. }

这里传入的plugin参数时上面SoftOMXPlugin 构造函数产生的实例

从代码可以看出主要是将enumerateComponents枚举出来的各种解码器存放在成员变量mPluginByComponentName中,类型为 KeyedVector<String8, OMXPluginBase *>

看下enumerateComponents实现

[html] view plain copy print ?
  1. static const struct {
  2. const char *mName;
  3. const char *mLibNameSuffix;
  4. const char *mRole;
  5. } kComponents[] = {
  6. { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
  7. { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
  8. { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
  9. { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
  10. { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
  11. { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
  12. { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
  13. { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
  14. { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
  15. { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
  16. { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
  17. { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
  18. { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
  19. { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
  20. { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
  21. { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
  22. { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" },
  23. { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
  24. { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
  25. };
[html] view plain copy print ?
  1. OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
  2. OMX_STRING name,
  3. size_t size,
  4. OMX_U32 index) {
  5. if (index >= kNumComponents) {
  6. return OMX_ErrorNoMore;
  7. }
  8. strcpy(name, kComponents[index].mName);
  9. return OMX_ErrorNone;
  10. }


这里enumerateComponents主要就是将数组kComponents中的以为index下标的plugin传递出来

这里只是将插件名字返回最终存储在mPluginByComponentName列表中

后面还会通过makeComponentInstance产生实际的解码器实例,后面再详细看

至此mClient->connect()就结束了。

这里的主要工作就是通过getOMX()在mediaplayerservice端构造一个OMX实例,并返回给mClient的IOMX成员mOmx中

而且在OMX的构造函数中调用OMXMaster的构造函数,可以通过makeComponentInstance 建立实际的解码器实例。

2 InitAudioDecoder & InitVideoDecoder

awesomeplayer构造函数结束后,在setDataSource之后会调用prepare方法,其实现中会调用initAudioDecoder和initVideoDecoder

由于在setDataSource中已经拿到了对应的解码器信息,因此此处initAudioDecoder 便可以构造实际的解码器了。以audio为例

[html] view plain copy print ?
  1. status_t AwesomePlayer::initAudioDecoder() {
  2. mAudioSource = OMXCodec::Create(
  3. mClient.interface(), mAudioTrack->getFormat(),
  4. false, // createEncoder
  5. mAudioTrack);}
  6. status_t err = mAudioSource->start();
  7. return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
  8. }

这里只列出的主要操作,下面依次来看OMXCodec::Create和mAudioSource->start的主要工作

代码比较多,我们这里主要将重要的代码列出,无关代码省略

[html] view plain copy print ?
  1. sp<MediaSource> OMXCodec::Create(*)
  2. {
  3. findMatchingCodecs(
  4. mime, createEncoder, matchComponentName, flags, &matchingCodecs);
  5. sp<OMXCodecObserver> observer = new OMXCodecObserver;
  6. IOMX::node_id node = 0;
  7. status_t err = omx->allocateNode(componentName, observer, &node);
  8. sp<OMXCodec> codec = new OMXCodec(
  9. omx, node, quirks, flags,
  10. createEncoder, mime, componentName,
  11. source, nativeWindow);
  12. observer->setCodec(codec);
  13. err = codec->configureCodec(meta);
  14. }

下面依次来看每个过程

2.1 findMatchingCodecs

先看下代码

[html] view plain copy print ?
  1. void OMXCodec::findMatchingCodecs(
  2. const char *mime,
  3. bool createEncoder, const char *matchComponentName,
  4. uint32_t flags,
  5. Vector<CodecNameAndQuirks> *matchingCodecs) {
  6. matchingCodecs->clear();
  7. const MediaCodecList *list = MediaCodecList::getInstance();
  8. if (list == NULL) {
  9. return;
  10. }
  11. size_t index = 0;
  12. for (;;) {
  13. ssize_t matchIndex =
  14. list->findCodecByType(mime, createEncoder, index);
  15. if (matchIndex < 0) {
  16. break;
  17. }
  18. index = matchIndex + 1;
  19. const char *componentName = list->getCodecName(matchIndex);
  20. // If a specific codec is requested, skip the non-matching ones.
  21. if (matchComponentName && strcmp(componentName, matchComponentName)) {
  22. continue;
  23. }
  24. // When requesting software-only codecs, only push software codecs
  25. // When requesting hardware-only codecs, only push hardware codecs
  26. // When there is request neither for software-only nor for
  27. // hardware-only codecs, push all codecs
  28. if (((flags & kSoftwareCodecsOnly) && IsSoftwareCodec(componentName)) ||
  29. ((flags & kHardwareCodecsOnly) && !IsSoftwareCodec(componentName)) ||
  30. (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
  31. ssize_t index = matchingCodecs->add();
  32. CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index);
  33. entry->mName = String8(componentName);
  34. entry->mQuirks = getComponentQuirks(list, matchIndex);
  35. ALOGV("matching '%s' quirks 0x%08x",
  36. entry->mName.string(), entry->mQuirks);
  37. }
  38. }
  39. if (flags & kPreferSoftwareCodecs) {
  40. matchingCodecs->sort(CompareSoftwareCodecsFirst);
  41. }
  42. }


从代码可以看到主要就是从MediaCodecList找到与传入的matchComponentName对应的解码器

MediaCodecList 的实现不看了,感兴趣的看下,主要就是从/etc/media_codecs.xml解析出支持的解码器并匹配出对应的解码器

举例:<MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg" />

这里需要注意的是在前面我们看到 kComponents 数组定义了支持的解码器,这里/etc/media_codecs.xml 也列出了对应的解码器,这里名字要对应上

这里找到符合条件的解码器便通过matchingCodecs->add()添加一个项,并将各个成员赋值,主要是name

最终符合条件的插件便都放在了matchingCodecs列表中

2.2 allocateNode

这里主要有如下重要代码

sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node = 0;

status_t err = omx->allocateNode(componentName, observer, &node);

observer的作用主要用于消息传递。

这里allocateNode主要是调用service端的OMX类来完成工作(省略中间的binder操作)

[html] view plain copy print ?
  1. status_t OMX::allocateNode(
  2. const char *name, const sp<IOMXObserver> &observer, node_id *node) {
  3. Mutex::Autolock autoLock(mLock);
  4. *node = 0;
  5. OMXNodeInstance *instance = new OMXNodeInstance(this, observer);
  6. OMX_COMPONENTTYPE *handle;
  7. OMX_ERRORTYPE err = mMaster->makeComponentInstance(
  8. name, &OMXNodeInstance::kCallbacks,
  9. instance, &handle);
  10. if (err != OMX_ErrorNone) {
  11. ALOGV("FAILED to allocate omx component '%s'", name);
  12. instance->onGetHandleFailed();
  13. return UNKNOWN_ERROR;
  14. }
  15. *node = makeNodeID(instance);
  16. mDispatchers.add(*node, new CallbackDispatcher(instance));
  17. instance->setHandle(*node, handle);
  18. mLiveNodes.add(observer->asBinder(), instance);
  19. observer->asBinder()->linkToDeath(this);
  20. return OK;
  21. }

首先构造了OMXNodeInstance,封装了传入的observer参数,作为消息传递的载体

调用mMaster->makeComponentInstance生成实际的解码器实例

生成node_id

将node_id与实际的解码器handle保存在instance中,最终instance会保存在OMX的mLiveNodes列表中

这样OMXCodec就可以通过OMXNodeInstance与解码器通信了,具体参考下面通信模型。

后面会介绍通信过程。这里重点讲解一下与解码器的操作

上面代码中通过mMaster->makeComponentInstance创建了解码器的实例,这里我们以android自带的mp3 解码器为例来讲解

通过上面介绍mp3解码器对应的项为(/etc/media_codecs.xml):

<MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg" />

而findMatchingCodecs 传入的字符串为:audio/mpeg 以此为依据进行匹配

这里找到对应的解码器后,解码器的名字为:OMX.google.mp3.decoder

这样便可以通过查表(数组kComponents )得到实际的解码器了

实际的mp3解码器代码文件为:framework/av/media/libstagefright/codecs/mp3dec/SoftMP3.cpp

调用的方法为:mMaster->makeComponentInstance 实际代码是

[html] view plain copy print ?
  1. OMX_ERRORTYPE OMXMaster::makeComponentInstance(
  2. const char *name,
  3. const OMX_CALLBACKTYPE *callbacks,
  4. OMX_PTR appData,
  5. OMX_COMPONENTTYPE **component) {
  6. Mutex::Autolock autoLock(mLock);
  7. *component = NULL;
  8. ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
  9. if (index < 0) {
  10. return OMX_ErrorInvalidComponentName;
  11. }
  12. OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
  13. OMX_ERRORTYPE err =
  14. plugin->makeComponentInstance(name, callbacks, appData, component);
  15. if (err != OMX_ErrorNone) {
  16. return err;
  17. }
  18. mPluginByInstance.add(*component, plugin);
  19. return err;
  20. }

主要是调用插件的makeComponentInstance方法

这里插件是通过OMXMaster构造函数addPlugin(new SoftOMXPlugin);加载的插件,因此这里makeComponentInstance 是SoftOMXPlugin 的方法

看下具体实现

[html] view plain copy print ?
  1. OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
  2. const char *name,
  3. const OMX_CALLBACKTYPE *callbacks,
  4. OMX_PTR appData,
  5. OMX_COMPONENTTYPE **component) {
  6. ALOGV("makeComponentInstance '%s'", name);
  7. for (size_t i = 0; i < kNumComponents; ++i) {
  8. if (strcmp(name, kComponents[i].mName)) {
  9. continue;
  10. }
  11. AString libName = "libstagefright_soft_";
  12. libName.append(kComponents[i].mLibNameSuffix);
  13. libName.append(".so");
  14. void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
  15. if (libHandle == NULL) {
  16. ALOGE("unable to dlopen %s", libName.c_str());
  17. return OMX_ErrorComponentNotFound;
  18. }
  19. typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
  20. const char *, const OMX_CALLBACKTYPE *,
  21. OMX_PTR, OMX_COMPONENTTYPE **);
  22. CreateSoftOMXComponentFunc createSoftOMXComponent =
  23. (CreateSoftOMXComponentFunc)dlsym(
  24. libHandle,
  25. "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
  26. "PvPP17OMX_COMPONENTTYPE");
  27. if (createSoftOMXComponent == NULL) {
  28. dlclose(libHandle);
  29. libHandle = NULL;
  30. return OMX_ErrorComponentNotFound;
  31. }
  32. sp<SoftOMXComponent> codec =
  33. (*createSoftOMXComponent)(name, callbacks, appData, component);
  34. if (codec == NULL) {
  35. dlclose(libHandle);
  36. libHandle = NULL;
  37. return OMX_ErrorInsufficientResources;
  38. }
  39. OMX_ERRORTYPE err = codec->initCheck();
  40. if (err != OMX_ErrorNone) {
  41. dlclose(libHandle);
  42. libHandle = NULL;
  43. return err;
  44. }
  45. codec->incStrong(this);
  46. codec->setLibHandle(libHandle);
  47. return OMX_ErrorNone;
  48. }
  49. return OMX_ErrorInvalidComponentName;
  50. }

这里主要是通过枚举kComponents找到对应的解码器记录

{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },

这里可以看到每个库都是以.so的方式提供的,命名符合如下规则:libstagefright_soft_mp3dec.so

通过dlopen加载后通过dlsym找到createSoftOMXComponent方法并执行,这里每个解码器都应该实现此函数

这里看下mp3的具体实现

[html] view plain copy print ?
  1. android::SoftOMXComponent *createSoftOMXComponent(
  2. const char *name, const OMX_CALLBACKTYPE *callbacks,
  3. OMX_PTR appData, OMX_COMPONENTTYPE **component) {
  4. return new android::SoftMP3(name, callbacks, appData, component);
  5. }

主要工作就是构造了SoftMP3的类对象并返回,这里注意并没有将解码器句柄返回给上层,而是在构造函数中将这种联系放在给定的OMX_COMPONENTTYPE **component参数中

看下构造函数

[html] view plain copy print ?
  1. SoftMP3::SoftMP3(
  2. const char *name,
  3. const OMX_CALLBACKTYPE *callbacks,
  4. OMX_PTR appData,
  5. OMX_COMPONENTTYPE **component)
  6. : SimpleSoftOMXComponent(name, callbacks, appData, component),
  7. mConfig(new tPVMP3DecoderExternal),
  8. mDecoderBuf(NULL),
  9. mAnchorTimeUs(0),
  10. mNumFramesOutput(0),
  11. mNumChannels(2),
  12. mSamplingRate(44100),
  13. mSignalledError(false),
  14. mOutputPortSettingsChange(NONE) {
  15. initPorts();
  16. initDecoder();
  17. }

这里就是基本的初始化操作,这里SoftMP3是继承自SimpleSoftOMXComponent,因此会调用其构造函数,如下

[html] view plain copy print ?
  1. SimpleSoftOMXComponent::SimpleSoftOMXComponent(
  2. const char *name,
  3. const OMX_CALLBACKTYPE *callbacks,
  4. OMX_PTR appData,
  5. OMX_COMPONENTTYPE **component)
  6. : SoftOMXComponent(name, callbacks, appData, component),
  7. mLooper(new ALooper),
  8. mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),
  9. mState(OMX_StateLoaded),
  10. mTargetState(OMX_StateLoaded) {
  11. mLooper->setName(name);
  12. mLooper->registerHandler(mHandler);
  13. mLooper->start(
  14. false, // runOnCallingThread
  15. false, // canCallJava
  16. ANDROID_PRIORITY_FOREGROUND);
  17. }

这里主要是构造了Alooper 对象来处理消息,以及调用了父类也就是SoftOMXComponent的构造函数‍

关于alooper的工作原理,后面会有一篇专门的文章介绍。

[html] view plain copy print ?
  1. SoftOMXComponent::SoftOMXComponent(
  2. const char *name,
  3. const OMX_CALLBACKTYPE *callbacks,
  4. OMX_PTR appData,
  5. OMX_COMPONENTTYPE **component)
  6. : mName(name),
  7. mCallbacks(callbacks),
  8. mComponent(new OMX_COMPONENTTYPE),
  9. mLibHandle(NULL) {
  10. mComponent->nSize = sizeof(*mComponent);
  11. mComponent->pComponentPrivate = this;
  12. mComponent->SetParameter = SetParameterWrapper;
  13. *********
  14. mComponent->UseEGLImage = NULL;
  15. mComponent->ComponentRoleEnum = NULL;
  16. *component = mComponent;
  17. }

这里才是构造component的地方,并初始化了其中的方法,如

mComponent->SetParameter = SetParameterWrapper;

[html] view plain copy print ?
  1. OMX_ERRORTYPE SoftOMXComponent::SetParameterWrapper(
  2. OMX_HANDLETYPE component,
  3. OMX_INDEXTYPE index,
  4. OMX_PTR params) {
  5. SoftOMXComponent *me =
  6. (SoftOMXComponent *)
  7. ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
  8. return me->setParameter(index, params);
  9. }

这里初始化了mComponent 的SetParameter 方法为SoftOMXComponent::SetParameterWrapper而从构造函数知道

mComponent->pComponentPrivate = this;

因此实际调用的是this->SetParameter 也就是其子类的实现

(这里很重要,请注意理解透彻)

通过上面分析可以知道,android已经为解码器的消息传递通过两个父类及SoftOMXComponent和SimpleSoftOMXComponent完成了

后面解码器只要从SimpleSoftOMXComponent 继承并实现对应的消息处理就可以了

在mp3的构造函数中还有如下语句

initPorts(); 主要作用是配置解码器的输入输出
initDecoder(); 实际的解码器

这里需要注意的是在SoftMP3的代码里便可以调用实际解码器的init decoder等操作了,而SoftMP3可以认为是实际解码器的封装

具体调用顺序会在后面消息处理阶段介绍。

到这里allocateNode 就介绍完了:主要工作就是建立与解码器的联系 observer nodeid,以及找到实际的解码器并初始化

2.3OMXCodec构造函数

后面的执行语句如下:

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

具体实现如下

[html] view plain copy print ?
  1. OMXCodec::OMXCodec(
  2. const sp<IOMX> &omx, IOMX::node_id node,
  3. uint32_t quirks, uint32_t flags,
  4. bool isEncoder,
  5. const char *mime,
  6. const char *componentName,
  7. const sp<MediaSource> &source,
  8. const sp<ANativeWindow> &nativeWindow)
  9. : mOMX(omx),
  10. mOMXLivesLocally(omx->livesLocally(node, getpid())),
  11. mNode(node),
  12. mQuirks(quirks),
  13. mFlags(flags),
  14. mIsEncoder(isEncoder),
  15. mIsVideo(!strncasecmp("video/", mime, 6)),
  16. mMIME(strdup(mime)),
  17. mComponentName(strdup(componentName)),
  18. mSource(source),
  19. mCodecSpecificDataIndex(0),
  20. mState(LOADED),
  21. mInitialBufferSubmit(true),
  22. mSignalledEOS(false),
  23. mNoMoreOutputData(false),
  24. mOutputPortSettingsHaveChanged(false),
  25. mSeekTimeUs(-1),
  26. mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC),
  27. mTargetTimeUs(-1),
  28. mOutputPortSettingsChangedPending(false),
  29. mSkipCutBuffer(NULL),
  30. mLeftOverBuffer(NULL),
  31. mPaused(false),
  32. mNativeWindow(
  33. (!strncmp(componentName, "OMX.google.", 11)
  34. || !strcmp(componentName, "OMX.Nvidia.mpeg2v.decode"))
  35. ? NULL : nativeWindow) {
  36. mPortStatus[kPortIndexInput] = ENABLED;
  37. mPortStatus[kPortIndexOutput] = ENABLED;
  38. setComponentRole();
  39. }

这里主要就是将之前的所有工作,都保存在OMXCodec实例中,之后awesomeplayer便直接操作OMXCodec(mAudioSource)了

这里 setComponentRole();主要是设置角色(编码器还是解码器),后面再介绍消息传递时介绍。

这里还要注意的是OMXCodec继承自MediaSource&MediaBufferObserver

因此才可以作为输出模块的数据源

2.4 配置解码器

observer->setCodec(codec);

err = codec->configureCodec(meta);

这两部分在我们后面介绍完消息机制之后,读者可自行回来分析

上面介绍了create的操作

下面介绍mAudioSource->start 的操作

[html] view plain copy print ?
  1. status_t OMXCodec::start(MetaData *meta) {
  2. mSource->start(params.get());
  3. return init();
  4. }

只列出了重要代码,其中mSource->start是指启动解码器的数据源MediaSource,这里也就是extractor中通过getTrack拿到的mediaSource。比较简单不说了

看下init实现 ,省略无关代码

[html] view plain copy print ?
  1. status_t OMXCodec::init() {
  2. err = allocateBuffers();
  3. return mState == ERROR ? UNKNOWN_ERROR : OK;
  4. }

主要工作是通过allocateBuffers申请内存

[html] view plain copy print ?
  1. status_t OMXCodec::allocateBuffers() {
  2. status_t err = allocateBuffersOnPort(kPortIndexInput);
  3. if (err != OK) {
  4. return err;
  5. }
  6. return allocateBuffersOnPort(kPortIndexOutput);
  7. }

这里分别申请输入和输出的buffer

这里分段来看allocateBuffersOnPort函数

[html] view plain copy print ?
  1. status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
  2. OMX_PARAM_PORTDEFINITIONTYPE def;
  3. InitOMXParams(&def);
  4. def.nPortIndex = portIndex;
  5. err = mOMX->getParameter(
  6. mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
  7. size_t totalSize = def.nBufferCountActual * def.nBufferSize;
  8. mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");

这里开头先通过命令OMX_IndexParamPortDefinition获取解码器配置的大小

然后构造MemoryDealer实例,存放buffer数量及大小信息

这里命令的传输过程请参考消息通讯机制模型的介绍,看完再回来理解这部分

以mp3为例,在SoftMP3的构造函数中会调用initPorts来初始化OMX_PARAM_PORTDEFINITIONTYPE对象

里面会确定buffer的大小:包括有几个buffer,每个buffer的容量等

这里OMX_IndexParamPortDefinition主要是查询此信息,然后就知道要申请多少内存了

[html] view plain copy print ?
  1. for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
  2. sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
  3. CHECK(mem.get() != NULL);
  4. BufferInfo info;
  5. info.mData = NULL;
  6. info.mSize = def.nBufferSize;
  7. err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
  8. if (mem != NULL) {
  9. info.mData = mem->pointer();
  10. }
  11. info.mBuffer = buffer;
  12. info.mStatus = OWNED_BY_US;
  13. info.mMem = mem;
  14. info.mMediaBuffer = NULL;
  15. mPortBuffers[portIndex].push(info);
  16. CODEC_LOGV("allocated buffer %p on %s port", buffer,
  17. portIndex == kPortIndexInput ? "input" : "output");
  18. }

下面是一个循环(忽略了secure等无关代码)

主要是申请内存,并为每个内存新建一个BufferInfo变量,最终都放在mPortBuffers[index]对应的栈中

至此InitAudioDecoder 便执行完毕了,主要做了两件事:建立实际的解码器+申请buffer

3 消息通信机制模型的介绍

当与解码器的联系建立之后,后面的工作主要就是传递消息由解码器处理将处理结果返回给调用者

但前面的介绍对消息模型并不清晰,这里专门介绍一下

下面就以 OMXCodec构造函数中的 setComponentRole();为例来介绍此过程

具体代码如下:

[html] view plain copy print ?
  1. void OMXCodec::setComponentRole() {
  2. setComponentRole(mOMX, mNode, mIsEncoder, mMIME);
  3. }

[html] view plain copy print ?
  1. // static
  2. void OMXCodec::setComponentRole(
  3. const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
  4. const char *mime) {
  5. struct MimeToRole {
  6. const char *mime;
  7. const char *decoderRole;
  8. const char *encoderRole;
  9. };
  10. static const MimeToRole kMimeToRole[] = {
  11. { MEDIA_MIMETYPE_AUDIO_MPEG,
  12. "audio_decoder.mp3", "audio_encoder.mp3" },
  13. { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I,
  14. "audio_decoder.mp1", "audio_encoder.mp1" },
  15. { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II,
  16. "audio_decoder.mp2", "audio_encoder.mp2" },
  17. { MEDIA_MIMETYPE_AUDIO_AMR_NB,
  18. "audio_decoder.amrnb", "audio_encoder.amrnb" },
  19. { MEDIA_MIMETYPE_AUDIO_AMR_WB,
  20. "audio_decoder.amrwb", "audio_encoder.amrwb" },
  21. { MEDIA_MIMETYPE_AUDIO_AAC,
  22. "audio_decoder.aac", "audio_encoder.aac" },
  23. { MEDIA_MIMETYPE_AUDIO_VORBIS,
  24. "audio_decoder.vorbis", "audio_encoder.vorbis" },
  25. { MEDIA_MIMETYPE_AUDIO_G711_MLAW,
  26. "audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },
  27. { MEDIA_MIMETYPE_AUDIO_G711_ALAW,
  28. "audio_decoder.g711alaw", "audio_encoder.g711alaw" },
  29. { MEDIA_MIMETYPE_VIDEO_AVC,
  30. "video_decoder.avc", "video_encoder.avc" },
  31. { MEDIA_MIMETYPE_VIDEO_MPEG4,
  32. "video_decoder.mpeg4", "video_encoder.mpeg4" },
  33. { MEDIA_MIMETYPE_VIDEO_H263,
  34. "video_decoder.h263", "video_encoder.h263" },
  35. { MEDIA_MIMETYPE_VIDEO_VPX,
  36. "video_decoder.vpx", "video_encoder.vpx" },
  37. { MEDIA_MIMETYPE_AUDIO_RAW,
  38. "audio_decoder.raw", "audio_encoder.raw" },
  39. { MEDIA_MIMETYPE_AUDIO_FLAC,
  40. "audio_decoder.flac", "audio_encoder.flac" },
  41. };
  42. static const size_t kNumMimeToRole =
  43. sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
  44. size_t i;
  45. for (i = 0; i < kNumMimeToRole; ++i) {
  46. if (!strcasecmp(mime, kMimeToRole[i].mime)) {
  47. break;
  48. }
  49. }
  50. if (i == kNumMimeToRole) {
  51. return;
  52. }
  53. const char *role =
  54. isEncoder ? kMimeToRole[i].encoderRole
  55. : kMimeToRole[i].decoderRole;
  56. if (role != NULL) {
  57. OMX_PARAM_COMPONENTROLETYPE roleParams;
  58. InitOMXParams(&roleParams);
  59. strncpy((char *)roleParams.cRole,
  60. role, OMX_MAX_STRINGNAME_SIZE - 1);
  61. roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
  62. status_t err = omx->setParameter(
  63. node, OMX_IndexParamStandardComponentRole,
  64. &roleParams, sizeof(roleParams));
  65. if (err != OK) {
  66. ALOGW("Failed to set standard component role '%s'.", role);
  67. }
  68. }
  69. }


这里是解码器因此role == audio_decoder.mp3

这里主要执行了如下步骤:

InitOMXParams(&roleParams);

status_t err = omx->setParameter(
node, OMX_IndexParamStandardComponentRole,
&roleParams, sizeof(roleParams));

其中InitOMXParams主要是初始化roleParams变量

主要是靠setParameter来完成工作:记录下传递进来的参数:OMX_IndexParamStandardComponentRole 是具体命令 roleParams 是具体参数,而node 则是与service的桥梁

具体调用的是OMX的方法(service端的,不了解可参考第一部分mClient->connect的介绍)

[html] view plain copy print ?
  1. status_t OMX::setParameter(
  2. node_id node, OMX_INDEXTYPE index,
  3. const void *params, size_t size) {
  4. return findInstance(node)->setParameter(
  5. index, params, size);
  6. }

首先是通过nodeID得到了OMXNodeInstance(这里OMXNodeInstance是封装了observer的实例)

继续进入instance

[html] view plain copy print ?
  1. status_t OMXNodeInstance::setParameter(
  2. OMX_INDEXTYPE index, const void *params, size_t size) {
  3. Mutex::Autolock autoLock(mLock);
  4. OMX_ERRORTYPE err = OMX_SetParameter(
  5. mHandle, index, const_cast<void *>(params));
  6. return StatusFromOMXError(err);
  7. }

看下 OMX_SetParameter实现

[html] view plain copy print ?
  1. #define OMX_SetParameter( \
  2. hComponent, \
  3. nParamIndex, \
  4. pComponentParameterStructure) \
  5. ((OMX_COMPONENTTYPE*)hComponent)->SetParameter( \
  6. hComponent, \
  7. nParamIndex, \
  8. pComponentParameterStructure) /* Macro End */

这里可以看到主要是通过OMX_COMPONENTTYPE对象(也就是SoftMP3父类构造函数初始化过的对象)来完成工作

这里在SoftOMXComponent没有具体实现,在SimpleSoftOMXComponent中,如下

[html] view plain copy print ?
  1. OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter(
  2. OMX_INDEXTYPE index, const OMX_PTR params) {
  3. Mutex::Autolock autoLock(mLock);
  4. CHECK(isSetParameterAllowed(index, params));
  5. return internalSetParameter(index, params);
  6. }
[html] view plain copy print ?
  1. OMX_ERRORTYPE SoftMP3::internalSetParameter(
  2. OMX_INDEXTYPE index, const OMX_PTR params) {
  3. switch (index) {
  4. case OMX_IndexParamStandardComponentRole:
  5. {
  6. const OMX_PARAM_COMPONENTROLETYPE *roleParams =
  7. (const OMX_PARAM_COMPONENTROLETYPE *)params;
  8. if (strncmp((const char *)roleParams->cRole,
  9. "audio_decoder.mp3",
  10. OMX_MAX_STRINGNAME_SIZE - 1)) {
  11. return OMX_ErrorUndefined;
  12. }
  13. return OMX_ErrorNone;
  14. }
  15. default:
  16. return SimpleSoftOMXComponent::internalSetParameter(index, params);
  17. }
  18. }

这里需要注意的是调用的internalSetParameter是SoftMP3的实现,而不是SimpleSoftOMXComponent 中的,代码如下

传入的命令为:OMX_IndexParamStandardComponentRole,处理完毕后返回OMX_ErrorNone

这里通过OMXCodec变量借由OMXNodeInstance得到OMX_COMPONENTYPE句柄,就获得了与解码器实际通信的能力。

4 解码过程介绍

下面介绍如何通过OMXCodec驱动解码一帧数据

这里建立了OMXCodec实例之后,在awesomeplayer中的audioplayer的fillbuffer中

mAudioPlayer便通过mSource->read(&mInputBuffer, &options来读取pcm数据

这里mSource为mAudioSource

看下read函数

具体代码在OMXCodec.cpp中,我们分段来看

[html] view plain copy print ?
  1. status_t OMXCodec::read(
  2. MediaBuffer **buffer, const ReadOptions *options) {
  3. status_t err = OK;
  4. *buffer = NULL;
  5. Mutex::Autolock autoLock(mLock);
  6. if (mState != EXECUTING && mState != RECONFIGURING) {
  7. return UNKNOWN_ERROR;
  8. }

前面设置好参数后,会经过几次回调将状态设置成EXECUTING

这里需要注意的是mInitialBufferSubmit默认是true

[html] view plain copy print ?
  1. if (mInitialBufferSubmit) {
  2. mInitialBufferSubmit = false;
  3. drainInputBuffers();
  4. fillOutputBuffers();
  5. }

drainInputBuffers可以认为从extractor读取一包数据

fillOutputBuffers是解码一包数据并放在输出buffer中

忽略seek代码

[html] view plain copy print ?
  1. size_t index = *mFilledBuffers.begin(); mFilledBuffers.erase(mFilledBuffers.begin());
  2. BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
  3. CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
  4. info->mStatus = OWNED_BY_CLIENT;
  5. info->mMediaBuffer->add_ref();
  6. if (mSkipCutBuffer != NULL) {
  7. mSkipCutBuffer->submit(info->mMediaBuffer);
  8. }
  9. *buffer = info->mMediaBuffer;
  10. return OK;
  11. }

这里我们将输出缓冲区中的bufferinfo取出来,并将其中的mediabuffer赋值给传递进来的参数buffer,当decoder解码出来数据后会将存放数据的buffer放在mFilledBuffers中,因此audioplayer每次从omxcodec读取数据时,会从mFilledBuffers中取。区别在于,当mFilledBuffers为空时会等待解码器解码并填充数据,如果有数据,则直接取走数据。

在audioplayer->start代码中用到这里返回的mediabuffer做了一些事情,后面设置了一些参数如

info->mStatus = OWNED_BY_CLIENT;

说明此info归client所有,client释放后会归还的,这里多啰嗦一句,通过设置mStatus可以让这一块内存由不同的模块来支配,如其角色有如下几个:

enum BufferStatus {

OWNED_BY_US,

OWNED_BY_COMPONENT,

OWNED_BY_NATIVE_WINDOW,

OWNED_BY_CLIENT,

};

显然component是解码器的,client是外部比如audioplayer的。

info->mMediaBuffer->add_ref();是增加一个引用,估计release的时候用~~

下面着重分析下如何从extractor读数据,和如何解码数据

4.1 看下 drainInputBuffers();实现

[html] view plain copy print ?
  1. <PRE class=html name="code">void OMXCodec::drainInputBuffers() {
  2. for (size_t i = 0; i < buffers->size(); ++i) {
  3. BufferInfo *info = &buffers->editItemAt(i);
  4. if (info->mStatus != OWNED_BY_US) {
  5. continue;
  6. }
  7. if (!drainInputBuffer(info)) {
  8. break;
  9. }
  10. if (mFlags & kOnlySubmitOneInputBufferAtOneTime) {
  11. break;
  12. }
  13. }
  14. }</PRE>

你可能感兴趣的:(【stagefrightplayer】4 OMX Codec介绍)