• 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进行摄像头预览


  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/12068978.html
Copyright © 2020-2023  润新知