Android多媒体框架:从MediaPlayer到NuPlayer

1.概述

Android上经常会使用到MediaPlayer去播放音频和视频。但是从严格意义上来说,MediaPlayer并不是播放器本身,它只是Android框架层众多媒体播放器(包括ROM厂商自定义的播放器)的“壳”。

MediaPlayer的主要操作(包括但不限于播放、暂停、快进等)都会调用到框架层的播放器,比如常见的NuPlayer播放器。

2.MediaPlayer播放视频

MediaPlayer的使用很简单,如果是想要在一个SurfaceView上播放assets下的video.mp4视频,以下简单的代码就能实现视频画面的显示:

SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        MediaPlayer player = new MediaPlayer();
        player.setDisplay(holder); //设置画面显示在哪
        try {
            player.setDataSource(getAssets().openFd("video.mp4"));//设置视频源
            player.prepare(); //准备视频数据
        } catch (IOException e) {
            e.printStackTrace();
        }
        player.start(); //开始播放
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
});


MediaPlayer的API和用法比较简单,只要熟悉掌握下面的状态图就能很方便的使用。主要涉及的状态有setDataSource、prepare、start、pause、stop、reset、release。

3.从MediaPlayer到NuPlayer

3.1 整体架构

应用里面调了MediaPlayer的方法,其实底层都会通过IPC机制调到MediaPlayerService。其实不仅是MediaPlayer,android.media包下的媒体播放接口像AudioTrack、SoundPool、MediaCodec都是会调到MediaPlayerService去做具体的编解码操作的,安卓的媒体播放是个典型的C/S架构。

其实android.media.MediaPlayer这个java类只是native层的一个代理,具体的实现都是通过jni调用到libmedia_jni.so里面的c/c++代码。比如设置音视频数据的setDataSource()方法有如下调用关系:

==> android.media.MediaPlayer
    ==> android_media_MediaPlayer.cpp
        ==> mediaplayer.cpp

  • mediaplayer.cpp
status_t MediaPlayer::setDataSource(const sp<IDataSource> &source)
{
    ALOGV("setDataSource(IDataSource)");
    status_t err = UNKNOWN_ERROR;
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(source))) {
            player.clear();
        }
        err = attachNewPlayer(player);
    }
    return err;
}


MediaPlayerService会创建一个Client返回给客户端,客户端这个Client调用到MediaPlayerService的功能了。Client是MediaPlayerService的一个内部类,它继承了BnMediaPlayerService,而BnMediaPlayer又继承了BnInterface。

3.2 NuPlayer创建

  • MediaPlayerService.cpp
status_t MediaPlayerService::Client::setDataSource(
        const sp<IDataSource> &source) {
    sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
    player_type playerType = MediaPlayerFactory::getPlayerType(this, dataSource); // 对已经注册的播放器进行打分,创建得分最高的播放器(NU_PLAYER)
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
    if (p == NULL) {
        return NO_INIT;
    }
    // now set data source
    return mStatus = setDataSource_post(p, p->setDataSource(dataSource));
}


  • MediaPlayerFactory::getPlayerType:该函数涉及Android底层媒体播放器的评分机制。通过评分,获得一个最优的播放器类型。一般情况下,函数调用返回的是NuPlayer对应的播放器类型NU_PLAYER。
  • setDataSource_pre:该函数的作用是根据前面获得的播放器类型创建播放器对象。
  • setDataSource_post:将媒体资源设置给播放器,这才是真正的setDataSource操作。

(1)setDataSource()方法中,这里先来看一下getPlayerType()获取的类型。如下,在MediaPlayerInterface.h中枚举所有的播放器类型。

  • 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,
};


目前只注册了NU_PLAYER和TEST_PLAYER两种播放器。 STAGEFRIGHT_PLAYER实际上指的是AwesomePlayer,在早期的安卓系统使用AwesomePlayer去播放本地视频,用NuPlayer去播放流媒体。后来因为某些原因所以逐渐用弃用了AwesomePlayer,统一使用NuPlayer去播放。在某些过度版本的安卓系统开发者选项里面还可以选择NuPlayer代替AwesomePlayer,到后期都不用选了,只有一个NuPlayer可以用。

用于创建两种播放器的NuPlayerFactory和TestPlayerFactory,在如下代码中注册的:

void MediaPlayerFactory::registerBuiltinFactories() {
    Mutex::Autolock lock_(&sLock);

    if (sInitComplete)
        return;

    IFactory* factory = new NuPlayerFactory();
    if (registerFactory_l(factory, NU_PLAYER) != OK)
        delete factory;
    factory = new TestPlayerFactory();
    if (registerFactory_l(factory, TEST_PLAYER) != OK)
        delete factory;

    sInitComplete = true;
}

(2)紧接着执行了setDataSource_pre()方法

  • MediaPlayerService.cpp
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
...
    sp<MediaPlayerBase> p = createPlayer(playerType); // 根据类型创建player
    if (p == NULL) {
        return p;
    }
...
}
---------------------
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    sp<MediaPlayerBase> p = mPlayer;
    ...
    p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);
    ...
    return p;
}
--------------------
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
    ALOGV(" create NuPlayer");
    return new NuPlayerDriver(pid);
}
--------------------
mPlayer(AVNuFactory::get()->createNuPlayer(pid)),
--------------------
sp<NuPlayer> AVNuFactory::createNuPlayer(pid_t pid) {
    return new NuPlayer(pid);
}


NuPlayerFactory创建出来的是NuPlayerDriver,不过NuPlayerDriver内部也是封装了NuPlayer。对应上述代码中的mPlayer,后续的start(),stop(),setDataSource()等都是通过该对象进行操作。

3.3 播放器工厂

默认情况下,只有NuPlayerFactory和TestPlayerFactory两种播放器的工厂,ROM厂商也可以根据标准进行自定义。播放器工厂的类图如下:

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值