Index: media/audio/win/audio_device_listener_win.cc |
diff --git a/media/audio/win/audio_device_listener_win.cc b/media/audio/win/audio_device_listener_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..588852ec385b82c476a994ec9f1f5222c1a3268e |
--- /dev/null |
+++ b/media/audio/win/audio_device_listener_win.cc |
@@ -0,0 +1,145 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/audio/win/audio_device_listener_win.h" |
+ |
+#include <Audioclient.h> |
+ |
+#include "base/logging.h" |
+#include "base/utf_string_conversions.h" |
+#include "base/win/scoped_co_mem.h" |
+#include "base/win/windows_version.h" |
+#include "media/audio/audio_util.h" |
+ |
+using base::win::ScopedCoMem; |
+ |
+namespace media { |
+ |
+// TODO(henrika): Move to CoreAudioUtil class. |
+static ScopedComPtr<IMMDeviceEnumerator> CreateDeviceEnumerator() { |
+ ScopedComPtr<IMMDeviceEnumerator> device_enumerator; |
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), |
+ NULL, |
+ CLSCTX_INPROC_SERVER, |
+ __uuidof(IMMDeviceEnumerator), |
+ device_enumerator.ReceiveVoid()); |
+ DLOG_IF(ERROR, FAILED(hr)) << "CoCreateInstance(IMMDeviceEnumerator): " |
+ << std::hex << hr; |
+ return device_enumerator; |
+} |
+ |
+AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb) |
+ : listener_cb_(listener_cb) { |
+ CHECK(media::IsWASAPISupported()); |
+ |
+ device_enumerator_ = CreateDeviceEnumerator(); |
+ if (!device_enumerator_) |
+ return; |
+ |
+ HRESULT hr = device_enumerator_->RegisterEndpointNotificationCallback(this); |
+ if (FAILED(hr)) { |
+ DLOG(ERROR) << "RegisterEndpointNotificationCallback failed: " |
+ << std::hex << hr; |
+ device_enumerator_ = NULL; |
+ return; |
+ } |
+ |
+ ScopedComPtr<IMMDevice> endpoint_render_device; |
+ hr = device_enumerator_->GetDefaultAudioEndpoint( |
+ eRender, eConsole, endpoint_render_device.Receive()); |
+ // This will fail if there are no audio devices currently plugged in, so we |
+ // still want to keep our endpoint registered. |
+ if (FAILED(hr)) { |
+ DVLOG(1) << "GetDefaultAudioEndpoint() failed. No devices? Error: " |
+ << std::hex << hr; |
+ return; |
+ } |
+ |
+ ScopedCoMem<WCHAR> render_device_id; |
+ hr = endpoint_render_device->GetId(&render_device_id); |
+ if (FAILED(hr)) { |
+ DLOG(ERROR) << "GetId() failed: " << std::hex << hr; |
+ return; |
+ } |
+ |
+ default_render_device_id_ = WideToUTF8(static_cast<WCHAR*>(render_device_id)); |
+ DVLOG(1) << "Default render device: " << default_render_device_id_; |
+} |
+ |
+AudioDeviceListenerWin::~AudioDeviceListenerWin() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (device_enumerator_) { |
+ HRESULT hr = |
+ device_enumerator_->UnregisterEndpointNotificationCallback(this); |
+ DLOG_IF(ERROR, FAILED(hr)) << "UnregisterEndpointNotificationCallback() " |
+ << "failed: " << std::hex << hr; |
+ } |
+} |
+ |
+STDMETHODIMP_(ULONG) AudioDeviceListenerWin::AddRef() { |
+ return 1; |
+} |
+ |
+STDMETHODIMP_(ULONG) AudioDeviceListenerWin::Release() { |
+ return 1; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::QueryInterface(REFIID iid, void** object) { |
+ if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) { |
+ *object = static_cast<IMMNotificationClient*>(this); |
+ return S_OK; |
+ } |
+ |
+ *object = NULL; |
+ return E_NOINTERFACE; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::OnPropertyValueChanged( |
+ LPCWSTR device_id, const PROPERTYKEY key) { |
+ // TODO(dalecurtis): We need to handle changes for the current default device |
+ // here. It's tricky because this method may be called many (20+) times for |
+ // a single change like sample rate. http://crbug.com/153056 |
+ return S_OK; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::OnDeviceAdded(LPCWSTR device_id) { |
+ // We don't care when devices are added. |
+ return S_OK; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::OnDeviceRemoved(LPCWSTR device_id) { |
+ // We don't care when devices are removed. |
+ return S_OK; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id, |
+ DWORD new_state) { |
+ return S_OK; |
+} |
+ |
+STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged( |
+ EDataFlow flow, ERole role, LPCWSTR new_default_device_id) { |
+ // Only listen for output device changes right now... |
+ if (flow != eConsole && role != eRender) |
+ return S_OK; |
+ |
+ // If no device is now available, |new_default_device_id| will be NULL. |
+ std::string new_device_id = ""; |
+ if (new_default_device_id) |
+ new_device_id = WideToUTF8(new_default_device_id); |
+ |
+ // Only fire a state change event if the device has actually changed. |
+ // TODO(dalecurtis): This still seems to fire an extra event on my machine for |
+ // an unplug event (probably others too); e.g., we get two transitions to a |
+ // new default device id. |
+ if (new_device_id.compare(default_render_device_id_) == 0) |
+ return S_OK; |
+ |
+ default_render_device_id_ = new_device_id; |
+ listener_cb_.Run(); |
+ |
+ return S_OK; |
+} |
+ |
+} // namespace media |