3.2使用視頻捕獲
3.2.1創(chuàng)建捕獲窗體
下面的例子通過使用capCreateCaptureWindow函數(shù)來創(chuàng)建一個捕獲窗體
hWndC = capCreateCaptureWindow (
(LPSTR) "My Capture Window", // 如果是Pop-up窗口的窗口名稱
WS_CHILD | WS_VISIBLE, // 窗口類型
0, 0, 160, 120, // 窗口位置和尺寸
(HWND) hwndParent,
(int) nID /* child ID */);
3.2.2連接到一個捕獲驅(qū)動器
下面舉例,如何通過捕獲窗口的句柄hWndC連接到MS VIDEO驅(qū)動程序上,同時還演示了如何斷開連接。使用capDriverDisconnect:
fOK = SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L);
// 或者使用宏連接:
// fOK = capDriverConnect(hWndC, 0);
// 關(guān)閉連接
capDriverDisconnect (hWndC);
3.2.3列舉安裝的捕獲驅(qū)動程序
使用capGetDriverDescription 函數(shù)來獲得系統(tǒng)已經(jīng)安裝的所有捕獲驅(qū)動程序的名稱和版本。
char szDeviceName[80];
char szDeviceVersion[80];
for (wIndex = 0; wIndex < 10; wIndex++)
{
if (capGetDriverDescription (wIndex, szDeviceName,
sizeof (szDeviceName), szDeviceVersion,
sizeof (szDeviceVersion))
{
// 加入名字到一個已經(jīng)安裝的設(shè)備列表中
// 讓用戶選擇一個使用。
}
}
3.2.4獲得捕獲驅(qū)動器的性能參數(shù)
WM_CAP_DRIVER_GET_CAPS消息可以返回捕獲驅(qū)動程序以及其硬件的性能參數(shù)。這些信息存放在一個CAPDRIVERCAPS的數(shù)據(jù)結(jié)構(gòu)中。當(dāng)你的應(yīng)用程序的捕獲窗口連接到一個新的捕獲驅(qū)動器后,都會刷新這個CAPDRIVERCAPS數(shù)據(jù)結(jié)構(gòu)。下面將使用capDriverGetCaps宏來獲得捕獲設(shè)備的性能參數(shù)。
CAPDRIVERCAPS CapDrvCaps;
SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS,
sizeof (CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps);
// 或者,使用宏來獲得驅(qū)動器的新能參數(shù)
// capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
3.2.5獲得捕獲窗口狀態(tài)(Status)
下面例子使用SetWindowPos函數(shù)區(qū)設(shè)置捕獲窗口的尺寸,這個尺寸的大小是基于輸入的視頻流大小的。輸入視頻流的尺寸大小由capGetStatus宏來獲得,獲得信息放在一個CAPSTATUS的數(shù)據(jù)結(jié)構(gòu)體中。
CAPSTATUS CapStatus;
capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
SetWindowPos(hWndC, NULL, 0, 0, CapStatus.uiImageWidth,
CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);
3.2.6顯示對話框區(qū)設(shè)置視頻屬性
每個捕獲驅(qū)動器都可以提高3個以上的不同對話框來控制數(shù)字視頻的特性和捕獲處理。下面的例子示范如何顯示這些對話框。在顯示每個對話框前,該例會調(diào)用capDriverGetCaps宏并且檢查返回的CAPDRIVERCAPS對象來查看是否可以能夠顯示特定的對話框。
CAPDRIVERCAPS CapDrvCaps;
capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
// 視頻源對話框
if (CapDriverCaps.fHasDlgVideoSource)
capDlgVideoSource(hWndC);
// 視頻格式對話框
if (CapDriverCaps.fHasDlgVideoFormat)
{
capDlgVideoFormat(hWndC);
// 是否由新的圖像尺寸?Are there new image dimensions?
capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
// 如果有,發(fā)送通知給父窗口,告訴它尺寸改變了
}
// 視頻顯示對話框
if (CapDriverCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(hWndC);
3.2.7獲得和設(shè)定視頻格式
BITMAPINFO數(shù)據(jù)結(jié)構(gòu)體可以實現(xiàn)長度可調(diào)節(jié)地去適應(yīng)標(biāo)準(zhǔn)壓縮的數(shù)據(jù)格式。因為它的長度可以變,所以在每次獲得當(dāng)前視頻格式前,都必須去查詢這個結(jié)構(gòu)的長度以及分配的內(nèi)存大小。該例子使用了capGetVideoFormatSize宏去獲得緩存區(qū)大小,使用capGetVideoFormat宏區(qū)獲得當(dāng)前視頻格式。
LPBITMAPINFO lpbi;
DWORD dwSize;
dwSize = capGetVideoFormatSize(hWndC);
lpbi = GlobalAllocPtr (GHND, dwSize);
capGetVideoFormat(hWndC, lpbi, dwSize);
// 訪問視頻格式,并且釋放分配的內(nèi)存。
應(yīng)用程序使用capSetVideoFormat宏(WM_CAP_SET_VIDEOFORMAT),把一個BITMAPINFO結(jié)構(gòu)發(fā)送給捕獲窗口,顯示修改。因為視頻格式由設(shè)備指定的,你的應(yīng)用程序可以去檢查獲得的返回值,來知道這個視頻格式是不是公開的。
3.2.8預(yù)覽視頻
下面使用capPreviewRate宏來設(shè)置預(yù)覽模式的幀頻率為66毫秒/幀,使用capPreview宏在捕獲窗口預(yù)覽圖像。
capPreviewRate(hWndC, 66); // 速度,微秒
capPreview(hWndC, TRUE); // 開始預(yù)覽
capPreview(hWnd, FALSE); // 屏蔽預(yù)覽
3.2.9允許視頻覆蓋(Overlay)
下面使用capDriverGetCaps宏去檢測這個捕獲驅(qū)動是否支持覆蓋(Overlay)模式,如果支持,就允許視頻覆蓋模式;
CAPDRIVERCAPS CapDrvCaps;
capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
if (CapDrvCaps.fHasOverlay)
capOverlay(hWndC, TRUE);
3.2.10捕獲文件命名
下例使用capFileSetCaptureFile宏來指定一個要命名的文件名(mycap.avi),使用capFileAlloc宏去預(yù)分配5MB的文件。
char szCaptureFile[] = "MYCAP.AVI";
capFileSetCaptureFile( hWndC, szCaptureFile);
capFileAlloc( hWndC, (1024L * 1024L * 5));
3.2.11格式化音頻捕獲
下例使用capSetAudioFormat來設(shè)置音頻格式為11-KHz PCM 8-bit,立體聲。
WAVEFORMATEX wfex;
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = 2; // 使用立體聲
wfex.nSamplesPerSec = 11025;
wfex.nAvgBytesPerSec = 22050;
wfex.nBlockAlign = 2;
wfex.wBitsPerSample = 8;
wfex.cbSize = 0;
capSetAudioFormat(hWndC, &wfex, sizeof(WAVEFORMATEX));
3.2.12改變視頻捕獲設(shè)置
下例使用capCaptureGetSetup和capCaptureSetSetup宏來改變捕獲速度,從默認(rèn)值(15幀/秒)到10幀/秒。
CAPTUREPARMS CaptureParms;
float FramesPerSec = 10.0;
capCaptureGetSetup(hWndC, &CaptureParms, sizeof(CAPTUREPARMS));
CaptureParms.dwRequestMicroSecPerFrame = (DWORD) (1.0e6 /
FramesPerSec);
capCaptureSetSetup(hWndC, &CaptureParms, sizeof (CAPTUREPARMS));
3.2.13捕獲數(shù)據(jù)
下例使用capCaptureSequence宏開始視頻捕獲,使用capFileSaveAs宏從捕獲文件拷貝數(shù)據(jù)到其他文件NEWFILE.AVI中。
char szNewName[] = "NEWFILE.AVI";
// Set up the capture operation.
capCaptureSequence(hWndC);
// Capture.
capFileSaveAs(hWndC, szNewName);
3.2.14加入信息塊
如果你想添加其他信息(除了音視頻),你可以建一個信息塊并把它們插入到一個捕獲文件中去。信息塊可以包含這個方面的內(nèi)容。比如版權(quán)信息,視頻源的ID,外部顯示的時間信息。下面的例子保存外部時間信息SMPTE()到一個信息塊中,并加入使用capFileSetInfoChunk宏加入到捕獲文件中。
// This example assumes the application controls
// the video source for preroll and postroll.
CAPINFOCHUNK cic;
// .
// .
// .
cic.fccInfoID = infotypeSMPTE_TIME;
cic.lpData = "00:20:30:12";
cic.cbData = strlen (cic.lpData) + 1;
capFileSetInfoChunk (hwndC, &cic);
3.2.15在程序中加入回調(diào)函數(shù)
應(yīng)用程序可以注冊捕獲窗口的回調(diào)函數(shù),這樣就可以把下面的情況通知給應(yīng)用程序:
l 狀態(tài)變化了
l 錯誤發(fā)生了
l 視頻和音頻的緩沖區(qū)的數(shù)據(jù)可以使用了
l 在捕獲期間,應(yīng)用程序?qū)?/span>yield
下面的例子將創(chuàng)建一個捕獲窗口并在應(yīng)用的消息循環(huán)中,注狀態(tài)、錯誤、視頻流、幀的回調(diào)函數(shù)。
case WM_CREATE:
{
char achDeviceName[80] ;
char achDeviceVersion[100] ;
char achBuffer[100] ;
WORD wDriverCount = 0 ;
WORD wIndex ;
WORD wError ;
HMENU hMenu ;
// 使用capCreateCaptureWindow宏創(chuàng)建一個捕獲窗體.
ghWndCap = capCreateCaptureWindow((LPSTR)"Capture Window",
WS_CHILD | WS_VISIBLE, 0, 0, 160, 120, (HWND) hWnd, (int) 0);
// 使用capSetCallbackOnError宏注冊錯誤回調(diào)函數(shù)
capSetCallbackOnError(ghWndCap, fpErrorCallback);
// 使用capSetCallbackOnStatus宏注冊狀態(tài)回調(diào)函數(shù)
capSetCallbackOnStatus(ghWndCap, fpStatusCallback);
//使用capSetCallbackOnVideoStream宏注冊視頻流回調(diào)函數(shù)
capSetCallbackOnVideoStream(ghWndCap, fpVideoCallback);
//使用capSetCallbackOnFrame宏注冊幀回調(diào)函數(shù)
capSetCallbackOnFrame(ghWndCap, fpFrameCallback);
// 連接到一個捕獲驅(qū)動器上
break;
}
case WM_CLOSE:
{
//使用capSetCallbackOnFrame宏關(guān)閉幀回調(diào)函數(shù)
// 類似可調(diào)用其他存在的回調(diào)函數(shù)。
capSetCallbackOnFrame(hWndC, NULL);
break;
}
3.2.16創(chuàng)建一個狀態(tài)回調(diào)函數(shù)
下面的例子是一個簡單的狀態(tài)回調(diào)函數(shù),使用capSetCallbackOnStatus宏來注冊這個回調(diào)函數(shù)。
// StatusCallbackProc: 狀態(tài)回調(diào)函數(shù)
// hWnd: 捕獲窗體句柄
// nID: 當(dāng)前狀態(tài)的狀態(tài)碼
// lpStatusText: 當(dāng)前狀態(tài)的文本字符
//
LRESULT PASCAL StatusCallbackProc(HWND hWnd, int nID,
LPSTR lpStatusText)
{
if (!ghWndMain)
return FALSE;
if (nID == 0) { // 清除舊的狀態(tài)信息
SetWindowText(ghWndMain, (LPSTR) gachAppName);
return (LRESULT) TRUE;
}
// 顯示狀態(tài)ID和狀態(tài)文本..
wsprintf(gachBuffer, "Status# %d: %s", nID, lpStatusText);
SetWindowText(ghWndMain, (LPSTR)gachBuffer);
return (LRESULT) TRUE;
}
3.2.17創(chuàng)建一個錯誤的回調(diào)函數(shù)
下面例子是一個簡單的錯誤回調(diào)函數(shù)。通過capSetCallbackOnError宏來注冊回調(diào)。
// ErrorCallbackProc: 錯誤回調(diào)函數(shù)
// hWnd: 捕獲窗口句柄
// nErrID: 錯誤代碼
// lpErrorText: 關(guān)于錯誤的文本信息
//
LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID,
LPSTR lpErrorText)
{
if (!ghWndMain)
return FALSE;
if (nErrID == 0) // Starting a new major function.
return TRUE; // 清除舊的錯誤
// 顯示錯誤ID和錯誤文本信息
wsprintf(gachBuffer, "Error# %d", nErrID);
MessageBox(hWnd, lpErrorText, gachBuffer,
MB_OK | MB_ICONEXCLAMATION);
return (LRESULT) TRUE;
}
3.2.18創(chuàng)建一個幀回調(diào)函數(shù)
下面是一個簡單的幀回調(diào)函數(shù)。通過capSetCallbackFrame宏來注冊回調(diào)函數(shù)。
// FrameCallbackProc: 幀回調(diào)函數(shù)
// hWnd: 捕獲窗體句柄
// lpVHdr: 指向一個包含幀信息的數(shù)據(jù)結(jié)構(gòu)體
//
LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
if (!ghWndMain)
return FALSE;
wsprintf(gachBuffer, "Preview frame# %ld ", gdwFrameNum++);
SetWindowText(ghWndMain, (LPSTR)gachBuffer);
return (LRESULT) TRUE ;
}