请支持作者原创:

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


Table of Contents
  • 1. MP3文件播放过程
    • 1.1. MP3文件简介
    • 1.2. stagefright框架
    • 1.3. parser步骤
    • 1.4. 解码器组件的加载
    • 1.5. 解码过程

本文以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 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 MediaPlayerService::Client::setDataSource_pre(        player_type playerType){    ALOGV("player type = %d", playerType);    // create the right type of player    sp 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(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 *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中。

// staticOMX_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 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 &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对象的指针

// staticOMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {    &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone};

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

OMXMaster的makeComponentInstance方法逻辑如下:

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

OMXMaster内部维护着一个map表:KeyedVector 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->FreeBuffer = FreeBufferWrapper;    mComponent->EmptyThisBuffer = EmptyThisBufferWrapper;    mComponent->FillThisBuffer = FillThisBufferWrapper;    mComponent->SetCallbacks = NULL;    mComponent->ComponentDeInit = NULL;    mComponent->UseEGLImage = NULL;    mComponent->ComponentRoleEnum = NULL;    *component = mComponent;}

由以上代码可知,OMX_COMPONENTTYPE内部的函数指针全部都指向SoftOMXComponent.cpp中的静态函数。以SendCommand为例子:

#define OMX_SendCommand(                                    \         hComponent,                                        \         Cmd,                                               \         nParam,                                            \         pCmdData)                                          \     ((OMX_COMPONENTTYPE*)hComponent)->SendCommand(         \         hComponent,                                        \         Cmd,                                               \         nParam,                                            \         pCmdData)                          /* Macro End */// staticOMX_ERRORTYPE SoftOMXComponent::SendCommandWrapper(        OMX_HANDLETYPE component,        OMX_COMMANDTYPE cmd,        OMX_U32 param,        OMX_PTR data) {    SoftOMXComponent *me =        (SoftOMXComponent *)            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;    return me->sendCommand(cmd, param, data);}

当OMXNodeInstance调用OMX_SendCommand的时候,这个宏展开后,实际上调用的是组件实例OMX_COMPONENTTYPE的SendCommand的方法。而这个方法却是由SoftOMXComponent的静态方法SendCommandWrapper负责具体的实现。最终是调用到了SoftOMXCompoent的虚函数` virtual OMX_ERRORTYPE sendCommand( OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data); `。

至此,一个组件实例OMX_COMPONENTTYPE的创建过程算是结束了。

1.5. 解码过程

经过上面的步骤之后,负责提供MP3音频压缩数据的MP3Source创建成功了,并且解码器对象OMXCodec也创建成功了,下面要做的就是触发解码器获取压缩数据,然后进行解码。将解码之后的数据送到下一个处理单元进行播放。

1.5.1. OMXCodec初始化

OMX的初始化是在其方法OMXCodec::init()中进行,大致做了如下事情:

  • 给组件发送命令,命令组件进入idle状态。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)

  • 组件在设置状态进入idle之后,会通过EventHandler给OMXCodec发送状态设置完毕的消息。OMXCodec::on_message方法中会对这个OMX_EventCmdComplete进行处理。OMXCodec在init的过程中,状态会经过,LOADED_TO_IDLE→IDLE_TO_EXECUTING→EXECUTING的变化过程.

  • 分配buffer。分别为input port和output port端口分配buffer,同时使用OMX的标准函数OMX_UseBuffer通知组件为buffer分配header.

1.5.2. 数据buffer的处理


更多相关文章

  1. Android高手进阶教程(四)之----Android(安卓)中自定义属性(attr.
  2. 在程序中设置android:gravity 和 android:layout_Gravity属性 .
  3. H5 Web网页通过JS(JavaScript)脚本调用Android本地原生方法函数
  4. Android深入浅出之Audio 第一部分 AudioTrack分析
  5. android studio 新建项目 界面一直停在 【“building ‘ 项目名
  6. ubuntu 使用adb shell命令识别android设备
  7. android不是内部或外部命令,也不是可运行的程序或批处理文件
  8. Android中Java与web通信
  9. Android(安卓)Opencore

随机推荐

  1. Android(安卓)使用帧动画内存溢出解决方
  2. android守护进程installd分析
  3. 【Android(安卓)Developers Training】 0
  4. Android(安卓)Binder分析三:Natvie Servic
  5. Android(安卓)Service的生命周期2
  6. Android实现对HOME键的捕获和屏幕
  7. flutter高德地图使用
  8. [5] Zygote
  9. 三种形式的hello world
  10. 解决listview设置背景图片以后,拖动出现黑