summaryrefslogtreecommitdiff
path: root/chromium/media/audio/win/audio_low_latency_input_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/audio/win/audio_low_latency_input_win.cc')
-rw-r--r--chromium/media/audio/win/audio_low_latency_input_win.cc136
1 files changed, 93 insertions, 43 deletions
diff --git a/chromium/media/audio/win/audio_low_latency_input_win.cc b/chromium/media/audio/win/audio_low_latency_input_win.cc
index e0819439109..a174ea2ea0d 100644
--- a/chromium/media/audio/win/audio_low_latency_input_win.cc
+++ b/chromium/media/audio/win/audio_low_latency_input_win.cc
@@ -103,9 +103,8 @@ bool WASAPIAudioInputStream::Open() {
// Verify that the selected audio endpoint supports the specified format
// set during construction.
- if (!DesiredFormatIsSupported()) {
+ if (!DesiredFormatIsSupported())
return false;
- }
// Initialize the audio stream between the client and the device using
// shared mode and a lowest possible glitch-free latency.
@@ -141,6 +140,9 @@ void WASAPIAudioInputStream::Start(AudioInputCallback* callback) {
HRESULT hr = audio_client_->Start();
DLOG_IF(ERROR, FAILED(hr)) << "Failed to start input streaming.";
+ if (SUCCEEDED(hr) && audio_render_client_for_loopback_)
+ hr = audio_render_client_for_loopback_->Start();
+
started_ = SUCCEEDED(hr);
}
@@ -276,6 +278,10 @@ HRESULT WASAPIAudioInputStream::GetMixFormat(const std::string& device_id,
// Retrieve the default capture audio endpoint.
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole,
endpoint_device.Receive());
+ } else if (device_id == AudioManagerBase::kLoopbackInputDeviceId) {
+ // Capture the default playback stream.
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole,
+ endpoint_device.Receive());
} else {
// Retrieve a capture endpoint device that is specified by an endpoint
// device-identification string.
@@ -454,42 +460,44 @@ void WASAPIAudioInputStream::HandleError(HRESULT err) {
HRESULT WASAPIAudioInputStream::SetCaptureDevice() {
ScopedComPtr<IMMDeviceEnumerator> enumerator;
- HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- NULL,
- CLSCTX_INPROC_SERVER,
- __uuidof(IMMDeviceEnumerator),
- enumerator.ReceiveVoid());
- if (SUCCEEDED(hr)) {
- // Retrieve the IMMDevice by using the specified role or the specified
- // unique endpoint device-identification string.
- // TODO(henrika): possibly add support for the eCommunications as well.
- if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
- // Retrieve the default capture audio endpoint for the specified role.
- // Note that, in Windows Vista, the MMDevice API supports device roles
- // but the system-supplied user interface programs do not.
- hr = enumerator->GetDefaultAudioEndpoint(eCapture,
- eConsole,
- endpoint_device_.Receive());
- } else {
- // Retrieve a capture endpoint device that is specified by an endpoint
- // device-identification string.
- hr = enumerator->GetDevice(UTF8ToUTF16(device_id_).c_str(),
- endpoint_device_.Receive());
- }
+ HRESULT hr = enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return hr;
- if (FAILED(hr))
- return hr;
+ // Retrieve the IMMDevice by using the specified role or the specified
+ // unique endpoint device-identification string.
+ // TODO(henrika): possibly add support for the eCommunications as well.
+ if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
+ // Retrieve the default capture audio endpoint for the specified role.
+ // Note that, in Windows Vista, the MMDevice API supports device roles
+ // but the system-supplied user interface programs do not.
+ hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole,
+ endpoint_device_.Receive());
+ } else if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ // Capture the default playback stream.
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole,
+ endpoint_device_.Receive());
+ } else {
+ // Retrieve a capture endpoint device that is specified by an endpoint
+ // device-identification string.
+ hr = enumerator->GetDevice(UTF8ToUTF16(device_id_).c_str(),
+ endpoint_device_.Receive());
+ }
- // Verify that the audio endpoint device is active, i.e., the audio
- // adapter that connects to the endpoint device is present and enabled.
- DWORD state = DEVICE_STATE_DISABLED;
- hr = endpoint_device_->GetState(&state);
- if (SUCCEEDED(hr)) {
- if (!(state & DEVICE_STATE_ACTIVE)) {
- DLOG(ERROR) << "Selected capture device is not active.";
- hr = E_ACCESSDENIED;
- }
- }
+ if (FAILED(hr))
+ return hr;
+
+ // Verify that the audio endpoint device is active, i.e., the audio
+ // adapter that connects to the endpoint device is present and enabled.
+ DWORD state = DEVICE_STATE_DISABLED;
+ hr = endpoint_device_->GetState(&state);
+ if (FAILED(hr))
+ return hr;
+
+ if (!(state & DEVICE_STATE_ACTIVE)) {
+ DLOG(ERROR) << "Selected capture device is not active.";
+ hr = E_ACCESSDENIED;
}
return hr;
@@ -565,16 +573,25 @@ bool WASAPIAudioInputStream::DesiredFormatIsSupported() {
}
HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
+ DWORD flags;
+ // Use event-driven mode only fo regular input devices. For loopback the
+ // EVENTCALLBACK flag is specified when intializing
+ // |audio_render_client_for_loopback_|.
+ if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ flags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
+ } else {
+ flags =
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
+ }
+
// Initialize the audio stream between the client and the device.
- // We connect indirectly through the audio engine by using shared mode
- // and WASAPI is initialized in an event driven mode.
+ // We connect indirectly through the audio engine by using shared mode.
// Note that, |hnsBufferDuration| is set of 0, which ensures that the
// buffer is never smaller than the minimum buffer size needed to ensure
// that glitches do not occur between the periodic processing passes.
// This setting should lead to lowest possible latency.
HRESULT hr = audio_client_->Initialize(AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
- AUDCLNT_STREAMFLAGS_NOPERSIST,
+ flags,
0, // hnsBufferDuration
0,
&format_,
@@ -590,6 +607,7 @@ HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
hr = audio_client_->GetBufferSize(&endpoint_buffer_size_frames_);
if (FAILED(hr))
return hr;
+
DVLOG(1) << "endpoint buffer size: " << endpoint_buffer_size_frames_
<< " [frames]";
@@ -618,9 +636,41 @@ HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
}
#endif
- // Set the event handle that the audio engine will signal each time
- // a buffer becomes ready to be processed by the client.
- hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get());
+ // Set the event handle that the audio engine will signal each time a buffer
+ // becomes ready to be processed by the client.
+ //
+ // In loopback case the capture device doesn't receive any events, so we
+ // need to create a separate playback client to get notifications. According
+ // to MSDN:
+ //
+ // A pull-mode capture client does not receive any events when a stream is
+ // initialized with event-driven buffering and is loopback-enabled. To
+ // work around this, initialize a render stream in event-driven mode. Each
+ // time the client receives an event for the render stream, it must signal
+ // the capture client to run the capture thread that reads the next set of
+ // samples from the capture endpoint buffer.
+ //
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd316551(v=vs.85).aspx
+ if (device_id_ == AudioManagerBase::kLoopbackInputDeviceId) {
+ hr = endpoint_device_->Activate(
+ __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
+ audio_render_client_for_loopback_.ReceiveVoid());
+ if (FAILED(hr))
+ return hr;
+
+ hr = audio_render_client_for_loopback_->Initialize(
+ AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
+ 0, 0, &format_, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ hr = audio_render_client_for_loopback_->SetEventHandle(
+ audio_samples_ready_event_.Get());
+ } else {
+ hr = audio_client_->SetEventHandle(audio_samples_ready_event_.Get());
+ }
+
if (FAILED(hr))
return hr;