http://blog.csdn.net/lskshz/article/details/17264113

原址:http://blog.csdn.net/myzhzygh/article/details/7429687

1 Android多媒体框架结构

Android 多媒体系统纵向跨越了Android系统的所有4个层次: Java应用程序层、Java框架层、本地代码层、Linux驱动层。多媒体本地代码层是多媒体系统的重点。

Android媒体播放器的模块结构如图1所示。

从上图可以看到,Android系统中多媒体框架的分层结构。

Framework层,对外提供构建与媒体相关应用程序的API接口。

Native层作为Android多媒体系统的核心,针对上图,主要由MediaPlayer、MediaPlayerService、Stagefrightplayer、AudioFlinger和Audio HAL几个部分组成。针对Native层,我们可以将其划分为两个逻辑范畴进行分析。

从媒体框架的角度来说,MediaPlayer、MediaPlayerService和Stagefrightplayer三个部分构成了Android多媒体的基本框架。多媒体框架部分采用了C/S的结构,MediaPlayer作为C/S结构的Client端,MediaPlayerService和Stagefrightplayer作为C/S结构Server端,承担着播放多媒体文件的责任,通过Stagefrightplayer,Server端完成Client端的请求并作出响应。

从Audio的角度来说,MediaPlayer、MediaPlayerService、Stagefrightplayer、AudioFlinger和Audio HAL又构成了Android系统中Audio系统的框架。MediaPlayer、MediaPlayerService和Stagefrightplayer作为Audio系统框架的Client端,最终由StagefrightPlayer将Audio数据交给Server端的AudioFlinger处理,由AudioFlinger将最终的Audio数据交由Audio HAL层输出到硬件设备上,完成Audio的播放。

从纵向的角度来看,上层的应用程序将媒体的URI作为输入,交给Java Framework层的MediaPlayer,经过JNI将请求交给本地框架层的MediaPlayer,可以认为Java Framework层的MediaPlayer模块就是本地MediaPlayer在Java层的代理,然后由本地框架层的MediaPlayer向MediaPlayerService发出请求,MediaPlayerService将接收到的请求交给Stagefrightplyaer组件进行处理。Stagefrightplayer将处理的结果反馈给MediaPlayerService,最后由MediaPlayerService将处理后的Audio数据交给AudioFlinger处理,最终AudioFlinger将处理的数据通过Audio HAL层交给硬件驱动层,完成Audio的播放。

Stagefright 是Android多媒体本地实现的核心。Stagefright中包括的内容很多,单从播放的角度来看StagefrightPlayer输入的是文件或网络媒体流,输出的是送往音视频输出设备播放的数据流,基本功能包括了媒体流控制、文件解析、音视频文件解码等方面。

2多媒体系统音频数据播放的流程

  从多媒体系统具体实现的角度来看,音频数据播放主要经过音频格式文件解析、音频编码流解码、PCM 输出播放3个阶段。音频播放器的基本结构如图2所示。

基于Android多媒体系统音频播放流程,对音频文件的播放主要有以下4个流程:

  (1)音频文件的识别;

  (2)音频文件的解析;

  (3)编码数据的读取;

  (4)编码数据的解码和输出。

2.1 MediaFramework层状态机

一个Android媒体播放应用程序在进行媒体文件播放的过程中,有多种状态。文件播放的过程就是这些状态的转换过程。基本的状态及其转换过程如下图所示:

         

Notice: About the details of each state, please reference the contents on the following URL:

http://developer.android.com/reference/android/media/MediaPlayer.html

2.2 Media Native层框架

Native层的调用关系如下图所示,从图4中可以看到,Native层由三大部分组成,MediaPlayer、MediaPlayerService和用于完成具体播放功能的MediaPlayerBase实现部分。

Framework层的调用最终通过Native层的调用完成相应的功能。MediaPlayer和MediaPlayerService之间通过Binder进行通信。

从图4中可以看到,MediaPlayerService端的播放任务最终会交由MediaPlayerBase抽象类的子类来完成。MediaPlayerBase作为执行具体播放模块的超类,为了实现具体的播放功能派生了两个子类,一个叫MediaPlayerHWInterface,用于提供直接通过硬件输出的接口。另一个子类叫MediaPlayerInterface,StagefrightPlayer类作为MediaPlayerInterface的一个实现类。从逻辑上StagefrightPlayer担当了执行具体播放功能的责任。

2.3 Media Stagefright框架

StageFright是一个framework,在这里也可以理解为是一个container,对上它提供了调用接口,对下它负责创建并管理那些实现具体功能的Palyer组件。

针对Audio播放,如图5所示,图中展现了Audio在Stagefright框架中的基本结构。通过这个基本的结构,Audio最终与Audio Server端AudioFlinger建立连接。

在具体的Stagefright框架实现中,StagefrightPlayer并没有实现具体的方法,仍然作为Player的一个抽象层存在,具体的功能由其Wrapper类AwesomePlayer来完成的,所以Native层的具体动作是通过AwesomePlayer类来完成。

从图中看到,Audio数据经过AwesomePlayer处理之后,具体应该是Decoder之后,交给AudioPlayer,在AudioSink存在的情况下,AudioSink作为MediaPlayerService的组成部分,负责完成Audio的输出。AudioPlayer会通过AudioSink的实现类AudioOutput完成数据的输出。而在具体实现中AudioOutput会创建用于声音播放的AudioTrack,最终通过AudioTrack与AudioFlinger建立连接,实现声音的输出。

2.4 Native层Audio播放流程

以播放本地媒体文件为例,在媒体播放的数据源设置阶段,对应Framework层的状态图,也就是IdleàInitialized的Initial阶段,如图6所示,在这个过程中需要进行setDataSource,Framework层的调用在AwesomePlayer中则会看到相应的处理,最终会通过调用AwesomePlayer对象的setDataSource方法来设置Native层的数据源;AwesomePlayer通过调用Stagefright包中的MediaExtractor类(提取器类的超类)的Create方法,通过识别数据源的格式,在获得数据源对应的数据格式之后,创建该格式对应的提取器实例<XXX>Extractor,例如,如果数据源格式是AAC格式,那么就创建一个AACExtractor的实例,AACExtractor类继承于MediaExtractor。在Create方法创建并返回一个<XXX>Extractor的实例之后,AwesomePlayer会通过该实例提供的接口方法解析数据源,获得数据源的元数据(metadata)随后通过调用<XXX>Extractor的getTrack方法获得一个<XXX>Source的实例,如果数据格式为AAC,创建的实例名可能是AACSource,然后将获得的实例进行保存。在这个阶段,AwesomePlayer通过MediaExtractor提供的接口完成了对数据源中Audio和Video的Split(这时只是从逻辑上建立Audio和Video,还没有真正地进行split,只是初步的分析,来创建响应的组件。),到此Media框架完成了整个数据源的设置过程;

StageFright创建了AwesomePlayer实例,AwesomePlayer通过MediaExtractor创建了真正的对应于具体文件格式的extractor,通过此具体的extractor创建了对应的读取数据类,即<xxx>source类的实例。

setDataSource的动作到此结束,它的动作是完成了对文件的初步解析,确定了文件格式,最终的目的是为了创建相应的<xxx>extractor以及<xxx>source。此时播放还没有开始。

值得注意的是:针对数据源的设置,Android媒体框架提供了多种方式,本地文件只是其中的一种形式方式,还有其它URL或者网络媒体等形式。不同数据源形式处理的过程也不一样。有同步的处理方式,也有异步的处理方式。

在完成了数据源的设置之后,到达Initialized状态,下一个状态就是对应Framework层的Prepared状态。InitializedàPrepared阶段有两种Prepare方式,一种是同步prepare,另一种是异步Prepare。这个阶段无论是同步还是异步,都需要初始化Audio和Video的Decoder组件。如果需要播放的媒体数据源(Audio和Video)是经过数据编码压缩过的,AwesomePlayer都会通过OMXCodec创建针对该压缩编码格式的OMXCodec对象,OMXCodec类继承于MediaSource类,通过OMXCodec对象我们可以为AudioPlayer提供原始的Audio数据。

在通过OMXCodec创建原始Audio数据的过程中,我们必须通过OMXClient得到OMX的接口,通过这个接口,我们才能够创建具体数据格式的OMXCodec对象。

