Android MediaPlayer 框架UML图

本文用一个UML类图,讲解mp3文件播放的框架流程。内容以下几个方面:

    1.UML类图

    2.stagefrightPlayer是如何创建的;

    3.mp3文件的解析和解码的简单介绍

    4.播放mp3文件过程中,生产者和消费者的关系;

    5.openmax和stagefright框架的消息机制

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>player_type playerType = MediaPlayerFactory::getPlayerType(this, url);</span><span>sp<MediaPlayerBase>p = setDataSource_pre(playerType);</span>if (p == NULL) {            return NO_INIT;        }        setDataSource_post(p,<span>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。

http://www.shangxueba.com/jingyan/1845910.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值