用户空间驱动的定时器¶
- 作者:
Ivan Orlov <ivan.orlov0322@gmail.com>
前言¶
本文档描述了用户空间驱动的定时器:虚拟 ALSA 定时器,可以使用 IOCTL 调用由用户空间应用程序创建和控制。当将音频流与我们没有导出 ALSA 定时器的定时源(例如,PTP 时钟)同步时,以及当使用 snd-aloop
同步通过两个虚拟声音设备的音频流时(例如,当我们的网络应用程序向一个 snd-aloop 设备发送帧时,而另一个声音应用程序在 snd-aloop 的另一端侦听时),这种定时器可能很有用。
启用用户空间驱动的定时器¶
可以使用 CONFIG_SND_UTIMER
配置选项在内核中启用用户空间驱动的定时器。它依赖于 CONFIG_SND_TIMER
选项,因此也应该启用它。
用户空间驱动的定时器 API¶
用户空间应用程序可以通过在 /dev/snd/timer
设备文件描述符上执行 SNDRV_TIMER_IOCTL_CREATE
ioctl 调用来创建用户空间驱动的 ALSA 定时器。 应将 snd_timer_uinfo
结构作为 ioctl 参数传递。
struct snd_timer_uinfo {
__u64 resolution;
int fd;
unsigned int id;
unsigned char reserved[16];
}
resolution
字段设置虚拟定时器的期望分辨率,以纳秒为单位。resolution
字段只是提供有关虚拟定时器的信息,但不会影响计时本身。id
字段会被 ioctl 覆盖,并且在调用后在此字段中获得的标识符可以用作将定时器传递给 snd-aloop
内核模块或其他用户空间应用程序时的定时器子设备编号。 系统中一次最多可以有 128 个用户空间驱动的定时器,因此 id 值范围从 0 到 127。
除了覆盖 snd_timer_uinfo
结构外,ioctl 还会存储一个定时器文件描述符,该描述符可用于触发定时器,存储在 snd_timer_uinfo
结构的 fd
字段中。 为定时器分配文件描述符可确保该定时器只能由创建它的进程触发。 然后,可以使用定时器文件描述符上的 SNDRV_TIMER_IOCTL_TRIGGER
ioctl 调用来触发定时器。
因此,创建和触发定时器的示例代码如下:
static struct snd_timer_uinfo utimer_info = {
/* Timer is going to tick (presumably) every 1000000 ns */
.resolution = 1000000ULL,
.id = -1,
};
int timer_device_fd = open("/dev/snd/timer", O_RDWR | O_CLOEXEC);
if (ioctl(timer_device_fd, SNDRV_TIMER_IOCTL_CREATE, &utimer_info)) {
perror("Failed to create the timer");
return -1;
}
...
/*
* Now we want to trigger the timer. Callbacks of all of the
* timer instances binded to this timer will be executed after
* this call.
*/
ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);
...
/* Now, destroy the timer */
close(timer_info.fd);
在 utimer ALSA 自检中可以找到创建和定时定时器的更详细示例。
用户空间驱动的定时器和 snd-aloop¶
当在虚拟声音回环的两端同步两个声音应用程序时,可以轻松地将用户空间驱动的定时器与 snd-aloop
模块一起使用。 例如,如果其中一个应用程序从网络接收声音帧并将它们发送到 snd-aloop pcm 设备,而另一个应用程序在另一个 snd-aloop pcm 设备上侦听帧,那么当通过网络接收到新的数据周期时,而不是在经过一定数量的 jiffies 时,ALSA 中间层应该启动数据传输,这是有意义的。 用户空间驱动的 ALSA 定时器可用于实现此目的。
要使用用户空间驱动的 ALSA 定时器作为 snd-aloop 的定时器源,请传递以下字符串作为 snd-aloop timer_source
参数
# modprobe snd-aloop timer_source="-1.4.<utimer_id>"
其中 utimer_id
是你使用 SNDRV_TIMER_IOCTL_CREATE
创建的定时器的 ID,4
是用户空间驱动的定时器设备的编号 (SNDRV_TIMER_GLOBAL_UDRIVEN
)。
与 snd-aloop 一起使用的用户空间驱动的 ALSA 定时器的 resolution
应计算为 1000000000ULL / frame_rate * period_size
,因为定时器将在每次准备好新的帧周期时进行计时。
之后,每次你使用 SNDRV_TIMER_IOCTL_TRIGGER
触发定时器时,新的数据周期将从一个 snd-aloop 设备传输到另一个设备。