视频采集器--设计
福建工程学院
VC++
课 题: 视频采集器
指导老师: 黄旭红
班 级: 电子1001
组 员:刘碧端 3100204104
李鑫煌 3100204111
沈文海 3100204112
郑德明 3100204113
王桂德 3100204122
陈志阳 3100204123
吴碧武 3100204124
日 期:2013年3月15日
1
目录
一、设计目的与要求 ........................................................................................3二、准备工作 ....................................................................................................3 三、系统分析 ....................................................................................................4
3.1 系统整体设计设计思路 ......................................................................4
3.1.1、基于VC++的视频采集系统 ...................................................4
3.1.2、显示模块 .................................................................................4
3.2系统的实现方法 ...................................................................................4
3.2.1设计
分析 ............................................................................4
3.2.2、主要类函数的实现 .................................................................5
四、系统实现步骤 ............................................................................................9 五、测试与结论 .............................................................................................. 14 六、课程设计总结 .......................................................................................... 16
6.1程序特点及功能扩展 ......................................................................... 16
6.2 独创及创新点.................................................................................... 16 七、参考文献 .................................................................................................. 16 八、附录 .......................................................................................................... 16
8.1 完整程序代码.................................................................................... 16
8.1.1 CCaptureClass视频捕捉类实现文件CaptureVideo.cpp.......... 16
8.1.2 CaptureVideo.h : header file ..................................................... 30
8.1.3CaptureVideo.cpp : 定义应用程序的类行为。 ........................ 32
8.1.4 CaptureVideo.h : PROJECT_NAME 应用程序的主头文件 .... 34
8.1.5 CaptureVideoDlg.cpp : 实现文件 ............................................ 35
8.1.6CaptureVideoDlg.h : 头文件 .................................................... 45
8.2 界面图 ............................................................................................... 47
8.2.1 总体界面图 ............................................................................. 47
8.2.2 视频格式设置 ......................................................................... 48
8.2.3 画面参数设置 ......................................................................... 48
2
一、设计目的与要求
为了轻松地
视频和从摄像头、电视调谐卡、数码摄像机中捕捉实时图像,可利用VC++的Direct Show开发包,编写视频采集器软件系统,采集所需的图像。
1. 菜单式的人机界面。
2. 具有开始、停止、录像、抓图等功能。
二、准备工作
本次设计在Visual Studio 2005集成开发环境下完成,利用VC++的Direct Show开发包,编写视频采集器软件系统,采集所需的图像。
DirectShow SDK是Microsoft微软公司为简化或降低音/视频开发的难度而为音/视频应用开发人员定制的开发包、软件库。它是我们熟悉的DirectX家族中的一个组件,主要针对程序员开发数字音/视频程序提供支持。。使用DirectShow我们可以在很短的时间内搭建自己的视频播放、捕获和编辑应用程序,不必对有关视频硬件如显卡,视频捕获设备,视频编码算法,解码算法,音/视频数据同步等,复杂的技术做细节的掌握。另外,由于现在的视频捕获设备如USB摄像头,视频捕获卡,显卡等主流的驱动均是WDM模式。VC++开发人员自己访问、控制、读取此类设备的数据显然不是件容易的事情。DirectShow却可以很灵活地帮助我们去完成工作,即屏蔽了我们直接对硬件的操作,又可快速开发出我们自己的产品。
DirectShow SDK是为多媒体数据流的应用而开发的。它支持多媒体流的捕获和预览。运用Directshow,用户可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且可以进行相应的后期处理甚至存储到文件中;同时可以方便地构建影音文件的回放、编辑等操作。使用DirectShow能够完成以下工作:
.音/视频多媒体流的捕获和预览。
.支持多媒体格式ASF、MPEG、AVI、MP3、WAVE的回放。
.集成其他DirectX技术,增强音/视频硬件如声卡、显卡的性能。
.视频文件的回放、非线性吧,编辑等。
.支持DVD等设备。
3
.定制自己的Filter。
三、系统分析
3.1 系统整体设计设计思路
、基于VC++的视频采集系统 3.1.1
基于VC++的视频采集
系统
文件
图片保存 视频保存 播放、停止 参数设置
3.1.2、显示模块
显示模块
抓图 开始、停止 录像 前端显示
3.2系统的实现方法
3.2.1设计方案分析
3-1系统框图
4
USB视频捕捉设备输入视频流,经过Smart Tee 分流成两路,一路流向AVI Mux 混频器最终保存为avi视频文件。另一路通过AVI Decompressor 视频解码器输出到电脑显示器VideoRender。整个方案实现了“视频监控”与“文件保存”的同时进行。
3.2.2、主要类函数的实现
3.2.2.1采集设备的枚举
使用采集设备前,需要首先确定系统己经安装的采集设备:视频、音频采集设备。系统设备枚举器为按类型枚举已注册在系统中的滤波器提供了统一的方法。而且它能够区分不同的硬件设备。当利用系统设备枚举器查询设备的时候,系统设备枚举器为特定类型的设备(如音频捕获和视频压缩)牛成了一张枚举表.类型枚举器为每个这种类型的设备返回一个Moniker,类型枚举器自动把每种即插即用的设备包含在内。调用标准方法生成系统设备枚举器,类标识,代码如下:
int CCaptureClass::EnumDevices(HWND hList)
{
if (!hList) return -1;
int id = 0;
ICreateDevEnum *pCreateDevEnum;//枚举捕获设备
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER,IID_ICreateDevEnum,
(void**)&pCreateDevEnum);
if (hr != NOERROR) return -1;
IEnumMoniker *pEm;
//创建视频类型枚举器
pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,&pE
m, 0);
if (hr != NOERROR) return -1;
//类型枚举器复位
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
5
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;//设备属性页
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
id++;
::SendMessage(hList, CB_ADDSTRING,
0,(LPARAM)var.bstrVal);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
}
return id;
}
3.2.2.2 把指定采集设备与滤波器捆绑
使用DirectShov的滤波器进行流媒体开发,需要一个管理器即滤波器链表管
理器, DirectShow SDK提供的是Graph Builder接口IgraphBuilder。不过针对捕
获仟务(Capture) ,还有另一个接口ICaptureBuilder2针对采集捕获的增强型接口. /* 创建滤波器链表管理器,查询其各种控制接口*/
HRESULT CCaptureClass::InitCaptureGraphBuilder()
{
6
HRESULT hr;
// 创建IGraphBuilder接口
hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB);
if (FAILED(hr)) return hr;
// 创建ICaptureGraphBuilder2接口
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2 , NULL,
CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
if (FAILED(hr)) return hr;
// 初始化滤波器链表管理器IGraphBuilder
m_pCapture->SetFiltergraph(m_pGB);
// 查询媒体控制接口
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
if (FAILED(hr)) return hr;
// 查询视频窗口接口
hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);
if (FAILED(hr)) return hr;
return hr;
}
在上述程序中,IGraphBuilder接口,然后创建IcapturGraphBuilder2接口,
接着使用ICaptureGraphBuilder初始化IGraphBuiider接口。在IGraphBuilder
接口下查询媒体控制接口、视频窗口接口。
/*开始预览视频数据*/
HRESULT CCaptureClass::PreviewImages(int iDeviceID, HWND hWnd) {
HRESULT hr;
7
// 初始化视频捕获滤波器链表管理器
hr = InitCaptureGraphBuilder();
if (FAILED(hr))
{ AfxMessageBox(_T("Failed to get video interfaces!"));
return hr;
}
// 把指定采集设备与滤波器捆绑
if(!BindFilter(iDeviceID, &m_pBF))
return S_FALSE;
// 把滤波器添加到滤波器链表中
hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
// 渲染媒体,把链表中滤波器连接起来
Hr=m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_V
ideo, m_pBF, NULL, NULL );
if( FAILED( hr ) )
{ AfxMessageBox(_T("Can’t build the graph"));
return hr;
}
//设置视频显示窗口
m_hWnd = hWnd;
SetupVideoWindow();
//test for config
hr = m_pMC->Run();
if(FAILED(hr))
{ AfxMessageBox(_T("Couldn’t run the graph!"));
return hr;
}
return S_OK;
}
8
四、系统实现步骤
1.应用VS 2005应用程序向导建立对话框程序框架,项目名称为CapnueVideoe。
2.在项目CaptureVideo的主界面中添加控件:6个Button, 1个Combo Box,1
个Picture Convol,如表4-1所示。根据其功能修改所有控件的ID。
名 称 说 明
ID_PREVIEW 连接设备
ID_CAPTURE 开始录像
ID_VIDEO_FORMAT 视频格式
ID_IMAGE_PARAMETER 画面参数
ID_SAVEGRAPH 截取图片
ID_EXIT 退出保存
IDC_DEVICE_LISTER 设备列表组合框
IDC_VIDEO_WINDOW 显示捕获的视频图像
表4—1
3.在添加所有的控件后,修改控件大小。重排其位置。添加控件后的主界面设计如图4-2所示。
图4-2
9
4.控件与变量捆绑。为便于控制,把IDC _VIDEO WINDOW控件、 IDC_DEVICE LISTER控件分别与变量捆绑。途径是选择控件的右键菜单中的“添加变量”命令。
5.添加按钮在线提示ToolTip,首先在类CCaptureVideoDlg定义中声明tooltip控件。接着在类CCapwreVideoDlg实现文件的对话框初始化函数OnInidJialog中添加:
m_tooltip.Create(this);
m_tooltip.Activate(TRUE);
m_tooltip.AddTool(GetDlgItem(ID_PREVIEW), _T("打开摄像头")); 在PreTranslateMessage消息处理函数中添加如下代码: m_tooltip.RelayEvent(pMsg); 6. 功能按钮的消息响应,即单击按钮的事件处理。双击某按钮,实现单击按钮事件处理函数的添加。为了使用类CCaptureClass的变量和函数在CCapmreVideoDlg类中引入头文件并定义视频捕获类的对象。首先引入头文件:#include "CaptureClass.h"。接着在CCaptureVideoDlg类中定义视频捕获类的对象:
7.CCaptureClass m_cap;双击“连接设备”按钮,添加事件处理代码: void CCaptureVideoDlg::OnBnClickedPreview()
{
HWND hVWindow = m_videoWindow.GetSafeHwnd();
int id = m_listCtrl.GetCurSel();
m_cap.PreviewImages(id , hVWindow); }
获取视频显示窗口的句柄,根据程序最初枚举到的所有采集设备,选择用户选中的采集设备。调用视频捕获类的成员函数Previewlmage,完成整个视频的采集、显示任务。图4-3所示为“连接设备”按钮实现的效果图。
10
图4-3
双击“开始录像”按钮,添加事件处理代码。
void CCaptureVideoDlg::OnBnClickedCapture() {
CString strFilter = _T("AVI File (*.avi) | *.avi|");
strFilter += "All File (*.*) | *.*|";
CFileDialogdlg(TRUE,NULL,NULL,
OFN_PATHMUSTEXIST|OFN_HIDEREADONLY, strFilter, this);
if (dlg.DoModal() == IDOK)
{
CString m_sourceFile = dlg.GetPathName();
m_cap.CaptureImages(m_sourceFile);
}
}
11
存储捕获的视频,这里采用AVI格式,视频数据未经压缩。调用视频捕获类CCaptureClass的成员函数CaptureImages完成视频的捕获、存储任务。本
需要先预览视频,然后再开始捕获、存储视频,不能直接捕获、存储视颇。双击“视频格式"按钮, 添加事件处理代码。
void CCaptureVideoDlg::OnBnClickedVideoFormat()
{
m_cap.ConfigCameraPin(this->m_hWnd);
}
设置视频格式前,首先启动“连接设备”,然后再配置视频的格式。视频格式包
括帧率、颜色空间、视频分辨率等,该功能的效果如图4-4所示。
图 4-4
双击“画面参数”按钮,添加事件处理代码。
void CCaptureVideoDlg::OnBnClickedImageParameter()
{
m_cap.ConfigCameraFilter(this->m_hWnd);
}
设置视频图像参数前,首先启动“连接设备”,然后配置画面参数。画面参数包括图像渲染、白平衡、防闪烁、亮度、对比度等,该功能的效果如图4-5所示。并且该功能调整的参数马上起作用。
12
-5 图4
双击“保存图表”按钮,添加事件处理代码。
void CCaptureVideoDlg::OnBnClickedSavegraph()
{
CFileDialog dlg(TRUE);
if (dlg.DoModal()==IDOK)
{
CString str=dlg.GetPathName();
13
TCHAR *inFileName = str.GetBuffer(str.GetLength());
str.ReleaseBuffer();
m_cap.SaveGraph(inFileName);
}
}
本功能存储的Graph文件可以使用程序GraphEdit播放。双击“退出程序”按钮,
aptureClass的析构函数,释放资源和添加事件处理代码。程序隐含调用了类CC
COM库。
void CCaptureVideoDlg::OnBnClickedExit() {
CDialog::OnOK();
}
退出本程序时,由于视频捕获类CCaptureClass的析构函数已经包含了释放资源、指针的工作,所以退出应用程序时不用释放任何资源,只是关闭应用程序。
8.在对话框的初始化OnInitDialog中枚举本系统的视频采集设备,添加到列表框并默认显示第一个设备。
m_cap.EnumDevices(m_listCtrl.GetSafeHwnd());
m_listCtrl.SetCurSel (0);
五、测试与结论
程序的屏幕显示如5-1所示:
14
图5-1
说明:1.灰色款图为视频显示框,当连接设备后会显示摄像头捕获的图像。 2.图中组合框为设备名称,默认为显示第一个设备的名称,当电脑有多个摄像头设备时可选择。
3.连接设备按钮即打开摄像头,只有当连接设备后才可以进行后面按钮的操作,否则将退出程序。
4.开始录像按钮为开始捕获、保存视频,保存路径在按钮后选择。 5.视频格式中为数据流选项,可以选择视频帧率、大小等选项。 6.画面参数中有高级选项,摄像头设置,可以控制摄像头亮度、对比度、饱和度等选项。
7.截取图片能够实现对图像的截取并保存图片。
8.保存退出按钮退出程序。
综上所述,我们小组较好的完成了本次设计题目的要求,并还增加了其他功能。
15
六、课程设计总结
6.1程序特点及功能扩展
程序在实现各项基本要求的情况下还有其他扩展功能:
(1)、能够实现对不同设备的识别,并能够自动加载设备本身所具有的媒体特性。
(2)、能够实现对画面的亮度、对比度、饱和度等基本选项进行设置,也能对画面进行渲染等特殊功能。
(3)、能够实现对视频像素的调节。
(4)、能够调节帧率。
6.2 独创及创新点
(1)能够对不同设备的识别并且调用设备的媒体特性,如可以对电脑上多个设备进行切换,并且能够有相应的设备选项供选择。
七、参考文献
1、董正言,张聪. 面向对象程序设计(C++版). 清华大学出版社.
2、路锦正,周冬梅. Visual C++音频/视频处理技术及工程实践. 电子工业出版社.
3、文东,华进. Visual C++程序设计基础与项目实训. 北京科海电子出版社.
4、陆其明. DirectShow开发指南. 清华大学出版社.
5、电子发烧友网
6、全球最大中文IT社区
八、附录
8.1 完整程序代码
8.1.1 CCaptureClass视频捕捉类实现文件CaptureVideo.cpp
// CaptureVideo.cpp: implementation of the CCaptureClass class.
//
16
#include "stdafx.h"
#include "CaptureClass.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction //////////////////////////////////////////////////////////////////////
CCaptureClass::CCaptureClass() {
// COM 库初始化
CoInitialize(NULL);
m_hWnd = NULL;
m_pVW = NULL;
m_pMC = NULL;
m_pGB = NULL;
m_pBF = NULL;
m_pCapture = NULL;
}
CCaptureClass::~CCaptureClass() {
//
if(m_pMC)
m_pMC->Stop();
if(m_pVW)
17
{
m_pVW->put_Visible(OAFALSE);
m_pVW->put_Owner(NULL);
}
srelease(m_pCapture);
srelease(m_pMC);
srelease(m_pGB);
srelease(m_pBF);
CoUninitialize();
}
int CCaptureClass::EnumDevices(HWND hList)
{
if (!hList) return -1;
int id = 0;
//枚举捕获设备
ICreateDevEnum *pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
(void**)&pCreateDevEnum);
if (hr != NOERROR) return -1;
IEnumMoniker *pEm;
//创建视频类型枚举器
hr =
pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pE
18
m, 0);
//创建音频捕获类
//hr =
pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,&pE
m, 0);
if (hr != NOERROR) return -1;
//类型枚举器复位
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
//设备属性页
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
id++;
::SendMessage(hList, CB_ADDSTRING,
0,(LPARAM)var.bstrVal);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
19
pM->Release();
}
return id;
}
/*开始预览视频数据*/
HRESULT CCaptureClass::PreviewImages(int iDeviceID, HWND hWnd)
{
HRESULT hr;
// 初始化视频捕获滤波器链表管理器
hr = InitCaptureGraphBuilder();
if (FAILED(hr))
{
AfxMessageBox(_T("Failed to get video interfaces!"));
return hr;
}
// 把指定采集设备与滤波器捆绑
if(!BindFilter(iDeviceID, &m_pBF))
return S_FALSE;
// 把滤波器添加到滤波器链表中
hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
// 渲染媒体,把链表中滤波器连接起来
hr = m_pCapture->RenderStream( &PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, m_pBF, NULL, NULL );
if( FAILED( hr ) )
20
{
AfxMessageBox(_T("Can’t build the graph"));
return hr;
}
//设置视频显示窗口
m_hWnd = hWnd;
SetupVideoWindow();
//test for config
hr = m_pMC->Run();
if(FAILED(hr))
{
AfxMessageBox(_T("Couldn’t run the graph!"));
return hr;
}
return S_OK;
}
/*设置捕获视频的文件,开始捕捉视频数据写文件*/ HRESULT CCaptureClass::CaptureImages(CString inFileName)
{
HRESULT hr=0;
// 先停止视频
m_pMC->Stop();
// 设置文件名,注意该函数的第二个参数的类型
hr = m_pCapture->SetOutputFileName(&MEDIASUBTYPE_Avi,
inFileName.AllocSysString(), &pMux, NULL );
// 渲染媒体,连接所有滤波器
hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE,
21
&MEDIATYPE_Video, m_pBF, NULL, pMux );
pMux->Release();
// 回复视频
m_pMC->Run();
return hr;
}
// 把指定采集设备与滤波器捆绑
bool CCaptureClass::BindFilter(int deviceId, IBaseFilter **pFilter)
{
if (deviceId < 0) return false;
// enumerate all video capture devices
ICreateDevEnum *pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
(void**)&pCreateDevEnum);
if (hr != NOERROR) return false;
IEnumMoniker *pEm;
hr =
pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pE
m, 0);
if (hr != NOERROR) return false;
pEm->Reset();
ULONG cFetched;
22
IMoniker *pM;
int index = 0;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
if (index == deviceId)
{
pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
index++;
}
return true;
}
/* 创建滤波器链表管理器,查询其各种控制接口 */
HRESULT CCaptureClass::InitCaptureGraphBuilder()
{
23
HRESULT hr;
// 创建IGraphBuilder接口
hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB);
if (FAILED(hr)) return hr;
// 创建ICaptureGraphBuilder2接口
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2 , NULL,
CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
if (FAILED(hr)) return hr;
// 初始化滤波器链表管理器IGraphBuilder
m_pCapture->SetFiltergraph(m_pGB);
// 查询媒体控制接口
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
if (FAILED(hr)) return hr;
// 查询视频窗口接口
hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);
if (FAILED(hr)) return hr;
return hr;
}
/* 设置视频显示窗口的特性 */
HRESULT CCaptureClass::SetupVideoWindow()
24
{
HRESULT hr;
//ljz
hr = m_pVW->put_Visible(OAFALSE);
hr = m_pVW->put_Owner((OAHWND)m_hWnd);
if (FAILED(hr)) return hr;
hr = m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
if (FAILED(hr)) return hr;
ResizeVideoWindow();
hr = m_pVW->put_Visible(OATRUE);
return hr;
}
/* 更改视频窗口大小 */
void CCaptureClass::ResizeVideoWindow() {
if (m_pVW)
{
//让图像充满整个窗口
CRect rc;
::GetClientRect(m_hWnd,&rc);
m_pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
}
}
/* 保存整个滤波器链表到文件,以便于使用GraphEdit软件查看*/
void CCaptureClass::SaveGraph(TCHAR *wFileName)
25
{
HRESULT hr;
//CFileDialog dlg(TRUE);
//if (dlg.DoModal()==IDOK)
{
//WCHAR wFileName[MAX_PATH];
//MultiByteToWideChar(CP_ACP, 0, dlg.GetPathName(), -1, wFileName, MAX_PATH);
IStorage* pStorage=NULL;
// First, create a document file that will hold the GRF file
hr = ::StgCreateDocfile(wFileName,
STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &pStorage);
if (FAILED(hr))
{
AfxMessageBox(TEXT("Can not create a document"));
return;
}
// Next, create a stream to store.
WCHAR wszStreamName[] = L"ActiveMovieGraph";
IStream *pStream;
hr =
pStorage->CreateStream(wszStreamName,STGM_WRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE, 0, 0, &pStream);
26
if(FAILED(hr))
{
AfxMessageBox(TEXT("Can not create a stream"));
pStorage->Release();
return;
}
// The IpersistStream::Save method converts a stream
// into a persistent object.
IPersistStream *pPersist = NULL;
m_pGB->QueryInterface(IID_IPersistStream,
reinterpret_cast
(&pPersist));
hr = pPersist->Save(pStream, TRUE);
pStream->Release();
pPersist->Release();
if(SUCCEEDED(hr))
{
hr = pStorage->Commit(STGC_DEFAULT);
if (FAILED(hr))
{
AfxMessageBox(TEXT("can not store it"));
}
}
pStorage->Release();
}
}
27
/*配置摄像头数据源格式:分辨率、RGB/I420等*/
void CCaptureClass::ConfigCameraPin(HWND hwndParent) {
HRESULT hr;
IAMStreamConfig *pSC;
ISpecifyPropertyPages *pSpec;
//只有停止后,才能进行pin属性的设置
m_pMC->Stop();
hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,
&MEDIATYPE_Video, m_pBF,
IID_IAMStreamConfig, (void **)&pSC);
CAUUID cauuid;
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if(hr == S_OK)
{
hr = pSpec->GetPages(&cauuid);
//显示属性页
hr = OleCreatePropertyFrame(hwndParent, 30, 30, NULL, 1,
(IUnknown **)&pSC, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
//释放内存、资源
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
pSC->Release();
}
28
//回复运行
m_pMC->Run();
}
/*配置图像参数:亮度、色度、饱和度等*/
void CCaptureClass::ConfigCameraFilter(HWND hwndParent)
{
HRESULT hr=0;
ISpecifyPropertyPages *pProp;
hr = m_pBF->QueryInterface(IID_ISpecifyPropertyPages, (void
**)&pProp);
if (SUCCEEDED(hr))
{
// 获取滤波器名称和IUnknown接口指针
FILTER_INFO FilterInfo;
hr = m_pBF->QueryFilterInfo(&FilterInfo);
IUnknown *pFilterUnk;
m_pBF->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
// 显示该页
CAUUID caGUID;
pProp->GetPages(&caGUID);
OleCreatePropertyFrame(
hwndParent, // 父窗口
0, 0, // Reserved
29
FilterInfo.achName, // 对话框标题
1, // 该滤波器的目标数目
&pFilterUnk, // 目标指针数组
caGUID.cElems, // 属性页数目
caGUID.pElems, // 属性页的CLSID数组
0, // 本地标识
0, NULL // Reserved
);
// 释放内存、资源
CoTaskMemFree(caGUID.pElems);
pFilterUnk->Release();
FilterInfo.pGraph->Release();
pProp->Release();
}
//m_pMC->Run();
}
8.1.2 CaptureVideo.h : header file
#ifndef _CAPCLASS_HEAD_ #define _CAPCLASS_HEAD_
#include
#ifndef srelease
#define srelease(x) \
if ( NULL != x ) \
{ \
x->Release( ); \
x = NULL; \
30
}
#endif
class CCaptureClass
{
public:
CCaptureClass();
virtual ~CCaptureClass();
int EnumDevices(HWND hList);
void SaveGraph(TCHAR *wFileName);
void ConfigCameraPin(HWND hwndParent);
void ConfigCameraFilter(HWND hwndParent);
HRESULT CaptureImages(CString inFileName);
HRESULT PreviewImages(int iDeviceID, HWND hWnd);
private:
HWND m_hWnd;
IGraphBuilder *m_pGB;
ICaptureGraphBuilder2 *m_pCapture;
IBaseFilter *m_pBF;
IMediaControl *m_pMC;
IVideoWindow *m_pVW;
IBaseFilter *pMux;
protected:
31
bool BindFilter(int deviceId, IBaseFilter **pFilter);
void ResizeVideoWindow();
HRESULT SetupVideoWindow();
HRESULT InitCaptureGraphBuilder(); };
#endif
8.1.3CaptureVideo.cpp : 定义应用程序的类行为。
#include "stdafx.h"
#include "CaptureVideo.h"
#include "CaptureVideoDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CCaptureVideoApp
BEGIN_MESSAGE_MAP(CCaptureVideoApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP()
// CCaptureVideoApp 构造
CCaptureVideoApp::CCaptureVideoApp()
32
{
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CCaptureVideoApp 对象
CCaptureVideoApp theApp;
// CCaptureVideoApp 初始化
BOOL CCaptureVideoApp::InitInstance() {
// 如果一个运行在 Windows XP 上的应用程序指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
33
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
CCaptureVideoDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此处放置处理何时用“确定”来关闭
// 对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用“取消”来关闭
// 对话框的代码
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
8.1.4 CaptureVideo.h : PROJECT_NAME 应用程序的主头文件 #pragma once
34
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件" #endif
#include "resource.h" // 主符号
// CCaptureVideoApp:
// 有关此类的实现,请参阅 CaptureVideo.cpp //
class CCaptureVideoApp : public CWinApp
{
public:
CCaptureVideoApp();
// 重写
public:
virtual BOOL InitInstance();
// 实现
DECLARE_MESSAGE_MAP()
};
extern CCaptureVideoApp theApp; 8.1.5 CaptureVideoDlg.cpp : 实现文件
#include "stdafx.h"
#include "CaptureVideo.h" #include "CaptureVideoDlg.h"
35
#include
#include #ifdef _DEBUG
#define new DEBUG_NEW #endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支
持
// 实现
protected:
DECLARE_MESSAGE_MAP() };
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
36
void CAboutDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CCaptureVideoDlg 对话框
CCaptureVideoDlg::CCaptureVideoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCaptureVideoDlg::IDD, pParent) {
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }
void CCaptureVideoDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_VIDEO_WINDOW, m_videoWindow);
DDX_Control(pDX, IDC_COMBO1, m_listCtrl); }
BEGIN_MESSAGE_MAP(CCaptureVideoDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
37
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(ID_PREVIEW,
&CCaptureVideoDlg::OnBnClickedPreview)
ON_BN_CLICKED(ID_CAPTURE,
&CCaptureVideoDlg::OnBnClickedCapture)
ON_BN_CLICKED(ID_VIDEO_FORMAT,
&CCaptureVideoDlg::OnBnClickedVideoFormat)
ON_BN_CLICKED(ID_IMAGE_PARAMETER, &CCaptureVideoDlg::OnBnClickedImageParameter)
ON_BN_CLICKED(ID_SAVEGRAPH,
&CCaptureVideoDlg::OnBnClickedSavegraph)
ON_BN_CLICKED(ID_EXIT, &CCaptureVideoDlg::OnBnClickedExit)
END_MESSAGE_MAP()
// CCaptureVideoDlg 消息处理程序
BOOL CCaptureVideoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
38
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,
strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
//ShowWindow(SW_MINIMIZE);
// TODO: 在此添加额外的初始化代码
m_cap.EnumDevices(m_listCtrl.GetSafeHwnd());
m_listCtrl.SetCurSel (0);
m_tooltip.Create(this);
m_tooltip.Activate(TRUE);
m_tooltip.AddTool(GetDlgItem(ID_PREVIEW), _T("开始预览视频"));
m_tooltip.AddTool(GetDlgItem(ID_CAPTURE), _T("开始捕获、保存视频
"));
m_tooltip.AddTool(GetDlgItem(ID_VIDEO_FORMAT), _T("视频格式"));
m_tooltip.AddTool(GetDlgItem(ID_IMAGE_PARAMETER), _T("图像参数
"));
39
m_tooltip.AddTool(GetDlgItem(ID_SAVEGRAPH), _T("保存截图"));
m_tooltip.AddTool(GetDlgItem(ID_EXIT), _T("退出应用程序"));
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }
void CCaptureVideoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。
void CCaptureVideoDlg::OnPaint() {
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND,
40
reinterpret_cast(dc.GetSafeHdc()), 0);
// 使图标在工作矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标显示。
//
HCURSOR CCaptureVideoDlg::OnQueryDragIcon() {
return static_cast(m_hIcon); }
BOOL CCaptureVideoDlg::PreTranslateMessage(MSG* pMsg)
{
// CG: The following block was added by the ToolTips component.
41
{
// Let the ToolTip process this message.
m_tooltip.RelayEvent(pMsg);
}
return CDialog::PreTranslateMessage(pMsg); }
void CCaptureVideoDlg::OnBnClickedPreview() {
// TODO: 在此添加控件通知处理程序代码
HWND hVWindow = m_videoWindow.GetSafeHwnd();
int id = m_listCtrl.GetCurSel();
m_cap.PreviewImages(id , hVWindow); }
void CCaptureVideoDlg::OnBnClickedCapture() {
// TODO: 在此添加控件通知处理程序代码
CString filename=_T("Video");
CString formatname=_T(".avi");
CString strFilter = _T("AVI File (*.avi) | *.avi|");
strFilter += "All File (*.*) | *.*|";
CFileDialog dlg(FALSE,formatname,filename,
OFN_PATHMUSTEXIST|OFN_HIDEREADONLY, strFilter, this);
42
if (dlg.DoModal() == IDOK)
{
CString m_sourceFile = dlg.GetPathName();
m_cap.CaptureImages(m_sourceFile);
}
}
void CCaptureVideoDlg::OnBnClickedVideoFormat() {
// TODO: 在此添加控件通知处理程序代码
m_cap.ConfigCameraPin(this->m_hWnd); }
void CCaptureVideoDlg::OnBnClickedImageParameter() {
// TODO: 在此添加控件通知处理程序代码
m_cap.ConfigCameraFilter(this->m_hWnd); }
void CCaptureVideoDlg::OnBnClickedSavegraph() {
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(FALSE);
if (dlg.DoModal()==IDOK)
{
CString str=dlg.GetPathName();
TCHAR *inFileName = str.GetBuffer(str.GetLength());
str.ReleaseBuffer();
43
//m_cap.SaveGraph(inFileName);
SaveAsJPG(inFileName);
}
}
void CCaptureVideoDlg::SaveAsJPG(CString name)
{
//HWND hwnd = ::GetDesktopWindow();
HWND hwnd = ::GetActiveWindow();
//HWND hwnd = this->GetSafeHwnd();
HDC hDC = ::GetDC(hwnd);//获取屏幕DC
RECT rect;
::GetClientRect(hwnd, &rect);//获取屏幕大小
HDC hDCMem = ::CreateCompatibleDC(hDC);//创建兼容DC
HBITMAP hBitMap = ::CreateCompatibleBitmap(hDC, rect.right,
rect.bottom);//创建兼容位图
HBITMAP hOldMap = (HBITMAP)::SelectObject(hDCMem, hBitMap);//
将位图选入DC,并保存返回值
::BitBlt(hDCMem, 0, 0, rect.right, rect.bottom, hDC, 0, 0, SRCCOPY);//将
屏幕DC的图象复制到内存DC中
CImage image;
image.Attach(hBitMap);
//image.Save(_T("c://B.jpg"));//如果文件后缀为.bmp,则保存为为bmp
44
格式
CString filename;
filename = name+_T(".jpg");
image.Save(filename);
image.Detach();
::SelectObject(hDCMem, hOldMap);//选入上次的返回值
//释放
::DeleteObject(hBitMap);
::DeleteDC(hDCMem);
::DeleteDC(hDC);
}
void CCaptureVideoDlg::OnBnClickedExit()
{
// TODO: 在此添加控件通知处理程序代码
CDialog::OnOK();
}
8.1.6CaptureVideoDlg.h : 头文件
#pragma once
#include "afxwin.h"
#include "CaptureClass.h"
// CCaptureVideoDlg 对话框
class CCaptureVideoDlg : public CDialog
{
// 构造
public:
CCaptureVideoDlg(CWnd* pParent = NULL); // 标准构造函数
void SaveAsJPG(CString name);
45
// 对话框数据
enum { IDD = IDD_CAPTUREVIDEO_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
virtual BOOL PreTranslateMessage(MSG* pMsg);
// user data
CCaptureClass m_cap;
CToolTipCtrl m_tooltip;
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
// 显示捕获的图像
CStatic m_videoWindow;
public:
afx_msg void OnBnClickedPreview(); public:
afx_msg void OnBnClickedCapture(); public:
afx_msg void OnBnClickedVideoFormat(); public:
46
afx_msg void OnBnClickedImageParameter();
public:
afx_msg void OnBnClickedSavegraph(); public:
afx_msg void OnBnClickedExit(); public:
// 组合框列表,显示设备名称
CComboBox m_listCtrl;
};
8.2 界面图
8.2.1 总体界面图
47
8.2.2 视频格式设置
8.2.3 画面参数设置
48
49