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 plaincopy
 
  1. AwesomePlayer::AwesomePlayer()  
  2. {  
  3.     ******  
  4.     CHECK_EQ(mClient.connect(), (status_t)OK);  
  5.     ******  
  6. }  

 

看下具體實現

 

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

 

這里主要就是通過binder機制與mediaplayerservice通信來完成,具體實現看mediaplayerservice

 

[html] view plaincopy
 
  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 plaincopy
 
  1. OMX::OMX()  
  2.     : mMaster(new OMXMaster),  
  3.       mNodeCounter(0) {  
  4. }  

 

在構造函數中又調用了OMXMaster的構造函數,代碼如下

 

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

 

這里OMXMaster可以看成是解碼器的入口,通過makeComponentInstance建立解碼器的實例,之后就可以進行解碼操作了。

這里我們以軟件解碼器插件為例來看整個流程,主要是addPlugin(newSoftOMXPlugin);先看SoftOMXPlugin構造函數

 

[html] view plaincopy
 
  1. SoftOMXPlugin::SoftOMXPlugin() {  
  2. }  

 

是空的~~

再看下addPlugin代碼

 

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

 

這里傳入的plugin參數時上面SoftOMXPlugin 構造函數產生的實例

從代碼可以看出主要是將enumerateComponents枚舉出來的各種解碼器存放在成員變量mPluginByComponentName中,類型為 KeyedVector<String8, OMXPluginBase *>

看下enumerateComponents實現

 

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


這里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 plaincopy
 
  1. status_t AwesomePlayer::initAudioDecoder() {  
  2.         mAudioSource = OMXCodec::Create(  
  3.     
  4.                 mClient.interface(), mAudioTrack->getFormat(),  
  5.                 false, // createEncoder  
  6.     
  7.                 mAudioTrack);}  
  8.     
  9.            status_t err = mAudioSource->start();  
  10.     
  11.      return mAudioSource != NULL ? OK : UNKNOWN_ERROR;  
  12.     
  13. }  

 

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

代碼比較多,我們這里主要將重要的代碼列出,無關代碼省略

 

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

下面依次來看每個過程

 

2.1 findMatchingCodecs

先看下代碼

 

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


從代碼可以看到主要就是從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 plaincopy
 
  1. status_t OMX::allocateNode(  
  2.         const char *name, const sp<IOMXObserver> &observer, node_id *node) {  
  3.     Mutex::Autolock autoLock(mLock);  
  4.     
  5.     *node = 0;  
  6.     
  7.     OMXNodeInstance *instance = new OMXNodeInstance(this, observer);  
  8.     
  9.     OMX_COMPONENTTYPE *handle;  
  10.     OMX_ERRORTYPE err = mMaster->makeComponentInstance(  
  11.             name, &OMXNodeInstance::kCallbacks,  
  12.             instance, &handle);  
  13.     
  14.     if (err != OMX_ErrorNone) {  
  15.         ALOGV("FAILED to allocate omx component '%s'", name);  
  16.     
  17.         instance->onGetHandleFailed();  
  18.     
  19.         return UNKNOWN_ERROR;  
  20.     }  
  21.     
  22.     *node = makeNodeID(instance);  
  23.     mDispatchers.add(*node, new CallbackDispatcher(instance));  
  24.     
  25.     instance->setHandle(*node, handle);  
  26.     
  27.     mLiveNodes.add(observer->asBinder(), instance);  
  28.     observer->asBinder()->linkToDeath(this);  
  29.     
  30.     return OK;  
  31. }  

 

 首先構造了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 plaincopy
 
  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.     
  8.     *component = NULL;  
  9.     
  10.     ssize_t index = mPluginByComponentName.indexOfKey(String8(name));  
  11.     
  12.     if (index 0) {  
  13.         return OMX_ErrorInvalidComponentName;  
  14.     }  
  15.     
  16.     OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);  
  17.     OMX_ERRORTYPE err =  
  18.         plugin->makeComponentInstance(name, callbacks, appData, component);  
  19.     
  20.     if (err != OMX_ErrorNone) {  
  21.         return err;  
  22.     }  
  23.     
  24.     mPluginByInstance.add(*component, plugin);  
  25.     
  26.     return err;  
  27. }  
 

 

主要是調用插件的makeComponentInstance方法

這里插件是通過OMXMaster構造函數addPlugin(new SoftOMXPlugin);加載的插件,因此這里makeComponentInstance 是SoftOMXPlugin 的方法

