[gitbook] Android框架分析系列之Android stagefright框架

Posted _ArcticOcean

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[gitbook] Android框架分析系列之Android stagefright框架相关的知识,希望对你有一定的参考价值。

请支持作者原创:

https://mr-cao.gitbooks.io/Android/content 点击打开链接


Table of Contents

本文以android6.0系统源码为基础,分析Android stagefright的框架。

1. MP3文件播放过程

我觉得弄清楚一个播放框架是如何运转的,应该以播放某个具体的文件为例子,沿着函数调用的流程一步步的深入下去,如果能够大致弄明白在播放文件过程中诸如文件是如何解析,数据buffer的分配与传递,以及消息机制的处理这些知识点,可以算是入门了。因为一个音视频播放框架代码其实还是蛮庞大的,如果陷入每一个细节的深究,可能不能把握整体的脉络。这篇文章主要以MP3文件和MP4文件为例子讲解stagefright的框架。因为音频文件的格式和播放流程相对简单,所以首先以MP3文件的处理来展开分析。

1.1. MP3文件简介

MP3文件是一种很常见的数字音频格式,属于有损编码。这里不介绍MP3文件的具体编码是如何实现的,而是关注MP3的文件规范,也就是MP3文件的数据是如何组织的。

一个MP3文件由一个个独立的frame组成。每一个frame又包括frame header和真正的音频编码数据。在MP3文件的开头或者结尾可能存在ID3标准的多媒体信息,这些信息包括歌曲的Title,Artist,Album等。

下面图简单的描述了MP3文件的格式: 

其中符号A的长度是11,代表着FrameSync,A全部置为1.符号B的长度为2位,代表着文件类型:

  • 00: MPEG Version2.5

  • 01: 保留

  • 10: MPEG Version2

  • 11: MPEG Version 1

符号C长度为2,描述着了MPEG文件层次:

  • 00: 保留

  • 01: Layer III

  • 10: Layer II

  • 11: Layrr I

其它位的描述可以参考:MPEG Audio Layer I/II/III frame header

检测一个文件是否是MP3文件,就是通过检测文件中是否有满足MP3规范的Frame存在。播放一个MP3文件,基本存在以下几个步骤:

  • 分辨文件是否是MP3文件

  • 从MP3文件的Frame中提取出Audio数据,交给解码器进行解码

  • 解码器所得的pcm数据进行播放

1.2. stagefright框架

这里以MediaPlayer的实现为入口探究stagefright的框架。在java层,Android提供了MediaPlayer类用于播放音视频。这个java类通过调用jni的方法,访问native的MediaPlayer类的功能。可以说java层的MediaPlayer类只是native层的MediaPlayer的一个包装。

那么native层的MediaPlayer与stagefrgiht又有什么关系呢?可以参考下图: 

从上图中可以看出,MediaPlayer使用binder通信协议访问MediaPlayerService,每个MediaPlayer在meida_server进程中都对应一个Client。MediaPlayer持有Client的代理端,通过IMediaPlayer接口类跨进程调用Client的服务。同时MediaPlayer也实现了binder接口,Client类内部也持有其代理类:IMediaPlayerClient。Client内部的状态以及各种消息都是通过IMediaPlayerClient的virtual void notify(int msg,int ext1,int ext2,const Parcel *obj) =0接口通知给MediaPlayer。MediaPlayer进而通过jni将消息传递到java层的MediaPlayer对象。

每个Client内部都持有一个MediaPlayerBase的智能指针。MediaPlayer的start,stop等函数的实现,实际上是转交给MediaPlayerBase去实现。用户可以扩展多种MediaPlayerBase,根据需求不同使用不同的MediaPlayerBase来播放文件。MediaPlayerFactory就是用来管理多种player,它的实现使用了抽象工厂模式。IFactory是一个抽象的工厂类,用户负责实现。它的主要功能是探测文件,以及生产对应的MediaPlayerBase产品。

Android内部注册的IFactory有:

void MediaPlayerFactory::registerBuiltinFactories() 
    Mutex::Autolock lock_(&sLock);
    if (sInitComplete)
        return;

    registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER);
    registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
    registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);

    sInitComplete = true;

