首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

局域網即時語音通訊實作:多執行緒、循環緩衝區與網路擁塞處理

     編輯:關於VC++

架構概覽

基於 WaveIn/WaveOut API 的錄音/播放,配合多執行緒實現「錄音 → 網路傳輸 → 播放」管線並行,採用循環緩衝區與指標追蹤機制處理網路擁塞與速率不匹配。

核心資料結構

struct CAudioData {
    PBYTE lpdata;   // 動態分配的音訊資料指標
    DWORD dwLength; // 資料長度
};

// 兩個循環隊列:輸入(錄音)與輸出(播放)
CAudioData m_AudioDataIn[InBlocks], m_AudioDataOut[OutBlocks];
int nAudioIn, nSend;      // 錄入、發送指標
int nAudioOut, nReceive;  // 接收、播放指標

指標追蹤機制與擁塞處理

四個指標在循環緩衝區中追逐,形成自我調節的流控:

  • 正常網速:nAudioIn 領先 nSend,nReceive 領先 nAudioOut,穩定循環
  • 網路極快:nSend 追上 nAudioIn → 發送端「空轉」等待新資料;接收端因採樣率相同,nReceive 不會超越 nAudioOut
  • 網路極慢/阻塞:nAudioIn 追上 nSend → 錄音資料**追加**至當前區塊尾部 (不丟失);nAudioOut 追上 nReceive → 播放端「空轉」靜音等待

指標追蹤示意圖1 指標追蹤示意圖2

關鍵執行緒實作

1. 錄音回呼 - OnMM_WIM_DATA

void CRecTestDlg::OnMM_WIM_DATA(UINT wParam, LONG lParam) {
    int nextBlock = (nAudioIn+1) % InBlocks;
    PWAVEHDR pHdr = (PWAVEHDR)lParam;

    if (m_AudioDataIn[nextBlock].dwLength != 0) { // 下一塊未發走,追加至當前塊
        m_AudioDataIn[nAudioIn].lpdata = (PBYTE)realloc(
            m_AudioDataIn[nAudioIn].lpdata,
            pHdr->dwBytesRecorded + m_AudioDataIn[nAudioIn].dwLength);
        CopyMemory(m_AudioDataIn[nAudioIn].lpdata + m_AudioDataIn[nAudioIn].dwLength,
                   pHdr->lpData, pHdr->dwBytesRecorded);
        m_AudioDataIn[nAudioIn].dwLength += pHdr->dwBytesRecorded;
    } else { // 移至下一塊
        nAudioIn = nextBlock;
        m_AudioDataIn[nAudioIn].lpdata = (PBYTE)realloc(0, pHdr->dwBytesRecorded);
        CopyMemory(m_AudioDataIn[nAudioIn].lpdata, pHdr->lpData, pHdr->dwBytesRecorded);
        m_AudioDataIn[nAudioIn].dwLength = pHdr->dwBytesRecorded;
    }
    waveInAddBuffer(hWaveIn, pHdr, sizeof(WAVEHDR)); // 重新排隊緩衝區
}

2. 播放回呼 - OnMM_WOM_DONE

void CRecTestDlg::OnMM_WOM_DONE(UINT wParam, LONG lParam) {
    free(m_AudioDataOut[nAudioOut].lpdata);
    m_AudioDataOut[nAudioOut].lpdata = (PBYTE)malloc(1);
    m_AudioDataOut[nAudioOut].dwLength = 0;
    nAudioOut = (nAudioOut+1) % OutBlocks;

    PWAVEHDR pHdr = (PWAVEHDR)lParam;
    pHdr->lpData = (LPTSTR)m_AudioDataOut[nAudioOut].lpdata;
    pHdr->dwBufferLength = m_AudioDataOut[nAudioOut].dwLength;
    waveOutPrepareHeader(hWaveOut, pHdr, sizeof(WAVEHDR));
    waveOutWrite(hWaveOut, pHdr, sizeof(WAVEHDR));
}

3. 接收執行緒 - Audio_Listen_Thread

  • 建立 Socket 監聽埠 4002
  • 循環:先收 4 位元組長度 → 分配記憶體 → 循環收滿指定位元組 → 更新 nReceive
  • 使用 recSo.Receive 回傳值檢測實際收到位元組數,確保完整接收

4. 發送執行緒 - Audio_Send_Thread

  • 連線至 127.0.0.1:4002
  • 循環檢查 nSend 指向的區塊是否有資料 (dwLength != 0)
  • 先發長度 (4 位元組) → 再循環發送資料內容直到完成 → 釋放區塊記憶體 → 更新 nSend

已知問題與改進方向

  1. waveSetGetVolume 導致單聲道:波形音量控制失衡,需檢查雙聲道設定
  2. 播放端雙緩衝調試失敗:單緩衝勉強可用,需深入分析 WaveOut 回呼時序
  3. 擴充多人會議:發送/接收執行緒邏輯微調即可支援多點

執行畫面

語音通訊程式執行畫面

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved