点对点语音通信(转)
隨著計算機網(wǎng)絡的日益普及,人們通過網(wǎng)絡進行交流顯得越來越重要,于是出現(xiàn)了一系列語音通信的軟件,比如NetMeeting、IPPhone、MediaRing以及VoxPhone等等,但這些軟件都功能完善、相對獨立,不利于集成到自己開發(fā)的軟件中,有時我們也希望將這種語音通信功能集成到自己的軟件中,尤其當一個單位的局域網(wǎng)用戶分散在不同的房間時。本文給出一種靈活、簡單的實現(xiàn)方法,采用基于對話框的方式編程,硬件上只需要一塊雙DMA通道的聲卡(目前的聲卡大多支持雙DMA通道)和一支耳麥,其余全部由軟件編程實現(xiàn)。程序在 Windows98/2000、Visual C++6.0 下編譯通過,在Windows NT 100M 以太網(wǎng)上運行良好。
設計思路
要實現(xiàn)點對點語音通信,原理非常簡單,只要針對一個點實現(xiàn)話音的實時采集、處理、播放,同時能進行可靠的傳送和接收,這樣兩點一連便可通話。對于前者,采用Windows SDK的低層音頻服務比較合適,因為低層音頻服務中的回調(diào)機制為我們提供了很大的方便。當應用程序不斷向設備驅(qū)動程序提供音頻數(shù)據(jù)時,設備驅(qū)動程序控制音頻設備在后臺完成錄音和放音的具體操作,通過回調(diào)機制,我們又可以檢測到什么時候用完一個數(shù)據(jù)塊,并及時傳送下一個數(shù)據(jù)塊,從而保證了聲音的連續(xù),有了這種單機上的實時采集、回放功能后,接下來的工作就是在網(wǎng)絡上傳送話音數(shù)據(jù)。在點對點網(wǎng)絡傳輸方面,選擇面向連接的TCP協(xié)議,TCP傳輸協(xié)議自動處理分組丟失和交付失序問題,這樣我們不用為這些問題操心,只需很好地利用這個連接,在采集話音回放之前一方面將自己的話音傳給網(wǎng)絡,另一方面接收網(wǎng)絡傳來的話音,這樣便實現(xiàn)了點對點語音通信。其結構框圖如下:
具體實現(xiàn)
一、話音的實時采集、處理、回放
首先要介紹一下Windows低層波形音頻數(shù)據(jù)塊結構 WAVEHDR,其聲明如下:
type struct{?
LPSTR lpData; //指向鎖定的數(shù)據(jù)緩沖區(qū)的指針?
DWORD dwBufferLength; //數(shù)據(jù)緩沖區(qū)的大小?
DWORD dwByteRecorded; //錄音時指明緩沖區(qū)中的數(shù)據(jù)量?
DWORD dwUser; //用戶數(shù)據(jù)?
DWORD dwFlag; //提供緩沖區(qū)信息的標志?
DWORD dwLoops; //循環(huán)播放的次數(shù)?
struct wavehdr_tag * lpNext; //保留?
DWORD reserved; //保留?
} WAVEHDR;?
聲音的采集和播放都是在操作這個音頻數(shù)據(jù)塊結構,實際上主要用到的就是第一個成員變量lpData, 所以我們只要在分配緩沖區(qū)(內(nèi)存)的同時相應分配WAVEHDR數(shù)據(jù)塊結構,然后將緩沖區(qū)的指針賦給對應的數(shù)據(jù)塊結構的成員變量 lpData,這樣當一個緩沖區(qū)填滿后,也就是一個音頻數(shù)據(jù)塊填滿了,通過消息機制就可以在消息函數(shù)中進行處理和播放,播放完后又可通過消息函數(shù)把緩沖區(qū)再送給音頻設備輸入驅(qū)動程序,繼續(xù)進行采集并播放,當你一次性分配多個緩沖區(qū)和數(shù)據(jù)塊結構并賦給音頻設備輸入驅(qū)動程序后,至于把哪個緩沖區(qū)填滿,然后再把哪個空緩沖區(qū)賦給設備輸入驅(qū)動程序,不需人為干預,完全由Windows控制,這就是一種用動態(tài)循環(huán)緩沖區(qū)實現(xiàn)話音的實時采集、播放的簡單而巧妙的辦法。實現(xiàn)步驟:
1.初始化操作
①用waveInGetNumDevs()和waveOutGetNumDevs()查看當前系統(tǒng)波形音頻輸入、輸出設備;
②按11025Hz,16Bit,單聲道,22K/S的格式設置WAVEFORMATEX結構的成員變量,也可以改為其他WAVE格式;
③用waveInOpen(...) 和waveOutOpen(...)分別調(diào)用WAVE_FORMAT_QUERY參數(shù)查看波形輸入設備是否支持所設定的格式;
④再次用waveInOpen(...) 和waveOutOpen(...)分別調(diào)用CALLBACK_WINDOW參數(shù)打開波形輸入設備;
⑤分別給音頻數(shù)據(jù)塊和音頻數(shù)據(jù)緩沖區(qū)分配、鎖定全局內(nèi)存;
⑥初始化音頻數(shù)據(jù)塊結構各成員變量,主要是將每個緩沖區(qū)指針賦給對應數(shù)據(jù)塊結構中的緩沖區(qū)指針變量lpData;調(diào)用waveInPrepareHeader(...)和waveInAddBuffer(...)將音頻數(shù)據(jù)塊賦給輸入設備驅(qū)動程序;
⑦調(diào)用waveInStart(...)函數(shù)開始錄音。?
2.消息操作
錄音開始后,每當有采樣數(shù)據(jù)填滿數(shù)據(jù)塊后,設備驅(qū)動程序就會發(fā)消息MM_WIM_DATA給用戶窗口,相應的消息回調(diào)函數(shù)OnMmWimData(...)對數(shù)據(jù)塊中的采樣數(shù)據(jù)進行處理,然后就可以發(fā)送給輸出設備進行回放,每當一個音頻數(shù)據(jù)塊播放完畢,設備驅(qū)動程序又會發(fā)出消息MM_WOM_DONE,相應的消息回調(diào)函數(shù) OnMmWomDone(...)記錄音頻數(shù)據(jù)并經(jīng)必要準備后重新發(fā)送給輸入設備,以準備接收后續(xù)的采樣數(shù)據(jù)。這樣,最初為輸入設備準備的音頻數(shù)據(jù)塊就在消息的控制下,在輸入、輸出設備間循環(huán)使用,無需人為控制實現(xiàn)了實時采集、處理和播放。
當結束通話時要關閉音頻輸入設備,這時音頻設備驅(qū)動程序會發(fā)送MM_WIM_CLOSE消息,可在相應的消息函數(shù)OnMmWimClose(..)中清除賦給輸入、輸出設備的音頻數(shù)據(jù)塊。
TD>?
二、基于TCP協(xié)議的點對點話音傳輸
對于聲音的傳送和接收主要是采用面向連接的TCP協(xié)議,并用Windows Socket進行網(wǎng)絡編程實現(xiàn),但首先要將發(fā)送和接收的函數(shù)接口放在 OnMmWimData(...)函數(shù)中,這樣才能做到采集數(shù)據(jù)塊填滿后被發(fā)送,接收的數(shù)據(jù)收到后被播放。 Windows Socket對于從事過網(wǎng)絡編程的人來說應該不陌生,因為我們要實現(xiàn)點對點通信,所以得把客戶和服務器模式融合為一種模式,讓服務器可以做客戶,客戶也可以做服務器,從而使雙方都有呼叫對方和接受對方呼叫的能力,這只需增加一個監(jiān)聽Socket就行了。一旦呼叫連接建立成功,便在兩個點之間建立了一個數(shù)據(jù)流,即使雙方不講話,每個點也在不停地收、發(fā)數(shù)據(jù),一方有話音自然就隨著這個數(shù)據(jù)流傳給了對方,所以關鍵的問題就是怎樣讀取話音數(shù)據(jù)流,因為TCP提供的流式服務是不保證邊界的,當發(fā)送方想一次發(fā)送 4000個字節(jié)時,調(diào)用語句Send(sBuffer,4000),并不能保證一定能發(fā)送出4000個字節(jié);同樣,接收方準備一次接收發(fā)過來的數(shù)據(jù),調(diào)用語句Receive(rBuffer,4000),也不能保證一定能接收4000個字節(jié),因此實際一次發(fā)送和接收的字節(jié)數(shù)會是1到4000中的任何一個值,最壞的情況是只有1個字節(jié)。相反,如果用Send函數(shù)連續(xù)發(fā)送少量數(shù)據(jù),比如一次發(fā)送400個字節(jié),連續(xù)發(fā)送10次,接收方用Receive函數(shù)可能一次就把這4000個字節(jié)都接收下來了,而為了實現(xiàn)播放,我們希望調(diào)用一次發(fā)送函數(shù)就能把緩沖區(qū)大小的話音數(shù)據(jù)發(fā)送出去,調(diào)用一次接收函數(shù)就能把對方一次發(fā)送的話音數(shù)據(jù)準確接收下來,以便進行播放,所以一種比較簡單實用的辦法,就是利用TCP協(xié)議發(fā)送數(shù)據(jù)時為每個數(shù)據(jù)包加個標志頭:
這個標志頭包含長型(4字節(jié))的話音數(shù)據(jù)量值和一個標志字符串,程序中可為這兩項標志相應設置偏移量,同時也為話音數(shù)據(jù)設置偏移量,通過重載OnReceive(...)進行接收。開始接收前,偏移量都置0,接收開始后先檢測是否收到了4 個字節(jié)的話音數(shù)據(jù)量大小值和字符串標志,如果沒有收到,則通過偏移量來控制將它們準確收到,之后校驗字符串標志的正確性,如果兩項標志都正確收到了,則按收到的數(shù)據(jù)量大小值進行真正的話音數(shù)據(jù)接收,如果在OnReceive函數(shù)中一次調(diào)用Receive(...)接收沒有達到這個值,則采用非阻塞模式,調(diào)用AsyncSelect(...)函數(shù)繼續(xù)接收,直至全部收到,這樣重載 OnReceive(...)接收函數(shù)后就可以一次接收對方發(fā)過來的數(shù)據(jù);同理,我們重載CAsyncSocket 的OnSend函數(shù),也可以實現(xiàn)一次發(fā)完一個緩沖區(qū)中的數(shù)據(jù)。這樣只要將收發(fā)函數(shù)的接口放在OnMmWimData(...)函數(shù)中,使收和發(fā)都產(chǎn)生恒定速率的數(shù)據(jù)流,從而實現(xiàn)了網(wǎng)絡話音的傳輸和回放。
三、界面及其他功能
界面及其他功能也要做一番設計:
1.設置“查找鄰居"項,可查看誰在網(wǎng)上,用ListBox進行顯示,在ListBox選中一位鄰居,對應的鄰居編輯框中就顯示出該鄰居,便于呼叫,“查找鄰居"主要用到的函數(shù)是WnetOpenEnum(...)、WnetEnumResource(...)、 WnetCloseEnum(...)。
2.程序是基于對話框的,運行后不做任何顯示,直接放入系統(tǒng)托盤,直至雙擊托盤圖標或選擇菜單再運行,但可隨時監(jiān)聽是否有呼叫接入,一旦連接建立立刻開啟話音處理功能。
3.每次運行都在注冊表的RunServicesOnce鍵中寫入或修改NetPhone項,使程序能保持開機就自動運行。
4.加入了調(diào)節(jié)麥克和耳機音量的功能。
5.為通話對方提供音樂播放,但只支持與采樣格式相同的.wav文件,其他格式的.wav文件可用Windows的錄音機轉(zhuǎn)換即可
轉(zhuǎn)載于:https://www.cnblogs.com/myitm/archive/2011/07/20/2111227.html
總結
以上是生活随笔為你收集整理的点对点语音通信(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法实现自动扫雷游戏
- 下一篇: js 获取移动端设备类型及系统版本号