看下具體實現

 

[html] view plaincopy
 
  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.     
  8.     for (size_t i = 0; i kNumComponents; ++i) {  
  9.         if (strcmp(name, kComponents[i].mName)) {  
  10.             continue;  
  11.         }  
  12.     
  13.         AString libName = "libstagefright_soft_";  
  14.         libName.append(kComponents[i].mLibNameSuffix);  
  15.         libName.append(".so");  
  16.     
  17.         void *libHandle = dlopen(libName.c_str(), RTLD_NOW);  
  18.     
  19.         if (libHandle == NULL) {  
  20.             ALOGE("unable to dlopen %s", libName.c_str());  
  21.     
  22.             return OMX_ErrorComponentNotFound;  
  23.         }  
  24.     
  25.         typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(  
  26.                 const char *, const OMX_CALLBACKTYPE *,  
  27.                 OMX_PTR, OMX_COMPONENTTYPE **);  
  28.     
  29.         CreateSoftOMXComponentFunc createSoftOMXComponent =  
  30.             (CreateSoftOMXComponentFunc)dlsym(  
  31.                     libHandle,  
  32.                     "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"  
  33.                     "PvPP17OMX_COMPONENTTYPE");  
  34.     
  35.         if (createSoftOMXComponent == NULL) {  
  36.             dlclose(libHandle);  
  37.             libHandle = NULL;  
  38.     
  39.             return OMX_ErrorComponentNotFound;  
  40.         }  
  41.     
  42.         sp<SoftOMXComponentcodec =  
  43.             (*createSoftOMXComponent)(name, callbacks, appData, component);  
  44.     
  45.         if (codec == NULL) {  
  46.             dlclose(libHandle);  
  47.             libHandle = NULL;  
  48.     
  49.             return OMX_ErrorInsufficientResources;  
  50.         }  
  51.     
  52.         OMX_ERRORTYPE err = codec->initCheck();  
  53.         if (err != OMX_ErrorNone) {  
  54.             dlclose(libHandle);  
  55.             libHandle = NULL;  
  56.     
  57.             return err;  
  58.         }  
  59.     
  60.         codec->incStrong(this);  
  61.         codec->setLibHandle(libHandle);  
  62.     
  63.         return OMX_ErrorNone;  
  64.     }  
  65.     
  66.     return OMX_ErrorInvalidComponentName;  
  67. }  
 

 

這里主要是通過枚舉kComponents找到對應的解碼器記錄

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

這里可以看到每個庫都是以.so的方式提供的,命名符合如下規則:libstagefright_soft_mp3dec.so

通過dlopen加載后通過dlsym找到createSoftOMXComponent方法並執行,這里每個解碼器都應該實現此函數

這里看下mp3的具體實現

 

[html] view plaincopy
 
  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 plaincopy
 
  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 plaincopy
 
  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.     
  14.     mLooper->start(  
  15.             false, // runOnCallingThread  
  16.             false, // canCallJava  
  17.             ANDROID_PRIORITY_FOREGROUND);  
  18. }  

 

這里主要是構造了Alooper 對象來處理消息,以及調用了父類也就是SoftOMXComponent的構造函數‍

關於alooper的工作原理,后面會有一篇專門的文章介紹。

 

[html] view plaincopy
 
  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.     
  18. }  

 

這里才是構造component的地方,並初始化了其中的方法,如

 mComponent->SetParameter = SetParameterWrapper;

 

[html] view plaincopy
 
  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.     
  9.     return me->setParameter(index, params);  
  10. }  

 

這里初始化了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 plaincopy
 
  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.     
  39.     setComponentRole();  
  40. }  

 

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

這里    setComponentRole();主要是設置角色(編碼器還是解碼器),后面再介紹消息傳遞時介紹。

這里還要注意的是OMXCodec繼承自MediaSource&MediaBufferObserver

因此才可以作為輸出模塊的數據源

 

 

2.4 配置解碼器

            observer->setCodec(codec);

            err = codec->configureCodec(meta);

這兩部分在我們后面介紹完消息機制之后,讀者可自行回來分析

 

上面介紹了create的操作

下面介紹mAudioSource->start 的操作

 

[html] view plaincopy
 
  1. status_t OMXCodec::start(MetaData *meta) {  
  2.     
  3.     mSource->start(params.get());  
  4.     
  5.      return init();  
  6.     
  7. }  

 

只列出了重要代碼,其中mSource->start是指啟動解碼器的數據源MediaSource,這里也就是extractor中通過getTrack拿到的mediaSource。比較簡單不說了

看下init實現 ,省略無關代碼

 

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

 

主要工作是通過allocateBuffers申請內存

 

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

 

這里分別申請輸入和輸出的buffer

這里分段來看allocateBuffersOnPort函數

 

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

 

這里開頭先通過命令OMX_IndexParamPortDefinition獲取解碼器配置的大小

然后構造MemoryDealer實例,存放buffer數量及大小信息

這里命令的傳輸過程請參考消息通訊機制模型的介紹,看完再回來理解這部分

以mp3為例,在SoftMP3的構造函數中會調用initPorts來初始化OMX_PARAM_PORTDEFINITIONTYPE對象

里面會確定buffer的大小:包括有幾個buffer,每個buffer的容量等

這里OMX_IndexParamPortDefinition主要是查詢此信息,然后就知道要申請多少內存了

 

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

 

下面是一個循環(忽略了secure等無關代碼)

主要是申請內存,並為每個內存新建一個BufferInfo變量,最終都放在mPortBuffers[index]對應的棧中

 

至此InitAudioDecoder 便執行完畢了,主要做了兩件事:建立實際的解碼器+申請buffer

 

3 消息通信機制模型的介紹


當與解碼器的聯系建立之后,后面的工作主要就是傳遞消息由解碼器處理將處理結果返回給調用者

但前面的介紹對消息模型並不清晰,這里專門介紹一下

下面就以 OMXCodec構造函數中的   setComponentRole();為例來介紹此過程

具體代碼如下:

 

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

 

 

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


這里是解碼器因此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 plaincopy
 
  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 plaincopy
 
  1. status_t OMXNodeInstance::setParameter(  
  2.         OMX_INDEXTYPE index, const void *params, size_t size) {  
  3.     Mutex::Autolock autoLock(mLock);  
  4.     
  5.     OMX_ERRORTYPE err = OMX_SetParameter(  
  6.             mHandle, index, const_cast<void *>(params));  
  7.     
  8.     return StatusFromOMXError(err);  
  9. }  

 

看下 OMX_SetParameter實現

 

[html] view plaincopy
 
  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 plaincopy
 
  1. OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter(  
  2.         OMX_INDEXTYPE index, const OMX_PTR params) {  
  3.     Mutex::Autolock autoLock(mLock);  
  4.     
  5.     CHECK(isSetParameterAllowed(index, params));  
  6.     
  7.     return internalSetParameter(index, params);  
  8. }  
[html] view plaincopy
 
  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.     
  9.             if (strncmp((const char *)roleParams->cRole,  
  10.                         "audio_decoder.mp3",  
  11.                         OMX_MAX_STRINGNAME_SIZE - 1)) {  
  12.                 return OMX_ErrorUndefined;  
  13.             }  
  14.     
  15.             return OMX_ErrorNone;  
  16.         }  
  17.     
  18.         default:  
  19.             return SimpleSoftOMXComponent::internalSetParameter(index, params);  
  20.     }  
  21. }  

