在软件的设计中,使用类的概念可以提高程序的简洁性及可读性,以及一系列好处。在软件设计中,需要实现多个摄像头的摄像及显示,我试图把视频的设备获取,创建DirectShow Filter,连接DirectShow Filter,对视频的控制等等操作封装到一个CVideo类中,这样程序简洁明了,易于操作,不容易出错,提高了程序的鲁棒性。
1. 类的定义:
在现实世界中,经常有属于同一类的对象。例如,你的自行车只是世界上很多自行车中的一辆。在面向对象软件中,也有很多共享相同特征的不同的对象:矩形、雇用记录、视频剪辑等。可以利用这些对象的相同特征为它们建立一个蓝图。对象的软件蓝图称为类。
类是定义同一类所有对象的变量和方法的蓝图或原型。例如,可以建立一个定义包含当前档位等实例变量的自行车类。这个类也定义和提供了实例方法(变档、刹车)的实现。实例变量的值由类的每个实例提供。因此,当你创建自行车类以后,必须在使用之前对它进行实例化。当创建类的实例时,就建立了这种类型的一个对象,然后系统为类定义的实例变量分配内存。然后可以调用对象的实例方法实现一些功能。相同类的实例共享相同的实例方法。
C++ 程序设计允许程序员使用类(class)定义特定程序中的数据类型。这些数据类型的实例被称为对象,这些事例可以包含程序员定义的成员变量、常量、成员函数,以及重载的运算符。语法上,类是C中结构体(struct)的扩展,结构体不能包含函数以及重载的运算符。
2. 区别:类和对象
对象和类的说明很相似。实际上,类和对象之间的差别经常是一些困惑的起源。在现实世界中很明显,类不是它描述的对象 - 自行车的蓝图不是自行车。但是在软件中就有点难区分类和对象。这部分是由于软件对象只是现实世界的电子模型或抽象概念。但是也由于很多人用“对象”指类和它们的实例这两者。
3. VideoCapture中封装类的具体实现
(a)利用ClassWizard新建一个基于CWnd的MFC类,类名CVideo。
(b)在Attributes中定义变量IGraphBuilder * pGraph;用于创建Graph Filter Manager
定义ICaptureGraphBuilder2 *pBuilder2;用于创建Graph Stream流
定义IMediaControl *pControl;用于媒体流的控制
定义IMediaEvent *pEvent;用于控制媒体流事件
定义IVideoWindow *pWindow;视频显示的窗口
(c)在类定义的头文件中,变量是不能被初始化的,变量的初始化在Video类的构造函数中,在CPP文件中,即CVideo::CVideo()中,定义5个指针变量的初始值为NULL,指针定义的使用之前一定要初始化,这里为NULL。
CVideo的成员函数
GetInterface()创建Filter Graph的接口
GetDevice()获得系统的摄像头设备
MediaControl()对媒体流进行控制
Void CVideo::GetInterface() //创建Filter Graph的接口
{
//Graph接口的创建
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **) &pGraph);
if (FAILED(hr))
MessageBox("create graph failed");
// capture graph builder的创建
hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **) &pBuilder2);
if(FAILED(hr))
MessageBox("create capture graph failed");
// 把filter graph 连接到 capture graph
hr = pBuilder2->SetFiltergraph(pGraph);
// 获得媒体控制和媒体窗口
hr = pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &pControl);
if (FAILED(hr))
MessageBox("error");
hr = pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &pWindow);
if (FAILED(hr))
MessageBox("error");
hr = pGraph->QueryInterface(IID_IMediaEvent, (LPVOID *) &pEvent);
if (FAILED(hr))
MessageBox("error");
}
Void CVideo:: GetDevice() //获得系统的摄像头设备
{
ICreateDevEnum *pDevEnum = NULL;//创建一个设备枚举器
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **) &pDevEnum);
if(FAILED(hr))
MessageBox("error");
IEnumMoniker *pClassEnum = NULL;//创建一个设备媒体类型
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if(FAILED(hr))
MessageBox("error");
if(pClassEnum==NULL)
{
MessageBox("pClassEnum=NULL");
}
// Use the first video capture device on the device list.
// Note that if the Next() call succeeds but there are no monikers,
// it will return S_FALSE (which is not a failure). Therefore, we
// check that the return code is S_OK instead of using SUCCEEDED() macro.
IMoniker *pMoniker =NULL; //创建一个媒体设备
IBaseFilter * pSrc = NULL; //指向设备的指针
ULONG cFetched;
int count = 0;
while(S_OK == (pClassEnum->Next(1,&pMoniker,&cFetched)))
{
//把设备绑定到设备指针
count++;
if(count == 1)
{
hr = pMoniker->BindToObject(0,0,IID_IBaseFilter,(void**)&pSrc);
if(FAILED(hr))
MessageBox("pMoniker Failed");
}
}
}
4. 性能测试
以上就是一个完整的摄像机视频类,在主类的程序中如果需要添加一个摄像头视频,只要在类中定义一个CVideo的类对象即可。CVideo类的对象完成摄像头的Filter创建,连接,如果需要对摄像头视频进行控制,直接调用CVideo类对象的成员函数。如果有多个视频,各个视频之间的控制不会打架。