void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { /* ensure that ALSA has been initialized */ snd_lib_error_set_handler(sndLibErrorHandler); if(!snd_config || force) { if(force) snd_config_update_free_global(); snd_config_update(); } snd_config_t *config; snd_config_copy(&config, snd_config); /* Always enumerate the default device. * Note: If "default" is a stereo device, EnumerateDevice() * will automatically add "@" instead to enable surroundXX mangling. * We don't want to do that if "default" can handle multichannel * itself (e.g. in case of a pulseaudio server). */ EnumerateDevice(list, "default", "", config); void **hints; if (snd_device_name_hint(-1, "pcm", &hints) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices"); return; } std::string defaultDescription; for (void** hint = hints; *hint != NULL; ++hint) { char *io = snd_device_name_get_hint(*hint, "IOID"); char *name = snd_device_name_get_hint(*hint, "NAME"); char *desc = snd_device_name_get_hint(*hint, "DESC"); if ((!io || strcmp(io, "Output") == 0) && name && strcmp(name, "null") != 0) { std::string baseName = std::string(name); baseName = baseName.substr(0, baseName.find(':')); if (strcmp(name, "default") == 0) { /* added already, but lets get the description if we have one */ if (desc) defaultDescription = desc; } else if (baseName == "front") { /* Enumerate using the surroundXX mangling */ /* do not enumerate basic "front", it is already handled * by the default "@" entry added in the very beginning */ if (strcmp(name, "front") != 0) EnumerateDevice(list, std::string("@") + (name+5), desc ? desc : name, config); } /* Do not enumerate "default", it is already enumerated above. */ /* Do not enumerate the sysdefault or surroundXX devices, those are * always accompanied with a "front" device and it is handled above * as "@". The below devices will be automatically used if available * for a "@" device. */ /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended" * defaults to "on" instead of upstream "off", causing lots of unwanted * extra devices (many of which are not actually routed properly) to be * found by the enumeration process. Skip them as well ("hw", "dmix", * "plughw", "dsnoop"). */ else if (baseName != "default" && baseName != "sysdefault" && baseName != "surround40" && baseName != "surround41" && baseName != "surround50" && baseName != "surround51" && baseName != "surround71" && baseName != "hw" && baseName != "dmix" && baseName != "plughw" && baseName != "dsnoop") { EnumerateDevice(list, name, desc ? desc : name, config); } } free(io); free(name); free(desc); } snd_device_name_free_hint(hints); /* set the displayname for default device */ if (!list.empty() && list[0].m_deviceName == "default") { /* If we have one from a hint (DESC), use it */ if (!defaultDescription.empty()) list[0].m_displayName = defaultDescription; /* Otherwise use the discovered name or (unlikely) "Default" */ else if (list[0].m_displayName.empty()) list[0].m_displayName = "Default"; } /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */ /* If even a single device of card/dev X clashes with Y, add suffixes to * all devices of both them, for clarity. */ /* clashing card names, e.g. "NVidia", "NVidia_2" */ std::set<std::string> cardsToAppend; /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */ std::set<std::pair<std::string, std::string> > devsToAppend; for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1) { for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2) { if (it1->m_displayName == it2->m_displayName && it1->m_displayNameExtra == it2->m_displayNameExtra) { /* something needs to be done */ std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD"); std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD"); if (cardString1 != cardString2) { /* card name differs, add identifiers to all devices */ cardsToAppend.insert(cardString1); cardsToAppend.insert(cardString2); continue; } std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV"); std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV"); if (devString1 != devString2) { /* device number differs, add identifiers to all such devices */ devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1)); devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2)); continue; } /* if we got here, the configuration is really weird, just append the whole device string */ it1->m_displayName += " (" + it1->m_deviceName + ")"; it2->m_displayName += " (" + it2->m_deviceName + ")"; } } } for (std::set<std::string>::iterator it = cardsToAppend.begin(); it != cardsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (cardString == *it) /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */ itl->m_displayName += " (" + cardString + ")"; } } for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin(); it != devsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':')); std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (baseName == it->first && cardString == it->second) { std::string devString = GetParamFromName(itl->m_deviceName, "DEV"); /* "HDMI #0", "HDMI #1" ... */ itl->m_displayNameExtra += " #" + devString; } } } }
void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { CAEDeviceInfo deviceInfo; IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; HRESULT hr; /* See if we are on Windows XP */ if (!g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionVista)) { /* We are on XP - WASAPI not supported - enumerate using DS devices */ LPGUID deviceGUID = NULL; RPC_CSTR cszGUID; std::string szGUID; std::list<DSDevice> DSDeviceList; DirectSoundEnumerate(DSEnumCallback, &DSDeviceList); for(std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); ++itt) { if (UuidToString((*itt).lpGuid, &cszGUID) != RPC_S_OK) continue; /* could not convert GUID to string - skip device */ deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); szGUID = (LPSTR)cszGUID; deviceInfo.m_deviceName = "{" + szGUID + "}"; deviceInfo.m_displayName = (*itt).name; deviceInfo.m_displayNameExtra = std::string("DirectSound: ") + (*itt).name; deviceInfo.m_deviceType = AE_DEVTYPE_PCM; deviceInfo.m_channels = layoutsByChCount[2]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back((DWORD) 96000); deviceInfoList.push_back(deviceInfo); } RpcStringFree(&cszGUID); return; } /* Windows Vista or later - supporting WASAPI device probing */ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr) UINT uiCount = 0; hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for (UINT i = 0; i < uiCount; i++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strFriendlyName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strDevName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); /* In shared mode Windows tells us what format the audio must be in. */ IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr)); goto failed; } //hr = pClient->GetMixFormat(&pwfxex); hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName); if (SUCCEEDED(hr) && varName.blob.cbSize > 0) { WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData; deviceInfo.m_channels = layoutsByChCount[std::max(std::min(smpwfxex->nChannels, (WORD) DS_SPEAKER_COUNT), (WORD) 2)]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 192000)); } else { CLog::Log(LOGERROR, __FUNCTION__": Getting DeviceFormat failed (%s)", WASAPIErrToStr(hr)); } pClient->Release(); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; deviceInfoList.push_back(deviceInfo); } // since AE takes the first device in deviceInfoList as default audio device we need // to sort it in order to use the real default device if(deviceInfoList.size() > 1) { std::string strDD = GetDefaultDevice(); for (AEDeviceInfoList::iterator itt = deviceInfoList.begin(); itt != deviceInfoList.end(); ++itt) { CAEDeviceInfo devInfo = *itt; if(devInfo.m_deviceName == strDD) { deviceInfoList.erase(itt); deviceInfoList.insert(deviceInfoList.begin(), devInfo); break; } } } return; failed: if (FAILED(hr)) CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr)); SAFE_RELEASE(pEnumDevices); SAFE_RELEASE(pEnumerator); }