Chromium Code Reviews| 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 #define KSAUDIO_SPEAKER_UNSUPPORTED 0 | |
|
DaleCurtis
2013/04/01 20:43:50
enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 } instead ?
henrika (OOO until Aug 14)
2013/04/02 10:54:05
Done.
| |
| 24 | |
| 23 namespace media { | 25 namespace media { |
| 24 | 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 |
| (...skipping 22 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 |