這里需要注意的是調用的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 plaincopy
 
  1. status_t OMXCodec::read(  
  2.     
  3.        MediaBuffer **buffer, const ReadOptions *options) {  
  4.     
  5.  status_t err = OK;  
  6.     
  7.    *buffer = NULL;  
  8.     
  9.    Mutex::Autolock autoLock(mLock);  
  10.     
  11.    if (mState != EXECUTING && mState != RECONFIGURING) {  
  12.     
  13.        return UNKNOWN_ERROR;  
  14.     
  15. }  

 

前面設置好參數后,會經過幾次回調將狀態設置成EXECUTING

這里需要注意的是mInitialBufferSubmit默認是true

 

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

 

drainInputBuffers可以認為從extractor讀取一包數據

fillOutputBuffers是解碼一包數據並放在輸出buffer中

忽略seek代碼     

 

[html] view plaincopy
 
  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 plaincopy
 
  1. <pre name="code" class="html">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>  

 

這里解釋下,我們可能申請了多個輸入緩沖區,因此是一個循環,先檢查我們有沒有權限使用即OWNED_BY_US,這一緩沖區獲取完數據后會檢測

kOnlySubmitOneInputBufferAtOneTime即每次只允許讀一個包,否則循環都讀滿。

下面繼續跟進drainInputBuffer(info),忽略無關代碼:

 

[html] view plaincopy
 
  1. bool OMXCodec::drainInputBuffer(BufferInfo *info) {  
  2.        **********  
  3.    status_t err;  
  4.    bool signalEOS = false;  
  5.    int64_t timestampUs = 0;  
  6.    size_t offset = 0;  
  7.    int32_t n = 0;  
  8. for (;;) {  
  9.        MediaBuffer *srcBuffer;  
  10. err = mSource->read(&srcBuffer);  
  11.        size_t remainingBytes = info->mSize - offset;  
  12.       下面是判斷從extractor讀取到的數據是不是超過了總大小  
  13.       if (srcBuffer->range_length() > remainingBytes) {  
  14.            if (offset == 0) {  
  15.                srcBuffer->release();  
  16.                srcBuffer = NULL;        
  17.     
  18.       setState(ERROR);  
  19.                return false;  
  20.            }  
  21.            mLeftOverBuffer = srcBuffer;  
  22.            break;  
  23.        }                 memcpy((uint8_t *)info->mData + offset,  
  24.                        (const uint8_t *)srcBuffer->data()  
  25.                            + srcBuffer->range_offset(),  
  26.                        srcBuffer->range_length());  
  27.                  offset += srcBuffer->range_length();  
  28. if (releaseBuffer) {  
  29.            srcBuffer->release();  
  30.            srcBuffer = NULL;  
  31.        }  
  32.                  數據讀取完畢后將srcBufferrelease掉  
  33. }  
  34.    err = mOMX->emptyBuffer(  
  35.            mNode, info->mBuffer, 0, offset,  
  36.            flags, timestampUs);  
  37.    info->mStatus = OWNED_BY_COMPONENT;  
  38. }  


這里讀取完畢后將緩沖區的狀態設置成OWNED_BY_COMPONENT 解碼器就可以解碼了

 

這里可以看出來讀取數據時實現了一次拷貝~~,而不是用的同一塊緩沖區,注意下

讀取數據可以參考前面介紹的extractor的內容,比較簡單不說了。

下面看讀取數據完畢后調用mOMX->emptyBuffer都干了些啥

通過前面我們很容易的理解實際調用的是

omx::emptybufferèOMXNodeInstance::emptyBuffer,

從代碼可以看到最終調用的是

((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer()

實際代碼在SimpleSoftOMXComponent.cpp中,具體如下

 

[html] view plaincopy
 
  1. OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer(  
  2.        OMX_BUFFERHEADERTYPE *buffer) {  
  3.    sp<AMessagemsg = new AMessage(kWhatEmptyThisBuffer, mHandler->id());  
  4.    msg->setPointer("header", buffer);  
  5.    msg->post();  
  6.    return OMX_ErrorNone;  
  7. }  

 

可以看到就是發了一條命令kWhatEmptyThisBuffer

通過handler->id確定了自己發的還得自己收,處理函數如下:

 

[html] view plaincopy
 
  1. void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {  
  2.    Mutex::Autolock autoLock(mLock);  
  3.    uint32_t msgType = msg->what();  
  4.    ALOGV("msgType = %d", msgType);  
  5.    switch (msgType) {  
  6. ********  
  7.        case kWhatEmptyThisBuffer:  
  8.        case kWhatFillThisBuffer:  
  9.        {  
  10.            OMX_BUFFERHEADERTYPE *header;  
  11.            CHECK(msg->findPointer("header", (void **)&header));  
  12.            CHECK(mState == OMX_StateExecuting && mTargetState == mState);  
  13.            bool found = false;  
  14.            size_t portIndex = (kWhatEmptyThisBuffer == msgType)?                   header->nInputPortIndex: header->nOutputPortIndex;  
  15.            PortInfo *port = &mPorts.editItemAt(portIndex);  
  16.            for (size_t j = 0; j port->mBuffers.size(); ++j) {  
  17.                BufferInfo *buffer = &port->mBuffers.editItemAt(j);  
  18.     
  19.                if (buffer->mHeader == header) {  
  20.                    CHECK(!buffer->mOwnedByUs);  
  21.                    buffer->mOwnedByUs = true;  
  22.                    CHECK((msgType == kWhatEmptyThisBuffer  
  23.                            && port->mDef.eDir == OMX_DirInput)|| (port->mDef.eDir == OMX_DirOutput));  
  24.                    port->mQueue.push_back(buffer);  
  25.                    onQueueFilled(portIndex);  
  26.                found = true;  
  27.                    break;  
  28.                }  
  29.            }  
  30.            CHECK(found);  
  31.            break;  
  32.        }  
  33.        default:  
  34.            TRESPASS();  
  35.            break;  
  36.    }  
  37. }  

 


從代碼這里來看這兩個case都走同一套代碼,而且都是通過onQueueFilled來處理,這樣我們就引出了實際的處理函數,也就是onQueueFilled,

以mp3為例這里具體實現在SoftMP3中。

具體解釋看代碼中注釋

 

[html] view plaincopy
 
  1. void SoftMP3::onQueueFilled(OMX_U32 portIndex) {  
  2.     if (mSignalledError || mOutputPortSettingsChange != NONE) {  
  3.         return;  
  4.     }  
  5.     
  6.     獲取輸入輸出鏈表  
  7.     
  8.     List<BufferInfo *> &inQueue = getPortQueue(0);  
  9.     List<BufferInfo *> &outQueue = getPortQueue(1);  
  10.     
  11.     while (!inQueue.empty() && !outQueue.empty()) {  
  12.     
  13.         各自取輸入輸出緩沖區中的第一個緩沖區   
  14.         BufferInfo *inInfo = *inQueue.begin();  
  15.         OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;  
  16.     
  17.         BufferInfo *outInfo = *outQueue.begin();  
  18.         OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;          
  19.     
  20.         判斷緩沖區是不是沒有數據,若果第一個都沒有那就是沒有   
  21.     
  22.         if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {  
  23.             inQueue.erase(inQueue.begin());  
  24.             inInfo->mOwnedByUs = false;  
  25.             notifyEmptyBufferDone(inHeader);  
  26.     
  27.             if (!mIsFirst) {  
  28.                 // pad the end of the stream with 529 samples, since that many samples  
  29.                 // were trimmed off the beginning when decoding started  
  30.                 outHeader->nFilledLen =  
  31.                     kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);  
  32.     
  33.                 memset(outHeader->pBuffer, 0, outHeader->nFilledLen);  
  34.             } else {  
  35.                 // Since we never discarded frames from the start, we won't have  
  36.                 // to add any padding at the end either.  
  37.                 outHeader->nFilledLen = 0;  
  38.             }  
  39.     
  40.             outHeader->nFlags = OMX_BUFFERFLAG_EOS;  
  41.     
  42.             outQueue.erase(outQueue.begin());  
  43.             outInfo->mOwnedByUs = false;  
  44.             notifyFillBufferDone(outHeader);  
  45.             return;  
  46.         }  
  47.     
  48.     
  49.     
  50.         如果offset==0說明是第一包的開頭,需要讀取pts,請結合extractor理解  
  51.     
  52.         if (inHeader->nOffset == 0) {  
  53.             mAnchorTimeUs = inHeader->nTimeStamp;  
  54.             mNumFramesOutput = 0;  
  55.         }  
  56.     
  57.         mConfig->pInputBuffer =  
  58.             inHeader->pBuffer + inHeader->nOffset;  
  59.     
  60.         mConfig->inputBufferCurrentLength = inHeader->nFilledLen;  
  61.         mConfig->inputBufferMaxLength = 0;  
  62.         mConfig->inputBufferUsedLength = 0;  
  63.     
  64.         mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);  
  65.     
  66.         mConfig->pOutputBuffer =  
  67.             reinterpret_cast<int16_t *>(outHeader->pBuffer);  
  68.     
  69.         ERROR_CODE decoderErr;  
  70.     
  71.         上面是配置參數 下面調用自己的解碼器進行解碼  
  72.         if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))  
  73.                 != NO_DECODING_ERROR) {          
  74.            ***出錯處理*  
  75.     
  76.             這里注意如果解碼失敗,則填充0數據,也就是靜音幀  
  77.             // play silence instead.  
  78.             memset(outHeader->pBuffer,  
  79.                    0,  
  80.                    mConfig->outputFrameSize * sizeof(int16_t));  
  81.     
  82.             mConfig->inputBufferUsedLength = inHeader->nFilledLen;  
  83.         } else if (mConfig->samplingRate != mSamplingRate  
  84.                 || mConfig->num_channels != mNumChannels) {  
  85.     
  86.             這里說明參數發生了改變,即采樣率等改變了,需要重新設置輸出  
  87.             mSamplingRate = mConfig->samplingRate;  
  88.             mNumChannels = mConfig->num_channels;  
  89.     
  90.             notify(OMX_EventPortSettingsChanged, 1, 0, NULL);  
  91.             mOutputPortSettingsChange = AWAITING_DISABLED;  
  92.             return;  
  93.         }  
  94.     
  95.         if (mIsFirst) {  
  96.             mIsFirst = false;  
  97.             // The decoder delay is 529 samples, so trim that many samples off  
  98.             // the start of the first output buffer. This essentially makes this  
  99.             // decoder have zero delay, which the rest of the pipeline assumes.  
  100.             outHeader->nOffset =  
  101.                 kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);  
  102.     
  103.             outHeader->nFilledLen =  
  104.                 mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset;  
  105.         } else {  
  106.             outHeader->nOffset = 0;  
  107.             outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);  
  108.         }  
  109.     
  110.         outHeader->nTimeStamp =  
  111.             mAnchorTimeUs  
  112.                 + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate;  
  113.     
  114.         outHeader->nFlags = 0;  
  115.     
  116.         CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength);  
  117.     
  118.         inHeader->nOffset += mConfig->inputBufferUsedLength;  
  119.         inHeader->nFilledLen -= mConfig->inputBufferUsedLength;  
  120.     
  121.         mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;  
  122.     
  123.         如果輸入緩沖區數據都解碼完了,則調用notifyEmptyBufferDone  
  124.     
  125.         if (inHeader->nFilledLen == 0) {  
  126.             inInfo->mOwnedByUs = false;  
  127.             inQueue.erase(inQueue.begin());  
  128.             inInfo = NULL;  
  129.             notifyEmptyBufferDone(inHeader);  
  130.             inHeader = NULL;  
  131.         }  
  132.     
  133.         outInfo->mOwnedByUs = false;  
  134.         outQueue.erase(outQueue.begin());  
  135.         outInfo = NULL;  
  136.     
  137.         這是將解碼出來的數據告訴外部,通過調用notifyFillBufferDone  
  138.     
  139.         notifyFillBufferDone(outHeader);  
  140.         outHeader = NULL;  
  141.     }  
  142. }  

 

