forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle audio device changes on Windows.
Uses the new AudioDeviceListener framework to notify of device changes. Handles only default device changes at the moment, e.g., not manually changing the sample rate, etc on a current default device. This all works well enough that I can connect / disconnect remote desktop sessions with and without audio and everything continues to play seamlessly and in sync! BUG=153056 TEST=Unplug... Plug... Unplug! Plug! Review URL: https://codereview.chromium.org/11233023 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164236 0039d316-1c4b-4281-b951-d872f2087c98
- Loading branch information
dalecurtis@google.com
committed
Oct 26, 2012
1 parent
89b4d95
commit 0b4125c
Showing
14 changed files
with
407 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.