请支持作者原创:
https://mr-cao.gitbooks.io/Android/content 点击打开链接
本文以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。MediaPlayerFactor