用Delphi實現網絡視頻編程


在MSN、QQ等聊天類的應用程序中,都應用到了網絡視頻技術。Delphi使用Object Pascal語言是一種完全面向對象語言,可以開發出靈活強大的程序,開發網絡視頻程序也不在話下。一個完整的網絡視頻程序應包括以下幾個關鍵技術:視頻捕獲、視頻壓縮與解壓、數據傳輸。

一、視頻獲捕

1.基本概念

微軟為軟件開發人員提供了一個專門用於視頻捕獲的VFW (Video for Windows) SDK,為在Windows系統中實現視頻捕獲提供了標准的接口,從而大大方便了視頻捕獲程序的開發。由於VFW SDK只有VC和VB版,沒有Delphi版,因此需要在Delphi中重新聲明DLL中的各個函數和變量(可以參考MSDN中的VC的函數聲明以及變量定義,也可以從網上下載寫好的頭文件vfw.pas)。

VFW是Microsoft 1992年推出的關於數字視頻的一個軟件包,它能使應用程序數字化並播放從傳統模擬視頻源得到的視頻剪輯。VFW的一個關鍵思想是播放時不需要專用硬件,為了解決數字視頻數據量大的問題,需要對數據進行壓縮。它引進了一種叫AVI的文件標准,該標准未規定如何對視頻進行捕獲、壓縮及播放,僅規定視頻和音頻該如何存儲在硬盤上,在AVI文件中交替存儲視頻幀和與之相匹配的音頻數據。VFW給程序員提供VBX和AVICap窗口類的高級編程工具,使程序員能通過發送消息或設置屬性來捕獲、播放和編輯視頻剪輯。

2.AVICap編程

AVICap支持實時的視頻流捕獲和單幀捕獲並提供對視頻源的控制,它能直接訪問視頻緩沖區,不需要生成中間文件,實時性很強,效率很高。同時,它也可將數字視頻捕獲到文件。AVICap編程的基本步驟包括:

第一步,創建AVICap窗口。通過capCreateCaptureWindow函數創建一個捕獲窗,所有的捕獲操作及其設置都以它為基礎。窗口風格一般為WS_CHILD和WS_VISIBLE。

第二步,設置AVICap窗口的相關屬性。通過capPreviewRate函數設置視頻捕獲速率;通過capPreview函數或capOverlay函數設置顯示視頻時的模式(普通的攝像頭不能用overlay的方式);通過capSetVideoFormat函數設置視頻格式(包括長度、寬度等);通過capDlgVideoSource、capDlgVideoFormat、capDlgVideoCompression顯示控制視頻源、視頻格式、視頻壓縮的對話框。

第三步,定義回調函數。定義捕獲幀回調函數,獲得每一幀的數據,並對每一幀的數據進行處理,比如壓縮、傳輸到客戶端等;定義窗口狀態回調函數,獲得窗口的狀態;定義錯誤回調函數,獲得並處理錯誤信息。

3.相關控件

雖然利用上面介紹的API可以實現視頻捕獲編程,但如果將這些API封裝成一個控件則編程更為方便,這樣的控件可以從常用Delphi網站找到。本文以TvideoCap控件為例,實現視頻捕獲。

(1)相關屬性及方法

DriverIndex該屬性是用來指定視頻捕獲設備序號,從0開始。

DriverOpen該屬性是用來確定是否打開指定的視頻捕獲設備。設置為True表示打開,False表示關閉。

CapToFile該屬性是用來確定捕獲的同時是否將捕獲的畫面保存成AVI格式的視頻文件。設置為True表示保存,False表示不保存。

VideoPreview該屬性是確定捕獲的同時是否預覽。設置為True表示預覽,False表示不預覽。

StartCapture該方法是用來捕獲視頻數據,執行該方法后才會觸發相關事件。

其他的屬性和方法這里就不一一介紹。

(2)相關事件

OnVideoStream當捕獲視頻數據時觸發該事件,在這里可以獲得每一幀的數據,進行相關處理,發送到客戶端。

二、視頻壓縮與解壓

通過AVICap窗口捕獲的每一幀的數據是以BMP(RAW)文件格式存放的,若直接進行傳輸,數據量非常大,對網絡的帶寬要求非常高,因此在傳輸之前必須對每一幀的數據進行壓縮處理后再進行傳輸。具體步驟:

第一步,安裝視頻壓縮引擎。媒體播放器軟件都帶有壓縮引擎,也可以從網上下載單獨的解壓縮引擎,比如MPEG4或DIVX等。

第二步,初始化壓縮引擎。選擇壓縮引擎,獲得壓縮引擎的支持,確定輸入、輸出格式,設置壓縮器。

第三步,壓縮幀數據。通過指定的壓縮引擎,對獲取的每一幀數據進行壓縮。

解壓的過程與壓縮的過程類似,通過選擇相對應的解壓引擎,將壓縮的數據解壓,以便於回放。

三、數據傳輸

1.基本概念

計算機在傳輸數據時有兩種方式:分別是TCP(Transmission Control Protocol,傳輸控制協議)及UDP(User Datagram Protocol,用戶數據報協議),兩者分別因數據傳輸的不同請求而提供不同的數據傳輸方式。

(1)  TCP協議

TCP是一個基於連接的通信協議,主要目的是提供大量數據傳輸並確保其傳輸無誤,因此提供錯誤檢查、數據復原及數據重傳等機制。TCP在傳輸數據之前,會先在主機間(例如主控端與被控端)創建連接。根據此連接,數據可在計算機間相互傳輸,即所謂的雙向傳輸模式。

(2)  UDP協議

UDP是一個非連接式的通信協議,主要目的在於傳輸少量的數據。與TCP不同的是,TCP在傳輸之前必須創建連接,而UDP不需要,只要設置計算機間的IP及使用相同的端口,就可以相互傳輸數據。因此UDP只提供單向的數據傳輸,即所謂的單向無連接傳輸模式。

由於UDP不需要先創建連接,節省了TCP創建連接所需的時間,所以適合在主機間進行單向的數據傳輸。由於視頻數據的傳輸對於實時性要求很高,即使傳輸過程中有個別幀的數據有錯,也不會影響整個視頻的效果,故本文將會詳細介紹如何通過UDP實現視頻數據的傳輸。

2.控件及相關內容介紹

在Delphi中對於UDP及TCP都提供了很好的支持,而且將它們封裝起來。開發人員無須知道協議的具體實現細節,而只要使用Delphi提供的TIdUDPServer元件(在Indy Servers頁)即可完成相應的功能。下面我們一起來認識一下這個元件。

(1)  相關屬性

DefaultPort該屬性是用來指定作為客戶端時要打開的端口號,也就是通過該端口來接收數據。

Active該屬性是用來打開指定的端口號,設置為True表示打開端口,False表示關閉端口。

BroadcastEnabled該屬性是用來設置是否用來實現廣播,設置為True表示可以廣播,False表示不能廣播。

(2)  相關事件

OnUDPRead當客戶端收到服務器端發來數據時觸發該事件,通過該事件我們可以取得服務器端發的每一幀的數據,以便在客戶端回放。

除了以上提到的一些屬性及事件外,TIdUDPServer還有一個重要的方法需要了解,那就是SendBuffer,通過該方法可以在服務器端向指定客戶端的指定端口發送數據。

四、具體實現

1.服務器端程序

新建一個項目,並將VFW.PAS包含到USES中。在窗體上加入一個控件TVideoCap,命名為VideoCap1,該控件用於視頻捕獲、顯示;放置一個TIdUDPServer,命名為VideoSender,用於傳輸視頻數據;放置兩個文本框,CilentIP設置客戶端IP,ClientPort設置發送到客戶端的端口;一個復選框CheckSend用來決定是否向客戶端傳輸視頻;放置兩個TButton控件,一個命名為“StartVideo”,用來打開視頻,另一個命名為“Closevideo”,用來關閉視頻,整體布局如圖1所示。

 

 

圖1 服務器端界面

定義發送數據包的結構:

type

  VideoData=record

    buf:array[0..8079] of byte; //壓縮后的視頻數據

    Num:integer;//幀數據過大時,分幾個數據包發送,數據包在這一幀中的編號

    IsLast:boolean;//是否是這一幀的最后一個數據包

end;

定義全局變量:

Var

FCV: TCOMPVARS;//幀壓縮結構

FInInfo: TBitmapInfo;//壓縮時輸入結構

FOutInfo: TBitmapInfo;//壓縮時輸出結構

FoutActSize: DWORD;//壓縮后幀數據的大小

Buffer:^byte; //壓縮后幀數據地址

Buf: array of Byte;// 壓縮后幀數據

主要代碼:

//填充BMP頭結構

procedure TForm1.FillBitmapStruc;

begin

  FillChar(FInInfo.bmiHeader, SizeOf(TBitmapInfoHeader), 0);

  with FInInfo.bmiHeader do

  begin

        biBitCount := 24;

    biCompression := BI_RGB;

    biHeight := 240;

    biPlanes := 1;

    biSize := SizeOf(TBitmapInfoHeader);

    biWidth := 320;

  end;

end;

//初始化壓縮引擎

procedure TForm1.InitCompressor;

begin

  FillChar(FCV, SizeOf(FCV), 0);

  with FCV do

  begin

    dwFlags := ICMF_COMPVARS_VALID;

    cbSize := SizeOf(FCV);

    fccHandler := mmioFOURCC('d','i','v','x');   //選擇壓縮引擎,這里選擇divx

    fccType := ICTYPE_VIDEO;

    hic := ICOpen(ICTYPE_VIDEO,fccHandler, ICMODE_COMPRESS);

    lDataRate := 780;

    lKey := 15;

    lQ :=dword(ICQUALITY_DEFAULT);

    if hic <> 0 then

    begin

      FillChar(FOutInfo, SizeOf(FOutInfo), 0);

      ICCompressGetFormat(hic, @FInInfo, @FOutInfo);

      FInInfo.bmiHeader.biCompression:=BI_RGB;

      FOutInfo.bmiHeader.biCompression:=fccHandler;

      ICSeqCompressFrameStart(@FCV, @FInInfo);

    end;

  end;

end;

//FormOnShow事件

procedure TForm1.FormShow(Sender: TObject);

begin

  FillBitmapStruc;

  InitCompressor;

 //設置VideoCap1的相關屬性

  VideoCap1.DriverIndex:=0;

VideoCap1.CapToFile:=false;

  VideoCap1.DriverOpen:=true;

  videocap1.VideoPreview:=true;

end;

//StartVideoOnClick事件

procedure TForm1.StartVideoClick(Sender: TObject);

begin

VideoCap1.StartCapture;

end;

// VideoCap1OnVideoStream事件

procedure TForm1.VideoCap1VideoStream(sender: TObject; lpVhdr: PVIDEOHDR);

var

KeyFrame:boolean;

  MyVideo:VideoData;

  i:integer;

begin

  if CheckSend.Checked then

  begin

    FOutActSize:=0;

    MyVideo.Num:=0;

    //壓縮幀數據

    Buffer:= ICSeqCompressFrame(@FCV, 0, lpVHdr^.lpData, @KeyFrame, @FOutActSize);

    SetLength(buf,FOutActSize);

    Move(Buffer^, Buf[0], FOutActSize);

    //當幀數據太大時,分幾個數據包發送

    while FOutActSize>8080 do

    begin

      MyVideo.Num:=MyVideo.Num+1;

      MyVideo.IsLast:=false;

      for i:=0 to 8079 do

      begin

       MyVideo.buf[i]:= buf[(MyVideo.Num-1)*8080+i];

      end;

      FOutActSize:=FOutActSize-8080;

      // 向客戶端發送數據包

