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 chromeos::CrasAudioHandler::NodeIdList id_list;
139 for (size_t i = 0; i < device_list.size(); ++i) {
140 chromeos::AudioDevice device;
141 if (FindDevice(GetIdFromStr(device_list[i]), &device))
142 id_list.push_back(device.id);
143 }
144 cras_audio_handler_->ChangeActiveNodes(id_list);
145 }
146
SetDeviceProperties(const std::string & device_id,bool muted,int volume,int gain)147 bool AudioServiceImpl::SetDeviceProperties(const std::string& device_id,
148 bool muted,
149 int volume,
150 int gain) {
151 DCHECK(cras_audio_handler_);
152 if (!cras_audio_handler_)
153 return false;
154
155 chromeos::AudioDevice device;
156 bool found = FindDevice(GetIdFromStr(device_id), &device);
157 if (!found)
158 return false;
159
160 cras_audio_handler_->SetMuteForDevice(device.id, muted);
161
162 if (!device.is_input && volume != -1) {
163 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, volume);
164 return true;
165 } else if (device.is_input && gain != -1) {
166 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, gain);
167 return true;
168 }
169
170 return false;
171 }
172
FindDevice(uint64 id,chromeos::AudioDevice * device)173 bool AudioServiceImpl::FindDevice(uint64 id, chromeos::AudioDevice* device) {
174 chromeos::AudioDeviceList devices;
175 cras_audio_handler_->GetAudioDevices(&devices);
176
177 for (size_t i = 0; i < devices.size(); ++i) {
178 if (devices[i].id == id) {
179 *device = devices[i];
180 return true;
181 }
182 }
183 return false;
184 }
185
GetIdFromStr(const std::string & id_str)186 uint64 AudioServiceImpl::GetIdFromStr(const std::string& id_str) {
187 uint64 device_id;
188 if (!base::StringToUint64(id_str, &device_id))
189 return 0;
190 else
191 return device_id;
192 }
193
OnOutputVolumeChanged()194 void AudioServiceImpl::OnOutputVolumeChanged() {
195 NotifyDeviceChanged();
196 }
197
OnOutputMuteChanged()198 void AudioServiceImpl::OnOutputMuteChanged() {
199 NotifyDeviceChanged();
200 }
201
OnInputGainChanged()202 void AudioServiceImpl::OnInputGainChanged() {
203 NotifyDeviceChanged();
204 }
205
OnInputMuteChanged()206 void AudioServiceImpl::OnInputMuteChanged() {
207 NotifyDeviceChanged();
208 }
209
OnAudioNodesChanged()210 void AudioServiceImpl::OnAudioNodesChanged() {
211 NotifyDeviceChanged();
212 }
213
OnActiveOutputNodeChanged()214 void AudioServiceImpl::OnActiveOutputNodeChanged() {
215 NotifyDeviceChanged();
216 }
217
OnActiveInputNodeChanged()218 void AudioServiceImpl::OnActiveInputNodeChanged() {
219 NotifyDeviceChanged();
220 }
221
NotifyDeviceChanged()222 void AudioServiceImpl::NotifyDeviceChanged() {
223 FOR_EACH_OBSERVER(AudioService::Observer, observer_list_, OnDeviceChanged());
224 }
225
CreateInstance()226 AudioService* AudioService::CreateInstance() {
227 return new AudioServiceImpl;
228 }
229
230 } // namespace extensions
231