Chromium Code Reviews| 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..030d1698c6a3a93e011a676c35f0be67d9aade26 100644 |
| --- a/media/audio/pulse/pulse_output.cc |
| +++ b/media/audio/pulse/pulse_output.cc |
| @@ -16,6 +16,15 @@ namespace media { |
| using pulse::AutoPulseLock; |
| using pulse::WaitForOperationCompletion; |
| +// Helper macro to avoid code spam and string bloat. |
| +#define RETURN_ON_FAILURE(expression, message) \ |
|
tommi (sloooow) - chröme
2016/03/16 15:21:47
macros are discouraged and especially ones that ha
rchtara
2016/03/19 12:07:30
Done.
|
| + 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 +70,86 @@ PulseAudioOutputStream::~PulseAudioOutputStream() { |
| DCHECK(!pa_mainloop_); |
| } |
| -bool PulseAudioOutputStream::Open() { |
| +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()); |
|
tommi (sloooow) - chröme
2016/03/16 15:21:47
When do we get an empty string from GetGlobalAppNa
rchtara
2016/03/19 12:07:30
I'm just moving some code from CreateOutputStream
|
| + 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; |
| +} |
| + |
| +// static, used by pa_context_get_server_info. |
| +void PulseAudioOutputStream::GetSystemDefaultOutputDeviceCallback( |
| + pa_context* context, |
| + const pa_server_info* info, |
| + void* user_data) { |
| + media::PulseAudioOutputStream* stream = |
| + reinterpret_cast<media::PulseAudioOutputStream*>(user_data); |
| + stream->default_system_device_name_ = info->default_sink_name; |
| + pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); |
| +} |
| + |
| +void PulseAudioOutputStream::GetSystemDefaultOutputDevice() { |
| + DCHECK(pa_mainloop_); |
| + DCHECK(pa_context_); |
| + pa_operation* operation = pa_context_get_server_info( |
| + pa_context_, PulseAudioOutputStream::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); |
| } |