parent
c97cf26192
commit
1872c16afb
@ -0,0 +1,327 @@
|
|||||||
|
diff --git a/phonon/pulsesupport.cpp b/phonon/pulsesupport.cpp
|
||||||
|
index d0387c3..a3733ad 100644
|
||||||
|
--- a/phonon/pulsesupport.cpp
|
||||||
|
+++ b/phonon/pulsesupport.cpp
|
||||||
|
@@ -135,7 +135,6 @@ static bool s_pulseActive = false;
|
||||||
|
|
||||||
|
static pa_glib_mainloop *s_mainloop = NULL;
|
||||||
|
static pa_context *s_context = NULL;
|
||||||
|
-static QEventLoop *s_connectionEventloop = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -179,31 +178,25 @@ static void createGenericDevices()
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSEAUDIO_DEVICE_MANAGER
|
||||||
|
-static void ext_device_manager_subscribe_cb(pa_context *, void *);
|
||||||
|
static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manager_info *info, int eol, void *userdata) {
|
||||||
|
Q_ASSERT(c);
|
||||||
|
Q_ASSERT(userdata);
|
||||||
|
|
||||||
|
- // If this is our first iteration, set things up properly
|
||||||
|
- if (s_connectionEventloop) {
|
||||||
|
- logMessage("Exiting connection event loop (PulseAudio server found)");
|
||||||
|
- s_connectionEventloop->exit(0);
|
||||||
|
- s_connectionEventloop = NULL;
|
||||||
|
- s_pulseActive = true;
|
||||||
|
-
|
||||||
|
- pa_operation *o;
|
||||||
|
- pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, NULL);
|
||||||
|
- if ((o = pa_ext_device_manager_subscribe(c, 1, NULL, NULL)))
|
||||||
|
- pa_operation_unref(o);
|
||||||
|
- }
|
||||||
|
+ PulseUserData *u = reinterpret_cast<PulseUserData*>(userdata);
|
||||||
|
|
||||||
|
if (eol < 0) {
|
||||||
|
logMessage(QString("Failed to initialize device manager extension: %1").arg(pa_strerror(pa_context_errno(c))));
|
||||||
|
+ logMessage("Falling back to single device mode");
|
||||||
|
createGenericDevices();
|
||||||
|
+ delete u;
|
||||||
|
+
|
||||||
|
+ // If this is our probe phase, exit now
|
||||||
|
+ if (s_context != c)
|
||||||
|
+ pa_context_disconnect(c);
|
||||||
|
+
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- PulseUserData *u = reinterpret_cast<PulseUserData*>(userdata);
|
||||||
|
if (eol) {
|
||||||
|
// We're done reading the data, so order it by priority and copy it into the
|
||||||
|
// static variables where it can then be accessed by those classes that need it.
|
||||||
|
@@ -289,6 +282,8 @@ static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manage
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_instance) {
|
||||||
|
+ // This wont be emitted durring the connection probe phase
|
||||||
|
+ // which is intensional
|
||||||
|
if (output_changed)
|
||||||
|
s_instance->emitObjectDescriptionChanged(AudioOutputDeviceType);
|
||||||
|
if (capture_changed)
|
||||||
|
@@ -323,6 +318,11 @@ static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // If this is our probe phase, exit now as we're finished reading
|
||||||
|
+ // our device info and can exit and reconnect
|
||||||
|
+ if (s_context != c)
|
||||||
|
+ pa_context_disconnect(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
@@ -374,6 +374,19 @@ static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+static void ext_device_manager_subscribe_cb(pa_context *c, void *) {
|
||||||
|
+ Q_ASSERT(c);
|
||||||
|
+
|
||||||
|
+ pa_operation *o;
|
||||||
|
+ PulseUserData *u = new PulseUserData;
|
||||||
|
+ if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, u))) {
|
||||||
|
+ logMessage(QString("pa_ext_device_manager_read() failed."));
|
||||||
|
+ delete u;
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ pa_operation_unref(o);
|
||||||
|
+}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
|
||||||
|
@@ -515,49 +528,6 @@ static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-static void ext_device_manager_subscribe_cb(pa_context *c, void *) {
|
||||||
|
- Q_ASSERT(c);
|
||||||
|
-
|
||||||
|
- pa_operation *o;
|
||||||
|
-#ifdef HAVE_PULSEAUDIO_DEVICE_MANAGER
|
||||||
|
- PulseUserData *u = new PulseUserData; /** @todo Make some object to receive the info... */
|
||||||
|
- if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, u))) {
|
||||||
|
- // We need to deal with failure on first iteration
|
||||||
|
- if (s_connectionEventloop) {
|
||||||
|
- logMessage("Entering connection eventloop (initialisation failed)");
|
||||||
|
- s_connectionEventloop->exit(0);
|
||||||
|
- s_connectionEventloop = NULL;
|
||||||
|
- }
|
||||||
|
- logMessage(QString("pa_ext_device_manager_read() failed"));
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
- pa_operation_unref(o);
|
||||||
|
-#else
|
||||||
|
- // If we do not have Device Manager support. We just bail out now
|
||||||
|
- // and say we are active with our single "devices" for playback and capture
|
||||||
|
- s_pulseActive = true;
|
||||||
|
- logMessage("Entering connection eventloop (successfully detected PulseAudio)");
|
||||||
|
- if (s_connectionEventloop) {
|
||||||
|
- s_connectionEventloop->exit(0);
|
||||||
|
- s_connectionEventloop = NULL;
|
||||||
|
- }
|
||||||
|
- createGenericDevices();
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
-
|
||||||
|
- // Register for the stream changes...
|
||||||
|
- pa_context_set_subscribe_callback(c, subscribe_cb, NULL);
|
||||||
|
-
|
||||||
|
- if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
|
||||||
|
- (PA_SUBSCRIPTION_MASK_SINK_INPUT|
|
||||||
|
- PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
|
||||||
|
- logMessage(QString("pa_context_subscribe() failed"));
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
- pa_operation_unref(o);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
static const char* statename(pa_context_state_t state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
@@ -581,32 +551,68 @@ static void context_state_callback(pa_context *c, void *)
|
||||||
|
Q_ASSERT(c);
|
||||||
|
|
||||||
|
logMessage(QString("context_state_callback %1").arg(statename(pa_context_get_state(c))));
|
||||||
|
- switch (pa_context_get_state(c)) {
|
||||||
|
- case PA_CONTEXT_UNCONNECTED:
|
||||||
|
- case PA_CONTEXT_CONNECTING:
|
||||||
|
- case PA_CONTEXT_AUTHORIZING:
|
||||||
|
- case PA_CONTEXT_SETTING_NAME:
|
||||||
|
- break;
|
||||||
|
+ pa_context_state_t state = pa_context_get_state(c);
|
||||||
|
+ if (state == PA_CONTEXT_READY) {
|
||||||
|
+ // We've connected to PA, so it is active
|
||||||
|
+ s_pulseActive = true;
|
||||||
|
|
||||||
|
- case PA_CONTEXT_READY:
|
||||||
|
- // Attempt to load things up
|
||||||
|
- ext_device_manager_subscribe_cb(c, NULL);
|
||||||
|
- break;
|
||||||
|
+ // Attempt to load things up
|
||||||
|
+ pa_operation *o;
|
||||||
|
+
|
||||||
|
+ // 1. Register for the stream changes (except during probe)
|
||||||
|
+ if (s_context == c) {
|
||||||
|
+ pa_context_set_subscribe_callback(c, subscribe_cb, NULL);
|
||||||
|
|
||||||
|
- case PA_CONTEXT_FAILED:
|
||||||
|
- s_pulseActive = false;
|
||||||
|
- if (s_connectionEventloop) {
|
||||||
|
- logMessage("Entering connection eventloop (connection failed)");
|
||||||
|
- s_connectionEventloop->exit(0);
|
||||||
|
- s_connectionEventloop = NULL;
|
||||||
|
+ if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
|
||||||
|
+ (PA_SUBSCRIPTION_MASK_SINK_INPUT|
|
||||||
|
+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
|
||||||
|
+ logMessage(QString("pa_context_subscribe() failed"));
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
- break;
|
||||||
|
+ pa_operation_unref(o);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- case PA_CONTEXT_TERMINATED:
|
||||||
|
- default:
|
||||||
|
- s_pulseActive = false;
|
||||||
|
- /// @todo Deal with reconnection...
|
||||||
|
- break;
|
||||||
|
+#ifdef HAVE_PULSEAUDIO_DEVICE_MANAGER
|
||||||
|
+ // 2a. Attempt to initialise Device Manager info (except during probe)
|
||||||
|
+ if (s_context == c) {
|
||||||
|
+ pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, NULL);
|
||||||
|
+ if (!(o = pa_ext_device_manager_subscribe(c, 1, NULL, NULL))) {
|
||||||
|
+ logMessage(QString("pa_ext_device_manager_subscribe() failed"));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ pa_operation_unref(o);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // 3. Attempt to read info from Device Manager
|
||||||
|
+ PulseUserData *u = new PulseUserData;
|
||||||
|
+ if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, u))) {
|
||||||
|
+ logMessage(QString("pa_ext_device_manager_read() failed. Attempting to continue without device manager support"));
|
||||||
|
+ createGenericDevices();
|
||||||
|
+ delete u;
|
||||||
|
+
|
||||||
|
+ // If this is our probe phase, exit immediately
|
||||||
|
+ if (s_context != c)
|
||||||
|
+ pa_context_disconnect(c);
|
||||||
|
+
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ pa_operation_unref(o);
|
||||||
|
+
|
||||||
|
+#else
|
||||||
|
+ // If we know do not have Device Manager support, we just create our dummy devices now
|
||||||
|
+ createGenericDevices();
|
||||||
|
+
|
||||||
|
+ // If this is our probe phase, exit immediately
|
||||||
|
+ if (s_context != c)
|
||||||
|
+ pa_context_disconnect(c);
|
||||||
|
+#endif
|
||||||
|
+ } else if (!PA_CONTEXT_IS_GOOD(state)) {
|
||||||
|
+ /// @todo Deal with reconnection...
|
||||||
|
+ //logMessage("Connection to PulseAudio lost");
|
||||||
|
+
|
||||||
|
+ // If this is our probe phase, exit our context immediately
|
||||||
|
+ if (s_context != c)
|
||||||
|
+ pa_context_disconnect(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // HAVE_PULSEAUDIO
|
||||||
|
@@ -645,27 +651,67 @@ PulseSupport::PulseSupport()
|
||||||
|
|
||||||
|
// To allow for easy debugging, give an easy way to disable this pulseaudio check
|
||||||
|
QString pulseenv = qgetenv("PHONON_PULSEAUDIO_DISABLE");
|
||||||
|
- if (pulseenv.toInt())
|
||||||
|
+ if (pulseenv.toInt()) {
|
||||||
|
+ logMessage("PulseAudio support disabled: PHONON_PULSEAUDIO_DISABLE is set");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // First of all conenct to PA via simple/blocking means and if that succeeds,
|
||||||
|
+ // use a fully async integrated mainloop method to connect and get proper support.
|
||||||
|
+ pa_mainloop *p_test_mainloop;
|
||||||
|
+ if (!(p_test_mainloop = pa_mainloop_new())) {
|
||||||
|
+ logMessage("PulseAudio support disabled: Unable to create mainloop");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pa_context *p_test_context;
|
||||||
|
+ if (!(p_test_context = pa_context_new(pa_mainloop_get_api(p_test_mainloop), "libphonon-probe"))) {
|
||||||
|
+ logMessage("PulseAudio support disabled: Unable to create context");
|
||||||
|
+ pa_mainloop_free(p_test_mainloop);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ logMessage("Probing for PulseAudio...");
|
||||||
|
+ // (cg) Convert to PA_CONTEXT_NOFLAGS when PulseAudio 0.9.19 is required
|
||||||
|
+ if (pa_context_connect(p_test_context, NULL, static_cast<pa_context_flags_t>(0), NULL) < 0) {
|
||||||
|
+ logMessage(QString("PulseAudio support disabled: %1").arg(pa_strerror(pa_context_errno(p_test_context))));
|
||||||
|
+ pa_context_disconnect(p_test_context);
|
||||||
|
+ pa_context_unref(p_test_context);
|
||||||
|
+ pa_mainloop_free(p_test_mainloop);
|
||||||
|
return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pa_context_set_state_callback(p_test_context, &context_state_callback, NULL);
|
||||||
|
+ for (;;) {
|
||||||
|
+ pa_mainloop_iterate(p_test_mainloop, 1, NULL);
|
||||||
|
|
||||||
|
+ if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(p_test_context))) {
|
||||||
|
+ logMessage("PulseAudio probe complete.");
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ pa_context_disconnect(p_test_context);
|
||||||
|
+ pa_context_unref(p_test_context);
|
||||||
|
+ pa_mainloop_free(p_test_mainloop);
|
||||||
|
+
|
||||||
|
+ if (!s_pulseActive) {
|
||||||
|
+ logMessage("PulseAudio support is not available.");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // If we're still here, PA is available.
|
||||||
|
+ logMessage("PulseAudio support enabled");
|
||||||
|
+
|
||||||
|
+ // Now we connect for real using a proper main loop that we can forget
|
||||||
|
+ // all about processing.
|
||||||
|
s_mainloop = pa_glib_mainloop_new(NULL);
|
||||||
|
Q_ASSERT(s_mainloop);
|
||||||
|
pa_mainloop_api *api = pa_glib_mainloop_get_api(s_mainloop);
|
||||||
|
|
||||||
|
- // We create a simple event loop to allow the glib loop
|
||||||
|
- // to iterate until we've connected or not to the server.
|
||||||
|
- s_connectionEventloop = new QEventLoop;
|
||||||
|
-
|
||||||
|
- // XXX I don't want to show up in the client list. All I want to know is the list of sources
|
||||||
|
- // and sinks...
|
||||||
|
s_context = pa_context_new(api, "libphonon");
|
||||||
|
// (cg) Convert to PA_CONTEXT_NOFLAGS when PulseAudio 0.9.19 is required
|
||||||
|
- if (pa_context_connect(s_context, NULL, static_cast<pa_context_flags_t>(0), 0) >= 0) {
|
||||||
|
- pa_context_set_state_callback(s_context, &context_state_callback, s_connectionEventloop);
|
||||||
|
- // Now we block until we connect or otherwise...
|
||||||
|
- logMessage("Entering connection eventloop...");
|
||||||
|
- s_connectionEventloop->exec();
|
||||||
|
- }
|
||||||
|
+ if (pa_context_connect(s_context, NULL, static_cast<pa_context_flags_t>(0), 0) >= 0)
|
||||||
|
+ pa_context_set_state_callback(s_context, &context_state_callback, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -681,11 +727,6 @@ PulseSupport::~PulseSupport()
|
||||||
|
pa_glib_mainloop_free(s_mainloop);
|
||||||
|
s_mainloop = NULL;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- if (s_connectionEventloop) {
|
||||||
|
- delete s_connectionEventloop;
|
||||||
|
- s_connectionEventloop = NULL;
|
||||||
|
- }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue