ALSA:无法从欠载运行中恢复,准备失败:管道破裂
ALSA: cannot recovery from underrun, prepare failed: Broken pipe
我正在编写一个程序,从两个单声道ALSA设备中读取并将它们写入一个立体声ALSA设备。
我使用三个线程和乒乓缓冲区来管理它们。两条阅读线索和一条写作线索。它们的配置如下:
// Capture ALSA device
alsaBufferSize = 16384;
alsaCaptureChunkSize = 4096;
bitsPerSample = 16;
samplingFrequency = 24000;
numOfChannels = 1;
block = true;
accessType = SND_PCM_ACCESS_RW_INTERLEAVED;
// Playback device (only list params that are different from above)
alsaBufferSize = 16384 * 2;
numOfChannels = 2;
accessType = SND_PCM_ACCESS_RW_NON_INTERLEAVED;
两个读取线程将分别写入ping缓冲区和pong缓冲区。写入线程将等待两个缓冲区中的任何一个准备就绪,锁定它,从中读取,然后解锁它
但当我运行这个程序时,xrun出现了,无法恢复。
ALSA lib pcm.c:7316:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:7319:(snd_pcm_recover) cannot recovery from underrun, prepare failed: Broken pipe
下面是我写ALSA播放设备的代码:
bool CALSAWriter::writen(uint8_t** a_pOutputBuffer, uint32_t a_rFrames)
{
bool ret = false;
// 1. write audio chunk from ALSA
const snd_pcm_sframes_t alsaCaptureChunkSize = static_cast<snd_pcm_sframes_t>(a_rFrames); //(m_pALSACfg->alsaCaptureChunkSize);
const snd_pcm_sframes_t writenFrames = snd_pcm_writen(m_pALSAHandle, (void**)a_pOutputBuffer, alsaCaptureChunkSize);
if (0 < writenFrames)
{// write succeeded
ret = true;
}
else
{// write failed
logPrint("CALSAWriter WRITE FAILED for writen farmes = %d ", writenFrames);
ret = false;
const int alsaReadError = static_cast<int>(writenFrames);// alsa error is of int type
if (ALSA_OK == snd_pcm_recover(m_pALSAHandle, alsaReadError, 0))
{// recovery succeeded
a_rFrames = 0;// only recovery was done, no write at all was done
}
else
{
logPrint("CALSAWriter: failed to recover from ALSA write error: %s (%i)", snd_strerror(alsaReadError), alsaReadError);
ret = false;
}
}
// 2. check current buffer load
snd_pcm_sframes_t framesInBuffer = 0;
snd_pcm_sframes_t delayedFrames = 0;
snd_pcm_avail_delay(m_pALSAHandle, &framesInBuffer, &delayedFrames);
// round to nearest int, cast is safe, buffer size is no bigger than uint32_t
const int32_t ONE_HUNDRED_PERCENTS = 100;
const uint32_t bufferLoadInPercents = ONE_HUNDRED_PERCENTS *
static_cast<int32_t>(framesInBuffer) / static_cast<int32_t>(m_pALSACfg->alsaBufferSize);
logPrint("write: ALSA buffer percentage: %u, delayed frames: %d", bufferLoadInPercents, delayedFrames);
return ret;
}
其他诊断信息:
02:53:00.465047 log info V 1 [write: ALSA buffer percentage: 75, delayed frames: 4096]
02:53:00.635758 log info V 1 [write: ALSA buffer percentage: 74, delayed frames: 4160]
02:53:00.805714 log info V 1 [write: ALSA buffer percentage: 74, delayed frames: 4152]
02:53:00.976781 log info V 1 [write: ALSA buffer percentage: 74, delayed frames: 4144]
02:53:01.147948 log info V 1 [write: ALSA buffer percentage: 0, delayed frames: 0]
02:53:01.317113 log error V 1 [CALSAWriter WRITE FAILED for writen farmes = -32 ]
02:53:01.317795 log error V 1 [CALSAWriter: failed to recover from ALSA write error: Broken pipe (-32)]
我花了大约3天的时间才找到解决方案。感谢@CL。"写得太迟了"的提示。
问题:
- 线程切换时间不是恒定的
解决方案:
- 在第一次调用"writen"之前插入一个空缓冲区。该缓冲区的时间长度可以是任何值,以避免多线程切换。我把它设置为150毫秒
- 或者你可以将线程优先级设置为高,而我不能这样做。请参阅ALSA:防止演讲者被低估的方法
问题诊断:
事实是:
- "readi"每171ms返回一次(4096/24000=0.171)。读取线程将缓冲区设置为就绪
- 一旦缓冲区准备就绪,"writen"就会在写入线程中被调用。缓冲区被复制到ALSA播放设备。播放设备播放这部分缓冲区需要171ms
- 如果播放设备已经播放完所有缓冲区,并且没有写入新的缓冲区。发生"不足"
这里的真实场景:
- 在0ms时,"readi"开始。171毫秒"readi"结束
- 在172毫秒时(线程切换为1毫秒),"writen"开始。在343ms时,如果没有写入新的缓冲区,将发生"欠载"
- 在171毫秒时,"readi"再次启动。在342ms"readi"结束时
- 此时,线程切换需要2ms。在344ms开始"写入"之前,343ms
当CPU负载很高时,不能保证"线程切换"需要多长时间。这就是为什么您可以在第一次写入时插入一个空缓冲区。并将场景转化为:
- 在0ms时,"readi"开始。171毫秒"readi"结束
- 当172毫秒时(线程切换为1毫秒),"writen"以150毫秒长的缓冲区开始。在493ms时,如果没有写入新的缓冲区,将发生"欠载"
- 在171毫秒时,"readi"再次启动。在342ms"readi"结束时
- 此时,线程切换需要50ms。"writen"从392ms开始,"underrun"根本不会发生
相关文章:
- 如果没有malloc,链表实现将失败
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 视图中的参数推导失败:take_while
- IPC使用多个管道和分支进程来运行Python程序
- 链接到自行创建的dll失败
- 带有特殊路径部分的"std::filesystem::weakly_canonical"失败
- GetShortPathName在网络驱动器上使用中文文件夹时失败
- gcc和c++17的过载解析失败
- 为什么使用 P/Invoke 调用 dll 时,某些计算机中的 LoadLibrary 失败?
- 在WSL:configure_file上对config_file的每次调用都失败:配置文件时出现问题
- 由于管道破裂,提升 asio 发送消息失败
- C 系统调用中的管道失败
- 尝试创建命名管道失败并显示ERROR_IS_SUBSTED
- 在管道中调用时,从 std::cin 加载 pugi::xml_document 失败
- 从管道读取随机失败
- ALSA:无法从欠载运行中恢复,准备失败:管道破裂
- Hadoop管道(wordcount)示例失败(使用Hadoop 0.21.0)
- 如何在简单的 TcpServerSrc 到 TcpServerSink 管道中解决失败的 gstreamer 断言