每个MeidaPlayerBase都有其对应的player_type,源码中的定义在MediaPlayerInterface.h中:

enum player_type 
    STAGEFRIGHT_PLAYER = 3,
    NU_PLAYER = 4,
    // Test players are available only in the 'test' and 'eng' builds.
    // The shared library with the test player is passed passed as an
    // argument to the 'test:' url in the setDataSource call.
    TEST_PLAYER = 5,
;

在播放一个文件的时候,首先要为这个文件找到匹配的MediaPlayerBase。在MediaPlayer调用setDataSource过程中,Client会创建一个合适的MediaPlayerBase,并调用其setDataSource:

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)


    struct stat sb;
    int ret = fstat(fd, &sb);
    if (ret != 0) 
        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
        return UNKNOWN_ERROR;
    
    if (offset >= sb.st_size) 
        ALOGE("offset error");
        ::close(fd);
        return UNKNOWN_ERROR;
    
    if (offset + length > sb.st_size) 
        length = sb.st_size - offset;
        ALOGV("calculated length = %lld", length);
    
    player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                    fd,
                                    offset,
                                    length);
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
    if (p == NULL) 
        return NO_INIT;
    
    // now set data source
    setDataSource_post(p, p->setDataSource(fd, offset, length));
    return mStatus;


sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)

    ALOGV("player type = %d", playerType);

    // create the right type of player
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) 
        return p;
    

    if (!p->hardwareOutput()) 
        Mutex::Autolock l(mLock);
        mAudioOutput = new AudioOutput(mAudiosessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    

    return p;

上图中首先是获得文件对应的player_type,然后创建player_type对应的MediaPlayerBase,最后调用MediaPlayerBase的setDataSource。

这里最关键的是,如何根据文件来获得其player_type。MediaPlayerFactory的getPlayerType方法会调用注册的IFactory的scoreFactory方法,这个方法会返回一个得分,得分高的IFactory的player_type被返回。如果系统中有多种MeidaPlayerBase,那么具体的IFactory在实现scoreFactory的时候,需要读取文件的内容,判断文件的格式,看这个格式自己是否支持播放,如果支持那么就可以返回1(最高得分),或者是返回0(最低得分)。

在源码的实现中,StagefrightFactory的scoreFactory直接返回了1,被当做默认的IFactory。理所当然的,系统默认的player就是StagefrightPlayer了。

那StagefrightPlayer的实现机制又是如何的呢?下面会通过几个图来简单的讲解下 

通过上图可以发现,StagefrightPlayer内部有指针指向一个AwesomePlayer对象,StagefrightPlayer内部的操作基本转手又交给AwesomePlayer去完成了;而AwesomePlaer也有指针指向StageFrightPlayer,这个指针的作用是用于播放过程中的消息传递,通过callback机制,将AwesomePlayer内部的状态传递给MediaPlayerBase(StagefrightPlayer)。播放MP3文件中,很重要的一环就是解码压缩的音频数据,这个是由解码组件完成。在stagefright框架中,组件的实现使用了OpenMax标准。Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent。OMXPluginBase扮演者组件的管理者。它负责加载组件库,创建组件实例。而OMXMaster则管理着OMXPluginBase,Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。对于厂商来说,如果要实现自己的组件管理模块,需要实现OMXPluginBase,并将之编译为libstagefrighthw.so。在OMXMaster中会加载这个库文件,然后调用其createOMXPlugin方法获得一个OMXPluginBase指针,后续都会通过OMXPluginBase来管理组件。

OMXPluginBase的定义如下:

struct OMXPluginBase 
    OMXPluginBase() 
    virtual ~OMXPluginBase() 

    virtual OMX_ERRORTYPE makeComponentInstance(
            const char *name,
            const OMX_CALLBACKTYPE *callbacks,
            OMX_PTR appData,
            OMX_COMPONENTTYPE **component) = 0;

    virtual OMX_ERRORTYPE destroyComponentInstance(
            OMX_COMPONENTTYPE *component) = 0;

    virtual OMX_ERRORTYPE enumerateComponents(
            OMX_STRING name,
            size_t size,
            OMX_U32 index) = 0;

    virtual OMX_ERRORTYPE getRolesOfComponent(
            const char *name,
            Vector<String8> *roles) = 0;

private:
    OMXPluginBase(const OMXPluginBase &);
    OMXPluginBase &operator=(const OMXPluginBase &);
;

OMXPluginBase负责:

  • 创建组件实例OMX_COMPONENTTYPE

  • 销毁组件实例

  • 枚举组件名

  • 获得组件对应的role(这个该怎么翻译?角色?表示组件的作用)

所以对组件的管理可以这么总结: 通过OMXMaster加载libstagefrighthw.so库文件,创建OMXPluginBase,通过这个类来管理组件。

对于AwesomePlayer来说,它并不直接接触解码组件,而是通过创建OMXCodec来和组件交互。OMXCode内部有一个id,这个id对应于一个OMXNodeInstance。OMX对象中会对产生的每一个OMXNodeInstance分配一个唯一的node_id。每一个OMXNodeInstance内部又保存着组件实例的指针,通过这个指针就可以和组件进行交互了。交互的流程为:OMXCodec → OMX → OMXNodeInstance → OMX_COMPONENTTYPE。而组件的消息则通过OMX_CALLBACKTYPE传递到OMX类,然后也OMX分发给对应OMXNodeInstance。OMXNodeInstance在把消息转发给OMXCodec。

总结下,AwesomePlayer通过OMXCodec建立起和OMX组件的联系。OMXNodeInstance中保存着组件的实例指针,通过OM_Core.h中的宏来操作组件。比如OMX_SendCommand宏的实现如下:

#define OMX_SendCommand(                                    \\
         hComponent,                                        \\
         Cmd,                                               \\
         nParam,                                            \\
         pCmdData)                                          \\
     ((OMX_COMPONENTTYPE*)hComponent)->SendCommand(         \\
         hComponent,                                        \\
         Cmd,                                               \\
         nParam,                                            \\
         pCmdData)

