Audio System 二 之 Audio系统框架
二、Linux Audio系统框架
上图中:
2.1 Application 层
音乐播放器软件等
2.2 Framework 层
Android也提供了另两个相似功能的类,即AudioTrack和AudioRecorder。 MediaPlayerService内部的实现就是通过它们来完成的, 只不过MediaPlayer/MediaRecorder提供了更强大的控制功能,相比前者也更易于使用。
除此以外,Android系统还为我们控制音频系统提供了AudioManager、AudioService及AudioSystem类。 这些都是framework为便利上层应用开发所设计的。
2.3 Libraries 层
framework 只是向应用程序提供访问 Android 库的桥梁,具体功能放在 库 中完成。 比如上面的 Audio Track、Audio Recorder 、MediaPlayer 和 MediaRecorder 等库中都能找到相对应的类。
1、frameworks/av/media/libmedia【libmedia.so】 2、frameworks/av/services/audioflinger【libaudioflinger.so】 3、frameworks/av/media/libmediaplayerservice【libmediaplayerservice.so】
2.4 HAL 层
从设计上来看,硬件抽象层是AudioFlinger直接访问的对象。 这说明了两个问题,一方面AudioFlinger并不直接调用底层的驱动程序; 另一方面,AudioFlinger上层模块只需要与它进行交互就可以实现音频相关的功能了。
因而我们可以认为AudioFlinger是Android音频系统中真正的“隔离板”,无论下面如何变化,上层的实现都可以保持兼容。
音频方面的硬件抽象层主要分为两部分,即AudioFlinger和AudioPolicyService。实际上后者并不是一个真实的设备,只是采用虚拟设备的方式来让厂商可以方便地定制出自己的策略。
抽象层的任务是将AudioFlinger/AudioPolicyService真正地与硬件设备关联起来 ,但又必须提供灵活的结构来应对变化——特别是对于Android这个更新相当频繁的系统。 比如以前Android系统中的Audio系统依赖于ALSA-lib,但后期就变为了tinyalsa,这样的转变不应该对上层造成破坏。
因而Audio HAL提供了统一的接口来定义它与AudioFlinger/AudioPolicyService之间的通信方式,这就是audio_hw_device、audio_stream_in及audio_stream_out等等存在的目的 。这些Struct数据类型内部大多只是函数指针的定义,是一些“壳”。当AudioFlinger/AudioPolicyService初始化时,它们会去寻找系统中最匹配的实现(这些实现驻留在以audio.primary.,audio.a2dp.为名的各种库中)来填充这些“壳”。
根据产品的不同,音频设备存在很大差异,在Android的音频架构中,这些问题都是由HAL层的audio.primary等等库来解决的,而不需要大规模地修改上层实现。换句话说,厂商在定制时的重点就是如何提供这部分库的高效实现了。
2.5 Tinyalsa 层
源码在external/tinyalsa目录下 Tinyalsa:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制
2.6 Kernel部分
6.1 ALSA 和 ASoC
Native ALSA Application :tinyplay / tinycap / tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制 功能。
ALSA Library API :alsa 用户库接口,常见有 tinyalsa、alsa-lib
ALSA CORE :alsa 核心层,向上提供逻辑设备(PCM / CTL / MIDI / TIMER /…)系统调用,向下驱动硬件设备
( Machine / I2S / DMA / CODEC )
ASoC CORE :asoc 是建立在标准 alsa core 基础上,为了更好支持嵌入式系统和应用于移动设备的音频 codec 的一套软件体系
Hardware Driver :音频硬件设备驱动,由三大部分组成,分别是 Machine、Platform、Codec
6.2 ASoC ASoC被分为Machine、Platform和Codec三大部分。 其中的Machine驱动负责Platform和Codec之间的耦合和设备或板子特定的代码。
Platform驱动 的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。
6.2.1 Machine 用于描述设备组件信息和特定的控制如耳机/外放等。
Machine是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。
Machine 这一部分将平台驱动和 Codec 驱动绑定在一起,描述了板级的硬件特征。 主要负责 Platform 和 Codec 之间的耦合以及部分和设备或板子特定的代码。 Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);
单独的 Platform 和 Codec 驱动是不能工作的,它必须由 Machine 驱动把它们结合在一起才能完成整个设备的音频处理工作。
ASoC 的一切都从 Machine 驱动开始,包括声卡的注册,绑定 Platform 和 Codec 驱动等等。
6.2.2 Platform 用于实现平台相关的DMA驱动和音频接口等。
Platform 一般是指某一个SoC平台,比如 pxaxxx,s3cxxxx,omapxxx 等等,与音频相关的通常包含该 SoC 中的时钟、DMA、I2S、PCM等等,只要指定了 SoC,那么我们可以认为它会有一个对应的 Platform,它只与 SoC 相关,与 Machine 无关,这样我们就可以把 Platform 抽象出来,使得同一款 SoC 不用做任何的改动,就可以用在不同的 Machine 中。实际上,把 Platform 认为是某个 SoC 更好理解。
这一部分只关心CPU本身,不关心Codec。 主要处理两个问题:DMA引擎 和 SoC集成的PCM、I2S或AC ‘97数字接口控制。 主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。
在具体实现上,ASoC 有把 Platform 驱动分为两个部分:snd_soc_platform_driver 和 snd_soc_dai_driver。 其中,platform_driver 负责管理音频数据 ,把音频数据通过dma或其他操作传送至cpu dai中,dai_driver则主要完成cpu一侧的dai的参数配置 ,同时也会通过一定的途径把必要的dma等参数与snd_soc_platform_driver进行交互。
6.2.3 Codec 用于实现平台无关的功能,如寄存器读写接口,音频接口,各widgets的控制接口和DAPM的实现等。
字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和 多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个 Codec 可以被不同的Machine使用。嵌入式 Codec 通常通过I2C对内部的寄存器进行控制。
这一部分只关心 Codec 本身,与 CPU 平台相关的特性不由此部分操作。
在移动设备中,Codec 的作用可以归结为4种,分别是: 1、对 PCM 等信号进行 D/A 转换,把数字的音频信号转换为模拟信号。 2、对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号。 3、对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的。 4、对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等。
ASoC 对 Codec 的这些功能都定义好了一些列相应的接口,以方便地对Codec进行控制。 ASoC 对 Codec 驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个 Codec 的代码不经修改即可用在不同的平台上。
ASoC对于Alsa来说,就是分别注册PCM/CONTROL类型的snd_device设备,并实现相应的操作方法集。 图中DAI是数字音频接口,用于配置音频数据格式等。
☁ Codec 驱动 向 ASoC 注册 snd_soc_codec 和 snd_soc_dai 设备。 ☁ Platform 驱动 向 ASoC 注册 snd_soc_platform 和 snd_soc_dai 设备。 ☁ Machine 驱动通过 snd_soc_dai_link 绑定 codec / dai / platform 。
Widget是各个组件内部的小单元。处在活动通路上电,不在活动通路下电。ASoC的DAPM正是通过控制这些Widget的上下电达到动态电源管理的效果。
☁ path描述与其它widget的连接关系。 ☁ event用于通知该widget的上下电状态。 ☁ power指示当前的上电状态。 ☁ control实现空间用户接口用于控制widget的音量/通路切换等。 对驱动开者来说,就可以很好的解耦了:
☁ codec驱动的开发者,实现codec的IO读写方法,描述DAI支持的数据格式/操作方法和Widget的连接关系就可以了; ☁ soc芯片的驱动开发者,Platform实现snd_pcm的操作方法集和DAI的配置如操作 DMA,I2S / AC97 / PCM 的设定等; ☁ 板级的开发者,描述Machine上codec与platform之间的总线连接, earphone/Speaker的布线情况就可以了。
6.2.4 DAPM DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,
DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。 DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。 用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。
2.7 Audio Devices 部分
是指具体的 Audio 设备。
三、Qualcomm 平台 - Audio系统框架
由于接下来的一系列Android && kernel 源码分析都是基于Qualcomm 平台的,十分有必要介绍Qualcomm 平台的Audio 系统框架。
硬件平台及软件版本: ☁ Kernel - 3.18 ☁ SoC - Qualcomm snapdragon ☁ CODEC - WCD9335 ☁ Machine - msm8996 ☁ Userspace - tinyalsa
3.1 Qualcomm Audio系统总体框架图
3.2 ASoC driver
ALSA 片上系统 (ASoC) 驱动程序将音频系统分为四个组成部分: Machine driver、Platform driver、CPU driver、Codec driver。
3.2.1 Machine driver
将平台、CPU、 和编解码 驱动程序整合在一起 /Kernel/sound/soc/msm/xxxx.c。 定义 Frontend (FE) and Backend (BE),Digital Audio Interface (DAI) links。
3.2.2 Platform driver
包含用于 流数据传输 与 路由的平台 特定的控件(control), 细分为 FE 和 BE 平台驱动程序
FE ☁ Audio — 实例化 PCM 播放和录制会话;借助 ASM 接口,将 PCM 数据从用户空间传输到 DSP 进行播放 以及 从DSP 传输到 用户空间进行录制, 在 /kernel/sound/soc/msm-pcm-q6-v2.c 中实现。
☁ Voice — 初始化 / 取消初始化 语音呼叫设置,在 /kernel/sound/soc/msm-pcm-voice-v2.c 中实现。
☁ VoIP — 初始化 / 取消 初始化 MVS 接口 以及 传输自 / 至 DSP 的 PCM 数据,在 /kernel/sound/soc/msm-pcmvoip-v2.c 中实现
☁ Compressed offload — 支持 将压缩 数据发送到 DSP 进行压缩分流播放,在 /kernel/sound/soc/msm-compress-q6-v2.c 中实现
BE ☁ 路由 — 执行音频路由任务,在 /kernel/sound/soc/msm-pcm-routing-v2.c 中实现
3.2.3 CPU driver
FE ☁ 向 ASoC 框架提供关于 FE PCM 设备的信息 ☁ ASoC 框架与平台驱动程序提供的路由表共同将 PCM 播放/捕获从 FE 传递至 BE ☁ 没有针对播放和录制的内置逻辑 ☁ 定义 FE CPU DAI – 在 kernel/sound/soc/msm/msm-dai-fe.c 中实现
BE ☁ 要在初始化 PCM 播放/捕获时激活所需音频硬件端口,则配置 DSP AFE 模块 ☁ 定义 BE CPU DAI – 在 kernel/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c 中实现
3.2.4 Codec driver
与平台无关,其中包含 音频控制、音频接口功能、编解码器 DAPM 定义 及 编解码器输入输出 功能。 此处, 实现 MBHC 状态机,用于检测有线耳机 插入/拔出、附件类型、连接器类型 和 多按钮 检测。
3.3 DSP driver
ASM(Audio Stream Manager) ☁ 用于与DSP ASM 模块通信的接口 ☁ 提供将 PCM 数据路由至 DSP 的机制,支持按数据流进行后期处理/预处理
ADM(Audio Device Manager) ☁ 允许在 DSP 中使用 ADM 服务 ☁ 配置 COPP 和路由矩阵 ☁ 与音频校准数据库 (ACDB) 进行通信,使用正确的校准数据配置 COPP ☁ 将 ASM 会话 ID 路由至 ADM 会话
AFE(Audio Front-End) ☁ 允许在 DSP 中使用 AFE 服务 ☁ 激活/禁用音频硬件端口 ☁ 子系统管理器 – 发生 MDSP 复位事件时,通知音频和语音驱动程序关闭待处理会话、执行清理操作并等待一个指示 MDSP 已启动的事件
APR(Asynchronous Packet Router) ☁ 为处理器间通信提供异步框架 ☁ 用于与 Hexagon 和调制解调器处理器进行通信 ☁ Image loader PIL – 载入 MDSP 图像
3.4 User Space
Audio Hardware Abstraction Layer (AHAL) 通过 tinyALSA 将 AudioFlinger 调用映射至ASoC 驱动程序的硬件抽象层。
ACDB loader 检索特定设备的校准信息,并写入 PMEM。 ACDB 驱动程序在启动过程中分配该 PMEM。在设备切换时,此校准将被发送到 DSP。
tinyALSA 连接至内核 ASoC 驱动程序的接口,供音频 HAL 使用。 提供用于音频流和设备管理的基本 PCM 和混音控件 API。
Audio route 此模块会从一个 .xml 文件读取 ALSA 混音控件,并根据音频 HAL 所选的设备设置混音控件。
Concurrency Manager 在 MSM8x10 中,视频解码和编码在 DSP 中完成; 因此,有对可支持的并发性有一些限制。 MSM8x10 中引入的并发管理器管理并发性可以支持涉及语音和音频的不同用例。
user space 的服务
Multimedia framework – Stagefright ☁ 支持标准音频格式的播放/录制 ☁ 与解码器/编码器库以及 OpenMAX IL 组件通信,以便进行解码和编码
Audio service ☁ 由系统服务器启动并由服务管理器管理的运行时服务之一 ☁ 意图注册;当从各种应用程序(HDMI、蓝牙等)接收到这些意图时,通知音频系统
AudioFlinger ☁ 通过 libaudio 接口、蓝牙 A2DP 接口管理所有音频输出/输入设备 ☁ 将多个音频流处理为单一的 PCM 音频;混合后的输出被传送到输出设备 ☁ 播放音乐流时的音量
Audio Policy Manager (APM) ☁ 定义多个音频用例之间的并发规则 ☁ 用例示例 ––– 电话通话、音乐播放、系统声音 和 通知 ☁ 定义播放的音频(例如:语音、播放、铃声)以及播放的设备(蓝牙、扬声器和耳机)
APM 用途 : ☁ 管理各种输入输出设备接口 ☁ 管理各种输入输出设备,例如:麦克风、扬声器、耳机、听筒、A2DP、蓝牙 SCO ☁ 基于音频流、模式和方法选择和定义适当的路由策略 ☁ 管理每个音频流的音量/静音设置(在它们激活或禁用时)
参考资料(特别感谢各位前辈的分析和图示):
Android音频模块启动流程分析
Jhuster的专栏 Android音频开发
高通audio offload学习 | Thinking
DroidPhone的专栏 - CSDN博客
alsa音频架构1-CSDN博客
alsa音频架构2-ASoc - CSDN博客
alsa音频架构3-Pcm - CSDN博客
alsa音频架构4-声卡控制 - CSDN博客
Linux ALSA 音频系统:逻辑设备篇 - CSDN博客
Linux ALSA 音频系统:物理链路篇 - CSDN博客
专栏:MultiMedia框架总结(基于6.0源码) - CSDN博客
Android 音频系统:从 AudioTrack 到 AudioFlinger - CSDN博客
AZURE - CSDN博客 - ALSA-Android Audio
AZURE - CSDN博客 - ANDROID音频系统
Audio驱动总结–ALSA | Winddoing’s Blog
audio HAL - 牧 天 - 博客园
林学森的Android专栏 - CSDN博客
深入剖析Android音频 - CSDN博客Yangwen123
播放框架 - 标签 - Tocy - 博客园
Android-7.0-Nuplayer概述 - CSDN博客
Android-7.0-Nuplayer-启动流程 - CSDN博客
Android Media Player 框架分析-Nuplayer(1) - CSDN博客
Android Media Player 框架分析-AHandler AMessage ALooper - CSDN博客
Android N Audio播放 start真面目- (六篇) CSDN博客
深入理解Android音视频同步机制(五篇)NuPlayer的avsync逻辑 - CSDN博客
wangyf的专栏 - CSDN博客-MT6737 Android N 平台 Audio系统学习
Android 7.0 Audio: Mediaplayer - CSDN博客
Android 7.0 Audio-相关类浅析- CSDN博客
Android N Audio播放六:如何读取buffer - CSDN博客
Fuchsia OS中的RPC机制-FIDL - CSDN博客
高通Audio中ASOC的codec驱动 - yooooooo - 博客园