OpenHarmony南向之Audio
OpenHarmony南向之Audio
音频架构
Audio驱动框架基于HDF驱动框架实现,包含内核态(KHDF),和用户态(UHDF), 对北向提供音频HDI接口
音频框架图
驱动架构主要由以下几部分组成。
- HDI adapter:实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。
- Audio Interface Lib:向下配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入,向上和上层的Audio HDI Adapter层进行对接。
- ADM(Audio Driver Model):音频驱动框架模型,向上服务于多媒体音频子系统,向下统一接口适配各自的驱动代码。
主要代码目录
drivers/hdf_core/framework/model/audio
及device/board/xxx/yyy/audio_drivers
等: ADM相关驱动,KHDF部分drivers/peripheral/audio
: audio HAL 实现,UHDF部分foundation/multimedia/audio_framework
: audio framework 实现
调用流程
Audio Service
目录:foundation/multimedia/audio_framework/services/
client:
通过 Remote()->SendRequest
,和服务端进行通信(IPC),binder
server:
server端向上通过IPC与client交互,向下与HAL交互
使用PulseAudio进行音频流管理foundation/multimedia/audio_framework/services/audio_service/client/src/audio_service_client.cpp
HAL
HAL简单架构
- alsa adapter(
drivers/peripheral/audio/supportlibs/alsa_adapter
):
基于alsa lib(alsa用户态接口库),对alsa接口的封装,向下驱动基于alsa - adm adapter(
drivers/peripheral/audio/supportlibs/adm_adapter
):
基于amate(ADM用户态接口库),对ADM接口的封装,向下驱动基于alsa - supportlib
屏蔽声卡访问控制的差异,在用户态实现一套符合 hdi-adapter 规范的访问控制 - hdi-passthrough
将声卡(片内声卡、usb 声卡、HDMI 声卡等)抽象成 adapter,每个 adapter 都包含 supportlibs 抽象的 audioRender 和 audiocapture,最后通过 audiomanager 管理 adapters。进一步将音频 HDI 接口规范化封装 - hdi-binder
HDF音频驱动框架最上层的封装,基于C/S的IPC机制
所以,由此可以得到音频驱动适配的几种主流方案:
- 通过”adm adapter”对接自研ADM内核驱动,是目前社区主流方案
- 通过”alsa lib”对接ASLA,是针对已支持ASLA产品的友好适配方案
- 通过自己实现Vendor HAL来对接音频HDI接口,主要针对厂商有自己成熟的音频中间件的情况
目录结构
从下面代码目录结构可以很容易和上面的架构图一一对应
zdd@zdd-PC:~/WorkSpace/OHOS/oh-v3.2.2/drivers/peripheral/audio$ tree -L 2
.
├── audio.gni
├── BUILD.gn
├── bundle.json
├── config
├── hal
│ ├── hdi_binder
│ │ ├── proxy
│ │ └── server
│ ├── hdi_passthrough
│ └── pathselect
├── hdi_service
│ ├── binder
│ ├── BUILD.gn
│ ├── passthrough
│ ├── pathselect
│ └── supportlibs
├── interfaces
│ ├── 2.0
│ └── include
├── supportlibs
│ ├── adm_adapter
│ ├── alsa_adapter
│ ├── BUILD.gn
│ └── interfaces
└── test
├── BUILD.gn
├── fuzztest
├── resource
├── sample
├── systemtest
└── unittest
HAL流程
ADM流程
ADM流程基本上都是基于驱动消息机制(dispatch)实现:drivers/peripheral/audio/supportlibs/adm_adapter/src/audio_interface_lib_common.c
下面看看几种基本的ADM流程:
ADM启动流程
- 系统启动时Audio模块的Platform、Codec、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
- 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
- ADM模块读取hdf_audio_driver_0和hdf_audio_driver_1配置信息,加载各模块的具体设备。
- ADM模块调用各模块的初始化函数对各模块设备进行初始化。
- 将初始化成功的音频设备添加到cardManager链表。
HCS
以rk3568平台,结合上面的启动流程看看audio相关的hcs文件
device_info.hcs
:
...
audio :: host {
hostName = "audio_host";
priority = 110;
device_dai0 :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "DAI_RK3568";
serviceName = "dai_service";
deviceMatchAttr = "hdf_dai_driver";
}
}
device_codec_0 :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "CODEC_RK809";
serviceName = "codec_service_0";
deviceMatchAttr = "hdf_codec_driver_0";
}
}
device_codec_1 :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "CODEC_RK817";
serviceName = "codec_service_1";
deviceMatchAttr = "hdf_codec_driver_1";
}
}
device_dsp :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "DSP_RK3568";
serviceName = "dsp_service_0";
deviceMatchAttr = "hdf_dsp_driver";
}
}
device_dma :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "DMA_RK3568";
serviceName = "dma_service_0";
deviceMatchAttr = "hdf_dma_driver";
}
}
device_audio :: device {
device0 :: deviceNode {
policy = 2;
priority = 60;
preload = 0;
permission = 0666;
moduleName = "HDF_AUDIO";
deviceMatchAttr = "hdf_audio_driver_0";
serviceName = "hdf_audio_codec_primary_dev0";
}
device1 :: deviceNode {
policy = 2;
priority = 60;
preload = 0;
permission = 0666;
moduleName = "HDF_AUDIO";
deviceMatchAttr = "hdf_audio_driver_1";
serviceName = "hdf_audio_codec_primary_dev11";
}
}
device_stream :: device {
device0 :: deviceNode {
policy = 2;
priority = 80;
preload = 0;
permission = 0666;
moduleName = "HDF_AUDIO_STREAM";
serviceName = "hdf_audio_render";
}
device1 :: deviceNode {
policy = 2;
priority = 80;
preload = 0;
permission = 0666;
moduleName = "HDF_AUDIO_STREAM";
serviceName = "hdf_audio_capture";
}
}
device_control :: device {
device0 :: deviceNode {
policy = 2;
priority = 80;
preload = 0;
permission = 0666;
moduleName = "HDF_AUDIO_CONTROL";
serviceName = "hdf_audio_control";
}
}
device_analog_headset :: device {
device0 :: deviceNode {
policy = 1;
priority = 90;
preload = 0;
permission = 0666;
moduleName = "AUDIO_ANALOG_HEADSET";
serviceName = "analog_headset_service";
deviceMatchAttr = "analog_headset_attr";
}
}
}
...
audio_config.hcs
:
root {
platform {
template card_controller {
match_attr = "";
serviceName = "";
codecName = "";
platformName = "";
cpuDaiName = "";
codecDaiName = "";
dspName = "";
dspDaiName = "";
}
controller_0x120c1000 :: card_controller {
match_attr = "hdf_audio_driver_0";
serviceName = "hdf_audio_codec_primary_dev0";
codecName = "codec_service_0";
platformName = "dma_service_0";
cpuDaiName = "dai_service";
codecDaiName = "codec_dai";
dspName = "dsp_service_0";
dspDaiName = "dsp_dai";
}
controller_0x120c1001 :: card_controller {
match_attr = "hdf_audio_driver_1";
serviceName = "hdf_audio_codec_primary_dev11";
codecName = "codec_service_1";
platformName = "dma_service_0";
cpuDaiName = "dai_service";
codecDaiName = "rk817_dai";
dspName = "dsp_service_0";
dspDaiName = "dsp_dai";
}
}
}
codec_config.hcs
root {
platform {
template codec_controller {
match_attr = "";
serviceName = "";
codecDaiName = "";
}
controller_0x120c1030 :: codec_controller {
match_attr = "hdf_codec_driver_0";
serviceName = "codec_service_0";
codecDaiName = "codec_dai";
regConfig {
/* reg, value */
initSeqConfig = [
0x13, 0xf4,
...
];
controlsConfig = [
/*array index, iface, mixer/mux, enable,*/
0, 2, 0, 1,
...
];
/* reg, rreg, shift, rshift, min, max, mask, invert, value */
ctrlParamsSeqConfig = [
0x31, 0x32, 0, 0, 0x00, 0xFF, 0xFF, 1, 0x00, // DACL/R Playback Volume
...
];
/* reg, rreg, shift, rshift, min, max, mask, invert, value */
daiParamsSeqConfig = [
0x45, 0x45, 0, 0, 0x0, 0xFF, 0xFF, 0, 0x0C, // PLL_PREDIV_BIT
...
];
ctrlSapmParamsSeqConfig = [
0x27, 0x27, 5, 5, 0x00, 0x1, 0x1, 1, 0x00, //LPGA MIC -- connect MIC1
...
];
/*
sapm
reg is 0xFFFF: component has no sapm register bit
sapmType, compNameIndex, reg, mask, shift, invert, kcontrolNews, kcontrolsNum
*/
sapmComponent = [
10, 0, 0x18, 0x1, 7, 1, 0, 0, //ADCL
...
];
/*array index, iface, mixer/mux, enable*/
sapmConfig = [
0, 2, 0, 1,
...
];
}
}
controller_0x120c1031 :: codec_controller {
match_attr = "hdf_codec_driver_1";
serviceName = "codec_service_1";
codecDaiName = "rk817_dai";
}
}
}
ADM播放流程
- 播放音频时,Interface Lib层通过播放流服务下发Render Open指令,Audio Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。
- Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。
- Interface Lib层通过播放流服务下发硬件参数,Audio Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。
- Interface Lib层通过播放流服务下发播放启动指令,Audio Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。
- Interface Lib层通过播放流服务下发音频数据,Audio Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。
- Interface Lib层通过播放流服务下发播放停止指令,Audio Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。
- Interface Lib层通过播放流服务下发Render Close指令,Audio Stream Dispatch服务收到指令后调用Platform AudioRenderClose对已申请资源进行释放。
ADM控制流程
- 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Get函数,获取可设置音量的范围。
- Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Set函数设置音量。
分布式音频组件
分布式音频是指多个设备之间音频外设跨设备协同使用的能力,如将设备A的音频通过设备B的Speaker进行播音,或者设备A使用设备B的Mic进行录音。
分布式音频不直接向应用提供接口,应用可以通过音频框架的接口来调用分布式音频能力,使用方式与本地音频一致。
概念说明
主控端(source) :分布式音频控制端设备,向被控端设备发送指令,实现在被控端设备上音频播放和录制的功能;
被控端(sink) :分布式音频被控制端设备,接收来自主控端设备的指令,使本地音频外设为主控端设备所用,用来播音或录音。
参考
- https://laval.csdn.net/64eef49d6ffa502025762163.html
- https://blog.csdn.net/qq_37596943/article/details/128616800
- https://docs.openharmony.cn/pages/v3.2/zh-cn/device-dev/driver/driver-peripherals-audio-des.md/
- 分布式音频部件官方文档