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" |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 return CHANNEL_LAYOUT_7_1_WIDE; | 60 return CHANNEL_LAYOUT_7_1_WIDE; |
| 61 case KSAUDIO_SPEAKER_7POINT1_SURROUND: | 61 case KSAUDIO_SPEAKER_7POINT1_SURROUND: |
| 62 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; | 62 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; |
| 63 return CHANNEL_LAYOUT_7_1; | 63 return CHANNEL_LAYOUT_7_1; |
| 64 default: | 64 default: |
| 65 DVLOG(2) << "Unsupported channel layout: " << config; | 65 DVLOG(2) << "Unsupported channel layout: " << config; |
| 66 return CHANNEL_LAYOUT_UNSUPPORTED; | 66 return CHANNEL_LAYOUT_UNSUPPORTED; |
| 67 } | 67 } |
| 68 } | 68 } |
| 69 | 69 |
| 70 // TODO(henrika): add mapping for all types in the ChannelLayout enumerator. | |
| 71 static ChannelConfig ChannelLayoutToChannelConfig(ChannelLayout layout) { | |
| 72 switch (layout) { | |
| 73 case CHANNEL_LAYOUT_NONE: | |
|
DaleCurtis
2013/03/27 17:30:18
These top two should be UNSUPPORTED.
henrika (OOO until Aug 14)
2013/03/28 09:15:05
Defined KSAUDIO_SPEAKER_UNSUPPORTED locally.
| |
| 74 DVLOG(2) << "CHANNEL_LAYOUT_NONE=>KSAUDIO_SPEAKER_DIRECTOUT"; | |
| 75 return KSAUDIO_SPEAKER_DIRECTOUT; | |
| 76 case CHANNEL_LAYOUT_UNSUPPORTED: | |
| 77 DVLOG(2) << "CHANNEL_LAYOUT_UNSUPPORTED=>KSAUDIO_SPEAKER_DIRECTOUT"; | |
| 78 return KSAUDIO_SPEAKER_DIRECTOUT; | |
| 79 case CHANNEL_LAYOUT_MONO: | |
| 80 DVLOG(2) << "CHANNEL_LAYOUT_MONO=>KSAUDIO_SPEAKER_MONO"; | |
| 81 return KSAUDIO_SPEAKER_MONO; | |
| 82 case CHANNEL_LAYOUT_STEREO: | |
| 83 DVLOG(2) << "CHANNEL_LAYOUT_STEREO=>KSAUDIO_SPEAKER_STEREO"; | |
| 84 return KSAUDIO_SPEAKER_STEREO; | |
| 85 case CHANNEL_LAYOUT_QUAD: | |
| 86 DVLOG(2) << "CHANNEL_LAYOUT_QUAD=>KSAUDIO_SPEAKER_QUAD"; | |
| 87 return KSAUDIO_SPEAKER_QUAD; | |
| 88 case CHANNEL_LAYOUT_4_0: | |
| 89 DVLOG(2) << "CHANNEL_LAYOUT_4_0=>KSAUDIO_SPEAKER_SURROUND"; | |
| 90 return KSAUDIO_SPEAKER_SURROUND; | |
| 91 case CHANNEL_LAYOUT_5_1_BACK: | |
| 92 DVLOG(2) << "CHANNEL_LAYOUT_5_1_BACK=>KSAUDIO_SPEAKER_5POINT1"; | |
| 93 return KSAUDIO_SPEAKER_5POINT1; | |
| 94 case CHANNEL_LAYOUT_5_1: | |
| 95 DVLOG(2) << "CHANNEL_LAYOUT_5_1=>KSAUDIO_SPEAKER_5POINT1_SURROUND"; | |
| 96 return KSAUDIO_SPEAKER_5POINT1_SURROUND; | |
| 97 case CHANNEL_LAYOUT_7_1_WIDE: | |
| 98 DVLOG(2) << "CHANNEL_LAYOUT_7_1_WIDE=>KSAUDIO_SPEAKER_7POINT1"; | |
| 99 return KSAUDIO_SPEAKER_7POINT1; | |
| 100 case CHANNEL_LAYOUT_7_1: | |
| 101 DVLOG(2) << "CHANNEL_LAYOUT_7_1=>KSAUDIO_SPEAKER_7POINT1_SURROUND"; | |
| 102 return KSAUDIO_SPEAKER_7POINT1_SURROUND; | |
| 103 default: | |
| 104 DVLOG(2) << "Unsupported channel layout: " << layout; | |
| 105 return CHANNEL_LAYOUT_UNSUPPORTED; | |
|
DaleCurtis
2013/03/27 17:30:18
I'm surprised this ChannelLayout returns correctly
henrika (OOO until Aug 14)
2013/03/28 09:15:05
fixed.
| |
| 106 } | |
| 107 } | |
| 108 | |
| 109 static std::ostream& operator<<(std::ostream& os, | |
| 110 const WAVEFORMATPCMEX& format) { | |
| 111 os << "wFormatTag: 0x" << std::hex << format.Format.wFormatTag | |
| 112 << ", nChannels: " << std::dec << format.Format.nChannels | |
| 113 << ", nSamplesPerSec: " << format.Format.nSamplesPerSec | |
| 114 << ", nAvgBytesPerSec: " << format.Format.nAvgBytesPerSec | |
| 115 << ", nBlockAlign: " << format.Format.nBlockAlign | |
| 116 << ", wBitsPerSample: " << format.Format.wBitsPerSample | |
| 117 << ", cbSize: " << format.Format.cbSize | |
| 118 << ", wValidBitsPerSample: " << format.Samples.wValidBitsPerSample | |
| 119 << ", dwChannelMask: 0x" << std::hex << format.dwChannelMask; | |
| 120 return os; | |
| 121 } | |
| 122 | |
| 70 bool LoadAudiosesDll() { | 123 bool LoadAudiosesDll() { |
| 71 static const wchar_t* const kAudiosesDLL = | 124 static const wchar_t* const kAudiosesDLL = |
| 72 L"%WINDIR%\\system32\\audioses.dll"; | 125 L"%WINDIR%\\system32\\audioses.dll"; |
| 73 | 126 |
| 74 wchar_t path[MAX_PATH] = {0}; | 127 wchar_t path[MAX_PATH] = {0}; |
| 75 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); | 128 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); |
| 76 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); | 129 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); |
| 77 } | 130 } |
| 78 | 131 |
| 79 bool CanCreateDeviceEnumerator() { | 132 bool CanCreateDeviceEnumerator() { |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 342 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; | 395 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; |
| 343 HRESULT hr = client->GetMixFormat( | 396 HRESULT hr = client->GetMixFormat( |
| 344 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); | 397 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); |
| 345 if (FAILED(hr)) | 398 if (FAILED(hr)) |
| 346 return hr; | 399 return hr; |
| 347 | 400 |
| 348 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; | 401 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; |
| 349 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); | 402 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); |
| 350 | 403 |
| 351 memcpy(format, format_pcmex, bytes); | 404 memcpy(format, format_pcmex, bytes); |
| 352 | 405 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 | 406 |
| 363 return hr; | 407 return hr; |
| 364 } | 408 } |
| 365 | 409 |
| 366 HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( | 410 HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( |
| 367 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { | 411 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { |
| 368 DCHECK(IsSupported()); | 412 DCHECK(IsSupported()); |
| 369 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); | 413 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); |
| 370 if (!client) { | 414 if (!client) { |
| 371 // Map NULL-pointer to new error code which can be different from the | 415 // 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( | 427 HRESULT hr = client->IsFormatSupported( |
| 384 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), | 428 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), |
| 385 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); | 429 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); |
| 386 | 430 |
| 387 // This log can only be triggered for shared mode. | 431 // This log can only be triggered for shared mode. |
| 388 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " | 432 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " |
| 389 << "but a closest match exists."; | 433 << "but a closest match exists."; |
| 390 // This log can be triggered both for shared and exclusive modes. | 434 // This log can be triggered both for shared and exclusive modes. |
| 391 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; | 435 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; |
| 392 if (hr == S_FALSE) { | 436 if (hr == S_FALSE) { |
| 393 DVLOG(2) << "wFormatTag: " << closest_match->Format.wFormatTag | 437 DVLOG(2) << *closest_match; |
| 394 << ", nChannels: " << closest_match->Format.nChannels | |
| 395 << ", nSamplesPerSec: " << closest_match->Format.nSamplesPerSec | |
| 396 << ", wBitsPerSample: " << closest_match->Format.wBitsPerSample; | |
| 397 } | 438 } |
| 398 | 439 |
| 399 return (hr == S_OK); | 440 return (hr == S_OK); |
| 400 } | 441 } |
| 401 | 442 |
| 443 bool CoreAudioUtil::IsChannelLayoutSupported(EDataFlow data_flow, ERole role, | |
| 444 ChannelLayout channel_layout) { | |
| 445 DCHECK(IsSupported()); | |
| 446 | |
| 447 // First, get the preferred mixing format for shared mode streams. | |
| 448 | |
| 449 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); | |
| 450 if (!client) | |
| 451 return false; | |
| 452 | |
| 453 WAVEFORMATPCMEX format; | |
| 454 HRESULT hr = CoreAudioUtil::GetSharedModeMixFormat(client, &format); | |
| 455 if (FAILED(hr)) | |
| 456 return false; | |
| 457 | |
| 458 // Next, check if it is possible to use an alternative format where the | |
| 459 // channel layout (and possibly number of channels) is modified. | |
| 460 | |
| 461 const int channels = ChannelLayoutToChannelCount(channel_layout); | |
| 462 if (channels != format.Format.nChannels) { | |
| 463 format.Format.nChannels = channels; | |
| 464 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels; | |
| 465 format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * | |
| 466 format.Format.nBlockAlign; | |
| 467 } | |
| 468 | |
| 469 ChannelConfig new_config = ChannelLayoutToChannelConfig(channel_layout); | |
| 470 format.dwChannelMask = new_config; | |
|
DaleCurtis
2013/03/27 17:30:18
Bail early if new_config == unsupported? Should pr
henrika (OOO until Aug 14)
2013/03/28 09:15:05
Done.
| |
| 471 DVLOG(2) << format; | |
| 472 | |
| 473 // Some devices can initialize a shared-mode stream with a format that is | |
| 474 // not identical to the mix format obtained from the GetMixFormat() method. | |
| 475 // However, chances of succeeding increases if we use the same number of | |
| 476 // channels and the same sample rate as the mix format. I.e, this call will | |
| 477 // return true only in those cases where the audio engine is able to support | |
| 478 // an even wider range of shared-mode formats where the installation package | |
| 479 // for the audio device includes a local effects (LFX) audio processing | |
| 480 // object (APO) that can handle format conversions. | |
| 481 return CoreAudioUtil::IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, | |
|
DaleCurtis
2013/03/27 17:30:18
align &format or wrap the whole line.
henrika (OOO until Aug 14)
2013/03/28 09:15:05
Done.
| |
| 482 &format); | |
| 483 } | |
| 484 | |
| 402 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, | 485 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, |
| 403 AUDCLNT_SHAREMODE share_mode, | 486 AUDCLNT_SHAREMODE share_mode, |
| 404 REFERENCE_TIME* device_period) { | 487 REFERENCE_TIME* device_period) { |
| 405 DCHECK(IsSupported()); | 488 DCHECK(IsSupported()); |
| 406 | 489 |
| 407 // Get the period of the engine thread. | 490 // Get the period of the engine thread. |
| 408 REFERENCE_TIME default_period = 0; | 491 REFERENCE_TIME default_period = 0; |
| 409 REFERENCE_TIME minimum_period = 0; | 492 REFERENCE_TIME minimum_period = 0; |
| 410 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); | 493 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); |
| 411 if (FAILED(hr)) | 494 if (FAILED(hr)) |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 598 return false; | 681 return false; |
| 599 | 682 |
| 600 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to | 683 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to |
| 601 // explicitly write silence data to the rendering buffer. | 684 // explicitly write silence data to the rendering buffer. |
| 602 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; | 685 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; |
| 603 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, | 686 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, |
| 604 AUDCLNT_BUFFERFLAGS_SILENT)); | 687 AUDCLNT_BUFFERFLAGS_SILENT)); |
| 605 } | 688 } |
| 606 | 689 |
| 607 } // namespace media | 690 } // namespace media |
| OLD | NEW |