| OLD | NEW |
| 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 <devicetopology.h> | 7 #include <devicetopology.h> |
| 8 #include <dxdiag.h> | 8 #include <dxdiag.h> |
| 9 #include <functiondiscoverykeys_devpkey.h> | 9 #include <functiondiscoverykeys_devpkey.h> |
| 10 #include <stddef.h> | 10 #include <stddef.h> |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 #include "media/base/media_switches.h" | 23 #include "media/base/media_switches.h" |
| 24 | 24 |
| 25 using base::win::ScopedCoMem; | 25 using base::win::ScopedCoMem; |
| 26 using base::win::ScopedHandle; | 26 using base::win::ScopedHandle; |
| 27 | 27 |
| 28 namespace media { | 28 namespace media { |
| 29 | 29 |
| 30 // See header file for documentation. | 30 // See header file for documentation. |
| 31 // {BE39AF4F-087C-423F-9303-234EC1E5B8EE} | 31 // {BE39AF4F-087C-423F-9303-234EC1E5B8EE} |
| 32 const GUID kCommunicationsSessionId = { | 32 const GUID kCommunicationsSessionId = { |
| 33 0xbe39af4f, 0x87c, 0x423f, { 0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee } | 33 0xbe39af4f, |
| 34 }; | 34 0x87c, |
| 35 0x423f, |
| 36 {0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee}}; |
| 35 | 37 |
| 36 enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 }; | 38 enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 }; |
| 37 | 39 |
| 38 // Converts Microsoft's channel configuration to ChannelLayout. | 40 // Converts Microsoft's channel configuration to ChannelLayout. |
| 39 // This mapping is not perfect but the best we can do given the current | 41 // This mapping is not perfect but the best we can do given the current |
| 40 // ChannelLayout enumerator and the Windows-specific speaker configurations | 42 // ChannelLayout enumerator and the Windows-specific speaker configurations |
| 41 // defined in ksmedia.h. Don't assume that the channel ordering in | 43 // defined in ksmedia.h. Don't assume that the channel ordering in |
| 42 // ChannelLayout is exactly the same as the Windows specific configuration. | 44 // ChannelLayout is exactly the same as the Windows specific configuration. |
| 43 // As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to | 45 // As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to |
| 44 // CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R | 46 // CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), | 190 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), |
| 189 NULL, CLSCTX_INPROC_SERVER); | 191 NULL, CLSCTX_INPROC_SERVER); |
| 190 if (hr == CO_E_NOTINITIALIZED && allow_reinitialize) { | 192 if (hr == CO_E_NOTINITIALIZED && allow_reinitialize) { |
| 191 LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED"; | 193 LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED"; |
| 192 // We have seen crashes which indicates that this method can in fact | 194 // We have seen crashes which indicates that this method can in fact |
| 193 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party | 195 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party |
| 194 // modules. Calling CoInitializeEx is an attempt to resolve the reported | 196 // modules. Calling CoInitializeEx is an attempt to resolve the reported |
| 195 // issues. See http://crbug.com/378465 for details. | 197 // issues. See http://crbug.com/378465 for details. |
| 196 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); | 198 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
| 197 if (SUCCEEDED(hr)) { | 199 if (SUCCEEDED(hr)) { |
| 198 hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), | 200 hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, |
| 199 NULL, CLSCTX_INPROC_SERVER); | 201 CLSCTX_INPROC_SERVER); |
| 200 } | 202 } |
| 201 } | 203 } |
| 202 return device_enumerator; | 204 return device_enumerator; |
| 203 } | 205 } |
| 204 | 206 |
| 205 static bool IsSupportedInternal() { | 207 static bool IsSupportedInternal() { |
| 206 // It is possible to force usage of WaveXxx APIs by using a command line flag. | 208 // It is possible to force usage of WaveXxx APIs by using a command line flag. |
| 207 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | 209 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 208 if (cmd_line->HasSwitch(switches::kForceWaveAudio)) { | 210 if (cmd_line->HasSwitch(switches::kForceWaveAudio)) { |
| 209 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs"; | 211 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs"; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 // Create the IMMDeviceEnumerator interface. | 265 // Create the IMMDeviceEnumerator interface. |
| 264 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = | 266 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
| 265 CreateDeviceEnumerator(); | 267 CreateDeviceEnumerator(); |
| 266 if (!device_enumerator.get()) | 268 if (!device_enumerator.get()) |
| 267 return 0; | 269 return 0; |
| 268 | 270 |
| 269 // Generate a collection of active (present and not disabled) audio endpoint | 271 // Generate a collection of active (present and not disabled) audio endpoint |
| 270 // devices for the specified data-flow direction. | 272 // devices for the specified data-flow direction. |
| 271 // This method will succeed even if all devices are disabled. | 273 // This method will succeed even if all devices are disabled. |
| 272 ScopedComPtr<IMMDeviceCollection> collection; | 274 ScopedComPtr<IMMDeviceCollection> collection; |
| 273 HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow, | 275 HRESULT hr = device_enumerator->EnumAudioEndpoints( |
| 274 DEVICE_STATE_ACTIVE, | 276 data_flow, DEVICE_STATE_ACTIVE, collection.Receive()); |
| 275 collection.Receive()); | |
| 276 if (FAILED(hr)) { | 277 if (FAILED(hr)) { |
| 277 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; | 278 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; |
| 278 return 0; | 279 return 0; |
| 279 } | 280 } |
| 280 | 281 |
| 281 // Retrieve the number of active audio devices for the specified direction | 282 // Retrieve the number of active audio devices for the specified direction |
| 282 UINT number_of_active_devices = 0; | 283 UINT number_of_active_devices = 0; |
| 283 collection->GetCount(&number_of_active_devices); | 284 collection->GetCount(&number_of_active_devices); |
| 284 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") | 285 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") |
| 285 << "number of devices: " << number_of_active_devices; | 286 << "number of devices: " << number_of_active_devices; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 304 CreateDeviceEnumerator(); | 305 CreateDeviceEnumerator(); |
| 305 if (!device_enumerator.get()) | 306 if (!device_enumerator.get()) |
| 306 return endpoint_device; | 307 return endpoint_device; |
| 307 | 308 |
| 308 // Retrieve the default audio endpoint for the specified data-flow | 309 // Retrieve the default audio endpoint for the specified data-flow |
| 309 // direction and role. | 310 // direction and role. |
| 310 HRESULT hr = device_enumerator->GetDefaultAudioEndpoint( | 311 HRESULT hr = device_enumerator->GetDefaultAudioEndpoint( |
| 311 data_flow, role, endpoint_device.Receive()); | 312 data_flow, role, endpoint_device.Receive()); |
| 312 | 313 |
| 313 if (FAILED(hr)) { | 314 if (FAILED(hr)) { |
| 314 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: " | 315 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: " << std::hex |
| 315 << std::hex << hr; | 316 << hr; |
| 316 return endpoint_device; | 317 return endpoint_device; |
| 317 } | 318 } |
| 318 | 319 |
| 319 // Verify that the audio endpoint device is active, i.e., that the audio | 320 // Verify that the audio endpoint device is active, i.e., that the audio |
| 320 // adapter that connects to the endpoint device is present and enabled. | 321 // adapter that connects to the endpoint device is present and enabled. |
| 321 if (!IsDeviceActive(endpoint_device.get())) { | 322 if (!IsDeviceActive(endpoint_device.get())) { |
| 322 DVLOG(1) << "Selected endpoint device is not active"; | 323 DVLOG(1) << "Selected endpoint device is not active"; |
| 323 endpoint_device.Release(); | 324 endpoint_device.Release(); |
| 324 } | 325 } |
| 325 return endpoint_device; | 326 return endpoint_device; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 339 // Create the IMMDeviceEnumerator interface. | 340 // Create the IMMDeviceEnumerator interface. |
| 340 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = | 341 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
| 341 CreateDeviceEnumerator(); | 342 CreateDeviceEnumerator(); |
| 342 if (!device_enumerator.get()) | 343 if (!device_enumerator.get()) |
| 343 return endpoint_device; | 344 return endpoint_device; |
| 344 | 345 |
| 345 // Retrieve an audio device specified by an endpoint device-identification | 346 // Retrieve an audio device specified by an endpoint device-identification |
| 346 // string. | 347 // string. |
| 347 HRESULT hr = device_enumerator->GetDevice( | 348 HRESULT hr = device_enumerator->GetDevice( |
| 348 base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive()); | 349 base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive()); |
| 349 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " | 350 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " << std::hex |
| 350 << std::hex << hr; | 351 << hr; |
| 351 | 352 |
| 352 if (FAILED(hr)) { | 353 if (FAILED(hr)) { |
| 353 DVLOG(1) << "IMMDeviceEnumerator::GetDevice: " << std::hex << hr; | 354 DVLOG(1) << "IMMDeviceEnumerator::GetDevice: " << std::hex << hr; |
| 354 return endpoint_device; | 355 return endpoint_device; |
| 355 } | 356 } |
| 356 | 357 |
| 357 // Verify that the audio endpoint device is active, i.e., that the audio | 358 // Verify that the audio endpoint device is active, i.e., that the audio |
| 358 // adapter that connects to the endpoint device is present and enabled. | 359 // adapter that connects to the endpoint device is present and enabled. |
| 359 if (!IsDeviceActive(endpoint_device.get())) { | 360 if (!IsDeviceActive(endpoint_device.get())) { |
| 360 DVLOG(1) << "Selected endpoint device is not active"; | 361 DVLOG(1) << "Selected endpoint device is not active"; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 376 HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name.device_name); | 377 HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name.device_name); |
| 377 if (FAILED(hr)) | 378 if (FAILED(hr)) |
| 378 return hr; | 379 return hr; |
| 379 | 380 |
| 380 *name = device_name; | 381 *name = device_name; |
| 381 DVLOG(2) << "friendly name: " << device_name.device_name; | 382 DVLOG(2) << "friendly name: " << device_name.device_name; |
| 382 DVLOG(2) << "unique id : " << device_name.unique_id; | 383 DVLOG(2) << "unique id : " << device_name.unique_id; |
| 383 return hr; | 384 return hr; |
| 384 } | 385 } |
| 385 | 386 |
| 386 std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device, | 387 std::string CoreAudioUtil::GetAudioControllerID( |
| 388 IMMDevice* device, |
| 387 IMMDeviceEnumerator* enumerator) { | 389 IMMDeviceEnumerator* enumerator) { |
| 388 DCHECK(IsSupported()); | 390 DCHECK(IsSupported()); |
| 389 | 391 |
| 390 // Fetching the controller device id could be as simple as fetching the value | 392 // Fetching the controller device id could be as simple as fetching the value |
| 391 // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property | 393 // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property |
| 392 // store of the |device|, but that key isn't defined in any header and | 394 // store of the |device|, but that key isn't defined in any header and |
| 393 // according to MS should not be relied upon. | 395 // according to MS should not be relied upon. |
| 394 // So, instead, we go deeper, look at the device topology and fetch the | 396 // So, instead, we go deeper, look at the device topology and fetch the |
| 395 // PKEY_Device_InstanceId of the associated physical audio device. | 397 // PKEY_Device_InstanceId of the associated physical audio device. |
| 396 ScopedComPtr<IDeviceTopology> topology; | 398 ScopedComPtr<IDeviceTopology> topology; |
| 397 ScopedComPtr<IConnector> connector; | 399 ScopedComPtr<IConnector> connector; |
| 398 ScopedCoMem<WCHAR> filter_id; | 400 ScopedCoMem<WCHAR> filter_id; |
| 399 if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, | 401 if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, |
| 400 topology.ReceiveVoid())) || | 402 topology.ReceiveVoid())) || |
| 401 // For our purposes checking the first connected device should be enough | 403 // For our purposes checking the first connected device should be enough |
| 402 // and if there are cases where there are more than one device connected | 404 // and if there are cases where there are more than one device connected |
| 403 // we're not sure how to handle that anyway. So we pass 0. | 405 // we're not sure how to handle that anyway. So we pass 0. |
| 404 FAILED(topology->GetConnector(0, connector.Receive())) || | 406 FAILED(topology->GetConnector(0, connector.Receive())) || |
| 405 FAILED(connector->GetDeviceIdConnectedTo(&filter_id))) { | 407 FAILED(connector->GetDeviceIdConnectedTo(&filter_id))) { |
| 406 DLOG(ERROR) << "Failed to get the device identifier of the audio device"; | 408 DLOG(ERROR) << "Failed to get the device identifier of the audio device"; |
| 407 return std::string(); | 409 return std::string(); |
| 408 } | 410 } |
| 409 | 411 |
| 410 // Now look at the properties of the connected device node and fetch the | 412 // Now look at the properties of the connected device node and fetch the |
| 411 // instance id (PKEY_Device_InstanceId) of the device node that uniquely | 413 // instance id (PKEY_Device_InstanceId) of the device node that uniquely |
| 412 // identifies the controller. | 414 // identifies the controller. |
| 413 ScopedComPtr<IMMDevice> device_node; | 415 ScopedComPtr<IMMDevice> device_node; |
| 414 ScopedComPtr<IPropertyStore> properties; | 416 ScopedComPtr<IPropertyStore> properties; |
| 415 base::win::ScopedPropVariant instance_id; | 417 base::win::ScopedPropVariant instance_id; |
| 416 if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) || | 418 if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) || |
| 417 FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) || | 419 FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) || |
| 418 FAILED(properties->GetValue(PKEY_Device_InstanceId, | 420 FAILED(properties->GetValue(PKEY_Device_InstanceId, |
| 419 instance_id.Receive())) || | 421 instance_id.Receive())) || |
| 420 instance_id.get().vt != VT_LPWSTR) { | 422 instance_id.get().vt != VT_LPWSTR) { |
| 421 DLOG(ERROR) << "Failed to get instance id of the audio device node"; | 423 DLOG(ERROR) << "Failed to get instance id of the audio device node"; |
| 422 return std::string(); | 424 return std::string(); |
| 423 } | 425 } |
| 424 | 426 |
| 425 std::string controller_id; | 427 std::string controller_id; |
| 426 base::WideToUTF8(instance_id.get().pwszVal, | 428 base::WideToUTF8(instance_id.get().pwszVal, wcslen(instance_id.get().pwszVal), |
| 427 wcslen(instance_id.get().pwszVal), | |
| 428 &controller_id); | 429 &controller_id); |
| 429 | 430 |
| 430 return controller_id; | 431 return controller_id; |
| 431 } | 432 } |
| 432 | 433 |
| 433 std::string CoreAudioUtil::GetMatchingOutputDeviceID( | 434 std::string CoreAudioUtil::GetMatchingOutputDeviceID( |
| 434 const std::string& input_device_id) { | 435 const std::string& input_device_id) { |
| 435 // Special handling for the default communications device. | 436 // Special handling for the default communications device. |
| 436 // We always treat the configured communications devices, as a pair. | 437 // We always treat the configured communications devices, as a pair. |
| 437 // If we didn't do that and the user has e.g. configured a mic of a headset | 438 // If we didn't do that and the user has e.g. configured a mic of a headset |
| (...skipping 20 matching lines...) Expand all Loading... |
| 458 ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator()); | 459 ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator()); |
| 459 std::string controller_id( | 460 std::string controller_id( |
| 460 GetAudioControllerID(input_device.get(), enumerator.get())); | 461 GetAudioControllerID(input_device.get(), enumerator.get())); |
| 461 if (controller_id.empty()) | 462 if (controller_id.empty()) |
| 462 return std::string(); | 463 return std::string(); |
| 463 | 464 |
| 464 // Now enumerate the available (and active) output devices and see if any of | 465 // Now enumerate the available (and active) output devices and see if any of |
| 465 // them is associated with the same controller. | 466 // them is associated with the same controller. |
| 466 ScopedComPtr<IMMDeviceCollection> collection; | 467 ScopedComPtr<IMMDeviceCollection> collection; |
| 467 enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, | 468 enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, |
| 468 collection.Receive()); | 469 collection.Receive()); |
| 469 if (!collection.get()) | 470 if (!collection.get()) |
| 470 return std::string(); | 471 return std::string(); |
| 471 | 472 |
| 472 UINT count = 0; | 473 UINT count = 0; |
| 473 collection->GetCount(&count); | 474 collection->GetCount(&count); |
| 474 ScopedComPtr<IMMDevice> output_device; | 475 ScopedComPtr<IMMDevice> output_device; |
| 475 for (UINT i = 0; i < count; ++i) { | 476 for (UINT i = 0; i < count; ++i) { |
| 476 collection->Item(i, output_device.Receive()); | 477 collection->Item(i, output_device.Receive()); |
| 477 std::string output_controller_id( | 478 std::string output_controller_id( |
| 478 GetAudioControllerID(output_device.get(), enumerator.get())); | 479 GetAudioControllerID(output_device.get(), enumerator.get())); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 528 return data_flow; | 529 return data_flow; |
| 529 } | 530 } |
| 530 | 531 |
| 531 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( | 532 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( |
| 532 IMMDevice* audio_device) { | 533 IMMDevice* audio_device) { |
| 533 DCHECK(IsSupported()); | 534 DCHECK(IsSupported()); |
| 534 | 535 |
| 535 // Creates and activates an IAudioClient COM object given the selected | 536 // Creates and activates an IAudioClient COM object given the selected |
| 536 // endpoint device. | 537 // endpoint device. |
| 537 ScopedComPtr<IAudioClient> audio_client; | 538 ScopedComPtr<IAudioClient> audio_client; |
| 538 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), | 539 HRESULT hr = |
| 539 CLSCTX_INPROC_SERVER, | 540 audio_device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, |
| 540 NULL, | 541 audio_client.ReceiveVoid()); |
| 541 audio_client.ReceiveVoid()); | |
| 542 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; | 542 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; |
| 543 return audio_client; | 543 return audio_client; |
| 544 } | 544 } |
| 545 | 545 |
| 546 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( | 546 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( |
| 547 EDataFlow data_flow, ERole role) { | 547 EDataFlow data_flow, |
| 548 ERole role) { |
| 548 DCHECK(IsSupported()); | 549 DCHECK(IsSupported()); |
| 549 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); | 550 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); |
| 550 return (default_device.get() ? CreateClient(default_device.get()) | 551 return (default_device.get() ? CreateClient(default_device.get()) |
| 551 : ScopedComPtr<IAudioClient>()); | 552 : ScopedComPtr<IAudioClient>()); |
| 552 } | 553 } |
| 553 | 554 |
| 554 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( | 555 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( |
| 555 const std::string& device_id, EDataFlow data_flow, ERole role) { | 556 const std::string& device_id, |
| 557 EDataFlow data_flow, |
| 558 ERole role) { |
| 556 if (IsDefaultDeviceId(device_id)) | 559 if (IsDefaultDeviceId(device_id)) |
| 557 return CreateDefaultClient(data_flow, role); | 560 return CreateDefaultClient(data_flow, role); |
| 558 | 561 |
| 559 ScopedComPtr<IMMDevice> device(CreateDevice(device_id)); | 562 ScopedComPtr<IMMDevice> device(CreateDevice(device_id)); |
| 560 if (!device.get()) | 563 if (!device.get()) |
| 561 return ScopedComPtr<IAudioClient>(); | 564 return ScopedComPtr<IAudioClient>(); |
| 562 | 565 |
| 563 return CreateClient(device.get()); | 566 return CreateClient(device.get()); |
| 564 } | 567 } |
| 565 | 568 |
| 566 HRESULT CoreAudioUtil::GetSharedModeMixFormat( | 569 HRESULT CoreAudioUtil::GetSharedModeMixFormat(IAudioClient* client, |
| 567 IAudioClient* client, WAVEFORMATPCMEX* format) { | 570 WAVEFORMATPCMEX* format) { |
| 568 DCHECK(IsSupported()); | 571 DCHECK(IsSupported()); |
| 569 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; | 572 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; |
| 570 HRESULT hr = client->GetMixFormat( | 573 HRESULT hr = |
| 571 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); | 574 client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); |
| 572 if (FAILED(hr)) | 575 if (FAILED(hr)) |
| 573 return hr; | 576 return hr; |
| 574 | 577 |
| 575 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; | 578 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; |
| 576 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); | 579 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); |
| 577 | 580 |
| 578 memcpy(format, format_pcmex, bytes); | 581 memcpy(format, format_pcmex, bytes); |
| 579 DVLOG(2) << *format; | 582 DVLOG(2) << *format; |
| 580 | 583 |
| 581 return hr; | 584 return hr; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 return false; | 631 return false; |
| 629 } | 632 } |
| 630 format.dwChannelMask = new_config; | 633 format.dwChannelMask = new_config; |
| 631 | 634 |
| 632 // Modify the format if the new channel layout has changed the number of | 635 // Modify the format if the new channel layout has changed the number of |
| 633 // utilized channels. | 636 // utilized channels. |
| 634 const int channels = ChannelLayoutToChannelCount(channel_layout); | 637 const int channels = ChannelLayoutToChannelCount(channel_layout); |
| 635 if (channels != format.Format.nChannels) { | 638 if (channels != format.Format.nChannels) { |
| 636 format.Format.nChannels = channels; | 639 format.Format.nChannels = channels; |
| 637 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels; | 640 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels; |
| 638 format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * | 641 format.Format.nAvgBytesPerSec = |
| 639 format.Format.nBlockAlign; | 642 format.Format.nSamplesPerSec * format.Format.nBlockAlign; |
| 640 } | 643 } |
| 641 DVLOG(2) << format; | 644 DVLOG(2) << format; |
| 642 | 645 |
| 643 // Some devices can initialize a shared-mode stream with a format that is | 646 // Some devices can initialize a shared-mode stream with a format that is |
| 644 // not identical to the mix format obtained from the GetMixFormat() method. | 647 // not identical to the mix format obtained from the GetMixFormat() method. |
| 645 // However, chances of succeeding increases if we use the same number of | 648 // However, chances of succeeding increases if we use the same number of |
| 646 // channels and the same sample rate as the mix format. I.e, this call will | 649 // channels and the same sample rate as the mix format. I.e, this call will |
| 647 // return true only in those cases where the audio engine is able to support | 650 // return true only in those cases where the audio engine is able to support |
| 648 // an even wider range of shared-mode formats where the installation package | 651 // an even wider range of shared-mode formats where the installation package |
| 649 // for the audio device includes a local effects (LFX) audio processing | 652 // for the audio device includes a local effects (LFX) audio processing |
| 650 // object (APO) that can handle format conversions. | 653 // object (APO) that can handle format conversions. |
| 651 return CoreAudioUtil::IsFormatSupported(client.get(), | 654 return CoreAudioUtil::IsFormatSupported(client.get(), |
| 652 AUDCLNT_SHAREMODE_SHARED, &format); | 655 AUDCLNT_SHAREMODE_SHARED, &format); |
| 653 } | 656 } |
| 654 | 657 |
| 655 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, | 658 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, |
| 656 AUDCLNT_SHAREMODE share_mode, | 659 AUDCLNT_SHAREMODE share_mode, |
| 657 REFERENCE_TIME* device_period) { | 660 REFERENCE_TIME* device_period) { |
| 658 DCHECK(IsSupported()); | 661 DCHECK(IsSupported()); |
| 659 | 662 |
| 660 // Get the period of the engine thread. | 663 // Get the period of the engine thread. |
| 661 REFERENCE_TIME default_period = 0; | 664 REFERENCE_TIME default_period = 0; |
| 662 REFERENCE_TIME minimum_period = 0; | 665 REFERENCE_TIME minimum_period = 0; |
| 663 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); | 666 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); |
| 664 if (FAILED(hr)) | 667 if (FAILED(hr)) |
| 665 return hr; | 668 return hr; |
| 666 | 669 |
| 667 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : | 670 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period |
| 668 minimum_period; | 671 : minimum_period; |
| 669 DVLOG(2) << "device_period: " | 672 DVLOG(2) << "device_period: " |
| 670 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() | 673 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() |
| 671 << " [ms]"; | 674 << " [ms]"; |
| 672 return hr; | 675 return hr; |
| 673 } | 676 } |
| 674 | 677 |
| 675 HRESULT CoreAudioUtil::GetPreferredAudioParameters( | 678 HRESULT CoreAudioUtil::GetPreferredAudioParameters(IAudioClient* client, |
| 676 IAudioClient* client, AudioParameters* params) { | 679 AudioParameters* params) { |
| 677 DCHECK(IsSupported()); | 680 DCHECK(IsSupported()); |
| 678 WAVEFORMATPCMEX mix_format; | 681 WAVEFORMATPCMEX mix_format; |
| 679 HRESULT hr = GetSharedModeMixFormat(client, &mix_format); | 682 HRESULT hr = GetSharedModeMixFormat(client, &mix_format); |
| 680 if (FAILED(hr)) | 683 if (FAILED(hr)) |
| 681 return hr; | 684 return hr; |
| 682 | 685 |
| 683 REFERENCE_TIME default_period = 0; | 686 REFERENCE_TIME default_period = 0; |
| 684 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); | 687 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); |
| 685 if (FAILED(hr)) | 688 if (FAILED(hr)) |
| 686 return hr; | 689 return hr; |
| 687 | 690 |
| 688 // Get the integer mask which corresponds to the channel layout the | 691 // Get the integer mask which corresponds to the channel layout the |
| 689 // audio engine uses for its internal processing/mixing of shared-mode | 692 // audio engine uses for its internal processing/mixing of shared-mode |
| 690 // streams. This mask indicates which channels are present in the multi- | 693 // streams. This mask indicates which channels are present in the multi- |
| 691 // channel stream. The least significant bit corresponds with the Front Left | 694 // channel stream. The least significant bit corresponds with the Front Left |
| 692 // speaker, the next least significant bit corresponds to the Front Right | 695 // speaker, the next least significant bit corresponds to the Front Right |
| 693 // speaker, and so on, continuing in the order defined in KsMedia.h. | 696 // speaker, and so on, continuing in the order defined in KsMedia.h. |
| 694 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx | 697 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx |
| 695 // for more details. | 698 // for more details. |
| 696 ChannelConfig channel_config = mix_format.dwChannelMask; | 699 ChannelConfig channel_config = mix_format.dwChannelMask; |
| 697 | 700 |
| 698 // Convert Microsoft's channel configuration to genric ChannelLayout. | 701 // Convert Microsoft's channel configuration to genric ChannelLayout. |
| 699 ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config); | 702 ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config); |
| 700 | 703 |
| 701 // Some devices don't appear to set a valid channel layout, so guess based on | 704 // Some devices don't appear to set a valid channel layout, so guess based on |
| 702 // the number of channels. See http://crbug.com/311906. | 705 // the number of channels. See http://crbug.com/311906. |
| 703 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { | 706 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { |
| 704 DVLOG(1) << "Unsupported channel config: " | 707 DVLOG(1) << "Unsupported channel config: " << std::hex << channel_config |
| 705 << std::hex << channel_config | 708 << ". Guessing layout by channel count: " << std::dec |
| 706 << ". Guessing layout by channel count: " | 709 << mix_format.Format.nChannels; |
| 707 << std::dec << mix_format.Format.nChannels; | |
| 708 channel_layout = GuessChannelLayout(mix_format.Format.nChannels); | 710 channel_layout = GuessChannelLayout(mix_format.Format.nChannels); |
| 709 } | 711 } |
| 710 | 712 |
| 711 // Preferred sample rate. | 713 // Preferred sample rate. |
| 712 int sample_rate = mix_format.Format.nSamplesPerSec; | 714 int sample_rate = mix_format.Format.nSamplesPerSec; |
| 713 | 715 |
| 714 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead. | 716 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead. |
| 715 // We use a hard-coded value of 16 bits per sample today even if most audio | 717 // We use a hard-coded value of 16 bits per sample today even if most audio |
| 716 // engines does the actual mixing in 32 bits per sample. | 718 // engines does the actual mixing in 32 bits per sample. |
| 717 int bits_per_sample = 16; | 719 int bits_per_sample = 16; |
| 718 | 720 |
| 719 // We are using the native device period to derive the smallest possible | 721 // We are using the native device period to derive the smallest possible |
| 720 // buffer size in shared mode. Note that the actual endpoint buffer will be | 722 // buffer size in shared mode. Note that the actual endpoint buffer will be |
| 721 // larger than this size but it will be possible to fill it up in two calls. | 723 // larger than this size but it will be possible to fill it up in two calls. |
| 722 // TODO(henrika): ensure that this scheme works for capturing as well. | 724 // TODO(henrika): ensure that this scheme works for capturing as well. |
| 723 int frames_per_buffer = static_cast<int>(sample_rate * | 725 int frames_per_buffer = static_cast<int>( |
| 724 RefererenceTimeToTimeDelta(default_period).InSecondsF() + 0.5); | 726 sample_rate * RefererenceTimeToTimeDelta(default_period).InSecondsF() + |
| 727 0.5); |
| 725 | 728 |
| 726 DVLOG(1) << "channel_layout : " << channel_layout; | 729 DVLOG(1) << "channel_layout : " << channel_layout; |
| 727 DVLOG(1) << "sample_rate : " << sample_rate; | 730 DVLOG(1) << "sample_rate : " << sample_rate; |
| 728 DVLOG(1) << "bits_per_sample : " << bits_per_sample; | 731 DVLOG(1) << "bits_per_sample : " << bits_per_sample; |
| 729 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer; | 732 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer; |
| 730 | 733 |
| 731 AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, | 734 AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 732 channel_layout, | 735 channel_layout, sample_rate, bits_per_sample, |
| 733 sample_rate, | |
| 734 bits_per_sample, | |
| 735 frames_per_buffer); | 736 frames_per_buffer); |
| 736 | 737 |
| 737 *params = audio_params; | 738 *params = audio_params; |
| 738 return hr; | 739 return hr; |
| 739 } | 740 } |
| 740 | 741 |
| 741 HRESULT CoreAudioUtil::GetPreferredAudioParameters(const std::string& device_id, | 742 HRESULT CoreAudioUtil::GetPreferredAudioParameters(const std::string& device_id, |
| 742 bool is_output_device, | 743 bool is_output_device, |
| 743 AudioParameters* params) { | 744 AudioParameters* params) { |
| 744 DCHECK(IsSupported()); | 745 DCHECK(IsSupported()); |
| 745 | 746 |
| 746 ScopedComPtr<IMMDevice> device; | 747 ScopedComPtr<IMMDevice> device; |
| 747 if (device_id == AudioDeviceDescription::kDefaultDeviceId) { | 748 if (device_id == AudioDeviceDescription::kDefaultDeviceId) { |
| 748 device = CoreAudioUtil::CreateDefaultDevice( | 749 device = CoreAudioUtil::CreateDefaultDevice( |
| 749 is_output_device ? eRender : eCapture, eConsole); | 750 is_output_device ? eRender : eCapture, eConsole); |
| 750 } else if (device_id == AudioDeviceDescription::kLoopbackInputDeviceId) { | 751 } else if (device_id == AudioDeviceDescription::kLoopbackInputDeviceId || |
| 752 device_id == AudioDeviceDescription::kLoopbackWithMuteDeviceId) { |
| 751 DCHECK(!is_output_device); | 753 DCHECK(!is_output_device); |
| 752 device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); | 754 device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); |
| 753 } else if (device_id == AudioDeviceDescription::kCommunicationsDeviceId) { | 755 } else if (device_id == AudioDeviceDescription::kCommunicationsDeviceId) { |
| 754 device = CoreAudioUtil::CreateDefaultDevice( | 756 device = CoreAudioUtil::CreateDefaultDevice( |
| 755 is_output_device ? eRender : eCapture, eCommunications); | 757 is_output_device ? eRender : eCapture, eCommunications); |
| 756 } else { | 758 } else { |
| 757 device = CreateDevice(device_id); | 759 device = CreateDevice(device_id); |
| 758 } | 760 } |
| 759 | 761 |
| 760 if (!device.get()) { | 762 if (!device.get()) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 811 // ensure that the volume level and muting state for a rendering session | 813 // ensure that the volume level and muting state for a rendering session |
| 812 // are persistent across system restarts. The volume level and muting | 814 // are persistent across system restarts. The volume level and muting |
| 813 // state for a capture session are never persistent. | 815 // state for a capture session are never persistent. |
| 814 DWORD stream_flags = 0; | 816 DWORD stream_flags = 0; |
| 815 | 817 |
| 816 // Enable event-driven streaming if a valid event handle is provided. | 818 // Enable event-driven streaming if a valid event handle is provided. |
| 817 // After the stream starts, the audio engine will signal the event handle | 819 // After the stream starts, the audio engine will signal the event handle |
| 818 // to notify the client each time a buffer becomes ready to process. | 820 // to notify the client each time a buffer becomes ready to process. |
| 819 // Event-driven buffering is supported for both rendering and capturing. | 821 // Event-driven buffering is supported for both rendering and capturing. |
| 820 // Both shared-mode and exclusive-mode streams can use event-driven buffering. | 822 // Both shared-mode and exclusive-mode streams can use event-driven buffering. |
| 821 bool use_event = (event_handle != NULL && | 823 bool use_event = |
| 822 event_handle != INVALID_HANDLE_VALUE); | 824 (event_handle != NULL && event_handle != INVALID_HANDLE_VALUE); |
| 823 if (use_event) | 825 if (use_event) |
| 824 stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; | 826 stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; |
| 825 DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags; | 827 DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags; |
| 826 | 828 |
| 827 // Initialize the shared mode client for minimal delay. | 829 // Initialize the shared mode client for minimal delay. |
| 828 HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, | 830 HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, stream_flags, 0, 0, |
| 829 stream_flags, | |
| 830 0, | |
| 831 0, | |
| 832 reinterpret_cast<const WAVEFORMATEX*>(format), | 831 reinterpret_cast<const WAVEFORMATEX*>(format), |
| 833 session_guid); | 832 session_guid); |
| 834 if (FAILED(hr)) { | 833 if (FAILED(hr)) { |
| 835 DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr; | 834 DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr; |
| 836 return hr; | 835 return hr; |
| 837 } | 836 } |
| 838 | 837 |
| 839 if (use_event) { | 838 if (use_event) { |
| 840 hr = client->SetEventHandle(event_handle); | 839 hr = client->SetEventHandle(event_handle); |
| 841 if (FAILED(hr)) { | 840 if (FAILED(hr)) { |
| 842 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr; | 841 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr; |
| 843 return hr; | 842 return hr; |
| 844 } | 843 } |
| 845 } | 844 } |
| 846 | 845 |
| 847 UINT32 buffer_size_in_frames = 0; | 846 UINT32 buffer_size_in_frames = 0; |
| 848 hr = client->GetBufferSize(&buffer_size_in_frames); | 847 hr = client->GetBufferSize(&buffer_size_in_frames); |
| 849 if (FAILED(hr)) { | 848 if (FAILED(hr)) { |
| 850 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; | 849 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; |
| 851 return hr; | 850 return hr; |
| 852 } | 851 } |
| 853 | 852 |
| 854 *endpoint_buffer_size = buffer_size_in_frames; | 853 *endpoint_buffer_size = buffer_size_in_frames; |
| 855 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; | 854 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; |
| 856 | 855 |
| 857 // TODO(henrika): utilize when delay measurements are added. | 856 // TODO(henrika): utilize when delay measurements are added. |
| 858 REFERENCE_TIME latency = 0; | 857 REFERENCE_TIME latency = 0; |
| 859 hr = client->GetStreamLatency(&latency); | 858 hr = client->GetStreamLatency(&latency); |
| 860 DVLOG(2) << "stream latency: " | 859 DVLOG(2) << "stream latency: " |
| 861 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; | 860 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; |
| 862 return hr; | 861 return hr; |
| 863 } | 862 } |
| 864 | 863 |
| 865 ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( | 864 ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( |
| 866 IAudioClient* client) { | 865 IAudioClient* client) { |
| 867 DCHECK(IsSupported()); | 866 DCHECK(IsSupported()); |
| 868 | 867 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 888 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), | 887 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), |
| 889 audio_capture_client.ReceiveVoid()); | 888 audio_capture_client.ReceiveVoid()); |
| 890 if (FAILED(hr)) { | 889 if (FAILED(hr)) { |
| 891 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; | 890 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; |
| 892 return ScopedComPtr<IAudioCaptureClient>(); | 891 return ScopedComPtr<IAudioCaptureClient>(); |
| 893 } | 892 } |
| 894 return audio_capture_client; | 893 return audio_capture_client; |
| 895 } | 894 } |
| 896 | 895 |
| 897 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( | 896 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( |
| 898 IAudioClient* client, IAudioRenderClient* render_client) { | 897 IAudioClient* client, |
| 898 IAudioRenderClient* render_client) { |
| 899 DCHECK(IsSupported()); | 899 DCHECK(IsSupported()); |
| 900 | 900 |
| 901 UINT32 endpoint_buffer_size = 0; | 901 UINT32 endpoint_buffer_size = 0; |
| 902 if (FAILED(client->GetBufferSize(&endpoint_buffer_size))) | 902 if (FAILED(client->GetBufferSize(&endpoint_buffer_size))) |
| 903 return false; | 903 return false; |
| 904 | 904 |
| 905 UINT32 num_queued_frames = 0; | 905 UINT32 num_queued_frames = 0; |
| 906 if (FAILED(client->GetCurrentPadding(&num_queued_frames))) | 906 if (FAILED(client->GetCurrentPadding(&num_queued_frames))) |
| 907 return false; | 907 return false; |
| 908 | 908 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 964 | 964 |
| 965 if (variant.type() == VT_BSTR && variant.ptr()->bstrVal) { | 965 if (variant.type() == VT_BSTR && variant.ptr()->bstrVal) { |
| 966 base::WideToUTF8(variant.ptr()->bstrVal, wcslen(variant.ptr()->bstrVal), | 966 base::WideToUTF8(variant.ptr()->bstrVal, wcslen(variant.ptr()->bstrVal), |
| 967 driver_version); | 967 driver_version); |
| 968 } | 968 } |
| 969 | 969 |
| 970 return true; | 970 return true; |
| 971 } | 971 } |
| 972 | 972 |
| 973 } // namespace media | 973 } // namespace media |
| OLD | NEW |