[gitbook] Android框架分析系列之Android stagefright框架

请支持作者原创:

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值