筆 者序:也許在寫(xiě)這編文章時(shí),有很多朋友正被老板要求做類(lèi)似QQ一樣的***軟件,在這里,我把自己的一些經(jīng)驗(yàn)和代碼寫(xiě)出來(lái)與大家一起分享,高手不要笑我 哈!看了這編文章后,你也可以自己做一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)視頻通訊軟件,如果自己家里上了網(wǎng),就可以在公司和家人進(jìn)行可視通訊了,多爽,不用給電話(huà)費(fèi)了。 本 例子使用的是簡(jiǎn)的老技術(shù)(VFW),開(kāi)發(fā)起來(lái)相對(duì)簡(jiǎn)單,以下是Delphi代碼,你需要先加入VFW.PAS文件,沒(méi)有這個(gè)文件你可以在網(wǎng)上找一下。作者 從Delphi4就開(kāi)始編程,其實(shí)Delphi可以做很多事情,只是太多Delphi程序員沒(méi)有深專(zhuān)技術(shù)和思想,沒(méi)有超越自己,Delphi只是一個(gè)開(kāi)發(fā) 工具,代碼思想是的設(shè)計(jì)的精髓。 下面讓我們一起來(lái)講解一下: 在程序的開(kāi)始,你需要用capCreateCaptureWindow來(lái)創(chuàng)建一個(gè)攝像頭句柄, CapWnd := capCreateCaptureWindow('預(yù)覽窗口',WS_VISIBLE or WS_CHILD,0,0,320,240,PrevWnd,1); 在后面的參數(shù):PrevWnd代表預(yù)覽窗口的句柄,你可以指定一個(gè)Panel的句柄;320和240代表了窗口的長(zhǎng)寬。 if CapWnd = 0 then exit; capDriverConnect(CapWnd,0); //連接攝像頭設(shè)備 capDlgVideoFormat(CapWnd); //顯示視頻設(shè)置對(duì)話(huà)框,進(jìn)行配置視頻的大小、顏色位數(shù)等。 capGetVideoFormat(CapWnd,@BmpInInfo,sizeof(BITMAPINFO)); //取得視頻圖像數(shù)據(jù)頭,后面壓縮時(shí)需要用到 capPreviewRate(CapWnd, 33); //設(shè)置預(yù)覽視頻的頻率,33代表第秒30幀。 capPreview(CapWnd, TRUE); capSetCallbackOnFrame(CapWnd,FrameCallBack); InitCaptureParams; 最后一句是設(shè)置視頻壓縮參數(shù), 后面會(huì)進(jìn)行說(shuō)明。其中的capSetCallbackOnFrame(CapWnd,FrameCallBack)是設(shè)置每幀視頻數(shù)據(jù)的回調(diào)函數(shù),我們就可以將回調(diào)時(shí)的視頻數(shù)據(jù)通過(guò)網(wǎng)絡(luò)進(jìn)行傳輸,這樣的就實(shí)現(xiàn)了***的核心了。 回調(diào)函數(shù)如下的格式: 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代表壓縮后的視頻數(shù)據(jù)大小 // form1.Label3.Caption := 'Compressed size:'+inttostr(OutActSize); //我用的是UDP方式, 因?yàn)閁DP數(shù)據(jù)包大小限制, 所以我控制了數(shù)據(jù)大小, 超出的數(shù)據(jù)會(huì)發(fā)生丟幀 if (OutActSize <= sizeof(videodata.Buf) ) then begin zeromemory(@VideoData ,sizeof(TVIDEO_DATA)); //記錄是否為關(guān)鍵幀 VideoData.bKeyFrame:=bKeyFrame; copymemory(@VideoData.Buf, Buf, OutActSize); VideoData.SampleNum:=SampleNum; //我們可以記錄下幀數(shù), 可以做擴(kuò)展用 VideoData.BufSize:=OutActSize; //記錄數(shù)據(jù)大小, 傳輸時(shí)用 //在這里, 你可以用你喜歡的網(wǎng)絡(luò)方式傳輸視頻數(shù)據(jù), //cc1.SendBuffer(VideoData,sizeof(TVIDEO_DATA)-SendBufferSize+Outactsize); inc(SampleNum); end; result := 0; end; 其中,PVIDEOHDR類(lèi)型可以從VFW中看到其定義: TVIDEOHDR = record lpData : PBYTE; // 視頻數(shù)據(jù)buffer dwBufferLength : DWORD; // 數(shù)據(jù)buffer長(zhǎng)度 dwBytesUsed : DWORD; dwTimeCaptured : DWORD; // 時(shí)間長(zhǎng)度(毫秒) dwUser : DWORD; dwFlags : DWORD; dwReserved : array[0..3] of DWORD; end; 在回調(diào)函數(shù)中, 只用到了視頻函數(shù): ICSeqCompressFrame,可以看到此函數(shù)傳入了CapVar參數(shù),這個(gè)參數(shù)是由我們先前看到的InitCaptureParams函數(shù)產(chǎn)生,下面代碼來(lái)實(shí)現(xiàn): function InitCaptureParams : boolean; begin result := False; //初始化CapVar zeromemory(@CapVar,sizeof(TCOMPVARS)); CapVar.cbSize:=sizeof(CapVar); //必須指定cbSize為T(mén)COMPVARS結(jié)構(gòu)大小 CapVar.dwFlags:=ICMF_COMPVARS_VALID; CapVar.cbState:=0; //fccHandler代表壓縮編碼類(lèi)型,我們使用的是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); //我們可以通過(guò)初始化時(shí)得到的BmpInInfo來(lái)獲取壓縮傳出圖像頭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('請(qǐng)先安裝視頻壓縮編碼器'); Exit; end end; 使用之后,如果要斷開(kāi)編碼器連接,是這樣調(diào)用的: if (CapVar.hic > 0) then begin ICSeqCompressFrameEnd(@CapVar); ICCompressorFree(@CapVar); ICClose(CapVar.hic); end; 于是,服務(wù)端的攝像頭數(shù)據(jù)捕捉連接就完成了,那么對(duì)于客戶(hù)端是乍樣進(jìn)行視頻數(shù)據(jù)解壓呢?這個(gè)問(wèn)題當(dāng)然還是通過(guò)IC函數(shù)解決,但你必須先把服務(wù)端上的BmpOutinfo和CapVar傳輸?shù)娇蛻?hù)端才行。 接著,一起來(lái)看看客戶(hù)端的圖像顯示過(guò)程: //先用取得的CapVar來(lái)連接視頻編碼器 CapVar.hic := ICOpen(CapVar.fccType,CapVar.fccHandler,ICMODE_DECOMPRESS); //成功后,用服務(wù)器傳來(lái)的BmpOutInfo當(dāng)作客戶(hù)端的BmpInInfo來(lái)取得解壓輸出的圖像頭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); 最后,當(dāng)然是視頻數(shù)據(jù)的解壓過(guò)程 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; 這樣,傳送過(guò)來(lái)的視頻數(shù)據(jù)變直接畫(huà)到了Canvas.Handle上了。 還忘記了服務(wù)端關(guān)閉攝像頭的方法,調(diào)用capDriverDisconnect(CapWnd) 就OK了。 全 文就Over了,jasonke還要說(shuō)的就是,這個(gè)方法是用的微軟的老函數(shù),不過(guò)實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,相信會(huì)點(diǎn)API的都能開(kāi)發(fā)出來(lái),還有一種方法當(dāng)然是用 DirectShow了喲,這需要你開(kāi)發(fā)Filter,要搞明白微軟的幾個(gè)接口,你可以看看DShowNetwork例子。 網(wǎng)中戲 發(fā)表于 2006-7-27 12:25:42 廖長(zhǎng)科 在關(guān)鍵的回調(diào)函數(shù)一筆帶過(guò)、不過(guò)可以參考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代表壓縮后的視頻數(shù)據(jù)大小 // form1.Label3.Caption := 'Compressed size:'+inttostr(OutActSize); //我用的是UDP方式, 因?yàn)閁DP數(shù)據(jù)包大小限制, 所以我控制了數(shù)據(jù)大小, 超出的數(shù)據(jù)會(huì)發(fā)生丟幀 if (OutActSize <= sizeof(videodata.Buf) ) then begin zeromemory(@VideoData ,sizeof(TVIDEO_DATA)); //記錄是否為關(guān)鍵幀 VideoData.bKeyFrame:=bKeyFrame; copymemory(@VideoData.Buf, Buf, OutActSize); VideoData.SampleNum:=SampleNum; //我們可以記錄下幀數(shù), 可以做擴(kuò)展用 VideoData.BufSize:=OutActSize; //記錄數(shù)據(jù)大小, 傳輸時(shí)用 //在這里, 你可以用你喜歡的網(wǎng)絡(luò)方式傳輸視頻數(shù)據(jù), //cc1.SendBuffer(VideoData,sizeof(TVIDEO_DATA)-SendBufferSize+Outactsize); inc(SampleNum); end; result := 0; end; 原文來(lái)自: http://www.cnblogs.com/python001/ 【100腳本網(wǎng) 】 |
|
來(lái)自: 昵稱(chēng)22130903 > 《delphi》