C++ waveOutWrite缓冲区永远不会返回到应用程序
我对Microsoft的WaveOut API有问题: edit1:添加到示例项目的链接: edit2:删除链接,它不代表问题 播放一些音频后,当我想终止给定的播放流时,我调用函数:C++ waveOutWrite缓冲区永远不会返回到应用程序,c++,waveout,C++,Waveout,我对Microsoft的WaveOut API有问题: edit1:添加到示例项目的链接: edit2:删除链接,它不代表问题 播放一些音频后,当我想终止给定的播放流时,我调用函数: waveOutClose(hWaveOut_); 但是,即使在调用waveOutClose()之后,有时库仍会访问以前由waveOutWrite()传递给它的内存,从而导致无效的内存访问 然后,在释放缓冲区之前,我尝试确保所有缓冲区都标记为已完成: PcmPlayback::~PcmPlayback() { if
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_u2;.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));
}
void PcmPlayback::addData(const std::vector&rhs)
{
removeCompletedBuffers();
if(rhs.empty())
返回;
//添加新数据
buffers_u.推回(Buffer());
Buffer&Buffer=buffers_.back();
buffer.data=rhs;
零内存(&buffers_.back().wavehdr_,sizeof(wavehdr));
buffer.wavehdr_u2;.dwBufferLength=buffer.data_2;u2;.size()*sizeof(短);
buffer.wavehdr_u2;.lpData=(char*)(buffer.data_2;u2data());
waveOutprepreaheader(hWaveOut_u,&buffer.wavehdr_u,sizeof(wavehdr));//准备播放块
waveOutWrite(hWaveOut_u和buffer.wavehdr_u,sizeof(wavehdr));
}
如果您不调用
waveOutUnprepareHeader
到您使用之前使用的每个缓冲区
waveOutClose
flagfield _dwFlags似乎表示缓冲区仍在排队(WHDR_INQUEUE | WHDR_PREPARED)请尝试:
在未准备缓冲之前
在分析您的代码之后,我发现了两个与waveOut无关的问题/bug(有趣的是,您使用的是C++11,但却是最古老的媒体接口)。使用向量作为缓冲区。在一些调用操作中,向量被复制!我发现的一个bug是:
typedef std::function<void(std::vector<short>)> CALLBACK_FN;
typedef std::函数回调\u FN;
而不是:
typedef std::function<void(std::vector<short>&)> CALLBACK_FN;
typedef std::函数回调\u FN;
这将强制生成向量的副本。
如果您希望将向量主要用作rawbuffer,请尽量避免使用向量。最好使用std::unique_指针作为缓冲区指针
记录器中的回调不受互斥体的监视,也不检查是否已调用析构函数。破坏发生在回调期间(大部分时间),导致异常
对于您的测试程序,在指责waveOut之前,返回并使用原始指针和静态回调。您的代码不错,但第一个bug已经显示,一个小bug将导致不可预测的错误。在std::数组中组织缓冲区时,我会在那里搜索bug。我猜,您无意中复制了整个缓冲区数组,取消了对错误缓冲区的准备
我没有时间深入挖掘,但我想这些就是问题所在。我最终找到了我的问题,它是由多个bug和死锁造成的。我将记录这里发生的事情,以便人们将来可以从中学习 当我修复了样本中的bug时,我了解到发生了什么:
- 在~Recorder.cpp中的waveInClose()之前调用waveInStop()
- 在~PcmPlayback中调用waveOutClose()之前,请等待所有缓冲区都具有WHDR_DONE标志李>
- 我有一个对象向量,代表我正在流式传输音频的每个对等点
- 每个对象都拥有一个播放类
- 此向量受互斥锁保护
- 互斥锁
- 向每个对等方发送音频包
- 互斥锁
- ~PcmPlayback
- 等待标记WHDR_DONE标志
- 请注意,这种情况经常发生,因为播放缓冲区通常为(~4*20ms),而录音机的节奏为20ms李>
- 在~PcmPlayback中,缓冲区永远不会被标记为WHDR_DONE,对WaveOut API的任何调用也永远不会返回,因为WaveOut API正在等待记录器回调完成,而记录器回调又在mutex.lock()上等待,从而导致死锁
typedef std::function<void(std::vector<short>&)> CALLBACK_FN;