2013-10-21 45 views
1

我有與微軟的waveout的API一個問題:waveOutWrite緩衝區永遠不會返回給應用程序

EDIT1:添加鏈接到樣本項目: EDIT2:去掉鏈接,它不能代表問題

玩一些後音頻,當我想結束一個給定的播放流,我調用該函數:

waveOutClose(hWaveOut_); 

然而,waveOutClose()被調用後,也有時庫仍可以訪問以前waveOutW傳遞給它的內存rite(),導致無效的內存訪問。

我再努力確保標記在所有緩衝區爲釋放緩衝區之前完成:

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; 
} 
} 

然而,這種情況從未發生過 - 緩衝區永遠不會變空。將有4-5塊剩餘的wavehdr_.dwFlags == 18(我相信這意味着塊仍被標記爲在回放中)

如何解決此問題?

@馬丁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

您能否提供將緩衝區寫入waveOutWrite的循環? –

+0

我添加了一個鏈接到一個小項目來演示這個問題,它會運行3秒鐘並崩潰 – aCuria

回答

2

如果你不叫

waveOutUnprepareHeader 

到每一個可能發生的描述的行爲,你使用

之前緩衝區您使用

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已經顯示,一個小錯誤會導致不可預知的錯誤。正如你還將你的緩衝區組織在一個std ::數組中,我會在那裏搜索錯誤。我想,你做了整個緩衝區陣列的無意複製,沒有預料到錯誤的緩衝區。

我沒有時間深入挖掘,但我想這些都是問題所在。

+0

在waveOutClose()不能解決問題之前,在每個緩衝區調用waveOutUnprepareHeader()。我仍然遇到訪問衝突:app.exe中的0x75629E80(msvcrt.dll)未處理的異常:0xC0000005:訪問衝突讀取位置0xFEEEFEEE。 – aCuria

+0

Microsoft的文檔狀態「在設備驅動程序使用數據塊完成後必須調用此函數」,我認爲何時設置了WHDR_DONE標誌? (這不會發生在〜PcmPlayback()) – aCuria

+1

那就對了。 Unpreparing只能與「完成」緩衝區一起工作。嘗試waveOutReset屁股之前我提到它可能延長答案。 –

0

我終於找到了我的問題,它是由多個錯誤和死鎖引起的。我將記錄在這裏發生了什麼,使人們可以從這個在未來 我到發生了什麼事,當我固定樣本中的錯誤避讓學習:waveInClose()在〜錄音機前

  • 調用waveInStop() .cpp
  • 在調用~PcmPlayback中的waveOutClose()之前,等待所有緩衝區都有WHDR_DONE標誌。

這樣做後,樣品的罰款和沒有顯示WHDR_DONE標誌從未被標記的行爲。

  • 我有對象代表每個同行,我的音頻流與
  • 每個對象擁有一個向量:

    在我的主要程序,該行爲是由發生在下列情況下一個死鎖引起的播放類

  • 這個載體由互斥鎖保護的

記錄回調:

  • mutex.lock()
  • 發送音頻數據包到每個對等體。

刪除對等體:

  • mutex.lock()
  • 〜PcmPlayback
  • 等待待標記WHDR_DONE標誌

當我刪除對等體發生死鎖,鎖定互斥鎖,記錄器回調也嘗試獲取鎖。

  • 請注意,這經常會發生,因爲播放緩衝區通常是(〜4 * 20ms),而錄音機的節奏爲20ms。
  • 在~PcmPlayback中,緩衝區永遠不會被標記爲WHDR_DONE,並且任何對WaveOut API的調用都不會返回,因爲WaveOut API正在等待Recorder回調完成,而後者又等待着mutex.lock(),造成死鎖。
相關問題