本文档介绍了音频驱动程序公开的音频流式传输接口。 紫红色的衣服旨在供用户和 驱动程序作者,并明确定义驱动程序的接口协定 且用户必须遵守。
概览
音频流是由驱动程序服务发布的、旨在使用的设备节点 以便在 Fuchsia 设备上捕获或渲染音频。 系统中的每个声音流(输入或输出)都代表一个数字音频流 可能由设备接收或传输的信息。视频流 且可能由系统随时创建或销毁。哪些数据流 存在于任何给定时间点,以及控制其生命周期的因素 视为涉及音频政策和编解码器管理的问题, 本文档中讨论的。此外,音频中呈现的信息 输出流是流的应用所有者所独有的。混合 audio 不是音频流接口提供的服务。
定义
术语 | 定义 |
---|---|
试阅内容 | 由 一个人讲话, 麦克风。 |
LPCM | 线性脉冲编码调制。使用 音频样本的表示 所有 Fuchsia 未压缩音频流。LPCM 音频样本是 输入或输出的 其中, 编码音频线性分布 呈现的 或捕获设备。这与 A 定律和 μ 律编码, 从数值到 振幅级。 |
频道 | 在音频流中, 呈现的信息 或是在一个扬声器上 单个麦克风 |
框架 | 一组音频样本,用于 以单个视频的形式捕获/呈现的音频流, 实现这一目标 |
帧速率 | 也称为“抽样率”。频率(以 Hz 为单位) 生成或使用了哪些音频帧。 常见的采样率包括 44.1 KHz、48 KHz、96 KHz 等。 |
客户端、用户或应用 | 在这一章中,这两个术语 文档。它们指的是使用 与容器进行通信 音频驱动程序/设备。 |
基本操作
与音频流设备的通信是通过发送 频道。应用会打开设备节点 流式传输,并通过发出 FIDL 请求获取频道。在获取 那么设备节点可能会关闭。与 都是使用频道进行的
流频道用于大多数命令和控制任务,包括:
- 功能询问
- 格式协商
- 硬件增益控制
- 确定外部延迟
- 插头检测通知
- 访问控制功能检测和信号
为了在音频流中实际发送或接收音频信息,
必须先设置要使用的特定格式。对成功事件的响应
CreateRingBuffer
操作将包含一个新的“环形缓冲区”。环形缓冲区
通道可用于从流(传送于
VMO 形式),以便可以映射到
空间,根据需要用于发送或接收音频数据。
通常,在环形缓冲区通道上执行的操作包括:
- 请求共享缓冲区
- 开始和停止播放和捕获视频流
- 接收播放和捕获进度的通知
- 在音频输出时钟的情况下接收时钟恢复信息 所用的振荡器与支持 单调时钟
操作细节
设备节点
音频流设备节点必须由驱动程序使用协议发布 预处理器符号。这会导致流媒体设备 要在表中指定的位置发布的节点数量。应用可以 监控这些目录,以便在新数据流发布时发现它们 信息。
数据流类型 | 协议 | 位置 |
---|---|---|
输入 | ZX_PROTOCOL_AUDIO_INPUT |
/dev/class/audio-input |
输出 | ZX_PROTOCOL_AUDIO_OUTPUT |
/dev/class/audio-output |
建立直播频道
打开设备节点后,客户端应用可以获得一个流频道
使用
fuchsia.hardware.audio.Device/GetChannel
FIDL 消息。
流信道在客户端终止
客户端可以随时终止与数据流的连接,只需 对流调用 zx_handle_close(...) 。驱动程序必须关闭所有已建立的有效环形缓冲区通道 使用此直播频道,必须尽一切努力优雅地安静 任何正在进行的流式操作。
在串流和环形缓冲区通道上发送和接收消息
可通过 串流和环形缓冲区通道在 stream_config.fidl 和 ring_buffer.fidl 的访问权限。 您可以使用 zx_channel_write(...) 系统调用。如果响应为 则使用 zx_channel_read(...) 系统调用。最佳做法 为您的通道将数据包排入队列 端口 zx_port_queue(...) 系统调用,并使用 zx_port_wait(...) 系统调用,用于确定 的渠道具有消息(预期响应或异步 通知)。 我们为不同语言设置了绑定,以方便发送和接收 FIDL 消息,特别是对于 C++ 驱动程序,还有一个库 SimpleAudioStream 上,以便于创建 采用 C++ 语言,该库使用 用于发送和接收的新 C++ 绑定 FIDL 消息。
格式协商
示例格式
与 Format
相关的协议消息让驾驶员可以列出其支持的协议
传送给客户端支持的格式可能包括多个速率、每个样本位、
等等。每个司机都宣传其支持的功能,而客户要求使用
都使用同一个模式。
要确定指定驱动程序支持哪些格式,客户端会使用
GetSupportedFormats
函数。驾驶员以
SupportedFormats
,其中每个 SupportedFormats
都包含一个 PcmSupportedFormats
,其中:
- 频道数的矢量。这里会列出支持的渠道数量
由驾驶员驱动,例如
<2,4,6,8>
。支持两个或 四个渠道将报告包含两个元素<2,4>
的矢量。必须按升序排列。 - 样本格式的向量,例如
PCM_SIGNED
。 - 费率矢量。帧速率,例如 44100、48000 和 96000。必须按升序排列。
- 每个样本的字节数。必须按升序排列。
- 每个样本的位数向量。样本宽度,此值可以小于 可用的总字节数,例如4 字节样本中的 24 位。必须按升序排列。
当驾驶员支持的所有组合都无法用一个组合进行描述时,
PcmSupportedFormats
时,驱动程序在PcmSupportedFormats
所返回的向量。例如,如果一个 PcmSupportedFormats
允许在
48KHz 和 16 位在 96KHz 时采样,但 32 位在 96KHz 时采样,则驱动程序
有 2 个PcmSupportedFormats
的回复:<<16bits,32bits>,<48KHz>>
和
<<16bits>,<96KHz>>
。为简单起见,此示例会忽略
每个样本的速率和比特率如果驱动程序支持 16 或 32
位采样在 48 或 96 KHz,驱动程序应回复 1
PcmSupportedFormats
:<<16bits,32bits>,<48KHz,96KHz>>
。
此外,假设每个样本的位数始终小于或等于
每个样本 8 * 字节。因此,司机可以
<<2bytes_per_sample,4bytes_per_sample>,<16bits_per_sample,32bits_per_sample>>
这并不表示报告为 2 个字节上每个样本 32 位
示例有效,但它仅指定了 3 种有效组合:
- 对具有 16 个有效位的通道进行 2 字节采样
- 包含 32 个有效位的 4 字节样本
- 包含 16 个有效位的 4 字节样本
客户端会根据以下内容指定要与 CreateRingBuffer
函数搭配使用的格式:
驾驶员在 GetSupportedFormats
回复中提供的信息,以及支持哪些信息
以及任何其他要求。此函数接受一个指定以下内容的形参:
- 多个频道。这是缓冲区中可用的通道数量。
- 要使用的通道的位掩码。这些是缓冲区中的通道,供
驾驶员。例如,对于立体声,此字段必须是启用了 2 位
0x3
的位掩码, 也就是说,同时使用频道 0 和 1。 - 示例格式。
- 帧速率。
- 每个样本的字节数。
- 每个样本的位数。
注意:
- 默认情况下,假定多字节样本格式使用主机端字节序。
PCM_FLOAT
编码专门使用 IEEE 754 浮点 表示。- 默认情况下,假设非浮点 PCM 编码采用
二进制补码有符号
整数。例如16 位 PCM 采样格式的位值范围为
[0x8000, 0x7FFF] 0x0000 表示扬声器偏转零。如果
使用
PCM_UNSIGNED
样本格式,位值的范围为 [0x0000, 0xFFFF],其中 0x8000 表示零偏转。 - 在较大的通道中对较小的样本大小进行编码时(例如, 32),则 32 位容器的最高有效位用于 最低有效位将被忽略(左对齐)。例如一个 20 位样本将被映射到 移到 32 位容器的范围 [12,31] 范围内(将忽略位 [0,11])。
设置所需的流格式
为了选择流格式,应用会通过CreateRingBuffer
直播频道在消息中,应用指定要使用的格式。
客户端会指定新的环形缓冲区通道,该通道将用于 执行流式传输操作如果先前的环形缓冲区通道已被 且仍处于活动状态,则驱动程序必须关闭此通道,然后 尝试妥善地停止任何正在进行的流式传输操作, 过程。
确定外部延迟时间
音频流的外部延迟时间是指 将输出音频从系统的互连传输至音响系统 或从麦克风传输到系统的传入音频 互连方式。以一个连接到系统的外部编解码器为例, 使用 TDM 互连:如果此互连存在 4 帧延迟 在接收 TDM 帧和在 那么此音频路径的外部延迟就是 时长相当于 4 个音频帧。
外部延迟会在 DelayInfo
的 external_delay
字段中报告
对 WatchDelayInfo
的响应。驾驶员应尽最大努力
准确报告司机了解的所有延迟原因的总体情况。
有关这种延迟的信息经常可以在编解码器数据表中找到,
使用 Intel HDA 等协议动态报告为编解码器的属性
或由 USB 音频规范的下行设备使用
例如使用 HDMI 或 DisplayPort 互连时的 EDID 等机制。
确定开启延迟时间
音频流的 turn_on_delay
是指
对环形缓冲区上的音频样本进行实际开始播放,
扬声器或在发出 Start
命令后发出音频,或麦克风发出音频
在 Start
之后也开始记录到环形缓冲区中
命令。例如,如果我们有一个外部编解码器连接到系统,
我们将其关闭以节省电量,在发出 Start
命令后,
提供环形缓冲区的驱动程序将回复一个 start_time
,指明
环形缓冲区上的位置开始移动,音频样本开始
发送到外部编解码器。不过,它独立于 external_delay
(请参阅
Determining external latency
),外部编解码器可能仍在供电
从低功耗模式调高音量。turn_on_delay
,在
RingBufferProperties
表,是驱动程序对
让外部编解码器真正输出音频所需的时间
示例。延迟也可能是由其他原因造成的,例如,编解码器的
内置累积延迟,以避免故障,或因驱动程序抽象出蓝牙功能
通信延迟让远程设备开始播放音频
示例。由于 turn_on_delay
是估算的上限,因此音频可能会开始
在 turn_on_delay
过去之前播放或被捕获,这就是为什么
如果系统未播放初始音频样本,则必须考虑延迟时间
是不可接受的
external_delay
表示音频样本延迟的时间量
扬声器或麦克风。turn_on_delay
不会延迟
并且不指示任何缓冲,而是指示
音频样本实际不得播放/录制的时长。
turn_on_delay
不会影响呈现时间的计算,但
确实会影响演示。为了进行播放,我们可将
导致音频样本进入扬声器的延迟:
|<--- external delay --->|
|S|--|T|----------------------|P|----|O|
|<------- turn on delay ------->|
其中 S
表示 Start
的签发时间,T
表示时间
当环形缓冲区中的位置开始移动时,即 start_time
,
Start
命令所返回的值。P
表示
O
指示放大器完成开启和音频处理的时间
声音。由于O
已超过P
,此处的turn_on_delay
影响了实际
演示文稿。
随着时间的推移,我们可以直观呈现环形缓冲区 C
上的当前位置
示例正在 P
时间在演讲者中展示
超过放大器开启时 O
的时间。
|<--- external delay --->|
|S|--|T|---------|C|-----------------|O|--|P|
|<------- turn on delay ------->|
|<--- external delay --->|
|S|--|T|-----------------------------|O|-|C|---------------------|P|
|<------- turn on delay ------->|
硬件增益控制
硬件增益控制功能报告
为了确定流的增益控制功能(如果尚未
因此,应用会通过流式传输通道发送 GetProperties
消息。
无需随此消息提供任何参数。司机用“
StreamProperties
,包括获取能力等。所有音频流驱动程序
无论视频流是否
硬件能够进行任何增益控制。所有增益值均使用 32 表示,
位浮点数以 dB 表示。
驱动程序在响应这条消息时提供指示当前数据流的值
获得控制能力。当前增益设置使用布尔值表示
指示流是否可以静音,布尔值,用于指明
音频流支持 AGC、最小和最大增益设置以及 gain_step_db
。通过
gain_step_db
表示增益可采用的最小增量
从最小增益值开始进行受控计数。
例如,某个放大器有 5 个增益步长,每个增益步长为 7.5 dB,最大值为 0 dB 增益表示范围为 (-30.0, 0.0),步长为 7.5。 能够在功能上实现连续增益控制的放大器可能会对其 增益步长为 0.0。
无论静音功能如何,固定增益流的驱动程序都必须报告
其最小和最大增益为 (0.0, 0.0)。gain_step_db
在这里没有意义
但驾驶员应报告为 0.0。
设置硬件增益控制级别
为了更改流的当前增益设置,应用会向
SetGain
消息。此消息包含一个参数
GainState
指示要配置的增益参数,包括
应应用到视频流、静音和 AGC 启用。
假设请求有效,驾驶员应将请求舍入为 最接近支持的增益步长。例如,如果流可以控制其增益 频率范围为 -60.0 到 0.0 dB,增益步长为 0.5 dB,则 请求将增益设置为 -33.3 dB,则会导致 -33.5 的增益 。对同一流的增益 -33.2 dB 的请求应该会导致 应用了 -33.0 的增益。
增益状态通知
客户端可以请求流式传输向它们发送
使用 WatchGainState
命令获取状态变化。司机会回复
第一个 |WatchGainState|由客户端发送,并且不会对后续
客户 |WatchGainState|调用,直到增益状态从最近的
被举报。
插头检测
除了发布/取消发布视频流之外, 连接或断开其总线时,视频流可能能够 在任何给定的时间点插入或拔下电源。例如,一组 USB 连接 USB 后,头戴式耳机可能会发布新的输出流, 是“有线网络”从插头检测的角度看问题。其他 USB 音频适配器 具有标准 3.5 毫米留声机插孔的耳机在连接后可能会发布一个输出流 但使用 USB 连接 USB 接口,但在使用 USB 接口时,可以选择 拔下具有 3.5 毫米耳机插孔的模拟设备。
能够查询流的当前插接状态或未插电状态;以及 注册插头状态变化的异步通知(如果支持) 通过插头检测消息进行处理。
插头检测功能
为了确定音频流的插头检测功能(如果尚未这样做)
因此,应用会通过流式传输频道发送 GetProperties
命令。
驾驶员使用包含插头检测功能的 StreamProperties
进行回复
在 plug_detect_capabilities
和其他字段中。
当前定义的有效插头检测功能标志如下:
HARDWIRED
当流硬件被视为 “硬线”。也就是说,视频流会以 但前提是该设备已发布。例如,一组内置于 扬声器、一对 USB 耳机或无插头的可插入音频设备 检测功能。- 如果流硬件既支持又支持,则设置
CAN_ASYNC_NOTIFY
异步检测设备的插头状态是否已更改,并将 如果客户端请求了这些通知,则显示通知消息。
插头状态通知
客户端可以请求流式传输向它们发送
如果 CAN_ASYNC_NOTIFY
WatchPlugState
标志在 StreamProperties
中由驱动程序发送。例如,可驱动
未设置 CAN_ASYNC_NOTIFY
标志,可以随意忽略 WatchPlugState
应用。设置了CAN_ASYNC_NOTIFY
的司机将会回复第一条
|WatchPlugState|由客户端发送,并且不会回复后续客户端
|WatchPlugState|调用,直到插头状态与最近报告的状态不同为止。
数据流目的和关联
环形缓冲区通道
概览
一旦应用成功设置了流的格式,它就会接收 响应是一个表示其连接的新 channel 流式传输到流的环形缓冲区客户端使用环形缓冲区通道来建立 共享内存缓冲区,以及开始和停止播放和捕获音频流数据。
环形缓冲区内容由客户端生成(用于播放)和 驱动程序端(用于录制)。因此,客户端既是播放的提供方,也是使用方 驱动程序是指录制的提供方和播放的使用方。 环形缓冲区内容可以直接由音频硬件消耗或生成,或者 可能会对驱动程序完成的每个样本进行软件处理。
环形缓冲区数据生成从时间点开始以标称速率继续
Start
命令的成功响应。不过请注意
几乎肯定会有某种形式的 FIFO 缓冲区
内存总线与音频硬件之间,这会导致它
流中预读(如果是播放),也可以保留
(在捕获的情况下)。对于客户端来说,请务必查询
开始前
操作,这样它们就知道流的名义推断提前/落后了多远
读取/写入位置,以防止音频干扰。
另请注意,由于系统的共享缓冲区性质,
驱动程序可能会直接从此缓冲区对硬件执行 DMA 操作,
对在非自动
确保缓存连贯地进行回写
将播放数据写入缓冲区,或在读取之前使其缓存失效
捕获的数据。查看以下时间范围内的driver_transfer_bytes
:RingBufferProperties
ring_buffer.fidl
了解环形缓冲区数据传输的说明。
获取共享缓冲区
要发送或接收音频,应用必须先建立共享内存
缓冲区。方法是通过CreateRingBuffer
环形缓冲区通道。此操作只能在环形缓冲区停止时执行。
例如,如果使用 CreateRingBuffer
创建的通道被驱动程序关闭
因为已建立缓冲区且环形缓冲区
它不得停止环形缓冲区,也不得舍弃
现有共享回忆如果应用在请求新的缓冲区之后
在环形缓冲区停止时已经建立了一个缓冲区,那么它必须
现有缓冲区 ii 必须无效,旧的缓冲区现已消失。
请求环形缓冲区时,应用必须指定两个参数:
min_frames
和clock_recovery_notifications_per_ring
。
min_frames
客户端需要为环分配的最小音频帧数 缓冲区。驱动程序可能会增大此缓冲区以满足硬件要求。 客户端必须使用返回的 VMO 大小(以字节为单位)来确定实际的 环形缓冲区的大小。客户端不得假定缓冲区的大小 (由驱动程序确定)与其请求的大小完全一致。驱动程序 必须确保环形缓冲区的大小是音频的整数个数, 帧。
clock_recovery_notifications_per_ring
客户端希望驾驶员接收的位置更新通知的可选数量
每个周期通过环形缓冲区发送,这些通知旨在用于时钟
恢复。驱动程序必须仅在对 WatchClockRecoveryPositionInfo
请求的回复中发送这些内容。
驾驶员应尝试在整个赛场内以均匀的间距放置通知;不过,
不得依赖于完全均匀分布的更新通知间距。
ring_buffer
如果请求成功,驱动程序必须向 具有允许应用映射权限的 VMO 使用 zx_vmar_map 将 VMO 推送到其地址空间, 在播放时读取/写入缓冲区中的数据,或者只是读取 缓冲区中的数据(在捕获的情况下)。
num_frames
如果请求成功,驱动程序还会返回实际的音频帧数
以供其在缓冲区中使用返回的 VMO 大小(如报告所示)
由 zx_vmo_get_size() 上传)不得大于
此帧数(转换为字节时)。此数字可能较大
小于客户端的 min_frames
请求,但不得小于此数字。
启动和停止环形缓冲区
客户端可以使用 Start
和 Stop
请求环形缓冲区启动或停止
命令。尝试启动数据流
已经启动的操作必须视为失败。尝试阻止
即视为已成功。环形缓冲区
共享缓冲区之前,
使用 CreateRingBuffer
操作建立。
成功启动数据流后,驾驶员必须提供最准确的
其硬件开始在
响应的 start_time
字段。此时间戳必须是从时钟中提取的
与
zx_clock_get_monotonic()
系统调用。加上环形缓冲区的 FIFO 深度属性,该时间戳
让应用无需定期
从驱动程序进行位置更新。连同外板延迟时间估算
该时间戳可让应用
在多个视频流中同步音频信息的呈现,甚至
多台设备(前提是外部时间同步协议是
用于同步
单调时间轴
已同步设备的同类群组)。
成功启动数据流后,驱动程序必须保证 在开始响应加入队列之前,系统会先发送通知。 环形缓冲区通道。
成功停止数据流后,驾驶员必须保证 通知会在停止播放后加入环形缓冲区通道 已加入队列。
位置通知
如果客户端通过clock_recovery_notifications_per_ring
CreateRingBuffer
操作,驱动程序将
定期向客户发送更新,告知其当前正式版
或消耗位置。此位置以字节为单位,
于 发送的 RingBufferPositionInfo
结构体的 position
字段
对WatchClockRecoveryPositionInfo
邮件的回复。通过
消息还包含一个 timestamp
字段,其中包含时间(如
zx::time)。WatchClockRecoveryPositionInfo
请求只有在发送 clock_recovery_notifications_per_ring
之后才能发送
GetVmo
函数所指定的值,而 GetVmo
函数已返回。请注意:
这些位置
通知会指明驾驶员在缓冲区中的什么位置消耗或生产了数据
数据,不是标称的播放或捕获位置(有时称为
“写入游标”或“读取光标”)。他们到家的时间
不一定完全均匀,不应用于影响时钟
恢复。不过,对应关系对(timestamp
、position
)
值本身旨在用于恢复
。如果客户端发现驱动程序使用了
环形缓冲区,即客户端写入播放数据的位置,
未定义。客户应增加时钟准备时间,并确保留下来
同样,
音频不应尝试读取超出环形缓冲区中相应点的位置
由驾驶员发送的最新位置通知指示。
驱动程序播放和捕获位置必须始终从环形缓冲区字节 0 开始,
在成功的 Start
命令之后立即运行。当戒指
缓冲区位置到达 VMO 末尾(如
zx_vmo_get_size(...)),环形缓冲区位置
返回零。驱动程序不需要使用或生成
音频帧的整数个数。采用流位置概念的客户端
取决于位置通知。应注意请求足够的
每个响铃通知的数量(至少 2 个)并快速处理通知
这样就不会发生混叠
时钟恢复和同步
收到 AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN
消息后,驾驶员必须
使用包含该设备的时钟域的标识符进行响应。如果
音频设备锁定为本地系统单调时钟,并且不会显示
调整速率的机制,则会返回
0 表示本地 CLOCK_MONOTONIC 域。客户端可以使用
信息(以及 AUDIO_RB_POSITION_NOTIFY
消息),以简化
恢复音频设备时钟的过程。
错误通知
客户端意外终止
如果环形缓冲区控制通道的客户端出于任何原因关闭, 司机必须立即关闭控制通道并关闭环 这样,系统便不会发出或捕获其他音频。司机在 鼓励观众妥善地过渡到静音的方式, 他们必须确保音频流为静音状态,而不是循环播放。一次 无声过渡已完成、与播放或 捕获可以释放并由驱动程序重复使用。
这样,如果播放客户端意外终止,系统会关闭 客户端通道,这会导致音频播放停止而不是继续循环播放。