這一篇講怎么采集攝像頭圖像並預覽,以及錄制視頻到本地。
程序實現流程
這里通過使用 CaptureGraphBuilder 來簡化 Graph 的創建流程。
具體流程如下:
- 初始化 COM 庫
- 創建各 Filter
- 找到視頻采集設備,也就是通過 USB 連接的攝像頭
- 渲染並預覽視頻
- 銷毀先前創建的 Filter
- 釋放COM
視頻采集類
先看一下視頻采集類的頭文件,而源文件就不一次性全部貼出了,而是只介紹幾個重要的成員函數。captrue.h 的內容如下:
#pragma once
#include <Windows.h>
#include <dshow.h>
// 用於確保安全釋放的宏
#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }
class CCapture
{
public:
CCapture();
~CCapture();
HRESULT Init(HWND hwnd); // 初始化
HRESULT FindCaptureDevice(); // 尋找視頻采集設備
HRESULT Render(); // 渲染並預覽視頻
void DestroyGraph(); // 銷毀先前創建的filter
void ResizeWindow(); // 重設窗口
private:
// 窗口句柄
HWND m_hwnd;
// 視頻采集預覽相關
IGraphBuilder *m_pGraph; // filter granph(manager)
ICaptureGraphBuilder2 *m_pCapture; // capture granph
IMediaControl *m_pMediaC; // 媒體控制接口
IMediaEventEx *m_pMediaE; // 媒體事件接口
IVideoWindow *m_pVideoW; // 視頻窗口接口
IBaseFilter *m_pFilter; // 基類filter
};
1.初始化
// 初始化
HRESULT CCapture::Init(HWND hwnd)
{
HRESULT hr;
// 創建filter graph manager
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGraph);
if (FAILED(hr))
return hr;
// 創建capture granph
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
if (FAILED(hr))
return hr;
// 查詢graph中各IID參數標識的接口指針
hr = m_pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&m_pMediaC);
if (FAILED(hr))
return hr;
hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (LPVOID *)&m_pMediaE);
if (FAILED(hr))
return hr;
hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *)&m_pVideoW);
if (FAILED(hr))
return hr;
// 為capture graph指定要使用的filter graph
hr = m_pCapture->SetFiltergraph(m_pGraph);
if (FAILED(hr))
return hr;
// 將Win32窗口句柄賦給m_hwnd
m_hwnd = hwnd;
return hr;
}
進行初始化操作。
2.尋找視頻采集設備
// 尋找視頻采集設備
HRESULT CCapture::FindCaptureDevice()
{
HRESULT hr = S_OK;
ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pClassEnum = NULL; // 用於視頻采集設備的枚舉
IMoniker* pMoniker = NULL; // 設備Moniker號
// 創建系統設備枚舉
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **)&pDevEnum);
if (FAILED(hr))
return hr;
// 創建一個指定視頻采集設備的枚舉
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if (FAILED(hr) || pClassEnum == NULL)
{
SAFE_RELEASE(pDevEnum);
return hr;
}
// 使用第一個找到的視頻采集設備(只適用於單攝像頭的情況)
hr = pClassEnum->Next(1, &pMoniker, NULL);
if (hr == S_FALSE)
{
SAFE_RELEASE(pDevEnum);
SAFE_RELEASE(pClassEnum);
return hr;
}
// 綁定找到攝像頭的moniker到filter graph
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pFilter);
if (FAILED(hr))
{
SAFE_RELEASE(pDevEnum);
SAFE_RELEASE(pClassEnum);
SAFE_RELEASE(pMoniker);
return hr;
}
// 增加filter graph的引用計數
m_pFilter->AddRef();
return hr;
}
初始化之后,就要找到視頻采集設備,即通過 USB 連接的攝像頭。這里沒有去循環枚舉查找多個視頻采集設備,固定選擇了找到的第一個視頻采集設備。
3.渲染並預覽視頻
// 渲染並預覽視頻
HRESULT CCapture::Render()
{
HRESULT hr;
// 將base filter添加到filter graph中
hr = m_pGraph->AddFilter(m_pFilter, L"Video capture");
if (FAILED(hr))
{
m_pFilter->Release();
return hr;
}
// 用ICaptureGraphBuilder2接口構建預覽的filter鏈路
hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pFilter, NULL, NULL);
if (FAILED(hr))
{
m_pFilter->Release();
return hr;
}
// 同時構建一個寫文件的filter鏈路
IBaseFilter *pMux;
hr = m_pCapture->SetOutputFileName(&MEDIASUBTYPE_Avi, L"D:\\example.avi", &pMux, NULL); // 設置輸出視頻文件位置
hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pFilter, NULL, pMux); // 將m_pFilter的輸出pin連接到pMux
// 使用完就可以釋放base filter了
pMux->Release();
m_pFilter->Release();
// 顯示窗口 , 預覽采集圖形
hr = m_pVideoW->put_Owner((OAHWND)m_hwnd);
if (FAILED(hr))
return hr;
hr = m_pVideoW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
if (FAILED(hr))
return hr;
ResizeWindow(); // 重設窗口
hr = m_pVideoW->put_Visible(OATRUE);
if (FAILED(hr))
return hr;
hr = m_pMediaC->Run();
return hr;
}
實現效果
代碼下載
參考:
(一) DirectShow簡單采集程序——使用CaptureGraphBuilder