【发布时间】:2017-09-28 23:12:56
【问题描述】:
当在非阻塞模式下使用 snd_pcm_writei() 时,一切都可以正常工作一段时间,但最终音频会变得断断续续。听起来环形缓冲区指针不同步(即,有时我可以说音频播放不正常)。问题开始需要多长时间取决于硬件。在真实硬件上的 Gentoo 机器上很少发生这种情况,但在 QEMU 上运行的 buildroot 系统上,它会在大约 5 分钟后发生。在这两种情况下,排出 pcm 流都可以解决问题。我已经通过将它们写入文件并使用 aplay 播放它们来验证我正在正确编写样本。
目前我将avail_min 设置为周期大小(1024 帧)并在写入周期大小的块之前调用 snd_pcm_wait()。但是我尝试了许多不同的变体(不同的块大小,检查自己并使用 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 重置时自行纠正。
【问题讨论】: