text: #include "pch.h"
  #include "DeviceEnumerator.h"
  
  std::map<int, Device> DeviceEnumerator::getVideoDevicesMap()
  {
  	return getDevicesMap(CLSID_VideoInputDeviceCategory);
  }
  
  std::map<int, Device> DeviceEnumerator::getAudioDevicesMap()
  {
  	return getDevicesMap(CLSID_AudioInputDeviceCategory);
  }
  
  // Returns a map of id and devices that can be used
  std::map<int, Device> DeviceEnumerator::getDevicesMap(const GUID deviceClass)
  {
  	std::map<int, Device> deviceMap;
  
  	HRESULT hr = CoInitialize(nullptr);
  	if (FAILED(hr))
  	{
  		return deviceMap; // Empty deviceMap as an error
  	}
  
  	// Create the System Device Enumerator
  	ICreateDevEnum *pDevEnum;
  	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
  
  	// If succeeded, create an enumerator for the category
  	IEnumMoniker *pEnum = NULL;
  	if (SUCCEEDED(hr))
  	{
  		hr = pDevEnum->CreateClassEnumerator(deviceClass, &pEnum, 0);
  		if (hr == S_FALSE)
  		{
  			hr = VFW_E_NOT_FOUND;
  		}
  		pDevEnum->Release();
  	}
  
  	// Now we check if the enumerator creation succeeded
  	int deviceId = -1;
  	if (SUCCEEDED(hr))
  	{
  		// Fill the map with id and friendly device name
  		IMoniker *pMoniker = NULL;
  		while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
  		{
  
  			IPropertyBag *pPropBag;
  			HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
  			if (FAILED(hr))
  			{
  				pMoniker->Release();
  				continue;
  			}
  
  			// Create variant to hold data
  			VARIANT var;
  			VariantInit(&var);
  
  			std::string deviceName;
  			std::string devicePath;
  
  			// Read FriendlyName or Description
  			hr = pPropBag->Read(L"Description", &var, 0); // Read description
  			if (FAILED(hr))
  			{
  				// If description fails, try with the friendly name
  				hr = pPropBag->Read(L"FriendlyName", &var, 0);
  			}
  			// If still fails, continue with next device
  			if (FAILED(hr))
  			{
  				VariantClear(&var);
  				continue;
  			}
  			// Convert to string
  			else
  			{
  				deviceName = ConvertBSTRToMBS(var.bstrVal);
  			}
  
  			VariantClear(&var); // We clean the variable in order to read the next value
  
  								// We try to read the DevicePath
  			hr = pPropBag->Read(L"DevicePath", &var, 0);
  			if (FAILED(hr))
  			{
  				VariantClear(&var);
  				continue; // If it fails we continue with next device
  			}
  			else
  			{
  				devicePath = ConvertBSTRToMBS(var.bstrVal);
  			}
  
  			// We populate the map
  			deviceId++;
  			Device currentDevice;
  			currentDevice.id = deviceId;
  			currentDevice.deviceName = deviceName;
  			currentDevice.devicePath = devicePath;
  			deviceMap[deviceId] = currentDevice;
  
  		}
  		pEnum->Release();
  	}
  	CoUninitialize();
  	return deviceMap;
  }
  
  /*
  This two methods were taken from
  https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
  */
  
  std::string DeviceEnumerator::ConvertBSTRToMBS(BSTR bstr)
  {
  	int wslen = ::SysStringLen(bstr);
  	return ConvertWCSToMBS((wchar_t*)bstr, wslen);
  }
  
  std::string DeviceEnumerator::ConvertWCSToMBS(const wchar_t* pstr, long wslen)
  {
  	int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);
  
  	std::string dblstr(len, '\0');
  	len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
  		pstr, wslen /* not necessary NULL-terminated */,
  		&dblstr[0], len,
  		NULL, NULL /* no default char */);
  
  	return dblstr;
  }
  