下面分析下,如何將輸入緩沖區釋放和將輸出緩沖區中的數據傳遞出去

A、輸入部分的清空

 

[html] view plaincopy
 
  1. void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) {  
  2.     
  3.    (*mCallbacks->EmptyBufferDone)(  
  4.     
  5.            mComponent, mComponent->pApplicationPrivate, header);  
  6.     
  7. }  

 

通知外面我們emptythisbuffer完工了,具體調用的是OMXNodeInstance中的方法,具體怎么傳進去的,大家可以自己分析下:

 

[html] view plaincopy
 
  1. OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(    
  2.        OMX_IN OMX_HANDLETYPE hComponent,    
  3.        OMX_IN OMX_PTR pAppData,    
  4.        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {    
  5.    OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);    
  6.    if (instance->mDying) {    
  7.        return OMX_ErrorNone;    
  8.    }   
  9.    return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);    
  10. }  

 

OMXNodeInstance的ownner是OMX,因此代碼為

 

[html] view plaincopy
 
  1. OMX_ERRORTYPE OMX::OnEmptyBufferDone(   
  2.     
  3.        node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {   
  4.     
  5.    ALOGV("OnEmptyBufferDone buffer=%p", pBuffer);   
  6.     
  7.    omx_message msg;   
  8.     
  9.    msg.type = omx_message::EMPTY_BUFFER_DONE;   
  10.     
  11.    msg.node = node;   
  12.     
  13.    msg.u.buffer_data.buffer = pBuffer;  
  14.     
  15.    findDispatcher(node)->post(msg);  
  16.     
  17.    return OMX_ErrorNone;   
  18.     
  19. }  

 

 其中findDispatcher定義如下

 

[html] view plaincopy
 
  1. sp<OMX::CallbackDispatcher> OMX::findDispatcher(node_id node) {  
  2.     Mutex::Autolock autoLock(mLock);  
  3.     ssize_t index = mDispatchers.indexOfKey(node);  
  4.     return index 0 ? NULL : mDispatchers.valueAt(index);  
  5. }  

 

這里mDispatcher在之前allocateNode中通過mDispatchers.add(*node, new CallbackDispatcher(instance)); 創建的

看下實際的實現可知道,CallbackDispatcher的post方法最終會調用dispatch

 

[html] view plaincopy
 
  1. void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {  
  2.     if (mOwner == NULL) {  
  3.         ALOGV("Would have dispatched a message to a node that's already gone.");  
  4.         return;  
  5.     }  
  6.     mOwner->onMessage(msg);  
  7. }  

 

而owner是OMXNodeInstance,因此消息饒了一圈還是到了OMXNodeInstance的OnMessage方法接收了

 

[html] view plaincopy
 
  1. void OMXNodeInstance::onMessage(const omx_message &msg) {  
  2.     if (msg.type == omx_message::FILL_BUFFER_DONE) {  
  3.         OMX_BUFFERHEADERTYPE *buffer =  
  4.             static_cast<OMX_BUFFERHEADERTYPE *>(  
  5.                     msg.u.extended_buffer_data.buffer);  
  6.     
  7.         BufferMeta *buffer_meta =  
  8.             static_cast<BufferMeta *>(buffer->pAppPrivate);  
  9.     
  10.         buffer_meta->CopyFromOMX(buffer);  
  11.     }  
  12.     
  13.     mObserver->onMessage(msg);  
  14. }  

 

而onMessage又將消息傳遞到 mObserver中,也就是在OMXCodec::Create中構造的OMXCodecObserver對象,其OnMessage實現如下

 

[html] view plaincopy
 
  1. virtual void onMessage(const omx_message &msg) {  
  2.     sp<OMXCodeccodec = mTarget.promote();  
  3.     
  4.     if (codec.get() != NULL) {  
  5.         Mutex::Autolock autoLock(codec->mLock);  
  6.         codec->on_message(msg);  
  7.         codec.clear();  
  8.     }  
  9. }  

 

最終還是傳遞給了OMXCodec里,具體看下:

 

[html] view plaincopy
 
  1. void OMXCodec::on_message(const omx_message &msg) {    
  2. switch (msg.type) {    
  3. ************   
  4.        case omx_message::EMPTY_BUFFER_DONE:    
  5.        {    
  6.            IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;    
  7.            Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];    
  8.            size_t i = 0;    
  9.            while (i buffers->size() && (*buffers)[i].mBuffer != buffer) {    
  10.                ++i;    
  11.            }    
  12.            BufferInfo* info = &buffers->editItemAt(i);    
  13.            info->mStatus = OWNED_BY_US;    
  14.            // Buffer could not be released until empty buffer done is called.    
  15.            if (info->mMediaBuffer != NULL) {    
  16.                info->mMediaBuffer->release();    
  17.                info->mMediaBuffer = NULL;    
  18.            }    
  19.            drainInputBuffer(&buffers->editItemAt(i));   
  20.            break;    
  21.        }    
  22. ****************    
  23. }  


這部分很繞,但搞清楚就好了,請大家仔細閱讀,此處雖然調用了info->mMediaBuffer->release();但是由於其引用始終大於0,因此不會真正的release

 

二是當release完畢后,會調用drainInputBuffer(&buffers->editItemAt(i));來填充數據

也就是說當我們啟動一次解碼播放后,會在此處循環讀取數和據解碼數據。而輸出數據在后面的filloutbuffer中。

 

B、輸出數據的清空notifyFillBufferDone(outHeader);

 

[html] view plaincopy
 
  1. void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) {  
  2.    (*mCallbacks->FillBufferDone)(  
  3.            mComponent, mComponent->pApplicationPrivate, header);  
  4. }  

 

 

[html] view plaincopy
 
  1. OMX_ERRORTYPE OMX::OnFillBufferDone(   
  2.        node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {   
  3.    ALOGV("OnFillBufferDone buffer=%p", pBuffer);   
  4.    omx_message msg;   
  5.    msg.type = omx_message::FILL_BUFFER_DONE;   
  6.    msg.node = node;   
  7.    msg.u.extended_buffer_data.buffer = pBuffer;   
  8.    msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;   
  9.    msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;   
  10.    msg.u.extended_buffer_data.flags = pBuffer->nFlags;   
  11.    msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;   
  12.    msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;   
  13.    msg.u.extended_buffer_data.data_ptr = pBuffer->pBuffer;  
  14.    findDispatcher(node)->post(msg);    
  15.    return OMX_ErrorNone;   
  16. }  

 

最終處理在OMXCodec.cpp中

 

[html] view plaincopy
 
  1. void OMXCodec::on_message(const omx_message &msg) {  
  2. {  
  3. case omx_message::FILL_BUFFER_DONE:  
  4.                info->mStatus = OWNED_BY_US;  
  5.                mFilledBuffers.push_back(i);  
  6.                mBufferFilled.signal();          
  7.            break;  
  8.        }  
  9. }  


主體就這么幾句,先將mStatus設置成OWNED_BY_US,這樣component便不能操作了,后面將這個buffer push到mFilledBuffers中。

 

 

4.2 fillOutputBuffers

 

[html] view plaincopy
 
  1. void OMXCodec::fillOutputBuffers() {  
  2.    CHECK_EQ((int)mState, (int)EXECUTING);  
  3.    Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];  
  4.    for (size_t i = 0; i buffers->size(); ++i) {  
  5.        BufferInfo *info = &buffers->editItemAt(i);  
  6.        if (info->mStatus == OWNED_BY_US) {  
  7.            fillOutputBuffer(&buffers->editItemAt(i));  
  8.        }  
  9.    }  
  10. }  

 

