diff --git a/phonon-4.3.80-pulse-devicemove-rejig.patch b/phonon-4.3.80-pulse-devicemove-rejig.patch new file mode 100644 index 0000000..4eff732 --- /dev/null +++ b/phonon-4.3.80-pulse-devicemove-rejig.patch @@ -0,0 +1,454 @@ +diff --git a/phonon/audiooutput.cpp b/phonon/audiooutput.cpp +index e99c394..ddbf360 100644 +--- a/phonon/audiooutput.cpp ++++ b/phonon/audiooutput.cpp +@@ -99,7 +99,9 @@ void AudioOutputPrivate::init(Phonon::Category c) + + category = c; + streamUuid = QUuid::createUuid().toString(); +- PulseSupport::getInstance()->setStreamPropList(category, streamUuid); ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ pulse->setStreamPropList(category, streamUuid); ++ q->connect(pulse, SIGNAL(usingDevice(QString,int)), SLOT(_k_deviceChanged(QString,int))); + + createBackendObject(); + +@@ -232,14 +234,14 @@ bool AudioOutput::setOutputDevice(const AudioOutputDevice &newAudioOutputDevice) + { + K_D(AudioOutput); + if (!newAudioOutputDevice.isValid()) { +- d->outputDeviceOverridden = false; ++ d->outputDeviceOverridden = d->forceMove = false; + const int newIndex = GlobalConfig().audioOutputDeviceFor(d->category); + if (newIndex == d->device.index()) { + return true; + } + d->device = AudioOutputDevice::fromIndex(newIndex); + } else { +- d->outputDeviceOverridden = true; ++ d->outputDeviceOverridden = d->forceMove = true; + if (d->device == newAudioOutputDevice) { + return true; + } +@@ -272,7 +274,10 @@ void AudioOutputPrivate::setupBackendObject() + pINTERFACE_CALL(setVolume(pow(volume, VOLTAGE_TO_LOUDNESS_EXPONENT))); + + // if the output device is not available and the device was not explicitly set +- if (!callSetOutputDevice(this, device) && !outputDeviceOverridden) { ++ // There is no need to set the output device initially if PA is used as ++ // we know it will not work (stream doesn't exist yet) and that this will be ++ // handled by _k_deviceChanged() ++ if (!PulseSupport::getInstance()->isActive() && !callSetOutputDevice(this, device) && !outputDeviceOverridden) { + // fall back in the preference list of output devices + QList deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices); + if (deviceList.isEmpty()) { +@@ -316,6 +321,9 @@ void AudioOutputPrivate::_k_revertFallback() + + void AudioOutputPrivate::_k_audioDeviceFailed() + { ++ if (PulseSupport::getInstance()->isActive()) ++ return; ++ + pDebug() << Q_FUNC_INFO; + // outputDeviceIndex identifies a failing device + // fall back in the preference list of output devices +@@ -338,7 +346,14 @@ void AudioOutputPrivate::_k_audioDeviceFailed() + + void AudioOutputPrivate::_k_deviceListChanged() + { ++ if (PulseSupport::getInstance()->isActive()) ++ return; ++ + pDebug() << Q_FUNC_INFO; ++ // Check to see if we have an override and do not change to a higher priority device if the overridden device is still present. ++ if (outputDeviceOverridden && device.property("available").toBool()) { ++ return; ++ } + // let's see if there's a usable device higher in the preference list + QList deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings); + DeviceChangeType changeType = HigherPreferenceChange; +@@ -365,6 +380,36 @@ void AudioOutputPrivate::_k_deviceListChanged() + } + } + ++void AudioOutputPrivate::_k_deviceChanged(QString inStreamUuid, int deviceIndex) ++{ ++ // Note that this method is only used by PulseAudio at present. ++ if (inStreamUuid == streamUuid) { ++ // 1. Check to see if we are overridden. If we are, and devices do not match, ++ // then try and apply our own device as the output device. ++ // We only do this the first time ++ if (outputDeviceOverridden && forceMove) { ++ forceMove = false; ++ const AudioOutputDevice ¤tDevice = AudioOutputDevice::fromIndex(deviceIndex); ++ if (currentDevice != device) { ++ if (!callSetOutputDevice(this, device)) { ++ // What to do if we are overridden and cannot change to our preferred device? ++ } ++ } ++ } ++ // 2. If we are not overridden, then we need to update our perception of what ++ // device we are using. If the devices do not match, something lower in the ++ // stack is overriding our preferences (e.g. a per-application stream preference, ++ // specific application move, priority list changed etc. etc.) ++ else if (!outputDeviceOverridden) { ++ const AudioOutputDevice ¤tDevice = AudioOutputDevice::fromIndex(deviceIndex); ++ if (currentDevice != device) { ++ // The device is not what we think it is, so lets say what is happening. ++ handleAutomaticDeviceChange(currentDevice, SoundSystemChange); ++ } ++ } ++ } ++} ++ + static struct + { + int first; +@@ -409,6 +454,27 @@ void AudioOutputPrivate::handleAutomaticDeviceChange(const AudioOutputDevice &de + g_lastFallback.second = 0; + } + break; ++ case SoundSystemChange: ++ { ++#ifndef QT_NO_PHONON_PLATFORMPLUGIN ++ if (device1.property("available").toBool()) { ++ const QString text = AudioOutput::tr("Switching to the audio playback device %1
" ++ "which has higher preference or is specifically configured for this stream.").arg(device2.name()); ++ Platform::notification("AudioDeviceFallback", text, ++ QStringList(AudioOutput::tr("Revert back to device '%1'").arg(device1.name())), ++ q, SLOT(_k_revertFallback())); ++ } else { ++ const QString &text = ++ AudioOutput::tr("The audio playback device %1 does not work.
" ++ "Falling back to %2.").arg(device1.name()).arg(device2.name()); ++ Platform::notification("AudioDeviceFallback", text); ++ } ++#endif //QT_NO_PHONON_PLATFORMPLUGIN ++ //outputDeviceOverridden = true; ++ g_lastFallback.first = 0; ++ g_lastFallback.second = 0; ++ } ++ break; + } + } + +diff --git a/phonon/audiooutput.h b/phonon/audiooutput.h +index 4edf135..513a863 100644 +--- a/phonon/audiooutput.h ++++ b/phonon/audiooutput.h +@@ -169,6 +169,7 @@ namespace Phonon + Q_PRIVATE_SLOT(k_func(), void _k_revertFallback()) + Q_PRIVATE_SLOT(k_func(), void _k_audioDeviceFailed()) + Q_PRIVATE_SLOT(k_func(), void _k_deviceListChanged()) ++ Q_PRIVATE_SLOT(k_func(), void _k_deviceChanged(QString streamUuid, int device)) + }; + } //namespace Phonon + +diff --git a/phonon/audiooutput_p.h b/phonon/audiooutput_p.h +index 0a90c3d..01dc48f 100644 +--- a/phonon/audiooutput_p.h ++++ b/phonon/audiooutput_p.h +@@ -59,6 +59,7 @@ class AudioOutputPrivate : public AbstractAudioOutputPrivate + #endif + deviceBeforeFallback(-1), + outputDeviceOverridden(false), ++ forceMove(false), + muted(false) + { + } +@@ -67,7 +68,8 @@ class AudioOutputPrivate : public AbstractAudioOutputPrivate + + enum DeviceChangeType { + FallbackChange, +- HigherPreferenceChange ++ HigherPreferenceChange, ++ SoundSystemChange + }; + void handleAutomaticDeviceChange(const AudioOutputDevice &newDev, DeviceChangeType type); + +@@ -75,6 +77,7 @@ class AudioOutputPrivate : public AbstractAudioOutputPrivate + void _k_revertFallback(); + void _k_audioDeviceFailed(); + void _k_deviceListChanged(); ++ void _k_deviceChanged(QString streamUuid, int deviceIndex); + + private: + QString name; +@@ -87,6 +90,7 @@ class AudioOutputPrivate : public AbstractAudioOutputPrivate + Category category; + int deviceBeforeFallback; + bool outputDeviceOverridden; ++ bool forceMove; + bool muted; + }; + } //namespace Phonon +diff --git a/phonon/pulsesupport.cpp b/phonon/pulsesupport.cpp +index f102177..9d3dd82 100644 +--- a/phonon/pulsesupport.cpp ++++ b/phonon/pulsesupport.cpp +@@ -145,13 +145,11 @@ static QMap s_outputDeviceIndexes; + static QMap s_outputDevices; + static QMap > s_outputDevicePriorities; // prio, device + static QMap s_outputStreamIndexMap; +-static QMap s_outputStreamMoveQueue; + + static QMap s_captureDeviceIndexes; + static QMap s_captureDevices; + static QMap > s_captureDevicePriorities; // prio, device + static QMap s_captureStreamIndexMap; +-static QMap s_captureStreamMoveQueue; + + static void createGenericDevices() + { +@@ -378,80 +376,6 @@ static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manage + } + #endif + +-static void set_output_device(QString streamUuid) +-{ +- // If we only have one device, bail. This will be true if we are not using module-device-manager +- if (s_outputDevices.size() < 2) +- return; +- +- if (!s_outputStreamMoveQueue.contains(streamUuid)) +- return; +- +- if (!s_outputStreamIndexMap.contains(streamUuid)) +- return; +- +- if (s_outputStreamIndexMap[streamUuid] == PA_INVALID_INDEX) +- return; +- +- int device = s_outputStreamMoveQueue[streamUuid]; +- if (!s_outputDevices.contains(device)) +- return; +- +- // We don't remove the uuid from the s_captureStreamMoveQueue +- // as an application may reuse the phonon AudioOutput object +- +- uint32_t pulse_device_index = s_outputDevices[device].pulseIndex; +- uint32_t pulse_stream_index = s_outputStreamIndexMap[streamUuid]; +- +- const QVariant var = s_outputDevices[device].properties["name"]; +- logMessage(QString("Moving Pulse Sink Input %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); +- +- /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. +- pa_operation* o; +- if (!(o = pa_context_move_sink_input_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { +- logMessage(QString("pa_context_move_sink_input_by_index() failed")); +- return; +- } +- pa_operation_unref(o); +-} +- +-static void set_capture_device(QString streamUuid) +-{ +- // If we only have one device, bail. This will be true if we are not using module-device-manager +- if (s_captureDevices.size() < 2) +- return; +- +- if (!s_captureStreamMoveQueue.contains(streamUuid)) +- return; +- +- if (!s_captureStreamIndexMap.contains(streamUuid)) +- return; +- +- if (s_captureStreamIndexMap[streamUuid] == PA_INVALID_INDEX) +- return; +- +- int device = s_captureStreamMoveQueue[streamUuid]; +- if (!s_captureDevices.contains(device)) +- return; +- +- // We don't remove the uuid from the s_captureStreamMoveQueue +- // as an application may reuse the phonon AudioCapture object (when it exists!) +- +- uint32_t pulse_device_index = s_captureDevices[device].pulseIndex; +- uint32_t pulse_stream_index = s_captureStreamIndexMap[streamUuid]; +- +- const QVariant var = s_captureDevices[device].properties["name"]; +- logMessage(QString("Moving Pulse Source Output %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); +- +- /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. +- pa_operation* o; +- if (!(o = pa_context_move_source_output_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { +- logMessage(QString("pa_context_move_source_output_by_index() failed")); +- return; +- } +- pa_operation_unref(o); +-} +- + void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { + Q_UNUSED(userdata); + Q_ASSERT(c); +@@ -474,8 +398,25 @@ void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *us + if ((t = pa_proplist_gets(i->proplist, "phonon.streamid"))) { + logMessage(QString("Found PulseAudio stream index %1 for Phonon Output Stream %2").arg(i->index).arg(t)); + s_outputStreamIndexMap[QString(t)] = i->index; +- // Process any pending moves... +- set_output_device(QString(t)); ++ ++ // Find the sink's phonon index and notify whoever cares... ++ if (PA_INVALID_INDEX != i->sink) { ++ bool found = false; ++ int device; ++ QMap::iterator it; ++ for (it = s_outputDevices.begin(); it != s_outputDevices.end(); ++it) { ++ if ((*it).pulseIndex == i->sink) { ++ found = true; ++ device = it.key(); ++ break; ++ } ++ } ++ if (found) { ++ // OK so we just emit our signal ++ logMessage(QString("Letting the rest of phonon know about this")); ++ s_instance->emitUsingDevice(QString(t), device); ++ } ++ } + } + } + +@@ -501,8 +442,25 @@ void source_output_cb(pa_context *c, const pa_source_output_info *i, int eol, vo + if ((t = pa_proplist_gets(i->proplist, "phonon.streamid"))) { + logMessage(QString("Found PulseAudio stream index %1 for Phonon Capture Stream %2").arg(i->index).arg(t)); + s_captureStreamIndexMap[QString(t)] = i->index; +- // Process any pending moves... +- set_capture_device(QString(t)); ++ ++ // Find the source's phonon index and notify whoever cares... ++ if (PA_INVALID_INDEX != i->source) { ++ bool found = false; ++ int device; ++ QMap::iterator it; ++ for (it = s_captureDevices.begin(); it != s_captureDevices.end(); ++it) { ++ if ((*it).pulseIndex == i->source) { ++ found = true; ++ device = it.key(); ++ break; ++ } ++ } ++ if (found) { ++ // OK so we just emit our signal ++ logMessage(QString("Letting the rest of phonon know about this")); ++ s_instance->emitUsingDevice(QString(t), device); ++ } ++ } + } + } + +@@ -520,7 +478,6 @@ static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t + } else { + logMessage(QString("Removing Phonon Output Stream %1 (it's gone!)").arg(phononid)); + s_outputStreamIndexMap.remove(phononid); +- s_outputStreamMoveQueue.remove(phononid); + } + } + } else { +@@ -543,7 +500,6 @@ static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t + } else { + logMessage(QString("Removing Phonon Capture Stream %1 (it's gone!)").arg(phononid)); + s_captureStreamIndexMap.remove(phononid); +- s_captureStreamMoveQueue.remove(phononid); + } + } + } else { +@@ -933,6 +889,11 @@ void PulseSupport::emitObjectDescriptionChanged(ObjectDescriptionType type) + emit objectDescriptionChanged(type); + } + ++void PulseSupport::emitUsingDevice(QString streamUuid, int device) ++{ ++ emit usingDevice(streamUuid, device); ++} ++ + bool PulseSupport::setOutputDevice(QString streamUuid, int device) { + #ifndef HAVE_PULSEAUDIO + Q_UNUSED(streamUuid); +@@ -949,13 +910,24 @@ bool PulseSupport::setOutputDevice(QString streamUuid, int device) { + const QVariant var = s_outputDevices[device].properties["name"]; + logMessage(QString("Attempting to set Output Device to '%1' for Output Stream %2").arg(var.toString()).arg(streamUuid)); + +- s_outputStreamMoveQueue[streamUuid] = device; + // Attempt to look up the pulse stream index. + if (s_outputStreamIndexMap.contains(streamUuid) && s_outputStreamIndexMap[streamUuid] != PA_INVALID_INDEX) { + logMessage(QString("... Found in map. Moving now")); +- set_output_device(streamUuid); ++ ++ uint32_t pulse_device_index = s_outputDevices[device].pulseIndex; ++ uint32_t pulse_stream_index = s_outputStreamIndexMap[streamUuid]; ++ ++ logMessage(QString("Moving Pulse Sink Input %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); ++ ++ /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. ++ pa_operation* o; ++ if (!(o = pa_context_move_sink_input_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { ++ logMessage(QString("pa_context_move_sink_input_by_index() failed")); ++ return false; ++ } ++ pa_operation_unref(o); + } else { +- logMessage(QString("... Not found in map. Saving move for when the stream appears")); ++ logMessage(QString("... Not found in map. We will be notified of the device when the stream appears and we can process any moves needed then")); + } + return true; + #endif +@@ -977,13 +949,24 @@ bool PulseSupport::setCaptureDevice(QString streamUuid, int device) { + const QVariant var = s_captureDevices[device].properties["name"]; + logMessage(QString("Attempting to set Capture Device to '%1' for Capture Stream %2").arg(var.toString()).arg(streamUuid)); + +- s_captureStreamMoveQueue[streamUuid] = device; + // Attempt to look up the pulse stream index. + if (s_captureStreamIndexMap.contains(streamUuid) && s_captureStreamIndexMap[streamUuid] == PA_INVALID_INDEX) { + logMessage(QString("... Found in map. Moving now")); +- set_capture_device(streamUuid); ++ ++ uint32_t pulse_device_index = s_captureDevices[device].pulseIndex; ++ uint32_t pulse_stream_index = s_captureStreamIndexMap[streamUuid]; ++ ++ logMessage(QString("Moving Pulse Source Output %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); ++ ++ /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. ++ pa_operation* o; ++ if (!(o = pa_context_move_source_output_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { ++ logMessage(QString("pa_context_move_source_output_by_index() failed")); ++ return false; ++ } ++ pa_operation_unref(o); + } else { +- logMessage(QString("... Not found in map. Saving move for when the stream appears")); ++ logMessage(QString("... Not found in map. We will be notified of the device when the stream appears and we can process any moves needed then")); + } + return true; + #endif +@@ -996,9 +979,7 @@ void PulseSupport::clearStreamCache(QString streamUuid) { + #else + logMessage(QString("Clearing stream cache for stream %1").arg(streamUuid)); + s_outputStreamIndexMap.remove(streamUuid); +- s_outputStreamMoveQueue.remove(streamUuid); + s_captureStreamIndexMap.remove(streamUuid); +- s_captureStreamMoveQueue.remove(streamUuid); + #endif + } + +diff --git a/phonon/pulsesupport.h b/phonon/pulsesupport.h +index bba5fd5..c38bece 100644 +--- a/phonon/pulsesupport.h ++++ b/phonon/pulsesupport.h +@@ -54,6 +54,7 @@ namespace Phonon + + void setStreamPropList(Category category, QString streamUuid); + void emitObjectDescriptionChanged(ObjectDescriptionType); ++ void emitUsingDevice(QString streamUuid, int device); + + bool setOutputDevice(QString streamUuid, int device); + bool setCaptureDevice(QString streamUuid, int device); +@@ -61,6 +62,8 @@ namespace Phonon + + signals: + void objectDescriptionChanged(ObjectDescriptionType); ++ void usingDevice(QString streamUuid, int device); ++ + private: + PulseSupport(); + ~PulseSupport(); diff --git a/phonon.spec b/phonon.spec index c8048f4..7afbe5e 100644 --- a/phonon.spec +++ b/phonon.spec @@ -4,7 +4,7 @@ Summary: Multimedia framework api Name: phonon Version: 4.4.0 -Release: 0.2%{?dist} +Release: 0.3%{?dist} Group: System Environment/Libraries License: LGPLv2+ URL: http://phonon.kde.org/ @@ -32,6 +32,7 @@ Patch1: phonon-4.3.50-xine_pulseaudio.patch Patch51: phonon-4.3.50-fix-decodebin-usage.patch Patch52: phonon-4.3.50-gstreamer-fix-seekable-query-failed.patch Patch53: phonon-4.3.50-phonon-allow-stop-empty-source.patch +Patch57: phonon-4.3.80-pulse-devicemove-rejig.patch ## Upstream patches @@ -101,6 +102,7 @@ Provides: %{name}-backend-gst = %{version}-%{release} %patch51 -p0 -b .fix-decodebin-usage %patch52 -p1 -b .gstreamer-fix-seekable-query-failed %patch53 -p1 -b .phonon-allow-stop-empty-source +%patch57 -p1 -b .pulse-devicemove-rejig %build @@ -206,6 +208,9 @@ gtk-update-icon-cache %{_kde4_iconsdir}/hicolor &> /dev/null ||: %changelog +* Fri Mar 12 2010 Rex Dieter - 4.4.0-0.3 +- phonon-4.3.80-pulse-devicemove-rejig.patch (from mdv) + * Wed Feb 24 2010 Rex Dieter - 4.4.0-0.2 - preliminary phonon-4.4.0 tarball