在视频聊天、视频会议、在线监控和视频展台等项目中,需要查找出本地电脑上连接的所有摄像头,网上流传比较多的方式是ffmpeg的方式,这种方式可以跨平台,不同的平台下调用不同的库。这种方式在控制台直接打印了摄像头的信息,无法(或者说我暂时没找到)在内存中获取,因此直接采用了DirectShow的方式,DirectShow枚举IMoniker和Ipin。因为网上的文档,不是特别详尽,所以我写了本文,我尽量解释清楚,分段贴出部分代码,主要是要看明白并且理解,通过本文中的方式,基本可以列出电脑上的摄像头和麦克风,以及他们的参数。
在另外一篇文章中,介绍了如何利用获取的设备信息播放和编解码:《MFC中如何利用ffmpeg和SDL2.0多线程多窗口播放摄像头的视频》
1、用ffmpeg的方式
1)ffmpeg功能强大,关于ffmpeg的详细文档,可以去官网看看:http://ffmpeg.org/
直接静态库或者动态库好了,不怕麻烦的可以下载开发版:https://ffmpeg.zeranoe.com/builds/
2)关于ffmpeg支持的设备列表,可以参考下面的链接:
http://www.ffmpeg.org/ffmpeg-devices.html
3)这种方式比较简单,其实在Widows下还是调用dshow,
直接传入“list_devices”,列出设备列表。先看看命令行方式。
- ffmpeg -list_devices true -f dshow -i dummy
所以啊,如果看到命令行的例子,在写代码调用接口时可以参考他。
- //Show directshow device
- void show_dshow_device() {
- AVFormatContext *pFormatCtx = avformat_alloc_context();
- AVDictionary* options = NULL;
- av_dict_set(&options, "list_devices", "true", 0);
- AVInputFormat *iformat = av_find_input_format("dshow");
- avformat_open_input(&pFormatCtx, "video=dummy", iformat, &options);
- }
- //Show device options
- void show_dshow_device_option(const char* cameraName) {
- AVFormatContext *pFormatCtx = avformat_alloc_context();
- AVDictionary* options = NULL;
- av_dict_set(&options, "list_options", "true", 0);
- AVInputFormat *iformat = av_find_input_format("dshow");
- char buffer[128];
- sprintf(buffer, "video=%s", cameraName);
- avformat_open_input(&pFormatCtx, buffer, iformat, &options);
- }
上面已经说了,这种方式可以列出来,但是内存中不好获取。
2、直接使用DirectShow的方式
- //设备参数
- struct TDeviceParam {
- int width; //分辨率宽
- int height; //分辨率高
- int avgTimePerFrame; //每帧的时间
- TDeviceParam BestParam; //最好的参数
- TDeviceParam() {
- Reset();
- }
- void Reset() {
- width = 0;
- height = 0;
- avgTimePerFrame = 1;
- }
- void Set(int w, int h, int avgTime) {
- width = w;
- height = h;
- avgTimePerFrame = avgTime;
- }
- void Copy(TDeviceParam& param) {
- Set(param.width, param.height, param.avgTimePerFrame);
- }
- };
- //设备信息
- struct TDeviceInfo {
- WCHAR FriendlyName[MAX_FRIENDLY_NAME_LENGTH]; // 设备友好名
- WCHAR MonikerName[MAX_MONIKER_NAME_LENGTH]; // 设备Moniker名
- int ParamCount; // 参数数量
- TDeviceParam Params[MAX_PARAM_COUNT]; // 支持的分辨率
- TDeviceInfo() {
- Reset();
- }
- void Reset() {
- ParamCount = 0;
- }
- int SetResolution(int w, int h, int avgTime) {
- if (ParamCount >= MAX_PARAM_COUNT)
- return -1;
- for (int i = 0; i < ParamCount; i++) {
- if (Params[i].width == w && Params[i].height == h) {
- return 0;
- }
- }
- int insertIndex = 0;
- for (int i = 0; i < ParamCount; i++) {
- if (w > Params[i].width || h > Params[i].height) {
- break;
- }
- else {
- insertIndex++;
- }
- }
- for (int i = ParamCount - 1; i >= insertIndex; i--) {
- Params[i + 1].Copy(Params[i]);
- }
- Params[insertIndex].Set(w, h, avgTime);
- ParamCount++;
- if (w > BestParam.width) {
- BestParam.Set(w, h, avgTime);
- }
- };
- //根据设备最好的参数排序
- bool SortDevice(const TDeviceInfo& device1, const TDeviceInfo& device2) {
- <span style="white-space:pre"> </span>if (device1.BestParam.width > device2.BestParam.width)
- <span style="white-space:pre"> </span>return true;
- <span style="white-space:pre"> </span>return false;
- }
- //guidValue:
- //CLSID_AudioInputDeviceCategory:获取音频输入设备列表
- //CLSID_VideoInputDeviceCategory:获取视频输入设备列表
- HRESULT DsGetAudioVideoInputDevices(std::vector<TDeviceInfo>& deviceVec, REFGUID guidValue)
- {
- TDeviceInfo info;
- HRESULT hr;
- // 初始化
- deviceVec.clear();
- // 初始化COM
- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- if (FAILED(hr)) {
- printf("Init error!\n");
- return hr;
- }
- // 创建系统设备枚举器实例
- ICreateDevEnum *pSysDevEnum = NULL;
- hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
- if (FAILED(hr)){
- CoUninitialize();
- printf("Create instance error!\n");
- return hr;
- }
- // 获取设备类枚举器
- IEnumMoniker *pEnumCat = NULL;
- hr = pSysDevEnum->CreateClassEnumerator(guidValue, &pEnumCat, 0);
- if (hr != S_OK) {
- CoUninitialize();
- //pSysDevEnum->Release();
- return hr;
- }
- // 枚举设备名称
- IMoniker *pMoniker = NULL;
- ULONG cFetched;
- while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
- IPropertyBag *pPropBag;
- hr = pMoniker->BindToStorage(NULL, NULL, IID_IPropertyBag, (void **)&pPropBag);
- if ( FAILED( hr ) ) {
- pMoniker->Release();
- continue;
- }
- info.Reset();
- // 获取设备友好名
- VARIANT varName;
- VariantInit(&varName);
- hr = pPropBag->Read(L"FriendlyName", &varName, NULL);
- if (SUCCEEDED(hr)) {
- StringCchCopy(info.FriendlyName, MAX_FRIENDLY_NAME_LENGTH, varName.bstrVal);
- #if PRINT_DEBUG
- wprintf(L"Device:%s\n", info.FriendlyName);
- #endif
- // 获取设备Moniker名
- LPOLESTR pOleDisplayName = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc(MAX_MONIKER_NAME_LENGTH * 2));
- if (pOleDisplayName != NULL) {
- hr = pMoniker->GetDisplayName(NULL, NULL, &pOleDisplayName);
- if (SUCCEEDED(hr)) {
- StringCchCopy( info.MonikerName, MAX_MONIKER_NAME_LENGTH, pOleDisplayName );
- //获取设备支持的分辨率
- DsGetOptionDevice( pMoniker, info );
- deviceVec.push_back( info );
- }
- CoTaskMemFree(pOleDisplayName);
- }
- }
- VariantClear(&varName);
- pPropBag->Release();
- pMoniker->Release();
- } // End for While
- pEnumCat->Release();
- pSysDevEnum->Release();
- CoUninitialize();
- std::sort( deviceVec.begin(), deviceVec.end(), SortDevice );
- for (int i = 0; i < deviceVec.size(); i++) {
- deviceVec[i].Debug();
- }
- return hr;
- }
- OUR_GUID_ENTRY(MEDIATYPE_Video,
- 0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)
BITMAPINFOHEADER的类型请参考
https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
也就是说,如果想要什么参数,可以从上面几个类型中找。
- int GuidToString(const GUID &guid, char* buffer){
- int buf_len = 64;
- snprintf(
- buffer,
- buf_len,
- "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
- guid.Data1, guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1],
- guid.Data4[2], guid.Data4[3],
- guid.Data4[4], guid.Data4[5],
- guid.Data4[6], guid.Data4[7]);
- return 0;
- }
- int GetMajorType(GUID guid, char* buffer) {
- memset(buffer, 0, 256);
- if (guid == MEDIATYPE_Video) {
- snprintf(buffer, 256, "MEDIATYPE_Video");
- return 0;
- }
- if (guid == MEDIATYPE_Audio) {
- snprintf(buffer, 256, "MEDIATYPE_Audio");
- return 0;
- }
- if (guid == MEDIASUBTYPE_RGB24) {
- snprintf(buffer, 256, "MEDIATYPE_Stream");
- return 0;
- }
- return -1;
- }
- int GetSubType(GUID guid, char* buffer) {
- memset(buffer, 0, 256);
- if( guid == MEDIASUBTYPE_YUY2){
- snprintf(buffer, 256, "MEDIASUBTYPE_YUY2");
- return 0;
- }
- if (guid == MEDIASUBTYPE_MJPG) {
- snprintf(buffer, 256, "MEDIASUBTYPE_MJPG");
- return 0;
- }
- if (guid == MEDIASUBTYPE_RGB24) {
- snprintf(buffer, 256, "MEDIASUBTYPE_RGB24");
- return 0;
- }
- return -1;
- }
- int GetFormatType(GUID guid, char* buffer) {
- memset(buffer, 0, 256);
- if (guid == FORMAT_VideoInfo) {
- snprintf(buffer, 256, "FORMAT_VideoInfo");
- return 0;
- }
- if (guid == FORMAT_VideoInfo2) {
- snprintf(buffer, 256, "FORMAT_VideoInfo2");
- return 0;
- }
- return -1;
- }
- int DsGetOptionDevice(IMoniker* pMoniker,TDeviceInfo& info) {
- USES_CONVERSION;
- HRESULT hr = NULL;
- IBaseFilter *pFilter;
- hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
- if (!pFilter) {
- return -1;
- }
- IEnumPins * pinEnum = NULL;
- IPin * pin = NULL;
- if (FAILED(pFilter->EnumPins(&pinEnum))) {
- pinEnum->Release();
- return -1;
- }
- pinEnum->Reset();
- ULONG pinFetched = 0;
- while (SUCCEEDED(pinEnum->Next(1, &pin, &pinFetched)) && pinFetched) {
- if (!pin) {
- continue;
- }
- PIN_INFO pinInfo;
- if (FAILED(pin->QueryPinInfo(&pinInfo))) {
- continue;
- }
- if (pinInfo.dir != PINDIR_OUTPUT) {
- continue;
- }
- #if PRINT_DEBUG
- printf("\t[Pin] Dir:Output Name %s\n", W2A(pinInfo.achName));
- #endif
- IEnumMediaTypes *mtEnum = NULL;
- AM_MEDIA_TYPE *mt = NULL;
- if (FAILED(pin->EnumMediaTypes(&mtEnum)))
- break;
- mtEnum->Reset();
- ULONG mtFetched = 0;
- while (SUCCEEDED(mtEnum->Next(1, &mt, &mtFetched)) && mtFetched) {
- char majorbuf[256];
- if ( GetMajorType(mt->majortype, majorbuf) != 0) {
- GuidToString(mt->majortype, majorbuf);
- }
- char subtypebuf[256];
- if (GetSubType(mt->subtype, subtypebuf) != 0) {
- GuidToString(mt->subtype, subtypebuf);
- }
- char formatbuf[256];
- if (GetFormatType(mt->formattype, formatbuf) != 0) {
- GuidToString(mt->formattype, formatbuf);
- }
- #if PRINT_DEBUG
- printf("\t%s\t%s\t%s", majorbuf, subtypebuf, formatbuf);
- #endif
- BITMAPINFOHEADER* bmi = NULL;
- int avgTime;
- if (mt->formattype == FORMAT_VideoInfo) {
- if ( mt->cbFormat >= sizeof(VIDEOINFOHEADER)){
- VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>( mt->pbFormat);
- bmi = &( pVih->bmiHeader );
- avgTime = pVih->AvgTimePerFrame;
- }
- } else if (mt->formattype == FORMAT_VideoInfo2) {
- if (mt->cbFormat >= sizeof(VIDEOINFOHEADER2)) {
- VIDEOINFOHEADER2* pVih = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat);
- bmi = &(pVih->bmiHeader);
- avgTime = pVih->AvgTimePerFrame;
- }
- }
- if( bmi ){
- info.SetResolution(bmi->biWidth, bmi->biHeight, avgTime);
- #if PRINT_DEBUG
- printf("\t%d * %d, Bit %d\n", bmi->biWidth, bmi->biHeight, bmi->biBitCount);
- #endif
- }else {
- printf("\tNo find\n");
- }
- }
- pin->Release();
- }
- return 0;
- }
- HRESULT hrrst;
- GUID guid = CLSID_VideoInputDeviceCategory;
- hrrst = DsGetAudioVideoInputDevices(videoDeviceVec, guid);
- guid = CLSID_AudioInputDeviceCategory;
- hrrst = DsGetAudioVideoInputDevices(audioDeviceVec, guid);
http://blog.csdn.net/leixiaohua1020/article/details/42649379