Skip to content
Snippets Groups Projects
Commit ca26195f authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Ming Rui Zhang
Browse files

videomonitor(win32): bridge ffmpeg/win32api device ids

- Fixes wstring conversion issue when obtaining video capture device
  friendly name, and uses device id when adding/removing devices using
  the usb-pnp monitoring APIs. As the uid suffix reported by the pnp
  api is different from what returns from the device enumerator, we
  strip the uid and make substring searches for device nodes instead
  of exact matches.

Change-Id: Ibe3d79ed0632c663c2b067772ca592d5c5d466e6
Gitlab: #171
parent 0613ef38
No related branches found
No related tags found
No related merge requests found
...@@ -284,7 +284,7 @@ vector<VideoDevice>::iterator ...@@ -284,7 +284,7 @@ vector<VideoDevice>::iterator
VideoDeviceMonitor::findDeviceByNode(const string& node) VideoDeviceMonitor::findDeviceByNode(const string& node)
{ {
for (auto it = devices_.begin(); it != devices_.end(); ++it) for (auto it = devices_.begin(); it != devices_.end(); ++it)
if (it->getNode() == node) if (it->getNode().find(node) != std::string::npos)
return it; return it;
return devices_.end(); return devices_.end();
} }
...@@ -293,7 +293,7 @@ vector<VideoDevice>::const_iterator ...@@ -293,7 +293,7 @@ vector<VideoDevice>::const_iterator
VideoDeviceMonitor::findDeviceByNode(const string& node) const VideoDeviceMonitor::findDeviceByNode(const string& node) const
{ {
for (auto it = devices_.cbegin(); it != devices_.cend(); ++it) for (auto it = devices_.cbegin(); it != devices_.cend(); ++it)
if (it->getNode() == node) if (it->getNode().find(node) != std::string::npos)
return it; return it;
return devices_.end(); return devices_.end();
} }
...@@ -348,5 +348,5 @@ VideoDeviceMonitor::unserialize(const YAML::Node &in) ...@@ -348,5 +348,5 @@ VideoDeviceMonitor::unserialize(const YAML::Node &in)
else else
defaultDevice_ = first; defaultDevice_ = first;
} }
#pragma optimize("", on)
}} // namespace jami::video }} // namespace jami::video
...@@ -67,7 +67,8 @@ class VideoDeviceImpl { ...@@ -67,7 +67,8 @@ class VideoDeviceImpl {
}; };
VideoDeviceImpl::VideoDeviceImpl(const std::string& path) VideoDeviceImpl::VideoDeviceImpl(const std::string& path)
: name(path) : device(path)
, name()
, cInterface(new CaptureGraphInterfaces()) , cInterface(new CaptureGraphInterfaces())
{ {
setup(); setup();
...@@ -96,44 +97,75 @@ VideoDeviceImpl::setup() ...@@ -96,44 +97,75 @@ VideoDeviceImpl::setup()
if (FAILED(hr)) if (FAILED(hr))
return fail("Could not set filtergraph."); return fail("Could not set filtergraph.");
ICreateDevEnum *pSysDevEnum = nullptr; ICreateDevEnum *pDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
(void **)&pSysDevEnum);
if (FAILED(hr))
return fail("Could not create the enumerator!");
IEnumMoniker* pEnumCat = nullptr; IEnumMoniker* pEnum = NULL;
hr = pSysDevEnum->CreateClassEnumerator( hr = pDevEnum->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory, CLSID_VideoInputDeviceCategory, &pEnum, 0);
&pEnumCat, if (hr == S_FALSE) {
0); hr = VFW_E_NOT_FOUND;
if (SUCCEEDED(hr)) { }
// Auto-deletion at if {} exist or at exception pDevEnum->Release();
if (FAILED(hr) || pEnum == nullptr) {
JAMI_ERR() << "No webcam found";
return;
}
// Auto-deletion at exception
auto IEnumMonikerDeleter = [](IEnumMoniker* p) { p->Release(); }; auto IEnumMonikerDeleter = [](IEnumMoniker* p) { p->Release(); };
std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumCatGuard {pEnumCat, IEnumMonikerDeleter}; std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumGuard{ pEnum, IEnumMonikerDeleter };
IMoniker *pMoniker = nullptr; IMoniker *pMoniker = NULL;
ULONG cFetched; while ((pEnumGuard->Next(1, &pMoniker, NULL) == S_OK)) {
while ((pEnumCatGuard->Next(1, &pMoniker, &cFetched) == S_OK)) {
IPropertyBag *pPropBag; IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if (FAILED(hr)) { if (FAILED(hr)) {
pMoniker->Release();
continue; 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; VARIANT var;
VariantInit(&var); VariantInit(&var);
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr)) {
hr = pPropBag->Read(L"FriendlyName", &var, 0); hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
// We want to get the capabilities of a device with the friendly name this->name = to_string(var.bstrVal);
// 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; pPropBag->Write(L"FriendlyName", &var);
hr = pMoniker->BindToObject( hr = pMoniker->BindToObject(
nullptr, nullptr, nullptr, nullptr,
IID_IBaseFilter, IID_IBaseFilter,
...@@ -162,16 +194,19 @@ VideoDeviceImpl::setup() ...@@ -162,16 +194,19 @@ VideoDeviceImpl::setup()
fail("Couldn't config the stream!"); fail("Couldn't config the stream!");
} }
} }
VariantClear(&var);
pPropBag->Release();
// Device found. // Device found.
break; break;
} }
VariantClear(&var);
pPropBag->Release();
pPropBag = nullptr;
pMoniker->Release(); pMoniker->Release();
pMoniker = nullptr;
if (FAILED(hr) || cInterface->streamConf_ == NULL) {
fail("Could not find the video device.");
} }
if (SUCCEEDED(hr)) {
int piCount; int piCount;
int piSize; int piSize;
cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize); cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize);
...@@ -209,10 +244,6 @@ VideoDeviceImpl::setup() ...@@ -209,10 +244,6 @@ VideoDeviceImpl::setup()
capMap_[size] = pmt; capMap_[size] = pmt;
} }
} }
}
pSysDevEnum->Release();
pSysDevEnum = NULL;
}
void void
VideoDeviceImpl::fail(const std::string& error) VideoDeviceImpl::fail(const std::string& error)
......
...@@ -83,66 +83,18 @@ VideoDeviceMonitorImpl::~VideoDeviceMonitorImpl() ...@@ -83,66 +83,18 @@ VideoDeviceMonitorImpl::~VideoDeviceMonitorImpl()
} }
std::string 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 std::string unique_name = pbdi->dbcc_name;
// 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;
DWORD DataT;
DWORD nSize = 0;
TCHAR buf[260];
if (!SetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { std::transform(unique_name.begin(), unique_name.end(), unique_name.begin(),
break; [](unsigned char c) { return std::tolower(c); }
} );
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;
}
}
}
if (pspDevInfoData) { auto pos = unique_name.find_last_of("#");
HeapFree(GetProcessHeap(), 0, pspDevInfoData); unique_name = unique_name.substr(0, pos);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return friendlyName; return unique_name;
} }
bool bool
...@@ -207,15 +159,19 @@ VideoDeviceMonitorImpl::WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, ...@@ -207,15 +159,19 @@ VideoDeviceMonitorImpl::WinProcCallback(HWND hWnd, UINT message, WPARAM wParam,
case DBT_DEVICEREMOVECOMPLETE: case DBT_DEVICEREMOVECOMPLETE:
case DBT_DEVICEARRIVAL: case DBT_DEVICEARRIVAL:
{ {
PDEV_BROADCAST_DEVICEINTERFACE p = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; PDEV_BROADCAST_DEVICEINTERFACE pbdi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
auto friendlyName = getDeviceFriendlyName(p); auto unique_name = getDeviceUniqueName(pbdi);
if (!friendlyName.empty()) { if (!unique_name.empty()) {
JAMI_DBG() << friendlyName << ((wParam == DBT_DEVICEARRIVAL) ? " plugged" : " unplugged"); JAMI_DBG() << unique_name << ((wParam == DBT_DEVICEARRIVAL) ? " plugged" : " unplugged");
if (pThis = reinterpret_cast<VideoDeviceMonitorImpl*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))) { if (pThis = reinterpret_cast<VideoDeviceMonitorImpl*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))) {
if (wParam == DBT_DEVICEARRIVAL) { 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) { } else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
pThis->monitor_->removeDevice(friendlyName); pThis->monitor_->removeDevice(unique_name);
} }
} }
} }
...@@ -288,7 +244,8 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() ...@@ -288,7 +244,8 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices()
} }
IEnumMoniker *pEnum = nullptr; IEnumMoniker *pEnum = nullptr;
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); hr = pDevEnum->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory, &pEnum, 0);
if (hr == S_FALSE) { if (hr == S_FALSE) {
hr = VFW_E_NOT_FOUND; hr = VFW_E_NOT_FOUND;
} }
...@@ -299,7 +256,6 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() ...@@ -299,7 +256,6 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices()
} }
IMoniker *pMoniker = NULL; IMoniker *pMoniker = NULL;
unsigned deviceID = 0;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
IPropertyBag *pPropBag; IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
...@@ -308,27 +264,25 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices() ...@@ -308,27 +264,25 @@ VideoDeviceMonitorImpl::enumerateVideoInputDevices()
continue; continue;
} }
VARIANT var; IBindCtx *bind_ctx = NULL;
VariantInit(&var); LPOLESTR olestr = NULL;
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr)) {
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr)) { hr = CreateBindCtx(0, &bind_ctx);
auto deviceName = bstrToStdString(var.bstrVal); if (hr != S_OK) {
if (!deviceName.empty()) { continue;
deviceList.push_back(deviceName);
} }
VariantClear(&var); 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(); pPropBag->Release();
pMoniker->Release();
deviceID++;
} }
pEnum->Release(); pEnum->Release();
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
namespace jami { namespace jami {
#ifdef _WIN32 #ifdef _WIN32
std::wstring std::wstring
to_wstring(const std::string& str, int codePage) to_wstring(const std::string& str, int codePage)
{ {
...@@ -65,17 +64,6 @@ to_string(const std::wstring& wstr, int codePage) ...@@ -65,17 +64,6 @@ to_string(const std::wstring& wstr, int codePage)
} }
return result; 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 #endif
std::string std::string
......
...@@ -43,7 +43,6 @@ std::string to_string(double value); ...@@ -43,7 +43,6 @@ std::string to_string(double value);
#ifdef _WIN32 #ifdef _WIN32
std::wstring to_wstring(const std::string& str, int codePage = CP_UTF8); 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 to_string(const std::wstring& wstr, int codePage = CP_UTF8);
std::string bstrToStdString(BSTR bstr);
#endif #endif
static inline int static inline int
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment