程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 在網絡上進行攝像頭視頻通訊

在網絡上進行攝像頭視頻通訊

編輯:Delphi

      筆者序:也許在寫這編文章時,有很多朋友正被老板要求做類似QQ一樣的視頻聊天軟件,在這裡,我把自己的一些經驗和代碼寫出來與大家一起分享,高手不要笑我哈!看了這編文章後,你也可以自己做一個簡單的網絡視頻通訊軟件,如果自己家裡上了網,就可以在公司和家人進行可視通訊了,多爽,不用給電話費了。

        本例子使用的是簡的老技術(VFW),開發起來相對簡單,以下是Delphi代碼,你需要先加入VFW.PAS文件,沒有這個文件你可以在網上找一下。作者從Delphi4就開始編程,其實Delphi可以做很多事情,只是太多Delphi程序員沒有深專技術和思想,沒有超越自己,Delphi只是一個開發工具,代碼思想是的設計的精髓。
        下面讓我們一起來講解一下:

        在程序的開始,你需要用capCreateCaptureWindow來創建一個攝像頭句柄,
        CapWnd := capCreateCaptureWindow('預覽窗口',WS_VISIBLE or WS_CHILD,0,0,320,240,PrevWnd,1);
        在後面的參數:PrevWnd代表預覽窗口的句柄,你可以指定一個Panel的句柄;320和240代表了窗口的長寬。
      
        if CapWnd = 0 then exit;
        capDriverConnect(CapWnd,0);   //連接攝像頭設備

        capDlgVideoFormat(CapWnd);  //顯示視頻設置對話框,進行配置視頻的大小、顏色位數等。
        capGetVideoFormat(CapWnd,@BmpInInfo,sizeof(BITMAPINFO));  //取得視頻圖像數據頭,後面壓縮時需要用到

        capPrevIEwRate(CapWnd, 33);  //設置預覽視頻的頻率,33代表第秒30幀。
        capPrevIEw(CapWnd, TRUE);

        capSetCallbackOnFrame(CapWnd,FrameCallBack); 
       
        InitCaptureParams;
       
        最後一句是設置視頻壓縮參數, 後面會進行說明。其中的capSetCallbackOnFrame(CapWnd,FrameCallBack)是設置每幀視頻數據的回調函數,我們就可以將回調時的視頻數據通過網絡進行傳輸,這樣的就實現了視頻聊天的核心了。

      回調函數如下的格式:

      function  FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): DWord; stdcall;
      var
         bKeyFrame : BOOL ;
         Buf : PBYTE;
         VideoData : TVIDEO_DATA;
         OutActSize : dWord;
         i : integer;
      begin
         OutActSize := BmpInInfo.bmiHeader.biSizeImage;
         Buf := ICSeqCompressFrame(@CapVar,0,lpVHdr.lpData,@bKeyFrame,@OutActSize);
        
         //在這裡, OutActSize代表壓縮後的視頻數據大小
         //  form1.Label3.Caption := 'Compressed size:'+inttostr(OutActSize);

  
         //我用的是UDP方式, 因為UDP數據包大小限制, 所以我控制了數據大小, 超出的數據會發生丟幀
         if (OutActSize <= sizeof(videodata.Buf) ) then
         begin
           zeromemory(@VideoData ,sizeof(TVIDEO_DATA));
          
           //記錄是否為關鍵幀
           VideoData.bKeyFrame:=bKeyFrame;

  
           copymemory(@VideoData.Buf, Buf, OutActSize);
          
           VideoData.SampleNum:=SampleNum; //我們可以記錄下幀數, 可以做擴展用
           VideoData.BufSize:=OutActSize;  //記錄數據大小, 傳輸時用
          
           //在這裡, 你可以用你喜歡的網絡方式傳輸視頻數據,
          
           //cc1.SendBuffer(VideoData,sizeof(TVIDEO_DATA)-SendBufferSize+Outactsize);

           inc(SampleNum);
         end;     
         result := 0;
      end;

      其中,PVIDEOHDR類型可以從VFW中看到其定義:
      TVIDEOHDR               = record
          lpData              : PBYTE;  // 視頻數據buffer
          dwBufferLength      : DWord;  // 數據buffer長度
          dwBytesUsed         : DWord;               
          dwTimeCaptured      : DWord;  // 時間長度(毫秒)
          dwUser              : DWord;               
          dwFlags             : DWord;               
          dwReserved          : array[0..3] of DWord;
      end;
      
      在回調函數中, 只用到了視頻函數: ICSeqCompressFrame,可以看到此函數傳入了CapVar參數,這個參數是由我們先前看到的InitCaptureParams函數產生,下面代碼來實現:
      function InitCaptureParams : boolean;
      begin
        result := False;
   
        //初始化CapVar
        zeromemory(@CapVar,sizeof(TCOMPVARS));

        CapVar.cbSize:=sizeof(CapVar); //必須指定cbSize為TCOMPVARS結構大小
        CapVar.dwFlags:=ICMF_COMPVARS_VALID;

        CapVar.cbState:=0;
   
        //fccHandler代表壓縮編碼類型,我們使用的是DIVX的編碼器
        CapVar.fccHandler:=mmioFOURCC('d','i','v','x');
        CapVar.fccType:=ICTYPE_VIDEO;

       
        //正式連接編碼器
        CapVar.hic:=ICOpen(ICTYPE_VIDEO, CapVar.fccHandler, ICMODE_COMPRESS);
      
        if (CapVar.hic>0) then
        begin

          OutFormatSize:=ICCompressGetFormatSize(CapVar.hic,@BmpInInfo.bmiHeader);
          getmem(BmpOutInfo,OutFormatSize);
          
          //我們可以通過初始化時得到的BmpInInfo來獲取壓縮傳出圖像頭BmpOutInfo
          ICCompressGetFormat(CapVar.hic,@BmpInInfo.bmiHeader,@BmpOutInfo^.bmiHeader);
          OutBufferSize:=ICCompressGetSize(CapVar.hic,@BmpInInfo.bmiHeader,@BmpOutInfo^.bmiHeader);
          ICSeqCompressFrameStart(@CapVar, @BmpInInfo);
          result := True;
        end
        else
        begin
          ShowMsg('請先安裝視頻壓縮編碼器');
          Exit;
        end
      end;

      使用之後,如果要斷開編碼器連接,是這樣調用的:
      if (CapVar.hic > 0) then
      begin
         ICSeqCompressFrameEnd(@CapVar);
         ICCompressorFree(@CapVar);
         ICClose(CapVar.hic);
      end;

      於是,服務端的攝像頭數據捕捉連接就完成了,那麼對於客戶端是乍樣進行視頻數據解壓呢?這個問題當然還是通過IC函數解決,但你必須先把服務端上的BmpOutinfo和CapVar傳輸到客戶端才行。
     
      接著,一起來看看客戶端的圖像顯示過程:
      //先用取得的CapVar來連接視頻編碼器
      CapVar.hic := ICOpen(CapVar.fccType,CapVar.fccHandler,ICMODE_DECOMPRESS);
     
      //成功後,用服務器傳來的BmpOutInfo當作客戶端的BmpInInfo來取得解壓輸出的圖像頭BmpOutInfo

      OutFormatSize:=ICDecompressGetFormatSize(CapVar.hic,@BmpInInfo.bmiHeader);
      GetMem(BmpOutInfo,OutFormatSize);
      zeromemory(BmpOutInfo,OutFormatSize);

      ICDecompressGetFormat(CapVar.hic, @BmpInInfo.bmiHeader, @BmpOutInfo^.bmiHeader);

      OutBufferSize:=BmpOutInfo^.bmiHeader.biSizeImage;
      getmem(OutBuffer,OutBufferSize);

      zeromemory(OutBuffer,OutBufferSize);
      ICDecompressBegin(CapVar.hic,@BmpInInfo.bmiHeader, @BmpOutInfo^.bmiHeader);

  
      最後,當然是視頻數據的解壓過程

      if VIDEO_DATA.bKeyFrame then
         Result:=ICDecompress(CapVar.hic,0,@BmpInInfo,@VIDEO_DATA.Buf,
                     @BmpOutInfo.bmiHeader,OutBuffer)
      else
         Result:=ICDecompress(CapVar.hic,ICDECOMPRESS_NOTKEYFRAME,@BmpInInfo,@VIDEO_DATA.Buf,
                     @BmpOutInfo.bmiHeader,OutBuffer);
      if (Result=ICERR_OK) then
      begin
         SetDIBitsToDevice(Canvas.Handle,0,0,bmptmp.Width,bmptmp.Height,0,0,0,BmpOutInfo^.bmiHeader.biHeight ,
                    OutBuffer,BmpOutInfo^,DIB_RGB_COLORS);
      end;

      這樣,傳送過來的視頻數據變直接畫到了Canvas.Handle上了。
      還忘記了服務端關閉攝像頭的方法,調用capDriverDisconnect(CapWnd) 就OK了。

      全文就Over了,jasonke還要說的就是,這個方法是用的微軟的老函數,不過實現起來很簡單,相信會點API的都能開發出來,還有一種方法當然是用DirectShow了喲,這需要你開發Filter,要搞明白微軟的幾個接口,你可以看看DShowNetwork例子。這個方法也有很多C++的兄弟在痛苦的實現,想一想DirectShow的功能真是強大喲,哈哈。

  

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