Index: media/audio/pulse/audio_manager_pulse.cc |
diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc |
index d6e9543183a0f86c8130b1459f8cd6a65e54ef2d..e9e27707a8c17848742d9492258942d12c7879c7 100644 |
--- a/media/audio/pulse/audio_manager_pulse.cc |
+++ b/media/audio/pulse/audio_manager_pulse.cc |
@@ -47,18 +47,97 @@ static const base::FilePath::CharType kPulseLib[] = |
FILE_PATH_LITERAL("libpulse.so.0"); |
#endif |
-// static |
-AudioManager* AudioManagerPulse::Create(AudioLogFactory* audio_log_factory) { |
- scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse(audio_log_factory)); |
- if (ret->Init()) |
- return ret.release(); |
- |
- DVLOG(1) << "PulseAudio is not available on the OS"; |
- return NULL; |
+namespace { |
+void DestroyPulse(pa_threaded_mainloop* input_mainloop, |
+ pa_context* input_context) { |
+ DCHECK(input_mainloop); |
+ |
+ { |
+ AutoPulseLock auto_lock(input_mainloop); |
+ if (input_context) { |
+ // Clear our state callback. |
+ pa_context_set_state_callback(input_context, NULL, NULL); |
+ pa_context_disconnect(input_context); |
+ pa_context_unref(input_context); |
+ } |
+ } |
+ |
+ pa_threaded_mainloop_stop(input_mainloop); |
+ pa_threaded_mainloop_free(input_mainloop); |
} |
-AudioManagerPulse::AudioManagerPulse(AudioLogFactory* audio_log_factory) |
- : AudioManagerBase(audio_log_factory), |
+bool InitPulse(pa_threaded_mainloop** input_mainloop, |
+ pa_context** input_context) { |
+#if defined(DLOPEN_PULSEAUDIO) |
+ StubPathMap paths; |
+ |
+ // Check if the pulse library is avialbale. |
+ paths[kModulePulse].push_back(kPulseLib); |
+ if (!InitializeStubs(paths)) { |
+ VLOG(1) << "Failed on loading the Pulse library and symbols"; |
+ return false; |
+ } |
+#endif // defined(DLOPEN_PULSEAUDIO) |
+ |
+ // Create a mainloop API and connect to the default server. |
+ // The mainloop is the internal asynchronous API event loop. |
+ pa_threaded_mainloop* mainloop = pa_threaded_mainloop_new(); |
+ if (!mainloop) |
+ return false; |
+ |
+ // Start the threaded mainloop. |
+ if (pa_threaded_mainloop_start(mainloop)) { |
+ DestroyPulse(mainloop, nullptr); |
+ return false; |
+ } |
+ |
+ // Lock the event loop object, effectively blocking the event loop thread |
+ // from processing events. This is necessary. |
+ AutoPulseLock auto_lock(mainloop); |
+ |
+ pa_mainloop_api* mainloop_api = pa_threaded_mainloop_get_api(mainloop); |
+ pa_context* context = pa_context_new(mainloop_api, "Chrome input"); |
+ if (!context) { |
+ DestroyPulse(mainloop, nullptr); |
+ return false; |
+ } |
+ |
+ pa_context_set_state_callback(context, &pulse::ContextStateCallback, |
+ mainloop); |
+ if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) { |
+ VLOG(1) << "Failed to connect to the context. Error: " |
+ << pa_strerror(pa_context_errno(context)); |
+ DestroyPulse(mainloop, context); |
+ return false; |
+ } |
+ |
+ // Wait until |input_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(context); |
+ if (!PA_CONTEXT_IS_GOOD(context_state)) { |
+ DestroyPulse(mainloop, context); |
+ return false; |
+ } |
+ if (context_state == PA_CONTEXT_READY) |
+ break; |
+ pa_threaded_mainloop_wait(mainloop); |
+ } |
+ |
+ *input_mainloop = mainloop; |
+ *input_context = context; |
+ return true; |
+} |
+} // namespace |
+ |
+AudioManagerPulse::AudioManagerPulse( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, |
+ AudioLogFactory* audio_log_factory) |
+ : AudioManagerBase(std::move(task_runner), |
+ std::move(worker_task_runner), |
+ audio_log_factory), |
input_mainloop_(NULL), |
input_context_(NULL), |
devices_(NULL), |
@@ -67,11 +146,24 @@ AudioManagerPulse::AudioManagerPulse(AudioLogFactory* audio_log_factory) |
} |
AudioManagerPulse::~AudioManagerPulse() { |
- Shutdown(); |
+ DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
+ // Do not destroy pulse here since AudioManagerBase destructor needs it. |
+ if (input_mainloop_) { |
+ GetTaskRunner()->PostTask( |
+ FROM_HERE, base::Bind(&DestroyPulse, input_mainloop_, input_context_)); |
+ } else { |
+ DCHECK(!input_context_); |
+ } |
+} |
- // The Pulse objects are the last things to be destroyed since Shutdown() |
- // needs them. |
- DestroyPulse(); |
+bool AudioManagerPulse::Init() { |
+ DCHECK(!input_mainloop_); |
+ // TODO(alokp): Investigate if InitPulse can happen on the audio thread. |
+ // It currently needs to happen on the main thread so that is InitPulse fails, |
+ // we can fallback to ALSA implementation. Initializing it on audio thread |
+ // would unblock the main thread and make InitPulse consistent with |
+ // DestroyPulse which happens on the audio thread. |
+ return InitPulse(&input_mainloop_, &input_context_); |
} |
// Implementation of AudioManager. |
@@ -217,85 +309,6 @@ int AudioManagerPulse::GetNativeSampleRate() { |
return native_input_sample_rate_; |
} |
-bool AudioManagerPulse::Init() { |
- DCHECK(!input_mainloop_); |
- |
-#if defined(DLOPEN_PULSEAUDIO) |
- StubPathMap paths; |
- |
- // Check if the pulse library is avialbale. |
- paths[kModulePulse].push_back(kPulseLib); |
- if (!InitializeStubs(paths)) { |
- VLOG(1) << "Failed on loading the Pulse library and symbols"; |
- return false; |
- } |
-#endif // defined(DLOPEN_PULSEAUDIO) |
- |
- // Create a mainloop API and connect to the default server. |
- // The mainloop is the internal asynchronous API event loop. |
- input_mainloop_ = pa_threaded_mainloop_new(); |
- if (!input_mainloop_) |
- return false; |
- |
- // Start the threaded mainloop. |
- if (pa_threaded_mainloop_start(input_mainloop_)) |
- return false; |
- |
- // Lock the event loop object, effectively blocking the event loop thread |
- // from processing events. This is necessary. |
- AutoPulseLock auto_lock(input_mainloop_); |
- |
- pa_mainloop_api* pa_mainloop_api = |
- pa_threaded_mainloop_get_api(input_mainloop_); |
- input_context_ = pa_context_new(pa_mainloop_api, "Chrome input"); |
- if (!input_context_) |
- return false; |
- |
- pa_context_set_state_callback(input_context_, &pulse::ContextStateCallback, |
- input_mainloop_); |
- if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) { |
- VLOG(1) << "Failed to connect to the context. Error: " |
- << pa_strerror(pa_context_errno(input_context_)); |
- return false; |
- } |
- |
- // Wait until |input_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(input_context_); |
- if (!PA_CONTEXT_IS_GOOD(context_state)) |
- return false; |
- if (context_state == PA_CONTEXT_READY) |
- break; |
- pa_threaded_mainloop_wait(input_mainloop_); |
- } |
- |
- return true; |
-} |
- |
-void AudioManagerPulse::DestroyPulse() { |
- if (!input_mainloop_) { |
- DCHECK(!input_context_); |
- return; |
- } |
- |
- { |
- AutoPulseLock auto_lock(input_mainloop_); |
- if (input_context_) { |
- // Clear our state callback. |
- pa_context_set_state_callback(input_context_, NULL, NULL); |
- pa_context_disconnect(input_context_); |
- pa_context_unref(input_context_); |
- input_context_ = NULL; |
- } |
- } |
- |
- pa_threaded_mainloop_stop(input_mainloop_); |
- pa_threaded_mainloop_free(input_mainloop_); |
- input_mainloop_ = NULL; |
-} |
- |
void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context, |
const pa_source_info* info, |
int error, void *user_data) { |