summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authordalecurtis <dalecurtis@chromium.org>2015-04-08 10:21:35 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-08 17:22:59 +0000
commitd0d6ea4bbdd431f0b794ae5e8cf353e2e87593ab (patch)
tree9fb1b98c56cdfa592c94366f6c40398d437ffca9 /media/audio
parentde96417fd6268522589f13f29a3c3cdb87d71bea (diff)
downloadchromium_src-d0d6ea4bbdd431f0b794ae5e8cf353e2e87593ab.zip
chromium_src-d0d6ea4bbdd431f0b794ae5e8cf353e2e87593ab.tar.gz
chromium_src-d0d6ea4bbdd431f0b794ae5e8cf353e2e87593ab.tar.bz2
Disable low latency audio under remote desktop sessions.
CoreAudio seems to be the source of a lot of hangs when under a remote desktop session and we've had past success with moving these users over to WaveOut, so do so for all remote sessions. This first queries the SM_REMOTESESSION system metric and then tries to get the friendly name of the default audio device; if it can't do that and a remote session is present or it can and friendly name is "Remote Audio", CoreAudio functions are disabled. This change also adds a check to WASAPIAudioOutputStream::Open to fail when a remote audio stream is requested; which will trigger fallback in the AudioOutputResampler (if the user resumes their session locally later they'll get WASAPI again once streams have been closed for a few seconds). BUG=422522 TEST=remote desktop session w/ remote audio gets WaveOut; works with xfreerdp and Microsoft Remote Desktop client for OS; local session gets WASAPI, connecting and starting a stream remotely gets WaveOut. Review URL: https://codereview.chromium.org/1060673002 Cr-Commit-Position: refs/heads/master@{#324246}
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/win/audio_low_latency_output_win.cc5
-rw-r--r--media/audio/win/core_audio_util_win.cc161
-rw-r--r--media/audio/win/core_audio_util_win.h3
3 files changed, 120 insertions, 49 deletions
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index f7b31a3..53b8a8d 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -135,6 +135,11 @@ bool WASAPIAudioOutputStream::Open() {
DCHECK(!audio_client_.get());
DCHECK(!audio_render_client_.get());
+ // Don't allow WASAPI streams to be created for remote output devices, this
+ // frequently leads to hangs of the audio thread. http://crbug.com/422522.
+ if (CoreAudioUtil::IsRemoteOutputDevice(device_id_))
+ return false;
+
// Will be set to true if we ended up opening the default communications
// device.
bool communications_device = false;
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index b56d6e6..b3bc62f 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);
+ 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";
+}
+
+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))
+ 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
diff --git a/media/audio/win/core_audio_util_win.h b/media/audio/win/core_audio_util_win.h
index 4e93531..75e90ce 100644
--- a/media/audio/win/core_audio_util_win.h
+++ b/media/audio/win/core_audio_util_win.h
@@ -229,6 +229,9 @@ class MEDIA_EXPORT CoreAudioUtil {
static bool FillRenderEndpointBufferWithSilence(
IAudioClient* client, IAudioRenderClient* render_client);
+ // Returns true if the given output device is a remote audio endpoint.
+ static bool IsRemoteOutputDevice(const std::string& device_id);
+
private:
CoreAudioUtil() {}
~CoreAudioUtil() {}