- add sources.
[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 AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb)
34     : listener_cb_(listener_cb) {
35   CHECK(CoreAudioUtil::IsSupported());
36
37   ScopedComPtr<IMMDeviceEnumerator> device_enumerator(
38       CoreAudioUtil::CreateDeviceEnumerator());
39   if (!device_enumerator)
40     return;
41
42   HRESULT hr = device_enumerator->RegisterEndpointNotificationCallback(this);
43   if (FAILED(hr)) {
44     LOG(ERROR)  << "RegisterEndpointNotificationCallback failed: "
45                 << std::hex << hr;
46     return;
47   }
48
49   device_enumerator_ = device_enumerator;
50
51   ScopedComPtr<IMMDevice> device =
52       CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
53   if (!device) {
54     // Most probable reason for ending up here is that all audio devices are
55     // disabled or unplugged.
56     VLOG(1)  << "CoreAudioUtil::CreateDefaultDevice failed. No device?";
57     return;
58   }
59
60   AudioDeviceName device_name;
61   hr = CoreAudioUtil::GetDeviceName(device, &device_name);
62   if (FAILED(hr)) {
63     VLOG(1)  << "Failed to retrieve the device id: " << std::hex << hr;
64     return;
65   }
66   default_render_device_id_ = device_name.unique_id;
67 }
68
69 AudioDeviceListenerWin::~AudioDeviceListenerWin() {
70   DCHECK(thread_checker_.CalledOnValidThread());
71   if (device_enumerator_) {
72     HRESULT hr =
73         device_enumerator_->UnregisterEndpointNotificationCallback(this);
74     LOG_IF(ERROR, FAILED(hr)) << "UnregisterEndpointNotificationCallback() "
75                               << "failed: " << std::hex << hr;
76   }
77 }
78
79 STDMETHODIMP_(ULONG) AudioDeviceListenerWin::AddRef() {
80   return 1;
81 }
82
83 STDMETHODIMP_(ULONG) AudioDeviceListenerWin::Release() {
84   return 1;
85 }
86
87 STDMETHODIMP AudioDeviceListenerWin::QueryInterface(REFIID iid, void** object) {
88   if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) {
89     *object = static_cast<IMMNotificationClient*>(this);
90     return S_OK;
91   }
92
93   *object = NULL;
94   return E_NOINTERFACE;
95 }
96
97 STDMETHODIMP AudioDeviceListenerWin::OnPropertyValueChanged(
98     LPCWSTR device_id, const PROPERTYKEY key) {
99   // TODO(dalecurtis): We need to handle changes for the current default device
100   // here.  It's tricky because this method may be called many (20+) times for
101   // a single change like sample rate.  http://crbug.com/153056
102   return S_OK;
103 }
104
105 STDMETHODIMP AudioDeviceListenerWin::OnDeviceAdded(LPCWSTR device_id) {
106   // We don't care when devices are added.
107   return S_OK;
108 }
109
110 STDMETHODIMP AudioDeviceListenerWin::OnDeviceRemoved(LPCWSTR device_id) {
111   // We don't care when devices are removed.
112   return S_OK;
113 }
114
115 STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id,
116                                                           DWORD new_state) {
117   if (new_state != DEVICE_STATE_ACTIVE && new_state != DEVICE_STATE_NOTPRESENT)
118     return S_OK;
119
120   base::SystemMonitor* monitor = base::SystemMonitor::Get();
121   if (monitor)
122     monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
123
124   return S_OK;
125 }
126
127 STDMETHODIMP AudioDeviceListenerWin::OnDefaultDeviceChanged(
128     EDataFlow flow, ERole role, LPCWSTR new_default_device_id) {
129   // Only listen for output device changes right now...
130   if (flow != eConsole && role != eRender)
131     return S_OK;
132
133   // If no device is now available, |new_default_device_id| will be NULL.
134   std::string new_device_id;
135   if (new_default_device_id)
136     new_device_id = WideToUTF8(new_default_device_id);
137
138   VLOG(1) << "OnDefaultDeviceChanged() "
139           << "new_default_device: "
140           << (new_default_device_id ?
141               CoreAudioUtil::GetFriendlyName(new_device_id) : "No device")
142           << ", flow: " << FlowToString(flow)
143           << ", role: " << RoleToString(role);
144
145   // Only fire a state change event if the device has actually changed.
146   // TODO(dalecurtis): This still seems to fire an extra event on my machine for
147   // an unplug event (probably others too); e.g., we get two transitions to a
148   // new default device id.
149   if (new_device_id.compare(default_render_device_id_) == 0)
150     return S_OK;
151
152   default_render_device_id_ = new_device_id;
153   listener_cb_.Run();
154
155   return S_OK;
156 }
157
158 }  // namespace media