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 处理时间和延迟。

如果应用程序请求硬件/底层驱动程序不支持的音频 tstamp,则类型将被覆盖为 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 字段; 此时间戳显示驱动程序何时将信息放在一起,然后再从 STATUSSTATUS_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 时间戳,没有延迟补偿,delta 约为 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