Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: media/audio/win/core_audio_util_win.cc

Issue 1060673002: Disable low latency audio under remote desktop sessions. (Closed) Base URL: http://chromium.googlesource.com/chromium/src.git@master
Patch Set: Disable for remote audio, on Open() too. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/audio/win/core_audio_util_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/win/core_audio_util_win.h" 5 #include "media/audio/win/core_audio_util_win.h"
6 6
7 #include <audioclient.h> 7 #include <audioclient.h>
8 #include <devicetopology.h> 8 #include <devicetopology.h>
9 #include <functiondiscoverykeys_devpkey.h> 9 #include <functiondiscoverykeys_devpkey.h>
10 10
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 129
130 static bool LoadAudiosesDll() { 130 static bool LoadAudiosesDll() {
131 static const wchar_t* const kAudiosesDLL = 131 static const wchar_t* const kAudiosesDLL =
132 L"%WINDIR%\\system32\\audioses.dll"; 132 L"%WINDIR%\\system32\\audioses.dll";
133 133
134 wchar_t path[MAX_PATH] = {0}; 134 wchar_t path[MAX_PATH] = {0};
135 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); 135 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path));
136 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); 136 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL);
137 } 137 }
138 138
139 static bool CanCreateDeviceEnumerator() {
140 ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
141 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
142 NULL, CLSCTX_INPROC_SERVER);
143
144 // If we hit CO_E_NOTINITIALIZED, CoInitialize has not been called and it
145 // must be called at least once for each thread that uses the COM library.
146 CHECK_NE(hr, CO_E_NOTINITIALIZED);
147
148 return SUCCEEDED(hr);
149 }
150
151 static std::string GetDeviceID(IMMDevice* device) { 139 static std::string GetDeviceID(IMMDevice* device) {
152 ScopedCoMem<WCHAR> device_id_com; 140 ScopedCoMem<WCHAR> device_id_com;
153 std::string device_id; 141 std::string device_id;
154 if (SUCCEEDED(device->GetId(&device_id_com))) 142 if (SUCCEEDED(device->GetId(&device_id_com)))
155 base::WideToUTF8(device_id_com, wcslen(device_id_com), &device_id); 143 base::WideToUTF8(device_id_com, wcslen(device_id_com), &device_id);
156 return device_id; 144 return device_id;
157 } 145 }
158 146
159 bool CoreAudioUtil::IsSupported() { 147 static HRESULT GetDeviceFriendlyNameInternal(IMMDevice* device,
148 std::string* friendly_name) {
149 // Retrieve user-friendly name of endpoint device.
150 // Example: "Microphone (Realtek High Definition Audio)".
151 ScopedComPtr<IPropertyStore> properties;
152 HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
153 if (FAILED(hr))
154 return hr;
155
156 base::win::ScopedPropVariant friendly_name_pv;
157 hr = properties->GetValue(PKEY_Device_FriendlyName,
158 friendly_name_pv.Receive());
159 if (FAILED(hr))
160 return hr;
161
162 if (friendly_name_pv.get().vt == VT_LPWSTR &&
163 friendly_name_pv.get().pwszVal) {
164 base::WideToUTF8(friendly_name_pv.get().pwszVal,
165 wcslen(friendly_name_pv.get().pwszVal), friendly_name);
166 }
167
168 return hr;
169 }
170
171 static ScopedComPtr<IMMDeviceEnumerator> CreateDeviceEnumeratorInternal() {
172 ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
173 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
174 NULL, CLSCTX_INPROC_SERVER);
175 if (hr == CO_E_NOTINITIALIZED) {
176 LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED";
177 // We have seen crashes which indicates that this method can in fact
178 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party
179 // modules. Calling CoInitializeEx is an attempt to resolve the reported
180 // issues. See http://crbug.com/378465 for details.
181 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
182 if (SUCCEEDED(hr)) {
183 hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
184 NULL, CLSCTX_INPROC_SERVER);
185 }
186 }
187 return device_enumerator;
188 }
189
190 static bool IsRemoteSession() {
191 return !!GetSystemMetrics(SM_REMOTESESSION);
192 }
193
194 static bool IsRemoteDeviceInternal(IMMDevice* device) {
195 DCHECK(IsRemoteSession());
196
197 std::string device_name;
198 HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name);
199
200 // This method should only be called if IsRemoteSession() is true, so assume
201 // we have a remote audio device if we can't tell.
202 if (FAILED(hr))
203 return true;
204
205 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.
206 }
207
208 static bool IsSupportedInternal() {
160 // It is possible to force usage of WaveXxx APIs by using a command line flag. 209 // It is possible to force usage of WaveXxx APIs by using a command line flag.
161 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); 210 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
162 if (cmd_line->HasSwitch(switches::kForceWaveAudio)) { 211 if (cmd_line->HasSwitch(switches::kForceWaveAudio)) {
163 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs"; 212 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs";
164 return false; 213 return false;
165 } 214 }
166 215
167 // Microsoft does not plan to make the Core Audio APIs available for use 216 // Microsoft does not plan to make the Core Audio APIs available for use
168 // with earlier versions of Windows, including Microsoft Windows Server 2003, 217 // with earlier versions of Windows, including Microsoft Windows Server 2003,
169 // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98. 218 // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98.
170 if (base::win::GetVersion() < base::win::VERSION_VISTA) 219 if (base::win::GetVersion() < base::win::VERSION_VISTA)
171 return false; 220 return false;
172 221
173 // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll 222 // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll
174 // system components. 223 // system components.
175 // Dependency Walker shows that it is enough to verify possibility to load 224 // Dependency Walker shows that it is enough to verify possibility to load
176 // the Audioses DLL since it depends on Mmdevapi.dll. 225 // the Audioses DLL since it depends on Mmdevapi.dll.
177 // See http://crbug.com/166397 why this extra step is required to guarantee 226 // See http://crbug.com/166397 why this extra step is required to guarantee
178 // Core Audio support. 227 // Core Audio support.
179 static bool g_audioses_dll_available = LoadAudiosesDll(); 228 if (!LoadAudiosesDll())
180 if (!g_audioses_dll_available)
181 return false; 229 return false;
182 230
183 // Being able to load the Audioses.dll does not seem to be sufficient for 231 // Being able to load the Audioses.dll does not seem to be sufficient for
184 // all devices to guarantee Core Audio support. To be 100%, we also verify 232 // all devices to guarantee Core Audio support. To be 100%, we also verify
185 // that it is possible to a create the IMMDeviceEnumerator interface. If this 233 // that it is possible to a create the IMMDeviceEnumerator interface. If this
186 // works as well we should be home free. 234 // works as well we should be home free.
187 static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator(); 235 ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
188 LOG_IF(ERROR, !g_can_create_device_enumerator) 236 CreateDeviceEnumeratorInternal();
189 << "Failed to create Core Audio device enumerator on thread with ID " 237 if (!device_enumerator) {
190 << GetCurrentThreadId(); 238 LOG(ERROR)
191 return g_can_create_device_enumerator; 239 << "Failed to create Core Audio device enumerator on thread with ID "
240 << GetCurrentThreadId();
241 return false;
242 }
243
244 // Don't use CoreAudio when a remote desktop session with remote audio is
245 // present; several users report only WaveAudio working for them and crash
246 // reports show hangs when calling into the OS for CoreAudio API calls. See
247 // http://crbug.com/422522 and http://crbug.com/180591.
248 //
249 // Note: There's another check in WASAPIAudioOutputStream::Open() for the case
250 // where a remote session is created after Chrome has been started. Graceful
251 // fallback to WaveOut will occur in this case via AudioOutputResampler.
252 if (!IsRemoteSession())
253 return true;
254
255 ScopedComPtr<IMMDevice> device;
256 HRESULT hr = device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole,
257 device.Receive());
258
259 // Assume remote audio playback if we can't tell.
260 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.
261 return false;
262
263 return !IsRemoteDeviceInternal(device.get());
264 }
265
266 bool CoreAudioUtil::IsSupported() {
267 static bool g_is_supported = IsSupportedInternal();
268 return g_is_supported;
192 } 269 }
193 270
194 base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) { 271 base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) {
195 // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond. 272 // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond.
196 return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5); 273 return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5);
197 } 274 }
198 275
199 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() { 276 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() {
200 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); 277 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
201 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) 278 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio))
(...skipping 24 matching lines...) Expand all
226 // Retrieve the number of active audio devices for the specified direction 303 // Retrieve the number of active audio devices for the specified direction
227 UINT number_of_active_devices = 0; 304 UINT number_of_active_devices = 0;
228 collection->GetCount(&number_of_active_devices); 305 collection->GetCount(&number_of_active_devices);
229 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") 306 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ")
230 << "number of devices: " << number_of_active_devices; 307 << "number of devices: " << number_of_active_devices;
231 return static_cast<int>(number_of_active_devices); 308 return static_cast<int>(number_of_active_devices);
232 } 309 }
233 310
234 ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { 311 ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() {
235 DCHECK(IsSupported()); 312 DCHECK(IsSupported());
236 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; 313 ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
237 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), 314 CreateDeviceEnumeratorInternal();
238 NULL, CLSCTX_INPROC_SERVER); 315 CHECK(device_enumerator);
239 if (hr == CO_E_NOTINITIALIZED) {
240 LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED";
241 // We have seen crashes which indicates that this method can in fact
242 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party
243 // modules. Calling CoInitializeEx is an attempt to resolve the reported
244 // issues. See http://crbug.com/378465 for details.
245 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
246 if (SUCCEEDED(hr)) {
247 hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
248 NULL, CLSCTX_INPROC_SERVER);
249 }
250 }
251 CHECK(SUCCEEDED(hr));
252 return device_enumerator; 316 return device_enumerator;
253 } 317 }
254 318
255 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, 319 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow,
256 ERole role) { 320 ERole role) {
257 DCHECK(IsSupported()); 321 DCHECK(IsSupported());
258 ScopedComPtr<IMMDevice> endpoint_device; 322 ScopedComPtr<IMMDevice> endpoint_device;
259 323
260 // Create the IMMDeviceEnumerator interface. 324 // Create the IMMDeviceEnumerator interface.
261 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 325 ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { 380 HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) {
317 DCHECK(IsSupported()); 381 DCHECK(IsSupported());
318 382
319 // Retrieve unique name of endpoint device. 383 // Retrieve unique name of endpoint device.
320 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". 384 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
321 AudioDeviceName device_name; 385 AudioDeviceName device_name;
322 device_name.unique_id = GetDeviceID(device); 386 device_name.unique_id = GetDeviceID(device);
323 if (device_name.unique_id.empty()) 387 if (device_name.unique_id.empty())
324 return E_FAIL; 388 return E_FAIL;
325 389
326 // Retrieve user-friendly name of endpoint device. 390 HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name.device_name);
327 // Example: "Microphone (Realtek High Definition Audio)".
328 ScopedComPtr<IPropertyStore> properties;
329 HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
330 if (FAILED(hr)) 391 if (FAILED(hr))
331 return hr; 392 return hr;
332 base::win::ScopedPropVariant friendly_name;
333 hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive());
334 if (FAILED(hr))
335 return hr;
336 if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) {
337 base::WideToUTF8(friendly_name.get().pwszVal,
338 wcslen(friendly_name.get().pwszVal),
339 &device_name.device_name);
340 }
341 393
342 *name = device_name; 394 *name = device_name;
343 DVLOG(2) << "friendly name: " << device_name.device_name; 395 DVLOG(2) << "friendly name: " << device_name.device_name;
344 DVLOG(2) << "unique id : " << device_name.unique_id; 396 DVLOG(2) << "unique id : " << device_name.unique_id;
345 return hr; 397 return hr;
346 } 398 }
347 399
348 std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device, 400 std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device,
349 IMMDeviceEnumerator* enumerator) { 401 IMMDeviceEnumerator* enumerator) {
350 DCHECK(IsSupported()); 402 DCHECK(IsSupported());
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after
847 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data))) 899 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data)))
848 return false; 900 return false;
849 901
850 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to 902 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to
851 // explicitly write silence data to the rendering buffer. 903 // explicitly write silence data to the rendering buffer.
852 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; 904 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence";
853 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, 905 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill,
854 AUDCLNT_BUFFERFLAGS_SILENT)); 906 AUDCLNT_BUFFERFLAGS_SILENT));
855 } 907 }
856 908
909 bool CoreAudioUtil::IsRemoteOutputDevice(const std::string& device_id) {
910 DCHECK(IsSupported());
911 if (!IsRemoteSession())
912 return false;
913 ScopedComPtr<IMMDevice> device(device_id.empty()
914 ? CreateDefaultDevice(eRender, eConsole)
915 : CreateDevice(device_id));
916 // Assume remote audio if we can't tell.
917 return device ? IsRemoteDeviceInternal(device.get()) : true;
918 }
919
857 } // namespace media 920 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/win/core_audio_util_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698