Index: media/audio/pulse/pulse_util.cc |
diff --git a/media/audio/pulse/pulse_util.cc b/media/audio/pulse/pulse_util.cc |
index ba4d4bed83757c8135d7d7c9da1b47a02f43f6f5..438d4c8d68dfdca959ce532de4deef83aafbc505 100644 |
--- a/media/audio/pulse/pulse_util.cc |
+++ b/media/audio/pulse/pulse_util.cc |
@@ -6,12 +6,22 @@ |
#include <stdint.h> |
+#include "base/files/file_path.h" |
#include "base/logging.h" |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
#include "media/audio/audio_device_description.h" |
#include "media/base/audio_parameters.h" |
#include "media/base/audio_timestamp_helper.h" |
+#if defined(DLOPEN_PULSEAUDIO) |
+#include "media/audio/pulse/pulse_stubs.h" |
+ |
+using media_audio_pulse::kModulePulse; |
+using media_audio_pulse::InitializeStubs; |
+using media_audio_pulse::StubPathMap; |
+#endif // defined(DLOPEN_PULSEAUDIO) |
+ |
namespace media { |
namespace pulse { |
@@ -24,6 +34,22 @@ static const char kBrowserDisplayName[] = "google-chrome"; |
static const char kBrowserDisplayName[] = "chromium-browser"; |
#endif |
+#if defined(DLOPEN_PULSEAUDIO) |
+static const base::FilePath::CharType kPulseLib[] = |
+ FILE_PATH_LITERAL("libpulse.so.0"); |
+#endif |
+ |
+void DestroyMainloop(pa_threaded_mainloop* mainloop) { |
+ pa_threaded_mainloop_stop(mainloop); |
+ pa_threaded_mainloop_free(mainloop); |
+} |
+ |
+void DestroyContext(pa_context* context) { |
+ pa_context_set_state_callback(context, NULL, NULL); |
+ pa_context_disconnect(context); |
+ pa_context_unref(context); |
+} |
+ |
pa_channel_position ChromiumToPAChannelPosition(Channels channel) { |
switch (channel) { |
// PulseAudio does not differentiate between left/right and |
@@ -70,6 +96,87 @@ class ScopedPropertyList { |
} // namespace |
+bool InitPulse(pa_threaded_mainloop** mainloop, pa_context** 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* pa_mainloop = pa_threaded_mainloop_new(); |
+ if (!pa_mainloop) |
+ return false; |
+ |
+ // Start the threaded mainloop. |
+ if (pa_threaded_mainloop_start(pa_mainloop)) { |
+ pa_threaded_mainloop_free(pa_mainloop); |
+ return false; |
+ } |
+ |
+ // Lock the event loop object, effectively blocking the event loop thread |
+ // from processing events. This is necessary. |
+ auto mainloop_lock = base::MakeUnique<AutoPulseLock>(pa_mainloop); |
+ |
+ pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop); |
+ pa_context* pa_context = pa_context_new(pa_mainloop_api, "Chrome input"); |
+ if (!pa_context) { |
+ mainloop_lock.reset(); |
+ DestroyMainloop(pa_mainloop); |
+ return false; |
+ } |
+ |
+ pa_context_set_state_callback(pa_context, &pulse::ContextStateCallback, |
+ pa_mainloop); |
+ if (pa_context_connect(pa_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) { |
+ VLOG(1) << "Failed to connect to the context. Error: " |
+ << pa_strerror(pa_context_errno(pa_context)); |
+ pa_context_set_state_callback(pa_context, NULL, NULL); |
+ pa_context_unref(pa_context); |
+ mainloop_lock.reset(); |
+ DestroyMainloop(pa_mainloop); |
+ return false; |
+ } |
+ |
+ // 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); |
+ if (!PA_CONTEXT_IS_GOOD(context_state)) { |
+ DestroyContext(pa_context); |
+ mainloop_lock.reset(); |
+ DestroyMainloop(pa_mainloop); |
+ return false; |
+ } |
+ if (context_state == PA_CONTEXT_READY) |
+ break; |
+ pa_threaded_mainloop_wait(pa_mainloop); |
+ } |
+ |
+ *mainloop = pa_mainloop; |
+ *context = pa_context; |
+ return true; |
+} |
+ |
+void DestroyPulse(pa_threaded_mainloop* mainloop, pa_context* context) { |
+ DCHECK(mainloop); |
+ DCHECK(context); |
+ |
+ { |
+ AutoPulseLock auto_lock(mainloop); |
+ DestroyContext(context); |
+ } |
+ |
+ DestroyMainloop(mainloop); |
+} |
+ |
// static, pa_stream_success_cb_t |
void StreamSuccessCallback(pa_stream* s, int error, void* mainloop) { |
pa_threaded_mainloop* pa_mainloop = |