經過幾天的努力終于将vfw視訊采集與顯示功能完整實作了,不得不說網上對這方面完整的詳細講解文章是在太少了。是以就要本人來好好總結一下讓後來者不再像我一樣折騰好久。在本文中我将詳細講解vfw視訊采集過程的實作,以及采集後視訊的顯示方法。
雖然這是篇技術博文,但是我覺得用一個東西,那麼關于它的概述還是不能少,是以特從百度上copy了下vfw的概念描述,如果讀者不想看可以直接去觀看正文部分。
vfw(video for
windows)是microsoft推出的關于數字視訊的一個軟體開發包,vfw的核心是avi檔案标準。avi(audio video
interleave)檔案中的音、視訊交錯存放。圍繞avi檔案,vfw推出了一整套完整的、壓縮、、回放和編輯的(api)。它引進的檔案标準,該标準未規定如何對視訊進行捕獲、壓縮及播放,僅規定視訊和音頻該如何存儲在硬碟上,在avi檔案中交替存儲視訊幀和與之相比對的音頻資料。vfw給提供.vbx和avicap的進階程式設計工具,使程式員能通過發送消息或設定屬性來捕獲、播放和編輯視訊剪輯。現在使用者不必專門安裝vfw了,windows95本身包括了video
for windows1.1,當使用者在安裝windows時,安裝程式會自動地安裝配置視訊所需的元件,如裝置驅動程式、視訊壓縮程式等。 由于avi推出較早且在數字視訊技術中有廣泛的應用,是以vfw仍然有很大的實用價值,而且進一步發展的趨勢。
vfw主要由以下六個子產品組成:
(1)avicap.dll:包含了執行視訊捕獲的函數,它給avi檔案i/o和視訊、音頻裝置提供一個進階接口;
(2)msvideo.dll:用一套特殊的drawdib函數來處理螢幕上的視訊操作;
(3)mciavi.drv:此驅動程式包括對vfw的mci指令的
(4)avifile.dll:支援由标準i/o(mmio)函數提供的更高的指令來通路.avi檔案;
(5)壓縮管理器(icm):管理用于視訊壓縮-的編解碼器(codec);
(6)音頻壓縮管理器acm:提供與icm相似的服務,不同的是它适于波形音頻。
visual c++在支援vfw方面提供有vfw32.lib、 msacm32.lib
、winmm.lib等類似的庫。特别是它提供了功能強大、簡單易行、類似于mciwnd的avicap。avicap為應用程式提供了一個簡單的、基于消息的接口,使之能通路視訊和波形音頻硬體,并能在将捕獲到硬碟上的過程中進行控制
在vc++開發環境中調用vfw和使用其它開發包沒有什麼不同,隻是需要将vfw32.lib檔案加入工程中,但在開放視訊捕捉與壓縮管理程式時需要其它軟體硬體設定。vfw為avi檔案提供了豐富的處理函數和宏定義,avi檔案的特點在于它是典型的檔案,它由、、文本流組成。是以對avi檔案的處理主要是處理檔案流。
關于vfw采集的過程我大緻歸納如下幾個步驟:
一、capcreatecapturewindow 建立視訊采集視窗,注意此處建立的視窗并不是mfc中的視窗。
//建立視訊采集視窗(注意此視窗與我們所說的顯示視窗不同),并設定預覽視窗
//idc_video_local
參數是采集視窗的id,此處直接使用的是顯示視窗的id
m_capwnd =
capcreatecapturewindow(text("my video capture"), ws_child |
ws_visible, 0, 0,
localrect.width(),
localrect.height(),
localwnd->getsafehwnd(), idc_video_local);
二、設定回調函數,在vfw中可以設定的回調函數有以下幾種,可以根據程式需要設定:
1、bool
capsetcallbackoncapcontrol(hwnd, fpproc );
此宏可以設定用于精确控制采集的開始和結束的控制函數,此回調函數原型為:
lresult callback
capcontrolcallback(hwnd hwnd, int nstate );
hwnd參數為第一步建立的采集視窗句柄, nstate
目前capture的狀态,可取值為controlcallback_preroll(等待capture開始)
和controlcallback_capturing(capture正在采集),程式要控制capture的開啟和關閉時通過對目前capture狀态傳回适當的值,當為controlcallback_preroll是傳回true則代表要開啟capture的捕捉,傳回false代表中止capture,當為controlcallback_capturing時,傳回true表示要繼續采集,傳回false表示要停止采集。
2、bool capsetcallbackonerror(hwnd,
fpproc );此宏用于設定當capture采集過程中出錯的時候回報給程式處理的回調函數,回調函數原型為:
caperrorcallback( hwnd hwnd, int
nid, lpcstr lpsz );
hwnd參數為第一步建立的采集視窗句柄,nid
為目前出錯的錯誤id辨別,lpsz
代表出錯原因的一個文本描述内容
3、bool capsetcallbackonframe(hwnd,
fpproc ); 此宏用于設定當capture采集過程中每采集到一幀圖像的時候,回報給程式處理的回調函數,回調函數原型為:
lresult (callback* capvideocallback)
(hwnd hwnd, lpvideohdr lpvhdr);
hwnd參數為第一步建立的采集視窗句柄,lpvhdr
為采集到的一幀資料,lpvideohdr 結構體定義如下:
typedef struct videohdr_tag
{
lpbyte lpdata; //指向采集到的資料buffer
dword dwbufferlength; //buffer的長度
dword dwbytesused; //buffer實際使用的byte數
dword dwtimecaptured; //從開始采集到目前幀采集時經過的毫秒數
dword dwuser; //使用者通過
capsetuserdata
設定的自定義參數
dword dwflags; //目前幀的辨別,可取如下值
/*
vhdr_done done
bit
vhdr_prepared set
if this header has been
prepared
vhdr_inqueue reserved
for
driver
vhdr_keyframe key
frame
*/
dword_ptr dwreserved[4]; //保留給驅動使用的空間
}
videohdr, near *pvideohdr, far * lpvideohdr;
4、bool capsetcallbackonstatus(hwnd, fpproc );
此宏用于設定監控capture狀态改變的回調函數,函數原型為:
lresult callback
capstatuscallback( hwnd hwnd, int
hwnd參數為第一步建立的采集視窗句柄,nid
定義的狀态消息值,關于目前更新的狀态的一個文本描述。
5、bool capsetcallbackonvideostream(hwnd,
fpproc ); 此宏設定當capture采集到一個video buffer(其實也是一幀資料)資料後回報給程式的功能函數,函數原型為:
lresult callback capvideostreamcallback(
hwnd hwnd, lpvideohdr lpvhdr );
hwnd參數為第一步建立的采集視窗句柄,lpvhdr 為采集到的一幀資料,具體意義可以參看第三個
6、bool
capsetcallbackonwavestream(hwnd, fpproc
);此宏設定當采集到一個audio音頻資料buffer時,回報給程式的函數,函數原型為:
lresult callback capwavestreamcallback( hwnd
hwnd, lpwavehdr lpwhdr );
hwnd參數為第一步建立的采集視窗句柄,lpwhdr
為采集到audio音頻資料,其結構體定義如下:
typedef struct {
lpstr lpdata; //指向采集的音頻資料buffer
dword dwbufferlength; //buffer長度
dword dwbytesrecorded; //采集到的資料byte數
dword_ptr dwuser; //使用者通過capsetuserdate自定義的資料
dword dwflags; //
dword dwloops; //播放時長
struct wavehdr_tag
* lpnext;
dword_ptr
reserved; //保留字段
} wavehdr;
7、bool capsetcallbackonyield(hwnd, fpproc );
此宏用于設定一個回調函數當每采集一幀圖像時回報給程式,回調函數原型為:
lresult callback capyieldcallback( hwnd
hwnd );
hwnd參數為第一步建立的采集視窗句柄
三、capgetdriverdescription
獲得目前可用的capture裝置驅動的版本資訊。我們可以通過此函數枚舉目前系統中可用的驅動。方法如下:
bool vfwapi
capgetdriverdescription(
word
wdriverindex,//要擷取的裝置驅動的索引值,取值範圍為0-9
lpstr
lpszname, //指向儲存擷取到的裝置名字buffer
int
cbname,//裝置名字buffer的長度
lpszver,//指向儲存擷取到的裝置版本資訊buffer
cbver //裝置版本資訊buffer長度
);
我們可以通過循環枚舉索引值為 0-9
時函數的傳回值,如果傳回為真則此索引對應的裝置存在,并可以獲得裝置的描述資訊。
四、capdriverconnect 連接配接指定索引值的裝置驅動
五、配置capture采集參數,可設定參數所需函數如下(注意要先連接配接後配置參數):
1、bool
capcapturesetsetup(hwnd, pscapparms, wsize );
用于設定視訊流采集過程的配置參數。hwnd 采集視窗句柄, pscapparms 為配置結構體 captureparms 其結構體定義如下,wsize
為pscapparms結構體的大小:
dword
dwrequestmicrosecperframe;//請求的幀率,預設為66667,即每秒15幀。
bool fmakeuserhitoktocapture; //如果為true,将顯示一個對話框幫助使用者快速地進行捕捉設定,預設為false
uint wpercentdropforerror; //在捕捉過程中允許棄幀的最大百分比
bool fyield; //如果為true,将産生一個背景線程來進行視訊捕捉
dwindexsize; //表示avi檔案最大的索引入口數
uint wchunkgranularity; //以位元組為機關表示avi檔案的大小
bool fusingdosmemory; //未使用
uint wnumvideorequested; //配置設定視訊緩沖區的最大數量
bool fcaptureaudio; //為true,表示音頻被捕捉,預設值依賴于安裝的音頻裝置
uint wnumaudiorequested; //表示配置設定的音頻緩沖區的最大數量
uint vkeyabort; //表示終止捕捉的虛拟鍵
bool fabortleftmouse; //為true,表示單擊滑鼠左鍵停止捕捉
bool fabortrightmouse; //為true,表示單擊滑鼠右鍵停止捕捉
bool flimitenabled; //為true,表示設定捕捉時間限制
uint wtimelimit; //以秒為機關設定捕捉的逾時時間
bool fmcicontrol; //為true,控制mci(媒體裝置接口)相容的視訊源
bool fstepmcidevice; //為true,使用mci裝置使用步進幀進行捕捉,為false,使用mci裝置進行時時捕捉,如果fmcicontrol成員為false,該成員被忽略
dwmcistarttime; //以毫秒為機關辨別mci裝置視訊捕捉序列的起始位置,如果fmcicontrol成員為false,該成員被忽略
dwmcistoptime; //以毫秒為機關辨別mci裝置視訊捕捉序列的停止位置,如果fmcicontrol成員為false,該成員被忽略
bool fstepcaptureat2x; //為true,捕捉的視訊幀使用兩個分辨率,它可以使用軟體在某個分辨率的基礎上改寫像素,将其該為高清晰度的圖像
uint wstepcaptureaverageframes; //在捕捉時每幀圖像使用的時間大小
dwaudiobuffersize; //音頻緩沖區大小
bool fdisablewritecache;//未使用
uint avstreammaster; //确定在寫入avi檔案時,音頻流是否控制時鐘
captureparms;
2、bool capsetvideoformat(
hwnd, psvideoformat, wsize );
設定video每幀圖像的格式,hwnd為采集視窗句柄,psvideoformat為 bitmapinfo 結構體,wsize為bitmapinfo
結構體的大小。
typedef struct tagbitmapinfo
bitmapinfoheader
bmiheader; //位圖圖像格式
rgbquad
bmicolors[1]; //調色闆,
} bitmapinfo;
typedef struct tagbitmapinfoheader{
dword bisize; //bitmapinfoheader
結構體的大小
long biwidth; //圖像的寬度,按像素
long biheight; //圖像的高度,按像素
word biplanes; //裝置的面數,必須設定為1
word bibitcount; //每個像素所包含的bit數
dword bicompression; //圖像的壓縮格式
dword bisizeimage; //圖像的大小
long bixpelspermeter; //圖像的水準分辨率,機關為每米的像素個數
long biypelspermeter; //圖像的垂直分辨率,機關為每米的像素個數
dword biclrused; //圖像所用到的顔色數,為0則為bibitcount對應的數目
dword biclrimportant; //圖像中重要的顔色數,為0則所有顔色都重要
bitmapinfoheader, *pbitmapinfoheader;
3、bool capsetaudioformat(
hwnd, psaudioformat, wsize); 設定采集的音頻資料格式
hwnd為采集視窗句柄,psaudioformat為 waveformatex 或 pcmwaveformat 結構體,wsize為psaudioformat
word wformattag; //音頻資料格式
word nchannels; //音頻的聲道數,為1為單聲道,2為立體聲
nsamplespersec; //每秒的采樣頻率
navgbytespersec; //每秒的資料傳輸頻率
word nblockalign; //一個塊的大小,采樣bit的位元組數乘以聲道數
word wbitspersample; //每個采樣資料的比特數
word cbsize; //一般為0
waveformatex;
word wformattag; //音頻資料格式
nsamplespersec; //每秒的采樣頻率
navgbytespersec; //每秒的資料傳輸頻率
waveformat;
waveformat
wf;
word wbitspersample; //每個采樣資料的比特數
pcmwaveformat;
4、bool capsetscrollpos(
hwnd, lpp );設定視訊幀的客戶區卷軸位置 hwnd 為采集視窗句柄,lpp 為卷軸位置的指針
上面4中參數裝置函數,都有相應的參數擷取函數,在編寫程式時,我們可以先擷取裝置原先設定的參數,然後在修改我們關心的參數在進行設定,相應的擷取函數隻要将對應函數的set換成get就可以了。
六、預覽配置,在設定好預覽後我們運作程式就能在我們capcreatecapturewindow 中指定的視窗中預覽到采集到視訊資料了。
1、bool cappreviewrate(
hwnd, wms ); 設定預覽時的采集頻率, hwnd為采集視窗句柄,wms為設定的頻率
2、bool cappreviewscale(
hwnd, f ); 設定預覽時圖像是否 可伸縮,即根據顯示視窗大小顯示,hwnd為采集視窗句柄,f
為bool值,為true代表圖像可伸縮
3、bool cappreview(
hwnd, f
);設定是否使用預覽模式,hwnd為采集視窗句柄,f為bool值,為true則使用預覽模式
七、一幀圖像的顯示。如果想要處理每一幀采集到的圖像,那麼程式必須調用
capsetcallbackonvideostream或capsetcallbackonframe設定回調函數。在設定的回調函數中将圖像資料發送給圖像顯示的視窗,在這裡要注意的是,我們在設定參數的時候在capsetvideoformat函數中設定的圖像壓縮格式如果為bi_rgb那麼采集的圖像就為rgb資料,rgb的位數為設定的bibitcount。在顯示視窗中進行如下操作:
1、用getdc獲得顯示視窗的顯示裝置句柄
cdc* pdc = showwnd->getdc();
2、建立視窗顯示裝置句柄的相容句柄
cdc
memdc;
memdc.createcompatibledc(pdc);
3、建立一個裝置相關的bitmap
cbitmap bmp;
bmp.createcompatiblebitmap(pdc, bmpinfo.bmiheader.biwidth,
bmpinfo.bmiheader.biheight);
cbitmap *poldbmp =
memdc.selectobject(&bmp);
4、将采集到的位圖資料發送給裝置,然後将其刷在螢幕上
::setdibitstodevice(memdc.getsafehdc(),0, 0, bmpinfo.bmiheader.biwidth,
bmpinfo.bmiheader.biheight, 0, 0,
0,
bmpinfo.bmiheader.biheight, videodate,
&bmpinfo, dib_rgb_colors);
pdc->bitblt(0, 0, wndrect.width()-6,
wndrect.height(), &memdc, 0, 0, srccopy);
八、拍照功能實作。其實所謂的拍照隻是将采集到的一幀圖像資料寫入一個檔案中,隻是在圖像資料前還要加上一些圖像格式和參數的說明參數。具體實作如下:
1、設定圖像檔案參數說明結構體
bitmapfileheader , 和 圖像格式資訊結構體 bitmapinfoheader
typedef struct tagbitmapfileheader
{
word bftype; //圖像的類型,必須為bm即
0x4d42即十進制的19778
dword bfsize; //圖像檔案的大小
即 圖像資料大小 +
54(兩個參數說明結構體的大小)
word bfreserved1; //保留字段必須為0
word bfreserved2; //保留字段必須為0
dword bfoffbits; //從檔案開始到
圖像資料的偏移
} bitmapfileheader, *pbitmapfileheader;
dword bisize; //結構體的大小
long biwidth; //圖像的寬度
long biheight; //圖像的高度
word biplanes; //圖像目标裝置的面數,必須為1
word bibitcount; //每個像素的bit數
dword bicompression; //圖像壓縮格式
dword bisizeimage; //圖像資料大小
long bixpelspermeter; //圖像的水準分辨率
long biypelspermeter; //圖像的垂直分辨率
dword biclrused; //圖像所使用到的顔色數
dword biclrimportant; //圖像中重要的顔色數
} bitmapinfoheader, *pbitmapinfoheader;
注意這兩個結構體的參數一定要根據你采集圖像前設定的圖像格式來設定,或者直接用當時設定的資料。設定好這兩個結構體後就将他們寫入檔案,然後将采集到的圖像資料也寫入檔案就形成了一張照片了。
cfile filepic(picpath_one,
cfile::modecreate|cfile::modewrite);//存放報警圖檔
filepic.write(pbmpfilehd,14);
filepic.write(&m_bmpinfo.bmiheader,
40);
filepic.write(m_pimagetmp->lpdata,
m_pimagetmp->dwbytesused);
九、采集結束,關閉裝置。這個操作 可以通過capsetcallbackoncapcontrol設定的回調函數實作,也可以直接調用
capcapturestop、capcaptureabort停止采集,然後再将原先設定的功能回調函數給關閉,即将函數指針傳遞null值。然後capdriverdisconnect斷開和裝置的連接配接。
筆者自己編寫的一個測試源程式 中下載下傳
videoplay–視訊語音采集完整版.rar
本文,筆者耗時幾個小時認真編寫,如果有什麼錯誤,歡迎讨論。如若轉載請标明出處
from: