DirectShow 是微軟提供的串流媒體處理架構,支援從 WDM 驅動裝置 (如 USB 攝影機、擷取卡) 擷取影音資料並進行預覽、儲存、靜態擷圖。本文封裝一個 CCaptureVideo MFC 類別,將複雜的 Filter Graph 建立、裝置列舉、預覽視窗設定、Sample Grabber 靜態擷圖等功能整合,僅需三個公開方法即可完成影像擷取應用。
| 方法 | 用途 |
int EnumDevices(HWND hList) | 列舉系統視訊擷取裝置,填入下拉清單 (ComboBox) |
HRESULT Init(int iDeviceID, HWND hWnd) | 初始化指定裝置,於 hWnd 顯示預覽畫面 |
void GrabOneFrame(BOOL bGrab) | 觸發單幀擷取,儲存為 BMP (預設 c:\\donaldo.bmp) |
關鍵私有成員
IGraphBuilder *m_pGB;
ICaptureGraphBuilder2 *m_pCapture;
IBaseFilter *m_pBF; // Capture Filter
IMediaControl *m_pMC; // 媒體控制 (Run/Stop)
IVideoWindow *m_pVW; // 視窗顯示控制
CComPtr<ISampleGrabber> m_pGrabber; // Sample Grabber
CSampleGrabberCB mCB; // 回呼類別,處理 BufferCB
關鍵實作細節
1. 裝置列舉 (EnumDevices)
int CCaptureVideo::EnumDevices(HWND hList) {
ICreateDevEnum *pCreateDevEnum;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
CComPtr<IEnumMoniker> pEm;
pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
pEm->Reset();
IMoniker *pM; ULONG cFetched;
int id = 0;
while (pEm->Next(1, &pM, &cFetched) == S_OK) {
IPropertyBag *pBag;
pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
VARIANT var; var.vt = VT_BSTR;
pBag->Read(L"FriendlyName", &var, NULL);
TCHAR str[2048];
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, str, 2048, NULL, NULL);
::SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)str);
SysFreeString(var.bstrVal);
pBag->Release(); pM->Release(); id++;
}
return id;
}
2. 建立擷取圖表
InitCaptureGraphBuilder():建立 IGraphBuilder、ICaptureGraphBuilder2、IMediaControl、IVideoWindow。
BindFilter():依索引綁定裝置 Moniker 至 IBaseFilter。
- 加入 Capture Filter 至 Graph。
- 建立 Sample Grabber,設定媒體類型為
MEDIASUBTYPE_RGB24,加入 Graph。
RenderStream(PIN_CATEGORY_PREVIEW, MEDIATYPE_Video, pCap, pGrabBase, NULL) 連接預覽腳位 → Grabber → Renderer。
- 取得連接後的媒體格式 (
VIDEOINFOHEADER) 記錄寬高,供 BMP 儲存用。
- 設定 Grabber 回呼模式:
SetCallback(&mCB, 1) (使用 BufferCB)。
SetupVideoWindow():設定 Video Window 為子視窗、填滿客戶區、顯示。
m_pMC->Run() 開始預覽。
3. 靜態擷圖回呼 (CSampleGrabberCB::BufferCB)
STDMETHODIMP BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize) {
if (!bOneShot) return 0; // 全域旗標,由 GrabOneFrame(TRUE) 設定
if (!pBuffer) return E_POINTER;
SaveBitmap(pBuffer, lBufferSize); // 寫入 BMP 檔案
bOneShot = FALSE;
return 0;
}
SaveBitmap 手動建構 BITMAPFILEHEADER + BITMAPINFOHEADER (24-bit RGB) + 像素資料。
使用範例:MFC 對話方塊應用程式
- 建立 MFC 對話方塊專案,放置
CComboBox (IDC_COMBO1)、兩個按鈕 (IDC_HAVEALOOK 啟動、IDC_PHOTO 擷圖)、一個 Picture Control (IDC_STATIC_SCREEN, Type=Rectangle, Color=Gray)。
- 加入成員變數:
CComboBox m_ListCtrl、CStatic m_staticScreen、CCaptureVideo m_cap。
OnInitDialog:m_cap.EnumDevices(m_ListCtrl); m_ListCtrl.SetCurSel(0);
- 啟動按鈕:
m_cap.Init(m_ListCtrl.GetCurSel(), m_staticScreen.GetSafeHwnd());
- 擷圖按鈕:
m_cap.GrabOneFrame(TRUE);
編譯與部署注意事項
- 環境:Windows 2000 + DirectX 9 SDK + VC6 (亦適用 VS2005+ 配合 Windows SDK)。
- 連結庫:
Strmiids.lib、Quartz.lib (DirectX SDK 提供)。
- 標頭檔:
<dshow.h>、<atlbase.h> (需 ATL 支援)。
- 若
qedit.dll 未註冊,Sample Grabber 建立會失敗,請執行 regsvr32 qedit.dll。
總結
CCaptureVideo 將 DirectShow 複雜的 Filter Graph 建立、裝置管理、預覽視窗綁定、靜態擷圖等功能封裝為簡潔的三介面類別,大幅降低開發門檻。開發者可依需求擴充:支援多種輸出格式 (JPG/PNG)、影片錄製 (ASF/MP4)、影像處理 (OpenCV 整合) 等。
- 上一篇文章: WinSock 程式設計學習筆記 (一):Socket 基礎、資料結構與完整通訊流程
- 下一篇文章: VC++ 剖析木馬程式隱藏技術:任務列與進程列表雙重隱身原理與防範
Copyright ©
程式碼之家 All Rights Reserved