程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 一種基於局域網的點對點語音通信

一種基於局域網的點對點語音通信

編輯:關於VC++

引言

隨著計算機網絡的日益普及,人們通過網絡進行交流顯得越來越重要,於是出現了一系列語音通信的軟件,比如NetMeeting、IPPhone、MediaRing以及VoxPhone等等,但這些軟件都功能完善、相對獨立,不利於集成到自己開發的軟件中,有時我們也希望將這種語音通信功能集成到自己的軟件中,尤其當一個單位的局域網用戶分散在不同的房間時。本文給出一種靈活、簡單的實現方法,采用基於對話框的方式編程,硬件上只需要一塊雙DMA通道的聲卡(目前的聲卡大多支持雙DMA通道)和一支耳麥,其余全部由軟件編程實現。程序在 Windows98/2000、Visual C++6.0 下編譯通過,在Windows NT 100M 以太網上運行良好。

設計思路

要實現點對點語音通信,原理非常簡單,只要針對一個點實現話音的實時采集、處理、播放,同時能進行可靠的傳送和接收,這樣兩點一連便可通話。對於前者,采用Windows MDK的低層音頻服務比較合適,因為低層音頻服務中的回調機制為我們提供了很大的方便。當應用程序不斷向設備驅動程序提供音頻數據時,設備驅動程序控制音頻設備在後台完成錄音和放音的具體操作,通過回調機制,我們又可以檢測到什麼時候用完一個數據塊,並及時傳送下一個數據塊,從而保證了聲音的連續,有了這種單機上的實時采集、回放功能後,接下來的工作就是在網絡上傳送話音數據。在點對點網絡傳輸方面,選擇面向連接的TCP協議,TCP傳輸協議自動處理分組丟失和交付失序問題,這樣我們不用為這些問題操心,只需很好地利用這個連接,在采集話音回放之前一方面將自己的話音傳給網絡,另一方面接收網絡傳來的話音,這樣便實現了點對點語音通信。其結構框圖如下:

具體實現

一、話音的實時采集、處理、回放

首先要介紹一下Windows低層波形音頻數據塊結構 WAVEHDR,其聲明如下:

type struct{

LPSTR lpData; //指向鎖定的數據緩沖區的指針

DWORD dwBufferLength; //數據緩沖區的大小

DWORD dwByteRecorded; //錄音時指明緩沖區中的數據量

DWORD dwUser; //用戶數據

DWORD dwFlag; //提供緩沖區信息的標志

DWORD dwLoops; //循環播放的次數

struct wavehdr_tag * lpNext; //保留

DWORD reserved; //保留

} WAVEHDR;

聲音的采集和播放都是在操作這個音頻數據塊結構,實際上主要用到的就是第一個成員變量lpData, 所以我們只要在分配緩沖區(內存)的同時相應分配WAVEHDR數據塊結構,然後將緩沖區的指針賦給對應的數據塊結構的成員變量 lpData,這樣當一個緩沖區填滿後,也就是一個音頻數據塊填滿了,通過消息機制就可以在消息函數中進行處理和播放,播放完後又可通過消息函數把緩沖區再送給音頻設備輸入驅動程序,繼續進行采集並播放,當你一次性分配多個緩沖區和數據塊結構並賦給音頻設備輸入驅動程序後,至於把哪個緩沖區填滿,然後再把哪個空緩沖區賦給設備輸入驅動程序,不需人為干預,完全由Windows控制,這就是一種用動態循環緩沖區實現話音的實時采集、播放的簡單而巧妙的辦法。實現步驟:

1.初始化操作

①用waveInGetNumDevs()和waveOutGetNumDevs()查看當前系統波形音頻輸入、輸出設備;

②按11025Hz,16Bit,單聲道,22K/S的格式設置WAVEFORMATEX結構的成員變量,也可以改為其他WAVE格式;

③用waveInOpen(...) 和waveOutOpen(...)分別調用WAVE_FORMAT_QUERY參數查看波形輸入設備是否支持所設定的格式;

④再次用waveInOpen(...) 和waveOutOpen(...)分別調用CALLBACK_WINDOW參數打開波形輸入設備;

⑤分別給音頻數據塊和音頻數據緩沖區分配、鎖定全局內存;

⑥初始化音頻數據塊結構各成員變量,主要是將每個緩沖區指針賦給對應數據塊結構中的緩沖區指針變量lpData;調用waveInPrepareHeader(...)和waveInAddBuffer(...)將音頻數據塊賦給輸入設備驅動程序;

⑦調用waveInStart(...)函數開始錄音。

2.消息操作

