mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-01-30 03:32:43 +00:00
Allow audio sinks to match on device names
Names are more stable than IDs on Windows
This commit is contained in:
parent
3fa5f74635
commit
1d6ea8c759
@ -447,6 +447,8 @@ audio_sink
|
||||
|
||||
tools\audio-info.exe
|
||||
|
||||
.. Tip:: If you have multiple audio devices with identical names, use the Device ID instead.
|
||||
|
||||
.. Tip:: If you want to mute the host speakers, use `virtual_sink`_ instead.
|
||||
|
||||
**Default**
|
||||
@ -466,7 +468,7 @@ audio_sink
|
||||
**Windows**
|
||||
.. code-block:: text
|
||||
|
||||
audio_sink = {0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}
|
||||
audio_sink = Speakers (High Definition Audio Device)
|
||||
|
||||
virtual_sink
|
||||
^^^^^^^^^^^^
|
||||
@ -488,7 +490,7 @@ virtual_sink
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
virtual_sink = {0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}
|
||||
virtual_sink = Steam Streaming Speakers
|
||||
|
||||
Network
|
||||
-------
|
||||
|
@ -515,7 +515,10 @@ namespace platf::audio {
|
||||
UINT count;
|
||||
collection->GetCount(&count);
|
||||
|
||||
std::string virtual_device_id = config::audio.virtual_sink;
|
||||
// If the sink isn't a device name, we'll assume it's a device ID
|
||||
auto virtual_device_id = find_device_id_by_name(config::audio.virtual_sink).value_or(converter.from_bytes(config::audio.virtual_sink));
|
||||
auto virtual_device_found = false;
|
||||
|
||||
for (auto x = 0; x < count; ++x) {
|
||||
audio::device_t device;
|
||||
collection->Item(x, &device);
|
||||
@ -526,6 +529,7 @@ namespace platf::audio {
|
||||
|
||||
audio::wstring_t wstring;
|
||||
device->GetId(&wstring);
|
||||
std::wstring device_id { wstring.get() };
|
||||
|
||||
audio::prop_t prop;
|
||||
device->OpenPropertyStore(STGM_READ, &prop);
|
||||
@ -548,17 +552,27 @@ namespace platf::audio {
|
||||
<< std::endl;
|
||||
|
||||
if (virtual_device_id.empty() && adapter_name == virtual_adapter_name) {
|
||||
virtual_device_id = converter.to_bytes(wstring.get());
|
||||
virtual_device_id = std::move(device_id);
|
||||
virtual_device_found = true;
|
||||
break;
|
||||
}
|
||||
else if (virtual_device_id == device_id) {
|
||||
virtual_device_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!virtual_device_id.empty()) {
|
||||
if (virtual_device_found) {
|
||||
auto name_suffix = converter.to_bytes(virtual_device_id);
|
||||
sink.null = std::make_optional(sink_t::null_t {
|
||||
"virtual-"s.append(formats[format_t::stereo - 1].name) + virtual_device_id,
|
||||
"virtual-"s.append(formats[format_t::surr51 - 1].name) + virtual_device_id,
|
||||
"virtual-"s.append(formats[format_t::surr71 - 1].name) + virtual_device_id,
|
||||
"virtual-"s.append(formats[format_t::stereo - 1].name) + name_suffix,
|
||||
"virtual-"s.append(formats[format_t::surr51 - 1].name) + name_suffix,
|
||||
"virtual-"s.append(formats[format_t::surr71 - 1].name) + name_suffix,
|
||||
});
|
||||
}
|
||||
else if (!virtual_device_id.empty()) {
|
||||
BOOST_LOG(warning) << "Unable to find the specified virtual sink: "sv << virtual_device_id;
|
||||
}
|
||||
|
||||
return sink;
|
||||
}
|
||||
@ -604,7 +618,8 @@ namespace platf::audio {
|
||||
}
|
||||
}
|
||||
|
||||
auto wstring_device_id = converter.from_bytes(sv.data());
|
||||
// If the sink isn't a device name, we'll assume it's a device ID
|
||||
auto wstring_device_id = find_device_id_by_name(sink).value_or(converter.from_bytes(sv.data()));
|
||||
|
||||
if (type == format_t::none) {
|
||||
// wstring_device_id does not contain virtual-(format name)
|
||||
@ -660,6 +675,83 @@ namespace platf::audio {
|
||||
return failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find the audio device ID given a user-specified name
|
||||
*
|
||||
* @param name The name provided by the user
|
||||
*
|
||||
* @return The matching device ID, or nothing if not found
|
||||
*/
|
||||
std::optional<std::wstring>
|
||||
find_device_id_by_name(const std::string &name) {
|
||||
if (name.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
audio::device_enum_t device_enum;
|
||||
auto status = CoCreateInstance(
|
||||
CLSID_MMDeviceEnumerator,
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **) &device_enum);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
collection_t collection;
|
||||
status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
UINT count;
|
||||
collection->GetCount(&count);
|
||||
|
||||
auto wstring_name = converter.from_bytes(name.data());
|
||||
|
||||
for (auto x = 0; x < count; ++x) {
|
||||
audio::device_t device;
|
||||
collection->Item(x, &device);
|
||||
|
||||
if (!validate_device(device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
audio::wstring_t wstring_id;
|
||||
device->GetId(&wstring_id);
|
||||
|
||||
audio::prop_t prop;
|
||||
device->OpenPropertyStore(STGM_READ, &prop);
|
||||
|
||||
prop_var_t adapter_friendly_name;
|
||||
prop_var_t device_friendly_name;
|
||||
prop_var_t device_desc;
|
||||
|
||||
prop->GetValue(PKEY_Device_FriendlyName, &device_friendly_name.prop);
|
||||
prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop);
|
||||
prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop);
|
||||
|
||||
auto adapter_name = no_null((LPWSTR) adapter_friendly_name.prop.pszVal);
|
||||
auto device_name = no_null((LPWSTR) device_friendly_name.prop.pszVal);
|
||||
auto device_description = no_null((LPWSTR) device_desc.prop.pszVal);
|
||||
|
||||
// Match the user-specified name against any of the user-visible strings
|
||||
if (std::wcscmp(wstring_name.c_str(), adapter_name) == 0 ||
|
||||
std::wcscmp(wstring_name.c_str(), device_name) == 0 ||
|
||||
std::wcscmp(wstring_name.c_str(), device_description) == 0) {
|
||||
return std::make_optional(std::wstring { wstring_id.get() });
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int
|
||||
init() {
|
||||
auto status = CoCreateInstance(
|
||||
|
@ -448,13 +448,14 @@
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="audio_sink"
|
||||
placeholder="{0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}"
|
||||
placeholder="Speakers (High Definition Audio Device)"
|
||||
v-model="config.audio_sink"
|
||||
/>
|
||||
<div class="form-text">
|
||||
The name of the audio sink used for Audio Loopback<br />
|
||||
The name of the audio sink used for audio capture. If not set, the default audio device will be used.<br />
|
||||
You can find the name of the audio sink using the following command:<br />
|
||||
<pre>tools\audio-info.exe</pre>
|
||||
If you have multiple audio devices with identical names, use the Device ID instead.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3" v-if="platform === 'linux'">
|
||||
@ -506,12 +507,12 @@
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="virtual_sink"
|
||||
placeholder="{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}"
|
||||
placeholder="Steam Streaming Speakers"
|
||||
v-model="config.virtual_sink"
|
||||
/>
|
||||
<div class="form-text">
|
||||
The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows Sunshine to
|
||||
stream audio, while muting the speakers.
|
||||
The virtual sink is an audio device that's virtual (like Steam Streaming Speakers). It allows Sunshine to
|
||||
stream audio, while muting the host PC speakers.
|
||||
</div>
|
||||
</div>
|
||||
<!--Adapter Name -->
|
||||
|
Loading…
x
Reference in New Issue
Block a user