diff --git a/phonon-4.4.0-eventloop.patch b/phonon-4.4.0-eventloop.patch new file mode 100644 index 0000000..a20b5e8 --- /dev/null +++ b/phonon-4.4.0-eventloop.patch @@ -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(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(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(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(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(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 + } + diff --git a/phonon.spec b/phonon.spec index 8ec9d3b..65b723f 100644 --- a/phonon.spec +++ b/phonon.spec @@ -4,7 +4,7 @@ Summary: Multimedia framework api Name: phonon Version: 4.4.0 -Release: 1%{?dist} +Release: 2%{?dist} Group: System Environment/Libraries License: LGPLv2+ URL: http://phonon.kde.org/ @@ -28,6 +28,8 @@ Patch53: phonon-4.3.50-phonon-allow-stop-empty-source.patch Patch57: phonon-4.3.80-pulse-devicemove-rejig.patch ## Upstream patches +# https://bugs.kde.org/show_bug.cgi?id=228324#c23 +Patch100: phonon-4.4.0-eventloop.patch BuildRequires: automoc4 >= 0.9.86 BuildRequires: cmake >= 2.6.0 @@ -87,6 +89,8 @@ Provides: %{name}-backend-gst = %{version}-%{release} %patch53 -p1 -b .phonon-allow-stop-empty-source %patch57 -p1 -b .pulse-devicemove-rejig +%patch100 -p1 -b .eventloop + %build mkdir -p %{_target_platform} @@ -191,6 +195,9 @@ gtk-update-icon-cache %{_kde4_iconsdir}/hicolor &> /dev/null ||: %changelog +* Wed Mar 17 2010 Rex Dieter - 4.4.0-2 +- pa glib/qt eventloop patch (kde#228324) + * Tue Mar 16 2010 Rex Dieter - 4.4.0-1 - phonon-4.4.0 final