Chromium Code Reviews| 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..77eea2e2ddd7919cf7f39cda6a7792c24e112b4f |
| --- /dev/null |
| +++ b/media/audio/win/audio_device_listener_win.cc |
| @@ -0,0 +1,143 @@ |
| +// 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/utf_string_conversions.h" |
| +#include "base/win/scoped_co_mem.h" |
| +#include "base/win/windows_version.h" |
| + |
| +using base::win::ScopedCoMem; |
| + |
| +namespace media { |
| + |
| +// TODO(henrika): Move to CoreAudioUtil class. |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
Nit, is is actually a bunch of static media::win::
|
| +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( |
| + AudioManager::AudioDeviceListener* listener) |
| + : listener_(listener) { |
| + // Windows Core Audio is only supported on Vista or higher. |
| + if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| + return; |
| + |
| + 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; |
| + hr = device_enumerator_->GetDefaultAudioEndpoint( |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
Should we comment on the fact that you are hard co
DaleCurtis
2012/10/24 00:09:27
I've renamed the variable to default_render_device
|
| + 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?"; |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
Easier to debug if you add << std::hex << hr.
DaleCurtis
2012/10/24 00:09:27
Done.
|
| + return; |
| + } |
| + |
| + ScopedCoMem<WCHAR> device_id; |
| + hr = endpoint_device->GetId(&device_id); |
| + if (FAILED(hr)) { |
| + DLOG(ERROR) << "GetId() failed"; |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
dito
DaleCurtis
2012/10/24 00:09:27
Done.
|
| + return; |
| + } |
| + |
| + default_device_id_ = WideToUTF8(static_cast<WCHAR*>(device_id)); |
| + DVLOG(1) << "Default Device: " << default_device_id_; |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
render
DaleCurtis
2012/10/24 00:09:27
Done.
|
| +} |
| + |
| +AudioDeviceListenerWin::~AudioDeviceListenerWin() { |
| + if (device_enumerator_) |
| + device_enumerator_->UnregisterEndpointNotificationCallback(this); |
| +} |
| + |
| +ULONG AudioDeviceListenerWin::AddRef() { |
| + return 1; |
| +} |
| + |
| +ULONG AudioDeviceListenerWin::Release() { |
| + return 1; |
| +} |
| + |
| +HRESULT AudioDeviceListenerWin::QueryInterface(REFIID iid, void** object) { |
| + 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/23 07:38:06
I have written code that would work here but it is
|
| + // 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; |
| +} |
| + |
| +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; |
| +} |
| + |
| +HRESULT AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id, |
| + DWORD new_state) { |
| + // TODO(dalecurtis): Are there any events we need to handle here? |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
Not at this stage. These events are very early and
DaleCurtis
2012/10/24 00:09:27
Done.
|
| + return S_OK; |
| +} |
| + |
| +HRESULT AudioDeviceListenerWin::OnDefaultDeviceChanged( |
| + EDataFlow flow, ERole role, LPCWSTR new_default_device_id) { |
| + // Only listen for output device changes right now... |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
TODO?
DaleCurtis
2012/10/24 00:09:27
Added to class docs.
|
| + 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. |
| + if (new_device_id.compare(default_device_id_) == 0) |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
I also prefer using STL here. Another way could be
DaleCurtis
2012/10/23 20:09:08
I'm a complete windows string noob, so if you've g
|
| + return S_OK; |
| + |
| + default_device_id_ = new_device_id; |
| + |
| + // TODO(dalecurtis): This still seems to fire an extra event on my machine for |
|
henrika (OOO until Aug 14)
2012/10/23 07:38:06
Still true?
DaleCurtis
2012/10/24 00:09:27
Yes.
|
| + // an unplug event (probably others too); e.g., we get two transitions to a |
| + // new default device id. |
| + listener_->OnDeviceChange(); |
| + |
| + return S_OK; |
| +} |
| + |
| +} // namespace media |