<一>Android Audio音频框架

目录

1.0 设备驱动

2.0  android hal层

3.0  选择设备的暗箱策略AudioPolicy

4.0  软件层面的混音,AudioFlinger

5.0 完整的对外接口 AudioSystem.cpp

6.0 换壳java形似的对外接口AudioSystem.java

7.0 java层的服务供应AudioService.java

8.0 应用层的一对一服务AudioManager

9.0 为了支持和兼容多音频设备的car, 提供的动态策略。  

10.0 CarAudioService CarAudioManager

11.0 AudioTrack OpenslEs AAudio

Aaudiotrack:

opensl: 

AAudio: 

12.0 oboe 库


总览:个人对于Android 音频模块的整体理解,分析各个子模块存在的意义,领悟设计者架构思想,探讨未来可能的发展趋势。篇幅有限,码字不易,部分子模块的详细分析还为上blog
放一张自制图:

图中有一个小的拼写错误,为AlSA ,非ALSO。
全文涉及到 AudioFlinger  AudioPolicy  AudioSystem  AudioService  AudioManager
以及用于音频流数据传输的 AudioTrack  OpenSLes  AAudio。 部分子篇还为编写,先预留占坑。

1.0 设备驱动

        linux 系统上一个应用要播放一个音频,原理是open一个音频播放设备的设备节点,然后往节点写入数据即可。设备节点由内核里面的驱动提供,往该设备节点写数据就是输入到了驱动中,对应音频设备的驱动,linux 提供的是名为ALSA 的框架,通常对音频来说都是pcm设备,所以里面的驱动也就是pcm设备驱动。属于字符设备,串行。(linux 设备类型:字符设备,块设备,网络设备)   由该pcm设备的驱动,处理获得的pcm数据给到硬件信号达到播放声音的目的。
        驱动要处理的问题:竞争,阻塞,可重入。
         pcm设备当然也是支持多个进程同时使用的,那么内核的pcm驱动就需要处理这个并发的情况   (内部也应该是作了混合音频的处理的,待后续补充详细)
        android 也有相应的工具,tinyalsa.  就是直接通过和linux 内核的alsa框架进行交互,可以不经过hal层。当然这个机制只是为了方便调试的,android并不打算把它给应用层使用,应用层的接口还需要经过android系统的层层包装和管理。
详细参考:
《  1.有待补充blog linux pcm设备驱动  》
《 
1. android tinyalsa 理解_王二の黄金时代的博客-CSDN博客

2.0  android hal层

        既然是基于linux内核,硬件厂商完全可以实现自己的驱动,然后系统启动insmod加载进去,普通的应用程序即可通过驱动的设备节点,来open write/read iocontrol  完成对设备的操作。但是为了厂家并不乐意公开自己的驱动全部,于是安卓整了一套硬件抽象层 hal.( 关于hal,介绍的资料不少,不再赘述)
       一个普通的c++的层序,就已经可以直接调用hal层的接口,打开指定的设备,把pcm数据流写入进行播放了。
2..1 android 直接使用hal库播放pcm demo
但是设备众多,总不能都需要开发者自己选择具体的输出设备,于是android又为我们提供了一个自动确定具体设备的模块:AudioPolicy

3.0  选择设备的暗箱策略AudioPolicy

         AudioPolicy   根据有限的固定的流类型,来确定最终的设备。之所以说它是暗箱操作,应为应用层不会参与也不清楚具体选择那个输出设备。很好地将应用开发者和设备制作者再进行了一次分离,通过这个相对不会有太多变化的流类型,让应用开发者不需要再往下关注不同android设备具体音频设备的组成。 而到底这个流类型的数据是输出给听筒,还是扬声器,还是一起都输出,这就需要设备制作者来暗箱操作了,这个就是AudioPolicyServe的用途。
详细参考:
《3. 待后续补充blog  AudioPolicy 选择设备策略的源码分析》

4.0  软件层面的混音,AudioFlinger

        每一个应用都需要自己去调用hal驱动,这看起来是linux上面的开发模式,移动端的系统总是期望什么功能都有系统的一个统一管理,Android系统音频也就是这么干的,这就是AudioFlinger.   从linux的角度来看, android 系统只有一个进程在播放声音,就是AudioFlinger所在的进程。  
        关于AudioFlinger的角色,从其命名就可以直接看出来,Flinger的原本意思就是将多个汇合成一个的意思,同样命名的还有SurfaceFlinger,所以它的作用就是汇合所有的应用的音频输入,合成一个音频流再输入hal层。故而android应用想要播放生音,就需要通过跨进程的方式将自己的pcm数据传递给AudioFlinger,由AudioFlinger汇合,AudioFlinger顺便在汇合的时候可以根据需要做一个音效处理,然后输出给对应的hal层设备。
        当然,如果我们应用不想这么做,而是想直接自行通过hal层输出数据,也是可以的,只要你能访问到hal层接口,有足够的权限,那就不走AudioFlinger中间商,当然也就没办法享用到AudiFlinger提供的增值服务比如音效。
       要说明的是,输入给AudioFlinger的数据也不全是经过解码的pcm数据,有些特殊的设备它可以直接自己解码特殊格式的音频,这些音频数据就不能被AudioFlinger进行混合,AudioFlinger对这些数据就不会处理,直接交给对应的dsp设备,这算是特殊通道,这样的数据流类型被标记为offload  顾名思义就是包袱,打包的数据,一般用于dsp硬件解码的流数据
详细参考:
《4. 待补充blog AudioFlinger 混音及线程模型分析》
4. Android native层直接使用AudioFlinger播放pcm

5.0 完整的对外接口 AudioSystem.cpp

        到此已经完全具备完整的音频能力了,如果是开发一个c++的程序,完全可以使用AudioPolicyServe 和AudioFlingerServer 的接口,从AudioPolicy获取到合适的输出设备,然后把pcm数据给到AudioFlinger让其帮忙播放。这个AudioSystem正是对内部AudioPolicyServe 和AudioFlingerServer的接口的一个外壳包装,让它门两个看起来成为一个整体,已经具备完整的音频能力,取名System也无可厚非。


6.0 换壳java形似的对外接口AudioSystem.java

        为了给java使用,同样再换了个外壳,AudioSystem.java ,AudioSystem.java的作用就完全是为了通过java ->jni-> cpp了。没什么逻辑,只是个桥梁。

7.0 java层的服务供应AudioService.java

        如何让应用层使用到AudioSystem.java? Android还是不想直接将AudioSystem的接口暴露给应用,毕竟还涉及到权限管理,音量的控制,焦点的控制等等,这些逻辑就都被放在了java sdk层统一处理,所以从java 层面来看,android 将音频相关的控制,都放在了 AudioService.java  可以说这是android 音频java 层的大本营, 由它来唯一和AudioSystem同底层控制交互。(也不完全是唯一和AudioSystem交互的模块,比如AudioManager在列举所有音频设备时,没有经过AudioService.java ,而是直接调用的AudioSystem.java. 但是总的设计上的思想,应该是都要经过AudioService的)
AudioService.java 源码有相关的注释:

The implementation of the audio service for volume, audio focus, device management...

详细参考《7. 待后续补充blog  AudioService 如何同AudioManager AudioSystem关联》

8.0 应用层的一对一服务AudioManager

        为了更加方便应用层的使用,AudioService.java 服务为每一个应用派遣了一个一对一的接待AudioManager 。 每一个应用可以申请到一个只为自己服务的AudioManager对象。 但是AudioManager 的实际业务基本上是去跨进程调用AudioService服务, 应用获得AudioManager对象也是通过getSystemService(Context.AUDIO_SERVICE) 得到。 

9.0 为了支持和兼容多音频设备的car, 提供的动态策略。  

        原来的android只是用于音频固件相对比较少的手机,一般只有 扬声器、听筒、有线耳机和蓝牙耳机,以及虚拟的用于无线投屏远程播放的 REMOTE_SUBMIX。 应用层只需要表明音频流的类型,由AudioPolicy来决定该流类型应该使用具体是扬声器设备还是听筒设备,这个AudioPolicy就提自定义策略的实现,由Android设备制作厂家来定制自己的策略,当然这个策略是在 底层AudioPolicy内部的。  对于音频输出设备比较多的car 而言,动不动就6个到10个的喇叭,光靠几个流类型显得不太够用,并且策略完全在底层的AudioPolicy ,修改也很不灵活,于是从 Android 9 开始,  对AudioPolicy进行了修改, 他定义了一个策略的接口 AudioMix ,可以由外部实现,然后注册进来覆盖原来的传统策略模式,这个外部策略甚至可以更加灵活,一直对外暴露到AudioSystem -> AudioService.java->AudioManager.java .  在java层这个策略的定义为AudioPolicy.java 。
        这样连应用层都可以使用AudioManager.java来定义注册自己的AudioPolicy,这就是动态音频策略。当没有app来注册动态策略的时候,系统默认就使用传统的内置在AudioPolicy的策略。当有应用向系统注册了音频策略的时候,就优先使用注册的动态外部策略。当然并不是所有的应用都能随意注册,需要SystemApi级别, 需要android.Manifest.permission.MODIFY_AUDIO_ROUTING权限。
        于是对于传统的手机,可以默认使用原来的策略,对于汽车,只需要外挂一个系统级别的app,即源码中的 packages/services/Car  然后在里面实现自己的AudioPolicy ,用AudioManager.registerAudioPolicy注入即可。
详细参考《 9. 待后续补充blog  android 动态音频策略的原理》

10.0 CarAudioService CarAudioManager

        这就是对于car 设备,外挂的系统级别app, 在应用层 java 实现了定制的音频策略,比如多音区.
       安卓中很多这种manager+service的结构,类似于AudioManager+AudioService的结构,就像我们在8.0 AudioManager中提到的那样, manager是给应用层一对一专门服务的接待对象,而manager内部实现则是经过统一的跨进程到service中去完成功能。
  详细参考:《10. 待后续补充 blog  android CarAudio》

11.0 AudioTrack OpenslEs AAudio

         以上全部在讨论关于设备的选择策略,音量的控制等等,对于应用如何把pcm数据流交给系统,就需要看AudioTrack了。

Aaudiotrack:

        Track原本的意思,是轨迹,轨道,这个名词在安卓系统中有多处使用。 在AudioFlinger中, 每一路音频输入,都是一个 track.    这个就是AudioFlinger 内部创建的track, 为了让外部应用层可以访问到,于是专门抽象出一个类AudioTrack.cpp (为了方便区分是java还是c++ 故意在对象后面加上后缀名), 专门负责和AudioFlinger内部的track进行流数据的拷贝交互。 AudioTrack.cpp 负责直接和AudioFlinger进行流数据的交互, 它不负责具体使用那个音频设备。  它被暴露给应用层,也就是java层的AudioTrack.java  , app 可以用AudioTrack来输出一个音频流到AudioFlinger. 然后AudioFlinger将其输送到具体的音频输出设备。
native层为了配合媒体播放,利用AudioTrack.cpp 的接口编写了一个TrackPlayer,让他符合plaer播放器的特性。 
11. Android native层使用TrackPlayer播放pcm

opensl: 

        同时为了支持opensl 这套公共api标准, 又基于TrackPlayer  实现了Wilhelm project这个工程,所以android 上使用 opensl 实际上就是通过native层的AudioTrack 来和AudioFlinger交互数据流。  至于opensles 相对于AudioTrack.java的性能提升,其实就是少了一层从java 到 native的拷贝延时,两者最后都会通过AudioTrack.cpp -> AudioFlinger -> hal

AAudio: 

        其实就是Android Audio的缩写,android 在java层提供了AudioTrack.java 这个api,但是在native层却没有相应的独立API, 而对 opensles 的支持只是为了适应一套通用规范的接口,并不能提供一些android 自己特性的音频接口,于是在Android O(8.0版本,API-26 ) 版本中引入的全新 Android C API , 取名AAudio (Android Audio)  其中还支持 mmap通路,内存映射的方式直接通到内核层,以减少拷贝,降低延迟。
详细参考:
        《11. Android native层使用TrackPlayer播放pcm_》
        《11. 有待补充 blog android opensl 源码原理分析》
        《11. 有待补充 blog android AAudio分析》

12.0 oboe 库

        为了简化调用流程,自动选择是使用 opensl 还是AAudio (毕竟在低于8.0的设备上是没有AAudio的支持的),谷歌整合了一个c++ 库,叫做oboe, 目前是以第三方库的方式提供,应用层可以嵌入使用,相比于opensl 确实调用起来简洁多了。

官方源码 https://github.com/google/oboe

Oboe audio library | Android Developers (google.cn)

读源码不容易! 码字更是费时费力!  原创!!!发扬共享精神! 

猜你喜欢

转载自blog.csdn.net/u012459903/article/details/126517566