1

Microsoft の 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));
}
4

2 に答える 2

0

最終的に問題を見つけることができました。これは、複数のバグとデッドロックが原因でした。サンプルのバグを修正したときに何が起こっていたのかを知ることができました。

  • ~Recorder.cpp で waveInClose() の前に waveInStop() を呼び出します
  • ~PcmPlayback で waveOutClose() を呼び出す前に、すべてのバッファーに WHDR_DONE フラグが設定されるのを待ちます。

これを行った後、サンプルは正常に動作し、WHDR_DONE フラグがマークされないという動作は表示されませんでした

私のメイン プログラムでは、次の状況で発生するデッドロックが原因で、この動作が発生しました。

  • オーディオをストリーミングしている各ピアを表すオブジェクトのベクトルがあります
  • 各オブジェクトは Playback クラスを所有しています
  • このベクトルはミューテックスによって保護されています

レコーダー コールバック:

  • ミューテックス.ロック()
  • 音声パケットを各ピアに送信します。

ピアを削除:

  • ミューテックス.ロック()
  • ~PcmPlayback
  • WHDR_DONE フラグがマークされるのを待ちます

ピアを削除するとデッドロックが発生し、mutex がロックされ、レコーダ コールバックもロックを取得しようとします。

  • レコーダーのケイデンスが 20ms であるのに対し、再生バッファーは通常 (~4 * 20ms) であるため、これは頻繁に発生することに注意してください。
  • ~PcmPlayback では、バッファが WHDR_DONE としてマークされることはなく、WaveOut API への呼び出しは返されません。これは、WaveOut API が Recorder コールバックの完了を待っているためです。これは、mutex.lock() を待機しており、デッドロックを引き起こしています。 .
于 2013-10-23T09:26:47.993 に答える