1 // Copyright (c) 2013 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 "chrome/browser/extensions/api/audio/audio_service.h"
6
7 #include "base/callback.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "chromeos/audio/audio_device.h"
11 #include "chromeos/audio/cras_audio_handler.h"
12 #include "content/public/browser/browser_thread.h"
13
14 using content::BrowserThread;
15
16 namespace extensions {
17
18 using api::audio::OutputDeviceInfo;
19 using api::audio::InputDeviceInfo;
20
21 class AudioServiceImpl : public AudioService,
22 public chromeos::CrasAudioHandler::AudioObserver {
23 public:
24 AudioServiceImpl();
25 virtual ~AudioServiceImpl();
26
27 // Called by listeners to this service to add/remove themselves as observers.
28 virtual void AddObserver(AudioService::Observer* observer) OVERRIDE;
29 virtual void RemoveObserver(AudioService::Observer* observer) OVERRIDE;
30
31 // Start to query audio device information.
32 virtual void StartGetInfo(const GetInfoCallback& callback) OVERRIDE;
33 virtual void SetActiveDevices(const DeviceIdList& device_list) OVERRIDE;
34 virtual bool SetDeviceProperties(const std::string& device_id,
35 bool muted,
36 int volume,
37 int gain) OVERRIDE;
38
39 protected:
40 // chromeos::CrasAudioHandler::AudioObserver overrides.
41 virtual void OnOutputVolumeChanged() OVERRIDE;
42 virtual void OnInputGainChanged() OVERRIDE;
43 virtual void OnOutputMuteChanged() OVERRIDE;
44 virtual void OnInputMuteChanged() OVERRIDE;
45 virtual void OnAudioNodesChanged() OVERRIDE;
46 virtual void OnActiveOutputNodeChanged() OVERRIDE;
47 virtual void OnActiveInputNodeChanged() OVERRIDE;
48
49 private:
50 void NotifyDeviceChanged();
51
52 bool FindDevice(uint64 id, chromeos::AudioDevice* device);
53 uint64 GetIdFromStr(const std::string& id_str);
54
55 // List of observers.
56 ObserverList<AudioService::Observer> observer_list_;
57
58 chromeos::CrasAudioHandler* cras_audio_handler_;
59
60 // Note: This should remain the last member so it'll be destroyed and
61 // invalidate the weak pointers before any other members are destroyed.
62 base::WeakPtrFactory<AudioServiceImpl> weak_ptr_factory_;
63
64 DISALLOW_COPY_AND_ASSIGN(AudioServiceImpl);
65 };
66
AudioServiceImpl()67 AudioServiceImpl::AudioServiceImpl()
68 : cras_audio_handler_(NULL),
69 weak_ptr_factory_(this) {
70 if (chromeos::CrasAudioHandler::IsInitialized()) {
71 cras_audio_handler_ = chromeos::CrasAudioHandler::Get();
72 cras_audio_handler_->AddAudioObserver(this);
73 }
74 }
75
~AudioServiceImpl()76 AudioServiceImpl::~AudioServiceImpl() {
77 if (cras_audio_handler_ && chromeos::CrasAudioHandler::IsInitialized()) {
78 cras_audio_handler_->RemoveAudioObserver(this);
79 }
80 }
81
AddObserver(AudioService::Observer * observer)82 void AudioServiceImpl::AddObserver(AudioService::Observer* observer) {
83 observer_list_.AddObserver(observer);
84 }
85
RemoveObserver(AudioService::Observer * observer)86 void AudioServiceImpl::RemoveObserver(AudioService::Observer* observer) {
87 observer_list_.RemoveObserver(observer);
88 }
89
StartGetInfo(const GetInfoCallback & callback)90 void AudioServiceImpl::StartGetInfo(const GetInfoCallback& callback) {
91 DCHECK_CURRENTLY_ON(BrowserThread::UI);
92 DCHECK(cras_audio_handler_);
93 DCHECK(!callback.is_null());
94
95 if (callback.is_null())
96 return;
97
98 OutputInfo output_info;
99 InputInfo input_info;
100 if (!cras_audio_handler_) {
101 callback.Run(output_info, input_info, false);
102 return;
103 }
104
105 chromeos::AudioDeviceList devices;
106 cras_audio_handler_->GetAudioDevices(&devices);
107 for (size_t i = 0; i < devices.size(); ++i) {
108 if (!devices[i].is_input) {
109 linked_ptr<OutputDeviceInfo> info(new OutputDeviceInfo());
110 info->id = base::Uint64ToString(devices[i].id);
111 info->name = devices[i].device_name + ": " + devices[i].display_name;
112 info->is_active = devices[i].active;
113 info->volume =
114 cras_audio_handler_->GetOutputVolumePercentForDevice(devices[i].id);
115 info->is_muted =
116 cras_audio_handler_->IsOutputMutedForDevice(devices[i].id);
117 output_info.push_back(info);
118 } else {
119 linked_ptr<InputDeviceInfo> info(new InputDeviceInfo());
120 info->id = base::Uint64ToString(devices[i].id);
121 info->name = devices[i].device_name + ": " + devices[i].display_name;
122 info->is_active = devices[i].active;
123 info->gain =
124 cras_audio_handler_->GetInputGainPercentForDevice(devices[i].id);
125 info->is_muted =
126 cras_audio_handler_->IsInputMutedForDevice(devices[i].id);
127 input_info.push_back(info);
128 }
129 }
130 callback.Run(output_info, input_info, true);
131 }
132
SetActiveDevices(const DeviceIdList & device_list)133 void AudioServiceImpl::SetActiveDevices(const DeviceIdList& device_list) {
134 DCHECK(cras_audio_handler_);
135 if (!cras_audio_handler_)
136 return;
137
138 bool input_device_set = false;
139 bool output_device_set = false;
140
141 for (size_t i = 0; i < device_list.size(); ++i) {
142 chromeos::AudioDevice device;
143 bool found = FindDevice(GetIdFromStr(device_list[i]), &device);
144 if (found) {
145 if (device.is_input && !input_device_set) {
146 cras_audio_handler_->SwitchToDevice(device);
147 input_device_set = true;
148 } else if (!device.is_input && !output_device_set) {
149 cras_audio_handler_->SwitchToDevice(device);
150 output_device_set = true;
151 }
152 }
153 }
154 }
155
SetDeviceProperties(const std::string & device_id,bool muted,int volume,int gain)156 bool AudioServiceImpl::SetDeviceProperties(const std::string& device_id,
157 bool muted,
158 int volume,
159 int gain) {
160 DCHECK(cras_audio_handler_);
161 if (!cras_audio_handler_)
162 return false;
163
164 chromeos::AudioDevice device;
165 bool found = FindDevice(GetIdFromStr(device_id), &device);
166 if (!found)
167 return false;
168
169 cras_audio_handler_->SetMuteForDevice(device.id, muted);
170
171 if (!device.is_input && volume != -1) {
172 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, volume);
173 return true;
174 } else if (device.is_input && gain != -1) {
175 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, gain);
176 return true;
177 }
178
179 return false;
180 }
181
FindDevice(uint64 id,chromeos::AudioDevice * device)182 bool AudioServiceImpl::FindDevice(uint64 id, chromeos::AudioDevice* device) {
183 chromeos::AudioDeviceList devices;
184 cras_audio_handler_->GetAudioDevices(&devices);
185
186 for (size_t i = 0; i < devices.size(); ++i) {
187 if (devices[i].id == id) {
188 *device = devices[i];
189 return true;
190 }
191 }
192 return false;
193 }
194
GetIdFromStr(const std::string & id_str)195 uint64 AudioServiceImpl::GetIdFromStr(const std::string& id_str) {
196 uint64 device_id;
197 if (!base::StringToUint64(id_str, &device_id))
198 return 0;
199 else
200 return device_id;
201 }
202
OnOutputVolumeChanged()203 void AudioServiceImpl::OnOutputVolumeChanged() {
204 NotifyDeviceChanged();
205 }
206
OnOutputMuteChanged()207 void AudioServiceImpl::OnOutputMuteChanged() {
208 NotifyDeviceChanged();
209 }
210
OnInputGainChanged()211 void AudioServiceImpl::OnInputGainChanged() {
212 NotifyDeviceChanged();
213 }
214
OnInputMuteChanged()215 void AudioServiceImpl::OnInputMuteChanged() {
216 NotifyDeviceChanged();
217 }
218
OnAudioNodesChanged()219 void AudioServiceImpl::OnAudioNodesChanged() {
220 NotifyDeviceChanged();
221 }
222
OnActiveOutputNodeChanged()223 void AudioServiceImpl::OnActiveOutputNodeChanged() {
224 NotifyDeviceChanged();
225 }
226
OnActiveInputNodeChanged()227 void AudioServiceImpl::OnActiveInputNodeChanged() {
228 NotifyDeviceChanged();
229 }
230
NotifyDeviceChanged()231 void AudioServiceImpl::NotifyDeviceChanged() {
232 FOR_EACH_OBSERVER(AudioService::Observer, observer_list_, OnDeviceChanged());
233 }
234
CreateInstance()235 AudioService* AudioService::CreateInstance() {
236 return new AudioServiceImpl;
237 }
238
239 } // namespace extensions
240