Index: media/audio/win/core_audio_util_win.cc |
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc |
index b56d6e6a6e13f9ef749ec4ac57c5e5111c102164..b3bc62fb8bc867c1a08275461d8d966453090d85 100644 |
--- a/media/audio/win/core_audio_util_win.cc |
+++ b/media/audio/win/core_audio_util_win.cc |
@@ -136,18 +136,6 @@ static bool LoadAudiosesDll() { |
return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); |
} |
-static bool CanCreateDeviceEnumerator() { |
- ScopedComPtr<IMMDeviceEnumerator> device_enumerator; |
- HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
- NULL, CLSCTX_INPROC_SERVER); |
- |
- // If we hit CO_E_NOTINITIALIZED, CoInitialize has not been called and it |
- // must be called at least once for each thread that uses the COM library. |
- CHECK_NE(hr, CO_E_NOTINITIALIZED); |
- |
- return SUCCEEDED(hr); |
-} |
- |
static std::string GetDeviceID(IMMDevice* device) { |
ScopedCoMem<WCHAR> device_id_com; |
std::string device_id; |
@@ -156,7 +144,68 @@ static std::string GetDeviceID(IMMDevice* device) { |
return device_id; |
} |
-bool CoreAudioUtil::IsSupported() { |
+static HRESULT GetDeviceFriendlyNameInternal(IMMDevice* device, |
+ std::string* friendly_name) { |
+ // Retrieve user-friendly name of endpoint device. |
+ // Example: "Microphone (Realtek High Definition Audio)". |
+ ScopedComPtr<IPropertyStore> properties; |
+ HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); |
+ if (FAILED(hr)) |
+ return hr; |
+ |
+ base::win::ScopedPropVariant friendly_name_pv; |
+ hr = properties->GetValue(PKEY_Device_FriendlyName, |
+ friendly_name_pv.Receive()); |
+ if (FAILED(hr)) |
+ return hr; |
+ |
+ if (friendly_name_pv.get().vt == VT_LPWSTR && |
+ friendly_name_pv.get().pwszVal) { |
+ base::WideToUTF8(friendly_name_pv.get().pwszVal, |
+ wcslen(friendly_name_pv.get().pwszVal), friendly_name); |
+ } |
+ |
+ return hr; |
+} |
+ |
+static ScopedComPtr<IMMDeviceEnumerator> CreateDeviceEnumeratorInternal() { |
+ ScopedComPtr<IMMDeviceEnumerator> device_enumerator; |
+ HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
+ NULL, CLSCTX_INPROC_SERVER); |
+ if (hr == CO_E_NOTINITIALIZED) { |
+ LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED"; |
+ // We have seen crashes which indicates that this method can in fact |
+ // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party |
+ // modules. Calling CoInitializeEx is an attempt to resolve the reported |
+ // issues. See http://crbug.com/378465 for details. |
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
henrika (OOO until Aug 14)
2015/04/07 07:41:08
I hope Tommi can comment on this part.
DaleCurtis
2015/04/07 22:24:54
Can you elaborate on your question? I think it was
henrika (OOO until Aug 14)
2015/04/08 06:57:36
I think you are correct but maybe Tommi has a seco
|
+ if (SUCCEEDED(hr)) { |
+ hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
+ NULL, CLSCTX_INPROC_SERVER); |
+ } |
+ } |
+ return device_enumerator; |
+} |
+ |
+static bool IsRemoteSession() { |
+ return !!GetSystemMetrics(SM_REMOTESESSION); |
+} |
+ |
+static bool IsRemoteDeviceInternal(IMMDevice* device) { |
+ DCHECK(IsRemoteSession()); |
+ |
+ std::string device_name; |
+ HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name); |
+ |
+ // This method should only be called if IsRemoteSession() is true, so assume |
+ // we have a remote audio device if we can't tell. |
+ if (FAILED(hr)) |
+ return true; |
+ |
+ return device_name == "Remote Audio"; |
henrika (OOO until Aug 14)
2015/04/07 07:41:08
Any reference to how we know that this is always t
DaleCurtis
2015/04/07 22:24:54
Couldn't find any reference. I have no idea if "Re
henrika (OOO until Aug 14)
2015/04/08 06:57:36
Acknowledged.
|
+} |
+ |
+static bool IsSupportedInternal() { |
// It is possible to force usage of WaveXxx APIs by using a command line flag. |
const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
if (cmd_line->HasSwitch(switches::kForceWaveAudio)) { |
@@ -176,19 +225,47 @@ bool CoreAudioUtil::IsSupported() { |
// the Audioses DLL since it depends on Mmdevapi.dll. |
// See http://crbug.com/166397 why this extra step is required to guarantee |
// Core Audio support. |
- static bool g_audioses_dll_available = LoadAudiosesDll(); |
- if (!g_audioses_dll_available) |
+ if (!LoadAudiosesDll()) |
return false; |
// Being able to load the Audioses.dll does not seem to be sufficient for |
// all devices to guarantee Core Audio support. To be 100%, we also verify |
// that it is possible to a create the IMMDeviceEnumerator interface. If this |
// works as well we should be home free. |
- static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator(); |
- LOG_IF(ERROR, !g_can_create_device_enumerator) |
- << "Failed to create Core Audio device enumerator on thread with ID " |
- << GetCurrentThreadId(); |
- return g_can_create_device_enumerator; |
+ ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
+ CreateDeviceEnumeratorInternal(); |
+ if (!device_enumerator) { |
+ LOG(ERROR) |
+ << "Failed to create Core Audio device enumerator on thread with ID " |
+ << GetCurrentThreadId(); |
+ return false; |
+ } |
+ |
+ // Don't use CoreAudio when a remote desktop session with remote audio is |
+ // present; several users report only WaveAudio working for them and crash |
+ // reports show hangs when calling into the OS for CoreAudio API calls. See |
+ // http://crbug.com/422522 and http://crbug.com/180591. |
+ // |
+ // Note: There's another check in WASAPIAudioOutputStream::Open() for the case |
+ // where a remote session is created after Chrome has been started. Graceful |
+ // fallback to WaveOut will occur in this case via AudioOutputResampler. |
+ if (!IsRemoteSession()) |
+ return true; |
+ |
+ ScopedComPtr<IMMDevice> device; |
+ HRESULT hr = device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, |
+ device.Receive()); |
+ |
+ // Assume remote audio playback if we can't tell. |
+ if (FAILED(hr)) |
henrika (OOO until Aug 14)
2015/04/07 07:41:08
Do we need this check? Is it not included in IsRem
DaleCurtis
2015/04/07 22:24:54
Seems GetDefaultAudioEndpoint could still fail? th
henrika (OOO until Aug 14)
2015/04/08 06:57:36
Acknowledged.
|
+ return false; |
+ |
+ return !IsRemoteDeviceInternal(device.get()); |
+} |
+ |
+bool CoreAudioUtil::IsSupported() { |
+ static bool g_is_supported = IsSupportedInternal(); |
+ return g_is_supported; |
} |
base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) { |
@@ -233,22 +310,9 @@ int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) { |
ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { |
DCHECK(IsSupported()); |
- ScopedComPtr<IMMDeviceEnumerator> device_enumerator; |
- HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
- NULL, CLSCTX_INPROC_SERVER); |
- if (hr == CO_E_NOTINITIALIZED) { |
- LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED"; |
- // We have seen crashes which indicates that this method can in fact |
- // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party |
- // modules. Calling CoInitializeEx is an attempt to resolve the reported |
- // issues. See http://crbug.com/378465 for details. |
- hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
- if (SUCCEEDED(hr)) { |
- hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
- NULL, CLSCTX_INPROC_SERVER); |
- } |
- } |
- CHECK(SUCCEEDED(hr)); |
+ ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
+ CreateDeviceEnumeratorInternal(); |
+ CHECK(device_enumerator); |
return device_enumerator; |
} |
@@ -323,21 +387,9 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { |
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; |
- HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); |
- if (FAILED(hr)) |
- return hr; |
- base::win::ScopedPropVariant friendly_name; |
- hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive()); |
+ HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name.device_name); |
if (FAILED(hr)) |
return hr; |
- if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) { |
- base::WideToUTF8(friendly_name.get().pwszVal, |
- wcslen(friendly_name.get().pwszVal), |
- &device_name.device_name); |
- } |
*name = device_name; |
DVLOG(2) << "friendly name: " << device_name.device_name; |
@@ -854,4 +906,15 @@ bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( |
AUDCLNT_BUFFERFLAGS_SILENT)); |
} |
+bool CoreAudioUtil::IsRemoteOutputDevice(const std::string& device_id) { |
+ DCHECK(IsSupported()); |
+ if (!IsRemoteSession()) |
+ return false; |
+ ScopedComPtr<IMMDevice> device(device_id.empty() |
+ ? CreateDefaultDevice(eRender, eConsole) |
+ : CreateDevice(device_id)); |
+ // Assume remote audio if we can't tell. |
+ return device ? IsRemoteDeviceInternal(device.get()) : true; |
+} |
+ |
} // namespace media |