VideoSender.SendBuffer(CilentIP.text,strtoint(ClientPort.text),MyVideo,sizeof(MyVideo));

    end;

    if FOutActSize<8080 then

    begin

      MyVideo.Num:=MyVideo.Num+1;

      MyVideo.IsLast:=true; //當前幀最后一個數據包

      for i:=0 to FOutActSize  do

      begin

        MyVideo.buf[i]:= buf[(MyVideo.Num-1)*8080+i];

      end;

     //向客戶端發送數據包

VideoSender.SendBuffer(CilentIP.text,strtoint(ClientPort.text),MyVideo,sizeof(MyVideo));

    end;

  end;

  application.ProcessMessages;

end;

//StopVideoOnClick事件

procedure TForm1. StopVideoClick (Sender: TObject);

begin

VideoCap1.StopCapture;

end;

//FormOnClose事件

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  if FCV.hic <> 0 then

  begin

    ICSeqCompressFrameEnd(@FCV);

    ICCompressorFree(@FCV);

    ICClose(FCV.hic);

  end;

end;

2.客戶端程序

新建一個項目,並將VFW.PAS包含到USES中。在窗體上加入一個控件TImage,命名為Image1,該控件用於視頻顯示;放置一個TIdUDPServer,命名為VideoReceiver,用於接收視頻數據,DefaultPot屬性設置為6000,整體布局如圖2所示。

 

 

圖2 客戶器端界面

定義全局變量:

Var

FCV: TCOMPVARS;//幀壓縮結構

FInInfo: TBitmapInfo;//解壓時輸入結構

FOutInfo: TBitmapInfo;//解壓時輸出結構

FoutActSize: DWORD;//壓縮后幀數據的大小

Buf: array of Byte;// 未解壓時幀數據

myout:array[0..230399] of byte;// 解壓后幀數據

主要代碼:

//填充BMP頭結構

procedure TForm1.FillBitmapStruc;

begin

  //略,見服務器端程序

end;

//初始化解壓縮引擎

procedure TForm1.InitDeCompressor;

begin

   //略,見服務器端程序

end;

//FormOnShow事件

procedure TForm1.FormShow(Sender: TObject);

begin

  FillBitmapStruc;

  InitDeCompressor;

  VideoReceiver.Active:=true;

end;

//VideoReceiverOnUDPRead事件

procedure TForm1.VideoReceiverUDPRead(Sender: TObject; AData: TStream;

  ABinding: TIdSocketHandle);

var

RetVal:integer;

  MyVideo:VideoData;

  i:integer;

begin

  RetVal:=-1;

  AData.Position:=0;

  //讀取數據

  AData.ReadBuffer(MyVideo,sizeof(MyVideo));

  if MyVideo.Num=1 then  SetLength(buf,0);

  SetLength(buf,(MyVideo.Num)*8080);

  for i:=0 to 8079 do

  begin

    buf[(MyVideo.Num-1)*8080+i]:=MyVideo.buf[i];

  end;

  if MyVideo.IsLast  then  //當前幀最后一個數據包

  begin

  //解壓數據

    RetVal := ICDeCompress(FCV.hic,0,@FoutInfo.bmiHeader,@Buf[0],@FOutInfo.bmiHeader,@myout);

    if  RetVal= ICERR_OK then

    begin

      //在Image1上畫出一幀圖像

StretchDIBits(Image1.Canvas.Handle,0,0,Image1.Width,Image1.Height,0,0,FOutInfo.bmiHeader.biWidth,FOutInfo.bmiHeader.biHeight,@myout,FOutInfo,0,SRCCOPY );

      Image1.Repaint;

    end;

  end;

end;

//FormOnClose事件

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  //略,同服務器端

end;

到此為止,整個網絡視頻程序就全部實現了。當然本程序的功能還有限,不過只要掌握了網絡視頻的的基本原理及相關知識后,根據需要在本程序的基礎上進行擴充,完全可以成為一個實用的網絡視頻軟件。本程序所有代碼在Delphi7、Windows 2000/XP 及局域網中調試運行通過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM