| 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_low_latency_input_win.h" | 5 #include "media/audio/win/audio_low_latency_input_win.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 opened_(false), | 28 opened_(false), |
| 29 started_(false), | 29 started_(false), |
| 30 frame_size_(0), | 30 frame_size_(0), |
| 31 packet_size_frames_(0), | 31 packet_size_frames_(0), |
| 32 packet_size_bytes_(0), | 32 packet_size_bytes_(0), |
| 33 endpoint_buffer_size_frames_(0), | 33 endpoint_buffer_size_frames_(0), |
| 34 device_id_(device_id), | 34 device_id_(device_id), |
| 35 perf_count_to_100ns_units_(0.0), | 35 perf_count_to_100ns_units_(0.0), |
| 36 ms_to_frame_count_(0.0), | 36 ms_to_frame_count_(0.0), |
| 37 sink_(NULL), | 37 sink_(NULL), |
| 38 audio_bus_(media::AudioBus::Create(params)) { | 38 audio_bus_(media::AudioBus::Create(params)), |
| 39 mute_done_(false) { |
| 39 DCHECK(manager_); | 40 DCHECK(manager_); |
| 40 | 41 |
| 41 // Load the Avrt DLL if not already loaded. Required to support MMCSS. | 42 // Load the Avrt DLL if not already loaded. Required to support MMCSS. |
| 42 bool avrt_init = avrt::Initialize(); | 43 bool avrt_init = avrt::Initialize(); |
| 43 DCHECK(avrt_init) << "Failed to load the Avrt.dll"; | 44 DCHECK(avrt_init) << "Failed to load the Avrt.dll"; |
| 44 | 45 |
| 45 // Set up the desired capture format specified by the client. | 46 // Set up the desired capture format specified by the client. |
| 46 format_.nSamplesPerSec = params.sample_rate(); | 47 format_.nSamplesPerSec = params.sample_rate(); |
| 47 format_.wFormatTag = WAVE_FORMAT_PCM; | 48 format_.wFormatTag = WAVE_FORMAT_PCM; |
| 48 format_.wBitsPerSample = params.bits_per_sample(); | 49 format_.wBitsPerSample = params.bits_per_sample(); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 HRESULT hr = SetCaptureDevice(); | 99 HRESULT hr = SetCaptureDevice(); |
| 99 if (FAILED(hr)) | 100 if (FAILED(hr)) |
| 100 return false; | 101 return false; |
| 101 | 102 |
| 102 // Obtain an IAudioClient interface which enables us to create and initialize | 103 // Obtain an IAudioClient interface which enables us to create and initialize |
| 103 // an audio stream between an audio application and the audio engine. | 104 // an audio stream between an audio application and the audio engine. |
| 104 hr = ActivateCaptureDevice(); | 105 hr = ActivateCaptureDevice(); |
| 105 if (FAILED(hr)) | 106 if (FAILED(hr)) |
| 106 return false; | 107 return false; |
| 107 | 108 |
| 108 // Retrieve the stream format which the audio engine uses for its internal | 109 // Retrieve the stream format which the audio engine uses for its internal |
| 109 // processing/mixing of shared-mode streams. This function call is for | 110 // processing/mixing of shared-mode streams. This function call is for |
| 110 // diagnostic purposes only and only in debug mode. | 111 // diagnostic purposes only and only in debug mode. |
| 111 #ifndef NDEBUG | 112 #ifndef NDEBUG |
| 112 hr = GetAudioEngineStreamFormat(); | 113 hr = GetAudioEngineStreamFormat(); |
| 113 #endif | 114 #endif |
| 114 | 115 |
| 115 // Verify that the selected audio endpoint supports the specified format | 116 // Verify that the selected audio endpoint supports the specified format |
| 116 // set during construction. | 117 // set during construction. |
| 117 if (!DesiredFormatIsSupported()) | 118 if (!DesiredFormatIsSupported()) |
| 118 return false; | 119 return false; |
| 119 | 120 |
| 120 // Initialize the audio stream between the client and the device using | 121 // Initialize the audio stream between the client and the device using |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 | 159 |
| 159 started_ = SUCCEEDED(hr); | 160 started_ = SUCCEEDED(hr); |
| 160 } | 161 } |
| 161 | 162 |
| 162 void WASAPIAudioInputStream::Stop() { | 163 void WASAPIAudioInputStream::Stop() { |
| 163 DCHECK(CalledOnValidThread()); | 164 DCHECK(CalledOnValidThread()); |
| 164 DVLOG(1) << "WASAPIAudioInputStream::Stop()"; | 165 DVLOG(1) << "WASAPIAudioInputStream::Stop()"; |
| 165 if (!started_) | 166 if (!started_) |
| 166 return; | 167 return; |
| 167 | 168 |
| 169 // We have muted system audio for capturing, so we need to unmute it when |
| 170 // capturing stops. |
| 171 if (device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId && |
| 172 mute_done_) { |
| 173 DCHECK(system_audio_volume_); |
| 174 if (system_audio_volume_) { |
| 175 system_audio_volume_->SetMute(false, NULL); |
| 176 mute_done_ = false; |
| 177 } |
| 178 } |
| 179 |
| 168 // Stops periodic AGC microphone measurements. | 180 // Stops periodic AGC microphone measurements. |
| 169 StopAgc(); | 181 StopAgc(); |
| 170 | 182 |
| 171 // Shut down the capture thread. | 183 // Shut down the capture thread. |
| 172 if (stop_capture_event_.IsValid()) { | 184 if (stop_capture_event_.IsValid()) { |
| 173 SetEvent(stop_capture_event_.Get()); | 185 SetEvent(stop_capture_event_.Get()); |
| 174 } | 186 } |
| 175 | 187 |
| 176 // Stop the input audio streaming. | 188 // Stop the input audio streaming. |
| 177 HRESULT hr = audio_client_->Stop(); | 189 HRESULT hr = audio_client_->Stop(); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 266 | 278 |
| 267 return is_muted != FALSE; | 279 return is_muted != FALSE; |
| 268 } | 280 } |
| 269 | 281 |
| 270 void WASAPIAudioInputStream::Run() { | 282 void WASAPIAudioInputStream::Run() { |
| 271 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); | 283 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); |
| 272 | 284 |
| 273 // Enable MMCSS to ensure that this thread receives prioritized access to | 285 // Enable MMCSS to ensure that this thread receives prioritized access to |
| 274 // CPU resources. | 286 // CPU resources. |
| 275 DWORD task_index = 0; | 287 DWORD task_index = 0; |
| 276 HANDLE mm_task = avrt::AvSetMmThreadCharacteristics(L"Pro Audio", | 288 HANDLE mm_task = |
| 277 &task_index); | 289 avrt::AvSetMmThreadCharacteristics(L"Pro Audio", &task_index); |
| 278 bool mmcss_is_ok = | 290 bool mmcss_is_ok = |
| 279 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL)); | 291 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL)); |
| 280 if (!mmcss_is_ok) { | 292 if (!mmcss_is_ok) { |
| 281 // Failed to enable MMCSS on this thread. It is not fatal but can lead | 293 // Failed to enable MMCSS on this thread. It is not fatal but can lead |
| 282 // to reduced QoS at high load. | 294 // to reduced QoS at high load. |
| 283 DWORD err = GetLastError(); | 295 DWORD err = GetLastError(); |
| 284 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; | 296 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; |
| 285 } | 297 } |
| 286 | 298 |
| 287 // Allocate a buffer with a size that enables us to take care of cases like: | 299 // Allocate a buffer with a size that enables us to take care of cases like: |
| 288 // 1) The recorded buffer size is smaller, or does not match exactly with, | 300 // 1) The recorded buffer size is smaller, or does not match exactly with, |
| 289 // the selected packet size used in each callback. | 301 // the selected packet size used in each callback. |
| 290 // 2) The selected buffer size is larger than the recorded buffer size in | 302 // 2) The selected buffer size is larger than the recorded buffer size in |
| 291 // each event. | 303 // each event. |
| 292 size_t buffer_frame_index = 0; | 304 size_t buffer_frame_index = 0; |
| 293 size_t capture_buffer_size = std::max( | 305 size_t capture_buffer_size = |
| 294 2 * endpoint_buffer_size_frames_ * frame_size_, | 306 std::max(2 * endpoint_buffer_size_frames_ * frame_size_, |
| 295 2 * packet_size_frames_ * frame_size_); | 307 2 * packet_size_frames_ * frame_size_); |
| 296 std::unique_ptr<uint8_t[]> capture_buffer(new uint8_t[capture_buffer_size]); | 308 std::unique_ptr<uint8_t[]> capture_buffer(new uint8_t[capture_buffer_size]); |
| 297 | 309 |
| 298 LARGE_INTEGER now_count = {}; | 310 LARGE_INTEGER now_count = {}; |
| 299 bool recording = true; | 311 bool recording = true; |
| 300 bool error = false; | 312 bool error = false; |
| 301 double volume = GetVolume(); | 313 double volume = GetVolume(); |
| 302 HANDLE wait_array[2] = | 314 HANDLE wait_array[2] = {stop_capture_event_.Get(), |
| 303 { stop_capture_event_.Get(), audio_samples_ready_event_.Get() }; | 315 audio_samples_ready_event_.Get()}; |
| 304 | 316 |
| 305 base::win::ScopedComPtr<IAudioClock> audio_clock; | 317 base::win::ScopedComPtr<IAudioClock> audio_clock; |
| 306 audio_client_->GetService(__uuidof(IAudioClock), audio_clock.ReceiveVoid()); | 318 audio_client_->GetService(__uuidof(IAudioClock), audio_clock.ReceiveVoid()); |
| 307 | 319 |
| 308 while (recording && !error) { | 320 while (recording && !error) { |
| 309 HRESULT hr = S_FALSE; | 321 HRESULT hr = S_FALSE; |
| 310 | 322 |
| 311 // Wait for a close-down event or a new capture event. | 323 // Wait for a close-down event or a new capture event. |
| 312 DWORD wait_result = WaitForMultipleObjects(2, wait_array, FALSE, INFINITE); | 324 DWORD wait_result = WaitForMultipleObjects(2, wait_array, FALSE, INFINITE); |
| 313 switch (wait_result) { | 325 switch (wait_result) { |
| 314 case WAIT_FAILED: | 326 case WAIT_FAILED: |
| 315 error = true; | 327 error = true; |
| 316 break; | 328 break; |
| 317 case WAIT_OBJECT_0 + 0: | 329 case WAIT_OBJECT_0 + 0: |
| 318 // |stop_capture_event_| has been set. | 330 // |stop_capture_event_| has been set. |
| 319 recording = false; | 331 recording = false; |
| 320 break; | 332 break; |
| 321 case WAIT_OBJECT_0 + 1: | 333 case WAIT_OBJECT_0 + 1: { |
| 322 { | 334 TRACE_EVENT0("audio", "WASAPIAudioInputStream::Run_0"); |
| 323 TRACE_EVENT0("audio", "WASAPIAudioInputStream::Run_0"); | 335 // |audio_samples_ready_event_| has been set. |
| 324 // |audio_samples_ready_event_| has been set. | 336 BYTE* data_ptr = NULL; |
| 325 BYTE* data_ptr = NULL; | 337 UINT32 num_frames_to_read = 0; |
| 326 UINT32 num_frames_to_read = 0; | 338 DWORD flags = 0; |
| 327 DWORD flags = 0; | 339 UINT64 device_position = 0; |
| 328 UINT64 device_position = 0; | 340 UINT64 first_audio_frame_timestamp = 0; |
| 329 UINT64 first_audio_frame_timestamp = 0; | 341 |
| 330 | 342 // Retrieve the amount of data in the capture endpoint buffer, |
| 331 // Retrieve the amount of data in the capture endpoint buffer, | 343 // replace it with silence if required, create callbacks for each |
| 332 // replace it with silence if required, create callbacks for each | 344 // packet and store non-delivered data for the next event. |
| 333 // packet and store non-delivered data for the next event. | 345 hr = audio_capture_client_->GetBuffer(&data_ptr, &num_frames_to_read, |
| 334 hr = audio_capture_client_->GetBuffer(&data_ptr, | 346 &flags, &device_position, |
| 335 &num_frames_to_read, | 347 &first_audio_frame_timestamp); |
| 336 &flags, | 348 if (FAILED(hr)) { |
| 337 &device_position, | 349 DLOG(ERROR) << "Failed to get data from the capture buffer"; |
| 338 &first_audio_frame_timestamp); | 350 continue; |
| 339 if (FAILED(hr)) { | 351 } |
| 340 DLOG(ERROR) << "Failed to get data from the capture buffer"; | 352 |
| 341 continue; | 353 if (audio_clock) { |
| 354 // The reported timestamp from GetBuffer is not as reliable as the |
| 355 // clock from the client. We've seen timestamps reported for |
| 356 // USB audio devices, be off by several days. Furthermore we've |
| 357 // seen them jump back in time every 2 seconds or so. |
| 358 audio_clock->GetPosition(&device_position, |
| 359 &first_audio_frame_timestamp); |
| 360 } |
| 361 |
| 362 if (num_frames_to_read != 0) { |
| 363 size_t pos = buffer_frame_index * frame_size_; |
| 364 size_t num_bytes = num_frames_to_read * frame_size_; |
| 365 DCHECK_GE(capture_buffer_size, pos + num_bytes); |
| 366 |
| 367 if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { |
| 368 // Clear out the local buffer since silence is reported. |
| 369 memset(&capture_buffer[pos], 0, num_bytes); |
| 370 } else { |
| 371 // Copy captured data from audio engine buffer to local buffer. |
| 372 memcpy(&capture_buffer[pos], data_ptr, num_bytes); |
| 342 } | 373 } |
| 343 | 374 |
| 344 if (audio_clock) { | 375 buffer_frame_index += num_frames_to_read; |
| 345 // The reported timestamp from GetBuffer is not as reliable as the | 376 } |
| 346 // clock from the client. We've seen timestamps reported for | 377 |
| 347 // USB audio devices, be off by several days. Furthermore we've | 378 hr = audio_capture_client_->ReleaseBuffer(num_frames_to_read); |
| 348 // seen them jump back in time every 2 seconds or so. | 379 DLOG_IF(ERROR, FAILED(hr)) << "Failed to release capture buffer"; |
| 349 audio_clock->GetPosition( | 380 |
| 350 &device_position, &first_audio_frame_timestamp); | 381 // Derive a delay estimate for the captured audio packet. |
| 351 } | 382 // The value contains two parts (A+B), where A is the delay of the |
| 352 | 383 // first audio frame in the packet and B is the extra delay |
| 353 | 384 // contained in any stored data. Unit is in audio frames. |
| 354 if (num_frames_to_read != 0) { | 385 QueryPerformanceCounter(&now_count); |
| 355 size_t pos = buffer_frame_index * frame_size_; | 386 // first_audio_frame_timestamp will be 0 if we didn't get a timestamp. |
| 356 size_t num_bytes = num_frames_to_read * frame_size_; | 387 double audio_delay_frames = |
| 357 DCHECK_GE(capture_buffer_size, pos + num_bytes); | 388 first_audio_frame_timestamp == 0 |
| 358 | 389 ? num_frames_to_read |
| 359 if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { | 390 : ((perf_count_to_100ns_units_ * now_count.QuadPart - |
| 360 // Clear out the local buffer since silence is reported. | 391 first_audio_frame_timestamp) / |
| 361 memset(&capture_buffer[pos], 0, num_bytes); | 392 10000.0) * |
| 362 } else { | 393 ms_to_frame_count_ + |
| 363 // Copy captured data from audio engine buffer to local buffer. | 394 buffer_frame_index - num_frames_to_read; |
| 364 memcpy(&capture_buffer[pos], data_ptr, num_bytes); | 395 |
| 365 } | 396 // Get a cached AGC volume level which is updated once every second |
| 366 | 397 // on the audio manager thread. Note that, |volume| is also updated |
| 367 buffer_frame_index += num_frames_to_read; | 398 // each time SetVolume() is called through IPC by the render-side AGC. |
| 368 } | 399 GetAgcVolume(&volume); |
| 369 | 400 |
| 370 hr = audio_capture_client_->ReleaseBuffer(num_frames_to_read); | 401 // Deliver captured data to the registered consumer using a packet |
| 371 DLOG_IF(ERROR, FAILED(hr)) << "Failed to release capture buffer"; | 402 // size which was specified at construction. |
| 372 | 403 uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5); |
| 373 // Derive a delay estimate for the captured audio packet. | 404 while (buffer_frame_index >= packet_size_frames_) { |
| 374 // The value contains two parts (A+B), where A is the delay of the | 405 // Copy data to audio bus to match the OnData interface. |
| 375 // first audio frame in the packet and B is the extra delay | 406 uint8_t* audio_data = |
| 376 // contained in any stored data. Unit is in audio frames. | 407 reinterpret_cast<uint8_t*>(capture_buffer.get()); |
| 377 QueryPerformanceCounter(&now_count); | 408 audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(), |
| 378 // first_audio_frame_timestamp will be 0 if we didn't get a timestamp. | 409 format_.wBitsPerSample / 8); |
| 379 double audio_delay_frames = first_audio_frame_timestamp == 0 ? | 410 |
| 380 num_frames_to_read : | 411 // Deliver data packet, delay estimation and volume level to |
| 381 ((perf_count_to_100ns_units_ * now_count.QuadPart - | 412 // the user. |
| 382 first_audio_frame_timestamp) / 10000.0) * ms_to_frame_count_ + | 413 sink_->OnData(this, audio_bus_.get(), delay_frames * frame_size_, |
| 383 buffer_frame_index - num_frames_to_read; | 414 volume); |
| 384 | 415 |
| 385 // Get a cached AGC volume level which is updated once every second | 416 // Store parts of the recorded data which can't be delivered |
| 386 // on the audio manager thread. Note that, |volume| is also updated | 417 // using the current packet size. The stored section will be used |
| 387 // each time SetVolume() is called through IPC by the render-side AGC. | 418 // either in the next while-loop iteration or in the next |
| 388 GetAgcVolume(&volume); | 419 // capture event. |
| 389 | 420 // TODO(tommi): If this data will be used in the next capture |
| 390 // Deliver captured data to the registered consumer using a packet | 421 // event, we will report incorrect delay estimates because |
| 391 // size which was specified at construction. | 422 // we'll use the one for the captured data that time around |
| 392 uint32_t delay_frames = | 423 // (i.e. in the future). |
| 393 static_cast<uint32_t>(audio_delay_frames + 0.5); | 424 memmove(&capture_buffer[0], &capture_buffer[packet_size_bytes_], |
| 394 while (buffer_frame_index >= packet_size_frames_) { | 425 (buffer_frame_index - packet_size_frames_) * frame_size_); |
| 395 // Copy data to audio bus to match the OnData interface. | 426 |
| 396 uint8_t* audio_data = | 427 DCHECK_GE(buffer_frame_index, packet_size_frames_); |
| 397 reinterpret_cast<uint8_t*>(capture_buffer.get()); | 428 buffer_frame_index -= packet_size_frames_; |
| 398 audio_bus_->FromInterleaved( | 429 if (delay_frames > packet_size_frames_) { |
| 399 audio_data, audio_bus_->frames(), format_.wBitsPerSample / 8); | 430 delay_frames -= packet_size_frames_; |
| 400 | 431 } else { |
| 401 // Deliver data packet, delay estimation and volume level to | 432 delay_frames = 0; |
| 402 // the user. | |
| 403 sink_->OnData( | |
| 404 this, audio_bus_.get(), delay_frames * frame_size_, volume); | |
| 405 | |
| 406 // Store parts of the recorded data which can't be delivered | |
| 407 // using the current packet size. The stored section will be used | |
| 408 // either in the next while-loop iteration or in the next | |
| 409 // capture event. | |
| 410 // TODO(tommi): If this data will be used in the next capture | |
| 411 // event, we will report incorrect delay estimates because | |
| 412 // we'll use the one for the captured data that time around | |
| 413 // (i.e. in the future). | |
| 414 memmove(&capture_buffer[0], | |
| 415 &capture_buffer[packet_size_bytes_], | |
| 416 (buffer_frame_index - packet_size_frames_) * frame_size_); | |
| 417 | |
| 418 DCHECK_GE(buffer_frame_index, packet_size_frames_); | |
| 419 buffer_frame_index -= packet_size_frames_; | |
| 420 if (delay_frames > packet_size_frames_) { | |
| 421 delay_frames -= packet_size_frames_; | |
| 422 } else { | |
| 423 delay_frames = 0; | |
| 424 } | |
| 425 } | 433 } |
| 426 } | 434 } |
| 427 break; | 435 } break; |
| 428 default: | 436 default: |
| 429 error = true; | 437 error = true; |
| 430 break; | 438 break; |
| 431 } | 439 } |
| 432 } | 440 } |
| 433 | 441 |
| 434 if (recording && error) { | 442 if (recording && error) { |
| 435 // TODO(henrika): perhaps it worth improving the cleanup here by e.g. | 443 // TODO(henrika): perhaps it worth improving the cleanup here by e.g. |
| 436 // stopping the audio client, joining the thread etc.? | 444 // stopping the audio client, joining the thread etc.? |
| 437 NOTREACHED() << "WASAPI capturing failed with error code " | 445 NOTREACHED() << "WASAPI capturing failed with error code " |
| 438 << GetLastError(); | 446 << GetLastError(); |
| 439 } | 447 } |
| 440 | 448 |
| 441 // Disable MMCSS. | 449 // Disable MMCSS. |
| 442 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { | 450 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { |
| 443 PLOG(WARNING) << "Failed to disable MMCSS"; | 451 PLOG(WARNING) << "Failed to disable MMCSS"; |
| 444 } | 452 } |
| 445 } | 453 } |
| 446 | 454 |
| 447 void WASAPIAudioInputStream::HandleError(HRESULT err) { | 455 void WASAPIAudioInputStream::HandleError(HRESULT err) { |
| 448 NOTREACHED() << "Error code: " << err; | 456 NOTREACHED() << "Error code: " << err; |
| 449 if (sink_) | 457 if (sink_) |
| 450 sink_->OnError(this); | 458 sink_->OnError(this); |
| 451 } | 459 } |
| 452 | 460 |
| 453 HRESULT WASAPIAudioInputStream::SetCaptureDevice() { | 461 HRESULT WASAPIAudioInputStream::SetCaptureDevice() { |
| 454 DCHECK(!endpoint_device_.get()); | 462 DCHECK(!endpoint_device_.get()); |
| 455 | 463 |
| 456 ScopedComPtr<IMMDeviceEnumerator> enumerator; | 464 ScopedComPtr<IMMDeviceEnumerator> enumerator; |
| 457 HRESULT hr = enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), | 465 HRESULT hr = enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, |
| 458 NULL, CLSCTX_INPROC_SERVER); | 466 CLSCTX_INPROC_SERVER); |
| 459 if (FAILED(hr)) | 467 if (FAILED(hr)) |
| 460 return hr; | 468 return hr; |
| 461 | 469 |
| 462 // Retrieve the IMMDevice by using the specified role or the specified | 470 // Retrieve the IMMDevice by using the specified role or the specified |
| 463 // unique endpoint device-identification string. | 471 // unique endpoint device-identification string. |
| 464 | 472 |
| 465 if (device_id_ == AudioDeviceDescription::kDefaultDeviceId) { | 473 if (device_id_ == AudioDeviceDescription::kDefaultDeviceId) { |
| 466 // Retrieve the default capture audio endpoint for the specified role. | 474 // Retrieve the default capture audio endpoint for the specified role. |
| 467 // Note that, in Windows Vista, the MMDevice API supports device roles | 475 // Note that, in Windows Vista, the MMDevice API supports device roles |
| 468 // but the system-supplied user interface programs do not. | 476 // but the system-supplied user interface programs do not. |
| 469 hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole, | 477 hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole, |
| 470 endpoint_device_.Receive()); | 478 endpoint_device_.Receive()); |
| 471 } else if (device_id_ == AudioDeviceDescription::kCommunicationsDeviceId) { | 479 } else if (device_id_ == AudioDeviceDescription::kCommunicationsDeviceId) { |
| 472 hr = enumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, | 480 hr = enumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, |
| 473 endpoint_device_.Receive()); | 481 endpoint_device_.Receive()); |
| 482 } else if (device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId) { |
| 483 // Capture the default playback stream. |
| 484 hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, |
| 485 endpoint_device_.Receive()); |
| 486 |
| 487 endpoint_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, |
| 488 system_audio_volume_.ReceiveVoid()); |
| 489 if (system_audio_volume_) { |
| 490 BOOL muted = false; |
| 491 system_audio_volume_->GetMute(&muted); |
| 492 |
| 493 // If the system audio is muted at the time of capturing, then no need to |
| 494 // mute it again, and later we do not unmute system audio when stopping |
| 495 // capturing. |
| 496 if (!muted) { |
| 497 system_audio_volume_->SetMute(true, NULL); |
| 498 mute_done_ = true; |
| 499 } |
| 500 } |
| 474 } else if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId) { | 501 } else if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId) { |
| 475 // Capture the default playback stream. | 502 // Capture the default playback stream. |
| 476 hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, | 503 hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, |
| 477 endpoint_device_.Receive()); | 504 endpoint_device_.Receive()); |
| 478 } else { | 505 } else { |
| 479 hr = enumerator->GetDevice(base::UTF8ToUTF16(device_id_).c_str(), | 506 hr = enumerator->GetDevice(base::UTF8ToUTF16(device_id_).c_str(), |
| 480 endpoint_device_.Receive()); | 507 endpoint_device_.Receive()); |
| 481 } | 508 } |
| 482 | 509 |
| 483 if (FAILED(hr)) | 510 if (FAILED(hr)) |
| (...skipping 10 matching lines...) Expand all Loading... |
| 494 DLOG(ERROR) << "Selected capture device is not active."; | 521 DLOG(ERROR) << "Selected capture device is not active."; |
| 495 hr = E_ACCESSDENIED; | 522 hr = E_ACCESSDENIED; |
| 496 } | 523 } |
| 497 | 524 |
| 498 return hr; | 525 return hr; |
| 499 } | 526 } |
| 500 | 527 |
| 501 HRESULT WASAPIAudioInputStream::ActivateCaptureDevice() { | 528 HRESULT WASAPIAudioInputStream::ActivateCaptureDevice() { |
| 502 // Creates and activates an IAudioClient COM object given the selected | 529 // Creates and activates an IAudioClient COM object given the selected |
| 503 // capture endpoint device. | 530 // capture endpoint device. |
| 504 HRESULT hr = endpoint_device_->Activate(__uuidof(IAudioClient), | 531 HRESULT hr = |
| 505 CLSCTX_INPROC_SERVER, | 532 endpoint_device_->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, |
| 506 NULL, | 533 NULL, audio_client_.ReceiveVoid()); |
| 507 audio_client_.ReceiveVoid()); | |
| 508 return hr; | 534 return hr; |
| 509 } | 535 } |
| 510 | 536 |
| 511 HRESULT WASAPIAudioInputStream::GetAudioEngineStreamFormat() { | 537 HRESULT WASAPIAudioInputStream::GetAudioEngineStreamFormat() { |
| 512 HRESULT hr = S_OK; | 538 HRESULT hr = S_OK; |
| 513 #ifndef NDEBUG | 539 #ifndef NDEBUG |
| 514 // The GetMixFormat() method retrieves the stream format that the | 540 // The GetMixFormat() method retrieves the stream format that the |
| 515 // audio engine uses for its internal processing of shared-mode streams. | 541 // audio engine uses for its internal processing of shared-mode streams. |
| 516 // The method always uses a WAVEFORMATEXTENSIBLE structure, instead | 542 // The method always uses a WAVEFORMATEXTENSIBLE structure, instead |
| 517 // of a stand-alone WAVEFORMATEX structure, to specify the format. | 543 // of a stand-alone WAVEFORMATEX structure, to specify the format. |
| 518 // An WAVEFORMATEXTENSIBLE structure can specify both the mapping of | 544 // An WAVEFORMATEXTENSIBLE structure can specify both the mapping of |
| 519 // channels to speakers and the number of bits of precision in each sample. | 545 // channels to speakers and the number of bits of precision in each sample. |
| 520 base::win::ScopedCoMem<WAVEFORMATEXTENSIBLE> format_ex; | 546 base::win::ScopedCoMem<WAVEFORMATEXTENSIBLE> format_ex; |
| 521 hr = audio_client_->GetMixFormat( | 547 hr = |
| 522 reinterpret_cast<WAVEFORMATEX**>(&format_ex)); | 548 audio_client_->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&format_ex)); |
| 523 | 549 |
| 524 // See http://msdn.microsoft.com/en-us/windows/hardware/gg463006#EFH | 550 // See http://msdn.microsoft.com/en-us/windows/hardware/gg463006#EFH |
| 525 // for details on the WAVE file format. | 551 // for details on the WAVE file format. |
| 526 WAVEFORMATEX format = format_ex->Format; | 552 WAVEFORMATEX format = format_ex->Format; |
| 527 DVLOG(2) << "WAVEFORMATEX:"; | 553 DVLOG(2) << "WAVEFORMATEX:"; |
| 528 DVLOG(2) << " wFormatTags : 0x" << std::hex << format.wFormatTag; | 554 DVLOG(2) << " wFormatTags : 0x" << std::hex << format.wFormatTag; |
| 529 DVLOG(2) << " nChannels : " << format.nChannels; | 555 DVLOG(2) << " nChannels : " << format.nChannels; |
| 530 DVLOG(2) << " nSamplesPerSec : " << format.nSamplesPerSec; | 556 DVLOG(2) << " nSamplesPerSec : " << format.nSamplesPerSec; |
| 531 DVLOG(2) << " nAvgBytesPerSec: " << format.nAvgBytesPerSec; | 557 DVLOG(2) << " nAvgBytesPerSec: " << format.nAvgBytesPerSec; |
| 532 DVLOG(2) << " nBlockAlign : " << format.nBlockAlign; | 558 DVLOG(2) << " nBlockAlign : " << format.nBlockAlign; |
| 533 DVLOG(2) << " wBitsPerSample : " << format.wBitsPerSample; | 559 DVLOG(2) << " wBitsPerSample : " << format.wBitsPerSample; |
| 534 DVLOG(2) << " cbSize : " << format.cbSize; | 560 DVLOG(2) << " cbSize : " << format.cbSize; |
| 535 | 561 |
| 536 DVLOG(2) << "WAVEFORMATEXTENSIBLE:"; | 562 DVLOG(2) << "WAVEFORMATEXTENSIBLE:"; |
| 537 DVLOG(2) << " wValidBitsPerSample: " << | 563 DVLOG(2) << " wValidBitsPerSample: " |
| 538 format_ex->Samples.wValidBitsPerSample; | 564 << format_ex->Samples.wValidBitsPerSample; |
| 539 DVLOG(2) << " dwChannelMask : 0x" << std::hex << | 565 DVLOG(2) << " dwChannelMask : 0x" << std::hex |
| 540 format_ex->dwChannelMask; | 566 << format_ex->dwChannelMask; |
| 541 if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) | 567 if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) |
| 542 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_PCM"; | 568 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_PCM"; |
| 543 else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) | 569 else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) |
| 544 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT"; | 570 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT"; |
| 545 else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) | 571 else if (format_ex->SubFormat == KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) |
| 546 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_WAVEFORMATEX"; | 572 DVLOG(2) << " SubFormat : KSDATAFORMAT_SUBTYPE_WAVEFORMATEX"; |
| 547 #endif | 573 #endif |
| 548 return hr; | 574 return hr; |
| 549 } | 575 } |
| 550 | 576 |
| 551 bool WASAPIAudioInputStream::DesiredFormatIsSupported() { | 577 bool WASAPIAudioInputStream::DesiredFormatIsSupported() { |
| 552 // An application that uses WASAPI to manage shared-mode streams can rely | 578 // An application that uses WASAPI to manage shared-mode streams can rely |
| 553 // on the audio engine to perform only limited format conversions. The audio | 579 // on the audio engine to perform only limited format conversions. The audio |
| 554 // engine can convert between a standard PCM sample size used by the | 580 // engine can convert between a standard PCM sample size used by the |
| 555 // application and the floating-point samples that the engine uses for its | 581 // application and the floating-point samples that the engine uses for its |
| 556 // internal processing. However, the format for an application stream | 582 // internal processing. However, the format for an application stream |
| 557 // typically must have the same number of channels and the same sample | 583 // typically must have the same number of channels and the same sample |
| 558 // rate as the stream format used by the device. | 584 // rate as the stream format used by the device. |
| 559 // Many audio devices support both PCM and non-PCM stream formats. However, | 585 // Many audio devices support both PCM and non-PCM stream formats. However, |
| 560 // the audio engine can mix only PCM streams. | 586 // the audio engine can mix only PCM streams. |
| 561 base::win::ScopedCoMem<WAVEFORMATEX> closest_match; | 587 base::win::ScopedCoMem<WAVEFORMATEX> closest_match; |
| 562 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, | 588 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, |
| 563 &format_, | 589 &format_, &closest_match); |
| 564 &closest_match); | |
| 565 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " | 590 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " |
| 566 << "but a closest match exists."; | 591 << "but a closest match exists."; |
| 567 return (hr == S_OK); | 592 return (hr == S_OK); |
| 568 } | 593 } |
| 569 | 594 |
| 570 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() { | 595 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() { |
| 571 DWORD flags; | 596 DWORD flags; |
| 572 // Use event-driven mode only fo regular input devices. For loopback the | 597 // Use event-driven mode only fo regular input devices. For loopback the |
| 573 // EVENTCALLBACK flag is specified when intializing | 598 // EVENTCALLBACK flag is specified when intializing |
| 574 // |audio_render_client_for_loopback_|. | 599 // |audio_render_client_for_loopback_|. |
| 575 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId) { | 600 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId || |
| 601 device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId) { |
| 576 flags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_NOPERSIST; | 602 flags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_NOPERSIST; |
| 577 } else { | 603 } else { |
| 578 flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST; | 604 flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST; |
| 579 } | 605 } |
| 580 | 606 |
| 581 // Initialize the audio stream between the client and the device. | 607 // Initialize the audio stream between the client and the device. |
| 582 // We connect indirectly through the audio engine by using shared mode. | 608 // We connect indirectly through the audio engine by using shared mode. |
| 583 // Note that, |hnsBufferDuration| is set of 0, which ensures that the | 609 // Note that, |hnsBufferDuration| is set of 0, which ensures that the |
| 584 // buffer is never smaller than the minimum buffer size needed to ensure | 610 // buffer is never smaller than the minimum buffer size needed to ensure |
| 585 // that glitches do not occur between the periodic processing passes. | 611 // that glitches do not occur between the periodic processing passes. |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 639 // to MSDN: | 665 // to MSDN: |
| 640 // | 666 // |
| 641 // A pull-mode capture client does not receive any events when a stream is | 667 // A pull-mode capture client does not receive any events when a stream is |
| 642 // initialized with event-driven buffering and is loopback-enabled. To | 668 // initialized with event-driven buffering and is loopback-enabled. To |
| 643 // work around this, initialize a render stream in event-driven mode. Each | 669 // work around this, initialize a render stream in event-driven mode. Each |
| 644 // time the client receives an event for the render stream, it must signal | 670 // time the client receives an event for the render stream, it must signal |
| 645 // the capture client to run the capture thread that reads the next set of | 671 // the capture client to run the capture thread that reads the next set of |
| 646 // samples from the capture endpoint buffer. | 672 // samples from the capture endpoint buffer. |
| 647 // | 673 // |
| 648 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).a
spx | 674 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).a
spx |
| 649 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId) { | 675 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId || |
| 676 device_id_ == AudioDeviceDescription::kLoopbackWithMuteDeviceId) { |
| 650 hr = endpoint_device_->Activate( | 677 hr = endpoint_device_->Activate( |
| 651 __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, | 678 __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, |
| 652 audio_render_client_for_loopback_.ReceiveVoid()); | 679 audio_render_client_for_loopback_.ReceiveVoid()); |
| 653 if (FAILED(hr)) | 680 if (FAILED(hr)) |
| 654 return hr; | 681 return hr; |
| 655 | 682 |
| 656 hr = audio_render_client_for_loopback_->Initialize( | 683 hr = audio_render_client_for_loopback_->Initialize( |
| 657 AUDCLNT_SHAREMODE_SHARED, | 684 AUDCLNT_SHAREMODE_SHARED, |
| 658 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, | 685 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, 0, 0, |
| 659 0, 0, &format_, NULL); | 686 &format_, NULL); |
| 660 if (FAILED(hr)) | 687 if (FAILED(hr)) |
| 661 return hr; | 688 return hr; |
| 662 | 689 |
| 663 hr = audio_render_client_for_loopback_->SetEventHandle( | 690 hr = audio_render_client_for_loopback_->SetEventHandle( |
| 664 audio_samples_ready_event_.Get()); | 691 audio_samples_ready_event_.Get()); |
| 665 } else { | 692 } else { |
| 666 hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get()); | 693 hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get()); |
| 667 } | 694 } |
| 668 | 695 |
| 669 if (FAILED(hr)) | 696 if (FAILED(hr)) |
| 670 return hr; | 697 return hr; |
| 671 | 698 |
| 672 // Get access to the IAudioCaptureClient interface. This interface | 699 // Get access to the IAudioCaptureClient interface. This interface |
| 673 // enables us to read input data from the capture endpoint buffer. | 700 // enables us to read input data from the capture endpoint buffer. |
| 674 hr = audio_client_->GetService(__uuidof(IAudioCaptureClient), | 701 hr = audio_client_->GetService(__uuidof(IAudioCaptureClient), |
| 675 audio_capture_client_.ReceiveVoid()); | 702 audio_capture_client_.ReceiveVoid()); |
| 676 if (FAILED(hr)) | 703 if (FAILED(hr)) |
| 677 return hr; | 704 return hr; |
| 678 | 705 |
| 679 // Obtain a reference to the ISimpleAudioVolume interface which enables | 706 // Obtain a reference to the ISimpleAudioVolume interface which enables |
| 680 // us to control the master volume level of an audio session. | 707 // us to control the master volume level of an audio session. |
| 681 hr = audio_client_->GetService(__uuidof(ISimpleAudioVolume), | 708 hr = audio_client_->GetService(__uuidof(ISimpleAudioVolume), |
| 682 simple_audio_volume_.ReceiveVoid()); | 709 simple_audio_volume_.ReceiveVoid()); |
| 683 return hr; | 710 return hr; |
| 684 } | 711 } |
| 685 | 712 |
| 686 } // namespace media | 713 } // namespace media |
| OLD | NEW |