找到一個輸出緩沖區bufferinfo,啟動輸出

 

[html] view plaincopy
 
  1. void OMXCodec::fillOutputBuffer(BufferInfo *info) {  
  2.    **************  
  3.    status_t err = mOMX->fillBuffer(mNode, info->mBuffer);  
  4.    info->mStatus = OWNED_BY_COMPONENT;  
  5. }  

 

下面和解碼流程類似,我們依次來看:

 

[html] view plaincopy
 
  1. status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {   
  2.    Mutex::Autolock autoLock(mLock);  
  3.    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;  
  4.    header->nFilledLen = 0;   
  5.    header->nOffset = 0;   
  6.    header->nFlags = 0;   
  7.    OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header);   
  8.    return StatusFromOMXError(err);   
  9. }  

 

進行一些初始化后,調用進入了softMP3中,也就是

 

[html] view plaincopy
 
  1. OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer(   
  2.        OMX_BUFFERHEADERTYPE *buffer) {   
  3.    sp<AMessagemsg = new AMessage(kWhatFillThisBuffer, mHandler->id());   
  4.    msg->setPointer("header", buffer);  
  5.    msg->post();  
  6.    return OMX_ErrorNone;  
  7. }  

 

同理,接收程序也在本文件中:

 

[html] view plaincopy
 
  1. void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {   
  2.    Mutex::Autolock autoLock(mLock);   
  3.    uint32_t msgType = msg->what();   
  4.    ALOGV("msgType = %d", msgType);   
  5.    switch (msgType) {   
  6.        case kWhatEmptyThisBuffer:   
  7.        case kWhatFillThisBuffer:  
  8.        {   
  9.            OMX_BUFFERHEADERTYPE *header;   
  10.            CHECK(msg->findPointer("header", (void **)&header));   
  11.            CHECK(mState == OMX_StateExecuting && mTargetState == mState);  
  12.            bool found = false;   
  13.            size_t portIndex = (kWhatEmptyThisBuffer == msgType)?   
  14.                    header->nInputPortIndex: header->nOutputPortIndex;   
  15.            PortInfo *port = &mPorts.editItemAt(portIndex);  
  16.            for (size_t j = 0; j port->mBuffers.size(); ++j) {   
  17.                BufferInfo *buffer = &port->mBuffers.editItemAt(j);   
  18.                if (buffer->mHeader == header) {   
  19.                    CHECK(!buffer->mOwnedByUs);   
  20.                    buffer->mOwnedByUs = true;   
  21.                 CHECK((msgType == kWhatEmptyThisBuffer   
  22.                            && port->mDef.eDir == OMX_DirInput)   
  23.                            || (port->mDef.eDir == OMX_DirOutput));   
  24.                    port->mQueue.push_back(buffer);   
  25.                  onQueueFilled(portIndex);   
  26.                    found = true;   
  27.                    break;   
  28.                }   
  29.            }   
  30.            CHECK(found);   
  31.            break;   
  32.        }  
  33.        default:   
  34.            TRESPASS();   
  35.            break;   
  36.    }   
  37.     
  38. }  


也會調用void SoftMP3::onQueueFilled執行一次解碼操作,然后再通過

 

notifyEmptyBufferDone(inHeader);

notifyFillBufferDone(outHeader);

兩個函數來推進播放進度。

 

【結束】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM