Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Unified Diff: media/audio/win/audio_device_listener_win.cc

Issue 11233023: Handle audio device changes on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Wow! It actually works! Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..9cbd7ade98f76a9a83800c9657690ab3bcd7eb31
--- /dev/null
+++ b/media/audio/win/audio_device_listener_win.cc
@@ -0,0 +1,155 @@
+// 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 <Functiondiscoverykeys_devpkey.h>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Not used.
DaleCurtis 2012/10/23 01:49:42 Done.
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/windows_version.h"
+#include "media/audio/win/avrt_wrapper_win.h"
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Don't need this one.
DaleCurtis 2012/10/23 01:49:42 Done.
+
+using base::win::ScopedCoMem;
+
+namespace media {
+
+static ScopedComPtr<IMMDeviceEnumerator> CreateDeviceEnumerator() {
henrika (OOO until Aug 14) 2012/10/21 09:00:20 TODO(henrika)
DaleCurtis 2012/10/23 01:49:42 Done.
+ 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(
+ AudioManager::AudioDeviceListener* listener)
+ : listener_(listener) {
+ // Windows Core Audio is only supported on Vista or higher.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return;
+
+ bool avrt_init = avrt::Initialize();
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Don't need this for the notification parts. Only r
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Don't need the avrt-part. Only required in combina
DaleCurtis 2012/10/23 01:49:42 Done.
+ DCHECK(avrt_init) << "Failed to load the avrt.dll";
+
+ device_enumerator_ = CreateDeviceEnumerator();
+ if (!device_enumerator_)
+ return;
+
+ HRESULT hr = device_enumerator_->RegisterEndpointNotificationCallback(this);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "RegisterEndpointNotificationCallback failed";
+ return;
+ }
+
+ ScopedComPtr<IMMDevice> endpoint_device;
henrika (OOO until Aug 14) 2012/10/21 09:00:20 The last part of this method is for logging only;
DaleCurtis 2012/10/23 01:49:42 No we need the default_device_id to filter OnDefau
+ hr = device_enumerator_->GetDefaultAudioEndpoint(
+ eRender, eConsole, endpoint_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)) {
+ DLOG(ERROR) << "GetDefaultAudioEndpoint() failed. No devices?";
+ return;
+ }
+
+ ScopedCoMem<WCHAR> device_id;
+ hr = endpoint_device->GetId(&device_id);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "GetId() failed";
+ return;
+ }
+
+ default_device_id_ = WideToUTF8(static_cast<WCHAR*>(device_id));
+ DLOG(ERROR) << "Default Device: " << default_device_id_;
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Why ERROR?
DaleCurtis 2012/10/23 01:49:42 Done.
+}
+
+AudioDeviceListenerWin::~AudioDeviceListenerWin() {
+ if (device_enumerator_)
+ device_enumerator_->UnregisterEndpointNotificationCallback(this);
+}
+
+ULONG AudioDeviceListenerWin::AddRef() {
+ NOTREACHED() << "IMMNotificationClient should not use this method.";
+ return 1;
+}
+
+ULONG AudioDeviceListenerWin::Release() {
+ NOTREACHED() << "IMMNotificationClient should not use this method.";
+ return 1;
+}
+
+HRESULT AudioDeviceListenerWin::QueryInterface(REFIID iid, void** object) {
+ NOTREACHED() << "IMMNotificationClient should not use this method.";
+ // TODO(dalecurtis): Why do we have this code under NOTREACHED()? Henrik?
henrika (OOO until Aug 14) 2012/10/21 09:00:20 The IMMNotification implementation feels like a ha
henrika (OOO until Aug 14) 2012/10/22 10:40:24 The interface requires that we add implementations
DaleCurtis 2012/10/23 01:49:42 Done.
+ if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) {
+ *object = static_cast < IMMNotificationClient*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+HRESULT AudioDeviceListenerWin::OnPropertyValueChanged(LPCWSTR device_id,
+ const PROPERTYKEY key) {
+ // TODO(dalecurtis): We need to handle changes for the current default device
henrika (OOO until Aug 14) 2012/10/22 13:44:31 Hhmm, what if we have three devices, A, B and C an
DaleCurtis 2012/10/23 01:49:42 We can use the default_device_id_ saved in constru
+ // here. It's tricky because this method may be called many (20+) times for
+ // a single change like sample rate.
scherkus (not reviewing) 2012/10/22 23:51:05 this seems like a worthy follow-up optimization...
DaleCurtis 2012/10/23 01:49:42 Done.
+ return S_OK;
+}
+
+HRESULT AudioDeviceListenerWin::OnDeviceAdded(LPCWSTR device_id) {
+ // We don't care when devices are added.
+ return S_OK;
+}
+
+HRESULT 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) {
+ // TODO(dalecurtis): Are there any events we need to handle here?
+ return S_OK;
+}
+
+STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged(
+ EDataFlow flow, ERole role, LPCWSTR new_default_device_id) {
+ // Only listen for output device changes right now...
+ // TODO(dalecurtis): This is slightly different for exclusive mode right? We
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Not related to exclusive mode. Notifications shoul
DaleCurtis 2012/10/23 01:49:42 Done.
+ // shouldn't be hard coding eConsole, eRender...
+ 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(static_cast<const WCHAR*>(
+ new_default_device_id));
+ }
+
+ // Only fire a state change event if the device has actually changed.
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Let me do some more tests on Monday and get back w
henrika (OOO until Aug 14) 2012/10/22 13:39:01 I think we must take this off line. It is actually
DaleCurtis 2012/10/23 01:49:42 This will still work in that case, it's just that
+ // TODO(dalecurtis): Is this correct?? We get 3 of OnDefaultDeviceChanged()
+ // events for our flow and role. Why??
+ if (new_device_id.compare(default_device_id_) == 0)
+ return S_OK;
+
+ default_device_id_ = new_device_id;
+
+ // 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.
+ listener_->OnDeviceChange();
henrika (OOO until Aug 14) 2012/10/21 09:00:20 Should we comment on the fact that is call will co
DaleCurtis 2012/10/23 01:49:42 Done.
+
+ return S_OK;
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698