首页 > 代码库 > Android MediaPlayer 框架UML图

Android MediaPlayer 框架UML图

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。