| // Copyright 2020 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 "media/audio/cras/audio_manager_cras.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/check_op.h" |
| #include "base/command_line.h" |
| #include "base/environment.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/nix/xdg_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/system/sys_info.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "media/audio/audio_device_description.h" |
| #include "media/audio/audio_features.h" |
| #include "media/audio/cras/cras_input.h" |
| #include "media/audio/cras/cras_unified.h" |
| #include "media/audio/cras/cras_util.h" |
| #include "media/base/channel_layout.h" |
| #include "media/base/limits.h" |
| #include "media/base/localized_strings.h" |
| |
| namespace media { |
| namespace { |
| |
| // Default sample rate for input and output streams. |
| const int kDefaultSampleRate = 48000; |
| |
| // Default input buffer size. |
| const int kDefaultInputBufferSize = 1024; |
| |
| // Default output buffer size. |
| const int kDefaultOutputBufferSize = 512; |
| |
| } // namespace |
| |
| bool AudioManagerCras::HasAudioOutputDevices() { |
| return true; |
| } |
| |
| bool AudioManagerCras::HasAudioInputDevices() { |
| return !CrasGetAudioDevices(DeviceType::kInput).empty(); |
| } |
| |
| AudioManagerCras::AudioManagerCras( |
| std::unique_ptr<AudioThread> audio_thread, |
| AudioLogFactory* audio_log_factory) |
| : AudioManagerCrasBase(std::move(audio_thread), audio_log_factory), |
| main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| weak_ptr_factory_(this) { |
| weak_this_ = weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| AudioManagerCras::~AudioManagerCras() = default; |
| |
| void AudioManagerCras::GetAudioInputDeviceNames( |
| AudioDeviceNames* device_names) { |
| device_names->push_back(AudioDeviceName::CreateDefault()); |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) { |
| device_names->emplace_back(device.name, base::NumberToString(device.id)); |
| } |
| } |
| |
| void AudioManagerCras::GetAudioOutputDeviceNames( |
| AudioDeviceNames* device_names) { |
| device_names->push_back(AudioDeviceName::CreateDefault()); |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) { |
| device_names->emplace_back(device.name, base::NumberToString(device.id)); |
| } |
| } |
| |
| AudioParameters AudioManagerCras::GetInputStreamParameters( |
| const std::string& device_id) { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| |
| int user_buffer_size = GetUserBufferSize(); |
| int buffer_size = |
| user_buffer_size ? user_buffer_size : kDefaultInputBufferSize; |
| |
| AudioParameters params( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, |
| kDefaultSampleRate, buffer_size, |
| AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, |
| limits::kMaxAudioBufferSize)); |
| |
| if (CrasHasKeyboardMic()) |
| params.set_effects(AudioParameters::KEYBOARD_MIC); |
| |
| // Allow experimentation with system echo cancellation with all devices, |
| // but enable it by default on devices that actually support it. |
| params.set_effects(params.effects() | |
| AudioParameters::EXPERIMENTAL_ECHO_CANCELLER); |
| if (base::FeatureList::IsEnabled(features::kCrOSSystemAEC)) { |
| if (CrasGetAecSupported()) { |
| const int32_t aec_group_id = CrasGetAecGroupId(); |
| |
| // Check if the system AEC has a group ID which is flagged to be |
| // deactivated by the field trial. |
| const bool system_aec_deactivated = |
| base::GetFieldTrialParamByFeatureAsBool( |
| features::kCrOSSystemAECDeactivatedGroups, |
| base::NumberToString(aec_group_id), false); |
| |
| if (!system_aec_deactivated) { |
| params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER); |
| } |
| } |
| } |
| |
| return params; |
| } |
| |
| std::string AudioManagerCras::GetDefaultInputDeviceID() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| return base::NumberToString(GetPrimaryActiveInputNode()); |
| } |
| |
| std::string AudioManagerCras::GetDefaultOutputDeviceID() { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| return base::NumberToString(GetPrimaryActiveOutputNode()); |
| } |
| |
| std::string AudioManagerCras::GetGroupIDInput(const std::string& device_id) { |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) { |
| if (base::NumberToString(device.id) == device_id || |
| (AudioDeviceDescription::IsDefaultDevice(device_id) && device.active)) { |
| return device.dev_name; |
| } |
| } |
| return ""; |
| } |
| |
| std::string AudioManagerCras::GetGroupIDOutput(const std::string& device_id) { |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) { |
| if (base::NumberToString(device.id) == device_id || |
| (AudioDeviceDescription::IsDefaultDevice(device_id) && device.active)) { |
| return device.dev_name; |
| } |
| } |
| return ""; |
| } |
| |
| std::string AudioManagerCras::GetAssociatedOutputDeviceID( |
| const std::string& input_device_id) { |
| if (AudioDeviceDescription::IsDefaultDevice(input_device_id)) { |
| // Note: the default input should not be associated to any output, as this |
| // may lead to accidental uses of a pinned stream. |
| return ""; |
| } |
| |
| std::string device_name = GetGroupIDInput(input_device_id); |
| |
| if (device_name.empty()) |
| return ""; |
| |
| // Now search for an output device with the same device name. |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) { |
| if (device.dev_name == device_name) |
| return base::NumberToString(device.id); |
| } |
| return ""; |
| } |
| |
| AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( |
| const std::string& output_device_id, |
| const AudioParameters& input_params) { |
| DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; |
| int sample_rate = kDefaultSampleRate; |
| int buffer_size = GetUserBufferSize(); |
| if (input_params.IsValid()) { |
| channel_layout = input_params.channel_layout(); |
| sample_rate = input_params.sample_rate(); |
| if (!buffer_size) // Not user-provided. |
| buffer_size = |
| std::min(static_cast<int>(limits::kMaxAudioBufferSize), |
| std::max(static_cast<int>(limits::kMinAudioBufferSize), |
| input_params.frames_per_buffer())); |
| } |
| |
| if (!buffer_size) // Not user-provided. |
| buffer_size = CrasGetDefaultOutputBufferSize(); |
| |
| if (buffer_size <= 0) |
| buffer_size = kDefaultOutputBufferSize; |
| |
| return AudioParameters( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate, |
| buffer_size, |
| AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize, |
| limits::kMaxAudioBufferSize)); |
| } |
| |
| uint64_t AudioManagerCras::GetPrimaryActiveInputNode() { |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) { |
| if (device.active) |
| return device.id; |
| } |
| return 0; |
| } |
| |
| uint64_t AudioManagerCras::GetPrimaryActiveOutputNode() { |
| for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) { |
| if (device.active) |
| return device.id; |
| } |
| return 0; |
| } |
| |
| bool AudioManagerCras::IsDefault(const std::string& device_id, bool is_input) { |
| return AudioDeviceDescription::IsDefaultDevice(device_id); |
| } |
| |
| enum CRAS_CLIENT_TYPE AudioManagerCras::GetClientType() { |
| return CRAS_CLIENT_TYPE_LACROS; |
| } |
| |
| } // namespace media |