Commit f84fffab authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

Improve support for audio-device group IDs on Linux.

This is a reland of https://codereview.chromium.org/2258143002, originally authored by maxmorin@.
This version contains minor additions, lint fixes and restricts functionality to just non-ChromeOS
Linux.

This CL adds the following AudioManagerPulse overrides in order to better support group IDs:
  * GetDefaultInputDeviceID()
  * GetDefaultOutputDeviceID()
  * GetAssociatedOutputDeviceID()

The original CL was reverted due to regressions on ChromeOS with AudioManagerPulse. This CL keeps
behavior of AudioManagerPulse on ChromeOS unmodified.

Bug: 636300
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: Id48dc1477efd2fecd5ae111df22dfe2b744de6e0
Reviewed-on: https://chromium-review.googlesource.com/1072649
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561855}
parent f4f94d4f
......@@ -4,6 +4,9 @@
#include "media/audio/pulse/audio_manager_pulse.h"
#include <algorithm>
#include <utility>
#include "base/command_line.h"
#include "base/environment.h"
#include "base/logging.h"
......@@ -149,6 +152,46 @@ AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
return MakeInputStream(params, device_id);
}
std::string AudioManagerPulse::GetDefaultInputDeviceID() {
#if defined(OS_CHROMEOS)
return AudioManagerBase::GetDefaultInputDeviceID();
#else
return pulse::GetRealDefaultDeviceId(input_mainloop_, input_context_,
pulse::RequestType::INPUT);
#endif
}
std::string AudioManagerPulse::GetDefaultOutputDeviceID() {
#if defined(OS_CHROMEOS)
return AudioManagerBase::GetDefaultOutputDeviceID();
#else
return pulse::GetRealDefaultDeviceId(input_mainloop_, input_context_,
pulse::RequestType::OUTPUT);
#endif
}
std::string AudioManagerPulse::GetAssociatedOutputDeviceID(
const std::string& input_device_id) {
#if defined(OS_CHROMEOS)
return AudioManagerBase::GetAssociatedOutputDeviceID(input_device_id);
#else
DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
DCHECK(input_mainloop_);
DCHECK(input_context_);
std::string input =
(input_device_id == AudioDeviceDescription::kDefaultDeviceId)
? pulse::GetRealDefaultDeviceId(input_mainloop_, input_context_,
pulse::RequestType::INPUT)
: input_device_id;
std::string input_bus =
pulse::GetBusOfInput(input_mainloop_, input_context_, input);
return input_bus.empty() ? ""
: pulse::GetOutputCorrespondingTo(
input_mainloop_, input_context_, input_bus);
#endif
}
AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
const std::string& output_device_id,
const AudioParameters& input_params) {
......
......@@ -6,6 +6,8 @@
#define MEDIA_AUDIO_PULSE_AUDIO_MANAGER_PULSE_H_
#include <pulse/pulseaudio.h>
#include <memory>
#include <string>
#include "base/compiler_specific.h"
......@@ -48,6 +50,10 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
const AudioParameters& params,
const std::string& device_id,
const LogCallback& log_callback) override;
std::string GetDefaultInputDeviceID() override;
std::string GetDefaultOutputDeviceID() override;
std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) override;
protected:
void ShutdownOnAudioThread() override;
......
......@@ -9,7 +9,7 @@ pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop* m);
void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
pa_threaded_mainloop* pa_threaded_mainloop_new();
void pa_threaded_mainloop_lock(pa_threaded_mainloop* m);
int pa_threaded_mainloop_in_thread(pa_threaded_mainloop* m);
int pa_threaded_mainloop_in_thread(pa_threaded_mainloop* m);
void pa_threaded_mainloop_signal(pa_threaded_mainloop* m, int wait_for_accept);
int pa_threaded_mainloop_start(pa_threaded_mainloop* m);
void pa_threaded_mainloop_stop(pa_threaded_mainloop* m);
......@@ -22,7 +22,7 @@ void pa_context_disconnect(pa_context* c);
pa_operation* pa_context_get_server_info(pa_context* c, pa_server_info_cb_t cb, void* userdata);
pa_operation* pa_context_get_source_info_by_index(pa_context* c, uint32_t idx, pa_source_info_cb_t cb, void* userdata);
pa_operation* pa_context_get_source_info_list(pa_context* c, pa_source_info_cb_t cb, void* userdata);
pa_operation* pa_context_get_sink_info_list(pa_context* c, pa_sink_info_cb_t cb, void* userdata);
pa_operation* pa_context_get_sink_info_list(pa_context* c, pa_sink_info_cb_t cb, void* userdata);
pa_context_state_t pa_context_get_state(pa_context* c);
pa_context* pa_context_new(pa_mainloop_api* mainloop, const char* name);
pa_operation* pa_context_set_source_volume_by_index(pa_context* c, uint32_t idx, const pa_cvolume* volume, pa_context_success_cb_t cb, void* userdata);
......@@ -43,7 +43,9 @@ pa_stream_state_t pa_stream_get_state(pa_stream* p);
pa_stream* pa_stream_new(pa_context* c, const char* name, const pa_sample_spec* ss, const pa_channel_map * map);
pa_stream* pa_stream_new_with_proplist(pa_context* c, const char* name, const pa_sample_spec* ss, const pa_channel_map* map, pa_proplist* p);
pa_proplist* pa_proplist_new(void);
int pa_proplist_contains(pa_proplist* p, const char* key);
void pa_proplist_free(pa_proplist* p);
const char* pa_proplist_gets(pa_proplist* p, const char* key);
int pa_proplist_sets(pa_proplist* p, const char* key, const char* value);
size_t pa_stream_readable_size(pa_stream *p);
int pa_stream_peek(pa_stream* p, const void** data, size_t* nbytes);
......
......@@ -5,6 +5,9 @@
#include "media/audio/pulse/pulse_util.h"
#include <stdint.h>
#include <string.h>
#include <memory>
#include "base/files/file_path.h"
#include "base/logging.h"
......@@ -93,6 +96,77 @@ class ScopedPropertyList {
DISALLOW_COPY_AND_ASSIGN(ScopedPropertyList);
};
struct InputBusData {
InputBusData(pa_threaded_mainloop* loop, const std::string& name)
: loop_(loop), name_(name), bus_() {}
pa_threaded_mainloop* const loop_;
const std::string& name_;
std::string bus_;
};
struct OutputBusData {
OutputBusData(pa_threaded_mainloop* loop, const std::string& bus)
: loop_(loop), name_(), bus_(bus) {}
pa_threaded_mainloop* const loop_;
std::string name_;
const std::string& bus_;
};
void InputBusCallback(pa_context* context,
const pa_source_info* info,
int error,
void* user_data) {
InputBusData* data = static_cast<InputBusData*>(user_data);
if (error) {
// We have checked all the devices now.
pa_threaded_mainloop_signal(data->loop_, 0);
return;
}
if (strcmp(info->name, data->name_.c_str()) == 0 &&
pa_proplist_contains(info->proplist, PA_PROP_DEVICE_BUS)) {
data->bus_ = pa_proplist_gets(info->proplist, PA_PROP_DEVICE_BUS);
}
}
void OutputBusCallback(pa_context* context,
const pa_sink_info* info,
int error,
void* user_data) {
OutputBusData* data = static_cast<OutputBusData*>(user_data);
if (error) {
// We have checked all the devices now.
pa_threaded_mainloop_signal(data->loop_, 0);
return;
}
if (pa_proplist_contains(info->proplist, PA_PROP_DEVICE_BUS) &&
strcmp(pa_proplist_gets(info->proplist, PA_PROP_DEVICE_BUS),
data->bus_.c_str()) == 0) {
data->name_ = info->name;
}
}
struct DefaultDevicesData {
explicit DefaultDevicesData(pa_threaded_mainloop* loop) : loop_(loop) {}
std::string input_;
std::string output_;
pa_threaded_mainloop* const loop_;
};
void GetDefaultDeviceIdCallback(pa_context* c,
const pa_server_info* info,
void* userdata) {
DefaultDevicesData* data = static_cast<DefaultDevicesData*>(userdata);
data->input_ = info->default_source_name;
data->output_ = info->default_sink_name;
pa_threaded_mainloop_signal(data->loop_, 0);
}
} // namespace
bool InitPulse(pa_threaded_mainloop** mainloop, pa_context** context) {
......@@ -448,6 +522,42 @@ bool CreateOutputStream(pa_threaded_mainloop** mainloop,
return true;
}
std::string GetBusOfInput(pa_threaded_mainloop* mainloop,
pa_context* context,
const std::string& name) {
DCHECK(mainloop);
DCHECK(context);
AutoPulseLock auto_lock(mainloop);
InputBusData data(mainloop, name);
pa_operation* operation =
pa_context_get_source_info_list(context, InputBusCallback, &data);
WaitForOperationCompletion(mainloop, operation);
return data.bus_;
}
std::string GetOutputCorrespondingTo(pa_threaded_mainloop* mainloop,
pa_context* context,
const std::string& bus) {
DCHECK(mainloop);
DCHECK(context);
AutoPulseLock auto_lock(mainloop);
OutputBusData data(mainloop, bus);
pa_operation* operation =
pa_context_get_sink_info_list(context, OutputBusCallback, &data);
WaitForOperationCompletion(mainloop, operation);
return data.name_;
}
std::string GetRealDefaultDeviceId(pa_threaded_mainloop* mainloop,
pa_context* context,
RequestType type) {
DefaultDevicesData data(mainloop);
pa_operation* op =
pa_context_get_server_info(context, &GetDefaultDeviceIdCallback, &data);
WaitForOperationCompletion(mainloop, op);
return (type == RequestType::INPUT) ? data.input_ : data.output_;
}
#undef RETURN_ON_FAILURE
} // namespace pulse
......
......@@ -21,6 +21,8 @@ class AudioParameters;
namespace pulse {
enum class RequestType : int8_t { INPUT, OUTPUT };
// A helper class that acquires pa_threaded_mainloop_lock() while in scope.
class AutoPulseLock {
public:
......@@ -81,6 +83,16 @@ bool CreateOutputStream(pa_threaded_mainloop** mainloop,
pa_stream_request_cb_t write_callback,
void* user_data);
// Utility functions to match up outputs and inputs.
std::string GetBusOfInput(pa_threaded_mainloop* mainloop,
pa_context* context,
const std::string& name);
std::string GetOutputCorrespondingTo(pa_threaded_mainloop* mainloop,
pa_context* context,
const std::string& bus);
std::string GetRealDefaultDeviceId(pa_threaded_mainloop* mainloop,
pa_context* context,
RequestType type);
} // namespace pulse
} // namespace media
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment