[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详解