#include "media/audio/pulse/audio_manager_pulse.h"
#include <algorithm>
#include <utility>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/environment.h"
#include "base/logging.h"
#include "base/nix/xdg_util.h"
#include "build/build_config.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/pulse/pulse_input.h"
#include "media/audio/pulse/pulse_loopback_manager.h"
#include "media/audio/pulse/pulse_output.h"
#include "media/audio/pulse/pulse_util.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_layout.h"
namespace media {
using pulse::AutoPulseLock;
using pulse::WaitForOperationCompletion;
constexpr int kMaxOutputStreams = 50;
constexpr int kMinimumOutputBufferSize = 512;
constexpr int kMaximumOutputBufferSize = 8192;
constexpr int kDefaultInputBufferSize = 1024;
constexpr int kDefaultSampleRate = 48000;
constexpr int kDefaultChannelCount = 2;
AudioManagerPulse::AudioManagerPulse(std::unique_ptr<AudioThread> audio_thread,
AudioLogFactory* audio_log_factory,
pa_threaded_mainloop* pa_mainloop,
pa_context* pa_context)
: AudioManagerBase(std::move(audio_thread), audio_log_factory),
input_mainloop_(pa_mainloop),
input_context_(pa_context),
devices_(nullptr),
native_input_sample_rate_(kDefaultSampleRate),
native_channel_count_(kDefaultChannelCount),
default_source_is_monitor_(false) {
DCHECK(input_mainloop_);
DCHECK(input_context_);
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
}
AudioManagerPulse::~AudioManagerPulse() = default;
void AudioManagerPulse::ShutdownOnAudioThread() {
AudioManagerBase::ShutdownOnAudioThread();
pulse::DestroyPulse(input_mainloop_.ExtractAsDangling(),
input_context_.ExtractAsDangling());
}
bool AudioManagerPulse::HasAudioOutputDevices() {
AudioDeviceNames devices;
GetAudioOutputDeviceNames(&devices);
return !devices.empty();
}
bool AudioManagerPulse::HasAudioInputDevices() {
AudioDeviceNames devices;
GetAudioInputDeviceNames(&devices);
return !devices.empty();
}
void AudioManagerPulse::GetAudioDeviceNames(
bool input, media::AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
DCHECK(input_mainloop_);
DCHECK(input_context_);
AutoPulseLock auto_lock(input_mainloop_);
devices_ = device_names;
pa_operation* operation = NULL;
if (input) {
operation = pa_context_get_source_info_list(
input_context_, InputDevicesInfoCallback, this);
} else {
operation = pa_context_get_sink_info_list(
input_context_, OutputDevicesInfoCallback, this);
}
WaitForOperationCompletion(input_mainloop_, operation, input_context_);
if (!device_names->empty())
device_names->push_front(AudioDeviceName::CreateDefault());
}
void AudioManagerPulse::GetAudioInputDeviceNames(
AudioDeviceNames* device_names) {
GetAudioDeviceNames(true, device_names);
}
void AudioManagerPulse::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) {
GetAudioDeviceNames(false, device_names);
}
AudioParameters AudioManagerPulse::GetInputStreamParameters(
const std::string& device_id) {
UpdateNativeAudioHardwareInfo();
{
AutoPulseLock auto_lock(input_mainloop_);
auto* operation = pa_context_get_source_info_by_name(
input_context_, default_source_name_.c_str(), DefaultSourceInfoCallback,
this);
WaitForOperationCompletion(input_mainloop_, operation, input_context_);
}
if (device_id == AudioDeviceDescription::kDefaultDeviceId &&
default_source_is_monitor_) {
return AudioParameters();
}
const int user_buffer_size = GetUserBufferSize();
const int buffer_size =
user_buffer_size ? user_buffer_size : kDefaultInputBufferSize;
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
ChannelLayoutConfig::Stereo(),
native_input_sample_rate_ ? native_input_sample_rate_
: kDefaultSampleRate,
buffer_size);
}
const std::string_view AudioManagerPulse::GetName() {
return "PulseAudio";
}
AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
const AudioParameters& params,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
return MakeOutputStream(params, AudioDeviceDescription::kDefaultDeviceId,
log_callback);
}
AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
return MakeOutputStream(
params,
device_id.empty() ? AudioDeviceDescription::kDefaultDeviceId : device_id,
log_callback);
}
AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
return MakeInputStream(params, device_id, log_callback);
}
AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
return MakeInputStream(params, device_id, log_callback);
}
std::string AudioManagerPulse::GetDefaultInputDeviceID() {
return AudioManagerBase::GetDefaultInputDeviceID();
}
std::string AudioManagerPulse::GetDefaultOutputDeviceID() {
return AudioManagerBase::GetDefaultOutputDeviceID();
}
std::string AudioManagerPulse::GetAssociatedOutputDeviceID(
const std::string& input_device_id) {
#if BUILDFLAG(IS_CHROMEOS)
return AudioManagerBase::GetAssociatedOutputDeviceID(input_device_id);
#else
DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
DCHECK(input_mainloop_);
DCHECK(input_context_);
if (input_device_id == AudioDeviceDescription::kDefaultDeviceId)
return std::string();
std::string input_bus =
pulse::GetBusOfInput(input_mainloop_, input_context_, input_device_id);
return input_bus.empty() ? std::string()
: pulse::GetOutputCorrespondingTo(
input_mainloop_, input_context_, input_bus);
#endif
}
AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
const std::string& output_device_id,
const AudioParameters& input_params) {
VLOG_IF(0, !output_device_id.empty()) << "Not implemented!";
int buffer_size = kMinimumOutputBufferSize;
UpdateNativeAudioHardwareInfo();
int sample_rate = native_input_sample_rate_ ? native_input_sample_rate_
: kDefaultSampleRate;
ChannelLayoutConfig channel_layout_config = ChannelLayoutConfig::Guess(
native_channel_count_ ? native_channel_count_ : 2);
if (input_params.IsValid()) {
if (input_params.channel_layout() != CHANNEL_LAYOUT_DISCRETE)
channel_layout_config = input_params.channel_layout_config();
buffer_size =
std::min(kMaximumOutputBufferSize,
std::max(buffer_size, input_params.frames_per_buffer()));
}
int user_buffer_size = GetUserBufferSize();
if (user_buffer_size)
buffer_size = user_buffer_size;
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
channel_layout_config, sample_rate, buffer_size);
}
AudioOutputStream* AudioManagerPulse::MakeOutputStream(
const AudioParameters& params,
const std::string& device_id,
LogCallback log_callback) {
DCHECK(!device_id.empty());
return new PulseAudioOutputStream(params, device_id, this,
std::move(log_callback));
}
AudioInputStream* AudioManagerPulse::MakeInputStream(
const AudioParameters& params,
const std::string& device_id,
LogCallback log_callback) {
if (AudioDeviceDescription::IsLoopbackDevice(device_id)) {
if (!loopback_manager_) {
loopback_manager_ = PulseLoopbackManager::Create(
base::BindRepeating(&AudioManagerBase::ReleaseInputStream,
base::Unretained(this)),
input_context_, input_mainloop_);
}
bool should_mute_system_audio =
(device_id == AudioDeviceDescription::kLoopbackWithMuteDeviceId);
if (loopback_manager_) {
return loopback_manager_->MakeLoopbackStream(
params, std::move(log_callback), should_mute_system_audio);
}
return nullptr;
}
return new PulseAudioInputStream(this, device_id, params, input_mainloop_,
input_context_, std::move(log_callback));
}
void AudioManagerPulse::UpdateNativeAudioHardwareInfo() {
DCHECK(input_mainloop_);
DCHECK(input_context_);
AutoPulseLock auto_lock(input_mainloop_);
pa_operation* operation = pa_context_get_server_info(
input_context_, AudioHardwareInfoCallback, this);
WaitForOperationCompletion(input_mainloop_, operation, input_context_);
}
void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context,
const pa_source_info* info,
int eol,
void* user_data) {
AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
if (eol) {
pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
return;
}
if (info->monitor_of_sink != PA_INVALID_INDEX)
return;
if (info->n_ports > 0) {
UNSAFE_BUFFERS(
base::span<pa_source_port_info*> ports(info->ports, info->n_ports));
bool no_ports_available = std::ranges::all_of(ports, [](auto* port) {
return port->available == PA_PORT_AVAILABLE_NO;
});
if (no_ports_available) {
return;
}
}
manager->devices_->emplace_back(info->description, info->name);
}
void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context,
const pa_sink_info* info,
int eol,
void* user_data) {
AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
if (eol) {
pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
return;
}
manager->devices_->push_back(AudioDeviceName(info->description, info->name));
}
void AudioManagerPulse::AudioHardwareInfoCallback(pa_context* context,
const pa_server_info* info,
void* user_data) {
AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
manager->native_input_sample_rate_ = info->sample_spec.rate;
manager->native_channel_count_ = info->sample_spec.channels;
if (info->default_source_name)
manager->default_source_name_ = info->default_source_name;
pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
}
void AudioManagerPulse::DefaultSourceInfoCallback(pa_context* context,
const pa_source_info* info,
int eol,
void* user_data) {
AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
if (eol) {
pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
return;
}
DCHECK(info);
manager->default_source_is_monitor_ =
info->monitor_of_sink != PA_INVALID_INDEX;
}
}