DirectShow 进行视频预览和录制
这一篇讲怎么采集摄像头图像并预览,以及录制视频到本地。
程序实现流程
这里通过使用 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
DirectShow 进行视频预览和录制的更多相关文章
- VS2010 C++学习(5):基于DirectShow的视频预览录像程序
VS2010 C++学习(5):基于DirectShow的视频 预览录像程序 学习VC++编制的基于DirectShow视频捕获程序,主要练习基于DirectShow程序的应用. 一. ...
- 上传APP加入视频预览--精简点名
上传APP加入视频预览--精简点名 在为精简点名APP制作视频预览时的坑: 1.视频预览不能太长.也不能太短15-30s就好.我录制的是18s 2.视频的帧数不能太大.也就是说你在录制视频的时候.要慢 ...
- 关于云平台中OFFICE预览与视频预览的解决办法
最近,随着firefox x64的升级,出现flash插件完全被禁止的现象,html5替换是大势所趋,原来我们在云平台中有多处使用flash的地方,比如OFFICE预览,视频播放,游戏等,现对于OFF ...
- mvc实现上传视频预览
上个项目中用到了上传视频,本来打算用百度的webuploader做的,但是webuploader可能有个毛病就是不能上传太大的东西. 于是乎,只能换个方法做了啊,看了半天最终决定用传统的uploade ...
- 终极指南:如何为iOS8应用制作预览视频
最近一两个月里,苹果的世界里出现了很多新东西,比如屏幕更大的iPhone 6,可穿戴设备Apple Watch,iOS8,以及旨在帮助用户更好的发现应用的App Store改版等等. 说到App St ...
- APP_Store - 怎样为iOS8应用制作预览视频
关于iOS 8应用预览视频的话题,从设计.技术规范,到录屏.编辑工具,介绍的都比较详尽:建议收藏,在接下来用的到的时候作以参考.下面进入译文. 最近一两个月里,苹果的世界里出现了很多新东西,比如屏幕更 ...
- HTML5-video标签-实现点击预览图播放或暂停视频
HTML5-video标签-实现点击预览图播放或暂停视频 刚刚参加工作,开始更多的接触到一些新的知识,促使我开始了解html5和css3的新特性.这时我才真的发现到html5和css3的强大. 之前关 ...
- Yii2 框架下bootstrap 弹窗预览视频等~
Yii2 本身已经引用了'yii\bootstrap\BootstrapAsset',所以使用bootstrap 非常简洁. 1 在PHP页面引用命名空间 use app\assets\AppAsse ...
- Android开发 获取视频中的信息(例如预览图或视频时长) MediaMetadataRetriever媒体元数据检索器
前言 在Android里获取视频的信息主要依靠MediaMetadataRetriever实现 获取最佳视频预览图 所谓的最佳就是MediaMetadataRetriever自己计算的 /** * 获 ...
随机推荐
- arcgis api for javascript 学习(一) 调用在线发布的动态地图
1.图中显示为arcgis软件中显示的地图文件 2.调用动态地图主要的是知道动态地图的URL地址 3.通过IDE(webstorm)调用动态地图,如图 4.话不多说,直接上代码 <!DOCTYP ...
- 46.QT-自带库QSerialPort串口使用
之前一章学习的是第三方库使用: 34.QT-qextserialport第三方库制作串口助手(并动态检测在线串口,附带源码) 本章来学习自带serial库 1.QSerialPortInfo QLis ...
- 基于 Unity 的一种透明通道压缩处理
由于 Android 平台各种硬件标准的不统一,为了开发的软件项目能够在大部分 Android 机上完美运行,我们需要以较差的硬件支持为基础做准备. Android 平台基本上都支持对不带 Alpha ...
- MATLAB实例:求相关系数、绘制热图并找到强相关对
MATLAB实例:求相关系数.绘制热图并找到强相关对 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 用MATLAB编程,求给定数据不同维度之间的相关系 ...
- 剑指Offer-37.二叉树的深度(C++/Java)
题目: 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. 分析: 递归求解左右子树的最大值即可,每遍历到一个结点,深度加1,最后 ...
- ccf-csp201909题解
目录 ccf-csp201909题解 1. 201909-1 小明种苹果 题目描述 解析 通过代码 2. 201909-2 小明种苹果(续) 题目描述 解析 通过代码 3. 201909-3 字符画 ...
- POJ-2018 Authors Register Update your info Authors ranklist
Best Cow Fences Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 16945 Accepted: 5425 ...
- go语言的json
简介 json 中提供的处理 json 的标准包是 encoding/json,主要使用的是以下两个方法: // 序列化 func Marshal(v interface{}) ([]byte, er ...
- Java描述设计模式(03):工厂方法模式
本文源码:GitHub·点这里 || GitEE·点这里 一.工厂方法模式 1.生活场景 系统常见的数据导出功能:数据导出PDF.WORD等常见格式. 2.工厂方法模式 是类的创建模式,又叫做虚拟构造 ...
- ubuntu16搭建文件服务器
这篇记录,如何在ubuntu16 安装 FastDFS 文件服务器,详细步骤 环境依赖 apt-get install make apt-get install unzip apt-get insta ...