summaryrefslogtreecommitdiff
path: root/chromium/media/audio/win/core_audio_util_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/audio/win/core_audio_util_win.cc')
-rw-r--r--chromium/media/audio/win/core_audio_util_win.cc128
1 files changed, 107 insertions, 21 deletions
diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc
index 392184b7a01..4adfdda090a 100644
--- a/chromium/media/audio/win/core_audio_util_win.cc
+++ b/chromium/media/audio/win/core_audio_util_win.cc
@@ -4,8 +4,9 @@
#include "media/audio/win/core_audio_util_win.h"
-#include <Audioclient.h>
-#include <Functiondiscoverykeys_devpkey.h>
+#include <audioclient.h>
+#include <devicetopology.h>
+#include <functiondiscoverykeys_devpkey.h>
#include "base/command_line.h"
#include "base/logging.h"
@@ -122,7 +123,7 @@ static std::ostream& operator<<(std::ostream& os,
return os;
}
-bool LoadAudiosesDll() {
+static bool LoadAudiosesDll() {
static const wchar_t* const kAudiosesDLL =
L"%WINDIR%\\system32\\audioses.dll";
@@ -131,7 +132,7 @@ bool LoadAudiosesDll() {
return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL);
}
-bool CanCreateDeviceEnumerator() {
+static bool CanCreateDeviceEnumerator() {
ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER);
@@ -143,6 +144,14 @@ bool CanCreateDeviceEnumerator() {
return SUCCEEDED(hr);
}
+static std::string GetDeviceID(IMMDevice* device) {
+ ScopedCoMem<WCHAR> device_id_com;
+ std::string device_id;
+ if (SUCCEEDED(device->GetId(&device_id_com)))
+ WideToUTF8(device_id_com, wcslen(device_id_com), &device_id);
+ return device_id;
+}
+
bool CoreAudioUtil::IsSupported() {
// It is possible to force usage of WaveXxx APIs by using a command line flag.
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
@@ -262,6 +271,12 @@ ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow,
return endpoint_device;
}
+std::string CoreAudioUtil::GetDefaultOutputDeviceID() {
+ DCHECK(IsSupported());
+ ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole));
+ return device ? GetDeviceID(device) : std::string();
+}
+
ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice(
const std::string& device_id) {
DCHECK(IsSupported());
@@ -288,17 +303,14 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) {
// Retrieve unique name of endpoint device.
// Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
AudioDeviceName device_name;
- ScopedCoMem<WCHAR> endpoint_device_id;
- HRESULT hr = device->GetId(&endpoint_device_id);
- if (FAILED(hr))
- return hr;
- WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id),
- &device_name.unique_id);
+ device_name.unique_id = GetDeviceID(device);
+ if (device_name.unique_id.empty())
+ return E_FAIL;
// Retrieve user-friendly name of endpoint device.
// Example: "Microphone (Realtek High Definition Audio)".
ScopedComPtr<IPropertyStore> properties;
- hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
+ HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
if (FAILED(hr))
return hr;
base::win::ScopedPropVariant friendly_name;
@@ -317,6 +329,88 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) {
return hr;
}
+std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device,
+ IMMDeviceEnumerator* enumerator) {
+ DCHECK(IsSupported());
+
+ // Fetching the controller device id could be as simple as fetching the value
+ // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property
+ // store of the |device|, but that key isn't defined in any header and
+ // according to MS should not be relied upon.
+ // So, instead, we go deeper, look at the device topology and fetch the
+ // PKEY_Device_InstanceId of the associated physical audio device.
+ ScopedComPtr<IDeviceTopology> topology;
+ ScopedComPtr<IConnector> connector;
+ ScopedCoMem<WCHAR> filter_id;
+ if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL,
+ topology.ReceiveVoid()) ||
+ // For our purposes checking the first connected device should be enough
+ // and if there are cases where there are more than one device connected
+ // we're not sure how to handle that anyway. So we pass 0.
+ FAILED(topology->GetConnector(0, connector.Receive())) ||
+ FAILED(connector->GetDeviceIdConnectedTo(&filter_id)))) {
+ DLOG(ERROR) << "Failed to get the device identifier of the audio device";
+ return std::string();
+ }
+
+ // Now look at the properties of the connected device node and fetch the
+ // instance id (PKEY_Device_InstanceId) of the device node that uniquely
+ // identifies the controller.
+ ScopedComPtr<IMMDevice> device_node;
+ ScopedComPtr<IPropertyStore> properties;
+ base::win::ScopedPropVariant instance_id;
+ if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) ||
+ FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) ||
+ FAILED(properties->GetValue(PKEY_Device_InstanceId,
+ instance_id.Receive())) ||
+ instance_id.get().vt != VT_LPWSTR) {
+ DLOG(ERROR) << "Failed to get instance id of the audio device node";
+ return std::string();
+ }
+
+ std::string controller_id;
+ WideToUTF8(instance_id.get().pwszVal,
+ wcslen(instance_id.get().pwszVal),
+ &controller_id);
+
+ return controller_id;
+}
+
+std::string CoreAudioUtil::GetMatchingOutputDeviceID(
+ const std::string& input_device_id) {
+ ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id));
+ if (!input_device)
+ return std::string();
+
+ // See if we can get id of the associated controller.
+ ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator());
+ std::string controller_id(GetAudioControllerID(input_device, enumerator));
+ if (controller_id.empty())
+ return std::string();
+
+ // Now enumerate the available (and active) output devices and see if any of
+ // them is associated with the same controller.
+ ScopedComPtr<IMMDeviceCollection> collection;
+ enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE,
+ collection.Receive());
+ if (!collection)
+ return std::string();
+
+ UINT count = 0;
+ collection->GetCount(&count);
+ ScopedComPtr<IMMDevice> output_device;
+ for (UINT i = 0; i < count; ++i) {
+ collection->Item(i, output_device.Receive());
+ std::string output_controller_id(CoreAudioUtil::GetAudioControllerID(
+ output_device, enumerator));
+ if (output_controller_id == controller_id)
+ break;
+ output_device = NULL;
+ }
+
+ return output_device ? GetDeviceID(output_device) : std::string();
+}
+
std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id);
@@ -339,16 +433,8 @@ bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow,
if (!device)
return false;
- ScopedCoMem<WCHAR> default_device_id;
- HRESULT hr = device->GetId(&default_device_id);
- if (FAILED(hr))
- return false;
-
- std::string str_default;
- WideToUTF8(default_device_id, wcslen(default_device_id), &str_default);
- if (device_id.compare(str_default) != 0)
- return false;
- return true;
+ std::string str_default(GetDeviceID(device));
+ return device_id.compare(str_default) == 0;
}
EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) {