錄音開始後,每當有采樣數據填滿數據塊後,設備驅動程序就會發消息MM_WIM_DATA給用戶窗口,相應的消息回調函數OnMmWimData(...)對數據塊中的采樣數據進行處理,然後就可以發送給輸出設備進行回放,每當一個音頻數據塊播放完畢,設備驅動程序又會發出消息MM_WOM_DONE,相應的消息回調函數 OnMmWomDone(...)記錄音頻數據並經必要准備後重新發送給輸入設備,以准備接收後續的采樣數據。這樣,最初為輸入設備准備的音頻數據塊就在消息的控制下,在輸入、輸出設備間循環使用,無需人為控制實現了實時采集、處理和播放。

當結束通話時要關閉音頻輸入設備,這時音頻設備驅動程序會發送MM_WIM_CLOSE消息,可在相應的消息函數OnMmWimClose(..)中清除賦給輸入、輸出設備的音頻數據塊。

二、基於TCP協議的點對點話音傳輸

對於聲音的傳送和接收主要是采用面向連接的TCP協議,並用Windows Socket進行網絡編程實現,但首先要將發送和接收的函數接口放在 OnMmWimData(...)函數中,這樣才能做到采集數據塊填滿後被發送,接收的數據收到後被播放。 Windows Socket對於從事過網絡編程的人來說應該不陌生,因為我們要實現點對點通信,所以得把客戶和服務器模式融合為一種模式,讓服務器可以做客戶,客戶也可以做服務器,從而使雙方都有呼叫對方和接受對方呼叫的能力,這只需增加一個監聽Socket就行了。一旦呼叫連接建立成功,便在兩個點之間建立了一個數據流,即使雙方不講話,每個點也在不停地收、發數據,一方有話音自然就隨著這個數據流傳給了對方,所以關鍵的問題就是怎樣讀取話音數據流,因為TCP提供的流式服務是不保證邊界的,當發送方想一次發送 4000個字節時,調用語句Send(sBuffer,4000),並不能保證一定能發送出4000個字節;同樣,接收方准備一次接收發過來的數據,調用語句Receive(rBuffer,4000),也不能保證一定能接收4000個字節,因此實際一次發送和接收的字節數會是1到4000中的任何一個值,最壞的情況是只有1個字節。相反,如果用Send函數連續發送少量數據,比如一次發送400個字節,連續發送10次,接收方用Receive函數可能一次就把這4000個字節都接收下來了,而為了實現播放,我們希望調用一次發送函數就能把緩沖區大小的話音數據發送出去,調用一次接收函數就能把對方一次發送的話音數據准確接收下來,以便進行播放,所以一種比較簡單實用的辦法,就是利用TCP協議發送數據時為每個數據包加個標志頭:

這個標志頭包含長型(4字節)的話音數據量值和一個標志字符串,程序中可為這兩項標志相應設置偏移量,同時也為話音數據設置偏移量,通過重載OnReceive(...)進行接收。開始接收前,偏移量都置0,接收開始後先檢測是否收到了4 個字節的話音數據量大小值和字符串標志,如果沒有收到,則通過偏移量來控制將它們准確收到,之後校驗字符串標志的正確性,如果兩項標志都正確收到了,則按收到的數據量大小值進行真正的話音數據接收,如果在OnReceive函數中一次調用Receive(...)接收沒有達到這個值,則采用非阻塞模式,調用AsyncSelect(...)函數繼續接收,直至全部收到,這樣重載 OnReceive(...)接收函數後就可以一次接收對方發過來的數據;同理,我們重載CAsyncSocket 的OnSend函數,也可以實現一次發完一個緩沖區中的數據。這樣只要將收發函數的接口放在OnMmWimData(...)函數中,使收和發都產生恆定速率的數據流,從而實現了網絡話音的傳輸和回放。

三、界面及其他功能

界面及其他功能也要做一番設計:

1.設置“查找鄰居"項,可查看誰在網上,用ListBox進行顯示,在ListBox選中一位鄰居,對應的鄰居編輯框中就顯示出該鄰居,便於呼叫,“查找鄰居"主要用到的函數是WnetOpenEnum(...)、WnetEnumResource(...)、 WnetCloseEnum(...)。

2.程序是基於對話框的,運行後不做任何顯示,直接放入系統托盤,直至雙擊托盤圖標或選擇菜單再運行,但可隨時監聽是否有呼叫接入,一旦連接建立立刻開啟話音處理功能。

3.每次運行都在注冊表的RunServicesOnce鍵中寫入或修改NetPhone項,使程序能保持開機就自動運行。

4.加入了調節麥克和耳機音量的功能。

5.為通話對方提供音樂播放,但只支持與采樣格式相同的.wav文件,其他格式的.wav文件可用Windows的錄音機轉換即可。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved