1. DirectX、Directshow及Wince Directshow
1、DirectX是什么
DirectX(简称:DX)是微软推出的一套基于Windows系统的多媒体应用程式接口APIs函式。在开发中,DX分为两个部分,一个是运行库,通过DX编译出来的程式必须要有运行库的支持,另外一个是开发库,也就是常说的SDK,这部分是在编译DX程序中是必需的。
DirectX是一种图形应用程序接口(API),简单的说它是一个辅助软件,一个提高系统性能的加速软件,微软创建开发的。他的意思不难理解,Direct是直接的意思,X是很多东西,加在一起就是一组具有共性的东西,这个共性就是直接。微软定义它为“硬件设备无关性”。
DirectX 是微软开发的图形及媒体加速接口,只有安装了它,系统中软件才能比较直接的利用硬件加速资源(高速访问硬件),目前该软件最高版本为9.0c
在Windows操作系统的体系构架中,在内核与硬件之间有一层抽象层,专门对硬件进行屏蔽抽象,所以用户不再被允许对硬件进行直接访问。这样做以后,大大地提高了操作系统的抗破坏性和抗干扰性,但这样以来,使硬件操作的效率大打折扣,许多新硬件的新特性无法直接使用,这对多媒体和游戏的发展显然是一种障碍。DirectX是微软公司提供的一套优秀的应用程序编程接口(APIs),用于联系应用程序和硬件自身,它对发展Windows平台下的多媒体应用程序和电脑游戏起到了关键的作用。
DirectX组件包括:DirectDraw、DirectSound、DirectPlay、Direct3D、DirectInput、DirectSetup、AutoPlay等。
总之,DirectX的主要好处有两个:为软件开发者提供与硬件的无关性;为硬件开发提供策略。
为得到最新的版本,应该从最新的Microsoft Platform SDK中将DirectX安装到系统中。 可以在http://www.microsoft.com/msdn站点或者MSDN光盘中找到platform SDK。缺省情况下,Microsoft Platform SDK被安装到缺省驱动器根目录下的\MSSDK目录中。DirectX 的头文件安装在\MSSDK\INCLUDE目录中,Lib文件安装在\MSSDK\LIB目录中。
Platform SDK包含了一些非常好的DirectX例子和文档。早期发布的DirectX 文档非常粗略而且有些是错误的,现在的版本已经极大地改正了这一问题。最好要熟悉这些文档。
所幸的是,不必一次就处理DirectX的全部功能。DirectX是一套可以分别使用的组件。实际上,在编程概念中,DirectX的不同部分互相没有联系。它们仅仅是具有相同的设计风格和目标:使Windows的游戏编程变得容易。
使用DirectX组件的程序有什么特殊的地方吗?根本没有。使用DirectX组件的程序是基于Win32的程序,它们使用普通Win32 API集,并且可以访问所有可以获得的操作系统工具。实际上,DirectX既可以用于GUI程序,也可以用于控制台程序。可以直接用Petzold-style SDK编程开发程序,也可以用基本类库,如MFC。总的说,唯一的要求是大多数DirectX组件在程序中需要HWND,所以至少要有一个窗口。
2、DirectX 9.0 家族的所有成员
DirectX Graphics:集成了以前的DirectDraw 和Direct3D技术。DirectDraw主要负责2D加速,以实现对显卡内存和系统内存的直接操作;Direct3D主要提供三维绘图硬件接口,它是开发三维DirectX游戏的基础。
DirectInput:主要支持输入服务(包括鼠标、键盘、游戏杆等),同时支持输出设备。
DirectPlay:主要提供多人网络游戏的通信、组织功能。
DirectSetup:主要提供自动安装DirectX组件的API功能。
DirectMusic:主要支持MIDI音乐合成和播放功能。
DirectSound:主要提供音频捕捉、回放、音效处理、硬件加速、直接设备访问等功能。
DirectShow:为Windows平台上处理各种格式的媒体文件的回放、音视频采集等高性能要求的多媒体应用,提供了完整的解决方案。
DirectX Media Objects:DirectShow Filter 的简化模型,提供更方便的流数据处理方案。
3、DirectX的作用
DirectX软件开发包是微软公司提供的一套Windows操作平台上的开发高性能图形、声音、输入、输出和网络游戏的编程接口。它提供了硬件设备无关性。
4、DirectShow的由来及介绍
DirectShow是从DirectX6.0中的DirectX Media 发展而来的,它集成了DirectX家族中其他成员(DirectDraw、DirectSound等)的技术。DirectX Media Objects是从DirectX8.1的DirectShow中分离出来的,成为了另一种高效率的流数据处理解决方案。
Microsoft DirectShow是一个基于Microsoft Windows平台的媒体流结构。它支持各种格式,包括高级流模式(ASF)、运动图像专家组(MPEG)、音频视频交错(AVI)、音频动态压缩第三层(MP3)和WAV声音文件。它支持Windows驱动模式(WDM)设备的捕捉,以及早期Widows设备的视频。DirectShow结合了其它的DirectX技术。当视频和音频的硬件加速可用时,它能够自动检测并进行使用,同时也支持没有硬件加速的系统。
DirectShow媒体重放、格式转换和捕捉的任务变得简单。同时,它为需要自定义的解决方案的应用程序提供了对底层流控制结构的访问。您也可以创建自己的DirectShow组件,来支持新的格式或自定义效果。
使用DirectShow的应用程序类型包括DVD播放器、视频编辑应用程序、AVI到ASF的转换器、MP3播放器和数字视频捕捉应用程序。
DirectShow是基于组件对象模型(COM)的。要做一个DirectShow的应用程序,您必须了解COM客户端编程。对于大多数应用程序,您不需要实现您自己的COM对象。DirectShow提供了您所需要的组件。(如果您想写自己的组件扩展DirectShow,则必须以COM对象的方式来实现。)
5、程序开发包的选择
请问在Mirosoft DirectX主页上的下载连接有如下:
它们有什么区别:
l DirectX 9.0 SDK Update - (April 2005)
l DirectX 9.0 SDK Update - (April 2005) Symbol Files
l DirectX 9.0c Redistributable for Software Developers - (April 2005)
l DirectX 9.0c End-User Runtime
--------------------------------------------------------------------------------
l DirectX 9.0 SDK Update - (April 2005) - 就是通常说的DXSDK
l DirectX 9.0 SDK Update - (April 2005) Symbol Files - 调试时用的符号文件
l DirectX 9.0c Redistributable for Software Developers - (April 2005) - 能够随你的程序一起发行的部分
l DirectX 9.0c End-User Runtime - 普通用户安装的DX
6、如何把DirectX关联到VC中?
我们要进行DirectX程序的编译就必须要有DirectX SDK库文件,此文件可以到微软或者本站获取,然后通过VC设置将其关联。下面说明了在VC6和VS.Net下的安装方法。
VC6(英文版):选择菜单Tools->Options,打开Options对话框,选择Directions标签页,选择Include files项,在里面添加DirectX头文件的文件夹路径目录,同样,在Library files项中添加DirectX头文件的文件夹路径目录。
VS.Net(中文版):选择菜单"工具->选项",打开选项对话框,打开Projects标签页,分别选择"包含文件"和"库文件"进行相应的路径添加即可。
注:VC在进行编译时,会根据排列顺序来进行库文件选取,假设有两个相同名字的库,VC会优先使用排列在前面的库文件。
DirectShow应用程序至少连接库文件Strmiids.lib和Quartz.lib。前者定义了SirectShow标准的CLSID和IID,后者定义了导出函数AMGetErrorText(如果应用程序中没有使用到这个函数,也可以不连接这个库)。
DirectShow应用程序都应该包含Dshow.h文件,但常常用Streams.h文件来代替它。如果包含了Streams.h,则一般库文件还要连接strmbasd.lib、uuid.lib和winmm.lib。
7、DirectShow开发环境的配置
(1)使用VC向导生成一个具体项目,如Win32 Dynamic-Link;
(2)包含头文件streams.h;
(3)在VC的菜单中选择Project|Settings|C/C++,在弹出的对话框中的Category中选择Code generation,然后在Calling convention中选择_stdcall;
(4)使用多线程语言运行时库,即在VC的菜单中选择Project|Settings|C/C++,在弹出的对话框中的Category中选择Code generation,然后在Use run-time library中,Debug版选择Debug Multithreaded,Release版选择Multithreaded。
(5)配置必要的链接库文件,即在VC的菜单中选择Project|Settings|Link,在弹出的对话框中的Category中选择General,然后在Object/library modules中输入如下代码:
Debug版本 strmbasd.lib, msvcrtd.lib, winmm.lib
Release版本 strmbase.lib, msvcrt.lib, winmm.lib
并且选中Ignore all default libraries。
DirectShow SDK建议,DirectShow应用程序应该至少连接库文件strmiids.lib和quartz.lib。前者定义了DirectShow标准的CLSID和IID,后者定义了导出函数AMGetErrorText(如果应用程序中没有使用到这个函数,也可以不连接这个库)。如果程序里包含了头文件streams.h,则一般库文件还要连接strmbasd.lib、uuid.lib、winmm.lib。
(6)将DirectX SDK的Include和Lib目录配置到VC的系统目录中去,并且放在标准的VC目录之前,以保证编译器能够拿到最新版本的源文件。选择Tools|Options|Directories,在弹出的对话框中的Show directories for中选择Include files,配置如下(假设DirectX SDK安装在D:\DXSDK目录下,VC安装在C:\Program Files下):
D:\DXSDK\Include
D:\DXSDK\SAMPLES\C++\DIRECTSHOW\BASECLASSES
D:\DXSDK\SAMPLES\C++\COMMON\INCLUDE
C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE
C:\Program Files\Microsoft Visual Studio\VC98\MFC\INCLUDE
C:\Program Files\Microsoft Visual Studio\VC98\ATL\INCLUDE
再在Show directories for中选择Library files,配置如下:
D:\DXSDK\Lib
D:\DXSDK\SAMPLES\C++\DIRECTSHOW\BASECLASSES\DEBUG
D:\DXSDK\SAMPLES\C++\DIRECTSHOW\BASECLASSES\RELEASE
C:\PROGRAM FILES\MICROSOFT SDK\LIB
C:\Program Files\Microsoft Visual Studio\VC98\LIB
C:\Program Files\Microsoft Visual Studio\VC98\MFC\LIB
(7)因为DirectShow应用程序是一种COM客户程序,因此在调用任何COM函数之前调用CoInitialize()(或CoInitializeEx)函数进行COM库的初始化(一般是在应用程序启动的时候调用一次),在结束COM库使用时调用CoUninitialize()函数进行反初始化(一般是在应用程序退出前调用一次)。
8、VC编译DX程序出现"无法解析的外部符号"是怎么回事?
这个错误经常出现在初学者要进行编译DirectX程序的时候,主要是因为没有将DX的库文件引用到工程中,这里需要注意,我们将DX SDK的路径设置到VC后,并不代表我们已设置好了DX SDK,在我们的DX工程中,我们还需要进行相应的设置操作,把我们所需要的库文件(DirectX SDK Library)加入到我们的工程中,要设置这个库文件有两个方法,一个是在你工程的编译选项中进行添加,另外一种可以通过代码的方法来添加(推荐)。
命令行:#pragma comment( lib,"xxx.lib" )
这个是VC的编译预处理指令,将其加在代码中即可。
例如:#pragma comment( lib,"ddraw.lib" ) 这句的意思是将ddraw.lib库加入到工程中进行编译。
注:此命令行不需要加分号(“;”)。
9、DirectShow SDK基类库
在DirectShow SDK基类库中,除了Filter和Pin类外,还有很多工具类。有了这些类的支持,我们开发Filter组件或者DirectShow应用程序会更加轻松。这些类主要包括:CPullPin、COutputQueue、CSourceSeeking、CEnumPins、CEnumMedieTypes、CMemAllocator、CMediaSample、CBaseReferenceClock、CMediaType、CBaseProperyPage等。
10、DirectShow的COM编程基础
DirectX采用了COM标准,而DirectShow是一套完全基于COM的应用系统。
DirectShow应用程序实际上是一种COM组件的客户程序,只是COM组件的“使用”问题。这些问题包括如何创建COM组件、如何得到组件对象上的解风口以及调用接口方法、如何管理组件对象(即需要熟悉COM的引用计数机制)等。
而对于Filter开发人员来说,需要掌握的COM知识就要多一点。因为Filter本身是一种COM组件,开发Filter牵涉到了COM组件的“实现”问题。
COM本身只是一种规范,而不是实现。但是当使用C++来实现时,COM组件就是一个C++类,而接口都是纯虚类。COM规范规定,任何组件或接口都必须从IUnknown接口中继承而来,每个组件都必须实现一个与支相对应的类工厂(Class Factory),类工厂也是一个COM组件,他实现了IClassFactory接口。在IClassFactory的接口函数CreateInstance中,才能使用new操作生成一个与之对应的COM组件类对象实例。
COM组件有3种类型:进程内组件、本地进程组件和远程组件。Filter一般是一种进程内组件,以DLL的形式提供服务。
每个COM组件都使用一个GUID来唯一标识。当创建一个COM组件时,总是首先通过这个GUID调用CoGetClassObject来获得创建这个组件对象的类工厂。然后调用类工厂的接口方法IClassFactory::CreateInstance,就能真正地创建GUID标示的组件对象了。
一个典型的自注册COM组件DLL所必需的5个函数如下:
l DllMain:DLL的入口函数(DirectShow实现的是DllEntryPoint);
l DllGetClassObject:用于获得类工厂指针;
l DllCanUnloadNow:系统空闲时会调用这个函数,以确定是否可以卸载DLL;
l DllRegisterServer:将COM组件注册到注册表中;
l DllUnregisterServer:删除注册表中COM组件的注册信息。
11、wince6.0 DirectShow
wince下的directshow 是pc directshow的子集,在wince下进行音视频的应用开发,不需要安装专门的directx SDK,其所需要的头文件与库函数包含在你选择的平台的SDK中,当然,在你所运行的系统中,要选择上所需的directshow的组件,比如video capture及音视频的编解码器!
2.
微软很想放弃XP甚至Vista,想强迫所有用户升级到Windows 7。而DirectShow太依赖于旧的显示架构(包括早已经被放弃的DirectDraw)。现在DirectX的新版本,甚至连Direct2D这么简单的东西,都只支持Windows 7,未来的方向,微软是很明确的。
随着Windows操作系统的不断演变,用于捕获视频的API接口也在进化,微软提供了VFW、DirectShow和MediaFoundation这三代接口。其中VFW早已被DirectShow取代,而最新的MediaFoundation被Windows Vista和Windows 7所支持。
随着Windows操作系统的不断演变,用于捕获视频的API接口也在进化,微软提供了VFW、DirectShow和MediaFoundation这三代接口。其中VFW早已被DirectShow取代,而最新的MediaFoundation被Windows Vista和Windows 7所支持。可惜的是,上述接口基于COM技术且灵活性很大,在.net中并不方便直接使用。
.net封装
老外有很多活雷锋,他们奉献了不少的开源项目,DirectShow.net是对DirectShow的封装,而MediaFoundation.net是对MediaFoundation的封装。它们都能在http://sourceforge.net上找到。这两个封装之后的类库基本上跟原来的COM是一一对应的关系,可以用于视频捕获,但是用起来还是不够简便。
通过不断的google搜索,我认为以下类库对视频捕获封装得不错,它们是:DirectX.Capture、OpenCv、EmguCv和AForge。
DirectX.Capture
DirectX.Capture是发表在CodeProject上的一个项目,它能很方便的捕获视频和音频,在窗口预览,并将结果保存到文件。使用DirectX.Capture的示例如下:
DirectX.Capture
双击代码全选
1
2
3
4
5
6 Capture capture = new Capture( Filters.VideoInputDevices[0],
Filters.AudioInputDevices[1] );
capture.Filename = "C:\MyVideo.avi";
capture.Start();
//...
capture.Stop();
但是,它没有提供单独获取某帧内容的方法。如果您只是需要预览并保存视频,它很好用。
OpenCv
OpenCv对VFW和DirectShow的视频捕获部分进行了很好的封装,能够很方便的获取到某帧的内容,也可以将结果保存到视频文件中。使用OpenCv的示例如下:
OpenCv
双击代码全选
1
2
3
4
5
6
7
8
9
10 IntPtr ptrCapture = CvInvoke.cvCreateCameraCapture(param.deviceInfo.Index);
while (!stop)
{
IntPtr ptrImage = CvInvoke.cvQueryFrame(ptrCapture);
lock (lockObject)
{
stop = stopCapture;
}
}
CvInvoke.cvReleaseCapture(ref ptrCapture);
不过OpenCv并未对音频捕获进行封装,如果需要同时录制音频,这个搞不定。
值得注意的是,从OpenCv 1.1开始已经实现了对DirectShow的封装,这跟网上很多人所说的OpenCv使用VFW进行视频捕获效率低下这种观点不一致。关于OpenCv使用DirectShow的论据请看本文的附录。
EmguCv
EmguCv是对OpenCv在.net的封装,继承了OpenCv快速的优点,同时它更加好用。使用EmguCv的示例代码如下:
EmguCv
双击代码全选
1
2
3
4
5
6
7
8
9
10 Capture capture = new Capture(param.deviceInfo.Index);
while (!stop)
{
pbCapture.Image = capture.QueryFrame().Bitmap;
lock (lockObject)
{
stop = stopCapture;
}
}
capture.Dispose();
AForge
AForge是一套纯正的.net开源图像处理类库,它的视频捕获类也是基于DirectShow的,但更加好用,功能更多,从使用和帮助来看更类似微软的类库。
AForge
双击代码全选
1
2
3
4
5
6
7
8
9 captureAForge = new VideoCaptureDevice(cameraDevice.MonikerString);
captureAForge.NewFrame += new NewFrameEventHandler(captureAForge_NewFrame);
captureAForge.Start();
//...
captureAForge.SignalToStop();
private void captureAForge_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
pbCapture.Image = (Bitmap)eventArgs.Frame.Clone();
}
查看原图(大图)
对比
介绍完它们之后,我们来比较下它们。它们都是基于DirectShow的,所以性能几乎一样。实际上,我个人认为,摄像头所用的硬件和驱动程序的支持对性能影响更大。我的摄像头在Windows 7下没有专门的驱动程序,只能使用Microsoft提供的默认驱动,性能比WindowsXp要差一截。
值得注意的是主要有几点:
(1)只有DirectX.Capture实现了对音频的捕获;
(2)只有DirectX.Capture不能获取单独的某帧图像;
(3)EmguCv的免费版基于商业许可,而其他类库的许可都很宽松;
(4)AForge的示例和帮助比较好,而且功能多些。
附录:OpenCv也用DirectShow来捕获视频
通过分析OpenCv 2.0的源代码,我得出了OpenCv使用DirectShow来捕获视频的结论。证据如下:
DirectShow In OpenCv
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 (1)
//_highgui.h line:100
#if (_MSC_VER >= 1400 || defined __GNUC__) && !defined WIN64 && !defined _WIN64
#define HAVE_VIDEOINPUT 1
#endif
(2)
//cvcap_dshow.cpp line:44
#ifdef HAVE_VIDEOINPUT
#include "videoinput.h"
/********************* Capturing video from camera via VFW *********************/
class CvCaptureCAM_DShow : public CvCapture
(3)
//cvapp.cpp line:102
CV_IMPL CvCapture * cvCreateCameraCapture (int index)
{
//.....
//line:140
switch (domains[i])
{
#ifdef HAVE_VIDEOINPUT
case CV_CAP_DSHOW:
capture = cvCreateCameraCapture_DShow (index);
if (capture)
return capture;
break;
#endif
本文完整源代码
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using AForge.Video;
using AForge.Video.DirectShow;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using System.Threading;
namespace ImageProcessLearn
{
public partial class FormCameraCapture : Form
{
private int framesCaptured; //已经捕获的视频帧数
private int frameCount; //需要捕获的总帧数
private Stopwatch sw; //计时器
private VideoCaptureDevice captureAForge = null; //AForge视频捕获对象
private bool stopCapture; //是否停止捕获视频
private object lockObject = new object();
public FormCameraCapture()
{
InitializeComponent();
sw = new Stopwatch();
}
//窗体加载时,获取视频捕获设备列表
private void FormCameraCapture_Load(object sender, EventArgs e)
{
FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices != null && videoDevices.Count > 0)
{
int idx = 0;
foreach (FilterInfo device in videoDevices)
{
cmbCaptureDevice.Items.Add(new DeviceInfo(device.Name, device.MonikerString, idx, FilterCategory.VideoInputDevice));
idx++;
}
cmbCaptureDevice.SelectedIndex = 0;
}
}
//当改变视频设备时,重新填充该设备对应的能力
private void cmbCaptureDevice_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbCaptureDevice.SelectedItem != null)
{
//保存原来选择的设备能力
Size oldFrameSize = new Size(0, 0);
int oldMaxFrameRate = 0;
if (cmbDeviceCapability.SelectedItem != null)
{
oldFrameSize = ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize;
oldMaxFrameRate = ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).MaxFrameRate;
}
//清除设备能力
cmbDeviceCapability.Items.Clear();
//添加新的设备能力
int oldCapIndex = -1; //原来选择的设备能力的新索引
VideoCaptureDevice video = new VideoCaptureDevice(((DeviceInfo)cmbCaptureDevice.SelectedItem).MonikerString);
for (int i = 0; i < video.VideoCapabilities.Length; i++)
{
VideoCapabilities cap = video.VideoCapabilities[i];
DeviceCapabilityInfo capInfo = new DeviceCapabilityInfo(cap.FrameSize, cap.MaxFrameRate);
cmbDeviceCapability.Items.Add(capInfo);
if (oldFrameSize == capInfo.FrameSize && oldMaxFrameRate == capInfo.MaxFrameRate)
oldCapIndex = i;
}
//重新选择原来的设备能力,或者选一个新的能力
if (oldCapIndex == -1)
oldCapIndex = 0;
cmbDeviceCapability.SelectedIndex = oldCapIndex;
}
}
//当改变设备能力时
private void cmbDeviceCapability_SelectedIndexChanged(object sender, EventArgs e)
{
if (int.Parse(txtRate.Text) >= ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).MaxFrameRate)
txtRate.Text = ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).MaxFrameRate.ToString();
}
//性能测试:测试获取指定帧数的视频,并将其转换成图像,所需要的时间,然后计算出FPS
private void btnPerformTest_Click(object sender, EventArgs e)
{
int frameCount = int.Parse(txtFrameCount.Text);
if (frameCount <= 0)
frameCount = 300;
DeviceInfo device = (DeviceInfo)cmbCaptureDevice.SelectedItem;
btnPerformTest.Enabled = false;
btnStart.Enabled = false;
txtResult.Text += PerformTestWithAForge(device.MonikerString, frameCount);
txtResult.Text += PerformTestWithEmguCv(device.Index, frameCount);
txtResult.Text += PerformTestWithOpenCv(device.Index, frameCount);
btnPerformTest.Enabled = true;
btnStart.Enabled = true;
}
//AForge性能测试
private string PerformTestWithAForge(string deviceMonikerString, int frameCount)
{
VideoCaptureDevice video = new VideoCaptureDevice(deviceMonikerString);
video.NewFrame += new NewFrameEventHandler(PerformTest_NewFrame);
video.DesiredFrameSize = ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize;
video.DesiredFrameRate = int.Parse(txtRate.Text);
framesCaptured = 0;
this.frameCount = frameCount;
video.Start();
sw.Reset();
sw.Start();
video.WaitForStop();
double time = sw.Elapsed.TotalMilliseconds;
return string.Format("AForge性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());
}
void PerformTest_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
framesCaptured++;
if (framesCaptured > frameCount)
{
sw.Stop();
VideoCaptureDevice video = sender as VideoCaptureDevice;
video.SignalToStop();
}
}
//EmguCv性能测试
private string PerformTestWithEmguCv(int deviceIndex, int frameCount)
{
Capture video = new Capture(deviceIndex);
video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Width);
video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Height);
video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FPS, double.Parse(txtRate.Text));
sw.Reset();
sw.Start();
for (int i = 0; i < frameCount; i++)
video.QueryFrame();
sw.Stop();
video.Dispose();
double time = sw.Elapsed.TotalMilliseconds;
return string.Format("EmguCv性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());
}
//OpenCv性能测试
private string PerformTestWithOpenCv(int deviceIndex, int frameCount)
{
IntPtr ptrVideo = CvInvoke.cvCreateCameraCapture(deviceIndex);
CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Width);
CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Height);
CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FPS, double.Parse(txtRate.Text));
sw.Reset();
sw.Start();
for (int i = 0; i < frameCount; i++)
CvInvoke.cvQueryFrame(ptrVideo);
sw.Stop();
CvInvoke.cvReleaseCapture(ref ptrVideo);
double time = sw.Elapsed.TotalMilliseconds;
return string.Format("OpenCv性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());
}
//得到设置所对应的字符串
private string GetSettings()
{
return string.Format("摄像头:{0},尺寸:{1}x{2},FPS:{3}", ((DeviceInfo)cmbCaptureDevice.SelectedItem).Name,
((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Width,
((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize.Height,
txtRate.Text);
}
//开始捕获视频
private void btnStart_Click(object sender, EventArgs e)
{
//得到设置项
DeviceInfo cameraDevice = (DeviceInfo)cmbCaptureDevice.SelectedItem;
Size frameSize = ((DeviceCapabilityInfo)cmbDeviceCapability.SelectedItem).FrameSize;
int rate = int.Parse(txtRate.Text);
ThreadParam param = new ThreadParam(cameraDevice, new DeviceCapabilityInfo(frameSize, rate));
if (rbAForge.Checked)
{
captureAForge = new VideoCaptureDevice(cameraDevice.MonikerString);
captureAForge.DesiredFrameSize = frameSize;
captureAForge.DesiredFrameRate = rate;
captureAForge.NewFrame += new NewFrameEventHandler(captureAForge_NewFrame);
txtResult.Text += string.Format("开始捕获视频(方式:AForge,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString());
framesCaptured = 0;
sw.Reset();
sw.Start();
captureAForge.Start();
}
else if (rbEmguCv.Checked)
{
stopCapture = false;
Thread captureThread = new Thread(new ParameterizedThreadStart(CaptureWithEmguCv));
captureThread.Start(param);
}
else if (rbOpenCv.Checked)
{
stopCapture = false;
Thread captureThread = new Thread(new ParameterizedThreadStart(CaptureWithOpenCv));
captureThread.Start(param);
}
btnStart.Enabled = false;
btnStop.Enabled = true;
btnPerformTest.Enabled = false;
}
private void captureAForge_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
pbCapture.Image = (Bitmap)eventArgs.Frame.Clone();
lock (lockObject)
{
framesCaptured++;
}
}
//EmguCv视频捕获
private void CaptureWithEmguCv(object objParam)
{
bool stop = false;
int framesCaptured = 0;
Stopwatch sw = new Stopwatch();
txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("开始捕获视频(方式:EmguCv,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString()));
ThreadParam param = (ThreadParam)objParam;
Capture capture = new Capture(param.deviceInfo.Index);
capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, param.deviceCapability.FrameSize.Width);
capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, param.deviceCapability.FrameSize.Height);
capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FPS, param.deviceCapability.MaxFrameRate);
sw.Start();
while (!stop)
{
pbCapture.Image = capture.QueryFrame().Bitmap;
framesCaptured++;
lock (lockObject)
{
stop = stopCapture;
}
}
sw.Stop();
txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("捕获视频结束(方式:EmguCv,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",
DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds));
capture.Dispose();
}
//OpenCv视频捕获
private void CaptureWithOpenCv(object objParam)
{
bool stop = false;
int framesCaptured = 0;
Stopwatch sw = new Stopwatch();
txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("开始捕获视频(方式:OpenCv,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString()));
ThreadParam param = (ThreadParam)objParam;
IntPtr ptrCapture = CvInvoke.cvCreateCameraCapture(param.deviceInfo.Index);
CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, param.deviceCapability.FrameSize.Width);
CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, param.deviceCapability.FrameSize.Height);
CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FPS, param.deviceCapability.MaxFrameRate);
sw.Start();
while (!stop)
{
IntPtr ptrImage = CvInvoke.cvQueryFrame(ptrCapture);
MIplImage iplImage = (MIplImage)Marshal.PtrToStructure(ptrImage, typeof(MIplImage));
Image image = new Image(iplImage.width, iplImage.height, iplImage.widthStep, iplImage.imageData);
pbCapture.Image = image.Bitmap;
//pbCapture.Image = ImageConverter.IplImagePointerToBitmap(ptrImage);
framesCaptured++;
lock (lockObject)
{
stop = stopCapture;
}
}
sw.Stop();
txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("捕获视频结束(方式:OpenCv,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",
DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds));
CvInvoke.cvReleaseCapture(ref ptrCapture);
}
//停止捕获视频
private void btnStop_Click(object sender, EventArgs e)
{
if (captureAForge != null)
{
sw.Stop();
if (captureAForge.IsRunning)
captureAForge.SignalToStop();
captureAForge = null;
txtResult.Text += string.Format("捕获视频结束(方式:AForge,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",
DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds);
}
lock (lockObject)
{
stopCapture = true;
}
btnStart.Enabled = true;
btnStop.Enabled = false;
btnPerformTest.Enabled = true;
}
//用于在工作线程中更新结果的委托及方法
public delegate void AddResultDelegate(string result);
public void AddResultMethod(string result)
{
txtResult.Text += result;
}
}
//设备信息
public struct DeviceInfo
{
public string Name;
public string MonikerString;
public int Index;
Guid Category;
public DeviceInfo(string name, string monikerString, int index) :
this(name, monikerString, index, Guid.Empty)
{
}
public DeviceInfo(string name, string monikerString, int index, Guid category)
{
Name = name;
MonikerString = monikerString;
Index = index;
Category = category;
}
public override string ToString()
{
return Name;
}
}
//设备能力
public struct DeviceCapabilityInfo
{
public Size FrameSize;
public int MaxFrameRate;
public DeviceCapabilityInfo(Size frameSize, int maxFrameRate)
{
FrameSize = frameSize;
MaxFrameRate = maxFrameRate;
}
public override string ToString()
{
return string.Format("{0}x{1} {2}fps", FrameSize.Width, FrameSize.Height, MaxFrameRate);
}
}
//传递到捕获视频工作线程的参数
public struct ThreadParam
{
public DeviceInfo deviceInfo;
public DeviceCapabilityInfo deviceCapability;
public ThreadParam(DeviceInfo deviceInfo, DeviceCapabilityInfo deviceCapability)
{
this.deviceInfo = deviceInfo;
this.deviceCapability = deviceCapability;
}
3.DirectShow 的换代产品是Media Foundation,它将取代DirectShow 就象当年DirectShow 取代 Video for Windows 一样.
VS2002中包含有Windows SDK 2002版(支持Windows XP及其以前的Windows 并包含有DirctX 8.1 SDK(包括DirectShow SDK)),这之后DirectX9 SDK不被包含在Windows SDK中.
VS2003中包含有Windows SDK 2003版(支持Windows Server 2003及其以前的Windows)和DirectX 8.1 SDK(其中包括DirectShow8 SDK)
DirectX 9.0C SDK 的第一个版本是DirectX SDK Summer 2004也是整合有DirectShow SDK 的最后一个版本,这之后面向DirectX 9.0C用C++开发应用程序没有发生太大变化,以后的版本主要是针对MDX做的改动,如果在VS2003下用C++开发DirectShow应用程序使用这个版本最合适.另外Windows XP SP2 中包含的DirectX 的版本就是9.0c ,这也是广泛使用的DirectX版本.Windows SDK 2004版,支持Windows XP SP2及其以前的Windows 但不包含DirectX SDK.
之后 DirectShow SDK 不被整合在DirectX SDK中而是以Extras 的形式发布.DirectShow SDK的最后一个版本是在2005.2版的DirectX SDK Extras中,这之后DirectShow SDK被整合在Windows SDK中 Windows SDK 2005版(支持Windows Server 2003 SP1 及其之前的Windows)中包含有DirectShow SDK,Windows SDK 2006年3月版(支持Windows Server 2003 R2及其之前的Windows)也包含.
VS2005 中包含有Windows SDK 2005 版,但是没有DirectShow SDK,所以在VS2005中开发DirectShow 应用程序要手工安装DirectX SDK Extras 2005,2 版,和2005年6月之后的DirectX SDK,因为DirectShow 会调用DirectDraw,DirectSound等,DirectX SDK 从2005.6开始支持VS2005,我使用的是2005.8版,在这之后的DirectX SDK中,主要多加入了MDX 2 beta 而这个MDX 2 beta在2006.10中取消了,而被一个叫做XNAGS的东西取代了.
Windows SDK for Vista 已经正式发布,DirectShow的示例被更新为VS2005版本,DirectShow 的换代产品是Media Foundation,它将取代DirectShow 就象当年DirectShow 取代 Video for Windows 一样.
4.
重要提醒:
为什么你下载下来的DirectShow.NET咱们不用那?
- 因为DirectShow是C++的,所以咱们下载DirectShow.NET。可以在.NET平台下使用。C++转C#。
- 因为DirectShow本身对于工作流的机制太复杂。老外又对它进行了更好的封装。再DirectShow.NET版本之上又抽象了个DirectX.Capture(和DirtectX无关,只是个名字。)
- 使用时,调用DirectX.Capture类库的方法。但是DirectX.capture是基于DirectShow.NET的。所以。DirectX.capture要引用DirectShow.NET.DLL。
- 而在我们项目中,根据我们的功能使用或者修改DirectX.Capture就可以了。