// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/api/audio/audio_service.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_number_conversions.h" #include "chromeos/audio/audio_device.h" #include "chromeos/audio/cras_audio_handler.h" #include "chromeos/dbus/audio_node.h" #include "chromeos/dbus/cras_audio_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" using content::BrowserThread; namespace extensions { using api::audio::OutputDeviceInfo; using api::audio::InputDeviceInfo; class AudioServiceImpl : public AudioService, public chromeos::CrasAudioHandler::AudioObserver { public: AudioServiceImpl(); virtual ~AudioServiceImpl(); // Called by listeners to this service to add/remove themselves as observers. virtual void AddObserver(AudioService::Observer* observer) OVERRIDE; virtual void RemoveObserver(AudioService::Observer* observer) OVERRIDE; // Start to query audio device information. virtual void StartGetInfo(const GetInfoCallback& callback) OVERRIDE; virtual void SetActiveDevices(const DeviceIdList& device_list) OVERRIDE; virtual bool SetDeviceProperties(const std::string& device_id, bool muted, int volume, int gain) OVERRIDE; protected: // chromeos::CrasAudioHandler::AudioObserver overrides. virtual void OnOutputVolumeChanged() OVERRIDE; virtual void OnInputGainChanged() OVERRIDE; virtual void OnOutputMuteChanged() OVERRIDE; virtual void OnInputMuteChanged() OVERRIDE; virtual void OnAudioNodesChanged() OVERRIDE; virtual void OnActiveOutputNodeChanged() OVERRIDE; virtual void OnActiveInputNodeChanged() OVERRIDE; private: void NotifyDeviceChanged(); // Callback for CrasAudioClient::GetNodes(). void OnGetNodes(const GetInfoCallback& callback, const chromeos::AudioNodeList& audio_nodes, bool success); // ErrorCallback for CrasAudioClient::GetNodes(). void OnGetNodesError(const std::string& error_name, const std::string& error_msg); bool FindDevice(uint64 id, chromeos::AudioDevice* device); uint64 GetIdFromStr(const std::string& id_str); // List of observers. ObserverList observer_list_; chromeos::CrasAudioClient* cras_audio_client_; chromeos::CrasAudioHandler* cras_audio_handler_; // Note: This should remain the last member so it'll be destroyed and // invalidate the weak pointers before any other members are destroyed. base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(AudioServiceImpl); }; AudioServiceImpl::AudioServiceImpl() : cras_audio_client_(NULL), cras_audio_handler_(NULL), weak_ptr_factory_(this) { if (chromeos::DBusThreadManager::IsInitialized() && chromeos::DBusThreadManager::Get()) { cras_audio_client_ = chromeos::DBusThreadManager::Get()->GetCrasAudioClient(); if (chromeos::CrasAudioHandler::IsInitialized()) { cras_audio_handler_ = chromeos::CrasAudioHandler::Get(); cras_audio_handler_->AddAudioObserver(this); } } } AudioServiceImpl::~AudioServiceImpl() { if (cras_audio_handler_ && chromeos::CrasAudioHandler::IsInitialized()) { cras_audio_handler_->RemoveAudioObserver(this); } } void AudioServiceImpl::AddObserver(AudioService::Observer* observer) { observer_list_.AddObserver(observer); } void AudioServiceImpl::RemoveObserver(AudioService::Observer* observer) { observer_list_.RemoveObserver(observer); } void AudioServiceImpl::StartGetInfo(const GetInfoCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(cras_audio_client_); // TODO(jennyz,rkc): Replace cras_audio_client_ call with CrasAudioHandler // Api call. if (cras_audio_client_) cras_audio_client_->GetNodes(base::Bind(&AudioServiceImpl::OnGetNodes, weak_ptr_factory_.GetWeakPtr(), callback), base::Bind(&AudioServiceImpl::OnGetNodesError, weak_ptr_factory_.GetWeakPtr())); } void AudioServiceImpl::SetActiveDevices(const DeviceIdList& device_list) { DCHECK(cras_audio_handler_); if (!cras_audio_handler_) return; bool input_device_set = false; bool output_device_set = false; for (size_t i = 0; i < device_list.size(); ++i) { chromeos::AudioDevice device; bool found = FindDevice(GetIdFromStr(device_list[i]), &device); if (found) { if (device.is_input && !input_device_set) { cras_audio_handler_->SwitchToDevice(device); input_device_set = true; } else if (!device.is_input && !output_device_set) { cras_audio_handler_->SwitchToDevice(device); output_device_set = true; } } } } bool AudioServiceImpl::SetDeviceProperties(const std::string& device_id, bool muted, int volume, int gain) { DCHECK(cras_audio_handler_); if (!cras_audio_handler_) return false; chromeos::AudioDevice device; bool found = FindDevice(GetIdFromStr(device_id), &device); if (!found) return false; cras_audio_handler_->SetMuteForDevice(device.id, muted); if (!device.is_input && volume != -1) { cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, volume); return true; } else if (device.is_input && gain != -1) { cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, gain); return true; } return false; } void AudioServiceImpl::OnGetNodes(const GetInfoCallback& callback, const chromeos::AudioNodeList& audio_nodes, bool success) { OutputInfo output_info; InputInfo input_info; if (success) { for (chromeos::AudioNodeList::const_iterator iter = audio_nodes.begin(); iter != audio_nodes.end(); ++iter) { if (!iter->is_input) { linked_ptr info(new OutputDeviceInfo()); info->id = base::Uint64ToString(iter->id); info->name = iter->device_name + ": " + iter->name; info->is_active = iter->active; info->volume = cras_audio_handler_->GetOutputVolumePercentForDevice( iter->id); info->is_muted = cras_audio_handler_->IsOutputMutedForDevice(iter->id); output_info.push_back(info); } else { linked_ptr info(new InputDeviceInfo()); info->id = base::Uint64ToString(iter->id); info->name = iter->device_name + ": " + iter->name; info->is_active = iter->active; info->gain = cras_audio_handler_->GetInputGainPercentForDevice( iter->id); info->is_muted = cras_audio_handler_->IsInputMutedForDevice(iter->id); input_info.push_back(info); } } } DCHECK(!callback.is_null()); if (!callback.is_null()) callback.Run(output_info, input_info, success); } void AudioServiceImpl::OnGetNodesError(const std::string& error_name, const std::string& error_msg) { } bool AudioServiceImpl::FindDevice(uint64 id, chromeos::AudioDevice* device) { chromeos::AudioDeviceList devices; cras_audio_handler_->GetAudioDevices(&devices); for (size_t i = 0; i < devices.size(); ++i) { if (devices[i].id == id) { *device = devices[i]; return true; } } return false; } uint64 AudioServiceImpl::GetIdFromStr(const std::string& id_str) { uint64 device_id; if (!base::StringToUint64(id_str, &device_id)) return 0; else return device_id; } void AudioServiceImpl::OnOutputVolumeChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnOutputMuteChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnInputGainChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnInputMuteChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnAudioNodesChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnActiveOutputNodeChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::OnActiveInputNodeChanged() { NotifyDeviceChanged(); } void AudioServiceImpl::NotifyDeviceChanged() { FOR_EACH_OBSERVER(AudioService::Observer, observer_list_, OnDeviceChanged()); } AudioService* AudioService::CreateInstance() { return new AudioServiceImpl; } } // namespace extensions