在完成Prepare动作之后,到达Prepared状态,下一个状态就是对应Framework层的Started状态。上层调用start方法之后,在AwesomePlayer层会调用play方法,针对Audio,AwesomePlayer层Play阶段会创建一个AudioPlayer类的对象mAudioPlayer,并调用该对象的setSource方法,把之前OMXCodec::Create返回的mAudioSource作为参数,设置该对象的成员变量mSource;随后调用该对象的start方法;开始Audio的播放(播放还是解码?这里的播放时逻辑上的概念,实际是一边播放,一边解码,异步过程)。

这里AudioPlayer就是对一个audio播放设备的模拟,通过该类完成audio数据的播放。

AudioPlayer 获取OMXCodec对象中的相关参数:文件类型、采样率、声道数,之后调用AudioSink对象的open方法创建Audio数据的AudioTrack,因为最终的Audio播放都是通过AudioTrack和AudioFlinger打交道的。(调用此方法做什么?),并把AudioPlayer类的AudioSinkCallback方法做为回调函数传给AudioSink。AudioPlayer先调用XXXDecoder解第一帧数据,并把该数据传给AudioSink去播放,当播放完成后AudioSink会调用回调函数AudioSinkCallback再去取解码后的数据,在这个函数中主要实现两个功能,一是从解码缓冲区中读取解码后的数据,二是把读到的数据拷贝到AudioFlinger提供的环形缓冲区中。这个过程中,涉及数据从Codec缓冲区到AudioFlinger缓冲区的一次拷贝。(AudioSinkCallback函数的动作有哪些?)具体过程是AudioSinkCallback会调用FillBuffer函数获取解码后的原始数据,解码后数据如果被取完后,AudioPlayer又会调用XXXDecoder解下一帧数据给AudioSink,来回反复,直到文件中数全部被播放。在拉动滚动条时,上层会传来SeekTime,经AudioPlayer传给XXXDecoder再传给XXXExtractor,XXXExtractor根据上层传来的SeekTime判断出要播放的原始数据的起始位置,然后从该位置读取一个数据包传给XXXDecoder解码。

AudioSink是audio播放过程的控制者,以一个thead的形式存在。它不停地调用decoder的接口来获取数据,并将这些数据送往audio播放设备。(AudioSink与decoder没太大关系,逻辑上没关系)

在整个数据格式解码播放过程中,主要涉及两个模块:XXXExtractor和XXXDecoder。

XXXExtractor主要执行数据格式文件解析和数据读取功能;

XXXDecoder主要执行编码数据的解码功能,是由OMXCodec来提供的。

这里最终的一个东西就是AudioSinkCallback回调函数。首先我们要知道,这个函数是谁实现的:

size_tAudioPlayer::AudioSinkCallback(

MediaPlayerBase::AudioSink *audioSink,

void *buffer, size_t size, void*cookie) {

AudioPlayer *me = (AudioPlayer *)cookie;

return me->fillBuffer(buffer, size);

}

我们看到这个函数是由AudioPlayer类实现的。函数的内容直接说明了这个函数的功能,那就是填充所给的buffer。这个buffer是谁给的,这就要看是谁调用了这个函数。我们知道回调函数是实现出来给别人调用的,而不是自己调用。通过不断的注册,最终调用这个函数的类是AudioTrack。所以一切都明白了,是AudioTrack提供一个Buffer,让AudioPlayer来填充这个Buffer。只不过这个注册和调用的过程曲折了一点,注册的时候通过AudioSink转了个手,而调用的时候,也由AudioSink转了个手。本质上在这个过程中AuidoSink并没有做什么。AudioSink是衔接MediaPlayerService和AudioTrack的。是一个逻辑上的声音池。MediaPlayerService处理后的Audio都要丢到这个声音池中,而AudioTrack是由这个声音池创建和控制。至于AudioSink在什么时候创建,由谁创建的,AudioSink的定义和结构就说明了这个,因为它是MediaPlayerService的组成部分,AudioSink是由MediaPlayerService在setDataSource阶段创建的。

转载于:https://www.cnblogs.com/senior-engineer/p/7889799.html