这第一个参数就是OMXInstance中的mHandle变量。

在OMXPluginBase创建组件实例的时候,需要传递一个callback给组件:

typedef struct OMX_CALLBACKTYPE

   OMX_ERRORTYPE (*EventHandler)(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData);

    OMX_ERRORTYPE (*EmptyBufferDone)(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);

    OMX_ERRORTYPE (*FillBufferDone)(
        OMX_OUT OMX_HANDLETYPE hComponent,
        OMX_OUT OMX_PTR pAppData,
        OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);

 OMX_CALLBACKTYPE;

这个callback用于接收组件的消息,它的实现是在OMXNodeInstance.cpp中。

// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = 
 &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
;

kcallbacks是OMXNodeInstance的静态成员变量,它内部的三个函数指针分别指向了OMXNodeInstance的三个静态方法。

下面是一张组件图,大致描述了AwesomePlayer和libstagefrighthw的关系: 

AwesomePlayer会为每一个播放的文件,常见一个MediaExtractor,这个类的作用就是做解析用,概念上等同于demuxer或者是Parser。MediaExtractor负责从文件中分离音视频数据,并抽象为MediaSource。MediaSource生产数据,送往OMXCodec。OMXCodec又将数据通过OpenMax的接口送往组件。组件解码之后的数据会返回给OMXCodec,之后由OMXCodec送往播放模块。所以OMXCodec起到一个桥梁作用。

下面就是的类图展示了MP3播放过程中几个关键类的关系: 

在上图中,MP3Source负责为OMXCodec从原始文件中读取数据,OMXCodec负责解码,将解码之后的数据松给AudioPlayer,对于不支持hardware output属性的平台,AudioPlayer实际上是调用AudioTrack去播放这些数据。

1.3. parser步骤

对一个MP3文件而言,parser的作用就是

  • 提取metadata数据,比如artist,album等;

  • 提供文件中的音频压缩数据给解码器

以上两点在Android中是如何实现的呢?

在AwesomePlayer的setDataSource方法中,首先是根据文件句柄创建了一个FileSource对象,这个对象负责读取文件,并提取文件的mime type(文件类型)。FileSource的基类是DataSource,在DataSource中,注册了一些探测器函数,这些函数用来探测文件的类型。

void DataSource::RegisterDefaultSniffers() 
    Mutex::Autolock autoLock(gSnifferMutex);
    if (gSniffersRegistered) 
        return;
    

    RegisterSniffer_l(SniffMPEG4);
    RegisterSniffer_l(SniffMatroska);
    RegisterSniffer_l(SniffOgg);
    RegisterSniffer_l(SniffWAV);
    RegisterSniffer_l(SniffFLAC);
    RegisterSniffer_l(SniffAMR);
    RegisterSniffer_l(SniffMPEG2TS);
    RegisterSniffer_l(SniffMP3);
    RegisterSniffer_l(SniffAAC);
    RegisterSniffer_l(SniffMPEG2PS);
    RegisterSniffer_l(SniffWVM);
    RegisterSniffer_l(SniffMidi);

    char value[PROPERTY_VALUE_MAX];
    if (property_get("drm.service.enabled", value, NULL)
            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) 
        RegisterSniffer_l(SniffDRM);
    
    gSniffersRegistered = true;

在AwesomePlayer的构造函数中就会调用DataSource::RegisterDefaultSniffers();来注册这些探测函数。

在Android中,FileSource只是负责对文件类型进行探测,以及负责读取文件,真正对文件数据进行分析的是MediaExtractor类。MediaExtractor也是个基类,不同的子类对应不同的文件类型。MediaExtractor创建的过程是,首先调用DataSource的探测函数获得文件类型,对于MP3文件来说,其文件类型为“audio/mpeg”。然后根据这个文件类型创建MP3Extractor对象。

MP3Extractor就充当着parser的作用,它负责从FileSource中读取数据,解析出文件的metadata,并且为音视频流创建一个抽象类:MediaSource。对于MP3而言就是其子类MP3Source。

sp<MediaSource> MP3Extractor::getTrack(size_t index) 
    if (mInitCheck != OK || index != 0) 
        return NULL;
    

    return new MP3Source(
            mMeta, mDataSource, mFirstFramePos, mFixedHeader,
            mSeeker);

MP3Source负责从文件中定位帧数据的位置,并读取帧数据。

1.4. 解码器组件的加载

通过parser部分,已经从mp3文件中提取了metadata数据,以及定位了帧数据。下一步就是创建解码器来负责解码帧数据。

解码器的创建是在prepare步骤。之前已经讲过,对于AwesomePlayer来说,创建解码器就是创建一个OMXCodec对象。

OMXCodec的静态方法create,负责创建对应的OMXCodec,值得注意的是OMXCodec的基类也是MediaSource。

创建OMXCodec的步骤如下:

  • 根据文件的mime type,找到匹配的组件;

  • 调用omx的allocateNode方法,创建一个组件实例,并返回组件实例的id号;

  • 以组件名,mine type字符串和组件实例的id号码为参数创建OMXCodec

1.4.1. 根据mime type 匹配组件

在Android系统中,文件/etc/media_codecs.xml描述了系统支持的codec的情况。 以moto shamu设备的此文件为例子,其内容如下:

<MediaCodecs>
    <Include href="media_codecs_google_audio.xml" />
    <Include href="media_codecs_google_telephony.xml" />
    <Settings>
        <Setting name="max-video-encoder-input-buffers" value="9" />
    </Settings>
    <Encoders>
        <MediaCodec name="OMX.qcom.video.encoder.mpeg4" type="video/mp4v-es" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports"/>
            <Quirk name="requires-loaded-to-idle-after-allocation"/>
            <Limit name="size" min="96x64" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="489600" />
            <Limit name="bitrate" range="1-60000000" />
            <Limit name="concurrent-instances" max="13" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.encoder.h263" type="video/3gpp" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports"/>
            <Quirk name="requires-loaded-to-idle-after-allocation"/>
            <Limit name="size" min="96x64" max="720x576" />
            <Limit name="alignment" value="2x2" />
            <Limit name="concurrent-instances" max="13" />
        </MediaCodec>

    …………

    </Encoders>
    <Decoders>
            <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports"/>
            <Limit name="size" min="64x64" max="4096x2160" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="972000" />
            <Limit name="bitrate" range="1-100000000" />
            <Feature name="adaptive-playback" />
            <Limit name="concurrent-instances" max="13" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.decoder.avc.secure" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports"/>
            <Limit name="size" min="64x64" max="4096x2160" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="972000" />
            <Limit name="bitrate" range="1-100000000" />
            <Feature name="adaptive-playback" />
            <Feature name="secure-playback" required="true" />
            <Limit name="concurrent-instances" max="5" />
        </MediaCodec>
    …………
    </Decoders>
    <Include href="media_codecs_google_video.xml" />
