ALSA PCM 时间戳¶
ALSA API 可以提供两种不同的系统时间戳
Trigger_tstamp 是调用 .trigger 回调时获取的系统时间快照。 在一般情况下,此快照由 ALSA 核心获取,但特定的硬件可能具有同步功能,或者相反,可能只能提供具有延迟的正确估计。 在后两种情况下,底层驱动程序负责在最合适和精确的时刻更新 trigger_tstamp。 如果驱动程序提供了具有延迟的精确估计,则应用程序不应仅依赖第一个 trigger_tstamp,而是应更新其内部计算。
tstamp 是在上次事件或应用程序查询期间更新的当前系统时间戳。 差值 (tstamp - trigger_tstamp) 定义了经过的时间。
ALSA API 提供了两个基本信息:avail 和 delay,它们与触发和当前系统时间戳相结合,允许应用程序跟踪环形缓冲区的“满度”和排队样本的数量。
这些不同指针和时间信息的使用取决于应用程序的需求
avail
报告环形缓冲区中可以写入多少数据delay
报告在播放完所有排队的样本后,听到新样本所需的时间。
启用时间戳后,将报告 avail/delay 信息以及系统时间的快照。 应用程序可以从 CLOCK_REALTIME
(包括向后走的 NTP 校正)、CLOCK_MONOTONIC
(NTP 校正但永不向后走)、CLOCK_MONOTIC_RAW
(没有 NTP 校正)中选择,并通过 sw_params 动态更改模式
ALSA API 还提供了一个 audio_tstamp,它反映了音频硬件的不同组件测量的时间流逝。 用 ASCII 艺术表示,可以如下所示(对于播放情况)
--------------------------------------------------------------> time
^ ^ ^ ^ ^
| | | | |
analog link dma app FullBuffer
time time time time time
| | | | |
|< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
|<----------------- delay---------------------->| |
|<----ring buffer length---->|
模拟时间是在播放的最后阶段,尽可能靠近实际传感器时获取的。
链路时间是在 SoC/芯片组的输出端获取的,因为样本被推送到链路上。 如果硬件支持,则可以直接通过样本计数器或挂钟(例如,使用 HDAudio 24MHz 或用于网络解决方案的 PTP 时钟)直接测量链路时间,或者间接估计(例如,使用 USB 中的帧计数器)。
DMA 时间是使用计数器测量的 - 由于 DMA 传输的突发性,通常是所有测量中最不可靠的。
应用程序时间对应于应用程序在写入环形缓冲区后跟踪的时间。
应用程序可以查询硬件功能,通过在 audio_tstamp_config 字段中选择相关设置来定义要报告的音频时间,从而获得时间戳精度的估计值。 它还可以请求在测量中包括到模拟的延迟。 在提供嵌入式 DSP 的平台上,直接访问链路时间非常有趣; 使用专用硬件直接测量链路时间(可能与系统时间同步),无需跟踪内部 DSP 处理时间和延迟。
如果应用程序请求硬件/底层驱动程序不支持的音频时间戳,则类型将被覆盖为 DEFAULT,并且时间戳将基于 hw_pointer 值报告 DMA 时间。
为了向后兼容以前未提供时间戳选择的实现,对于零值的 COMPAT 时间戳类型,结果将默认为播放流的 HDAudio 挂钟以及所有其他情况下的 DMA 时间 (hw_ptr)。
音频时间戳精度可以返回到用户空间,以便做出适当的决策
对于 DMA 时间(默认),可以从更新之间的步长推断传输的粒度,进而提供有关应用程序指针可以安全回退多少的信息。
链路时间可用于使用 (tstamp-trigger_tstamp)/audio_tstamp 比率跟踪音频和系统时间之间的长期漂移,精度有助于定义需要多少平滑/低通滤波。 链路时间可以在启动时重置,也可以按原样报告(后者可用于比较不同流的进度 - 但可能需要挂钟始终运行,并且在空闲期间不回绕)。 如果硬件支持,绝对链路时间也可以用于定义精确的开始时间(正在进行中的补丁)。
在音频时间戳中包含延迟可能与直觉相反,不会增加时间戳的精度,例如,如果编解码器包括可变延迟 DSP 处理或硬件组件链,则延迟通常无法精确得知。
精度以纳秒为单位报告(使用无符号 32 位字),这给出了 4.29 秒的最大精度,足以满足音频应用程序的需求...
由于时间戳需求的性质各异,即使对于单个应用程序,audio_tstamp_config 也可以动态更改。 在 STATUS
ioctl 中,参数是只读的,不允许任何应用程序选择。 为了解决此限制而不影响旧版应用程序,引入了具有读/写参数的新 STATUS_EXT
ioctl。 ALSA-lib 将被修改为使用 STATUS_EXT
并有效地弃用 STATUS
。
ALSA API 一次只允许报告一个音频时间戳。 这是一个有意识的设计决策,从硬件寄存器或 IPC 读取音频时间戳需要时间,读取的时间戳越多,组合测量就越不精确。 为了避免任何解释问题,报告单个(系统、音频)时间戳。 需要不同时间戳的应用程序将需要发出多个查询并执行结果的插值
在某些硬件特定的配置中,系统时间戳由底层音频子系统锁定,并将信息返回给驱动程序。 由于与硬件通信中可能存在的延迟,存在与 avail 和 delay 信息不一致的风险。 为确保应用程序不会混淆,在 snd_pcm_status 结构中添加了一个 driver_timestamp 字段; 此时间戳显示驱动程序在从 STATUS
和 STATUS_EXT
ioctl 返回之前何时将信息放在一起。 在大多数情况下,此 driver_timestamp 将与常规系统 tstamp 相同。
使用 HDAudio 进行时间戳的示例
1. DMA 时间戳,不补偿 DMA+模拟延迟
$ ./audio_time -p --ts_type=1
playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
2. DMA 时间戳,补偿 DMA+模拟延迟
$ ./audio_time -p --ts_type=1 -d
playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
3. 链路时间戳,补偿 DMA+模拟延迟
$ ./audio_time -p --ts_type=2 -d
playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
示例 1 表明,DMA 级的时间戳比实际播放时间提前接近 1 毫秒(作为旁注,这种测量可以帮助定义回退保护措施)。 在示例 2 中补偿 DMA-链路延迟有助于消除硬件缓冲,但信息仍然非常不稳定,误差高达一个样本。 在示例 3 中,使用链路挂钟测量时间戳,时间戳显示出单调的行为和较低的离散度。
示例 3 和 4 是使用 USB 音频类。 示例 3 显示由于缓冲,音频时间和系统时间之间存在较高的偏移。 示例 4 显示了如何通过补偿延迟来暴露 1 毫秒的精度(由于驱动程序使用了帧计数器)。
示例 3:DMA 时间戳,不补偿延迟,增量约为 5 毫秒
$ ./audio_time -p -Dhw:1 -t1
playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
示例 4:DMA 时间戳,补偿延迟,延迟约为 1 毫秒
$ ./audio_time -p -Dhw:1 -t1 -d
playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847