android音频系统,分为两个部分:数据和控制(策略);这种划分方式可以扩展到任意软件系统。
数据:数据流从源(source)到目的(sink)的过程以及数据节点。audiotrack和audioflinger的主要职责就是与数据相关,当然也涉及一些音频控制。
控制:管理和修改数据链路/数据节点。audioservice和audiopolicy的主要职责与此相关。
AudioService:
①音频系统在java层中基本上不参与数据流,AudioService这个系统服务包含了几乎所有与音频有关的内容,所以说AudioService是音频系统在java层的基石;
②AudioManager拥有AudioService的Bp端,是AudioService在客户端的一个代理,几乎所有客户端对AudioManager进行的请求,最终都会交由AudioService实现;
③AudioService的功能实现依赖于AudioSystem类,AudioSystem无法实例化,它是java层到native层的代理,AudioService通过它与AudioPolicyService以及AudioFlinger进行通信
音量键被按下后,按键事件会一路派发给Acitivity,如果无人拦截并处理,承载当前Activity的显示PhoneWindow类的onKeyDown()以及onKeyUp()函数将会被调用,从而开始通过音量键调整音量的处理流程;Window对象在事件的派发队列中位于Acitivity的后面,所以应用程序可以重写自己的Activity.onKeyDown()函数以截获音量键的消息,并将其用作其他的功能。比如说,在一个相机应用中,按下音量键所执行的动作是拍照而不是调节音量;(按键事件的处理流程就是个责任链设计模式的应用)
AudioService.adjustSuggestedStreamVolume(direction, suggestedStream,flags, packageName, TAG)函数是调节音量的入口。
android的音量系统依赖于流类型的,如果Android定义了N个流类型,AudioService就需要维护N个音量值与之相对应,所以AudioService提供了VolumeStreamState,为每一种流类型都分配了一个VolumeStreamState对象,VolumeStreamState保存与一个流类型所有音量相关的信息。并且以流类型的的值为索引,将它保存在一个名为mStreamStates的数组中;
adjustSuggestedStreamVolume会调用到adjustStreamVolume()
音量管理的总结
AudioService中负责音量管理的类是VolumeStreamState,它保存了一种流类型所有的音量信息;
VolumeStreamState保存了运行时的音量信息,而音量的生效则是在底层AudioFlinger完成的;
所以进行音量设置需要做两件事情:更新VolumeStreamState存储的音量值,设置音量到Audio底层系统;
audioService关于音量做了哪些工作:
1.管理和维护流之间的映射关系。
2.音量持久化
3.提供java层音量设置接口
4.同步音量状态。
5.实现步进/默认值功能。
6,适配不同场景下的音量调节需求。(volumepanel强制修改streamtype音量)
7.处理固定音量,安全音量等。
所谓别名的概念就是将N个stream的音量同步增加或者减少。关键点事是两个:1.修改任何stream类型都要修改以这个类型为别名的stream(更新index和将VSS的音量同步设置到底层)。index需要在不同stream之间做rescale。(VSS内部实现)
AudioService内部有mAudioHandler,mAudioHandler是运行在AudioService主线程上的Handler。所有耗时长的代码都会运行在Handler上。包括与底层交互,存文件。
Android系统的音频服务可以分为两类:
1.以java语言编写的systemservice子类。
2.以C++语言编写的native服务。
AudioPolicyService
维护整个音频系统的数据链路(路由)。记录系统的设备节点。
AudioPolicyService 和AudioFlinger是native层两个服务的名称,分别都包含了很多的子模块。
AudioPolicyService 的主要责任:
1.管理音频设备
2.管理音频路由
3.管理系统音量(个人觉得音量处理也是一种常见的简单音效)
4.管理系统音效
这里的管理包含了动态再平衡的概念
设计一个服务就像开一家餐馆一样,需要以下的角色。
1.厨师(算法,处理模块)负责做出满足客户需求的菜品。
2.小二(线程)时刻准备接受客户的点菜(函数调用)并将请求传递到厨师
3.菜单(服务接口)描述饭店的菜品(服务的功能)
4.门店(服务名字外观模式)
Android audio框架的代码之间耦合低,基本都做了依赖倒置。audiopolicyservcie和audiopolicymanger之间通过接口进行依赖,audiopolicymanager和engine之间也通过接口进行依赖。
配置文件:
1.描述端口能力
2.描述系统路由
3.描述音量曲线
4.描述音量曲线的映射关系
5.描述音效,
6.描述音效的映射关系
android音频系统的特点:算法处理,策略业务等非常丰富。扩展性也好(模块的高内聚,低耦合做得很好,但是有很多类,比较碎,比如为了实现跨进程调用往往会新建一个类作为代理去调用原始的业务代码类),但是自由度小,需要充分了解Android音频的体系和概念后才能扩展。我们可以把它理解为精装房,可以扩展但必须匹配原先的风格。编码/设计风格和整个Android系统接轨。调试信息非常丰富。
非adnroid音频系统的特点:扩展性强,扩展接口简单。因为框架本身包含的业务少。可以理解为毛坯房。可以自由扩展,因为房子本身只有一个骨架。框架种类较多,pipewire,pulseaudio等。主要是按照linux风格进行设计和编码。框架之间解耦不佳。不同的框架,模块开发厂商和组织不一样,相关之间差异较大。
类设计的简洁高内聚,也可以减少依赖该类的负担。
最小缓冲区的意义:
最小缓冲区我们可以理解为高速公路的车道数。而CPU的执行速度(受CPU频率/调度等影响)可以理解为车速。缓冲区过小,车速再快,当车的数量增加也会造成堵塞。
数据链路。(写阻塞或者是消费者/生产者模型) 写阻塞不需要考虑缓冲buffer否则考虑最少满足乒乓的要求。
控制链路。
AudioService是binder通信结构的服务端(native),audiomanager是binder通信结构的代理端(proxy)。AudioService持有context并向servicemanager注册。注意context的重要性,java层很多功能都依赖context才能调用。