</MediaCodecs>

一份media_codecs.xml文件中可以包括以下几部分内容:

  • include其他codec的配置文件

  • encoder的配置

  • decoder的配置

MediaCodecList类负责加载解析codec配置文件。每一个MediaCodec标签对应于一个codec。以decoders为例,每一个codec都有name和type。这个type就是对应着代码中的mime type。也就是如果文件的mime type和组件的type字符串一样,那么此codec就可以解码这个文件。通过配置文件,可以很方便的获得所有支持文件mime type的组件名。

1.4.2. 分配组件实例

经过上面的步骤,找到了匹配mimetype的组件名。接着调用OMX的接口来分配一个组件实例。

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, name);

    OMX_COMPONENTTYPE *handle;
    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
            name, &OMXNodeInstance::kCallbacks,
            instance, &handle);

    if (err != OMX_ErrorNone) 
        ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);

        instance->onGetHandleFailed();

        return StatusFromOMXError(err);
    

    *node = makeNodeID(instance);
    mDispatchers.add(*node, new CallbackDispatcher(instance));

    instance->setHandle(*node, handle);

    mLiveNodes.add(IInterface::asBinder(observer), instance);
    IInterface::asBinder(observer)->linkToDeath(this);

    return OK;
  • 在OMX中以OMXNodeInstance对象代表着一个组件实例。每一个OMXNodeInstance都有一个唯一的编号

  • OMXMaster的makeComponentInstance方法负责生成OMX_COMPONENTTYPE对象,并将OMX_COMPONENTTYPE对象的指针保存在形参中返回

  • OMXNodeInstance的setHandle方法,保存了创建的OMX_COMPONENTTYPE对象的指针

// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = 
    &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
;

kCallbacks用来接收组件内部的消息。

OMXMaster的makeComponentInstance方法逻辑如下:

根据组件的名字,找到其对应的OMXPluginBase(类似于组件管理者的角色)对象。然后调用OMXPluginBase的makeComponentInstance方法获得OMX_COMPONENTTYPE对象的指针。

OMXMaster内部维护着一个map表:KeyedVector<String8, OMXPluginBase *> mPluginByComponentName; 。key为组件的名字,value为OMXPluginBase的指针。一个OMXPluginBase管理着很多的组件,所以有多个组件名对应着同一个OMXPluginBase。

那么OMXPluginBase又是从何而来呢?

OMXMaster::OMXMaster()
    : mVendorLibHandle(NULL) 
    addVendorPlugin();
    addPlugin(new SoftOMXPlugin);


void OMXMaster::addVendorPlugin() 
    addPlugin("libstagefrighthw.so");
  • 加载厂商实现的libstagefrighthw.so,然后中动态库中找到函数符号createOMXPlugin的地址,调用此函数返回一个OMXPluginBase指针

  • Android Stagefright框架提供的SoftOMXPlugin(负责管理Stagefright框架提供的软codec)

以下的代码片段,显示了OMXMaster调用OMXPluginBase的enumerateComnonets方法获得其内部管理的组件的名字。

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) 
            ALOGE("A component of name '%s' already exists, ignoring this one.",
                 name8.string());

            continue;
        

        mPluginByComponentName.add(name8, plugin);
    

    if (err != OMX_ErrorNoMore) 
        ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu "
             "components", err, mPluginByComponentName.size());
    

看清了OMXMaster如何管理组件之后,我们在回到组件实例的创建过程中来。上面已经说道,OMXMaster根据组件名找到其管理者OMXPluginBase。接着会调用OMXPluginBase的makeComponentInstance方法,获得OMX_COMPONENTTYPE指针。

OMXPluginBase只是Android提供的抽象类。具体的实现依赖各个平台。但是我们可以以SoftOMXPlugin来看看一个OMXPluginBase应该如何实现。因为SoftOMXPlugin也是继承于OMXPluginBase。

首先,SoftOMXPlugin内部维持着一张表,描述着它支持的组件信息,包括组件名,组件所对应的动态库的前缀名,以及组件的role字符串。

static const struct 
    const char *mName;
    const char *mLibNameSuffix;
    const char *mRole;

 kComponents[] = 
     "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" ,
     "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" ,
     "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" ,
     "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" ,
     "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" ,
     "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" ,
     "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" ,
     "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" ,
     "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" ,
     "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" ,
     "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" ,
     "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" ,
     "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" ,
     "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" ,
     "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" ,
     "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" ,
     "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" ,
     "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" ,
     "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" ,
     "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" ,
     "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" ,
     "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" ,
     "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" ,
     "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" ,
     "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" ,
;

SoftOMXPlugin::makeComponentInstance的逻辑如下:

  • 首先根据组件名找到对应的库名前缀

  • 然后和字符串“libstagefright_soft_”拼接出完整的组件动态库名

  • 然后加载组件库文件,并找到符号createSoftComonent的地址

  • 调用函数createSoftComponent获得OMX_COMPONENTTYPE指针

以MP3为例子,它的mime type为audio/mpeg,在xml配置文件中它所对应的组件:

<MediaCodec name="OMX.google.mp3.decoder" type="audio/mpeg">
    <Limit name="channel-count" max="2" />
    <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
    <Limit name="bitrate" range="8000-320000" />
</MediaCodec>

以组件名OMX.google.mp3.decoder找到其对应的OMXPluginBase,即SoftOMXPlugin。然后根据OMX.google.mp3.decoder组件对应的库前缀名mp3dec,拼接出完整的库文件名:libstagefright_soft_mp3dec.so。加载这个库,获得createSoftOMXComponent的地址之后,调用这个方法:

android::SoftOMXComponent *createSoftOMXComponent(
        const char *name, const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData, OMX_COMPONENTTYPE **component) 
    return new android::SoftMP3(name, callbacks, appData, component);

SoftMP3的基类为SoftOMXComponent。其构造函数如下:

SoftOMXComponent::SoftOMXComponent(
        const char *name,
        const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData,
        OMX_COMPONENTTYPE **component)
    : mName(name),
      mCallbacks(callbacks),
      mComponent(new OMX_COMPONENTTYPE),
      mLibHandle(NULL) 
    mComponent->nSize = sizeof(*mComponent);
    mComponent->nVersion.s.nVersionMajor = 1;
    mComponent->nVersion.s.nVersionMinor = 0;
    mComponent->nVersion.s.nRevision = 0;
    mComponent->nVersion.s.nStep = 0;
    mComponent->pComponentPrivate = this;
    mComponent->pApplicationPrivate = appData;

    mComponent->GetComponentVersion = NULL;
    mComponent->SendCommand = SendCommandWrapper;
    mComponent->GetParameter = GetParameterWrapper;
    mComponent->SetParameter = SetParameterWrapper;
    mComponent->GetConfig = GetConfigWrapper;
    mComponent->SetConfig = SetConfigWrapper;
    mComponent->GetExtensionIndex = GetExtensionIndexWrapper;
    mComponent->GetState = GetStateWrapper;
    mComponent->ComponentTunnelRequest = NULL;
    mComponent->UseBuffer = UseBufferWrapper;
    mComponent->AllocateBuffer = AllocateBufferWrapper;
    mComponent

以上是关于[gitbook] Android框架分析系列之Android stagefright框架的主要内容,如果未能解决你的问题,请参考以下文章

[gitbook] Android框架分析系列之Android PackageManager

[gitbook] Android框架分析系列之Android Binder详解

[gitbook] Android框架分析系列之Android Binder详解

[gitbook] Android框架分析系列之Android traces.txt文件

Android搭建应用框架系列之BaseActivity

gitbook 入门教程之小白都能看懂的 Gitbook 插件开发全流程