DirectShow 進行視頻預覽和錄制


這一篇講怎么采集攝像頭圖像並預覽,以及錄制視頻到本地。


程序實現流程

這里通過使用 CaptureGraphBuilder 來簡化 Graph 的創建流程。


具體流程如下:

  1. 初始化 COM 庫
  2. 創建各 Filter
  3. 找到視頻采集設備,也就是通過 USB 連接的攝像頭
  4. 渲染並預覽視頻
  5. 銷毀先前創建的 Filter
  6. 釋放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;
}

實現效果


代碼下載

Github - DShow_captruePreview


參考:

(一) DirectShow簡單采集程序——使用CaptureGraphBuilder

2.使用DShow進行攝像頭預覽



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM