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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 } | 131 } |
| 132 | 132 |
| 133 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() { | 133 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() { |
| 134 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 134 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 135 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) | 135 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) |
| 136 return AUDCLNT_SHAREMODE_EXCLUSIVE; | 136 return AUDCLNT_SHAREMODE_EXCLUSIVE; |
| 137 return AUDCLNT_SHAREMODE_SHARED; | 137 return AUDCLNT_SHAREMODE_SHARED; |
| 138 } | 138 } |
| 139 | 139 |
| 140 int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) { | 140 int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) { |
| 141 DCHECK(CoreAudioUtil::IsSupported()); | 141 DCHECK(IsSupported()); |
| 142 // Create the IMMDeviceEnumerator interface. | 142 // Create the IMMDeviceEnumerator interface. |
| 143 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = | 143 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
| 144 CreateDeviceEnumerator(); | 144 CreateDeviceEnumerator(); |
| 145 if (!device_enumerator) | 145 if (!device_enumerator) |
| 146 return 0; | 146 return 0; |
| 147 | 147 |
| 148 // Generate a collection of active (present and not disabled) audio endpoint | 148 // Generate a collection of active (present and not disabled) audio endpoint |
| 149 // devices for the specified data-flow direction. | 149 // devices for the specified data-flow direction. |
| 150 // This method will succeed even if all devices are disabled. | 150 // This method will succeed even if all devices are disabled. |
| 151 ScopedComPtr<IMMDeviceCollection> collection; | 151 ScopedComPtr<IMMDeviceCollection> collection; |
| 152 HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow, | 152 HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow, |
| 153 DEVICE_STATE_ACTIVE, | 153 DEVICE_STATE_ACTIVE, |
| 154 collection.Receive()); | 154 collection.Receive()); |
| 155 if (FAILED(hr)) { | 155 if (FAILED(hr)) { |
| 156 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; | 156 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; |
| 157 return 0; | 157 return 0; |
| 158 } | 158 } |
| 159 | 159 |
| 160 // Retrieve the number of active audio devices for the specified direction | 160 // Retrieve the number of active audio devices for the specified direction |
| 161 UINT number_of_active_devices = 0; | 161 UINT number_of_active_devices = 0; |
| 162 collection->GetCount(&number_of_active_devices); | 162 collection->GetCount(&number_of_active_devices); |
| 163 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") | 163 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") |
| 164 << "number of devices: " << number_of_active_devices; | 164 << "number of devices: " << number_of_active_devices; |
| 165 return static_cast<int>(number_of_active_devices); | 165 return static_cast<int>(number_of_active_devices); |
| 166 } | 166 } |
| 167 | 167 |
| 168 ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { | 168 ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { |
| 169 DCHECK(CoreAudioUtil::IsSupported()); | 169 DCHECK(IsSupported()); |
| 170 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; | 170 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; |
| 171 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), | 171 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), |
| 172 NULL, | 172 NULL, |
| 173 CLSCTX_INPROC_SERVER, | 173 CLSCTX_INPROC_SERVER, |
| 174 __uuidof(IMMDeviceEnumerator), | 174 __uuidof(IMMDeviceEnumerator), |
| 175 device_enumerator.ReceiveVoid()); | 175 device_enumerator.ReceiveVoid()); |
| 176 // CO_E_NOTINITIALIZED is the most likely reason for failure and if that | 176 // CO_E_NOTINITIALIZED is the most likely reason for failure and if that |
| 177 // happens we might as well die here. | 177 // happens we might as well die here. |
| 178 CHECK(SUCCEEDED(hr)); | 178 CHECK(SUCCEEDED(hr)); |
| 179 return device_enumerator; | 179 return device_enumerator; |
| 180 } | 180 } |
| 181 | 181 |
| 182 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, | 182 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, |
| 183 ERole role) { | 183 ERole role) { |
| 184 DCHECK(CoreAudioUtil::IsSupported()); | 184 DCHECK(IsSupported()); |
| 185 ScopedComPtr<IMMDevice> endpoint_device; | 185 ScopedComPtr<IMMDevice> endpoint_device; |
| 186 | 186 |
| 187 // Create the IMMDeviceEnumerator interface. | 187 // Create the IMMDeviceEnumerator interface. |
| 188 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = | 188 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
| 189 CreateDeviceEnumerator(); | 189 CreateDeviceEnumerator(); |
| 190 if (!device_enumerator) | 190 if (!device_enumerator) |
| 191 return endpoint_device; | 191 return endpoint_device; |
| 192 | 192 |
| 193 // Retrieve the default audio endpoint for the specified data-flow | 193 // Retrieve the default audio endpoint for the specified data-flow |
| 194 // direction and role. | 194 // direction and role. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 209 if (!(state & DEVICE_STATE_ACTIVE)) { | 209 if (!(state & DEVICE_STATE_ACTIVE)) { |
| 210 DVLOG(1) << "Selected endpoint device is not active"; | 210 DVLOG(1) << "Selected endpoint device is not active"; |
| 211 endpoint_device.Release(); | 211 endpoint_device.Release(); |
| 212 } | 212 } |
| 213 } | 213 } |
| 214 return endpoint_device; | 214 return endpoint_device; |
| 215 } | 215 } |
| 216 | 216 |
| 217 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( | 217 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( |
| 218 const std::string& device_id) { | 218 const std::string& device_id) { |
| 219 DCHECK(CoreAudioUtil::IsSupported()); | 219 DCHECK(IsSupported()); |
| 220 ScopedComPtr<IMMDevice> endpoint_device; | 220 ScopedComPtr<IMMDevice> endpoint_device; |
| 221 | 221 |
| 222 // Create the IMMDeviceEnumerator interface. | 222 // Create the IMMDeviceEnumerator interface. |
| 223 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = | 223 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = |
| 224 CreateDeviceEnumerator(); | 224 CreateDeviceEnumerator(); |
| 225 if (!device_enumerator) | 225 if (!device_enumerator) |
| 226 return endpoint_device; | 226 return endpoint_device; |
| 227 | 227 |
| 228 // Retrieve an audio device specified by an endpoint device-identification | 228 // Retrieve an audio device specified by an endpoint device-identification |
| 229 // string. | 229 // string. |
| 230 HRESULT hr = device_enumerator->GetDevice(UTF8ToUTF16(device_id).c_str(), | 230 HRESULT hr = device_enumerator->GetDevice(UTF8ToUTF16(device_id).c_str(), |
| 231 endpoint_device.Receive()); | 231 endpoint_device.Receive()); |
| 232 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " | 232 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " |
| 233 << std::hex << hr; | 233 << std::hex << hr; |
| 234 return endpoint_device; | 234 return endpoint_device; |
| 235 } | 235 } |
| 236 | 236 |
| 237 HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { | 237 HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { |
| 238 DCHECK(CoreAudioUtil::IsSupported()); | 238 DCHECK(IsSupported()); |
| 239 | 239 |
| 240 // Retrieve unique name of endpoint device. | 240 // Retrieve unique name of endpoint device. |
| 241 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". | 241 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". |
| 242 AudioDeviceName device_name; | 242 AudioDeviceName device_name; |
| 243 ScopedCoMem<WCHAR> endpoint_device_id; | 243 ScopedCoMem<WCHAR> endpoint_device_id; |
| 244 HRESULT hr = device->GetId(&endpoint_device_id); | 244 HRESULT hr = device->GetId(&endpoint_device_id); |
| 245 if (FAILED(hr)) | 245 if (FAILED(hr)) |
| 246 return hr; | 246 return hr; |
| 247 WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id), | 247 WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id), |
| 248 &device_name.unique_id); | 248 &device_name.unique_id); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 263 &device_name.device_name); | 263 &device_name.device_name); |
| 264 } | 264 } |
| 265 | 265 |
| 266 *name = device_name; | 266 *name = device_name; |
| 267 DVLOG(2) << "friendly name: " << device_name.device_name; | 267 DVLOG(2) << "friendly name: " << device_name.device_name; |
| 268 DVLOG(2) << "unique id : " << device_name.unique_id; | 268 DVLOG(2) << "unique id : " << device_name.unique_id; |
| 269 return hr; | 269 return hr; |
| 270 } | 270 } |
| 271 | 271 |
| 272 std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { | 272 std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { |
| 273 DCHECK(CoreAudioUtil::IsSupported()); | 273 DCHECK(IsSupported()); |
| 274 ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); | 274 ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); |
| 275 if (!audio_device) | 275 if (!audio_device) |
| 276 return std::string(); | 276 return std::string(); |
| 277 | 277 |
| 278 AudioDeviceName device_name; | 278 AudioDeviceName device_name; |
| 279 HRESULT hr = GetDeviceName(audio_device, &device_name); | 279 HRESULT hr = GetDeviceName(audio_device, &device_name); |
| 280 if (FAILED(hr)) | 280 if (FAILED(hr)) |
| 281 return std::string(); | 281 return std::string(); |
| 282 | 282 |
| 283 return device_name.device_name; | 283 return device_name.device_name; |
| 284 } | 284 } |
| 285 | 285 |
| 286 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, | 286 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, |
| 287 ERole role, | 287 ERole role, |
| 288 std::string device_id) { | 288 std::string device_id) { |
| 289 DCHECK(CoreAudioUtil::IsSupported()); | 289 DCHECK(IsSupported()); |
| 290 ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role); | 290 ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role); |
| 291 if (!device) | 291 if (!device) |
| 292 return false; | 292 return false; |
| 293 | 293 |
| 294 ScopedCoMem<WCHAR> default_device_id; | 294 ScopedCoMem<WCHAR> default_device_id; |
| 295 HRESULT hr = device->GetId(&default_device_id); | 295 HRESULT hr = device->GetId(&default_device_id); |
| 296 if (FAILED(hr)) | 296 if (FAILED(hr)) |
| 297 return false; | 297 return false; |
| 298 | 298 |
| 299 std::string str_default; | 299 std::string str_default; |
| 300 WideToUTF8(default_device_id, wcslen(default_device_id), &str_default); | 300 WideToUTF8(default_device_id, wcslen(default_device_id), &str_default); |
| 301 if (device_id.compare(str_default) != 0) | 301 if (device_id.compare(str_default) != 0) |
| 302 return false; | 302 return false; |
| 303 return true; | 303 return true; |
| 304 } | 304 } |
| 305 | 305 |
| 306 EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { | 306 EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { |
| 307 DCHECK(CoreAudioUtil::IsSupported()); | 307 DCHECK(IsSupported()); |
| 308 ScopedComPtr<IMMEndpoint> endpoint; | 308 ScopedComPtr<IMMEndpoint> endpoint; |
| 309 HRESULT hr = device->QueryInterface(endpoint.Receive()); | 309 HRESULT hr = device->QueryInterface(endpoint.Receive()); |
| 310 if (FAILED(hr)) { | 310 if (FAILED(hr)) { |
| 311 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr; | 311 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr; |
| 312 return eAll; | 312 return eAll; |
| 313 } | 313 } |
| 314 | 314 |
| 315 EDataFlow data_flow; | 315 EDataFlow data_flow; |
| 316 hr = endpoint->GetDataFlow(&data_flow); | 316 hr = endpoint->GetDataFlow(&data_flow); |
| 317 if (FAILED(hr)) { | 317 if (FAILED(hr)) { |
| 318 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr; | 318 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr; |
| 319 return eAll; | 319 return eAll; |
| 320 } | 320 } |
| 321 return data_flow; | 321 return data_flow; |
| 322 } | 322 } |
| 323 | 323 |
| 324 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( | 324 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( |
| 325 IMMDevice* audio_device) { | 325 IMMDevice* audio_device) { |
| 326 DCHECK(CoreAudioUtil::IsSupported()); | 326 DCHECK(IsSupported()); |
| 327 | 327 |
| 328 // Creates and activates an IAudioClient COM object given the selected | 328 // Creates and activates an IAudioClient COM object given the selected |
| 329 // endpoint device. | 329 // endpoint device. |
| 330 ScopedComPtr<IAudioClient> audio_client; | 330 ScopedComPtr<IAudioClient> audio_client; |
| 331 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), | 331 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), |
| 332 CLSCTX_INPROC_SERVER, | 332 CLSCTX_INPROC_SERVER, |
| 333 NULL, | 333 NULL, |
| 334 audio_client.ReceiveVoid()); | 334 audio_client.ReceiveVoid()); |
| 335 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; | 335 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; |
| 336 return audio_client; | 336 return audio_client; |
| 337 } | 337 } |
| 338 | 338 |
| 339 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( | 339 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( |
| 340 EDataFlow data_flow, ERole role) { | 340 EDataFlow data_flow, ERole role) { |
| 341 DCHECK(CoreAudioUtil::IsSupported()); | 341 DCHECK(IsSupported()); |
| 342 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); | 342 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); |
| 343 return (default_device ? CreateClient(default_device) : | 343 return (default_device ? CreateClient(default_device) : |
| 344 ScopedComPtr<IAudioClient>()); | 344 ScopedComPtr<IAudioClient>()); |
| 345 } | 345 } |
| 346 | 346 |
| 347 HRESULT CoreAudioUtil::GetSharedModeMixFormat( | 347 HRESULT CoreAudioUtil::GetSharedModeMixFormat( |
| 348 IAudioClient* client, WAVEFORMATPCMEX* format) { | 348 IAudioClient* client, WAVEFORMATPCMEX* format) { |
| 349 DCHECK(CoreAudioUtil::IsSupported()); | 349 DCHECK(IsSupported()); |
| 350 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; | 350 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; |
| 351 HRESULT hr = client->GetMixFormat( | 351 HRESULT hr = client->GetMixFormat( |
| 352 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); | 352 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); |
| 353 if (FAILED(hr)) | 353 if (FAILED(hr)) |
| 354 return hr; | 354 return hr; |
| 355 | 355 |
| 356 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; | 356 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; |
| 357 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); | 357 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); |
| 358 | 358 |
| 359 memcpy(format, format_pcmex, bytes); | 359 memcpy(format, format_pcmex, bytes); |
| 360 | 360 |
| 361 DVLOG(2) << "wFormatTag: 0x" << std::hex << format->Format.wFormatTag | 361 DVLOG(2) << "wFormatTag: 0x" << std::hex << format->Format.wFormatTag |
| 362 << ", nChannels: " << std::dec << format->Format.nChannels | 362 << ", nChannels: " << std::dec << format->Format.nChannels |
| 363 << ", nSamplesPerSec: " << format->Format.nSamplesPerSec | 363 << ", nSamplesPerSec: " << format->Format.nSamplesPerSec |
| 364 << ", nAvgBytesPerSec: " << format->Format.nAvgBytesPerSec | 364 << ", nAvgBytesPerSec: " << format->Format.nAvgBytesPerSec |
| 365 << ", nBlockAlign: " << format->Format.nBlockAlign | 365 << ", nBlockAlign: " << format->Format.nBlockAlign |
| 366 << ", wBitsPerSample: " << format->Format.wBitsPerSample | 366 << ", wBitsPerSample: " << format->Format.wBitsPerSample |
| 367 << ", cbSize: " << format->Format.cbSize | 367 << ", cbSize: " << format->Format.cbSize |
| 368 << ", wValidBitsPerSample: " << format->Samples.wValidBitsPerSample | 368 << ", wValidBitsPerSample: " << format->Samples.wValidBitsPerSample |
| 369 << ", dwChannelMask: 0x" << std::hex << format->dwChannelMask; | 369 << ", dwChannelMask: 0x" << std::hex << format->dwChannelMask; |
| 370 | 370 |
| 371 return hr; | 371 return hr; |
| 372 } | 372 } |
| 373 | 373 |
| 374 HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( | |
| 375 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { | |
|
tommi (sloooow) - chröme
2013/01/31 16:18:24
indent
henrika (OOO until Aug 14)
2013/02/01 10:55:56
Done.
| |
| 376 DCHECK(IsSupported()); | |
| 377 ScopedComPtr<IAudioClient> client; | |
|
tommi (sloooow) - chröme
2013/01/31 16:18:24
pass CreateDefaultClient to the constructor
henrika (OOO until Aug 14)
2013/02/01 10:55:56
Done.
| |
| 378 client = CreateDefaultClient(data_flow, role); | |
| 379 if (!client) { | |
| 380 // Map NULL-pointer to new error code which can be different from the | |
| 381 // actual error code. The exact value is not important here. | |
| 382 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; | |
| 383 } | |
| 384 return CoreAudioUtil::GetSharedModeMixFormat(client, format); | |
| 385 } | |
| 386 | |
| 374 bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, | 387 bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, |
| 375 AUDCLNT_SHAREMODE share_mode, | 388 AUDCLNT_SHAREMODE share_mode, |
| 376 const WAVEFORMATPCMEX* format) { | 389 const WAVEFORMATPCMEX* format) { |
| 377 DCHECK(CoreAudioUtil::IsSupported()); | 390 DCHECK(IsSupported()); |
| 378 ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match; | 391 ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match; |
| 379 HRESULT hr = client->IsFormatSupported( | 392 HRESULT hr = client->IsFormatSupported( |
| 380 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), | 393 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), |
| 381 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); | 394 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); |
| 382 | 395 |
| 383 // This log can only be triggered for shared mode. | 396 // This log can only be triggered for shared mode. |
| 384 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " | 397 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " |
| 385 << "but a closest match exists."; | 398 << "but a closest match exists."; |
| 386 // This log can be triggered both for shared and exclusive modes. | 399 // This log can be triggered both for shared and exclusive modes. |
| 387 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; | 400 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; |
| 388 if (hr == S_FALSE) { | 401 if (hr == S_FALSE) { |
| 389 DVLOG(2) << "wFormatTag: " << closest_match->Format.wFormatTag | 402 DVLOG(2) << "wFormatTag: " << closest_match->Format.wFormatTag |
| 390 << ", nChannels: " << closest_match->Format.nChannels | 403 << ", nChannels: " << closest_match->Format.nChannels |
| 391 << ", nSamplesPerSec: " << closest_match->Format.nSamplesPerSec | 404 << ", nSamplesPerSec: " << closest_match->Format.nSamplesPerSec |
| 392 << ", wBitsPerSample: " << closest_match->Format.wBitsPerSample; | 405 << ", wBitsPerSample: " << closest_match->Format.wBitsPerSample; |
| 393 } | 406 } |
| 394 | 407 |
| 395 return (hr == S_OK); | 408 return (hr == S_OK); |
| 396 } | 409 } |
| 397 | 410 |
| 398 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, | 411 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, |
| 399 AUDCLNT_SHAREMODE share_mode, | 412 AUDCLNT_SHAREMODE share_mode, |
| 400 REFERENCE_TIME* device_period) { | 413 REFERENCE_TIME* device_period) { |
| 401 DCHECK(CoreAudioUtil::IsSupported()); | 414 DCHECK(IsSupported()); |
| 402 | 415 |
| 403 // Get the period of the engine thread. | 416 // Get the period of the engine thread. |
| 404 REFERENCE_TIME default_period = 0; | 417 REFERENCE_TIME default_period = 0; |
| 405 REFERENCE_TIME minimum_period = 0; | 418 REFERENCE_TIME minimum_period = 0; |
| 406 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); | 419 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); |
| 407 if (FAILED(hr)) | 420 if (FAILED(hr)) |
| 408 return hr; | 421 return hr; |
| 409 | 422 |
| 410 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : | 423 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : |
| 411 minimum_period; | 424 minimum_period; |
| 412 DVLOG(2) << "device_period: " | 425 DVLOG(2) << "device_period: " |
| 413 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() | 426 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() |
| 414 << " [ms]"; | 427 << " [ms]"; |
| 415 return hr; | 428 return hr; |
| 416 } | 429 } |
| 417 | 430 |
| 418 HRESULT CoreAudioUtil::GetPreferredAudioParameters( | 431 HRESULT CoreAudioUtil::GetPreferredAudioParameters( |
| 419 IAudioClient* client, AudioParameters* params) { | 432 IAudioClient* client, AudioParameters* params) { |
| 420 DCHECK(CoreAudioUtil::IsSupported()); | 433 DCHECK(IsSupported()); |
| 421 WAVEFORMATPCMEX format; | 434 WAVEFORMATPCMEX format; |
| 422 HRESULT hr = GetSharedModeMixFormat(client, &format); | 435 HRESULT hr = GetSharedModeMixFormat(client, &format); |
| 423 if (FAILED(hr)) | 436 if (FAILED(hr)) |
| 424 return hr; | 437 return hr; |
| 425 | 438 |
| 426 REFERENCE_TIME default_period = 0; | 439 REFERENCE_TIME default_period = 0; |
| 427 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); | 440 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); |
| 428 if (FAILED(hr)) | 441 if (FAILED(hr)) |
| 429 return hr; | 442 return hr; |
| 430 | 443 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 461 sample_rate, | 474 sample_rate, |
| 462 bits_per_sample, | 475 bits_per_sample, |
| 463 frames_per_buffer); | 476 frames_per_buffer); |
| 464 | 477 |
| 465 *params = audio_params; | 478 *params = audio_params; |
| 466 return hr; | 479 return hr; |
| 467 } | 480 } |
| 468 | 481 |
| 469 HRESULT CoreAudioUtil::GetPreferredAudioParameters( | 482 HRESULT CoreAudioUtil::GetPreferredAudioParameters( |
| 470 EDataFlow data_flow, ERole role, AudioParameters* params) { | 483 EDataFlow data_flow, ERole role, AudioParameters* params) { |
| 471 DCHECK(CoreAudioUtil::IsSupported()); | 484 DCHECK(IsSupported()); |
| 472 | |
| 473 ScopedComPtr<IAudioClient> client = CreateDefaultClient(data_flow, role); | 485 ScopedComPtr<IAudioClient> client = CreateDefaultClient(data_flow, role); |
| 474 if (!client) { | 486 if (!client) { |
| 475 // Map NULL-pointer to new error code which can be different from the | 487 // Map NULL-pointer to new error code which can be different from the |
| 476 // actual error code. The exact value is not important here. | 488 // actual error code. The exact value is not important here. |
| 477 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; | 489 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; |
| 478 } | 490 } |
| 479 return GetPreferredAudioParameters(client, params); | 491 return GetPreferredAudioParameters(client, params); |
| 480 } | 492 } |
| 481 | 493 |
| 482 HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client, | 494 HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client, |
| 483 const WAVEFORMATPCMEX* format, | 495 const WAVEFORMATPCMEX* format, |
| 484 HANDLE event_handle, | 496 HANDLE event_handle, |
| 485 size_t* endpoint_buffer_size) { | 497 size_t* endpoint_buffer_size) { |
| 486 DCHECK(CoreAudioUtil::IsSupported()); | 498 DCHECK(IsSupported()); |
| 487 | |
| 488 DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST; | 499 DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST; |
| 489 | 500 |
| 490 // Enable event-driven streaming if a valid event handle is provided. | 501 // Enable event-driven streaming if a valid event handle is provided. |
| 491 // After the stream starts, the audio engine will signal the event handle | 502 // After the stream starts, the audio engine will signal the event handle |
| 492 // to notify the client each time a buffer becomes ready to process. | 503 // to notify the client each time a buffer becomes ready to process. |
| 493 // Event-driven buffering is supported for both rendering and capturing. | 504 // Event-driven buffering is supported for both rendering and capturing. |
| 494 // Both shared-mode and exclusive-mode streams can use event-driven buffering. | 505 // Both shared-mode and exclusive-mode streams can use event-driven buffering. |
| 495 bool use_event = (event_handle != NULL && | 506 bool use_event = (event_handle != NULL && |
| 496 event_handle != INVALID_HANDLE_VALUE); | 507 event_handle != INVALID_HANDLE_VALUE); |
| 497 if (use_event) | 508 if (use_event) |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 531 // TODO(henrika): utilize when delay measurements are added. | 542 // TODO(henrika): utilize when delay measurements are added. |
| 532 REFERENCE_TIME latency = 0; | 543 REFERENCE_TIME latency = 0; |
| 533 hr = client->GetStreamLatency(&latency); | 544 hr = client->GetStreamLatency(&latency); |
| 534 DVLOG(2) << "stream latency: " | 545 DVLOG(2) << "stream latency: " |
| 535 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; | 546 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; |
| 536 return hr; | 547 return hr; |
| 537 } | 548 } |
| 538 | 549 |
| 539 ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( | 550 ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( |
| 540 IAudioClient* client) { | 551 IAudioClient* client) { |
| 541 DCHECK(CoreAudioUtil::IsSupported()); | 552 DCHECK(IsSupported()); |
| 542 | 553 |
| 543 // Get access to the IAudioRenderClient interface. This interface | 554 // Get access to the IAudioRenderClient interface. This interface |
| 544 // enables us to write output data to a rendering endpoint buffer. | 555 // enables us to write output data to a rendering endpoint buffer. |
| 545 ScopedComPtr<IAudioRenderClient> audio_render_client; | 556 ScopedComPtr<IAudioRenderClient> audio_render_client; |
| 546 HRESULT hr = client->GetService(__uuidof(IAudioRenderClient), | 557 HRESULT hr = client->GetService(__uuidof(IAudioRenderClient), |
| 547 audio_render_client.ReceiveVoid()); | 558 audio_render_client.ReceiveVoid()); |
| 548 if (FAILED(hr)) { | 559 if (FAILED(hr)) { |
| 549 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; | 560 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; |
| 550 return ScopedComPtr<IAudioRenderClient>(); | 561 return ScopedComPtr<IAudioRenderClient>(); |
| 551 } | 562 } |
| 552 | |
| 553 // TODO(henrika): verify that this scheme is the same for shared mode and | |
| 554 // exclusive mode streams. | |
| 555 | |
| 556 // Avoid start-up glitches by filling up the endpoint buffer with "silence" | |
| 557 // before starting the stream. | |
| 558 UINT32 endpoint_buffer_size = 0; | |
| 559 hr = client->GetBufferSize(&endpoint_buffer_size); | |
| 560 DVLOG_IF(1, FAILED(hr)) << "IAudioClient::GetBufferSize: " << std::hex << hr; | |
| 561 | |
| 562 BYTE* data = NULL; | |
| 563 hr = audio_render_client->GetBuffer(endpoint_buffer_size, &data); | |
| 564 DVLOG_IF(1, FAILED(hr)) << "IAudioRenderClient::GetBuffer: " | |
| 565 << std::hex << hr; | |
| 566 if (SUCCEEDED(hr)) { | |
| 567 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to | |
| 568 // explicitly write silence data to the rendering buffer. | |
| 569 hr = audio_render_client->ReleaseBuffer(endpoint_buffer_size, | |
| 570 AUDCLNT_BUFFERFLAGS_SILENT); | |
| 571 DVLOG_IF(1, FAILED(hr)) << "IAudioRenderClient::ReleaseBuffer: " | |
| 572 << std::hex << hr; | |
| 573 } | |
| 574 | |
| 575 // Sanity check: verify that the endpoint buffer is filled with silence. | |
| 576 UINT32 num_queued_frames = 0; | |
| 577 client->GetCurrentPadding(&num_queued_frames); | |
| 578 DCHECK(num_queued_frames == endpoint_buffer_size); | |
| 579 | |
| 580 return audio_render_client; | 563 return audio_render_client; |
| 581 } | 564 } |
| 582 | 565 |
| 583 ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient( | 566 ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient( |
| 584 IAudioClient* client) { | 567 IAudioClient* client) { |
| 585 DCHECK(CoreAudioUtil::IsSupported()); | 568 DCHECK(IsSupported()); |
| 586 | 569 |
| 587 // Get access to the IAudioCaptureClient interface. This interface | 570 // Get access to the IAudioCaptureClient interface. This interface |
| 588 // enables us to read input data from a capturing endpoint buffer. | 571 // enables us to read input data from a capturing endpoint buffer. |
| 589 ScopedComPtr<IAudioCaptureClient> audio_capture_client; | 572 ScopedComPtr<IAudioCaptureClient> audio_capture_client; |
| 590 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), | 573 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), |
| 591 audio_capture_client.ReceiveVoid()); | 574 audio_capture_client.ReceiveVoid()); |
| 592 if (FAILED(hr)) { | 575 if (FAILED(hr)) { |
| 593 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; | 576 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; |
| 594 return ScopedComPtr<IAudioCaptureClient>(); | 577 return ScopedComPtr<IAudioCaptureClient>(); |
| 595 } | 578 } |
| 596 return audio_capture_client; | 579 return audio_capture_client; |
| 597 } | 580 } |
| 598 | 581 |
| 582 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( | |
| 583 IAudioClient* client, IAudioRenderClient* render_client) { | |
| 584 DCHECK(IsSupported()); | |
| 585 | |
| 586 UINT32 endpoint_buffer_size = 0; | |
| 587 if (FAILED(client->GetBufferSize(&endpoint_buffer_size))) | |
| 588 return false; | |
| 589 | |
| 590 UINT32 num_queued_frames = 0; | |
| 591 if (FAILED(client->GetCurrentPadding(&num_queued_frames))) | |
| 592 return false; | |
| 593 | |
| 594 BYTE* data = NULL; | |
| 595 int num_frames_to_fill = endpoint_buffer_size - num_queued_frames; | |
| 596 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data))) | |
| 597 return false; | |
| 598 | |
| 599 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to | |
| 600 // explicitly write silence data to the rendering buffer. | |
| 601 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; | |
| 602 if (FAILED(render_client->ReleaseBuffer(num_frames_to_fill, | |
|
tommi (sloooow) - chröme
2013/01/31 16:18:24
return SUCCEEDED(render_client->ReleaseBuffer(...)
henrika (OOO until Aug 14)
2013/02/01 10:55:56
Done.
| |
| 603 AUDCLNT_BUFFERFLAGS_SILENT))) | |
| 604 return false; | |
| 605 return true; | |
| 606 } | |
| 607 | |
| 599 } // namespace media | 608 } // namespace media |
| OLD | NEW |