diff --git a/src/media/video/video_device_monitor.cpp b/src/media/video/video_device_monitor.cpp index 7508e4661c23f2414e3d1b9c9e1f4d1dd3107221..6bffe160baa683f61d0b72032fb15167c92c2405 100644 --- a/src/media/video/video_device_monitor.cpp +++ b/src/media/video/video_device_monitor.cpp @@ -284,7 +284,7 @@ vector<VideoDevice>::iterator VideoDeviceMonitor::findDeviceByNode(const string& node) { for (auto it = devices_.begin(); it != devices_.end(); ++it) - if (it->getNode() == node) + if (it->getNode().find(node) != std::string::npos) return it; return devices_.end(); } @@ -293,7 +293,7 @@ vector<VideoDevice>::const_iterator VideoDeviceMonitor::findDeviceByNode(const string& node) const { for (auto it = devices_.cbegin(); it != devices_.cend(); ++it) - if (it->getNode() == node) + if (it->getNode().find(node) != std::string::npos) return it; return devices_.end(); } @@ -348,5 +348,5 @@ VideoDeviceMonitor::unserialize(const YAML::Node &in) else defaultDevice_ = first; } - +#pragma optimize("", on) }} // namespace jami::video diff --git a/src/media/video/winvideo/video_device_impl.cpp b/src/media/video/winvideo/video_device_impl.cpp index d77d2b7821b36f677f60e15ddfe6a2dbba5eb78c..44155cb268eff4c58b0869f088bb00aba9745e53 100644 --- a/src/media/video/winvideo/video_device_impl.cpp +++ b/src/media/video/winvideo/video_device_impl.cpp @@ -67,7 +67,8 @@ class VideoDeviceImpl { }; VideoDeviceImpl::VideoDeviceImpl(const std::string& path) - : name(path) + : device(path) + , name() , cInterface(new CaptureGraphInterfaces()) { setup(); @@ -81,14 +82,14 @@ VideoDeviceImpl::setup() nullptr, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, - (void**) &cInterface->captureGraph_); + (void**)&cInterface->captureGraph_); if (FAILED(hr)) return fail("Could not create the Filter Graph Manager"); hr = CoCreateInstance(CLSID_FilterGraph, nullptr, - CLSCTX_INPROC_SERVER,IID_IGraphBuilder, - (void**) &cInterface->graph_); + CLSCTX_INPROC_SERVER, IID_IGraphBuilder, + (void**)&cInterface->graph_); if (FAILED(hr)) return fail("Could not add the graph builder!"); @@ -96,122 +97,152 @@ VideoDeviceImpl::setup() if (FAILED(hr)) return fail("Could not set filtergraph."); - ICreateDevEnum *pSysDevEnum = nullptr; - hr = CoCreateInstance(CLSID_SystemDeviceEnum, - nullptr, - CLSCTX_INPROC_SERVER, - IID_ICreateDevEnum, - (void **)&pSysDevEnum); - if (FAILED(hr)) - return fail("Could not create the enumerator!"); + ICreateDevEnum *pDevEnum; + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum)); - IEnumMoniker* pEnumCat = nullptr; - hr = pSysDevEnum->CreateClassEnumerator( - CLSID_VideoInputDeviceCategory, - &pEnumCat, - 0); - if (SUCCEEDED(hr)) { - // Auto-deletion at if {} exist or at exception - auto IEnumMonikerDeleter = [](IEnumMoniker* p){ p->Release(); }; - std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumCatGuard {pEnumCat, IEnumMonikerDeleter}; - - IMoniker *pMoniker = nullptr; - ULONG cFetched; - while ((pEnumCatGuard->Next(1, &pMoniker, &cFetched) == S_OK)) { - IPropertyBag *pPropBag; - hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); - if (FAILED(hr)) { - continue; - } - VARIANT var; - VariantInit(&var); - hr = pPropBag->Read(L"FriendlyName", &var, 0); - if (SUCCEEDED(hr)) { - // We want to get the capabilities of a device with the friendly name - // that corresponds to what was enumerated by the video device monitor, - // and passed in the ctor as the name of this device. - if (this->name != bstrToStdString(var.bstrVal)) { - continue; - } - this->device = std::string("video=") + this->name; - hr = pMoniker->BindToObject( - nullptr, nullptr, - IID_IBaseFilter, - (void**)&cInterface->videoInputFilter_); - if (SUCCEEDED(hr)) - hr = cInterface->graph_->AddFilter( - cInterface->videoInputFilter_, - var.bstrVal); - else { - fail("Could not add filter to video device."); - } - hr = cInterface->captureGraph_->FindInterface( - &PIN_CATEGORY_PREVIEW, - &MEDIATYPE_Video, - cInterface->videoInputFilter_, - IID_IAMStreamConfig, - (void **)&cInterface->streamConf_); - if (FAILED(hr)) { - hr = cInterface->captureGraph_->FindInterface( - &PIN_CATEGORY_CAPTURE, - &MEDIATYPE_Video, - cInterface->videoInputFilter_, - IID_IAMStreamConfig, - (void **)&cInterface->streamConf_); - if (FAILED(hr)) { - fail("Couldn't config the stream!"); - } - } - // Device found. - break; - } - VariantClear(&var); - pPropBag->Release(); - pPropBag = nullptr; + IEnumMoniker* pEnum = NULL; + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (hr == S_FALSE) { + hr = VFW_E_NOT_FOUND; + } + pDevEnum->Release(); + if (FAILED(hr) || pEnum == nullptr) { + JAMI_ERR() << "No webcam found"; + return; + } + + // Auto-deletion at exception + auto IEnumMonikerDeleter = [](IEnumMoniker* p) { p->Release(); }; + std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumGuard{ pEnum, IEnumMonikerDeleter }; + + IMoniker *pMoniker = NULL; + while ((pEnumGuard->Next(1, &pMoniker, NULL) == S_OK)) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); + if (FAILED(hr)) { pMoniker->Release(); - pMoniker = nullptr; + continue; + } + + IBindCtx *bind_ctx = NULL; + LPOLESTR olestr = NULL; + + hr = CreateBindCtx(0, &bind_ctx); + if (hr != S_OK) { + continue; + } + hr = pMoniker->GetDisplayName(bind_ctx, NULL, &olestr); + if (hr != S_OK) { + continue; + } + auto unique_name = to_string(olestr); + if (unique_name.empty()) { + continue; + } + + // replace ':' with '_' since ffmpeg uses : to delineate between sources */ + std::replace(unique_name.begin(), unique_name.end(), ':', '_'); + unique_name = std::string("video=") + unique_name; + + // We want to get the capabilities of a device with the unique_name + // that corresponds to what was enumerated by the video device monitor. + if (unique_name.find(this->device) == std::string::npos) { + continue; + } + + this->device = unique_name; + + // get friendly name + VARIANT var; + VariantInit(&var); + hr = pPropBag->Read(L"Description", &var, 0); + if (FAILED(hr)) { + hr = pPropBag->Read(L"FriendlyName", &var, 0); } if (SUCCEEDED(hr)) { - int piCount; - int piSize; - cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize); - AM_MEDIA_TYPE *pmt; - VIDEO_STREAM_CONFIG_CAPS pSCC; - std::map<std::pair<jami::video::VideoSize, jami::video::FrameRate>, LONG> bitrateList; - for (int i = 0; i < piCount; i++) { - cInterface->streamConf_->GetStreamCaps(i, &pmt, (BYTE*)&pSCC); - if (pmt->formattype != FORMAT_VideoInfo) { - continue; - } - auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat; - auto size = jami::video::VideoSize(videoInfo->bmiHeader.biWidth, videoInfo->bmiHeader.biHeight); - auto rate = jami::video::FrameRate(1e7, videoInfo->AvgTimePerFrame); - auto bitrate = videoInfo->dwBitRate; - // Only add configurations with positive bitrates. - if (bitrate == 0) - continue; - // Avoid adding multiple rates with different bitrates. - auto ratesIt = rateList_.find(size); - if (ratesIt != rateList_.end() && - std::find(ratesIt->second.begin(), ratesIt->second.end(), rate) != ratesIt->second.end()) { - // Update bitrate and cap map if the bitrate is greater. - auto key = std::make_pair(size, rate); - if (bitrate > bitrateList[key]) { - bitrateList[key] = bitrate; - capMap_[size] = pmt; - } - continue; - } - // Add new size, rate, bitrate, and cap map. - sizeList_.emplace_back(size); - rateList_[size].emplace_back(rate); - bitrateList[std::make_pair(size, rate)] = bitrate; + this->name = to_string(var.bstrVal); + } + pPropBag->Write(L"FriendlyName", &var); + + hr = pMoniker->BindToObject( + nullptr, nullptr, + IID_IBaseFilter, + (void**)&cInterface->videoInputFilter_); + if (SUCCEEDED(hr)) + hr = cInterface->graph_->AddFilter( + cInterface->videoInputFilter_, + var.bstrVal); + else { + fail("Could not add filter to video device."); + } + hr = cInterface->captureGraph_->FindInterface( + &PIN_CATEGORY_PREVIEW, + &MEDIATYPE_Video, + cInterface->videoInputFilter_, + IID_IAMStreamConfig, + (void **)&cInterface->streamConf_); + if (FAILED(hr)) { + hr = cInterface->captureGraph_->FindInterface( + &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + cInterface->videoInputFilter_, + IID_IAMStreamConfig, + (void **)&cInterface->streamConf_); + if (FAILED(hr)) { + fail("Couldn't config the stream!"); + } + } + + VariantClear(&var); + pPropBag->Release(); + + // Device found. + break; + } + pMoniker->Release(); + + if (FAILED(hr) || cInterface->streamConf_ == NULL) { + fail("Could not find the video device."); + } + + int piCount; + int piSize; + cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize); + AM_MEDIA_TYPE *pmt; + VIDEO_STREAM_CONFIG_CAPS pSCC; + std::map<std::pair<jami::video::VideoSize, jami::video::FrameRate>, LONG> bitrateList; + for (int i = 0; i < piCount; i++) { + cInterface->streamConf_->GetStreamCaps(i, &pmt, (BYTE*)&pSCC); + if (pmt->formattype != FORMAT_VideoInfo) { + continue; + } + auto videoInfo = (VIDEOINFOHEADER*)pmt->pbFormat; + auto size = jami::video::VideoSize(videoInfo->bmiHeader.biWidth, videoInfo->bmiHeader.biHeight); + auto rate = jami::video::FrameRate(1e7, videoInfo->AvgTimePerFrame); + auto bitrate = videoInfo->dwBitRate; + // Only add configurations with positive bitrates. + if (bitrate == 0) + continue; + // Avoid adding multiple rates with different bitrates. + auto ratesIt = rateList_.find(size); + if (ratesIt != rateList_.end() && + std::find(ratesIt->second.begin(), ratesIt->second.end(), rate) != ratesIt->second.end()) { + // Update bitrate and cap map if the bitrate is greater. + auto key = std::make_pair(size, rate); + if (bitrate > bitrateList[key]) { + bitrateList[key] = bitrate; capMap_[size] = pmt; } + continue; } + // Add new size, rate, bitrate, and cap map. + sizeList_.emplace_back(size); + rateList_[size].emplace_back(rate); + bitrateList[std::make_pair(size, rate)] = bitrate; + capMap_[size] = pmt; } - pSysDevEnum->Release(); - pSysDevEnum = NULL; } void diff --git a/src/media/video/winvideo/video_device_monitor_impl.cpp b/src/media/video/winvideo/video_device_monitor_impl.cpp index 8ef60e284fd9d9bf88a64d26b133e93c7cf7a977..2230d942b8b7df71fac2e000ec878159db854881 100644 --- a/src/media/video/winvideo/video_device_monitor_impl.cpp +++ b/src/media/video/winvideo/video_device_monitor_impl.cpp @@ -83,66 +83,18 @@ VideoDeviceMonitorImpl::~VideoDeviceMonitorImpl() } std::string -getDeviceFriendlyName(PDEV_BROADCAST_DEVICEINTERFACE pbdi) +getDeviceUniqueName(PDEV_BROADCAST_DEVICEINTERFACE pbdi) { - // As per: https://www.codeproject.com/Articles/14500/Detecting-Hardware-Insertion-and-or-Removal - // we need to convert the usb device descriptor string from: - // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed} - // to something like: - // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06 - // in order to match the device's registry entry, and finally obtain the friendly name. (-_-)? - - std::string friendlyName; - std::string name = pbdi->dbcc_name; - name = name.substr(4); - auto pos = name.find("#"); - name.replace(pos, 1, "\\"); - pos = name.find_last_of("#"); - name = name.substr(0, pos); - pos = name.find("#"); - name.replace(pos, 1, "\\"); - std::transform(name.begin(), name.end(), name.begin(), - [](unsigned char c) { return std::toupper(c); } - ); - - DWORD dwFlag = DIGCF_ALLCLASSES; - HDEVINFO hDevInfo = SetupDiGetClassDevs(&guidCamera, 0, NULL, dwFlag); - if (INVALID_HANDLE_VALUE == hDevInfo) { - return {}; - } - - SP_DEVINFO_DATA* pspDevInfoData = - (SP_DEVINFO_DATA*)HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); - pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); - - for (int i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { - GUID guid; - guid = pspDevInfoData->ClassGuid; + std::string unique_name = pbdi->dbcc_name; - DWORD DataT; - DWORD nSize = 0; - TCHAR buf[260]; - - if (!SetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { - break; - } - - std::string strBuf(&buf[0]); - if (strBuf.find(name) != std::string::npos) { - if (SetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, - SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, sizeof(buf), &nSize)) { - friendlyName = std::string(buf); - break; - } - } - } + std::transform(unique_name.begin(), unique_name.end(), unique_name.begin(), + [](unsigned char c) { return std::tolower(c); } + ); - if (pspDevInfoData) { - HeapFree(GetProcessHeap(), 0, pspDevInfoData); - } - SetupDiDestroyDeviceInfoList(hDevInfo); + auto pos = unique_name.find_last_of("#"); + unique_name = unique_name.substr(0, pos); - return friendlyName; + return unique_name; } bool @@ -207,15 +159,19 @@ VideoDeviceMonitorImpl::WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, case DBT_DEVICEREMOVECOMPLETE: case DBT_DEVICEARRIVAL: { - PDEV_BROADCAST_DEVICEINTERFACE p = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; - auto friendlyName = getDeviceFriendlyName(p); - if (!friendlyName.empty()) { - JAMI_DBG() << friendlyName << ((wParam == DBT_DEVICEARRIVAL) ? " plugged" : " unplugged"); + PDEV_BROADCAST_DEVICEINTERFACE pbdi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; + auto unique_name = getDeviceUniqueName(pbdi); + if (!unique_name.empty()) { + JAMI_DBG() << unique_name << ((wParam == DBT_DEVICEARRIVAL) ? " plugged" : " unplugged"); if (pThis = reinterpret_cast<VideoDeviceMonitorImpl*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))) { if (wParam == DBT_DEVICEARRIVAL) { - pThis->monitor_->addDevice(friendlyName); + auto captureDeviceList = pThis->enumerateVideoInputDevices(); + for (auto node : captureDeviceList) { + if (node.find(unique_name) != std::string::npos) + pThis->monitor_->addDevice(node); + } } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { - pThis->monitor_->removeDevice(friendlyName); + pThis->monitor_->removeDevice(unique_name); } } } @@ -288,7 +244,8 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() } IEnumMoniker *pEnum = nullptr; - hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, &pEnum, 0); if (hr == S_FALSE) { hr = VFW_E_NOT_FOUND; } @@ -299,7 +256,6 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() } IMoniker *pMoniker = NULL; - unsigned deviceID = 0; while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { IPropertyBag *pPropBag; HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); @@ -308,27 +264,25 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() continue; } - VARIANT var; - VariantInit(&var); - hr = pPropBag->Read(L"Description", &var, 0); - if (FAILED(hr)) { - hr = pPropBag->Read(L"FriendlyName", &var, 0); - } + IBindCtx *bind_ctx = NULL; + LPOLESTR olestr = NULL; - if (SUCCEEDED(hr)) { - auto deviceName = bstrToStdString(var.bstrVal); - if (!deviceName.empty()) { - deviceList.push_back(deviceName); - } - VariantClear(&var); + hr = CreateBindCtx(0, &bind_ctx); + if (hr != S_OK) { + continue; + } + hr = pMoniker->GetDisplayName(bind_ctx, NULL, &olestr); + if (hr != S_OK) { + continue; + } + auto unique_name = to_string(olestr); + if (!unique_name.empty()) { + // replace ':' with '_' since ffmpeg uses : to delineate between sources + std::replace(unique_name.begin(), unique_name.end(), ':', '_'); + deviceList.push_back(std::string("video=") + unique_name); } - - hr = pPropBag->Write(L"FriendlyName", &var); pPropBag->Release(); - pMoniker->Release(); - - deviceID++; } pEnum->Release(); diff --git a/src/string_utils.cpp b/src/string_utils.cpp index adadbae1638d25aa777bcf7347f9dfc0199a24a3..cd872a82d66b9cc8053214964fbe03eee12c4920 100644 --- a/src/string_utils.cpp +++ b/src/string_utils.cpp @@ -35,7 +35,6 @@ namespace jami { #ifdef _WIN32 - std::wstring to_wstring(const std::string& str, int codePage) { @@ -65,17 +64,6 @@ to_string(const std::wstring& wstr, int codePage) } return result; } - -std::string -bstrToStdString(BSTR bstr) -{ - int wslen = ::SysStringLen(bstr); - if (wslen != 0) { - std::wstring wstr(bstr, wslen); - return std::string(wstr.begin(), wstr.end()); - } - return {}; -} #endif std::string diff --git a/src/string_utils.h b/src/string_utils.h index 50f8b9097e5b911292ffbc4456bf1a3b2d7320f4..593276a702f2e9350562b045d8d3a48c656b77ea 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -43,7 +43,6 @@ std::string to_string(double value); #ifdef _WIN32 std::wstring to_wstring(const std::string& str, int codePage = CP_UTF8); std::string to_string(const std::wstring& wstr, int codePage = CP_UTF8); -std::string bstrToStdString(BSTR bstr); #endif static inline int