c - 音频输出有问题,没有欠载

标签 c linux audio qemu alsa

当在非阻塞模式下使用 snd_pcm_writei() 时,一切都会在一段时间内完美运行,但最终音频会变得断断续续。听起来环形缓冲区指针不同步(即,有时我可以看出音频播放顺序不对)。问题开始需要多长时间取决于硬件。在真实硬件上的 Gentoo box 上,它很少发生,但在 QEMU 上运行的 buildroot 系统上,它会在大约 5 分钟后发生。在这两种情况下,排空 pcm 流都可以解决问题。我已通过将示例写入文件并使用 aplay 播放来验证我正在正确编写示例。

目前我将 avail_min 设置为周期大小(1024 帧)并在写入周期大小的 block 之前调用 snd_pcm_wait()。但我尝试了多种不同的变体(不同的 block 大小,自己检查 avail 并使用 pthread_cond_timedwait() 而不是 snd_pcm_wait() 等)。但唯一能正常工作的是使用阻塞模式,但我不能那样做。

您可以在此处查看当前源代码:https://bitbucket.org/frodzdev/mediabox/src/5a6471316c7ae481b329e7e0d4af1bb68a32e71d/src/audio.c?at=staging&fileviewer=file-view-default (它需要一点清理,因为我正在尝试各种各样的事情)。执行实际 IO 的代码从第 375 行开始。

编辑: 我想我找到了解决方案,但我不明白为什么它似乎有效。似乎我是否使用非阻塞模式并不重要,问题是当我等待以确保缓冲区上有空间时(通过 snd_pcm_wait()、pthread_cond_timedwait() 或 usleep())。

似乎有效的版本在这里:https://bitbucket.org/frodzdev/mediabox/src/c3eb290087d9bbe0d5f37653a33a1ba88ef0628b/src/audio.c?fileviewer=file-view-default .在调用 snd_pcm_writei() 之前,我在等待的同时切换到阻塞模式,但没有任何区别。然后我在调用 avbox_audiostream_gettime() 上的 snd_pcm_status() 之前添加了对 snd_pcm_avail() 的调用。另一个线程不断调用此函数以获取流时钟,它仅使用 snd_pcm_status() 来获取时间戳。现在它似乎起作用了(至少发生的可能性要小得多)但我不明白为什么。我知道 snd_pcm_avail() 会将指针与内核同步,但我真的不明白什么时候需要调用它以及 snd_pcm_state() 等和 snd_pcm_status() 之间的区别。 snd_pcm_status() 是否也同步任何东西?似乎不是,因为有时 snd_pcm_avail() 返回 -EPIPE 时 snd_pcm_status_get_state() 会返回 RUNNING。 ALSA 文档真的很含糊。或许理解这些事情会帮助我理解我的问题?

现在,当我说它似乎可以工作时,我的意思是我无法在真实硬件上重现它。它仍然在 QEMU 上发生,尽管频率较低。但是考虑到在下一次提交时我没有等待就切换到阻塞模式(我过去使用过并且在真实硬件上从未遇到过问题)并且它仍然发生在 QEMU 中并且这是一个常见问题QEMU 我开始认为我可能已经解决了这个问题,现在它只是一个 QEMU 问题。有什么方法可以确定问题是我这边的错误更容易在模拟器上触发,还是只是模拟器问题?

编辑:我意识到我应该在等待之前填充缓冲区,但此时我关心的不是防止欠载运行,而是确保我的代码能够在它们发生时处理它们。此外,缓冲区在几次迭代后被填满。在写入每个数据包之前,我通过输出 avail、buffer_size 等来确认这一点,但我得到的数字并不完全合理,它们大约每 8 个周期显示 1 或 2 个周期的错误。另外(这是主要问题)我没有检测到任何欠载,音频变得断断续续,但所有写入都成功了。事实上,如果问题开始发生并且我通过重载 CPU 触发了欠载运行,它会在 pcm 重置时自行纠正。

最佳答案

line 505 : 您正在使用时间作为 malloc 的参数。

line 568 : 你不是在播放音频吗?在这种情况下,您应该只在编写框架后等待。让我们想想...

音频设备在终止处理一个句点时会产生一个中断。

|  period A  |  period B  |
             ^            ^
            irq          irq

在启动 pcm 之前,音频设备不会产生任何中断。公告here你在等待,但你还没有启动 pcm。只有在调用 snd_pcm_writei() 时才会启动它。

当您等待音频数据时,只有当当前周期已被完全处理时您才会醒来——在您的第一次等待中,第一个周期甚至没有被写入——所以在一个舒适的情况下,您应该写入整个缓冲区,等待第一个中断,然后写入刚刚处理的周期,等等。

Initially, buffer is empty:
  |            |            |
write():
  |############|############|
wait():
  ..............
When we wake up:
  |            |############|
write():
  |############|############|

我发现问题是您正在播放音频之前写入音频,然后有时它可能会延迟到达缓冲区。

关于c - 音频输出有问题,没有欠载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43720778/

相关文章:

c - 在c中返回一个固定大小的数组

python - 将 C 转换为 Python

mysql - 无法连接到 MySQL 服务器错误 111

swift - 声音与UIAlert/UIAlertController

c - 为什么 Matlab 是用 C 而不是 Fortran 编写的?

c - 格式化字符串攻击 - 跳转到 x64 上的 shell

linux - Openstack glance 同步错误

mysql - 只读安装phpmyadmin

c# - 如何显示AudioMeterInformation?

android - 试图将音频嵌入html for android