摘要:目前網上視訊交談軟體、視頻會議軟體、可視IP電話軟體隨處可見,你是否想自己做一個玩玩?其實這類軟體無非是視頻加上網路而建成的。如果熟悉視頻捕捉和網路傳輸技術,根本就難不倒你。微軟為軟體開發人員提供了一個專門用於視頻捕捉的VFW SDK(Video for Windows SDK),用它實現視頻捕捉很簡單,至於網路傳輸嘛,Delphi更是提供了N多種網路組件,隨你用了。本文詳細介紹了如何利用Delphi中開發視訊交談軟體,同時給出了兩個程式樣本。
關鍵詞:Delphi VFW 視頻 視頻會議 視訊交談 Video for Windows
一、引言
我們知道視訊交談軟體的關鍵技術在於採集視頻,並即時傳輸給聊天軟體線上的人。對於視頻的採集,這裡採用微軟公司的關於數位視訊的一個軟體包VFW(Video for Windows)。相信很多人對它都很熟習,VFW能使應用程式通過數字化裝置從傳統的類比視頻源得到數字化的視訊剪輯,VFW的一個關鍵思想是播放時不需要專用硬體。為瞭解決數位視訊資料量大的問題,需要對資料進行壓縮,而VFW引進了AVI的檔案標準。該標準未規定如何對視頻進行捕捉、壓縮及播放,僅規定視頻和音頻該如何儲存在硬碟上及在AVI檔案中交替儲存視訊框架和與之相匹配的音頻資料。通過VFW,開發人員通過發送訊息或設定屬性來捕捉、播放和編輯視訊剪輯。當使用者在安裝VFW時,安裝程式會自動地安裝配置視頻所需要的組件,如裝置驅動程式、視頻壓縮程式等。VFW主要由6個模組組成。VFW功能模組:
AVICAP.DLL 包含執行視頻捕捉的函數,它給AVI檔案的I/O處理和視頻、音訊裝置驅動程式提供一個進階介面
MSVIDEO.DLL 包含一套特殊的DrawDib函數,用來處理螢幕上的視頻操作
MCIAVI.DRV 包括對VFW的MCI命令直譯器的驅動程式
AVIFILE.DLL 包含由標準多媒體I/O(mmio)函數提供的更高的命令,用來訪問.AVI檔案
ICM 壓縮管理器,用於管理的視頻壓縮/解壓縮的編解碼器(Codec)
ACM 音頻壓縮管理器,提供與ICM相似的服務,適用于波形音頻
對於視頻的傳輸,我們使用UDP來傳,因為UDP傳輸速度快,TCP是連線導向的,建立串連時雙方需經過三向交握,資料轉送可靠,FTP、telnet等就是基於TCP的,UDP是面向非串連的,發出資訊不需對方確認,但這樣速度比TCP快,但有可能遺失資料,象SMTP、tftp等就是基於UDP的。另外UDP還支援廣播,UDP廣播兩種,一種是directed broadcast,比如你的網段是192.168.0.X,你就往192.168.0.255發就可以了。另一種是limited broadcast,廣播位址是255.255.255.255
二、視訊交談軟體的開發步驟
2.1 建立捕捉視窗,採集視頻
在進行視頻捕捉之前必需要先建立一個捕捉視窗,並應以此為基礎進行所有的捕捉及設定作業。捕捉視窗可用AVICap視窗類別的"CapCreateCaptureWindow"函數來建立,其視窗風格可設定為WSCHILD和WS_VISIBLE參數。
有了捕捉視窗,我們就可以將視頻流和音頻流捕捉到一個AVI檔案中;動態地同視頻和音頻輸入器件串連或斷開;用Overlay或Preview模式對輸入的視頻流進行即時顯示,設定捕捉速率,顯示控制視頻源、視頻格式及視頻壓縮的對話方塊,建立、儲存或載入調色盤,將映像和相關的調色盤拷貝到剪貼簿,將捕捉的單幀映像儲存到BMP格式檔案中。
2.2 捕捉視窗和驅動程式的關聯
僅僅一個捕捉視窗是不能工作起來的,它必須要與一個裝置相關聯才能取得視頻訊號。用函數CapDriverConnect可使捕捉窗與其裝置驅動程式相關聯。
2.3設定視頻裝置的屬性
通過設定TcaptureParms結構變數的各個成員變數,可以控制裝置的採樣頻率、中斷採樣按鍵、狀態行為。設定好TcaptureParms結構變數後,可以用函CapCaptureSetSetup使設定生效。之後還可以用CapPreviewScale、CapPreviewRate設定預覽的比例與速度,也可以直接使用裝置的預設值。
2.4開啟預覽
利用函數CapOverlay可選擇是否採用疊加模式預覽,以使系統資源佔用小,視頻顯示速度加快。然後用CapPreview啟動預覽功能,這時就可以在螢幕上看到來自網路攝影機的映像了。
2.5使用捕捉窗回呼函數
前的四個步驟就可以建立一個基本的視頻捕捉程式了,如果想自己處理從裝置捕捉到的視頻資料,則要使用捕捉窗回呼函數來處理,比如一幀一幀地獲得視頻資料,也可以以流的方式獲得視頻資料等等。
2.6傳輸視頻流
使用回呼函數可以取得第一幀的資料,我們使用網路技術將資料發給其它機器,其它機品將接收的資料顯示出來。
2.7接收視頻
接收UDP資料,同時將接收到的資料回顯出來,這樣就可以看到遠處傳來的視頻了。
三、用Delphi編寫程式碼
微軟的VFW SDK只有VC和VB版,並沒有Delphi版,不過在網上可以找到VFW.PAS檔案,FW.PAS檔案聲明了調用DLL中的各個函數和變數。(註:原始碼中提供了VFW.PAS檔案)
下面就以Delphi7開發一個網路視訊交談軟體,聊天軟體分兩個程式,一個是視頻採集程式並進行UDP廣播的視訊交談軟體伺服器,另一個是接收UDP廣播程式顯示傳來的視頻資料的視訊交談軟體用戶端。
3.1建立視訊交談軟體伺服器
3.1.1建立一個工程,命名為Project1.dpr,並把VFW.PAS加到USE中
3.1.2在Form1上放置一個Tpanel控制項,該控制項用於顯示視頻。之後再放置兩個Tbutton控制項,一個caption為"開始",另一個Name為"停止",放置一個UDP組件,這裡用indy的IdUDPClient用來傳輸視頻,示:
3.1.3定義全域變數
CapWnd:THandle; //定義捕捉窗控制代碼
CapParms:TcaptureParms; //用於設定裝置屬性的結構變數
BMPINFO:TBitmapInfo; //BMP映像資訊
3.1.4編碼事件代碼
開始按鈕代碼:
CapWnd := capCreateCaptureWindow('我的視窗', WS_VISIBLE or WS_CHILD,//視窗樣式 0, //X座標 0, //Y座標 panel1.Width, //視窗寬 panel1.Height, //視窗高 panel1.handle, //視窗控制代碼 0); //通常為0
if CapWnd = 0 then exit; //定義幀捕捉回呼函數
CapSetCallbackOnFrame(CapWnd,FrameCallBack);
CapParms.dwRequestMicroSecPerFrame:=1;
CapParms.fLimitEnabled:=FALSE;
CapParms.fCaptureAudio:=FALSE;
CapParms.fMCIControl:=FALSE;
CapParms.fYield:=TRUE;
CapParms.vKeyAbort:=VK_ESCAPE;
CapParms.fAbortLeftMouse:=False;
CapParms.fAbortRightMouse:=FALSE; //讓設定生效
CapCaptureSetSetup(capWnd,@CapParms,sizeof(TCAPTUREPARMS));
CapPreviewRate(capWnd,33); //設定預覽視頻的頻率
CapCaptureSequenceNoFile(capWnd); //如果要捕捉視頻流,則要使用函數來指定不組建檔案,不然會自動產生AVI檔案 CapDriverConnect(CapWnd,0); //串連網路攝影機裝置,第二個參數是個序號,當系統中裝有多個顯示驅動程式時,其值分別依次為0到總個數如果有多個網路攝影機,那麼就是0->1->2
capGetVideoFormat(capWnd, @BMPINFO,sizeof(TBitmapInfo)); //取得視頻映像資料頭
CapPreviewScale(capWnd,TRUE); //是否縮放
CapOverlay(capWnd,true); //指定是否使用疊加模式,true為使用,否則為falseCapPreview(capWnd,true);
回呼函數代碼:
var hd:Thandle;
jpg:TJpegImage;
memStream :TMemoryStream;
Bitmap:TBitmap;
begin //將資料顯在Image,
Bitmap:=TBitmap.Create;
Bitmap.Width :=BMPINFO.bmiHeader.biWidth;
// New size of Bitmap
Bitmap.Height:=BMPINFO.bmiHeader.biHeight;
hd:= DrawDibOpen; DrawDibDraw(hd,Bitmap.canvas.handle,0,0,_
BMPINFO.BmiHeader.biwidth,BMPINFO.bmiheader.biheight,_
@BMPINFO.bmiHeader,lpVHdr^.lpData,0,0,BMPINFO.bmiHeader.biWidth,_
BMPINFO.bmiHeader.biheight,0);
DrawDibClose(hd); //發送資料
memStream := TMemoryStream.Create;
jpg := TJpegImage.Create;
jpg.Assign(Bitmap);
jpg.CompressionQuality := 10; //jpg壓縮品質
jpg.JPEGNeeded;
jpg.Compress;
jpg.SaveToStream(memStream);
jpg.Free; //因為UDP資料包有大小限制,這裡如果超出部分,就沒有傳輸,完全可以發幾次發出去
Form1.IdUDPClient1.BroadcastEnabled:=true;//用廣播功能
if memStream.Size>Form1.IdUDPClient1.BufferSize then
//向192.168.0.X網段廣播,連接埠 9001
Form1.IdUDPClient1.SendBuffer('192.168.0.255',9001,memStream.Memory^,Form1.IdUDPClient1.BufferSize) else Form1.IdUDPClient1.SendBuffer('192.168.0.255',9001,memStream.Memory^,memStream.Size);
memStream.Free;
Bitmap.Free;
End;
停止代碼:
capCaptureAbort(CapWnd); //停止捕捉 capDriverDisconnect(CapWnd); //將捕捉窗同磁碟機斷開
完整的視訊交談軟體伺服器代碼:
unit Unit1;
interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,VFW, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient,jpeg;type TForm1 = class(TForm) Panel1: TPanel;
Button1: TButton;
Button2: TButton;
IdUDPClient1: TIdUDPClient;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private { Private declarations }
public { Public declarations }
end;
var Form1: TForm1;
CapWnd:THandle; //定義捕捉窗控制代碼
CapParms:TcaptureParms; //用於設定裝置屬性的結構變數
BMPINFO:TBitmapInfo; //BMP映像資訊
implementation{$R *.dfm}
function FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): LongInt;stdcall;
var hd:Thandle;
jpg:TJpegImage;
memStream :TMemoryStream;
Bitmap:TBitmap;
begin //將資料顯在Image,
Bitmap:=TBitmap.Create;
Bitmap.Width :=BMPINFO.bmiHeader.biWidth;
// New size of Bitmap
Bitmap.Height:=BMPINFO.bmiHeader.biHeight;
hd:= DrawDibOpen;
DrawDibDraw(hd,Bitmap.canvas.handle,0,0,BMPINFO.BmiHeader.biwidth,BMPINFO._
bmiheader.biheight,@BMPINFO.bmiHeader,_
lpVHdr^.lpData,0,0,BMPINFO.bmiHeader.biWidth,BMPINFO.bmiHeader.biheight,0);
DrawDibClose(hd);
//發送資料
memStream := TMemoryStream.Create;
jpg := TJpegImage.Create; jpg.Assign(Bitmap);
jpg.CompressionQuality := 10;
//jpg壓縮品質
jpg.JPEGNeeded;
jpg.Compress;
jpg.SaveToStream(memStream);
jpg.Free;
//因為UDP資料包有大小限制,這裡如果超出部分,就沒有傳輸,完全可以發幾次發出去
Form1.IdUDPClient1.BroadcastEnabled:=true;//用廣播功能
if memStream.Size>Form1.IdUDPClient1.BufferSize then
//向192.168.0.X網段廣播,連接埠 9001
Form1.IdUDPClient1.SendBuffer('192.168.0.255',9001,memStream.Memory^,Form1.IdUDPClient1.BufferSize)
else
Form1.IdUDPClient1.SendBuffer('192.168.0.255',9001,memStream.Memory^,memStream.Size);
memStream.Free;
Bitmap.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
CapWnd := capCreateCaptureWindow('我的視窗', WS_VISIBLE or WS_CHILD,//視窗樣式
0, //X座標
0, //Y座標
panel1.Width, //視窗寬
panel1.Height, //視窗高
panel1.handle, //視窗控制代碼
0); //通常為0
if CapWnd = 0 then exit; //定義幀捕捉回呼函數
CapSetCallbackOnFrame(CapWnd,FrameCallBack);
CapParms.dwRequestMicroSecPerFrame:=1;
CapParms.fLimitEnabled:=FALSE;
CapParms.fCaptureAudio:=FALSE;
CapParms.fMCIControl:=FALSE;
CapParms.fYield:=TRUE;
CapParms.vKeyAbort:=VK_ESCAPE;
CapParms.fAbortLeftMouse:=False;
CapParms.fAbortRightMouse:=FALSE; //讓設定生效
CapCaptureSetSetup(capWnd,@CapParms,sizeof(TCAPTUREPARMS));
CapPreviewRate(capWnd,33); //設定預覽視頻的頻率
CapCaptureSequenceNoFile(capWnd); //如果要捕捉視頻流,則要使用函數來指定不組建檔案,不然會自動產生AVI檔案
CapDriverConnect(CapWnd,0); //串連網路攝影機裝置,第二個參數是個序號,當系統中裝有多個顯示驅動程式時,其值分別依次為0到總個數如果有多個網路攝影機,那麼就是0->1->2 capGetVideoFormat(capWnd, @BMPINFO,sizeof(TBitmapInfo)); //取得視頻映像資料頭
CapPreviewScale(capWnd,TRUE); //是否縮放
CapOverlay(capWnd,true); //指定是否使用疊加模式,true為使用,否則為false CapPreview(capWnd,true);end;procedure TForm1.Button2Click(Sender: TObject);
begin capCaptureAbort(CapWnd); //停止捕捉
capDriverDisconnect(CapWnd); //將捕捉窗同磁碟機斷開
end;
end.
3.2建立視訊交談軟體用戶端
3.2.1建立一個工程,命名為Project2.dpr
3.2.2在程式視窗Form2上放置一個image控制項,該控制項用於接收的映像內容,再放置一個Tbutton控制項,caption為"接收",,放置一個UDPServer組件,這裡用indy的IdUDPServer用來接收網路視頻,示:
接收按鈕代碼:
IdUDPServer1.DefaultPort:=9001; //接收埠 IdUDPServer1.Active:=true; //啟用
IdUDPServer1的UDPRead事件代碼:
var jpg:TJpegImage;begin try jpg := TJpegImage.Create; jpg.LoadFromStream(Adata); Image1.Picture.Bitmap.Assign(jpg); jpg.Free; exceptend;end;
視頻的傳輸是壓縮成JPG進行傳輸的,伺服器端和接收端都用到了jpeg單元,所以use中都要加入jpeg。
完整的視訊交談軟體用戶端代碼:
unit Unit2;
interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer, ExtCtrls,jpeg,IdSocketHandle;
type TForm1 = class(TForm) Image1: TImage;
IdUDPServer1: TIdUDPServer;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure IdUDPServer1UDPRead(Sender: TObject;
AData: TStream; ABinding: TIdSocketHandle);
private { Private declarations }
public
{
Public declarations
}
end;
var Form1: TForm1;
implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
begin
IdUDPServer1.DefaultPort:=9001; //接收埠
IdUDPServer1.Active:=true; //啟用
end;
procedure TForm1.IdUDPServer1UDPRead(Sender: TObject;AData: TStream; ABinding: TIdSocketHandle);
var jpg:TJpegImage;
begin
try
jpg := TJpegImage.Create;
jpg.LoadFromStream(Adata);
Image1.Picture.Bitmap.Assign(jpg);
jpg.Free;
except
end;
end;
end.
好了,到這裡程式碼也就寫完了。在機上運行視訊交談軟體伺服器程式,點開始就開始進行視頻的傳輸了,在網路上(網段為192.168.0.X,根據你的網路設定IP地址,我這用的區域網路測試)的任何一台機上運行視訊交談軟體用戶端點接收都能接收到視頻了。
如果要接收的視頻內容清晰點,可以設定jpg.CompressionQuality:=10;(這個值可以是從1至100,數值越大,映像越清晰,當然傳輸的速度會越慢了,映像越清晰,資料包就會越大,如果超出了UDP包限制,看到映像就不完整了)
視訊交談軟體伺服器
聊天軟體用戶端
四、結束語
在這裡,我把自己的一些經驗和代碼拿出來與大家一起分享,請高手不要扔雞蛋啊,我真的是花了不少力氣的!看了這篇文章後,相信你也可以自己動手做一個網路視訊交談軟體,也可以做個類似MSN、QQ、E話通一樣的視訊交談軟體,有了網路視頻就可以在千裡之外和家人進行可視通訊了。上面的樣本程式還有很多地方需要改進的,比如視頻的壓縮可以用其它視頻壓縮編碼器進行壓縮,這裡只講了傳輸映像,並沒有聲音,再改一下就才能傳輸音視頻了,有興趣的朋友不妨自己動手去試一試。