Android Media (Audio) Framework 多媒体系统框架相关推荐

  1. Android中Audio框架

    原址 原文链接 Android中的音频硬件抽象层(HAL)连接android.media中高层的,特定音频框架API到底层的音频驱动和硬件 下列图表描述了音频功能是如何实现的,以及相关实现的相关源代码 ...

  2. Android系统Audio框架介绍(一)

    原址 音频基础知识 声音有哪些重要属性呢? 响度(Loudness) 响度就是人类可以感知到的各种声音的大小,也就是音量.响度与声波的振幅有直接关系. 音调(Pitch) 音调与声音的频率有关系,当声 ...

  3. Android系统Audio框架介绍

    音频基础知识 声音有哪些重要属性呢? 响度(Loudness) 响度就是人类可以感知到的各种声音的大小,也就是音量.响度与声波的振幅有直接关系. 音调(Pitch) 音调与声音的频率有关系,当声音的频 ...

  4. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)

    文章大纲 引言 一.DirectFragment 1.当选中DirectoryFragment中RecyclerView的Item时 2.选中DirectoryFragment中RecyclerVie ...

  5. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(一)

    文章大纲 引言 一.Android Storage Access Framework 二.Storage Access Framework 的主要角色成员 1.Document Provider 文件 ...

  6. Android TV框架 TIF(Android TV Input Framework)入门实践

    Tamic/CSDN http://blog.csdn.net/sk719887916/article/details/53645615 做TV开发一段时间了,国内目前关于这方面的资料并不多,这里我来 ...

  7. android eclipse 报error loading /system/media/audio/ xxx 错的解决办法。

    只针对 报错..error   loading /system/media/audio/ xxx.ogg 一步操作 解决烦恼..把 模拟器声音 关了..所有的错 都没了. 包括 关闭按键声音,触摸声音 ...

  8. Android 音频(Audio)架构

    一.概述 Android 的音频硬件抽象层 (HAL) 可将 android.media 中特定于音频的较高级别的框架 API 连接到底层音频驱动程序和硬件.本部分介绍了有关提升性能的实现说明和提示. ...

  9. 【初学音频】Android的Audio系统之AudioTrack

    目录 前言 1. AudioTrack 2. 用例介绍 2.1 过程 2.2 数据加载模式 2.3 音频流的类型 2.4 Buffer分配和Frame的概念 3. AudioTrack (Java空间 ...

最新文章

  1. 释放内存触发断点及数组、指针的NULL初始化
  2. html上的样式 ui vant_解决vue中组件库vant等ui组件库的移动端适配问题
  3. count 多个字段_一句话实现字段拆分成多行
  4. DCMTK:C ++字符串类OFString的测试程序
  5. Jenkins任务失败,发送邮件通知
  6. 肖像:作家艺术家之一
  7. [转]浅析DDD(领域驱动设计)
  8. linux 命令行使用wget下载百度云资源
  9. java 图片路径裁剪图片,Java中实现图片的裁剪
  10. 使用Fresco实现简单的显示一张图片
  11. 安时积分法的c语言程序,代码生成 | 安时积分法模型搭建
  12. 记一次产品需求:图片等比缩放和CSS自适应布局16:9
  13. 《C++ Primer Plus》学习笔记——共用体union
  14. 彻底关闭Windows更新
  15. JAVA编程基本步骤
  16. 计算机开机自动进入bios,为什么电脑一开机就自动进入BIOS界面 电脑开机重复进入bios解决方法...
  17. windows 下安装软件乱码
  18. 小程序自定义导航栏组件
  19. 实验四+163+张玉洁
  20. 【ZHYP005】子涵优品开发日志

热门文章

  1. delphi 实体类 JSON 数组
  2. 爬出数据标注陷阱,半监督学习模型暗藏多少玄机?
  3. 更少的标签,更好的学习,谷歌半监督学习算法FixMatch
  4. nonlocal python3_Python 中的 global、nonlocal 辨析
  5. 【python教程入门学习】什么是生成器?
  6. 每日整理Python基础——python教程入门学习
  7. Python中的实用小技巧,可以省下不是事情,喜欢记得收下
  8. 学习Python技术,怎么才能更好找到工作
  9. 从入门到精通!零基础上手车辆目标跟踪与计数
  10. 百度研究院|2020年10大人工智能科技趋势