| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/win/audio_device_listener_win.h" | 5 #include "media/audio/win/audio_device_listener_win.h" |
| 6 | 6 |
| 7 #include <Audioclient.h> | 7 #include <Audioclient.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "base/system_monitor/system_monitor.h" | 11 #include "base/system_monitor/system_monitor.h" |
| 12 #include "base/time/default_tick_clock.h" |
| 12 #include "base/win/scoped_co_mem.h" | 13 #include "base/win/scoped_co_mem.h" |
| 13 #include "base/win/windows_version.h" | 14 #include "base/win/windows_version.h" |
| 14 #include "media/audio/win/core_audio_util_win.h" | 15 #include "media/audio/win/core_audio_util_win.h" |
| 15 | 16 |
| 16 using base::win::ScopedCoMem; | 17 using base::win::ScopedCoMem; |
| 17 | 18 |
| 18 namespace media { | 19 namespace media { |
| 19 | 20 |
| 20 static std::string FlowToString(EDataFlow flow) { | 21 static std::string FlowToString(EDataFlow flow) { |
| 21 return (flow == eRender) ? "eRender" : "eConsole"; | 22 return (flow == eRender) ? "eRender" : "eConsole"; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 45 HRESULT hr = CoreAudioUtil::GetDeviceName(device.get(), &device_name); | 46 HRESULT hr = CoreAudioUtil::GetDeviceName(device.get(), &device_name); |
| 46 if (FAILED(hr)) { | 47 if (FAILED(hr)) { |
| 47 DVLOG(1) << "Failed to retrieve the device id: " << std::hex << hr; | 48 DVLOG(1) << "Failed to retrieve the device id: " << std::hex << hr; |
| 48 return std::string(); | 49 return std::string(); |
| 49 } | 50 } |
| 50 | 51 |
| 51 return device_name.unique_id; | 52 return device_name.unique_id; |
| 52 } | 53 } |
| 53 | 54 |
| 54 AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb) | 55 AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb) |
| 55 : listener_cb_(listener_cb) { | 56 : listener_cb_(listener_cb), tick_clock_(new base::DefaultTickClock()) { |
| 56 CHECK(CoreAudioUtil::IsSupported()); | 57 CHECK(CoreAudioUtil::IsSupported()); |
| 57 | 58 |
| 58 ScopedComPtr<IMMDeviceEnumerator> device_enumerator( | 59 ScopedComPtr<IMMDeviceEnumerator> device_enumerator( |
| 59 CoreAudioUtil::CreateDeviceEnumerator()); | 60 CoreAudioUtil::CreateDeviceEnumerator()); |
| 60 if (!device_enumerator.get()) | 61 if (!device_enumerator.get()) |
| 61 return; | 62 return; |
| 62 | 63 |
| 63 HRESULT hr = device_enumerator->RegisterEndpointNotificationCallback(this); | 64 HRESULT hr = device_enumerator->RegisterEndpointNotificationCallback(this); |
| 64 if (FAILED(hr)) { | 65 if (FAILED(hr)) { |
| 65 LOG(ERROR) << "RegisterEndpointNotificationCallback failed: " | 66 LOG(ERROR) << "RegisterEndpointNotificationCallback failed: " |
| 66 << std::hex << hr; | 67 << std::hex << hr; |
| 67 return; | 68 return; |
| 68 } | 69 } |
| 69 | 70 |
| 70 device_enumerator_ = device_enumerator; | 71 device_enumerator_ = device_enumerator; |
| 71 | |
| 72 default_render_device_id_ = GetDeviceId(eRender, eConsole); | |
| 73 default_capture_device_id_ = GetDeviceId(eCapture, eConsole); | |
| 74 default_communications_render_device_id_ = | |
| 75 GetDeviceId(eRender, eCommunications); | |
| 76 default_communications_capture_device_id_ = | |
| 77 GetDeviceId(eCapture, eCommunications); | |
| 78 } | 72 } |
| 79 | 73 |
| 80 AudioDeviceListenerWin::~AudioDeviceListenerWin() { | 74 AudioDeviceListenerWin::~AudioDeviceListenerWin() { |
| 81 DCHECK(thread_checker_.CalledOnValidThread()); | 75 DCHECK(thread_checker_.CalledOnValidThread()); |
| 82 if (device_enumerator_.get()) { | 76 if (device_enumerator_.get()) { |
| 83 HRESULT hr = | 77 HRESULT hr = |
| 84 device_enumerator_->UnregisterEndpointNotificationCallback(this); | 78 device_enumerator_->UnregisterEndpointNotificationCallback(this); |
| 85 LOG_IF(ERROR, FAILED(hr)) << "UnregisterEndpointNotificationCallback() " | 79 LOG_IF(ERROR, FAILED(hr)) << "UnregisterEndpointNotificationCallback() " |
| 86 << "failed: " << std::hex << hr; | 80 << "failed: " << std::hex << hr; |
| 87 } | 81 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 } | 127 } |
| 134 | 128 |
| 135 STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged( | 129 STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged( |
| 136 EDataFlow flow, ERole role, LPCWSTR new_default_device_id) { | 130 EDataFlow flow, ERole role, LPCWSTR new_default_device_id) { |
| 137 // Only listen for console and communication device changes. | 131 // Only listen for console and communication device changes. |
| 138 if ((role != eConsole && role != eCommunications) || | 132 if ((role != eConsole && role != eCommunications) || |
| 139 (flow != eRender && flow != eCapture)) { | 133 (flow != eRender && flow != eCapture)) { |
| 140 return S_OK; | 134 return S_OK; |
| 141 } | 135 } |
| 142 | 136 |
| 143 // Grab a pointer to the appropriate ID member. | |
| 144 // Note that there are three "?:"'s here to select the right ID. | |
| 145 std::string* current_device_id = | |
| 146 flow == eRender ? ( | |
| 147 role == eConsole ? | |
| 148 &default_render_device_id_ : | |
| 149 &default_communications_render_device_id_ | |
| 150 ) : ( | |
| 151 role == eConsole ? | |
| 152 &default_capture_device_id_ : | |
| 153 &default_communications_capture_device_id_ | |
| 154 ); | |
| 155 | |
| 156 // If no device is now available, |new_default_device_id| will be NULL. | 137 // If no device is now available, |new_default_device_id| will be NULL. |
| 157 std::string new_device_id; | 138 std::string new_device_id; |
| 158 if (new_default_device_id) | 139 if (new_default_device_id) |
| 159 new_device_id = base::WideToUTF8(new_default_device_id); | 140 new_device_id = base::WideToUTF8(new_default_device_id); |
| 160 | 141 |
| 142 // Only output device changes should be forwarded. Do not attempt to filter |
| 143 // changes based on device id since some devices may not change their device |
| 144 // id and instead trigger some internal flow change: http://crbug.com/506712 |
| 145 // |
| 146 // We rate limit device changes to avoid a single device change causing back |
| 147 // to back changes for eCommunications and eConsole; this is worth doing as |
| 148 // it provides a substantially faster resumption of playback. |
| 149 bool did_run_listener_cb = false; |
| 150 const base::TimeTicks now = tick_clock_->NowTicks(); |
| 151 if (flow == eRender && |
| 152 now - last_device_change_time_ > |
| 153 base::TimeDelta::FromMilliseconds(kDeviceChangeLimitMs)) { |
| 154 last_device_change_time_ = now; |
| 155 listener_cb_.Run(); |
| 156 did_run_listener_cb = true; |
| 157 } |
| 158 |
| 161 DVLOG(1) << "OnDefaultDeviceChanged() " | 159 DVLOG(1) << "OnDefaultDeviceChanged() " |
| 162 << "new_default_device: " | 160 << "new_default_device: " |
| 163 << (new_default_device_id ? | 161 << (new_default_device_id |
| 164 CoreAudioUtil::GetFriendlyName(new_device_id) : "No device") | 162 ? CoreAudioUtil::GetFriendlyName(new_device_id) |
| 163 : "no device") |
| 165 << ", flow: " << FlowToString(flow) | 164 << ", flow: " << FlowToString(flow) |
| 166 << ", role: " << RoleToString(role); | 165 << ", role: " << RoleToString(role) |
| 167 | 166 << ", notified manager: " << (did_run_listener_cb ? "Yes" : "No"); |
| 168 // Only fire a state change event if the device has actually changed. | |
| 169 // TODO(dalecurtis): This still seems to fire an extra event on my machine for | |
| 170 // an unplug event (probably others too); e.g., we get two transitions to a | |
| 171 // new default device id. | |
| 172 if (new_device_id.compare(*current_device_id) == 0) | |
| 173 return S_OK; | |
| 174 | |
| 175 // Store the new id in the member variable (that current_device_id points to). | |
| 176 *current_device_id = new_device_id; | |
| 177 listener_cb_.Run(); | |
| 178 | 167 |
| 179 return S_OK; | 168 return S_OK; |
| 180 } | 169 } |
| 181 | 170 |
| 182 } // namespace media | 171 } // namespace media |
| OLD | NEW |