我的微软WaveOut API存在问题:
edit1:添加了示例项目的链接: edit2:删除链接,它不代表问题
播放一些音频后,当我想终止给定的播放流时,我会调用该函数:
waveOutClose(hWaveOut_);
但是,即使在调用waveOutClose()之后,有时库仍将访问waveOutWrite()先前传递给它的内存,从而导致无效的内存访问。
然后我尝试确保在释放缓冲区之前将所有缓冲区标记为已完成:
PcmPlayback::~PcmPlayback()
{
if(hWaveOut_ == nullptr)
return;
waveOutReset(hWaveOut_); // infinite-loops, never returns
for(auto it = buffers_.begin(); it != buffers_.end(); ++it)
waveOutUnprepareHeader(hWaveOut_, &it->wavehdr_, sizeof(WAVEHDR));
while( buffers_.empty() == false ) // infinite loops
removeCompletedBuffers();
waveOutClose(hWaveOut_);
//Unhandled exception at 0x75629E80 (msvcrt.dll) in app.exe:
// 0xC0000005: Access violation reading location 0xFEEEFEEE.
}
void PcmPlayback::removeCompletedBuffers()
{
for(auto it = buffers_.begin(); it != buffers_.end();)
{
if( it->wavehdr_.dwFlags & WHDR_DONE )
{
waveOutUnprepareHeader(hWaveOut_, &it->wavehdr_, sizeof(WAVEHDR));
it = buffers_.erase(it);
}
else
++it;
}
}
但是,这种情况永远不会发生 - 缓冲区永远不会变空。 wavehdr_.dwFlags == 18还剩下4-5个块(我相信这意味着这些块仍然标记为回放)
如何解决此问题?
@ Martin Schlott(“你能提供将缓冲区写入waveOutWrite的循环吗?”) 它不是一个循环,而是每当我通过网络收到音频数据包时都会调用一个函数:
void PcmPlayback::addData(const std::vector<short> &rhs)
{
removeCompletedBuffers();
if(rhs.empty())
return;
// add new data
buffers_.push_back(Buffer());
Buffer & buffer = buffers_.back();
buffer.data_ = rhs;
ZeroMemory(&buffers_.back().wavehdr_, sizeof(WAVEHDR));
buffer.wavehdr_.dwBufferLength = buffer.data_.size() * sizeof(short);
buffer.wavehdr_.lpData = (char *)(buffer.data_.data());
waveOutPrepareHeader(hWaveOut_, &buffer.wavehdr_, sizeof(WAVEHDR)); // prepare block for playback
waveOutWrite(hWaveOut_, &buffer.wavehdr_, sizeof(WAVEHDR));
}
答案 0 :(得分:2)
如果您不致电
,可能会发生上述行为waveOutUnprepareHeader
到您使用之前使用的每个缓冲区
waveOutClose
flagfield _dwFlags似乎表明缓冲区仍然排队(WHDR_INQUEUE | WHDR_PREPARED)尝试:
waveOutReset
在无准备缓冲之前。
在分析了你的代码之后,我发现了两个与waveOut无关的问题/错误(很有趣,你使用C ++ 11但是最老的媒体界面)。您使用向量作为缓冲区。在某些调用操作期间,矢量被复制!我找到的一个错误是:
typedef std::function<void(std::vector<short>)> CALLBACK_FN;
而不是:
typedef std::function<void(std::vector<short>&)> CALLBACK_FN;
强制矢量的副本。 如果您希望将其主要用作rawbuffer,请尽量避免使用向量。最好使用std :: unique_pointer作为缓冲区指针。
录音机中的回调不受互斥锁监视,也不检查是否已经调用了析构函数。在回调期间(主要是)发生破坏,导致异常。
对于您的测试程序,在归咎于waveOut之前,请返回并使用原始指针和静态回调。你的代码还不错,但第一个bug已经显示,一个小bug会导致不可预测的错误。当您还在std :: array中组织缓冲区时,我会在那里搜索bug。我想,你做了整个缓冲区数组的无意复制,没有准备好错误的缓冲区。
我没有时间深入挖掘,但我想这些都是问题。
答案 1 :(得分:0)
我设法最终找到了我的问题,这是由多个错误和死锁造成的。我将记录这里发生的事情,以便人们将来可以从中学习 当我修复样本中的错误时,我了解到发生了什么:
执行此操作后,示例工作正常,没有显示WHDR_DONE标志的行为从未被标记。
在我的主程序中,该行为是由在以下情况下发生的死锁引起的:
记录器回调:
删除Peer:
当我删除对等体时发生死锁,锁定互斥锁并且记录器回调尝试也获取锁定。