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/audio_manager_win.h" | 5 #include "media/audio/win/audio_manager_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <objbase.h> // This has to be before initguid.h | 8 #include <objbase.h> // This has to be before initguid.h |
| 9 #include <initguid.h> | 9 #include <initguid.h> |
| 10 #include <mmsystem.h> | 10 #include <mmsystem.h> |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 #include "base/strings/string_number_conversions.h" | 24 #include "base/strings/string_number_conversions.h" |
| 25 #include "base/strings/string_util.h" | 25 #include "base/strings/string_util.h" |
| 26 #include "base/win/windows_version.h" | 26 #include "base/win/windows_version.h" |
| 27 #include "media/audio/audio_device_description.h" | 27 #include "media/audio/audio_device_description.h" |
| 28 #include "media/audio/audio_io.h" | 28 #include "media/audio/audio_io.h" |
| 29 #include "media/audio/win/audio_device_listener_win.h" | 29 #include "media/audio/win/audio_device_listener_win.h" |
| 30 #include "media/audio/win/audio_low_latency_input_win.h" | 30 #include "media/audio/win/audio_low_latency_input_win.h" |
| 31 #include "media/audio/win/audio_low_latency_output_win.h" | 31 #include "media/audio/win/audio_low_latency_output_win.h" |
| 32 #include "media/audio/win/core_audio_util_win.h" | 32 #include "media/audio/win/core_audio_util_win.h" |
| 33 #include "media/audio/win/device_enumeration_win.h" | 33 #include "media/audio/win/device_enumeration_win.h" |
| 34 #include "media/audio/win/wavein_input_win.h" | |
| 35 #include "media/audio/win/waveout_output_win.h" | 34 #include "media/audio/win/waveout_output_win.h" |
| 36 #include "media/base/audio_parameters.h" | 35 #include "media/base/audio_parameters.h" |
| 37 #include "media/base/bind_to_current_loop.h" | 36 #include "media/base/bind_to_current_loop.h" |
| 38 #include "media/base/channel_layout.h" | 37 #include "media/base/channel_layout.h" |
| 39 #include "media/base/limits.h" | 38 #include "media/base/limits.h" |
| 40 #include "media/base/media_switches.h" | 39 #include "media/base/media_switches.h" |
| 41 | 40 |
| 42 // The following are defined in various DDK headers, and we (re)define them here | 41 // The following are defined in various DDK headers, and we (re)define them here |
| 43 // to avoid adding the DDK as a chrome dependency. | 42 // to avoid adding the DDK as a chrome dependency. |
| 44 #define DRV_QUERYDEVICEINTERFACE 0x80c | 43 #define DRV_QUERYDEVICEINTERFACE 0x80c |
| 45 #define DRVM_MAPPER_PREFERRED_GET 0x2015 | 44 #define DRVM_MAPPER_PREFERRED_GET 0x2015 |
| 46 #define DRV_QUERYDEVICEINTERFACESIZE 0x80d | 45 #define DRV_QUERYDEVICEINTERFACESIZE 0x80d |
| 47 DEFINE_GUID(AM_KSCATEGORY_AUDIO, 0x6994ad04, 0x93ef, 0x11d0, | 46 DEFINE_GUID(AM_KSCATEGORY_AUDIO, |
|
tommi (sloooow) - chröme
2017/01/24 21:10:51
some changes, like this one, are due to git cl for
| |
| 48 0xa3, 0xcc, 0x00, 0xa0, 0xc9, 0x22, 0x31, 0x96); | 47 0x6994ad04, |
| 48 0x93ef, | |
| 49 0x11d0, | |
| 50 0xa3, | |
| 51 0xcc, | |
| 52 0x00, | |
| 53 0xa0, | |
| 54 0xc9, | |
| 55 0x22, | |
| 56 0x31, | |
| 57 0x96); | |
| 49 | 58 |
| 50 namespace media { | 59 namespace media { |
| 51 | 60 |
| 52 // Maximum number of output streams that can be open simultaneously. | 61 // Maximum number of output streams that can be open simultaneously. |
| 53 static const int kMaxOutputStreams = 50; | 62 static const int kMaxOutputStreams = 50; |
| 54 | 63 |
| 55 // Up to 8 channels can be passed to the driver. This should work, given the | 64 // Up to 8 channels can be passed to the driver. This should work, given the |
| 56 // right drivers, but graceful error handling is needed. | 65 // right drivers, but graceful error handling is needed. |
| 57 static const int kWinMaxChannels = 8; | 66 static const int kWinMaxChannels = 8; |
| 58 | 67 |
| 59 // We use 3 buffers for recording audio so that if a recording callback takes | |
| 60 // some time to return we won't lose audio. More buffers while recording are | |
| 61 // ok because they don't introduce any delay in recording, unlike in playback | |
| 62 // where you first need to fill in that number of buffers before starting to | |
| 63 // play. | |
| 64 static const int kNumInputBuffers = 3; | |
| 65 | |
| 66 // Buffer size to use for input and output stream when a proper size can't be | 68 // Buffer size to use for input and output stream when a proper size can't be |
| 67 // determined from the system | 69 // determined from the system |
| 68 static const int kFallbackBufferSize = 2048; | 70 static const int kFallbackBufferSize = 2048; |
| 69 | 71 |
| 70 static int GetVersionPartAsInt(DWORDLONG num) { | 72 static int GetVersionPartAsInt(DWORDLONG num) { |
| 71 return static_cast<int>(num & 0xffff); | 73 return static_cast<int>(num & 0xffff); |
| 72 } | 74 } |
| 73 | 75 |
| 74 // Returns a string containing the given device's description and installed | 76 // Returns a string containing the given device's description and installed |
| 75 // driver version. | 77 // driver version. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 87 &device_install_params); | 89 &device_install_params); |
| 88 | 90 |
| 89 SP_DRVINFO_DATA driver_data; | 91 SP_DRVINFO_DATA driver_data; |
| 90 driver_data.cbSize = sizeof(driver_data); | 92 driver_data.cbSize = sizeof(driver_data); |
| 91 base::string16 device_and_driver_info; | 93 base::string16 device_and_driver_info; |
| 92 if (SetupDiBuildDriverInfoList(device_info, device_data, | 94 if (SetupDiBuildDriverInfoList(device_info, device_data, |
| 93 SPDIT_COMPATDRIVER)) { | 95 SPDIT_COMPATDRIVER)) { |
| 94 if (SetupDiEnumDriverInfo(device_info, device_data, SPDIT_COMPATDRIVER, 0, | 96 if (SetupDiEnumDriverInfo(device_info, device_data, SPDIT_COMPATDRIVER, 0, |
| 95 &driver_data)) { | 97 &driver_data)) { |
| 96 DWORDLONG version = driver_data.DriverVersion; | 98 DWORDLONG version = driver_data.DriverVersion; |
| 97 device_and_driver_info = base::string16(driver_data.Description) + L" v" + | 99 device_and_driver_info = |
| 100 base::string16(driver_data.Description) + L" v" + | |
| 98 base::IntToString16(GetVersionPartAsInt((version >> 48))) + L"." + | 101 base::IntToString16(GetVersionPartAsInt((version >> 48))) + L"." + |
| 99 base::IntToString16(GetVersionPartAsInt((version >> 32))) + L"." + | 102 base::IntToString16(GetVersionPartAsInt((version >> 32))) + L"." + |
| 100 base::IntToString16(GetVersionPartAsInt((version >> 16))) + L"." + | 103 base::IntToString16(GetVersionPartAsInt((version >> 16))) + L"." + |
| 101 base::IntToString16(GetVersionPartAsInt(version)); | 104 base::IntToString16(GetVersionPartAsInt(version)); |
| 102 } | 105 } |
| 103 SetupDiDestroyDriverInfoList(device_info, device_data, SPDIT_COMPATDRIVER); | 106 SetupDiDestroyDriverInfoList(device_info, device_data, SPDIT_COMPATDRIVER); |
| 104 } | 107 } |
| 105 | 108 |
| 106 SetupDiSetDeviceInstallParams(device_info, device_data, | 109 SetupDiSetDeviceInstallParams(device_info, device_data, |
| 107 &old_device_install_params); | 110 &old_device_install_params); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 127 // - Some XP configurations (even multi-processor ones) also need 3. | 130 // - Some XP configurations (even multi-processor ones) also need 3. |
| 128 return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3; | 131 return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3; |
| 129 } | 132 } |
| 130 | 133 |
| 131 AudioManagerWin::AudioManagerWin( | 134 AudioManagerWin::AudioManagerWin( |
| 132 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 135 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 133 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, | 136 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, |
| 134 AudioLogFactory* audio_log_factory) | 137 AudioLogFactory* audio_log_factory) |
| 135 : AudioManagerBase(std::move(task_runner), | 138 : AudioManagerBase(std::move(task_runner), |
| 136 std::move(worker_task_runner), | 139 std::move(worker_task_runner), |
| 137 audio_log_factory), | 140 audio_log_factory) { |
| 138 // |CoreAudioUtil::IsSupported()| uses static variables to avoid doing | |
| 139 // multiple initializations. This is however not thread safe. | |
| 140 // So, here we call it explicitly before we kick off the audio thread | |
| 141 // or do any other work. | |
| 142 enumeration_type_(CoreAudioUtil::IsSupported() ? kMMDeviceEnumeration | |
|
DaleCurtis
2017/01/24 21:09:30
This may be a race now in the setup of the interna
tommi (sloooow) - chröme
2017/01/24 21:34:57
Ah, thanks for pointing that out. I had forgotten
DaleCurtis
2017/01/24 21:47:26
I think that's the safest change for now. It'd be
| |
| 143 : kWaveEnumeration) { | |
| 144 SetMaxOutputStreamsAllowed(kMaxOutputStreams); | 141 SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
| 145 | 142 |
| 146 // WARNING: This is executed on the UI loop, do not add any code here which | 143 // WARNING: This is executed on the UI loop, do not add any code here which |
| 147 // loads libraries or attempts to call out into the OS. Instead add such code | 144 // loads libraries or attempts to call out into the OS. Instead add such code |
| 148 // to the InitializeOnAudioThread() method below. | 145 // to the InitializeOnAudioThread() method below. |
| 149 | 146 |
| 150 // Task must be posted last to avoid races from handing out "this" to the | 147 // Task must be posted last to avoid races from handing out "this" to the |
| 151 // audio thread. | 148 // audio thread. |
| 152 GetTaskRunner()->PostTask(FROM_HERE, base::Bind( | 149 GetTaskRunner()->PostTask( |
| 153 &AudioManagerWin::InitializeOnAudioThread, base::Unretained(this))); | 150 FROM_HERE, base::Bind(&AudioManagerWin::InitializeOnAudioThread, |
| 151 base::Unretained(this))); | |
| 154 } | 152 } |
| 155 | 153 |
| 156 AudioManagerWin::~AudioManagerWin() { | 154 AudioManagerWin::~AudioManagerWin() { |
| 157 Shutdown(); | 155 Shutdown(); |
| 158 } | 156 } |
| 159 | 157 |
| 160 bool AudioManagerWin::HasAudioOutputDevices() { | 158 bool AudioManagerWin::HasAudioOutputDevices() { |
| 161 return (::waveOutGetNumDevs() != 0); | 159 return (::waveOutGetNumDevs() != 0); |
| 162 } | 160 } |
| 163 | 161 |
| 164 bool AudioManagerWin::HasAudioInputDevices() { | 162 bool AudioManagerWin::HasAudioInputDevices() { |
| 165 return (::waveInGetNumDevs() != 0); | 163 return (::waveInGetNumDevs() != 0); |
| 166 } | 164 } |
| 167 | 165 |
| 168 void AudioManagerWin::InitializeOnAudioThread() { | 166 void AudioManagerWin::InitializeOnAudioThread() { |
| 169 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 167 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| 170 | 168 |
| 171 if (core_audio_supported()) { | 169 // AudioDeviceListenerWin must be initialized on a COM thread. |
| 172 // AudioDeviceListenerWin must be initialized on a COM thread and should | 170 output_device_listener_.reset(new AudioDeviceListenerWin(BindToCurrentLoop( |
| 173 // only be used if WASAPI / Core Audio is supported. | 171 base::Bind(&AudioManagerWin::NotifyAllOutputDeviceChangeListeners, |
| 174 output_device_listener_.reset(new AudioDeviceListenerWin(BindToCurrentLoop( | 172 base::Unretained(this))))); |
| 175 base::Bind(&AudioManagerWin::NotifyAllOutputDeviceChangeListeners, | |
| 176 base::Unretained(this))))); | |
| 177 } | |
| 178 } | 173 } |
| 179 | 174 |
| 180 base::string16 AudioManagerWin::GetAudioInputDeviceModel() { | 175 base::string16 AudioManagerWin::GetAudioInputDeviceModel() { |
|
henrika (OOO until Aug 14)
2017/01/25 12:43:33
Used at https://cs.chromium.org/chromium/src/conte
tommi (sloooow) - chröme
2017/01/25 21:44:33
It's not needed now that Olga has landed her patch
| |
| 181 // Get the default audio capture device and its device interface name. | 176 // Get the default audio capture device and its device interface name. |
| 182 DWORD device_id = 0; | 177 DWORD device_id = 0; |
| 183 waveInMessage(reinterpret_cast<HWAVEIN>(WAVE_MAPPER), | 178 waveInMessage(reinterpret_cast<HWAVEIN>(WAVE_MAPPER), |
| 184 DRVM_MAPPER_PREFERRED_GET, | 179 DRVM_MAPPER_PREFERRED_GET, |
| 185 reinterpret_cast<DWORD_PTR>(&device_id), NULL); | 180 reinterpret_cast<DWORD_PTR>(&device_id), NULL); |
| 186 ULONG device_interface_name_size = 0; | 181 ULONG device_interface_name_size = 0; |
| 187 waveInMessage(reinterpret_cast<HWAVEIN>(device_id), | 182 waveInMessage(reinterpret_cast<HWAVEIN>(device_id), |
| 188 DRV_QUERYDEVICEINTERFACESIZE, | 183 DRV_QUERYDEVICEINTERFACESIZE, |
| 189 reinterpret_cast<DWORD_PTR>(&device_interface_name_size), 0); | 184 reinterpret_cast<DWORD_PTR>(&device_interface_name_size), 0); |
| 190 size_t bytes_in_char16 = sizeof(base::string16::value_type); | 185 size_t bytes_in_char16 = sizeof(base::string16::value_type); |
| 191 DCHECK_EQ(0u, device_interface_name_size % bytes_in_char16); | 186 DCHECK_EQ(0u, device_interface_name_size % bytes_in_char16); |
| 192 if (device_interface_name_size <= bytes_in_char16) | 187 if (device_interface_name_size <= bytes_in_char16) |
| 193 return base::string16(); // No audio capture device. | 188 return base::string16(); // No audio capture device. |
| 194 | 189 |
| 195 base::string16 device_interface_name; | 190 base::string16 device_interface_name; |
| 196 base::string16::value_type* name_ptr = base::WriteInto(&device_interface_name, | 191 base::string16::value_type* name_ptr = base::WriteInto( |
| 197 device_interface_name_size / bytes_in_char16); | 192 &device_interface_name, device_interface_name_size / bytes_in_char16); |
| 198 waveInMessage(reinterpret_cast<HWAVEIN>(device_id), | 193 waveInMessage(reinterpret_cast<HWAVEIN>(device_id), DRV_QUERYDEVICEINTERFACE, |
| 199 DRV_QUERYDEVICEINTERFACE, | |
| 200 reinterpret_cast<DWORD_PTR>(name_ptr), | 194 reinterpret_cast<DWORD_PTR>(name_ptr), |
| 201 static_cast<DWORD_PTR>(device_interface_name_size)); | 195 static_cast<DWORD_PTR>(device_interface_name_size)); |
| 202 | 196 |
| 203 // Enumerate all audio devices and find the one matching the above device | 197 // Enumerate all audio devices and find the one matching the above device |
| 204 // interface name. | 198 // interface name. |
| 205 HDEVINFO device_info = SetupDiGetClassDevs( | 199 HDEVINFO device_info = SetupDiGetClassDevs( |
| 206 &AM_KSCATEGORY_AUDIO, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | 200 &AM_KSCATEGORY_AUDIO, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); |
| 207 if (device_info == INVALID_HANDLE_VALUE) | 201 if (device_info == INVALID_HANDLE_VALUE) |
| 208 return base::string16(); | 202 return base::string16(); |
| 209 | 203 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 220 &interface_detail_size, &device_data); | 214 &interface_detail_size, &device_data); |
| 221 if (!interface_detail_size) | 215 if (!interface_detail_size) |
| 222 continue; | 216 continue; |
| 223 | 217 |
| 224 std::unique_ptr<char[]> interface_detail_buffer( | 218 std::unique_ptr<char[]> interface_detail_buffer( |
| 225 new char[interface_detail_size]); | 219 new char[interface_detail_size]); |
| 226 SP_DEVICE_INTERFACE_DETAIL_DATA* interface_detail = | 220 SP_DEVICE_INTERFACE_DETAIL_DATA* interface_detail = |
| 227 reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>( | 221 reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>( |
| 228 interface_detail_buffer.get()); | 222 interface_detail_buffer.get()); |
| 229 interface_detail->cbSize = interface_detail_size; | 223 interface_detail->cbSize = interface_detail_size; |
| 230 if (!SetupDiGetDeviceInterfaceDetail(device_info, &interface_data, | 224 if (!SetupDiGetDeviceInterfaceDetail( |
| 231 interface_detail, | 225 device_info, &interface_data, interface_detail, |
| 232 interface_detail_size, NULL, | 226 interface_detail_size, NULL, &device_data)) |
| 233 &device_data)) | |
| 234 return base::string16(); | 227 return base::string16(); |
| 235 | 228 |
| 236 bool device_found = (device_interface_name == interface_detail->DevicePath); | 229 bool device_found = (device_interface_name == interface_detail->DevicePath); |
| 237 | 230 |
| 238 if (device_found) | 231 if (device_found) |
| 239 return GetDeviceAndDriverInfo(device_info, &device_data); | 232 return GetDeviceAndDriverInfo(device_info, &device_data); |
| 240 } | 233 } |
| 241 | 234 |
| 242 return base::string16(); | 235 return base::string16(); |
| 243 } | 236 } |
| 244 | 237 |
| 245 void AudioManagerWin::ShowAudioInputSettings() { | 238 void AudioManagerWin::ShowAudioInputSettings() { |
| 246 std::wstring program; | |
| 247 std::string argument; | |
| 248 if (!core_audio_supported()) { | |
| 249 program = L"sndvol32.exe"; | |
| 250 argument = "-R"; | |
| 251 } else { | |
| 252 program = L"control.exe"; | |
| 253 argument = "mmsys.cpl,,1"; | |
| 254 } | |
| 255 | |
| 256 base::FilePath path; | 239 base::FilePath path; |
| 257 PathService::Get(base::DIR_SYSTEM, &path); | 240 PathService::Get(base::DIR_SYSTEM, &path); |
| 258 path = path.Append(program); | 241 path = path.Append(L"control.exe"); |
| 259 base::CommandLine command_line(path); | 242 base::CommandLine command_line(path); |
| 260 command_line.AppendArg(argument); | 243 command_line.AppendArg("mmsys.cpl,,1"); |
| 261 base::LaunchProcess(command_line, base::LaunchOptions()); | 244 base::LaunchProcess(command_line, base::LaunchOptions()); |
| 262 } | 245 } |
| 263 | 246 |
| 264 void AudioManagerWin::GetAudioDeviceNamesImpl( | 247 void AudioManagerWin::GetAudioDeviceNamesImpl(bool input, |
| 265 bool input, | 248 AudioDeviceNames* device_names) { |
| 266 AudioDeviceNames* device_names) { | |
| 267 DCHECK(device_names->empty()); | 249 DCHECK(device_names->empty()); |
| 268 // Enumerate all active audio-endpoint capture devices. | 250 // Enumerate all active audio-endpoint capture devices. |
| 269 if (enumeration_type() == kWaveEnumeration) { | 251 if (input) |
| 270 // Utilize the Wave API for Windows XP. | 252 GetInputDeviceNamesWin(device_names); |
| 271 if (input) | 253 else |
| 272 GetInputDeviceNamesWinXP(device_names); | 254 GetOutputDeviceNamesWin(device_names); |
| 273 else | |
| 274 GetOutputDeviceNamesWinXP(device_names); | |
| 275 } else { | |
| 276 // Utilize the MMDevice API (part of Core Audio) for Vista and higher. | |
| 277 if (input) | |
| 278 GetInputDeviceNamesWin(device_names); | |
| 279 else | |
| 280 GetOutputDeviceNamesWin(device_names); | |
| 281 } | |
| 282 | 255 |
| 283 if (!device_names->empty()) { | 256 if (!device_names->empty()) { |
| 284 if (enumeration_type() == kMMDeviceEnumeration) | 257 device_names->push_front(AudioDeviceName::CreateCommunications()); |
| 285 device_names->push_front(AudioDeviceName::CreateCommunications()); | |
| 286 | 258 |
| 287 // Always add default device parameters as first element. | 259 // Always add default device parameters as first element. |
| 288 device_names->push_front(AudioDeviceName::CreateDefault()); | 260 device_names->push_front(AudioDeviceName::CreateDefault()); |
| 289 } | 261 } |
| 290 } | 262 } |
| 291 | 263 |
| 292 void AudioManagerWin::GetAudioInputDeviceNames(AudioDeviceNames* device_names) { | 264 void AudioManagerWin::GetAudioInputDeviceNames(AudioDeviceNames* device_names) { |
| 293 GetAudioDeviceNamesImpl(true, device_names); | 265 GetAudioDeviceNamesImpl(true, device_names); |
| 294 } | 266 } |
| 295 | 267 |
| 296 void AudioManagerWin::GetAudioOutputDeviceNames( | 268 void AudioManagerWin::GetAudioOutputDeviceNames( |
| 297 AudioDeviceNames* device_names) { | 269 AudioDeviceNames* device_names) { |
| 298 GetAudioDeviceNamesImpl(false, device_names); | 270 GetAudioDeviceNamesImpl(false, device_names); |
| 299 } | 271 } |
| 300 | 272 |
| 301 AudioParameters AudioManagerWin::GetInputStreamParameters( | 273 AudioParameters AudioManagerWin::GetInputStreamParameters( |
| 302 const std::string& device_id) { | 274 const std::string& device_id) { |
| 303 HRESULT hr = E_FAIL; | |
| 304 AudioParameters parameters; | 275 AudioParameters parameters; |
| 305 if (core_audio_supported()) { | 276 HRESULT hr = |
| 306 hr = CoreAudioUtil::GetPreferredAudioParameters(device_id, false, | 277 CoreAudioUtil::GetPreferredAudioParameters(device_id, false, ¶meters); |
|
DaleCurtis
2017/01/24 21:09:30
This method will DCHECK() IsSupported(). I don't f
tommi (sloooow) - chröme
2017/01/24 21:34:56
I read over the bug, refreshing my memory. In the
DaleCurtis
2017/01/24 21:47:26
sgtm
| |
| 307 ¶meters); | |
| 308 } | |
| 309 | 278 |
| 310 if (FAILED(hr) || !parameters.IsValid()) { | 279 if (FAILED(hr) || !parameters.IsValid()) { |
| 311 // Windows Wave implementation is being used. | 280 // Fall back on default parameters. |
| 281 // TODO(tommi): Is this necessary? I'd guess that things would go wrong | |
| 282 // elsewhere if we weren't able to get the preferred audio parameters. | |
|
tommi (sloooow) - chröme
2017/01/24 21:10:51
Should we remove this block and return invalid aud
DaleCurtis
2017/01/24 21:47:25
That sgtm, there's not point anymore.
henrika (OOO until Aug 14)
2017/01/25 12:43:33
sgtm
tommi (sloooow) - chröme
2017/01/25 12:49:25
Done. (just return if we failed or params aren't v
| |
| 312 parameters = | 283 parameters = |
| 313 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, | 284 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, |
| 314 CHANNEL_LAYOUT_STEREO, 48000, 16, kFallbackBufferSize); | 285 CHANNEL_LAYOUT_STEREO, 48000, 16, kFallbackBufferSize); |
| 315 } | 286 } |
| 316 | 287 |
| 317 int user_buffer_size = GetUserBufferSize(); | 288 int user_buffer_size = GetUserBufferSize(); |
| 318 if (user_buffer_size) | 289 if (user_buffer_size) |
| 319 parameters.set_frames_per_buffer(user_buffer_size); | 290 parameters.set_frames_per_buffer(user_buffer_size); |
| 320 | 291 |
| 321 return parameters; | 292 return parameters; |
| 322 } | 293 } |
| 323 | 294 |
| 324 std::string AudioManagerWin::GetAssociatedOutputDeviceID( | 295 std::string AudioManagerWin::GetAssociatedOutputDeviceID( |
| 325 const std::string& input_device_id) { | 296 const std::string& input_device_id) { |
| 326 if (!core_audio_supported()) { | |
| 327 NOTIMPLEMENTED() | |
| 328 << "GetAssociatedOutputDeviceID is not supported on this OS"; | |
| 329 return std::string(); | |
| 330 } | |
| 331 return CoreAudioUtil::GetMatchingOutputDeviceID(input_device_id); | 297 return CoreAudioUtil::GetMatchingOutputDeviceID(input_device_id); |
| 332 } | 298 } |
| 333 | 299 |
| 334 const char* AudioManagerWin::GetName() { | 300 const char* AudioManagerWin::GetName() { |
| 335 return "Windows"; | 301 return "Windows"; |
| 336 } | 302 } |
| 337 | 303 |
| 338 // Factory for the implementations of AudioOutputStream for AUDIO_PCM_LINEAR | 304 // Factory for the implementations of AudioOutputStream for AUDIO_PCM_LINEAR |
| 339 // mode. | 305 // mode. |
| 340 // - PCMWaveOutAudioOutputStream: Based on the waveOut API. | 306 // - PCMWaveOutAudioOutputStream: Based on the waveOut API. |
| 341 AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( | 307 AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( |
| 342 const AudioParameters& params, | 308 const AudioParameters& params, |
| 343 const LogCallback& log_callback) { | 309 const LogCallback& log_callback) { |
| 344 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); | 310 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); |
| 345 if (params.channels() > kWinMaxChannels) | 311 if (params.channels() > kWinMaxChannels) |
| 346 return NULL; | 312 return NULL; |
| 347 | 313 |
| 348 return new PCMWaveOutAudioOutputStream(this, | 314 return new PCMWaveOutAudioOutputStream(this, params, NumberOfWaveOutBuffers(), |
| 349 params, | |
| 350 NumberOfWaveOutBuffers(), | |
| 351 WAVE_MAPPER); | 315 WAVE_MAPPER); |
| 352 } | 316 } |
| 353 | 317 |
| 354 // Factory for the implementations of AudioOutputStream for | 318 // Factory for the implementations of AudioOutputStream for |
| 355 // AUDIO_PCM_LOW_LATENCY mode. Two implementations should suffice most | 319 // AUDIO_PCM_LOW_LATENCY mode. Two implementations should suffice most |
| 356 // windows user's needs. | 320 // windows user's needs. |
| 357 // - PCMWaveOutAudioOutputStream: Based on the waveOut API. | 321 // - PCMWaveOutAudioOutputStream: Based on the waveOut API. |
| 358 // - WASAPIAudioOutputStream: Based on Core Audio (WASAPI) API. | 322 // - WASAPIAudioOutputStream: Based on Core Audio (WASAPI) API. |
| 359 AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( | 323 AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( |
| 360 const AudioParameters& params, | 324 const AudioParameters& params, |
| 361 const std::string& device_id, | 325 const std::string& device_id, |
| 362 const LogCallback& log_callback) { | 326 const LogCallback& log_callback) { |
| 363 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); | 327 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); |
| 364 if (params.channels() > kWinMaxChannels) | 328 if (params.channels() > kWinMaxChannels) |
| 365 return NULL; | 329 return NULL; |
| 366 | 330 |
| 367 if (!core_audio_supported()) { | |
| 368 // Fall back to Windows Wave implementation on Windows XP or lower. | |
| 369 DLOG_IF(ERROR, !device_id.empty() && | |
| 370 device_id != AudioDeviceDescription::kDefaultDeviceId) | |
| 371 << "Opening by device id not supported by PCMWaveOutAudioOutputStream"; | |
| 372 DVLOG(1) << "Using WaveOut since WASAPI requires at least Vista."; | |
| 373 return new PCMWaveOutAudioOutputStream( | |
| 374 this, params, NumberOfWaveOutBuffers(), WAVE_MAPPER); | |
| 375 } | |
| 376 | |
| 377 // Pass an empty string to indicate that we want the default device | 331 // Pass an empty string to indicate that we want the default device |
| 378 // since we consistently only check for an empty string in | 332 // since we consistently only check for an empty string in |
| 379 // WASAPIAudioOutputStream. | 333 // WASAPIAudioOutputStream. |
| 380 bool communications = | 334 bool communications = |
| 381 device_id == AudioDeviceDescription::kCommunicationsDeviceId; | 335 device_id == AudioDeviceDescription::kCommunicationsDeviceId; |
| 382 return new WASAPIAudioOutputStream( | 336 return new WASAPIAudioOutputStream( |
| 383 this, | 337 this, |
| 384 communications || device_id == AudioDeviceDescription::kDefaultDeviceId | 338 communications || device_id == AudioDeviceDescription::kDefaultDeviceId |
| 385 ? std::string() | 339 ? std::string() |
| 386 : device_id, | 340 : device_id, |
| 387 params, communications ? eCommunications : eConsole); | 341 params, communications ? eCommunications : eConsole); |
| 388 } | 342 } |
| 389 | 343 |
| 390 // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR | 344 // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR |
| 391 // mode. | 345 // mode. |
| 392 AudioInputStream* AudioManagerWin::MakeLinearInputStream( | 346 AudioInputStream* AudioManagerWin::MakeLinearInputStream( |
| 393 const AudioParameters& params, | 347 const AudioParameters& params, |
| 394 const std::string& device_id, | 348 const std::string& device_id, |
| 395 const LogCallback& log_callback) { | 349 const LogCallback& log_callback) { |
| 396 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); | 350 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); |
| 397 return CreatePCMWaveInAudioInputStream(params, device_id); | 351 return MakeLowLatencyInputStream(params, device_id, log_callback); |
| 398 } | 352 } |
| 399 | 353 |
| 400 // Factory for the implementations of AudioInputStream for | 354 // Factory for the implementations of AudioInputStream for |
| 401 // AUDIO_PCM_LOW_LATENCY mode. | 355 // AUDIO_PCM_LOW_LATENCY mode. |
| 402 AudioInputStream* AudioManagerWin::MakeLowLatencyInputStream( | 356 AudioInputStream* AudioManagerWin::MakeLowLatencyInputStream( |
| 403 const AudioParameters& params, | 357 const AudioParameters& params, |
| 404 const std::string& device_id, | 358 const std::string& device_id, |
| 405 const LogCallback& log_callback) { | 359 const LogCallback& log_callback) { |
| 406 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); | 360 // Used for both AUDIO_PCM_LOW_LATENCY and AUDIO_PCM_LINEAR. |
| 407 DVLOG(1) << "MakeLowLatencyInputStream: " << device_id; | 361 DVLOG(1) << "MakeLowLatencyInputStream: " << device_id; |
| 408 AudioInputStream* stream = NULL; | 362 return new WASAPIAudioInputStream(this, params, device_id); |
| 409 UMA_HISTOGRAM_BOOLEAN("Media.WindowsCoreAudioInput", core_audio_supported()); | |
| 410 if (!core_audio_supported()) { | |
| 411 // Fall back to Windows Wave implementation on Windows XP or lower. | |
| 412 DVLOG(1) << "Using WaveIn since WASAPI requires at least Vista."; | |
| 413 stream = CreatePCMWaveInAudioInputStream(params, device_id); | |
| 414 } else { | |
| 415 stream = new WASAPIAudioInputStream(this, params, device_id); | |
| 416 } | |
| 417 | |
| 418 return stream; | |
| 419 } | 363 } |
| 420 | 364 |
| 421 std::string AudioManagerWin::GetDefaultOutputDeviceID() { | 365 std::string AudioManagerWin::GetDefaultOutputDeviceID() { |
| 422 if (!core_audio_supported()) | |
| 423 return std::string(); | |
| 424 return CoreAudioUtil::GetDefaultOutputDeviceID(); | 366 return CoreAudioUtil::GetDefaultOutputDeviceID(); |
| 425 } | 367 } |
| 426 | 368 |
| 427 AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( | 369 AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( |
| 428 const std::string& output_device_id, | 370 const std::string& output_device_id, |
| 429 const AudioParameters& input_params) { | 371 const AudioParameters& input_params) { |
| 430 DLOG_IF(ERROR, !core_audio_supported() && !output_device_id.empty()) | |
| 431 << "CoreAudio is required to open non-default devices."; | |
| 432 | |
| 433 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | 372 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 434 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; | 373 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| 435 int sample_rate = 48000; | 374 int sample_rate = 48000; |
| 436 int buffer_size = kFallbackBufferSize; | 375 int buffer_size = kFallbackBufferSize; |
| 437 int bits_per_sample = 16; | 376 int bits_per_sample = 16; |
| 438 int effects = AudioParameters::NO_EFFECTS; | 377 int effects = AudioParameters::NO_EFFECTS; |
| 439 bool use_input_params = !core_audio_supported(); | 378 bool use_input_params = false; |
| 440 if (core_audio_supported()) { | 379 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { |
|
DaleCurtis
2017/01/24 21:09:30
Hmm, I wonder if we should remove the exclusive au
tommi (sloooow) - chröme
2017/01/24 21:34:56
Good question. Perhaps we could add a counter for
DaleCurtis
2017/01/24 21:47:26
If crashes are any indication, there are 9 reports
henrika (OOO until Aug 14)
2017/01/25 12:43:33
Smart to check crashes Dale ;-)
I added the suppo
tommi (sloooow) - chröme
2017/01/25 12:49:25
Added TODO for henrik to remove the switch and ass
| |
| 441 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { | 380 // TODO(rtoy): tune these values for best possible WebAudio |
| 442 // TODO(rtoy): tune these values for best possible WebAudio | 381 // performance. WebRTC works well at 48kHz and a buffer size of 480 |
| 443 // performance. WebRTC works well at 48kHz and a buffer size of 480 | 382 // samples will be used for this case. Note that exclusive mode is |
| 444 // samples will be used for this case. Note that exclusive mode is | 383 // experimental. This sample rate will be combined with a buffer size of |
| 445 // experimental. This sample rate will be combined with a buffer size of | 384 // 256 samples, which corresponds to an output delay of ~5.33ms. |
| 446 // 256 samples, which corresponds to an output delay of ~5.33ms. | 385 sample_rate = 48000; |
| 447 sample_rate = 48000; | 386 buffer_size = 256; |
| 448 buffer_size = 256; | 387 if (input_params.IsValid()) |
| 449 if (input_params.IsValid()) | 388 channel_layout = input_params.channel_layout(); |
| 450 channel_layout = input_params.channel_layout(); | 389 } else { |
| 390 AudioParameters params; | |
| 391 HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( | |
| 392 output_device_id.empty() ? GetDefaultOutputDeviceID() | |
| 393 : output_device_id, | |
| 394 true, ¶ms); | |
| 395 if (SUCCEEDED(hr)) { | |
| 396 bits_per_sample = params.bits_per_sample(); | |
| 397 buffer_size = params.frames_per_buffer(); | |
| 398 channel_layout = params.channel_layout(); | |
| 399 sample_rate = params.sample_rate(); | |
| 400 effects = params.effects(); | |
| 451 } else { | 401 } else { |
| 452 AudioParameters params; | 402 // TODO(tommi): This should never happen really and I'm not sure that |
| 453 HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( | 403 // setting use_input_params is the right thing to do since WASAPI i |
| 454 output_device_id.empty() ? GetDefaultOutputDeviceID() | 404 // definitely supported (see core_audio_supported() above) and |
| 455 : output_device_id, | 405 // |use_input_params| is only for cases when it isn't supported. |
| 456 true, ¶ms); | 406 DLOG(ERROR) << "GetPreferredAudioParameters failed: " << std::hex << hr; |
| 457 if (SUCCEEDED(hr)) { | 407 use_input_params = true; |
|
tommi (sloooow) - chröme
2017/01/24 21:10:51
this is another thing that I think we may have unn
DaleCurtis
2017/01/24 21:47:26
Since we're keeping WaveOut I think we still need
tommi (sloooow) - chröme
2017/01/25 12:49:25
Makes sense. Looking at the case where input_param
henrika (OOO until Aug 14)
2017/01/25 12:59:42
I kind of have the same feeling. It would probably
| |
| 458 bits_per_sample = params.bits_per_sample(); | |
| 459 buffer_size = params.frames_per_buffer(); | |
| 460 channel_layout = params.channel_layout(); | |
| 461 sample_rate = params.sample_rate(); | |
| 462 effects = params.effects(); | |
| 463 } else { | |
| 464 // TODO(tommi): This should never happen really and I'm not sure that | |
| 465 // setting use_input_params is the right thing to do since WASAPI i | |
| 466 // definitely supported (see core_audio_supported() above) and | |
| 467 // |use_input_params| is only for cases when it isn't supported. | |
| 468 DLOG(ERROR) << "GetPreferredAudioParameters failed: " << std::hex << hr; | |
| 469 use_input_params = true; | |
| 470 } | |
| 471 } | 408 } |
| 472 } | 409 } |
| 473 | 410 |
| 474 if (input_params.IsValid()) { | 411 if (input_params.IsValid()) { |
| 475 // If the user has enabled checking supported channel layouts or we don't | 412 // If the user has enabled checking supported channel layouts or we don't |
| 476 // have a valid channel layout yet, try to use the input layout. See bugs | 413 // have a valid channel layout yet, try to use the input layout. See bugs |
| 477 // http://crbug.com/259165 and http://crbug.com/311906 for more details. | 414 // http://crbug.com/259165 and http://crbug.com/311906 for more details. |
| 478 if (core_audio_supported() && | 415 if (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) || |
| 479 (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) || | 416 channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { |
| 480 channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)) { | |
| 481 // Check if it is possible to open up at the specified input channel | 417 // Check if it is possible to open up at the specified input channel |
| 482 // layout but avoid checking if the specified layout is the same as the | 418 // layout but avoid checking if the specified layout is the same as the |
| 483 // hardware (preferred) layout. We do this extra check to avoid the | 419 // hardware (preferred) layout. We do this extra check to avoid the |
| 484 // CoreAudioUtil::IsChannelLayoutSupported() overhead in most cases. | 420 // CoreAudioUtil::IsChannelLayoutSupported() overhead in most cases. |
| 485 if (input_params.channel_layout() != channel_layout) { | 421 if (input_params.channel_layout() != channel_layout) { |
| 486 // TODO(henrika): Internally, IsChannelLayoutSupported does many of the | 422 // TODO(henrika): Internally, IsChannelLayoutSupported does many of the |
| 487 // operations that have already been done such as opening up a client | 423 // operations that have already been done such as opening up a client |
| 488 // and fetching the WAVEFORMATPCMEX format. Ideally we should only do | 424 // and fetching the WAVEFORMATPCMEX format. Ideally we should only do |
| 489 // that once. Then here, we can check the layout from the data we | 425 // that once. Then here, we can check the layout from the data we |
| 490 // already hold. | 426 // already hold. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 517 int user_buffer_size = GetUserBufferSize(); | 453 int user_buffer_size = GetUserBufferSize(); |
| 518 if (user_buffer_size) | 454 if (user_buffer_size) |
| 519 buffer_size = user_buffer_size; | 455 buffer_size = user_buffer_size; |
| 520 | 456 |
| 521 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 457 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
| 522 sample_rate, bits_per_sample, buffer_size); | 458 sample_rate, bits_per_sample, buffer_size); |
| 523 params.set_effects(effects); | 459 params.set_effects(effects); |
| 524 return params; | 460 return params; |
| 525 } | 461 } |
| 526 | 462 |
| 527 AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream( | 463 // static |
| 528 const AudioParameters& params, | |
| 529 const std::string& device_id) { | |
| 530 std::string xp_device_id = device_id; | |
| 531 if (device_id != AudioDeviceDescription::kDefaultDeviceId && | |
| 532 enumeration_type_ == kMMDeviceEnumeration) { | |
| 533 xp_device_id = ConvertToWinXPInputDeviceId(device_id); | |
| 534 if (xp_device_id.empty()) { | |
| 535 DLOG(ERROR) << "Cannot find a waveIn device which matches the device ID " | |
| 536 << device_id; | |
| 537 return NULL; | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 return new PCMWaveInAudioInputStream(this, params, kNumInputBuffers, | |
| 542 xp_device_id); | |
| 543 } | |
| 544 | |
| 545 /// static | |
| 546 ScopedAudioManagerPtr CreateAudioManager( | 464 ScopedAudioManagerPtr CreateAudioManager( |
| 547 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 465 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 548 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, | 466 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, |
| 549 AudioLogFactory* audio_log_factory) { | 467 AudioLogFactory* audio_log_factory) { |
| 550 return ScopedAudioManagerPtr( | 468 return ScopedAudioManagerPtr( |
| 551 new AudioManagerWin(std::move(task_runner), std::move(worker_task_runner), | 469 new AudioManagerWin(std::move(task_runner), std::move(worker_task_runner), |
| 552 audio_log_factory)); | 470 audio_log_factory)); |
| 553 } | 471 } |
| 554 | 472 |
| 555 } // namespace media | 473 } // namespace media |
| OLD | NEW |