Android开发交流群:50342056
目的
本文用一个UML类图,讲解mp3文件播放的框架流程。内容以下几个方面:
1.UML类图
2.stagefrightPlayer是如何创建的;
3.mp3文件的解析和解码的简单介绍4.播放mp3文件过程中,生产者和消费者的关系;
5.openmax和stagefright框架的消息机制
UML类图
stagefrightPlayer是如何创建的
对照着UML图,看下StagefrightPlayer创建的过程。
故事的开始是Java层的MediaPlayer调用了setDataSource这个函数(参数为一个path),导致native对应的MediaPlayer 通过Binder通信机制在MediaPlayerService中开辟了一个“户口”,即创建Client对象,这个对象是个匿名的Binder。这个“户口”在native MediaPlayer中表现为一个IMediaPlayer的接口。
上述过程代码如下:
路径:frameworks/av/media/libmedia/MediaPlayer.cpp:setDataSource
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(url, headers))) {
player.clear();
}
err = attachNewPlayer(player);
创建了IMediaPlayer对象之后,就调用其setDataSource方法。经过Binder的通信机制一番的转换之后,调用流程来到MediaPlayerService.cpp中:
代码路径:frameworks/av/media/libmediaplayerservice/MediaPlayerService
status_t MediaPlayerService::Client::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
if (url == NULL)
return UNKNOWN_ERROR;
if ((strncmp(url, "http://", 7) == 0) ||
(strncmp(url, "https://", 8) == 0) ||
(strncmp(url, "rtsp://", 7) == 0)) {
if (!checkPermission("android.permission.INTERNET")) {
return PERMISSION_DENIED;
}
}
if (strncmp(url, "content://", 10) == 0) {
// get a filedescriptor for the content Uri and
// pass it to the setDataSource(fd) method
String16 url16(url);
int fd = android::openContentProviderFile(url16);
if (fd < 0)
{
ALOGE("Couldn't open fd for %s", url);
return UNKNOWN_ERROR;
}
setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
close(fd);
return mStatus;
} else {
<span style="color:#FF0000;">player_type playerType = MediaPlayerFactory::getPlayerType(this, url);</span>
<span style="color:#FF0000;">sp<MediaPlayerBase> p = setDataSource_pre(playerType);</span>
if (p == NULL) {
return NO_INIT;
}
setDataSource_post(p,<span style="color:#FF0000;"> p->setDataSource(url, headers)</span>);
return mStatus;
}
}
上面大段的代码和分析流程没有关系,skim it。要创建一个合适的播放器,就要分析文件的格式,根据文件的格式来匹配一个合适的播放器。这个工作交给了MediaPlayerFactory。在其中注册了许多的工厂用于生产一个合适的播放器。基本原理就是读取歌曲文件的开头一段字节,根据相关的container来解析歌曲的格式,做的粗糙一点的话就直接根据后缀名来判断了。这其中的实现取决于厂商或者组件提供商了。
不管怎么样,我们现在假设MediaPlayerFactory根据路径得到了一个合适的播放器,它的基类是MediaPlayerBase。android中默认的是StagefrightPlayer,继承了MediaPlayerBase。于是StagefrightPlayer在工厂中被生产出来了。然后调用其setDataSource。
其实StagefrightPlayer只是一个空壳,真正的工作是AwesomePlayer去做,这个对象在StagefrightPlayer构造函数中产生。看到这里的时候一定要记得看看UML类图。看看他们两个之间的关系。于是这个path最终就保存到了AwesomePlayer内部。
mp3文件的解析和解码的简单介绍
Java层的MediaPlayer设置完路径之后,还要调用prepare。调用流程和上面设置路径一样:MediaPlayer->Client->StagefrightPlayer->AwesomePlayer,略过不表。AwesomePlayer的prepare做了两件事情。给AwesomePlayer的事件队列发送一条Event,然后等待这个事件的处理完成。
事件的处理在onPrepareAsyncEvent()函数中完成。做了以下工作:
1.创建一个原始Audio Track流。从这个流中读取压缩数据;对应于图中的mAudioTrack。
2.创建一个读取pcm数据的流。对应于图中的mAudioSource。
mAudioTrack实际指向一个Mp3Source,而mAudioSource指向一个OMXCodec。Mp3Source提供Mp3原始数据供OMXCodec解码,然后把解码完成之后的数据传递给AudioPlayer。OMXCodec的解码工作实际上是由具体的解码组件完成。一个OMXCodec对应一个Omx中的node instance,node instance操作解码组件。而node instance提供calback接受组件的消息。
图中蓝色部分表示读取原始mp3文件流程;绿色表示组件的callback 传递流程;
播放mp3文件过程中,生产者和消费者的关系
播放过程中存在两组生产者和消费者。
第一组:原始数据的生产者,Mp3Source;原始数据消费者OMXCodec;
第二组:pcm数据的提供者OMXCodec和pcm数据的消费者AudioPlayer。
其中AudioPlayer其实是一个中间桥梁,真正的pcm数据的消费者是AudioTrack。它不断的通过callback机制来从AudioPlayer中读取pcm数据。
openmax和stagefright框架的消息机制
这一部分有两条主线:
1.OMXCodec如何操作组件;
2.组件的消息如何传递到StagefrightPlayer。
首先说第一条:OMXCodec的构造函数会调用omx的接口创建一个OMXNodeInstance实例,通过OMXNodeInstance实例来操作组件;在创建OMXNodeInstance的同时会传递给OMXNodeInstance一个观察者OMXCodecObserver。一旦OMXNodeInstance接收到来自组件的消息,就会通过这个观察者把消息传递给OMXCodec。
在解码过程中如果OMXCodec发生了错误,AudioPlayer会检测到read错误,会把相关的信息通过AwesomePlayer传递到StagefrightPlayer中去。进而通知到native的MediaPlayer。最后透过jni会post给java的MediaPlayer。