[C++]DirectShow檢測音視訊輸入裝置及其採集引數
阿新 • • 發佈:2021-01-09
官方文件:https://docs.microsoft.com/zh-cn/windows/win32/directshow/directshow
建立CLR類庫專案(CSharpDirectShow),編寫託管的DirectShow類庫,右鍵專案屬性-->連結器--> 輸入-->附加依賴項;新增靜態庫檔案Strmiids.lib和Quartz.lib;
定義標頭檔案CSharpDirectShow.h,包含標頭檔案dshow.h,定義如下方法:
#pragma once #include <dshow.h> using namespace System; usingnamespace System::Runtime::InteropServices; public ref class DirectShow { public: /// <summary> /// 在當前執行緒上初始化COM庫,並將併發模型標識為單執行緒單元(STA) /// </summary> /// <returns></returns> static Boolean ComInit(); /// <summary> /// 關閉當前執行緒上的COM庫,解除安裝該執行緒載入的所有DLL,釋放該執行緒維護的所有其他資源,並強制關閉該執行緒上的所有RPC連線/// </summary> static void ComUinit(); /// <summary> /// 獲取視訊輸入裝置 /// </summary> /// <param name="devices"></param> /// <returns></returns> static Int32 GetVideoInputDevices([Out]array<VideoInputDsDevice^>^% devices); /// <summary> /// 獲取音訊輸入裝置/// </summary> /// <param name="device"></param> /// <returns></returns> static Int32 GetAudioInputDevices([Out]array<AudioInputDsDevice^>^% device); };
其中,初始化COM庫只能在STA執行緒呼叫,控制檯程式直接呼叫會返回失敗,在Winform和WPF等應用程式應該在第一次使用時初始化COM庫;
當前執行緒初始化COM庫和關閉COM庫:
Boolean DirectShow::ComInit() { HRESULT hr = CoInitialize(NULL); return hr == S_OK || hr == S_FALSE; } void DirectShow::ComUinit() { CoUninitialize(); }
一、列舉視訊輸入裝置,參考:https://docs.microsoft.com/zh-cn/windows/win32/directshow/selecting-a-capture-device
1、建立視訊輸入裝置的列舉器IEnumMoniker
HRESULT DirectShow::EnumerateDevices(REFGUID category, IEnumMoniker** ppEnum) { // 建立系統裝置列舉器 ICreateDevEnum* pDevEnum; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum)); if (SUCCEEDED(hr)) { // 為類別建立列舉數. hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0); if (hr == S_FALSE) hr = VFW_E_NOT_FOUND; // 類別是空的,視為錯誤 pDevEnum->Release(); } return hr; } Int32 DirectShow::GetVideoInputDevices([Out]array<VideoInputDsDevice^>^% devices) { devices = nullptr; IEnumMoniker* pEnum; HRESULT hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum); // 建立視訊輸入裝置列舉器 if (FAILED(hr)) return hr; hr = EnumerateVideoInputDevices(pEnum, devices); // 列舉視訊輸入裝置 pEnum->Release(); return hr; }
2、呼叫IEnumMoniker::Next()方法列舉裝置,呼叫IPropertyBag::Read方法讀取裝置屬性,以獲取裝置的友好名稱和裝置標識字串:
HRESULT DirectShow::EnumerateVideoInputDevices(IEnumMoniker* pEnum, [Out]array<VideoInputDsDevice^>^% devices) { HRESULT hr = S_FALSE; devices = nullptr; List<VideoInputDsDevice^>^ list = gcnew List<VideoInputDsDevice^>(); IMoniker* pMoniker; while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { IPropertyBag* pPropBag; hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); if (FAILED(hr)) { pMoniker->Release(); continue; } VideoInputDsDevice^ device = gcnew VideoInputDsDevice(); VARIANT var; VariantInit(&var); // 獲取裝置友好名 hr = pPropBag->Read(L"FriendlyName", &var, 0); if (FAILED(hr)) hr = pPropBag->Read(L"Description", &var, 0); if (SUCCEEDED(hr)) { device->FriendlyName = System::String(var.bstrVal).ToString(); VariantClear(&var); } // 獲取裝置Moniker名 LPOLESTR pOleDisplayName = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc(MAX_MONIKER_NAME_LENGTH * 2)); hr = pMoniker->GetDisplayName(NULL, NULL, &pOleDisplayName); if (SUCCEEDED(hr)) { device->MonikerName = System::String(pOleDisplayName).ToString(); array<VideoParams^>^ params; hr = EnumerateVideoParams(pMoniker, params); // 列舉裝置的採集引數 if (SUCCEEDED(hr)) device->Params = params; } CoTaskMemFree(pOleDisplayName); list->Add(device); pPropBag->Release(); pMoniker->Release(); } devices = list->ToArray(); return S_OK; }
3、列舉視訊輸入裝置的採集引數:
HRESULT DirectShow::EnumerateVideoParams(IMoniker* pMoniker, [Out]array<VideoParams^>^% params) { params = nullptr; IBaseFilter* pFilter; HRESULT hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter); if (FAILED(hr)) return hr; IEnumPins* pinEnum; hr = pFilter->EnumPins(&pinEnum); if (FAILED(hr)) { pFilter->Release(); return hr; } List<VideoParams^>^ list = gcnew List<VideoParams^>(); IPin* pPins; while (pinEnum->Next(1, &pPins, NULL) == S_OK) { PIN_INFO pinInfo; hr = pPins->QueryPinInfo(&pinInfo); if (FAILED(hr) || pinInfo.dir != PINDIR_OUTPUT) { pPins->Release(); continue; } IEnumMediaTypes* mtEnum; hr = pPins->EnumMediaTypes(&mtEnum); if (FAILED(hr)) { pPins->Release(); continue; } AM_MEDIA_TYPE* mt; while (mtEnum->Next(1, &mt, NULL) == S_OK) { VideoParams^ param = nullptr; if (mt->formattype == FORMAT_VideoInfo) { VIDEOINFOHEADER* pVih = reinterpret_cast<VIDEOINFOHEADER*>(mt->pbFormat); param = gcnew VideoParams(); param->FrameWidth = pVih->bmiHeader.biWidth; param->FrameHeight = pVih->bmiHeader.biHeight; param->AverageFrameRate = pVih->AvgTimePerFrame == 0 ? 0 : 10000000 / pVih->AvgTimePerFrame; } else if (mt->formattype == FORMAT_VideoInfo2) { VIDEOINFOHEADER2* pVih = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat); param = gcnew VideoParams(); param->FrameWidth = pVih->bmiHeader.biWidth; param->FrameHeight = pVih->bmiHeader.biHeight; param->AverageFrameRate = pVih->AvgTimePerFrame == 0 ? 0 : 10000000 / pVih->AvgTimePerFrame; } if (param && param->AverageFrameRate > 1) { Boolean isExit = false; for each (VideoParams ^ item in list) { if (item->FrameWidth == param->FrameWidth && item->FrameHeight == param->FrameHeight && item->AverageFrameRate == param->AverageFrameRate) { isExit = true; break; } } if (!isExit) list->Add(param); } } pPins->Release(); } pFilter->Release(); params = list->ToArray(); return S_OK; }
呼叫程式碼及結果:
var ret = DirectShow.GetVideoInputDevices (out VideoInputDsDevice[] videoInputDevices); if (ret == 0) { Console.WriteLine ("視訊輸入裝置:"); foreach (var videoInputDevice in videoInputDevices) { Console.WriteLine ($"{videoInputDevice.FriendlyName}\t{videoInputDevice.MonikerName}"); if (videoInputDevice.Params.Length > 0) { Console.WriteLine ("畫素寬度\t畫素高度\t1秒平均幀數"); foreach (var param in videoInputDevice.Params) { Console.WriteLine ($"{param.FrameWidth}\t{param.FrameHeight}\t{param.AverageFrameRate}"); } } Console.WriteLine (); } }
二、列舉音訊輸入裝置,參考https://docs.microsoft.com/zh-cn/windows/win32/directshow/selecting-a-capture-device,過程和視訊一樣:
ret = DirectShow.GetAudioInputDevices (out AudioInputDsDevice[] audioInputDevices); if (ret == 0) { Console.WriteLine ("音訊輸入裝置:"); foreach (var audioInputDevice in audioInputDevices) { Console.WriteLine ($"{audioInputDevice.FriendlyName}\t{audioInputDevice.MonikerName}"); if (audioInputDevice.Params.Length > 0) { Console.WriteLine ("音訊格式\t通道數\t取樣速率\t塊對齊\t位數"); foreach (var param in audioInputDevice.Params) { Console.WriteLine ($"{param.Format}\t{param.Channels}\t{param.SampleRate}\t{param.BlockAlign}\t{param.BitsPerSample}"); } } Console.WriteLine (); } }
程式碼已上傳至Github:https://github.com/LowPlayer/CameraCapture