ecf838748677b58bc8ecc95484144e6434522d00
[platform/framework/web/crosswalk.git] / src / media / audio / win / audio_device_listener_win.cc
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
3 // found in the LICENSE file.
4
5 #include "media/audio/win/audio_device_listener_win.h"
6
7 #include <Audioclient.h>
8
9 #include "base/logging.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/system_monitor/system_monitor.h"
12 #include "base/win/scoped_co_mem.h"
13 #include "base/win/windows_version.h"
14 #include "media/audio/win/core_audio_util_win.h"
15
16 using base::win::ScopedCoMem;
17
18 namespace media {
19
20 static std::string FlowToString(EDataFlow flow) {
21   return (flow == eRender) ? "eRender" : "eConsole";
22 }
23
24 static std::string RoleToString(ERole role) {
25   switch (role) {
26     case eConsole: return "eConsole";
27     case eMultimedia: return "eMultimedia";
28     case eCommunications: return "eCommunications";
29     default: return "undefined";
30   }
31 }
32
33 static std::string GetDeviceId(EDataFlow flow,
34                                ERole role) {
35   ScopedComPtr<IMMDevice> device =
36       CoreAudioUtil::CreateDefaultDevice(flow, role);
37   if (!device) {
38     // Most probable reason for ending up here is that all audio devices are
39     // disabled or unplugged.
40     DVLOG(1) << "CoreAudioUtil::CreateDefaultDevice failed. No device?";
41     return std::string();
42   }
43
44   AudioDeviceName device_name;
45   HRESULT hr = CoreAudioUtil::GetDeviceName(device, &device_name);
46   if (FAILED(hr)) {
47     DVLOG(1) << "Failed to retrieve the device id: " << std::hex << hr;
48     return std::string();
49   }
50
51   return device_name.unique_id;
52 }
53
54 AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb)
55     : listener_cb_(listener_cb) {
56   CHECK(CoreAudioUtil::IsSupported());
57
58   ScopedComPtr<IMMDeviceEnumerator> device_enumerator(
59       CoreAudioUtil::CreateDeviceEnumerator());
60   if (!device_enumerator)
61     return;
62
63   HRESULT hr = device_enumerator->RegisterEndpointNotificationCallback(this);
64   if (FAILED(hr)) {
65     LOG(ERROR)  << "RegisterEndpointNotificationCallback failed: "
66                 << std::hex << hr;
67     return;
68   }
69
70   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 }
79
80 AudioDeviceListenerWin::~AudioDeviceListenerWin() {
81   DCHECK(thread_checker_.CalledOnValidThread());
82   if (device_enumerator_) {
83     HRESULT hr =
84         device_enumerator_->UnregisterEndpointNotificationCallback(this);
85     LOG_IF(ERROR, FAILED(hr)) << "UnregisterEndpointNotificationCallback() "
86                               << "failed: " << std::hex << hr;
87   }
88 }
89
90 STDMETHODIMP_(ULONG) AudioDeviceListenerWin::AddRef() {
91   return 1;
92 }
93
94 STDMETHODIMP_(ULONG) AudioDeviceListenerWin::Release() {
95   return 1;
96 }
97
98 STDMETHODIMP AudioDeviceListenerWin::QueryInterface(REFIID iid, void** object) {
99   if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) {
100     *object = static_cast<IMMNotificationClient*>(this);
101     return S_OK;
102   }
103
104   *object = NULL;
105   return E_NOINTERFACE;
106 }
107
108 STDMETHODIMP AudioDeviceListenerWin::OnPropertyValueChanged(
109     LPCWSTR device_id, const PROPERTYKEY key) {
110   // TODO(dalecurtis): We need to handle changes for the current default device
111   // here.  It's tricky because this method may be called many (20+) times for
112   // a single change like sample rate.  http://crbug.com/153056
113   return S_OK;
114 }
115
116 STDMETHODIMP AudioDeviceListenerWin::OnDeviceAdded(LPCWSTR device_id) {
117   // We don't care when devices are added.
118   return S_OK;
119 }
120
121 STDMETHODIMP AudioDeviceListenerWin::OnDeviceRemoved(LPCWSTR device_id) {
122   // We don't care when devices are removed.
123   return S_OK;
124 }
125
126 STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id,
127                                                           DWORD new_state) {
128   if (new_state != DEVICE_STATE_ACTIVE && new_state != DEVICE_STATE_NOTPRESENT)
129     return S_OK;
130
131   base::SystemMonitor* monitor = base::SystemMonitor::Get();
132   if (monitor)
133     monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
134
135   return S_OK;
136 }
137
138 STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged(
139     EDataFlow flow, ERole role, LPCWSTR new_default_device_id) {
140   // Only listen for console and communication device changes.
141   if ((role != eConsole && role != eCommunications) ||
142       (flow != eRender && flow != eCapture)) {
143     return S_OK;
144   }
145
146   // Grab a pointer to the appropriate ID member.
147   // Note that there are three "?:"'s here to select the right ID.
148   std::string* current_device_id =
149       role == eRender ? (
150           flow == eConsole ?
151               &default_render_device_id_ :
152               &default_communications_render_device_id_
153       ) : (
154           flow == eConsole ?
155               &default_capture_device_id_ :
156               &default_communications_capture_device_id_
157       );
158
159   // If no device is now available, |new_default_device_id| will be NULL.
160   std::string new_device_id;
161   if (new_default_device_id)
162     new_device_id = base::WideToUTF8(new_default_device_id);
163
164   VLOG(1) << "OnDefaultDeviceChanged() "
165           << "new_default_device: "
166           << (new_default_device_id ?
167               CoreAudioUtil::GetFriendlyName(new_device_id) : "No device")
168           << ", flow: " << FlowToString(flow)
169           << ", role: " << RoleToString(role);
170
171   // Only fire a state change event if the device has actually changed.
172   // TODO(dalecurtis): This still seems to fire an extra event on my machine for
173   // an unplug event (probably others too); e.g., we get two transitions to a
174   // new default device id.
175   if (new_device_id.compare(*current_device_id) == 0)
176     return S_OK;
177
178   // Store the new id in the member variable (that current_device_id points to).
179   *current_device_id = new_device_id;
180   listener_cb_.Run();
181
182   return S_OK;
183 }
184
185 }  // namespace media