摘要:目前網上視頻聊天軟件、視頻會議軟件、可視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包限制,看到圖像就不完整了)
http://blog.sina.com.cn/s/blog_56234909010103tr.html