音频驱动程序流式传输接口

本文档介绍了音频驱动程序公开的音频流式传输接口。 紫红色的衣服旨在供用户和 驱动程序作者,并明确定义驱动程序的接口协定 且用户必须遵守。

概览

音频流是由驱动程序服务发布的、旨在使用的设备节点 以便在 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.fidlring_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 个音频帧。

外部延迟会在 DelayInfoexternal_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_timeStart 命令所返回的值。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_NOTIFYWatchPlugState 标志在 StreamProperties 中由驱动程序发送。例如,可驱动 未设置 CAN_ASYNC_NOTIFY 标志,可以随意忽略 WatchPlugState 应用。设置了CAN_ASYNC_NOTIFY的司机将会回复第一条 |WatchPlugState|由客户端发送,并且不会回复后续客户端 |WatchPlugState|调用,直到插头状态与最近报告的状态不同为止。

数据流目的和关联

环形缓冲区通道

概览

一旦应用成功设置了流的格式,它就会接收 响应是一个表示其连接的新 channel 流式传输到流的环形缓冲区客户端使用环形缓冲区通道来建立 共享内存缓冲区,以及开始和停止播放和捕获音频流数据。

环形缓冲区内容由客户端生成(用于播放)和 驱动程序端(用于录制)。因此,客户端既是播放的提供方,也是使用方 驱动程序是指录制的提供方和播放的使用方。 环形缓冲区内容可以直接由音频硬件消耗或生成,或者 可能会对驱动程序完成的每个样本进行软件处理。

环形缓冲区数据生成从时间点开始以标称速率继续 Start 命令的成功响应。不过请注意 几乎肯定会有某种形式的 FIFO 缓冲区 内存总线与音频硬件之间,这会导致它 流中预读(如果是播放),也可以保留 (在捕获的情况下)。对于客户端来说,请务必查询 开始前 操作,这样它们就知道流的名义推断提前/落后了多远 读取/写入位置,以防止音频干扰。 另请注意,由于系统的共享缓冲区性质, 驱动程序可能会直接从此缓冲区对硬件执行 DMA 操作, 对在非自动 确保缓存连贯地进行回写 将播放数据写入缓冲区,或在读取之前使其缓存失效 捕获的数据。查看以下时间范围内的driver_transfer_bytesRingBufferProperties ring_buffer.fidl 了解环形缓冲区数据传输的说明。

获取共享缓冲区

要发送或接收音频,应用必须先建立共享内存 缓冲区。方法是通过CreateRingBuffer 环形缓冲区通道。此操作只能在环形缓冲区停止时执行。

例如,如果使用 CreateRingBuffer 创建的通道被驱动程序关闭 因为已建立缓冲区且环形缓冲区 它不得停止环形缓冲区,也不得舍弃 现有共享回忆如果应用在请求新的缓冲区之后 在环形缓冲区停止时已经建立了一个缓冲区,那么它必须 现有缓冲区 ii 必须无效,旧的缓冲区现已消失。

请求环形缓冲区时,应用必须指定两个参数: min_framesclock_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 请求,但不得小于此数字。

启动和停止环形缓冲区

客户端可以使用 StartStop 请求环形缓冲区启动或停止 命令。尝试启动数据流 已经启动的操作必须视为失败。尝试阻止 即视为已成功。环形缓冲区 共享缓冲区之前, 使用 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 函数已返回。请注意: 这些位置 通知会指明驾驶员在缓冲区中的什么位置消耗或生产了数据 数据,不是标称的播放或捕获位置(有时称为 “写入游标”或“读取光标”)。他们到家的时间 不一定完全均匀,不应用于影响时钟 恢复。不过,对应关系对(timestampposition) 值本身旨在用于恢复 。如果客户端发现驱动程序使用了 环形缓冲区,即客户端写入播放数据的位置, 未定义。客户应增加时钟准备时间,并确保留下来 同样, 音频不应尝试读取超出环形缓冲区中相应点的位置 由驾驶员发送的最新位置通知指示。

驱动程序播放和捕获位置必须始终从环形缓冲区字节 0 开始, 在成功的 Start 命令之后立即运行。当戒指 缓冲区位置到达 VMO 末尾(如 zx_vmo_get_size(...)),环形缓冲区位置 返回零。驱动程序不需要使用或生成 音频帧的整数个数。采用流位置概念的客户端 取决于位置通知。应注意请求足够的 每个响铃通知的数量(至少 2 个)并快速处理通知 这样就不会发生混叠

时钟恢复和同步

收到 AUDIO_STREAM_CMD_GET_CLOCK_DOMAIN 消息后,驾驶员必须 使用包含该设备的时钟域的标识符进行响应。如果 音频设备锁定为本地系统单调时钟,并且不会显示 调整速率的机制,则会返回 0 表示本地 CLOCK_MONOTONIC 域。客户端可以使用 信息(以及 AUDIO_RB_POSITION_NOTIFY 消息),以简化 恢复音频设备时钟的过程。

错误通知

客户端意外终止

如果环形缓冲区控制通道的客户端出于任何原因关闭, 司机必须立即关闭控制通道并关闭环 这样,系统便不会发出或捕获其他音频。司机在 鼓励观众妥善地过渡到静音的方式, 他们必须确保音频流为静音状态,而不是循环播放。一次 无声过渡已完成、与播放或 捕获可以释放并由驱动程序重复使用。

这样,如果播放客户端意外终止,系统会关闭 客户端通道,这会导致音频播放停止而不是继续循环播放。