| 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 <Audioclient.h> | 7 #include <Audioclient.h> |
| 8 #include <Functiondiscoverykeys_devpkey.h> | 8 #include <Functiondiscoverykeys_devpkey.h> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 14 #include "base/win/scoped_co_mem.h" | 14 #include "base/win/scoped_co_mem.h" |
| 15 #include "base/win/scoped_handle.h" | 15 #include "base/win/scoped_handle.h" |
| 16 #include "base/win/scoped_propvariant.h" | 16 #include "base/win/scoped_propvariant.h" |
| 17 #include "base/win/windows_version.h" | 17 #include "base/win/windows_version.h" |
| 18 #include "media/base/media_switches.h" | 18 #include "media/base/media_switches.h" |
| 19 | 19 |
| 20 using base::win::ScopedCoMem; | 20 using base::win::ScopedCoMem; |
| 21 using base::win::ScopedHandle; | 21 using base::win::ScopedHandle; |
| 22 | 22 |
| 23 namespace media { | 23 namespace media { |
| 24 | 24 |
| 25 enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 }; |
| 26 |
| 25 typedef uint32 ChannelConfig; | 27 typedef uint32 ChannelConfig; |
| 26 | 28 |
| 27 // Converts Microsoft's channel configuration to ChannelLayout. | 29 // Converts Microsoft's channel configuration to ChannelLayout. |
| 28 // This mapping is not perfect but the best we can do given the current | 30 // This mapping is not perfect but the best we can do given the current |
| 29 // ChannelLayout enumerator and the Windows-specific speaker configurations | 31 // ChannelLayout enumerator and the Windows-specific speaker configurations |
| 30 // defined in ksmedia.h. Don't assume that the channel ordering in | 32 // defined in ksmedia.h. Don't assume that the channel ordering in |
| 31 // ChannelLayout is exactly the same as the Windows specific configuration. | 33 // ChannelLayout is exactly the same as the Windows specific configuration. |
| 32 // As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to | 34 // As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to |
| 33 // CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R | 35 // CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R |
| 34 // speakers are different in these two definitions. | 36 // speakers are different in these two definitions. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 55 case KSAUDIO_SPEAKER_5POINT1_SURROUND: | 57 case KSAUDIO_SPEAKER_5POINT1_SURROUND: |
| 56 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1"; | 58 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1"; |
| 57 return CHANNEL_LAYOUT_5_1; | 59 return CHANNEL_LAYOUT_5_1; |
| 58 case KSAUDIO_SPEAKER_7POINT1: | 60 case KSAUDIO_SPEAKER_7POINT1: |
| 59 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE"; | 61 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE"; |
| 60 return CHANNEL_LAYOUT_7_1_WIDE; | 62 return CHANNEL_LAYOUT_7_1_WIDE; |
| 61 case KSAUDIO_SPEAKER_7POINT1_SURROUND: | 63 case KSAUDIO_SPEAKER_7POINT1_SURROUND: |
| 62 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; | 64 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; |
| 63 return CHANNEL_LAYOUT_7_1; | 65 return CHANNEL_LAYOUT_7_1; |
| 64 default: | 66 default: |
| 65 DVLOG(2) << "Unsupported channel layout: " << config; | 67 DVLOG(2) << "Unsupported channel configuration: " << config; |
| 66 return CHANNEL_LAYOUT_UNSUPPORTED; | 68 return CHANNEL_LAYOUT_UNSUPPORTED; |
| 67 } | 69 } |
| 68 } | 70 } |
| 69 | 71 |
| 72 // TODO(henrika): add mapping for all types in the ChannelLayout enumerator. |
| 73 static ChannelConfig ChannelLayoutToChannelConfig(ChannelLayout layout) { |
| 74 switch (layout) { |
| 75 case CHANNEL_LAYOUT_NONE: |
| 76 DVLOG(2) << "CHANNEL_LAYOUT_NONE=>KSAUDIO_SPEAKER_UNSUPPORTED"; |
| 77 return KSAUDIO_SPEAKER_UNSUPPORTED; |
| 78 case CHANNEL_LAYOUT_UNSUPPORTED: |
| 79 DVLOG(2) << "CHANNEL_LAYOUT_UNSUPPORTED=>KSAUDIO_SPEAKER_UNSUPPORTED"; |
| 80 return KSAUDIO_SPEAKER_UNSUPPORTED; |
| 81 case CHANNEL_LAYOUT_MONO: |
| 82 DVLOG(2) << "CHANNEL_LAYOUT_MONO=>KSAUDIO_SPEAKER_MONO"; |
| 83 return KSAUDIO_SPEAKER_MONO; |
| 84 case CHANNEL_LAYOUT_STEREO: |
| 85 DVLOG(2) << "CHANNEL_LAYOUT_STEREO=>KSAUDIO_SPEAKER_STEREO"; |
| 86 return KSAUDIO_SPEAKER_STEREO; |
| 87 case CHANNEL_LAYOUT_QUAD: |
| 88 DVLOG(2) << "CHANNEL_LAYOUT_QUAD=>KSAUDIO_SPEAKER_QUAD"; |
| 89 return KSAUDIO_SPEAKER_QUAD; |
| 90 case CHANNEL_LAYOUT_4_0: |
| 91 DVLOG(2) << "CHANNEL_LAYOUT_4_0=>KSAUDIO_SPEAKER_SURROUND"; |
| 92 return KSAUDIO_SPEAKER_SURROUND; |
| 93 case CHANNEL_LAYOUT_5_1_BACK: |
| 94 DVLOG(2) << "CHANNEL_LAYOUT_5_1_BACK=>KSAUDIO_SPEAKER_5POINT1"; |
| 95 return KSAUDIO_SPEAKER_5POINT1; |
| 96 case CHANNEL_LAYOUT_5_1: |
| 97 DVLOG(2) << "CHANNEL_LAYOUT_5_1=>KSAUDIO_SPEAKER_5POINT1_SURROUND"; |
| 98 return KSAUDIO_SPEAKER_5POINT1_SURROUND; |
| 99 case CHANNEL_LAYOUT_7_1_WIDE: |
| 100 DVLOG(2) << "CHANNEL_LAYOUT_7_1_WIDE=>KSAUDIO_SPEAKER_7POINT1"; |
| 101 return KSAUDIO_SPEAKER_7POINT1; |
| 102 case CHANNEL_LAYOUT_7_1: |
| 103 DVLOG(2) << "CHANNEL_LAYOUT_7_1=>KSAUDIO_SPEAKER_7POINT1_SURROUND"; |
| 104 return KSAUDIO_SPEAKER_7POINT1_SURROUND; |
| 105 default: |
| 106 DVLOG(2) << "Unsupported channel layout: " << layout; |
| 107 return KSAUDIO_SPEAKER_UNSUPPORTED; |
| 108 } |
| 109 } |
| 110 |
| 111 static std::ostream& operator<<(std::ostream& os, |
| 112 const WAVEFORMATPCMEX& format) { |
| 113 os << "wFormatTag: 0x" << std::hex << format.Format.wFormatTag |
| 114 << ", nChannels: " << std::dec << format.Format.nChannels |
| 115 << ", nSamplesPerSec: " << format.Format.nSamplesPerSec |
| 116 << ", nAvgBytesPerSec: " << format.Format.nAvgBytesPerSec |
| 117 << ", nBlockAlign: " << format.Format.nBlockAlign |
| 118 << ", wBitsPerSample: " << format.Format.wBitsPerSample |
| 119 << ", cbSize: " << format.Format.cbSize |
| 120 << ", wValidBitsPerSample: " << format.Samples.wValidBitsPerSample |
| 121 << ", dwChannelMask: 0x" << std::hex << format.dwChannelMask; |
| 122 return os; |
| 123 } |
| 124 |
| 70 bool LoadAudiosesDll() { | 125 bool LoadAudiosesDll() { |
| 71 static const wchar_t* const kAudiosesDLL = | 126 static const wchar_t* const kAudiosesDLL = |
| 72 L"%WINDIR%\\system32\\audioses.dll"; | 127 L"%WINDIR%\\system32\\audioses.dll"; |
| 73 | 128 |
| 74 wchar_t path[MAX_PATH] = {0}; | 129 wchar_t path[MAX_PATH] = {0}; |
| 75 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); | 130 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); |
| 76 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); | 131 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); |
| 77 } | 132 } |
| 78 | 133 |
| 79 bool CanCreateDeviceEnumerator() { | 134 bool CanCreateDeviceEnumerator() { |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; | 397 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; |
| 343 HRESULT hr = client->GetMixFormat( | 398 HRESULT hr = client->GetMixFormat( |
| 344 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); | 399 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); |
| 345 if (FAILED(hr)) | 400 if (FAILED(hr)) |
| 346 return hr; | 401 return hr; |
| 347 | 402 |
| 348 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; | 403 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; |
| 349 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); | 404 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); |
| 350 | 405 |
| 351 memcpy(format, format_pcmex, bytes); | 406 memcpy(format, format_pcmex, bytes); |
| 352 | 407 DVLOG(2) << *format; |
| 353 DVLOG(2) << "wFormatTag: 0x" << std::hex << format->Format.wFormatTag | |
| 354 << ", nChannels: " << std::dec << format->Format.nChannels | |
| 355 << ", nSamplesPerSec: " << format->Format.nSamplesPerSec | |
| 356 << ", nAvgBytesPerSec: " << format->Format.nAvgBytesPerSec | |
| 357 << ", nBlockAlign: " << format->Format.nBlockAlign | |
| 358 << ", wBitsPerSample: " << format->Format.wBitsPerSample | |
| 359 << ", cbSize: " << format->Format.cbSize | |
| 360 << ", wValidBitsPerSample: " << format->Samples.wValidBitsPerSample | |
| 361 << ", dwChannelMask: 0x" << std::hex << format->dwChannelMask; | |
| 362 | 408 |
| 363 return hr; | 409 return hr; |
| 364 } | 410 } |
| 365 | 411 |
| 366 HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( | 412 HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( |
| 367 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { | 413 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { |
| 368 DCHECK(IsSupported()); | 414 DCHECK(IsSupported()); |
| 369 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); | 415 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); |
| 370 if (!client) { | 416 if (!client) { |
| 371 // Map NULL-pointer to new error code which can be different from the | 417 // Map NULL-pointer to new error code which can be different from the |
| (...skipping 11 matching lines...) Expand all Loading... |
| 383 HRESULT hr = client->IsFormatSupported( | 429 HRESULT hr = client->IsFormatSupported( |
| 384 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), | 430 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), |
| 385 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); | 431 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); |
| 386 | 432 |
| 387 // This log can only be triggered for shared mode. | 433 // This log can only be triggered for shared mode. |
| 388 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " | 434 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " |
| 389 << "but a closest match exists."; | 435 << "but a closest match exists."; |
| 390 // This log can be triggered both for shared and exclusive modes. | 436 // This log can be triggered both for shared and exclusive modes. |
| 391 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; | 437 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; |
| 392 if (hr == S_FALSE) { | 438 if (hr == S_FALSE) { |
| 393 DVLOG(2) << "wFormatTag: " << closest_match->Format.wFormatTag | 439 DVLOG(2) << *closest_match; |
| 394 << ", nChannels: " << closest_match->Format.nChannels | |
| 395 << ", nSamplesPerSec: " << closest_match->Format.nSamplesPerSec | |
| 396 << ", wBitsPerSample: " << closest_match->Format.wBitsPerSample; | |
| 397 } | 440 } |
| 398 | 441 |
| 399 return (hr == S_OK); | 442 return (hr == S_OK); |
| 400 } | 443 } |
| 401 | 444 |
| 445 bool CoreAudioUtil::IsChannelLayoutSupported(EDataFlow data_flow, ERole role, |
| 446 ChannelLayout channel_layout) { |
| 447 DCHECK(IsSupported()); |
| 448 |
| 449 // First, get the preferred mixing format for shared mode streams. |
| 450 |
| 451 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); |
| 452 if (!client) |
| 453 return false; |
| 454 |
| 455 WAVEFORMATPCMEX format; |
| 456 HRESULT hr = CoreAudioUtil::GetSharedModeMixFormat(client, &format); |
| 457 if (FAILED(hr)) |
| 458 return false; |
| 459 |
| 460 // Next, check if it is possible to use an alternative format where the |
| 461 // channel layout (and possibly number of channels) is modified. |
| 462 |
| 463 // Convert generic channel layout into Windows-specific channel configuration. |
| 464 ChannelConfig new_config = ChannelLayoutToChannelConfig(channel_layout); |
| 465 if (new_config == KSAUDIO_SPEAKER_UNSUPPORTED) { |
| 466 return false; |
| 467 } |
| 468 format.dwChannelMask = new_config; |
| 469 |
| 470 // Modify the format if the new channel layout has changed the number of |
| 471 // utilized channels. |
| 472 const int channels = ChannelLayoutToChannelCount(channel_layout); |
| 473 if (channels != format.Format.nChannels) { |
| 474 format.Format.nChannels = channels; |
| 475 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels; |
| 476 format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * |
| 477 format.Format.nBlockAlign; |
| 478 } |
| 479 DVLOG(2) << format; |
| 480 |
| 481 // Some devices can initialize a shared-mode stream with a format that is |
| 482 // not identical to the mix format obtained from the GetMixFormat() method. |
| 483 // However, chances of succeeding increases if we use the same number of |
| 484 // channels and the same sample rate as the mix format. I.e, this call will |
| 485 // return true only in those cases where the audio engine is able to support |
| 486 // an even wider range of shared-mode formats where the installation package |
| 487 // for the audio device includes a local effects (LFX) audio processing |
| 488 // object (APO) that can handle format conversions. |
| 489 return CoreAudioUtil::IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, |
| 490 &format); |
| 491 } |
| 492 |
| 402 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, | 493 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, |
| 403 AUDCLNT_SHAREMODE share_mode, | 494 AUDCLNT_SHAREMODE share_mode, |
| 404 REFERENCE_TIME* device_period) { | 495 REFERENCE_TIME* device_period) { |
| 405 DCHECK(IsSupported()); | 496 DCHECK(IsSupported()); |
| 406 | 497 |
| 407 // Get the period of the engine thread. | 498 // Get the period of the engine thread. |
| 408 REFERENCE_TIME default_period = 0; | 499 REFERENCE_TIME default_period = 0; |
| 409 REFERENCE_TIME minimum_period = 0; | 500 REFERENCE_TIME minimum_period = 0; |
| 410 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); | 501 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); |
| 411 if (FAILED(hr)) | 502 if (FAILED(hr)) |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 598 return false; | 689 return false; |
| 599 | 690 |
| 600 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to | 691 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to |
| 601 // explicitly write silence data to the rendering buffer. | 692 // explicitly write silence data to the rendering buffer. |
| 602 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; | 693 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; |
| 603 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, | 694 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, |
| 604 AUDCLNT_BUFFERFLAGS_SILENT)); | 695 AUDCLNT_BUFFERFLAGS_SILENT)); |
| 605 } | 696 } |
| 606 | 697 |
| 607 } // namespace media | 698 } // namespace media |
| OLD | NEW |