9.1.1 采集设备的枚举
使用采集设备前,需要首先确定系统已经安装的采集设备:视频、音频采集设备。系统设备枚举器为按类型枚举已注册在系统中的滤波器提供了统一的方法。而且它能够区分不同的硬件设备,即便是同一个滤波器支持它们。这对那些使用Windows驱动模型、KSProxy Filter的设备来说是非常有用的,系统设备枚举器对它们按不同的设备实例进行对待。
当利用系统设备枚举器查询设备的时候,系统设备枚举器为特定类型的设备(如音频捕获和视频压缩)生成了一张枚举表(Enumerator)。类型枚举器(Category Enumerator)为每个这种类型的设备返回一个Moniker,类型枚举器自动把每种即插即用的设备包含在内。
调用标准方法CoCreateInstance生成系统设备枚举器(Device Enumerator),类标识(CLSID)为CLSID_SystemDeviceEnum,代码如下。
//枚举捕获设备 ICreateDevEnum *pCreateDevEnum; //创建设备枚举器 //创建设备枚举管理器 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, //要创建的Filter的Class ID NULL, //表示Filter不被聚合 CLSCTX_INPROC_SERVER, //创建进程内COM对象 IID_ICreateDevEnum, //获得的接口ID (void**)&pCreateDevEnum); //创建的接口对象的指针 if (hr != NOERROR) return -1;
|
函数原型为:
HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); |
第1个参数clsid是一个GUID,它可唯一指定客户端需要使用的COM类,GUID或者CLSID是COM类的标识符,世界上的每个COM类都有自己唯一的CLSID。COM将使用该ID来查找可产生请求COM对象的服务器,一旦连接到服务器,即会创建该对象。
第2个参数pUnknownOuter是一个指针,它指向"outer unknown"。我们不会使用这个参数,因此传送NULL。在涉及"aggregation"(聚合)概念时,outer unknown是很重要的,aggregation可让一个接口直接调用另一个COM接口而无须通知客户端,aggregation和containment是接口用来调用其他接口的两个方法。
第3个参数dwClsContext定义COM类的Context或者CLSCTX。该参数控制服务器的范围,我们可以通过它来控制服务器是进程内的服务器还是一个EXE或者是在远程的计算机上。CLSCTX是一个位掩码,因此用户可以混合几个值,这里我们使用的是CLSCTX_INPROC_SERVER,该服务器将运行在本地的计算机(进程内)上,并且作为一个DLL连接到客户。
第4个参数iid是接口的标识符或者IID。这是另一个GUID,用来标识我们请求的接口。我们请求的IID必须是存在的,即被由CLSID指定的COM类支持,再次,IID的值通常由一个头文件提供,或者使用接口名查找出来。
第5个参数ppv是指向一个接口的指针。CoCreateInstance() 将创建所请求的类对象和接口,并且返回一个指向接口的指针,这个参数也是CoCreateInstance调用的目的,然后我们就可以使用该接口指针来调用服务器上的方法。
调用ICreateDevEnum::CreateClassEnumerator方法生成类型枚举器,参数为用户想要得到的类的ID(CLSID),该方法返回一个IEnumMoniker接口指针。如果指定的类型是空的或不存在,则函数ICreateDevEnum::CreateClassEnumerator将返回S_FALSE而不是错误代码,同时IEnumMoniker指针也是空的,这就要求我们在调用CreateClassEnumerator的时候明确用S_OK进行比较而不使用宏SUCCEEDED,代码如下。
IEnumMoniker *pEm; //枚举监控器接口 //获取视频类的枚举器 hr=pCreateDevEnum->CreateClassEnumerator(CLSID_ VideoInputDeviceCategory, &pEm, 0); //如果想获取音频类的枚举器,则使用如下代码 //hr=pCreateDevEnum->CreateClassEnumerator(CLSID _AudioInputDeviceCategory, &pEm, 0); if (hr != NOERROR) return -1;
|
函数原型如下:
HRESULT CreateClassEnumerator( REFCLSID clsidDeviceClass, IEnumMoniker **ppEnumMoniker, DWORD dwFlags );
|
clsidDeviceClass表示设备种类的类标识符CLSID;ppEnumMoniker 是接收IEnumMoniker接口类型的指针;dwFlags表示枚举的种类标识,为0表示枚举所有类型。
类型枚举器复位后,使用IEnumMoniker::Next方法依次得到IEnumMoniker指针中的每个Moniker,该方法返回一个IMoniker接口指针。当Next到达枚举的底部时,它的返回值仍然是S_FALSE,这里仍需要用S_OK来进行检验。要想得到该设备较为友好的名称(如想要用户界面在组合框中显示),则调用IMoniker::BindToStorage方法,代码如下。
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); //获取FriendlyName形式的信息 if (hr == NOERROR) { id++; // 把FriendlyName信息添加到组合列表框 ::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)var.bstrVal); SysFreeString(var.bstrVal); //释放资源,特别要注意 } pBag->Release(); //释放属性页接口指针 } pM->Release(); //释放监控器接口指针 } |
函数原型如下:
HRESULT Next( ULONG celt, IMoniker **rgelt, ULONG *pceltFetched) |
celt表示要查询的对象的数目;rgelt表示返回的接口对象的指针;pceltFetched是返回的包含rgelt对象的实际数目。函数原型如下:
HRESULT BindToStorage( IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riid, void **ppvObj)
|
pbc参数指向捆绑环境接口IbindCtx;pmkToLeft指向监控器的左边监控器,一般该值设置为空;iid参数是监控器的类标识符;ppvObj指向获取接口的指针。
以上的3个步骤封装在函数EnumDevices中实现,参数为显示设备的窗口句柄。代码如下。
int CCaptureClass::EnumDevices(HWND hList) { if (!hList) return -1; //句柄为空则返回-1 int id = 0; //上述的3个步骤 return id; } |
该函数的入口参数为hList,它是应用程序传入的组合框的句柄。在查询到视频捕获设备后,把设备的名字添加到组合框中。
【责任编辑:
云霞 TEL:(010)68476606】