前段时间SkeyeLive开放了DirectShow采集库,这个库底层采用DirectShow SDK的接口实现音视频的预览(播放)和采集;很多人可能还不太了解这个封装库的回调方式和之前的DShow线程采集方式有什么不同,或者说对DirectShow的采集流程还不太熟悉,下面我将就Windows平台下用使用DirectShow的过滤器(滤波器)进行流媒体开发的前端采集部分进行简要介绍,如果大家想深入的学习和探索,推荐大家去看看《Visual C++音频/视频处理技术及工程实践》这本书,第9章有详细的流程讲解。
一、枚举采集设备
使用采集设备前,需要首先确定系统已经安装的采集设备:视频、音频采集设备。系统设备枚举器为按类型枚举已注册在系统中的滤波器提供了统一的方法。而且它能够区分不同的硬件设备,即便是同一个滤波器支持它们。这对那些使用Windows驱动模型、KSProxy Filter的设备来说是非常有用的,系统设备枚举器对它们按不同的设备实例进行对待。
当利用系统设备枚举器查询设备的时候,系统设备枚举器为特定类型的设备(如音频捕获和视频压缩)生成了一张枚举表(Enumerator)。类型枚举器(Category Enumerator)为每个这种类型的设备返回一个Moniker,类型枚举器自动把每种即插即用的设备包含在内。
调用标准方法CoCreateInstance生成系统设备枚举器(Device Enumerator),类标识(CLSID)为CLSID_SystemDeviceEnum,方法如下:
CAMERA_LIST_T *CCameraDS::GetCameraList()
{
if (NULL != cameraList.pCamera || cameraList.count > 0)
{
return &cameraList;
}
if (NULL == cameraList.pCamera)
{
cameraList.count = 0;
// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
if (hr != NOERROR)
{
return &cameraList;
}
pEm->Reset();
CAMERA_INFO_T *pCameraList = cameraList.pCamera;
CAMERA_INFO_T *pCameraInfo = NULL;
ULONG cFetched;
IMoniker *pM = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
pCameraInfo = new CAMERA_INFO_T;
memset(pCameraInfo, 0x00, sizeof(CAMERA_INFO_T));
IPropertyBag *pBag=0;
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)
{
//获取设备名称
WideCharToMultiByte(CP_ACP,0,var.bstrVal,-1,pCameraInfo->friendlyName, sizeof(pCameraInfo->friendlyName) ,"",NULL);
SysFreeString(var.bstrVal);
}
pBag->Release();
cameraList.count++;
{
pCameraList = cameraList.pCamera;
if (NULL == cameraList.pCamera) cameraList.pCamera = pCameraInfo;
else
{
while (NULL != pCameraList->pNext)
{
pCameraList = pCameraList->pNext;
}
pCameraList->pNext = pCameraInfo;
}
}
}
pM->Release();
}
pCreateDevEnum = NULL;
pEm = NULL;
}
return &cameraList;
}
这是视频设备枚举部分,当然音频也是一样的,只需要把CreateClassEnumerator函数的第一个参数换成CLSID_AudioInputDeviceCategory即可。
注意:调用ICreateDevEnum::CreateClassEnumerator方法生成类型枚举器,参数为用户想要得到的类的ID(CLSID),该方法返回一个IEnumMoniker接口指针。如果指定的类型是空的或不存在,则函数ICreateDevEnum::CreateClassEnumerator将返回S_FALSE而不是错误代码,同时IEnumMoniker指针也是空的,这就要求我们在调用CreateClassEnumerator的时候明确用S_OK进行比较而不使用宏SUCCEEDED;(扯远了…)而在SkeyeLive中还提供了另外一种枚举音频设备的方式,那就是采用DirectSound的枚举方式:DirectSoundCaptureEnumerate这个函数来实现的,需要注意,这个函数枚举出的设备GUID有可能是空的,设备名称可能表象为”声卡主设备驱动“,经测试