| Index: media/audio/pulse/pulse_output.cc
|
| diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc
|
| index 953f9acebfacf10e06b754c2c28b93adb6bd23e8..158f2223bef873059235a3770f7d01c204140f89 100644
|
| --- a/media/audio/pulse/pulse_output.cc
|
| +++ b/media/audio/pulse/pulse_output.cc
|
| @@ -13,9 +13,34 @@
|
|
|
| namespace media {
|
|
|
| +namespace {
|
| +
|
| +// pa_context_get_server_info callback. It's used by
|
| +// GetSystemDefaultOutputDevice to set |default_system_device_name_| to the
|
| +// default system output device.
|
| +void GetSystemDefaultOutputDeviceCallback(pa_context* context,
|
| + const pa_server_info* info,
|
| + void* user_data) {
|
| + media::PulseAudioOutputStream* stream =
|
| + reinterpret_cast<media::PulseAudioOutputStream*>(user_data);
|
| + stream->SetDefaultSystemDeviceName(info->default_sink_name);
|
| + pa_threaded_mainloop_signal(stream->GetPAMainloop(), 0);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| using pulse::AutoPulseLock;
|
| using pulse::WaitForOperationCompletion;
|
|
|
| +// Helper macro to avoid code spam and string bloat.
|
| +#define RETURN_ON_FAILURE(expression, message) \
|
| + do { \
|
| + if (!(expression)) { \
|
| + DLOG(ERROR) << message; \
|
| + return false; \
|
| + } \
|
| + } while (0)
|
| +
|
| // static, pa_stream_notify_cb
|
| void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) {
|
| PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
|
| @@ -61,10 +86,83 @@ PulseAudioOutputStream::~PulseAudioOutputStream() {
|
| DCHECK(!pa_mainloop_);
|
| }
|
|
|
| -bool PulseAudioOutputStream::Open() {
|
| +pa_threaded_mainloop* PulseAudioOutputStream::GetPAMainloop() {
|
| + return pa_mainloop_;
|
| +}
|
| +
|
| +void PulseAudioOutputStream::SetDefaultSystemDeviceName(
|
| + const std::string& name) {
|
| + default_system_device_name_ = name;
|
| +}
|
| +
|
| +bool PulseAudioOutputStream::InitializeMainloopAndContext() {
|
| + DCHECK(!pa_mainloop_);
|
| + DCHECK(!pa_context_);
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| + pa_mainloop_ = pa_threaded_mainloop_new();
|
| + RETURN_ON_FAILURE(pa_mainloop_, "Failed to create PulseAudio main loop.");
|
| +
|
| + pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_);
|
| + std::string app_name = AudioManager::GetGlobalAppName();
|
| + pa_context_ = pa_context_new(
|
| + pa_mainloop_api, app_name.empty() ? "Chromium" : app_name.c_str());
|
| + RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");
|
| +
|
| + // A state callback must be set before calling pa_threaded_mainloop_lock() or
|
| + // pa_threaded_mainloop_wait() calls may lead to dead lock.
|
| + pa_context_set_state_callback(pa_context_, &pulse::ContextStateCallback,
|
| + pa_mainloop_);
|
| + {
|
| + // Lock the main loop while setting up the context. Failure to do so may
|
| + // lead to crashes as the PulseAudio thread tries to run before things are
|
| + // ready.
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| +
|
| + RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0,
|
| + "Failed to start PulseAudio main loop.");
|
| + RETURN_ON_FAILURE(pa_context_connect(pa_context_, NULL,
|
| + PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
|
| + "Failed to connect PulseAudio context.");
|
| +
|
| + // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be
|
| + // called after pa_context_get_state() in case the context is already ready,
|
| + // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
|
| + while (true) {
|
| + pa_context_state_t context_state = pa_context_get_state(pa_context_);
|
| + RETURN_ON_FAILURE(PA_CONTEXT_IS_GOOD(context_state),
|
| + "Invalid PulseAudio context state.");
|
| + if (context_state == PA_CONTEXT_READY)
|
| + break;
|
| + pa_threaded_mainloop_wait(pa_mainloop_);
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void PulseAudioOutputStream::GetSystemDefaultOutputDevice() {
|
| + DCHECK(pa_mainloop_);
|
| + DCHECK(pa_context_);
|
| + pa_operation* operation = pa_context_get_server_info(
|
| + pa_context_, GetSystemDefaultOutputDeviceCallback, this);
|
| + WaitForOperationCompletion(pa_mainloop_, operation);
|
| +}
|
| +
|
| +bool PulseAudioOutputStream::Open() {
|
| + if (!InitializeMainloopAndContext()) {
|
| + return false;
|
| + }
|
| +
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| +
|
| + std::string device_name_to_use = device_id_;
|
| + if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
|
| + GetSystemDefaultOutputDevice();
|
| + device_name_to_use = default_system_device_name_;
|
| + }
|
| +
|
| return pulse::CreateOutputStream(
|
| - &pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_,
|
| + pa_mainloop_, pa_context_, &pa_stream_, params_, device_name_to_use,
|
| AudioManager::GetGlobalAppName(), &StreamNotifyCallback,
|
| &StreamRequestCallback, this);
|
| }
|
|
|