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
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
......@@ -67,7 +67,8 @@ class VideoDeviceImpl {
};
VideoDeviceImpl::VideoDeviceImpl(const std::string& path)
: name(path)
: device(path)
, name()
, cInterface(new CaptureGraphInterfaces())
{
setup();
......@@ -96,44 +97,75 @@ 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
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)&> pEnumCatGuard {pEnumCat, IEnumMonikerDeleter};
std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumGuard{ pEnum, IEnumMonikerDeleter };
IMoniker *pMoniker = nullptr;
ULONG cFetched;
while ((pEnumCatGuard->Next(1, &pMoniker, &cFetched) == S_OK)) {
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();
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)) {
// 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->name = to_string(var.bstrVal);
}
this->device = std::string("video=") + this->name;
pPropBag->Write(L"FriendlyName", &var);
hr = pMoniker->BindToObject(
nullptr, nullptr,
IID_IBaseFilter,
......@@ -162,16 +194,19 @@ VideoDeviceImpl::setup()
fail("Couldn't config the stream!");
}
}
VariantClear(&var);
pPropBag->Release();
// Device found.
break;
}
VariantClear(&var);
pPropBag->Release();
pPropBag = nullptr;
pMoniker->Release();
pMoniker = nullptr;
if (FAILED(hr) || cInterface->streamConf_ == NULL) {
fail("Could not find the video device.");
}
if (SUCCEEDED(hr)) {
int piCount;
int piSize;
cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize);
......@@ -209,10 +244,6 @@ VideoDeviceImpl::setup()
capMap_[size] = pmt;
}
}
}
pSysDevEnum->Release();
pSysDevEnum = NULL;
}
void
VideoDeviceImpl::fail(const std::string& error)
......
......@@ -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;
DWORD DataT;
DWORD nSize = 0;
TCHAR buf[260];
std::string unique_name = pbdi->dbcc_name;
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);
hr = CreateBindCtx(0, &bind_ctx);
if (hr != S_OK) {
continue;
}
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();
pMoniker->Release();
deviceID++;
}
pEnum->Release();
......
......@@ -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
......
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment