blob: abce101b1c3e9197ff88c972b726b90d07ae5b1b [file] [log] [blame]
// 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<AudioService::Observer> 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<AudioServiceImpl> 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<OutputDeviceInfo> 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<InputDeviceInfo> 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