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.
5 #include "extensions/browser/api/audio/audio_service.h"
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"
14 using content::BrowserThread
;
16 namespace extensions
{
18 using core_api::audio::OutputDeviceInfo
;
19 using core_api::audio::InputDeviceInfo
;
20 using core_api::audio::AudioDeviceInfo
;
22 class AudioServiceImpl
: public AudioService
,
23 public chromeos::CrasAudioHandler::AudioObserver
{
26 ~AudioServiceImpl() override
;
28 // Called by listeners to this service to add/remove themselves as observers.
29 void AddObserver(AudioService::Observer
* observer
) override
;
30 void RemoveObserver(AudioService::Observer
* observer
) override
;
32 // Start to query audio device information.
33 void StartGetInfo(const GetInfoCallback
& callback
) override
;
34 void SetActiveDevices(const DeviceIdList
& device_list
) override
;
35 bool SetDeviceProperties(const std::string
& device_id
,
41 // chromeos::CrasAudioHandler::AudioObserver overrides.
42 void OnOutputNodeVolumeChanged(uint64_t id
, int volume
) override
;
43 void OnInputNodeGainChanged(uint64_t id
, int gain
) override
;
44 void OnOutputMuteChanged(bool mute_on
) override
;
45 void OnInputMuteChanged(bool mute_on
) override
;
46 void OnAudioNodesChanged() override
;
47 void OnActiveOutputNodeChanged() override
;
48 void OnActiveInputNodeChanged() override
;
51 void NotifyDeviceChanged();
52 void NotifyLevelChanged(uint64_t id
, int level
);
53 void NotifyMuteChanged(bool is_input
, bool is_muted
);
54 void NotifyDevicesChanged();
56 bool FindDevice(uint64_t id
, chromeos::AudioDevice
* device
);
57 uint64_t GetIdFromStr(const std::string
& id_str
);
60 base::ObserverList
<AudioService::Observer
> observer_list_
;
62 chromeos::CrasAudioHandler
* cras_audio_handler_
;
64 // Note: This should remain the last member so it'll be destroyed and
65 // invalidate the weak pointers before any other members are destroyed.
66 base::WeakPtrFactory
<AudioServiceImpl
> weak_ptr_factory_
;
68 DISALLOW_COPY_AND_ASSIGN(AudioServiceImpl
);
71 AudioServiceImpl::AudioServiceImpl()
72 : cras_audio_handler_(NULL
),
73 weak_ptr_factory_(this) {
74 if (chromeos::CrasAudioHandler::IsInitialized()) {
75 cras_audio_handler_
= chromeos::CrasAudioHandler::Get();
76 cras_audio_handler_
->AddAudioObserver(this);
80 AudioServiceImpl::~AudioServiceImpl() {
81 if (cras_audio_handler_
&& chromeos::CrasAudioHandler::IsInitialized()) {
82 cras_audio_handler_
->RemoveAudioObserver(this);
86 void AudioServiceImpl::AddObserver(AudioService::Observer
* observer
) {
87 observer_list_
.AddObserver(observer
);
90 void AudioServiceImpl::RemoveObserver(AudioService::Observer
* observer
) {
91 observer_list_
.RemoveObserver(observer
);
94 void AudioServiceImpl::StartGetInfo(const GetInfoCallback
& callback
) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
96 DCHECK(cras_audio_handler_
);
97 DCHECK(!callback
.is_null());
99 if (callback
.is_null())
102 OutputInfo output_info
;
103 InputInfo input_info
;
104 if (!cras_audio_handler_
) {
105 callback
.Run(output_info
, input_info
, false);
109 chromeos::AudioDeviceList devices
;
110 cras_audio_handler_
->GetAudioDevices(&devices
);
111 for (size_t i
= 0; i
< devices
.size(); ++i
) {
112 if (!devices
[i
].is_input
) {
113 linked_ptr
<OutputDeviceInfo
> info(new OutputDeviceInfo());
114 info
->id
= base::Uint64ToString(devices
[i
].id
);
115 info
->name
= devices
[i
].device_name
+ ": " + devices
[i
].display_name
;
116 info
->is_active
= devices
[i
].active
;
118 cras_audio_handler_
->GetOutputVolumePercentForDevice(devices
[i
].id
);
120 cras_audio_handler_
->IsOutputMutedForDevice(devices
[i
].id
);
121 output_info
.push_back(info
);
123 linked_ptr
<InputDeviceInfo
> info(new InputDeviceInfo());
124 info
->id
= base::Uint64ToString(devices
[i
].id
);
125 info
->name
= devices
[i
].device_name
+ ": " + devices
[i
].display_name
;
126 info
->is_active
= devices
[i
].active
;
128 cras_audio_handler_
->GetInputGainPercentForDevice(devices
[i
].id
);
130 cras_audio_handler_
->IsInputMutedForDevice(devices
[i
].id
);
131 input_info
.push_back(info
);
134 callback
.Run(output_info
, input_info
, true);
137 void AudioServiceImpl::SetActiveDevices(const DeviceIdList
& device_list
) {
138 DCHECK(cras_audio_handler_
);
139 if (!cras_audio_handler_
)
142 chromeos::CrasAudioHandler::NodeIdList id_list
;
143 for (size_t i
= 0; i
< device_list
.size(); ++i
) {
144 chromeos::AudioDevice device
;
145 if (FindDevice(GetIdFromStr(device_list
[i
]), &device
))
146 id_list
.push_back(device
.id
);
148 cras_audio_handler_
->ChangeActiveNodes(id_list
);
151 bool AudioServiceImpl::SetDeviceProperties(const std::string
& device_id
,
155 DCHECK(cras_audio_handler_
);
156 if (!cras_audio_handler_
)
159 chromeos::AudioDevice device
;
160 bool found
= FindDevice(GetIdFromStr(device_id
), &device
);
164 cras_audio_handler_
->SetMuteForDevice(device
.id
, muted
);
166 if (!device
.is_input
&& volume
!= -1) {
167 cras_audio_handler_
->SetVolumeGainPercentForDevice(device
.id
, volume
);
169 } else if (device
.is_input
&& gain
!= -1) {
170 cras_audio_handler_
->SetVolumeGainPercentForDevice(device
.id
, gain
);
177 bool AudioServiceImpl::FindDevice(uint64_t id
, chromeos::AudioDevice
* device
) {
178 chromeos::AudioDeviceList devices
;
179 cras_audio_handler_
->GetAudioDevices(&devices
);
181 for (size_t i
= 0; i
< devices
.size(); ++i
) {
182 if (devices
[i
].id
== id
) {
183 *device
= devices
[i
];
190 uint64_t AudioServiceImpl::GetIdFromStr(const std::string
& id_str
) {
192 if (!base::StringToUint64(id_str
, &device_id
))
198 void AudioServiceImpl::OnOutputNodeVolumeChanged(uint64_t id
, int volume
) {
199 NotifyLevelChanged(id
, volume
);
202 void AudioServiceImpl::OnOutputMuteChanged(bool mute_on
) {
203 NotifyMuteChanged(false, mute_on
);
206 void AudioServiceImpl::OnInputNodeGainChanged(uint64_t id
, int gain
) {
207 NotifyLevelChanged(id
, gain
);
210 void AudioServiceImpl::OnInputMuteChanged(bool mute_on
) {
211 NotifyMuteChanged(true, mute_on
);
214 void AudioServiceImpl::OnAudioNodesChanged() {
215 NotifyDevicesChanged();
218 void AudioServiceImpl::OnActiveOutputNodeChanged() {
219 NotifyDeviceChanged();
222 void AudioServiceImpl::OnActiveInputNodeChanged() {
223 NotifyDeviceChanged();
226 void AudioServiceImpl::NotifyDeviceChanged() {
227 FOR_EACH_OBSERVER(AudioService::Observer
, observer_list_
, OnDeviceChanged());
230 void AudioServiceImpl::NotifyLevelChanged(uint64_t id
, int level
) {
231 FOR_EACH_OBSERVER(AudioService::Observer
, observer_list_
,
232 OnLevelChanged(base::Uint64ToString(id
), level
));
234 // Notify DeviceChanged event for backward compatibility.
235 // TODO(jennyz): remove this code when the old version of hotrod retires.
236 NotifyDeviceChanged();
239 void AudioServiceImpl::NotifyMuteChanged(bool is_input
, bool is_muted
) {
240 FOR_EACH_OBSERVER(AudioService::Observer
, observer_list_
,
241 OnMuteChanged(is_input
, is_muted
));
243 // Notify DeviceChanged event for backward compatibility.
244 // TODO(jennyz): remove this code when the old version of hotrod retires.
245 NotifyDeviceChanged();
248 void AudioServiceImpl::NotifyDevicesChanged() {
249 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
250 DCHECK(cras_audio_handler_
);
252 DeviceInfoList devices_info_list
;
253 chromeos::AudioDeviceList devices
;
254 cras_audio_handler_
->GetAudioDevices(&devices
);
255 for (size_t i
= 0; i
< devices
.size(); ++i
) {
256 linked_ptr
<AudioDeviceInfo
> info(new AudioDeviceInfo());
257 info
->id
= base::Uint64ToString(devices
[i
].id
);
258 info
->is_input
= devices
[i
].is_input
;
259 info
->device_type
= chromeos::AudioDevice::GetTypeString(devices
[i
].type
);
260 info
->display_name
= devices
[i
].display_name
;
261 info
->device_name
= devices
[i
].device_name
;
262 info
->is_active
= devices
[i
].active
;
265 ? cras_audio_handler_
->IsInputMutedForDevice(devices
[i
].id
)
266 : cras_audio_handler_
->IsOutputMutedForDevice(devices
[i
].id
);
269 ? cras_audio_handler_
->GetOutputVolumePercentForDevice(
271 : cras_audio_handler_
->GetInputGainPercentForDevice(devices
[i
].id
);
272 // TODO(jennyz): Set stable_device_id once it is implemented.
273 // See crbug.com/466768.
275 devices_info_list
.push_back(info
);
278 FOR_EACH_OBSERVER(AudioService::Observer
, observer_list_
,
279 OnDevicesChanged(devices_info_list
));
281 // Notify DeviceChanged event for backward compatibility.
282 // TODO(jennyz): remove this code when the old version of hotrod retires.
283 NotifyDeviceChanged();
286 AudioService
* AudioService::CreateInstance() {
287 return new AudioServiceImpl
;
290 } // namespace extensions